From 57b4a072964baa1bc987591f040fa805b0068da8 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 14 Feb 2026 01:57:49 -0500 Subject: [PATCH 01/25] WIP --- build.gradle.kts | 24 +- gradle/libs.versions.toml | 4 + .../volmit => art/arcane}/adapt/Adapt.java | 629 +++---- .../arcane}/adapt/AdaptConfig.java | 8 +- .../arcane}/adapt/PapiExpansion.java | 12 +- .../arcane}/adapt/api/Component.java | 20 +- .../adapt/api/adaptation/Adaptation.java | 48 +- .../api/adaptation/SimpleAdaptation.java | 34 +- .../api/adaptation/chunk/ChunkLoading.java | 6 +- .../api/advancement/AdaptAdvancement.java | 8 +- .../advancement/AdaptAdvancementFrame.java | 2 +- .../api/advancement/AdvancementManager.java | 14 +- .../api/advancement/AdvancementSpec.java | 6 +- .../advancement/AdvancementVisibility.java | 2 +- .../arcane}/adapt/api/data/WorldData.java | 14 +- .../arcane}/adapt/api/data/unit/Earnings.java | 2 +- .../arcane}/adapt/api/item/DataItem.java | 6 +- .../arcane}/adapt/api/item/PotionItem.java | 6 +- .../notification/ActionBarNotification.java | 8 +- .../notification/AdvancementNotification.java | 10 +- .../adapt/api/notification/Notification.java | 4 +- .../adapt/api/notification/Notifier.java | 20 +- .../api/notification/SoundNotification.java | 8 +- .../api/notification/TitleNotification.java | 4 +- .../adapt/api/potion/BrewingManager.java | 10 +- .../adapt/api/potion/BrewingRecipe.java | 2 +- .../arcane}/adapt/api/potion/BrewingTask.java | 6 +- .../adapt/api/potion/PotionBuilder.java | 6 +- .../adapt/api/protection/Protector.java | 4 +- .../api/protection/ProtectorRegistry.java | 4 +- .../arcane}/adapt/api/recipe/AdaptRecipe.java | 4 +- .../adapt/api/recipe/MaterialChar.java | 2 +- .../adapt/api/runtime/AdaptationGate.java | 6 +- .../arcane}/adapt/api/skill/SimpleSkill.java | 36 +- .../arcane}/adapt/api/skill/Skill.java | 50 +- .../adapt/api/skill/SkillRegistry.java | 284 ++-- .../arcane}/adapt/api/tick/Ticked.java | 6 +- .../arcane}/adapt/api/tick/TickedObject.java | 8 +- .../arcane}/adapt/api/tick/Ticker.java | 6 +- .../adapt/api/value/MaterialCount.java | 2 +- .../adapt/api/value/MaterialRecipe.java | 2 +- .../adapt/api/value/MaterialValue.java | 15 +- .../arcane}/adapt/api/version/IAttribute.java | 4 +- .../arcane}/adapt/api/version/IBindings.java | 10 +- .../adapt/api/version/RuntimeAttribute.java | 4 +- .../adapt/api/version/RuntimeBindings.java | 6 +- .../arcane}/adapt/api/version/Version.java | 2 +- .../adapt/api/world/AdaptComponent.java | 6 +- .../arcane}/adapt/api/world/AdaptPlayer.java | 27 +- .../adapt/api/world/AdaptPlayerTracker.java | 2 +- .../arcane}/adapt/api/world/AdaptServer.java | 38 +- .../adapt/api/world/AdaptServerData.java | 6 +- .../adapt/api/world/AdaptStatTracker.java | 2 +- .../adapt/api/world/AdvancementHandler.java | 6 +- .../arcane}/adapt/api/world/Discovery.java | 4 +- .../adapt/api/world/PlayerAdaptation.java | 4 +- .../arcane}/adapt/api/world/PlayerData.java | 34 +- .../api/world/PlayerDataPersistenceQueue.java | 8 +- .../adapt/api/world/PlayerSkillLine.java | 30 +- .../arcane}/adapt/api/xp/Curves.java | 2 +- .../arcane}/adapt/api/xp/NewtonCurve.java | 4 +- .../adapt/api/xp/ResolvedNewtonCurve.java | 2 +- .../arcane}/adapt/api/xp/SpatialXP.java | 6 +- .../arcane}/adapt/api/xp/XP.java | 16 +- .../arcane}/adapt/api/xp/XPMultiplier.java | 4 +- .../arcane}/adapt/command/CommandAdapt.java | 47 +- .../arcane}/adapt/command/CommandClear.java | 22 +- .../arcane}/adapt/command/CommandDebug.java | 23 +- .../arcane}/adapt/command/CommandDefault.java | 28 +- .../arcane}/adapt/command/CommandReset.java | 18 +- .../adaptation/agility/AgilityArmorUp.java | 53 +- .../agility/AgilityLadderSlide.java | 56 +- .../agility/AgilityParkourMomentum.java | 76 +- .../agility/AgilityRollLanding.java | 70 +- .../adaptation/agility/AgilitySuperJump.java | 47 +- .../adaptation/agility/AgilityWallJump.java | 60 +- .../adaptation/agility/AgilityWindUp.java | 67 +- .../architect/ArchitectElevator.java | 48 +- .../architect/ArchitectFoundation.java | 53 +- .../adaptation/architect/ArchitectGlass.java | 37 +- .../architect/ArchitectPlacement.java | 45 +- .../architect/ArchitectSmartShape.java | 38 +- .../architect/ArchitectWirelessRedstone.java | 57 +- .../adapt/content/adaptation/axe/AxeChop.java | 51 +- .../adaptation/axe/AxeCraftLogSwap.java | 36 +- .../adaptation/axe/AxeDropToInventory.java | 38 +- .../adaptation/axe/AxeGroundSmash.java | 54 +- .../adaptation/axe/AxeLeafVeinminer.java | 49 +- .../content/adaptation/axe/AxeTimberMark.java | 56 +- .../adaptation/axe/AxeWoodVeinminer.java | 55 +- .../blocking/BlockingBastionStance.java | 64 +- .../blocking/BlockingBulwarkBash.java | 78 +- .../blocking/BlockingChainArmorer.java | 36 +- .../blocking/BlockingCounterGuard.java | 62 +- .../blocking/BlockingHorseArmorer.java | 36 +- .../blocking/BlockingMirrorBlock.java | 72 +- .../blocking/BlockingMultiArmor.java | 41 +- .../blocking/BlockingSaddlecrafter.java | 36 +- .../adaptation/brewing/BrewingAbsorption.java | 42 +- .../adaptation/brewing/BrewingBlindness.java | 40 +- .../adaptation/brewing/BrewingDarkness.java | 40 +- .../adaptation/brewing/BrewingDecay.java | 40 +- .../adaptation/brewing/BrewingFatigue.java | 42 +- .../adaptation/brewing/BrewingHaste.java | 44 +- .../brewing/BrewingHealthBoost.java | 42 +- .../adaptation/brewing/BrewingHunger.java | 40 +- .../adaptation/brewing/BrewingLingering.java | 62 +- .../adaptation/brewing/BrewingNausea.java | 42 +- .../adaptation/brewing/BrewingResistance.java | 40 +- .../adaptation/brewing/BrewingSaturation.java | 42 +- .../brewing/BrewingSuperHeated.java | 53 +- .../chronos/ChronosAberrantTouch.java | 70 +- .../chronos/ChronosInstantRecall.java | 141 +- .../adaptation/chronos/ChronosSoundFX.java | 6 +- .../chronos/ChronosTemporalEcho.java | 58 +- .../adaptation/chronos/ChronosTimeBomb.java | 106 +- .../chronos/ChronosTimeInABottle.java | 172 +- .../crafting/CraftingBackpacks.java | 30 +- .../crafting/CraftingDeconstruction.java | 28 +- .../adaptation/crafting/CraftingLeather.java | 28 +- .../crafting/CraftingReconstruction.java | 34 +- .../adaptation/crafting/CraftingSkulls.java | 30 +- .../adaptation/crafting/CraftingStations.java | 30 +- .../adaptation/crafting/CraftingXP.java | 28 +- .../discovery/DiscoveryArchaeologist.java | 60 +- .../adaptation/discovery/DiscoveryArmor.java | 55 +- .../discovery/DiscoveryBetterMending.java | 52 +- .../discovery/DiscoveryCartographerPulse.java | 48 +- .../adaptation/discovery/DiscoveryUnity.java | 47 +- .../discovery/DiscoveryVillagerAtt.java | 52 +- .../discovery/DiscoveryXpResist.java | 51 +- .../enchanting/EnchantingAnvilSavant.java | 44 +- .../EnchantingBookshelfAttunement.java | 36 +- .../EnchantingGrindstoneRecovery.java | 54 +- .../enchanting/EnchantingLapisReturn.java | 34 +- .../enchanting/EnchantingOfferReroll.java | 50 +- .../enchanting/EnchantingQuickEnchant.java | 44 +- .../enchanting/EnchantingXPReturn.java | 34 +- .../excavation/ExcavationDropToInventory.java | 38 +- .../excavation/ExcavationHaste.java | 34 +- .../excavation/ExcavationOmniTool.java | 45 +- .../excavation/ExcavationSeismicPing.java | 68 +- .../excavation/ExcavationSpelunker.java | 47 +- .../herbalism/HerbalismBeeShepherd.java | 68 +- .../herbalism/HerbalismCompostCascade.java | 92 +- .../herbalism/HerbalismCraftableCobweb.java | 36 +- .../HerbalismCraftableMushroomBlocks.java | 36 +- .../herbalism/HerbalismDropToInventory.java | 38 +- .../herbalism/HerbalismGrowthAura.java | 57 +- .../herbalism/HerbalismHungryHippo.java | 36 +- .../herbalism/HerbalismHungryShield.java | 38 +- .../adaptation/herbalism/HerbalismLuck.java | 42 +- .../herbalism/HerbalismMyconid.java | 34 +- .../herbalism/HerbalismReplant.java | 58 +- .../herbalism/HerbalismRootedFooting.java | 44 +- .../herbalism/HerbalismSeedSower.java | 52 +- .../herbalism/HerbalismSporeBloom.java | 80 +- .../herbalism/HerbalismTerralid.java | 36 +- .../adaptation/hunter/HunterAdrenaline.java | 38 +- .../hunter/HunterDropToInventory.java | 38 +- .../adaptation/hunter/HunterInvis.java | 54 +- .../adaptation/hunter/HunterJumpBoost.java | 56 +- .../content/adaptation/hunter/HunterLuck.java | 54 +- .../adaptation/hunter/HunterRegen.java | 54 +- .../adaptation/hunter/HunterResistance.java | 56 +- .../adaptation/hunter/HunterSpeed.java | 74 +- .../adaptation/hunter/HunterStrength.java | 56 +- .../hunter/HunterTrophySkinner.java | 60 +- .../adaptation/nether/NetherBlazeLeech.java | 66 +- .../adaptation/nether/NetherFireResist.java | 40 +- .../adaptation/nether/NetherGhastWard.java | 60 +- .../adaptation/nether/NetherLavaWalker.java | 52 +- .../adaptation/nether/NetherPiglinBroker.java | 58 +- .../adaptation/nether/NetherSkullYeet.java | 42 +- .../adaptation/nether/NetherWitherResist.java | 36 +- .../adaptation/pickaxe/PickaxeAutosmelt.java | 44 +- .../adaptation/pickaxe/PickaxeChisel.java | 53 +- .../pickaxe/PickaxeDropToInventory.java | 38 +- .../pickaxe/PickaxeQuarrySense.java | 68 +- .../pickaxe/PickaxeSilkSpawner.java | 36 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 54 +- .../ranged/RangedArrowRecovery.java | 38 +- .../adaptation/ranged/RangedFloaters.java | 56 +- .../adaptation/ranged/RangedForce.java | 43 +- .../adaptation/ranged/RangedLungeShot.java | 40 +- .../adaptation/ranged/RangedPiercing.java | 34 +- .../adaptation/ranged/RangedPinningShot.java | 66 +- .../adaptation/ranged/RangedRicochetBolt.java | 90 +- .../ranged/RangedTrajectorySight.java | 102 +- .../adaptation/ranged/RangedWebBomb.java | 49 +- .../content/adaptation/rift/RiftAccess.java | 47 +- .../content/adaptation/rift/RiftBlink.java | 90 +- .../content/adaptation/rift/RiftDescent.java | 37 +- .../adaptation/rift/RiftEnderTaglock.java | 63 +- .../adaptation/rift/RiftEnderchest.java | 30 +- .../content/adaptation/rift/RiftGate.java | 42 +- .../rift/RiftInflatedPocketDimension.java | 46 +- .../content/adaptation/rift/RiftResist.java | 42 +- .../content/adaptation/rift/RiftVisage.java | 32 +- .../adaptation/rift/RiftVoidMagnet.java | 58 +- .../seaborrne/SeaborneFishersFantasy.java | 36 +- .../adaptation/seaborrne/SeaborneOxygen.java | 38 +- .../seaborrne/SeabornePressureDiver.java | 68 +- .../adaptation/seaborrne/SeaborneSpeed.java | 34 +- .../seaborrne/SeaborneTidecaller.java | 92 +- .../seaborrne/SeaborneTurtlesMiningSpeed.java | 36 +- .../seaborrne/SeaborneTurtlesVision.java | 34 +- .../adaptation/stealth/StealthEnderVeil.java | 36 +- .../adaptation/stealth/StealthGhostArmor.java | 53 +- .../stealth/StealthShadowDecoy.java | 84 +- .../adaptation/stealth/StealthSight.java | 37 +- .../adaptation/stealth/StealthSilentStep.java | 82 +- .../adaptation/stealth/StealthSnatch.java | 45 +- .../adaptation/stealth/StealthSpeed.java | 89 +- .../stealth/util/EntityListing.java | 2 +- .../adaptation/sword/SwordsBloodyBlade.java | 52 +- .../sword/SwordsCrimsonCyclone.java | 80 +- .../adaptation/sword/SwordsDualWield.java | 44 +- .../sword/SwordsExecutionersEdge.java | 50 +- .../adaptation/sword/SwordsMachete.java | 57 +- .../adaptation/sword/SwordsPoisonedBlade.java | 46 +- .../adaptation/sword/SwordsRiposteWindow.java | 52 +- .../sword/effects/DamagingBleedEffect.java | 4 +- .../adaptation/taming/TamingBeastRecall.java | 50 +- .../adaptation/taming/TamingDamage.java | 46 +- .../adaptation/taming/TamingHealthBoost.java | 46 +- .../taming/TamingHealthRegeneration.java | 49 +- .../taming/TamingMountedTactics.java | 80 +- .../taming/TamingPackLeaderAura.java | 48 +- .../adaptation/taming/TamingSharedPain.java | 50 +- .../adaptation/tragoul/TragoulBloodPact.java | 82 +- .../tragoul/TragoulBoneHarvest.java | 80 +- .../adaptation/tragoul/TragoulGlobe.java | 50 +- .../adaptation/tragoul/TragoulHealing.java | 53 +- .../adaptation/tragoul/TragoulLance.java | 40 +- .../adaptation/tragoul/TragoulThorns.java | 45 +- .../tragoul/utils/EntityThings.java | 2 +- .../unarmed/UnarmedBatteringCharge.java | 56 +- .../adaptation/unarmed/UnarmedComboChain.java | 58 +- .../unarmed/UnarmedGlassCannon.java | 42 +- .../adaptation/unarmed/UnarmedPower.java | 36 +- .../unarmed/UnarmedSuckerPunch.java | 42 +- .../adapt/content/block/ScaffoldMatter.java | 2 +- .../content/event/AdaptAdaptationEvent.java | 10 +- .../event/AdaptAdaptationTeleportEvent.java | 6 +- .../event/AdaptAdaptationUseEvent.java | 6 +- .../adapt/content/event/AdaptEvent.java | 6 +- .../adapt/content/event/AdaptPlayerEvent.java | 4 +- .../arcane}/adapt/content/gui/ConfigGui.java | 50 +- .../arcane}/adapt/content/gui/SkillsGui.java | 31 +- .../adapt/content/item/BoundEnderPearl.java | 10 +- .../adapt/content/item/BoundEyeOfEnder.java | 10 +- .../content/item/BoundRedstoneTorch.java | 10 +- .../adapt/content/item/BoundSnowBall.java | 10 +- .../content/item/ChronoTimeBombItem.java | 12 +- .../adapt/content/item/ChronoTimeBottle.java | 12 +- .../adapt/content/item/ExperienceOrb.java | 16 +- .../adapt/content/item/ItemListings.java | 10 +- .../adapt/content/item/KnowledgeOrb.java | 10 +- .../content/item/multiItems/MultiArmor.java | 6 +- .../content/item/multiItems/MultiItem.java | 12 +- .../content/item/multiItems/OmniTool.java | 6 +- .../content/matter/BrewingStandOwner.java | 2 +- .../matter/BrewingStandOwnerMatter.java | 2 +- .../protector/ChestProtectProtector.java | 10 +- .../protector/FactionsClaimProtector.java | 8 +- .../protector/GriefDefenderProtector.java | 8 +- .../protector/GriefPreventionProtector.java | 8 +- .../protector/LocketteProProtector.java | 10 +- .../content/protector/ResidenceProtector.java | 186 +- .../protector/WorldGuardProtector.java | 10 +- .../adapt/content/skill/SkillAgility.java | 52 +- .../adapt/content/skill/SkillArchitect.java | 34 +- .../adapt/content/skill/SkillAxes.java | 42 +- .../adapt/content/skill/SkillBlocking.java | 50 +- .../adapt/content/skill/SkillBrewing.java | 40 +- .../adapt/content/skill/SkillChronos.java | 96 +- .../adapt/content/skill/SkillCrafting.java | 38 +- .../adapt/content/skill/SkillDiscovery.java | 80 +- .../adapt/content/skill/SkillEnchanting.java | 44 +- .../adapt/content/skill/SkillExcavation.java | 44 +- .../adapt/content/skill/SkillHerbalism.java | 62 +- .../adapt/content/skill/SkillHunter.java | 42 +- .../adapt/content/skill/SkillNether.java | 58 +- .../adapt/content/skill/SkillPickaxes.java | 60 +- .../adapt/content/skill/SkillRanged.java | 40 +- .../adapt/content/skill/SkillRift.java | 54 +- .../adapt/content/skill/SkillSeaborne.java | 44 +- .../adapt/content/skill/SkillStealth.java | 52 +- .../adapt/content/skill/SkillSwords.java | 50 +- .../adapt/content/skill/SkillTaming.java | 52 +- .../adapt/content/skill/SkillTragOul.java | 60 +- .../adapt/content/skill/SkillUnarmed.java | 46 +- .../adapt/content/skill/package-info.java | 2 +- .../core/nms/container/BlockProperty.java | 165 ++ .../adapt/engine/framework/MeteredCache.java} | 24 +- .../volmit => art/arcane}/adapt/nms/NMS.java | 2 +- .../arcane}/adapt/service/CommandSVC.java | 18 +- .../arcane}/adapt/service/ConfigInputSVC.java | 12 +- .../arcane}/adapt/service/HotloadSVC.java | 42 +- .../adapt/util/common}/cache/AtomicCache.java | 10 +- .../adapt/util/common}/cache/Cache.java | 19 +- .../util/common/collection}/Dictionary.java | 6 +- .../adapt/util/common/collection}/GBiset.java | 4 +- .../util/common/collection}/GListAdapter.java | 2 +- .../util/common/collection}/KeyPair.java | 2 +- .../art/arcane/adapt/util/common/data/B.java | 157 ++ .../adapt/util/common}/data/Metadata.java | 4 +- .../adapt/util/common}/data/TinyColor.java | 2 +- .../util/common/decree/DecreeContext.java | 20 + .../common/decree/DecreeContextHandler.java | 20 + .../util/common}/decree/DecreeExecutor.java | 19 +- .../adapt/util/common/decree/DecreeNode.java | 17 + .../util/common/decree/DecreeParameter.java | 28 + .../common/decree/DecreeParameterHandler.java | 5 + .../util/common}/decree/DecreeSystem.java | 123 +- .../context/AdaptationListingHandler.java | 10 +- .../decree/context/WorldContextHandler.java | 18 + .../handlers/AdaptationListHandler.java | 10 +- .../handlers/AdaptationProviderHandler.java | 10 +- .../handlers/AdaptationSkillListHandler.java | 10 +- .../decree/handlers/BlockVectorHandler.java | 39 + .../decree/handlers/BooleanHandler.java | 7 + .../common/decree/handlers/ByteHandler.java | 7 + .../common/decree/handlers/DoubleHandler.java | 7 + .../common/decree/handlers/FloatHandler.java | 7 + .../decree/handlers/IntegerHandler.java | 7 + .../common/decree/handlers/LongHandler.java | 7 + .../handlers/OptionalWorldHandler.java} | 17 +- .../decree/handlers/ParticleHandler.java | 8 +- .../decree/handlers/PlayerHandler.java} | 39 +- .../common/decree/handlers/ShortHandler.java | 7 + .../decree/handlers/SkillProviderHandler.java | 10 +- .../common}/decree/handlers/SoundHandler.java | 8 +- .../common/decree/handlers/StringHandler.java | 7 + .../common/decree/handlers/VectorHandler.java | 39 + .../common/decree/handlers/WorldHandler.java | 12 + .../decree/specialhandlers/DummyHandler.java | 7 + .../NullablePlayerHandler.java | 14 + .../decree/virtual/VirtualDecreeCommand.java | 72 +- .../arcane/adapt/util/common/format}/C.java | 4 +- .../common/format}/HiddenStringUtils.java | 2 +- .../arcane/adapt/util/common/format}/ING.java | 3 +- .../adapt/util/common/format}/Localizer.java | 9 +- .../util/common/function}/CallbackCV.java | 2 +- .../util/common/function}/Consumer2.java | 5 +- .../util/common/function}/Consumer3.java | 6 +- .../util/common/function}/Consumer4.java | 6 +- .../util/common/function}/Consumer5.java | 6 +- .../util/common/function}/Consumer6.java | 6 +- .../util/common/function}/Consumer7.java | 6 +- .../util/common/function}/Consumer8.java | 6 +- .../util/common/function}/Function2.java | 5 +- .../util/common/function}/Function3.java | 6 +- .../util/common/function}/Function4.java | 6 +- .../util/common/function}/NastyFunction.java | 2 +- .../util/common/function}/NastyFuture.java | 2 +- .../util/common/function}/NastyRunnable.java | 2 +- .../util/common/function}/NoiseInjector.java | 2 +- .../util/common/function}/NoiseProvider.java | 2 +- .../util/common/function}/Supplier2.java | 2 +- .../util/common/function}/Supplier3.java | 2 +- .../util/common/inventorygui}/Element.java | 6 +- .../common/inventorygui}/ElementEvent.java | 5 +- .../util/common/inventorygui}/GuiConfirm.java | 8 +- .../util/common/inventorygui}/GuiEffects.java | 2 +- .../util/common/inventorygui}/GuiLayout.java | 2 +- .../util/common/inventorygui}/GuiTheme.java | 4 +- .../common/inventorygui}/Inventories.java | 2 +- .../util/common/inventorygui}/Items.java | 4 +- .../inventorygui}/PhantomInventory.java | 2 +- .../PhantomInventoryWrapper.java | 2 +- .../util/common/inventorygui}/UIElement.java | 14 +- .../inventorygui}/UIStaticDecorator.java | 4 +- .../common/inventorygui}/UIVoidDecorator.java | 2 +- .../util/common/inventorygui}/UIWindow.java | 9 +- .../util/common/inventorygui}/Window.java | 3 +- .../common/inventorygui}/WindowDecorator.java | 2 +- .../inventorygui}/WindowResolution.java | 2 +- .../adapt/util/common/io}/BukkitGson.java | 3 +- .../arcane/adapt/util/common/io}/Json.java | 2 +- .../adapt/util/common/io}/LZString.java | 2 +- .../adapt/util/common/io}/PersistentJson.java | 4 +- .../adapt/util/common/io}/ReactiveFolder.java | 4 +- .../adapt/util/common/io}/SQLManager.java | 6 +- .../util/common/io}/ShittyGsonDataClass.java | 2 +- .../adapt/util/common/mantle/Mantle.java | 112 ++ .../adapt/util/common/mantle/MantleChunk.java | 105 ++ .../util/common/mantle/TectonicPlate.java | 60 + .../adapt/util/common/mantle/io/IOWorker.java | 96 ++ .../adapt/util/common/math}/ArrayType.java | 2 +- .../adapt/util/common/math}/CarveResult.java | 2 +- .../adapt/util/common/math}/CaveResult.java | 2 +- .../util/common/math}/ChunkPosition.java | 2 +- .../adapt/util/common/math}/DataPalette.java | 4 +- .../adapt/util/common/math}/Dimension.java | 2 +- .../util/common/math}/DimensionFace.java | 2 +- .../adapt/util/common/math}/Direction.java | 7 +- .../util/common/math}/DoubleArrayUtils.java | 2 +- .../adapt/util/common/math}/HeightMap.java | 2 +- .../util/common/math}/IObjectPlacer.java | 2 +- .../util/common/math}/IPostBlockAccess.java | 2 +- .../arcane/adapt/util/common/math}/IRare.java | 2 +- .../util/common/math}/InterpolationType.java | 2 +- .../util/common/math}/MaterialBlock.java | 2 +- .../adapt/util/common/math}/MathHelper.java | 6 +- .../adapt/util/common/math}/Sphere.java | 5 +- .../adapt/util/common/math}/VectorMath.java | 6 +- .../util/common/math}/VelocitySpeed.java | 2 +- .../adapt/util/common/math}/Writable.java | 2 +- .../util/common/misc}/AdvancementUtils.java | 6 +- .../arcane/adapt/util/common/misc}/Area.java | 9 +- .../adapt/util/common/misc}/Chunker.java | 8 +- .../adapt/util/common/misc}/CustomModel.java | 17 +- .../adapt/util/common/misc}/DependsOn.java | 2 +- .../adapt/util/common/misc}/DirtyString.java | 7 +- .../util/common/misc}/DontObfuscate.java | 2 +- .../adapt/util/common/misc}/Impulse.java | 4 +- .../arcane/adapt/util/common/misc}/Info.java | 2 +- .../adapt/util/common/misc}/MaxNumber.java | 2 +- .../adapt/util/common/misc}/MinNumber.java | 2 +- .../adapt/util/common/misc}/NMSVersion.java | 2 +- .../adapt/util/common/misc}/Shrinkwrap.java | 2 +- .../adapt/util/common/misc}/SoundPlayer.java | 6 +- .../adapt/util/common/nbt}/ByteArrayTag.java | 2 +- .../adapt/util/common/nbt}/ByteTag.java | 2 +- .../adapt/util/common/nbt}/CompoundTag.java | 2 +- .../adapt/util/common/nbt}/DoubleTag.java | 2 +- .../arcane/adapt/util/common/nbt}/EndTag.java | 2 +- .../adapt/util/common/nbt}/FloatTag.java | 2 +- .../adapt/util/common/nbt}/IntArrayTag.java | 2 +- .../arcane/adapt/util/common/nbt}/IntTag.java | 2 +- .../adapt/util/common/nbt}/ListTag.java | 2 +- .../adapt/util/common/nbt}/LongTag.java | 2 +- .../adapt/util/common/nbt}/NBTConstants.java | 2 +- .../util/common/nbt}/NBTInputStream.java | 2 +- .../util/common/nbt}/NBTOutputStream.java | 2 +- .../adapt/util/common/nbt}/NBTUtils.java | 2 +- .../adapt/util/common/nbt}/NibbleArray.java | 4 +- .../adapt/util/common/nbt}/ShortTag.java | 2 +- .../adapt/util/common/nbt}/StringTag.java | 2 +- .../arcane/adapt/util/common/nbt}/Tag.java | 2 +- .../util/common/parallel/BurstExecutor.java | 9 + .../util/common/parallel/MultiBurst.java | 39 + .../util/common/plugin}/AdaptService.java | 4 +- .../adapt/util/common/plugin}/IActivator.java | 2 +- .../adapt/util/common/plugin}/Instance.java | 2 +- .../adapt/util/common/plugin}/Metrics.java | 4 +- .../adapt/util/common/plugin}/Permission.java | 2 +- .../util/common/plugin}/VolmitPlugin.java | 15 +- .../util/common/plugin}/VolmitSender.java | 29 +- .../adapt/util/common}/reflect/Reflect.java | 2 +- .../util/common}/reflect/WrappedField.java | 6 +- .../reflect/WrappedReturningMethod.java | 6 +- .../reflect/events/ReflectiveEvents.java | 22 +- .../common}/reflect/events/api/Event.java | 2 +- .../reflect/events/api/ReflectiveHandler.java | 2 +- .../api/entity/EndermanAttackPlayerEvent.java | 2 +- .../api/entity/EntityDismountEvent.java | 2 +- .../events/api/entity/EntityEvent.java | 4 +- .../events/api/entity/EntityMountEvent.java | 2 +- .../reflect/registries/Attributes.java | 2 +- .../reflect/registries/Enchantments.java | 2 +- .../reflect/registries/EntityTypes.java | 2 +- .../common}/reflect/registries/ItemFlags.java | 2 +- .../common}/reflect/registries/Materials.java | 2 +- .../common}/reflect/registries/Particles.java | 2 +- .../reflect/registries/PotionEffectTypes.java | 2 +- .../reflect/registries/PotionTypes.java | 2 +- .../reflect/registries/RegistryUtil.java | 4 +- .../adapt/util/common/scheduling}/J.java | 115 +- .../adapt/util/project/command}/Command.java | 2 +- .../util/project/command}/CommandDummy.java | 2 +- .../adapt/util/project/command}/Control.java | 2 +- .../adapt/util/project/command/FCommand.java | 5 + .../adapt/util/project}/command/FConst.java | 2 +- .../adapt/util/project}/command/FService.java | 2 +- .../adapt/util/project}/command/Feedback.java | 96 +- .../adapt/util/project/command}/ICommand.java | 2 +- .../util/project/command}/IController.java | 2 +- .../util/project/command}/MortarCommand.java | 7 +- .../project/command}/MortarPermission.java | 4 +- .../util/project/command}/MortarSender.java | 4 +- .../util/project/command}/RouterCommand.java | 2 +- .../util/project}/command/SoundFeedback.java | 2 +- .../util/project/command}/VirtualCommand.java | 12 +- .../util/project}/config/ConfigAdvanced.java | 2 +- .../project}/config/ConfigDescription.java | 2 +- .../adapt/util/project}/config/ConfigDoc.java | 2 +- .../project}/config/ConfigDocumentation.java | 2 +- .../project}/config/ConfigFileSupport.java | 10 +- .../config/ConfigMigrationManager.java | 6 +- .../config}/ConfigRewriteReporter.java | 6 +- .../adapt/util/project/config}/Denv.java | 2 +- .../adapt/util/project/config}/Desc.java | 2 +- .../adapt/util/project/config}/Required.java | 2 +- .../adapt/util/project}/config/TomlCodec.java | 4 +- .../adapt/util/project}/redis/RedisSync.java | 16 +- .../util/project}/secret/SecretSplash.java | 8 +- src/main/java/com/volmit/adapt/util/AR.java | 40 - .../com/volmit/adapt/util/AtomicAverage.java | 101 -- .../adapt/util/AtomicRollingSequence.java | 110 -- .../java/com/volmit/adapt/util/Average.java | 99 -- .../com/volmit/adapt/util/BlockPosition.java | 67 - .../java/com/volmit/adapt/util/Board.java | 148 -- .../com/volmit/adapt/util/BoardEntry.java | 57 - .../com/volmit/adapt/util/BoardManager.java | 105 -- .../com/volmit/adapt/util/BoardProvider.java | 32 - .../com/volmit/adapt/util/BoardSettings.java | 33 - .../volmit/adapt/util/BoardUpdateTask.java | 43 - .../com/volmit/adapt/util/BurstExecutor.java | 65 - src/main/java/com/volmit/adapt/util/CDou.java | 59 - .../java/com/volmit/adapt/util/Callback.java | 35 - .../volmit/adapt/util/CancellableTask.java | 23 - .../com/volmit/adapt/util/ChronoLatch.java | 50 - .../java/com/volmit/adapt/util/Contained.java | 45 - .../java/com/volmit/adapt/util/Converter.java | 29 - .../java/com/volmit/adapt/util/Cuboid.java | 772 --------- .../volmit/adapt/util/CuboidException.java | 32 - .../volmit/adapt/util/CustomOutputStream.java | 30 - src/main/java/com/volmit/adapt/util/DOP.java | 35 - .../com/volmit/adapt/util/FastParticle.java | 179 -- .../com/volmit/adapt/util/FastReflection.java | 77 - .../com/volmit/adapt/util/FinalInteger.java | 48 - src/main/java/com/volmit/adapt/util/Form.java | 1352 --------------- .../volmit/adapt/util/GroupedExecutor.java | 113 -- src/main/java/com/volmit/adapt/util/IO.java | 1509 ----------------- .../com/volmit/adapt/util/JarScanner.java | 94 - .../java/com/volmit/adapt/util/Looper.java | 45 - src/main/java/com/volmit/adapt/util/M.java | 374 ---- .../com/volmit/adapt/util/MultiBurst.java | 70 - src/main/java/com/volmit/adapt/util/O.java | 65 - .../com/volmit/adapt/util/Observable.java | 31 - .../java/com/volmit/adapt/util/Observer.java | 24 - .../com/volmit/adapt/util/ParticleSender.java | 122 -- .../adapt/util/ParticleSenderLegacy.java | 184 -- .../com/volmit/adapt/util/ParticleType.java | 195 --- .../java/com/volmit/adapt/util/Point3d.java | 179 -- .../java/com/volmit/adapt/util/Point3f.java | 180 -- .../java/com/volmit/adapt/util/Point4d.java | 214 --- .../java/com/volmit/adapt/util/Point4f.java | 214 --- .../volmit/adapt/util/PrecisionStopwatch.java | 123 -- .../java/com/volmit/adapt/util/Queue.java | 51 - .../com/volmit/adapt/util/QueueExecutor.java | 59 - src/main/java/com/volmit/adapt/util/RNG.java | 170 -- .../volmit/adapt/util/RollingSequence.java | 105 -- src/main/java/com/volmit/adapt/util/S.java | 29 - src/main/java/com/volmit/adapt/util/SR.java | 40 - .../com/volmit/adapt/util/ScoreDirection.java | 32 - .../com/volmit/adapt/util/ShurikenQueue.java | 102 -- .../java/com/volmit/adapt/util/Spiraled.java | 24 - .../java/com/volmit/adapt/util/Spiraler.java | 77 - .../java/com/volmit/adapt/util/Switch.java | 42 - .../com/volmit/adapt/util/TaskExecutor.java | 204 --- .../com/volmit/adapt/util/ThreadMonitor.java | 87 - .../java/com/volmit/adapt/util/Tuple2d.java | 581 ------- .../java/com/volmit/adapt/util/Tuple2f.java | 586 ------- .../java/com/volmit/adapt/util/Tuple3d.java | 744 -------- .../java/com/volmit/adapt/util/Tuple3f.java | 689 -------- .../java/com/volmit/adapt/util/Tuple4d.java | 849 ---------- .../java/com/volmit/adapt/util/Tuple4f.java | 773 --------- src/main/java/com/volmit/adapt/util/V.java | 128 -- .../com/volmit/adapt/util/VecMathUtil.java | 87 - .../java/com/volmit/adapt/util/Vector2d.java | 168 -- .../java/com/volmit/adapt/util/Vector2f.java | 168 -- .../java/com/volmit/adapt/util/Vector3d.java | 192 --- .../java/com/volmit/adapt/util/Vector3f.java | 189 --- .../java/com/volmit/adapt/util/Violator.java | 270 --- .../volmit/adapt/util/VoidOutputStream.java | 29 - .../java/com/volmit/adapt/util/WeightMap.java | 63 - .../java/com/volmit/adapt/util/Wrapper.java | 70 - .../volmit/adapt/util/cache/ChunkCache2D.java | 46 - .../volmit/adapt/util/cache/WorldCache2D.java | 45 - .../volmit/adapt/util/collection/GBiset.java | 82 - .../adapt/util/collection/GListAdapter.java | 60 - .../volmit/adapt/util/collection/KList.java | 689 -------- .../volmit/adapt/util/collection/KMap.java | 390 ----- .../volmit/adapt/util/collection/KSet.java | 88 - .../volmit/adapt/util/collection/KeyPair.java | 60 - .../adapt/util/collection/StateList.java | 105 -- .../volmit/adapt/util/command/FCommand.java | 5 - .../java/com/volmit/adapt/util/data/B.java | 661 -------- .../volmit/adapt/util/data/ChunkCache.java | 53 - .../volmit/adapt/util/data/ComplexCache.java | 46 - .../com/volmit/adapt/util/data/Cuboid.java | 751 -------- .../java/com/volmit/adapt/util/data/DUTF.java | 53 - .../volmit/adapt/util/data/DataPalette.java | 90 - .../com/volmit/adapt/util/data/Dimension.java | 90 - .../volmit/adapt/util/data/DimensionFace.java | 42 - .../adapt/util/data/DoubleArrayUtils.java | 49 - .../com/volmit/adapt/util/data/Heafty.java | 25 - .../com/volmit/adapt/util/data/HeightMap.java | 39 - .../com/volmit/adapt/util/data/IOAdapter.java | 30 - .../adapt/util/data/InvertedBiomeGrid.java | 55 - .../com/volmit/adapt/util/data/KCache.java | 90 - .../volmit/adapt/util/data/MaterialBlock.java | 127 -- .../volmit/adapt/util/data/NibbleArray.java | 195 --- .../adapt/util/data/NibbleDataPalette.java | 127 -- .../com/volmit/adapt/util/data/Recycler.java | 170 -- .../com/volmit/adapt/util/data/Varint.java | 225 --- .../adapt/util/data/WeightedRandom.java | 66 - .../com/volmit/adapt/util/data/Writable.java | 30 - .../adapt/util/decree/DecreeContext.java | 47 - .../util/decree/DecreeContextHandler.java | 47 - .../volmit/adapt/util/decree/DecreeNode.java | 102 -- .../adapt/util/decree/DecreeOrigin.java | 45 - .../adapt/util/decree/DecreeParameter.java | 117 -- .../util/decree/DecreeParameterHandler.java | 165 -- .../adapt/util/decree/annotations/Decree.java | 66 - .../adapt/util/decree/annotations/Param.java | 70 - .../decree/context/WorldContextHandler.java | 34 - .../exceptions/DecreeParsingException.java | 29 - .../decree/handlers/BlockVectorHandler.java | 111 -- .../util/decree/handlers/BooleanHandler.java | 61 - .../util/decree/handlers/ByteHandler.java | 58 - .../util/decree/handlers/DoubleHandler.java | 62 - .../util/decree/handlers/FloatHandler.java | 62 - .../util/decree/handlers/IntegerHandler.java | 61 - .../util/decree/handlers/LongHandler.java | 64 - .../decree/handlers/OptionalWorldHandler.java | 63 - .../util/decree/handlers/PlayerHandler.java | 70 - .../util/decree/handlers/ShortHandler.java | 61 - .../util/decree/handlers/StringHandler.java | 56 - .../util/decree/handlers/VectorHandler.java | 112 -- .../util/decree/handlers/WorldHandler.java | 72 - .../NullablePlayerHandler.java | 13 - .../volmit/adapt/util/function/Consumer2.java | 26 - .../adapt/util/function/Consumer2IO.java | 28 - .../volmit/adapt/util/function/Consumer3.java | 26 - .../volmit/adapt/util/function/Consumer4.java | 25 - .../adapt/util/function/Consumer4IO.java | 27 - .../volmit/adapt/util/function/Consumer5.java | 25 - .../volmit/adapt/util/function/Consumer6.java | 25 - .../volmit/adapt/util/function/Consumer8.java | 25 - .../volmit/adapt/util/function/Function2.java | 25 - .../volmit/adapt/util/function/Function3.java | 25 - .../volmit/adapt/util/function/Function4.java | 25 - .../adapt/util/function/NastyFunction.java | 24 - .../adapt/util/function/NastyFuture.java | 24 - .../adapt/util/function/NastyRunnable.java | 24 - .../adapt/util/function/NastySupplier.java | 24 - .../adapt/util/function/NoiseInjector.java | 25 - .../adapt/util/function/NoiseProvider.java | 25 - .../adapt/util/function/NoiseProvider3.java | 25 - .../volmit/adapt/util/function/Supplier2.java | 24 - .../volmit/adapt/util/function/Supplier3.java | 24 - .../adapt/util/function/Supplier3R.java | 24 - src/main/resources/plugin.yml | 2 +- velocity/build.gradle.kts | 2 +- .../arcane}/adapt/AdaptVelocity.java | 4 +- .../arcane}/adapt/RedisHandler.java | 8 +- .../util/project}/redis/RedisConfig.java | 2 +- .../util/project}/redis/VelocityConfig.java | 2 +- .../redis/codec/ByteBufferInputStream.java | 2 +- .../util/project}/redis/codec/Codec.java | 2 +- .../project}/redis/codec/DataMessage.java | 2 +- .../project}/redis/codec/DataRequest.java | 2 +- .../util/project}/redis/codec/Message.java | 2 +- .../src/main/templates/BuildConstants.java | 2 +- 659 files changed, 7987 insertions(+), 27202 deletions(-) rename src/main/java/{com/volmit => art/arcane}/adapt/Adapt.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/AdaptConfig.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/PapiExpansion.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/Component.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/adaptation/Adaptation.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/adaptation/SimpleAdaptation.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/adaptation/chunk/ChunkLoading.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/advancement/AdaptAdvancement.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/advancement/AdaptAdvancementFrame.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/advancement/AdvancementManager.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/advancement/AdvancementSpec.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/advancement/AdvancementVisibility.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/data/WorldData.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/data/unit/Earnings.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/item/DataItem.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/item/PotionItem.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/ActionBarNotification.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/AdvancementNotification.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/Notification.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/Notifier.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/SoundNotification.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/notification/TitleNotification.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/potion/BrewingManager.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/potion/BrewingRecipe.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/potion/BrewingTask.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/potion/PotionBuilder.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/protection/Protector.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/protection/ProtectorRegistry.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/recipe/AdaptRecipe.java (99%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/recipe/MaterialChar.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/runtime/AdaptationGate.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/skill/SimpleSkill.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/skill/Skill.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/skill/SkillRegistry.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/tick/Ticked.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/tick/TickedObject.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/tick/Ticker.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/value/MaterialCount.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/value/MaterialRecipe.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/value/MaterialValue.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/version/IAttribute.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/version/IBindings.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/version/RuntimeAttribute.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/version/RuntimeBindings.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/version/Version.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptComponent.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptPlayer.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptPlayerTracker.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptServer.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptServerData.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdaptStatTracker.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/AdvancementHandler.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/Discovery.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/PlayerAdaptation.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/PlayerData.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/PlayerDataPersistenceQueue.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/world/PlayerSkillLine.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/Curves.java (99%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/NewtonCurve.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/ResolvedNewtonCurve.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/SpatialXP.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/XP.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/api/xp/XPMultiplier.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/command/CommandAdapt.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/command/CommandClear.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/command/CommandDebug.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/command/CommandDefault.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/command/CommandReset.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityArmorUp.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityLadderSlide.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityParkourMomentum.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityRollLanding.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilitySuperJump.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityWallJump.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/agility/AgilityWindUp.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectElevator.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectFoundation.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectGlass.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectPlacement.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectSmartShape.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeChop.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeCraftLogSwap.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeDropToInventory.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeGroundSmash.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeLeafVeinminer.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeTimberMark.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/axe/AxeWoodVeinminer.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingBastionStance.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingBulwarkBash.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingChainArmorer.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingCounterGuard.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingHorseArmorer.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingMirrorBlock.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingMultiArmor.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingAbsorption.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingBlindness.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingDarkness.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingDecay.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingFatigue.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingHaste.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingHealthBoost.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingHunger.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingLingering.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingNausea.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingResistance.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingSaturation.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/brewing/BrewingSuperHeated.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosAberrantTouch.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosInstantRecall.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosSoundFX.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosTemporalEcho.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosTimeBomb.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/chronos/ChronosTimeInABottle.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingBackpacks.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingDeconstruction.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingLeather.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingReconstruction.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingSkulls.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingStations.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/crafting/CraftingXP.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryArmor.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryBetterMending.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryUnity.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/discovery/DiscoveryXpResist.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/enchanting/EnchantingXPReturn.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/excavation/ExcavationDropToInventory.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/excavation/ExcavationHaste.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/excavation/ExcavationOmniTool.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/excavation/ExcavationSeismicPing.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/excavation/ExcavationSpelunker.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismHungryShield.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismLuck.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismMyconid.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismReplant.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismSeedSower.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/herbalism/HerbalismTerralid.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterAdrenaline.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterDropToInventory.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterInvis.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterJumpBoost.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterLuck.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterRegen.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterResistance.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterSpeed.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterStrength.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/hunter/HunterTrophySkinner.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherBlazeLeech.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherFireResist.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherGhastWard.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherLavaWalker.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherPiglinBroker.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherSkullYeet.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/nether/NetherWitherResist.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeChisel.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedArrowRecovery.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedFloaters.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedForce.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedLungeShot.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedPiercing.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedPinningShot.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedRicochetBolt.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedTrajectorySight.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/ranged/RangedWebBomb.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftAccess.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftBlink.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftDescent.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftEnderTaglock.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftEnderchest.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftGate.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftResist.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftVisage.java (80%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/rift/RiftVoidMagnet.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneOxygen.java (79%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneSpeed.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthEnderVeil.java (79%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthGhostArmor.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthShadowDecoy.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthSight.java (81%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthSilentStep.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthSnatch.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/StealthSpeed.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/stealth/util/EntityListing.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsBloodyBlade.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsDualWield.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsExecutionersEdge.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsMachete.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsPoisonedBlade.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/SwordsRiposteWindow.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingBeastRecall.java (86%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingDamage.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingHealthBoost.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingHealthRegeneration.java (82%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingMountedTactics.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingPackLeaderAura.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/taming/TamingSharedPain.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulBloodPact.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulGlobe.java (84%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulHealing.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulLance.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/TragoulThorns.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/tragoul/utils/EntityThings.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java (88%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/unarmed/UnarmedComboChain.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/unarmed/UnarmedPower.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java (85%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/block/ScaffoldMatter.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/event/AdaptAdaptationEvent.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/event/AdaptAdaptationTeleportEvent.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/event/AdaptAdaptationUseEvent.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/event/AdaptEvent.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/event/AdaptPlayerEvent.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/gui/ConfigGui.java (98%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/gui/SkillsGui.java (89%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/BoundEnderPearl.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/BoundEyeOfEnder.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/BoundRedstoneTorch.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/BoundSnowBall.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/ChronoTimeBombItem.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/ChronoTimeBottle.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/ExperienceOrb.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/ItemListings.java (99%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/KnowledgeOrb.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/multiItems/MultiArmor.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/multiItems/MultiItem.java (95%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/item/multiItems/OmniTool.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/matter/BrewingStandOwner.java (96%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/matter/BrewingStandOwnerMatter.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/ChestProtectProtector.java (83%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/FactionsClaimProtector.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/GriefDefenderProtector.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/GriefPreventionProtector.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/LocketteProProtector.java (77%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/ResidenceProtector.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/protector/WorldGuardProtector.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillAgility.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillArchitect.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillAxes.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillBlocking.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillBrewing.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillChronos.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillCrafting.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillDiscovery.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillEnchanting.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillExcavation.java (92%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillHerbalism.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillHunter.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillNether.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillPickaxes.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillRanged.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillRift.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillSeaborne.java (93%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillStealth.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillSwords.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillTaming.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillTragOul.java (90%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/SkillUnarmed.java (91%) rename src/main/java/{com/volmit => art/arcane}/adapt/content/skill/package-info.java (96%) create mode 100644 src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java rename src/main/java/{com/volmit/adapt/util/data/Shrinkwrap.java => art/arcane/adapt/engine/framework/MeteredCache.java} (70%) rename src/main/java/{com/volmit => art/arcane}/adapt/nms/NMS.java (97%) rename src/main/java/{com/volmit => art/arcane}/adapt/service/CommandSVC.java (87%) rename src/main/java/{com/volmit => art/arcane}/adapt/service/ConfigInputSVC.java (94%) rename src/main/java/{com/volmit => art/arcane}/adapt/service/HotloadSVC.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/cache/AtomicCache.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/cache/Cache.java (73%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/collection}/Dictionary.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/collection}/GBiset.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/collection}/GListAdapter.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/collection}/KeyPair.java (96%) create mode 100644 src/main/java/art/arcane/adapt/util/common/data/B.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/data/Metadata.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/data/TinyColor.java (99%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/DecreeExecutor.java (70%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/DecreeSystem.java (53%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/context/AdaptationListingHandler.java (94%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/AdaptationListHandler.java (72%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/AdaptationProviderHandler.java (73%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/AdaptationSkillListHandler.java (73%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java rename src/main/java/{com/volmit/adapt/util/data/CuboidException.java => art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java} (66%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/ParticleHandler.java (75%) rename src/main/java/{com/volmit/adapt/util/decree/specialhandlers/DummyHandler.java => art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java} (52%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/SkillProviderHandler.java (72%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/handlers/SoundHandler.java (75%) create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/decree/virtual/VirtualDecreeCommand.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/format}/C.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/format}/HiddenStringUtils.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/format}/ING.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/format}/Localizer.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/CallbackCV.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer2.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer3.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer4.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer5.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer6.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer7.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Consumer8.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Function2.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Function3.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Function4.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/NastyFunction.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/NastyFuture.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/NastyRunnable.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/NoiseInjector.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/NoiseProvider.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Supplier2.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/function}/Supplier3.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/Element.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/ElementEvent.java (91%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/GuiConfirm.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/GuiEffects.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/GuiLayout.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/GuiTheme.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/Inventories.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/Items.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/PhantomInventory.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/PhantomInventoryWrapper.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/UIElement.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/UIStaticDecorator.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/UIVoidDecorator.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/UIWindow.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/Window.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/WindowDecorator.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/inventorygui}/WindowResolution.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/BukkitGson.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/Json.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/LZString.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/PersistentJson.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/ReactiveFolder.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/SQLManager.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/io}/ShittyGsonDataClass.java (96%) create mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java create mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java create mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java create mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/ArrayType.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/CarveResult.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/CaveResult.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/ChunkPosition.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/DataPalette.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/Dimension.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/DimensionFace.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/Direction.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/DoubleArrayUtils.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/HeightMap.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/IObjectPlacer.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/IPostBlockAccess.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/IRare.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/InterpolationType.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/MaterialBlock.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/MathHelper.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/Sphere.java (87%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/VectorMath.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/VelocitySpeed.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/math}/Writable.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/AdvancementUtils.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/Area.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/Chunker.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/CustomModel.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/DependsOn.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/DirtyString.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/DontObfuscate.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/Impulse.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/Info.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/MaxNumber.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/MinNumber.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/NMSVersion.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/Shrinkwrap.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/misc}/SoundPlayer.java (93%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/ByteArrayTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/ByteTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/CompoundTag.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/DoubleTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/EndTag.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/FloatTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/IntArrayTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/IntTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/ListTag.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/LongTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/NBTConstants.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/NBTInputStream.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/NBTOutputStream.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/NBTUtils.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/NibbleArray.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/ShortTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/StringTag.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/nbt}/Tag.java (97%) create mode 100644 src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java create mode 100644 src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/AdaptService.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/IActivator.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/Instance.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/Metrics.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/Permission.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/VolmitPlugin.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/plugin}/VolmitSender.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/Reflect.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/WrappedField.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/WrappedReturningMethod.java (91%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/ReflectiveEvents.java (90%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/Event.java (53%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/ReflectiveHandler.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/entity/EndermanAttackPlayerEvent.java (84%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/entity/EntityDismountEvent.java (80%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/entity/EntityEvent.java (60%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/events/api/entity/EntityMountEvent.java (79%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/Attributes.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/Enchantments.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/EntityTypes.java (78%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/ItemFlags.java (80%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/Materials.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/Particles.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/PotionEffectTypes.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/PotionTypes.java (92%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common}/reflect/registries/RegistryUtil.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/common/scheduling}/J.java (74%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/Command.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/CommandDummy.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/Control.java (96%) create mode 100644 src/main/java/art/arcane/adapt/util/project/command/FCommand.java rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/command/FConst.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/command/FService.java (59%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/command/Feedback.java (86%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/ICommand.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/IController.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/MortarCommand.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/MortarPermission.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/MortarSender.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/RouterCommand.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/command/SoundFeedback.java (88%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/command}/VirtualCommand.java (94%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigAdvanced.java (87%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigDescription.java (88%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigDoc.java (89%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigDocumentation.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigFileSupport.java (97%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/ConfigMigrationManager.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/config}/ConfigRewriteReporter.java (98%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/config}/Denv.java (95%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/config}/Desc.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project/config}/Required.java (96%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/config/TomlCodec.java (99%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/RedisSync.java (87%) rename src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/secret/SecretSplash.java (98%) delete mode 100644 src/main/java/com/volmit/adapt/util/AR.java delete mode 100644 src/main/java/com/volmit/adapt/util/AtomicAverage.java delete mode 100644 src/main/java/com/volmit/adapt/util/AtomicRollingSequence.java delete mode 100644 src/main/java/com/volmit/adapt/util/Average.java delete mode 100644 src/main/java/com/volmit/adapt/util/BlockPosition.java delete mode 100644 src/main/java/com/volmit/adapt/util/Board.java delete mode 100644 src/main/java/com/volmit/adapt/util/BoardEntry.java delete mode 100644 src/main/java/com/volmit/adapt/util/BoardManager.java delete mode 100644 src/main/java/com/volmit/adapt/util/BoardProvider.java delete mode 100644 src/main/java/com/volmit/adapt/util/BoardSettings.java delete mode 100644 src/main/java/com/volmit/adapt/util/BoardUpdateTask.java delete mode 100644 src/main/java/com/volmit/adapt/util/BurstExecutor.java delete mode 100644 src/main/java/com/volmit/adapt/util/CDou.java delete mode 100644 src/main/java/com/volmit/adapt/util/Callback.java delete mode 100644 src/main/java/com/volmit/adapt/util/CancellableTask.java delete mode 100644 src/main/java/com/volmit/adapt/util/ChronoLatch.java delete mode 100644 src/main/java/com/volmit/adapt/util/Contained.java delete mode 100644 src/main/java/com/volmit/adapt/util/Converter.java delete mode 100644 src/main/java/com/volmit/adapt/util/Cuboid.java delete mode 100644 src/main/java/com/volmit/adapt/util/CuboidException.java delete mode 100644 src/main/java/com/volmit/adapt/util/CustomOutputStream.java delete mode 100644 src/main/java/com/volmit/adapt/util/DOP.java delete mode 100644 src/main/java/com/volmit/adapt/util/FastParticle.java delete mode 100644 src/main/java/com/volmit/adapt/util/FastReflection.java delete mode 100644 src/main/java/com/volmit/adapt/util/FinalInteger.java delete mode 100644 src/main/java/com/volmit/adapt/util/Form.java delete mode 100644 src/main/java/com/volmit/adapt/util/GroupedExecutor.java delete mode 100644 src/main/java/com/volmit/adapt/util/IO.java delete mode 100644 src/main/java/com/volmit/adapt/util/JarScanner.java delete mode 100644 src/main/java/com/volmit/adapt/util/Looper.java delete mode 100644 src/main/java/com/volmit/adapt/util/M.java delete mode 100644 src/main/java/com/volmit/adapt/util/MultiBurst.java delete mode 100644 src/main/java/com/volmit/adapt/util/O.java delete mode 100644 src/main/java/com/volmit/adapt/util/Observable.java delete mode 100644 src/main/java/com/volmit/adapt/util/Observer.java delete mode 100644 src/main/java/com/volmit/adapt/util/ParticleSender.java delete mode 100644 src/main/java/com/volmit/adapt/util/ParticleSenderLegacy.java delete mode 100644 src/main/java/com/volmit/adapt/util/ParticleType.java delete mode 100644 src/main/java/com/volmit/adapt/util/Point3d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Point3f.java delete mode 100644 src/main/java/com/volmit/adapt/util/Point4d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Point4f.java delete mode 100644 src/main/java/com/volmit/adapt/util/PrecisionStopwatch.java delete mode 100644 src/main/java/com/volmit/adapt/util/Queue.java delete mode 100644 src/main/java/com/volmit/adapt/util/QueueExecutor.java delete mode 100644 src/main/java/com/volmit/adapt/util/RNG.java delete mode 100644 src/main/java/com/volmit/adapt/util/RollingSequence.java delete mode 100644 src/main/java/com/volmit/adapt/util/S.java delete mode 100644 src/main/java/com/volmit/adapt/util/SR.java delete mode 100644 src/main/java/com/volmit/adapt/util/ScoreDirection.java delete mode 100644 src/main/java/com/volmit/adapt/util/ShurikenQueue.java delete mode 100644 src/main/java/com/volmit/adapt/util/Spiraled.java delete mode 100644 src/main/java/com/volmit/adapt/util/Spiraler.java delete mode 100644 src/main/java/com/volmit/adapt/util/Switch.java delete mode 100644 src/main/java/com/volmit/adapt/util/TaskExecutor.java delete mode 100644 src/main/java/com/volmit/adapt/util/ThreadMonitor.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple2d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple2f.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple3d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple3f.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple4d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Tuple4f.java delete mode 100644 src/main/java/com/volmit/adapt/util/V.java delete mode 100644 src/main/java/com/volmit/adapt/util/VecMathUtil.java delete mode 100644 src/main/java/com/volmit/adapt/util/Vector2d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Vector2f.java delete mode 100644 src/main/java/com/volmit/adapt/util/Vector3d.java delete mode 100644 src/main/java/com/volmit/adapt/util/Vector3f.java delete mode 100644 src/main/java/com/volmit/adapt/util/Violator.java delete mode 100644 src/main/java/com/volmit/adapt/util/VoidOutputStream.java delete mode 100644 src/main/java/com/volmit/adapt/util/WeightMap.java delete mode 100644 src/main/java/com/volmit/adapt/util/Wrapper.java delete mode 100644 src/main/java/com/volmit/adapt/util/cache/ChunkCache2D.java delete mode 100644 src/main/java/com/volmit/adapt/util/cache/WorldCache2D.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/GBiset.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/GListAdapter.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/KList.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/KMap.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/KSet.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/KeyPair.java delete mode 100644 src/main/java/com/volmit/adapt/util/collection/StateList.java delete mode 100644 src/main/java/com/volmit/adapt/util/command/FCommand.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/B.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/ChunkCache.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/ComplexCache.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Cuboid.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/DUTF.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/DataPalette.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Dimension.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/DimensionFace.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/DoubleArrayUtils.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Heafty.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/HeightMap.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/IOAdapter.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/InvertedBiomeGrid.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/KCache.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/MaterialBlock.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/NibbleArray.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/NibbleDataPalette.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Recycler.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Varint.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/WeightedRandom.java delete mode 100644 src/main/java/com/volmit/adapt/util/data/Writable.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeContext.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeContextHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeNode.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeOrigin.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeParameter.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/DecreeParameterHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/annotations/Decree.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/annotations/Param.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/context/WorldContextHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/exceptions/DecreeParsingException.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/BlockVectorHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/BooleanHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/ByteHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/DoubleHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/FloatHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/IntegerHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/LongHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/OptionalWorldHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/PlayerHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/ShortHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/StringHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/VectorHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/handlers/WorldHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/decree/specialhandlers/NullablePlayerHandler.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer2.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer2IO.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer3.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer4.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer4IO.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer5.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer6.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Consumer8.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Function2.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Function3.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Function4.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NastyFunction.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NastyFuture.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NastyRunnable.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NastySupplier.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NoiseInjector.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NoiseProvider.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/NoiseProvider3.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Supplier2.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Supplier3.java delete mode 100644 src/main/java/com/volmit/adapt/util/function/Supplier3R.java rename velocity/src/main/java/{com/volmit => art/arcane}/adapt/AdaptVelocity.java (98%) rename velocity/src/main/java/{com/volmit => art/arcane}/adapt/RedisHandler.java (89%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/RedisConfig.java (95%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/VelocityConfig.java (80%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/codec/ByteBufferInputStream.java (95%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/codec/Codec.java (97%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/codec/DataMessage.java (93%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/codec/DataRequest.java (92%) rename velocity/src/main/java/{com/volmit/adapt/util => art/arcane/adapt/util/project}/redis/codec/Message.java (89%) diff --git a/build.gradle.kts b/build.gradle.kts index 03fae830a..93b1b58a5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,6 +21,7 @@ import io.github.slimjar.resolver.data.Mirror import org.gradle.api.plugins.JavaPluginExtension import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import xyz.jpenilla.runpaper.task.RunServer import kotlin.system.exitProcess @@ -32,11 +33,12 @@ plugins { alias(libs.plugins.runVelocity) alias(libs.plugins.idea) alias(libs.plugins.slimjar) + alias(libs.plugins.kotlin.jvm) } version = "1.18.0-1.20.2-1.21.11" val apiVersion = "1.20" -val main = "com.volmit.adapt.Adapt" +val main = "art.arcane.adapt.Adapt" // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -134,6 +136,16 @@ allprojects { } } +sourceSets { + main { + java.srcDir("../VolmLib/shared/src/main/java") + } +} + +kotlin.sourceSets.named("main") { + kotlin.srcDir("../VolmLib/shared/src/main/kotlin") +} + dependencies { implementation(project(":velocity")) implementation(slimjarHelper("spigot")) @@ -149,6 +161,7 @@ dependencies { slimApi(libs.amulet) slimApi(libs.chrono) slimApi(libs.spatial) + implementation(libs.kotlin.coroutines) // Dynamically Loaded slimApi(libs.adventure.minimessage) @@ -180,7 +193,7 @@ dependencies { compileOnlyApi(fileTree("libs") { include("*.jar") }) } -val lib = "com.volmit.adapt.util" +val lib = "art.arcane.adapt.util" slimJar { mirrors = listOf(Mirror( uri("https://maven-central.storage-download.googleapis.com/maven2").toURL(), @@ -233,6 +246,13 @@ java { } } +kotlin { + jvmToolchain(21) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } +} + tasks { build { dependsOn(shadowJar) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a71650af..ba6cbe2c9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,8 @@ shadow = "9.0.0-rc3" runTask = "2.3.1" idea = "1.2" slimjar = "2.1.7" +kotlin = "2.2.0" +kotlin-coroutines = "1.10.2" # Cancer fukkit = "23.6.1" @@ -60,6 +62,7 @@ toml4j = { module = "com.moandjiezana.toml:toml4j", version.ref = "toml4j" } fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" } glowingentities = { module = "fr.skytasul:glowingentities", version.ref = "glowingentities" } caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" } +kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" } [plugins] lombok = { id = "io.freefair.lombok", version.ref = "lombok" } @@ -68,3 +71,4 @@ runPaper = { id = "xyz.jpenilla.run-paper", version.ref = "runTask" } runVelocity = { id = "xyz.jpenilla.run-velocity", version.ref = "runTask" } idea = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "idea" } slimjar = { id = "de.crazydev22.slimjar", version.ref = "slimjar" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/src/main/java/com/volmit/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java similarity index 91% rename from src/main/java/com/volmit/adapt/Adapt.java rename to src/main/java/art/arcane/adapt/Adapt.java index 00f4754cc..c4dc629bb 100644 --- a/src/main/java/com/volmit/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -1,131 +1,144 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt; - -import com.jeff_media.customblockdata.CustomBlockData; -import com.volmit.adapt.api.advancement.AdvancementManager; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingManager; -import com.volmit.adapt.api.protection.ProtectorRegistry; -import com.volmit.adapt.api.tick.Ticker; -import com.volmit.adapt.api.value.MaterialValue; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptServer; -import com.volmit.adapt.api.world.PlayerDataPersistenceQueue; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.content.protector.*; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.config.ConfigMigrationManager; -import com.volmit.adapt.util.redis.RedisSync; -import com.volmit.adapt.util.secret.SecretSplash; -import de.crazydev22.platformutils.AudienceProvider; -import de.crazydev22.platformutils.Platform; -import de.crazydev22.platformutils.PlatformUtils; -import de.slikey.effectlib.EffectManager; -import fr.skytasul.glowingentities.GlowingEntities; -import io.github.slimjar.app.builder.SpigotApplicationBuilder; -import lombok.Getter; -import lombok.SneakyThrows; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; - +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt; + +import com.jeff_media.customblockdata.CustomBlockData; +import art.arcane.adapt.api.advancement.AdvancementManager; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingManager; +import art.arcane.adapt.api.protection.ProtectorRegistry; +import art.arcane.adapt.api.tick.Ticker; +import art.arcane.adapt.api.value.MaterialValue; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptServer; +import art.arcane.adapt.api.world.PlayerDataPersistenceQueue; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.content.protector.*; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.io.JarScanner; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.config.ConfigMigrationManager; +import art.arcane.adapt.util.project.redis.RedisSync; +import art.arcane.adapt.util.secret.SecretSplash; +import de.crazydev22.platformutils.AudienceProvider; +import de.crazydev22.platformutils.Platform; +import de.crazydev22.platformutils.PlatformUtils; +import de.slikey.effectlib.EffectManager; +import fr.skytasul.glowingentities.GlowingEntities; +import io.github.slimjar.app.builder.SpigotApplicationBuilder; +import lombok.Getter; +import lombok.SneakyThrows; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + import java.io.*; import java.lang.annotation.Annotation; import java.net.URL; import java.text.MessageFormat; import java.util.*; import java.util.function.Supplier; - -import static com.volmit.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; - -public class Adapt extends VolmitPlugin { - public static Adapt instance; - public static HashMap wordKey = new HashMap<>(); - public final EffectManager adaptEffectManager; - public static Platform platform; - public static AudienceProvider audiences; - private KMap, AdaptService> services; - - @Getter - private GlowingEntities glowingEntities; - @Getter - private Ticker ticker; + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.io.SQLManager; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.plugin.Metrics; +import art.arcane.adapt.util.common.plugin.VolmitPlugin; +import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.adapt.util.common.scheduling.J; + +import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; + +public class Adapt extends VolmitPlugin { + public static Adapt instance; + public static HashMap wordKey = new HashMap<>(); + public final EffectManager adaptEffectManager; + public static Platform platform; + public static AudienceProvider audiences; + private KMap, AdaptService> services; + + @Getter + private GlowingEntities glowingEntities; + @Getter + private Ticker ticker; @Getter private AdaptServer adaptServer; @Getter private SQLManager sqlManager; - @Getter - private ProtectorRegistry protectorRegistry; - @Getter - private Map guiLeftovers = new HashMap<>(); - - @Getter - private AdvancementManager manager; + @Getter + private ProtectorRegistry protectorRegistry; + @Getter + private Map guiLeftovers = new HashMap<>(); + + @Getter + private AdvancementManager manager; @Getter private RedisSync redisSync; @Getter private PlayerDataPersistenceQueue playerDataPersistenceQueue; - - + + private final KList postShutdown = new KList<>(); private static VolmitSender sender; private static final long STARTUP_SLOW_PHASE_MS = 1500L; - - - public Adapt() { - instance = this; - getLogger().info("Loading Libraries..."); - new SpigotApplicationBuilder(this) - .remap(true) - .build(); - getLogger().info("Libraries Loaded!"); - adaptEffectManager = new EffectManager(this); - } - - @SuppressWarnings("unchecked") - public static T service(Class c) { - return (T) instance.services.get(c); - } - - @Override - public void onLoad() { - manager = new AdvancementManager(); - if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { - WorldGuardProtector.registerFlag(); - } - } - + + + public Adapt() { + instance = this; + getLogger().info("Loading Libraries..."); + new SpigotApplicationBuilder(this) + .remap(true) + .build(); + getLogger().info("Libraries Loaded!"); + adaptEffectManager = new EffectManager(this); + } + + @SuppressWarnings("unchecked") + public static T service(Class c) { + return (T) instance.services.get(c); + } + + @Override + public void onLoad() { + manager = new AdvancementManager(); + if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { + WorldGuardProtector.registerFlag(); + } + } + @Override public void start() { runStartupPhaseVoid("backup-legacy-configs", ConfigMigrationManager::backupLegacyJsonConfigsOnce); platform = PlatformUtils.createPlatform(this); audiences = platform.getAudienceProvider(); services = new KMap<>(); - runStartupPhaseVoid("discover-services", () -> initialize("com.volmit.adapt.service") + runStartupPhaseVoid("discover-services", () -> initialize("art.arcane.adapt.service") .forEach((i) -> services.put((Class) i.getClass(), (AdaptService) i))); runStartupPhaseVoid("language-update", Localizer::updateLanguageFile); @@ -138,7 +151,7 @@ public void start() { if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { new PapiExpansion().register(); } - printInformation(); + printInformation(); sqlManager = new SQLManager(); if (AdaptConfig.get().isUseSql()) { runStartupPhase("sql-connect", () -> { @@ -160,34 +173,34 @@ public void start() { registerListener(new BrewingManager()); registerListener(Version.get()); setupMetrics(); - startupPrint(); // Splash screen - if (AdaptConfig.get().isAutoUpdateCheck()) { - autoUpdateCheck(); - } - protectorRegistry = new ProtectorRegistry(); - if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { - protectorRegistry.registerProtector(new WorldGuardProtector()); - } - if (getServer().getPluginManager().getPlugin("Factions") != null) { - protectorRegistry.registerProtector(new FactionsClaimProtector()); - } - if (getServer().getPluginManager().getPlugin("ChestProtect") != null) { - protectorRegistry.registerProtector(new ChestProtectProtector()); - } - if (getServer().getPluginManager().getPlugin("Residence") != null) { - protectorRegistry.registerProtector(new ResidenceProtector()); - } - if (getServer().getPluginManager().getPlugin("GriefDefender") != null) { - protectorRegistry.registerProtector(new GriefDefenderProtector()); - } - if (getServer().getPluginManager().getPlugin("GriefPrevention") != null) { - protectorRegistry.registerProtector(new GriefPreventionProtector()); - } - if (getServer().getPluginManager().getPlugin("LockettePro") != null) { - protectorRegistry.registerProtector(new LocketteProProtector()); - } - glowingEntities = new GlowingEntities(this); - initializeAdaptationListings(); + startupPrint(); // Splash screen + if (AdaptConfig.get().isAutoUpdateCheck()) { + autoUpdateCheck(); + } + protectorRegistry = new ProtectorRegistry(); + if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { + protectorRegistry.registerProtector(new WorldGuardProtector()); + } + if (getServer().getPluginManager().getPlugin("Factions") != null) { + protectorRegistry.registerProtector(new FactionsClaimProtector()); + } + if (getServer().getPluginManager().getPlugin("ChestProtect") != null) { + protectorRegistry.registerProtector(new ChestProtectProtector()); + } + if (getServer().getPluginManager().getPlugin("Residence") != null) { + protectorRegistry.registerProtector(new ResidenceProtector()); + } + if (getServer().getPluginManager().getPlugin("GriefDefender") != null) { + protectorRegistry.registerProtector(new GriefDefenderProtector()); + } + if (getServer().getPluginManager().getPlugin("GriefPrevention") != null) { + protectorRegistry.registerProtector(new GriefPreventionProtector()); + } + if (getServer().getPluginManager().getPlugin("LockettePro") != null) { + protectorRegistry.registerProtector(new LocketteProProtector()); + } + glowingEntities = new GlowingEntities(this); + initializeAdaptationListings(); services.values().forEach(AdaptService::onEnable); services.values().forEach(this::registerListener); } @@ -250,8 +263,8 @@ private void migrateAllSkillAndAdaptationConfigs() { int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); Adapt.info("Canonicalized skill/adaptation configs to TOML (skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson + ")."); } - - + + public void startSim() { long startTicker = System.currentTimeMillis(); ticker = new Ticker(); @@ -270,11 +283,11 @@ public void startSim() { manager.enable(); verbose("start-sim detail: advancement manager enable in " + (System.currentTimeMillis() - startAdv) + "ms"); } - - public void postShutdown(Runnable r) { - postShutdown.add(r); - } - + + public void postShutdown(Runnable r) { + postShutdown.add(r); + } + public void stopSim() { if (ticker != null) { ticker.clear(); @@ -289,9 +302,9 @@ public void stopSim() { MaterialValue.save(); WorldData.stop(); CustomModel.clear(); - } - - + } + + @Override public void stop() { if (services != null) { @@ -324,168 +337,168 @@ public void stop() { services.clear(); } } - - private void startupPrint() { - if (!AdaptConfig.get().isSplashScreen()) { - return; - } - Random r = new Random(); - int game = r.nextInt(100); - if (game < 90) { - Adapt.info("\n" + C.GRAY + " █████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "█████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "████████" + C.DARK_RED + "╗\n" + - C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗╚══" + C.GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + - C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + - C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║ \n" + - C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); - } else { - info(SecretSplash.getSecretSplash().getRandom()); - } - } - - public File getJarFile() { - return getFile(); - } - - @Override - public String getTag(String subTag) { - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.DARK_RED + "Adapt" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; - } - - private void setupMetrics() { - if (AdaptConfig.get().isMetrics()) { - new Metrics(this, 24221); - } - } - - public static VolmitSender getSender() { - if (sender == null) { - sender = new VolmitSender(Bukkit.getConsoleSender()); - sender.setTag(instance.getTag()); - } - return sender; - } - - public static List initialize(String s) { - return initialize(s, null); - } - - public static KList initialize(String s, Class slicedClass) { - JarScanner js = new JarScanner(instance.getFile(), s); - KList v = new KList<>(); - J.attempt(js::scan); - for (Class i : js.getClasses()) { - if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { - try { - Adapt.verbose("Found class: " + i.getName()); - v.add(i.getDeclaredConstructor().newInstance()); - } catch (Throwable e) { - Adapt.verbose("Failed to load class: " + i.getName()); - StringWriter writer = new StringWriter(); - e.printStackTrace(new PrintWriter(writer)); - for (String line : writer.toString().split("\n")) { - verbose(line); - } - } - } - } - - return v; - } - - public static int getJavaVersion() { - String version = System.getProperty("java.version"); - if (version.startsWith("1.")) { - version = version.substring(2, 3); - } else { - int dot = version.indexOf("."); - if (dot != -1) { - version = version.substring(0, dot); - } - } - return Integer.parseInt(version); - } - - public static void printInformation() { - debug("XP Curve: " + AdaptConfig.get().getXpCurve()); - debug("XP/Level base: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); - debug("XP/Level multiplier: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()); - info("Language: " + AdaptConfig.get().getLanguage() + " - Language Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); - } - - @SneakyThrows - public static void autoUpdateCheck() { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts").openStream()))) { - info("Checking for updates..."); - String inputLine; - while ((inputLine = in.readLine()) != null) { - if (inputLine.contains("version '")) { - String version = inputLine.replace("version '", "").replace("'", "").replace("// Needs to be version specific", "").replace(" ", ""); - if (instance.getDescription().getVersion().contains("development")) { - info("Development build detected. Skipping update check."); - return; - } else if (!version.equals(instance.getDescription().getVersion())) { - info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", instance.getDescription().getVersion(), version)); - } else { - info("You are running the latest version of Adapt!"); - } - break; - } - } - } catch (Throwable e) { - error("Failed to check for updates."); - } - } - - public static void actionbar(Player p, String msg) { - new VolmitSender(p).sendAction(msg); - } - - public static void debug(String string) { - if (AdaptConfig.get().isDebug()) { - msg(C.DARK_PURPLE + string); - } - } - - public static void warn(String string) { - msg(C.YELLOW + string); - } - - public static void error(String string) { - msg(C.RED + string); - } - - public static void verbose(String string) { - if (AdaptConfig.get().isVerbose()) { - msg(C.LIGHT_PURPLE + string); - } - } - - public static void success(String string) { - msg(C.GREEN + string); - } - - public static void info(String string) { - msg(C.WHITE + string); - } - - public static void messagePlayer(Player p, String string) { - String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; - p.sendMessage(msg); - } - - public static void msg(String string) { - try { - if (instance == null) { - System.out.println("[Adapt]: " + string); - return; - } - - String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; - Bukkit.getConsoleSender().sendMessage(msg); - } catch (Throwable e) { - System.out.println("[Adapt]: " + string); - } - } - + + private void startupPrint() { + if (!AdaptConfig.get().isSplashScreen()) { + return; + } + Random r = new Random(); + int game = r.nextInt(100); + if (game < 90) { + Adapt.info("\n" + C.GRAY + " █████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "█████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "████████" + C.DARK_RED + "╗\n" + + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗╚══" + C.GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + + C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║ \n" + + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); + } else { + info(SecretSplash.getSecretSplash().getRandom()); + } + } + + public File getJarFile() { + return getFile(); + } + + @Override + public String getTag(String subTag) { + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.DARK_RED + "Adapt" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + } + + private void setupMetrics() { + if (AdaptConfig.get().isMetrics()) { + new Metrics(this, 24221); + } + } + + public static VolmitSender getSender() { + if (sender == null) { + sender = new VolmitSender(Bukkit.getConsoleSender()); + sender.setTag(instance.getTag()); + } + return sender; + } + + public static List initialize(String s) { + return initialize(s, null); + } + + public static KList initialize(String s, Class slicedClass) { + JarScanner js = new JarScanner(instance.getFile(), s); + KList v = new KList<>(); + J.attempt(js::scan); + for (Class i : js.getClasses()) { + if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { + try { + Adapt.verbose("Found class: " + i.getName()); + v.add(i.getDeclaredConstructor().newInstance()); + } catch (Throwable e) { + Adapt.verbose("Failed to load class: " + i.getName()); + StringWriter writer = new StringWriter(); + e.printStackTrace(new PrintWriter(writer)); + for (String line : writer.toString().split("\n")) { + verbose(line); + } + } + } + } + + return v; + } + + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + public static void printInformation() { + debug("XP Curve: " + AdaptConfig.get().getXpCurve()); + debug("XP/Level base: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); + debug("XP/Level multiplier: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()); + info("Language: " + AdaptConfig.get().getLanguage() + " - Language Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); + } + + @SneakyThrows + public static void autoUpdateCheck() { + try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts").openStream()))) { + info("Checking for updates..."); + String inputLine; + while ((inputLine = in.readLine()) != null) { + if (inputLine.contains("version '")) { + String version = inputLine.replace("version '", "").replace("'", "").replace("// Needs to be version specific", "").replace(" ", ""); + if (instance.getDescription().getVersion().contains("development")) { + info("Development build detected. Skipping update check."); + return; + } else if (!version.equals(instance.getDescription().getVersion())) { + info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", instance.getDescription().getVersion(), version)); + } else { + info("You are running the latest version of Adapt!"); + } + break; + } + } + } catch (Throwable e) { + error("Failed to check for updates."); + } + } + + public static void actionbar(Player p, String msg) { + new VolmitSender(p).sendAction(msg); + } + + public static void debug(String string) { + if (AdaptConfig.get().isDebug()) { + msg(C.DARK_PURPLE + string); + } + } + + public static void warn(String string) { + msg(C.YELLOW + string); + } + + public static void error(String string) { + msg(C.RED + string); + } + + public static void verbose(String string) { + if (AdaptConfig.get().isVerbose()) { + msg(C.LIGHT_PURPLE + string); + } + } + + public static void success(String string) { + msg(C.GREEN + string); + } + + public static void info(String string) { + msg(C.WHITE + string); + } + + public static void messagePlayer(Player p, String string) { + String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; + p.sendMessage(msg); + } + + public static void msg(String string) { + try { + if (instance == null) { + System.out.println("[Adapt]: " + string); + return; + } + + String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; + Bukkit.getConsoleSender().sendMessage(msg); + } catch (Throwable e) { + System.out.println("[Adapt]: " + string); + } + } + } diff --git a/src/main/java/com/volmit/adapt/AdaptConfig.java b/src/main/java/art/arcane/adapt/AdaptConfig.java similarity index 98% rename from src/main/java/com/volmit/adapt/AdaptConfig.java rename to src/main/java/art/arcane/adapt/AdaptConfig.java index 87031902d..f632112ae 100644 --- a/src/main/java/com/volmit/adapt/AdaptConfig.java +++ b/src/main/java/art/arcane/adapt/AdaptConfig.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt; +package art.arcane.adapt; -import com.volmit.adapt.api.xp.Curves; -import com.volmit.adapt.util.redis.RedisConfig; -import com.volmit.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.api.xp.Curves; +import art.arcane.adapt.util.project.redis.RedisConfig; +import art.arcane.adapt.util.config.ConfigFileSupport; import lombok.Getter; import lombok.Setter; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/PapiExpansion.java b/src/main/java/art/arcane/adapt/PapiExpansion.java similarity index 96% rename from src/main/java/com/volmit/adapt/PapiExpansion.java rename to src/main/java/art/arcane/adapt/PapiExpansion.java index 3a9a982e0..2afaa208d 100644 --- a/src/main/java/com/volmit/adapt/PapiExpansion.java +++ b/src/main/java/art/arcane/adapt/PapiExpansion.java @@ -1,11 +1,11 @@ -package com.volmit.adapt; +package art.arcane.adapt; import com.google.common.collect.Maps; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.util.common.format.Localizer; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/volmit/adapt/api/Component.java b/src/main/java/art/arcane/adapt/api/Component.java similarity index 98% rename from src/main/java/com/volmit/adapt/api/Component.java rename to src/main/java/art/arcane/adapt/api/Component.java index ff9b2b6d0..f93164737 100644 --- a/src/main/java/com/volmit/adapt/api/Component.java +++ b/src/main/java/art/arcane/adapt/api/Component.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api; +package art.arcane.adapt.api; import com.francobm.magicosmetics.api.CosmeticType; import com.francobm.magicosmetics.api.MagicAPI; import com.google.common.collect.Lists; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.value.MaterialValue; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.SoundPlayer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.value.MaterialValue; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.misc.SoundPlayer; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -54,8 +54,8 @@ import java.util.Set; import java.util.function.Predicate; -import static com.volmit.adapt.util.reflect.registries.Particles.ENCHANTMENT_TABLE; -import static com.volmit.adapt.util.reflect.registries.Particles.REDSTONE; +import static art.arcane.adapt.util.reflect.registries.Particles.ENCHANTMENT_TABLE; +import static art.arcane.adapt.util.reflect.registries.Particles.REDSTONE; import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public interface Component { diff --git a/src/main/java/com/volmit/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/adaptation/Adaptation.java rename to src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 2b16e81b7..142439aa0 100644 --- a/src/main/java/com/volmit/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -16,24 +16,26 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.adaptation; +package art.arcane.adapt.api.adaptation; +import art.arcane.volmlib.util.format.Form; import com.google.common.collect.ImmutableSet; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.Component; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.protection.Protector; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.tick.Ticked; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.event.AdaptAdaptationUseEvent; -import com.volmit.adapt.util.*; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.Component; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.protection.Protector; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.tick.Ticked; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -44,6 +46,20 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.adapt.util.common.inventorygui.UIElement; +import art.arcane.adapt.util.common.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public interface Adaptation extends Ticked, Component { Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); diff --git a/src/main/java/com/volmit/adapt/api/adaptation/SimpleAdaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/adaptation/SimpleAdaptation.java rename to src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java index db83b52af..9b81d1f98 100644 --- a/src/main/java/com/volmit/adapt/api/adaptation/SimpleAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java @@ -16,25 +16,26 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.adaptation; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigFileSupport; +package art.arcane.adapt.api.adaptation; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigFileSupport; import lombok.Data; import lombok.EqualsAndHashCode; import org.bukkit.Material; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.collection.KList; import java.io.File; import java.io.IOException; @@ -43,6 +44,9 @@ import java.util.List; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; + @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleAdaptation extends TickedObject implements Adaptation { diff --git a/src/main/java/com/volmit/adapt/api/adaptation/chunk/ChunkLoading.java b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java similarity index 91% rename from src/main/java/com/volmit/adapt/api/adaptation/chunk/ChunkLoading.java rename to src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java index 22d494d09..6ca1b1cd3 100644 --- a/src/main/java/com/volmit/adapt/api/adaptation/chunk/ChunkLoading.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.adaptation.chunk; +package art.arcane.adapt.api.adaptation.chunk; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.J; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Chunk; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancement.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancement.java rename to src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java index dbabd4486..937d6f297 100644 --- a/src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancement.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.advancement; +package art.arcane.adapt.api.advancement; import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; @@ -25,9 +25,9 @@ import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementDisplay; import com.fren_gor.ultimateAdvancementAPI.database.TeamProgression; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.collection.KList; import lombok.Builder; import lombok.Data; import lombok.Singular; diff --git a/src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancementFrame.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java similarity index 90% rename from src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancementFrame.java rename to src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java index 9425511ff..7ee4d173b 100644 --- a/src/main/java/com/volmit/adapt/api/advancement/AdaptAdvancementFrame.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.api.advancement; +package art.arcane.adapt.api.advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementFrameType; diff --git a/src/main/java/com/volmit/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/advancement/AdvancementManager.java rename to src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index 22a96be9a..1b8d85d44 100644 --- a/src/main/java/com/volmit/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -1,22 +1,22 @@ -package com.volmit.adapt.api.advancement; +package art.arcane.adapt.api.advancement; import com.fren_gor.ultimateAdvancementAPI.AdvancementMain; import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.J; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -import static com.volmit.adapt.Adapt.instance; +import static art.arcane.adapt.Adapt.instance; public class AdvancementManager { private final AdvancementMain main; diff --git a/src/main/java/com/volmit/adapt/api/advancement/AdvancementSpec.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/advancement/AdvancementSpec.java rename to src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java index a981b3fe2..18f1c8165 100644 --- a/src/main/java/com/volmit/adapt/api/advancement/AdvancementSpec.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.advancement; +package art.arcane.adapt.api.advancement; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.CustomModel; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.Builder; import lombok.Data; import lombok.Singular; diff --git a/src/main/java/com/volmit/adapt/api/advancement/AdvancementVisibility.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java similarity index 98% rename from src/main/java/com/volmit/adapt/api/advancement/AdvancementVisibility.java rename to src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java index 72330d431..4f1acde5b 100644 --- a/src/main/java/com/volmit/adapt/api/advancement/AdvancementVisibility.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.api.advancement; +package art.arcane.adapt.api.advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; diff --git a/src/main/java/com/volmit/adapt/api/data/WorldData.java b/src/main/java/art/arcane/adapt/api/data/WorldData.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/data/WorldData.java rename to src/main/java/art/arcane/adapt/api/data/WorldData.java index 2a9b4eb2e..3fc65aaba 100644 --- a/src/main/java/com/volmit/adapt/api/data/WorldData.java +++ b/src/main/java/art/arcane/adapt/api/data/WorldData.java @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.data; +package art.arcane.adapt.api.data; -import art.arcane.spatial.mantle.Mantle; +import art.arcane.adapt.util.mantle.Mantle; import art.arcane.spatial.matter.ClassReader; import art.arcane.spatial.matter.SpatialMatter; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.data.unit.Earnings; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.collection.KMap; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.data.unit.Earnings; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.collection.KMap; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; diff --git a/src/main/java/com/volmit/adapt/api/data/unit/Earnings.java b/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java similarity index 98% rename from src/main/java/com/volmit/adapt/api/data/unit/Earnings.java rename to src/main/java/art/arcane/adapt/api/data/unit/Earnings.java index 2bce44d0b..c5972c596 100644 --- a/src/main/java/com/volmit/adapt/api/data/unit/Earnings.java +++ b/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.data.unit; +package art.arcane.adapt.api.data.unit; import art.arcane.spatial.matter.slices.RawMatter; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/volmit/adapt/api/item/DataItem.java b/src/main/java/art/arcane/adapt/api/item/DataItem.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/item/DataItem.java rename to src/main/java/art/arcane/adapt/api/item/DataItem.java index cff12b05d..99ddbea30 100644 --- a/src/main/java/com/volmit/adapt/api/item/DataItem.java +++ b/src/main/java/art/arcane/adapt/api/item/DataItem.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.item; +package art.arcane.adapt.api.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.BukkitGson; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.io.BukkitGson; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/com/volmit/adapt/api/item/PotionItem.java b/src/main/java/art/arcane/adapt/api/item/PotionItem.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/item/PotionItem.java rename to src/main/java/art/arcane/adapt/api/item/PotionItem.java index e2e890286..ac0c8bc7d 100644 --- a/src/main/java/com/volmit/adapt/api/item/PotionItem.java +++ b/src/main/java/art/arcane/adapt/api/item/PotionItem.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.item; +package art.arcane.adapt.api.item; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffectType; diff --git a/src/main/java/com/volmit/adapt/api/notification/ActionBarNotification.java b/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java similarity index 91% rename from src/main/java/com/volmit/adapt/api/notification/ActionBarNotification.java rename to src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java index 6a3968a29..534e08926 100644 --- a/src/main/java/com/volmit/adapt/api/notification/ActionBarNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; +package art.arcane.adapt.api.notification; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.M; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.volmlib.util.math.M; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/notification/AdvancementNotification.java b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java similarity index 89% rename from src/main/java/com/volmit/adapt/api/notification/AdvancementNotification.java rename to src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java index c64954dc7..77ce74059 100644 --- a/src/main/java/com/volmit/adapt/api/notification/AdvancementNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java @@ -16,13 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; +package art.arcane.adapt.api.notification; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.util.AdvancementUtils; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.util.common.misc.AdvancementUtils; +import art.arcane.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.CustomModel; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.Builder; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/api/notification/Notification.java b/src/main/java/art/arcane/adapt/api/notification/Notification.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/notification/Notification.java rename to src/main/java/art/arcane/adapt/api/notification/Notification.java index 5c941c391..69062cead 100644 --- a/src/main/java/com/volmit/adapt/api/notification/Notification.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notification.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; +package art.arcane.adapt.api.notification; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptPlayer; public interface Notification { String DEFAULT_GROUP = "default"; diff --git a/src/main/java/com/volmit/adapt/api/notification/Notifier.java b/src/main/java/art/arcane/adapt/api/notification/Notifier.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/notification/Notifier.java rename to src/main/java/art/arcane/adapt/api/notification/Notifier.java index 6f9a666f6..4d60e05ba 100644 --- a/src/main/java/com/volmit/adapt/api/notification/Notifier.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notifier.java @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.collection.KMap; +package art.arcane.adapt.api.notification; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KMap; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/src/main/java/com/volmit/adapt/api/notification/SoundNotification.java b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/notification/SoundNotification.java rename to src/main/java/art/arcane/adapt/api/notification/SoundNotification.java index 633d57722..9a861ea62 100644 --- a/src/main/java/com/volmit/adapt/api/notification/SoundNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; +package art.arcane.adapt.api.notification; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.SoundPlayer; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.misc.SoundPlayer; import lombok.Builder; import lombok.Data; import org.bukkit.Sound; diff --git a/src/main/java/com/volmit/adapt/api/notification/TitleNotification.java b/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/notification/TitleNotification.java rename to src/main/java/art/arcane/adapt/api/notification/TitleNotification.java index 8551de2cc..0770bc846 100644 --- a/src/main/java/com/volmit/adapt/api/notification/TitleNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.notification; +package art.arcane.adapt.api.notification; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptPlayer; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/potion/BrewingManager.java b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/potion/BrewingManager.java rename to src/main/java/art/arcane/adapt/api/potion/BrewingManager.java index 3c3fa7d3e..0ea087b4f 100644 --- a/src/main/java/com/volmit/adapt/api/potion/BrewingManager.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java @@ -1,11 +1,11 @@ -package com.volmit.adapt.api.potion; +package art.arcane.adapt.api.potion; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.reflect.Reflect; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.Reflect; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.BrewingStand; diff --git a/src/main/java/com/volmit/adapt/api/potion/BrewingRecipe.java b/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java similarity index 89% rename from src/main/java/com/volmit/adapt/api/potion/BrewingRecipe.java rename to src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java index ce8297ad0..2c320c913 100644 --- a/src/main/java/com/volmit/adapt/api/potion/BrewingRecipe.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.api.potion; +package art.arcane.adapt.api.potion; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/potion/BrewingTask.java b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/potion/BrewingTask.java rename to src/main/java/art/arcane/adapt/api/potion/BrewingTask.java index 66643f906..827bc9576 100644 --- a/src/main/java/com/volmit/adapt/api/potion/BrewingTask.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java @@ -1,7 +1,7 @@ -package com.volmit.adapt.api.potion; +package art.arcane.adapt.api.potion; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.SoundPlayer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.misc.SoundPlayer; import lombok.Getter; import org.bukkit.Location; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/api/potion/PotionBuilder.java b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/potion/PotionBuilder.java rename to src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java index bb8b348de..434d603c5 100644 --- a/src/main/java/com/volmit/adapt/api/potion/PotionBuilder.java +++ b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java @@ -1,8 +1,8 @@ -package com.volmit.adapt.api.potion; +package art.arcane.adapt.api.potion; import com.google.common.collect.Lists; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.AllArgsConstructor; import lombok.Getter; import net.kyori.adventure.text.Component; diff --git a/src/main/java/com/volmit/adapt/api/protection/Protector.java b/src/main/java/art/arcane/adapt/api/protection/Protector.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/protection/Protector.java rename to src/main/java/art/arcane/adapt/api/protection/Protector.java index 87e1166d7..14d2ed3be 100644 --- a/src/main/java/com/volmit/adapt/api/protection/Protector.java +++ b/src/main/java/art/arcane/adapt/api/protection/Protector.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.protection; +package art.arcane.adapt.api.protection; -import com.volmit.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.Adaptation; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/api/protection/ProtectorRegistry.java b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/protection/ProtectorRegistry.java rename to src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java index 5eddab983..095c09d25 100644 --- a/src/main/java/com/volmit/adapt/api/protection/ProtectorRegistry.java +++ b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.protection; +package art.arcane.adapt.api.protection; import com.google.common.collect.ImmutableList; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/api/recipe/AdaptRecipe.java b/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java similarity index 99% rename from src/main/java/com/volmit/adapt/api/recipe/AdaptRecipe.java rename to src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java index e98496099..ae8eec0ba 100644 --- a/src/main/java/com/volmit/adapt/api/recipe/AdaptRecipe.java +++ b/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.recipe; +package art.arcane.adapt.api.recipe; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import lombok.Builder; import lombok.Data; import lombok.Singular; diff --git a/src/main/java/com/volmit/adapt/api/recipe/MaterialChar.java b/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java similarity index 97% rename from src/main/java/com/volmit/adapt/api/recipe/MaterialChar.java rename to src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java index 2645376f6..4d3b14d73 100644 --- a/src/main/java/com/volmit/adapt/api/recipe/MaterialChar.java +++ b/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.recipe; +package art.arcane.adapt.api.recipe; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/runtime/AdaptationGate.java b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/runtime/AdaptationGate.java rename to src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java index c31d828a9..1c2522882 100644 --- a/src/main/java/com/volmit/adapt/api/runtime/AdaptationGate.java +++ b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.runtime; +package art.arcane.adapt.api.runtime; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.skill.Skill; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.skill.Skill; import org.bukkit.GameMode; import org.bukkit.World; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/skill/SimpleSkill.java rename to src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index 9395276f3..9718b82dc 100644 --- a/src/main/java/com/volmit/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -16,22 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.skill; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.runtime.AdaptationGate; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigFileSupport; -import com.volmit.adapt.util.collection.KList; +package art.arcane.adapt.api.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.runtime.AdaptationGate; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.volmlib.util.collection.KList; import lombok.Data; import lombok.EqualsAndHashCode; import org.bukkit.Material; @@ -45,6 +46,9 @@ import java.lang.reflect.Field; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; + @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleSkill extends TickedObject implements Skill { diff --git a/src/main/java/com/volmit/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/skill/Skill.java rename to src/main/java/art/arcane/adapt/api/skill/Skill.java index 372662c29..8f4416436 100644 --- a/src/main/java/com/volmit/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -16,23 +16,25 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.skill; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.Component; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.tick.Ticked; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KList; +package art.arcane.adapt.api.skill; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.Component; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.tick.Ticked; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KList; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -45,6 +47,20 @@ import java.util.List; import java.util.Locale; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.adapt.util.common.inventorygui.UIElement; +import art.arcane.adapt.util.common.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public interface Skill extends Ticked, Component { AdaptAdvancement buildAdvancements(); diff --git a/src/main/java/com/volmit/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/skill/SkillRegistry.java rename to src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index e2f24fc5d..6f5ff1ab3 100644 --- a/src/main/java/com/volmit/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -1,40 +1,40 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.api.skill; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.potion.BrewingManager; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.api.xp.XPMultiplier; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.content.skill.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.registries.Particles; +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.potion.BrewingManager; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.content.skill.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.reflect.registries.Particles; import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.Location; @@ -62,7 +62,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; - + public class SkillRegistry extends TickedObject { private static final long SLOW_SKILL_REG_MS = 300L; private static final int DEFERRED_SKILLS_PER_TICK = 2; @@ -78,97 +78,97 @@ public class SkillRegistry extends TickedObject { public SkillRegistry() { super("registry", UUID.randomUUID() + "-sk", 1250); registerSkill(SkillAgility.class); - registerSkill(SkillArchitect.class); - registerSkill(SkillAxes.class); - registerSkill(SkillBlocking.class); - registerSkill(SkillChronos.class); - registerSkill(SkillCrafting.class); - registerSkill(SkillDiscovery.class); - registerSkill(SkillEnchanting.class); - registerSkill(SkillHerbalism.class); - registerSkill(SkillHunter.class); - registerSkill(SkillPickaxes.class); - registerSkill(SkillRanged.class); - registerSkill(SkillRift.class); - registerSkill(SkillSeaborne.class); - registerSkill(SkillStealth.class); - registerSkill(SkillSwords.class); - registerSkill(SkillTaming.class); - registerSkill(SkillTragOul.class); - registerSkill(SkillUnarmed.class); + registerSkill(SkillArchitect.class); + registerSkill(SkillAxes.class); + registerSkill(SkillBlocking.class); + registerSkill(SkillChronos.class); + registerSkill(SkillCrafting.class); + registerSkill(SkillDiscovery.class); + registerSkill(SkillEnchanting.class); + registerSkill(SkillHerbalism.class); + registerSkill(SkillHunter.class); + registerSkill(SkillPickaxes.class); + registerSkill(SkillRanged.class); + registerSkill(SkillRift.class); + registerSkill(SkillSeaborne.class); + registerSkill(SkillStealth.class); + registerSkill(SkillSwords.class); + registerSkill(SkillTaming.class); + registerSkill(SkillTragOul.class); + registerSkill(SkillUnarmed.class); registerSkill(SkillExcavation.class); registerSkill(SkillBrewing.class); registerSkill(SkillNether.class); bootstrapLoading = false; scheduleDeferredBootstrapRecipeRegistration(); } - - @EventHandler - public void on(PlayerExpChangeEvent e) { - Player p = e.getPlayer(); - if (e.getAmount() > 0) { - getPlayer(p).boostXPToRecents(0.03, 10000); - } - } - - private boolean canInteract(Player player, Location targetLocation) { - return Adapt.instance.getProtectorRegistry().getAllProtectors().stream().allMatch(protector -> protector.canInteract(player, targetLocation, null)); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - - boolean commonConditions = p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null; - boolean isLectern = commonConditions && e.getClickedBlock().getType().equals(Material.LECTERN); - boolean isObserver = commonConditions && e.getClickedBlock().getType().equals(Material.OBSERVER); - boolean isAdaptActivator = !e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN) && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) - && e.getClickedBlock() != null - && canInteract(p, e.getClickedBlock().getLocation()) - && e.getClickedBlock().getType().equals(Material.valueOf(AdaptConfig.get().adaptActivatorBlock)) && (p.getInventory().getItemInMainHand().getType().equals(Material.AIR) - || !p.getInventory().getItemInMainHand().getType().isBlock()) && - (p.getInventory().getItemInOffHand().getType().equals(Material.AIR) || !p.getInventory().getItemInOffHand().getType().isBlock()); - - if (isAdaptActivator) { - SoundPlayer spw = SoundPlayer.of(e.getClickedBlock().getWorld()); - spw.play(e.getClickedBlock().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.5f, 0.72f); - spw.play(e.getClickedBlock().getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 0.35f, 0.755f); - SkillsGui.open(p); - e.setCancelled(true); - p.getWorld().spawnParticle(Particles.CRIT_MAGIC, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 25, 0, 0, 0, 1.1); - p.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 12, 0, 0, 0, 1.1); - } - - if (isLectern) { - ItemStack it = p.getInventory().getItemInMainHand(); - if (it.getItemMeta() != null && !it.getItemMeta().getPersistentDataContainer().getKeys().isEmpty()) { - e.setCancelled(true); - playDebug(p); - it.getItemMeta().getPersistentDataContainer().getKeys().forEach(k -> Bukkit.getServer().getConsoleSender().sendMessage(k + " = " + it.getItemMeta().getPersistentDataContainer().getOrDefault(k, PersistentDataType.STRING, "Not a String"))); - } - } - - if (isObserver) { - ItemStack it = p.getInventory().getItemInMainHand(); - if (it.getType().equals(Material.EXPERIENCE_BOTTLE)) { - e.setCancelled(true); - Bukkit.getServer().getConsoleSender().sendMessage(" "); - p.setCooldown(Material.ENCHANTED_BOOK, 3); - AdaptPlayer a = getPlayer(p); - playDebug(p); - - String xv = a.getData().getMultiplier() - 1d > 0 ? "+" + Form.pc(a.getData().getMultiplier() - 1D) : Form.pc(a.getData().getMultiplier() - 1D); - Bukkit.getServer().getConsoleSender().sendMessage("Global" + C.GRAY + ": " + C.GREEN + xv); - - for (XPMultiplier i : a.getData().getMultipliers()) { - String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); - } - for (XPMultiplier i : Adapt.instance.getAdaptServer().getData().getMultipliers()) { - String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); - } - + + @EventHandler + public void on(PlayerExpChangeEvent e) { + Player p = e.getPlayer(); + if (e.getAmount() > 0) { + getPlayer(p).boostXPToRecents(0.03, 10000); + } + } + + private boolean canInteract(Player player, Location targetLocation) { + return Adapt.instance.getProtectorRegistry().getAllProtectors().stream().allMatch(protector -> protector.canInteract(player, targetLocation, null)); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + + boolean commonConditions = p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null; + boolean isLectern = commonConditions && e.getClickedBlock().getType().equals(Material.LECTERN); + boolean isObserver = commonConditions && e.getClickedBlock().getType().equals(Material.OBSERVER); + boolean isAdaptActivator = !e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN) && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) + && e.getClickedBlock() != null + && canInteract(p, e.getClickedBlock().getLocation()) + && e.getClickedBlock().getType().equals(Material.valueOf(AdaptConfig.get().adaptActivatorBlock)) && (p.getInventory().getItemInMainHand().getType().equals(Material.AIR) + || !p.getInventory().getItemInMainHand().getType().isBlock()) && + (p.getInventory().getItemInOffHand().getType().equals(Material.AIR) || !p.getInventory().getItemInOffHand().getType().isBlock()); + + if (isAdaptActivator) { + SoundPlayer spw = SoundPlayer.of(e.getClickedBlock().getWorld()); + spw.play(e.getClickedBlock().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.5f, 0.72f); + spw.play(e.getClickedBlock().getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 0.35f, 0.755f); + SkillsGui.open(p); + e.setCancelled(true); + p.getWorld().spawnParticle(Particles.CRIT_MAGIC, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 25, 0, 0, 0, 1.1); + p.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 12, 0, 0, 0, 1.1); + } + + if (isLectern) { + ItemStack it = p.getInventory().getItemInMainHand(); + if (it.getItemMeta() != null && !it.getItemMeta().getPersistentDataContainer().getKeys().isEmpty()) { + e.setCancelled(true); + playDebug(p); + it.getItemMeta().getPersistentDataContainer().getKeys().forEach(k -> Bukkit.getServer().getConsoleSender().sendMessage(k + " = " + it.getItemMeta().getPersistentDataContainer().getOrDefault(k, PersistentDataType.STRING, "Not a String"))); + } + } + + if (isObserver) { + ItemStack it = p.getInventory().getItemInMainHand(); + if (it.getType().equals(Material.EXPERIENCE_BOTTLE)) { + e.setCancelled(true); + Bukkit.getServer().getConsoleSender().sendMessage(" "); + p.setCooldown(Material.ENCHANTED_BOOK, 3); + AdaptPlayer a = getPlayer(p); + playDebug(p); + + String xv = a.getData().getMultiplier() - 1d > 0 ? "+" + Form.pc(a.getData().getMultiplier() - 1D) : Form.pc(a.getData().getMultiplier() - 1D); + Bukkit.getServer().getConsoleSender().sendMessage("Global" + C.GRAY + ": " + C.GREEN + xv); + + for (XPMultiplier i : a.getData().getMultipliers()) { + String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); + } + for (XPMultiplier i : Adapt.instance.getAdaptServer().getData().getMultipliers()) { + String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); + } + for (PlayerSkillLine i : a.getData().getSkillLines().v()) { Skill s = i.getRawSkill(a); if (s == null) { @@ -179,21 +179,21 @@ && canInteract(p, e.getClickedBlock().getLocation()) for (XPMultiplier j : i.getMultipliers()) { String vv = j.getMultiplier() > 0 ? "+" + Form.pc(j.getMultiplier()) : Form.pc(j.getMultiplier()); Bukkit.getServer().getConsoleSender().sendMessage(" " + s.getShortName() + C.GRAY + " " + vv + " for " + Form.duration(j.getGoodFor() - M.ms(), 0)); - } - } - } - } - } - - private void playDebug(Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_BELL_RESONATE, 1f, 0.6f); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 0.1f); - sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.6f); - sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.2f); - - } - + } + } + } + } + } + + private void playDebug(Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_BELL_RESONATE, 1f, 0.6f); + sp.play(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 0.1f); + sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.6f); + sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.2f); + + } + public Skill getSkill(String i) { if (i == null) { return null; @@ -382,7 +382,7 @@ private Skill instantiateSkill(Class> skillType) { return null; } } - + private void unregisterRecipes(Skill s) { s.getRecipes().forEach(AdaptRecipe::unregister); s.getAdaptations().forEach(adaptation -> { @@ -409,7 +409,7 @@ private void registerRecipes(Skill s) { adaptation.getBrewingRecipes().forEach(r -> BrewingManager.registerRecipe(adaptation.getName(), r)); }); } - + @Override public void unregister() { BukkitTask pendingTask = deferredBootstrapRecipeTask; @@ -427,8 +427,8 @@ public void unregister() { skillTypes.clear(); adaptationRecipeIndex.clear(); } - - @Override + + @Override public void onTick() { } diff --git a/src/main/java/com/volmit/adapt/api/tick/Ticked.java b/src/main/java/art/arcane/adapt/api/tick/Ticked.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/tick/Ticked.java rename to src/main/java/art/arcane/adapt/api/tick/Ticked.java index 6bf539a16..518f4a23e 100644 --- a/src/main/java/com/volmit/adapt/api/tick/Ticked.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticked.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.tick; +package art.arcane.adapt.api.tick; -import com.volmit.adapt.api.world.AdaptComponent; -import com.volmit.adapt.util.M; +import art.arcane.adapt.api.world.AdaptComponent; +import art.arcane.volmlib.util.math.M; public interface Ticked extends AdaptComponent { default void retick() { diff --git a/src/main/java/com/volmit/adapt/api/tick/TickedObject.java b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java similarity index 97% rename from src/main/java/com/volmit/adapt/api/tick/TickedObject.java rename to src/main/java/art/arcane/adapt/api/tick/TickedObject.java index e8102a848..8540c3403 100644 --- a/src/main/java/com/volmit/adapt/api/tick/TickedObject.java +++ b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.tick; +package art.arcane.adapt.api.tick; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.M; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.math.M; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; diff --git a/src/main/java/com/volmit/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java similarity index 97% rename from src/main/java/com/volmit/adapt/api/tick/Ticker.java rename to src/main/java/art/arcane/adapt/api/tick/Ticker.java index 31e91972f..ee508d098 100644 --- a/src/main/java/com/volmit/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.tick; +package art.arcane.adapt.api.tick; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.collection.KList; import java.util.Comparator; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/api/value/MaterialCount.java b/src/main/java/art/arcane/adapt/api/value/MaterialCount.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/value/MaterialCount.java rename to src/main/java/art/arcane/adapt/api/value/MaterialCount.java index 238de8fe8..aac3d4216 100644 --- a/src/main/java/com/volmit/adapt/api/value/MaterialCount.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialCount.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.value; +package art.arcane.adapt.api.value; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/value/MaterialRecipe.java b/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java similarity index 97% rename from src/main/java/com/volmit/adapt/api/value/MaterialRecipe.java rename to src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java index e50e1c052..5673a04f0 100644 --- a/src/main/java/com/volmit/adapt/api/value/MaterialRecipe.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.value; +package art.arcane.adapt.api.value; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/com/volmit/adapt/api/value/MaterialValue.java b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/value/MaterialValue.java rename to src/main/java/art/arcane/adapt/api/value/MaterialValue.java index 7fddbfa35..7e1c2911b 100644 --- a/src/main/java/com/volmit/adapt/api/value/MaterialValue.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java @@ -16,12 +16,15 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.value; +package art.arcane.adapt.api.value; +import art.arcane.volmlib.util.format.Form; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.util.*; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -29,6 +32,8 @@ import java.io.File; import java.io.IOException; +import art.arcane.adapt.util.common.io.Json; + import java.util.*; @Getter diff --git a/src/main/java/com/volmit/adapt/api/version/IAttribute.java b/src/main/java/art/arcane/adapt/api/version/IAttribute.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/version/IAttribute.java rename to src/main/java/art/arcane/adapt/api/version/IAttribute.java index 669f42d51..25e378f6b 100644 --- a/src/main/java/com/volmit/adapt/api/version/IAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/IAttribute.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.api.version; +package art.arcane.adapt.api.version; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; import lombok.*; import org.bukkit.NamespacedKey; import org.bukkit.attribute.AttributeModifier; diff --git a/src/main/java/com/volmit/adapt/api/version/IBindings.java b/src/main/java/art/arcane/adapt/api/version/IBindings.java similarity index 91% rename from src/main/java/com/volmit/adapt/api/version/IBindings.java rename to src/main/java/art/arcane/adapt/api/version/IBindings.java index 6fbb11e27..a0b6a2f58 100644 --- a/src/main/java/com/volmit/adapt/api/version/IBindings.java +++ b/src/main/java/art/arcane/adapt/api/version/IBindings.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.api.version; +package art.arcane.adapt.api.version; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.potion.PotionBuilder.Type; -import com.volmit.adapt.util.CustomModel; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.potion.PotionBuilder.Type; +import art.arcane.adapt.util.common.misc.CustomModel; import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attribute; import org.bukkit.entity.EntityType; diff --git a/src/main/java/com/volmit/adapt/api/version/RuntimeAttribute.java b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java similarity index 98% rename from src/main/java/com/volmit/adapt/api/version/RuntimeAttribute.java rename to src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java index fd340633c..b4f6f430a 100644 --- a/src/main/java/com/volmit/adapt/api/version/RuntimeAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.api.version; +package art.arcane.adapt.api.version; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; import org.bukkit.NamespacedKey; import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; diff --git a/src/main/java/com/volmit/adapt/api/version/RuntimeBindings.java b/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/version/RuntimeBindings.java rename to src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java index 7f1ea6cd4..da10c0ae8 100644 --- a/src/main/java/com/volmit/adapt/api/version/RuntimeBindings.java +++ b/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java @@ -1,7 +1,7 @@ -package com.volmit.adapt.api.version; +package art.arcane.adapt.api.version; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.util.CustomModel; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.util.common.misc.CustomModel; import org.bukkit.NamespacedKey; import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attribute; diff --git a/src/main/java/com/volmit/adapt/api/version/Version.java b/src/main/java/art/arcane/adapt/api/version/Version.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/version/Version.java rename to src/main/java/art/arcane/adapt/api/version/Version.java index e728ddff2..2c616eb88 100644 --- a/src/main/java/com/volmit/adapt/api/version/Version.java +++ b/src/main/java/art/arcane/adapt/api/version/Version.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.api.version; +package art.arcane.adapt.api.version; import org.bukkit.inventory.InventoryView; diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptComponent.java b/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java similarity index 98% rename from src/main/java/com/volmit/adapt/api/world/AdaptComponent.java rename to src/main/java/art/arcane/adapt/api/world/AdaptComponent.java index ecfcb8ab3..78a088109 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptComponent.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.reflect.registries.Materials; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.reflect.registries.Materials; import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java similarity index 94% rename from src/main/java/com/volmit/adapt/api/world/AdaptPlayer.java rename to src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index a34adb80d..41004f7ff 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -16,15 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.notification.AdvancementNotification; -import com.volmit.adapt.api.notification.Notifier; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.util.*; +package art.arcane.adapt.api.world; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.notification.AdvancementNotification; +import art.arcane.adapt.api.notification.Notifier; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.math.RollingSequence; +import art.arcane.volmlib.util.scheduling.ChronoLatch; import lombok.Data; import lombok.EqualsAndHashCode; import org.bukkit.Location; @@ -36,6 +40,11 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.scheduling.J; + @EqualsAndHashCode(callSuper = false) @Data public class AdaptPlayer extends TickedObject { diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptPlayerTracker.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayerTracker.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/world/AdaptPlayerTracker.java rename to src/main/java/art/arcane/adapt/api/world/AdaptPlayerTracker.java index db4f95d3e..d36dda705 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptPlayerTracker.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayerTracker.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; public class AdaptPlayerTracker { diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptServer.java b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/world/AdaptServer.java rename to src/main/java/art/arcane/adapt/api/world/AdaptServer.java index fbf2b183f..09f925d14 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptServer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java @@ -16,25 +16,26 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.notification.AdvancementNotification; -import com.volmit.adapt.api.notification.SoundNotification; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.skill.SkillRegistry; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.api.xp.SpatialXP; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.api.xp.XPMultiplier; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.content.item.ExperienceOrb; -import com.volmit.adapt.content.item.KnowledgeOrb; -import com.volmit.adapt.util.*; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.notification.AdvancementNotification; +import art.arcane.adapt.api.notification.SoundNotification; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SkillRegistry; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.api.xp.SpatialXP; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.content.item.ExperienceOrb; +import art.arcane.adapt.content.item.KnowledgeOrb; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; @@ -57,6 +58,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class AdaptServer extends TickedObject { private final ReentrantLock clearLock = new ReentrantLock(); private final Map players = new ConcurrentHashMap<>(); diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptServerData.java b/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java similarity index 89% rename from src/main/java/com/volmit/adapt/api/world/AdaptServerData.java rename to src/main/java/art/arcane/adapt/api/world/AdaptServerData.java index f18e62893..4ea6da3c1 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptServerData.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.api.xp.XPMultiplier; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.volmlib.util.collection.KList; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/volmit/adapt/api/world/AdaptStatTracker.java b/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/world/AdaptStatTracker.java rename to src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java index 61bf89f3b..704875a27 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdaptStatTracker.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/api/world/AdvancementHandler.java b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/world/AdvancementHandler.java rename to src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java index 3d5427ce1..891ba1687 100644 --- a/src/main/java/com/volmit/adapt/api/world/AdvancementHandler.java +++ b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.AdaptConfig; +import art.arcane.adapt.AdaptConfig; import lombok.Data; -import static com.volmit.adapt.Adapt.instance; +import static art.arcane.adapt.Adapt.instance; @Data public class AdvancementHandler { diff --git a/src/main/java/com/volmit/adapt/api/world/Discovery.java b/src/main/java/art/arcane/adapt/api/world/Discovery.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/world/Discovery.java rename to src/main/java/art/arcane/adapt/api/world/Discovery.java index c0bc95988..c6e4cebf9 100644 --- a/src/main/java/com/volmit/adapt/api/world/Discovery.java +++ b/src/main/java/art/arcane/adapt/api/world/Discovery.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; import lombok.Getter; public class Discovery { diff --git a/src/main/java/com/volmit/adapt/api/world/PlayerAdaptation.java b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/world/PlayerAdaptation.java rename to src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java index 5431576ac..eaee66943 100644 --- a/src/main/java/com/volmit/adapt/api/world/PlayerAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.util.collection.KMap; +import art.arcane.volmlib.util.collection.KMap; import lombok.Data; import java.util.HashMap; diff --git a/src/main/java/com/volmit/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java similarity index 94% rename from src/main/java/com/volmit/adapt/api/world/PlayerData.java rename to src/main/java/art/arcane/adapt/api/world/PlayerData.java index 3115d57d0..568d0ca5e 100644 --- a/src/main/java/com/volmit/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.notification.ActionBarNotification; -import com.volmit.adapt.api.notification.SoundNotification; -import com.volmit.adapt.api.notification.TitleNotification; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.api.xp.XPMultiplier; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Json; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.collection.KSet; +package art.arcane.adapt.api.world; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.notification.ActionBarNotification; +import art.arcane.adapt.api.notification.SoundNotification; +import art.arcane.adapt.api.notification.TitleNotification; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.collection.KSet; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/api/world/PlayerDataPersistenceQueue.java b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/world/PlayerDataPersistenceQueue.java rename to src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java index bb274e139..705b9902e 100644 --- a/src/main/java/com/volmit/adapt/api/world/PlayerDataPersistenceQueue.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java @@ -1,8 +1,8 @@ -package com.volmit.adapt.api.world; +package art.arcane.adapt.api.world; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.util.IO; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.volmlib.util.io.IO; import java.io.File; import java.util.UUID; diff --git a/src/main/java/com/volmit/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/world/PlayerSkillLine.java rename to src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index 010e6d047..5a802d855 100644 --- a/src/main/java/com/volmit/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.world; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.notification.ActionBarNotification; -import com.volmit.adapt.api.notification.Notifier; -import com.volmit.adapt.api.notification.SoundNotification; -import com.volmit.adapt.api.notification.TitleNotification; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.api.xp.XPMultiplier; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; +package art.arcane.adapt.api.world; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.notification.ActionBarNotification; +import art.arcane.adapt.api.notification.Notifier; +import art.arcane.adapt.api.notification.SoundNotification; +import art.arcane.adapt.api.notification.TitleNotification; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Sound; diff --git a/src/main/java/com/volmit/adapt/api/xp/Curves.java b/src/main/java/art/arcane/adapt/api/xp/Curves.java similarity index 99% rename from src/main/java/com/volmit/adapt/api/xp/Curves.java rename to src/main/java/art/arcane/adapt/api/xp/Curves.java index 7a3592b43..4ec039036 100644 --- a/src/main/java/com/volmit/adapt/api/xp/Curves.java +++ b/src/main/java/art/arcane/adapt/api/xp/Curves.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; +package art.arcane.adapt.api.xp; import lombok.Getter; diff --git a/src/main/java/com/volmit/adapt/api/xp/NewtonCurve.java b/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java similarity index 96% rename from src/main/java/com/volmit/adapt/api/xp/NewtonCurve.java rename to src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java index b3ef54a0c..ef18481bf 100644 --- a/src/main/java/com/volmit/adapt/api/xp/NewtonCurve.java +++ b/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; +package art.arcane.adapt.api.xp; -import com.volmit.adapt.AdaptConfig; +import art.arcane.adapt.AdaptConfig; @FunctionalInterface public interface NewtonCurve { diff --git a/src/main/java/com/volmit/adapt/api/xp/ResolvedNewtonCurve.java b/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java similarity index 97% rename from src/main/java/com/volmit/adapt/api/xp/ResolvedNewtonCurve.java rename to src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java index af3f673d4..642a60728 100644 --- a/src/main/java/com/volmit/adapt/api/xp/ResolvedNewtonCurve.java +++ b/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; +package art.arcane.adapt.api.xp; public interface ResolvedNewtonCurve extends NewtonCurve { double getLevelForXP(double xp); diff --git a/src/main/java/com/volmit/adapt/api/xp/SpatialXP.java b/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java similarity index 92% rename from src/main/java/com/volmit/adapt/api/xp/SpatialXP.java rename to src/main/java/art/arcane/adapt/api/xp/SpatialXP.java index 514d72829..f958a7354 100644 --- a/src/main/java/com/volmit/adapt/api/xp/SpatialXP.java +++ b/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; +package art.arcane.adapt.api.xp; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.util.M; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.volmlib.util.math.M; import lombok.Data; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/api/xp/XP.java b/src/main/java/art/arcane/adapt/api/xp/XP.java similarity index 93% rename from src/main/java/com/volmit/adapt/api/xp/XP.java rename to src/main/java/art/arcane/adapt/api/xp/XP.java index 58a372c97..cce0fc537 100644 --- a/src/main/java/com/volmit/adapt/api/xp/XP.java +++ b/src/main/java/art/arcane/adapt/api/xp/XP.java @@ -16,14 +16,14 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.M; +package art.arcane.adapt.api.xp; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.volmlib.util.math.M; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/api/xp/XPMultiplier.java b/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java similarity index 95% rename from src/main/java/com/volmit/adapt/api/xp/XPMultiplier.java rename to src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java index d14d6fa8e..8b28f4fd6 100644 --- a/src/main/java/com/volmit/adapt/api/xp/XPMultiplier.java +++ b/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.api.xp; +package art.arcane.adapt.api.xp; -import com.volmit.adapt.util.M; +import art.arcane.volmlib.util.math.M; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/volmit/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java similarity index 91% rename from src/main/java/com/volmit/adapt/command/CommandAdapt.java rename to src/main/java/art/arcane/adapt/command/CommandAdapt.java index fed993e02..beb74569c 100644 --- a/src/main/java/com/volmit/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -1,30 +1,33 @@ -package com.volmit.adapt.command; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.skill.SkillRegistry; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptServer; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.content.gui.ConfigGui; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.content.item.ExperienceOrb; -import com.volmit.adapt.content.item.KnowledgeOrb; -import com.volmit.adapt.util.command.FConst; -import com.volmit.adapt.util.config.ConfigMigrationManager; -import com.volmit.adapt.util.decree.DecreeExecutor; -import com.volmit.adapt.util.decree.DecreeOrigin; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; -import com.volmit.adapt.util.decree.specialhandlers.NullablePlayerHandler; +package art.arcane.adapt.command; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SkillRegistry; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptServer; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.content.gui.ConfigGui; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.content.item.ExperienceOrb; +import art.arcane.adapt.content.item.KnowledgeOrb; +import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.config.ConfigMigrationManager; +import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; import java.util.HashMap; import java.util.Map; +import art.arcane.adapt.util.common.plugin.Permission; +import art.arcane.adapt.util.project.command.Command; + @Decree(name = "adapt", description = "Basic Command") public class CommandAdapt implements DecreeExecutor { private CommandDebug debug; diff --git a/src/main/java/com/volmit/adapt/command/CommandClear.java b/src/main/java/art/arcane/adapt/command/CommandClear.java similarity index 89% rename from src/main/java/com/volmit/adapt/command/CommandClear.java rename to src/main/java/art/arcane/adapt/command/CommandClear.java index 638b7258e..c10384c47 100644 --- a/src/main/java/com/volmit/adapt/command/CommandClear.java +++ b/src/main/java/art/arcane/adapt/command/CommandClear.java @@ -1,15 +1,17 @@ -package com.volmit.adapt.command; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.util.command.FConst; -import com.volmit.adapt.util.decree.DecreeExecutor; -import com.volmit.adapt.util.decree.DecreeOrigin; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; -import com.volmit.adapt.util.decree.specialhandlers.NullablePlayerHandler; +package art.arcane.adapt.command; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; +import art.arcane.adapt.util.common.plugin.Permission; + @Decree(name = "clear", origin = DecreeOrigin.BOTH, description = "Clear player progression data") public class CommandClear implements DecreeExecutor { diff --git a/src/main/java/com/volmit/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java similarity index 89% rename from src/main/java/com/volmit/adapt/command/CommandDebug.java rename to src/main/java/art/arcane/adapt/command/CommandDebug.java index f570f186f..012f7987f 100644 --- a/src/main/java/com/volmit/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -1,19 +1,22 @@ -package com.volmit.adapt.command; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.command.FConst; -import com.volmit.adapt.util.decree.DecreeExecutor; -import com.volmit.adapt.util.decree.DecreeOrigin; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; +package art.arcane.adapt.command; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.annotations.Param; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Player; import java.util.List; +import art.arcane.adapt.util.common.plugin.Permission; +import art.arcane.adapt.util.project.command.Command; + @Decree(name = "debug", origin = DecreeOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) public class CommandDebug implements DecreeExecutor { diff --git a/src/main/java/com/volmit/adapt/command/CommandDefault.java b/src/main/java/art/arcane/adapt/command/CommandDefault.java similarity index 91% rename from src/main/java/com/volmit/adapt/command/CommandDefault.java rename to src/main/java/art/arcane/adapt/command/CommandDefault.java index 72b70cf48..0bd3c4d09 100644 --- a/src/main/java/com/volmit/adapt/command/CommandDefault.java +++ b/src/main/java/art/arcane/adapt/command/CommandDefault.java @@ -1,17 +1,17 @@ -package com.volmit.adapt.command; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.util.command.FConst; -import com.volmit.adapt.util.decree.DecreeExecutor; -import com.volmit.adapt.util.decree.DecreeOrigin; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; +package art.arcane.adapt.command; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/volmit/adapt/command/CommandReset.java b/src/main/java/art/arcane/adapt/command/CommandReset.java similarity index 86% rename from src/main/java/com/volmit/adapt/command/CommandReset.java rename to src/main/java/art/arcane/adapt/command/CommandReset.java index c209611e2..f3efec445 100644 --- a/src/main/java/com/volmit/adapt/command/CommandReset.java +++ b/src/main/java/art/arcane/adapt/command/CommandReset.java @@ -1,13 +1,13 @@ -package com.volmit.adapt.command; +package art.arcane.adapt.command; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.util.command.FConst; -import com.volmit.adapt.util.decree.DecreeExecutor; -import com.volmit.adapt.util.decree.DecreeOrigin; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; -import com.volmit.adapt.util.decree.specialhandlers.NullablePlayerHandler; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; import java.util.HashMap; diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityArmorUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityArmorUp.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java index 0282b54b7..d96e4877d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityArmorUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Attributes; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -42,6 +44,11 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.reflect.registries.Particles; + public class AgilityArmorUp extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-armor-up".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:armor-up"); @@ -101,7 +108,7 @@ private double getWindupArmor(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); if (attribute == null) continue; @@ -167,25 +174,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain more armor the longer you sprint.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Armor Up adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Armor Up adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupTicksSlowest = 180; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupTicksFastest = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Base for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Base for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupArmorBase = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Level Multiplier for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Level Multiplier for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupArmorLevelMultiplier = 0.525; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityLadderSlide.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityLadderSlide.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java index e7f5900f7..fd7e375d8 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityLadderSlide.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -230,37 +230,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Climb and slide ladders much faster in both directions.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Multiplier applied to baseUpwardLadderSpeed to compute the target climb speed.", impact = "Higher values increase final ladder climb speed after the ramp-up phase.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to baseUpwardLadderSpeed to compute the target climb speed.", impact = "Higher values increase final ladder climb speed after the ramp-up phase.") double speedMultiplier = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Velocity difference threshold used to skip tiny Y-velocity adjustments.", impact = "Lower values apply more frequent micro-updates; higher values reduce minor velocity writes.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Velocity difference threshold used to skip tiny Y-velocity adjustments.", impact = "Lower values apply more frequent micro-updates; higher values reduce minor velocity writes.") double velocityEpsilon = 0.003; - @com.volmit.adapt.util.config.ConfigDoc(value = "Baseline climb speed used before the speed multiplier is applied.", impact = "Higher values raise the base climb profile and increase total ladder ascent speed.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Baseline climb speed used before the speed multiplier is applied.", impact = "Higher values raise the base climb profile and increase total ladder ascent speed.") double baseUpwardLadderSpeed = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vanilla-like upward speed used near ladder endpoints to avoid overshooting.", impact = "Higher values make endpoint climbing snappier; lower values keep transitions conservative.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vanilla-like upward speed used near ladder endpoints to avoid overshooting.", impact = "Higher values make endpoint climbing snappier; lower values keep transitions conservative.") double normalUpwardLadderSpeed = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum positive Y movement treated as intentional upward ladder motion.", impact = "Lower values are more sensitive to slight upward input; higher values require clearer upward movement.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum positive Y movement treated as intentional upward ladder motion.", impact = "Lower values are more sensitive to slight upward input; higher values require clearer upward movement.") double movementDirectionEpsilonUpward = 0.0004; - @com.volmit.adapt.util.config.ConfigDoc(value = "Smoothing factor for blending current upward velocity toward target ladder speed.", impact = "Values near 1.0 ramp quickly; lower values create a slower curve-like acceleration profile.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Smoothing factor for blending current upward velocity toward target ladder speed.", impact = "Values near 1.0 ramp quickly; lower values create a slower curve-like acceleration profile.") double upwardAccelerationSmoothing = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "How long to retain previous upward speed state between ladder movement samples.", impact = "Lower values reset ramp-up sooner; higher values preserve momentum between short interruptions.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How long to retain previous upward speed state between ladder movement samples.", impact = "Lower values reset ramp-up sooner; higher values preserve momentum between short interruptions.") long upwardStateResetMs = 200; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum upward look angle required to activate upward ladder acceleration.", impact = "Larger values require players to look farther upward before acceleration engages.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward look angle required to activate upward ladder acceleration.", impact = "Larger values require players to look farther upward before acceleration engages.") double lookUpPitchThreshold = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Distance from ladder top where motion reverts toward normal upward speed.", impact = "Higher values begin fallback earlier near ladder ends; lower values keep boosted speed longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Distance from ladder top where motion reverts toward normal upward speed.", impact = "Higher values begin fallback earlier near ladder ends; lower values keep boosted speed longer.") int revertDistanceBlocks = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum blocks scanned to detect ladder continuity when checking ladder endpoints.", impact = "Higher values support taller ladders at slightly higher per-check cost.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum blocks scanned to detect ladder continuity when checking ladder endpoints.", impact = "Higher values support taller ladders at slightly higher per-check cost.") int maxLadderScanDistance = 64; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityParkourMomentum.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityParkourMomentum.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java index 8911d4d30..156845eb9 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityParkourMomentum.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -124,7 +124,7 @@ public void on(PlayerMoveEvent e) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); UUID id = p.getUniqueId(); if (!hasAdaptation(p)) { @@ -292,55 +292,55 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Build momentum by chaining sprint-jumps and landings to gain speed and jump boosts.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Momentum Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double momentumBase = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Momentum Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double momentumFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedAmplifierFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpAmplifierFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Landing Gain for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Landing Gain for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int landingGain = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Failed Landing Penalty for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Failed Landing Penalty for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int failedLandingPenalty = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ground Decay On Move for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ground Decay On Move for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int groundDecayOnMove = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Passive Ground Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Ground Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int passiveGroundDecayPerTick = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Off Ledge Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Off Ledge Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int offLedgeDecayPerTick = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Move Squared for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Move Squared for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumMoveSquared = 0.0025; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for momentum velocity scaling.", impact = "Higher values increase movement speed when momentum speed is active.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for momentum velocity scaling.", impact = "Higher values increase movement speed when momentum speed is active.") double baseHorizontalSpeed = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent excessive momentum carry.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent excessive momentum carry.") double maxHorizontalSpeed = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the momentum target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the momentum target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double accelPerTick = 0.04; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values stop faster and reduce carry momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values stop faster and reduce carry momentum.") double brakePerTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") double stopThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, speed velocity is force-cleared when entering invalid states.", impact = "Prevents retained boosts if state transitions skip expected checks.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, speed velocity is force-cleared when entering invalid states.", impact = "Prevents retained boosts if state transitions skip expected checks.") boolean hardStopOnInvalidState = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityRollLanding.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityRollLanding.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java index 2fe48fc4e..08420998f 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityRollLanding.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -46,6 +46,8 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.inventorygui.Window; + public class AgilityRollLanding extends SimpleAdaptation { private final Map rollInputs = new HashMap<>(); private final Map proneUntilMillis = new HashMap<>(); @@ -246,43 +248,43 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Timed sneak before landing converts part of fall damage into hunger cost.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.62; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reductionBase = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reductionFactor = 0.43; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxReduction = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double inputWindowMillisBase = 190; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double inputWindowMillisFactor = 260; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerPerDamageBase = 1.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerPerDamageReduction = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Velocity For Roll Input for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Velocity For Roll Input for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxVerticalVelocityForRollInput = -0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double proneTicksBase = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double proneTicksFactor = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage Prevented for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage Prevented for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDamagePrevented = 4.2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilitySuperJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilitySuperJump.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java index 252ac687b..1531c94f8 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilitySuperJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java @@ -16,17 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,6 +45,11 @@ import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class AgilitySuperJump extends SimpleAdaptation { private final Map lastJump; @@ -170,23 +177,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak and jump for exceptional height advantage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Super Jump adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Super Jump adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Jump Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Jump Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseJumpMultiplier = 0.23; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Level Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Level Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpLevelMultiplier = 0.23; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWallJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWallJump.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java index b3ed617ee..e1547a16a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWallJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java @@ -16,17 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -42,6 +44,12 @@ import java.util.HashMap; import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class AgilityWallJump extends SimpleAdaptation { private final Map airjumps; private final Map horizontalIntent; @@ -132,7 +140,7 @@ public void on(PlayerMoveEvent e) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); int level = getLevel(p); if (level <= 0) { @@ -292,33 +300,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Hold shift while mid-air against a wall to latch and jump.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wall Jump adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wall Jump adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Jumps Level Bonus Divisor for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Jumps Level Bonus Divisor for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxJumpsLevelBonusDivisor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Height Base for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Base for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpHeightBase = 0.625; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Height Bonus Level Multiplier for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Bonus Level Multiplier for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpHeightBonusLevelMultiplier = 0.225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Backward Push Speed for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Push Speed for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double backwardPushSpeed = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Backward Intent Dot Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Intent Dot Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double backwardIntentDotThreshold = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Input Movement Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Movement Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double inputMovementThreshold = 0.0025; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Input Window Ms for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Ms for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long inputWindowMs = 450; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWindUp.java rename to src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index f24e4e83b..44d1033b3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.agility; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.reflect.events.api.ReflectiveHandler; -import com.volmit.adapt.util.reflect.events.api.entity.EntityDismountEvent; -import com.volmit.adapt.util.reflect.events.api.entity.EntityMountEvent; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.agility; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; +import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; +import art.arcane.adapt.util.reflect.events.api.entity.EntityMountEvent; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -42,6 +44,11 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.reflect.registries.Particles; + public class AgilityWindUp extends SimpleAdaptation { private final Map ticksRunning; private final Map speedBoosting; @@ -123,7 +130,7 @@ private double getWindupSpeed(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); UUID id = p.getUniqueId(); if (!hasAdaptation(p) || !isVelocityEligible(p)) { @@ -252,39 +259,39 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Get faster the longer you sprint.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wind Up adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wind Up adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupTicksSlowest = 180; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupTicksFastest = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Base for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Base for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupSpeedBase = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Level Multiplier for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Level Multiplier for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupSpeedLevelMultiplier = 0.225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for windup velocity scaling.", impact = "Higher values increase movement speed while windup is active.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for windup velocity scaling.", impact = "Higher values increase movement speed while windup is active.") double baseHorizontalSpeed = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") double maxHorizontalSpeed = 0.31; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the windup target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the windup target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double accelPerTick = 0.045; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity decays when sprint movement is not applied.", impact = "Higher values stop faster and reduce carry momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when sprint movement is not applied.", impact = "Higher values stop faster and reduce carry momentum.") double brakePerTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") double stopThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, windup velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, windup velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") boolean hardStopOnInvalidState = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectElevator.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectElevator.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java index 970e85cf6..3f7694de3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectElevator.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java @@ -16,25 +16,25 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; +package art.arcane.adapt.content.adaptation.architect; import com.jeff_media.customblockdata.CustomBlockData; import com.jeff_media.customblockdata.events.CustomBlockDataMoveEvent; import com.jeff_media.customblockdata.events.CustomBlockDataRemoveEvent; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -413,21 +413,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Build wool elevators to teleport vertically.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseDistance = 32; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Multiplier for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int multiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.40; } -} \ No newline at end of file +} diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectFoundation.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectFoundation.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java index 5a4910d1b..aad7276fc 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectFoundation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java @@ -16,17 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -50,6 +51,12 @@ import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Painting; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class ArchitectFoundation extends SimpleAdaptation { private static final BlockData AIR = Material.AIR.createBlockData(); private static final BlockData BLOCK = Material.TINTED_GLASS.createBlockData(); @@ -282,7 +289,7 @@ public int getBlockPower(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); if (!hasAdaptation(i)) { continue; @@ -332,27 +339,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak to place a temporary foundation beneath you.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long duration = 3000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int minBlocks = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int maxBlocks = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int cooldown = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Foundation adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Foundation adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.40; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectGlass.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectGlass.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java index f4d4aad8f..0d84620de 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectGlass.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java @@ -16,15 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; +package art.arcane.adapt.content.adaptation.architect; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -36,6 +37,12 @@ import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; + public class ArchitectGlass extends SimpleAdaptation { public ArchitectGlass() { super("architect-glass"); @@ -117,19 +124,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Silk-touch glass blocks when breaking them with an empty hand.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Glass adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Glass adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectPlacement.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectPlacement.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java index 5e8b55025..128fea317 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectPlacement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java @@ -16,17 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -46,6 +47,12 @@ import java.util.List; import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; + public class ArchitectPlacement extends SimpleAdaptation { private final KMap> totalMap = new KMap<>(); @@ -298,21 +305,21 @@ public void onTick() { @NoArgsConstructor @ConfigDescription("Place multiple blocks at once while sneaking with a matching block.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Placement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Placement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int maxBlocks = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Placement adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Placement adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectSmartShape.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectSmartShape.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java index 407e440ce..6152ff865 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectSmartShape.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; +package art.arcane.adapt.content.adaptation.architect; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -240,21 +240,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-left-click a block with an empty hand to rotate its orientation.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Xp Per Rotate for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Xp Per Rotate for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minXpPerRotate = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Orientation Option for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Orientation Option for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerOrientationOption = 0.16; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java rename to src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index 04e80d3fd..81dd6dab1 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -16,24 +16,25 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.architect; - -import static com.volmit.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.BoundRedstoneTorch; -import com.volmit.adapt.util.*; +package art.arcane.adapt.content.adaptation.architect; + +import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.BoundRedstoneTorch; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; import java.util.HashMap; import java.util.Map; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Color; @@ -54,6 +55,14 @@ import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.events.api.Event; +import art.arcane.adapt.util.reflect.registries.Particles; + public class ArchitectWirelessRedstone extends SimpleAdaptation { private final Map cooldowns; @@ -272,7 +281,7 @@ public boolean isEnabled() { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); ItemStack hand = p.getInventory().getItemInMainHand(); ItemStack offhand = p.getInventory().getItemInOffHand(); @@ -293,21 +302,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Use a crafted redstone remote to toggle redstone at a distance.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Wireless Redstone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Wireless Redstone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int cooldown = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Wireless Redstone adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Wireless Redstone adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeChop.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeChop.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java index 9dcc00e15..b8a5be247 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeChop.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java @@ -16,17 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.axe; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -39,6 +41,11 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class AxeChop extends SimpleAdaptation { public AxeChop() { @@ -181,27 +188,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Chop down trees by right-clicking the base log.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Range Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rangeLevelMultiplier = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksInverseLevelMultiplier = 16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerBlockBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerBlockInverseLevelMultiplier = 4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeCraftLogSwap.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java similarity index 98% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeCraftLogSwap.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java index af55a4aeb..418c4b2a8 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeCraftLogSwap.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; +package art.arcane.adapt.content.adaptation.axe; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -35,7 +35,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.inventory.CraftItemEvent; import org.bukkit.inventory.ItemStack; -import com.volmit.adapt.util.reflect.registries.Materials; +import art.arcane.adapt.util.reflect.registries.Materials; public class AxeCraftLogSwap extends SimpleAdaptation { @@ -1060,17 +1060,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Convert log types using a sapling in a crafting table.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeDropToInventory.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java index b97dceee5..9c846ae98 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; +package art.arcane.adapt.content.adaptation.axe; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -120,17 +120,17 @@ public void onTick() { @NoArgsConstructor @ConfigDescription("Chopped wood drops directly into your inventory.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeGroundSmash.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeGroundSmash.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java index d2b29cb30..98e5f477b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeGroundSmash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java @@ -16,16 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.axe; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -37,6 +39,12 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.Impulse; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class AxeGroundSmash extends SimpleAdaptation { public AxeGroundSmash() { super("axe-ground-smash"); @@ -157,31 +165,31 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Jump then crouch to smash all nearby enemies with your axe.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Falloff Factor for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Falloff Factor for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double falloffFactor = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusLevelFactorMultiplier = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageLevelFactorMultiplier = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Force Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double forceFactorMultiplier = 1.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Force Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double forceBase = 0.27; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksInverseLevelMultiplier = 225; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeLeafVeinminer.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index e0fa06af0..1036d97e9 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -16,18 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.axe; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -40,9 +41,15 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + import java.util.*; -import static com.volmit.adapt.util.data.Metadata.VEIN_MINED; +import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class AxeLeafVeinminer extends SimpleAdaptation { public AxeLeafVeinminer() { @@ -191,21 +198,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Break bulk leaves at once while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Leaf Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Leaf Veinminer adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.325; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Leaf Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Leaf Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseRange = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeTimberMark.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeTimberMark.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java index 1c10bf28d..984236a58 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeTimberMark.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.axe; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -288,33 +288,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Mark a trunk, then break the marked log to fell connected wood.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBlocksBase = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBlocksFactor = 56; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double markDurationMillisBase = 6000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double markDurationMillisFactor = 9000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Extra Log for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Extra Log for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerExtraLog = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxLeavesBase = 24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxLeavesFactor = 180; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Leaf Cleared for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Leaf Cleared for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerLeafCleared = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/axe/AxeWoodVeinminer.java rename to src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index 30826927a..02c19cf9d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -16,20 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.axe; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.axe; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -45,7 +46,13 @@ import java.util.HashSet; import java.util.Set; -import static com.volmit.adapt.util.data.Metadata.VEIN_MINED; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + +import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class AxeWoodVeinminer extends SimpleAdaptation { public AxeWoodVeinminer() { @@ -200,23 +207,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Break bulk wood at once while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Wood Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Wood Veinminer adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxBlocks = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseRange = 3; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBastionStance.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBastionStance.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java index ef46e4978..b12cb6240 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBastionStance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.blocking; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -152,7 +152,7 @@ private double getProjectileNegateChance(int level) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (hasAdaptation(p) && !isBastionStance(p)) { setStorage(p, "bastionSessionCount", 0); @@ -173,39 +173,39 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-block with a shield to brace against knockback and soften projectiles.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.68; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackReductionBase = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackReductionFactor = 0.52; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Knockback Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Knockback Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxKnockbackReduction = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double projectileReductionBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double projectileReductionFactor = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxProjectileReduction = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double projectileNegateChanceBase = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double projectileNegateChanceFactor = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Negate Chance for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Negate Chance for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxProjectileNegateChance = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerMitigatedDamage = 2.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Negate for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Negate for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnNegate = 8.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBulwarkBash.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBulwarkBash.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java index a4f52dec8..a0733cd63 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingBulwarkBash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.blocking; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -49,6 +49,8 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.inventorygui.Window; + public class BlockingBulwarkBash extends SimpleAdaptation { private final Map lastSprintMillis = new HashMap<>(); @@ -245,53 +247,53 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sprint, jump, and land a shielded crit to unleash a bash shockwave.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDamage = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusBase = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusFactor = 2.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Range Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rangeBase = 2.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Range Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rangeFactor = 1.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackBase = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double upwardKnockbackBase = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double upwardKnockbackFactor = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stunTicksBase = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stunTicksFactor = 24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stunAmplifierBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stunAmplifierFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 220; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 120; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") float minFallDistanceForCrit = 0.08f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Recent Sprint Window Millis for the Blocking Bulwark Bash adaptation.", impact = "Allows crit bash to trigger shortly after sprint momentum, even if sprint toggles off mid-air.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recent Sprint Window Millis for the Blocking Bulwark Bash adaptation.", impact = "Allows crit bash to trigger shortly after sprint momentum, even if sprint toggles off mid-air.") long recentSprintWindowMillis = 900L; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerTargetHit = 8; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingChainArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingChainArmorer.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java index 04b87a3b3..0e9516ac5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingChainArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; +package art.arcane.adapt.content.adaptation.blocking; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -129,17 +129,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Chainmail Armor using iron nuggets.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingCounterGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingCounterGuard.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java index ec27723f9..51c90cab1 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingCounterGuard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.blocking; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Entity; @@ -41,6 +41,8 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.reflect.Reflect; + public class BlockingCounterGuard extends SimpleAdaptation { public BlockingCounterGuard() { super("blocking-counter-guard"); @@ -166,37 +168,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Blocking builds retaliation stacks that reflect damage back to attackers.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Stacks for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Stacks for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseStacks = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stackFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectChanceBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectChanceFactor = 0.27; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxReflectChance = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Reflect Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Reflect Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseReflectDamage = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Damage Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Damage Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectDamageFactor = 3.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerStack = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Cost On Reflect for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Cost On Reflect for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int stackCostOnReflect = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reflected Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reflected Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerReflectedDamage = 5.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingHorseArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingHorseArmorer.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java index 9df08cd71..749c34477 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingHorseArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; +package art.arcane.adapt.content.adaptation.blocking; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -139,17 +139,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Horse Armor by surrounding a saddle with material.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMirrorBlock.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMirrorBlock.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java index 1aecdef1f..47198fc87 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMirrorBlock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.blocking; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -47,6 +47,8 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.reflect.Reflect; + public class BlockingMirrorBlock extends SimpleAdaptation { private static final String REFLECTED_META = "adapt-mirror-reflected"; private static final String DAMAGE_FACTOR_META = "adapt-mirror-damage-factor"; @@ -226,45 +228,45 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Blocking with a shield can reflect incoming projectiles at reduced force and damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectChanceBase = 0.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectChanceFactor = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxReflectChance = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectedDamageFactorBase = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Increase for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Increase for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectedDamageFactorIncrease = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Reflected Damage Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflected Damage Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxReflectedDamageFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectVelocityFactorBase = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reflectVelocityFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxReflectVelocityFactor = 1.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 1200; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Reflected Velocity Squared for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Reflected Velocity Squared for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minReflectedVelocitySquared = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fallback Reflected Speed for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Reflected Speed for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fallbackReflectedSpeed = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Reflect for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Reflect for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnReflect = 8; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMultiArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMultiArmor.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java index 6cb8eeb4f..07cff66b5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingMultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java @@ -16,17 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; +package art.arcane.adapt.content.adaptation.blocking; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.content.item.multiItems.MultiArmor; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.content.item.multiItems.MultiArmor; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -48,6 +49,12 @@ import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class BlockingMultiArmor extends SimpleAdaptation { private static final MultiArmor multiarmor = new MultiArmor(); private final Map cooldowns; @@ -251,19 +258,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Bind Elytras to armor for dynamic merge and swap.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Blocking Multi Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Blocking Multi Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int startingSlots = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java rename to src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java index 778027075..e94938da6 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.blocking; +package art.arcane.adapt.content.adaptation.blocking; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -105,17 +105,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft a Saddle using leather.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingAbsorption.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingAbsorption.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java index 477992d6f..e768c4353 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingAbsorption.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -124,17 +124,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Absorption from Instant Heal and Quartz.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingBlindness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingBlindness.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java index ff24f9f42..d1e9b466b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingBlindness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Blindness from Awkward Potion and Ink Sack.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDarkness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDarkness.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java index 3d6c00253..fce1398c8 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDarkness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -111,17 +111,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Darkness from NightVision Potion and Black Concrete.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDecay.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDecay.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java index d720bb89a..cb5960234 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingDecay.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Wither from Weakness Potion and Poisonous Potato.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingFatigue.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingFatigue.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java index 03db038f4..2707c5be3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingFatigue.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Fatigue from Weakness Potion and Slime.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHaste.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java index 7e831670f..6256ae252 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.reflect.registries.PotionTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Haste from Speed Potion and Amethyst.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHealthBoost.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java index 05d1f6a77..3452d43ea 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Health Boost from Instant Heal and Golden Apple.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHunger.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHunger.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java index 09ab37ef8..c86344192 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingHunger.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Hunger from Awkward Potion and Rotten Flesh.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingLingering.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingLingering.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java index e5a9e443f..255b3811a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingLingering.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java @@ -16,22 +16,24 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.reflect.registries.ItemFlags; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.brewing; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.reflect.registries.ItemFlags; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -58,6 +60,12 @@ import java.util.Map; import java.util.function.Function; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.function.Function3; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class BrewingLingering extends SimpleAdaptation { private static final Function getColor; private static final Function> getEffectAttributes; @@ -269,27 +277,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brewed potions last longer.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Duration Boost Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Boost Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDurationBoostTicks = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Boost Factor Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Boost Factor Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationBoostFactorTicks = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Multiplier Factor for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Multiplier Factor for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationMultiplierFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Duration Multiplier for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Multiplier for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDurationMultiplier = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Custom Lore for the Brewing Lingering adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Custom Lore for the Brewing Lingering adaptation.", impact = "True enables this behavior and false disables it.") boolean useCustomLore = true; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingNausea.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingNausea.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java index 868aad768..f0c81f0c6 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingNausea.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Nausea from Awkward Potion and Mushroom.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingResistance.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java index e5b0900ed..febfd7594 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Resistance from Awkward Potion and Iron.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSaturation.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSaturation.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java index 007e3a0be..334a04471 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSaturation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.potion.BrewingRecipe; -import com.volmit.adapt.api.potion.PotionBuilder; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.potion.BrewingRecipe; +import art.arcane.adapt.api.potion.PotionBuilder; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brew a Potion of Saturation from Regen Potion and Baked Potato.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSuperHeated.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSuperHeated.java rename to src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java index a8502301c..7165659b4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/brewing/BrewingSuperHeated.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java @@ -16,20 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.brewing; +package art.arcane.adapt.content.adaptation.brewing; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.RNG; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -49,6 +52,12 @@ import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class BrewingSuperHeated extends SimpleAdaptation { private static final int MAX_CHECKS_BEFORE_REMOVE = 20; @@ -240,23 +249,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brewing stands work faster when surrounded by fire or lava.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Multiplier Factor for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier Factor for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double multiplierFactor = 1.33; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fire Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fireMultiplier = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Lava Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lava Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lavaMultiplier = 0.69; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosAberrantTouch.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosAberrantTouch.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java index e589e6b31..8255fc539 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosAberrantTouch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -182,51 +182,51 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Melee attacks apply stacking slowness at the cost of hunger, with PvP caps.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Aberrant Touch adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Aberrant Touch adaptation.", impact = "True enables this behavior and false disables it.") boolean playClockSounds = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.38; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Add Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Add Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int durationAddTicks = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int durationPerLevelTicks = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int playerDurationCapTicks = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int playerAmplifierCap = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int entityDurationCapTicks = 120; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int entityDurationCapPerLevelTicks = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entity Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int entityAmplifierCap = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerCost = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Food Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Food Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int minimumFoodLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Root At Stacks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root At Stacks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rootAtStacks = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Root Duration Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Duration Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rootDurationTicks = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Root Amplifier for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Amplifier for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rootAmplifier = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Reset Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Reset Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long stackResetMillis = 2500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownMillis = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Proc for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Proc for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerProc = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerLevel = 1.25; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosInstantRecall.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index 0e953376e..6a91f8096 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ChronoTimeBombItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ChronoTimeBombItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -61,6 +61,9 @@ import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.reflect.registries.Particles; + import java.util.*; public class ChronosInstantRecall extends SimpleAdaptation { @@ -1113,109 +1116,109 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Click with a clock to rewind to a recent snapshot with health and hunger restored.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") boolean playClockSounds = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Rewind Trace Particles for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Rewind Trace Particles for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") boolean showRewindTraceParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rewind Trace Points for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Trace Points for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rewindTracePoints = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Target rewind animation duration in milliseconds.", impact = "Higher values make recall rewind visuals slower/smoother; lower values make them faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Target rewind animation duration in milliseconds.", impact = "Higher values make recall rewind visuals slower/smoother; lower values make them faster.") int rewindAnimationDurationMillis = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Legacy fallback rewind animation ticks used when duration is invalid.", impact = "Retained for backward compatibility with older configs.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Legacy fallback rewind animation ticks used when duration is invalid.", impact = "Retained for backward compatibility with older configs.") int rewindAnimationTicks = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Temporarily switches to spectator during rewind animation for smoother camera movement through obstacles.", impact = "True improves rewind smoothness; false keeps player in survival during rewind.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Temporarily switches to spectator during rewind animation for smoother camera movement through obstacles.", impact = "True improves rewind smoothness; false keeps player in survival during rewind.") boolean rewindUseTemporarySpectator = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Uses a client-side spectator camera anchor during rewind so server position only updates at the end.", impact = "True reduces jitter and rubber-banding by avoiding per-tick player teleports.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses a client-side spectator camera anchor during rewind so server position only updates at the end.", impact = "True reduces jitter and rubber-banding by avoiding per-tick player teleports.") boolean rewindUseClientCamera = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Extra ticks to suppress teleport XP/stat tracking after recall rewinds.", impact = "Higher values make it safer against teleport-XP side effects from other skills.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra ticks to suppress teleport XP/stat tracking after recall rewinds.", impact = "Higher values make it safer against teleport-XP side effects from other skills.") int rewindTeleportXpSuppressExtraTicks = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables direct click-with-clock activation for instant recall.", impact = "True allows recall by clicking with a valid recall clock; false disables direct clock-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables direct click-with-clock activation for instant recall.", impact = "True allows recall by clicking with a valid recall clock; false disables direct clock-click activation.") boolean enableClockClickTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows left-click to activate recall when clock-click trigger is enabled.", impact = "True allows left-click activation; false blocks left-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click to activate recall when clock-click trigger is enabled.", impact = "True allows left-click activation; false blocks left-click activation.") boolean clockClickLeftClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows right-click to activate recall when clock-click trigger is enabled.", impact = "True allows right-click activation; false blocks right-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click to activate recall when clock-click trigger is enabled.", impact = "True allows right-click activation; false blocks right-click activation.") boolean clockClickRightClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for instant recall with a valid recall clock.", impact = "True allows sprint-click activation; false disables sprint-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for instant recall with a valid recall clock.", impact = "True allows sprint-click activation; false disables sprint-click activation.") boolean enableSprintClickTrigger = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows left-click for sprint-click recall trigger.", impact = "True enables left-click sprint activation; false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click for sprint-click recall trigger.", impact = "True enables left-click sprint activation; false disables it.") boolean sprintClickLeftClick = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows right-click for sprint-click recall trigger.", impact = "True enables right-click sprint activation; false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click for sprint-click recall trigger.", impact = "True enables right-click sprint activation; false disables it.") boolean sprintClickRightClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows click-in-air interactions for recall click triggers.", impact = "True lets air-clicks activate enabled click modes; false blocks air-click activations.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-in-air interactions for recall click triggers.", impact = "True lets air-clicks activate enabled click modes; false blocks air-click activations.") boolean allowAirClicks = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows click-on-block interactions for recall click triggers.", impact = "True lets block-clicks activate enabled click modes; false blocks block-click activations.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-on-block interactions for recall click triggers.", impact = "True lets block-clicks activate enabled click modes; false blocks block-click activations.") boolean allowBlockClicks = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for instant recall.", impact = "True allows pressing sneak once to trigger recall.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for instant recall.", impact = "True allows pressing sneak once to trigger recall.") boolean enableSingleSneakTrigger = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak instant recall trigger.", impact = "True requires sprint state when using single-sneak activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak instant recall trigger.", impact = "True requires sprint state when using single-sneak activation.") boolean singleSneakRequiresSprint = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for single-sneak trigger.", impact = "True keeps single-sneak recall tied to clock usage.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for single-sneak trigger.", impact = "True keeps single-sneak recall tied to clock usage.") boolean singleSneakRequiresClockInHand = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables double-tap jump activation for instant recall.", impact = "True allows jump-based recall trigger; false disables jump trigger.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump activation for instant recall.", impact = "True allows jump-based recall trigger; false disables jump trigger.") boolean enableDoubleJumpTrigger = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require sprinting while double-jumping to trigger recall.", impact = "True requires sprint state for double-jump trigger; false allows it without sprint.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting while double-jumping to trigger recall.", impact = "True requires sprint state for double-jump trigger; false allows it without sprint.") boolean doubleJumpRequiresSprint = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for double-jump trigger.", impact = "True requires clock-in-hand for jump trigger; false allows jump trigger without holding a clock.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for double-jump trigger.", impact = "True requires clock-in-hand for jump trigger; false allows jump trigger without holding a clock.") boolean doubleJumpRequiresClockInHand = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum milliseconds allowed between jump taps for double-jump recall.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum milliseconds allowed between jump taps for double-jump recall.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") int doubleJumpWindowMillis = 450; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump recall.", impact = "Higher values reduce accidental arm events; lower values increase sensitivity.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump recall.", impact = "Higher values reduce accidental arm events; lower values increase sensitivity.") double doubleJumpMinVerticalVelocity = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Rewind Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Rewind Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseRewindSeconds = 3.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rewind Seconds Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Seconds Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rewindSecondsPerLevel = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Hard cap for recall rewind duration in seconds.", impact = "Prevents high levels/config values from exceeding this rewind window.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap for recall rewind duration in seconds.", impact = "Prevents high levels/config values from exceeding this rewind window.") double maxRewindSeconds = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int cooldownPaddingSeconds = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Snapshot Interval Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snapshot Interval Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int snapshotIntervalMillis = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls History Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls History Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int historyPaddingSeconds = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rewind Protection Ticks for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Protection Ticks for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rewindProtectionTicks = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Distance Block for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Distance Block for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDistanceBlock = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Health Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Health Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerHealthPoint = 0.85; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Hunger Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Hunger Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerHungerPoint = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Saturation Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Saturation Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerSaturationPoint = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Level Multiplier Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Multiplier Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpLevelMultiplierPerLevel = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Min Raw Reward for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Raw Reward for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpMinRawReward = 1.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Min Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpMinAward = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Max Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Max Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpMaxAward = 36; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Cross World Distance Credit for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Cross World Distance Credit for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpCrossWorldDistanceCredit = 16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long xpDiminishWindowMillis = 45000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Min Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Min Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpDiminishMinMultiplier = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long xpRepeatWindowMillis = 180000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Source Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Source Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpRepeatSourceRadius = 3.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Target Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Target Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpRepeatTargetRadius = 3.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Penalty Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Penalty Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpRepeatPenaltyMultiplier = 0.2; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosSoundFX.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java similarity index 98% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosSoundFX.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java index c91728d52..efc4c0ecf 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosSoundFX.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; +package art.arcane.adapt.content.adaptation.chronos; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.util.J; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Sound; diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTemporalEcho.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index 8c55e3632..d82438f70 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -177,33 +177,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Replays your last projectile action shortly after firing at reduced power.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double echoDelayTicksBase = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double echoDelayTicksFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double echoVelocityFactorBase = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double echoVelocityFactorFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Echo Velocity Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Echo Velocity Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxEchoVelocityFactor = 0.92; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 2600; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Echo for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Echo for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerEcho = 12; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeBomb.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index ef7fb9ee0..65201ff4e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ChronoTimeBombItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ChronoTimeBombItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Color; @@ -59,6 +59,10 @@ import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; +import art.arcane.adapt.util.common.math.Sphere; +import art.arcane.adapt.util.common.misc.Impulse; +import art.arcane.adapt.util.reflect.registries.Particles; + public class ChronosTimeBomb extends SimpleAdaptation { private static final EnumSet SUPPORTED_ACTIONS = EnumSet.of( Action.RIGHT_CLICK_AIR, @@ -620,75 +624,75 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Throw a crafted chrono bomb that creates a temporal field, slowing entities and freezing projectiles.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean playClockSounds = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseRadius = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusPerLevel = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Duration Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseDurationTicks = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int durationPerLevelTicks = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownMillis = 15000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Target Deploy Range for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Deploy Range for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double targetDeployRange = 64; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Center YOffset for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Center YOffset for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fieldCenterYOffset = 1.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int slownessAmplifier = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Caster Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Caster Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int casterSlownessAmplifier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fatigueAmplifier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Freeze Players In Air for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Freeze Players In Air for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean freezePlayersInAir = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Accumulate Frozen Impulse for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Accumulate Frozen Impulse for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean accumulateFrozenImpulse = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Min Magnitude for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Min Magnitude for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double frozenImpulseMinMagnitude = 0.03; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Sample Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Sample Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double frozenImpulseSampleCap = 2.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Release Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Release Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double frozenImpulseReleaseCap = 7.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Refresh Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Refresh Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int effectRefreshTicks = 24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Field Sphere for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Field Sphere for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean showFieldSphere = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Particle Count for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Particle Count for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fieldSphereParticleCount = 280; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Refresh Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Refresh Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long fieldSphereRefreshMillis = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Sound Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Sound Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fieldTickSoundIntervalMillis = 325; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Min Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Min Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fieldTickMinIntervalMillis = 70; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Start for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Start for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fieldTickPitchStart = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch End for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch End for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fieldTickPitchEnd = 1.96; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Curve Exponent for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Curve Exponent for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fieldTickPitchCurveExponent = 3.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Field Tick Acceleration Factor for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Acceleration Factor for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fieldTickAccelerationFactor = 0.82; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Cast for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Cast for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnCast = 28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerLevel = 3; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeInABottle.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeInABottle.java rename to src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java index 5ef7b7f41..61ebe8628 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/chronos/ChronosTimeInABottle.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.chronos; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ChronoTimeBottle; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ChronoTimeBottle; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Keyed; import org.bukkit.Material; @@ -57,6 +57,8 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.reflect.registries.Particles; + public class ChronosTimeInABottle extends SimpleAdaptation { private static final String RECIPE_KEY = "chronos-time-in-a-bottle"; @@ -638,7 +640,7 @@ private enum GrowthProfile { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p)) { continue; @@ -674,147 +676,147 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Carry a temporal bottle that stores time to accelerate timed blocks and baby animals.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") boolean playClockSounds = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Stored Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stored Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxStoredSeconds = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double chargePerSecond = 0.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double chargePerSecondPerLevel = 0.02; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Cook Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cook Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseCookTicksPerStoredSecond = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cook Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cook Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cookTicksPerSecondPerLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxCookTicksPerUse = 140; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxCookTicksPerUsePerLevel = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Furnace Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double furnaceSpendMultiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Brewing Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Brewing Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseBrewingTicksPerStoredSecond = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Brewing Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double brewingTicksPerSecondPerLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxBrewingTicksPerUse = 140; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxBrewingTicksPerUsePerLevel = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Brewing Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double brewingSpendMultiplier = 1.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Campfire Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Campfire Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseCampfireTicksPerStoredSecond = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Campfire Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double campfireTicksPerSecondPerLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxCampfireTicksPerUse = 160; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxCampfireTicksPerUsePerLevel = 40; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Campfire Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double campfireSpendMultiplier = 0.9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Entity Age Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Entity Age Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseEntityAgeTicksPerStoredSecond = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entity Age Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Age Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double entityAgeTicksPerSecondPerLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxEntityAgeTicksPerUse = 180; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxEntityAgeTicksPerUsePerLevel = 55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entity Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double entitySpendMultiplier = 1.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxGrowthStepsPerUse = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxGrowthStepsPerUsePerLevel = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Allow Sapling Tree Generation for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Sapling Tree Generation for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") boolean allowSaplingTreeGeneration = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Base for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Base for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double saplingGrowChanceBase = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double saplingGrowChancePerLevel = 0.04; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthCostMultiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Reduction Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Reduction Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthCostReductionPerLevel = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Growth Cost Level Scale for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Cost Level Scale for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minGrowthCostLevelScale = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Growth Step Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Step Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minGrowthStepSeconds = 0.06; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sapling Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int saplingGrowthSteps = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stem Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int stemGrowthSteps = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Berry Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int berryGrowthSteps = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int vineGrowthSteps = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int caveVineGrowthSteps = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Kelp Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int kelpGrowthSteps = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Default Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int defaultGrowthSteps = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crop Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cropNaturalSeconds = 300; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double netherWartNaturalSeconds = 420; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sapling Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double saplingNaturalSeconds = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stem Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stemNaturalSeconds = 660; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double berryBushNaturalSeconds = 260; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double vineNaturalSeconds = 300; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double caveVineNaturalSeconds = 280; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Kelp Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double kelpNaturalSeconds = 240; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Default Growable Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double defaultGrowableNaturalSeconds = 420; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crop Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cropCostMultiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double netherWartCostMultiplier = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sapling Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double saplingCostMultiplier = 2.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stem Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stemCostMultiplier = 1.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double berryBushCostMultiplier = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double vineCostMultiplier = 0.85; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double caveVineCostMultiplier = 0.9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Kelp Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double kelpCostMultiplier = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Default Growable Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double defaultGrowableCostMultiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Cook Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Cook Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerCookTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Brew Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Brew Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBrewTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Campfire Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Campfire Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerCampfireTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Entity Age Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Entity Age Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerEntityAgeTick = 0.06; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth Step for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth Step for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerGrowthStep = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max XPPer Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max XPPer Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxXPPerUse = 55; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingBackpacks.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingBackpacks.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java index 7571af672..3051b20b4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingBackpacks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; +package art.arcane.adapt.content.adaptation.crafting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -106,17 +106,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Bundles for portable item storage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingDeconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingDeconstruction.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java index c6f52aaee..facbc7d22 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingDeconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java @@ -16,15 +16,15 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.crafting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -202,15 +202,15 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Deconstruct blocks and items into salvageable base components using shears.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingLeather.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingLeather.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java index 760488716..b18512060 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingLeather.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java @@ -16,15 +16,15 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; +package art.arcane.adapt.content.adaptation.crafting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -94,17 +94,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Leather from Rotten Flesh on a campfire.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingReconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingReconstruction.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java index 3e0f2e1d3..ec7cd1ba3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingReconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; +package art.arcane.adapt.content.adaptation.crafting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -352,17 +352,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Recraft ores from their base smelted components.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingSkulls.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingSkulls.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java index eedb0f4bb..d321c3976 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingSkulls.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; +package art.arcane.adapt.content.adaptation.crafting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -158,17 +158,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Mob Skulls using materials surrounding a Bone Block.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingStations.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingStations.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java index 857549eea..684d30553 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingStations.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java @@ -16,15 +16,15 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; +package art.arcane.adapt.content.adaptation.crafting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -160,19 +160,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Use crafting tables, anvils, and other stations in the palm of your hand.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Crafting Stations adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Crafting Stations adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int cooldown = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingXP.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingXP.java rename to src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java index ce44d166a..c54ad7bd0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/crafting/CraftingXP.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java @@ -16,14 +16,14 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.crafting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdvancementSpec; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.crafting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdvancementSpec; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -121,17 +121,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain passive XP when crafting items.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java index 6287b13d8..85e60fb9a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.discovery; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -393,37 +393,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Brushing suspicious blocks has a chance to grant bonus archaeology loot.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusRollChanceBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusRollChanceFactor = 0.43; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Bonus Roll Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Bonus Roll Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBonusRollChance = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rareRewardChanceBase = 0.04; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rareRewardChanceFactor = 0.24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Rare Reward Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Reward Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRareRewardChance = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 1600; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reward for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reward for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerReward = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reward Value Xp Multiplier for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reward Value Xp Multiplier for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rewardValueXpMultiplier = 0.45; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArmor.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java index 6d809cdd9..393ffff99 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java @@ -16,20 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.IAttribute; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.registries.Attributes; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.discovery; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -45,6 +46,12 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.math.Sphere; +import art.arcane.adapt.util.common.math.VectorMath; + public class DiscoveryArmor extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-discovery-armor".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:discovery-armor"); @@ -137,7 +144,7 @@ private double getStrength(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) continue; @@ -189,23 +196,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain passive armor based on nearby block hardness.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int radiusFactor = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Strength Exponent for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Exponent for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double strengthExponent = 1.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery Armor adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery Armor adaptation.", impact = "True enables this behavior and false disables it.") public boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryBetterMending.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryBetterMending.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java index 2d51c01b8..d35cf1373 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryBetterMending.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.discovery; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -188,31 +188,31 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-left-click to spend XP and directly mend the Mending item in your hand.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double repairPerXpBase = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double repairPerXpFactor = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxXpSpendBase = 14.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxXpSpendFactor = 130.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 38.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksReduction = 26.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Skill Xp Per Durability for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp Per Durability for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double skillXpPerDurability = 0.35; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java index 39386b57d..cbf2cf5a4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.discovery; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -249,27 +249,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click with a compass to pulse toward a nearby structure target.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Search Range Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double searchRangeBase = 640; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Search Range Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double searchRangeFactor = 768; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 26000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 14000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Pulse for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Pulse for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerPulse = 25; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryUnity.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryUnity.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java index 143e77bdf..b616acfc7 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryUnity.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; +package art.arcane.adapt.content.adaptation.discovery; +import art.arcane.volmlib.util.format.Form; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -38,6 +40,11 @@ import java.util.List; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public class DiscoveryUnity extends SimpleAdaptation { @@ -120,23 +127,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Collecting Experience Orbs adds XP to random skills.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Gained Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gained Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpGainedMultiplier = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpBoostMultiplier = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Duration for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Duration for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int xpBoostDuration = 15000; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java index 244cb9dbc..16f0935ef 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KMap; +package art.arcane.adapt.content.adaptation.discovery; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KMap; import de.slikey.effectlib.effect.BleedEffect; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -46,6 +48,12 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class DiscoveryVillagerAtt extends SimpleAdaptation { private final KMap active = new KMap<>(); @@ -181,27 +189,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Get better villager trades at the cost of XP per interaction.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double effectivenessBase = 0.005; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxEffectiveness = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int levelDrain = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int levelCostAdd = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amplifier = 1.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryXpResist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryXpResist.java rename to src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java index 1917f6f8b..01f57520c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/discovery/DiscoveryXpResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java @@ -16,16 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.discovery; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.discovery; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -39,6 +41,11 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class DiscoveryXpResist extends SimpleAdaptation { private static final long COOLDOWN_MILLIS = 15000L; private final Map cooldowns; @@ -169,29 +176,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Consume experience to mitigate damage when a hit would drop you below 5 hearts.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double effectivenessBase = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxEffectiveness = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int levelDrain = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int levelCostAdd = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amplifier = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trigger Health Threshold for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Health Threshold for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double triggerHealthThreshold = 10.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java index 6de772086..fe7bd9ff2 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.enchanting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -139,25 +139,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Reduce anvil XP cost when combining, repairing, and renaming.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reductionBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reductionFactor = 0.37; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Maximum Reduction for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Reduction for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maximumReduction = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Cost for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Cost for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int minimumCost = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java index 665340f55..f35b51c1f 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; +package art.arcane.adapt.content.adaptation.enchanting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.enchantments.EnchantmentOffer; @@ -115,21 +115,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain virtual bookshelf power to improve enchanting table offer quality.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Power Base for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Base for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double powerBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Power Factor for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Factor for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double powerFactor = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java index 13597dce8..708ead9c5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.enchanting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -190,33 +190,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Using a grindstone can recover one removed enchant on a book with bonus XP.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.74; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double recoverChanceBase = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double recoverChanceFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Recover Chance for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Recover Chance for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRecoverChance = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusXpBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusXpFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 120; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 70; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Recovery for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Recovery for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double skillXpOnRecovery = 13; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java index 7c3313059..9e5b7cfc3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.enchanting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -130,17 +130,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Chance to return free lapis when enchanting at the cost of 1 extra level.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java index 537730f81..a89028836 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.enchanting; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -165,29 +165,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click an enchanting table to reroll offers for lapis and XP.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lapisCostBase = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lapisCostFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 320; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 220; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Level Cost for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Cost for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int xpLevelCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Gain On Reroll for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gain On Reroll for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpGainOnReroll = 15; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java index eb3dfb431..d8887f983 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.enchanting; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -192,21 +192,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Enchant items by clicking enchant books directly on them.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus Limit for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus Limit for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxPowerBonusLimit = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus1Per Levels for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus1Per Levels for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxPowerBonus1PerLevels = 3; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingXPReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingXPReturn.java rename to src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java index 5637cc8e3..a3488a05b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/enchanting/EnchantingXPReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.enchanting; +package art.arcane.adapt.content.adaptation.enchanting; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -116,19 +116,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Enchanting XP is partially refunded when you enchant an item.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Return for the Enchanting XPReturn adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Return for the Enchanting XPReturn adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int xpReturn = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationDropToInventory.java rename to src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java index 2f144a596..60ad0dd39 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.excavation; +package art.arcane.adapt.content.adaptation.excavation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -121,17 +121,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Excavated blocks drop directly into your inventory.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationHaste.java rename to src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java index e083cf316..feeb5eea5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.excavation; +package art.arcane.adapt.content.adaptation.excavation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -109,17 +109,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain Haste while excavating blocks.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationOmniTool.java rename to src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index a5acb2e30..e7ee98e02 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -16,18 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.excavation; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.content.item.multiItems.OmniTool; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.content.item.multiItems.OmniTool; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -53,6 +54,12 @@ import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class ExcavationOmniTool extends SimpleAdaptation { private static final OmniTool omniTool = new OmniTool(); @@ -376,19 +383,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Dynamically merge and swap tools on the fly based on what you are mining.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Excavation Omni Tool adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Excavation Omni Tool adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int startingSlots = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSeismicPing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSeismicPing.java rename to src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java index c68450850..d6d87bc98 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSeismicPing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.excavation; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Location; @@ -248,47 +248,47 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Mining can emit seismic pings that hint toward nearby ore direction.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.78; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Scan Range Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double scanRangeBase = 11; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Scan Range Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double scanRangeFactor = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pingChanceBase = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pingChanceFactor = 0.37; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Ping Chance for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ping Chance for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxPingChance = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 2600; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 1850; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hintSegmentsBase = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hintSegmentsFactor = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Segment Spacing for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Spacing for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double segmentSpacing = 0.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double particleSize = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Segment Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int segmentParticleCount = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tip Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tip Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int tipParticleCount = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ping for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ping for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerPing = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Target Value Xp Multiplier for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Value Xp Multiplier for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double targetValueXpMultiplier = 0.5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSpelunker.java rename to src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index 4ed87d2bf..85feece20 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -16,19 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.excavation; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; +package art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; import fr.skytasul.glowingentities.GlowingEntities; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -47,6 +48,12 @@ import java.util.HashMap; import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class ExcavationSpelunker extends SimpleAdaptation { private final Map cooldowns; @@ -219,21 +226,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("See ores through the ground using Glowberries in your main hand.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldown = 6.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Range Multiplier for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Multiplier for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rangeMultiplier = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java index 5328d6364..acdbd8c3d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -47,6 +47,8 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.reflect.registries.Particles; + public class HerbalismBeeShepherd extends SimpleAdaptation { private final Map lastPulse = new HashMap<>(); @@ -82,7 +84,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { long now = System.currentTimeMillis(); - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p) || !isHoldingFlower(p)) { continue; @@ -231,45 +233,45 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Holding flowers near crops emits growth pulses and draws nearby bees toward you.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Growth Particles for the Herbalism Bee Shepherd adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Growth Particles for the Herbalism Bee Shepherd adaptation.", impact = "True enables this behavior and false disables it.") boolean showGrowthParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.64; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthAttemptsBase = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthAttemptsFactor = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Step Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthStepBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Growth Step Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double growthStepFactor = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodCostBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodCostFactor = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pulseMillisBase = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pulseMillisFactor = 650; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double beePullStrengthBase = 0.07; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double beePullStrengthFactor = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerGrowth = 0.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index 1a5606ba9..6c8e6edd7 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -51,6 +51,8 @@ import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.inventorygui.Items; + public class HerbalismCompostCascade extends SimpleAdaptation { public HerbalismCompostCascade() { super("herbalism-compost-cascade"); @@ -466,69 +468,69 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click a composter to process nearby drops, crops, leaves, and your own compostables.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 5.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 12.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxItemsBase = 80.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxItemsFactor = 240.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fillChanceBase = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fillChanceFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Fill Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fill Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxFillChance = 0.98; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double leafCompostBurstsBase = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double leafCompostBurstsFactor = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double leafFillChanceMultiplierBase = 1.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double leafFillChanceMultiplierFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 36.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksReduction = 28.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double boneMealBase = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double boneMealFactor = 6.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double readyBonusBoneMealBase = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double readyBonusBoneMealFactor = 8.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double itemsPerBoneMealBase = 20.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double itemsPerBoneMealReduction = 14.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valuableChanceBase = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valuableChanceFactor = 0.09; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Valuable Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Valuable Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxValuableChance = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valuableRollsBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valuableRollsFactor = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Item Consumed for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Item Consumed for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerItemConsumed = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level Gain for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level Gain for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerLevelGain = 2.8; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java index 5ab6e7315..c6e6d4a2e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -108,17 +108,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Cobwebs from String in a Crafting Table.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java index 03c6a6543..7feb2e246 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -125,17 +125,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Mushroom Blocks from Mushrooms in a Crafting Table.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java index 5af2d3cec..d0fe06a9b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -114,17 +114,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Harvested crops drop directly into your inventory.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java index a07a67a25..810c844fd 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java @@ -16,17 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.RNG; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -40,6 +43,12 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class HerbalismGrowthAura extends SimpleAdaptation { public HerbalismGrowthAura() { super("herbalism-growth-aura"); @@ -94,7 +103,7 @@ private double getFoodCost(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); try { if (hasAdaptation(p)) { @@ -168,29 +177,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Grow nature around you in an aura at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Surface Only for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Surface Only for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") boolean surfaceOnly = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.325; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minFoodCost = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxFoodCost = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Strength Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double strengthFactor = 0.75; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java index e104b75ba..864d2d8f5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -105,17 +105,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Consuming food gives you more saturation.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryShield.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryShield.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java index 9e79c27b5..3699050e9 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismHungryShield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -116,21 +116,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Take damage to your hunger before your health.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.78; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double effectivenessBase = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxEffectiveness = 0.95; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismLuck.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java index 68722aec8..296ce0cfe 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.Materials; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -138,21 +138,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Breaking Grass or Flowers has a chance to drop random items.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Low Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Low Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lowChance = 0.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls High Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls High Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double highChance = 90; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismMyconid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismMyconid.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java index 9d72582a1..8195a7a44 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismMyconid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -103,17 +103,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Mycelium from Dirt and Mushrooms.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismReplant.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index d528bb723..684955f43 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -16,19 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.skill.SkillHerbalism; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.reflect.registries.Particles; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.skill.SkillHerbalism; +import art.arcane.volmlib.util.data.Cuboid; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,6 +45,12 @@ import java.util.Collection; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class HerbalismReplant extends SimpleAdaptation { public HerbalismReplant() { @@ -208,29 +216,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Right-click a crop with a hoe to harvest and replant it.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Replant adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Replant adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Lvl1 for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Lvl1 for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownLvl1 = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseCooldown = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Factor for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Factor for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownFactor = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusCooldown = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Sub for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Sub for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int radiusSub = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java index df33c44fd..ee3b50ef4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -151,25 +151,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Protect farmland and convert fall damage into hunger on natural ground.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Absorb Base for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Base for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double absorbBase = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Absorb Factor for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Factor for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double absorbFactor = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Absorb Percent for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Absorb Percent for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxAbsorbPercent = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Per Damage for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Per Damage for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodPerDamage = 1.8; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSeedSower.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSeedSower.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java index 5fd29700f..c6fee6593 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSeedSower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -213,31 +213,31 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click with seeds to plant nearby farmland and soul-sand plots.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.675; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseRadius = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Crop Count for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crop Count for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseCropCount = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crop Count Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Count Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cropCountFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksReduction = 42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Crop for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Crop for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerCrop = 1.45; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index 0007e8945..0e22c1ee1 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.herbalism; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -539,57 +539,57 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click mycelium with mushrooms to spread an outward spore-web that mutates nearby growth.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Convert Wood To Hyphae for the Herbalism Spore Bloom adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Convert Wood To Hyphae for the Herbalism Spore Bloom adaptation.", impact = "True enables this behavior and false disables it.") boolean convertWoodToHyphae = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows flowers hit by the bloom to be replaced with mushrooms.", impact = "Disable this to keep flowers untouched while still converting soil into mushroom blocks.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows flowers hit by the bloom to be replaced with mushrooms.", impact = "Disable this to keep flowers untouched while still converting soil into mushroom blocks.") boolean swapFlowersToMushrooms = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Branch Chance for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Branch Chance for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double branchChance = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mushroom Choices for the Herbalism Spore Bloom adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mushroom Choices for the Herbalism Spore Bloom adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String[] mushroomChoices = {"RED_MUSHROOM", "BROWN_MUSHROOM", "CRIMSON_FUNGUS", "WARPED_FUNGUS"}; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bloomAttemptsBase = 26; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bloomAttemptsFactor = 58; - @com.volmit.adapt.util.config.ConfigDoc(value = "Additional bloom attempts granted each adaptation level.", impact = "Higher values make each level spread across more total blocks.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional bloom attempts granted each adaptation level.", impact = "Higher values make each level spread across more total blocks.") double bloomAttemptsPerLevel = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bloomRadiusBase = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bloomRadiusFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spokes Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spokesBase = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spokes Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spokesFactor = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double blocksPerPulseBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double blocksPerPulseFactor = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spreadIntervalTicksBase = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spreadIntervalTicksFactor = 1.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodCostBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodCostFactor = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 1700; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 1100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mushroom Placed for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mushroom Placed for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerMushroomPlaced = 1.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismTerralid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismTerralid.java rename to src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java index 40e3c8f39..a6cc6e0b5 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/herbalism/HerbalismTerralid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.herbalism; +package art.arcane.adapt.content.adaptation.herbalism; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -108,17 +108,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft Grass Blocks from Seeds and Dirt.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterAdrenaline.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterAdrenaline.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java index 1b32af82b..69939e7f7 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterAdrenaline.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -112,21 +112,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Deal more melee damage the lower your health is.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 0.21; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterDropToInventory.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java index 175948549..e5920179e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -149,17 +149,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Mob and block drops teleport directly into your inventory.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterInvis.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterInvis.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java index 99d4f41eb..401e8b2aa 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterInvis.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -125,37 +125,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain invisibility when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Invis adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Invis adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterJumpBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterJumpBoost.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java index 849a3d9c8..40135f4e4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterJumpBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -127,37 +127,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain jump boost when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Jump Boost adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Jump Boost adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterLuck.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java index 952de97e7..53997682d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -128,37 +128,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain luck when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Luck adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Luck adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterRegen.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterRegen.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java index cac60be79..f036be153 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterRegen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -126,37 +126,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain regeneration when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Regen adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Regen adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterResistance.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java index 4d2ff9b16..e107b7310 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -127,37 +127,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain resistance when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Resistance adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Resistance adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterSpeed.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java index bcd417e1d..7ce22d3b1 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.hunter; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -153,7 +153,7 @@ private void grantSpeedBurst(org.bukkit.entity.Player p, int amplifier, int dura @Override public void onTick() { long now = System.currentTimeMillis(); - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { org.bukkit.entity.Player p = adaptPlayer.getPlayer(); SpeedBurst burst = speedBursts.get(p.getUniqueId()); if (burst == null) { @@ -269,53 +269,53 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain speed when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update velocity speed bursts.", impact = "Lower values feel more responsive but run updates more frequently.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update velocity speed bursts.", impact = "Lower values feel more responsive but run updates more frequently.") long setInterval = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for hunter bursts before amplifier scaling.", impact = "Higher values increase movement speed while a burst is active.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for hunter bursts before amplifier scaling.", impact = "Higher values increase movement speed while a burst is active.") double baseHorizontalSpeed = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") double maxHorizontalSpeed = 0.32; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double accelPerTick = 0.045; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") double brakePerTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny momentum longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny momentum longer.") double stopThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed when state changes skip expected flow.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed when state changes skip expected flow.") boolean hardStopOnInvalidState = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Speed adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Speed adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterStrength.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterStrength.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java index e1c828c83..2cac62a1a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterStrength.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; +package art.arcane.adapt.content.adaptation.hunter; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -127,37 +127,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain strength when struck, at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") boolean useConsumable = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") boolean poisonPenalty = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") boolean stackHungerPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") boolean stackPoisonPenalty = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") boolean stackBuff = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseEffectbyLevel = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerFromLevel = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int basePoisonFromLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseHungerDuration = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Strength adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Strength adaptation.", impact = "Changing this alters the identifier or text used by the feature.") String consumable = "ROTTEN_FLESH"; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterTrophySkinner.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterTrophySkinner.java rename to src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java index 0d04fae21..64fe8dd2a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/hunter/HunterTrophySkinner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.hunter; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.hunter; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -199,39 +199,39 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Precision kills can grant bonus trophy drops and occasional heads from elite targets.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dropChanceBase = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dropChanceFactor = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Maximum Drop Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Drop Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxDropChance = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Head Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double headChanceBase = 0.015; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Head Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double headChanceFactor = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Maximum Head Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Head Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHeadChance = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double trophyAmountBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double trophyAmountFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumRangeBase = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumRangeFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP Per Trophy for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Trophy for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerTrophy = 16; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherBlazeLeech.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherBlazeLeech.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java index d07bc4f1f..bf76416ae 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherBlazeLeech.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -195,45 +195,45 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Fire interactions can grant hunger and regeneration in short bursts.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.62; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double triggerChanceBase = 0.16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double triggerChanceFactor = 0.34; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Trigger Chance for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Trigger Chance for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxTriggerChance = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenTicksBase = 28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenTicksFactor = 42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenAmplifierFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Restore Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodRestoreBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Restore Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double foodRestoreFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Saturation Restore for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Saturation Restore for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double saturationRestore = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 1400; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Defensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Defensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnDefensiveProc = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Offensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Offensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnOffensiveProc = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherFireResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherFireResist.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java index 40e184e9c..8f3d2398a 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherFireResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java @@ -15,15 +15,17 @@ - You should have received a copy of the GNU General Public License - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -34,6 +36,10 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; + public class NetherFireResist extends SimpleAdaptation { public NetherFireResist() { super("nether-fire-resist"); @@ -119,21 +125,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Chance to negate the burning effect.") public static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Base for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Base for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fireResistBase = 0.10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Factor for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Factor for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fireResistFactor = 0.25; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherGhastWard.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherGhastWard.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java index 698b45417..36d792aea 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherGhastWard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.AbstractArrow; @@ -157,41 +157,41 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Harden against ghast blasts and wither-skeleton pressure in the Nether.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.73; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double ghastProjectileReductionBase = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double ghastProjectileReductionFactor = 0.54; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Ghast Projectile Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ghast Projectile Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxGhastProjectileReduction = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double explosionReductionBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double explosionReductionFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Explosion Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Explosion Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxExplosionReduction = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double witherSkeletonReductionBase = 0.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double witherSkeletonReductionFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Wither Skeleton Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Wither Skeleton Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxWitherSkeletonReduction = 0.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxFireTicksBase = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxFireTicksFactor = 70; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerMitigatedDamage = 4.2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherLavaWalker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherLavaWalker.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java index 32446d608..5bb9b718b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherLavaWalker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -147,33 +147,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Stride over lava in the Nether at the cost of hunger.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stride Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double strideBase = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Stride Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double strideFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerCostBase = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerCostFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisBase = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownMillisFactor = 700; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Ticks for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Ticks for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fireResistTicks = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Stride for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Stride for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerStride = 3.5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherPiglinBroker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherPiglinBroker.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java index 996c76123..0cf4ad9da 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherPiglinBroker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -185,37 +185,37 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Nearby piglin bartering can yield extra or improved rolls.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Broker Range for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Broker Range for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double brokerRange = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double extraRollChanceBase = 0.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double extraRollChanceFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Extra Roll Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Extra Roll Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxExtraRollChance = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rareBonusChanceBase = 0.03; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rareBonusChanceFactor = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Rare Bonus Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Bonus Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRareBonusChance = 0.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amountMultiplierBase = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amountMultiplierFactor = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Boosted Barter for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Boosted Barter for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnBoostedBarter = 12; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherSkullYeet.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherSkullYeet.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java index fe36ab367..5f46de97c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherSkullYeet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java @@ -16,16 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.nether; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.GameMode; @@ -48,6 +49,11 @@ import java.util.HashMap; import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class NetherSkullYeet extends SimpleAdaptation { private final Map lastJump = new HashMap<>(); @@ -200,21 +206,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Throw Wither Skulls that explode on impact.") public static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") public boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") private boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private int baseCooldown = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Level Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private int levelCooldown = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") private int baseCost = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") private double costFactor = 0.92; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") private int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") private int initialCost = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherWitherResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/nether/NetherWitherResist.java rename to src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java index 8698e9966..4f71d7d83 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/nether/NetherWitherResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.nether; +package art.arcane.adapt.content.adaptation.nether; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -125,21 +125,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Wearing Netherite Armor has a chance to negate the wither effect.") public static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") public boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") private boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") private int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") private double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") private int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") private int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Piece Chance for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Piece Chance for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double basePieceChance = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Chance Addition for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Addition for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double chanceAddition = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java index d7d67f6b2..82f0e196c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.pickaxe; +package art.arcane.adapt.content.adaptation.pickaxe; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.reflect.registries.Enchantments; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -255,17 +255,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Automatically smelt mined ores with a chance for extra drops.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeChisel.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeChisel.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java index 6abfda941..8043dd176 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeChisel.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java @@ -16,16 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.pickaxe; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Particles; +package art.arcane.adapt.content.adaptation.pickaxe; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -38,6 +40,11 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class PickaxeChisel extends SimpleAdaptation { public PickaxeChisel() { super("pickaxe-chisel"); @@ -169,31 +176,31 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Right-click ores to chisel extra ore at a severe durability cost.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Chisel adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Chisel adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Time for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Time for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int cooldownTime = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dropChanceBase = 0.07; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dropChanceFactor = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Break Chance for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Break Chance for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double breakChance = 0.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerBlockBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor Inverse Multiplier for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor Inverse Multiplier for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactorInverseMultiplier = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java index 929c62683..bf92d946e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.pickaxe; +package art.arcane.adapt.content.adaptation.pickaxe; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -120,17 +120,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Mined blocks drop directly into your inventory.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index 5477a3ced..77f046496 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.pickaxe; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.ChatColor; @@ -348,43 +348,43 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click a block with an iron+ pickaxe to highlight nearby ores.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Costs Reduce Max Durability for the Pickaxe Quarry Sense adaptation.", impact = "True reduces max durability instead of adding normal damage.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Costs Reduce Max Durability for the Pickaxe Quarry Sense adaptation.", impact = "True reduces max durability instead of adding normal damage.") boolean costsReduceMaxDurability = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double scanRadiusBase = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double scanRadiusFactor = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHighlightsBase = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHighlightsFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double highlightTicksBase = 90; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double highlightTicksFactor = 90; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 40; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durabilityCostPercentBase = 0.006; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durabilityCostPercentFactor = 0.0045; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Durability Cost Percent for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Durability Cost Percent for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minDurabilityCostPercent = 0.001; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Found Ore for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Found Ore for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerFoundOre = 6; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java index e8dc1690b..7862415aa 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java @@ -1,16 +1,16 @@ -package com.volmit.adapt.content.adaptation.pickaxe; +package art.arcane.adapt.content.adaptation.pickaxe; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.RNG; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -123,17 +123,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Spawners drop when broken with silk touch or while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java rename to src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index 315ef44a9..582c8a8ee 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -16,20 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.pickaxe; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,7 +45,14 @@ import java.util.HashMap; import java.util.Map; -import static com.volmit.adapt.util.data.Metadata.VEIN_MINED; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.registries.Particles; + +import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class PickaxeVeinminer extends SimpleAdaptation { public PickaxeVeinminer() { @@ -195,21 +203,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Break connected ore veins at once while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Veinminer adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Pickaxe Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Pickaxe Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int baseRange = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedArrowRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedArrowRecovery.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java index 19e5adabd..df2d4a12b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedArrowRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java @@ -1,16 +1,16 @@ -package com.volmit.adapt.content.adaptation.ranged; +package art.arcane.adapt.content.adaptation.ranged; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.Enchantments; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Arrow; @@ -115,19 +115,19 @@ public void addStats(int level, Element v) { @NoArgsConstructor @ConfigDescription("Chance to recover arrows after hitting or killing an enemy.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.78; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hit Chance for the Ranged Arrow Recovery adaptation.", impact = "Add or remove entries to control which values are included.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Chance for the Ranged Arrow Recovery adaptation.", impact = "Add or remove entries to control which values are included.") double[] hitChance = {10, 20, 30, 40, 50, 60, 70, 80}; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedFloaters.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedFloaters.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java index 4449a6c87..3c4cd4667 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedFloaters.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.ranged; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -44,6 +44,8 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.reflect.registries.Particles; + public class RangedFloaters extends SimpleAdaptation { public RangedFloaters() { super("ranged-floaters"); @@ -145,33 +147,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Projectiles have a chance to apply levitation to targets.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Floaters adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Floaters adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.78; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Chance Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double chanceBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Chance Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double chanceFactor = 0.58; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Chance for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Chance for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxChance = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationTicksBase = 26.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationTicksFactor = 110.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxAmplifier = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Proc for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Proc for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double skillXpOnProc = 8.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedForce.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedForce.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java index 11c2027b8..6c79b4a7c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedForce.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java @@ -16,16 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.ranged; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -36,6 +38,11 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; + public class RangedForce extends SimpleAdaptation { public RangedForce() { @@ -134,21 +141,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Shoot projectiles further and faster.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Reward Long Shot Reward for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Reward Long Shot Reward for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRewardLongShotReward = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Factor for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Factor for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedFactor = 1.135; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedLungeShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedLungeShot.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java index 9da5c66ee..9ea123892 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedLungeShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java @@ -16,15 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; +package art.arcane.adapt.content.adaptation.ranged; +import art.arcane.volmlib.util.format.Form; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -35,6 +37,12 @@ import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.util.Vector; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; + public class RangedLungeShot extends SimpleAdaptation { public RangedLungeShot() { super("ranged-lunge-shot"); @@ -123,21 +131,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("While falling, firing arrows launches you in a random direction.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Lunge Shot adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Lunge Shot adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Factor for the Ranged Lunge Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Factor for the Ranged Lunge Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double factor = 0.935; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPiercing.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPiercing.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java index 8e08aaf8b..5216b0ba0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPiercing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; +package art.arcane.adapt.content.adaptation.ranged; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.AbstractArrow; @@ -132,17 +132,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Projectiles pierce through multiple targets.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPinningShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPinningShot.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java index e6e944b14..72c844fc0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedPinningShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.ranged; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -173,45 +173,45 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Projectiles can pin targets with heavy slowness.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dampen Velocity On Proc for the Ranged Pinning Shot adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dampen Velocity On Proc for the Ranged Pinning Shot adaptation.", impact = "True enables this behavior and false disables it.") boolean dampenVelocityOnProc = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.74; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procChanceBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procChanceFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxProcChance = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationTicksBase = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durationTicksFactor = 90; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amplifier Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amplifierBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amplifier Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double amplifierFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reapplyCooldownMillisBase = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double reapplyCooldownMillisFactor = 2800; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Horizontal Velocity Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horizontal Velocity Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double horizontalVelocityFactor = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cleanup Threshold for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cleanup Threshold for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int cleanupThreshold = 128; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Entry Ttl Millis for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entry Ttl Millis for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long entryTtlMillis = 60000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Proc for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Proc for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnProc = 12; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedRicochetBolt.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedRicochetBolt.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java index c501fdb4d..54883d556 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedRicochetBolt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.ranged; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -56,6 +56,8 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.util.Vector; +import art.arcane.adapt.util.common.math.Direction; + public class RangedRicochetBolt extends SimpleAdaptation { private static final String RICOCHET_COUNT_META = "adapt-ricochet-count"; private static final String RICOCHET_MAX_META = "adapt-ricochet-max"; @@ -383,65 +385,65 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Arrows ricochet from block impacts with chained bounces, scaling speed, and bonus damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.74; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRicochetsBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRicochetsFactor = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedBonusPerRicochetBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedBonusPerRicochetFactor = 0.27; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Speed Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Speed Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxSpeedBonusPerRicochet = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusPerRicochetBase = 0.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusPerRicochetFactor = 2.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Damage Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxDamageBonusPerRicochet = 3.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Ricochet Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Ricochet Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minRicochetVelocitySquared = 0.09; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Live Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Live Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumLiveVelocitySquared = 0.0004; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Post Bounce Speed for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Post Bounce Speed for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumPostBounceSpeed = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset From Surface for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset From Surface for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spawnOffsetFromSurface = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset Along Direction for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset Along Direction for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spawnOffsetAlongDirection = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spark Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int sparkParticleCount = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spark Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sparkSpread = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crit Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int critParticleCount = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crit Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double critSpread = 0.14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bouncePitchBase = 1.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Drop Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Drop Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bouncePitchDropPerRicochet = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sparkPitchBase = 1.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Raise Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Raise Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sparkPitchRaisePerRicochet = 0.07; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerRicochet = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet Step for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet Step for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerRicochetStep = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) in addition to arrows/tridents.", impact = "True enables universal ricochet across most player-thrown projectiles.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) in addition to arrows/tridents.", impact = "True enables universal ricochet across most player-thrown projectiles.") boolean applyToAllProjectiles = true; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedTrajectorySight.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index 1a429331c..fcbd606fe 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.ranged; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -152,7 +152,7 @@ public void on(EntityDeathEvent e) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p)) { drawStartedMillis.remove(p.getUniqueId()); @@ -729,73 +729,73 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Preview ranged projectile flight while sneaking or drawing your shot.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Segments Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double segmentsBase = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Segments Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double segmentsFactor = 26; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Velocity Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double velocityBase = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Velocity Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double velocityFactor = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Gravity Step for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gravity Step for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double gravityStep = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Step Scale for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Step Scale for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double stepScale = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Drag Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drag Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dragFactor = 0.99; - @com.volmit.adapt.util.config.ConfigDoc(value = "Drag factor used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values keep thrown arcs flatter and longer; lower values make them lose speed faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values keep thrown arcs flatter and longer; lower values make them lose speed faster.") double lightProjectileDragFactor = 0.99; - @com.volmit.adapt.util.config.ConfigDoc(value = "Drag factor used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values keep heavier throws moving faster; lower values shorten their travel.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values keep heavier throws moving faster; lower values shorten their travel.") double heavyProjectileDragFactor = 0.99; - @com.volmit.adapt.util.config.ConfigDoc(value = "Gravity step used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values produce steeper arcs for light projectiles.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values produce steeper arcs for light projectiles.") double lightProjectileGravityStep = 0.03; - @com.volmit.adapt.util.config.ConfigDoc(value = "Gravity step used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values cause potions and bottles to drop faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values cause potions and bottles to drop faster.") double heavyProjectileGravityStep = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crossbow Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crossbow Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double crossbowVelocity = 3.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Launch velocity used for trident previews while sneaking.", impact = "Higher values extend trident prediction distance.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for trident previews while sneaking.", impact = "Higher values extend trident prediction distance.") double tridentVelocity = 2.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Launch velocity used for light thrown projectile previews.", impact = "Higher values extend snowball, egg, and pearl prediction distance.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for light thrown projectile previews.", impact = "Higher values extend snowball, egg, and pearl prediction distance.") double thrownProjectileVelocity = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Launch velocity used for potion and experience bottle previews.", impact = "Higher values extend heavy throw prediction distance.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for potion and experience bottle previews.", impact = "Higher values extend heavy throw prediction distance.") double thrownPotionVelocity = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Additional downward launch offset for heavy thrown projectile previews.", impact = "Higher values tilt potion and bottle trajectories downward more strongly.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional downward launch offset for heavy thrown projectile previews.", impact = "Higher values tilt potion and bottle trajectories downward more strongly.") double heavyProjectilePitchDrop = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fallback Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fallbackVelocity = 1.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sneak Preview Charge Ticks for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak Preview Charge Ticks for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sneakPreviewChargeTicks = 16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double particleSize = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "How much particle size grows per block of distance from the viewer.", impact = "Higher values make far trajectory points easier to see.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How much particle size grows per block of distance from the viewer.", impact = "Higher values make far trajectory points easier to see.") double particleSizePerBlock = 0.008; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum particle size used for the trajectory preview.", impact = "Caps distance scaling to prevent oversized particles.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum particle size used for the trajectory preview.", impact = "Caps distance scaling to prevent oversized particles.") double maxParticleSize = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int trailParticleCount = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Impact Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Impact Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int impactParticleCount = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Every for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Every for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int trailParticleEvery = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum distance from the player's eye before preview particles are shown.", impact = "Higher values keep particles out of your sightline and reduce visual obstruction.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum distance from the player's eye before preview particles are shown.", impact = "Higher values keep particles out of your sightline and reduce visual obstruction.") double minPreviewDistanceFromEye = 1.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "How many interpolated points are drawn between each simulated physics step.", impact = "Higher values smooth the line while increasing particle density.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How many interpolated points are drawn between each simulated physics step.", impact = "Higher values smooth the line while increasing particle density.") int trailSubSteps = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Offset forward from the eye where trajectory simulation begins.", impact = "Higher values start the preview further from your face.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Offset forward from the eye where trajectory simulation begins.", impact = "Higher values start the preview further from your face.") double previewStartOffset = 0.55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Highlights the predicted hit target entity with per-player glow.", impact = "Enable to glow whichever entity the preview would hit first.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Highlights the predicted hit target entity with per-player glow.", impact = "Enable to glow whichever entity the preview would hit first.") boolean glowPredictedTarget = true; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedWebBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedWebBomb.java rename to src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java index d8ade1949..8723c22bd 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/ranged/RangedWebBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java @@ -16,20 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.ranged; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.recipe.MaterialChar; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.BoundSnowBall; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Particles; +package art.arcane.adapt.content.adaptation.ranged; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.recipe.MaterialChar; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.BoundSnowBall; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -50,6 +51,12 @@ import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + import java.util.*; public class RangedWebBomb extends SimpleAdaptation { @@ -269,19 +276,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Throw a crafted web snare to trap targets in cobwebs.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Web Bomb adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Web Bomb adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftAccess.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftAccess.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java index 3314b79cf..765f6dc45 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftAccess.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java @@ -16,19 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.BoundEnderPearl; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.BoundEnderPearl; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import manifold.rt.api.util.Pair; @@ -53,7 +54,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import static com.volmit.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; + +import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class RiftAccess extends SimpleAdaptation { private final Map, List> activeViewsMap = new ConcurrentHashMap<>(); @@ -304,17 +311,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft a Reliquary Portkey to access marked containers remotely.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Access adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Access adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 15; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftBlink.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index 1aed565f8..5fef923de 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.event.AdaptAdaptationTeleportEvent; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -58,7 +58,9 @@ import java.util.Map; import java.util.UUID; -import static com.volmit.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; +import art.arcane.adapt.util.reflect.registries.Particles; + +import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class RiftBlink extends SimpleAdaptation { @@ -520,57 +522,57 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Short-ranged instant teleportation by double-tapping jump while sprinting.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Blink adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Blink adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Cooldown between successful Rift Blink triggers in milliseconds.", impact = "Higher values reduce blink frequency; lower values allow faster reuse.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown between successful Rift Blink triggers in milliseconds.", impact = "Higher values reduce blink frequency; lower values allow faster reuse.") int cooldownMillis = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables double-tap jump detection for Rift Blink.", impact = "True allows jump-based activation; false disables jump activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump detection for Rift Blink.", impact = "True allows jump-based activation; false disables jump activation.") boolean enableDoubleJumpTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require sprinting for the double-tap jump trigger.", impact = "True requires sprinting while double-tapping jump; false allows it without sprint.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for the double-tap jump trigger.", impact = "True requires sprinting while double-tapping jump; false allows it without sprint.") boolean doubleJumpRequiresSprint = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum time window between jump taps in milliseconds.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum time window between jump taps in milliseconds.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") int doubleJumpWindowMillis = 450; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump blink.", impact = "Higher values reduce accidental arming; lower values make detection more sensitive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump blink.", impact = "Higher values reduce accidental arming; lower values make detection more sensitive.") double doubleJumpMinVerticalVelocity = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for Rift Blink.", impact = "True allows clicking while sprinting to blink; false disables this trigger.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for Rift Blink.", impact = "True allows clicking while sprinting to blink; false disables this trigger.") boolean enableSprintClickTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for Rift Blink.", impact = "True allows pressing sneak once to trigger blink.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for Rift Blink.", impact = "True allows pressing sneak once to trigger blink.") boolean enableSingleSneakTrigger = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak trigger.", impact = "True requires sprint state when using single-sneak activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak trigger.", impact = "True requires sprint state when using single-sneak activation.") boolean singleSneakRequiresSprint = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows left-click as a sprint-click trigger.", impact = "True allows left-click activation while sprinting; false disables left-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click as a sprint-click trigger.", impact = "True allows left-click activation while sprinting; false disables left-click activation.") boolean sprintClickLeftClick = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows right-click as a sprint-click trigger.", impact = "True allows right-click activation while sprinting; false disables right-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click as a sprint-click trigger.", impact = "True allows right-click activation while sprinting; false disables right-click activation.") boolean sprintClickRightClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables click-with-ender-pearl activation for Rift Blink.", impact = "True allows pearl-click activation; false disables pearl-click activation.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables click-with-ender-pearl activation for Rift Blink.", impact = "True allows pearl-click activation; false disables pearl-click activation.") boolean enableEnderPearlClickTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows left-click with an ender pearl to trigger Rift Blink.", impact = "True enables left-click pearl activation; false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click with an ender pearl to trigger Rift Blink.", impact = "True enables left-click pearl activation; false disables it.") boolean enderPearlClickLeftClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows right-click with an ender pearl to trigger Rift Blink.", impact = "True enables right-click pearl activation; false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click with an ender pearl to trigger Rift Blink.", impact = "True enables right-click pearl activation; false disables it.") boolean enderPearlClickRightClick = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows air-click interactions to trigger Rift Blink.", impact = "True lets air clicks trigger enabled click modes; false blocks air-click triggers.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows air-click interactions to trigger Rift Blink.", impact = "True lets air clicks trigger enabled click modes; false blocks air-click triggers.") boolean allowAirClicks = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows block-click interactions to trigger Rift Blink.", impact = "True lets block clicks trigger enabled click modes; false blocks block-click triggers.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows block-click interactions to trigger Rift Blink.", impact = "True lets block clicks trigger enabled click modes; false blocks block-click triggers.") boolean allowBlockClicks = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDistance = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Distance Factor for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Factor for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double distanceFactor = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Adjustment for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Adjustment for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxVerticalAdjustment = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Distance Search Step for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Search Step for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double distanceSearchStep = 0.5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftDescent.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftDescent.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java index f5380c3d4..4fce92f2d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftDescent.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java @@ -16,15 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -38,6 +39,12 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class RiftDescent extends SimpleAdaptation { private final List cooldown = new ArrayList<>(); @@ -128,17 +135,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak to descend and negate levitation effects.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Rift Descent adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Rift Descent adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldown = 5.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderTaglock.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderTaglock.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java index ac3339307..d43888c27 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderTaglock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.BoundEnderPearl; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.BoundEnderPearl; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -74,6 +74,9 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.nbt.Tag; + public class RiftEnderTaglock extends SimpleAdaptation { private static final String PROJECTILE_TARGET_META = "adapt-rift-taglock-target"; private final NamespacedKey targetKey; @@ -445,33 +448,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Tag entities into ender pearls and throw those pearls to reposition the tagged target.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.95; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Base for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Base for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double throwCooldownTicksBase = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Factor for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Factor for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double throwCooldownTicksFactor = 14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Suppress Pearl Teleport Window Millis for the Rift Ender Taglock adaptation.", impact = "This should be long enough to catch the teleport event from a taglocked throw.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Suppress Pearl Teleport Window Millis for the Rift Ender Taglock adaptation.", impact = "This should be long enough to catch the teleport event from a taglocked throw.") long suppressPearlTeleportWindowMillis = 180000L; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Large Width Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Width Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double largeWidthThreshold = 1.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Large Height Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Height Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double largeHeightThreshold = 2.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP On Tag for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Tag for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnTag = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP On Throw for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Throw for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnThrow = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP On Teleport for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Teleport for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnTeleport = 14; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderchest.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderchest.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java index c988bc449..4462377e2 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftEnderchest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; +package art.arcane.adapt.content.adaptation.rift; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -116,9 +116,9 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Open an enderchest by left-clicking it in your hand.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftGate.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftGate.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java index e06564a6d..57dd6d55c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftGate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java @@ -16,19 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.recipe.AdaptRecipe; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.event.AdaptAdaptationTeleportEvent; -import com.volmit.adapt.content.item.BoundEyeOfEnder; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; +import art.arcane.adapt.content.item.BoundEyeOfEnder; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -43,6 +44,13 @@ import org.bukkit.scheduler.BukkitRunnable; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.registries.Particles; + public class RiftGate extends SimpleAdaptation { public RiftGate() { super("rift-gate"); @@ -275,13 +283,13 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Craft a gate item to teleport to a marked location.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Consume On Use for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consume On Use for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") boolean consumeOnUse = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index a8e194c6c..53a5b6b03 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,6 +44,8 @@ import java.util.Map; +import art.arcane.adapt.util.common.math.Dimension; + public class RiftInflatedPocketDimension extends SimpleAdaptation { public RiftInflatedPocketDimension() { super("rift-inflated-pocket-dimension"); @@ -294,23 +296,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Building and empty-hand block targeting can fetch materials from your ender chest, and sneak-drop stores items into it.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Build Refill Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Build Refill Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int buildRefillAmount = 64; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Right Click Pull Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Right Click Pull Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int rightClickPullAmount = 64; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP Per Transferred Item for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Transferred Item for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerTransferredItem = 0.08; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftResist.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftResist.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java index f3ec5dfaf..48f043fdc 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; +package art.arcane.adapt.content.adaptation.rift; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -124,21 +124,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain resistance when using Ender items and abilities.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Amplitude for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplitude for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int amplitude = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Duration for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int duration = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVisage.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java similarity index 80% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVisage.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java index 75115c6ef..72689be4e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVisage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java @@ -1,14 +1,14 @@ -package com.volmit.adapt.content.adaptation.rift; +package art.arcane.adapt.content.adaptation.rift; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Enderman; @@ -94,17 +94,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Prevents Endermen from becoming aggressive when you carry Enderpearls.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVoidMagnet.java rename to src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index a06a507a8..a473b1dee 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.rift; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.rift; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -41,6 +41,8 @@ import java.util.Map; +import art.arcane.adapt.util.common.inventorygui.Items; + public class RiftVoidMagnet extends SimpleAdaptation { public RiftVoidMagnet() { super("rift-void-magnet"); @@ -82,7 +84,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p) || !p.isSneaking() || p.getTicksLived() % getPulseTicks(getLevel(p)) != 0) { continue; @@ -183,33 +185,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak to periodically pull nearby dropped items into your ender chest first.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Allow Ender Chest Overflow for the Rift Void Magnet adaptation.", impact = "When true, leftovers that do not fit in ender chest can spill into player inventory.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Ender Chest Overflow for the Rift Void Magnet adaptation.", impact = "When true, leftovers that do not fit in ender chest can spill into player inventory.") boolean allowEnderChestOverflow = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxItemsBase = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxItemsFactor = 22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pulseTicksBase = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pulseTicksFactor = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Moved Item for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Moved Item for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerMovedItem = 0.7; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java index 95fc8e4f1..bd1672912 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; +package art.arcane.adapt.content.adaptation.seaborrne; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.ExperienceOrb; @@ -120,17 +120,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Earn more XP from fishing and catch more fish.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneOxygen.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java similarity index 79% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneOxygen.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java index b75b1dd41..8df5c90e6 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneOxygen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java @@ -16,15 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; +package art.arcane.adapt.content.adaptation.seaborrne; +import art.arcane.volmlib.util.format.Form; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -32,6 +34,10 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; + public class SeaborneOxygen extends SimpleAdaptation { public SeaborneOxygen() { @@ -67,7 +73,7 @@ public double getAirBoost(int level) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); if (i.getLocation().getBlock().getType() == Material.WATER && hasAdaptation(i)) { int airTicks = getLevel(i) * getConfig().airPerLevelTics; @@ -90,19 +96,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Hold more oxygen underwater.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.525; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Air Per Level Tics for the Seaborne Oxygen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Air Per Level Tics for the Seaborne Oxygen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int airPerLevelTics = 15; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index cdc9d3c16..ea6122fc0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.seaborrne; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -98,7 +98,7 @@ public void on(EntityDamageEvent e) { @Override public void onTick() { long now = System.currentTimeMillis(); - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p) || !p.isInWater()) { continue; @@ -198,47 +198,47 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain depth scaling protection underwater and partially counter mining fatigue in deep ocean play.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double depthThresholdBase = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double depthThresholdFactor = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double deepThresholdBase = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double deepThresholdFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageReductionBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageReductionFactor = 0.26; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Maximum Damage Reduction for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Damage Reduction for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxDamageReduction = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fatigueTrimChanceBase = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fatigueTrimChanceFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fatigueTrimAmountBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double fatigueTrimAmountFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int effectTicks = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Fatigue Replace Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Replace Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int fatigueReplaceTicks = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP Per Depth Pulse for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Depth Pulse for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDepthPulse = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP Pulse Cooldown Millis for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Pulse Cooldown Millis for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long xpPulseCooldownMillis = 3000; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneSpeed.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java index 9dad3bc15..252d67ed2 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; +package art.arcane.adapt.content.adaptation.seaborrne; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -76,7 +76,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); if (player.isInWater() && hasAdaptation(player)) { if (player.getLocation().getBlock().isLiquid()) { @@ -104,17 +104,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Swim faster with dolphin-like grace.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.525; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java index c28c2e896..1f0f1d50d 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.seaborrne; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -400,69 +400,69 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak while it is raining to dash like a water blink through the storm.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dashDistanceBase = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dashDistanceFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 140; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Burst for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Burst for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBurst = 11; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows the original rain-based trigger for Tidecaller dashes.", impact = "Disable this to make dashes water-only.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows the original rain-based trigger for Tidecaller dashes.", impact = "Disable this to make dashes water-only.") boolean allowRainTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows Tidecaller dashes while the player is in water.", impact = "Enable this to make sneak/attack water woosh work consistently.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows Tidecaller dashes while the player is in water.", impact = "Enable this to make sneak/attack water woosh work consistently.") boolean allowWaterTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables sneak-to-dash trigger.", impact = "Disable this if you only want attack-based trigger behavior.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sneak-to-dash trigger.", impact = "Disable this if you only want attack-based trigger behavior.") boolean enableSneakTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables attack-swing trigger (any item or empty hand).", impact = "Enable this for left-click water flings without requiring special items.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables attack-swing trigger (any item or empty hand).", impact = "Enable this for left-click water flings without requiring special items.") boolean enableAttackTrigger = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Requires sneaking for attack-swing trigger.", impact = "True makes attack trigger only fire while sneaking; false allows it anytime in valid dash environments.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires sneaking for attack-swing trigger.", impact = "True makes attack trigger only fire while sneaking; false allows it anytime in valid dash environments.") boolean attackTriggerRequiresSneak = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Restricts attack-swing trigger to water states only.", impact = "True prevents accidental attack-trigger dashes on land even if rain trigger is enabled.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Restricts attack-swing trigger to water states only.", impact = "True prevents accidental attack-trigger dashes on land even if rain trigger is enabled.") boolean attackTriggerWaterOnly = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Uses velocity-based dash movement instead of teleporting to a target.", impact = "True makes tidecaller behave like a movement burst and prevents blink-style wall bypass.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses velocity-based dash movement instead of teleporting to a target.", impact = "True makes tidecaller behave like a movement burst and prevents blink-style wall bypass.") boolean useVelocityDash = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, removes pitch from velocity dash direction.", impact = "True keeps movement mostly horizontal; false follows exact look direction including up/down.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, removes pitch from velocity dash direction.", impact = "True keeps movement mostly horizontal; false follows exact look direction including up/down.") boolean flattenVelocityDashDirection = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base forward velocity strength of a velocity dash.", impact = "Higher values produce faster bursts.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base forward velocity strength of a velocity dash.", impact = "Higher values produce faster bursts.") double velocityStrengthBase = 1.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Additional forward velocity strength gained by adaptation level.", impact = "Higher values make higher levels burst farther/faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional forward velocity strength gained by adaptation level.", impact = "Higher values make higher levels burst farther/faster.") double velocityStrengthFactor = 0.85; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base vertical velocity contribution for velocity dashes.", impact = "Small positive values keep water movement fluid; lower values stay flatter.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base vertical velocity contribution for velocity dashes.", impact = "Small positive values keep water movement fluid; lower values stay flatter.") double velocityVerticalBase = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "Additional vertical velocity contribution gained by adaptation level.", impact = "Higher values increase upward kick at higher levels.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional vertical velocity contribution gained by adaptation level.", impact = "Higher values increase upward kick at higher levels.") double velocityVerticalFactor = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Adds dash velocity on top of current velocity when true.", impact = "True preserves momentum chains; false applies a fresh velocity vector.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Adds dash velocity on top of current velocity when true.", impact = "True preserves momentum chains; false applies a fresh velocity vector.") boolean velocityAdditive = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Hard cap on resulting velocity magnitude after dash.", impact = "Lower values are safer for anticheat and collisions.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on resulting velocity magnitude after dash.", impact = "Lower values are safer for anticheat and collisions.") double maxResultingVelocity = 2.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Cancels dash if a solid block is detected directly ahead.", impact = "Prevents wall clipping and blink-like wall bypass.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Cancels dash if a solid block is detected directly ahead.", impact = "Prevents wall clipping and blink-like wall bypass.") boolean blockDashWhenWallAhead = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Distance ahead used to detect blocking walls.", impact = "Higher values are safer but can block dashes near tight spaces.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Distance ahead used to detect blocking walls.", impact = "Higher values are safer but can block dashes near tight spaces.") double wallCheckDistance = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Applies forward velocity after the dash teleport.", impact = "True makes the dash feel fluid instead of instantly stopping at the target.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Applies forward velocity after the dash teleport.", impact = "True makes the dash feel fluid instead of instantly stopping at the target.") boolean applyForwardMomentumAfterDash = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal forward momentum applied after the dash teleport.", impact = "Higher values create a stronger forward burst after each dash.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal forward momentum applied after the dash teleport.", impact = "Higher values create a stronger forward burst after each dash.") double forwardMomentum = 1.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vertical momentum added or set after the dash teleport.", impact = "Small positive values help keep water movement smooth; negative values push downward.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical momentum added or set after the dash teleport.", impact = "Small positive values help keep water movement smooth; negative values push downward.") double verticalMomentum = 0.02; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, replaces current vertical velocity with verticalMomentum.", impact = "False adds verticalMomentum on top of existing vertical motion.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, replaces current vertical velocity with verticalMomentum.", impact = "False adds verticalMomentum on top of existing vertical motion.") boolean replaceVerticalMomentum = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Re-applies swimming pose after dash when the player started swimming and remains in water.", impact = "Prevents dash teleports from popping swimmers out of swim posture.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Re-applies swimming pose after dash when the player started swimming and remains in water.", impact = "Prevents dash teleports from popping swimmers out of swim posture.") boolean preserveSwimmingAfterDash = true; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java index 668d85255..d19b9fc94 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; +package art.arcane.adapt.content.adaptation.seaborrne; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -75,7 +75,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); if (player.isInWater() && hasAdaptation(player)) { if (player.getLocation().getBlock().isLiquid()) { @@ -99,17 +99,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain haste while mining underwater.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java rename to src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java index b69df523b..23c1f1392 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.seaborrne; +package art.arcane.adapt.content.adaptation.seaborrne; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -66,7 +66,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); if (player.isInWater() && hasAdaptation(player)) { if (player.getLocation().getBlock().isLiquid()) { @@ -90,17 +90,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain night vision while underwater.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthEnderVeil.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java similarity index 79% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthEnderVeil.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java index cd18471a1..17b424a3f 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthEnderVeil.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java @@ -1,16 +1,16 @@ -package com.volmit.adapt.content.adaptation.stealth; +package art.arcane.adapt.content.adaptation.stealth; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.events.api.ReflectiveHandler; -import com.volmit.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; +import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.EntityType; @@ -96,17 +96,17 @@ public void onTarget(EndermanAttackPlayerEvent event) { @NoArgsConstructor @ConfigDescription("Prevent Enderman aggression without wearing a pumpkin.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthGhostArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthGhostArmor.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java index 38a66aaae..3fcf0fbd3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthGhostArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.IAttribute; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Attributes; +package art.arcane.adapt.content.adaptation.stealth; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -40,6 +42,11 @@ import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; + public class StealthGhostArmor extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-ghost-armor".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:ghost-armor"); @@ -91,7 +98,7 @@ public double getMaxArmorPerTick(double factor) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); @@ -147,25 +154,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Slowly build armor when not taking damage, consumed on the next hit.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxArmor = 16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int minArmor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int maxArmorPerTick = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int minArmorPerTick = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.335; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java similarity index 96% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthShadowDecoy.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index 5ec7695a3..8b645192c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.stealth; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -416,61 +416,61 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Stopping sneak spawns a short-lived shadow decoy that pulls your current aggro.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base cooldown after creating a decoy, in milliseconds.", impact = "Higher values mean longer time between activations.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base cooldown after creating a decoy, in milliseconds.", impact = "Higher values mean longer time between activations.") double cooldownMillisBase = 18000; - @com.volmit.adapt.util.config.ConfigDoc(value = "How much cooldown is reduced by leveling.", impact = "Higher values reduce cooldown more at higher levels.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How much cooldown is reduced by leveling.", impact = "Higher values reduce cooldown more at higher levels.") double cooldownMillisFactor = 12000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base active duration in ticks.", impact = "Higher values keep decoys active longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base active duration in ticks.", impact = "Higher values keep decoys active longer.") double decoyTicksBase = 60; - @com.volmit.adapt.util.config.ConfigDoc(value = "Duration scaling from level, in ticks.", impact = "Higher values extend duration more per level.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration scaling from level, in ticks.", impact = "Higher values extend duration more per level.") double decoyTicksFactor = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base aggro redirect radius.", impact = "Higher values pull aggro from farther away.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base aggro redirect radius.", impact = "Higher values pull aggro from farther away.") double decoyRadiusBase = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Aggro radius scaling from level.", impact = "Higher values expand pull range more per level.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Aggro radius scaling from level.", impact = "Higher values expand pull range more per level.") double decoyRadiusFactor = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Visual eye height used for fake player facing.", impact = "Adjust if head rotation appears too high or too low.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Visual eye height used for fake player facing.", impact = "Adjust if head rotation appears too high or too low.") double decoyEyeHeight = 1.62; - @com.volmit.adapt.util.config.ConfigDoc(value = "Delay before removing the fake player from tab list, in ticks.", impact = "Small values hide tab entries faster; larger values help skins load.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Delay before removing the fake player from tab list, in ticks.", impact = "Small values hide tab entries faster; larger values help skins load.") int tabListRemoveDelayTicks = -1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows armor stand visual fallback if packet NPC creation fails.", impact = "Turn off to disable fallback visuals on incompatible server builds.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows armor stand visual fallback if packet NPC creation fails.", impact = "Turn off to disable fallback visuals on incompatible server builds.") boolean legacyFallbackEnabled = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Refresh duration for owner invisibility while a decoy is active.", impact = "Higher values keep invisibility active longer between refreshes.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Refresh duration for owner invisibility while a decoy is active.", impact = "Higher values keep invisibility active longer between refreshes.") int ownerInvisibilityRefreshTicks = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Amplifier for the temporary invisibility effect.", impact = "Most servers should leave this at 0.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier for the temporary invisibility effect.", impact = "Most servers should leave this at 0.") int ownerInvisibilityAmplifier = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Smoke particles emitted around the invisible owner each tick while decoy is active.", impact = "Higher values create a stronger visible trail.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Smoke particles emitted around the invisible owner each tick while decoy is active.", impact = "Higher values create a stronger visible trail.") int ownerTrailParticles = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal spread for owner smoke trail.", impact = "Higher values make the trail wider.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal spread for owner smoke trail.", impact = "Higher values make the trail wider.") double ownerTrailHorizontalSpread = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vertical spread for owner smoke trail.", impact = "Higher values make the trail taller.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical spread for owner smoke trail.", impact = "Higher values make the trail taller.") double ownerTrailVerticalSpread = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vertical offset for smoke trail spawn location.", impact = "Adjust to move trail closer to feet or torso.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for smoke trail spawn location.", impact = "Adjust to move trail closer to feet or torso.") double ownerTrailYOffset = 0.1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Particle speed for owner smoke trail.", impact = "Higher values make trail movement more turbulent.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Particle speed for owner smoke trail.", impact = "Higher values make trail movement more turbulent.") double ownerTrailSpeed = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "How often owner equipment-hide packets are resent while invisible, in milliseconds.", impact = "Lower values keep visuals tighter for joining viewers, higher values reduce packet traffic.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How often owner equipment-hide packets are resent while invisible, in milliseconds.", impact = "Lower values keep visuals tighter for joining viewers, higher values reduce packet traffic.") long ownerEquipmentHideResendMillis = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal knockback applied when the decoy is hit.", impact = "Higher values make the decoy react more dramatically when struck.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal knockback applied when the decoy is hit.", impact = "Higher values make the decoy react more dramatically when struck.") double decoyHitKnockback = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vertical lift applied when the decoy is hit.", impact = "Higher values make impacts pop the decoy upward more.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical lift applied when the decoy is hit.", impact = "Higher values make impacts pop the decoy upward more.") double decoyHitLift = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Swing ray distance used to detect decoy hits.", impact = "Higher values make swings connect from farther away.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Swing ray distance used to detect decoy hits.", impact = "Higher values make swings connect from farther away.") double decoySwingDetectionReach = 4.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Bitmask for visible skin layers on the fake player decoy.", impact = "127 enables all standard skin layers (hat, jacket, sleeves, pants).") + @art.arcane.adapt.util.config.ConfigDoc(value = "Bitmask for visible skin layers on the fake player decoy.", impact = "127 enables all standard skin layers (hat, jacket, sleeves, pants).") int decoySkinLayerMask = 127; - @com.volmit.adapt.util.config.ConfigDoc(value = "Experience granted on each decoy spawn.", impact = "Higher values level the adaptation faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Experience granted on each decoy spawn.", impact = "Higher values level the adaptation faster.") double xpOnDecoy = 18; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java similarity index 81% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSight.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index e2be2c775..3acca00b2 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -16,15 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.stealth; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -37,6 +38,12 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class StealthSight extends SimpleAdaptation { private final List sneaking; @@ -117,17 +124,17 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain night vision while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSilentStep.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSilentStep.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java index ff5f47822..2d36340df 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSilentStep.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.stealth; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -196,7 +196,7 @@ public void on(EntityDamageByEntityEvent e) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p) || !p.isSneaking()) { clearDimming(p); @@ -447,57 +447,57 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneaking prevents hostile mob detection, and unseen hits deal backstab damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double playerDetectionRadiusBase = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double playerDetectionRadiusFactor = 14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dimDurationTicksBase = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double dimDurationTicksFactor = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Dim Amplifier for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Amplifier for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int dimAmplifier = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mobBackstabBase = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mobBackstabFactor = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double playerBackstabBase = 1.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double playerBackstabFactor = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Look Dot Threshold for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Look Dot Threshold for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lookDotThreshold = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Drop for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Drop for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerTargetDrop = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBonusDamage = 3.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Silent Fall Distance for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Silent Fall Distance for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") float maxSilentFallDistance = 1.6f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Shows nearby threats with per-player glowing while sneaking (red = can detect, gray = almost).", impact = "Enable to get visual awareness of entities that can or almost can spot you.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Shows nearby threats with per-player glowing while sneaking (red = can detect, gray = almost).", impact = "Enable to get visual awareness of entities that can or almost can spot you.") boolean showThreatGlows = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Look-dot margin below the full detection threshold used for gray 'almost detect' glow.", impact = "Higher values make gray warnings appear earlier; lower values make warnings stricter.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot margin below the full detection threshold used for gray 'almost detect' glow.", impact = "Higher values make gray warnings appear earlier; lower values make warnings stricter.") double almostLookDotMargin = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Look-dot threshold for stealth visibility checks while sneaking.", impact = "Lower values make crossing an entity's view count as seen more easily; higher values require a more direct look.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot threshold for stealth visibility checks while sneaking.", impact = "Lower values make crossing an entity's view count as seen more easily; higher values require a more direct look.") double detectionLookDotThreshold = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, all nearby mobs (including passive) can break hidden state when they have line-of-sight.", impact = "Enable to prevent stealth from feeling hidden in front of passive mobs like pigs.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, all nearby mobs (including passive) can break hidden state when they have line-of-sight.", impact = "Enable to prevent stealth from feeling hidden in front of passive mobs like pigs.") boolean allMobsAffectStealthVisibility = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Entity types that are NOT ignored by stealth targeting suppression.", impact = "Mobs listed here can still detect/target sneaking players with Silent Step.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Entity types that are NOT ignored by stealth targeting suppression.", impact = "Mobs listed here can still detect/target sneaking players with Silent Step.") List targetingBlacklistTypes = new ArrayList<>(List.of("WARDEN", "WITHER", "PHANTOM", "ENDER_DRAGON")); } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSnatch.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index 07fe18889..5926d0cb0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -16,15 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.stealth; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -41,6 +43,13 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.Inventories; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; + public class StealthSnatch extends SimpleAdaptation { private final Set holds; @@ -157,7 +166,7 @@ public void sendCollected(Player p, Item item) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); if (i.isSneaking()) { J.s(() -> snatch(i)); @@ -184,21 +193,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Snatch dropped items instantly while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Snatch Rate for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snatch Rate for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int snatchRate = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 5.55; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSpeed.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index fc903356d..3c4072f81 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -16,14 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.stealth; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Input; @@ -40,6 +42,11 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.math.VelocitySpeed; + public class StealthSpeed extends SimpleAdaptation { private static final Sound DEFAULT_ACTIVATION_SOUND = Sound.PARTICLE_SOUL_ESCAPE; private final Map states; @@ -86,7 +93,7 @@ public void onTick() { long now = System.currentTimeMillis(); long statIntervalMs = Math.max(50L, getConfig().statIntervalMs); - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); RuntimeState state = states.computeIfAbsent(p.getUniqueId(), key -> new RuntimeState()); @@ -390,69 +397,69 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain speed while sneaking.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update stealth speed.", impact = "Lower values feel more responsive but run updates more frequently.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update stealth speed.", impact = "Lower values feel more responsive but run updates more frequently.") long setInterval = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback baseline walk speed if no original speed has been captured yet.", impact = "Usually keep this at vanilla default unless another plugin changes baseline speeds globally.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback baseline walk speed if no original speed has been captured yet.", impact = "Usually keep this at vanilla default unless another plugin changes baseline speeds globally.") float baselineWalkSpeed = 0.2f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum additional walk speed granted at max level.", impact = "Higher values make stealth speed more noticeable.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum additional walk speed granted at max level.", impact = "Higher values make stealth speed more noticeable.") double maxSpeedBonus = 0.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Multiplier applied to bonus speed while crawling on land.", impact = "Higher values make crawling keep pace with sneaking.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to bonus speed while crawling on land.", impact = "Higher values make crawling keep pace with sneaking.") double crawlBonusMultiplier = 1.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum walk speed clamp used when applying the boost.", impact = "Keep near default to avoid unexpected slowdowns from conflicting systems.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum walk speed clamp used when applying the boost.", impact = "Keep near default to avoid unexpected slowdowns from conflicting systems.") float minWalkSpeed = -1f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum walk speed clamp used when applying the boost.", impact = "Lower values are safer for anticheat; higher values feel faster.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed clamp used when applying the boost.", impact = "Lower values are safer for anticheat; higher values feel faster.") float maxWalkSpeed = 1f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables automatic vertical stepping while stealth speed is active.", impact = "Helps smooth sneaking over one-block terrain changes.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables automatic vertical stepping while stealth speed is active.", impact = "Helps smooth sneaking over one-block terrain changes.") boolean enableAutoStep = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows stepping up one block while moving.", impact = "Reduces sneak interruption when encountering small ledges.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping up one block while moving.", impact = "Reduces sneak interruption when encountering small ledges.") boolean enableAutoStepUp = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows stepping down one block while moving.", impact = "Only steps down when the drop is exactly one block.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping down one block while moving.", impact = "Only steps down when the drop is exactly one block.") boolean enableAutoStepDown = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Forward probe distance for auto-step checks.", impact = "Higher values detect ledges earlier but can feel more aggressive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Forward probe distance for auto-step checks.", impact = "Higher values detect ledges earlier but can feel more aggressive.") double autoStepProbeDistance = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal push applied during each auto-step teleport.", impact = "Higher values move farther onto/off the next block and reduce repeat stepping in place.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal push applied during each auto-step teleport.", impact = "Higher values move farther onto/off the next block and reduce repeat stepping in place.") double autoStepForwardPush = 0.36; - @com.volmit.adapt.util.config.ConfigDoc(value = "Uses direct movement input for auto-step direction when available.", impact = "Helps auto-step trigger while pressing into obstacles, even when velocity is near zero.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses direct movement input for auto-step direction when available.", impact = "Helps auto-step trigger while pressing into obstacles, even when velocity is near zero.") boolean autoStepUseInput = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity required before auto-step runs.", impact = "Higher values avoid accidental stepping while nearly idle.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity required before auto-step runs.", impact = "Higher values avoid accidental stepping while nearly idle.") double autoStepVelocityThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum delay between auto-step teleports.", impact = "Higher values reduce repeated stepping in tight terrain.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum delay between auto-step teleports.", impact = "Higher values reduce repeated stepping in tight terrain.") long autoStepCooldownMs = 90; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum obstacle collision height that counts as a step-up blocker.", impact = "Higher values ignore small lips/slabs; lower values step up more aggressively.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum obstacle collision height that counts as a step-up blocker.", impact = "Higher values ignore small lips/slabs; lower values step up more aggressively.") double stepObstacleMinHeight = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Bounding-box height above which two-block headroom is required for step-up.", impact = "Lower values are stricter; higher values allow sneaking/crawling to step in tighter spaces.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Bounding-box height above which two-block headroom is required for step-up.", impact = "Lower values are stricter; higher values allow sneaking/crawling to step in tighter spaces.") double doubleHeadroomHeightThreshold = 1.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum bounding-box height counted as crawling on land.", impact = "Higher values make crawl detection more permissive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum bounding-box height counted as crawling on land.", impact = "Higher values make crawl detection more permissive.") double crawlHeightMax = 0.61; - @com.volmit.adapt.util.config.ConfigDoc(value = "Requires players to be grounded for stealth speed to run.", impact = "True avoids midair acceleration and keeps behavior stable.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires players to be grounded for stealth speed to run.", impact = "True avoids midair acceleration and keeps behavior stable.") boolean requireGrounded = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Allows stealth speed to run while the player is in water.", impact = "False prevents stealth from overriding seaborne-style underwater movement effects.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stealth speed to run while the player is in water.", impact = "False prevents stealth from overriding seaborne-style underwater movement effects.") boolean allowWhileInWater = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity used to count the player as moving for FX/stat tracking.", impact = "Higher values reduce effects while nearly stationary.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity used to count the player as moving for FX/stat tracking.", impact = "Higher values reduce effects while nearly stationary.") double movementVelocityThreshold = 0.005; - @com.volmit.adapt.util.config.ConfigDoc(value = "Shows a subtle soul particle near the player's feet while stealth speed is active.", impact = "Visual feedback visible only to the boosted player.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Shows a subtle soul particle near the player's feet while stealth speed is active.", impact = "Visual feedback visible only to the boosted player.") boolean showSoulParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Chance per tick to spawn a soul particle while moving.", impact = "Higher values make the effect denser; lower values are subtler.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance per tick to spawn a soul particle while moving.", impact = "Higher values make the effect denser; lower values are subtler.") double soulParticleChance = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Vertical offset for the soul particle effect.", impact = "Small positive values keep particles around floor level.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for the soul particle effect.", impact = "Small positive values keep particles around floor level.") double soulParticleYOffset = 0.02; - @com.volmit.adapt.util.config.ConfigDoc(value = "Activation sound volume heard by the boosted player.", impact = "Higher values are louder; lower values are subtler.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound volume heard by the boosted player.", impact = "Higher values are louder; lower values are subtler.") float activationSoundVolume = 1.6f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Activation sound pitch heard by the boosted player.", impact = "Higher values raise tone; lower values deepen it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound pitch heard by the boosted player.", impact = "Higher values raise tone; lower values deepen it.") float activationSoundPitch = 0.9f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum time between activation sounds.", impact = "Higher values reduce audio spam when repeatedly starting/stopping.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between activation sounds.", impact = "Higher values reduce audio spam when repeatedly starting/stopping.") long activationSoundCooldownMs = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Minimum time between progression stat increments while moving with stealth speed.", impact = "Controls how quickly the sneak-speed progression stat accumulates.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between progression stat increments while moving with stealth speed.", impact = "Controls how quickly the sneak-speed progression stat accumulates.") long statIntervalMs = 200; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/stealth/util/EntityListing.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java similarity index 97% rename from src/main/java/com/volmit/adapt/content/adaptation/stealth/util/EntityListing.java rename to src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java index 23e67d23c..a39101517 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/stealth/util/EntityListing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.stealth.util; +package art.arcane.adapt.content.adaptation.stealth.util; import lombok.Getter; import org.bukkit.entity.EntityType; diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsBloodyBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsBloodyBlade.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java index 751bc5488..089371c6f 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsBloodyBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.sword.effects.DamagingBleedEffect; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.sword; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -48,6 +48,8 @@ import java.util.Set; import java.util.UUID; +import art.arcane.adapt.util.reflect.registries.Particles; + public class SwordsBloodyBlade extends SimpleAdaptation { private final Map cooldowns; private final Set bleedingEntities = new HashSet<>(); @@ -177,25 +179,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sword strikes cause bleeding over time.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long cooldown = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Bleed Proc for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Bleed Proc for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double damagePerBleedProc = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long effectDuration = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Bloody Blade adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Bloody Blade adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.325; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java index bd96e5d6e..55d8b8c86 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.sword.effects.DamagingBleedEffect; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.sword; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -46,6 +46,8 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; +import art.arcane.adapt.util.reflect.registries.Particles; + public class SwordsCrimsonCyclone extends SimpleAdaptation { public SwordsCrimsonCyclone() { super("sword-crimson-cyclone"); @@ -269,51 +271,51 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Land a sword crit while falling to unleash a bleeding crimson cyclone around your target.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Bleed Particles for the Swords Crimson Cyclone adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Bleed Particles for the Swords Crimson Cyclone adaptation.", impact = "True enables this behavior and false disables it.") boolean showBleedParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.76; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 2.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 2.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDamage = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bleedTicksBase = 40; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bleedTicksFactor = 90; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bleedDamagePerProcBase = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bleedDamagePerProcFactor = 0.45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerCostBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hungerCostFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durabilityCostBase = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double durabilityCostFactor = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 320; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 160; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Swords Crimson Cyclone adaptation.", impact = "Minimum fall distance required to trigger the cyclone on hit.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Swords Crimson Cyclone adaptation.", impact = "Minimum fall distance required to trigger the cyclone on hit.") float minFallDistanceForCrit = 0.08f; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerTargetHit = 10; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsDualWield.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsDualWield.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java index f5e955d97..3f1c8ccb0 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsDualWield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; +package art.arcane.adapt.content.adaptation.sword; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -121,27 +121,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Wield a sword in both hands for increased damage output.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sameWeaponBase = 1.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sameWeaponFactor = 0.43; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mixedWeaponBase = 1.06; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mixedWeaponFactor = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDamage = 2.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsExecutionersEdge.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsExecutionersEdge.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java index f81c8b7b3..2cfebca37 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsExecutionersEdge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.sword; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.attribute.Attribute; @@ -163,29 +163,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sword strikes deal bonus damage to low-health targets.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusDamageBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusDamageFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Threshold Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double thresholdBase = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Threshold Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double thresholdFactor = 0.33; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Threshold for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Threshold for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxThreshold = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBuffedDamage = 1.9; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsMachete.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index c35a2bc8f..98352b775 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -16,16 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Materials; +package art.arcane.adapt.content.adaptation.sword; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.RNG; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.data.Cuboid; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Materials; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -42,6 +46,13 @@ import java.util.concurrent.ThreadLocalRandom; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.registries.Particles; + public class SwordsMachete extends SimpleAdaptation { public SwordsMachete() { super("sword-machete"); @@ -210,31 +221,31 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Cut through foliage with ease using a sword.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Machete adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Machete adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 2.36; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Slowest for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Slowest for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksSlowest = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double toolDamageBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Inverse Level Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Inverse Level Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double toolDamageInverseLevelFactor = 5; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsPoisonedBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsPoisonedBlade.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java index b62db8f7d..bfd96281e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsPoisonedBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.sword.effects.DamagingBleedEffect; -import com.volmit.adapt.content.item.ItemListings; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.sword; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; +import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -171,21 +171,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sword strikes apply poison.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long cooldown = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long effectDuration = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.325; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsRiposteWindow.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsRiposteWindow.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java index 66d2da26b..b59fb1a55 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/SwordsRiposteWindow.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.sword; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -45,6 +45,8 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.inventorygui.Window; + public class SwordsRiposteWindow extends SimpleAdaptation { private final Map riposteUntil = new HashMap<>(); @@ -191,27 +193,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Blocking with a shield arms a short riposte window for your next sword strike.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.71; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Window Millis Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windowMillisBase = 350; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Window Millis Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windowMillisFactor = 550; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusBase = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBonusFactor = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBuffedDamage = 1.8; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java rename to src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java index 441aab251..f78628d80 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.sword.effects; +package art.arcane.adapt.content.adaptation.sword.effects; -import com.volmit.adapt.util.J; +import art.arcane.adapt.util.common.scheduling.J; import de.slikey.effectlib.EffectManager; import de.slikey.effectlib.effect.BleedEffect; import org.bukkit.entity.LivingEntity; diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java similarity index 86% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingBeastRecall.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index b90076cfb..3f71e130e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.taming; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -194,29 +194,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sneak-right-click with a lead to recall your nearest tamed companion.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 38; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Recall Distance Squared for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Recall Distance Squared for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minRecallDistanceSquared = 9.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 420; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 280; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Recall for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Recall for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnRecall = 26; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingDamage.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingDamage.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java index 788fc0253..b3943e928 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingDamage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Attributes; +package art.arcane.adapt.content.adaptation.taming; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -46,6 +48,10 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; + public class TamingDamage extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-damage-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-damage-boost"); @@ -142,21 +148,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Increase your tamed animal damage dealt.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDamage = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 0.65; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthBoost.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java index 6d7068d97..b0b0ba4b4 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Attributes; +package art.arcane.adapt.content.adaptation.taming; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -42,6 +44,10 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; + public class TamingHealthBoost extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-health-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-health-boost"); @@ -126,21 +132,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Increase your tamed animal maximum health.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Health Boost Factor for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Factor for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double healthBoostFactor = 2.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Health Boost Base for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Base for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double healthBoostBase = 0.57; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthRegeneration.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java similarity index 82% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthRegeneration.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java index a4454876c..3591e2b27 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingHealthRegeneration.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java @@ -16,18 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Attributes; +package art.arcane.adapt.content.adaptation.taming; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -42,6 +44,11 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.reflect.registries.Particles; + import static org.bukkit.Particle.HEART; public class TamingHealthRegeneration extends SimpleAdaptation { @@ -136,23 +143,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Increase your tamed animal regeneration rate.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Taming Health Regeneration adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Taming Health Regeneration adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Factor for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Factor for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenFactor = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Regen Base for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Base for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double regenBase = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingMountedTactics.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingMountedTactics.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java index 1210c51da..f625ed161 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingMountedTactics.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; +package art.arcane.adapt.content.adaptation.taming; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -90,7 +90,7 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p)) { continue; @@ -239,61 +239,61 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Gain mount-specific combat and control bonuses while riding.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mountedDamageBonusBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mountedDamageBonusFactor = 0.22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Bonus for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Bonus for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxMountedDamageBonus = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mountedDamageReductionBase = 0.06; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double mountedDamageReductionFactor = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Reduction for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Reduction for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxMountedDamageReduction = 0.28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double horseSpeedAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double horseSpeedAmplifierFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double striderSpeedAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double striderSpeedAmplifierFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pigResistanceAmplifierBase = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pigResistanceAmplifierFactor = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for horse mounted speed scaling.", impact = "Higher values increase steady mounted horse acceleration when moving forward.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for horse mounted speed scaling.", impact = "Higher values increase steady mounted horse acceleration when moving forward.") double horseBaseHorizontalSpeed = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for strider mounted speed scaling.", impact = "Higher values increase steady mounted strider acceleration when moving forward.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for strider mounted speed scaling.", impact = "Higher values increase steady mounted strider acceleration when moving forward.") double striderBaseHorizontalSpeed = 0.24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force on mounts.", impact = "Acts as a hard cap to prevent runaway mounted momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force on mounts.", impact = "Acts as a hard cap to prevent runaway mounted momentum.") double mountMaxHorizontalSpeed = 0.78; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast mounts accelerate toward the target speed per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast mounts accelerate toward the target speed per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double mountAccelPerTick = 0.065; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Horse Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double horsePushBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Horse Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double horsePushFactor = 0.16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pig Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pigPushBase = 0.05; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Pig Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double pigPushFactor = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mounted Damage for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mounted Damage for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerMountedDamage = 1.5; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingPackLeaderAura.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index 7be2714a4..e8b066081 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.taming; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -135,25 +135,25 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Nearby tamed companions gain speed and regeneration near their owner.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusBase = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double radiusFactor = 14; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxAmplifier = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int effectTicks = 80; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingSharedPain.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/taming/TamingSharedPain.java rename to src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java index 1c887df6c..9a9b6fda6 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/taming/TamingSharedPain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.taming; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.taming; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -138,29 +138,29 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Redirect part of your pet's incoming damage to you, preserving companion survivability.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double redirectPercentBase = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double redirectPercentFactor = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Redirect Percent for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Redirect Percent for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxRedirectPercent = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double ownerHealthFloorBase = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double ownerHealthFloorFactor = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Redirected Damage for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Redirected Damage for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerRedirectedDamage = 2.0; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBloodPact.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBloodPact.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java index ebcc19b60..895050979 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBloodPact.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -261,7 +261,7 @@ public void onTick() { } private void applySpeedBursts(long now) { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); UUID id = p.getUniqueId(); SpeedBurst burst = speedBursts.get(id); @@ -375,57 +375,57 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Taking at least 2 hearts of damage can trigger temporary beneficial effects.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.62; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Damage Trigger Hearts for the Tragoul Blood Pact adaptation.", impact = "Minimum damage taken in hearts required before the proc roll happens.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Damage Trigger Hearts for the Tragoul Blood Pact adaptation.", impact = "Minimum damage taken in hearts required before the proc roll happens.") double minDamageTriggerHearts = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procChanceBase = 0.12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procChanceFactor = 0.38; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Tragoul Blood Pact adaptation.", impact = "Caps chance at the requested maximum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Tragoul Blood Pact adaptation.", impact = "Caps chance at the requested maximum.") double maxProcChance = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procCooldownMillisBase = 18000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double procCooldownMillisFactor = 12000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double effectDurationTicksBase = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double effectDurationTicksFactor = 150; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Buff Count Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double buffCountBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Buff Count Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double buffCountFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusBuffChanceBase = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusBuffChanceFactor = 0.34; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP Per Proc for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Proc for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerProc = 24; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for blood pact speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for blood pact speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") double baseHorizontalSpeed = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") double maxHorizontalSpeed = 0.33; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double accelPerTick = 0.045; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") double brakePerTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") double stopThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") boolean hardStopOnInvalidState = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java index d55582d1b..351e9d91e 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.VelocitySpeed; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -242,7 +242,7 @@ private int getGlobeLifetimeTicks(int level) { @Override public void onTick() { long now = System.currentTimeMillis(); - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); UUID id = p.getUniqueId(); SpeedBurst burst = speedBursts.get(id); @@ -356,55 +356,55 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Kills can spawn temporary blood and bone globes that grant short buffs when picked up.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double globeChanceBase = 0.16; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double globeChanceFactor = 0.42; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Globe Chance for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Globe Chance for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxGlobeChance = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double globeLifetimeTicksBase = 120; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double globeLifetimeTicksFactor = 220; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int bloodBuffTicks = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int bloodBuffAmplifier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int boneBuffTicks = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int boneBuffAmplifier = 0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double boneBuffCountBase = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double boneBuffCountFactor = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Globe Spawned for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Globe Spawned for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerGlobeSpawned = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for bone-harvest speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for bone-harvest speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") double baseHorizontalSpeed = 0.13; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") double maxHorizontalSpeed = 0.33; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") double accelPerTick = 0.045; - @com.volmit.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") double brakePerTick = 0.08; - @com.volmit.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") double stopThreshold = 0.01; - @com.volmit.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") boolean hardStopOnInvalidState = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; double fallbackInputVelocityThresholdSquared() { diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulGlobe.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java similarity index 84% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulGlobe.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java index c06fb0cf0..36dbc376c 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulGlobe.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -42,6 +42,8 @@ import java.util.HashMap; import java.util.Map; +import art.arcane.adapt.util.reflect.registries.Particles; + public class TragoulGlobe extends SimpleAdaptation { private final Map cooldowns; @@ -148,27 +150,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Spread your damage among all nearby enemies.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Globe adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Globe adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldown = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Range Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double rangePerLevel = 3.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Inital Range for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Inital Range for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double initalRange = 5.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bonusDamagePerLevel = 1; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulHealing.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulHealing.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java index d85c1157b..110e40868 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulHealing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; -import com.volmit.adapt.util.reflect.registries.Attributes; +package art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -42,6 +42,9 @@ import java.util.Map; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.reflect.registries.Particles; + public class TragoulHealing extends SimpleAdaptation { private final Map cooldowns; private final Map healingWindow; @@ -143,27 +146,27 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Regain health based on the damage you deal.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Healing adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Healing adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Min Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minHealPercent = 0.10; // 0.10% - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHealPercent = 0.45; // 0.45% - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int cooldownDuration = 1000; // 1 second - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Window Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int windowDuration = 3000; // 3 seconds } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulLance.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulLance.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java index f9e58e38a..65536509b 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulLance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.content.adaptation.tragoul;/*------------------------------------------------------------------------------ +package art.arcane.adapt.content.adaptation.tragoul;/*------------------------------------------------------------------------------ - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - Copyright (c) 2022 Arcane Arts (Volmit Software) - @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Color; @@ -160,23 +160,23 @@ public void addStats(int level, Element v) { @NoArgsConstructor @ConfigDescription("Killing an enemy spawns a lance that seeks and damages a nearby enemy.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Seeker Delay for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Delay for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int seekerDelay = 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Seeker Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double seekerDamageMultiplier = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Self Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Self Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double selfDamageMultiplier = 0.5; } } \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulThorns.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulThorns.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java index 1760adad8..4d7bbb3e3 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/TragoulThorns.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -42,6 +42,9 @@ import java.util.Map; +import art.arcane.adapt.util.reflect.Reflect; +import art.arcane.adapt.util.reflect.registries.Particles; + public class TragoulThorns extends SimpleAdaptation { private final Map cooldowns; @@ -149,21 +152,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Reflect damage back to your attacker.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Thorns adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Thorns adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Multiplier Per Level for the Tragoul Thorns adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Multiplier Per Level for the Tragoul Thorns adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageMultiplierPerLevel = 1.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.72; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/utils/EntityThings.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java similarity index 97% rename from src/main/java/com/volmit/adapt/content/adaptation/tragoul/utils/EntityThings.java rename to src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java index dec9b6337..e94d2dc28 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/tragoul/utils/EntityThings.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.tragoul.utils; +package art.arcane.adapt.content.adaptation.tragoul.utils; import org.bukkit.Location; import org.bukkit.entity.Entity; diff --git a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java similarity index 88% rename from src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java rename to src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index 8a1c72a24..3d2f7efc7 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.unarmed; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -201,7 +201,7 @@ private int getCooldownTicks(int level) { @Override public void onTick() { - for (com.volmit.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); if (!hasAdaptation(p)) { primedState.remove(p.getUniqueId()); @@ -240,33 +240,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sprint into enemies with fists or a shield to deal impact damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageBase = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 4.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackBase = 0.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double knockbackFactor = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksBase = 80; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double cooldownTicksFactor = 50; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Velocity Squared for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Velocity Squared for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumVelocitySquared = 0.18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDamage = 3.3; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedComboChain.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedComboChain.java rename to src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java index 634660e13..fab255f06 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedComboChain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java @@ -16,20 +16,20 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.unarmed; - -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -47,6 +47,8 @@ import java.util.Map; import java.util.UUID; +import art.arcane.adapt.util.common.inventorygui.Window; + public class UnarmedComboChain extends SimpleAdaptation { private final Map combos = new HashMap<>(); @@ -207,33 +209,33 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Consecutive unarmed hits build combo stacks for increased punch damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxStacksBase = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxStacksFactor = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerStackBase = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagePerStackFactor = 0.85; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double comboWindowMillisBase = 1300; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double comboWindowMillisFactor = 1400; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Miss Reset Grace Millis for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Miss Reset Grace Millis for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long missResetGraceMillis = 280; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerBonusDamage = 4.1; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java rename to src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java index f49ea776d..e5cbdc038 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.unarmed; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -135,23 +135,23 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Bonus unarmed damage the lower your armor value is.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.425; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Per Level Bonus Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Per Level Bonus Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double perLevelBonusMultiplier = 0.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Damage Factor for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Factor for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxDamageFactor = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Damage Per Level Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Per Level Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxDamagePerLevelMultiplier = 0.15; } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedPower.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedPower.java rename to src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java index 22898771b..3e3b80442 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedPower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.unarmed; +package art.arcane.adapt.content.adaptation.unarmed; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.config.ConfigDescription; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -134,19 +134,19 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Improved base unarmed damage.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") int maxLevel = 7; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.425; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Power adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Power adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 2.57; } } diff --git a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java similarity index 85% rename from src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java rename to src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java index 5d31739b4..131d02209 100644 --- a/src/main/java/com/volmit/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java @@ -16,15 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.adaptation.unarmed; - -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.config.ConfigDescription; +package art.arcane.adapt.content.adaptation.unarmed; +import art.arcane.volmlib.util.format.Form; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -36,6 +38,12 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; + public class UnarmedSuckerPunch extends SimpleAdaptation { public UnarmedSuckerPunch() { super("unarmed-sucker-punch"); @@ -158,21 +166,21 @@ public boolean isPermanent() { @NoArgsConstructor @ConfigDescription("Sprint punches deal extra damage based on your speed.") protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Unarmed Sucker Punch adaptation.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Unarmed Sucker Punch adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") int initialCost = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseDamage = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageFactor = 0.55; } } diff --git a/src/main/java/com/volmit/adapt/content/block/ScaffoldMatter.java b/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java similarity index 98% rename from src/main/java/com/volmit/adapt/content/block/ScaffoldMatter.java rename to src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java index 4f3a15188..10ba635de 100644 --- a/src/main/java/com/volmit/adapt/content/block/ScaffoldMatter.java +++ b/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.block; +package art.arcane.adapt.content.block; import art.arcane.spatial.matter.slices.RawMatter; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java similarity index 87% rename from src/main/java/com/volmit/adapt/content/event/AdaptAdaptationEvent.java rename to src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java index 11ed5b17f..3386f8583 100644 --- a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.event; +package art.arcane.adapt.content.event; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerSkillLine; public class AdaptAdaptationEvent extends AdaptPlayerEvent { private final Skill skill; diff --git a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationTeleportEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/event/AdaptAdaptationTeleportEvent.java rename to src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java index 917639d99..c43a3f3e2 100644 --- a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationTeleportEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.event; +package art.arcane.adapt.content.event; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.world.AdaptPlayer; import lombok.Getter; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationUseEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/event/AdaptAdaptationUseEvent.java rename to src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java index 87452030c..5a4221dde 100644 --- a/src/main/java/com/volmit/adapt/content/event/AdaptAdaptationUseEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.event; +package art.arcane.adapt.content.event; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.world.AdaptPlayer; public class AdaptAdaptationUseEvent extends AdaptAdaptationEvent { public AdaptAdaptationUseEvent(boolean async, AdaptPlayer player, Adaptation adaptation) { diff --git a/src/main/java/com/volmit/adapt/content/event/AdaptEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/event/AdaptEvent.java rename to src/main/java/art/arcane/adapt/content/event/AdaptEvent.java index 72919a140..28c2c0709 100644 --- a/src/main/java/com/volmit/adapt/content/event/AdaptEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java @@ -16,10 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.event; +package art.arcane.adapt.content.event; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.world.AdaptServer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.world.AdaptServer; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/volmit/adapt/content/event/AdaptPlayerEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/event/AdaptPlayerEvent.java rename to src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java index d7b35bf1b..42e75eef2 100644 --- a/src/main/java/com/volmit/adapt/content/event/AdaptPlayerEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.event; +package art.arcane.adapt.content.event; -import com.volmit.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptPlayer; public class AdaptPlayerEvent extends AdaptEvent { private final AdaptPlayer player; diff --git a/src/main/java/com/volmit/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java similarity index 98% rename from src/main/java/com/volmit/adapt/content/gui/ConfigGui.java rename to src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index 99334c04c..9ed2aa06a 100644 --- a/src/main/java/com/volmit/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -1,28 +1,28 @@ -package com.volmit.adapt.content.gui; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.service.ConfigInputSVC; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Element; -import com.volmit.adapt.util.GuiEffects; -import com.volmit.adapt.util.GuiLayout; -import com.volmit.adapt.util.GuiTheme; -import com.volmit.adapt.util.IO; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.MaterialBlock; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.UIElement; -import com.volmit.adapt.util.UIWindow; -import com.volmit.adapt.util.Window; -import com.volmit.adapt.util.config.ConfigDocumentation; -import com.volmit.adapt.util.config.TomlCodec; +package art.arcane.adapt.content.gui; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.service.ConfigInputSVC; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.inventorygui.UIElement; +import art.arcane.adapt.util.common.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.config.ConfigDocumentation; +import art.arcane.adapt.util.config.TomlCodec; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java similarity index 89% rename from src/main/java/com/volmit/adapt/content/gui/SkillsGui.java rename to src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index f548b2b43..705b1977c 100644 --- a/src/main/java/com/volmit/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -16,15 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.gui; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.api.xp.XP; -import com.volmit.adapt.util.*; +package art.arcane.adapt.content.gui; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.api.xp.XP; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -34,6 +35,18 @@ import java.util.List; import java.util.Locale; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.adapt.util.common.inventorygui.UIElement; +import art.arcane.adapt.util.common.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.scheduling.J; + public class SkillsGui { private static final int PAGE_JUMP = 5; diff --git a/src/main/java/com/volmit/adapt/content/item/BoundEnderPearl.java b/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/item/BoundEnderPearl.java rename to src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java index 0553838b3..b8e053ee8 100644 --- a/src/main/java/com/volmit/adapt/content/item/BoundEnderPearl.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/BoundEyeOfEnder.java b/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/item/BoundEyeOfEnder.java rename to src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java index 33d11589e..a8a6526ae 100644 --- a/src/main/java/com/volmit/adapt/content/item/BoundEyeOfEnder.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/content/item/BoundRedstoneTorch.java b/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/item/BoundRedstoneTorch.java rename to src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java index a3df6f2bc..c8546992b 100644 --- a/src/main/java/com/volmit/adapt/content/item/BoundRedstoneTorch.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/content/item/BoundSnowBall.java b/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/item/BoundSnowBall.java rename to src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java index 276db93e5..de2b91e1f 100644 --- a/src/main/java/com/volmit/adapt/content/item/BoundSnowBall.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/ChronoTimeBombItem.java b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/item/ChronoTimeBombItem.java rename to src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java index aec278856..248b3ef07 100644 --- a/src/main/java/com/volmit/adapt/content/item/ChronoTimeBombItem.java +++ b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java @@ -16,13 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.ItemFlags; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.ItemFlags; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/ChronoTimeBottle.java b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/item/ChronoTimeBottle.java rename to src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java index 4b8968adb..aa538b912 100644 --- a/src/main/java/com/volmit/adapt/content/item/ChronoTimeBottle.java +++ b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java @@ -16,13 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.ItemFlags; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.ItemFlags; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Color; diff --git a/src/main/java/com/volmit/adapt/content/item/ExperienceOrb.java b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/item/ExperienceOrb.java rename to src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java index a90879b30..b85c9af70 100644 --- a/src/main/java/com/volmit/adapt/content/item/ExperienceOrb.java +++ b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java @@ -16,14 +16,14 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.Localizer; +package art.arcane.adapt.content.item; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/ItemListings.java b/src/main/java/art/arcane/adapt/content/item/ItemListings.java similarity index 99% rename from src/main/java/com/volmit/adapt/content/item/ItemListings.java rename to src/main/java/art/arcane/adapt/content/item/ItemListings.java index 223ddead5..7d6cb2355 100644 --- a/src/main/java/com/volmit/adapt/content/item/ItemListings.java +++ b/src/main/java/art/arcane/adapt/content/item/ItemListings.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.reflect.registries.Materials; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.reflect.registries.Materials; import lombok.Getter; import org.bukkit.ChatColor; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/KnowledgeOrb.java b/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java similarity index 95% rename from src/main/java/com/volmit/adapt/content/item/KnowledgeOrb.java rename to src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java index ba65f5ed6..6ab493b59 100644 --- a/src/main/java/com/volmit/adapt/content/item/KnowledgeOrb.java +++ b/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item; +package art.arcane.adapt.content.item; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.item.DataItem; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.item.DataItem; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/content/item/multiItems/MultiArmor.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/item/multiItems/MultiArmor.java rename to src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java index d3252c92d..0f5117dc7 100644 --- a/src/main/java/com/volmit/adapt/content/item/multiItems/MultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item.multiItems; +package art.arcane.adapt.content.item.multiItems; -import com.volmit.adapt.util.Form; +import art.arcane.volmlib.util.format.Form; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.inventorygui.Items; + public class MultiArmor implements MultiItem { @Override public boolean supportsItem(ItemStack itemStack) { diff --git a/src/main/java/com/volmit/adapt/content/item/multiItems/MultiItem.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java similarity index 95% rename from src/main/java/com/volmit/adapt/content/item/multiItems/MultiItem.java rename to src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java index 3a1ff1372..473927477 100644 --- a/src/main/java/com/volmit/adapt/content/item/multiItems/MultiItem.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java @@ -16,13 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item.multiItems; +package art.arcane.adapt.content.item.multiItems; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.nms.NMS; -import com.volmit.adapt.util.BukkitGson; -import com.volmit.adapt.util.WindowResolution; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.nms.NMS; +import art.arcane.adapt.util.common.io.BukkitGson; +import art.arcane.adapt.util.common.inventorygui.WindowResolution; +import art.arcane.volmlib.util.collection.KList; import lombok.*; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/com/volmit/adapt/content/item/multiItems/OmniTool.java b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java similarity index 96% rename from src/main/java/com/volmit/adapt/content/item/multiItems/OmniTool.java rename to src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java index 7d995101f..1b5d1639f 100644 --- a/src/main/java/com/volmit/adapt/content/item/multiItems/OmniTool.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.item.multiItems; +package art.arcane.adapt.content.item.multiItems; -import com.volmit.adapt.util.Form; +import art.arcane.volmlib.util.format.Form; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.inventorygui.Items; + public class OmniTool implements MultiItem { @Override public boolean supportsItem(ItemStack itemStack) { diff --git a/src/main/java/com/volmit/adapt/content/matter/BrewingStandOwner.java b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java similarity index 96% rename from src/main/java/com/volmit/adapt/content/matter/BrewingStandOwner.java rename to src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java index 166448632..cea0e84d8 100644 --- a/src/main/java/com/volmit/adapt/content/matter/BrewingStandOwner.java +++ b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.matter; +package art.arcane.adapt.content.matter; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/content/matter/BrewingStandOwnerMatter.java b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java similarity index 97% rename from src/main/java/com/volmit/adapt/content/matter/BrewingStandOwnerMatter.java rename to src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java index 291b5c898..5631433db 100644 --- a/src/main/java/com/volmit/adapt/content/matter/BrewingStandOwnerMatter.java +++ b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.matter; +package art.arcane.adapt.content.matter; import art.arcane.spatial.matter.slices.RawMatter; diff --git a/src/main/java/com/volmit/adapt/content/protector/ChestProtectProtector.java b/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java similarity index 83% rename from src/main/java/com/volmit/adapt/content/protector/ChestProtectProtector.java rename to src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java index 01baed950..5c83ec12d 100644 --- a/src/main/java/com/volmit/adapt/content/protector/ChestProtectProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import me.angeschossen.chestprotect.api.addons.ChestProtectAddon; import me.angeschossen.chestprotect.api.protection.block.BlockProtection; import org.bukkit.Location; diff --git a/src/main/java/com/volmit/adapt/content/protector/FactionsClaimProtector.java b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/protector/FactionsClaimProtector.java rename to src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java index 8fb6fd381..bc986db33 100644 --- a/src/main/java/com/volmit/adapt/content/protector/FactionsClaimProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java @@ -16,12 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; import com.massivecraft.factions.*; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/content/protector/GriefDefenderProtector.java b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/protector/GriefDefenderProtector.java rename to src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java index 9d3c2e32a..82b7d5ced 100644 --- a/src/main/java/com/volmit/adapt/content/protector/GriefDefenderProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java @@ -16,13 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; import com.griefdefender.api.GriefDefender; import com.griefdefender.api.claim.Claim; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/content/protector/GriefPreventionProtector.java b/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/protector/GriefPreventionProtector.java rename to src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java index 66181895d..6308ac395 100644 --- a/src/main/java/com/volmit/adapt/content/protector/GriefPreventionProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.ClaimPermission; import me.ryanhamshire.GriefPrevention.GriefPrevention; diff --git a/src/main/java/com/volmit/adapt/content/protector/LocketteProProtector.java b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java similarity index 77% rename from src/main/java/com/volmit/adapt/content/protector/LocketteProProtector.java rename to src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java index f0613c310..62fe05236 100644 --- a/src/main/java/com/volmit/adapt/content/protector/LocketteProProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import me.angeschossen.chestprotect.api.addons.ChestProtectAddon; import me.angeschossen.chestprotect.api.protection.block.BlockProtection; import me.crafter.mc.lockettepro.LocketteProAPI; diff --git a/src/main/java/com/volmit/adapt/content/protector/ResidenceProtector.java b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/protector/ResidenceProtector.java rename to src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java index 02af9090f..e45c88d59 100644 --- a/src/main/java/com/volmit/adapt/content/protector/ResidenceProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java @@ -1,93 +1,93 @@ -package com.volmit.adapt.content.protector; - -import com.bekvon.bukkit.residence.Residence; -import com.bekvon.bukkit.residence.containers.Flags; -import com.bekvon.bukkit.residence.protection.ClaimedResidence; -import com.bekvon.bukkit.residence.protection.FlagPermissions; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; -import com.volmit.adapt.util.J; -import org.bukkit.Location; -import org.bukkit.entity.Player; - -import java.util.concurrent.atomic.AtomicBoolean; - -public class ResidenceProtector implements Protector { - - public ResidenceProtector() { - FlagPermissions.addFlag("use-adaptations"); - } - - @Override - public boolean checkRegion(Player player, Location location, Adaptation adaptation) { - return checkPerm(player, location, "use-adaptations"); - } - - @Override - public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.destroy); - } - - @Override - public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.place); - } - - @Override - public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.pvp); - } - - @Override - public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.damage); - } - - @Override - public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { - return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.use); - } - - @Override - public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { - return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.container); - } - - private boolean checkPerm(Player player, Location location, Flags flag) { - AtomicBoolean perm = new AtomicBoolean(true); - J.s(() -> { - if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); - if (res != null) { - perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); - } - } - }); - return perm.get(); - } - - private boolean checkPerm(Player player, Location location, String flag) { - AtomicBoolean perm = new AtomicBoolean(true); - J.s(() -> { - if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); - if (res != null) { - perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); - } - } - }); - return perm.get(); - } - - @Override - public String getName() { - return "Residence"; - } - - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isResidence(); - } - -} +package art.arcane.adapt.content.protector; + +import com.bekvon.bukkit.residence.Residence; +import com.bekvon.bukkit.residence.containers.Flags; +import com.bekvon.bukkit.residence.protection.ClaimedResidence; +import com.bekvon.bukkit.residence.protection.FlagPermissions; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; +import art.arcane.adapt.util.common.scheduling.J; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ResidenceProtector implements Protector { + + public ResidenceProtector() { + FlagPermissions.addFlag("use-adaptations"); + } + + @Override + public boolean checkRegion(Player player, Location location, Adaptation adaptation) { + return checkPerm(player, location, "use-adaptations"); + } + + @Override + public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.destroy); + } + + @Override + public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.place); + } + + @Override + public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.pvp); + } + + @Override + public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.damage); + } + + @Override + public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { + return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.use); + } + + @Override + public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { + return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.container); + } + + private boolean checkPerm(Player player, Location location, Flags flag) { + AtomicBoolean perm = new AtomicBoolean(true); + J.s(() -> { + if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { + ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); + if (res != null) { + perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); + } + } + }); + return perm.get(); + } + + private boolean checkPerm(Player player, Location location, String flag) { + AtomicBoolean perm = new AtomicBoolean(true); + J.s(() -> { + if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { + ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); + if (res != null) { + perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); + } + } + }); + return perm.get(); + } + + @Override + public String getName() { + return "Residence"; + } + + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isResidence(); + } + +} diff --git a/src/main/java/com/volmit/adapt/content/protector/WorldGuardProtector.java b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/protector/WorldGuardProtector.java rename to src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java index 2d70e0538..67d92bd85 100644 --- a/src/main/java/com/volmit/adapt/content/protector/WorldGuardProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.protector; +package art.arcane.adapt.content.protector; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldguard.LocalPlayer; @@ -28,10 +28,10 @@ import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; import com.sk89q.worldguard.protection.regions.RegionContainer; import com.sk89q.worldguard.protection.regions.RegionQuery; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.protection.Protector; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillAgility.java b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillAgility.java rename to src/main/java/art/arcane/adapt/content/skill/SkillAgility.java index d0ca8fa82..4a6fec166 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillAgility.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java @@ -16,24 +16,24 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.agility.AgilityArmorUp; -import com.volmit.adapt.content.adaptation.agility.AgilityLadderSlide; -import com.volmit.adapt.content.adaptation.agility.AgilityParkourMomentum; -import com.volmit.adapt.content.adaptation.agility.AgilityRollLanding; -import com.volmit.adapt.content.adaptation.agility.AgilitySuperJump; -import com.volmit.adapt.content.adaptation.agility.AgilityWallJump; -import com.volmit.adapt.content.adaptation.agility.AgilityWindUp; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.agility.AgilityArmorUp; +import art.arcane.adapt.content.adaptation.agility.AgilityLadderSlide; +import art.arcane.adapt.content.adaptation.agility.AgilityParkourMomentum; +import art.arcane.adapt.content.adaptation.agility.AgilityRollLanding; +import art.arcane.adapt.content.adaptation.agility.AgilitySuperJump; +import art.arcane.adapt.content.adaptation.agility.AgilityWallJump; +import art.arcane.adapt.content.adaptation.agility.AgilityWindUp; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -259,23 +259,23 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Move1k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Move1k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeMove1kReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint5k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint5k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSprint5kReward = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint Marathon Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint Marathon Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSprintMarathonReward = 6500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sprint Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sprint Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sprintXpPassive = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Swim Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double swimXpPassive = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Jump Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpXpPassive = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Climb Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Climb Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double climbXpPassive = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Move Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Move Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double moveXpPassive = 0.05; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/skill/SkillArchitect.java rename to src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index c5ee9cafc..aada1d940 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.architect.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.architect.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -249,15 +249,15 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Place1k Reward for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Place1k Reward for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengePlace1kReward = 1750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Value Multiplier for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Value Multiplier for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpValueMultiplier = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp Base for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Base for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpBase = 3; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillAxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/skill/SkillAxes.java rename to src/main/java/art/arcane/adapt/content/skill/SkillAxes.java index fe586bf0d..3de9d530d 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillAxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.axe.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.axe.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -276,26 +276,26 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Axes skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Axes skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHardnessBonus = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBlastResistanceBonus = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Chop Reward for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chop Reward for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeChopReward = 1750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Log Or Wood XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Log Or Wood XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double logOrWoodXPMultiplier = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Leaves Multiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaves Multiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double leavesMultiplier = 0.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valueXPMultiplier = 0.175; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double axeDamageXPMultiplier = 7.0; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillBlocking.java b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillBlocking.java rename to src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java index 0806701e8..7fdc1253e 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillBlocking.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java @@ -16,26 +16,26 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.blocking.BlockingChainArmorer; -import com.volmit.adapt.content.adaptation.blocking.BlockingBastionStance; -import com.volmit.adapt.content.adaptation.blocking.BlockingBulwarkBash; -import com.volmit.adapt.content.adaptation.blocking.BlockingCounterGuard; -import com.volmit.adapt.content.adaptation.blocking.BlockingHorseArmorer; -import com.volmit.adapt.content.adaptation.blocking.BlockingMirrorBlock; -import com.volmit.adapt.content.adaptation.blocking.BlockingMultiArmor; -import com.volmit.adapt.content.adaptation.blocking.BlockingSaddlecrafter; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.blocking.BlockingChainArmorer; +import art.arcane.adapt.content.adaptation.blocking.BlockingBastionStance; +import art.arcane.adapt.content.adaptation.blocking.BlockingBulwarkBash; +import art.arcane.adapt.content.adaptation.blocking.BlockingCounterGuard; +import art.arcane.adapt.content.adaptation.blocking.BlockingHorseArmorer; +import art.arcane.adapt.content.adaptation.blocking.BlockingMirrorBlock; +import art.arcane.adapt.content.adaptation.blocking.BlockingMultiArmor; +import art.arcane.adapt.content.adaptation.blocking.BlockingSaddlecrafter; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -262,17 +262,17 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Xp On Blocked Attack for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Blocked Attack for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnBlockedAttack = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Block1k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block1k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeBlock1kReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Block5k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block5k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeBlock5kReward = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Passive Xp For Using Shield for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Xp For Using Shield for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long passiveXpForUsingShield = 0; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillBrewing.java b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/skill/SkillBrewing.java rename to src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java index 2c41fa9e2..682cd568f 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillBrewing.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; import art.arcane.spatial.matter.SpatialMatter; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.data.WorldData; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.brewing.*; -import com.volmit.adapt.content.matter.BrewingStandOwner; -import com.volmit.adapt.content.matter.BrewingStandOwnerMatter; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.brewing.*; +import art.arcane.adapt.content.matter.BrewingStandOwner; +import art.arcane.adapt.content.matter.BrewingStandOwnerMatter; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -338,17 +338,17 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeBrew1k = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew Splash1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew Splash1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeBrewSplash1k = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Splash XP for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash XP for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double splashXP = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 2500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Splash Multiplier for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash Multiplier for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double splashMultiplier = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillChronos.java rename to src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index 84ea9d870..5e74f323a 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.chronos.ChronosAberrantTouch; -import com.volmit.adapt.content.adaptation.chronos.ChronosInstantRecall; -import com.volmit.adapt.content.adaptation.chronos.ChronosTemporalEcho; -import com.volmit.adapt.content.adaptation.chronos.ChronosTimeBomb; -import com.volmit.adapt.content.adaptation.chronos.ChronosTimeInABottle; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +package art.arcane.adapt.content.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.chronos.ChronosAberrantTouch; +import art.arcane.adapt.content.adaptation.chronos.ChronosInstantRecall; +import art.arcane.adapt.content.adaptation.chronos.ChronosTemporalEcho; +import art.arcane.adapt.content.adaptation.chronos.ChronosTimeBomb; +import art.arcane.adapt.content.adaptation.chronos.ChronosTimeInABottle; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -60,6 +60,8 @@ import java.util.Set; import java.util.UUID; +import art.arcane.adapt.util.common.inventorygui.Window; + public class SkillChronos extends SimpleSkill { private final Map lastPositions; private final Map> positionHistory; @@ -551,81 +553,81 @@ private static class SpeedPotionTracker { @NoArgsConstructor protected static class Config { // Existing - @com.volmit.adapt.util.config.ConfigDoc(value = "Tick interval used by this logic.", impact = "Lower values run logic more often; higher values run it less often.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval used by this logic.", impact = "Lower values run logic more often; higher values run it less often.") long setInterval = 5050; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Minimum Movement For Active Check for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Movement For Active Check for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumMovementForActiveCheck = 0.35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Distance Per Bonus XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Per Bonus XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double distancePerBonusXP = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Active Movement XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double activeMovementXP = 3.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Active Movement XPCap Per Tick for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XPCap Per Tick for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double activeMovementXPCapPerTick = 6; // Anti-AFK - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Position History Size for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Position History Size for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int positionHistorySize = 12; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Afk Variance Threshold for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Variance Threshold for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double afkVarianceThreshold = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Afk Min Action Types for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Min Action Types for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int afkMinActionTypes = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Afk Penalty Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Penalty Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double afkPenaltyMultiplier = 0.03; // Passive active XP - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Passive Active XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Active XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double passiveActiveXP = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Activity Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long activityWindow = 15000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Activity Types For Bonus for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Types For Bonus for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int activityTypesForBonus = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Activity Bonus Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Bonus Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double activityBonusMultiplier = 1.5; // Night bonus - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Night Activity Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Night Activity Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double nightActivityMultiplier = 1.3; // Sleep - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sleep Skip XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Skip XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sleepSkipXP = 150; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sleep Attempt XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Attempt XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sleepAttemptXP = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sleep Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long sleepCooldown = 30000; // Speed potion - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Base XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Base XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedPotionBaseXP = 45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Level Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Level Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedPotionLevelMultiplier = 1.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Decay for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Decay for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedPotionDiminishingDecay = 0.15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Floor for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Floor for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double speedPotionDiminishingFloor = 0.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Reset Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Reset Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long speedPotionResetWindow = 300000; // Ender pearl - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Throw XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Throw XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double enderPearlThrowXP = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Teleport XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Teleport XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double enderPearlTeleportXP = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long enderPearlCooldown = 10000; // Survival streak - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Survival XPPer Minute for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival XPPer Minute for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double survivalXPPerMinute = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Bonus Per Hour for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Bonus Per Hour for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double survivalStreakBonusPerHour = 0.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Hour Cap for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Hour Cap for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int survivalStreakHourCap = 5; // Challenge rewards - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Chronos Reward for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chronos Reward for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeChronosReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillCrafting.java b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java similarity index 94% rename from src/main/java/com/volmit/adapt/content/skill/SkillCrafting.java rename to src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java index 4dc15b2a4..e2db69e92 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillCrafting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.crafting.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.crafting.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -321,23 +321,23 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Furnace Base XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Base XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double furnaceBaseXP = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Furnace Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double furnaceValueXPMultiplier = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Furnace XPRadius for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPRadius for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int furnaceXPRadius = 32; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 3000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Furnace XPDuration for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPDuration for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long furnaceXPDuration = 10000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Crafting Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crafting Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double craftingValueXPMultiplier = 2.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Base Crafting XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crafting XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double baseCraftingXP = 3.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Craft1k Reward for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Craft1k Reward for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeCraft1kReward = 1200; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillDiscovery.java b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/skill/SkillDiscovery.java rename to src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java index 33fffad85..769dcae94 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillDiscovery.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java @@ -16,29 +16,29 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.Discovery; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryArmor; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryArchaeologist; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryBetterMending; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryCartographerPulse; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryUnity; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryVillagerAtt; -import com.volmit.adapt.content.adaptation.discovery.DiscoveryXpResist; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.Particles; +package art.arcane.adapt.content.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.Discovery; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryArmor; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryArchaeologist; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryBetterMending; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryCartographerPulse; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryUnity; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryVillagerAtt; +import art.arcane.adapt.content.adaptation.discovery.DiscoveryXpResist; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Biome; @@ -393,39 +393,39 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery skill.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Biome XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Biome XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverBiomeXP = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Potion XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Potion XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverPotionXP = 36; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Entity Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Entity Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverEntityTypeXP = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Food Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Food Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverFoodTypeXP = 75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Player XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Player XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverPlayerXP = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Environment XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Environment XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverEnvironmentXP = 750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover World XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover World XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverWorldXP = 750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Max XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Max XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverEnchantMaxXP = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Level XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Level XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverEnchantLevelXPMultiplier = 52; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverEnchantBaseXP = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Item Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverItemBaseXP = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Recipe Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Recipe Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverRecipeBaseXP = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Item Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverItemValueXPMultiplier = 1; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Block Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverBlockBaseXP = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Discover Block Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double discoverBlockValueXPMultiplier = 0.333; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillEnchanting.java b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/skill/SkillEnchanting.java rename to src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java index a6eed3e32..824ecf3e6 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillEnchanting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java @@ -16,24 +16,24 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; - -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingLapisReturn; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingAnvilSavant; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingBookshelfAttunement; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingGrindstoneRecovery; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingOfferReroll; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingQuickEnchant; -import com.volmit.adapt.content.adaptation.enchanting.EnchantingXPReturn; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +package art.arcane.adapt.content.skill; + +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingLapisReturn; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingAnvilSavant; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingBookshelfAttunement; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingGrindstoneRecovery; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingOfferReroll; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingQuickEnchant; +import art.arcane.adapt.content.adaptation.enchanting.EnchantingXPReturn; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -239,13 +239,13 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Enchant Power XPMultiplier for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Enchant Power XPMultiplier for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double enchantPowerXPMultiplier = 45; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 5250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Enchant Reward for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Enchant Reward for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeEnchantReward = 2500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java similarity index 92% rename from src/main/java/com/volmit/adapt/content/skill/SkillExcavation.java rename to src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index 3b1d42ccd..b262b61c8 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.excavation.ExcavationDropToInventory; -import com.volmit.adapt.content.adaptation.excavation.ExcavationHaste; -import com.volmit.adapt.content.adaptation.excavation.ExcavationOmniTool; -import com.volmit.adapt.content.adaptation.excavation.ExcavationSpelunker; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.excavation.ExcavationDropToInventory; +import art.arcane.adapt.content.adaptation.excavation.ExcavationHaste; +import art.arcane.adapt.content.adaptation.excavation.ExcavationOmniTool; +import art.arcane.adapt.content.adaptation.excavation.ExcavationSpelunker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -268,21 +268,21 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Excavation skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Excavation skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHardnessBonus = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBlastResistanceBonus = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Excavation Reward for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Excavation Reward for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeExcavationReward = 1200; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double valueXPMultiplier = 0.6; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double axeDamageXPMultiplier = 4.0; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillHerbalism.java rename to src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index 0d918ff00..b454cb4b9 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.herbalism.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.herbalism.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -334,45 +334,45 @@ public boolean isEnabled() { @NoArgsConstructor public static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") public boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Harvest Xp Cooldown for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Xp Cooldown for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double harvestXpCooldown = 3500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Food Consume XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Consume XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double foodConsumeXP = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Shear XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shear XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double shearXP = 35; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Harvest Per Age XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Per Age XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double harvestPerAgeXP = 5.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Plant Crop Seeds XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Plant Crop Seeds XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double plantCropSeedsXP = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Composter Base XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Base XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double composterBaseXP = 2.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Composter Level XPMultiplier for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Level XPMultiplier for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double composterLevelXPMultiplier = 1.25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Composter Non Zero Level Bonus for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Non Zero Level Bonus for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double composterNonZeroLevelBonus = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeEat100Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeEat1kReward = 6250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeHarvest100Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeHarvest1kReward = 6250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant100 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant100 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengePlant100Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengePlant1kReward = 6250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant5k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant5k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengePlant5kReward = 25000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeCompost50Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost500 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost500 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeCompost500Reward = 6250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeShear50Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear250 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear250 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double challengeShear250Reward = 6250; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillHunter.java b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/skill/SkillHunter.java rename to src/main/java/art/arcane/adapt/content/skill/SkillHunter.java index 9d031135e..380ae2ccf 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillHunter.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java @@ -16,19 +16,19 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.hunter.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.hunter.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -307,23 +307,23 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Hunter skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Hunter skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Turtle Egg Kill XP for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Turtle Egg Kill XP for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double turtleEggKillXP = 100; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Creeper Kill Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Creeper Kill Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double creeperKillMultiplier = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Kill Max Health XPMultiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kill Max Health XPMultiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double killMaxHealthXPMultiplier = 3.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Spawner Mob Reduction Xp Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawner Mob Reduction Xp Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double spawnerMobReductionXpMultiplier = 0.3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Kills Challenge Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kills Challenge Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double killsChallengeReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Boss Kill Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Boss Kill Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double bossKillReward = 1000; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillNether.java b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/skill/SkillNether.java rename to src/main/java/art/arcane/adapt/content/skill/SkillNether.java index 7269bba67..50e4c352c 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillNether.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java @@ -16,24 +16,24 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.nether.NetherFireResist; -import com.volmit.adapt.content.adaptation.nether.NetherBlazeLeech; -import com.volmit.adapt.content.adaptation.nether.NetherGhastWard; -import com.volmit.adapt.content.adaptation.nether.NetherLavaWalker; -import com.volmit.adapt.content.adaptation.nether.NetherPiglinBroker; -import com.volmit.adapt.content.adaptation.nether.NetherSkullYeet; -import com.volmit.adapt.content.adaptation.nether.NetherWitherResist; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.nether.NetherFireResist; +import art.arcane.adapt.content.adaptation.nether.NetherBlazeLeech; +import art.arcane.adapt.content.adaptation.nether.NetherGhastWard; +import art.arcane.adapt.content.adaptation.nether.NetherLavaWalker; +import art.arcane.adapt.content.adaptation.nether.NetherPiglinBroker; +import art.arcane.adapt.content.adaptation.nether.NetherSkullYeet; +import art.arcane.adapt.content.adaptation.nether.NetherWitherResist; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -262,29 +262,29 @@ public boolean isEnabled() { @Data @NoArgsConstructor public static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") private boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Damage Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Damage Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherDamageXp = 26.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Attack Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Attack Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherAttackXp = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherSkeletonKillXp = 225; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherKillXp = 900; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherRoseBreakXp = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Cooldown for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Cooldown for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private int witherRoseBreakCooldown = 60 * 20; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Nether Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Nether Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double challengeNetherReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Damage Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Damage Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double challengeWitherDmgReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Skeleton Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Skeleton Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double challengeWitherSkelReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Boss Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Boss Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double challengeWitherBossReward = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Roses Broken Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Roses Broken Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double challengeRosesReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillPickaxes.java rename to src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index 4f3be526f..aa0378a27 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.pickaxe.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.pickaxe.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -303,43 +303,43 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Debris Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Debris Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double debrisBonus = 210; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Pickaxes skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Pickaxes skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageXPMultiplier = 6.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Block Value Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Block Value Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double blockValueMultiplier = 0.125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxHardnessBonus = 9; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double maxBlastResistanceBonus = 10; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Coal Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Coal Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double coalBonus = 18; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Iron Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Iron Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double ironBonus = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Redstone Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redstone Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double redstoneBonus = 55; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Copper Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Copper Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double copperBonus = 22; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double goldBonus = 38; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Lapis Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lapisBonus = 75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Diamond Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Diamond Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double diamondBonus = 175; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Emerald Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Emerald Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double emeraldBonus = 210; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Nether Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double netherGoldBonus = 105; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Nether Quartz Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Quartz Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double netherQuartzBonus = 125; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Deepslate Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deepslate Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double deepslateMultiplier = 1.35; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillRanged.java b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/skill/SkillRanged.java rename to src/main/java/art/arcane/adapt/content/skill/SkillRanged.java index f570e829e..d2ef41b65 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillRanged.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java @@ -16,17 +16,17 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.ranged.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.ranged.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -270,25 +270,25 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Shoot XP for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shoot XP for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double shootXP = 5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hit Damage XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Damage XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hitDamageXPMultiplier = 1.75; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Hit Distance XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Distance XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double hitDistanceXPMultiplier = 1.2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRangedReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Damage Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Damage Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRangedDmgReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Distance Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Distance Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRangedDistReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Kills Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Kills Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRangedKillsReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Longshot Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Longshot Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRangedLongshotReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillRift.java b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillRift.java rename to src/main/java/art/arcane/adapt/content/skill/SkillRift.java index 74d4f9437..8465f361e 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillRift.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.chronos.ChronosInstantRecall; -import com.volmit.adapt.content.adaptation.rift.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.registries.Attributes; -import com.volmit.adapt.util.reflect.registries.EntityTypes; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.chronos.ChronosInstantRecall; +import art.arcane.adapt.content.adaptation.rift.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.util.reflect.registries.EntityTypes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -303,27 +303,27 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Destroy End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Destroy End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double destroyEndCrystalXP = 250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageEndCrystalXP = 110; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Enderman XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderman XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageEndermanXPMultiplier = 4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Endermite XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Endermite XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageEndermiteXPMultiplier = 2; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Enderdragon XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderdragon XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageEnderdragonXPMultiplier = 8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Throw Enderpearl XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Enderpearl XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double throwEnderpearlXP = 65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Throw Ender Eye XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Ender Eye XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double throwEnderEyeXP = 30; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Teleport XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double teleportXP = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Teleport XPCooldown for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XPCooldown for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double teleportXPCooldown = 60000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Rift Reward for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Rift Reward for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeRiftReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillSeaborne.java b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java similarity index 93% rename from src/main/java/com/volmit/adapt/content/skill/SkillSeaborne.java rename to src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java index 09ab57031..f5b76cce8 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillSeaborne.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java @@ -16,21 +16,21 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.seaborrne.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.seaborrne.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -291,21 +291,21 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sea Pickle Cooldown for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sea Pickle Cooldown for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public long seaPickleCooldown = 60000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tridentxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tridentxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double tridentxpmultiplier = 4.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damagedrownxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damagedrownxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damagedrownxpmultiplier = 3; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim1nm Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim1nm Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwim1nmReward = 750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim5k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim5k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwim5kReward = 1500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim20k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim20k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwim20kReward = 3750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Swim XP for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim XP for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double swimXP = 0.4; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillStealth.java b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillStealth.java rename to src/main/java/art/arcane/adapt/content/skill/SkillStealth.java index 6e4e55d6a..3d69c8038 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillStealth.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java @@ -16,18 +16,18 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.stealth.*; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.stealth.*; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -249,35 +249,35 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak1k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak1k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSneak1kReward = 1750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSneak5kReward = 3500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak20k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak20k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSneak20kReward = 8750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Sneak XP for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak XP for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sneakXP = 0.4; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP multiplier for dealing damage while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP multiplier for dealing damage while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sneakCombatXPMultiplier = 3.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP awarded for killing while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for killing while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double sneakKillXP = 15; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthDmg500Reward = 1500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthDmg5kReward = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 10 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 10 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthKills10Reward = 1000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 100 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 100 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthKills100Reward = 5000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 1h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 1h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthTime1hReward = 2000; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 10h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 10h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthTime10hReward = 7500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 50 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 50 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthArrows50Reward = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeStealthArrows500Reward = 5000; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillSwords.java b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/skill/SkillSwords.java rename to src/main/java/art/arcane/adapt/content/skill/SkillSwords.java index e29e067c0..27b76b515 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillSwords.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java @@ -16,24 +16,24 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.sword.SwordsBloodyBlade; -import com.volmit.adapt.content.adaptation.sword.SwordsDualWield; -import com.volmit.adapt.content.adaptation.sword.SwordsCrimsonCyclone; -import com.volmit.adapt.content.adaptation.sword.SwordsExecutionersEdge; -import com.volmit.adapt.content.adaptation.sword.SwordsMachete; -import com.volmit.adapt.content.adaptation.sword.SwordsPoisonedBlade; -import com.volmit.adapt.content.adaptation.sword.SwordsRiposteWindow; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.sword.SwordsBloodyBlade; +import art.arcane.adapt.content.adaptation.sword.SwordsDualWield; +import art.arcane.adapt.content.adaptation.sword.SwordsCrimsonCyclone; +import art.arcane.adapt.content.adaptation.sword.SwordsExecutionersEdge; +import art.arcane.adapt.content.adaptation.sword.SwordsMachete; +import art.arcane.adapt.content.adaptation.sword.SwordsPoisonedBlade; +import art.arcane.adapt.content.adaptation.sword.SwordsRiposteWindow; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -252,21 +252,21 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageXPMultiplier = 4.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwordReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Damage Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Damage Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwordDmgReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Kills Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Kills Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwordKillsReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Critical Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Critical Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwordCritReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Heavy Hits Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Heavy Hits Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwordHeavyReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillTaming.java b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/skill/SkillTaming.java rename to src/main/java/art/arcane/adapt/content/skill/SkillTaming.java index a34ead4d6..15ffaf351 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillTaming.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java @@ -16,23 +16,23 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.taming.TamingDamage; -import com.volmit.adapt.content.adaptation.taming.TamingBeastRecall; -import com.volmit.adapt.content.adaptation.taming.TamingHealthBoost; -import com.volmit.adapt.content.adaptation.taming.TamingHealthRegeneration; -import com.volmit.adapt.content.adaptation.taming.TamingMountedTactics; -import com.volmit.adapt.content.adaptation.taming.TamingPackLeaderAura; -import com.volmit.adapt.content.adaptation.taming.TamingSharedPain; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.taming.TamingDamage; +import art.arcane.adapt.content.adaptation.taming.TamingBeastRecall; +import art.arcane.adapt.content.adaptation.taming.TamingHealthBoost; +import art.arcane.adapt.content.adaptation.taming.TamingHealthRegeneration; +import art.arcane.adapt.content.adaptation.taming.TamingMountedTactics; +import art.arcane.adapt.content.adaptation.taming.TamingPackLeaderAura; +import art.arcane.adapt.content.adaptation.taming.TamingSharedPain; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -268,25 +268,25 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tame Xp Base for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Xp Base for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double tameXpBase = 65; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Tame Damage XPMultiplier for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Damage XPMultiplier for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double tameDamageXPMultiplier = 8.0; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP awarded for successfully taming an animal.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for successfully taming an animal.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double tameSuccessXP = 150; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP awarded when a pet kills a mob.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded when a pet kills a mob.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double petKillXP = 25; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Taming Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Taming Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeTamingReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Damage Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Damage Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengePetDmgReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Tamed Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Tamed Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeTamedReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Kills Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Kills Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengePetKillsReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java similarity index 90% rename from src/main/java/com/volmit/adapt/content/skill/SkillTragOul.java rename to src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 13864ab04..3ee1f4d4f 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -16,29 +16,29 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.api.world.PlayerAdaptation; -import com.volmit.adapt.api.world.PlayerSkillLine; -import com.volmit.adapt.content.adaptation.tragoul.TragoulGlobe; -import com.volmit.adapt.content.adaptation.tragoul.TragoulBloodPact; -import com.volmit.adapt.content.adaptation.tragoul.TragoulBoneHarvest; -import com.volmit.adapt.content.adaptation.tragoul.TragoulHealing; -import com.volmit.adapt.content.adaptation.tragoul.TragoulLance; -import com.volmit.adapt.content.adaptation.tragoul.TragoulThorns; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.SoundPlayer; -import com.volmit.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.adaptation.tragoul.TragoulGlobe; +import art.arcane.adapt.content.adaptation.tragoul.TragoulBloodPact; +import art.arcane.adapt.content.adaptation.tragoul.TragoulBoneHarvest; +import art.arcane.adapt.content.adaptation.tragoul.TragoulHealing; +import art.arcane.adapt.content.adaptation.tragoul.TragoulLance; +import art.arcane.adapt.content.adaptation.tragoul.TragoulThorns; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.reflect.registries.Particles; import de.slikey.effectlib.effect.CloudEffect; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -309,21 +309,21 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Death Xp Loss for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Death Xp Loss for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double deathXpLoss = -250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Take Away Skills On Death for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Take Away Skills On Death for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") boolean takeAwaySkillsOnDeath = false; - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 450; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage Received Xp Multiplier for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Received Xp Multiplier for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageReceivedXpMultiplier = 4.8; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls XP bonus for surviving a hit below 4 hearts.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP bonus for surviving a hit below 4 hearts.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double lowHealthSurvivalXP = 28; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Trag Reward for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Trag Reward for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeTragReward = 500; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/SkillUnarmed.java b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java similarity index 91% rename from src/main/java/com/volmit/adapt/content/skill/SkillUnarmed.java rename to src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java index 789126411..2810d9381 100644 --- a/src/main/java/com/volmit/adapt/content/skill/SkillUnarmed.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java @@ -16,22 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; +package art.arcane.adapt.content.skill; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; -import com.volmit.adapt.api.advancement.AdaptAdvancement; -import com.volmit.adapt.api.advancement.AdvancementVisibility; -import com.volmit.adapt.api.skill.SimpleSkill; -import com.volmit.adapt.api.world.AdaptPlayer; -import com.volmit.adapt.api.world.AdaptStatTracker; -import com.volmit.adapt.content.adaptation.unarmed.UnarmedGlassCannon; -import com.volmit.adapt.content.adaptation.unarmed.UnarmedBatteringCharge; -import com.volmit.adapt.content.adaptation.unarmed.UnarmedComboChain; -import com.volmit.adapt.content.adaptation.unarmed.UnarmedPower; -import com.volmit.adapt.content.adaptation.unarmed.UnarmedSuckerPunch; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.Localizer; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.content.adaptation.unarmed.UnarmedGlassCannon; +import art.arcane.adapt.content.adaptation.unarmed.UnarmedBatteringCharge; +import art.arcane.adapt.content.adaptation.unarmed.UnarmedComboChain; +import art.arcane.adapt.content.adaptation.unarmed.UnarmedPower; +import art.arcane.adapt.content.adaptation.unarmed.UnarmedSuckerPunch; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -255,21 +255,21 @@ public boolean isEnabled() { @NoArgsConstructor protected static class Config { - @com.volmit.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageXPMultiplier = 4.5; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeUnarmedReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Damage Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Damage Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeUnarmedDmgReward = 500; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Kills Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Kills Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeUnarmedKillsReward = 750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Critical Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Critical Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeUnarmedCritReward = 750; - @com.volmit.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Heavy Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Heavy Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeUnarmedHeavyReward = 750; } } diff --git a/src/main/java/com/volmit/adapt/content/skill/package-info.java b/src/main/java/art/arcane/adapt/content/skill/package-info.java similarity index 96% rename from src/main/java/com/volmit/adapt/content/skill/package-info.java rename to src/main/java/art/arcane/adapt/content/skill/package-info.java index 8611716ae..4a29de841 100644 --- a/src/main/java/com/volmit/adapt/content/skill/package-info.java +++ b/src/main/java/art/arcane/adapt/content/skill/package-info.java @@ -16,4 +16,4 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.content.skill; \ No newline at end of file +package art.arcane.adapt.content.skill; \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java b/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java new file mode 100644 index 000000000..2906dd87b --- /dev/null +++ b/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java @@ -0,0 +1,165 @@ +package art.arcane.adapt.core.nms.container; + +import art.arcane.volmlib.util.json.JSONArray; +import art.arcane.volmlib.util.json.JSONObject; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Function; + +public class BlockProperty { + private static final Set> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class); + private final String name; + private final Class type; + + private final Object defaultValue; + private final Set values; + private final Function nameFunction; + private final Function jsonFunction; + + public > BlockProperty( + String name, + Class type, + T defaultValue, + Collection values, + Function nameFunction + ) { + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + this.values = Collections.unmodifiableSet(new TreeSet<>(values)); + this.nameFunction = (Function) (Object) nameFunction; + jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply; + } + + public static > BlockProperty ofEnum(Class type, String name, T defaultValue) { + return new BlockProperty( + name, + type, + defaultValue, + Arrays.asList(type.getEnumConstants()), + val -> val == null ? "null" : val.name() + ); + } + + public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) { + return new BoundedDouble( + name, + defaultValue, + min, + max, + exclusiveMin, + exclusiveMax, + (f) -> String.format("%.2f", f) + ); + } + + public static BlockProperty ofBoolean(String name, boolean defaultValue) { + return new BlockProperty( + name, + Boolean.class, + defaultValue, + List.of(true, false), + (b) -> b ? "true" : "false" + ); + } + + @Override + public @NotNull String toString() { + return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]"; + } + + public String name() { + return name; + } + + public String defaultValue() { + return nameFunction.apply(defaultValue); + } + + public List names() { + return values.stream().map(nameFunction).toList(); + } + + public Object defaultValueAsJson() { + return jsonFunction.apply(defaultValue); + } + + public JSONArray valuesAsJson() { + return new JSONArray(values.stream().map(jsonFunction).toList()); + } + + public JSONObject buildJson() { + var json = new JSONObject(); + json.put("type", jsonType()); + json.put("default", defaultValueAsJson()); + if (!values.isEmpty()) { + json.put("enum", valuesAsJson()); + } + return json; + } + + public String jsonType() { + if (type == Boolean.class) { + return "boolean"; + } + if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class) { + return "integer"; + } + if (type == Float.class || type == Double.class) { + return "number"; + } + return "string"; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + var that = (BlockProperty) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.values, that.values) && + Objects.equals(this.type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, values, type); + } + + private static class BoundedDouble extends BlockProperty { + private final double min; + private final double max; + private final boolean exclusiveMin; + private final boolean exclusiveMax; + + public BoundedDouble( + String name, + double defaultValue, + double min, + double max, + boolean exclusiveMin, + boolean exclusiveMax, + Function nameFunction + ) { + super(name, Double.class, defaultValue, List.of(), nameFunction); + this.min = min; + this.max = max; + this.exclusiveMin = exclusiveMin; + this.exclusiveMax = exclusiveMax; + } + + @Override + public JSONObject buildJson() { + return super.buildJson() + .put("minimum", min) + .put("maximum", max) + .put("exclusiveMinimum", exclusiveMin) + .put("exclusiveMaximum", exclusiveMax); + } + } +} diff --git a/src/main/java/com/volmit/adapt/util/data/Shrinkwrap.java b/src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java similarity index 70% rename from src/main/java/com/volmit/adapt/util/data/Shrinkwrap.java rename to src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java index 288180afe..1cf463d9d 100644 --- a/src/main/java/com/volmit/adapt/util/data/Shrinkwrap.java +++ b/src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java @@ -17,24 +17,20 @@ * */ -package com.volmit.adapt.util.data; +package art.arcane.adapt.engine.framework; -public class Shrinkwrap { - private T t; +import art.arcane.volmlib.util.data.KCache; - public Shrinkwrap(T t) { - set(t); - } +public interface MeteredCache { + long getSize(); - public Shrinkwrap() { - this(null); - } + KCache getRawCache(); - public T get() { - return t; - } + long getMaxSize(); - public void set(T t) { - this.t = t; + default double getUsage() { + return (double) getSize() / (double) getMaxSize(); } + + boolean isClosed(); } diff --git a/src/main/java/com/volmit/adapt/nms/NMS.java b/src/main/java/art/arcane/adapt/nms/NMS.java similarity index 97% rename from src/main/java/com/volmit/adapt/nms/NMS.java rename to src/main/java/art/arcane/adapt/nms/NMS.java index c5b3928b0..ecb10c47c 100644 --- a/src/main/java/com/volmit/adapt/nms/NMS.java +++ b/src/main/java/art/arcane/adapt/nms/NMS.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.nms; +package art.arcane.adapt.nms; import org.bukkit.inventory.ItemStack; import org.bukkit.util.io.BukkitObjectInputStream; diff --git a/src/main/java/com/volmit/adapt/service/CommandSVC.java b/src/main/java/art/arcane/adapt/service/CommandSVC.java similarity index 87% rename from src/main/java/com/volmit/adapt/service/CommandSVC.java rename to src/main/java/art/arcane/adapt/service/CommandSVC.java index 39bb1ae39..24832b6a3 100644 --- a/src/main/java/com/volmit/adapt/service/CommandSVC.java +++ b/src/main/java/art/arcane/adapt/service/CommandSVC.java @@ -16,16 +16,16 @@ * along with this program. If not, see . */ -package com.volmit.adapt.service; +package art.arcane.adapt.service; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.command.CommandAdapt; -import com.volmit.adapt.util.AdaptService; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.cache.AtomicCache; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.decree.DecreeSystem; -import com.volmit.adapt.util.decree.virtual.VirtualDecreeCommand; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.command.CommandAdapt; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.cache.AtomicCache; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.decree.DecreeSystem; +import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; import org.bukkit.command.PluginCommand; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerCommandPreprocessEvent; diff --git a/src/main/java/com/volmit/adapt/service/ConfigInputSVC.java b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java similarity index 94% rename from src/main/java/com/volmit/adapt/service/ConfigInputSVC.java rename to src/main/java/art/arcane/adapt/service/ConfigInputSVC.java index 745e13f03..8264ba679 100644 --- a/src/main/java/com/volmit/adapt/service/ConfigInputSVC.java +++ b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java @@ -1,10 +1,10 @@ -package com.volmit.adapt.service; +package art.arcane.adapt.service; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.content.gui.ConfigGui; -import com.volmit.adapt.util.AdaptService; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.J; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.content.gui.ConfigGui; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/main/java/com/volmit/adapt/service/HotloadSVC.java b/src/main/java/art/arcane/adapt/service/HotloadSVC.java similarity index 95% rename from src/main/java/com/volmit/adapt/service/HotloadSVC.java rename to src/main/java/art/arcane/adapt/service/HotloadSVC.java index 473a0ba48..1849fcefd 100644 --- a/src/main/java/com/volmit/adapt/service/HotloadSVC.java +++ b/src/main/java/art/arcane/adapt/service/HotloadSVC.java @@ -1,27 +1,27 @@ -package com.volmit.adapt.service; +package art.arcane.adapt.service; import art.arcane.amulet.io.FolderWatcher; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.adaptation.SimpleAdaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.skill.SkillRegistry; -import com.volmit.adapt.api.tick.TickedObject; -import com.volmit.adapt.content.gui.ConfigGui; -import com.volmit.adapt.content.gui.SkillsGui; -import com.volmit.adapt.util.AdaptService; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.ConfigRewriteReporter; -import com.volmit.adapt.util.CustomModel; -import com.volmit.adapt.util.IO; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.Json; -import com.volmit.adapt.util.Localizer; -import com.volmit.adapt.util.Window; -import com.volmit.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SkillRegistry; +import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.content.gui.ConfigGui; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.project.config.ConfigRewriteReporter; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.config.ConfigFileSupport; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -39,7 +39,7 @@ import java.util.Set; import java.util.UUID; -import static com.volmit.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; +import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; public class HotloadSVC implements AdaptService { private static final long WATCHER_POLL_MS = 500; diff --git a/src/main/java/com/volmit/adapt/util/cache/AtomicCache.java b/src/main/java/art/arcane/adapt/util/common/cache/AtomicCache.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/cache/AtomicCache.java rename to src/main/java/art/arcane/adapt/util/common/cache/AtomicCache.java index da65037ee..26f4f802b 100644 --- a/src/main/java/com/volmit/adapt/util/cache/AtomicCache.java +++ b/src/main/java/art/arcane/adapt/util/common/cache/AtomicCache.java @@ -17,14 +17,12 @@ * */ -package com.volmit.adapt.util.cache; +package art.arcane.adapt.util.cache; -//import com.volmit.react.React; -//import com.volmit.react.util.function.NastySupplier; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.function.NastySupplier; +//import art.arcane.react.React; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.function.NastySupplier; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; diff --git a/src/main/java/com/volmit/adapt/util/cache/Cache.java b/src/main/java/art/arcane/adapt/util/common/cache/Cache.java similarity index 73% rename from src/main/java/com/volmit/adapt/util/cache/Cache.java rename to src/main/java/art/arcane/adapt/util/common/cache/Cache.java index e8f191d26..36a9f26e7 100644 --- a/src/main/java/com/volmit/adapt/util/cache/Cache.java +++ b/src/main/java/art/arcane/adapt/util/common/cache/Cache.java @@ -17,37 +17,34 @@ * */ -package com.volmit.adapt.util.cache; +package art.arcane.adapt.util.cache; +import art.arcane.volmlib.util.cache.CacheKey; import org.bukkit.Chunk; public interface Cache { static long key(Chunk chunk) { - return key(chunk.getX(), chunk.getZ()); + return CacheKey.key(chunk); } static long key(int x, int z) { - return (((long) x) << 32) | (z & 0xffffffffL); + return CacheKey.key(x, z); } static int keyX(long key) { - return (int) (key >> 32); + return CacheKey.keyX(key); } static int keyZ(long key) { - return (int) key; + return CacheKey.keyZ(key); } static int to1D(int x, int y, int z, int w, int h) { - return (z * w * h) + (y * w) + x; + return CacheKey.to1D(x, y, z, w, h); } static int[] to3D(int idx, int w, int h) { - final int z = idx / (w * h); - idx -= (z * w * h); - final int y = idx / w; - final int x = idx % w; - return new int[]{x, y, z}; + return CacheKey.to3D(idx, w, h); } int getId(); diff --git a/src/main/java/com/volmit/adapt/util/Dictionary.java b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/Dictionary.java rename to src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java index af492a9c4..9af8190df 100644 --- a/src/main/java/com/volmit/adapt/util/Dictionary.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java @@ -16,12 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.collection; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.data.B; + class Dictionary { //!\"#$%&[] private static final Pattern wordsPattern = Pattern.compile("[A-Z][A-Z][A-Z][A-Z]*"); diff --git a/src/main/java/com/volmit/adapt/util/GBiset.java b/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/GBiset.java rename to src/main/java/art/arcane/adapt/util/common/collection/GBiset.java index b64d8435f..5fce3fb15 100644 --- a/src/main/java/com/volmit/adapt/util/GBiset.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java @@ -16,11 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.collection; import java.io.Serializable; +import art.arcane.adapt.util.data.B; + /** * A Biset * diff --git a/src/main/java/com/volmit/adapt/util/GListAdapter.java b/src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/GListAdapter.java rename to src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java index 65e1f8c50..0175113c6 100644 --- a/src/main/java/com/volmit/adapt/util/GListAdapter.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.collection; import java.util.ArrayList; diff --git a/src/main/java/com/volmit/adapt/util/KeyPair.java b/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/KeyPair.java rename to src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java index becb952b6..9cf0a2f8c 100644 --- a/src/main/java/com/volmit/adapt/util/KeyPair.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.collection; /** * Represents a keypair diff --git a/src/main/java/art/arcane/adapt/util/common/data/B.java b/src/main/java/art/arcane/adapt/util/common/data/B.java new file mode 100644 index 000000000..3ffd76f8c --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/data/B.java @@ -0,0 +1,157 @@ +package art.arcane.adapt.util.data; + +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.data.BSupport; +import art.arcane.adapt.core.nms.container.BlockProperty; +import art.arcane.adapt.util.reflect.registries.Materials; +import it.unimi.dsi.fastutil.ints.IntSet; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; + +import java.util.List; + +public class B { + private static final BSupportImpl BASE = new BSupportImpl(); + + private static final class BSupportImpl extends BSupport { + @Override + protected Material shortGrassMaterial() { + return Materials.GRASS; + } + + @Override + protected void appendExtraFoliageMaterials(IntSet foliage) { + foliage.add(Materials.CHERRY_SAPLING.ordinal()); + foliage.add(Materials.PALE_OAK_SAPLING.ordinal()); + } + } + + public static BlockData toDeepSlateOre(BlockData block, BlockData ore) { + return BASE.toDeepSlateOre(block, ore); + } + + public static boolean isDeepSlate(BlockData blockData) { + return BASE.isDeepSlate(blockData); + } + + public static boolean isOre(BlockData blockData) { + return BASE.isOre(blockData); + } + + public static boolean canPlaceOnto(Material mat, Material onto) { + return BASE.canPlaceOnto(mat, onto); + } + + public static boolean isFoliagePlantable(BlockData d) { + return BASE.isFoliagePlantable(d); + } + + public static boolean isFoliagePlantable(Material d) { + return BASE.isFoliagePlantable(d); + } + + public static boolean isWater(BlockData b) { + return BASE.isWater(b); + } + + public static BlockData getAir() { + return BASE.getAir(); + } + + public static Material getMaterialOrNull(String bdx) { + return BASE.getMaterialOrNull(bdx); + } + + public static Material getMaterial(String bdx) { + return BASE.getMaterial(bdx); + } + + public static boolean isSolid(BlockData mat) { + return BASE.isSolid(mat); + } + + public static BlockData getOrNull(String bdxf) { + return BASE.getOrNull(bdxf); + } + + public static BlockData getOrNull(String bdxf, boolean warn) { + return BASE.getOrNull(bdxf, warn); + } + + public static BlockData getNoCompat(String bdxf) { + return BASE.getNoCompat(bdxf); + } + + public static BlockData get(String bdxf) { + return BASE.get(bdxf); + } + + public static boolean isStorage(BlockData mat) { + return BASE.isStorage(mat); + } + + public static boolean isStorageChest(BlockData mat) { + return BASE.isStorageChest(mat); + } + + public static boolean isLit(BlockData mat) { + return BASE.isLit(mat); + } + + public static boolean isUpdatable(BlockData mat) { + return BASE.isUpdatable(mat); + } + + public static boolean isFoliage(Material d) { + return BASE.isFoliage(d); + } + + public static boolean isFoliage(BlockData d) { + return BASE.isFoliage(d); + } + + public static boolean isDecorant(BlockData m) { + return BASE.isDecorant(m); + } + + public static KList get(KList find) { + return BASE.get(find); + } + + public static boolean isFluid(BlockData d) { + return BASE.isFluid(d); + } + + public static boolean isAirOrFluid(BlockData d) { + return BASE.isAirOrFluid(d); + } + + public static boolean isAir(BlockData d) { + return BASE.isAir(d); + } + + public synchronized static String[] getBlockTypes() { + return BASE.getBlockTypes(); + } + + public synchronized static KMap, List> getBlockStates() { + return BASE.getBlockStates(); + } + + public static String[] getItemTypes() { + return BASE.getItemTypes(); + } + + public static boolean isWaterLogged(BlockData b) { + return BASE.isWaterLogged(b); + } + + public static void registerCustomBlockData(String namespace, String key, BlockData blockData) { + BASE.registerCustomBlockData(namespace, key, blockData); + } + + public static boolean isVineBlock(BlockData data) { + return BASE.isVineBlock(data); + } +} diff --git a/src/main/java/com/volmit/adapt/util/data/Metadata.java b/src/main/java/art/arcane/adapt/util/common/data/Metadata.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/data/Metadata.java rename to src/main/java/art/arcane/adapt/util/common/data/Metadata.java index 857090d15..14baf11a4 100644 --- a/src/main/java/com/volmit/adapt/util/data/Metadata.java +++ b/src/main/java/art/arcane/adapt/util/common/data/Metadata.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.util.data; +package art.arcane.adapt.util.data; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.Metadatable; diff --git a/src/main/java/com/volmit/adapt/util/data/TinyColor.java b/src/main/java/art/arcane/adapt/util/common/data/TinyColor.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/data/TinyColor.java rename to src/main/java/art/arcane/adapt/util/common/data/TinyColor.java index 45b1b6a33..214339f64 100644 --- a/src/main/java/com/volmit/adapt/util/data/TinyColor.java +++ b/src/main/java/art/arcane/adapt/util/common/data/TinyColor.java @@ -17,7 +17,7 @@ * */ -package com.volmit.adapt.util.data; +package art.arcane.adapt.util.data; import java.awt.*; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java new file mode 100644 index 000000000..050a4fec0 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java @@ -0,0 +1,20 @@ +package art.arcane.adapt.util.decree; + +import art.arcane.volmlib.util.decree.context.DecreeContextBase; +import art.arcane.adapt.util.common.plugin.VolmitSender; + +public class DecreeContext { + private static final DecreeContextBase context = new DecreeContextBase<>(); + + public static VolmitSender get() { + return context.get(); + } + + public static void touch(VolmitSender c) { + context.touch(c); + } + + public static void remove() { + context.remove(); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java new file mode 100644 index 000000000..2c553de0b --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java @@ -0,0 +1,20 @@ +package art.arcane.adapt.util.decree; + +import art.arcane.volmlib.util.decree.context.DecreeContextHandlers; +import art.arcane.volmlib.util.decree.context.DecreeContextHandlerType; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.plugin.VolmitSender; + +import java.util.Map; + +public interface DecreeContextHandler extends DecreeContextHandlerType { + Map, DecreeContextHandler> contextHandlers = buildContextHandlers(); + + static Map, DecreeContextHandler> buildContextHandlers() { + return DecreeContextHandlers.buildOrEmpty( + Adapt.initialize("art.arcane.adapt.util.decree.context"), + DecreeContextHandler.class, + h -> ((DecreeContextHandler) h).getType(), + Throwable::printStackTrace); + } +} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeExecutor.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java similarity index 70% rename from src/main/java/com/volmit/adapt/util/decree/DecreeExecutor.java rename to src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java index 476390a32..95f124f13 100644 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeExecutor.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java @@ -17,13 +17,13 @@ * */ -package com.volmit.adapt.util.decree; +package art.arcane.adapt.util.decree; -import com.volmit.adapt.util.VolmitSender; -import org.bukkit.World; +import art.arcane.volmlib.util.decree.DecreeExecutorBase; +import art.arcane.adapt.util.common.plugin.VolmitSender; import org.bukkit.entity.Player; -public interface DecreeExecutor { +public interface DecreeExecutor extends DecreeExecutorBase { default VolmitSender sender() { return DecreeContext.get(); } @@ -31,15 +31,4 @@ default VolmitSender sender() { default Player player() { return sender().player(); } - - default World world() { - if (sender().isPlayer()) { - return sender().player().getWorld(); - } - return null; - } - - default T get(T v, T ifUndefined) { - return v == null ? ifUndefined : v; - } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java new file mode 100644 index 000000000..cec0234fa --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java @@ -0,0 +1,17 @@ +package art.arcane.adapt.util.decree; + +import art.arcane.volmlib.util.decree.DecreeNodeBase; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public class DecreeNode extends DecreeNodeBase { + public DecreeNode(Object instance, Method method) { + super(instance, method); + } + + @Override + protected DecreeParameter createParameter(Parameter parameter) { + return new DecreeParameter(parameter); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java new file mode 100644 index 000000000..149283120 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java @@ -0,0 +1,28 @@ +package art.arcane.adapt.util.decree; + +import art.arcane.volmlib.util.decree.DecreeParameterBase; +import art.arcane.volmlib.util.decree.specialhandlers.NoParameterHandler; +import art.arcane.adapt.util.decree.specialhandlers.DummyHandler; + +import java.lang.reflect.Parameter; + +public class DecreeParameter extends DecreeParameterBase { + public DecreeParameter(Parameter parameter) { + super(parameter); + } + + @Override + protected boolean useSystemHandler(Class customHandler) { + return customHandler.equals(NoParameterHandler.class) || customHandler.equals(DummyHandler.class); + } + + @Override + protected art.arcane.volmlib.util.decree.DecreeParameterHandler getSystemHandler(Class type) { + return DecreeSystem.getHandler(type); + } + + @Override + public DecreeParameterHandler getHandler() { + return (DecreeParameterHandler) super.getHandler(); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java new file mode 100644 index 000000000..c270bf6fa --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java @@ -0,0 +1,5 @@ +package art.arcane.adapt.util.decree; + +public interface DecreeParameterHandler + extends DecreeExecutor, art.arcane.volmlib.util.decree.DecreeParameterHandler { +} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeSystem.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java similarity index 53% rename from src/main/java/com/volmit/adapt/util/decree/DecreeSystem.java rename to src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java index 5acab1533..120b6a71e 100644 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeSystem.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java @@ -17,12 +17,15 @@ * */ -package com.volmit.adapt.util.decree; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.*; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.virtual.VirtualDecreeCommand; +package art.arcane.adapt.util.decree; +import art.arcane.volmlib.util.math.RNG; + +import art.arcane.volmlib.util.decree.DecreeSystemSupport; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -34,77 +37,20 @@ import java.util.List; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.plugin.Permission; +import art.arcane.adapt.util.common.plugin.VolmitSender; + public interface DecreeSystem extends CommandExecutor, TabCompleter { - KList> handlers = Adapt.initialize("com.volmit.adapt.util.decree.handlers", null).kConvert((i) -> (DecreeParameterHandler) i); + KList> handlers = Adapt.initialize("art.arcane.adapt.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); static KList enhanceArgs(String[] args) { - return enhanceArgs(args, true); + return new KList<>(DecreeSystemSupport.enhanceArgs(args)); } static KList enhanceArgs(String[] args, boolean trim) { - KList a = new KList<>(); - - if (args.length == 0) { - return a; - } - - StringBuilder flat = new StringBuilder(); - for (String i : args) { - if (trim) { - if (i.trim().isEmpty()) { - continue; - } - - flat.append(" ").append(i.trim()); - } else { - if (i.endsWith(" ")) { - flat.append(" ").append(i.trim()).append(" "); - } - } - } - - flat = new StringBuilder(flat.length() > 0 ? trim ? flat.toString().trim().length() > 0 ? flat.substring(1).trim() : flat.toString().trim() : flat.substring(1) : flat); - StringBuilder arg = new StringBuilder(); - boolean quoting = false; - - for (int x = 0; x < flat.length(); x++) { - char i = flat.charAt(x); - char j = x < flat.length() - 1 ? flat.charAt(x + 1) : i; - boolean hasNext = x < flat.length(); - - if (i == ' ' && !quoting) { - if (!arg.toString().trim().isEmpty() && trim) { - a.add(arg.toString().trim()); - arg = new StringBuilder(); - } - } else if (i == '"') { - if (!quoting && (arg.length() == 0)) { - quoting = true; - } else if (quoting) { - quoting = false; - - if (hasNext && j == ' ') { - if (!arg.toString().trim().isEmpty() && trim) { - a.add(arg.toString().trim()); - arg = new StringBuilder(); - } - } else if (!hasNext) { - if (!arg.toString().trim().isEmpty() && trim) { - a.add(arg.toString().trim()); - arg = new StringBuilder(); - } - } - } - } else { - arg.append(i); - } - } - - if (!arg.toString().trim().isEmpty() && trim) { - a.add(arg.toString().trim()); - } - - return a; + return new KList<>(DecreeSystemSupport.enhanceArgs(args, trim)); } /** @@ -114,11 +60,11 @@ static KList enhanceArgs(String[] args, boolean trim) { * @return The corresponding {@link DecreeParameterHandler}, or null */ static DecreeParameterHandler getHandler(Class type) { - for (DecreeParameterHandler i : handlers) { - if (i.supports(type)) { - return i; - } + DecreeParameterHandler handler = DecreeSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); + if (handler != null) { + return handler; } + Adapt.error("Unhandled type in Decree Parameter: " + type.getName() + ". This is bad!"); return null; } @@ -130,23 +76,32 @@ static DecreeParameterHandler getHandler(Class type) { default boolean call(VolmitSender sender, String[] args) { DecreeContext.touch(sender); - return getRoot().invoke(sender, enhanceArgs(args)); + try { + return getRoot().invoke(sender, enhanceArgs(args)); + } finally { + DecreeContext.remove(); + } } @Nullable @Override default List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { Adapt.verbose("Received Tab Complete from %s for %s".formatted(sender.getName(), "/" + alias + String.join(" ", args))); - KList enhanced = new KList<>(args); - KList v = getRoot().tabComplete(enhanced, enhanced.toString(" ")); - v.removeDuplicates(); + DecreeContext.touch(new VolmitSender(sender)); + try { + KList enhanced = new KList<>(args); + KList v = getRoot().tabComplete(enhanced, enhanced.toString(" ")); + v.removeDuplicates(); - if (sender instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); - } + if (sender instanceof Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); + } - return v; + return v; + } finally { + DecreeContext.remove(); + } } @Override diff --git a/src/main/java/com/volmit/adapt/util/decree/context/AdaptationListingHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/decree/context/AdaptationListingHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java index ba286bedf..413da8015 100644 --- a/src/main/java/com/volmit/adapt/util/decree/context/AdaptationListingHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util.decree.context; +package art.arcane.adapt.util.decree.context; -import com.volmit.adapt.api.adaptation.Adaptation; -import com.volmit.adapt.api.skill.Skill; -import com.volmit.adapt.api.skill.SkillRegistry; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SkillRegistry; +import art.arcane.volmlib.util.collection.KList; public class AdaptationListingHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java new file mode 100644 index 000000000..9cd3e68d5 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java @@ -0,0 +1,18 @@ +package art.arcane.adapt.util.decree.context; + +import art.arcane.volmlib.util.decree.context.WorldContextHandlerBase; +import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.adapt.util.decree.DecreeContextHandler; +import org.bukkit.World; + +public class WorldContextHandler extends WorldContextHandlerBase implements DecreeContextHandler { + @Override + protected boolean isPlayer(VolmitSender sender) { + return sender.isPlayer(); + } + + @Override + protected World getWorld(VolmitSender sender) { + return sender.player().getWorld(); + } +} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationListHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java similarity index 72% rename from src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationListHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java index 33ff7d46b..42a8f7271 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationListHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationListHandler implements DecreeParameterHandler { @Override diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationProviderHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java similarity index 73% rename from src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationProviderHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java index 975ebea9f..8b10b0a9b 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationProviderHandler implements DecreeParameterHandler { diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationSkillListHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java similarity index 73% rename from src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationSkillListHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java index 08ca99949..3a331c382 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/AdaptationSkillListHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationSkillListHandler implements DecreeParameterHandler { @Override diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java new file mode 100644 index 000000000..09d8b2e2b --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java @@ -0,0 +1,39 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.adapt.util.decree.DecreeContext; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.DecreeSystem; +import art.arcane.volmlib.util.decree.handlers.base.BlockVectorHandlerBase; +import art.arcane.volmlib.util.format.Form; +import org.bukkit.FluidCollisionMode; +import org.bukkit.entity.Player; +import org.bukkit.util.BlockVector; + +import java.util.List; + +public class BlockVectorHandler extends BlockVectorHandlerBase implements DecreeParameterHandler { + @Override + protected boolean isSenderPlayer() { + return DecreeContext.get().isPlayer(); + } + + @Override + protected BlockVector getSenderBlockVector() { + return DecreeContext.get().player().getLocation().toVector().toBlockVector(); + } + + @Override + protected BlockVector getLookBlockVector() { + return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); + } + + @Override + protected List playerPossibilities(String query) { + return DecreeSystem.getHandler(Player.class).getPossibilities(query); + } + + @Override + protected String format(double value) { + return Form.f(value, 2); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java new file mode 100644 index 000000000..2d3a98663 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.BooleanHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class BooleanHandler extends BooleanHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java new file mode 100644 index 000000000..a131ee75a --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.ByteHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class ByteHandler extends ByteHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java new file mode 100644 index 000000000..2149de645 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.DoubleHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class DoubleHandler extends DoubleHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java new file mode 100644 index 000000000..44504b984 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.FloatHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class FloatHandler extends FloatHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java new file mode 100644 index 000000000..de7fac054 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.IntegerHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class IntegerHandler extends IntegerHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java new file mode 100644 index 000000000..ca4589499 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.LongHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class LongHandler extends LongHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/com/volmit/adapt/util/data/CuboidException.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java similarity index 66% rename from src/main/java/com/volmit/adapt/util/data/CuboidException.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java index 4a9b7aa98..607118824 100644 --- a/src/main/java/com/volmit/adapt/util/data/CuboidException.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java @@ -17,17 +17,14 @@ * */ -package com.volmit.adapt.util.data; +package art.arcane.adapt.util.decree.handlers; -/** - * Represents a cuboid exception - * - * @author cyberpwn - */ -public class CuboidException extends Exception { - private static final long serialVersionUID = 1L; +import art.arcane.volmlib.util.decree.handlers.base.OptionalWorldHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; - public CuboidException(String string) { - super(string); +public class OptionalWorldHandler extends OptionalWorldHandlerBase implements DecreeParameterHandler { + @Override + protected String excludedPrefix() { + return "adapt/"; } } diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/ParticleHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java similarity index 75% rename from src/main/java/com/volmit/adapt/util/decree/handlers/ParticleHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java index 7eff87bbf..87b49c1d4 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/ParticleHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java @@ -1,8 +1,8 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import org.bukkit.Particle; public class ParticleHandler implements DecreeParameterHandler { diff --git a/src/main/java/com/volmit/adapt/util/decree/specialhandlers/DummyHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java similarity index 52% rename from src/main/java/com/volmit/adapt/util/decree/specialhandlers/DummyHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java index 96a585a2a..2514a0cfe 100644 --- a/src/main/java/com/volmit/adapt/util/decree/specialhandlers/DummyHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java @@ -17,36 +17,23 @@ * */ -package com.volmit.adapt.util.decree.specialhandlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.decree.handlers.base.PlayerHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.util.ArrayList; import java.util.List; -public class DummyHandler implements DecreeParameterHandler { +public class PlayerHandler extends PlayerHandlerBase implements DecreeParameterHandler { @Override - public KList getPossibilities() { - return null; - } - - public boolean isDummy() { - return true; - } - - @Override - public String toString(Object o) { - return null; - } - - @Override - public Object parse(String in, boolean force) throws DecreeParsingException { - return null; - } - - @Override - public boolean supports(Class type) { - return false; + protected List playerOptions() { + if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { + return new ArrayList<>(Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()); + } + return new ArrayList<>(Bukkit.getOnlinePlayers()); } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java new file mode 100644 index 000000000..14e8eecc2 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.ShortHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class ShortHandler extends ShortHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/SkillProviderHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java similarity index 72% rename from src/main/java/com/volmit/adapt/util/decree/handlers/SkillProviderHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java index 31bcc1fb6..d0f6f7f68 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/SkillProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.context.AdaptationListingHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class SkillProviderHandler implements DecreeParameterHandler { @Override diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/SoundHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java similarity index 75% rename from src/main/java/com/volmit/adapt/util/decree/handlers/SoundHandler.java rename to src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java index 5a2351336..4f3a03dc7 100644 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/SoundHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java @@ -1,8 +1,8 @@ -package com.volmit.adapt.util.decree.handlers; +package art.arcane.adapt.util.decree.handlers; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import org.bukkit.Sound; public class SoundHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java new file mode 100644 index 000000000..add795f3c --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.StringHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class StringHandler extends StringHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java new file mode 100644 index 000000000..173ff1b1e --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java @@ -0,0 +1,39 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.adapt.util.decree.DecreeContext; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.DecreeSystem; +import art.arcane.volmlib.util.decree.handlers.base.VectorHandlerBase; +import art.arcane.volmlib.util.format.Form; +import org.bukkit.FluidCollisionMode; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.List; + +public class VectorHandler extends VectorHandlerBase implements DecreeParameterHandler { + @Override + protected boolean isSenderPlayer() { + return DecreeContext.get().isPlayer(); + } + + @Override + protected Vector getSenderVector() { + return DecreeContext.get().player().getLocation().toVector(); + } + + @Override + protected Vector getLookVector() { + return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); + } + + @Override + protected List playerPossibilities(String query) { + return DecreeSystem.getHandler(Player.class).getPossibilities(query); + } + + @Override + protected String format(double value) { + return Form.f(value, 2); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java new file mode 100644 index 000000000..e1de617f2 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java @@ -0,0 +1,12 @@ +package art.arcane.adapt.util.decree.handlers; + +import art.arcane.volmlib.util.decree.handlers.base.WorldHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import org.bukkit.World; + +public class WorldHandler extends WorldHandlerBase implements DecreeParameterHandler { + @Override + protected String excludedPrefix() { + return "adapt/"; + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java new file mode 100644 index 000000000..07e8234a2 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.decree.specialhandlers; + +import art.arcane.volmlib.util.decree.handlers.base.DummyHandlerBase; +import art.arcane.adapt.util.decree.DecreeParameterHandler; + +public class DummyHandler extends DummyHandlerBase implements DecreeParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java new file mode 100644 index 000000000..4951dd41a --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java @@ -0,0 +1,14 @@ +package art.arcane.adapt.util.decree.specialhandlers; + +import art.arcane.volmlib.util.decree.handlers.base.NullablePlayerHandlerBase; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.adapt.util.decree.handlers.PlayerHandler; +import org.bukkit.entity.Player; + +public class NullablePlayerHandler extends PlayerHandler implements DecreeParameterHandler { + @Override + public Player parse(String in, boolean force) throws DecreeParsingException { + return NullablePlayerHandlerBase.parseNullable(this, in); + } +} diff --git a/src/main/java/com/volmit/adapt/util/decree/virtual/VirtualDecreeCommand.java b/src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/decree/virtual/VirtualDecreeCommand.java rename to src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java index fbd43ecb3..dbf5607cc 100644 --- a/src/main/java/com/volmit/adapt/util/decree/virtual/VirtualDecreeCommand.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java @@ -17,24 +17,24 @@ * */ -package com.volmit.adapt.util.decree.virtual; - +package art.arcane.adapt.util.decree.virtual; import art.arcane.amulet.format.Form; import art.arcane.chrono.ChronoLatch; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.CommandDummy; -import com.volmit.adapt.util.J; -import com.volmit.adapt.util.VolmitSender; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.decree.DecreeContext; -import com.volmit.adapt.util.decree.DecreeContextHandler; -import com.volmit.adapt.util.decree.DecreeNode; -import com.volmit.adapt.util.decree.DecreeParameter; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.project.command.CommandDummy; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.decree.DecreeContext; +import art.arcane.adapt.util.decree.DecreeContextHandler; +import art.arcane.adapt.util.decree.DecreeNode; +import art.arcane.adapt.util.decree.DecreeParameter; +import art.arcane.volmlib.util.decree.DecreeOrigin; +import art.arcane.volmlib.util.decree.annotations.Decree; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import lombok.Data; import java.lang.reflect.Field; @@ -248,12 +248,12 @@ private void tab(List args, List tabs) { if (last.contains("=")) { String[] vv = last.trim().split("\\Q=\\E"); String vx = vv.length == 2 ? vv[1] : ""; - for (String f : i.getHandler().getPossibilities(vx).kConvert((v) -> i.getHandler().toStringForce(v))) { + for (String f : i.getHandler().getPossibilities(vx).convert((v) -> i.getHandler().toStringForce(v))) { g++; tabs.add(i.getName() + "=" + f); } } else { - for (String f : i.getHandler().getPossibilities("").kConvert((v) -> i.getHandler().toStringForce(v))) { + for (String f : i.getHandler().getPossibilities("").convert((v) -> i.getHandler().toStringForce(v))) { g++; tabs.add(i.getName() + "=" + f); } @@ -371,6 +371,12 @@ public boolean invoke(VolmitSender sender, KList realArgs) { } public boolean invoke(VolmitSender sender, KList args, List skip) { + DecreeOrigin origin = type.getDeclaredAnnotation(Decree.class).origin(); + if (!origin.validFor(sender.isPlayer())) { + sender.sendMessage(C.RED + "This command has to be sent from another origin: " + C.GOLD + origin); + return false; + } + Adapt.debug("@ " + getPath() + " with " + args.toString(", ")); if (isNode()) { Adapt.debug("Invoke " + getPath() + "(" + args.toString(",") + ") at "); @@ -465,21 +471,27 @@ private boolean invokeNode(VolmitSender sender, KMap> m } DecreeContext.touch(sender); - Runnable rx = () -> { - try { + try { + Runnable rx = () -> { DecreeContext.touch(sender); - getNode().getMethod().setAccessible(true); - getNode().getMethod().invoke(getNode().getInstance(), params); - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException("Failed to execute "); // TODO: - } - }; + try { + getNode().getMethod().setAccessible(true); + getNode().getMethod().invoke(getNode().getInstance(), params); + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException("Failed to execute "); // TODO: + } finally { + DecreeContext.remove(); + } + }; - if (getNode().isSync()) { - J.s(rx); - } else { - rx.run(); + if (getNode().isSync()) { + J.s(rx); + } else { + rx.run(); + } + } finally { + DecreeContext.remove(); } return true; diff --git a/src/main/java/com/volmit/adapt/util/C.java b/src/main/java/art/arcane/adapt/util/common/format/C.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/C.java rename to src/main/java/art/arcane/adapt/util/common/format/C.java index 13ebc920a..830e423c4 100644 --- a/src/main/java/com/volmit/adapt/util/C.java +++ b/src/main/java/art/arcane/adapt/util/common/format/C.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.format; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; @@ -30,6 +30,8 @@ import java.util.Map; import java.util.regex.Pattern; +import art.arcane.adapt.util.common.plugin.VolmitSender; + /** * Colors * diff --git a/src/main/java/com/volmit/adapt/util/HiddenStringUtils.java b/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/HiddenStringUtils.java rename to src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java index 60da16564..4376499b1 100644 --- a/src/main/java/com/volmit/adapt/util/HiddenStringUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.format; import org.bukkit.ChatColor; diff --git a/src/main/java/com/volmit/adapt/util/ING.java b/src/main/java/art/arcane/adapt/util/common/format/ING.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/ING.java rename to src/main/java/art/arcane/adapt/util/common/format/ING.java index 5ed88895d..7b99902cb 100644 --- a/src/main/java/com/volmit/adapt/util/ING.java +++ b/src/main/java/art/arcane/adapt/util/common/format/ING.java @@ -16,7 +16,8 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.format; +import art.arcane.volmlib.util.math.RNG; public class ING { public ING(RNG rng) { diff --git a/src/main/java/com/volmit/adapt/util/Localizer.java b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/Localizer.java rename to src/main/java/art/arcane/adapt/util/common/format/Localizer.java index 2eb715055..c34550011 100644 --- a/src/main/java/com/volmit/adapt/util/Localizer.java +++ b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java @@ -16,13 +16,14 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.format; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.config.ConfigFileSupport; import lombok.SneakyThrows; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; diff --git a/src/main/java/com/volmit/adapt/util/CallbackCV.java b/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/CallbackCV.java rename to src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java index 516a956fa..037964667 100644 --- a/src/main/java/com/volmit/adapt/util/CallbackCV.java +++ b/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface CallbackCV { void run(T t); diff --git a/src/main/java/com/volmit/adapt/util/Consumer2.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/Consumer2.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer2.java index 63cd93ce3..d00318dfc 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java @@ -16,7 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer3.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer3.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer3.java index b76d47117..c68cbfbb3 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer4.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer4.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer4.java index 101000f14..ae7d9f4f7 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer5.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer5.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer5.java index 7fdfebe84..6cf87234d 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer5.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer6.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer6.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer6.java index 6ec98531a..c35cbf840 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer6.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer7.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer7.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer7.java index 68ee5ebef..dccec4ea0 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer7.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Consumer8.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Consumer8.java rename to src/main/java/art/arcane/adapt/util/common/function/Consumer8.java index c99dd303a..5425fb8f8 100644 --- a/src/main/java/com/volmit/adapt/util/Consumer8.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Function2.java b/src/main/java/art/arcane/adapt/util/common/function/Function2.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/Function2.java rename to src/main/java/art/arcane/adapt/util/common/function/Function2.java index c62dd142b..5c4975e10 100644 --- a/src/main/java/com/volmit/adapt/util/Function2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function2.java @@ -16,7 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Function3.java b/src/main/java/art/arcane/adapt/util/common/function/Function3.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Function3.java rename to src/main/java/art/arcane/adapt/util/common/function/Function3.java index 518b825ad..179066e50 100644 --- a/src/main/java/com/volmit/adapt/util/Function3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function3.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/Function4.java b/src/main/java/art/arcane/adapt/util/common/function/Function4.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/Function4.java rename to src/main/java/art/arcane/adapt/util/common/function/Function4.java index 5ec74050c..69b9baac3 100644 --- a/src/main/java/com/volmit/adapt/util/Function4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function4.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; + + +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.data.B; @SuppressWarnings("hiding") @FunctionalInterface diff --git a/src/main/java/com/volmit/adapt/util/NastyFunction.java b/src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/NastyFunction.java rename to src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java index 3c54f1767..8fb51e304 100644 --- a/src/main/java/com/volmit/adapt/util/NastyFunction.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface NastyFunction { R run(T t) throws Throwable; diff --git a/src/main/java/com/volmit/adapt/util/NastyFuture.java b/src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/NastyFuture.java rename to src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java index 4826af9a1..61a5e74d6 100644 --- a/src/main/java/com/volmit/adapt/util/NastyFuture.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface NastyFuture { R run() throws Throwable; diff --git a/src/main/java/com/volmit/adapt/util/NastyRunnable.java b/src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/NastyRunnable.java rename to src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java index 82d6d0b39..f89356ee9 100644 --- a/src/main/java/com/volmit/adapt/util/NastyRunnable.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface NastyRunnable { void run() throws Throwable; diff --git a/src/main/java/com/volmit/adapt/util/NoiseInjector.java b/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/NoiseInjector.java rename to src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java index db9f3cec2..d3201ec61 100644 --- a/src/main/java/com/volmit/adapt/util/NoiseInjector.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; @FunctionalInterface public interface NoiseInjector { diff --git a/src/main/java/com/volmit/adapt/util/NoiseProvider.java b/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/NoiseProvider.java rename to src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java index 41a893e5f..fee748568 100644 --- a/src/main/java/com/volmit/adapt/util/NoiseProvider.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; @FunctionalInterface public interface NoiseProvider { diff --git a/src/main/java/com/volmit/adapt/util/Supplier2.java b/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/Supplier2.java rename to src/main/java/art/arcane/adapt/util/common/function/Supplier2.java index c4108e5b7..7dfcc4048 100644 --- a/src/main/java/com/volmit/adapt/util/Supplier2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface Supplier2 { void get(T t, TT tt); diff --git a/src/main/java/com/volmit/adapt/util/Supplier3.java b/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/Supplier3.java rename to src/main/java/art/arcane/adapt/util/common/function/Supplier3.java index d0e00915a..b5051c6ef 100644 --- a/src/main/java/com/volmit/adapt/util/Supplier3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.function; public interface Supplier3 { void get(T t, TT tt, TTT ttt); diff --git a/src/main/java/com/volmit/adapt/util/Element.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/Element.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java index 6443c729a..00cff7fd0 100644 --- a/src/main/java/com/volmit/adapt/util/Element.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java @@ -16,12 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; +import art.arcane.volmlib.util.scheduling.Callback; import org.bukkit.inventory.ItemStack; import java.util.List; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; + public interface Element { MaterialBlock getMaterial(); diff --git a/src/main/java/com/volmit/adapt/util/ElementEvent.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java similarity index 91% rename from src/main/java/com/volmit/adapt/util/ElementEvent.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java index 1412dd59c..13b4f38b3 100644 --- a/src/main/java/com/volmit/adapt/util/ElementEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java @@ -16,7 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; + + +import art.arcane.adapt.util.reflect.events.api.Event; /** * Element Event. diff --git a/src/main/java/com/volmit/adapt/util/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/GuiConfirm.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index d32329add..728944ca6 100644 --- a/src/main/java/com/volmit/adapt/util/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -1,10 +1,14 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.scheduling.J; + public final class GuiConfirm { private GuiConfirm() { } diff --git a/src/main/java/com/volmit/adapt/util/GuiEffects.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/GuiEffects.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java index 7d4709db5..a2f75a334 100644 --- a/src/main/java/com/volmit/adapt/util/GuiEffects.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/util/GuiLayout.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/GuiLayout.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java index eedbe4233..7ea1716da 100644 --- a/src/main/java/com/volmit/adapt/util/GuiLayout.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; public final class GuiLayout { public static final int WIDTH = 9; diff --git a/src/main/java/com/volmit/adapt/util/GuiTheme.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/GuiTheme.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java index 7b3ec0d2a..afccf7045 100644 --- a/src/main/java/com/volmit/adapt/util/GuiTheme.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java @@ -1,7 +1,9 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.Material; +import art.arcane.adapt.util.common.math.MaterialBlock; + public final class GuiTheme { private GuiTheme() { } diff --git a/src/main/java/com/volmit/adapt/util/Inventories.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/Inventories.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java index bd7edfaa5..524ed426b 100644 --- a/src/main/java/com/volmit/adapt/util/Inventories.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.Material; import org.bukkit.inventory.Inventory; diff --git a/src/main/java/com/volmit/adapt/util/Items.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/Items.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java index 943296deb..c2c2d9cb5 100644 --- a/src/main/java/com/volmit/adapt/util/Items.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -25,6 +25,8 @@ import java.util.ArrayList; +import art.arcane.adapt.util.common.math.MaterialBlock; + /** * Itemstack utilities * diff --git a/src/main/java/com/volmit/adapt/util/PhantomInventory.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/PhantomInventory.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java index 0efefaed7..ee35cc93e 100644 --- a/src/main/java/com/volmit/adapt/util/PhantomInventory.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.Location; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/util/PhantomInventoryWrapper.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/PhantomInventoryWrapper.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java index 07c938cf8..d4130da14 100644 --- a/src/main/java/com/volmit/adapt/util/PhantomInventoryWrapper.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.inventory.Inventory; diff --git a/src/main/java/com/volmit/adapt/util/UIElement.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/UIElement.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java index 61d7ffcf7..e54dcc5cd 100644 --- a/src/main/java/com/volmit/adapt/util/UIElement.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java @@ -16,16 +16,22 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; +import art.arcane.volmlib.util.format.Form; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.reflect.registries.Enchantments; -import com.volmit.adapt.util.reflect.registries.ItemFlags; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.scheduling.Callback; +import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.adapt.util.reflect.registries.ItemFlags; import org.bukkit.Material; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; + public class UIElement implements Element { private final String id; private final KList lore; diff --git a/src/main/java/com/volmit/adapt/util/UIStaticDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/UIStaticDecorator.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java index 3a0c1e68a..8efac7ad4 100644 --- a/src/main/java/com/volmit/adapt/util/UIStaticDecorator.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java @@ -16,10 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.Material; +import art.arcane.adapt.util.common.math.MaterialBlock; + public class UIStaticDecorator implements WindowDecorator { private final Element element; diff --git a/src/main/java/com/volmit/adapt/util/UIVoidDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/UIVoidDecorator.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java index f184b628f..56d11edd6 100644 --- a/src/main/java/com/volmit/adapt/util/UIVoidDecorator.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; public class UIVoidDecorator implements WindowDecorator { @Override diff --git a/src/main/java/com/volmit/adapt/util/UIWindow.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/UIWindow.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java index 55fd483ba..993a47820 100644 --- a/src/main/java/com/volmit/adapt/util/UIWindow.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java @@ -16,10 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.version.Version; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.version.Version; +import art.arcane.volmlib.util.scheduling.Callback; import lombok.Getter; import lombok.Setter; import org.bukkit.Bukkit; @@ -40,6 +41,8 @@ import java.util.Map; import java.util.Set; +import art.arcane.adapt.util.common.scheduling.J; + public class UIWindow implements Window, Listener { private final Player viewer; private final Map elements; diff --git a/src/main/java/com/volmit/adapt/util/Window.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/Window.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java index dab519eb5..5b7735a98 100644 --- a/src/main/java/com/volmit/adapt/util/Window.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java @@ -16,8 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; +import art.arcane.volmlib.util.scheduling.Callback; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/com/volmit/adapt/util/WindowDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/WindowDecorator.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java index c5afc1302..06b72dc4f 100644 --- a/src/main/java/com/volmit/adapt/util/WindowDecorator.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; public interface WindowDecorator { Element onDecorateBackground(Window window, int position, int row); diff --git a/src/main/java/com/volmit/adapt/util/WindowResolution.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/WindowResolution.java rename to src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java index 1e86aca90..68aef05db 100644 --- a/src/main/java/com/volmit/adapt/util/WindowResolution.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.inventorygui; import org.bukkit.event.inventory.InventoryType; diff --git a/src/main/java/com/volmit/adapt/util/BukkitGson.java b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/BukkitGson.java rename to src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java index fc0d2c769..2128b9abb 100644 --- a/src/main/java/com/volmit/adapt/util/BukkitGson.java +++ b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java @@ -16,7 +16,8 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; +import art.arcane.volmlib.util.format.Form; import com.google.gson.*; import org.bukkit.Bukkit; diff --git a/src/main/java/com/volmit/adapt/util/Json.java b/src/main/java/art/arcane/adapt/util/common/io/Json.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Json.java rename to src/main/java/art/arcane/adapt/util/common/io/Json.java index d08ac8246..6a9c84f08 100644 --- a/src/main/java/com/volmit/adapt/util/Json.java +++ b/src/main/java/art/arcane/adapt/util/common/io/Json.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/src/main/java/com/volmit/adapt/util/LZString.java b/src/main/java/art/arcane/adapt/util/common/io/LZString.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/LZString.java rename to src/main/java/art/arcane/adapt/util/common/io/LZString.java index 0ce742628..fd4406ac6 100644 --- a/src/main/java/com/volmit/adapt/util/LZString.java +++ b/src/main/java/art/arcane/adapt/util/common/io/LZString.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; import java.util.*; diff --git a/src/main/java/com/volmit/adapt/util/PersistentJson.java b/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/PersistentJson.java rename to src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java index 234f0d56b..9553245d1 100644 --- a/src/main/java/com/volmit/adapt/util/PersistentJson.java +++ b/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; diff --git a/src/main/java/com/volmit/adapt/util/ReactiveFolder.java b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/ReactiveFolder.java rename to src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java index b86639523..9d68c8d14 100644 --- a/src/main/java/com/volmit/adapt/util/ReactiveFolder.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java @@ -16,13 +16,15 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; import art.arcane.amulet.io.FolderWatcher; import java.io.File; import java.util.List; +import art.arcane.adapt.util.common.function.Consumer3; + public class ReactiveFolder { private final File folder; private final Consumer3, List, List> hotload; diff --git a/src/main/java/com/volmit/adapt/util/SQLManager.java b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/SQLManager.java rename to src/main/java/art/arcane/adapt/util/common/io/SQLManager.java index d4395806d..f9a1329d8 100644 --- a/src/main/java/com/volmit/adapt/util/SQLManager.java +++ b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java @@ -1,7 +1,7 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/main/java/com/volmit/adapt/util/ShittyGsonDataClass.java b/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/ShittyGsonDataClass.java rename to src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java index 4c9b4e734..522b1478f 100644 --- a/src/main/java/com/volmit/adapt/util/ShittyGsonDataClass.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.io; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java b/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java new file mode 100644 index 000000000..128634936 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java @@ -0,0 +1,112 @@ +package art.arcane.adapt.util.mantle; + +import art.arcane.spatial.matter.Matter; +import art.arcane.spatial.matter.MatterSlice; +import art.arcane.volmlib.util.function.Consumer4; +import art.arcane.volmlib.util.parallel.HyperLockSupport; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.common.parallel.MultiBurst; +import art.arcane.adapt.util.mantle.io.IOWorker; + +import java.io.File; +import java.io.IOException; + +public class Mantle extends art.arcane.volmlib.util.mantle.Mantle { + public Mantle(File dataFolder, int worldHeight) { + super(dataFolder, worldHeight, DEFAULT_LOCK_SIZE, new HyperLockSupport(), MultiBurst.burst, new IOWorkerRegionIO(dataFolder, worldHeight)); + } + + @Override + protected TectonicPlate createRegion(int x, int z) { + return new TectonicPlate(getWorldHeight(), x, z); + } + + @Override + protected void setChunkValue(MantleChunk chunk, int x, int y, int z, T value) { + Matter matter = chunk.getOrCreate(y >> 4); + matter.slice(matter.getClass(value)).set(x & 15, y & 15, z & 15, value); + } + + @Override + protected void removeChunkValue(MantleChunk chunk, int x, int y, int z, Class type) { + Matter matter = chunk.getOrCreate(y >> 4); + matter.slice(type).set(x & 15, y & 15, z & 15, null); + } + + @Override + @SuppressWarnings("unchecked") + protected T getChunkValue(MantleChunk chunk, int x, int y, int z, Class type) { + return (T) chunk.getOrCreate(y >> 4) + .slice(type) + .get(x & 15, y & 15, z & 15); + } + + @Override + protected void iterateChunkValues(MantleChunk chunk, Class type, Consumer4 iterator) { + chunk.iterate(type, iterator); + } + + @Override + protected void deleteChunkSlice(MantleChunk chunk, Class type) { + chunk.deleteSlices(type); + } + + @Override + protected String formatDuration(double millis) { + return Form.duration(millis, 0); + } + + @Override + protected void onDebug(String message) { + Adapt.debug(message); + } + + @Override + protected void onWarn(String message) { + Adapt.warn(message); + } + + @Override + protected void onError(Throwable throwable) { + Adapt.error(throwable.getMessage() == null ? "Mantle error" : throwable.getMessage()); + throwable.printStackTrace(); + } + + @Override + protected void deleteTemporaryFiles() { + IO.delete(new File(getDataFolder(), ".tmp")); + } + + public void set(int x, int y, int z, MatterSlice slice) { + if (slice.isEmpty()) { + return; + } + + slice.iterateSync((xx, yy, zz, value) -> set(x + xx, y + yy, z + zz, value)); + } + + private static final class IOWorkerRegionIO implements RegionIO { + private final IOWorker worker; + + private IOWorkerRegionIO(File root, int worldHeight) { + this.worker = new IOWorker(root, worldHeight); + } + + @Override + public TectonicPlate read(String name) throws IOException { + return worker.read(name); + } + + @Override + public void write(String name, TectonicPlate region) throws IOException { + worker.write(name, region); + } + + @Override + public void close() throws IOException { + worker.close(); + } + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java b/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java new file mode 100644 index 000000000..e68e18cb5 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java @@ -0,0 +1,105 @@ +package art.arcane.adapt.util.mantle; + +import art.arcane.spatial.matter.Matter; +import art.arcane.spatial.matter.MatterSlice; +import art.arcane.spatial.matter.SpatialMatter; +import art.arcane.volmlib.util.function.Consumer4; +import art.arcane.volmlib.util.io.CountingDataInputStream; +import art.arcane.adapt.Adapt; +import org.jetbrains.annotations.Nullable; + +import java.io.DataOutputStream; +import java.io.IOException; + +public class MantleChunk extends art.arcane.volmlib.util.mantle.MantleChunk { + public MantleChunk(int sectionHeight, int x, int z) { + super(sectionHeight, x, z); + } + + public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { + super(version, sectionHeight, din); + } + + @Override + protected void onReadSectionFailure(int index, long start, long end, CountingDataInputStream din, IOException error) { + Adapt.warn("Failed to read mantle chunk section, skipping it."); + Adapt.error(error.getMessage() == null ? "Unknown mantle section read error" : error.getMessage()); + error.printStackTrace(); + TectonicPlate.addError(); + } + + @Override + protected Matter createSection() { + return new SpatialMatter(16, 16, 16); + } + + @Override + protected Matter readSection(CountingDataInputStream din) throws IOException { + try { + return Matter.readDin(din); + } catch (ClassNotFoundException e) { + throw new IOException("Failed to deserialize mantle section", e); + } + } + + @Override + protected void writeSection(Matter section, DataOutputStream dos) throws IOException { + section.writeDos(dos); + } + + @Override + protected void trimSection(Matter section) { + section.trimSlices(); + } + + @Override + protected boolean isSectionEmpty(Matter section) { + return section.getSliceMap().isEmpty(); + } + + @Override + public MantleChunk use() { + super.use(); + return this; + } + + public void copyFrom(MantleChunk chunk) { + super.copyFrom(chunk); + } + + @Nullable + @SuppressWarnings("unchecked") + public T get(int x, int y, int z, Class type) { + return (T) getOrCreate(y >> 4) + .slice(type) + .get(x & 15, y & 15, z & 15); + } + + public void iterate(Class type, Consumer4 iterator) { + for (int i = 0; i < sectionCount(); i++) { + int baseY = i << 4; + Matter section = get(i); + if (section == null) { + continue; + } + + MatterSlice slice = section.getSlice(type); + if (slice != null) { + slice.iterateSync((x, y, z, value) -> iterator.accept(x, y + baseY, z, value)); + } + } + } + + public void deleteSlices(Class type) { + for (int i = 0; i < sectionCount(); i++) { + Matter section = get(i); + if (section != null && section.hasSlice(type)) { + section.deleteSlice(type); + } + } + } + + public void trimSlices() { + trimSections(); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java b/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java new file mode 100644 index 000000000..1678ebffd --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java @@ -0,0 +1,60 @@ +package art.arcane.adapt.util.mantle; + +import art.arcane.volmlib.util.io.CountingDataInputStream; +import art.arcane.adapt.Adapt; + +import java.io.DataOutputStream; +import java.io.IOException; + +public class TectonicPlate extends art.arcane.volmlib.util.mantle.TectonicPlate { + public static final int MISSING = art.arcane.volmlib.util.mantle.TectonicPlate.MISSING; + public static final int CURRENT = art.arcane.volmlib.util.mantle.TectonicPlate.CURRENT; + + public TectonicPlate(int worldHeight, int x, int z) { + super(worldHeight, x, z); + } + + public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException { + super(worldHeight, din, versioned); + } + + @Override + protected void onReadChunkFailure(int index, long start, long end, CountingDataInputStream din, Throwable error) { + Adapt.warn("Failed to read mantle chunk, creating a new chunk instead."); + Adapt.error(error.getMessage() == null ? "Unknown mantle chunk read error" : error.getMessage()); + error.printStackTrace(); + } + + @Override + protected MantleChunk readChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { + return new MantleChunk(version, sectionHeight, din); + } + + @Override + protected MantleChunk createChunk(int sectionHeight, int x, int z) { + return new MantleChunk(sectionHeight, x, z); + } + + @Override + protected boolean isChunkInUse(MantleChunk chunk) { + return chunk.inUse(); + } + + @Override + protected void closeChunk(MantleChunk chunk) { + chunk.close(); + } + + @Override + protected void writeChunk(MantleChunk chunk, DataOutputStream dos) throws IOException { + chunk.write(dos); + } + + public static void addError() { + art.arcane.volmlib.util.mantle.TectonicPlate.addError(); + } + + public static boolean hasError() { + return art.arcane.volmlib.util.mantle.TectonicPlate.hasError(); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java b/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java new file mode 100644 index 000000000..38fbcaa1e --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java @@ -0,0 +1,96 @@ +package art.arcane.adapt.util.mantle.io; + +import art.arcane.spatial.mantle.MantleRegion; +import art.arcane.spatial.matter.Matter; +import art.arcane.spatial.matter.MatterSlice; +import art.arcane.volmlib.util.mantle.io.IOWorkerCodecSupport; +import art.arcane.volmlib.util.mantle.io.IOWorkerRuntimeSupport; +import art.arcane.volmlib.util.mantle.io.IOWorkerSupport; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.mantle.MantleChunk; +import art.arcane.adapt.util.mantle.TectonicPlate; + +import java.io.File; +import java.io.IOException; + +public class IOWorker { + private final File root; + private final int worldHeight; + private final IOWorkerSupport support; + private final IOWorkerRuntimeSupport runtime; + + public IOWorker(File root, int worldHeight) { + this.root = root; + this.worldHeight = worldHeight; + this.support = new IOWorkerSupport(root, 128, (name, millis) -> + Adapt.debug("Acquired mantle channel for " + name + " in " + millis + "ms") + ); + this.runtime = new IOWorkerRuntimeSupport(support, IOWorkerCodecSupport.identity()); + } + + public TectonicPlate read(String name) throws IOException { + try { + return runtime.read(name, (regionName, in) -> new TectonicPlate(worldHeight, in, true)); + } catch (IOException e) { + TectonicPlate migrated = readLegacy(name, e); + if (migrated != null) { + Adapt.warn("Migrated legacy mantle region " + name + " to shared mantle format."); + return migrated; + } + + throw e; + } + } + + public void write(String name, TectonicPlate plate) throws IOException { + runtime.write(name, plate, TectonicPlate::write); + } + + public void close() throws IOException { + support.close(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private TectonicPlate readLegacy(String name, IOException original) { + File file = new File(root, name); + if (!file.exists()) { + return null; + } + + try { + MantleRegion region = MantleRegion.read(worldHeight, file); + TectonicPlate plate = new TectonicPlate(worldHeight, region.getX(), region.getZ()); + int sectionCount = worldHeight >> 4; + + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + var legacyChunk = region.get(x, z); + if (legacyChunk == null) { + continue; + } + + MantleChunk chunk = plate.getOrCreate(x, z); + for (int section = 0; section < sectionCount; section++) { + Matter legacySection = legacyChunk.get(section); + if (legacySection == null || legacySection.getSliceMap().isEmpty()) { + continue; + } + + Matter target = chunk.getOrCreate(section); + target.clearSlices(); + + Matter copy = legacySection.copy(); + for (var entry : copy.getSliceMap().entrySet()) { + target.putSlice(entry.getKey(), (MatterSlice) entry.getValue()); + } + } + } + } + + return plate; + } catch (Throwable t) { + original.addSuppressed(t); + return null; + } + } +} diff --git a/src/main/java/com/volmit/adapt/util/ArrayType.java b/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/ArrayType.java rename to src/main/java/art/arcane/adapt/util/common/math/ArrayType.java index 466061aac..fb1faffcc 100644 --- a/src/main/java/com/volmit/adapt/util/ArrayType.java +++ b/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/CarveResult.java b/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/CarveResult.java rename to src/main/java/art/arcane/adapt/util/common/math/CarveResult.java index b3f9fd9cb..d6ec0eea9 100644 --- a/src/main/java/com/volmit/adapt/util/CarveResult.java +++ b/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import lombok.Value; diff --git a/src/main/java/com/volmit/adapt/util/CaveResult.java b/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/CaveResult.java rename to src/main/java/art/arcane/adapt/util/common/math/CaveResult.java index d93dbf85f..fb85180de 100644 --- a/src/main/java/com/volmit/adapt/util/CaveResult.java +++ b/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/util/ChunkPosition.java b/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/ChunkPosition.java rename to src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java index a0adf69d3..448f4b798 100644 --- a/src/main/java/com/volmit/adapt/util/ChunkPosition.java +++ b/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; public class ChunkPosition { private int x; diff --git a/src/main/java/com/volmit/adapt/util/DataPalette.java b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/DataPalette.java rename to src/main/java/art/arcane/adapt/util/common/math/DataPalette.java index df5367c5c..51599387e 100644 --- a/src/main/java/com/volmit/adapt/util/DataPalette.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -24,6 +24,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.nbt.NibbleArray; + public abstract class DataPalette implements Writable { private static final int DEFAULT_BITS_PER_BLOCK = 4; private static final int CAPACITY = 4096; diff --git a/src/main/java/com/volmit/adapt/util/Dimension.java b/src/main/java/art/arcane/adapt/util/common/math/Dimension.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/Dimension.java rename to src/main/java/art/arcane/adapt/util/common/math/Dimension.java index a1c2a649f..57123e48d 100644 --- a/src/main/java/com/volmit/adapt/util/Dimension.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Dimension.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; /** * Dimensions diff --git a/src/main/java/com/volmit/adapt/util/DimensionFace.java b/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/DimensionFace.java rename to src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java index 830efe6de..270aaa8f7 100644 --- a/src/main/java/com/volmit/adapt/util/DimensionFace.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; /** * Represents a dimension (coordinates not worlds) diff --git a/src/main/java/com/volmit/adapt/util/Direction.java b/src/main/java/art/arcane/adapt/util/common/math/Direction.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/Direction.java rename to src/main/java/art/arcane/adapt/util/common/math/Direction.java index 5ec7bfe3b..a2ed3b29d 100644 --- a/src/main/java/com/volmit/adapt/util/Direction.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Direction.java @@ -16,9 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; -import com.volmit.adapt.util.Cuboid.CuboidDirection; +import art.arcane.volmlib.util.data.Cuboid.CuboidDirection; +import art.arcane.volmlib.util.math.DOP; import org.bukkit.Axis; import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; @@ -28,6 +29,8 @@ import java.util.List; import java.util.Map; +import art.arcane.adapt.util.common.collection.GBiset; + /** * Directions * diff --git a/src/main/java/com/volmit/adapt/util/DoubleArrayUtils.java b/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/DoubleArrayUtils.java rename to src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java index 58d052dfa..12b8250b9 100644 --- a/src/main/java/com/volmit/adapt/util/DoubleArrayUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import com.google.common.util.concurrent.AtomicDoubleArray; diff --git a/src/main/java/com/volmit/adapt/util/HeightMap.java b/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/HeightMap.java rename to src/main/java/art/arcane/adapt/util/common/math/HeightMap.java index bfa55b995..3eec79aa9 100644 --- a/src/main/java/com/volmit/adapt/util/HeightMap.java +++ b/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import java.util.Arrays; diff --git a/src/main/java/com/volmit/adapt/util/IObjectPlacer.java b/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/IObjectPlacer.java rename to src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java index be25db2c5..eb806dc24 100644 --- a/src/main/java/com/volmit/adapt/util/IObjectPlacer.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import org.bukkit.block.data.BlockData; diff --git a/src/main/java/com/volmit/adapt/util/IPostBlockAccess.java b/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/IPostBlockAccess.java rename to src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java index afac7a871..2263a7a8e 100644 --- a/src/main/java/com/volmit/adapt/util/IPostBlockAccess.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import org.bukkit.block.data.BlockData; import org.bukkit.generator.ChunkGenerator.ChunkData; diff --git a/src/main/java/com/volmit/adapt/util/IRare.java b/src/main/java/art/arcane/adapt/util/common/math/IRare.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/IRare.java rename to src/main/java/art/arcane/adapt/util/common/math/IRare.java index 12dc3fae1..8a15c1c66 100644 --- a/src/main/java/com/volmit/adapt/util/IRare.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IRare.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; public interface IRare { static int get(Object v) { diff --git a/src/main/java/com/volmit/adapt/util/InterpolationType.java b/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/InterpolationType.java rename to src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java index 18c659e5f..30a612ec2 100644 --- a/src/main/java/com/volmit/adapt/util/InterpolationType.java +++ b/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; public enum InterpolationType { LINEAR, diff --git a/src/main/java/com/volmit/adapt/util/MaterialBlock.java b/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/MaterialBlock.java rename to src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java index d11af81c7..dfd6e9776 100644 --- a/src/main/java/com/volmit/adapt/util/MaterialBlock.java +++ b/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import org.bukkit.Location; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/util/MathHelper.java b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/MathHelper.java rename to src/main/java/art/arcane/adapt/util/common/math/MathHelper.java index 57877cf5c..73305e304 100644 --- a/src/main/java/com/volmit/adapt/util/MathHelper.java +++ b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java @@ -16,7 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; + +import art.arcane.volmlib.util.math.BlockPosition; import java.util.Random; import java.util.UUID; @@ -489,4 +491,4 @@ public static float j(float var0, float var1, float var2) { public static float k(float var0) { return var0 * var0; } -} \ No newline at end of file +} diff --git a/src/main/java/com/volmit/adapt/util/Sphere.java b/src/main/java/art/arcane/adapt/util/common/math/Sphere.java similarity index 87% rename from src/main/java/com/volmit/adapt/util/Sphere.java rename to src/main/java/art/arcane/adapt/util/common/math/Sphere.java index 07ea94bbb..105652cba 100644 --- a/src/main/java/com/volmit/adapt/util/Sphere.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Sphere.java @@ -1,6 +1,7 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.math.BlockPosition; import java.util.Iterator; diff --git a/src/main/java/com/volmit/adapt/util/VectorMath.java b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/VectorMath.java rename to src/main/java/art/arcane/adapt/util/common/math/VectorMath.java index f63943666..c000acea8 100644 --- a/src/main/java/com/volmit/adapt/util/VectorMath.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java @@ -16,8 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.CDou; import org.bukkit.Axis; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -28,6 +30,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.collection.GListAdapter; + /** * Vector utilities * diff --git a/src/main/java/com/volmit/adapt/util/VelocitySpeed.java b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/VelocitySpeed.java rename to src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java index 3675e7215..a23c7033c 100644 --- a/src/main/java/com/volmit/adapt/util/VelocitySpeed.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import org.bukkit.Input; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/util/Writable.java b/src/main/java/art/arcane/adapt/util/common/math/Writable.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Writable.java rename to src/main/java/art/arcane/adapt/util/common/math/Writable.java index 35eea50f5..babb7cc97 100644 --- a/src/main/java/com/volmit/adapt/util/Writable.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Writable.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.math; import java.io.DataInputStream; import java.io.DataOutputStream; diff --git a/src/main/java/com/volmit/adapt/util/AdvancementUtils.java b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/AdvancementUtils.java rename to src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java index ecb55d726..51f9a0e41 100644 --- a/src/main/java/com/volmit/adapt/util/AdvancementUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import com.fren_gor.ultimateAdvancementAPI.UltimateAdvancementAPI; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; @@ -6,8 +6,8 @@ import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementFrameTypeWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.packets.PacketPlayOutAdvancementsWrapper; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import com.google.common.base.Preconditions; import org.bukkit.Material; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/util/Area.java b/src/main/java/art/arcane/adapt/util/common/misc/Area.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/Area.java rename to src/main/java/art/arcane/adapt/util/common/misc/Area.java index 9a3ce1d89..db8639c7d 100644 --- a/src/main/java/com/volmit/adapt/util/Area.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Area.java @@ -16,9 +16,10 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.data.Cuboid; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; @@ -30,6 +31,8 @@ import java.util.Random; +import art.arcane.adapt.util.reflect.registries.Particles; + /** * Used to Create an instance of a spherical area based on a central location * Great for efficiently checking if an entity is within a spherical area. @@ -250,4 +253,4 @@ public Location random() { return location.clone().add(x, y, z); } -} \ No newline at end of file +} diff --git a/src/main/java/com/volmit/adapt/util/Chunker.java b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/Chunker.java rename to src/main/java/art/arcane/adapt/util/common/misc/Chunker.java index 4d2cb7d3a..1f9629100 100644 --- a/src/main/java/com/volmit/adapt/util/Chunker.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java @@ -16,7 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; + +import art.arcane.volmlib.util.scheduling.ChronoLatch; +import art.arcane.volmlib.util.scheduling.Callback; +import art.arcane.volmlib.util.scheduling.Contained; import java.util.List; import java.util.concurrent.ExecutorService; @@ -24,6 +28,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import art.arcane.adapt.util.common.scheduling.J; + public class Chunker { private final List q; private ExecutorService executor; diff --git a/src/main/java/com/volmit/adapt/util/CustomModel.java b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/CustomModel.java rename to src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java index 2dbd524ff..fb1b6be48 100644 --- a/src/main/java/com/volmit/adapt/util/CustomModel.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java @@ -1,12 +1,13 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.version.Version; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.version.Version; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.config.ConfigFileSupport; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -14,7 +15,9 @@ import java.io.File; import java.io.IOException; -import static com.volmit.adapt.Adapt.instance; +import art.arcane.adapt.util.common.io.Json; + +import static art.arcane.adapt.Adapt.instance; public record CustomModel(Material material, int model, NamespacedKey modelKey) { public static final NamespacedKey EMPTY_KEY = NamespacedKey.minecraft("empty"); diff --git a/src/main/java/com/volmit/adapt/util/DependsOn.java b/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/DependsOn.java rename to src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java index 1843e0be0..2f5f45a45 100644 --- a/src/main/java/com/volmit/adapt/util/DependsOn.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/DirtyString.java b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/DirtyString.java rename to src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java index 49db8d497..65d9792f5 100644 --- a/src/main/java/com/volmit/adapt/util/DirtyString.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java @@ -16,11 +16,14 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.ChatColor; +import art.arcane.adapt.util.common.format.HiddenStringUtils; +import art.arcane.adapt.util.common.io.Json; + public class DirtyString { public static String write(Object data) { diff --git a/src/main/java/com/volmit/adapt/util/DontObfuscate.java b/src/main/java/art/arcane/adapt/util/common/misc/DontObfuscate.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/DontObfuscate.java rename to src/main/java/art/arcane/adapt/util/common/misc/DontObfuscate.java index 6f5305119..0a6150ca9 100644 --- a/src/main/java/com/volmit/adapt/util/DontObfuscate.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DontObfuscate.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/Impulse.java b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/Impulse.java rename to src/main/java/art/arcane/adapt/util/common/misc/Impulse.java index bee490623..adf13b3a3 100644 --- a/src/main/java/com/volmit/adapt/util/Impulse.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import org.bukkit.Location; import org.bukkit.entity.Entity; @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.math.VectorMath; + public class Impulse { private final List ignore; private double radius; diff --git a/src/main/java/com/volmit/adapt/util/Info.java b/src/main/java/art/arcane/adapt/util/common/misc/Info.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Info.java rename to src/main/java/art/arcane/adapt/util/common/misc/Info.java index b63f1a57e..c121cb6e4 100644 --- a/src/main/java/com/volmit/adapt/util/Info.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Info.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import org.bukkit.Bukkit; diff --git a/src/main/java/com/volmit/adapt/util/MaxNumber.java b/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/MaxNumber.java rename to src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java index 229056aec..620b1e679 100644 --- a/src/main/java/com/volmit/adapt/util/MaxNumber.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/MinNumber.java b/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/MinNumber.java rename to src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java index 67259120a..d91da5206 100644 --- a/src/main/java/com/volmit/adapt/util/MinNumber.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/NMSVersion.java b/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/NMSVersion.java rename to src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java index 8dc8d22e3..8a0d89650 100644 --- a/src/main/java/com/volmit/adapt/util/NMSVersion.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/util/Shrinkwrap.java b/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Shrinkwrap.java rename to src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java index cb9fc7d06..a57f9d556 100644 --- a/src/main/java/com/volmit/adapt/util/Shrinkwrap.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; public class Shrinkwrap { private T t; diff --git a/src/main/java/com/volmit/adapt/util/SoundPlayer.java b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java similarity index 93% rename from src/main/java/com/volmit/adapt/util/SoundPlayer.java rename to src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java index 977352bbd..bbc6ab110 100644 --- a/src/main/java/com/volmit/adapt/util/SoundPlayer.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.misc; -import com.volmit.adapt.AdaptConfig; +import art.arcane.adapt.AdaptConfig; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.bukkit.Location; @@ -14,6 +14,8 @@ import java.util.Collection; import java.util.List; +import art.arcane.adapt.util.common.scheduling.J; + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class SoundPlayer { diff --git a/src/main/java/com/volmit/adapt/util/ByteArrayTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/ByteArrayTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java index cb4adaae9..a928d7fe1 100644 --- a/src/main/java/com/volmit/adapt/util/ByteArrayTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Byte_Array tag. diff --git a/src/main/java/com/volmit/adapt/util/ByteTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/ByteTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java index fbec382ec..a75c6887c 100644 --- a/src/main/java/com/volmit/adapt/util/ByteTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Byte tag. diff --git a/src/main/java/com/volmit/adapt/util/CompoundTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/CompoundTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java index f2a15a28a..e74f5ea2e 100644 --- a/src/main/java/com/volmit/adapt/util/CompoundTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.util.Map; diff --git a/src/main/java/com/volmit/adapt/util/DoubleTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/DoubleTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java index d16cefa57..2aa7bcc36 100644 --- a/src/main/java/com/volmit/adapt/util/DoubleTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Double tag. diff --git a/src/main/java/com/volmit/adapt/util/EndTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/EndTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java index 6117cf184..32a50b18a 100644 --- a/src/main/java/com/volmit/adapt/util/EndTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_End tag. diff --git a/src/main/java/com/volmit/adapt/util/FloatTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/FloatTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java index 303d2e20b..d46d19db5 100644 --- a/src/main/java/com/volmit/adapt/util/FloatTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Float tag. diff --git a/src/main/java/com/volmit/adapt/util/IntArrayTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/IntArrayTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java index ffa247e96..d271a96e8 100644 --- a/src/main/java/com/volmit/adapt/util/IntArrayTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.util.Arrays; diff --git a/src/main/java/com/volmit/adapt/util/IntTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/IntTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java index 1e3163524..92cf04d23 100644 --- a/src/main/java/com/volmit/adapt/util/IntTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Int tag. diff --git a/src/main/java/com/volmit/adapt/util/ListTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/ListTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java index 18abf0ddd..a06f6ea14 100644 --- a/src/main/java/com/volmit/adapt/util/ListTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.util.Collections; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/util/LongTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/LongTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java index 3b64ac587..b3220c1ed 100644 --- a/src/main/java/com/volmit/adapt/util/LongTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Long tag. diff --git a/src/main/java/com/volmit/adapt/util/NBTConstants.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/NBTConstants.java rename to src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java index ec3538afe..da7623f93 100644 --- a/src/main/java/com/volmit/adapt/util/NBTConstants.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/volmit/adapt/util/NBTInputStream.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/NBTInputStream.java rename to src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java index c0643a33f..4deb6fd56 100644 --- a/src/main/java/com/volmit/adapt/util/NBTInputStream.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.io.Closeable; import java.io.DataInputStream; diff --git a/src/main/java/com/volmit/adapt/util/NBTOutputStream.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/NBTOutputStream.java rename to src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java index 8cb0d4975..228898691 100644 --- a/src/main/java/com/volmit/adapt/util/NBTOutputStream.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.io.Closeable; import java.io.DataOutputStream; diff --git a/src/main/java/com/volmit/adapt/util/NBTUtils.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/NBTUtils.java rename to src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java index 0c9ae9495..e3afc41d7 100644 --- a/src/main/java/com/volmit/adapt/util/NBTUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * Changes : Neil Wightman - Support 19133 Tag_Int_Array tag diff --git a/src/main/java/com/volmit/adapt/util/NibbleArray.java b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/NibbleArray.java rename to src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java index d4507f51f..36b603e83 100644 --- a/src/main/java/com/volmit/adapt/util/NibbleArray.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -25,6 +25,8 @@ import java.util.Arrays; import java.util.StringJoiner; +import art.arcane.adapt.util.common.math.Writable; + public class NibbleArray implements Writable { private static final int[] MASKS = new int[8]; diff --git a/src/main/java/com/volmit/adapt/util/ShortTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/ShortTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java index 7d2e13951..c73827b47 100644 --- a/src/main/java/com/volmit/adapt/util/ShortTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_Short tag. diff --git a/src/main/java/com/volmit/adapt/util/StringTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/StringTag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java index cb0791cbb..c25dfb8e3 100644 --- a/src/main/java/com/volmit/adapt/util/StringTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * The TAG_String tag. diff --git a/src/main/java/com/volmit/adapt/util/Tag.java b/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/Tag.java rename to src/main/java/art/arcane/adapt/util/common/nbt/Tag.java index 65d6a3e36..267eaa66c 100644 --- a/src/main/java/com/volmit/adapt/util/Tag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.nbt; /** * Represents a single NBT tag. diff --git a/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java b/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java new file mode 100644 index 000000000..4592279ce --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java @@ -0,0 +1,9 @@ +package art.arcane.adapt.util.common.parallel; + +import java.util.concurrent.ExecutorService; + +public class BurstExecutor extends art.arcane.volmlib.util.parallel.BurstExecutorSupport { + public BurstExecutor(ExecutorService executor, int burstSizeEstimate) { + super(executor, burstSizeEstimate, Throwable::printStackTrace); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java new file mode 100644 index 000000000..209daac7b --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java @@ -0,0 +1,39 @@ +package art.arcane.adapt.util.common.parallel; + +import art.arcane.adapt.Adapt; +import java.util.concurrent.ExecutorService; +import java.util.function.IntSupplier; + +public class MultiBurst extends art.arcane.volmlib.util.parallel.MultiBurstSupport { + private static final long TIMEOUT = Long.getLong("adapt.burst.timeout", 15000); + public static MultiBurst burst = new MultiBurst(Runtime.getRuntime().availableProcessors()); + + public MultiBurst(int tc) { + this(() -> tc); + } + + public MultiBurst(IntSupplier parallelism) { + super("Adapt Workgroup", Thread.MAX_PRIORITY, parallelism, i -> Math.max(i, 1), System::currentTimeMillis, + e -> { + Adapt.info("Exception encountered in burst thread"); + e.printStackTrace(); + }, + Adapt::info, + Adapt::warn, + TIMEOUT); + } + + public ExecutorService getService() { + return service(); + } + + @Override + public BurstExecutor burst(int estimate) { + return new BurstExecutor(service(), estimate); + } + + @Override + public BurstExecutor burst() { + return burst(16); + } +} diff --git a/src/main/java/com/volmit/adapt/util/AdaptService.java b/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/AdaptService.java rename to src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java index 8a7932457..740b8f710 100644 --- a/src/main/java/com/volmit/adapt/util/AdaptService.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.event.Listener; public interface AdaptService extends Listener { diff --git a/src/main/java/com/volmit/adapt/util/IActivator.java b/src/main/java/art/arcane/adapt/util/common/plugin/IActivator.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/IActivator.java rename to src/main/java/art/arcane/adapt/util/common/plugin/IActivator.java index 4263bed91..a8b920798 100644 --- a/src/main/java/com/volmit/adapt/util/IActivator.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/IActivator.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; public interface IActivator { diff --git a/src/main/java/com/volmit/adapt/util/Instance.java b/src/main/java/art/arcane/adapt/util/common/plugin/Instance.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Instance.java rename to src/main/java/art/arcane/adapt/util/common/plugin/Instance.java index 5689aa2e9..4d970adbf 100644 --- a/src/main/java/com/volmit/adapt/util/Instance.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/Instance.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/Metrics.java b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/Metrics.java rename to src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java index 5629299d7..40265eb92 100644 --- a/src/main/java/com/volmit/adapt/util/Metrics.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/util/Permission.java b/src/main/java/art/arcane/adapt/util/common/plugin/Permission.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Permission.java rename to src/main/java/art/arcane/adapt/util/common/plugin/Permission.java index e087bddb6..3102f740e 100644 --- a/src/main/java/com/volmit/adapt/util/Permission.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/Permission.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/VolmitPlugin.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/VolmitPlugin.java rename to src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java index 5cadf70bd..e36d8b369 100644 --- a/src/main/java/com/volmit/adapt/util/VolmitPlugin.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java @@ -16,11 +16,13 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.events.ReflectiveEvents; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.reflect.events.ReflectiveEvents; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -35,6 +37,11 @@ import java.lang.reflect.Modifier; import java.util.List; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.project.command.Command; +import art.arcane.adapt.util.project.command.Control; +import art.arcane.adapt.util.project.command.IController; + public abstract class VolmitPlugin extends JavaPlugin implements Listener { public static boolean bad = false; private KMap controllers; diff --git a/src/main/java/com/volmit/adapt/util/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/VolmitSender.java rename to src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index 26885c1f1..c4b9bf587 100644 --- a/src/main/java/com/volmit/adapt/util/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -17,19 +17,21 @@ * */ -package com.volmit.adapt.util; +package art.arcane.adapt.util.common.plugin; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.RNG; -//import com.volmit.Adapt.Adapt; -//import com.volmit.Adapt.util.collection.List; -//import com.volmit.Adapt.util.collection.Map; -//import com.volmit.Adapt.util.decree.DecreeParameter; -//import com.volmit.Adapt.util.decree.virtual.VirtualDecreeCommand; +//import art.arcane.Adapt.Adapt; +//import art.arcane.Adapt.util.collection.List; +//import art.arcane.Adapt.util.collection.Map; +//import art.arcane.Adapt.util.decree.DecreeParameter; +//import art.arcane.Adapt.util.decree.virtual.VirtualDecreeCommand; import static art.arcane.amulet.MagicalSugar.*; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameter; -import com.volmit.adapt.util.decree.virtual.VirtualDecreeCommand; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.decree.DecreeParameter; +import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; @@ -52,6 +54,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.project.command.Command; +import art.arcane.adapt.util.project.command.CommandDummy; + /** * Represents a volume sender. A command sender with extra crap in its * @@ -87,7 +94,7 @@ public VolmitSender(CommandSender s, String tag) { } public static long getTick() { - return com.volmit.adapt.util.M.ms() / 16; + return art.arcane.volmlib.util.math.M.ms() / 16; } public static String pulse(String colorA, String colorB, double speed) { diff --git a/src/main/java/com/volmit/adapt/util/reflect/Reflect.java b/src/main/java/art/arcane/adapt/util/common/reflect/Reflect.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/reflect/Reflect.java rename to src/main/java/art/arcane/adapt/util/common/reflect/Reflect.java index 04cb0eaac..7e17405e9 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/Reflect.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/Reflect.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect; +package art.arcane.adapt.util.reflect; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/volmit/adapt/util/reflect/WrappedField.java b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/reflect/WrappedField.java rename to src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java index 60bade744..cf5792e26 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/WrappedField.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java @@ -1,9 +1,11 @@ -package com.volmit.adapt.util.reflect; +package art.arcane.adapt.util.reflect; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import java.lang.reflect.Field; +import art.arcane.adapt.util.common.format.C; + public class WrappedField { private final Field field; diff --git a/src/main/java/com/volmit/adapt/util/reflect/WrappedReturningMethod.java b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java similarity index 91% rename from src/main/java/com/volmit/adapt/util/reflect/WrappedReturningMethod.java rename to src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java index 0d8f6a029..2522412ed 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/WrappedReturningMethod.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java @@ -1,11 +1,13 @@ -package com.volmit.adapt.util.reflect; +package art.arcane.adapt.util.reflect; -import com.volmit.adapt.Adapt; +import art.arcane.adapt.Adapt; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import art.arcane.adapt.util.common.format.C; + public final class WrappedReturningMethod { private final Method method; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/ReflectiveEvents.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java similarity index 90% rename from src/main/java/com/volmit/adapt/util/reflect/events/ReflectiveEvents.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java index 11e553662..d4c94e66d 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/ReflectiveEvents.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java @@ -1,14 +1,14 @@ -package com.volmit.adapt.util.reflect.events; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.Reflect; -import com.volmit.adapt.util.reflect.events.api.Event; -import com.volmit.adapt.util.reflect.events.api.ReflectiveHandler; -import com.volmit.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; -import com.volmit.adapt.util.reflect.events.api.entity.EntityDismountEvent; -import com.volmit.adapt.util.reflect.events.api.entity.EntityMountEvent; +package art.arcane.adapt.util.reflect.events; + +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.reflect.Reflect; +import art.arcane.adapt.util.reflect.events.api.Event; +import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; +import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; +import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; +import art.arcane.adapt.util.reflect.events.api.entity.EntityMountEvent; import lombok.NonNull; import org.bukkit.event.EventException; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/Event.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/Event.java similarity index 53% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/Event.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/Event.java index c6e8ce83a..46e4e742f 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/Event.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/Event.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.events.api; +package art.arcane.adapt.util.reflect.events.api; public interface Event { boolean isAsynchronous(); diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/ReflectiveHandler.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/ReflectiveHandler.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/ReflectiveHandler.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/ReflectiveHandler.java index 2b2197d00..ff5b2bc20 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/ReflectiveHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/ReflectiveHandler.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.events.api; +package art.arcane.adapt.util.reflect.events.api; import org.bukkit.event.EventPriority; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EndermanAttackPlayerEvent.java similarity index 84% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EndermanAttackPlayerEvent.java index b2affe7d9..5b3506d60 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EndermanAttackPlayerEvent.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.events.api.entity; +package art.arcane.adapt.util.reflect.events.api.entity; import org.bukkit.entity.Enderman; import org.bukkit.entity.Player; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityDismountEvent.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityDismountEvent.java similarity index 80% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityDismountEvent.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityDismountEvent.java index 59f7bbc07..28b423749 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityDismountEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityDismountEvent.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.events.api.entity; +package art.arcane.adapt.util.reflect.events.api.entity; import org.bukkit.entity.Entity; import org.bukkit.event.Cancellable; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityEvent.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityEvent.java similarity index 60% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityEvent.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityEvent.java index ee15f5212..cc2eee00b 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityEvent.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.util.reflect.events.api.entity; +package art.arcane.adapt.util.reflect.events.api.entity; -import com.volmit.adapt.util.reflect.events.api.Event; +import art.arcane.adapt.util.reflect.events.api.Event; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; diff --git a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityMountEvent.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityMountEvent.java similarity index 79% rename from src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityMountEvent.java rename to src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityMountEvent.java index fe3939160..add1551fb 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/events/api/entity/EntityMountEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityMountEvent.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.events.api.entity; +package art.arcane.adapt.util.reflect.events.api.entity; import org.bukkit.entity.Entity; import org.bukkit.event.Cancellable; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/Attributes.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Attributes.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/reflect/registries/Attributes.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/Attributes.java index 3b9d21282..369d98a02 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/Attributes.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Attributes.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.attribute.Attribute; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/Enchantments.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Enchantments.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/reflect/registries/Enchantments.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/Enchantments.java index ea122e71a..7fc4af2d2 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/Enchantments.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Enchantments.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.enchantments.Enchantment; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/EntityTypes.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/EntityTypes.java similarity index 78% rename from src/main/java/com/volmit/adapt/util/reflect/registries/EntityTypes.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/EntityTypes.java index c526fa196..0dae179fc 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/EntityTypes.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/EntityTypes.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.entity.EntityType; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/ItemFlags.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/ItemFlags.java similarity index 80% rename from src/main/java/com/volmit/adapt/util/reflect/registries/ItemFlags.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/ItemFlags.java index 61e5fbfb4..0b366b6f7 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/ItemFlags.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/ItemFlags.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.inventory.ItemFlag; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/Materials.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Materials.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/reflect/registries/Materials.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/Materials.java index f6f1236ba..554647aff 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/Materials.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Materials.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.Material; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/Particles.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Particles.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/reflect/registries/Particles.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/Particles.java index 7106eeb92..2d6219588 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/Particles.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/Particles.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.Particle; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/PotionEffectTypes.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionEffectTypes.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/reflect/registries/PotionEffectTypes.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionEffectTypes.java index cd468f587..b97c7a27c 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/PotionEffectTypes.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionEffectTypes.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.potion.PotionEffectType; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/PotionTypes.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionTypes.java similarity index 92% rename from src/main/java/com/volmit/adapt/util/reflect/registries/PotionTypes.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionTypes.java index e1b62817d..caba20d74 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/PotionTypes.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionTypes.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; import org.bukkit.potion.PotionType; diff --git a/src/main/java/com/volmit/adapt/util/reflect/registries/RegistryUtil.java b/src/main/java/art/arcane/adapt/util/common/reflect/registries/RegistryUtil.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/reflect/registries/RegistryUtil.java rename to src/main/java/art/arcane/adapt/util/common/reflect/registries/RegistryUtil.java index 77249a8dd..6dffde1ae 100644 --- a/src/main/java/com/volmit/adapt/util/reflect/registries/RegistryUtil.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/registries/RegistryUtil.java @@ -1,6 +1,6 @@ -package com.volmit.adapt.util.reflect.registries; +package art.arcane.adapt.util.reflect.registries; -import com.volmit.adapt.util.cache.AtomicCache; +import art.arcane.adapt.util.cache.AtomicCache; import lombok.NonNull; import manifold.rt.api.util.Pair; import org.bukkit.Bukkit; diff --git a/src/main/java/com/volmit/adapt/util/J.java b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java similarity index 74% rename from src/main/java/com/volmit/adapt/util/J.java rename to src/main/java/art/arcane/adapt/util/common/scheduling/J.java index 6851cbbd9..46597da72 100644 --- a/src/main/java/com/volmit/adapt/util/J.java +++ b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java @@ -16,38 +16,50 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; +package art.arcane.adapt.util.common.scheduling; + +import art.arcane.volmlib.util.math.FinalInteger; +import art.arcane.volmlib.util.scheduling.AR; +import art.arcane.volmlib.util.scheduling.JSupport; +import art.arcane.volmlib.util.scheduling.SR; +import art.arcane.volmlib.util.scheduling.SchedulerBridge; +import art.arcane.volmlib.util.scheduling.StartupQueueSupport; +import art.arcane.adapt.Adapt; import org.bukkit.Bukkit; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import art.arcane.adapt.util.common.function.NastyFunction; +import art.arcane.adapt.util.common.function.NastyFuture; +import art.arcane.adapt.util.common.function.NastyRunnable; +import art.arcane.adapt.util.common.parallel.MultiBurst; + public class J { private static final int tid = 0; - private static List afterStartup = new ArrayList<>(); - private static List afterStartupAsync = new ArrayList<>(); - private static boolean started = false; + private static final StartupQueueSupport STARTUP_QUEUE = new StartupQueueSupport(); + + static { + SchedulerBridge.setSyncScheduler(J::s); + SchedulerBridge.setDelayedSyncScheduler(J::s); + SchedulerBridge.setAsyncScheduler(J::a); + SchedulerBridge.setDelayedAsyncScheduler(J::a); + SchedulerBridge.setSyncRepeatingScheduler(J::sr); + SchedulerBridge.setAsyncRepeatingScheduler(J::ar); + SchedulerBridge.setCancelScheduler(J::car); + SchedulerBridge.setErrorHandler(Throwable::printStackTrace); + SchedulerBridge.setInfoLogger(Adapt::info); + } public static void dofor(int a, Function c, int ch, Consumer d) { - for (int i = a; c.apply(i); i += ch) { - c.apply(i); - } + JSupport.dofor(a, c, ch, d); } public static boolean doif(Supplier c, Runnable g) { - if (c.get()) { - g.run(); - return true; - } - - return false; + return JSupport.doif(c, g, null); } public static void a(Runnable a) { @@ -59,75 +71,38 @@ public static Future a(Callable a) { } public static void attemptAsync(NastyRunnable r) { - J.a(() -> J.attempt(r)); + JSupport.attemptAsync(r::run, J::a); } public static R attemptResult(NastyFuture r, R onError) { - try { - return r.run(); - } catch (Throwable e) { - e.printStackTrace(); - } - - return onError; + return JSupport.attemptResult(r::run, onError, Throwable::printStackTrace); } public static R attemptFunction(NastyFunction r, T param, R onError) { - try { - return r.run(param); - } catch (Throwable e) { - Adapt.verbose("Failed to run function: " + e.getMessage()); - } - - return onError; + return JSupport.attemptFunction(r::run, param, onError, e -> Adapt.verbose("Failed to run function: " + e.getMessage())); } public static boolean sleep(long ms) { - return J.attempt(() -> Thread.sleep(ms)); + return JSupport.sleep(ms); } public static boolean attempt(NastyRunnable r) { - return attemptCatch(r) == null; + return JSupport.attempt(r::run); } public static Throwable attemptCatch(NastyRunnable r) { - try { - r.run(); - } catch (Throwable e) { - return e; - } - - return null; + return JSupport.attemptCatch(r::run); } public static T attempt(Supplier t, T i) { - try { - return t.get(); - } catch (Throwable e) { - return i; - } + return JSupport.attempt(t::get, i, null); } /** * Dont call this unless you know what you are doing! */ public static void executeAfterStartupQueue() { - if (started) { - return; - } - - started = true; - - for (Runnable r : afterStartup) { - s(r); - } - - for (Runnable r : afterStartupAsync) { - a(r); - } - - afterStartup = null; - afterStartupAsync = null; + JSupport.executeAfterStartupQueue(STARTUP_QUEUE, J::s, J::a); } /** @@ -140,11 +115,7 @@ public static void executeAfterStartupQueue() { * @param r the runnable */ public static void ass(Runnable r) { - if (started) { - s(r); - } else { - afterStartup.add(r); - } + JSupport.enqueueAfterStartupSync(STARTUP_QUEUE, r, J::s); } /** @@ -157,11 +128,7 @@ public static void ass(Runnable r) { * @param r the runnable */ public static void asa(Runnable r) { - if (started) { - a(r); - } else { - afterStartupAsync.add(r); - } + JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a); } /** @@ -213,7 +180,7 @@ public static int sr(Runnable r, int interval) { public static void sr(Runnable r, int interval, int intervals) { FinalInteger fi = new FinalInteger(0); - new SR() { + new SR(interval) { @Override public void run() { fi.add(1); @@ -268,7 +235,7 @@ public static int ar(Runnable r, int interval) { public static void ar(Runnable r, int interval, int intervals) { FinalInteger fi = new FinalInteger(0); - new AR() { + new AR(interval) { @Override public void run() { fi.add(1); diff --git a/src/main/java/com/volmit/adapt/util/Command.java b/src/main/java/art/arcane/adapt/util/project/command/Command.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Command.java rename to src/main/java/art/arcane/adapt/util/project/command/Command.java index a5b0c035e..bda71e596 100644 --- a/src/main/java/com/volmit/adapt/util/Command.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Command.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/CommandDummy.java b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/CommandDummy.java rename to src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java index 3a905424e..e1839d859 100644 --- a/src/main/java/com/volmit/adapt/util/CommandDummy.java +++ b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java @@ -17,7 +17,7 @@ * */ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import org.bukkit.Server; import org.bukkit.command.CommandSender; diff --git a/src/main/java/com/volmit/adapt/util/Control.java b/src/main/java/art/arcane/adapt/util/project/command/Control.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Control.java rename to src/main/java/art/arcane/adapt/util/project/command/Control.java index 13f8db378..cdc15dc2d 100644 --- a/src/main/java/com/volmit/adapt/util/Control.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Control.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/art/arcane/adapt/util/project/command/FCommand.java b/src/main/java/art/arcane/adapt/util/project/command/FCommand.java new file mode 100644 index 000000000..1d49177d6 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/project/command/FCommand.java @@ -0,0 +1,5 @@ +package art.arcane.adapt.util.command; + +public interface FCommand { + +} diff --git a/src/main/java/com/volmit/adapt/util/command/FConst.java b/src/main/java/art/arcane/adapt/util/project/command/FConst.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/command/FConst.java rename to src/main/java/art/arcane/adapt/util/project/command/FConst.java index 4c244e80d..17d10e3ac 100644 --- a/src/main/java/com/volmit/adapt/util/command/FConst.java +++ b/src/main/java/art/arcane/adapt/util/project/command/FConst.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.command; +package art.arcane.adapt.util.command; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; diff --git a/src/main/java/com/volmit/adapt/util/command/FService.java b/src/main/java/art/arcane/adapt/util/project/command/FService.java similarity index 59% rename from src/main/java/com/volmit/adapt/util/command/FService.java rename to src/main/java/art/arcane/adapt/util/project/command/FService.java index 50526b997..d8278abe4 100644 --- a/src/main/java/com/volmit/adapt/util/command/FService.java +++ b/src/main/java/art/arcane/adapt/util/project/command/FService.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.command; +package art.arcane.adapt.util.command; public interface FService { void start(); diff --git a/src/main/java/com/volmit/adapt/util/command/Feedback.java b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java similarity index 86% rename from src/main/java/com/volmit/adapt/util/command/Feedback.java rename to src/main/java/art/arcane/adapt/util/project/command/Feedback.java index ead28264a..b9df86f4d 100644 --- a/src/main/java/com/volmit/adapt/util/command/Feedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java @@ -1,48 +1,48 @@ -package com.volmit.adapt.util.command; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.VolmitSender; -import lombok.Builder; -import lombok.Data; -import lombok.Singular; -import lombok.experimental.Accessors; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.List; - -@Builder -@Data -@Accessors(chain = true, fluent = true) -public class Feedback { - @Singular - private List sounds; - @Singular - private List messages; - - public void send(CommandSender serverOrPlayer) { - if (serverOrPlayer instanceof Player p) { - for (SoundFeedback i : sounds) { - i.play(p); - } - } - - Component prefix = Component.text("[").color(NamedTextColor.GRAY) - .append(Component.text("Adapt").color(NamedTextColor.DARK_RED)) - .append(Component.text("] ")); - for (TextComponent i : messages) { - Adapt.audiences.sender(serverOrPlayer).sendMessage(Component.text() - .append(prefix) - .append(i) - .build()); - } - } - - public void send(VolmitSender sender) { - send(sender.getS()); - } -} +package art.arcane.adapt.util.command; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.plugin.VolmitSender; +import lombok.Builder; +import lombok.Data; +import lombok.Singular; +import lombok.experimental.Accessors; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.List; + +@Builder +@Data +@Accessors(chain = true, fluent = true) +public class Feedback { + @Singular + private List sounds; + @Singular + private List messages; + + public void send(CommandSender serverOrPlayer) { + if (serverOrPlayer instanceof Player p) { + for (SoundFeedback i : sounds) { + i.play(p); + } + } + + Component prefix = Component.text("[").color(NamedTextColor.GRAY) + .append(Component.text("Adapt").color(NamedTextColor.DARK_RED)) + .append(Component.text("] ")); + for (TextComponent i : messages) { + Adapt.audiences.sender(serverOrPlayer).sendMessage(Component.text() + .append(prefix) + .append(i) + .build()); + } + } + + public void send(VolmitSender sender) { + send(sender.getS()); + } +} diff --git a/src/main/java/com/volmit/adapt/util/ICommand.java b/src/main/java/art/arcane/adapt/util/project/command/ICommand.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/ICommand.java rename to src/main/java/art/arcane/adapt/util/project/command/ICommand.java index b3bf860e8..8307e88f7 100644 --- a/src/main/java/com/volmit/adapt/util/ICommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/ICommand.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import java.util.List; diff --git a/src/main/java/com/volmit/adapt/util/IController.java b/src/main/java/art/arcane/adapt/util/project/command/IController.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/IController.java rename to src/main/java/art/arcane/adapt/util/project/command/IController.java index d06d23ee8..00b05239c 100644 --- a/src/main/java/com/volmit/adapt/util/IController.java +++ b/src/main/java/art/arcane/adapt/util/project/command/IController.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import org.bukkit.event.Listener; diff --git a/src/main/java/com/volmit/adapt/util/MortarCommand.java b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/MortarCommand.java rename to src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java index 7d72a54c3..c24ee2e06 100644 --- a/src/main/java/com/volmit/adapt/util/MortarCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java @@ -16,9 +16,9 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; -import com.volmit.adapt.util.collection.KList; +import art.arcane.volmlib.util.collection.KList; import org.bukkit.Sound; import java.lang.reflect.Field; @@ -26,6 +26,9 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; + /** * Represents a pawn command * diff --git a/src/main/java/com/volmit/adapt/util/MortarPermission.java b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/MortarPermission.java rename to src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java index ecaf27e30..9cd51813b 100644 --- a/src/main/java/com/volmit/adapt/util/MortarPermission.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import org.bukkit.command.CommandSender; @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; +import art.arcane.adapt.util.common.plugin.Permission; + public abstract class MortarPermission { private MortarPermission parent; diff --git a/src/main/java/com/volmit/adapt/util/MortarSender.java b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/MortarSender.java rename to src/main/java/art/arcane/adapt/util/project/command/MortarSender.java index 505ea08be..ca7f10e08 100644 --- a/src/main/java/com/volmit/adapt/util/MortarSender.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import lombok.Getter; import lombok.Setter; @@ -33,6 +33,8 @@ import java.util.Set; import java.util.UUID; +import art.arcane.adapt.util.common.format.C; + /** * Represents a volume sender. A command sender with extra crap in it * diff --git a/src/main/java/com/volmit/adapt/util/RouterCommand.java b/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/RouterCommand.java rename to src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java index 3fcbe9b71..f74788058 100644 --- a/src/main/java/com/volmit/adapt/util/RouterCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; diff --git a/src/main/java/com/volmit/adapt/util/command/SoundFeedback.java b/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java similarity index 88% rename from src/main/java/com/volmit/adapt/util/command/SoundFeedback.java rename to src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java index a3646248e..859dc62dc 100644 --- a/src/main/java/com/volmit/adapt/util/command/SoundFeedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.command; +package art.arcane.adapt.util.command; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/com/volmit/adapt/util/VirtualCommand.java b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java similarity index 94% rename from src/main/java/com/volmit/adapt/util/VirtualCommand.java rename to src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java index 2d2d64830..a4f9e75c3 100644 --- a/src/main/java/com/volmit/adapt/util/VirtualCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java @@ -16,11 +16,12 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.command; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.reflect.V; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.command.CommandSender; @@ -30,6 +31,9 @@ import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; + /** * Represents a virtual command. A chain of iterative processing through * subcommands. diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigAdvanced.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigAdvanced.java similarity index 87% rename from src/main/java/com/volmit/adapt/util/config/ConfigAdvanced.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigAdvanced.java index 0edf82468..ccccb08e1 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigAdvanced.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigAdvanced.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigDescription.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java similarity index 88% rename from src/main/java/com/volmit/adapt/util/config/ConfigDescription.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java index 0b8ef4917..e68214984 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigDescription.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigDoc.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java similarity index 89% rename from src/main/java/com/volmit/adapt/util/config/ConfigDoc.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java index a5aff1c3a..08da6d7e4 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigDoc.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigDocumentation.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/config/ConfigDocumentation.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java index b61ab4523..119fc2203 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigDocumentation.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import java.lang.reflect.Field; import java.util.ArrayList; diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigFileSupport.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java similarity index 97% rename from src/main/java/com/volmit/adapt/util/config/ConfigFileSupport.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java index 1dc598201..23ece7785 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigFileSupport.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java @@ -1,10 +1,10 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import com.google.gson.JsonElement; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.ConfigRewriteReporter; -import com.volmit.adapt.util.IO; -import com.volmit.adapt.util.Json; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.project.config.ConfigRewriteReporter; +import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.common.io.Json; import java.io.File; import java.io.IOException; diff --git a/src/main/java/com/volmit/adapt/util/config/ConfigMigrationManager.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/config/ConfigMigrationManager.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java index e7419377b..23865b89a 100644 --- a/src/main/java/com/volmit/adapt/util/config/ConfigMigrationManager.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java @@ -1,7 +1,7 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.IO; +import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.io.IO; import java.io.File; import java.io.FileInputStream; diff --git a/src/main/java/com/volmit/adapt/util/ConfigRewriteReporter.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/ConfigRewriteReporter.java rename to src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java index 18b4c9b38..c73b2214b 100644 --- a/src/main/java/com/volmit/adapt/util/ConfigRewriteReporter.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java @@ -1,9 +1,9 @@ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.config; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.config.ConfigFileSupport; import java.io.File; import java.util.ArrayList; diff --git a/src/main/java/com/volmit/adapt/util/Denv.java b/src/main/java/art/arcane/adapt/util/project/config/Denv.java similarity index 95% rename from src/main/java/com/volmit/adapt/util/Denv.java rename to src/main/java/art/arcane/adapt/util/project/config/Denv.java index 946730a6b..8cb747eff 100644 --- a/src/main/java/com/volmit/adapt/util/Denv.java +++ b/src/main/java/art/arcane/adapt/util/project/config/Denv.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.config; public class Denv { diff --git a/src/main/java/com/volmit/adapt/util/Desc.java b/src/main/java/art/arcane/adapt/util/project/config/Desc.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Desc.java rename to src/main/java/art/arcane/adapt/util/project/config/Desc.java index 2d8dc6b20..436baa234 100644 --- a/src/main/java/com/volmit/adapt/util/Desc.java +++ b/src/main/java/art/arcane/adapt/util/project/config/Desc.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.config; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/Required.java b/src/main/java/art/arcane/adapt/util/project/config/Required.java similarity index 96% rename from src/main/java/com/volmit/adapt/util/Required.java rename to src/main/java/art/arcane/adapt/util/project/config/Required.java index f7b29d9f4..f4f979fd8 100644 --- a/src/main/java/com/volmit/adapt/util/Required.java +++ b/src/main/java/art/arcane/adapt/util/project/config/Required.java @@ -16,7 +16,7 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util; +package art.arcane.adapt.util.project.config; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/src/main/java/com/volmit/adapt/util/config/TomlCodec.java b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java similarity index 99% rename from src/main/java/com/volmit/adapt/util/config/TomlCodec.java rename to src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java index 555d232be..081b93f0d 100644 --- a/src/main/java/com/volmit/adapt/util/config/TomlCodec.java +++ b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java @@ -1,7 +1,7 @@ -package com.volmit.adapt.util.config; +package art.arcane.adapt.util.config; import com.google.gson.JsonElement; -import com.volmit.adapt.util.Json; +import art.arcane.adapt.util.common.io.Json; import com.moandjiezana.toml.Toml; import java.io.IOException; diff --git a/src/main/java/com/volmit/adapt/util/redis/RedisSync.java b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java similarity index 87% rename from src/main/java/com/volmit/adapt/util/redis/RedisSync.java rename to src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java index 133d80c2d..f4da54172 100644 --- a/src/main/java/com/volmit/adapt/util/redis/RedisSync.java +++ b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java @@ -1,14 +1,14 @@ -package com.volmit.adapt.util.redis; +package art.arcane.adapt.util.project.redis; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.AdaptConfig; -import com.volmit.adapt.api.world.PlayerData; -import com.volmit.adapt.util.redis.codec.Codec; -import com.volmit.adapt.util.redis.codec.DataMessage; -import com.volmit.adapt.util.redis.codec.DataRequest; -import com.volmit.adapt.util.redis.codec.Message; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.util.project.redis.codec.Codec; +import art.arcane.adapt.util.project.redis.codec.DataMessage; +import art.arcane.adapt.util.project.redis.codec.DataRequest; +import art.arcane.adapt.util.project.redis.codec.Message; import io.lettuce.core.RedisClient; import io.lettuce.core.pubsub.api.reactive.ChannelMessage; import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; diff --git a/src/main/java/com/volmit/adapt/util/secret/SecretSplash.java b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java similarity index 98% rename from src/main/java/com/volmit/adapt/util/secret/SecretSplash.java rename to src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java index a8677429f..12289cb9f 100644 --- a/src/main/java/com/volmit/adapt/util/secret/SecretSplash.java +++ b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java @@ -16,11 +16,11 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ -package com.volmit.adapt.util.secret; +package art.arcane.adapt.util.secret; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.C; -import com.volmit.adapt.util.collection.KList; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.collection.KList; import lombok.Getter; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/com/volmit/adapt/util/AR.java b/src/main/java/com/volmit/adapt/util/AR.java deleted file mode 100644 index eba27578c..000000000 --- a/src/main/java/com/volmit/adapt/util/AR.java +++ /dev/null @@ -1,40 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public abstract class AR implements Runnable, CancellableTask { - private int id = 0; - - public AR() { - this(0); - } - - public AR(int interval) { - id = J.ar(this, interval); - } - - @Override - public void cancel() { - J.car(id); - } - - public int getId() { - return id; - } -} diff --git a/src/main/java/com/volmit/adapt/util/AtomicAverage.java b/src/main/java/com/volmit/adapt/util/AtomicAverage.java deleted file mode 100644 index b1da46e31..000000000 --- a/src/main/java/com/volmit/adapt/util/AtomicAverage.java +++ /dev/null @@ -1,101 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.google.common.util.concurrent.AtomicDoubleArray; - -/** - * Provides an incredibly fast averaging object. It swaps values from a sum - * using an array. Averages do not use any form of looping. An average of 10,000 - * entries is the same speed as an average with 5 entries. - * - * @author cyberpwn - */ -public class AtomicAverage { - protected AtomicDoubleArray values; - protected int cursor; - private double average; - private double lastSum; - private boolean dirty; - private boolean brandNew; - - /** - * Create an average holder - * - * @param size the size of entries to keep - */ - public AtomicAverage(int size) { - values = new AtomicDoubleArray(size); - DoubleArrayUtils.fill(values, 0); - brandNew = true; - average = 0; - cursor = 0; - lastSum = 0; - dirty = false; - } - - /** - * Put a value into the average (rolls over if full) - * - * @param i the value - */ - public void put(double i) { - - dirty = true; - - if (brandNew) { - DoubleArrayUtils.fill(values, i); - lastSum = size() * i; - brandNew = false; - return; - } - - double current = values.get(cursor); - lastSum = (lastSum - current) + i; - values.set(cursor, i); - cursor = cursor + 1 < size() ? cursor + 1 : 0; - } - - /** - * Get the current average - * - * @return the average - */ - public double getAverage() { - if (dirty) { - calculateAverage(); - return getAverage(); - } - - return average; - } - - private void calculateAverage() { - average = lastSum / (double) size(); - dirty = false; - } - - public int size() { - return values.length(); - } - - public boolean isDirty() { - return dirty; - } -} diff --git a/src/main/java/com/volmit/adapt/util/AtomicRollingSequence.java b/src/main/java/com/volmit/adapt/util/AtomicRollingSequence.java deleted file mode 100644 index ebe9da578..000000000 --- a/src/main/java/com/volmit/adapt/util/AtomicRollingSequence.java +++ /dev/null @@ -1,110 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.util.collection.KList; - -public class AtomicRollingSequence extends AtomicAverage { - private double median; - private double max; - private double min; - private boolean dirtyMedian; - private int dirtyExtremes; - private boolean precision; - - public AtomicRollingSequence(int size) { - super(size); - median = 0; - min = 0; - max = 0; - setPrecision(false); - } - - public double addLast(int amt) { - double f = 0; - - for (int i = 0; i < Math.min(values.length(), amt); i++) { - f += values.get(i); - } - - return f; - } - - public boolean isPrecision() { - return precision; - } - - public void setPrecision(boolean p) { - this.precision = p; - } - - public double getMin() { - if (dirtyExtremes > (isPrecision() ? 0 : values.length())) { - resetExtremes(); - } - - return min; - } - - public double getMax() { - if (dirtyExtremes > (isPrecision() ? 0 : values.length())) { - resetExtremes(); - } - - return max; - } - - public double getMedian() { - if (dirtyMedian) { - recalculateMedian(); - } - - return median; - } - - private void recalculateMedian() { - double[] a = new double[values.length()]; - for (int i = 0; i < a.length; i++) { - a[i] = values.get(i); - } - median = new KList().forceAdd(a).sort().middleValue(); - dirtyMedian = false; - } - - public void resetExtremes() { - max = Integer.MIN_VALUE; - min = Integer.MAX_VALUE; - - for (int i = 0; i < values.length(); i++) { - double v = values.get(i); - max = M.max(max, v); - min = M.min(min, v); - } - - dirtyExtremes = 0; - } - - public void put(double i) { - super.put(i); - dirtyMedian = true; - dirtyExtremes++; - max = M.max(max, i); - min = M.min(min, i); - } -} diff --git a/src/main/java/com/volmit/adapt/util/Average.java b/src/main/java/com/volmit/adapt/util/Average.java deleted file mode 100644 index 408f26b6b..000000000 --- a/src/main/java/com/volmit/adapt/util/Average.java +++ /dev/null @@ -1,99 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * Provides an incredibly fast averaging object. It swaps values from a sum - * using an array. Averages do not use any form of looping. An average of 10,000 - * entries is the same speed as an average with 5 entries. - * - * @author cyberpwn - */ -public class Average { - protected double[] values; - protected int cursor; - private double average; - private double lastSum; - private boolean dirty; - private boolean brandNew; - - /** - * Create an average holder - * - * @param size the size of entries to keep - */ - public Average(int size) { - values = new double[size]; - DoubleArrayUtils.fill(values, 0); - brandNew = true; - average = 0; - cursor = 0; - lastSum = 0; - dirty = false; - } - - /** - * Put a value into the average (rolls over if full) - * - * @param i the value - */ - public void put(double i) { - - dirty = true; - - if (brandNew) { - DoubleArrayUtils.fill(values, i); - lastSum = size() * i; - brandNew = false; - return; - } - - double current = values[cursor]; - lastSum = (lastSum - current) + i; - values[cursor] = i; - cursor = cursor + 1 < size() ? cursor + 1 : 0; - } - - /** - * Get the current average - * - * @return the average - */ - public double getAverage() { - if (dirty) { - calculateAverage(); - return getAverage(); - } - - return average; - } - - private void calculateAverage() { - average = lastSum / (double) size(); - dirty = false; - } - - public int size() { - return values.length; - } - - public boolean isDirty() { - return dirty; - } -} diff --git a/src/main/java/com/volmit/adapt/util/BlockPosition.java b/src/main/java/com/volmit/adapt/util/BlockPosition.java deleted file mode 100644 index e06c3dbec..000000000 --- a/src/main/java/com/volmit/adapt/util/BlockPosition.java +++ /dev/null @@ -1,67 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@ToString -public class BlockPosition { - private int x; - private int y; - private int z; - - public BlockPosition(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } - - public boolean equals(Object o) { - if (o == null) { - return false; - } - - if (o instanceof BlockPosition ot) { - - return ot.x == x && ot.y == y && ot.z == z; - } - - return false; - } - - public int getChunkX() { - return x >> 4; - } - - public int getChunkZ() { - return z >> 4; - } - - public boolean is(int x, int z) { - return this.x == x && this.z == z; - } - - public boolean is(int x, int y, int z) { - return this.x == x && this.y == y && this.z == z; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Board.java b/src/main/java/com/volmit/adapt/util/Board.java deleted file mode 100644 index 5b343a622..000000000 --- a/src/main/java/com/volmit/adapt/util/Board.java +++ /dev/null @@ -1,148 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import lombok.NonNull; -import lombok.Setter; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.scoreboard.DisplaySlot; -import org.bukkit.scoreboard.Objective; -import org.bukkit.scoreboard.Scoreboard; -import org.bukkit.scoreboard.Team; - -import java.util.Collections; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * @author Missionary (missionarymc@gmail.com) - * @since 3/23/2018 - */ -public class Board { - - private static final String[] CACHED_ENTRIES = new String[C.values().length]; - - private static final Function APPLY_COLOR_TRANSLATION = s -> C.translateAlternateColorCodes('&', s); - - static { - IntStream.range(0, 15).forEach(i -> CACHED_ENTRIES[i] = C.values()[i].toString() + C.RESET); - } - - private final Player player; - private final Objective objective; - private final Team team; - @Setter - private BoardSettings boardSettings; - private boolean ready; - - @SuppressWarnings("deprecation") - public Board(@NonNull final Player player, final BoardSettings boardSettings) { - this.player = player; - this.boardSettings = boardSettings; - this.objective = this.getScoreboard().getObjective("board") == null ? this.getScoreboard().registerNewObjective("board", "dummy") : this.getScoreboard().getObjective("board"); - this.objective.setDisplaySlot(DisplaySlot.SIDEBAR); - this.team = this.getScoreboard().getTeam("board") == null ? this.getScoreboard().registerNewTeam("board") : this.getScoreboard().getTeam("board"); - this.team.setAllowFriendlyFire(true); - this.team.setCanSeeFriendlyInvisibles(false); - this.team.setPrefix(""); - this.team.setSuffix(""); - this.ready = true; - } - - public Scoreboard getScoreboard() { - return (player != null) ? player.getScoreboard() : null; - } - - public void remove() { - this.resetScoreboard(); - } - - public void update() { - // Checking if we are ready to start updating the Scoreboard. - if (!ready) { - return; - } - - // Making sure the player is connected. - if (!player.isOnline()) { - remove(); - return; - } - - // Making sure the Scoreboard Provider is set. - if (boardSettings == null) { - return; - } - - // Getting their Scoreboard display from the Scoreboard Provider. - final List entries = boardSettings.getBoardProvider().getLines(player).stream().map(APPLY_COLOR_TRANSLATION).collect(Collectors.toList()); - - if (boardSettings.getScoreDirection() == ScoreDirection.UP) { - Collections.reverse(entries); - } - - // Setting the Scoreboard title - String title = boardSettings.getBoardProvider().getTitle(player); - if (title.length() > 32) { - Bukkit.getLogger().warning("The title " + title + " is over 32 characters in length, substringing to prevent errors."); - title = title.substring(0, 32); - } - objective.setDisplayName(C.translateAlternateColorCodes('&', title)); - - // Clearing previous Scoreboard values if entry sizes don't match. - if (this.getScoreboard().getEntries().size() != entries.size()) - this.getScoreboard().getEntries().forEach(this::removeEntry); - - // Setting Scoreboard lines. - for (int i = 0; i < entries.size(); i++) { - String str = entries.get(i); - BoardEntry entry = BoardEntry.translateToEntry(str); - Team team = getScoreboard().getTeam(CACHED_ENTRIES[i]); - - if (team == null) { - team = this.getScoreboard().registerNewTeam(CACHED_ENTRIES[i]); - team.addEntry(team.getName()); - } - - team.setPrefix(entry.getPrefix()); - team.setSuffix(entry.getSuffix()); - - switch (boardSettings.getScoreDirection()) { - case UP: - objective.getScore(team.getName()).setScore(1 + i); - break; - case DOWN: - objective.getScore(team.getName()).setScore(15 - i); - break; - } - } - } - - public void removeEntry(String id) { - this.getScoreboard().resetScores(id); - } - - public void resetScoreboard() { - ready = false; - player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard()); - } -} diff --git a/src/main/java/com/volmit/adapt/util/BoardEntry.java b/src/main/java/com/volmit/adapt/util/BoardEntry.java deleted file mode 100644 index f8cd76a54..000000000 --- a/src/main/java/com/volmit/adapt/util/BoardEntry.java +++ /dev/null @@ -1,57 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import lombok.Getter; -import org.apache.commons.lang3.StringUtils; - -/** - * @author Missionary (missionarymc@gmail.com) - * @since 3/29/2018 - */ -public class BoardEntry { - - @Getter - private final String prefix, suffix; - - private BoardEntry(final String prefix, final String suffix) { - this.prefix = prefix; - this.suffix = suffix; - } - - public static BoardEntry translateToEntry(String input) { - if (input.isEmpty()) { - return new BoardEntry("", ""); - } - if (input.length() <= 16) { - return new BoardEntry(input, ""); - } else { - String prefix = input.substring(0, 16); - String suffix = ""; - - if (prefix.endsWith("\u00a7")) { - prefix = prefix.substring(0, prefix.length() - 1); - suffix = "\u00a7" + suffix; - } - - suffix = StringUtils.left(C.getLastColors(prefix) + suffix + input.substring(16), 16); - return new BoardEntry(prefix, suffix); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/BoardManager.java b/src/main/java/com/volmit/adapt/util/BoardManager.java deleted file mode 100644 index 47156752d..000000000 --- a/src/main/java/com/volmit/adapt/util/BoardManager.java +++ /dev/null @@ -1,105 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitTask; - -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -@DontObfuscate -public class BoardManager { - @DontObfuscate - private final JavaPlugin plugin; - @DontObfuscate - private final Map scoreboards; - @DontObfuscate - private final BukkitTask updateTask; - @DontObfuscate - private BoardSettings boardSettings; - - @DontObfuscate - public BoardManager(JavaPlugin plugin, BoardSettings boardSettings) { - this.plugin = plugin; - this.boardSettings = boardSettings; - this.scoreboards = new ConcurrentHashMap<>(); - this.updateTask = new BoardUpdateTask(this).runTaskTimer(plugin, 2L, 2L); - for (Player player : onlinePlayers()) { - setup(player); - } - } - - @DontObfuscate - public void setBoardSettings(BoardSettings boardSettings) { - this.boardSettings = boardSettings; - scoreboards.values().forEach(board -> board.setBoardSettings(boardSettings)); - } - - @DontObfuscate - public boolean hasBoard(Player player) { - return scoreboards.containsKey(player.getUniqueId()); - } - - @DontObfuscate - public Optional getBoard(Player player) { - return Optional.ofNullable(scoreboards.get(player.getUniqueId())); - } - - @DontObfuscate - public void setup(Player player) { - Optional.ofNullable(scoreboards.remove(player.getUniqueId())).ifPresent(Board::resetScoreboard); - if (Bukkit.getScoreboardManager() != null && player.getScoreboard().equals(Bukkit.getScoreboardManager().getMainScoreboard())) { - player.setScoreboard(Bukkit.getScoreboardManager().getNewScoreboard()); - } - scoreboards.put(player.getUniqueId(), new Board(player, boardSettings)); - } - - @DontObfuscate - public void remove(Player player) { - Optional.ofNullable(scoreboards.remove(player.getUniqueId())).ifPresent(Board::remove); - } - - @DontObfuscate - public Map getScoreboards() { - return Collections.unmodifiableMap(scoreboards); - } - - @DontObfuscate - public void onDisable() { - updateTask.cancel(); - for (Player player : onlinePlayers()) { - remove(player); - } - scoreboards.clear(); - } - - private Iterable onlinePlayers() { - if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { - return Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot(); - } - return plugin.getServer().getOnlinePlayers(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/BoardProvider.java b/src/main/java/com/volmit/adapt/util/BoardProvider.java deleted file mode 100644 index 18e9d16bd..000000000 --- a/src/main/java/com/volmit/adapt/util/BoardProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.entity.Player; - -import java.util.List; - -@DontObfuscate -public interface BoardProvider { - @DontObfuscate - String getTitle(Player player); - - @DontObfuscate - List getLines(Player player); -} diff --git a/src/main/java/com/volmit/adapt/util/BoardSettings.java b/src/main/java/com/volmit/adapt/util/BoardSettings.java deleted file mode 100644 index a4b36075b..000000000 --- a/src/main/java/com/volmit/adapt/util/BoardSettings.java +++ /dev/null @@ -1,33 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import lombok.Builder; -import lombok.Getter; - -@DontObfuscate -@Getter -@Builder -public class BoardSettings { - @DontObfuscate - private BoardProvider boardProvider; - - @DontObfuscate - private ScoreDirection scoreDirection; -} diff --git a/src/main/java/com/volmit/adapt/util/BoardUpdateTask.java b/src/main/java/com/volmit/adapt/util/BoardUpdateTask.java deleted file mode 100644 index 6bf38bfe2..000000000 --- a/src/main/java/com/volmit/adapt/util/BoardUpdateTask.java +++ /dev/null @@ -1,43 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import lombok.RequiredArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.scheduler.BukkitRunnable; - -import java.util.UUID; -import java.util.function.Predicate; - -/** - * @author Missionary (missionarymc@gmail.com) - * @since 5/31/2018 - */ -@RequiredArgsConstructor -public class BoardUpdateTask extends BukkitRunnable { - - private static final Predicate PLAYER_IS_ONLINE = uuid -> Bukkit.getPlayer(uuid) != null; - - private final BoardManager boardManager; - - @Override - public void run() { - boardManager.getScoreboards().entrySet().stream().filter(entrySet -> PLAYER_IS_ONLINE.test(entrySet.getKey())).forEach(entrySet -> entrySet.getValue().update()); - } -} diff --git a/src/main/java/com/volmit/adapt/util/BurstExecutor.java b/src/main/java/com/volmit/adapt/util/BurstExecutor.java deleted file mode 100644 index 9c312f9f3..000000000 --- a/src/main/java/com/volmit/adapt/util/BurstExecutor.java +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; - -public class BurstExecutor { - private final ExecutorService executor; - private final List> futures; - - public BurstExecutor(ExecutorService executor, int burstSizeEstimate) { - this.executor = executor; - futures = new ArrayList<>(burstSizeEstimate); - } - - public CompletableFuture queue(Runnable r) { - synchronized (futures) { - CompletableFuture c = CompletableFuture.runAsync(r, executor); - futures.add(c); - return c; - } - } - - public BurstExecutor queue(Runnable[] r) { - synchronized (futures) { - for (Runnable i : r) { - CompletableFuture c = CompletableFuture.runAsync(i, executor); - futures.add(c); - } - } - - return this; - } - - public void complete() { - synchronized (futures) { - try { - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(); - futures.clear(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/CDou.java b/src/main/java/com/volmit/adapt/util/CDou.java deleted file mode 100644 index ded39c473..000000000 --- a/src/main/java/com/volmit/adapt/util/CDou.java +++ /dev/null @@ -1,59 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class CDou { - private final double max; - private double number; - - public CDou(double max) { - number = 0; - this.max = max; - } - - public CDou set(double n) { - number = n; - circ(); - return this; - } - - public CDou add(double a) { - number += a; - circ(); - return this; - } - - public CDou sub(double a) { - number -= a; - circ(); - return this; - } - - public double get() { - return number; - } - - public void circ() { - if (number < 0) { - number = max - (Math.abs(number) > max ? max : Math.abs(number)); - } - - number = number % (max); - } -} diff --git a/src/main/java/com/volmit/adapt/util/Callback.java b/src/main/java/com/volmit/adapt/util/Callback.java deleted file mode 100644 index 3e2d9215a..000000000 --- a/src/main/java/com/volmit/adapt/util/Callback.java +++ /dev/null @@ -1,35 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * Callback for async workers - * - * @param the type of object to be returned in the runnable - * @author cyberpwn - */ -@FunctionalInterface -public interface Callback { - /** - * Called when the callback calls back... - * - * @param t the object to be called back - */ - void run(T t); -} diff --git a/src/main/java/com/volmit/adapt/util/CancellableTask.java b/src/main/java/com/volmit/adapt/util/CancellableTask.java deleted file mode 100644 index 07742af6c..000000000 --- a/src/main/java/com/volmit/adapt/util/CancellableTask.java +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public interface CancellableTask { - void cancel(); -} diff --git a/src/main/java/com/volmit/adapt/util/ChronoLatch.java b/src/main/java/com/volmit/adapt/util/ChronoLatch.java deleted file mode 100644 index 70106e5a0..000000000 --- a/src/main/java/com/volmit/adapt/util/ChronoLatch.java +++ /dev/null @@ -1,50 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class ChronoLatch { - private final long interval; - private long since; - - public ChronoLatch(long interval, boolean openedAtStart) { - this.interval = interval; - since = System.currentTimeMillis() - (openedAtStart ? interval * 2 : 0); - } - - public ChronoLatch(long interval) { - this(interval, true); - } - - public void flipDown() { - since = System.currentTimeMillis(); - } - - public boolean couldFlip() { - return System.currentTimeMillis() - since > interval; - } - - public boolean flip() { - if (System.currentTimeMillis() - since > interval) { - since = System.currentTimeMillis(); - return true; - } - - return false; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Contained.java b/src/main/java/com/volmit/adapt/util/Contained.java deleted file mode 100644 index 358cd73a4..000000000 --- a/src/main/java/com/volmit/adapt/util/Contained.java +++ /dev/null @@ -1,45 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.util.function.Function; - -public class Contained { - private T t; - - public Contained(T t) { - set(t); - } - - public Contained() { - this(null); - } - - public void mod(Function x) { - set(x.apply(t)); - } - - public T get() { - return t; - } - - public void set(T t) { - this.t = t; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Converter.java b/src/main/java/com/volmit/adapt/util/Converter.java deleted file mode 100644 index d33e8af4c..000000000 --- a/src/main/java/com/volmit/adapt/util/Converter.java +++ /dev/null @@ -1,29 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.io.File; - -public interface Converter { - String getInExtension(); - - String getOutExtension(); - - void convert(File in, File out); -} diff --git a/src/main/java/com/volmit/adapt/util/Cuboid.java b/src/main/java/com/volmit/adapt/util/Cuboid.java deleted file mode 100644 index 62a820585..000000000 --- a/src/main/java/com/volmit/adapt/util/Cuboid.java +++ /dev/null @@ -1,772 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.entity.Entity; - -import java.util.*; - -/** - * Cuboids - * - * @author cyberpwn - */ -public class Cuboid implements Iterable, Cloneable, ConfigurationSerializable { - protected final String worldName; - protected int x1, y1, z1; - protected int x2, y2, z2; - - /** - * Construct a Cuboid given two Location objects which represent any two corners - * of the Cuboid. - * - * @param l1 one of the corners - * @param l2 the other corner - */ - public Cuboid(Location l1, Location l2) { - if (!l1.getWorld().equals(l2.getWorld())) { - throw new IllegalArgumentException("locations must be on the same world"); - } - - worldName = l1.getWorld().getName(); - x1 = Math.min(l1.getBlockX(), l2.getBlockX()); - y1 = Math.min(l1.getBlockY(), l2.getBlockY()); - z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); - x2 = Math.max(l1.getBlockX(), l2.getBlockX()); - y2 = Math.max(l1.getBlockY(), l2.getBlockY()); - z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); - } - - /** - * Construct a one-block Cuboid at the given Location of the Cuboid. - * - * @param l1 location of the Cuboid - */ - public Cuboid(Location l1) { - this(l1, l1); - } - - /** - * Copy constructor. - * - * @param other the Cuboid to copy - */ - public Cuboid(Cuboid other) { - this(other.getWorld().getName(), other.x1, other.y1, other.z1, other.x2, other.y2, other.z2); - } - - /** - * Construct a Cuboid in the given World and xyz co-ordinates - * - * @param world the Cuboid's world - * @param x1 X co-ordinate of corner 1 - * @param y1 Y co-ordinate of corner 1 - * @param z1 Z co-ordinate of corner 1 - * @param x2 X co-ordinate of corner 2 - * @param y2 Y co-ordinate of corner 2 - * @param z2 Z co-ordinate of corner 2 - */ - public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2) { - this.worldName = world.getName(); - this.x1 = Math.min(x1, x2); - this.x2 = Math.max(x1, x2); - this.y1 = Math.min(y1, y2); - this.y2 = Math.max(y1, y2); - this.z1 = Math.min(z1, z2); - this.z2 = Math.max(z1, z2); - } - - /** - * Construct a Cuboid in the given world name and xyz co-ordinates. - * - * @param worldName the Cuboid's world name - * @param x1 X co-ordinate of corner 1 - * @param y1 Y co-ordinate of corner 1 - * @param z1 Z co-ordinate of corner 1 - * @param x2 X co-ordinate of corner 2 - * @param y2 Y co-ordinate of corner 2 - * @param z2 Z co-ordinate of corner 2 - */ - private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2) { - this.worldName = worldName; - this.x1 = Math.min(x1, x2); - this.x2 = Math.max(x1, x2); - this.y1 = Math.min(y1, y2); - this.y2 = Math.max(y1, y2); - this.z1 = Math.min(z1, z2); - this.z2 = Math.max(z1, z2); - } - - public Cuboid(Map map) { - worldName = (String) map.get("worldName"); - x1 = (Integer) map.get("x1"); - x2 = (Integer) map.get("x2"); - y1 = (Integer) map.get("y1"); - y2 = (Integer) map.get("y2"); - z1 = (Integer) map.get("z1"); - z2 = (Integer) map.get("z2"); - } - - public List getEntities() { - List en = new ArrayList<>(); - - for (Chunk i : getChunks()) { - for (Entity j : i.getEntities()) { - if (contains(j.getLocation())) { - en.add(j); - } - } - } - - return en; - } - - /** - * Set the locations - * - * @param l1 a - * @param l2 b - */ - public void set(Location l1, Location l2) { - x1 = Math.min(l1.getBlockX(), l2.getBlockX()); - y1 = Math.min(l1.getBlockY(), l2.getBlockY()); - z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); - x2 = Math.max(l1.getBlockX(), l2.getBlockX()); - y2 = Math.max(l1.getBlockY(), l2.getBlockY()); - z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); - } - - @Override - public Map serialize() { - Map map = new HashMap(); - map.put("worldName", worldName); - map.put("x1", x1); - map.put("y1", y1); - map.put("z1", z1); - map.put("x2", x2); - map.put("y2", y2); - map.put("z2", z2); - return map; - } - - public Cuboid flatten(int level) { - return new Cuboid(getWorld(), x1, level, z1, x2, level, z2); - } - - /** - * Get the Location of the lower northeast corner of the Cuboid (minimum XYZ - * co-ordinates). - * - * @return Location of the lower northeast corner - */ - public Location getLowerNE() { - return new Location(getWorld(), x1, y1, z1); - } - - /** - * Get the Location of the upper southwest corner of the Cuboid (maximum XYZ - * co-ordinates). - * - * @return Location of the upper southwest corner - */ - public Location getUpperSW() { - return new Location(getWorld(), x2, y2, z2); - } - - /** - * Get the the centre of the Cuboid - * - * @return Location at the centre of the Cuboid - */ - public Location getCenter() { - int x1 = getUpperX() + 1; - int y1 = getUpperY() + 1; - int z1 = getUpperZ() + 1; - return new Location(getWorld(), getLowerX() + (x1 - getLowerX()) / 2.0, getLowerY() + (y1 - getLowerY()) / 2.0, getLowerZ() + (z1 - getLowerZ()) / 2.0); - } - - /** - * Get the Cuboid's world. - * - * @return the World object representing this Cuboid's world - * @throws IllegalStateException if the world is not loaded - */ - public World getWorld() { - World world = Bukkit.getWorld(worldName); - if (world == null) { - throw new IllegalStateException("world '" + worldName + "' is not loaded"); - } - return world; - } - - /** - * Get the size of this Cuboid along the X axis - * - * @return Size of Cuboid along the X axis - */ - public int getSizeX() { - return (x2 - x1) + 1; - } - - /** - * Get the size of this Cuboid along the Y axis - * - * @return Size of Cuboid along the Y axis - */ - public int getSizeY() { - return (y2 - y1) + 1; - } - - /** - * Get the size of this Cuboid along the Z axis - * - * @return Size of Cuboid along the Z axis - */ - public int getSizeZ() { - return (z2 - z1) + 1; - } - - /** - * Get the cuboid dimensions - * - * @return the dimensions - */ - public Dimension getDimension() { - return new Dimension(getSizeX(), getSizeY(), getSizeZ()); - } - - /** - * Get the minimum X co-ordinate of this Cuboid - * - * @return the minimum X co-ordinate - */ - public int getLowerX() { - return x1; - } - - /** - * Get the minimum Y co-ordinate of this Cuboid - * - * @return the minimum Y co-ordinate - */ - public int getLowerY() { - return y1; - } - - /** - * Get the minimum Z co-ordinate of this Cuboid - * - * @return the minimum Z co-ordinate - */ - public int getLowerZ() { - return z1; - } - - /** - * Get the maximum X co-ordinate of this Cuboid - * - * @return the maximum X co-ordinate - */ - public int getUpperX() { - return x2; - } - - /** - * Get the maximum Y co-ordinate of this Cuboid - * - * @return the maximum Y co-ordinate - */ - public int getUpperY() { - return y2; - } - - /** - * Get the maximum Z co-ordinate of this Cuboid - * - * @return the maximum Z co-ordinate - */ - public int getUpperZ() { - return z2; - } - - /** - * Get the Blocks at the eight corners of the Cuboid. - * - * @return array of Block objects representing the Cuboid corners - */ - public Block[] corners() { - Block[] res = new Block[8]; - World w = getWorld(); - res[0] = w.getBlockAt(x1, y1, z1); - res[1] = w.getBlockAt(x1, y1, z2); - res[2] = w.getBlockAt(x1, y2, z1); - res[3] = w.getBlockAt(x1, y2, z2); - res[4] = w.getBlockAt(x2, y1, z1); - res[5] = w.getBlockAt(x2, y1, z2); - res[6] = w.getBlockAt(x2, y2, z1); - res[7] = w.getBlockAt(x2, y2, z2); - return res; - } - - /** - * Expand the Cuboid in the given direction by the given amount. Negative - * amounts will shrink the Cuboid in the given direction. Shrinking a cuboid's - * face past the opposite face is not an error and will return a valid Cuboid. - * - * @param dir the direction in which to expand - * @param amount the number of blocks by which to expand - * @return a new Cuboid expanded by the given direction and amount - */ - public Cuboid expand(CuboidDirection dir, int amount) { - switch (dir) { - case North: - return new Cuboid(worldName, x1 - amount, y1, z1, x2, y2, z2); - case South: - return new Cuboid(worldName, x1, y1, z1, x2 + amount, y2, z2); - case East: - return new Cuboid(worldName, x1, y1, z1 - amount, x2, y2, z2); - case West: - return new Cuboid(worldName, x1, y1, z1, x2, y2, z2 + amount); - case Down: - return new Cuboid(worldName, x1, y1 - amount, z1, x2, y2, z2); - case Up: - return new Cuboid(worldName, x1, y1, z1, x2, y2 + amount, z2); - default: - throw new IllegalArgumentException("invalid direction " + dir); - } - } - - public Cuboid expand(Direction dir, int amount) { - int ax = dir.toVector().getBlockX() == 1 ? amount : 0; - int sx = dir.toVector().getBlockX() == -1 ? -amount : 0; - int ay = dir.toVector().getBlockY() == 1 ? amount : 0; - int sy = dir.toVector().getBlockY() == -1 ? -amount : 0; - int az = dir.toVector().getBlockZ() == 1 ? amount : 0; - int sz = dir.toVector().getBlockZ() == -1 ? -amount : 0; - return new Cuboid(worldName, x1 + sx, y1 + sy, z1 + sz, x2 + ax, y2 + ay, z2 + az); - } - - /** - * Shift the Cuboid in the given direction by the given amount. - * - * @param dir the direction in which to shift - * @param amount the number of blocks by which to shift - * @return a new Cuboid shifted by the given direction and amount - */ - public Cuboid shift(CuboidDirection dir, int amount) { - return expand(dir, amount).expand(dir.opposite(), -amount); - } - - /** - * Outset (grow) the Cuboid in the given direction by the given amount. - * - * @param dir the direction in which to outset (must be Horizontal, Vertical, or - * Both) - * @param amount the number of blocks by which to outset - * @return a new Cuboid outset by the given direction and amount - */ - public Cuboid outset(CuboidDirection dir, int amount) { - Cuboid c; - switch (dir) { - case Horizontal: - c = expand(CuboidDirection.North, amount).expand(CuboidDirection.South, amount).expand(CuboidDirection.East, amount).expand(CuboidDirection.West, amount); - break; - case Vertical: - c = expand(CuboidDirection.Down, amount).expand(CuboidDirection.Up, amount); - break; - case Both: - c = outset(CuboidDirection.Horizontal, amount).outset(CuboidDirection.Vertical, amount); - break; - default: - throw new IllegalArgumentException("invalid direction " + dir); - } - return c; - } - - /** - * Inset (shrink) the Cuboid in the given direction by the given amount. - * Equivalent to calling outset() with a negative amount. - * - * @param dir the direction in which to inset (must be Horizontal, Vertical, or - * Both) - * @param amount the number of blocks by which to inset - * @return a new Cuboid inset by the given direction and amount - */ - public Cuboid inset(CuboidDirection dir, int amount) { - return outset(dir, -amount); - } - - /** - * Return true if the point at (x,y,z) is contained within this Cuboid. - * - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return true if the given point is within this Cuboid, false otherwise - */ - public boolean contains(int x, int y, int z) { - return x >= x1 && x <= x2 && y >= y1 && y <= y2 && z >= z1 && z <= z2; - } - - /** - * Check if the given Block is contained within this Cuboid. - * - * @param b the Block to check for - * @return true if the Block is within this Cuboid, false otherwise - */ - public boolean contains(Block b) { - return contains(b.getLocation()); - } - - /** - * Check if the given Location is contained within this Cuboid. - * - * @param l the Location to check for - * @return true if the Location is within this Cuboid, false otherwise - */ - public boolean contains(Location l) { - return worldName.equals(l.getWorld().getName()) && contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - } - - /** - * Get the volume of this Cuboid. - * - * @return the Cuboid volume, in blocks - */ - public int volume() { - return getSizeX() * getSizeY() * getSizeZ(); - } - - /** - * Get the average light level of all empty (air) blocks in the Cuboid. Returns - * 0 if there are no empty blocks. - * - * @return the average light level of this Cuboid - */ - public byte averageLightLevel() { - long total = 0; - int n = 0; - for (Block b : this) { - if (b.isEmpty()) { - total += b.getLightLevel(); - ++n; - } - } - return n > 0 ? (byte) (total / n) : 0; - } - - /** - * Contract the Cuboid, returning a Cuboid with any air around the edges - * removed, just large enough to include all non-air blocks. - * - * @return a new Cuboid with no external air blocks - */ - public Cuboid contract() { - return this.contract(CuboidDirection.Down).contract(CuboidDirection.South).contract(CuboidDirection.East).contract(CuboidDirection.Up).contract(CuboidDirection.North).contract(CuboidDirection.West); - } - - /** - * Contract the Cuboid in the given direction, returning a new Cuboid which has - * no exterior empty space. E.g. a direction of Down will push the top face - * downwards as much as possible. - * - * @param dir the direction in which to contract - * @return a new Cuboid contracted in the given direction - */ - public Cuboid contract(CuboidDirection dir) { - Cuboid face = getFace(dir.opposite()); - switch (dir) { - case Down: - while (face.containsOnly(Material.AIR) && face.getLowerY() > this.getLowerY()) { - face = face.shift(CuboidDirection.Down, 1); - } - return new Cuboid(worldName, x1, y1, z1, x2, face.getUpperY(), z2); - case Up: - while (face.containsOnly(Material.AIR) && face.getUpperY() < this.getUpperY()) { - face = face.shift(CuboidDirection.Up, 1); - } - return new Cuboid(worldName, x1, face.getLowerY(), z1, x2, y2, z2); - case North: - while (face.containsOnly(Material.AIR) && face.getLowerX() > this.getLowerX()) { - face = face.shift(CuboidDirection.North, 1); - } - return new Cuboid(worldName, x1, y1, z1, face.getUpperX(), y2, z2); - case South: - while (face.containsOnly(Material.AIR) && face.getUpperX() < this.getUpperX()) { - face = face.shift(CuboidDirection.South, 1); - } - return new Cuboid(worldName, face.getLowerX(), y1, z1, x2, y2, z2); - case East: - while (face.containsOnly(Material.AIR) && face.getLowerZ() > this.getLowerZ()) { - face = face.shift(CuboidDirection.East, 1); - } - return new Cuboid(worldName, x1, y1, z1, x2, y2, face.getUpperZ()); - case West: - while (face.containsOnly(Material.AIR) && face.getUpperZ() < this.getUpperZ()) { - face = face.shift(CuboidDirection.West, 1); - } - return new Cuboid(worldName, x1, y1, face.getLowerZ(), x2, y2, z2); - default: - throw new IllegalArgumentException("Invalid direction " + dir); - } - } - - /** - * Get the Cuboid representing the face of this Cuboid. The resulting Cuboid - * will be one block thick in the axis perpendicular to the requested face. - * - * @param dir which face of the Cuboid to get - * @return the Cuboid representing this Cuboid's requested face - */ - public Cuboid getFace(CuboidDirection dir) { - switch (dir) { - case Down: - return new Cuboid(worldName, x1, y1, z1, x2, y1, z2); - case Up: - return new Cuboid(worldName, x1, y2, z1, x2, y2, z2); - case North: - return new Cuboid(worldName, x1, y1, z1, x1, y2, z2); - case South: - return new Cuboid(worldName, x2, y1, z1, x2, y2, z2); - case East: - return new Cuboid(worldName, x1, y1, z1, x2, y2, z1); - case West: - return new Cuboid(worldName, x1, y1, z2, x2, y2, z2); - default: - throw new IllegalArgumentException("Invalid direction " + dir); - } - } - - /** - * Check if the Cuboid contains only blocks of the given type - * - * @param material the material to check for - * @return true if this Cuboid contains only blocks of the given type - */ - public boolean containsOnly(Material material) { - for (Block b : this) { - if (b.getType() != material) { - return false; - } - } - return true; - } - - /** - * Get the Cuboid big enough to hold both this Cuboid and the given one. - * - * @param other the other Cuboid to include - * @return a new Cuboid large enough to hold this Cuboid and the given Cuboid - */ - public Cuboid getBoundingCuboid(Cuboid other) { - if (other == null) { - return this; - } - - int xMin = Math.min(getLowerX(), other.getLowerX()); - int yMin = Math.min(getLowerY(), other.getLowerY()); - int zMin = Math.min(getLowerZ(), other.getLowerZ()); - int xMax = Math.max(getUpperX(), other.getUpperX()); - int yMax = Math.max(getUpperY(), other.getUpperY()); - int zMax = Math.max(getUpperZ(), other.getUpperZ()); - - return new Cuboid(worldName, xMin, yMin, zMin, xMax, yMax, zMax); - } - - /** - * Get a block relative to the lower NE point of the Cuboid. - * - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return the block at the given position - */ - public Block getRelativeBlock(int x, int y, int z) { - return getWorld().getBlockAt(x1 + x, y1 + y, z1 + z); - } - - /** - * Get a block relative to the lower NE point of the Cuboid in the given World. - * This version of getRelativeBlock() should be used if being called many times, - * to avoid excessive calls to getWorld(). - * - * @param w the World - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return the block at the given position - */ - public Block getRelativeBlock(World w, int x, int y, int z) { - return w.getBlockAt(x1 + x, y1 + y, z1 + z); - } - - /** - * Get a list of the chunks which are fully or partially contained in this - * cuboid. - * - * @return a list of Chunk objects - */ - public List getChunks() { - List res = new ArrayList(); - - World w = getWorld(); - int x1 = getLowerX() & ~0xf; - int x2 = getUpperX() & ~0xf; - int z1 = getLowerZ() & ~0xf; - int z2 = getUpperZ() & ~0xf; - for (int x = x1; x <= x2; x += 16) { - for (int z = z1; z <= z2; z += 16) { - res.add(w.getChunkAt(x >> 4, z >> 4)); - } - } - return res; - } - - /** - * Set all the blocks within the Cuboid to the given MaterialData, using a - * MassBlockUpdate object for fast updates. - * - * @param mat - * the MaterialData to set - * @param mbu - * the MassBlockUpdate object - */ - - /** - * Reset the light level of all blocks within this Cuboid. - */ - - /* - * (non-Javadoc) - * - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#clone() - */ - @Override - public Cuboid clone() throws CloneNotSupportedException { - return new Cuboid(this); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "Cuboid: " + worldName + "," + x1 + "," + y1 + "," + z1 + "=>" + x2 + "," + y2 + "," + z2; - } - - public enum CuboidDirection { - - North, - East, - South, - West, - Up, - Down, - Horizontal, - Vertical, - Both, - Unknown; - - public CuboidDirection opposite() { - switch (this) { - case North: - return South; - case East: - return West; - case South: - return North; - case West: - return East; - case Horizontal: - return Vertical; - case Vertical: - return Horizontal; - case Up: - return Down; - case Down: - return Up; - case Both: - return Both; - default: - return Unknown; - } - } - } - - public class CuboidIterator implements Iterator { - private final World w; - private final int baseX; - private final int baseY; - private final int baseZ; - private final int sizeX; - private final int sizeY; - private final int sizeZ; - private int x, y, z; - - public CuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2) { - this.w = w; - baseX = x1; - baseY = y1; - baseZ = z1; - sizeX = Math.abs(x2 - x1) + 1; - sizeY = Math.abs(y2 - y1) + 1; - sizeZ = Math.abs(z2 - z1) + 1; - x = y = z = 0; - } - - @Override - public boolean hasNext() { - return x < sizeX && y < sizeY && z < sizeZ; - } - - @Override - public Block next() { - Block b = w.getBlockAt(baseX + x, baseY + y, baseZ + z); - if (++x >= sizeX) { - x = 0; - if (++y >= sizeY) { - y = 0; - ++z; - } - } - return b; - } - - @Override - public void remove() { - // nop - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/CuboidException.java b/src/main/java/com/volmit/adapt/util/CuboidException.java deleted file mode 100644 index 16b5656a8..000000000 --- a/src/main/java/com/volmit/adapt/util/CuboidException.java +++ /dev/null @@ -1,32 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * Represents a cuboid exception - * - * @author cyberpwn - */ -public class CuboidException extends Exception { - private static final long serialVersionUID = 1L; - - public CuboidException(String string) { - super(string); - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/CustomOutputStream.java b/src/main/java/com/volmit/adapt/util/CustomOutputStream.java deleted file mode 100644 index 079a3c8a7..000000000 --- a/src/main/java/com/volmit/adapt/util/CustomOutputStream.java +++ /dev/null @@ -1,30 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.zip.GZIPOutputStream; - -public class CustomOutputStream extends GZIPOutputStream { - public CustomOutputStream(OutputStream out, int level) throws IOException { - super(out); - def.setLevel(level); - } -} diff --git a/src/main/java/com/volmit/adapt/util/DOP.java b/src/main/java/com/volmit/adapt/util/DOP.java deleted file mode 100644 index 1c7a84e8b..000000000 --- a/src/main/java/com/volmit/adapt/util/DOP.java +++ /dev/null @@ -1,35 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.util.Vector; - -public abstract class DOP { - private final String type; - - public DOP(String type) { - this.type = type; - } - - public abstract Vector op(Vector v); - - public String getType() { - return type; - } -} diff --git a/src/main/java/com/volmit/adapt/util/FastParticle.java b/src/main/java/com/volmit/adapt/util/FastParticle.java deleted file mode 100644 index ab0ece1f8..000000000 --- a/src/main/java/com/volmit/adapt/util/FastParticle.java +++ /dev/null @@ -1,179 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -/** - * Simple Bukkit Particles API with 1.7 to 1.13.2 support ! - *

- * You can find the project on GitHub - * - * @author MrMicky - */ -public final class FastParticle { - - private static final ParticleSender PARTICLE_SENDER; - - static { - if (FastReflection.optionalClass("org.bukkit.Particle$DustOptions").isPresent()) { - PARTICLE_SENDER = new ParticleSender.ParticleSender1_13(); - } else if (FastReflection.optionalClass("org.bukkit.Particle").isPresent()) { - PARTICLE_SENDER = new ParticleSender.ParticleSenderImpl(); - } else { - PARTICLE_SENDER = new ParticleSenderLegacy(); - } - } - - private FastParticle() { - throw new UnsupportedOperationException(); - } - - /* - * Worlds methods - */ - public static void spawnParticle(World world, ParticleType particle, Location location, int count) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count) { - spawnParticle(world, particle, x, y, z, count, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - T data) { - spawnParticle(world, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, - offsetZ, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ, double extra) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra) { - spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); - } - - public static void spawnParticle(World world, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); - } - - public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - sendParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - /* - * Player methods - */ - public static void spawnParticle(Player player, ParticleType particle, Location location, int count) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count) { - spawnParticle(player, particle, x, y, z, count, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - T data) { - spawnParticle(player, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, T data) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX, - double offsetY, double offsetZ, double extra) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra) { - spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); - } - - public static void spawnParticle(Player player, ParticleType particle, Location location, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); - } - - public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, T data) { - sendParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - private static void sendParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, - double offsetX, double offsetY, double offsetZ, double extra, Object data) { - if (!particle.isSupported()) { - throw new IllegalArgumentException("The particle '" + particle + "' is not compatible with your server version"); - } - - PARTICLE_SENDER.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } -} diff --git a/src/main/java/com/volmit/adapt/util/FastReflection.java b/src/main/java/com/volmit/adapt/util/FastReflection.java deleted file mode 100644 index 023a8a3c2..000000000 --- a/src/main/java/com/volmit/adapt/util/FastReflection.java +++ /dev/null @@ -1,77 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.Bukkit; - -import java.util.Optional; - -/** - * Small reflection class to use CraftBukkit and NMS - * - * @author MrMicky - */ -public final class FastReflection { - - public static final String OBC_PACKAGE = "org.bukkit.craftbukkit"; - public static final String NMS_PACKAGE = "net.minecraft.server"; - - public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1); - - private FastReflection() { - throw new UnsupportedOperationException(); - } - - public static String nmsClassName(String className) { - return NMS_PACKAGE + '.' + VERSION + '.' + className; - } - - public static Class nmsClass(String className) throws ClassNotFoundException { - return Class.forName(nmsClassName(className)); - } - - public static Optional> nmsOptionalClass(String className) { - return optionalClass(nmsClassName(className)); - } - - public static String obcClassName(String className) { - return OBC_PACKAGE + '.' + VERSION + '.' + className; - } - - public static Class obcClass(String className) throws ClassNotFoundException { - return Class.forName(obcClassName(className)); - } - - public static Optional> obcOptionalClass(String className) { - return optionalClass(obcClassName(className)); - } - - public static Optional> optionalClass(String className) { - try { - return Optional.of(Class.forName(className)); - } catch (ClassNotFoundException e) { - return Optional.empty(); - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static Object enumValueOf(Class enumClass, String enumName) { - return Enum.valueOf((Class) enumClass, enumName.toUpperCase()); - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/FinalInteger.java b/src/main/java/com/volmit/adapt/util/FinalInteger.java deleted file mode 100644 index 7877c923b..000000000 --- a/src/main/java/com/volmit/adapt/util/FinalInteger.java +++ /dev/null @@ -1,48 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * Represents a number that can be finalized and be changed - * - * @author cyberpwn - */ -public class FinalInteger extends Wrapper { - public FinalInteger(Integer t) { - super(t); - } - - /** - * Add to this value - * - * @param i the number to add to this value (value = value + i) - */ - public void add(int i) { - set(get() + i); - } - - /** - * Subtract from this value - * - * @param i the number to subtract from this value (value = value - i) - */ - public void sub(int i) { - set(get() - i); - } -} diff --git a/src/main/java/com/volmit/adapt/util/Form.java b/src/main/java/com/volmit/adapt/util/Form.java deleted file mode 100644 index e3d43f774..000000000 --- a/src/main/java/com/volmit/adapt/util/Form.java +++ /dev/null @@ -1,1352 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.math.BigInteger; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Form { - private static final String[] NAMES = new String[]{"Thousand", "Million", "Billion", "Trillion", "Quadrillion", "Quintillion", "Sextillion", "Septillion", "Octillion", "Nonillion", "Decillion", "Undecillion", "Duodecillion", "Tredecillion", "Quattuordecillion", "Quindecillion", "Sexdecillion", "Septendecillion", "Octodecillion", "Novemdecillion", "Vigintillion",}; - private static final BigInteger THOUSAND = BigInteger.valueOf(1000); - private static final NavigableMap MAP; - private static final LinkedHashMap roman_numerals = new LinkedHashMap(); - private static NumberFormat NF; - private static DecimalFormat DF; - - static { - MAP = new TreeMap(); - for (int i = 0; i < NAMES.length; i++) { - MAP.put(THOUSAND.pow(i + 1), NAMES[i]); - } - - roman_numerals.put("M", 1000); - roman_numerals.put("CM", 900); - roman_numerals.put("D", 500); - roman_numerals.put("CD", 400); - roman_numerals.put("C", 100); - roman_numerals.put("XC", 90); - roman_numerals.put("L", 50); - roman_numerals.put("XL", 40); - roman_numerals.put("X", 10); - roman_numerals.put("IX", 9); - roman_numerals.put("V", 5); - roman_numerals.put("IV", 4); - roman_numerals.put("I", 1); - } - - private static void instantiate() { - if (NF == null) { - NF = NumberFormat.getInstance(Locale.US); - } - } - - /** - * Scroll text - * - * @param smx the text - * @param viewport the viewport length - * @param time the timeline value - */ - public static String scroll(String smx, int viewport, long time) { - String src = Form.repeat(" ", viewport) + smx + Form.repeat(" ", viewport); - int len = src.length(); - int walk = (int) (time % (len - viewport)); - String base = src.substring(walk, M.min(walk + viewport, len - 1)); - base = base.length() < viewport ? base + Form.repeat(" ", (viewport - base.length()) - 3) : base; - - return base; - } - - /** - * Capitalize the first letter - * - * @param s the string - * @return the capitalized string - */ - public static String capitalize(String s) { - String roll = ""; - boolean f = true; - - for (Character i : s.trim().toCharArray()) { - if (f) { - roll += Character.toUpperCase(i); - f = false; - } else { - roll += i; - } - } - - return roll; - } - - /** - * Capitalize all words in the string - * - * @param s the string - * @return the capitalized string - */ - public static String capitalizeWords(String s) { - String rollx = ""; - - for (String i : s.trim().split(" ")) { - rollx += " " + capitalize(i.trim()); - } - - return rollx.substring(1); - } - - /** - * Hard word wrap - * - * @param s the words - * @param len the length per line - * @return the wrapped string - */ - public static String wrap(String s, int len) { - return wrap(s, len, null, false); - } - - /** - * Soft Word wrap - * - * @param s the string - * @param len the length to wrap - * @return the wrapped string - */ - public static String wrapWords(String s, int len) { - return wrap(s, len, null, true); - } - - public static String wrapWordsPrefixed(String s, String prefix, int len) { - return wrapPrefixed(s, prefix, len, null, true); - } - - /** - * Wrap words - * - * @param s the string - * @param len the wrap length - * @param newLineSep the new line seperator - * @param soft should it be soft wrapped or hard wrapped? - * @return the wrapped words - */ - public static String wrap(String s, int len, String newLineSep, boolean soft) { - return wrap(s, len, newLineSep, soft, " "); - } - - public static String wrapPrefixed(String s, String pref, int len, String newLineSep, boolean soft) { - return pref + wrapPrefixed(s, pref, len, newLineSep, soft, " ").replaceAll("\\Q\n\\E", "\n" + pref); - } - - /** - * Wrap words - * - * @param s the string - * @param len the length - * @param newLineSep the new line seperator - * @param soft soft or hard wrapping - * @param regex the regex - * @return the wrapped string - */ - public static String wrap(String s, int len, String newLineSep, boolean soft, String regex) { - if (s == null) { - return null; - } else { - if (newLineSep == null) { - newLineSep = "\n"; - } - - if (len < 1) { - len = 1; - } - - if (regex.trim().equals("")) { - regex = " "; - } - - Pattern arg4 = Pattern.compile(regex); - int arg5 = s.length(); - int arg6 = 0; - StringBuilder arg7 = new StringBuilder(arg5 + 32); - - while (arg6 < arg5) { - int arg8 = -1; - Matcher arg9 = arg4.matcher(s.substring(arg6, Math.min(arg6 + len + 1, arg5))); - if (arg9.find()) { - if (arg9.start() == 0) { - arg6 += arg9.end(); - continue; - } - - arg8 = arg9.start(); - } - - if (arg5 - arg6 <= len) { - break; - } - - while (arg9.find()) { - arg8 = arg9.start() + arg6; - } - - if (arg8 >= arg6) { - arg7.append(s, arg6, arg8); - arg7.append(newLineSep); - arg6 = arg8 + 1; - } else if (soft) { - arg7.append(s, arg6, len + arg6); - arg7.append(newLineSep); - arg6 += len; - } else { - arg9 = arg4.matcher(s.substring(arg6 + len)); - if (arg9.find()) { - arg8 = arg9.start() + arg6 + len; - } - - if (arg8 >= 0) { - arg7.append(s, arg6, arg8); - arg7.append(newLineSep); - arg6 = arg8 + 1; - } else { - arg7.append(s.substring(arg6)); - arg6 = arg5; - } - } - } - - arg7.append(s.substring(arg6)); - return arg7.toString(); - } - } - - public static String wrapPrefixed(String s, String pref, int len, String newLineSep, boolean soft, String regex) { - if (s == null) { - return null; - } else { - if (newLineSep == null) { - newLineSep = "\n"; - } - - if (len < 1) { - len = 1; - } - - if (regex.trim().equals("")) { - regex = " "; - } - - Pattern arg4 = Pattern.compile(regex); - int arg5 = s.length(); - int arg6 = 0; - StringBuilder arg7 = new StringBuilder(arg5 + 32); - - while (arg6 < arg5) { - int arg8 = -1; - Matcher arg9 = arg4.matcher(s.substring(arg6, Math.min(arg6 + len + 1, arg5))); - if (arg9.find()) { - if (arg9.start() == 0) { - arg6 += arg9.end(); - continue; - } - - arg8 = arg9.start(); - } - - if (arg5 - arg6 <= len) { - break; - } - - while (arg9.find()) { - arg8 = arg9.start() + arg6; - } - - if (arg8 >= arg6) { - arg7.append(s, arg6, arg8); - arg7.append(newLineSep); - arg6 = arg8 + 1; - } else if (soft) { - arg7.append(s, arg6, len + arg6); - arg7.append(newLineSep); - arg6 += len; - } else { - arg9 = arg4.matcher(s.substring(arg6 + len)); - if (arg9.find()) { - arg8 = arg9.start() + arg6 + len; - } - - if (arg8 >= 0) { - arg7.append(s, arg6, arg8); - arg7.append(newLineSep); - arg6 = arg8 + 1; - } else { - arg7.append(s.substring(arg6)); - arg6 = arg5; - } - } - } - - arg7.append(s.substring(arg6)); - return arg7.toString(); - } - } - - /** - * Returns a fancy duration up to Years - * - * @param duration the duration in ms - * @return the fancy duration - */ - public static String duration(RollingSequence rollingSequence, long duration) { - String suffix = "Millisecond"; - double phantom = duration; - int div = 1000; - - if (phantom > div) { - phantom /= div; - suffix = "Second"; - div = 60; - - if (phantom > div) { - phantom /= div; - suffix = "Minute"; - - if (phantom > div) { - phantom /= div; - suffix = "Hour"; - div = 24; - - if (phantom > 24) { - phantom /= div; - suffix = "Day"; - div = 7; - - if (phantom > div) { - phantom /= div; - suffix = "Week"; - div = 4; - - if (phantom > div) { - phantom /= div; - suffix = "Month"; - div = 12; - - if (phantom > div) { - phantom /= div; - suffix = "Year"; - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return Form.fd(phantom, 0) + " " + suffix + ((int) phantom == 1 ? "" : "s"); - } - } else { - return "Under a Second"; - } - } - - /** - * Fixes the minute issue with formatting - * - * @param c the calendar - * @return the minute string - */ - public static String fmin(Calendar c) { - String s = c.get(Calendar.MINUTE) + ""; - if (s.length() == 1) { - return "0" + s; - } - - return s; - } - - /** - * Get a fancy time stamp - * - * @param time the stamp in time (ago) - * @return the fancy stamp in time (ago) - */ - public static String ago(long time) { - long current = M.ms(); - - if (time > current - TimeUnit.SECONDS.toMillis(30) && time < current) { - return "Just Now"; - } else if (time > current - TimeUnit.SECONDS.toMillis(60) && time < current) { - return "Seconds Ago"; - } else if (time > current - TimeUnit.MINUTES.toMillis(10) && time < current) { - return "Minutes Ago"; - } else { - Calendar now = Calendar.getInstance(); - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(time); - boolean sameYear = now.get(Calendar.YEAR) == c.get(Calendar.YEAR); - boolean sameDay = now.get(Calendar.DAY_OF_YEAR) == c.get(Calendar.DAY_OF_YEAR); - - if (sameDay) { - int h = c.get(Calendar.HOUR); - h = h == 0 ? 12 : h; - - return "Today at " + h + ":" + fmin(c) + " " + (c.get(Calendar.AM_PM) == Calendar.PM ? "PM" : "AM"); - } else if (sameYear) { - boolean yesterday = now.get(Calendar.DAY_OF_YEAR) - 1 == c.get(Calendar.DAY_OF_YEAR); - - if (yesterday) { - int h = c.get(Calendar.HOUR); - h = h == 0 ? 12 : h; - - return "Yesterday at " + h + ":" + fmin(c) + " " + (c.get(Calendar.AM_PM) == Calendar.PM ? "PM" : "AM"); - } else { - int h = c.get(Calendar.HOUR); - h = h == 0 ? 12 : h; - String dow = "Error Day"; - - switch (c.get(Calendar.DAY_OF_WEEK)) { - case Calendar.SUNDAY: - dow = "Sunday"; - break; - case Calendar.MONDAY: - dow = "Monday"; - break; - case Calendar.TUESDAY: - dow = "Tuesday"; - break; - case Calendar.WEDNESDAY: - dow = "Wednesday"; - break; - case Calendar.THURSDAY: - dow = "Thursday"; - break; - case Calendar.FRIDAY: - dow = "Friday"; - break; - case Calendar.SATURDAY: - dow = "Saturday"; - break; - } - - String monthName = "Error Month"; - int month = c.get(Calendar.MONTH); - - switch (month) { - case Calendar.JANUARY: - monthName = "Jan"; - break; - case Calendar.FEBRUARY: - monthName = "Feb"; - break; - case Calendar.MARCH: - monthName = "Mar"; - break; - case Calendar.APRIL: - monthName = "Apr"; - break; - case Calendar.MAY: - monthName = "May"; - break; - case Calendar.JUNE: - monthName = "Jun"; - break; - case Calendar.JULY: - monthName = "Jul"; - break; - case Calendar.AUGUST: - monthName = "Aug"; - break; - case Calendar.SEPTEMBER: - monthName = "Sep"; - break; - case Calendar.OCTOBER: - monthName = "Oct"; - break; - case Calendar.NOVEMBER: - monthName = "Nov"; - break; - case Calendar.DECEMBER: - monthName = "Dec"; - break; - } - - int dayOfMonth = c.get(Calendar.DAY_OF_MONTH); - String suffix = numberSuffix(dayOfMonth); - - return dow + ", " + monthName + " " + suffix + " at " + h + ":" + fmin(c) + " " + (c.get(Calendar.AM_PM) == Calendar.PM ? "PM" : "AM"); - } - } else { - int h = c.get(Calendar.HOUR); - h = h == 0 ? 12 : h; - String dow = "Error Day"; - - switch (c.get(Calendar.DAY_OF_WEEK)) { - case Calendar.SUNDAY: - dow = "Sunday"; - break; - case Calendar.MONDAY: - dow = "Monday"; - break; - case Calendar.TUESDAY: - dow = "Tuesday"; - break; - case Calendar.WEDNESDAY: - dow = "Wednesday"; - break; - case Calendar.THURSDAY: - dow = "Thursday"; - break; - case Calendar.FRIDAY: - dow = "Friday"; - break; - case Calendar.SATURDAY: - dow = "Saturday"; - break; - } - - String monthName = "Error Month"; - int month = c.get(Calendar.MONTH); - - switch (month) { - case Calendar.JANUARY: - monthName = "Jan"; - break; - case Calendar.FEBRUARY: - monthName = "Feb"; - break; - case Calendar.MARCH: - monthName = "Mar"; - break; - case Calendar.APRIL: - monthName = "Apr"; - break; - case Calendar.MAY: - monthName = "May"; - break; - case Calendar.JUNE: - monthName = "Jun"; - break; - case Calendar.JULY: - monthName = "Jul"; - break; - case Calendar.AUGUST: - monthName = "Aug"; - break; - case Calendar.SEPTEMBER: - monthName = "Sep"; - break; - case Calendar.OCTOBER: - monthName = "Oct"; - break; - case Calendar.NOVEMBER: - monthName = "Nov"; - break; - case Calendar.DECEMBER: - monthName = "Dec"; - break; - } - - int dayOfMonth = c.get(Calendar.DAY_OF_MONTH); - String suffix = numberSuffix(dayOfMonth); - int year = c.get(Calendar.YEAR); - - return year + ", " + dow + ", " + monthName + " " + suffix + " at " + h + ":" + fmin(c) + " " + (c.get(Calendar.AM_PM) == Calendar.PM ? "PM" : "AM"); - } - } - } - - /** - * Get the suffix for a number i.e. 1st 2nd 3rd - * - * @param i the number - * @return the suffix - */ - public static String numberSuffix(int i) { - String[] sufixes = new String[]{"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}; - switch (i % 100) { - case 11: - case 12: - case 13: - return i + "th"; - default: - return i + sufixes[i % 10]; - - } - } - - /** - * Get a high accuracy but limited range duration (accurate up to a couple - * minutes) - * - * @param ms the milliseconds (double) - * @param prec the precision (decimal format) - * @return the formatted string - */ - public static String duration(double ms, int prec) { - if (ms < 1000.0) { - return Form.f(ms, prec) + "ms"; - } - - if (ms / 1000.0 < 60.0) { - return Form.f(ms / 1000.0, prec) + "s"; - } - - if (ms / 1000.0 / 60.0 < 60.0) { - return Form.f(ms / 1000.0 / 60.0, prec) + "m"; - } - - if (ms / 1000.0 / 60.0 / 60.0 < 24.0) { - return Form.f(ms / 1000.0 / 60.0 / 60.0, prec) + " hours"; - } - - if (ms / 1000.0 / 60.0 / 60.0 / 24.0 < 7) { - return Form.f(ms / 1000.0 / 60.0 / 24.0, prec) + " days"; - } - - return Form.f(ms, prec) + "ms"; - } - - public static String duration(long ms) { - return duration(ms, 0); - } - - /** - * Get a duration from milliseconds up to days - * - * @param ms the ms - * @param prec the precision (decimal format) - * @return the formatted string - */ - public static String duration(long ms, int prec) { - if (ms < 1000.0) { - return Form.f(ms, prec) + "ms"; - } - - if (ms / 1000.0 < 60.0) { - return Form.f(ms / 1000.0, prec) + " seconds"; - } - - if (ms / 1000.0 / 60.0 < 60.0) { - return Form.f(ms / 1000.0 / 60.0, prec) + " minutes"; - } - - if (ms / 1000.0 / 60.0 / 60.0 < 24.0) { - return Form.f(ms / 1000.0 / 60.0 / 60.0, prec) + " hours"; - } - - if (ms / 1000.0 / 60.0 / 60.0 / 24.0 < 7) { - return Form.f(ms / 1000.0 / 60.0 / 24.0, prec) + " days"; - } - - return Form.f(ms, prec) + "ms"; - } - - /** - * Format a big value - * - * @param i the number - * @return the full value in string - */ - public static String b(int i) { - return b(new BigInteger(String.valueOf(i))); - } - - /** - * Format a big value - * - * @param i the number - * @return the full value in string - */ - public static String b(long i) { - return b(new BigInteger(String.valueOf(i))); - } - - /** - * Format a big value - * - * @param i the number - * @return the full value in string - */ - public static String b(double i) { - return b(new BigInteger(String.valueOf((long) i))); - } - - /** - * Format a big number - * - * @param number the big number - * @return the value in string - */ - public static String b(BigInteger number) { - Entry entry = MAP.floorEntry(number); - if (entry == null) { - return "Nearly nothing"; - } - - BigInteger key = entry.getKey(); - BigInteger d = key.divide(THOUSAND); - BigInteger m = number.divide(d); - float f = m.floatValue() / 1000.0f; - float rounded = ((int) (f * 100.0)) / 100.0f; - - if (rounded % 1 == 0) { - return ((int) rounded) + " " + entry.getValue(); - } - - return rounded + " " + entry.getValue(); - } - - /** - * Calculate a fancy string representation of a file size. Adds a suffix of B, - * KB, MB, GB, or TB - * - * @param s the size (in bytes) - * @return the string - */ - public static String fileSize(long s) { - return ofSize(s, 1000); - } - - /** - * ":", "a", "b", "c" -> a:b:c - * - * @param splitter the splitter that goes in between - * @param strings the strings - * @return the result - */ - public static String split(String splitter, String... strings) { - StringBuilder b = new StringBuilder(); - - for (String i : strings) { - b.append(splitter); - b.append(i); - } - - return b.substring(splitter.length()); - } - - /** - * Calculate a fancy string representation of a file size. Adds a suffix of B, - * KB, MB, GB, or TB - * - * @param s the size (in bytes) - * @return the string - */ - public static String memSize(long s) { - return ofSize(s, 1024); - } - - public static String memSize(long s, int dec) { - return ofSize(s, 1024, dec); - } - - /** - * Get the timestamp of the time t (ms since 1970) - * - * @param t the time - * @return the stamp - */ - @SuppressWarnings("deprecation") - public static String stamp(long t) { - Date d = new Date(t); - return d.getMonth() + "-" + d.getDate() + "-" + (d.getYear() + 1900) + " " + d.getHours() + "h " + d.getMinutes() + "m " + d.getSeconds() + "s "; - } - - @SuppressWarnings("deprecation") - public static String stampTime(long t) { - Date d = new Date(t); - - return Calendar.getInstance().get(Calendar.HOUR_OF_DAY) + ":" + forceDoubleDigit(d.getMinutes()) + ":" + forceDoubleDigit(d.getSeconds()); - } - - public static String forceDoubleDigit(int dig) { - if (dig < 10) { - return "0" + dig; - } - - return dig + ""; - } - - @SuppressWarnings("deprecation") - public static String stampDay(long t) { - Date d = new Date(t); - return d.getMonth() + "-" + d.getDate() + "-" + (d.getYear() + 1900); - } - - /** - * Calculate a fancy string representation of a size in B, KB, MB, GB, or TB - * with a special divisor. The divisor decides how much goes up in the suffix - * chain. - * - * @param s the size (in bytes) - * @param div the divisor - * @return the string - */ - public static String ofSize(long s, int div) { - Double d = (double) s; - String sub = "Bytes"; - - if (d > div - 1) { - d /= div; - sub = "KB"; - - if (d > div - 1) { - d /= div; - sub = "MB"; - - if (d > div - 1) { - d /= div; - sub = "GB"; - - if (d > div - 1) { - d /= div; - sub = "TB"; - } - } - } - } - - if (sub.equals("GB") || sub.equals("TB")) { - return Form.f(d, 1) + sub; - } else { - return Form.f(d, 0) + sub; - } - } - - /** - * Calculate a fancy string representation of a size in B, KB, MB, GB, or TB - * with a special divisor. The divisor decides how much goes up in the suffix - * chain. - * - * @param s the size (in bytes) - * @param div the divisor - * @param dec the decimal places - * @return the string - */ - public static String ofSize(long s, int div, int dec) { - Double d = (double) s; - String sub = "Bytes"; - - if (d > div - 1) { - d /= div; - sub = "KB"; - - if (d > div - 1) { - d /= div; - sub = "MB"; - - if (d > div - 1) { - d /= div; - sub = "GB"; - - if (d > div - 1) { - d /= div; - sub = "TB"; - } - } - } - } - - return Form.f(d, dec) + " " + sub; - } - - /** - * Calculate a fancy string representation of a size in Grams, KG, MG, GG, TG - * with a special divisor. The divisor decides how much goes up in the suffix - * chain. - * - * @param s the size (in bytes) - * @param div the divisor - * @param dec the decimal places - * @return the string - */ - public static String ofSizeMetricWeight(long s, int div, int dec) { - boolean neg = s < 0; - if (neg) { - s = -s; - } - Double d = (double) s; - String sub = "Grams"; - - if (d > div - 1) { - d /= div; - sub = "KG"; - - if (d > div - 1) { - d /= div; - sub = "MG"; - - if (d > div - 1) { - d /= div; - sub = "GG"; - - if (d > div - 1) { - d /= div; - sub = "TG"; - } - } - } - } - - return (neg ? "-" : "") + Form.f(d, dec) + " " + sub; - } - - /** - * Trim a string to a length, then append ... at the end if it extends the limit - * - * @param s the string - * @param l the limit - * @return the modified string - */ - public static String trim(String s, int l) { - if (s.length() <= l) { - return s; - } - - return s.substring(0, l) + "..."; - } - - /** - * Get a class name into a configuration/filename key For example, - * PhantomController.class is converted to phantom-controller - * - * @param clazz the class - * @return the string representation - */ - public static String cname(String clazz) { - String codeName = ""; - - for (Character i : clazz.toCharArray()) { - if (Character.isUpperCase(i)) { - codeName = codeName + "-" + Character.toLowerCase(i); - } else { - codeName = codeName + i; - } - } - - if (codeName.startsWith("-")) { - codeName = codeName.substring(1); - } - - return codeName; - } - - /** - * Get a formatted representation of the memory given in megabytes - * - * @param mb the megabytes - * @return the string representation with suffixes - */ - public static String mem(long mb) { - if (mb < 1024) { - return f(mb) + " MB"; - } else { - return f(((double) mb / (double) 1024), 1) + " GB"; - } - } - - /** - * Get a formatted representation of the memory given in kilobytes - * - * @param kb the kilobytes - * @return the string representation with suffixes - */ - public static String memx(long kb) { - if (kb < 1024) { - return fd(kb, 2) + " KB"; - } else { - double mb = (double) kb / 1024.0; - - if (mb < 1024) { - return fd(mb, 2) + " MB"; - } else { - double gb = mb / 1024.0; - - return fd(gb, 2) + " GB"; - } - } - } - - /** - * Format a long. Changes -10334 into -10,334 - * - * @param i the number - * @return the string representation of the number - */ - public static String f(long i) { - instantiate(); - return NF.format(i); - } - - /** - * Format a number. Changes -10334 into -10,334 - * - * @param i the number - * @return the string representation of the number - */ - public static String f(int i) { - instantiate(); - return NF.format(i); - } - - /** - * Formats a double's decimals to a limit - * - * @param i the double - * @param p the number of decimal places to use - * @return the formated string - */ - public static String f(double i, int p) { - String form = "#"; - - if (p > 0) { - form = form + "." + repeat("#", p); - } - - DF = new DecimalFormat(form); - - return DF.format(i); - } - - /** - * Formats a double's decimals to a limit, however, this will add zeros to the - * decimal places that dont need to be placed down. 2.4343 formatted with 6 - * decimals gets returned as 2.434300 - * - * @param i the double - * @param p the number of decimal places to use - * @return the formated string - */ - public static String fd(double i, int p) { - String form = "0"; - - if (p > 0) { - form = form + "." + repeat("0", p); - } - - DF = new DecimalFormat(form); - - return DF.format(i); - } - - /** - * Formats a float's decimals to a limit - * - * @param i the float - * @param p the number of decimal places to use - * @return the formated string - */ - public static String f(float i, int p) { - String form = "#"; - - if (p > 0) { - form = form + "." + repeat("#", p); - } - - DF = new DecimalFormat(form); - - return DF.format(i); - } - - /** - * Formats a double's decimals (one decimal point) - * - * @param i the double - */ - public static String f(double i) { - return f(i, 1); - } - - /** - * Formats a float's decimals (one decimal point) - * - * @param i the float - */ - public static String f(float i) { - return f(i, 1); - } - - /** - * Get a percent representation of a double and decimal places (0.53) would - * return 53% - * - * @param i the double - * @param p the number of decimal points - * @return a string - */ - public static String pc(double i, int p) { - return f(i * 100.0, p) + "%"; - } - - /** - * Get a percent representation of a float and decimal places (0.53) would - * return 53% - * - * @param i the float - * @param p the number of decimal points - * @return a string - */ - public static String pc(float i, int p) { - return f(i * 100, p) + "%"; - } - - /** - * Get a percent representation of a double and zero decimal places (0.53) would - * return 53% - * - * @param i the double - * @return a string - */ - public static String pc(double i) { - return f(i * 100, 0) + "%"; - } - - /** - * Get a percent representation of a float and zero decimal places (0.53) would - * return 53% - * - * @param i the double - * @return a string - */ - public static String pc(float i) { - return f(i * 100, 0) + "%"; - } - - /** - * Get a percent as the percent of i out of "of" with custom decimal places - * - * @param i the percent out of - * @param of of of - * @param p the decimal places - * @return the string - */ - public static String pc(int i, int of, int p) { - return f(100.0 * (((double) i) / ((double) of)), p) + "%"; - } - - /** - * Get a percent as the percent of i out of "of" - * - * @param i the percent out of - * @param of of of - * @return the string - */ - public static String pc(int i, int of) { - return pc(i, of, 0); - } - - /** - * Get a percent as the percent of i out of "of" with custom decimal places - * - * @param i the percent out of - * @param of of of - * @param p the decimal places - * @return the string - */ - public static String pc(long i, long of, int p) { - return f(100.0 * (((double) i) / ((double) of)), p) + "%"; - } - - /** - * Get a percent as the percent of i out of "of" - * - * @param i the percent out of - * @param of of of - * @return the string - */ - public static String pc(long i, long of) { - return pc(i, of, 0); - } - - /** - * Milliseconds to seconds (double) - * - * @param ms the milliseconds - * @return a formatted string to milliseconds - */ - public static String msSeconds(long ms) { - return f((double) ms / 1000.0); - } - - /** - * Milliseconds to seconds (double) custom decimals - * - * @param ms the milliseconds - * @param p number of decimal points - * @return a formatted string to milliseconds - */ - public static String msSeconds(long ms, int p) { - return f((double) ms / 1000.0, p); - } - - /** - * nanoseconds to seconds (double) - * - * @param ms the nanoseconds - * @return a formatted string to nanoseconds - */ - public static String nsMs(long ns) { - return f((double) ns / 1000000.0); - } - - /** - * nanoseconds to seconds (double) custom decimals - * - * @param ms the nanoseconds - * @param p number of decimal points - * @return a formatted string to nanoseconds - */ - public static String nsMs(long ns, int p) { - return f((double) ns / 1000000.0, p); - } - - /** - * nanoseconds to seconds (double) custom decimals - * - * @param ms the nanoseconds - * @param p number of decimal points - * @return a formatted string to nanoseconds - */ - public static String nsMsd(long ns, int p) { - return fd((double) ns / 1000000.0, p); - } - - /** - * Get roman numeral representation of the int - * - * @param num the int - * @return the numerals - */ - public static String toRoman(int num) { - - String res = ""; - - for (Map.Entry entry : roman_numerals.entrySet()) { - int matches = num / entry.getValue(); - - res += repeat(entry.getKey(), matches); - num = num % entry.getValue(); - } - - return res; - } - - /** - * Get the number representation from roman numerals. - * - * @param number the roman number - * @return the int representation - */ - public static int fromRoman(String number) { - if (number.isEmpty()) { - return 0; - } - - number = number.toUpperCase(); - - if (number.startsWith("M")) { - return 1000 + fromRoman(number.substring(1)); - } - - if (number.startsWith("CM")) { - return 900 + fromRoman(number.substring(2)); - } - - if (number.startsWith("D")) { - return 500 + fromRoman(number.substring(1)); - } - - if (number.startsWith("CD")) { - return 400 + fromRoman(number.substring(2)); - } - - if (number.startsWith("C")) { - return 100 + fromRoman(number.substring(1)); - } - - if (number.startsWith("XC")) { - return 90 + fromRoman(number.substring(2)); - } - - if (number.startsWith("L")) { - return 50 + fromRoman(number.substring(1)); - } - - if (number.startsWith("XL")) { - return 40 + fromRoman(number.substring(2)); - } - - if (number.startsWith("X")) { - return 10 + fromRoman(number.substring(1)); - } - - if (number.startsWith("IX")) { - return 9 + fromRoman(number.substring(2)); - } - - if (number.startsWith("V")) { - return 5 + fromRoman(number.substring(1)); - } - - if (number.startsWith("IV")) { - return 4 + fromRoman(number.substring(2)); - } - - if (number.startsWith("I")) { - return 1 + fromRoman(number.substring(1)); - } - - return 0; - } - - /** - * Repeat a string - * - * @param s the string - * @param n the amount of times to repeat - * @return the repeated string - */ - public static String repeat(String s, int n) { - if (s == null) { - return null; - } - - final StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < n; i++) { - sb.append(s); - } - - return sb.toString(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/GroupedExecutor.java b/src/main/java/com/volmit/adapt/util/GroupedExecutor.java deleted file mode 100644 index d1fadbff5..000000000 --- a/src/main/java/com/volmit/adapt/util/GroupedExecutor.java +++ /dev/null @@ -1,113 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; -import java.util.concurrent.ForkJoinWorkerThread; - -public class GroupedExecutor { - private final ExecutorService service; - private final Map mirror; - private int xc; - - public GroupedExecutor(int threadLimit, int priority, String name) { - xc = 1; - mirror = new HashMap<>(); - - if (threadLimit == 1) { - service = Executors.newSingleThreadExecutor((r) -> - { - Thread t = new Thread(r); - t.setName(name); - t.setPriority(priority); - - return t; - }); - } else if (threadLimit > 1) { - final ForkJoinWorkerThreadFactory factory = new ForkJoinWorkerThreadFactory() { - @Override - public ForkJoinWorkerThread newThread(ForkJoinPool pool) { - final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); - worker.setName(name + " " + xc++); - worker.setPriority(priority); - return worker; - } - }; - - service = new ForkJoinPool(threadLimit, factory, null, false); - } else { - service = Executors.newCachedThreadPool((r) -> - { - Thread t = new Thread(r); - t.setName(name + " " + xc++); - t.setPriority(priority); - - return t; - }); - } - } - - public void waitFor(String g) { - if (g == null) { - return; - } - - if (!mirror.containsKey(g)) { - return; - } - - while (true) { - if (mirror.get(g) == 0) { - break; - } - } - } - - public void queue(String q, NastyRunnable r) { - mirror.compute(q, (k, v) -> k == null || v == null ? 1 : v + 1); - - service.execute(() -> - { - try { - r.run(); - } catch (Throwable e) { - e.printStackTrace(); - } - - mirror.compute(q, (k, v) -> v - 1); - }); - } - - public void close() { - J.a(() -> - { - J.sleep(100); - service.shutdown(); - }); - } - - public void closeNow() { - service.shutdown(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/IO.java b/src/main/java/com/volmit/adapt/util/IO.java deleted file mode 100644 index db783400e..000000000 --- a/src/main/java/com/volmit/adapt/util/IO.java +++ /dev/null @@ -1,1509 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; -import java.util.function.Consumer; -import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public class IO { - /** - * The Unix directory separator character. - */ - public static final char DIR_SEPARATOR_UNIX = '/'; - /** - * The Windows directory separator character. - */ - public static final char DIR_SEPARATOR_WINDOWS = '\\'; - /** - * The system directory separator character. - */ - public static final char DIR_SEPARATOR = File.separatorChar; - /** - * The Unix line separator string. - */ - public static final String LINE_SEPARATOR_UNIX = "\n"; - /** - * The Windows line separator string. - */ - public static final String LINE_SEPARATOR_WINDOWS = "\r\n"; - /** - * The system line separator string. - */ - public static final String LINE_SEPARATOR; - - /** - * The default buffer size to use. - */ - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; - private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); - - static { - // avoid security issues - StringWriter buf = new StringWriter(4); - PrintWriter out = new PrintWriter(buf); - out.println(); - LINE_SEPARATOR = buf.toString(); - } - - public static String decompress(String gz) throws IOException { - ByteArrayInputStream bin = new ByteArrayInputStream(Base64.getUrlDecoder().decode(gz)); - GZIPInputStream gzi = new GZIPInputStream(bin); - ByteArrayOutputStream boas = new ByteArrayOutputStream(); - IO.fullTransfer(gzi, boas, 256); - gzi.close(); - - return boas.toString(StandardCharsets.UTF_8); - } - - public static byte[] sdecompress(String compressed) throws IOException { - ByteArrayInputStream bin = new ByteArrayInputStream(Base64.getUrlDecoder().decode(compressed)); - GZIPInputStream gzi = new GZIPInputStream(bin); - ByteArrayOutputStream boas = new ByteArrayOutputStream(); - IO.fullTransfer(gzi, boas, 256); - gzi.close(); - - return boas.toByteArray(); - } - - public static String encode(byte[] data) { - return Base64.getUrlEncoder().encodeToString(data); - } - - public static byte[] decode(String u) { - return Base64.getUrlDecoder().decode(u); - } - - public static String hash(String b) { - try { - MessageDigest d = MessageDigest.getInstance("SHA-256"); - return bytesToHex(d.digest(b.getBytes(StandardCharsets.UTF_8))); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - - return "¯\\_(ツ)_/¯"; - } - - public static String hash(File b) { - try { - MessageDigest d = MessageDigest.getInstance("SHA-256"); - DigestInputStream din = new DigestInputStream(new FileInputStream(b), d); - fullTransfer(din, new VoidOutputStream(), 8192); - din.close(); - return bytesToHex(din.getMessageDigest().digest()); - } catch (Throwable e) { - e.printStackTrace(); - } - - return "¯\\_(ツ)_/¯"; - } - - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - - return new String(hexChars).toUpperCase(); - } - - /** - * Transfers the length of the buffer amount of data from the input stream to - * the output stream - * - * @param in the input - * @param out the output - * @param buffer the buffer and size to use - * @return the actual transfered amount - * @throws IOException shit happens - */ - public static int transfer(InputStream in, OutputStream out, byte[] buffer) throws IOException { - int r = in.read(buffer); - - if (r != -1) { - out.write(buffer, 0, r); - } - - return r; - } - - /** - * Transfers the length of the buffer amount of data from the input stream to - * the output stream - * - * @param in the input - * @param out the output - * @param targetBuffer the buffer and size to use - * @param totalSize the total amount to transfer - * @return the actual transfered amount - * @throws IOException shit happens - */ - public static long transfer(InputStream in, OutputStream out, int targetBuffer, long totalSize) throws IOException { - long total = totalSize; - long wrote = 0; - byte[] buf = new byte[targetBuffer]; - int r = 0; - - while ((r = in.read(buf, 0, (int) (total < targetBuffer ? total : targetBuffer))) != -1) { - total -= r; - out.write(buf, 0, r); - wrote += r; - - if (total <= 0) { - break; - } - } - - return wrote; - } - - /** - * Fully move data from a finite inputstream to an output stream using a buffer - * size of 8192. This does NOT close streams. - * - * @return total size transfered - */ - public static long fillTransfer(InputStream in, OutputStream out) throws IOException { - return fullTransfer(in, out, 8192); - } - - public static void deleteUp(File f) { - if (f.exists()) { - f.delete(); - - if (f.getParentFile().list().length == 0) { - deleteUp(f.getParentFile()); - } - } - } - - /** - * Fully move data from a finite inputstream to an output stream using a given - * buffer size. This does NOT close streams. - * - * @param in the input stream to read from - * @param out the output stream to write to - * @param bufferSize the target buffer size - * @return total size transfered - * @throws IOException shit happens - */ - public static long fullTransfer(InputStream in, OutputStream out, int bufferSize) throws IOException { - long wrote = 0; - byte[] buf = new byte[bufferSize]; - int r = 0; - - while ((r = in.read(buf)) != -1) { - out.write(buf, 0, r); - wrote += r; - } - - return wrote; - } - - /** - * Recursive delete (deleting folders) - * - * @param f the file to delete (and subfiles if folder) - */ - public static void delete(File f) { - if (f == null || !f.exists()) { - return; - } - - if (f.isDirectory()) { - for (File i : f.listFiles()) { - delete(i); - } - } - - f.delete(); - } - - public static long size(File file) { - long s = 0; - - if (file.exists()) { - if (file.isDirectory()) { - for (File i : file.listFiles()) { - s += size(i); - } - } else { - s += file.length(); - } - } - - return s; - } - - public static long count(File file) { - long s = 0; - - if (file.exists()) { - if (file.isDirectory()) { - for (File i : file.listFiles()) { - s += count(i); - } - } else { - s++; - } - } - - return s; - } - - public static long transfer(InputStream in, OutputStream out, byte[] buf, int totalSize) throws IOException { - long total = totalSize; - long wrote = 0; - int r = 0; - - while ((r = in.read(buf, 0, (int) (total < buf.length ? total : buf.length))) != -1) { - total -= r; - out.write(buf, 0, r); - wrote += r; - - if (total <= 0) { - break; - } - } - - return wrote; - } - - public static void readEntry(File zipfile, String entryname, Consumer v) throws IOException { - ZipFile file = new ZipFile(zipfile); - Throwable x = null; - - try { - Enumeration entries = file.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - - if (entryname.equals(entry.getName())) { - InputStream in = file.getInputStream(entry); - v.accept(in); - } - } - } catch (Exception ex) { - x = ex.getCause(); - } finally { - file.close(); - } - - if (x != null) { - throw new IOException("Failed to read zip entry, however it has been closed safely.", x); - } - } - - public static void writeAll(File f, Object c) throws IOException { - f.getParentFile().mkdirs(); - PrintWriter pw = new PrintWriter(new FileWriter(f)); - pw.println(c.toString()); - pw.close(); - } - - public static String readAll(File f) throws IOException { - StringBuilder content = new StringBuilder(); - try (BufferedReader bu = new BufferedReader(new FileReader(f))) { - String line; - while ((line = bu.readLine()) != null) { - content.append(line).append('\n'); - } - } - - return content.toString(); - } - - public static String readAll(InputStream in) throws IOException { - StringBuilder content = new StringBuilder(); - try (BufferedReader bu = new BufferedReader(new InputStreamReader(in))) { - String line; - while ((line = bu.readLine()) != null) { - content.append(line).append('\n'); - } - } - - return content.toString(); - } - - /** - * Implements the same behaviour as the "touch" utility on Unix. It creates a - * new file with size 0 or, if the file exists already, it is opened and closed - * without modifying it, but updating the file date and time. - * - * @param file the File to touch - * @throws IOException If an I/O problem occurs - */ - public static void touch(File file) throws IOException { - if (!file.exists()) { - OutputStream out = new FileOutputStream(file); - out.close(); - } - file.setLastModified(System.currentTimeMillis()); - } - - /** - * Copies a file to a new location preserving the file date. - *

- * This method copies the contents of the specified source file to the specified - * destination file. The directory holding the destination file is created if it - * does not exist. If the destination file exists, then this method will - * overwrite it. - * - * @param srcFile an existing file to copy, must not be null - * @param destFile the new file, must not be null - * @throws NullPointerException if source or destination is null - * @throws IOException if source or destination is invalid - * @throws IOException if an IO error occurs during copying - * @see #copyFileToDirectory - */ - public static void copyFile(File srcFile, File destFile) throws IOException { - copyFile(srcFile, destFile, true); - } - - /** - * Copies a file to a new location. - *

- * This method copies the contents of the specified source file to the specified - * destination file. The directory holding the destination file is created if it - * does not exist. If the destination file exists, then this method will - * overwrite it. - * - * @param srcFile an existing file to copy, must not be null - * @param destFile the new file, must not be null - * @param preserveFileDate true if the file date of the copy should be the same as the - * original - * @throws NullPointerException if source or destination is null - * @throws IOException if source or destination is invalid - * @throws IOException if an IO error occurs during copying - * @see #copyFileToDirectory - */ - public static void copyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { - if (srcFile == null) { - throw new NullPointerException("Source must not be null"); - } - if (destFile == null) { - throw new NullPointerException("Destination must not be null"); - } - if (srcFile.exists() == false) { - throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); - } - if (srcFile.isDirectory()) { - throw new IOException("Source '" + srcFile + "' exists but is a directory"); - } - if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { - throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); - } - if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) { - if (destFile.getParentFile().mkdirs() == false) { - throw new IOException("Destination '" + destFile + "' directory cannot be created"); - } - } - if (destFile.exists() && destFile.canWrite() == false) { - throw new IOException("Destination '" + destFile + "' exists but is read-only"); - } - doCopyFile(srcFile, destFile, preserveFileDate); - } - - // ----------------------------------------------------------------------- - - /** - * Internal copy file method. - * - * @param srcFile the validated source file, not null - * @param destFile the validated destination file, not null - * @param preserveFileDate whether to preserve the file date - * @throws IOException if an error occurs - */ - private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { - if (destFile.exists() && destFile.isDirectory()) { - throw new IOException("Destination '" + destFile + "' exists but is a directory"); - } - - try (FileInputStream input = new FileInputStream(srcFile)) { - try (FileOutputStream output = new FileOutputStream(destFile)) { - IO.copy(input, output); - } - } - - if (srcFile.length() != destFile.length()) { - throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'"); - } - if (preserveFileDate) { - destFile.setLastModified(srcFile.lastModified()); - } - } - - /** - * Unconditionally close an Reader. - *

- * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. - * This is typically used in finally blocks. - * - * @param input the Reader to close, may be null or already closed - */ - public static void closeQuietly(Reader input) { - try { - if (input != null) { - input.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - /** - * Unconditionally close a Writer. - *

- * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. - * This is typically used in finally blocks. - * - * @param output the Writer to close, may be null or already closed - */ - public static void closeQuietly(Writer output) { - try { - if (output != null) { - output.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - /** - * Unconditionally close an InputStream. - *

- * Equivalent to {@link InputStream#close()}, except any exceptions will be - * ignored. This is typically used in finally blocks. - * - * @param input the InputStream to close, may be null or already closed - */ - public static void closeQuietly(InputStream input) { - try { - if (input != null) { - input.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - // read toByteArray - // ----------------------------------------------------------------------- - - /** - * Unconditionally close an OutputStream. - *

- * Equivalent to {@link OutputStream#close()}, except any exceptions will be - * ignored. This is typically used in finally blocks. - * - * @param output the OutputStream to close, may be null or already closed - */ - public static void closeQuietly(OutputStream output) { - try { - if (output != null) { - output.close(); - } - } catch (IOException ioe) { - // ignore - } - } - - /** - * Get the contents of an InputStream as a byte[]. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static byte[] toByteArray(InputStream input) throws IOException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - copy(input, output); - return output.toByteArray(); - } - - /** - * Get the contents of a Reader as a byte[] using the - * default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static byte[] toByteArray(Reader input) throws IOException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - copy(input, output); - return output.toByteArray(); - } - - /** - * Get the contents of a Reader as a byte[] using the - * specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @param encoding the encoding to use, null means platform default - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static byte[] toByteArray(Reader input, String encoding) throws IOException { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - copy(input, output, encoding); - return output.toByteArray(); - } - - // read char[] - // ----------------------------------------------------------------------- - - /** - * Get the contents of a String as a byte[] using the - * default character encoding of the platform. - *

- * This is the same as {@link String#getBytes()}. - * - * @param input the String to convert - * @return the requested byte array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs (never occurs) - * @Deprecated Use {@link String#getBytes()} - */ - public static byte[] toByteArray(String input) throws IOException { - return input.getBytes(); - } - - /** - * Get the contents of an InputStream as a character array using - * the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param is the InputStream to read from - * @return the requested character array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static char[] toCharArray(InputStream is) throws IOException { - CharArrayWriter output = new CharArrayWriter(); - copy(is, output); - return output.toCharArray(); - } - - /** - * Get the contents of an InputStream as a character array using - * the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param is the InputStream to read from - * @param encoding the encoding to use, null means platform default - * @return the requested character array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static char[] toCharArray(InputStream is, String encoding) throws IOException { - CharArrayWriter output = new CharArrayWriter(); - copy(is, output, encoding); - return output.toCharArray(); - } - - // read toString - // ----------------------------------------------------------------------- - - /** - * Get the contents of a Reader as a character array. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @return the requested character array - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static char[] toCharArray(Reader input) throws IOException { - CharArrayWriter sw = new CharArrayWriter(); - copy(input, sw); - return sw.toCharArray(); - } - - /** - * Get the contents of an InputStream as a String using the default - * character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static String toString(InputStream input) throws IOException { - StringWriter sw = new StringWriter(); - copy(input, sw); - return sw.toString(); - } - - /** - * Get the contents of an InputStream as a String using the - * specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @param encoding the encoding to use, null means platform default - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static String toString(InputStream input, String encoding) throws IOException { - StringWriter sw = new StringWriter(); - copy(input, sw, encoding); - return sw.toString(); - } - - /** - * Get the contents of a Reader as a String. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - */ - public static String toString(Reader input) throws IOException { - StringWriter sw = new StringWriter(); - copy(input, sw); - return sw.toString(); - } - - /** - * Get the contents of a byte[] as a String using the default - * character encoding of the platform. - * - * @param input the byte array to read from - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs (never occurs) - * @Deprecated Use {@link String#String(byte[])} - */ - public static String toString(byte[] input) throws IOException { - return new String(input); - } - - // readLines - // ----------------------------------------------------------------------- - - /** - * Get the contents of a byte[] as a String using the specified - * character encoding. - *

- * Character encoding names can be found at - * IANA. - * - * @param input the byte array to read from - * @param encoding the encoding to use, null means platform default - * @return the requested String - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs (never occurs) - * @Deprecated Use {@link String#String(byte[], String)} - */ - public static String toString(byte[] input, String encoding) throws IOException { - if (encoding == null) { - return new String(input); - } else { - return new String(input, encoding); - } - } - - /** - * Get the contents of an InputStream as a list of Strings, one - * entry per line, using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from, not null - * @return the list of Strings, never null - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static List readLines(InputStream input) throws IOException { - InputStreamReader reader = new InputStreamReader(input); - return readLines(reader); - } - - /** - * Get the contents of an InputStream as a list of Strings, one - * entry per line, using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from, not null - * @param encoding the encoding to use, null means platform default - * @return the list of Strings, never null - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static List readLines(InputStream input, String encoding) throws IOException { - if (encoding == null) { - return readLines(input); - } else { - InputStreamReader reader = new InputStreamReader(input, encoding); - return readLines(reader); - } - } - - // ----------------------------------------------------------------------- - - /** - * Get the contents of a Reader as a list of Strings, one entry per - * line. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from, not null - * @return the list of Strings, never null - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static List readLines(Reader input) throws IOException { - BufferedReader reader = new BufferedReader(input); - List list = new ArrayList(); - String line = reader.readLine(); - while (line != null) { - list.add(line); - line = reader.readLine(); - } - return list; - } - - /** - * Convert the specified string to an input stream, encoded as bytes using the - * default character encoding of the platform. - * - * @param input the string to convert - * @return an input stream - * @since Commons IO 1.1 - */ - public static InputStream toInputStream(String input) { - byte[] bytes = input.getBytes(); - return new ByteArrayInputStream(bytes); - } - - // write byte[] - // ----------------------------------------------------------------------- - - /** - * Convert the specified string to an input stream, encoded as bytes using the - * specified character encoding. - *

- * Character encoding names can be found at - * IANA. - * - * @param input the string to convert - * @param encoding the encoding to use, null means platform default - * @return an input stream - * @throws IOException if the encoding is invalid - * @since Commons IO 1.1 - */ - public static InputStream toInputStream(String input, String encoding) throws IOException { - byte[] bytes = encoding != null ? input.getBytes(encoding) : input.getBytes(); - return new ByteArrayInputStream(bytes); - } - - /** - * Writes bytes from a byte[] to an OutputStream. - * - * @param data the byte array to write, do not modify during output, null ignored - * @param output the OutputStream to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(byte[] data, OutputStream output) throws IOException { - if (data != null) { - output.write(data); - } - } - - /** - * Writes bytes from a byte[] to chars on a Writer - * using the default character encoding of the platform. - *

- * This method uses {@link String#String(byte[])}. - * - * @param data the byte array to write, do not modify during output, null ignored - * @param output the Writer to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(byte[] data, Writer output) throws IOException { - if (data != null) { - output.write(new String(data)); - } - } - - // write char[] - // ----------------------------------------------------------------------- - - /** - * Writes bytes from a byte[] to chars on a Writer - * using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#String(byte[], String)}. - * - * @param data the byte array to write, do not modify during output, null ignored - * @param output the Writer to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(byte[] data, Writer output, String encoding) throws IOException { - if (data != null) { - if (encoding == null) { - write(data, output); - } else { - output.write(new String(data, encoding)); - } - } - } - - /** - * Writes chars from a char[] to a Writer using the - * default character encoding of the platform. - * - * @param data the char array to write, do not modify during output, null ignored - * @param output the Writer to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(char[] data, Writer output) throws IOException { - if (data != null) { - output.write(data); - } - } - - /** - * Writes chars from a char[] to bytes on an - * OutputStream. - *

- * This method uses {@link String#String(char[])} and {@link String#getBytes()}. - * - * @param data the char array to write, do not modify during output, null ignored - * @param output the OutputStream to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(char[] data, OutputStream output) throws IOException { - if (data != null) { - output.write(new String(data).getBytes()); - } - } - - // write String - // ----------------------------------------------------------------------- - - /** - * Writes chars from a char[] to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#String(char[])} and - * {@link String#getBytes(String)}. - * - * @param data the char array to write, do not modify during output, null ignored - * @param output the OutputStream to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(char[] data, OutputStream output, String encoding) throws IOException { - if (data != null) { - if (encoding == null) { - write(data, output); - } else { - output.write(new String(data).getBytes(encoding)); - } - } - } - - /** - * Writes chars from a String to a Writer. - * - * @param data the String to write, null ignored - * @param output the Writer to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(String data, Writer output) throws IOException { - if (data != null) { - output.write(data); - } - } - - /** - * Writes chars from a String to bytes on an - * OutputStream using the default character encoding of the - * platform. - *

- * This method uses {@link String#getBytes()}. - * - * @param data the String to write, null ignored - * @param output the OutputStream to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(String data, OutputStream output) throws IOException { - if (data != null) { - output.write(data.getBytes()); - } - } - - // write StringBuffer - // ----------------------------------------------------------------------- - - /** - * Writes chars from a String to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#getBytes(String)}. - * - * @param data the String to write, null ignored - * @param output the OutputStream to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(String data, OutputStream output, String encoding) throws IOException { - if (data != null) { - if (encoding == null) { - write(data, output); - } else { - output.write(data.getBytes(encoding)); - } - } - } - - /** - * Writes chars from a StringBuffer to a Writer. - * - * @param data the StringBuffer to write, null ignored - * @param output the Writer to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(StringBuffer data, Writer output) throws IOException { - if (data != null) { - output.write(data.toString()); - } - } - - /** - * Writes chars from a StringBuffer to bytes on an - * OutputStream using the default character encoding of the - * platform. - *

- * This method uses {@link String#getBytes()}. - * - * @param data the StringBuffer to write, null ignored - * @param output the OutputStream to write to - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(StringBuffer data, OutputStream output) throws IOException { - if (data != null) { - output.write(data.toString().getBytes()); - } - } - - // writeLines - // ----------------------------------------------------------------------- - - /** - * Writes chars from a StringBuffer to bytes on an - * OutputStream using the specified character encoding. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link String#getBytes(String)}. - * - * @param data the StringBuffer to write, null ignored - * @param output the OutputStream to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void write(StringBuffer data, OutputStream output, String encoding) throws IOException { - if (data != null) { - if (encoding == null) { - write(data, output); - } else { - output.write(data.toString().getBytes(encoding)); - } - } - } - - /** - * Writes the toString() value of each item in a collection to an - * OutputStream line by line, using the default character encoding - * of the platform and the specified line ending. - * - * @param lines the lines to write, null entries produce blank lines - * @param lineEnding the line separator to use, null is system default - * @param output the OutputStream to write to, not null, not closed - * @throws NullPointerException if the output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void writeLines(Collection lines, String lineEnding, OutputStream output) throws IOException { - if (lines == null) { - return; - } - if (lineEnding == null) { - lineEnding = LINE_SEPARATOR; - } - for (Iterator it = lines.iterator(); it.hasNext(); ) { - Object line = it.next(); - if (line != null) { - output.write(line.toString().getBytes()); - } - output.write(lineEnding.getBytes()); - } - } - - /** - * Writes the toString() value of each item in a collection to an - * OutputStream line by line, using the specified character - * encoding and the specified line ending. - *

- * Character encoding names can be found at - * IANA. - * - * @param lines the lines to write, null entries produce blank lines - * @param lineEnding the line separator to use, null is system default - * @param output the OutputStream to write to, not null, not closed - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if the output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void writeLines(Collection lines, String lineEnding, OutputStream output, String encoding) throws IOException { - if (encoding == null) { - writeLines(lines, lineEnding, output); - } else { - if (lines == null) { - return; - } - if (lineEnding == null) { - lineEnding = LINE_SEPARATOR; - } - for (Iterator it = lines.iterator(); it.hasNext(); ) { - Object line = it.next(); - if (line != null) { - output.write(line.toString().getBytes(encoding)); - } - output.write(lineEnding.getBytes(encoding)); - } - } - } - - // copy from InputStream - // ----------------------------------------------------------------------- - - /** - * Writes the toString() value of each item in a collection to a - * Writer line by line, using the specified line ending. - * - * @param lines the lines to write, null entries produce blank lines - * @param lineEnding the line separator to use, null is system default - * @param writer the Writer to write to, not null, not closed - * @throws NullPointerException if the input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void writeLines(Collection lines, String lineEnding, Writer writer) throws IOException { - if (lines == null) { - return; - } - if (lineEnding == null) { - lineEnding = LINE_SEPARATOR; - } - for (Iterator it = lines.iterator(); it.hasNext(); ) { - Object line = it.next(); - if (line != null) { - writer.write(line.toString()); - } - writer.write(lineEnding); - } - } - - /** - * Copy bytes from an InputStream to an OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * Large streams (over 2GB) will return a bytes copied value of -1 - * after the copy has completed since the correct number of bytes cannot be - * returned as an int. For large streams use the - * copyLarge(InputStream, OutputStream) method. - * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @return the number of bytes copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @throws ArithmeticException if the byte count is too large - * @since Commons IO 1.1 - */ - public static int copy(InputStream input, OutputStream output) throws IOException { - long count = copyLarge(input, output); - if (count > Integer.MAX_VALUE) { - return -1; - } - return (int) count; - } - - /** - * Copy bytes from a large (over 2GB) InputStream to an - * OutputStream. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream to read from - * @param output the OutputStream to write to - * @return the number of bytes copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.3 - */ - public static long copyLarge(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; - long count = 0; - int n = 0; - while (-1 != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - - /** - * Copy bytes from an InputStream to chars on a Writer - * using the default character encoding of the platform. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * This method uses {@link InputStreamReader}. - * - * @param input the InputStream to read from - * @param output the Writer to write to - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void copy(InputStream input, Writer output) throws IOException { - InputStreamReader in = new InputStreamReader(input); - copy(in, output); - } - - // copy from Reader - // ----------------------------------------------------------------------- - - /** - * Copy bytes from an InputStream to chars on a Writer - * using the specified character encoding. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - *

- * Character encoding names can be found at - * IANA. - *

- * This method uses {@link InputStreamReader}. - * - * @param input the InputStream to read from - * @param output the Writer to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void copy(InputStream input, Writer output, String encoding) throws IOException { - if (encoding == null) { - copy(input, output); - } else { - InputStreamReader in = new InputStreamReader(input, encoding); - copy(in, output); - } - } - - /** - * Copy chars from a Reader to a Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Large streams (over 2GB) will return a chars copied value of -1 - * after the copy has completed since the correct number of chars cannot be - * returned as an int. For large streams use the - * copyLarge(Reader, Writer) method. - * - * @param input the Reader to read from - * @param output the Writer to write to - * @return the number of characters copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @throws ArithmeticException if the character count is too large - * @since Commons IO 1.1 - */ - public static int copy(Reader input, Writer output) throws IOException { - long count = copyLarge(input, output); - if (count > Integer.MAX_VALUE) { - return -1; - } - return (int) count; - } - - /** - * Copy chars from a large (over 2GB) Reader to a - * Writer. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - * - * @param input the Reader to read from - * @param output the Writer to write to - * @return the number of characters copied - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.3 - */ - public static long copyLarge(Reader input, Writer output) throws IOException { - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - long count = 0; - int n = 0; - while (-1 != (n = input.read(buffer))) { - output.write(buffer, 0, n); - count += n; - } - return count; - } - - /** - * Copy chars from a Reader to bytes on an - * OutputStream using the default character encoding of the - * platform, and calling flush. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Due to the implementation of OutputStreamWriter, this method performs a - * flush. - *

- * This method uses {@link OutputStreamWriter}. - * - * @param input the Reader to read from - * @param output the OutputStream to write to - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void copy(Reader input, OutputStream output) throws IOException { - OutputStreamWriter out = new OutputStreamWriter(output); - copy(input, out); - // have to flush here. - out.flush(); - } - - // content equals - // ----------------------------------------------------------------------- - - /** - * Copy chars from a Reader to bytes on an - * OutputStream using the specified character encoding, and calling - * flush. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedReader. - *

- * Character encoding names can be found at - * IANA. - *

- * Due to the implementation of OutputStreamWriter, this method performs a - * flush. - *

- * This method uses {@link OutputStreamWriter}. - * - * @param input the Reader to read from - * @param output the OutputStream to write to - * @param encoding the encoding to use, null means platform default - * @throws NullPointerException if the input or output is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static void copy(Reader input, OutputStream output, String encoding) throws IOException { - if (encoding == null) { - copy(input, output); - } else { - OutputStreamWriter out = new OutputStreamWriter(output, encoding); - copy(input, out); - // we have to flush here. - out.flush(); - } - } - - /** - * Compare the contents of two Streams to determine if they are equal or not. - *

- * This method buffers the input internally using - * BufferedInputStream if they are not already buffered. - * - * @param input1 the first stream - * @param input2 the second stream - * @return true if the content of the streams are equal or they both don't - * exist, false otherwise - * @throws NullPointerException if either input is null - * @throws IOException if an I/O error occurs - */ - public static boolean contentEquals(InputStream input1, InputStream input2) throws IOException { - if (!(input1 instanceof BufferedInputStream)) { - input1 = new BufferedInputStream(input1); - } - if (!(input2 instanceof BufferedInputStream)) { - input2 = new BufferedInputStream(input2); - } - - int ch = input1.read(); - while (-1 != ch) { - int ch2 = input2.read(); - if (ch != ch2) { - return false; - } - ch = input1.read(); - } - - int ch2 = input2.read(); - return (ch2 == -1); - } - - /** - * Compare the contents of two Readers to determine if they are equal or not. - *

- * This method buffers the input internally using BufferedReader if - * they are not already buffered. - * - * @param input1 the first reader - * @param input2 the second reader - * @return true if the content of the readers are equal or they both don't - * exist, false otherwise - * @throws NullPointerException if either input is null - * @throws IOException if an I/O error occurs - * @since Commons IO 1.1 - */ - public static boolean contentEquals(Reader input1, Reader input2) throws IOException { - if (!(input1 instanceof BufferedReader)) { - input1 = new BufferedReader(input1); - } - if (!(input2 instanceof BufferedReader)) { - input2 = new BufferedReader(input2); - } - - int ch = input1.read(); - while (-1 != ch) { - int ch2 = input2.read(); - if (ch != ch2) { - return false; - } - ch = input1.read(); - } - - int ch2 = input2.read(); - return (ch2 == -1); - } -} diff --git a/src/main/java/com/volmit/adapt/util/JarScanner.java b/src/main/java/com/volmit/adapt/util/JarScanner.java deleted file mode 100644 index 50936793f..000000000 --- a/src/main/java/com/volmit/adapt/util/JarScanner.java +++ /dev/null @@ -1,94 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class JarScanner { - private final Set> classes; - private final File jar; - private final String superPackage; - - /** - * Create a scanner - * - * @param jar the path to the jar - */ - public JarScanner(File jar, String superPackage) { - this.jar = jar; - this.classes = new HashSet<>(); - this.superPackage = superPackage; - } - - /** - * Scan the jar - * - * @throws IOException bad things happen - */ - public void scan() throws IOException { - classes.clear(); - FileInputStream fin = new FileInputStream(jar); - ZipInputStream zip = new ZipInputStream(fin); - - for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { - if (!entry.isDirectory() && entry.getName().endsWith(".class")) { - if (entry.getName().contains("$")) { - continue; - } - - String c = entry.getName().replaceAll("/", ".").replace(".class", ""); - - if (c.startsWith(superPackage)) { - try { - Class clazz = Class.forName(c); - classes.add(clazz); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } - } - } - - zip.close(); - } - - /** - * Get the scanned clases - * - * @return a gset of classes - */ - public Set> getClasses() { - return classes; - } - - /** - * Get the file object for the jar - * - * @return a file object representing the jar - */ - public File getJar() { - return jar; - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/Looper.java b/src/main/java/com/volmit/adapt/util/Looper.java deleted file mode 100644 index 4c82a5af8..000000000 --- a/src/main/java/com/volmit/adapt/util/Looper.java +++ /dev/null @@ -1,45 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; - -public abstract class Looper extends Thread { - public void run() { - while (!interrupted()) { - try { - long m = loop(); - - if (m < 0) { - break; - } - - Thread.sleep(m); - } catch (InterruptedException e) { - break; - } catch (Throwable e) { - e.printStackTrace(); - } - } - - Adapt.info("Thread " + getName() + " Shutdown."); - } - - protected abstract long loop(); -} diff --git a/src/main/java/com/volmit/adapt/util/M.java b/src/main/java/com/volmit/adapt/util/M.java deleted file mode 100644 index e8dcbf769..000000000 --- a/src/main/java/com/volmit/adapt/util/M.java +++ /dev/null @@ -1,374 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import java.util.regex.Matcher; - -/** - * Math - * - * @author cyberpwn - */ -public class M { - private static final int precision = 128; - private static final int modulus = 360 * precision; - private static final float[] sin = new float[modulus]; - public static int tick = 0; - - static { - for (int i = 0; i < sin.length; i++) { - sin[i] = (float) Math.sin((i * Math.PI) / (precision * 180)); - } - } - - /** - * Scales B by an external range change so that
- *
- * BMIN < B < BMAX
- * AMIN < RESULT < AMAX
- *
- * So Given rangeScale(0, 20, 0, 10, 5) -> 10
- * 0 < 5 < 10
- * 0 < ? < 20
- *
- * would return 10 - * - * @param amin the resulting minimum - * @param amax the resulting maximum - * @param bmin the initial minimum - * @param bmax the initial maximum - * @param b the initial value - * @return the resulting value - */ - public static double rangeScale(double amin, double amax, double bmin, double bmax, double b) { - return amin + ((amax - amin) * ((b - bmin) / (bmax - bmin))); - } - - /** - * Get the percent (inverse lerp) from "from" to "to" where "at". - *

- * If from = 0 and to = 100 and at = 25 then it would return 0.25 - * - * @param from the from - * @param to the to - * @param at the at - * @return the percent - */ - public static double lerpInverse(double from, double to, double at) { - return M.rangeScale(0, 1, from, to, at); - } - - /** - * Linear interpolation from a to b where f is the percent across - * - * @param a the first pos (0) - * @param b the second pos (1) - * @param f the percent - * @return the value - */ - public static double lerp(double a, double b, double f) { - return a + (f * (b - a)); - } - - /** - * Bilinear interpolation - * - * @param a the first point (0, 0) - * @param b the second point (1, 0) - * @param c the third point (0, 1) - * @param d the fourth point (1, 1) - * @param x the x - * @param y the y - * @return the bilerped value - */ - public static double bilerp(double a, double b, double c, double d, double x, double y) { - return lerp(lerp(a, b, x), lerp(c, d, x), y); - } - - /** - * Trilinear interpolation - * - * @param a the first point (0, 0, 0) - * @param b the second point (1, 0, 0) - * @param c the third point (0, 0, 1) - * @param d the fourth point (1, 0, 1) - * @param e the fifth point (0, 1, 0) - * @param f the sixth point (1, 1, 0) - * @param g the seventh point (0, 1, 1) - * @param h the eighth point (1, 1, 1) - * @param x the x - * @param y the y - * @param z the z - * @return the trilerped value - */ - public static double trilerp(double a, double b, double c, double d, double e, double f, double g, double h, double x, double y, double z) { - return lerp(bilerp(a, b, c, d, x, y), bilerp(e, f, g, h, x, y), z); - } - - /** - * Clip a value - * - * @param value the value - * @param min the min - * @param max the max - * @return the clipped value - */ - @SuppressWarnings("unchecked") - public static T clip(T value, T min, T max) { - return (T) Double.valueOf(Math.min(max.doubleValue(), Math.max(min.doubleValue(), value.doubleValue()))); - } - - /** - * Get true or false based on random percent - * - * @param d between 0 and 1 - * @return true if true - */ - public static boolean r(Double d) { - if (d == null) { - return Math.random() < 0.5; - } - - return Math.random() < d; - } - - /** - * Get the ticks per second from a time in nanoseconds, the rad can be used for - * multiple ticks - * - * @param ns the time in nanoseconds - * @param rad the radius of the time - * @return the ticks per second in double form - */ - public static double tps(long ns, int rad) { - return (20.0 * (ns / 50000000.0)) / rad; - } - - /** - * Get the number of ticks from a time in nanoseconds - * - * @param ns the nanoseconds - * @return the amount of ticks - */ - public static double ticksFromNS(long ns) { - return (ns / 50000000.0); - } - - /** - * Get a random int from to (inclusive) - * - * @param f the from - * @param t the to - * @return the value - */ - public static int irand(int f, int t) { - return f + (int) (Math.random() * ((t - f) + 1)); - } - - /** - * Get a random float from to (inclusive) - * - * @param f the from - * @param t the to - * @return the value - */ - public static float frand(float f, float t) { - return f + (float) (Math.random() * ((t - f) + 1)); - } - - /** - * Get a random double from to (inclusive) - * - * @param f the from - * @param t the to - * @return the value - */ - public static double drand(double f, double t) { - return f + (Math.random() * ((t - f) + 1)); - } - - /** - * Get system Nanoseconds - * - * @return nanoseconds (current) - */ - public static long ns() { - return System.nanoTime(); - } - - /** - * Get the current millisecond time - * - * @return milliseconds - */ - public static long ms() { - return System.currentTimeMillis(); - } - - /** - * Fast sin function - * - * @param a the number - * @return the sin - */ - public static float sin(float a) { - return sinLookup((int) (a * precision + 0.5f)); - } - - /** - * Fast cos function - * - * @param a the number - * @return the cos - */ - public static float cos(float a) { - return sinLookup((int) ((a + 90f) * precision + 0.5f)); - } - - /** - * Fast tan function - * - * @param a the number - * @return the tan - */ - public static float tan(float a) { - float c = cos(a); - return sin(a) / (c == 0 ? 0.0000001f : c); - } - - /** - * Biggest number - * - * @param doubles the numbers - * @return the biggest one - */ - @SuppressWarnings("unchecked") - public static T max(T... doubles) { - double max = Double.MIN_VALUE; - - for (T i : doubles) { - if (i.doubleValue() > max) { - max = i.doubleValue(); - } - } - - return (T) Double.valueOf(max); - } - - /** - * Smallest number - * - * @param doubles the numbers - * @return the smallest one - */ - @SuppressWarnings("unchecked") - public static T min(T... doubles) { - double min = Double.MAX_VALUE; - - for (T i : doubles) { - if (i.doubleValue() < min) { - min = i.doubleValue(); - } - } - - return (T) Double.valueOf(min); - } - - /** - * Evaluates an expression using javascript engine and returns the double - * result. This can take variable parameters, so you need to define them. - * Parameters are defined as $[0-9]. For example evaluate("4$0/$1", 1, 2); This - * makes the expression (4x1)/2 == 2. Keep note that you must use 0-9, you - * cannot skip, or start at a number other than 0. - * - * @param expression the expression with variables - * @param args the arguments/variables - * @return the resulting double value - * @throws ScriptException ... gg - * @throws IndexOutOfBoundsException learn to count - */ - public static double evaluate(String expression, Double... args) throws ScriptException, IndexOutOfBoundsException { - for (int i = 0; i < args.length; i++) { - String current = "$" + i; - - if (expression.contains(current)) { - expression = expression.replaceAll(Matcher.quoteReplacement(current), args[i] + ""); - } - } - - return evaluate(expression); - } - - /** - * Evaluates an expression using javascript engine and returns the double - * - * @param expression the mathimatical expression - * @return the double result - * @throws ScriptException ... gg - */ - public static double evaluate(String expression) throws ScriptException { - ScriptEngineManager mgr = new ScriptEngineManager(); - ScriptEngine scriptEngine = mgr.getEngineByName("JavaScript"); - - return Double.valueOf(scriptEngine.eval(expression).toString()); - } - - /** - * is the number "is" within from-to - * - * @param from the lower end - * @param to the upper end - * @param is the check - * @return true if its within - */ - public static boolean within(int from, int to, int is) { - return is >= from && is <= to; - } - -// /** -// * Get the amount of days past since the epoch time (1970 jan 1 utc) -// * -// * @return the epoch days -// */ -// public static long epochDays() { -// return epochDays(M.ms()); -// } -// -// /** -// * Get the amount of days past since the epoch time (1970 jan 1 utc) -// * -// * @param ms the time in milliseconds -// * @return the epoch days -// */ -// private static long epochDays(long ms) { -// return ms / 1000 / 60 / 60 / 24; -// } - - private static float sinLookup(int a) { - return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)]; - } - - public static boolean interval(int tickInterval) { - return tick % (tickInterval <= 0 ? 1 : tickInterval) == 0; - } - -} diff --git a/src/main/java/com/volmit/adapt/util/MultiBurst.java b/src/main/java/com/volmit/adapt/util/MultiBurst.java deleted file mode 100644 index 81f7144b3..000000000 --- a/src/main/java/com/volmit/adapt/util/MultiBurst.java +++ /dev/null @@ -1,70 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; -import lombok.Getter; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class MultiBurst { - public static MultiBurst burst = new MultiBurst(Runtime.getRuntime().availableProcessors()); - @Getter - private final ExecutorService service; - private int tid; - - public MultiBurst(int tc) { - service = Executors.newFixedThreadPool(tc, r -> { - tid++; - Thread t = new Thread(r); - t.setName("Adapt Workgroup " + tid); - t.setPriority(Thread.MAX_PRIORITY); - t.setUncaughtExceptionHandler((et, e) -> - { - Adapt.info("Exception encountered in " + et.getName()); - e.printStackTrace(); - }); - - return t; - }); - } - - public void burst(Runnable... r) { - burst(r.length).queue(r).complete(); - } - - public void sync(Runnable... r) { - for (Runnable i : r) { - i.run(); - } - } - - public BurstExecutor burst(int estimate) { - return new BurstExecutor(service, estimate); - } - - public BurstExecutor burst() { - return burst(16); - } - - public void lazy(Runnable o) { - service.execute(o); - } -} diff --git a/src/main/java/com/volmit/adapt/util/O.java b/src/main/java/com/volmit/adapt/util/O.java deleted file mode 100644 index 50dbf654d..000000000 --- a/src/main/java/com/volmit/adapt/util/O.java +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.util.collection.KList; - -public class O implements Observable { - private T t = null; - private KList> observers; - - @Override - public T get() { - return t; - } - - @Override - public O set(T t) { - T x = t; - this.t = t; - - if (observers != null && observers.isNotEmpty()) { - observers.forEach((o) -> o.onChanged(x, t)); - } - - return this; - } - - @Override - public boolean has() { - return t != null; - } - - @Override - public O clearObservers() { - observers.clear(); - return this; - } - - @Override - public O observe(Observer t) { - if (observers == null) { - observers = new KList<>(); - } - - observers.add(t); - - return this; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Observable.java b/src/main/java/com/volmit/adapt/util/Observable.java deleted file mode 100644 index bf8a2afa9..000000000 --- a/src/main/java/com/volmit/adapt/util/Observable.java +++ /dev/null @@ -1,31 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public interface Observable { - T get(); - - Observable set(T t); - - boolean has(); - - Observable clearObservers(); - - Observable observe(Observer t); -} diff --git a/src/main/java/com/volmit/adapt/util/Observer.java b/src/main/java/com/volmit/adapt/util/Observer.java deleted file mode 100644 index 45da41aae..000000000 --- a/src/main/java/com/volmit/adapt/util/Observer.java +++ /dev/null @@ -1,24 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -@FunctionalInterface -public interface Observer { - void onChanged(T from, T to); -} diff --git a/src/main/java/com/volmit/adapt/util/ParticleSender.java b/src/main/java/com/volmit/adapt/util/ParticleSender.java deleted file mode 100644 index ae8852283..000000000 --- a/src/main/java/com/volmit/adapt/util/ParticleSender.java +++ /dev/null @@ -1,122 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Particle; -import org.bukkit.World; -import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Player; -import org.bukkit.material.MaterialData; - -/** - * Particle sender using the Bukkit particles API for 1.9+ servers - * - * @author MrMicky - */ -@SuppressWarnings("deprecation") -interface ParticleSender { - - void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data); - - Object getParticle(ParticleType particle); - - boolean isValidData(Object particle, Object data); - - default double color(double color) { - return color / 255.0; - } - - class ParticleSenderImpl implements ParticleSender { - - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) { - Particle bukkitParticle = Particle.valueOf(particle.toString()); - - if (data instanceof Color) { - if (particle.getDataType() == Color.class) { - Color color = (Color) data; - count = 0; - offsetX = color(color.getRed()); - offsetY = color(color.getGreen()); - offsetZ = color(color.getBlue()); - extra = 1.0; - } - data = null; - } - - if (receiver instanceof World) { - ((World) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } else if (receiver instanceof Player) { - ((Player) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - } - - @Override - public Particle getParticle(ParticleType particle) { - try { - return Particle.valueOf(particle.toString()); - } catch (IllegalArgumentException e) { - return null; - } - } - - @Override - public boolean isValidData(Object particle, Object data) { - return isValidDataBukkit((Particle) particle, data); - } - - public boolean isValidDataBukkit(Particle particle, Object data) { - return particle.getDataType() == Void.class || particle.getDataType().isInstance(data); - } - } - - class ParticleSender1_13 extends ParticleSenderImpl { - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) { - Particle bukkitParticle = Particle.valueOf(particle.toString()); - - if (bukkitParticle.getDataType() == Particle.DustOptions.class) { - if (data instanceof Color) { - data = new Particle.DustOptions((Color) data, 1); - } else if (data == null) { - data = new Particle.DustOptions(Color.RED, 1); - } - } else if (bukkitParticle.getDataType() == BlockData.class && data instanceof MaterialData) { - data = Bukkit.createBlockData(((MaterialData) data).getItemType()); - } - - super.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); - } - - @Override - public boolean isValidDataBukkit(Particle particle, Object data) { - if (particle.getDataType() == Particle.DustOptions.class && data instanceof Color) { - return true; - } - - if (particle.getDataType() == BlockData.class && data instanceof MaterialData) { - return true; - } - - return super.isValidDataBukkit(particle, data); - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/ParticleSenderLegacy.java b/src/main/java/com/volmit/adapt/util/ParticleSenderLegacy.java deleted file mode 100644 index 1d33b47da..000000000 --- a/src/main/java/com/volmit/adapt/util/ParticleSenderLegacy.java +++ /dev/null @@ -1,184 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.Color; -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Legacy particle sender with NMS for 1.7/1.8 servers - * - * @author MrMicky - */ -@SuppressWarnings("deprecation") -class ParticleSenderLegacy implements ParticleSender { - - private static final boolean SERVER_IS_1_8; - - private static final Constructor PACKET_PARTICLE; - private static final Class ENUM_PARTICLE; - - private static final Method WORLD_GET_HANDLE; - private static final Method WORLD_SEND_PARTICLE; - - private static final Method PLAYER_GET_HANDLE; - private static final Field PLAYER_CONNECTION; - private static final Method SEND_PACKET; - private static final int[] EMPTY = new int[0]; - - static { - ENUM_PARTICLE = FastReflection.nmsOptionalClass("EnumParticle").orElse(null); - SERVER_IS_1_8 = ENUM_PARTICLE != null; - - try { - Class packetParticleClass = FastReflection.nmsClass("PacketPlayOutWorldParticles"); - Class playerClass = FastReflection.nmsClass("EntityPlayer"); - Class playerConnectionClass = FastReflection.nmsClass("PlayerConnection"); - Class worldClass = FastReflection.nmsClass("WorldServer"); - Class entityPlayerClass = FastReflection.nmsClass("EntityPlayer"); - - Class craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer"); - Class craftWorldClass = FastReflection.obcClass("CraftWorld"); - - if (SERVER_IS_1_8) { - PACKET_PARTICLE = packetParticleClass.getConstructor(ENUM_PARTICLE, boolean.class, float.class, - float.class, float.class, float.class, float.class, float.class, float.class, int.class, - int[].class); - WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("sendParticles", entityPlayerClass, ENUM_PARTICLE, - boolean.class, double.class, double.class, double.class, int.class, double.class, double.class, - double.class, double.class, int[].class); - } else { - PACKET_PARTICLE = packetParticleClass.getConstructor(String.class, float.class, float.class, float.class, - float.class, float.class, float.class, float.class, int.class); - WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("a", String.class, double.class, double.class, - double.class, int.class, double.class, double.class, double.class, double.class); - } - - WORLD_GET_HANDLE = craftWorldClass.getDeclaredMethod("getHandle"); - PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle"); - PLAYER_CONNECTION = playerClass.getField("playerConnection"); - SEND_PACKET = playerConnectionClass.getMethod("sendPacket", FastReflection.nmsClass("Packet")); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } - - @Override - public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, - double offsetZ, double extra, Object data) { - try { - int[] datas = toData(particle, data); - - if (data instanceof Color) { - if (particle.getDataType() == Color.class) { - Color color = (Color) data; - count = 0; - offsetX = color(color.getRed()); - offsetY = color(color.getGreen()); - offsetZ = color(color.getBlue()); - extra = 1.0; - } - } - - if (receiver instanceof World) { - Object worldServer = WORLD_GET_HANDLE.invoke(receiver); - - if (SERVER_IS_1_8) { - WORLD_SEND_PARTICLE.invoke(worldServer, null, getEnumParticle(particle), true, x, y, z, count, offsetX, offsetY, offsetZ, extra, datas); - } else { - String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]); - WORLD_SEND_PARTICLE.invoke(worldServer, particleName, x, y, z, count, offsetX, offsetY, offsetZ, extra); - } - } else if (receiver instanceof Player) { - Object packet; - - if (SERVER_IS_1_8) { - packet = PACKET_PARTICLE.newInstance(getEnumParticle(particle), true, (float) x, (float) y, - (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, datas); - } else { - String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]); - packet = PACKET_PARTICLE.newInstance(particleName, (float) x, (float) y, (float) z, - (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count); - } - - Object entityPlayer = PLAYER_GET_HANDLE.invoke(receiver); - Object playerConnection = PLAYER_CONNECTION.get(entityPlayer); - SEND_PACKET.invoke(playerConnection, packet); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean isValidData(Object particle, Object data) { - return true; - } - - @Override - public Object getParticle(ParticleType particle) { - if (!SERVER_IS_1_8) { - return particle.getLegacyName(); - } - - try { - return getEnumParticle(particle); - } catch (IllegalArgumentException e) { - return null; - } - } - - private Object getEnumParticle(ParticleType particleType) { - return FastReflection.enumValueOf(ENUM_PARTICLE, particleType.toString()); - } - - private int[] toData(ParticleType particle, Object data) { - Class dataType = particle.getDataType(); - if (dataType == ItemStack.class) { - if (!(data instanceof ItemStack)) { - return SERVER_IS_1_8 ? new int[2] : new int[]{1, 0}; - } - - ItemStack itemStack = (ItemStack) data; - return new int[]{itemStack.getType().getId(), itemStack.getDurability()}; - } - - if (dataType == MaterialData.class) { - if (!(data instanceof MaterialData)) { - return SERVER_IS_1_8 ? new int[1] : new int[]{1, 0}; - } - - MaterialData materialData = (MaterialData) data; - if (SERVER_IS_1_8) { - return new int[]{materialData.getItemType().getId() + (materialData.getData() << 12)}; - } else { - return new int[]{materialData.getItemType().getId(), materialData.getData()}; - } - } - - return EMPTY; - } -} diff --git a/src/main/java/com/volmit/adapt/util/ParticleType.java b/src/main/java/com/volmit/adapt/util/ParticleType.java deleted file mode 100644 index 0d8be0985..000000000 --- a/src/main/java/com/volmit/adapt/util/ParticleType.java +++ /dev/null @@ -1,195 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import org.bukkit.Color; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.MaterialData; - -/** - * @author MrMicky - */ -@SuppressWarnings("deprecation") -public enum ParticleType { - - // 1.7+ - EXPLOSION_NORMAL("explode", "poof"), - EXPLOSION_LARGE("largeexplode", "explosion"), - EXPLOSION_HUGE("hugeexplosion", "explosion_emitter"), - FIREWORKS_SPARK("fireworksSpark", "firework"), - WATER_BUBBLE("bubble", "bubble"), - WATER_SPLASH("splash", "splash"), - WATER_WAKE("wake", "fishing"), - SUSPENDED("suspended", "underwater"), - SUSPENDED_DEPTH("depthsuspend", "underwater"), - CRIT("crit", "crit"), - CRIT_MAGIC("magicCrit", "enchanted_hit"), - SMOKE_NORMAL("smoke", "smoke"), - SMOKE_LARGE("largesmoke", "large_smoke"), - SPELL("spell", "effect"), - SPELL_INSTANT("instantSpell", "instant_effect"), - SPELL_MOB("mobSpell", "entity_effect"), - SPELL_MOB_AMBIENT("mobSpellAmbient", "ambient_entity_effect"), - SPELL_WITCH("witchMagic", "witch"), - DRIP_WATER("dripWater", "dripping_water"), - DRIP_LAVA("dripLava", "dripping_lava"), - VILLAGER_ANGRY("angryVillager", "angry_villager"), - VILLAGER_HAPPY("happyVillager", "happy_villager"), - TOWN_AURA("townaura", "mycelium"), - NOTE("note", "note"), - PORTAL("portal", "portal"), - ENCHANTMENT_TABLE("enchantmenttable", "enchant"), - FLAME("flame", "flame"), - LAVA("lava", "lava"), - // FOOTSTEP("footstep", null), - CLOUD("cloud", "cloud"), - REDSTONE("reddust", "dust"), - SNOWBALL("snowballpoof", "item_snowball"), - SNOW_SHOVEL("snowshovel", "item_snowball"), - SLIME("slime", "item_slime"), - HEART("heart", "heart"), - ITEM_CRACK("iconcrack", "item"), - BLOCK_CRACK("blockcrack", "block"), - BLOCK_DUST("blockdust", "block"), - - // 1.8+ - BARRIER("barrier", "barrier", 8), - WATER_DROP("droplet", "rain", 8), - MOB_APPEARANCE("mobappearance", "elder_guardian", 8), - // ITEM_TAKE("take", null, 8), - - // 1.9+ - DRAGON_BREATH("dragonbreath", "dragon_breath", 9), - END_ROD("endRod", "end_rod", 9), - DAMAGE_INDICATOR("damageIndicator", "damage_indicator", 9), - SWEEP_ATTACK("sweepAttack", "sweep_attack", 9), - - // 1.10+ - FALLING_DUST("fallingdust", "falling_dust", 10), - - // 1.11+ - TOTEM("totem", "totem_of_undying", 11), - SPIT("spit", "spit", 11), - - // 1.13+ - SQUID_INK(13), - BUBBLE_POP(13), - CURRENT_DOWN(13), - BUBBLE_COLUMN_UP(13), - NAUTILUS(13), - DOLPHIN(13), - - // 1.14+ - SNEEZE(14), - CAMPFIRE_COSY_SMOKE(14), - CAMPFIRE_SIGNAL_SMOKE(14), - COMPOSTER(14), - FLASH(14), - FALLING_LAVA(14), - LANDING_LAVA(14), - FALLING_WATER(14), - - // 1.15+ - DRIPPING_HONEY(15), - FALLING_HONEY(15), - LANDING_HONEY(15), - FALLING_NECTAR(15); - - private static final int SERVER_VERSION_ID; - - static { - String ver = FastReflection.VERSION; - SERVER_VERSION_ID = ver.charAt(4) == '_' ? Character.getNumericValue(ver.charAt(3)) : Integer.parseInt(ver.substring(3, 5)); - } - - private final String legacyName; - private final String name; - private final int minimumVersion; - - // 1.7 particles - ParticleType(String legacyName, String name) { - this(legacyName, name, -1); - } - - // 1.13+ particles - ParticleType(int minimumVersion) { - this.legacyName = null; - this.name = name().toLowerCase(); - this.minimumVersion = minimumVersion; - } - - // 1.8-1.12 particles - ParticleType(String legacyName, String name, int minimumVersion) { - this.legacyName = legacyName; - this.name = name; - this.minimumVersion = minimumVersion; - } - - public static ParticleType getParticle(String particleName) { - try { - return ParticleType.valueOf(particleName.toUpperCase()); - } catch (IllegalArgumentException e) { - for (ParticleType particle : values()) { - if (particle.getName().equalsIgnoreCase(particleName)) { - return particle; - } - - if (particle.hasLegacyName() && particle.getLegacyName().equalsIgnoreCase(particleName)) { - return particle; - } - } - } - return null; - } - - public boolean hasLegacyName() { - return legacyName != null; - } - - public String getLegacyName() { - if (!hasLegacyName()) { - throw new IllegalStateException("Particle " + name() + " don't have legacy name"); - } - return legacyName; - } - - public String getName() { - return name; - } - - public boolean isSupported() { - return minimumVersion <= 0 || SERVER_VERSION_ID >= minimumVersion; - } - - public Class getDataType() { - switch (this) { - case ITEM_CRACK: - return ItemStack.class; - case BLOCK_CRACK: - case BLOCK_DUST: - case FALLING_DUST: - //noinspection deprecation - return MaterialData.class; - case REDSTONE: - return Color.class; - default: - return Void.class; - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/Point3d.java b/src/main/java/com/volmit/adapt/util/Point3d.java deleted file mode 100644 index dbed324ac..000000000 --- a/src/main/java/com/volmit/adapt/util/Point3d.java +++ /dev/null @@ -1,179 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 3 element point that is represented by double precision floating point - * x,y,z coordinates. - */ -public class Point3d extends Tuple3d implements java.io.Serializable { - - // Compatible with 1.1 - static final long serialVersionUID = 5718062286069042927L; - - /** - * Constructs and initializes a Point3d from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Point3d(double x, double y, double z) { - super(x, y, z); - } - - - /** - * Constructs and initializes a Point3d from the array of length 3. - * - * @param p the array of length 3 containing xyz in order - */ - public Point3d(double[] p) { - super(p); - } - - - /** - * Constructs and initializes a Point3d from the specified Point3d. - * - * @param p1 the Point3d containing the initialization x y z data - */ - public Point3d(Point3d p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point3d from the specified Point3f. - * - * @param p1 the Point3f containing the initialization x y z data - */ - public Point3d(Point3f p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point3d from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Point3d(Tuple3f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point3d from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Point3d(Tuple3d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point3d to (0,0,0). - */ - public Point3d() { - super(); - } - - - /** - * Returns the square of the distance between this point and point p1. - * - * @param p1 the other point - * @return the square of the distance - */ - public final double distanceSquared(Point3d p1) { - double dx, dy, dz; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - return (dx * dx + dy * dy + dz * dz); - } - - - /** - * Returns the distance between this point and point p1. - * - * @param p1 the other point - * @return the distance - */ - public final double distance(Point3d p1) { - double dx, dy, dz; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - return Math.sqrt(dx * dx + dy * dy + dz * dz); - } - - - /** - * Computes the L-1 (Manhattan) distance between this point and - * point p1. The L-1 distance is equal to: - * abs(x1-x2) + abs(y1-y2) + abs(z1-z2). - * - * @param p1 the other point - * @return the L-1 distance - */ - public final double distanceL1(Point3d p1) { - return Math.abs(this.x - p1.x) + Math.abs(this.y - p1.y) + - Math.abs(this.z - p1.z); - } - - - /** - * Computes the L-infinite distance between this point and - * point p1. The L-infinite distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2)]. - * - * @param p1 the other point - * @return the L-infinite distance - */ - public final double distanceLinf(Point3d p1) { - double tmp; - tmp = Math.max(Math.abs(this.x - p1.x), Math.abs(this.y - p1.y)); - - return Math.max(tmp, Math.abs(this.z - p1.z)); - } - - - /** - * Multiplies each of the x,y,z components of the Point4d parameter - * by 1/w and places the projected values into this point. - * - * @param p1 the source Point4d, which is not modified - */ - public final void project(Point4d p1) { - double oneOw; - - oneOw = 1 / p1.w; - x = p1.x * oneOw; - y = p1.y * oneOw; - z = p1.z * oneOw; - - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Point3f.java b/src/main/java/com/volmit/adapt/util/Point3f.java deleted file mode 100644 index 7f04d5f61..000000000 --- a/src/main/java/com/volmit/adapt/util/Point3f.java +++ /dev/null @@ -1,180 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 3 element point that is represented by single precision floating point - * x,y,z coordinates. - */ -public class Point3f extends Tuple3f implements java.io.Serializable { - - - // Compatible with 1.1 - static final long serialVersionUID = -8689337816398030143L; - - /** - * Constructs and initializes a Point3f from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Point3f(float x, float y, float z) { - super(x, y, z); - } - - - /** - * Constructs and initializes a Point3f from the array of length 3. - * - * @param p the array of length 3 containing xyz in order - */ - public Point3f(float[] p) { - super(p); - } - - - /** - * Constructs and initializes a Point3f from the specified Point3f. - * - * @param p1 the Point3f containing the initialization x y z data - */ - public Point3f(Point3f p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point3f from the specified Point3d. - * - * @param p1 the Point3d containing the initialization x y z data - */ - public Point3f(Point3d p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point3f from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Point3f(Tuple3f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point3f from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Point3f(Tuple3d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point3f to (0,0,0). - */ - public Point3f() { - super(); - } - - - /** - * Computes the square of the distance between this point and - * point p1. - * - * @param p1 the other point - * @return the square of the distance - */ - public final float distanceSquared(Point3f p1) { - float dx, dy, dz; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - return dx * dx + dy * dy + dz * dz; - } - - - /** - * Computes the distance between this point and point p1. - * - * @param p1 the other point - * @return the distance - */ - public final float distance(Point3f p1) { - float dx, dy, dz; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - return (float) Math.sqrt(dx * dx + dy * dy + dz * dz); - } - - - /** - * Computes the L-1 (Manhattan) distance between this point and - * point p1. The L-1 distance is equal to: - * abs(x1-x2) + abs(y1-y2) + abs(z1-z2). - * - * @param p1 the other point - * @return the L-1 distance - */ - public final float distanceL1(Point3f p1) { - return (Math.abs(this.x - p1.x) + Math.abs(this.y - p1.y) + Math.abs(this.z - p1.z)); - } - - - /** - * Computes the L-infinite distance between this point and - * point p1. The L-infinite distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2)]. - * - * @param p1 the other point - * @return the L-infinite distance - */ - public final float distanceLinf(Point3f p1) { - float tmp; - tmp = Math.max(Math.abs(this.x - p1.x), Math.abs(this.y - p1.y)); - return (Math.max(tmp, Math.abs(this.z - p1.z))); - - } - - - /** - * Multiplies each of the x,y,z components of the Point4f parameter - * by 1/w and places the projected values into this point. - * - * @param p1 the source Point4f, which is not modified - */ - public final void project(Point4f p1) { - float oneOw; - - oneOw = 1 / p1.w; - x = p1.x * oneOw; - y = p1.y * oneOw; - z = p1.z * oneOw; - - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Point4d.java b/src/main/java/com/volmit/adapt/util/Point4d.java deleted file mode 100644 index c201e1fb8..000000000 --- a/src/main/java/com/volmit/adapt/util/Point4d.java +++ /dev/null @@ -1,214 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 4 element vector represented by double precision floating point - * x,y,z,w coordinates. - */ -public class Point4d extends Tuple4d implements java.io.Serializable { - - // Compatible with 1.1 - static final long serialVersionUID = 1733471895962736949L; - - - /** - * Constructs and initializes a Point4d from the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public Point4d(double x, double y, double z, double w) { - super(x, y, z, w); - } - - /** - * Constructs and initializes a Point4d from the coordinates contained - * in the array. - * - * @param p the array of length 4 containing xyzw in order - */ - public Point4d(double[] p) { - super(p); - } - - - /** - * Constructs and initializes a Point4d from the specified Point4d. - * - * @param p1 the Point4d containing the initialization x y z w data - */ - public Point4d(Point4d p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point4d from the specified Point4f. - * - * @param p1 the Point4f containing the initialization x y z w data - */ - public Point4d(Point4f p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point4d from the specified Tuple4f. - * - * @param t1 the Tuple4f containing the initialization x y z w data - */ - public Point4d(Tuple4f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point4d from the specified Tuple4d. - * - * @param t1 the Tuple4d containing the initialization x y z w data - */ - public Point4d(Tuple4d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point4d from the specified Tuple3d. - * The x,y,z components of this point are set to the corresponding - * components of tuple t1. The w component of this point - * is set to 1. - * - * @param t1 the tuple to be copied - * @since vecmath 1.2 - */ - public Point4d(Tuple3d t1) { - super(t1.x, t1.y, t1.z, 1.0); - } - - - /** - * Constructs and initializes a Point4d to (0,0,0,0). - */ - public Point4d() { - super(); - } - - - /** - * Sets the x,y,z components of this point to the corresponding - * components of tuple t1. The w component of this point - * is set to 1. - * - * @param t1 the tuple to be copied - * @since vecmath 1.2 - */ - public final void set(Tuple3d t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = 1.0; - } - - - /** - * Returns the square of the distance between this point and point p1. - * - * @param p1 the first point - * @return the square of distance between this point and point p1 - */ - public final double distanceSquared(Point4d p1) { - double dx, dy, dz, dw; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - dw = this.w - p1.w; - return (dx * dx + dy * dy + dz * dz + dw * dw); - } - - - /** - * Returns the distance between this point and point p1. - * - * @param p1 the first point - * @return the distance between these this point and point p1. - */ - public final double distance(Point4d p1) { - double dx, dy, dz, dw; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - dw = this.w - p1.w; - return Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw); - } - - - /** - * Computes the L-1 (Manhattan) distance between this point and - * point p1. The L-1 distance is equal to: - * abs(x1-x2) + abs(y1-y2) + abs(z1-z2) + abs(w1-w2). - * - * @param p1 the other point - * @return the L-1 distance - */ - public final double distanceL1(Point4d p1) { - return Math.abs(this.x - p1.x) + Math.abs(this.y - p1.y) + - Math.abs(this.z - p1.z) + Math.abs(this.w - p1.w); - } - - /** - * Computes the L-infinite distance between this point and - * point p1. The L-infinite distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2), abs(w1-w2)]. - * - * @param p1 the other point - * @return the L-infinite distance - */ - public final double distanceLinf(Point4d p1) { - double t1, t2; - t1 = Math.max(Math.abs(this.x - p1.x), Math.abs(this.y - p1.y)); - t2 = Math.max(Math.abs(this.z - p1.z), Math.abs(this.w - p1.w)); - - return Math.max(t1, t2); - } - - /** - * Multiplies each of the x,y,z components of the Point4d parameter - * by 1/w, places the projected values into this point, and places - * a 1 as the w parameter of this point. - * - * @param p1 the source Point4d, which is not modified - */ - public final void project(Point4d p1) { - double oneOw; - - oneOw = 1 / p1.w; - x = p1.x * oneOw; - y = p1.y * oneOw; - z = p1.z * oneOw; - w = 1.0; - - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Point4f.java b/src/main/java/com/volmit/adapt/util/Point4f.java deleted file mode 100644 index ed0f99723..000000000 --- a/src/main/java/com/volmit/adapt/util/Point4f.java +++ /dev/null @@ -1,214 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 4 element point represented by single precision floating point x,y,z,w - * coordinates. - */ -public class Point4f extends Tuple4f implements java.io.Serializable { - - - // Compatible with 1.1 - static final long serialVersionUID = 4643134103185764459L; - - /** - * Constructs and initializes a Point4f from the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public Point4f(float x, float y, float z, float w) { - super(x, y, z, w); - } - - - /** - * Constructs and initializes a Point4f from the array of length 4. - * - * @param p the array of length 4 containing xyzw in order - */ - public Point4f(float[] p) { - super(p); - } - - - /** - * Constructs and initializes a Point4f from the specified Point4f. - * - * @param p1 the Point4f containing the initialization x y z w data - */ - public Point4f(Point4f p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point4f from the specified Point4d. - * - * @param p1 the Point4d containing the initialization x y z w data - */ - public Point4f(Point4d p1) { - super(p1); - } - - - /** - * Constructs and initializes a Point4f from the specified Tuple4f. - * - * @param t1 the Tuple4f containing the initialization x y z w data - */ - public Point4f(Tuple4f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point4f from the specified Tuple4d. - * - * @param t1 the Tuple4d containing the initialization x y z w data - */ - public Point4f(Tuple4d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Point4f from the specified Tuple3f. - * The x,y,z components of this point are set to the corresponding - * components of tuple t1. The w component of this point - * is set to 1. - * - * @param t1 the tuple to be copied - * @since vecmath 1.2 - */ - public Point4f(Tuple3f t1) { - super(t1.x, t1.y, t1.z, 1.0f); - } - - - /** - * Constructs and initializes a Point4f to (0,0,0,0). - */ - public Point4f() { - super(); - } - - - /** - * Sets the x,y,z components of this point to the corresponding - * components of tuple t1. The w component of this point - * is set to 1. - * - * @param t1 the tuple to be copied - * @since vecmath 1.2 - */ - public final void set(Tuple3f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = 1.0f; - } - - - /** - * Computes the square of the distance between this point and point p1. - * - * @param p1 the other point - * @return the square of distance between these two points as a float - */ - public final float distanceSquared(Point4f p1) { - float dx, dy, dz, dw; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - dw = this.w - p1.w; - return (dx * dx + dy * dy + dz * dz + dw * dw); - } - - - /** - * Computes the distance between this point and point p1. - * - * @param p1 the other point - * @return the distance between the two points - */ - public final float distance(Point4f p1) { - float dx, dy, dz, dw; - - dx = this.x - p1.x; - dy = this.y - p1.y; - dz = this.z - p1.z; - dw = this.w - p1.w; - return (float) Math.sqrt(dx * dx + dy * dy + dz * dz + dw * dw); - } - - - /** - * Computes the L-1 (Manhattan) distance between this point and - * point p1. The L-1 distance is equal to: - * abs(x1-x2) + abs(y1-y2) + abs(z1-z2) + abs(w1-w2). - * - * @param p1 the other point - * @return the L-1 distance - */ - public final float distanceL1(Point4f p1) { - return (Math.abs(this.x - p1.x) + Math.abs(this.y - p1.y) + Math.abs(this.z - p1.z) + Math.abs(this.w - p1.w)); - } - - - /** - * Computes the L-infinite distance between this point and - * point p1. The L-infinite distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2), abs(w1-w2)]. - * - * @param p1 the other point - * @return the L-infinite distance - */ - public final float distanceLinf(Point4f p1) { - float t1, t2; - t1 = Math.max(Math.abs(this.x - p1.x), Math.abs(this.y - p1.y)); - t2 = Math.max(Math.abs(this.z - p1.z), Math.abs(this.w - p1.w)); - - return (Math.max(t1, t2)); - - } - - /** - * Multiplies each of the x,y,z components of the Point4f parameter - * by 1/w, places the projected values into this point, and places - * a 1 as the w parameter of this point. - * - * @param p1 the source Point4f, which is not modified - */ - public final void project(Point4f p1) { - float oneOw; - - oneOw = 1 / p1.w; - x = p1.x * oneOw; - y = p1.y * oneOw; - z = p1.z * oneOw; - w = 1.0f; - - } - -} diff --git a/src/main/java/com/volmit/adapt/util/PrecisionStopwatch.java b/src/main/java/com/volmit/adapt/util/PrecisionStopwatch.java deleted file mode 100644 index 6d031d5fe..000000000 --- a/src/main/java/com/volmit/adapt/util/PrecisionStopwatch.java +++ /dev/null @@ -1,123 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class PrecisionStopwatch { - private long nanos; - private long startNano; - private long millis; - private long startMillis; - private double time; - private boolean profiling; - - public PrecisionStopwatch() { - reset(); - profiling = false; - } - - public static PrecisionStopwatch start() { - PrecisionStopwatch p = new PrecisionStopwatch(); - p.begin(); - - return p; - } - - public void begin() { - profiling = true; - startNano = System.nanoTime(); - startMillis = System.currentTimeMillis(); - } - - public void end() { - if (!profiling) { - return; - } - - profiling = false; - nanos = System.nanoTime() - startNano; - millis = System.currentTimeMillis() - startMillis; - time = (double) nanos / 1000000.0; - time = (double) millis - time > 1.01 ? millis : time; - } - - public void reset() { - nanos = -1; - millis = -1; - startNano = -1; - startMillis = -1; - time = -0; - profiling = false; - } - - public double getTicks() { - return getMilliseconds() / 50.0; - } - - public double getSeconds() { - return getMilliseconds() / 1000.0; - } - - public double getMinutes() { - return getSeconds() / 60.0; - } - - public double getHours() { - return getMinutes() / 60.0; - } - - public double getMilliseconds() { - nanos = System.nanoTime() - startNano; - millis = System.currentTimeMillis() - startMillis; - time = (double) nanos / 1000000.0; - time = (double) millis - time > 1.01 ? millis : time; - return time; - } - - public long getNanoseconds() { - return (long) (time * 1000000.0); - } - - public long getNanos() { - return nanos; - } - - public long getStartNano() { - return startNano; - } - - public long getMillis() { - return millis; - } - - public long getStartMillis() { - return startMillis; - } - - public double getTime() { - return time; - } - - public boolean isProfiling() { - return profiling; - } - - public void rewind(long l) { - startMillis -= l; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Queue.java b/src/main/java/com/volmit/adapt/util/Queue.java deleted file mode 100644 index f8492679d..000000000 --- a/src/main/java/com/volmit/adapt/util/Queue.java +++ /dev/null @@ -1,51 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.util.Arrays; -import java.util.List; - -public interface Queue { - static Queue create(List t) { - return new ShurikenQueue().queue(t); - } - - @SuppressWarnings("unchecked") - static Queue create(T... t) { - return new ShurikenQueue().queue(Arrays.stream(t).toList()); - } - - Queue queue(T t); - - Queue queue(List t); - - boolean hasNext(int amt); - - boolean hasNext(); - - T next(); - - List next(int amt); - - Queue clear(); - - int size(); - - boolean contains(T p); -} diff --git a/src/main/java/com/volmit/adapt/util/QueueExecutor.java b/src/main/java/com/volmit/adapt/util/QueueExecutor.java deleted file mode 100644 index d61c9fa4e..000000000 --- a/src/main/java/com/volmit/adapt/util/QueueExecutor.java +++ /dev/null @@ -1,59 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class QueueExecutor extends Looper { - private final Queue queue; - private boolean shutdown; - - public QueueExecutor() { - queue = new ShurikenQueue(); - shutdown = false; - } - - public Queue queue() { - return queue; - } - - @Override - protected long loop() { - while (queue.hasNext()) { - try { - queue.next().run(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - if (shutdown && !queue.hasNext()) { - interrupt(); - return -1; - } - - return Math.max(500, (long) getRunTime() * 10); - } - - public double getRunTime() { - return 0; - } - - public void shutdown() { - shutdown = true; - } -} diff --git a/src/main/java/com/volmit/adapt/util/RNG.java b/src/main/java/com/volmit/adapt/util/RNG.java deleted file mode 100644 index 623e6ac57..000000000 --- a/src/main/java/com/volmit/adapt/util/RNG.java +++ /dev/null @@ -1,170 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.nio.charset.StandardCharsets; -import java.util.Random; -import java.util.UUID; - -public class RNG extends Random { - public static final RNG r = new RNG(); - private static final char[] CHARGEN = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-=!@#$%^&*()_+`~[];',./<>?:\\\"{}|\\\\".toCharArray(); - private static final long serialVersionUID = 5222938581174415179L; - private final long sx; - - public RNG() { - super(); - sx = 0; - } - - public RNG(long seed) { - super(seed); - this.sx = seed; - } - - /** - * Creates a seed (long) from the hash of the seed string - * - * @param seed the seed (string) - */ - public RNG(String seed) { - this(UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).getLeastSignificantBits() + UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).getMostSignificantBits() + (seed.length() * 32564)); - } - - public RNG nextParallelRNG(int signature) { - return new RNG(sx + signature); - } - - public RNG nextParallelRNG(long signature) { - return new RNG(sx + signature); - } - - public RNG nextRNG() { - return new RNG(nextLong()); - } - - public String s(int length) { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < length; i++) { - sb.append(c()); - } - - return sb.toString(); - } - - public char c() { - return CHARGEN[i(CHARGEN.length - 1)]; - } - - /** - * Pick a random enum - * - * @param t the enum class - * @return the enum - */ - public T e(Class t) { - T[] c = t.getEnumConstants(); - return c[i(c.length)]; - } - - public boolean b() { - return nextBoolean(); - } - - public boolean b(double percent) { - return d() > percent; - } - - public short si(int lowerBound, int upperBound) { - return (short) (lowerBound + (nextFloat() * ((upperBound - lowerBound) + 1))); - } - - public short si(int upperBound) { - return si(0, upperBound); - } - - public short si() { - return si(1); - } - - public float f(float lowerBound, float upperBound) { - return lowerBound + (nextFloat() * ((upperBound - lowerBound))); - } - - public float f(float upperBound) { - return f(0, upperBound); - } - - public float f() { - return f(1); - } - - public double d(double lowerBound, double upperBound) { - return M.lerp(lowerBound, upperBound, nextDouble()); - } - - public double d(double upperBound) { - return d(0, upperBound); - } - - public double d() { - return d(1); - } - - public int i(int lowerBound, int upperBound) { - return (int) Math.round(d(lowerBound, upperBound)); - } - - public int i(int upperBound) { - return i(Math.min(upperBound, 0), Math.max(0, upperBound)); - } - - public long l(long lowerBound, long upperBound) { - return Math.round(d(lowerBound, upperBound)); - } - - public long l(long upperBound) { - return l(0, upperBound); - } - - public int imax() { - return i(Integer.MIN_VALUE, Integer.MAX_VALUE); - } - - public long lmax() { - return l(Long.MIN_VALUE, Long.MAX_VALUE); - } - - public float fmax() { - return f(Float.MIN_VALUE, Float.MAX_VALUE); - } - - public double dmax() { - return d(Double.MIN_VALUE, Double.MAX_VALUE); - } - - public short simax() { - return si(Short.MIN_VALUE, Short.MAX_VALUE); - } - - public boolean chance(double chance) { - return chance >= nextDouble(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/RollingSequence.java b/src/main/java/com/volmit/adapt/util/RollingSequence.java deleted file mode 100644 index cf0a40fa7..000000000 --- a/src/main/java/com/volmit/adapt/util/RollingSequence.java +++ /dev/null @@ -1,105 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.util.collection.KList; - -public class RollingSequence extends Average { - private double median; - private double max; - private double min; - private boolean dirtyMedian; - private int dirtyExtremes; - private boolean precision; - - public RollingSequence(int size) { - super(size); - median = 0; - min = 0; - max = 0; - setPrecision(false); - } - - public double addLast(int amt) { - double f = 0; - - for (int i = 0; i < Math.min(values.length, amt); i++) { - f += values[i]; - } - - return f; - } - - public boolean isPrecision() { - return precision; - } - - public void setPrecision(boolean p) { - this.precision = p; - } - - public double getMin() { - if (dirtyExtremes > (isPrecision() ? 0 : values.length)) { - resetExtremes(); - } - - return min; - } - - public double getMax() { - if (dirtyExtremes > (isPrecision() ? 0 : values.length)) { - resetExtremes(); - } - - return max; - } - - public double getMedian() { - if (dirtyMedian) { - recalculateMedian(); - } - - return median; - } - - private void recalculateMedian() { - median = new KList().forceAdd(values).sort().middleValue(); - dirtyMedian = false; - } - - public void resetExtremes() { - max = Integer.MIN_VALUE; - min = Integer.MAX_VALUE; - - for (double i : values) { - max = M.max(max, i); - min = M.min(min, i); - } - - dirtyExtremes = 0; - } - - public void put(double i) { - super.put(i); - dirtyMedian = true; - dirtyExtremes++; - max = M.max(max, i); - min = M.min(min, i); - } -} diff --git a/src/main/java/com/volmit/adapt/util/S.java b/src/main/java/com/volmit/adapt/util/S.java deleted file mode 100644 index f6795dc3a..000000000 --- a/src/main/java/com/volmit/adapt/util/S.java +++ /dev/null @@ -1,29 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public abstract class S implements Runnable { - public S() { - J.s(this); - } - - public S(int delay) { - J.s(this, delay); - } -} diff --git a/src/main/java/com/volmit/adapt/util/SR.java b/src/main/java/com/volmit/adapt/util/SR.java deleted file mode 100644 index 7fd87b87b..000000000 --- a/src/main/java/com/volmit/adapt/util/SR.java +++ /dev/null @@ -1,40 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public abstract class SR implements Runnable, CancellableTask { - private int id = 0; - - public SR() { - this(0); - } - - public SR(int interval) { - id = J.sr(this, interval); - } - - @Override - public void cancel() { - J.csr(id); - } - - public int getId() { - return id; - } -} diff --git a/src/main/java/com/volmit/adapt/util/ScoreDirection.java b/src/main/java/com/volmit/adapt/util/ScoreDirection.java deleted file mode 100644 index 0fef120b9..000000000 --- a/src/main/java/com/volmit/adapt/util/ScoreDirection.java +++ /dev/null @@ -1,32 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * @author Missionary (missionarymc@gmail.com) - * @since 5/31/2018 - */ -@DontObfuscate -public enum ScoreDirection { - @DontObfuscate - UP, - - @DontObfuscate - DOWN -} diff --git a/src/main/java/com/volmit/adapt/util/ShurikenQueue.java b/src/main/java/com/volmit/adapt/util/ShurikenQueue.java deleted file mode 100644 index 07f46cdc2..000000000 --- a/src/main/java/com/volmit/adapt/util/ShurikenQueue.java +++ /dev/null @@ -1,102 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.util.collection.KList; - -import java.util.ArrayList; -import java.util.List; - -public class ShurikenQueue implements Queue { - private KList queue; - private boolean randomPop; - private boolean reversePop; - - public ShurikenQueue() { - clear(); - } - - public ShurikenQueue responsiveMode() { - reversePop = true; - return this; - } - - public ShurikenQueue randomMode() { - randomPop = true; - return this; - } - - @Override - public ShurikenQueue queue(T t) { - queue.add(t); - return this; - } - - @Override - public ShurikenQueue queue(List t) { - queue.addAll(t); - return this; - } - - @Override - public boolean hasNext(int amt) { - return queue.size() >= amt; - } - - @Override - public boolean hasNext() { - return !queue.isEmpty(); - } - - @Override - public T next() { - return reversePop ? queue.popLast() : randomPop ? queue.popRandom() : queue.pop(); - } - - @Override - public List next(int amt) { - List t = new ArrayList<>(); - - for (int i = 0; i < amt; i++) { - if (!hasNext()) { - break; - } - - t.add(next()); - } - - return t; - } - - @Override - public ShurikenQueue clear() { - queue = new KList<>(); - return this; - } - - @Override - public int size() { - return queue.size(); - } - - @Override - public boolean contains(T p) { - return queue.contains(p); - } -} diff --git a/src/main/java/com/volmit/adapt/util/Spiraled.java b/src/main/java/com/volmit/adapt/util/Spiraled.java deleted file mode 100644 index 15955c723..000000000 --- a/src/main/java/com/volmit/adapt/util/Spiraled.java +++ /dev/null @@ -1,24 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -@FunctionalInterface -public interface Spiraled { - void on(int x, int z); -} diff --git a/src/main/java/com/volmit/adapt/util/Spiraler.java b/src/main/java/com/volmit/adapt/util/Spiraler.java deleted file mode 100644 index 3ab93f4f6..000000000 --- a/src/main/java/com/volmit/adapt/util/Spiraler.java +++ /dev/null @@ -1,77 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class Spiraler { - private final Spiraled spiraled; - int x, z, dx, dz, sizeX, sizeZ, t, maxI, i; - int ox, oz; - - public Spiraler(int sizeX, int sizeZ, Spiraled spiraled) { - ox = 0; - oz = 0; - this.spiraled = spiraled; - retarget(sizeX, sizeZ); - } - - static void Spiral(int X, int Y) { - - } - - public void drain() { - while (hasNext()) { - next(); - } - } - - public Spiraler setOffset(int ox, int oz) { - this.ox = ox; - this.oz = oz; - return this; - } - - public void retarget(int sizeX, int sizeZ) { - this.sizeX = sizeX; - this.sizeZ = sizeZ; - x = z = dx = 0; - dz = -1; - i = 0; - t = Math.max(sizeX, sizeZ); - maxI = t * t; - } - - public boolean hasNext() { - return i < maxI; - } - - public void next() { - if ((-sizeX / 2 <= x) && (x <= sizeX / 2) && (-sizeZ / 2 <= z) && (z <= sizeZ / 2)) { - spiraled.on(x + ox, z + ox); - } - - if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) { - t = dx; - dx = -dz; - dz = t; - } - x += dx; - z += dz; - i++; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Switch.java b/src/main/java/com/volmit/adapt/util/Switch.java deleted file mode 100644 index 9139a55ce..000000000 --- a/src/main/java/com/volmit/adapt/util/Switch.java +++ /dev/null @@ -1,42 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class Switch { - private volatile boolean b; - - /** - * Defaulted off - */ - public Switch() { - b = false; - } - - public void flip() { - b = true; - } - - public boolean isFlipped() { - return b; - } - - public void reset() { - b = false; - } -} diff --git a/src/main/java/com/volmit/adapt/util/TaskExecutor.java b/src/main/java/com/volmit/adapt/util/TaskExecutor.java deleted file mode 100644 index 5d8760490..000000000 --- a/src/main/java/com/volmit/adapt/util/TaskExecutor.java +++ /dev/null @@ -1,204 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; -import java.util.concurrent.ForkJoinWorkerThread; - -public class TaskExecutor { - private final ExecutorService service; - private int xc; - - public TaskExecutor(int threadLimit, int priority, String name) { - xc = 1; - - if (threadLimit == 1) { - service = Executors.newSingleThreadExecutor((r) -> - { - Thread t = new Thread(r); - t.setName(name); - t.setPriority(priority); - - return t; - }); - } else if (threadLimit > 1) { - final ForkJoinWorkerThreadFactory factory = new ForkJoinWorkerThreadFactory() { - @Override - public ForkJoinWorkerThread newThread(ForkJoinPool pool) { - final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); - worker.setName(name + " " + xc++); - worker.setPriority(priority); - return worker; - } - }; - - service = new ForkJoinPool(threadLimit, factory, null, false); - } else { - service = Executors.newCachedThreadPool((r) -> - { - Thread t = new Thread(r); - t.setName(name + " " + xc++); - t.setPriority(priority); - - return t; - }); - } - } - - public TaskGroup startWork() { - return new TaskGroup(this); - } - - public void close() { - J.a(() -> - { - J.sleep(10000); - service.shutdown(); - }); - } - - public void closeNow() { - service.shutdown(); - } - - public enum TaskState { - QUEUED, - RUNNING, - COMPLETED, - FAILED - } - - public static class TaskGroup { - private final List tasks; - private final TaskExecutor e; - - public TaskGroup(TaskExecutor e) { - tasks = new ArrayList<>(); - this.e = e; - } - - public TaskGroup queue(NastyRunnable... r) { - for (NastyRunnable i : r) { - tasks.add(new AssignedTask(i)); - } - - return this; - } - - public TaskGroup queue(List r) { - for (NastyRunnable i : r) { - tasks.add(new AssignedTask(i)); - } - - return this; - } - - public TaskResult execute() { - double timeElapsed = 0; - int tasksExecuted = 0; - int tasksFailed = 0; - int tasksCompleted = 0; - tasks.forEach((t) -> t.go(e)); - long msv = M.ns(); - - waiting: - while (true) { - try { - Thread.sleep(0); - } catch (InterruptedException e1) { - Adapt.verbose("Interrupted while waiting for tasks to complete"); - } - - for (AssignedTask i : tasks) { - if (i.state.equals(TaskState.QUEUED) || i.state.equals(TaskState.RUNNING)) { - continue waiting; - } - } - - timeElapsed = (double) (M.ns() - msv) / 1000000D; - - for (AssignedTask i : tasks) { - if (i.state.equals(TaskState.COMPLETED)) { - tasksCompleted++; - } else { - tasksFailed++; - } - - tasksExecuted++; - } - - break; - } - - return new TaskResult(timeElapsed, tasksExecuted, tasksFailed, tasksCompleted); - } - } - - @ToString - public static class TaskResult { - public final double timeElapsed; - public final int tasksExecuted; - public final int tasksFailed; - public final int tasksCompleted; - - public TaskResult(double timeElapsed, int tasksExecuted, int tasksFailed, int tasksCompleted) { - this.timeElapsed = timeElapsed; - this.tasksExecuted = tasksExecuted; - this.tasksFailed = tasksFailed; - this.tasksCompleted = tasksCompleted; - } - } - - public static class AssignedTask { - @Getter - private final NastyRunnable task; - @Getter - @Setter - private TaskState state; - - public AssignedTask(NastyRunnable task) { - this.task = task; - state = TaskState.QUEUED; - } - - public void go(TaskExecutor e) { - e.service.execute(() -> - { - state = TaskState.RUNNING; - try { - task.run(); - state = TaskState.COMPLETED; - } catch (Throwable ex) { - ex.printStackTrace(); - state = TaskState.FAILED; - } - }); - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/ThreadMonitor.java b/src/main/java/com/volmit/adapt/util/ThreadMonitor.java deleted file mode 100644 index 91d0cc3b6..000000000 --- a/src/main/java/com/volmit/adapt/util/ThreadMonitor.java +++ /dev/null @@ -1,87 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.Adapt; - -/** - * Not particularly efficient or perfectly accurate but is great at fast thread - * switching detection - * - * @author dan - */ -public class ThreadMonitor extends Thread { - private final Thread monitor; - private final ChronoLatch cl; - private final RollingSequence sq = new RollingSequence(3); - int cycles = 0; - private boolean running; - private State lastState; - private PrecisionStopwatch st; - - private ThreadMonitor(Thread monitor) { - running = true; - st = PrecisionStopwatch.start(); - this.monitor = monitor; - lastState = State.NEW; - cl = new ChronoLatch(1000); - start(); - } - - public static ThreadMonitor bind(Thread monitor) { - return new ThreadMonitor(monitor); - } - - public void run() { - while (running) { - try { - Thread.sleep(0); - State s = monitor.getState(); - if (lastState != s) { - cycles++; - pushState(s); - } - - lastState = s; - - if (cl.flip()) { - Adapt.info("Cycles: " + Form.f(cycles) + " (" + Form.duration(sq.getAverage(), 2) + ")"); - } - } catch (Throwable e) { - running = false; - break; - } - } - } - - public void pushState(State s) { - if (s != State.RUNNABLE) { - if (st != null) { - sq.put(st.getMilliseconds()); - } - } else { - - st = PrecisionStopwatch.start(); - } - } - - public void unbind() { - running = false; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple2d.java b/src/main/java/com/volmit/adapt/util/Tuple2d.java deleted file mode 100644 index 04daeca2b..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple2d.java +++ /dev/null @@ -1,581 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A generic 2-element tuple that is represented by double-precision - * floating point x,y coordinates. - */ -public abstract class Tuple2d implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = 6205762482756093838L; - - /** - * The x coordinate. - */ - public double x; - - /** - * The y coordinate. - */ - public double y; - - - /** - * Constructs and initializes a Tuple2d from the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public Tuple2d(double x, double y) { - this.x = x; - this.y = y; - } - - - /** - * Constructs and initializes a Tuple2d from the specified array. - * - * @param t the array of length 2 containing xy in order - */ - public Tuple2d(double[] t) { - this.x = t[0]; - this.y = t[1]; - } - - - /** - * Constructs and initializes a Tuple2d from the specified Tuple2d. - * - * @param t1 the Tuple2d containing the initialization x y data - */ - public Tuple2d(Tuple2d t1) { - this.x = t1.x; - this.y = t1.y; - } - - - /** - * Constructs and initializes a Tuple2d from the specified Tuple2f. - * - * @param t1 the Tuple2f containing the initialization x y data - */ - public Tuple2d(Tuple2f t1) { - this.x = t1.x; - this.y = t1.y; - } - - /** - * Constructs and initializes a Tuple2d to (0,0). - */ - public Tuple2d() { - this.x = 0.0; - this.y = 0.0; - } - - - /** - * Sets the value of this tuple to the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public final void set(double x, double y) { - this.x = x; - this.y = y; - } - - - /** - * Sets the value of this tuple from the 2 values specified in - * the array. - * - * @param t the array of length 2 containing xy in order - */ - public final void set(double[] t) { - this.x = t[0]; - this.y = t[1]; - } - - - /** - * Sets the value of this tuple to the value of the Tuple2d argument. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple2d t1) { - this.x = t1.x; - this.y = t1.y; - } - - - /** - * Sets the value of this tuple to the value of Tuple2f t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple2f t1) { - this.x = t1.x; - this.y = t1.y; - } - - /** - * Copies the value of the elements of this tuple into the array t. - * - * @param t the array that will contain the values of the vector - */ - public final void get(double[] t) { - t[0] = this.x; - t[1] = this.y; - } - - - /** - * Sets the value of this tuple to the vector sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple2d t1, Tuple2d t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - } - - - /** - * Sets the value of this tuple to the vector sum of itself and tuple t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple2d t1) { - this.x += t1.x; - this.y += t1.y; - } - - - /** - * Sets the value of this tuple to the vector difference of - * tuple t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple2d t1, Tuple2d t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - } - - - /** - * Sets the value of this tuple to the vector difference of - * itself and tuple t1 (this = this - t1). - * - * @param t1 the other vector - */ - public final void sub(Tuple2d t1) { - this.x -= t1.x; - this.y -= t1.y; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source vector - */ - public final void negate(Tuple2d t1) { - this.x = -t1.x; - this.y = -t1.y; - } - - - /** - * Negates the value of this vector in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(double s, Tuple2d t1) { - this.x = s * t1.x; - this.y = s * t1.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself. - * - * @param s the scalar value - */ - public final void scale(double s) { - this.x *= s; - this.y *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1 and then adds tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be multipled - * @param t2 the tuple to be added - */ - public final void scaleAdd(double s, Tuple2d t1, Tuple2d t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(double s, Tuple2d t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple2d objects with identical data values - * (i.e., Tuple2d.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + VecMathUtil.doubleToLongBits(x); - bits = 31L * bits + VecMathUtil.doubleToLongBits(y); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * Returns true if all of the data members of Tuple2d t1 are - * equal to the corresponding data members in this Tuple2d. - * - * @param t1 the vector with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple2d t1) { - try { - return (this.x == t1.x && this.y == t1.y); - } catch (NullPointerException e2) { - return false; - } - - } - - /** - * Returns true if the Object t1 is of type Tuple2d and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple2d. - * - * @param t1 the object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - Tuple2d t2 = (Tuple2d) t1; - return (this.x == t2.x && this.y == t2.y); - } catch (NullPointerException | ClassCastException e2) { - return false; - } - - } - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to MAX[abs(x1-x2), abs(y1-y2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple2d t1, double epsilon) { - double diff; - - diff = x - t1.x; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Double.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - } - - /** - * Returns a string that contains the values of this Tuple2d. - * The form is (x,y). - * - * @return the String representation - */ - public String toString() { - return ("(" + this.x + ", " + this.y + ")"); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(double min, double max, Tuple2d t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(double min, Tuple2d t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(double max, Tuple2d t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple2d t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(double min, double max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(double min) { - if (x < min) x = min; - if (y < min) y = min; - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(double max) { - if (x > max) x = max; - if (y > max) y = max; - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple2d t1, Tuple2d t2, double alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple2d t1, double alpha) { - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final double getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(double x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final double getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(double y) { - this.y = y; - } - -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple2f.java b/src/main/java/com/volmit/adapt/util/Tuple2f.java deleted file mode 100644 index 97e2a78af..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple2f.java +++ /dev/null @@ -1,586 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A generic 2-element tuple that is represented by single-precision - * floating point x,y coordinates. - */ -public abstract class Tuple2f implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = 9011180388985266884L; - - /** - * The x coordinate. - */ - public float x; - - /** - * The y coordinate. - */ - public float y; - - - /** - * Constructs and initializes a Tuple2f from the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public Tuple2f(float x, float y) { - this.x = x; - this.y = y; - } - - - /** - * Constructs and initializes a Tuple2f from the specified array. - * - * @param t the array of length 2 containing xy in order - */ - public Tuple2f(float[] t) { - this.x = t[0]; - this.y = t[1]; - } - - - /** - * Constructs and initializes a Tuple2f from the specified Tuple2f. - * - * @param t1 the Tuple2f containing the initialization x y data - */ - public Tuple2f(Tuple2f t1) { - this.x = t1.x; - this.y = t1.y; - } - - - /** - * Constructs and initializes a Tuple2f from the specified Tuple2d. - * - * @param t1 the Tuple2d containing the initialization x y data - */ - public Tuple2f(Tuple2d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - } - - - /** - * Constructs and initializes a Tuple2f to (0,0). - */ - public Tuple2f() { - this.x = (float) 0.0; - this.y = (float) 0.0; - } - - - /** - * Sets the value of this tuple to the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public final void set(float x, float y) { - this.x = x; - this.y = y; - } - - - /** - * Sets the value of this tuple from the 2 values specified in - * the array. - * - * @param t the array of length 2 containing xy in order - */ - public final void set(float[] t) { - this.x = t[0]; - this.y = t[1]; - } - - - /** - * Sets the value of this tuple to the value of the Tuple2f argument. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple2f t1) { - this.x = t1.x; - this.y = t1.y; - } - - - /** - * Sets the value of this tuple to the value of the Tuple2d argument. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple2d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - } - - - /** - * Copies the value of the elements of this tuple into the array t. - * - * @param t the array that will contain the values of the vector - */ - public final void get(float[] t) { - t[0] = this.x; - t[1] = this.y; - } - - - /** - * Sets the value of this tuple to the vector sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple2f t1, Tuple2f t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - } - - - /** - * Sets the value of this tuple to the vector sum of itself and tuple t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple2f t1) { - this.x += t1.x; - this.y += t1.y; - } - - - /** - * Sets the value of this tuple to the vector difference of - * tuple t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple2f t1, Tuple2f t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - } - - - /** - * Sets the value of this tuple to the vector difference of - * itself and tuple t1 (this = this - t1). - * - * @param t1 the other tuple - */ - public final void sub(Tuple2f t1) { - this.x -= t1.x; - this.y -= t1.y; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source tuple - */ - public final void negate(Tuple2f t1) { - this.x = -t1.x; - this.y = -t1.y; - } - - - /** - * Negates the value of this vector in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(float s, Tuple2f t1) { - this.x = s * t1.x; - this.y = s * t1.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself. - * - * @param s the scalar value - */ - public final void scale(float s) { - this.x *= s; - this.y *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1 and then adds tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be multipled - * @param t2 the tuple to be added - */ - public final void scaleAdd(float s, Tuple2f t1, Tuple2f t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(float s, Tuple2f t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple2f objects with identical data values - * (i.e., Tuple2f.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(x); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(y); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * Returns true if all of the data members of Tuple2f t1 are - * equal to the corresponding data members in this Tuple2f. - * - * @param t1 the vector with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple2f t1) { - try { - return (this.x == t1.x && this.y == t1.y); - } catch (NullPointerException e2) { - return false; - } - - } - - /** - * Returns true if the Object t1 is of type Tuple2f and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple2f. - * - * @param t1 the object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - Tuple2f t2 = (Tuple2f) t1; - return (this.x == t2.x && this.y == t2.y); - } catch (NullPointerException e2) { - return false; - } catch (ClassCastException e1) { - return false; - } - - } - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to MAX[abs(x1-x2), abs(y1-y2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple2f t1, float epsilon) { - float diff; - - diff = x - t1.x; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Float.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - } - - /** - * Returns a string that contains the values of this Tuple2f. - * The form is (x,y). - * - * @return the String representation - */ - public String toString() { - return ("(" + this.x + ", " + this.y + ")"); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(float min, float max, Tuple2f t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(float min, Tuple2f t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(float max, Tuple2f t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple2f t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(float min, float max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(float min) { - if (x < min) x = min; - if (y < min) y = min; - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(float max) { - if (x > max) x = max; - if (y > max) y = max; - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple2f t1, Tuple2f t2, float alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple2f t1, float alpha) { - - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final float getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(float x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final float getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(float y) { - this.y = y; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple3d.java b/src/main/java/com/volmit/adapt/util/Tuple3d.java deleted file mode 100644 index f3ea10749..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple3d.java +++ /dev/null @@ -1,744 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A generic 3-element tuple that is represented by double-precision - * floating point x,y,z coordinates. - */ -public abstract class Tuple3d implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = 5542096614926168415L; - - /** - * The x coordinate. - */ - public double x; - - /** - * The y coordinate. - */ - public double y; - - /** - * The z coordinate. - */ - public double z; - - - /** - * Constructs and initializes a Tuple3d from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Tuple3d(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - /** - * Constructs and initializes a Tuple3d from the array of length 3. - * - * @param t the array of length 3 containing xyz in order - */ - public Tuple3d(double[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - } - - /** - * Constructs and initializes a Tuple3d from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Tuple3d(Tuple3d t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - /** - * Constructs and initializes a Tuple3d from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Tuple3d(Tuple3f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - /** - * Constructs and initializes a Tuple3d to (0,0,0). - */ - public Tuple3d() { - this.x = 0.0; - this.y = 0.0; - this.z = 0.0; - } - - /** - * Sets the value of this tuple to the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public final void set(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - /** - * Sets the value of this tuple to the value of the xyz coordinates - * located in the array of length 3. - * - * @param t the array of length 3 containing xyz in order - */ - public final void set(double[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - } - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple3d t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple3f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - /** - * Copies the x,y,z coordinates of this tuple into the array t - * of length 3. - * - * @param t the target array - */ - public final void get(double[] t) { - t[0] = this.x; - t[1] = this.y; - t[2] = this.z; - } - - - /** - * Copies the x,y,z coordinates of this tuple into the tuple t. - * - * @param t the Tuple3d object into which the values of this object are copied - */ - public final void get(Tuple3d t) { - t.x = this.x; - t.y = this.y; - t.z = this.z; - } - - - /** - * Sets the value of this tuple to the sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple3d t1, Tuple3d t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - this.z = t1.z + t2.z; - } - - - /** - * Sets the value of this tuple to the sum of itself and t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple3d t1) { - this.x += t1.x; - this.y += t1.y; - this.z += t1.z; - } - - /** - * Sets the value of this tuple to the difference of tuples - * t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple3d t1, Tuple3d t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - this.z = t1.z - t2.z; - } - - /** - * Sets the value of this tuple to the difference - * of itself and t1 (this = this - t1). - * - * @param t1 the other tuple - */ - public final void sub(Tuple3d t1) { - this.x -= t1.x; - this.y -= t1.y; - this.z -= t1.z; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source tuple - */ - public final void negate(Tuple3d t1) { - this.x = -t1.x; - this.y = -t1.y; - this.z = -t1.z; - } - - - /** - * Negates the value of this tuple in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(double s, Tuple3d t1) { - this.x = s * t1.x; - this.y = s * t1.y; - this.z = s * t1.z; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself. - * - * @param s the scalar value - */ - public final void scale(double s) { - this.x *= s; - this.y *= s; - this.z *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1 and then adds tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be multipled - * @param t2 the tuple to be added - */ - public final void scaleAdd(double s, Tuple3d t1, Tuple3d t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - this.z = s * t1.z + t2.z; - } - - - /** - * @Deprecated Use scaleAdd(double,Tuple3d) instead - */ - public final void scaleAdd(double s, Tuple3f t1) { - scaleAdd(s, new Point3d(t1)); - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(double s, Tuple3d t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - this.z = s * this.z + t1.z; - } - - - /** - * Returns a string that contains the values of this Tuple3d. - * The form is (x,y,z). - * - * @return the String representation - */ - public String toString() { - return "(" + this.x + ", " + this.y + ", " + this.z + ")"; - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple3d objects with identical data values - * (i.e., Tuple3d.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + VecMathUtil.doubleToLongBits(x); - bits = 31L * bits + VecMathUtil.doubleToLongBits(y); - bits = 31L * bits + VecMathUtil.doubleToLongBits(z); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * Returns true if all of the data members of Tuple3d t1 are - * equal to the corresponding data members in this Tuple3d. - * - * @param t1 the tuple with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple3d t1) { - try { - return (this.x == t1.x && this.y == t1.y && this.z == t1.z); - } catch (NullPointerException e2) { - return false; - } - } - - /** - * Returns true if the Object t1 is of type Tuple3d and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple3d. - * - * @param t1 the Object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - Tuple3d t2 = (Tuple3d) t1; - return (this.x == t2.x && this.y == t2.y && this.z == t2.z); - } catch (ClassCastException e1) { - return false; - } catch (NullPointerException e2) { - return false; - } - - } - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple3d t1, double epsilon) { - double diff; - - diff = x - t1.x; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = z - t1.z; - if (Double.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - - } - - - /** - * @Deprecated Use clamp(double,double,Tuple3d) instead - */ - public final void clamp(float min, float max, Tuple3d t) { - clamp(min, (double) max, t); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(double min, double max, Tuple3d t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else if (t.z < min) { - z = min; - } else { - z = t.z; - } - - } - - - /** - * @Deprecated Use clampMin(double,Tuple3d) instead - */ - public final void clampMin(float min, Tuple3d t) { - clampMin((double) min, t); - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(double min, Tuple3d t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z < min) { - z = min; - } else { - z = t.z; - } - - } - - - /** - * @Deprecated Use clampMax(double,Tuple3d) instead - */ - public final void clampMax(float max, Tuple3d t) { - clampMax((double) max, t); - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(double max, Tuple3d t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else { - z = t.z; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple3d t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - z = Math.abs(t.z); - - } - - - /** - * @Deprecated Use clamp(double,double) instead - */ - public final void clamp(float min, float max) { - clamp(min, (double) max); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(double min, double max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - if (z > max) { - z = max; - } else if (z < min) { - z = min; - } - - } - - - /** - * @Deprecated Use clampMin(double) instead - */ - public final void clampMin(float min) { - clampMin((double) min); - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(double min) { - if (x < min) x = min; - if (y < min) y = min; - if (z < min) z = min; - - } - - - /** - * @Deprecated Use clampMax(double) instead - */ - public final void clampMax(float max) { - clampMax((double) max); - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(double max) { - if (x > max) x = max; - if (y > max) y = max; - if (z > max) z = max; - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - z = Math.abs(z); - } - - - /** - * @Deprecated Use interpolate(Tuple3d,Tuple3d,double) instead - */ - public final void interpolate(Tuple3d t1, Tuple3d t2, float alpha) { - interpolate(t1, t2, (double) alpha); - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple3d t1, Tuple3d t2, double alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - this.z = (1 - alpha) * t1.z + alpha * t2.z; - } - - - /** - * @Deprecated Use interpolate(Tuple3d,double) instead - */ - public final void interpolate(Tuple3d t1, float alpha) { - interpolate(t1, (double) alpha); - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple3d t1, double alpha) { - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - this.z = (1 - alpha) * this.z + alpha * t1.z; - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final double getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(double x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final double getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(double y) { - this.y = y; - } - - /** - * Get the z coordinate. - * - * @return the z coordinate. - * @since vecmath 1.5 - */ - public final double getZ() { - return z; - } - - - /** - * Set the z coordinate. - * - * @param z value to z coordinate. - * @since vecmath 1.5 - */ - public final void setZ(double z) { - this.z = z; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple3f.java b/src/main/java/com/volmit/adapt/util/Tuple3f.java deleted file mode 100644 index e74541623..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple3f.java +++ /dev/null @@ -1,689 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A generic 3-element tuple that is represented by single precision-floating - * point x,y,z coordinates. - */ -public abstract class Tuple3f implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = 5019834619484343712L; - - /** - * The x coordinate. - */ - public float x; - - /** - * The y coordinate. - */ - public float y; - - /** - * The z coordinate. - */ - public float z; - - - /** - * Constructs and initializes a Tuple3f from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Tuple3f(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - } - - - /** - * Constructs and initializes a Tuple3f from the array of length 3. - * - * @param t the array of length 3 containing xyz in order - */ - public Tuple3f(float[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - } - - - /** - * Constructs and initializes a Tuple3f from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Tuple3f(Tuple3f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - - /** - * Constructs and initializes a Tuple3f from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Tuple3f(Tuple3d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - this.z = (float) t1.z; - } - - - /** - * Constructs and initializes a Tuple3f to (0,0,0). - */ - public Tuple3f() { - this.x = 0.0f; - this.y = 0.0f; - this.z = 0.0f; - } - - - /** - * Returns a string that contains the values of this Tuple3f. - * The form is (x,y,z). - * - * @return the String representation - */ - public String toString() { - return "(" + this.x + ", " + this.y + ", " + this.z + ")"; - } - - - /** - * Sets the value of this tuple to the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public final void set(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - } - - - /** - * Sets the value of this tuple to the xyz coordinates specified in - * the array of length 3. - * - * @param t the array of length 3 containing xyz in order - */ - public final void set(float[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple3f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple3d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - this.z = (float) t1.z; - } - - - /** - * Gets the value of this tuple and copies the values into t. - * - * @param t the array of length 3 into which the values are copied - */ - public final void get(float[] t) { - t[0] = this.x; - t[1] = this.y; - t[2] = this.z; - } - - - /** - * Gets the value of this tuple and copies the values into t. - * - * @param t the Tuple3f object into which the values of this object are copied - */ - public final void get(Tuple3f t) { - t.x = this.x; - t.y = this.y; - t.z = this.z; - } - - - /** - * Sets the value of this tuple to the vector sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple3f t1, Tuple3f t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - this.z = t1.z + t2.z; - } - - - /** - * Sets the value of this tuple to the vector sum of itself and tuple t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple3f t1) { - this.x += t1.x; - this.y += t1.y; - this.z += t1.z; - } - - - /** - * Sets the value of this tuple to the vector difference - * of tuples t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple3f t1, Tuple3f t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - this.z = t1.z - t2.z; - } - - - /** - * Sets the value of this tuple to the vector difference of - * itself and tuple t1 (this = this - t1) . - * - * @param t1 the other tuple - */ - public final void sub(Tuple3f t1) { - this.x -= t1.x; - this.y -= t1.y; - this.z -= t1.z; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source tuple - */ - public final void negate(Tuple3f t1) { - this.x = -t1.x; - this.y = -t1.y; - this.z = -t1.z; - } - - - /** - * Negates the value of this tuple in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - } - - - /** - * Sets the value of this vector to the scalar multiplication - * of tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(float s, Tuple3f t1) { - this.x = s * t1.x; - this.y = s * t1.y; - this.z = s * t1.z; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of the scale factor with this. - * - * @param s the scalar value - */ - public final void scale(float s) { - this.x *= s; - this.y *= s; - this.z *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1 and then adds tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be scaled and added - * @param t2 the tuple to be added without a scale - */ - public final void scaleAdd(float s, Tuple3f t1, Tuple3f t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - this.z = s * t1.z + t2.z; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(float s, Tuple3f t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - this.z = s * this.z + t1.z; - } - - - /** - * Returns true if the Object t1 is of type Tuple3f and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple3f. - * - * @param t1 the vector with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple3f t1) { - try { - return (this.x == t1.x && this.y == t1.y && this.z == t1.z); - } catch (NullPointerException e2) { - return false; - } - } - - /** - * Returns true if the Object t1 is of type Tuple3f and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple3f. - * - * @param t1 the Object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - Tuple3f t2 = (Tuple3f) t1; - return (this.x == t2.x && this.y == t2.y && this.z == t2.z); - } catch (NullPointerException e2) { - return false; - } catch (ClassCastException e1) { - return false; - } - } - - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple3f t1, float epsilon) { - float diff; - - diff = x - t1.x; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = z - t1.z; - if (Float.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple3f objects with identical data values - * (i.e., Tuple3f.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(x); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(y); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(z); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(float min, float max, Tuple3f t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else if (t.z < min) { - z = min; - } else { - z = t.z; - } - - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(float min, Tuple3f t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z < min) { - z = min; - } else { - z = t.z; - } - - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(float max, Tuple3f t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else { - z = t.z; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple3f t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - z = Math.abs(t.z); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(float min, float max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - if (z > max) { - z = max; - } else if (z < min) { - z = min; - } - - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(float min) { - if (x < min) x = min; - if (y < min) y = min; - if (z < min) z = min; - - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(float max) { - if (x > max) x = max; - if (y > max) y = max; - if (z > max) z = max; - - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - z = Math.abs(z); - - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple3f t1, Tuple3f t2, float alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - this.z = (1 - alpha) * t1.z + alpha * t2.z; - - - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public final void interpolate(Tuple3f t1, float alpha) { - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - this.z = (1 - alpha) * this.z + alpha * t1.z; - - - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final float getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(float x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final float getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(float y) { - this.y = y; - } - - /** - * Get the z coordinate. - * - * @return the z coordinate - * @since vecmath 1.5 - */ - public final float getZ() { - return z; - } - - - /** - * Set the Z coordinate. - * - * @param z value to z coordinate. - * @since vecmath 1.5 - */ - public final void setZ(float z) { - this.z = z; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple4d.java b/src/main/java/com/volmit/adapt/util/Tuple4d.java deleted file mode 100644 index ea83e34d9..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple4d.java +++ /dev/null @@ -1,849 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 4 element tuple represented by double precision floating point - * x,y,z,w coordinates. - */ -public abstract class Tuple4d implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = -4748953690425311052L; - - /** - * The x coordinate. - */ - public double x; - - /** - * The y coordinate. - */ - public double y; - - /** - * The z coordinate. - */ - public double z; - - /** - * The w coordinate. - */ - public double w; - - - /** - * Constructs and initializes a Tuple4d from the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public Tuple4d(double x, double y, double z, double w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - - /** - * Constructs and initializes a Tuple4d from the coordinates contained - * in the array. - * - * @param t the array of length 4 containing xyzw in order - */ - public Tuple4d(double[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - this.w = t[3]; - } - - - /** - * Constructs and initializes a Tuple4d from the specified Tuple4d. - * - * @param t1 the Tuple4d containing the initialization x y z w data - */ - public Tuple4d(Tuple4d t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Constructs and initializes a Tuple4d from the specified Tuple4f. - * - * @param t1 the Tuple4f containing the initialization x y z w data - */ - public Tuple4d(Tuple4f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Constructs and initializes a Tuple4d to (0,0,0,0). - */ - public Tuple4d() { - this.x = 0.0; - this.y = 0.0; - this.z = 0.0; - this.w = 0.0; - } - - - /** - * Sets the value of this tuple to the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public final void set(double x, double y, double z, double w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - - /** - * Sets the value of this tuple to the specified xyzw coordinates. - * - * @param t the array of length 4 containing xyzw in order - */ - public final void set(double[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - this.w = t[3]; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple4d t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple4f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Gets the value of this tuple and places it into the array t of - * length four in x,y,z,w order. - * - * @param t the array of length four - */ - public final void get(double[] t) { - t[0] = this.x; - t[1] = this.y; - t[2] = this.z; - t[3] = this.w; - } - - - /** - * Gets the value of this tuple and places it into the Tuple4d - * argument of - * length four in x,y,z,w order. - * - * @param t the Tuple into which the values will be copied - */ - public final void get(Tuple4d t) { - t.x = this.x; - t.y = this.y; - t.z = this.z; - t.w = this.w; - } - - - /** - * Sets the value of this tuple to the tuple sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple4d t1, Tuple4d t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - this.z = t1.z + t2.z; - this.w = t1.w + t2.w; - } - - - /** - * Sets the value of this tuple to the sum of itself and tuple t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple4d t1) { - this.x += t1.x; - this.y += t1.y; - this.z += t1.z; - this.w += t1.w; - } - - - /** - * Sets the value of this tuple to the difference - * of tuples t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple4d t1, Tuple4d t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - this.z = t1.z - t2.z; - this.w = t1.w - t2.w; - } - - - /** - * Sets the value of this tuple to the difference of itself - * and tuple t1 (this = this - t1). - * - * @param t1 the other tuple - */ - public final void sub(Tuple4d t1) { - this.x -= t1.x; - this.y -= t1.y; - this.z -= t1.z; - this.w -= t1.w; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source tuple - */ - public final void negate(Tuple4d t1) { - this.x = -t1.x; - this.y = -t1.y; - this.z = -t1.z; - this.w = -t1.w; - } - - - /** - * Negates the value of this tuple in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.w = -this.w; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of the scale factor with the tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(double s, Tuple4d t1) { - this.x = s * t1.x; - this.y = s * t1.y; - this.z = s * t1.z; - this.w = s * t1.w; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of the scale factor with this. - * - * @param s the scalar value - */ - public final void scale(double s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.w *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication by s - * of tuple t1 plus tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be multipled - * @param t2 the tuple to be added - */ - public final void scaleAdd(double s, Tuple4d t1, Tuple4d t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - this.z = s * t1.z + t2.z; - this.w = s * t1.w + t2.w; - } - - - /** - * @Deprecated Use scaleAdd(double,Tuple4d) instead - */ - public final void scaleAdd(float s, Tuple4d t1) { - scaleAdd((double) s, t1); - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(double s, Tuple4d t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - this.z = s * this.z + t1.z; - this.w = s * this.w + t1.w; - } - - - /** - * Returns a string that contains the values of this Tuple4d. - * The form is (x,y,z,w). - * - * @return the String representation - */ - public String toString() { - return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; - } - - - /** - * Returns true if all of the data members of Tuple4d t1 are - * equal to the corresponding data members in this Tuple4d. - * - * @param t1 the tuple with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple4d t1) { - try { - return (this.x == t1.x && this.y == t1.y && this.z == t1.z - && this.w == t1.w); - } catch (NullPointerException e2) { - return false; - } - } - - /** - * Returns true if the Object t1 is of type Tuple4d and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple4d. - * - * @param t1 the object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - - Tuple4d t2 = (Tuple4d) t1; - return (this.x == t2.x && this.y == t2.y && - this.z == t2.z && this.w == t2.w); - } catch (NullPointerException e2) { - return false; - } catch (ClassCastException e1) { - return false; - } - } - - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2), abs(w1-w2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple4d t1, double epsilon) { - double diff; - - diff = x - t1.x; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = z - t1.z; - if (Double.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = w - t1.w; - if (Double.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple4d objects with identical data values - * (i.e., Tuple4d.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + VecMathUtil.doubleToLongBits(x); - bits = 31L * bits + VecMathUtil.doubleToLongBits(y); - bits = 31L * bits + VecMathUtil.doubleToLongBits(z); - bits = 31L * bits + VecMathUtil.doubleToLongBits(w); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * @Deprecated Use clamp(double,double,Tuple4d) instead - */ - public final void clamp(float min, float max, Tuple4d t) { - clamp(min, (double) max, t); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(double min, double max, Tuple4d t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else if (t.z < min) { - z = min; - } else { - z = t.z; - } - - if (t.w > max) { - w = max; - } else if (t.w < min) { - w = min; - } else { - w = t.w; - } - - } - - - /** - * @Deprecated Use clampMin(double,Tuple4d) instead - */ - public final void clampMin(float min, Tuple4d t) { - clampMin((double) min, t); - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(double min, Tuple4d t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z < min) { - z = min; - } else { - z = t.z; - } - - if (t.w < min) { - w = min; - } else { - w = t.w; - } - - } - - - /** - * @Deprecated Use clampMax(double,Tuple4d) instead - */ - public final void clampMax(float max, Tuple4d t) { - clampMax((double) max, t); - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(double max, Tuple4d t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else { - z = t.z; - } - - if (t.w > max) { - w = max; - } else { - w = t.z; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple4d t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - z = Math.abs(t.z); - w = Math.abs(t.w); - - } - - - /** - * @Deprecated Use clamp(double,double) instead - */ - public final void clamp(float min, float max) { - clamp(min, (double) max); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(double min, double max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - if (z > max) { - z = max; - } else if (z < min) { - z = min; - } - - if (w > max) { - w = max; - } else if (w < min) { - w = min; - } - - } - - - /** - * @Deprecated Use clampMin(double) instead - */ - public final void clampMin(float min) { - clampMin((double) min); - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(double min) { - if (x < min) x = min; - if (y < min) y = min; - if (z < min) z = min; - if (w < min) w = min; - } - - - /** - * @Deprecated Use clampMax(double) instead - */ - public final void clampMax(float max) { - clampMax((double) max); - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(double max) { - if (x > max) x = max; - if (y > max) y = max; - if (z > max) z = max; - if (w > max) w = max; - - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - z = Math.abs(z); - w = Math.abs(w); - - } - - - /** - * @Deprecated Use interpolate(Tuple4d,Tuple4d,double) instead - */ - public void interpolate(Tuple4d t1, Tuple4d t2, float alpha) { - interpolate(t1, t2, (double) alpha); - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public void interpolate(Tuple4d t1, Tuple4d t2, double alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - this.z = (1 - alpha) * t1.z + alpha * t2.z; - this.w = (1 - alpha) * t1.w + alpha * t2.w; - } - - - /** - * @Deprecated Use interpolate(Tuple4d,double) instead - */ - public void interpolate(Tuple4d t1, float alpha) { - interpolate(t1, (double) alpha); - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public void interpolate(Tuple4d t1, double alpha) { - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - this.z = (1 - alpha) * this.z + alpha * t1.z; - this.w = (1 - alpha) * this.w + alpha * t1.w; - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final double getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(double x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final double getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(double y) { - this.y = y; - } - - /** - * Get the z coordinate. - * - * @return the z coordinate. - * @since vecmath 1.5 - */ - public final double getZ() { - return z; - } - - - /** - * Set the z coordinate. - * - * @param z value to z coordinate. - * @since vecmath 1.5 - */ - public final void setZ(double z) { - this.z = z; - } - - - /** - * Get the w coordinate. - * - * @return the w coordinate. - * @since vecmath 1.5 - */ - public final double getW() { - return w; - } - - - /** - * Set the w coordinate. - * - * @param w value to w coordinate. - * @since vecmath 1.5 - */ - public final void setW(double w) { - this.w = w; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Tuple4f.java b/src/main/java/com/volmit/adapt/util/Tuple4f.java deleted file mode 100644 index 8b954e17c..000000000 --- a/src/main/java/com/volmit/adapt/util/Tuple4f.java +++ /dev/null @@ -1,773 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 4-element tuple represented by single-precision floating point x,y,z,w - * coordinates. - */ -public abstract class Tuple4f implements java.io.Serializable, Cloneable { - - static final long serialVersionUID = 7068460319248845763L; - - /** - * The x coordinate. - */ - public float x; - - /** - * The y coordinate. - */ - public float y; - - /** - * The z coordinate. - */ - public float z; - - /** - * The w coordinate. - */ - public float w; - - - /** - * Constructs and initializes a Tuple4f from the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public Tuple4f(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - - /** - * Constructs and initializes a Tuple4f from the array of length 4. - * - * @param t the array of length 4 containing xyzw in order - */ - public Tuple4f(float[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - this.w = t[3]; - } - - - /** - * Constructs and initializes a Tuple4f from the specified Tuple4f. - * - * @param t1 the Tuple4f containing the initialization x y z w data - */ - public Tuple4f(Tuple4f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Constructs and initializes a Tuple4f from the specified Tuple4d. - * - * @param t1 the Tuple4d containing the initialization x y z w data - */ - public Tuple4f(Tuple4d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - this.z = (float) t1.z; - this.w = (float) t1.w; - } - - - /** - * Constructs and initializes a Tuple4f to (0,0,0,0). - */ - public Tuple4f() { - this.x = 0.0f; - this.y = 0.0f; - this.z = 0.0f; - this.w = 0.0f; - } - - - /** - * Sets the value of this tuple to the specified xyzw coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param w the w coordinate - */ - public final void set(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - - /** - * Sets the value of this tuple to the specified coordinates in the - * array of length 4. - * - * @param t the array of length 4 containing xyzw in order - */ - public final void set(float[] t) { - this.x = t[0]; - this.y = t[1]; - this.z = t[2]; - this.w = t[3]; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple4f t1) { - this.x = t1.x; - this.y = t1.y; - this.z = t1.z; - this.w = t1.w; - } - - - /** - * Sets the value of this tuple to the value of tuple t1. - * - * @param t1 the tuple to be copied - */ - public final void set(Tuple4d t1) { - this.x = (float) t1.x; - this.y = (float) t1.y; - this.z = (float) t1.z; - this.w = (float) t1.w; - } - - - /** - * Copies the values of this tuple into the array t. - * - * @param t the array - */ - public final void get(float[] t) { - t[0] = this.x; - t[1] = this.y; - t[2] = this.z; - t[3] = this.w; - } - - - /** - * Copies the values of this tuple into the tuple t. - * - * @param t the target tuple - */ - public final void get(Tuple4f t) { - t.x = this.x; - t.y = this.y; - t.z = this.z; - t.w = this.w; - } - - - /** - * Sets the value of this tuple to the sum of tuples t1 and t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void add(Tuple4f t1, Tuple4f t2) { - this.x = t1.x + t2.x; - this.y = t1.y + t2.y; - this.z = t1.z + t2.z; - this.w = t1.w + t2.w; - } - - - /** - * Sets the value of this tuple to the sum of itself and t1. - * - * @param t1 the other tuple - */ - public final void add(Tuple4f t1) { - this.x += t1.x; - this.y += t1.y; - this.z += t1.z; - this.w += t1.w; - } - - - /** - * Sets the value of this tuple to the difference - * of tuples t1 and t2 (this = t1 - t2). - * - * @param t1 the first tuple - * @param t2 the second tuple - */ - public final void sub(Tuple4f t1, Tuple4f t2) { - this.x = t1.x - t2.x; - this.y = t1.y - t2.y; - this.z = t1.z - t2.z; - this.w = t1.w - t2.w; - } - - - /** - * Sets the value of this tuple to the difference - * of itself and t1 (this = this - t1). - * - * @param t1 the other tuple - */ - public final void sub(Tuple4f t1) { - this.x -= t1.x; - this.y -= t1.y; - this.z -= t1.z; - this.w -= t1.w; - } - - - /** - * Sets the value of this tuple to the negation of tuple t1. - * - * @param t1 the source tuple - */ - public final void negate(Tuple4f t1) { - this.x = -t1.x; - this.y = -t1.y; - this.z = -t1.z; - this.w = -t1.w; - } - - - /** - * Negates the value of this tuple in place. - */ - public final void negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.w = -this.w; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1. - * - * @param s the scalar value - * @param t1 the source tuple - */ - public final void scale(float s, Tuple4f t1) { - this.x = s * t1.x; - this.y = s * t1.y; - this.z = s * t1.z; - this.w = s * t1.w; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of the scale factor with this. - * - * @param s the scalar value - */ - public final void scale(float s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.w *= s; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of tuple t1 plus tuple t2 (this = s*t1 + t2). - * - * @param s the scalar value - * @param t1 the tuple to be multipled - * @param t2 the tuple to be added - */ - public final void scaleAdd(float s, Tuple4f t1, Tuple4f t2) { - this.x = s * t1.x + t2.x; - this.y = s * t1.y + t2.y; - this.z = s * t1.z + t2.z; - this.w = s * t1.w + t2.w; - } - - - /** - * Sets the value of this tuple to the scalar multiplication - * of itself and then adds tuple t1 (this = s*this + t1). - * - * @param s the scalar value - * @param t1 the tuple to be added - */ - public final void scaleAdd(float s, Tuple4f t1) { - this.x = s * this.x + t1.x; - this.y = s * this.y + t1.y; - this.z = s * this.z + t1.z; - this.w = s * this.w + t1.w; - } - - - /** - * Returns a string that contains the values of this Tuple4f. - * The form is (x,y,z,w). - * - * @return the String representation - */ - public String toString() { - return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; - } - - /** - * Returns true if all of the data members of Tuple4f t1 are - * equal to the corresponding data members in this Tuple4f. - * - * @param t1 the vector with which the comparison is made - * @return true or false - */ - public boolean equals(Tuple4f t1) { - try { - return (this.x == t1.x && this.y == t1.y && this.z == t1.z - && this.w == t1.w); - } catch (NullPointerException e2) { - return false; - } - } - - /** - * Returns true if the Object t1 is of type Tuple4f and all of the - * data members of t1 are equal to the corresponding data members in - * this Tuple4f. - * - * @param t1 the object with which the comparison is made - * @return true or false - */ - public boolean equals(Object t1) { - try { - Tuple4f t2 = (Tuple4f) t1; - return (this.x == t2.x && this.y == t2.y && - this.z == t2.z && this.w == t2.w); - } catch (NullPointerException e2) { - return false; - } catch (ClassCastException e1) { - return false; - } - } - - - /** - * Returns true if the L-infinite distance between this tuple - * and tuple t1 is less than or equal to the epsilon parameter, - * otherwise returns false. The L-infinite - * distance is equal to - * MAX[abs(x1-x2), abs(y1-y2), abs(z1-z2), abs(w1-w2)]. - * - * @param t1 the tuple to be compared to this tuple - * @param epsilon the threshold value - * @return true or false - */ - public boolean epsilonEquals(Tuple4f t1, float epsilon) { - float diff; - - diff = x - t1.x; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = y - t1.y; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = z - t1.z; - if (Float.isNaN(diff)) return false; - if ((diff < 0 ? -diff : diff) > epsilon) return false; - - diff = w - t1.w; - if (Float.isNaN(diff)) return false; - return !((diff < 0 ? -diff : diff) > epsilon); - } - - - /** - * Returns a hash code value based on the data values in this - * object. Two different Tuple4f objects with identical data values - * (i.e., Tuple4f.equals returns true) will return the same hash - * code value. Two objects with different data members may return the - * same hash value, although this is not likely. - * - * @return the integer hash code value - */ - public int hashCode() { - long bits = 1L; - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(x); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(y); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(z); - bits = 31L * bits + (long) VecMathUtil.floatToIntBits(w); - return (int) (bits ^ (bits >> 32)); - } - - - /** - * Clamps the tuple parameter to the range [low, high] and - * places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clamp(float min, float max, Tuple4f t) { - if (t.x > max) { - x = max; - } else if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else if (t.z < min) { - z = min; - } else { - z = t.z; - } - - if (t.w > max) { - w = max; - } else if (t.w < min) { - w = min; - } else { - w = t.w; - } - - } - - - /** - * Clamps the minimum value of the tuple parameter to the min - * parameter and places the values into this tuple. - * - * @param min the lowest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMin(float min, Tuple4f t) { - if (t.x < min) { - x = min; - } else { - x = t.x; - } - - if (t.y < min) { - y = min; - } else { - y = t.y; - } - - if (t.z < min) { - z = min; - } else { - z = t.z; - } - - if (t.w < min) { - w = min; - } else { - w = t.w; - } - - - } - - - /** - * Clamps the maximum value of the tuple parameter to the max - * parameter and places the values into this tuple. - * - * @param max the highest value in the tuple after clamping - * @param t the source tuple, which will not be modified - */ - public final void clampMax(float max, Tuple4f t) { - if (t.x > max) { - x = max; - } else { - x = t.x; - } - - if (t.y > max) { - y = max; - } else { - y = t.y; - } - - if (t.z > max) { - z = max; - } else { - z = t.z; - } - - if (t.w > max) { - w = max; - } else { - w = t.z; - } - - } - - - /** - * Sets each component of the tuple parameter to its absolute - * value and places the modified values into this tuple. - * - * @param t the source tuple, which will not be modified - */ - public final void absolute(Tuple4f t) { - x = Math.abs(t.x); - y = Math.abs(t.y); - z = Math.abs(t.z); - w = Math.abs(t.w); - } - - - /** - * Clamps this tuple to the range [low, high]. - * - * @param min the lowest value in this tuple after clamping - * @param max the highest value in this tuple after clamping - */ - public final void clamp(float min, float max) { - if (x > max) { - x = max; - } else if (x < min) { - x = min; - } - - if (y > max) { - y = max; - } else if (y < min) { - y = min; - } - - if (z > max) { - z = max; - } else if (z < min) { - z = min; - } - - if (w > max) { - w = max; - } else if (w < min) { - w = min; - } - - } - - - /** - * Clamps the minimum value of this tuple to the min parameter. - * - * @param min the lowest value in this tuple after clamping - */ - public final void clampMin(float min) { - if (x < min) x = min; - if (y < min) y = min; - if (z < min) z = min; - if (w < min) w = min; - - } - - - /** - * Clamps the maximum value of this tuple to the max parameter. - * - * @param max the highest value in the tuple after clamping - */ - public final void clampMax(float max) { - if (x > max) x = max; - if (y > max) y = max; - if (z > max) z = max; - if (w > max) w = max; - - } - - - /** - * Sets each component of this tuple to its absolute value. - */ - public final void absolute() { - x = Math.abs(x); - y = Math.abs(y); - z = Math.abs(z); - w = Math.abs(w); - } - - - /** - * Linearly interpolates between tuples t1 and t2 and places the - * result into this tuple: this = (1-alpha)*t1 + alpha*t2. - * - * @param t1 the first tuple - * @param t2 the second tuple - * @param alpha the alpha interpolation parameter - */ - public void interpolate(Tuple4f t1, Tuple4f t2, float alpha) { - this.x = (1 - alpha) * t1.x + alpha * t2.x; - this.y = (1 - alpha) * t1.y + alpha * t2.y; - this.z = (1 - alpha) * t1.z + alpha * t2.z; - this.w = (1 - alpha) * t1.w + alpha * t2.w; - - } - - - /** - * Linearly interpolates between this tuple and tuple t1 and - * places the result into this tuple: this = (1-alpha)*this + alpha*t1. - * - * @param t1 the first tuple - * @param alpha the alpha interpolation parameter - */ - public void interpolate(Tuple4f t1, float alpha) { - this.x = (1 - alpha) * this.x + alpha * t1.x; - this.y = (1 - alpha) * this.y + alpha * t1.y; - this.z = (1 - alpha) * this.z + alpha * t1.z; - this.w = (1 - alpha) * this.w + alpha * t1.w; - - } - - /** - * Creates a new object of the same class as this object. - * - * @return a clone of this instance. - * @throws OutOfMemoryError if there is not enough memory. - * @see java.lang.Cloneable - * @since vecmath 1.3 - */ - public Object clone() { - // Since there are no arrays we can just use Object.clone() - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - } - - /** - * Get the x coordinate. - * - * @return the x coordinate. - * @since vecmath 1.5 - */ - public final float getX() { - return x; - } - - - /** - * Set the x coordinate. - * - * @param x value to x coordinate. - * @since vecmath 1.5 - */ - public final void setX(float x) { - this.x = x; - } - - - /** - * Get the y coordinate. - * - * @return the y coordinate. - * @since vecmath 1.5 - */ - public final float getY() { - return y; - } - - - /** - * Set the y coordinate. - * - * @param y value to y coordinate. - * @since vecmath 1.5 - */ - public final void setY(float y) { - this.y = y; - } - - /** - * Get the z coordinate. - * - * @return the z coordinate. - * @since vecmath 1.5 - */ - public final float getZ() { - return z; - } - - - /** - * Set the z coordinate. - * - * @param z value to z coordinate. - * @since vecmath 1.5 - */ - public final void setZ(float z) { - this.z = z; - } - - - /** - * Get the w coordinate. - * - * @return the w coordinate. - * @since vecmath 1.5 - */ - public final float getW() { - return w; - } - - - /** - * Set the w coordinate. - * - * @param w value to w coordinate. - * @since vecmath 1.5 - */ - public final void setW(float w) { - this.w = w; - } -} diff --git a/src/main/java/com/volmit/adapt/util/V.java b/src/main/java/com/volmit/adapt/util/V.java deleted file mode 100644 index 91c80c34b..000000000 --- a/src/main/java/com/volmit/adapt/util/V.java +++ /dev/null @@ -1,128 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.List; - -public class V { - private final Object o; - private boolean local; - private boolean suppress = false; - - public V(Class c, Object... parameters) { - this.o = Violator.construct(c, parameters); - this.local = true; - } - - public V(Object o) { - this.o = o; - this.local = true; - } - - public V(Object o, boolean local, boolean suppress) { - this(o); - this.local = local; - this.suppress = suppress; - } - - public V(Object o, boolean local) { - this(o); - this.local = local; - } - - public T get(Class t) { - try { - return local ? Violator.getDeclaredAnnotation(o.getClass(), t) : Violator.getAnnotation(o.getClass(), t); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - - return null; - } - - public T get(Class t, String mn, Class... pars) { - try { - return local ? Violator.getDeclaredAnnotation(Violator.getDeclaredMethod(o.getClass(), mn, pars), t) : Violator.getAnnotation(Violator.getMethod(o.getClass(), mn, pars), t); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - - return null; - } - - public T get(Class t, String mn) { - try { - return local ? Violator.getDeclaredAnnotation(Violator.getDeclaredField(o.getClass(), mn), t) : Violator.getAnnotation(Violator.getField(o.getClass(), mn), t); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - - return null; - } - - @SuppressWarnings("unchecked") - public T get(String field) { - try { - return (T) (local ? Violator.getDeclaredField(o.getClass(), field) : Violator.getField(o.getClass(), field)).get(o); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - - return null; - } - - public Object invoke(String method, Object... parameters) { - List> par = new ArrayList<>(); - - for (Object i : parameters) { - par.add(i.getClass()); - } - - try { - return (local ? Violator.getDeclaredMethod(o.getClass(), method, par.toArray(new Class[par.size()])) : Violator.getMethod(o.getClass(), method, par.toArray(new Class[par.size()]))).invoke(o, parameters); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - - return null; - } - - public void set(String field, Object value) { - try { - // https://github.com/VolmitSoftware/Mortar/issues/5 - (local ? Violator.getDeclaredField(o.getClass(), field) : Violator.getField(o.getClass(), field)).set(o, value); - } catch (Throwable e) { - if (!suppress) { - e.printStackTrace(); - } - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/VecMathUtil.java b/src/main/java/com/volmit/adapt/util/VecMathUtil.java deleted file mode 100644 index d8743b2e2..000000000 --- a/src/main/java/com/volmit/adapt/util/VecMathUtil.java +++ /dev/null @@ -1,87 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * Utility vecmath class used when computing the hash code for vecmath - * objects containing float or double values. This fixes Issue 36. - */ -class VecMathUtil { - /** - * Do not construct an instance of this class. - */ - private VecMathUtil() { - } - - /** - * Returns the representation of the specified floating-point - * value according to the IEEE 754 floating-point "single format" - * bit layout, after first mapping -0.0 to 0.0. This method is - * identical to Float.floatToIntBits(float) except that an integer - * value of 0 is returned for a floating-point value of - * -0.0f. This is done for the purpose of computing a hash code - * that satisfies the contract of hashCode() and equals(). The - * equals() method in each vecmath class does a pair-wise "==" - * test on each floating-point field in the class (e.g., x, y, and - * z for a Tuple3f). Since 0.0f == -0.0f returns true, - * we must also return the same hash code for two objects, one of - * which has a field with a value of -0.0f and the other of which - * has a cooresponding field with a value of 0.0f. - * - * @param f an input floating-point number - * @return the integer bits representing that floating-point - * number, after first mapping -0.0f to 0.0f - */ - static int floatToIntBits(float f) { - // Check for +0 or -0 - if (f == 0.0f) { - return 0; - } else { - return Float.floatToIntBits(f); - } - } - - /** - * Returns the representation of the specified floating-point - * value according to the IEEE 754 floating-point "double format" - * bit layout, after first mapping -0.0 to 0.0. This method is - * identical to Double.doubleToLongBits(double) except that an - * integer value of 0L is returned for a floating-point value of - * -0.0. This is done for the purpose of computing a hash code - * that satisfies the contract of hashCode() and equals(). The - * equals() method in each vecmath class does a pair-wise "==" - * test on each floating-point field in the class (e.g., x, y, and - * z for a Tuple3d). Since 0.0 == -0.0 returns true, we - * must also return the same hash code for two objects, one of - * which has a field with a value of -0.0 and the other of which - * has a cooresponding field with a value of 0.0. - * - * @param d an input double precision floating-point number - * @return the integer bits representing that floating-point - * number, after first mapping -0.0f to 0.0f - */ - static long doubleToLongBits(double d) { - // Check for +0 or -0 - if (d == 0.0) { - return 0L; - } else { - return Double.doubleToLongBits(d); - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/Vector2d.java b/src/main/java/com/volmit/adapt/util/Vector2d.java deleted file mode 100644 index 8788409b0..000000000 --- a/src/main/java/com/volmit/adapt/util/Vector2d.java +++ /dev/null @@ -1,168 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 2-element vector that is represented by double-precision floating - * point x,y coordinates. - */ -public class Vector2d extends Tuple2d implements java.io.Serializable { - - // Combatible with 1.1 - static final long serialVersionUID = 8572646365302599857L; - - /** - * Constructs and initializes a Vector2d from the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public Vector2d(double x, double y) { - super(x, y); - } - - - /** - * Constructs and initializes a Vector2d from the specified array. - * - * @param v the array of length 2 containing xy in order - */ - public Vector2d(double[] v) { - super(v); - } - - - /** - * Constructs and initializes a Vector2d from the specified Vector2d. - * - * @param v1 the Vector2d containing the initialization x y data - */ - public Vector2d(Vector2d v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector2d from the specified Vector2f. - * - * @param v1 the Vector2f containing the initialization x y data - */ - public Vector2d(Vector2f v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector2d from the specified Tuple2d. - * - * @param t1 the Tuple2d containing the initialization x y data - */ - public Vector2d(Tuple2d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector2d from the specified Tuple2f. - * - * @param t1 the Tuple2f containing the initialization x y data - */ - public Vector2d(Tuple2f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector2d to (0,0). - */ - public Vector2d() { - super(); - } - - - /** - * Computes the dot product of the this vector and vector v1. - * - * @param v1 the other vector - */ - public final double dot(Vector2d v1) { - return (this.x * v1.x + this.y * v1.y); - } - - - /** - * Returns the length of this vector. - * - * @return the length of this vector - */ - public final double length() { - return Math.sqrt(this.x * this.x + this.y * this.y); - } - - /** - * Returns the squared length of this vector. - * - * @return the squared length of this vector - */ - public final double lengthSquared() { - return (this.x * this.x + this.y * this.y); - } - - /** - * Sets the value of this vector to the normalization of vector v1. - * - * @param v1 the un-normalized vector - */ - public final void normalize(Vector2d v1) { - double norm; - - norm = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y); - this.x = v1.x * norm; - this.y = v1.y * norm; - } - - /** - * Normalizes this vector in place. - */ - public final void normalize() { - double norm; - - norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y); - this.x *= norm; - this.y *= norm; - } - - - /** - * Returns the angle in radians between this vector and the vector - * parameter; the return value is constrained to the range [0,PI]. - * - * @param v1 the other vector - * @return the angle in radians in the range [0,PI] - */ - public final double angle(Vector2d v1) { - double vDot = this.dot(v1) / (this.length() * v1.length()); - if (vDot < -1.0) vDot = -1.0; - if (vDot > 1.0) vDot = 1.0; - return Math.acos(vDot); - - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Vector2f.java b/src/main/java/com/volmit/adapt/util/Vector2f.java deleted file mode 100644 index 71bc62510..000000000 --- a/src/main/java/com/volmit/adapt/util/Vector2f.java +++ /dev/null @@ -1,168 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 2-element vector that is represented by single-precision floating - * point x,y coordinates. - */ -public class Vector2f extends Tuple2f implements java.io.Serializable { - - // Combatible with 1.1 - static final long serialVersionUID = -2168194326883512320L; - - /** - * Constructs and initializes a Vector2f from the specified xy coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - */ - public Vector2f(float x, float y) { - super(x, y); - } - - - /** - * Constructs and initializes a Vector2f from the specified array. - * - * @param v the array of length 2 containing xy in order - */ - public Vector2f(float[] v) { - super(v); - } - - - /** - * Constructs and initializes a Vector2f from the specified Vector2f. - * - * @param v1 the Vector2f containing the initialization x y data - */ - public Vector2f(Vector2f v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector2f from the specified Vector2d. - * - * @param v1 the Vector2d containing the initialization x y data - */ - public Vector2f(Vector2d v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector2f from the specified Tuple2f. - * - * @param t1 the Tuple2f containing the initialization x y data - */ - public Vector2f(Tuple2f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector2f from the specified Tuple2d. - * - * @param t1 the Tuple2d containing the initialization x y data - */ - public Vector2f(Tuple2d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector2f to (0,0). - */ - public Vector2f() { - super(); - } - - - /** - * Computes the dot product of the this vector and vector v1. - * - * @param v1 the other vector - */ - public final float dot(Vector2f v1) { - return (this.x * v1.x + this.y * v1.y); - } - - - /** - * Returns the length of this vector. - * - * @return the length of this vector - */ - public final float length() { - return (float) Math.sqrt(this.x * this.x + this.y * this.y); - } - - /** - * Returns the squared length of this vector. - * - * @return the squared length of this vector - */ - public final float lengthSquared() { - return (this.x * this.x + this.y * this.y); - } - - /** - * Sets the value of this vector to the normalization of vector v1. - * - * @param v1 the un-normalized vector - */ - public final void normalize(Vector2f v1) { - float norm; - - norm = (float) (1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y)); - this.x = v1.x * norm; - this.y = v1.y * norm; - } - - /** - * Normalizes this vector in place. - */ - public final void normalize() { - float norm; - - norm = (float) - (1.0 / Math.sqrt(this.x * this.x + this.y * this.y)); - this.x *= norm; - this.y *= norm; - } - - - /** - * Returns the angle in radians between this vector and the vector - * parameter; the return value is constrained to the range [0,PI]. - * - * @param v1 the other vector - * @return the angle in radians in the range [0,PI] - */ - public final float angle(Vector2f v1) { - double vDot = this.dot(v1) / (this.length() * v1.length()); - if (vDot < -1.0) vDot = -1.0; - if (vDot > 1.0) vDot = 1.0; - return ((float) (Math.acos(vDot))); - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Vector3d.java b/src/main/java/com/volmit/adapt/util/Vector3d.java deleted file mode 100644 index 1abd32e4c..000000000 --- a/src/main/java/com/volmit/adapt/util/Vector3d.java +++ /dev/null @@ -1,192 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 3-element vector that is represented by double-precision floating point - * x,y,z coordinates. If this value represents a normal, then it should - * be normalized. - */ -public class Vector3d extends Tuple3d implements java.io.Serializable { - - // Combatible with 1.1 - static final long serialVersionUID = 3761969948420550442L; - - /** - * Constructs and initializes a Vector3d from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Vector3d(double x, double y, double z) { - super(x, y, z); - } - - - /** - * Constructs and initializes a Vector3d from the array of length 3. - * - * @param v the array of length 3 containing xyz in order - */ - public Vector3d(double[] v) { - super(v); - } - - - /** - * Constructs and initializes a Vector3d from the specified Vector3d. - * - * @param v1 the Vector3d containing the initialization x y z data - */ - public Vector3d(Vector3d v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector3d from the specified Vector3f. - * - * @param v1 the Vector3f containing the initialization x y z data - */ - public Vector3d(Vector3f v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector3d from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Vector3d(Tuple3f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector3d from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Vector3d(Tuple3d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector3d to (0,0,0). - */ - public Vector3d() { - super(); - } - - - /** - * Sets this vector to the vector cross product of vectors v1 and v2. - * - * @param v1 the first vector - * @param v2 the second vector - */ - public final void cross(Vector3d v1, Vector3d v2) { - double x, y; - - x = v1.y * v2.z - v1.z * v2.y; - y = v2.x * v1.z - v2.z * v1.x; - this.z = v1.x * v2.y - v1.y * v2.x; - this.x = x; - this.y = y; - } - - - /** - * Sets the value of this vector to the normalization of vector v1. - * - * @param v1 the un-normalized vector - */ - public final void normalize(Vector3d v1) { - double norm; - - norm = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z); - this.x = v1.x * norm; - this.y = v1.y * norm; - this.z = v1.z * norm; - } - - - /** - * Normalizes this vector in place. - */ - public final void normalize() { - double norm; - - norm = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); - this.x *= norm; - this.y *= norm; - this.z *= norm; - } - - - /** - * Returns the dot product of this vector and vector v1. - * - * @param v1 the other vector - * @return the dot product of this and v1 - */ - public final double dot(Vector3d v1) { - return (this.x * v1.x + this.y * v1.y + this.z * v1.z); - } - - - /** - * Returns the squared length of this vector. - * - * @return the squared length of this vector - */ - public final double lengthSquared() { - return (this.x * this.x + this.y * this.y + this.z * this.z); - } - - - /** - * Returns the length of this vector. - * - * @return the length of this vector - */ - public final double length() { - return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); - } - - - /** - * Returns the angle in radians between this vector and the vector - * parameter; the return value is constrained to the range [0,PI]. - * - * @param v1 the other vector - * @return the angle in radians in the range [0,PI] - */ - public final double angle(Vector3d v1) { - double vDot = this.dot(v1) / (this.length() * v1.length()); - if (vDot < -1.0) vDot = -1.0; - if (vDot > 1.0) vDot = 1.0; - return Math.acos(vDot); - } - - -} diff --git a/src/main/java/com/volmit/adapt/util/Vector3f.java b/src/main/java/com/volmit/adapt/util/Vector3f.java deleted file mode 100644 index d73758ff0..000000000 --- a/src/main/java/com/volmit/adapt/util/Vector3f.java +++ /dev/null @@ -1,189 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -/** - * A 3-element vector that is represented by single-precision floating point - * x,y,z coordinates. If this value represents a normal, then it should - * be normalized. - */ -public class Vector3f extends Tuple3f implements java.io.Serializable { - - // Combatible with 1.1 - static final long serialVersionUID = -7031930069184524614L; - - /** - * Constructs and initializes a Vector3f from the specified xyz coordinates. - * - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - */ - public Vector3f(float x, float y, float z) { - super(x, y, z); - } - - - /** - * Constructs and initializes a Vector3f from the array of length 3. - * - * @param v the array of length 3 containing xyz in order - */ - public Vector3f(float[] v) { - super(v); - } - - - /** - * Constructs and initializes a Vector3f from the specified Vector3f. - * - * @param v1 the Vector3f containing the initialization x y z data - */ - public Vector3f(Vector3f v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector3f from the specified Vector3d. - * - * @param v1 the Vector3d containing the initialization x y z data - */ - public Vector3f(Vector3d v1) { - super(v1); - } - - - /** - * Constructs and initializes a Vector3f from the specified Tuple3f. - * - * @param t1 the Tuple3f containing the initialization x y z data - */ - public Vector3f(Tuple3f t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector3f from the specified Tuple3d. - * - * @param t1 the Tuple3d containing the initialization x y z data - */ - public Vector3f(Tuple3d t1) { - super(t1); - } - - - /** - * Constructs and initializes a Vector3f to (0,0,0). - */ - public Vector3f() { - super(); - } - - - /** - * Returns the squared length of this vector. - * - * @return the squared length of this vector - */ - public final float lengthSquared() { - return (this.x * this.x + this.y * this.y + this.z * this.z); - } - - /** - * Returns the length of this vector. - * - * @return the length of this vector - */ - public final float length() { - return (float) - Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); - } - - - /** - * Sets this vector to be the vector cross product of vectors v1 and v2. - * - * @param v1 the first vector - * @param v2 the second vector - */ - public final void cross(Vector3f v1, Vector3f v2) { - float x, y; - - x = v1.y * v2.z - v1.z * v2.y; - y = v2.x * v1.z - v2.z * v1.x; - this.z = v1.x * v2.y - v1.y * v2.x; - this.x = x; - this.y = y; - } - - /** - * Computes the dot product of this vector and vector v1. - * - * @param v1 the other vector - * @return the dot product of this vector and v1 - */ - public final float dot(Vector3f v1) { - return (this.x * v1.x + this.y * v1.y + this.z * v1.z); - } - - /** - * Sets the value of this vector to the normalization of vector v1. - * - * @param v1 the un-normalized vector - */ - public final void normalize(Vector3f v1) { - float norm; - - norm = (float) (1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z)); - this.x = v1.x * norm; - this.y = v1.y * norm; - this.z = v1.z * norm; - } - - /** - * Normalizes this vector in place. - */ - public final void normalize() { - float norm; - - norm = (float) - (1.0 / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)); - this.x *= norm; - this.y *= norm; - this.z *= norm; - } - - - /** - * Returns the angle in radians between this vector and the vector - * parameter; the return value is constrained to the range [0,PI]. - * - * @param v1 the other vector - * @return the angle in radians in the range [0,PI] - */ - public final float angle(Vector3f v1) { - double vDot = this.dot(v1) / (this.length() * v1.length()); - if (vDot < -1.0) vDot = -1.0; - if (vDot > 1.0) vDot = 1.0; - return ((float) (Math.acos(vDot))); - } - -} diff --git a/src/main/java/com/volmit/adapt/util/Violator.java b/src/main/java/com/volmit/adapt/util/Violator.java deleted file mode 100644 index 523f5d053..000000000 --- a/src/main/java/com/volmit/adapt/util/Violator.java +++ /dev/null @@ -1,270 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentSkipListMap; - -public class Violator { - protected static ConcurrentSkipListMap nodes = new ConcurrentSkipListMap(); - - private static String id(Object o, Object h) { - if (o instanceof Field) { - return id(((Field) o).getDeclaringClass(), null) + "." + ((Field) o).getName(); - } - - if (o instanceof String) { - return (String) o; - } - - if (o instanceof Class) { - return ((Class) o).getCanonicalName(); - } - - if (o instanceof Constructor) { - Constructor co = (Constructor) o; - - String mx = ""; - - for (Class i : co.getParameterTypes()) { - mx += "," + i.getCanonicalName(); - } - - mx = mx.length() >= 1 ? mx.substring(1) : mx; - - return id(co.getDeclaringClass(), null) + "(" + mx + ")"; - } - - if (o instanceof Method) { - String mx = ""; - - for (Class i : ((Method) o).getParameterTypes()) { - mx += "," + i.getCanonicalName(); - } - - mx = mx.length() >= 1 ? mx.substring(1) : mx; - - return id(((Method) o).getDeclaringClass(), null) + "." + ((Method) o).getName() + "(" + mx + ")"; - } - - if (o instanceof Annotation) { - Annotation a = (Annotation) o; - return "@" + a.annotationType().getCanonicalName() + "[" + id(h, null) + "]"; - } - - return o.hashCode() + o.toString(); - } - - private static void p(String n, Object o) { - nodes.put(n, o); - } - - private static boolean h(String n) { - return nodes.containsKey(n); - } - - private static Object g(String n) { - return nodes.get(n); - } - - public static Constructor getConstructor(Class c, Class... params) throws NoSuchMethodException, SecurityException { - String mx = ""; - - for (Class i : params) { - mx += "," + i.getCanonicalName(); - } - - mx = mx.length() >= 1 ? mx.substring(1) : mx; - - if (!h(id(c, null) + "(" + mx + ")")) { - Constructor co = c.getConstructor(params); - co.setAccessible(true); - p(id(co, null), co); - } - - return (Constructor) g(id(c, null) + "(" + mx + ")"); - } - - @SuppressWarnings("rawtypes") - public static Field getField(Class c, String name) throws Throwable { - if (!h(id(c, null) + "." + name)) { - try { - Field f = c.getField(name); - f.setAccessible(true); - p(id(c, null) + "." + name, f); - } catch (NoSuchFieldException e) { - Class s = c.getSuperclass(); - if (null == s) { - throw e; - } - Field f = s.getField(name); - f.setAccessible(true); - p(id(c, null) + "." + name, f); - } - } - - return (Field) g(id(c, null) + "." + name); - } - - @SuppressWarnings("rawtypes") - public static Field getDeclaredField(Class c, String name) throws Throwable { - if (!h(id(c, null) + "." + name)) { - try { - Field f = c.getDeclaredField(name); - f.setAccessible(true); - p(id(c, null) + "." + name, f); - } catch (NoSuchFieldException e) { - Class s = c.getSuperclass(); - if (null == s) { - throw e; - } - Field f = s.getDeclaredField(name); - f.setAccessible(true); - p(id(c, null) + "." + name, f); - } - } - - return (Field) g(id(c, null) + "." + name); - } - - public static Method getMethod(Class c, String name, Class... pars) throws Throwable { - String iv = ""; - String mx = ""; - - for (Class i : pars) { - mx += "," + i.getCanonicalName(); - } - - mx = mx.length() >= 1 ? mx.substring(1) : mx; - iv = id(c, null) + "." + name + "(" + mx + ")"; - - if (!h(iv)) { - Method f = c.getMethod(name, pars); - f.setAccessible(true); - p(iv, f); - } - - return (Method) g(iv); - } - - @SuppressWarnings("unchecked") - public static T construct(Class c, Object... parameters) { - List> cv = new ArrayList>(); - - for (Object i : parameters) { - cv.add(i.getClass()); - } - - try { - Constructor co = getConstructor(c, cv.toArray(new Class[cv.size()])); - return (T) co.newInstance(parameters); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - public static Method getDeclaredMethod(Class c, String name, Class... pars) throws Throwable { - String iv = ""; - String mx = ""; - - for (Class i : pars) { - mx += "," + i.getCanonicalName(); - } - - mx = mx.length() >= 1 ? mx.substring(1) : mx; - iv = id(c, null) + "." + name + "(" + mx + ")"; - - if (!h(iv)) { - Method f = c.getDeclaredMethod(name, pars); - f.setAccessible(true); - p(iv, f); - } - - return (Method) g(iv); - } - - @SuppressWarnings("unchecked") - public static T getAnnotation(Class c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + c.getCanonicalName() + "]")) { - T f = c.getAnnotation(a); - p(id(f, c), f); - } - - return (T) g("@" + a.getCanonicalName() + "[" + c.getCanonicalName() + "]"); - } - - @SuppressWarnings("unchecked") - public static T getDeclaredAnnotation(Class c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + c.getCanonicalName() + "]")) { - T f = c.getDeclaredAnnotation(a); - p(id(f, c), f); - } - - return (T) g("@" + a.getCanonicalName() + "[" + c.getCanonicalName() + "]"); - } - - @SuppressWarnings("unchecked") - public static T getAnnotation(Field c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + id(c, null) + "]")) { - T f = c.getAnnotation(a); - p(id(f, c), f); - } - - return (T) g("@" + a.getCanonicalName() + "[" + id(c, null) + "]"); - } - - @SuppressWarnings("unchecked") - public static T getDeclaredAnnotation(Field c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + id(c, null) + "]")) { - T f = c.getDeclaredAnnotation(a); - p(id(f, c), f); - } - - return (T) g("@" + a.getCanonicalName() + "[" + id(c, null) + "]"); - } - - @SuppressWarnings("unchecked") - public static T getAnnotation(Method c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + id(c, null) + "]")) { - T f = c.getAnnotation(a); - p(id(f, c), f); - } - - return (T) g("@" + a.getCanonicalName() + "[" + id(c, null) + "]"); - } - - @SuppressWarnings("unchecked") - public static T getDeclaredAnnotation(Method c, Class a) throws Throwable { - if (!h("@" + a.getCanonicalName() + "[" + id(c, null) + "]")) { - T f = c.getDeclaredAnnotation(a); - p(id(f, c), f); - - System.out.println("Set as " + id(f, c) + " as " + ("@" + a.getCanonicalName() + "[" + id(c, null) + "]")); - } - - return (T) g("@" + a.getCanonicalName() + "[" + id(c, null) + "]"); - } -} diff --git a/src/main/java/com/volmit/adapt/util/VoidOutputStream.java b/src/main/java/com/volmit/adapt/util/VoidOutputStream.java deleted file mode 100644 index 26304b70a..000000000 --- a/src/main/java/com/volmit/adapt/util/VoidOutputStream.java +++ /dev/null @@ -1,29 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import java.io.IOException; -import java.io.OutputStream; - -public class VoidOutputStream extends OutputStream { - @Override - public void write(int b) throws IOException { - // poof - } -} diff --git a/src/main/java/com/volmit/adapt/util/WeightMap.java b/src/main/java/com/volmit/adapt/util/WeightMap.java deleted file mode 100644 index 804d4381f..000000000 --- a/src/main/java/com/volmit/adapt/util/WeightMap.java +++ /dev/null @@ -1,63 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -import com.volmit.adapt.util.collection.KMap; - -public class WeightMap extends KMap { - private static final long serialVersionUID = 87558033900969389L; - private boolean modified = false; - private double lastWeight = 0; - - public double getPercentChance(T t) { - if (totalWeight() <= 0) { - return 0; - } - - return getWeight(t) / totalWeight(); - } - - public void clear() { - modified = true; - } - - public WeightMap setWeight(T t, double weight) { - modified = true; - put(t, weight); - - return this; - } - - public double getWeight(T t) { - return get(t); - } - - public double totalWeight() { - if (!modified) { - return lastWeight; - } - - modified = false; - Shrinkwrap s = new Shrinkwrap(0D); - k().forEach((d) -> s.set(s.get() + 1)); - lastWeight = s.get(); - - return lastWeight; - } -} diff --git a/src/main/java/com/volmit/adapt/util/Wrapper.java b/src/main/java/com/volmit/adapt/util/Wrapper.java deleted file mode 100644 index f1b38e74a..000000000 --- a/src/main/java/com/volmit/adapt/util/Wrapper.java +++ /dev/null @@ -1,70 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 com.volmit.adapt.util; - -public class Wrapper { - private T t; - - public Wrapper(T t) { - set(t); - } - - public void set(T t) { - this.t = t; - } - - public T get() { - return t; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((t == null) ? 0 : t.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Wrapper)) { - return false; - } - - Wrapper other = (Wrapper) obj; - if (t == null) { - return other.t == null; - } else return t.equals(other.t); - } - - @Override - public String toString() { - if (t != null) { - return get().toString(); - } - - return super.toString() + " (null)"; - } -} diff --git a/src/main/java/com/volmit/adapt/util/cache/ChunkCache2D.java b/src/main/java/com/volmit/adapt/util/cache/ChunkCache2D.java deleted file mode 100644 index 23a40c953..000000000 --- a/src/main/java/com/volmit/adapt/util/cache/ChunkCache2D.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.cache; - -//import com.volmit.react.util.function.Function2; - -import com.volmit.adapt.util.Function2; - -import java.util.concurrent.atomic.AtomicReferenceArray; - -public class ChunkCache2D { - private final AtomicReferenceArray cache; - - public ChunkCache2D() { - this.cache = new AtomicReferenceArray<>(256); - } - - public T get(int x, int z, Function2 resolver) { - int key = ((z & 15) * 16) + (x & 15); - T t = cache.get(key); - - if (t == null) { - t = resolver.apply(x, z); - cache.set(key, t); - } - - return t; - } -} diff --git a/src/main/java/com/volmit/adapt/util/cache/WorldCache2D.java b/src/main/java/com/volmit/adapt/util/cache/WorldCache2D.java deleted file mode 100644 index c6da7755e..000000000 --- a/src/main/java/com/volmit/adapt/util/cache/WorldCache2D.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.cache; - -//import com.volmit.react.util.data.KCache; -//import com.volmit.react.util.function.Function2; - -import com.volmit.adapt.util.Function2; -import com.volmit.adapt.util.data.KCache; - -public class WorldCache2D { - private final KCache> chunks; - private final Function2 resolver; - - public WorldCache2D(Function2 resolver) { - this.resolver = resolver; - chunks = new KCache<>((x) -> new ChunkCache2D<>(), 1024); - } - - public T get(int x, int z) { - ChunkCache2D chunk = chunks.get(Cache.key(x >> 4, z >> 4)); - return chunk.get(x, z, resolver); - } - - public long getSize() { - return chunks.getSize() * 256L; - } -} diff --git a/src/main/java/com/volmit/adapt/util/collection/GBiset.java b/src/main/java/com/volmit/adapt/util/collection/GBiset.java deleted file mode 100644 index 6a0f791da..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/GBiset.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - - -import java.io.Serializable; - -/** - * A Biset - * - * @param the first object type - * @param the second object type - * @author cyberpwn - */ -@SuppressWarnings("hiding") -public class GBiset implements Serializable { - private static final long serialVersionUID = 1L; - private A a; - private B b; - - /** - * Create a new Biset - * - * @param a the first object - * @param b the second object - */ - public GBiset(A a, B b) { - this.a = a; - this.b = b; - } - - /** - * Get the object of the type A - * - * @return the first object - */ - public A getA() { - return a; - } - - /** - * Set the first object - * - * @param a the first object A - */ - public void setA(A a) { - this.a = a; - } - - /** - * Get the second object - * - * @return the second object - */ - public B getB() { - return b; - } - - /** - * Set the second object - */ - public void setB(B b) { - this.b = b; - } -} diff --git a/src/main/java/com/volmit/adapt/util/collection/GListAdapter.java b/src/main/java/com/volmit/adapt/util/collection/GListAdapter.java deleted file mode 100644 index 9f29c552c..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/GListAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - - -import java.util.List; - -/** - * Adapts a list of objects into a list of other objects - * - * @param the from object in lists (the item INSIDE the list) - * @param the to object in lists (the item INSIDE the list) - * @author cyberpwn - */ -public abstract class GListAdapter { - /** - * Adapts a list of FROM to a list of TO - * - * @param from the from list - * @return the to list - */ - public List adapt(List from) { - List adapted = new KList<>(); - - for (FROM i : from) { - TO t = onAdapt(i); - - if (t != null) { - adapted.add(onAdapt(i)); - } - } - - return adapted; - } - - /** - * Adapts a list object FROM to TO for use with the adapt method - * - * @param from the from object - * @return the to object - */ - public abstract TO onAdapt(FROM from); -} diff --git a/src/main/java/com/volmit/adapt/util/collection/KList.java b/src/main/java/com/volmit/adapt/util/collection/KList.java deleted file mode 100644 index eafdef4c0..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/KList.java +++ /dev/null @@ -1,689 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - -import com.google.common.util.concurrent.AtomicDoubleArray; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.RNG; - -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collector; -import java.util.stream.Collectors; - -@SuppressWarnings("ALL") -public class KList extends ArrayList implements List { - private static final long serialVersionUID = -2892550695744823337L; - - @SafeVarargs - public KList(T... ts) { - super(); - add(ts); - } - - public KList() { - super(); - } - - public KList(int cap) { - super(cap); - } - - public KList(Collection values) { - super(); - add(values); - } - - public KList(Enumeration e) { - super(); - add(e); - } - - public static Collector> collector() { - return Collectors.toCollection(KList::new); - } - - public static KList asStringList(List oo) { - KList s = new KList(); - - for (Object i : oo) { - s.add(i.toString()); - } - - return s; - } - - public int indexOfAddIfNeeded(T v) { - addIfMissing(v); - return indexOf(v); - } - - /** - * Remove and return the last element. - */ - @Override - public T removeLast() { - return super.removeLast(); - } - - public void addMultiple(T t, int c) { - for (int i = 0; i < c; i++) { - add(t); - } - } - - private KList add(Enumeration e) { - while (e.hasMoreElements()) { - add(e.nextElement()); - } - - return this; - } - - public KList add(Collection values) { - addAll(values); - return this; - } - - /** - * Create a Map out of this list where this list becomes the values of the - * returned map. You must specify each key for each value in this list. In the - * function, returning null will not add the keyval pair. - * - * @param the inferred key type - * @param f the function - * @return the new map - */ - public KMap asValues(Function f) { - KMap m = new KMap(); - forEach((i) -> m.putNonNull(f.apply(i), i)); - return m; - } - - /** - * Create a Map out of this list where this list becomes the keys of the - * returned map. You must specify each value for each key in this list. In the - * function, returning null will not add the keyval pair. - * - * @param the inferred value type - * @param f the function - * @return the new map - */ - public KMap asKeys(Function f) { - KMap m = new KMap(); - forEach((i) -> m.putNonNull(i, f.apply(i))); - return m; - } - - /** - * Cut this list into targetCount sublists - * - * @param targetCount the target count of sublists - * @return the list of sublists - */ - public KList> divide(int targetCount) { - return split(size() / targetCount); - } - - /** - * Split this list into a list of sublists with roughly targetSize elements of T - * per sublist - * - * @param targetSize the target size - * @return the list of sublists - */ - public KList> split(int targetSize) { - targetSize = targetSize < 1 ? 1 : targetSize; - KList> gg = new KList<>(); - KList b = new KList<>(); - - for (T i : this) { - if (b.size() >= targetSize) { - gg.add(b.copy()); - b.clear(); - } - - b.add(i); - } - - if (!b.isEmpty()) { - gg.add(b); - } - - return gg; - } - - /** - * Rewrite this list by checking each value and changing the value (or not). - * Return null to remove the element in the function - * - * @param t the function - * @return the same list (not a copy) - */ - public KList rewrite(Function t) { - KList m = copy(); - clear(); - - for (T i : m) { - addNonNull(t.apply(i)); - } - - return this; - } - - /** - * To array - * - * @return the array - */ - @SuppressWarnings("unchecked") - public T[] array() { - return (T[]) toArray(); - } - - /** - * Return a copy of this list - * - * @return the copy - */ - public KList copy() { - return new KList().add(this); - } - - /** - * Shuffle the list - * - * @return the same list - */ - public KList shuffle() { - Collections.shuffle(this); - return this; - } - - public KList shuffle(Random rng) { - Collections.shuffle(this, rng); - return this; - } - - /** - * Sort the list (based on toString comparison) - * - * @return the same list - */ - public KList sort() { - Collections.sort(this, (a, b) -> a.toString().compareTo(b.toString())); - return this; - } - - /** - * Reverse this list - * - * @return the same list - */ - public KList reverse() { - Collections.reverse(this); - return this; - } - - @Override - public String toString() { - return "[" + toString(", ") + "]"; - } - - /** - * Tostring with a seperator for each item in the list - * - * @param split the seperator - * @return the string representing this object - */ - public String toString(String split) { - if (isEmpty()) { - return ""; - } - - if (size() == 1) { - return get(0) + ""; - } - - StringBuilder b = new StringBuilder(); - - for (String i : toStringList()) { - b.append(split).append(i == null ? "null" : i); - } - - return b.substring(split.length()); - } - - /** - * Invoke tostring on each value in the list into a string list - * - * @return the string list - */ - public KList toStringList() { - return kConvert((t) -> t + ""); - } - - /** - * Add the contents of the given list (v) into this list using a converter - * - * @param the type of the forign list - * @param v the forign (given) list - * @param converter the converter that converts the forign type into this list type - * @return this list (builder) - */ - public KList addFrom(List v, Function converter) { - v.forEach((g) -> add(converter.apply(g))); - return this; - } - - /** - * Convert this list into another list type. Such as GList to - * GList. list.convert((i) -> "" + i); - */ - public KList kConvert(Function converter) { - KList v = new KList(); - forEach((t) -> v.addNonNull(converter.apply(t))); - return v; - } - - public KList removeWhere(Predicate t) { - for (T i : copy()) { - if (t.test(i)) { - remove(i); - } - } - - return this; - } - - /** - * Adds T to the list, ignores if null - * - * @param t the value to add - * @return the same list - */ - public KList addNonNull(T t) { - if (t != null) { - super.add(t); - } - - return this; - } - - /** - * Swaps the values of index a and b. For example "hello", "world", "!" swap(1, - * 2) would change the list to "hello", "!", "world" - * - * @param a the first index - * @param b the second index - * @return the same list (builder), not a copy - */ - public KList swapIndexes(int a, int b) { - T aa = remove(a); - T bb = get(b); - add(a, bb); - remove(b); - add(b, aa); - - return this; - } - - /** - * Remove a number of elements from the list - * - * @param t the elements - * @return this list - */ - @SuppressWarnings("unchecked") - public KList remove(T... t) { - for (T i : t) { - super.remove(i); - } - - return this; - } - - /** - * Add another glist's contents to this one (addall builder) - * - * @param t the list - * @return the same list - */ - public KList add(KList t) { - super.addAll(t); - return this; - } - - /** - * Add a number of values to this list - * - * @param t the list - * @return this list - */ - @SuppressWarnings("unchecked") - public KList add(T... t) { - for (T i : t) { - super.add(i); - } - - return this; - } - - /** - * Check if this list has an index at the given index - * - * @param index the given index - * @return true if size > index - */ - public boolean hasIndex(int index) { - return size() > index && index >= 0; - } - - /** - * Get the last index of this list (size - 1) - * - * @return the last index of this list - */ - public int last() { - return size() - 1; - } - - /** - * Deduplicate this list by converting to linked hash set and back - * - * @return the deduplicated list - */ - public KList dedupe() { - LinkedHashSet lhs = new LinkedHashSet(this); - return qclear().add(lhs); - } - - /** - * Clear this list (and return it) - * - * @return the same list - */ - public KList qclear() { - super.clear(); - return this; - } - - /** - * Simply !isEmpty() - * - * @return true if this list has 1 or more element(s) - */ - public boolean hasElements() { - return !isEmpty(); - } - - /** - * Simply !isEmpty() - * - * @return true if this list has 1 or more element(s) - */ - public boolean isNotEmpty() { - return !isEmpty(); - } - - /** - * Pop the first item off this list and return it - * - * @return the popped off item or null if the list is empty - */ - public T pop() { - if (isEmpty()) { - return null; - } - - return remove(0); - } - - /** - * Pop the last item off this list and return it - * - * @return the popped off item or null if the list is empty - */ - public T popLast() { - if (isEmpty()) { - return null; - } - - return remove(last()); - } - - public T popRandom() { - if (isEmpty()) { - return null; - } - - if (size() == 1) { - return pop(); - } - - return remove(M.irand(0, last())); - } - - public T popRandom(RNG rng) { - if (isEmpty()) { - return null; - } - - if (size() == 1) { - return pop(); - } - - return remove(rng.i(0, last())); - } - - public KList sub(int f, int t) { - KList g = new KList<>(); - - for (int i = f; i < M.min(size(), t); i++) { - g.add(get(i)); - } - - return g; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(Object[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(int[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(double[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(AtomicDoubleArray values) { - for (int i = 0; i < values.length(); i++) { - add((T) ((Object) values.get(i))); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(float[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(byte[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(short[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(long[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - @SuppressWarnings("unchecked") - public KList forceAdd(boolean[] values) { - for (Object i : values) { - add((T) i); - } - - return this; - } - - public T middleValue() { - return get(middleIndex()); - } - - // todo: this should be private - public int middleIndex() { - return size() % 2 == 0 ? (size() / 2) : ((size() / 2) + 1); - } - - public T getRandom() { - if (isEmpty()) { - return null; - } - - if (size() == 1) { - return get(0); - } - - return get(M.irand(0, last())); - } - - public KList popRandom(RNG rng, int c) { - KList m = new KList<>(); - - for (int i = 0; i < c; i++) { - if (isEmpty()) { - break; - } - - m.add(popRandom()); - } - - return m; - } - - public T getRandom(RNG rng) { - if (isEmpty()) { - return null; - } - - if (size() == 1) { - return get(0); - } - - return get(rng.i(0, last())); - } - - public KList qdel(T t) { - remove(t); - return this; - } - - public KList qadd(T t) { - add(t); - return this; - } - - public KList qaddIfMissing(T t) { - addIfMissing(t); - return this; - } - - public KList removeDuplicates() { - KSet v = new KSet<>(); - v.addAll(this); - KList m = new KList<>(); - m.addAll(v); - return m; - } - - public KList nonNull() { - return removeWhere(Objects::isNull); - } - - public boolean addIfMissing(T t) { - if (!contains(t)) { - add(t); - return true; - } - - return false; - } - - public void addAllIfMissing(KList t) { - for (T i : t) { - if (!contains(i)) { - add(i); - } - } - } - - public KList shuffleCopy(Random rng) { - KList t = copy(); - t.shuffle(rng); - return t; - } - - public KList qdrop() { - pop(); - return this; - } -} diff --git a/src/main/java/com/volmit/adapt/util/collection/KMap.java b/src/main/java/com/volmit/adapt/util/collection/KMap.java deleted file mode 100644 index 50e9cde89..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/KMap.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - -import com.volmit.adapt.util.Consumer2; -import com.volmit.adapt.util.Consumer3; -import com.volmit.adapt.util.Queue; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@SuppressWarnings("ALL") -public class KMap extends ConcurrentHashMap { - private static final long serialVersionUID = 7288942695300448163L; - - public KMap() { - super(); - } - - public KMap(Map gMap) { - this(); - put(gMap); - } - - public K getKey(V value) { - for (KeyPair i : keypair()) { - if (i.getV().equals(value)) { - return i.getK(); - } - } - - return null; - } - - /** - * Puts a value into a map-value-list based on the key such that if GMap> where V is GList - * - * @param the list type in the value type - * @param k the key to look for - * @param vs the values to put into the list of the given key - * @return the same list (builder) - */ - @SuppressWarnings("unchecked") - public KMap putValueList(K k, S... vs) { - try { - KMap> s = (KMap>) this; - - if (!s.containsKey(k)) { - s.put(k, new KList()); - } - - s.get(k).add(vs); - } catch (Throwable e) { - e.printStackTrace(); - - } - - return this; - } - - /** - * Returns a sorted list of keys from this map, based on the sorting order of - * the values. - * - * @return the value-sorted key list - */ - public KList sortK() { - KList k = new KList(); - KList v = v(); - - Collections.sort(v, new Comparator() { - @Override - public int compare(V v, V t1) { - return v.toString().compareTo(t1.toString()); - } - }); - - for (V i : v) { - for (K j : k()) { - if (get(j).equals(i)) { - k.add(j); - } - } - } - - k.dedupe(); - return k; - } - - /** - * Returns a sorted list of keys from this map, based on the sorting order of - * the values. Sorting is based on numerical values - * - * @return the value-sorted key list - */ - public KList sortKNumber() { - KList k = new KList(); - KList v = v(); - - Collections.sort(v, new Comparator() { - @Override - public int compare(V v, V t1) { - Number n1 = (Number) v; - Number n2 = (Number) t1; - - return (int) ((n1.doubleValue() - n2.doubleValue()) * 1000); - } - }); - - for (V i : v) { - for (K j : k()) { - if (get(j).equals(i)) { - k.add(j); - } - } - } - - k.dedupe(); - return k; - } - - /** - * Put another map's values into this map - * - * @param m the map to insert - * @return this map (builder) - */ - public KMap put(Map m) { - putAll(m); - return this; - } - - /** - * Return a copy of this map - * - * @return the copied map - */ - public KMap copy() { - return new KMap(this); - } - - /** - * Loop through each keyvalue set (copy of it) with the map parameter - * - * @param f the function - * @return the same gmap - */ - public KMap rewrite(Consumer3> f) { - KMap m = copy(); - - for (K i : m.k()) { - f.accept(i, get(i), this); - } - - return this; - } - - /** - * Loop through each keyvalue set (copy of it) - * - * @param f the function - * @return the same gmap - */ - public KMap keach(Consumer2 f) { - for (K i : k()) { - f.accept(i, get(i)); - } - - return this; - } - - /** - * Flip the hashmap and flatten the value list even if there are multiple keys - * - * @return the flipped and flattened hashmap - */ - public KMap flipFlatten() { - KMap> f = kFlip(); - KMap m = new KMap<>(); - - for (V i : f.k()) { - m.putNonNull(i, m.isEmpty() ? null : m.get(0)); - } - - return m; - } - - /** - * Flip the hashmap so keys are now list-keys in the value position - * - * @return the flipped hashmap - */ - public KMap> kFlip() { - KMap> flipped = new KMap>(); - - for (K i : keySet()) { - if (i == null) { - continue; - } - - if (!flipped.containsKey(get(i))) { - flipped.put(get(i), new KList()); - } - - flipped.get(get(i)).add(i); - } - - return flipped; - } - - /** - * Sort values based on the keys sorting order - * - * @return the values (sorted) - */ - public KList sortV() { - KList v = new KList(); - KList k = k(); - - Collections.sort(k, new Comparator() { - @Override - public int compare(K v, K t1) { - return v.toString().compareTo(t1.toString()); - } - }); - - for (K i : k) { - for (V j : v()) { - if (get(i).equals(j)) { - v.add(j); - } - } - } - - v.dedupe(); - return v; - } - - public KList sortVNoDedupe() { - KList v = new KList(); - KList k = k(); - - Collections.sort(k, new Comparator() { - @Override - public int compare(K v, K t1) { - return v.toString().compareTo(t1.toString()); - } - }); - - for (K i : k) { - for (V j : v()) { - if (get(i).equals(j)) { - v.add(j); - } - } - } - - return v; - } - - /** - * Get a copy of this maps keys - * - * @return the keys - */ - public KList k() { - KList k = new KList(); - Enumeration kk = keys(); - - while (kk.hasMoreElements()) { - K kkk = kk.nextElement(); - k.add(kkk); - } - - return k; - } - - /** - * Get a copy of this maps values - * - * @return the values - */ - public KList v() { - return new KList(values()); - } - - /** - * Still works as it normally should except it returns itself (builder) - * - * @param key the key - * @param value the value (single only supported) - */ - public KMap qput(K key, V value) { - super.put(key, value); - return this; - } - - /** - * Works just like put, except it wont put anything unless the key and value are - * nonnull - * - * @param key the nonnull key - * @param value the nonnull value - * @return the same map - */ - public KMap putNonNull(K key, V value) { - if (key != null || value != null) { - put(key, value); - } - - return this; - } - - public V putThen(K key, V valueIfKeyNotPresent) { - if (!containsKey(key)) { - put(key, valueIfKeyNotPresent); - } - - return get(key); - } - - /** - * Clear this map and return it - * - * @return the cleared map - */ - public KMap qclear() { - super.clear(); - return this; - } - - /** - * Convert this map to keypairs - * - * @return the keypair list - */ - public KList> keypair() { - KList> g = new KList<>(); - forEach((k, v) -> g.add(new KeyPair(k, v))); - return g; - } - - /** - * Create a keypair queue - * - * @return the queue - */ - public Queue> enqueue() { - return Queue.create(keypair()); - } - - /** - * Create a key queue - * - * @return the queue - */ - public Queue enqueueKeys() { - return Queue.create(k()); - } - - /** - * Create a value queue - * - * @return the queue - */ - public Queue enqueueValues() { - return Queue.create(v()); - } -} diff --git a/src/main/java/com/volmit/adapt/util/collection/KSet.java b/src/main/java/com/volmit/adapt/util/collection/KSet.java deleted file mode 100644 index a8cd08539..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/KSet.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - -import org.jetbrains.annotations.NotNull; - -import java.io.Serial; -import java.io.Serializable; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; - - -public class KSet extends AbstractSet implements Serializable { - @Serial - private static final long serialVersionUID = 1L; - private final ConcurrentHashMap map; - - public KSet() { - map = new ConcurrentHashMap<>(); - } - - public KSet(Collection c) { - this(); - addAll(c); - } - - public KSet(int initialCapacity, float loadFactor) { - map = new ConcurrentHashMap<>(initialCapacity, loadFactor); - } - - public KSet(int initialCapacity) { - map = new ConcurrentHashMap<>(initialCapacity); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean contains(Object o) { - return map.containsKey(o); - } - - @Override - public boolean add(T t) { - return map.putIfAbsent(t, Boolean.TRUE) == null; - } - - @Override - public boolean remove(Object o) { - return map.remove(o) != null; - } - - @Override - public void clear() { - map.clear(); - } - - @NotNull - @Override - public Iterator iterator() { - return map.keySet().iterator(); - } - - public KSet copy() { - return new KSet<>(this); - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/collection/KeyPair.java b/src/main/java/com/volmit/adapt/util/collection/KeyPair.java deleted file mode 100644 index af0698f37..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/KeyPair.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - -/** - * Represents a keypair - * - * @param the key type - * @param the value type - * @author cyberpwn - */ -@SuppressWarnings("hiding") -public class KeyPair { - private K k; - private V v; - - /** - * Create a keypair - * - * @param k the key - * @param v the value - */ - public KeyPair(K k, V v) { - this.k = k; - this.v = v; - } - - public K getK() { - return k; - } - - public void setK(K k) { - this.k = k; - } - - public V getV() { - return v; - } - - public void setV(V v) { - this.v = v; - } -} diff --git a/src/main/java/com/volmit/adapt/util/collection/StateList.java b/src/main/java/com/volmit/adapt/util/collection/StateList.java deleted file mode 100644 index bad070c8f..000000000 --- a/src/main/java/com/volmit/adapt/util/collection/StateList.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.collection; - -public class StateList { - private final KList states; - - public StateList(String... states) { - this.states = new KList(states); - - if (getBits() > 64) { - throw new RuntimeException("StateLists cannot exceed 64 bits! You are trying to use " + getBits() + " bits!"); - } - } - - public StateList(Enum... states) { - this.states = new KList>().kConvert(Enum::name); - - if (getBits() > 64) { - throw new RuntimeException("StateLists cannot exceed 64 bits! You are trying to use " + getBits() + " bits!"); - } - } - - public long max() { - return (long) (Math.pow(2, getBits()) - 1); - } - - public KList getEnabled(long list) { - KList f = new KList<>(); - - for (String i : states) { - if (is(list, i)) { - f.add(i); - } - } - - return f; - } - - public long of(String... enabledStates) { - long b = 0; - - for (String i : enabledStates) { - b |= getBit(i); - } - - return b; - } - - public long set(long list, String state, boolean enabled) { - long bit = getBit(state); - boolean is = is(list, state); - - if (enabled && !is) { - return list | bit; - } else if (!enabled && is) { - return list ^ bit; - } - - return list; - } - - public boolean is(long list, String state) { - long bit = getBit(state); - - return bit > 0 && (list & bit) == bit; - } - - public boolean hasBit(String state) { - return getBit(state) > 0; - } - - public long getBit(String state) { - return getBit(states.indexOf(state)); - } - - public long getBit(int index) { - return (long) (index < 0 ? -1 : Math.pow(2, index)); - } - - public int getBytes() { - return getBits() == 0 ? 0 : ((getBits() >> 2) + 1); - } - - public int getBits() { - return states.size(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/command/FCommand.java b/src/main/java/com/volmit/adapt/util/command/FCommand.java deleted file mode 100644 index e850d5122..000000000 --- a/src/main/java/com/volmit/adapt/util/command/FCommand.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.volmit.adapt.util.command; - -public interface FCommand { - -} diff --git a/src/main/java/com/volmit/adapt/util/data/B.java b/src/main/java/com/volmit/adapt/util/data/B.java deleted file mode 100644 index 866922e5f..000000000 --- a/src/main/java/com/volmit/adapt/util/data/B.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - - -import art.arcane.chrono.ChronoLatch; -//import com.volmit.react.React; -//import com.volmit.react.util.collection.KList; -//import com.volmit.react.util.collection.KMap; -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KMap; -import com.volmit.adapt.util.reflect.registries.Materials; -import it.unimi.dsi.fastutil.ints.*; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.block.data.type.Leaves; -import org.bukkit.block.data.type.PointedDripstone; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.bukkit.Material.*; - -public class B { - private static final KMap custom = new KMap<>(); - - private static final Material AIR_MATERIAL = Material.AIR; - private static final BlockData AIR = AIR_MATERIAL.createBlockData(); - private static final IntSet foliageCache = buildFoliageCache(); - private static final IntSet deepslateCache = buildDeepslateCache(); - private static final Int2IntMap normal2DeepslateCache = buildNormal2DeepslateCache(); - private static final Int2IntMap deepslate2NormalCache = buildDeepslate2NormalCache(); - private static final IntSet decorantCache = buildDecorantCache(); - private static final IntSet storageCache = buildStorageCache(); - private static final IntSet storageChestCache = buildStorageChestCache(); - private static final IntSet litCache = buildLitCache(); - private static final ChronoLatch clw = new ChronoLatch(1000); - - private static IntSet buildFoliageCache() { - IntSet b = new IntOpenHashSet(); - Arrays.stream(new Material[]{ - POPPY, - DANDELION, - CORNFLOWER, - SWEET_BERRY_BUSH, - CRIMSON_ROOTS, - WARPED_ROOTS, - NETHER_SPROUTS, - ALLIUM, - AZURE_BLUET, - BLUE_ORCHID, - OXEYE_DAISY, - LILY_OF_THE_VALLEY, - WITHER_ROSE, - DARK_OAK_SAPLING, - ACACIA_SAPLING, - JUNGLE_SAPLING, - BIRCH_SAPLING, - SPRUCE_SAPLING, - Materials.CHERRY_SAPLING, - Materials.PALE_OAK_SAPLING, - OAK_SAPLING, - ORANGE_TULIP, - PINK_TULIP, - RED_TULIP, - WHITE_TULIP, - FERN, - LARGE_FERN, - Materials.GRASS, - TALL_GRASS - }).forEach((i) -> b.add(i.ordinal())); - - return IntSets.unmodifiable(b); - } - - private static IntSet buildDeepslateCache() { - IntSet b = new IntOpenHashSet(); - Arrays.stream(new Material[]{ - DEEPSLATE, - DEEPSLATE_BRICKS, - DEEPSLATE_BRICK_SLAB, - DEEPSLATE_BRICK_STAIRS, - DEEPSLATE_BRICK_WALL, - DEEPSLATE_TILE_SLAB, - DEEPSLATE_TILES, - DEEPSLATE_TILE_STAIRS, - DEEPSLATE_TILE_WALL, - CRACKED_DEEPSLATE_TILES - }).forEach((i) -> b.add(i.ordinal())); - - return IntSets.unmodifiable(b); - } - - private static Int2IntMap buildNormal2DeepslateCache() { - Int2IntMap b = new Int2IntOpenHashMap(); - - b.put(COAL_ORE.ordinal(), DEEPSLATE_COAL_ORE.ordinal()); - b.put(EMERALD_ORE.ordinal(), DEEPSLATE_EMERALD_ORE.ordinal()); - b.put(DIAMOND_ORE.ordinal(), DEEPSLATE_DIAMOND_ORE.ordinal()); - b.put(COPPER_ORE.ordinal(), DEEPSLATE_COPPER_ORE.ordinal()); - b.put(GOLD_ORE.ordinal(), DEEPSLATE_GOLD_ORE.ordinal()); - b.put(IRON_ORE.ordinal(), DEEPSLATE_IRON_ORE.ordinal()); - b.put(LAPIS_ORE.ordinal(), DEEPSLATE_LAPIS_ORE.ordinal()); - b.put(REDSTONE_ORE.ordinal(), DEEPSLATE_REDSTONE_ORE.ordinal()); - - return b; - } - - private static Int2IntMap buildDeepslate2NormalCache() { - Int2IntMap b = new Int2IntOpenHashMap(); - - b.put(DEEPSLATE_COAL_ORE.ordinal(), COAL_ORE.ordinal()); - b.put(DEEPSLATE_EMERALD_ORE.ordinal(), EMERALD_ORE.ordinal()); - b.put(DEEPSLATE_DIAMOND_ORE.ordinal(), DIAMOND_ORE.ordinal()); - b.put(DEEPSLATE_COPPER_ORE.ordinal(), COPPER_ORE.ordinal()); - b.put(DEEPSLATE_GOLD_ORE.ordinal(), GOLD_ORE.ordinal()); - b.put(DEEPSLATE_IRON_ORE.ordinal(), IRON_ORE.ordinal()); - b.put(DEEPSLATE_LAPIS_ORE.ordinal(), LAPIS_ORE.ordinal()); - b.put(DEEPSLATE_REDSTONE_ORE.ordinal(), REDSTONE_ORE.ordinal()); - - return b; - } - - private static IntSet buildDecorantCache() { - IntSet b = new IntOpenHashSet(); - Arrays.stream(new Material[]{ - Materials.GRASS, - TALL_GRASS, - FERN, - LARGE_FERN, - CORNFLOWER, - SUNFLOWER, - CHORUS_FLOWER, - POPPY, - DANDELION, - OXEYE_DAISY, - ORANGE_TULIP, - PINK_TULIP, - RED_TULIP, - WHITE_TULIP, - LILAC, - DEAD_BUSH, - SWEET_BERRY_BUSH, - ROSE_BUSH, - WITHER_ROSE, - ALLIUM, - BLUE_ORCHID, - LILY_OF_THE_VALLEY, - CRIMSON_FUNGUS, - WARPED_FUNGUS, - RED_MUSHROOM, - BROWN_MUSHROOM, - CRIMSON_ROOTS, - AZURE_BLUET, - WEEPING_VINES, - WEEPING_VINES_PLANT, - WARPED_ROOTS, - NETHER_SPROUTS, - TWISTING_VINES, - TWISTING_VINES_PLANT, - SUGAR_CANE, - WHEAT, - POTATOES, - CARROTS, - BEETROOTS, - NETHER_WART, - SEA_PICKLE, - SEAGRASS, - ACACIA_BUTTON, - BIRCH_BUTTON, - CRIMSON_BUTTON, - DARK_OAK_BUTTON, - JUNGLE_BUTTON, - OAK_BUTTON, - POLISHED_BLACKSTONE_BUTTON, - SPRUCE_BUTTON, - STONE_BUTTON, - WARPED_BUTTON, - TORCH, - SOUL_TORCH, - GLOW_LICHEN, - VINE, - SCULK_VEIN - }).forEach((i) -> b.add(i.ordinal())); - b.addAll(foliageCache); - - return IntSets.unmodifiable(b); - } - - private static IntSet buildLitCache() { - IntSet b = new IntOpenHashSet(); - Arrays.stream(new Material[]{ - GLOWSTONE, - AMETHYST_CLUSTER, - SMALL_AMETHYST_BUD, - MEDIUM_AMETHYST_BUD, - LARGE_AMETHYST_BUD, - END_ROD, - SOUL_SAND, - TORCH, - REDSTONE_TORCH, - SOUL_TORCH, - REDSTONE_WALL_TORCH, - WALL_TORCH, - SOUL_WALL_TORCH, - LANTERN, - CANDLE, - JACK_O_LANTERN, - REDSTONE_LAMP, - MAGMA_BLOCK, - LIGHT, - SHROOMLIGHT, - SEA_LANTERN, - SOUL_LANTERN, - FIRE, - SOUL_FIRE, - SEA_PICKLE, - BREWING_STAND, - REDSTONE_ORE, - }).forEach((i) -> b.add(i.ordinal())); - - return IntSets.unmodifiable(b); - } - - private static IntSet buildStorageCache() { - IntSet b = new IntOpenHashSet(); - Arrays.stream(new Material[]{ - CHEST, - SMOKER, - TRAPPED_CHEST, - SHULKER_BOX, - WHITE_SHULKER_BOX, - ORANGE_SHULKER_BOX, - MAGENTA_SHULKER_BOX, - LIGHT_BLUE_SHULKER_BOX, - YELLOW_SHULKER_BOX, - LIME_SHULKER_BOX, - PINK_SHULKER_BOX, - GRAY_SHULKER_BOX, - LIGHT_GRAY_SHULKER_BOX, - CYAN_SHULKER_BOX, - PURPLE_SHULKER_BOX, - BLUE_SHULKER_BOX, - BROWN_SHULKER_BOX, - GREEN_SHULKER_BOX, - RED_SHULKER_BOX, - BLACK_SHULKER_BOX, - BARREL, - DISPENSER, - DROPPER, - HOPPER, - FURNACE, - BLAST_FURNACE - }).forEach((i) -> b.add(i.ordinal())); - - return IntSets.unmodifiable(b); - } - - public static BlockData toDeepSlateOre(BlockData block, BlockData ore) { - int key = ore.getMaterial().ordinal(); - - if (isDeepSlate(block)) { - if (normal2DeepslateCache.containsKey(key)) { - return Material.values()[normal2DeepslateCache.get(key)].createBlockData(); - } - } else { - if (deepslate2NormalCache.containsKey(key)) { - return Material.values()[deepslate2NormalCache.get(key)].createBlockData(); - } - } - - return ore; - } - - public static boolean isDeepSlate(BlockData blockData) { - return deepslateCache.contains(blockData.getMaterial().ordinal()); - } - - public static boolean isOre(BlockData blockData) { - return blockData.getMaterial().name().endsWith("_ORE"); - } - - private static IntSet buildStorageChestCache() { - IntSet b = new IntOpenHashSet(storageCache); - b.remove(SMOKER.ordinal()); - b.remove(FURNACE.ordinal()); - b.remove(BLAST_FURNACE.ordinal()); - - return IntSets.unmodifiable(b); - } - - public static boolean canPlaceOnto(Material mat, Material onto) { - if ((onto.equals(CRIMSON_NYLIUM) || onto.equals(WARPED_NYLIUM)) && - (mat.equals(CRIMSON_FUNGUS) || mat.equals(CRIMSON_ROOTS) || mat.equals(WARPED_FUNGUS) || mat.equals(WARPED_ROOTS))) { - return true; - } - - if (isFoliage(mat)) { - if (!isFoliagePlantable(onto)) { - return false; - } - } - - if (onto.equals(Material.AIR) || - onto.equals(B.getMaterial("CAVE_AIR")) - || onto.equals(B.getMaterial("VOID_AIR"))) { - return false; - } - - if (onto.equals(Material.GRASS_BLOCK) && mat.equals(Material.DEAD_BUSH)) { - return false; - } - - if (onto.equals(Material.DIRT_PATH)) { - if (!mat.isSolid()) { - return false; - } - } - - if (onto.equals(Material.ACACIA_LEAVES) - || onto.equals(Material.BIRCH_LEAVES) - || onto.equals(Material.DARK_OAK_LEAVES) - || onto.equals(Material.JUNGLE_LEAVES) - || onto.equals(Material.OAK_LEAVES) - || onto.equals(Material.SPRUCE_LEAVES)) { - return mat.isSolid(); - } - - return true; - } - - public static boolean isFoliagePlantable(BlockData d) { - return d.getMaterial().equals(Material.GRASS_BLOCK) - || d.getMaterial().equals(Material.ROOTED_DIRT) - || d.getMaterial().equals(Material.DIRT) - || d.getMaterial().equals(Material.COARSE_DIRT) - || d.getMaterial().equals(Material.PODZOL); - } - - public static boolean isFoliagePlantable(Material d) { - return d.equals(Material.GRASS_BLOCK) - || d.equals(Material.DIRT) - || d.equals(TALL_GRASS) - || d.equals(TALL_SEAGRASS) - || d.equals(LARGE_FERN) - || d.equals(SUNFLOWER) - || d.equals(PEONY) - || d.equals(LILAC) - || d.equals(ROSE_BUSH) - || d.equals(Material.ROOTED_DIRT) - || d.equals(Material.COARSE_DIRT) - || d.equals(Material.PODZOL); - } - - public static boolean isWater(BlockData b) { - return b.getMaterial().equals(Material.WATER); - } - - public static BlockData getAir() { - return AIR; - } - - public static Material getMaterialOrNull(String bdx) { - try { - return Material.valueOf(bdx.trim().toUpperCase()); - } catch (Throwable e) { - e.printStackTrace(); - if (clw.flip()) { - Adapt.warn("Unknown Material: " + bdx); - } - return null; - } - } - - public static Material getMaterial(String bdx) { - Material m = getMaterialOrNull(bdx); - - if (m == null) { - return AIR_MATERIAL; - } - - return m; - } - - public static boolean isSolid(BlockData mat) { - if (mat == null) - return false; - return mat.getMaterial().isSolid(); - } - - public static BlockData getOrNull(String bdxf) { - try { - String bd = bdxf.trim(); - - if (!custom.isEmpty() && custom.containsKey(bd)) { - return custom.get(bd); - } - - if (bd.startsWith("minecraft:cauldron[level=")) { - bd = bd.replaceAll("\\Q:cauldron[\\E", ":water_cauldron["); - } - - if (bd.equals("minecraft:grass_path")) { - return DIRT_PATH.createBlockData(); - } - - BlockData bdx = parseBlockData(bd); - - if (bdx == null) { - return AIR; - } - - return bdx; - } catch (Throwable e) { - e.printStackTrace(); - } - - return null; - } - - public static BlockData get(String bdxf) { - BlockData bd = getOrNull(bdxf); - - if (bd != null) { - return bd; - } - - return AIR; - } - - private static synchronized BlockData createBlockData(String s) { - try { - return Bukkit.createBlockData(s); - } catch (IllegalArgumentException e) { - if (s.contains("[")) { - return createBlockData(s.split("\\Q[\\E")[0]); - } - } - - return null; - } - - private static BlockData parseBlockData(String ix) { - try { - BlockData bx = null; - - if (bx == null) { - try { - bx = createBlockData(ix.toLowerCase()); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - if (bx == null) { - try { - bx = createBlockData("minecraft:" + ix.toLowerCase()); - } catch (Throwable e) { - - } - } - - if (bx == null) { - try { - bx = Material.valueOf(ix.toUpperCase()).createBlockData(); - } catch (Throwable e) { - - } - } - - if (bx == null) { - return null; - } - - if (bx instanceof Leaves) { - ((Leaves) bx).setPersistent(false); - } - - return bx; - } catch (Throwable e) { - String block = ix.contains(":") ? ix.split(":")[1].toLowerCase() : ix.toLowerCase(); - String state = block.contains("[") ? block.split("\\Q[\\E")[1].split("\\Q]\\E")[0] : ""; - Map stateMap = new HashMap<>(); - if (!state.equals("")) { - Arrays.stream(state.split(",")).forEach(s -> stateMap.put(s.split("=")[0], s.split("=")[1])); - } - block = block.split("\\Q[\\E")[0]; - - switch (block) { - case "cauldron" -> block = "water_cauldron"; - case "grass_path" -> block = "dirt_path"; - case "concrete" -> block = "white_concrete"; - case "wool" -> block = "white_wool"; - case "beetroots" -> { - if (stateMap.containsKey("age")) { - String updated = stateMap.get("age"); - switch (updated) { - case "7" -> updated = "3"; - case "3", "4", "5" -> updated = "2"; - case "1", "2" -> updated = "1"; - } - stateMap.put("age", updated); - } - } - } - - Map newStates = new HashMap<>(); - for (String key : stateMap.keySet()) { //Iterate through every state and check if its valid - try { - String newState = block + "[" + key + "=" + stateMap.get(key) + "]"; - createBlockData(newState); - newStates.put(key, stateMap.get(key)); - - } catch (IllegalArgumentException ignored) { - } - } - - //Combine all the "good" states again - state = newStates.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.joining(",")); - if (!state.equals("")) state = "[" + state + "]"; - String newBlock = block + state; - Adapt.debug("Converting " + ix + " to " + newBlock); - - try { - return createBlockData(newBlock); - } catch (Throwable e1) { - e1.printStackTrace(); - } - - return null; - } - } - - public static boolean isStorage(BlockData mat) { - return storageCache.contains(mat.getMaterial().ordinal()); - } - - public static boolean isStorageChest(BlockData mat) { - return storageChestCache.contains(mat.getMaterial().ordinal()); - } - - public static boolean isLit(BlockData mat) { - return litCache.contains(mat.getMaterial().ordinal()); - } - - public static boolean isUpdatable(BlockData mat) { - return isLit(mat) - || isStorage(mat) - || (mat instanceof PointedDripstone - && ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP)); - } - - public static boolean isFoliage(Material d) { - return foliageCache.contains(d.ordinal()); - } - - public static boolean isFoliage(BlockData d) { - return isFoliage(d.getMaterial()); - } - - public static boolean isDecorant(BlockData m) { - return decorantCache.contains(m.getMaterial().ordinal()); - } - - public static KList get(KList find) { - KList b = new KList<>(); - - for (String i : find) { - BlockData bd = get(i); - - if (bd != null) { - b.add(bd); - } - } - - return b; - } - - public static boolean isFluid(BlockData d) { - return d.getMaterial().equals(Material.WATER) || d.getMaterial().equals(Material.LAVA); - } - - public static boolean isAirOrFluid(BlockData d) { - return isAir(d) || isFluid(d); - } - - public static boolean isAir(BlockData d) { - if (d == null) { - return true; - } - - return d.getMaterial().equals(Material.AIR) || d.getMaterial().equals(Material.CAVE_AIR) || d.getMaterial().equals(Material.VOID_AIR); - } - - - public synchronized static String[] getBlockTypes() { - KList bt = new KList<>(); - - for (Material i : Material.values()) { - if (i.isBlock()) { - String v = i.createBlockData().getAsString(true); - - if (v.contains("[")) { - v = v.split("\\Q[\\E")[0]; - } - - bt.add(v); - } - } - - return bt.toArray(new String[0]); - } - - public static String[] getItemTypes() { - KList bt = new KList<>(); - - for (Material i : Material.values()) { - String v = i.name().toLowerCase().trim(); - bt.add(v); - } - - return bt.toArray(new String[0]); - } - - public static boolean isWaterLogged(BlockData b) { - return (b instanceof Waterlogged) && ((Waterlogged) b).isWaterlogged(); - } - - public static void registerCustomBlockData(String namespace, String key, BlockData blockData) { - custom.put(namespace + ":" + key, blockData); - } - - public static boolean isVineBlock(BlockData data) { - return switch (data.getMaterial()) { - case VINE, SCULK_VEIN, GLOW_LICHEN -> true; - default -> false; - }; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/ChunkCache.java b/src/main/java/com/volmit/adapt/util/data/ChunkCache.java deleted file mode 100644 index 4d930dab2..000000000 --- a/src/main/java/com/volmit/adapt/util/data/ChunkCache.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.function.Function2; - -import com.volmit.adapt.util.Function2; - -import java.util.concurrent.atomic.AtomicReferenceArray; - -public class ChunkCache { - private final AtomicReferenceArray cache; - - public ChunkCache() { - cache = new AtomicReferenceArray<>(256); - } - - public T compute(int x, int z, Function2 function) { - T t = get(x & 15, z & 15); - - if (t == null) { - t = function.apply(x, z); - set(x & 15, z & 15, t); - } - - return t; - } - - private void set(int x, int z, T t) { - cache.set(x * 16 + z, t); - } - - private T get(int x, int z) { - return cache.get(x * 16 + z); - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/ComplexCache.java b/src/main/java/com/volmit/adapt/util/data/ComplexCache.java deleted file mode 100644 index effd8d590..000000000 --- a/src/main/java/com/volmit/adapt/util/data/ComplexCache.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.cache.Cache; -//import com.volmit.react.util.collection.KMap; - -import com.volmit.adapt.util.cache.Cache; -import com.volmit.adapt.util.collection.KMap; - -public class ComplexCache { - private final KMap> chunks; - - public ComplexCache() { - chunks = new KMap<>(); - } - - public boolean has(int x, int z) { - return chunks.containsKey(Cache.key(x, z)); - } - - public void invalidate(int x, int z) { - chunks.remove(Cache.key(x, z)); - } - - public ChunkCache chunk(int x, int z) { - return chunks.computeIfAbsent(Cache.key(x, z), (f) -> new ChunkCache<>()); - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/Cuboid.java b/src/main/java/com/volmit/adapt/util/data/Cuboid.java deleted file mode 100644 index 98cb517e1..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Cuboid.java +++ /dev/null @@ -1,751 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.collection.KList; -//import com.volmit.react.util.math.Direction; -import com.volmit.adapt.util.Direction; -import com.volmit.adapt.util.collection.KList; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.entity.Entity; - -import java.util.*; - -/** - * Cuboids - * - * @author cyberpwn - */ -public class Cuboid implements Iterable, Cloneable, ConfigurationSerializable { - protected final String worldName; - protected int x1, y1, z1; - protected int x2, y2, z2; - - /** - * Construct a Cuboid given two Location objects which represent any two corners - * of the Cuboid. - * - * @param l1 one of the corners - * @param l2 the other corner - */ - public Cuboid(Location l1, Location l2) { - if (!l1.getWorld().equals(l2.getWorld())) { - throw new IllegalArgumentException("locations must be on the same world"); - } - - worldName = l1.getWorld().getName(); - x1 = Math.min(l1.getBlockX(), l2.getBlockX()); - y1 = Math.min(l1.getBlockY(), l2.getBlockY()); - z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); - x2 = Math.max(l1.getBlockX(), l2.getBlockX()); - y2 = Math.max(l1.getBlockY(), l2.getBlockY()); - z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); - } - - /** - * Construct a one-block Cuboid at the given Location of the Cuboid. - * - * @param l1 location of the Cuboid - */ - public Cuboid(Location l1) { - this(l1, l1); - } - - /** - * Copy constructor. - * - * @param other the Cuboid to copy - */ - public Cuboid(Cuboid other) { - this(other.getWorld().getName(), other.x1, other.y1, other.z1, other.x2, other.y2, other.z2); - } - - /** - * Construct a Cuboid in the given World and xyz co-ordinates - * - * @param world the Cuboid's world - * @param x1 X co-ordinate of corner 1 - * @param y1 Y co-ordinate of corner 1 - * @param z1 Z co-ordinate of corner 1 - * @param x2 X co-ordinate of corner 2 - * @param y2 Y co-ordinate of corner 2 - * @param z2 Z co-ordinate of corner 2 - */ - public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2) { - this.worldName = world.getName(); - this.x1 = Math.min(x1, x2); - this.x2 = Math.max(x1, x2); - this.y1 = Math.min(y1, y2); - this.y2 = Math.max(y1, y2); - this.z1 = Math.min(z1, z2); - this.z2 = Math.max(z1, z2); - } - - /** - * Construct a Cuboid in the given world name and xyz co-ordinates. - * - * @param worldName the Cuboid's world name - * @param x1 X co-ordinate of corner 1 - * @param y1 Y co-ordinate of corner 1 - * @param z1 Z co-ordinate of corner 1 - * @param x2 X co-ordinate of corner 2 - * @param y2 Y co-ordinate of corner 2 - * @param z2 Z co-ordinate of corner 2 - */ - private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2) { - this.worldName = worldName; - this.x1 = Math.min(x1, x2); - this.x2 = Math.max(x1, x2); - this.y1 = Math.min(y1, y2); - this.y2 = Math.max(y1, y2); - this.z1 = Math.min(z1, z2); - this.z2 = Math.max(z1, z2); - } - - public Cuboid(Map map) { - worldName = (String) map.get("worldName"); - x1 = (Integer) map.get("x1"); - x2 = (Integer) map.get("x2"); - y1 = (Integer) map.get("y1"); - y2 = (Integer) map.get("y2"); - z1 = (Integer) map.get("z1"); - z2 = (Integer) map.get("z2"); - } - - public KList getEntities() { - KList en = new KList<>(); - - for (Chunk i : getChunks()) { - for (Entity j : i.getEntities()) { - if (contains(j.getLocation())) { - en.add(j); - } - } - } - - return en; - } - - /** - * Set the locations - * - * @param l1 a - * @param l2 b - */ - public void set(Location l1, Location l2) { - x1 = Math.min(l1.getBlockX(), l2.getBlockX()); - y1 = Math.min(l1.getBlockY(), l2.getBlockY()); - z1 = Math.min(l1.getBlockZ(), l2.getBlockZ()); - x2 = Math.max(l1.getBlockX(), l2.getBlockX()); - y2 = Math.max(l1.getBlockY(), l2.getBlockY()); - z2 = Math.max(l1.getBlockZ(), l2.getBlockZ()); - } - - @Override - public Map serialize() { - Map map = new HashMap<>(); - map.put("worldName", worldName); - map.put("x1", x1); - map.put("y1", y1); - map.put("z1", z1); - map.put("x2", x2); - map.put("y2", y2); - map.put("z2", z2); - return map; - } - - public Cuboid flatten(int level) { - return new Cuboid(getWorld(), x1, level, z1, x2, level, z2); - } - - /** - * Get the Location of the lower northeast corner of the Cuboid (minimum XYZ - * co-ordinates). - * - * @return Location of the lower northeast corner - */ - public Location getLowerNE() { - return new Location(getWorld(), x1, y1, z1); - } - - /** - * Get the Location of the upper southwest corner of the Cuboid (maximum XYZ - * co-ordinates). - * - * @return Location of the upper southwest corner - */ - public Location getUpperSW() { - return new Location(getWorld(), x2, y2, z2); - } - - /** - * Get the the centre of the Cuboid - * - * @return Location at the centre of the Cuboid - */ - public Location getCenter() { - int x1 = getUpperX() + 1; - int y1 = getUpperY() + 1; - int z1 = getUpperZ() + 1; - return new Location(getWorld(), getLowerX() + (x1 - getLowerX()) / 2.0, getLowerY() + (y1 - getLowerY()) / 2.0, getLowerZ() + (z1 - getLowerZ()) / 2.0); - } - - /** - * Get the Cuboid's world. - * - * @return the World object representing this Cuboid's world - * @throws IllegalStateException if the world is not loaded - */ - public World getWorld() { - World world = Bukkit.getWorld(worldName); - if (world == null) { - throw new IllegalStateException("world '" + worldName + "' is not loaded"); - } - return world; - } - - /** - * Get the size of this Cuboid along the X axis - * - * @return Size of Cuboid along the X axis - */ - public int getSizeX() { - return (x2 - x1) + 1; - } - - /** - * Get the size of this Cuboid along the Y axis - * - * @return Size of Cuboid along the Y axis - */ - public int getSizeY() { - return (y2 - y1) + 1; - } - - /** - * Get the size of this Cuboid along the Z axis - * - * @return Size of Cuboid along the Z axis - */ - public int getSizeZ() { - return (z2 - z1) + 1; - } - - /** - * Get the cuboid dimensions - * - * @return the dimensions - */ - public Dimension getDimension() { - return new Dimension(getSizeX(), getSizeY(), getSizeZ()); - } - - /** - * Get the minimum X co-ordinate of this Cuboid - * - * @return the minimum X co-ordinate - */ - public int getLowerX() { - return x1; - } - - /** - * Get the minimum Y co-ordinate of this Cuboid - * - * @return the minimum Y co-ordinate - */ - public int getLowerY() { - return y1; - } - - /** - * Get the minimum Z co-ordinate of this Cuboid - * - * @return the minimum Z co-ordinate - */ - public int getLowerZ() { - return z1; - } - - /** - * Get the maximum X co-ordinate of this Cuboid - * - * @return the maximum X co-ordinate - */ - public int getUpperX() { - return x2; - } - - /** - * Get the maximum Y co-ordinate of this Cuboid - * - * @return the maximum Y co-ordinate - */ - public int getUpperY() { - return y2; - } - - /** - * Get the maximum Z co-ordinate of this Cuboid - * - * @return the maximum Z co-ordinate - */ - public int getUpperZ() { - return z2; - } - - /** - * Get the Blocks at the eight corners of the Cuboid. - * - * @return array of Block objects representing the Cuboid corners - */ - public Block[] corners() { - Block[] res = new Block[8]; - World w = getWorld(); - res[0] = w.getBlockAt(x1, y1, z1); - res[1] = w.getBlockAt(x1, y1, z2); - res[2] = w.getBlockAt(x1, y2, z1); - res[3] = w.getBlockAt(x1, y2, z2); - res[4] = w.getBlockAt(x2, y1, z1); - res[5] = w.getBlockAt(x2, y1, z2); - res[6] = w.getBlockAt(x2, y2, z1); - res[7] = w.getBlockAt(x2, y2, z2); - return res; - } - - /** - * Expand the Cuboid in the given direction by the given amount. Negative - * amounts will shrink the Cuboid in the given direction. Shrinking a cuboid's - * face past the opposite face is not an error and will return a valid Cuboid. - * - * @param dir the direction in which to expand - * @param amount the number of blocks by which to expand - * @return a new Cuboid expanded by the given direction and amount - */ - public Cuboid expand(CuboidDirection dir, int amount) { - return switch (dir) { - case North -> new Cuboid(worldName, x1 - amount, y1, z1, x2, y2, z2); - case South -> new Cuboid(worldName, x1, y1, z1, x2 + amount, y2, z2); - case East -> new Cuboid(worldName, x1, y1, z1 - amount, x2, y2, z2); - case West -> new Cuboid(worldName, x1, y1, z1, x2, y2, z2 + amount); - case Down -> new Cuboid(worldName, x1, y1 - amount, z1, x2, y2, z2); - case Up -> new Cuboid(worldName, x1, y1, z1, x2, y2 + amount, z2); - default -> throw new IllegalArgumentException("invalid direction " + dir); - }; - } - - public Cuboid expand(Direction dir, int amount) { - int ax = dir.toVector().getBlockX() == 1 ? amount : 0; - int sx = dir.toVector().getBlockX() == -1 ? -amount : 0; - int ay = dir.toVector().getBlockY() == 1 ? amount : 0; - int sy = dir.toVector().getBlockY() == -1 ? -amount : 0; - int az = dir.toVector().getBlockZ() == 1 ? amount : 0; - int sz = dir.toVector().getBlockZ() == -1 ? -amount : 0; - return new Cuboid(worldName, x1 + sx, y1 + sy, z1 + sz, x2 + ax, y2 + ay, z2 + az); - } - - /** - * Shift the Cuboid in the given direction by the given amount. - * - * @param dir the direction in which to shift - * @param amount the number of blocks by which to shift - * @return a new Cuboid shifted by the given direction and amount - */ - public Cuboid shift(CuboidDirection dir, int amount) { - return expand(dir, amount).expand(dir.opposite(), -amount); - } - - /** - * Outset (grow) the Cuboid in the given direction by the given amount. - * - * @param dir the direction in which to outset (must be Horizontal, Vertical, or - * Both) - * @param amount the number of blocks by which to outset - * @return a new Cuboid outset by the given direction and amount - */ - public Cuboid outset(CuboidDirection dir, int amount) { - Cuboid c = switch (dir) { - case Horizontal -> - expand(CuboidDirection.North, amount).expand(CuboidDirection.South, amount).expand(CuboidDirection.East, amount).expand(CuboidDirection.West, amount); - case Vertical -> expand(CuboidDirection.Down, amount).expand(CuboidDirection.Up, amount); - case Both -> outset(CuboidDirection.Horizontal, amount).outset(CuboidDirection.Vertical, amount); - default -> throw new IllegalArgumentException("invalid direction " + dir); - }; - return c; - } - - /** - * Inset (shrink) the Cuboid in the given direction by the given amount. - * Equivalent to calling outset() with a negative amount. - * - * @param dir the direction in which to inset (must be Horizontal, Vertical, or - * Both) - * @param amount the number of blocks by which to inset - * @return a new Cuboid inset by the given direction and amount - */ - public Cuboid inset(CuboidDirection dir, int amount) { - return outset(dir, -amount); - } - - /** - * Return true if the point at (x,y,z) is contained within this Cuboid. - * - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return true if the given point is within this Cuboid, false otherwise - */ - public boolean contains(int x, int y, int z) { - return x >= x1 && x <= x2 && y >= y1 && y <= y2 && z >= z1 && z <= z2; - } - - /** - * Check if the given Block is contained within this Cuboid. - * - * @param b the Block to check for - * @return true if the Block is within this Cuboid, false otherwise - */ - public boolean contains(Block b) { - return contains(b.getLocation()); - } - - /** - * Check if the given Location is contained within this Cuboid. - * - * @param l the Location to check for - * @return true if the Location is within this Cuboid, false otherwise - */ - public boolean contains(Location l) { - return worldName.equals(l.getWorld().getName()) && contains(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - } - - /** - * Get the volume of this Cuboid. - * - * @return the Cuboid volume, in blocks - */ - public int volume() { - return getSizeX() * getSizeY() * getSizeZ(); - } - - /** - * Get the average light level of all empty (air) blocks in the Cuboid. Returns - * 0 if there are no empty blocks. - * - * @return the average light level of this Cuboid - */ - public byte averageLightLevel() { - long total = 0; - int n = 0; - for (Block b : this) { - if (b.isEmpty()) { - total += b.getLightLevel(); - ++n; - } - } - return n > 0 ? (byte) (total / n) : 0; - } - - /** - * Contract the Cuboid, returning a Cuboid with any air around the edges - * removed, just large enough to include all non-air blocks. - * - * @return a new Cuboid with no external air blocks - */ - public Cuboid contract() { - return this.contract(CuboidDirection.Down).contract(CuboidDirection.South).contract(CuboidDirection.East).contract(CuboidDirection.Up).contract(CuboidDirection.North).contract(CuboidDirection.West); - } - - /** - * Contract the Cuboid in the given direction, returning a new Cuboid which has - * no exterior empty space. E.g. a direction of Down will push the top face - * downwards as much as possible. - * - * @param dir the direction in which to contract - * @return a new Cuboid contracted in the given direction - */ - public Cuboid contract(CuboidDirection dir) { - Cuboid face = getFace(dir.opposite()); - switch (dir) { - case Down -> { - while (face.containsOnly(Material.AIR) && face.getLowerY() > this.getLowerY()) { - face = face.shift(CuboidDirection.Down, 1); - } - return new Cuboid(worldName, x1, y1, z1, x2, face.getUpperY(), z2); - } - case Up -> { - while (face.containsOnly(Material.AIR) && face.getUpperY() < this.getUpperY()) { - face = face.shift(CuboidDirection.Up, 1); - } - return new Cuboid(worldName, x1, face.getLowerY(), z1, x2, y2, z2); - } - case North -> { - while (face.containsOnly(Material.AIR) && face.getLowerX() > this.getLowerX()) { - face = face.shift(CuboidDirection.North, 1); - } - return new Cuboid(worldName, x1, y1, z1, face.getUpperX(), y2, z2); - } - case South -> { - while (face.containsOnly(Material.AIR) && face.getUpperX() < this.getUpperX()) { - face = face.shift(CuboidDirection.South, 1); - } - return new Cuboid(worldName, face.getLowerX(), y1, z1, x2, y2, z2); - } - case East -> { - while (face.containsOnly(Material.AIR) && face.getLowerZ() > this.getLowerZ()) { - face = face.shift(CuboidDirection.East, 1); - } - return new Cuboid(worldName, x1, y1, z1, x2, y2, face.getUpperZ()); - } - case West -> { - while (face.containsOnly(Material.AIR) && face.getUpperZ() < this.getUpperZ()) { - face = face.shift(CuboidDirection.West, 1); - } - return new Cuboid(worldName, x1, y1, face.getLowerZ(), x2, y2, z2); - } - default -> throw new IllegalArgumentException("Invalid direction " + dir); - } - } - - /** - * Get the Cuboid representing the face of this Cuboid. The resulting Cuboid - * will be one block thick in the axis perpendicular to the requested face. - * - * @param dir which face of the Cuboid to get - * @return the Cuboid representing this Cuboid's requested face - */ - public Cuboid getFace(CuboidDirection dir) { - return switch (dir) { - case Down -> new Cuboid(worldName, x1, y1, z1, x2, y1, z2); - case Up -> new Cuboid(worldName, x1, y2, z1, x2, y2, z2); - case North -> new Cuboid(worldName, x1, y1, z1, x1, y2, z2); - case South -> new Cuboid(worldName, x2, y1, z1, x2, y2, z2); - case East -> new Cuboid(worldName, x1, y1, z1, x2, y2, z1); - case West -> new Cuboid(worldName, x1, y1, z2, x2, y2, z2); - default -> throw new IllegalArgumentException("Invalid direction " + dir); - }; - } - - /** - * Check if the Cuboid contains only blocks of the given type - * - * @param material the material to check for - * @return true if this Cuboid contains only blocks of the given type - */ - public boolean containsOnly(Material material) { - for (Block b : this) { - if (b.getType() != material) { - return false; - } - } - return true; - } - - /** - * Get the Cuboid big enough to hold both this Cuboid and the given one. - * - * @param other the other Cuboid to include - * @return a new Cuboid large enough to hold this Cuboid and the given Cuboid - */ - public Cuboid getBoundingCuboid(Cuboid other) { - if (other == null) { - return this; - } - - int xMin = Math.min(getLowerX(), other.getLowerX()); - int yMin = Math.min(getLowerY(), other.getLowerY()); - int zMin = Math.min(getLowerZ(), other.getLowerZ()); - int xMax = Math.max(getUpperX(), other.getUpperX()); - int yMax = Math.max(getUpperY(), other.getUpperY()); - int zMax = Math.max(getUpperZ(), other.getUpperZ()); - - return new Cuboid(worldName, xMin, yMin, zMin, xMax, yMax, zMax); - } - - /** - * Get a block relative to the lower NE point of the Cuboid. - * - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return the block at the given position - */ - public Block getRelativeBlock(int x, int y, int z) { - return getWorld().getBlockAt(x1 + x, y1 + y, z1 + z); - } - - /** - * Get a block relative to the lower NE point of the Cuboid in the given World. - * This version of getRelativeBlock() should be used if being called many times, - * to avoid excessive calls to getWorld(). - * - * @param w the World - * @param x the X co-ordinate - * @param y the Y co-ordinate - * @param z the Z co-ordinate - * @return the block at the given position - */ - public Block getRelativeBlock(World w, int x, int y, int z) { - return w.getBlockAt(x1 + x, y1 + y, z1 + z); - } - - /** - * Get a list of the chunks which are fully or partially contained in this - * cuboid. - * - * @return a list of Chunk objects - */ - public List getChunks() { - List res = new ArrayList<>(); - - World w = getWorld(); - int x1 = getLowerX() & ~0xf; - int x2 = getUpperX() & ~0xf; - int z1 = getLowerZ() & ~0xf; - int z2 = getUpperZ() & ~0xf; - for (int x = x1; x <= x2; x += 16) { - for (int z = z1; z <= z2; z += 16) { - res.add(w.getChunkAt(x >> 4, z >> 4)); - } - } - return res; - } - - /* - Set all the blocks within the Cuboid to the given MaterialData, using a - MassBlockUpdate object for fast updates. - - @param mat - * the MaterialData to set - * @param mbu - * the MassBlockUpdate object - */ - - /** - * Reset the light level of all blocks within this Cuboid. - */ - - /* - * (non-Javadoc) - * - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#clone() - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Cuboid clone() { - return new Cuboid(this); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "Cuboid: " + worldName + "," + x1 + "," + y1 + "," + z1 + "=>" + x2 + "," + y2 + "," + z2; - } - - public enum CuboidDirection { - - North, - East, - South, - West, - Up, - Down, - Horizontal, - Vertical, - Both, - Unknown; - - public CuboidDirection opposite() { - return switch (this) { - case North -> South; - case East -> West; - case South -> North; - case West -> East; - case Horizontal -> Vertical; - case Vertical -> Horizontal; - case Up -> Down; - case Down -> Up; - case Both -> Both; - default -> Unknown; - }; - } - } - - public static class CuboidIterator implements Iterator { - private final World w; - private final int baseX; - private final int baseY; - private final int baseZ; - private final int sizeX; - private final int sizeY; - private final int sizeZ; - private int x, y, z; - - public CuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2) { - this.w = w; - baseX = x1; - baseY = y1; - baseZ = z1; - sizeX = Math.abs(x2 - x1) + 1; - sizeY = Math.abs(y2 - y1) + 1; - sizeZ = Math.abs(z2 - z1) + 1; - x = y = z = 0; - } - - @Override - public boolean hasNext() { - return x < sizeX && y < sizeY && z < sizeZ; - } - - @Override - public Block next() { - Block b = w.getBlockAt(baseX + x, baseY + y, baseZ + z); - if (++x >= sizeX) { - x = 0; - if (++y >= sizeY) { - y = 0; - ++z; - } - } - return b; - } - - @Override - public void remove() { - // nop - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/DUTF.java b/src/main/java/com/volmit/adapt/util/data/DUTF.java deleted file mode 100644 index 5b3580b2e..000000000 --- a/src/main/java/com/volmit/adapt/util/data/DUTF.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -/** - *

Encodes signed and unsigned values using a common variable-length - * scheme, found for example in - *
- * Google's Protocol Buffers. It uses fewer bytes to encode smaller values, - * but will use slightly more bytes to encode large values.

- *

- *

Signed values are further encoded using so-called zig-zag encoding - * in order to make them "compatible" with variable-length encoding.

- */ -public final class DUTF { - - private DUTF() { - } - - public static void write(String s, DataOutputStream dos) throws IOException { - byte[] b = s.getBytes(StandardCharsets.UTF_8); - dos.writeShort(b.length); - dos.write(b); - } - - public static String read(DataInputStream din) throws IOException { - byte[] d = new byte[din.readShort()]; - din.read(d); - return new String(d, StandardCharsets.UTF_8); - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/DataPalette.java b/src/main/java/com/volmit/adapt/util/data/DataPalette.java deleted file mode 100644 index abe115344..000000000 --- a/src/main/java/com/volmit/adapt/util/data/DataPalette.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.collection.KList; - -import com.volmit.adapt.util.collection.KList; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public class DataPalette { - private final KList palette; - - public DataPalette() { - this(new KList<>(16)); - } - - public DataPalette(KList palette) { - this.palette = palette; - } - - public static DataPalette getPalette(IOAdapter adapter, DataInputStream din) throws IOException { - KList palette = new KList<>(); - int s = din.readShort() - Short.MIN_VALUE; - - for (int i = 0; i < s; i++) { - palette.add(adapter.read(din)); - } - - return new DataPalette<>(palette); - } - - public KList getPalette() { - return palette; - } - - public T get(int index) { - synchronized (palette) { - if (!palette.hasIndex(index)) { - return null; - } - - return palette.get(index); - } - } - - public int getIndex(T t) { - int v = 0; - - synchronized (palette) { - v = palette.indexOf(t); - - if (v == -1) { - v = palette.size(); - palette.add(t); - } - } - - return v; - } - - public void write(IOAdapter adapter, DataOutputStream dos) throws IOException { - synchronized (palette) { - dos.writeShort(getPalette().size() + Short.MIN_VALUE); - - for (T t : palette) { - adapter.write(t, dos); - } - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/Dimension.java b/src/main/java/com/volmit/adapt/util/data/Dimension.java deleted file mode 100644 index c8cfec9f9..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Dimension.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -/** - * Dimensions - * - * @author cyberpwn - */ -public class Dimension { - private final int width; - private final int height; - private final int depth; - - /** - * Make a dimension - * - * @param width width of this (X) - * @param height the height (Y) - * @param depth the depth (Z) - */ - public Dimension(int width, int height, int depth) { - this.width = width; - this.height = height; - this.depth = depth; - } - - /** - * Make a dimension - * - * @param width width of this (X) - * @param height the height (Y) - */ - public Dimension(int width, int height) { - this.width = width; - this.height = height; - this.depth = 0; - } - - /** - * Get the direction of the flat part of this dimension (null if no thin - * face) - * - * @return the direction of the flat pane or null - */ - public DimensionFace getPane() { - if (width == 1) { - return DimensionFace.X; - } - - if (height == 1) { - return DimensionFace.Y; - } - - if (depth == 1) { - return DimensionFace.Z; - } - - return null; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public int getDepth() { - return depth; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/DimensionFace.java b/src/main/java/com/volmit/adapt/util/data/DimensionFace.java deleted file mode 100644 index cc9a2459d..000000000 --- a/src/main/java/com/volmit/adapt/util/data/DimensionFace.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -/** - * Represents a dimension (coordinates not worlds) - * - * @author cyberpwn - */ -public enum DimensionFace { - /** - * The X dimension (width) - */ - X, - - /** - * The Y dimension (height) - */ - Y, - - /** - * The Z dimension (depth) - */ - Z -} diff --git a/src/main/java/com/volmit/adapt/util/data/DoubleArrayUtils.java b/src/main/java/com/volmit/adapt/util/data/DoubleArrayUtils.java deleted file mode 100644 index cac4e0303..000000000 --- a/src/main/java/com/volmit/adapt/util/data/DoubleArrayUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - - -import com.google.common.util.concurrent.AtomicDoubleArray; - -import java.util.Arrays; - -public class DoubleArrayUtils { - public static void shiftRight(double[] values, double push) { - if (values.length - 2 + 1 >= 0) System.arraycopy(values, 0, values, 1, values.length - 2 + 1); - - values[0] = push; - } - - public static void wrapRight(double[] values) { - double last = values[values.length - 1]; - shiftRight(values, last); - } - - public static void fill(double[] values, double value) { - Arrays.fill(values, value); - } - - public static void fill(AtomicDoubleArray values, double value) { - for (int i = 0; i < values.length(); i++) { - values.set(i, value); - } - } - -} diff --git a/src/main/java/com/volmit/adapt/util/data/Heafty.java b/src/main/java/com/volmit/adapt/util/data/Heafty.java deleted file mode 100644 index c8d5c97b0..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Heafty.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -@FunctionalInterface -public interface Heafty { - int getHeaft(); -} diff --git a/src/main/java/com/volmit/adapt/util/data/HeightMap.java b/src/main/java/com/volmit/adapt/util/data/HeightMap.java deleted file mode 100644 index b9625ddf2..000000000 --- a/src/main/java/com/volmit/adapt/util/data/HeightMap.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.util.Arrays; - -public class HeightMap { - private final int[] height; - - public HeightMap() { - height = new int[256]; - Arrays.fill(height, 0); - } - - public void setHeight(int x, int z, int h) { - height[x * 16 + z] = (h); - } - - public int getHeight(int x, int z) { - return height[x * 16 + z]; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/IOAdapter.java b/src/main/java/com/volmit/adapt/util/data/IOAdapter.java deleted file mode 100644 index 97c128835..000000000 --- a/src/main/java/com/volmit/adapt/util/data/IOAdapter.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public interface IOAdapter { - void write(T t, DataOutputStream dos) throws IOException; - - T read(DataInputStream din) throws IOException; -} diff --git a/src/main/java/com/volmit/adapt/util/data/InvertedBiomeGrid.java b/src/main/java/com/volmit/adapt/util/data/InvertedBiomeGrid.java deleted file mode 100644 index 6986ef864..000000000 --- a/src/main/java/com/volmit/adapt/util/data/InvertedBiomeGrid.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import org.bukkit.block.Biome; -import org.bukkit.generator.ChunkGenerator.BiomeGrid; - -public class InvertedBiomeGrid implements BiomeGrid { - private final BiomeGrid grid; - - public InvertedBiomeGrid(BiomeGrid real) { - this.grid = real; - } - - - @SuppressWarnings("deprecation") - @Override - public Biome getBiome(int arg0, int arg1) { - return grid.getBiome(arg0, arg1); - } - - - @Override - public Biome getBiome(int arg0, int arg1, int arg2) { - return grid.getBiome(arg0, 255 - arg1, arg2); - } - - @SuppressWarnings("deprecation") - @Override - public void setBiome(int arg0, int arg1, Biome arg2) { - grid.setBiome(arg0, arg1, arg2); - } - - @Override - public void setBiome(int arg0, int arg1, int arg2, Biome arg3) { - grid.setBiome(arg0, 255 - arg1, arg2, arg3); - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/KCache.java b/src/main/java/com/volmit/adapt/util/data/KCache.java deleted file mode 100644 index a87cc65bf..000000000 --- a/src/main/java/com/volmit/adapt/util/data/KCache.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import com.github.benmanes.caffeine.cache.CacheLoader; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import com.volmit.adapt.util.RollingSequence; -//import com.volmit.react.util.math.RollingSequence; - -public class KCache { - private final long max; - private final LoadingCache cache; - private final boolean fastDump; - private final RollingSequence msu = new RollingSequence(100); - private CacheLoader loader; - - public KCache(CacheLoader loader, long max) { - this(loader, max, false); - } - - public KCache(CacheLoader loader, long max, boolean fastDump) { - this.max = max; - this.fastDump = fastDump; - this.loader = loader; - this.cache = create(loader); - } - - private LoadingCache create(CacheLoader loader) { - return Caffeine - .newBuilder() - .maximumSize(max) - .initialCapacity((int) (max)) - .build((k) -> loader == null ? null : loader.load(k)); - } - - - public void setLoader(CacheLoader loader) { - this.loader = loader; - } - - public void invalidate(K k) { - cache.invalidate(k); - } - - public void invalidate() { - cache.invalidateAll(); - } - - public V get(K k) { - return cache.get(k); - } - - public long getSize() { - return cache.estimatedSize(); - } - - public KCache getRawCache() { - return this; - } - - public long getMaxSize() { - return max; - } - - public boolean isClosed() { - return false; - } - - public boolean contains(K next) { - return cache.getIfPresent(next) != null; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/MaterialBlock.java b/src/main/java/com/volmit/adapt/util/data/MaterialBlock.java deleted file mode 100644 index 536d6b8ad..000000000 --- a/src/main/java/com/volmit/adapt/util/data/MaterialBlock.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; - -/** - * Material blocks - * - * @author cyberpwn - */ -@SuppressWarnings("deprecation") -public class MaterialBlock { - private Material material; - private Byte data; - - /** - * Create a materialblock - * - * @param material the material - * @param data the data - */ - public MaterialBlock(Material material, Byte data) { - this.material = material; - this.data = data; - } - - public MaterialBlock(Material material) { - this.material = material; - data = 0; - } - - public MaterialBlock(Location location) { - this(location.getBlock()); - } - - public MaterialBlock(BlockState state) { - material = state.getType(); - data = state.getData().getData(); - } - - public MaterialBlock(Block block) { - material = block.getType(); - data = block.getData(); - } - - public MaterialBlock() { - material = Material.AIR; - data = 0; - } - - public Material getMaterial() { - return material; - } - - public void setMaterial(Material material) { - this.material = material; - } - - public Byte getData() { - return data; - } - - public void setData(Byte data) { - this.data = data; - } - - @Override - public String toString() { - if (getData() == 0) { - return getMaterial().toString(); - } - - return getMaterial().toString() + ":" + getData(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((data == null) ? 0 : data.hashCode()); - result = prime * result + ((material == null) ? 0 : material.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - MaterialBlock other = (MaterialBlock) obj; - if (data == null) { - if (other.data != null) { - return false; - } - } else if (!data.equals(other.data)) { - return false; - } - return material == other.material; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/NibbleArray.java b/src/main/java/com/volmit/adapt/util/data/NibbleArray.java deleted file mode 100644 index 12572dd92..000000000 --- a/src/main/java/com/volmit/adapt/util/data/NibbleArray.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.StringJoiner; - -public class NibbleArray implements Writable { - private static final int[] MASKS = new int[8]; - - static { - for (int i = 0; i < MASKS.length; i++) { - MASKS[i] = maskFor(i); - } - } - - private final int size; - private final Object lock = new Object(); - private byte[] data; - private int depth; - private byte mask; - private transient int bitIndex, byteIndex, bitInByte; - - public NibbleArray(int capacity, DataInputStream in) throws IOException { - size = capacity; - read(in); - } - - public NibbleArray(int nibbleDepth, int capacity) { - if (nibbleDepth > 8 || nibbleDepth < 1) { - throw new IllegalArgumentException(); - } - - int neededBits = nibbleDepth * capacity; - - size = capacity; - depth = nibbleDepth; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(nibbleDepth); - } - - public NibbleArray(int nibbleDepth, int capacity, NibbleArray existing) { - if (nibbleDepth > 8 || nibbleDepth < 1) { - throw new IllegalArgumentException(); - } - - int neededBits = nibbleDepth * capacity; - size = capacity; - depth = nibbleDepth; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(nibbleDepth); - - for (int i = 0; i < Math.min(size, existing.size()); i++) { - set(i, existing.get(i)); - } - } - - public static int maskFor(int amountOfBits) { - return powerOfTwo(amountOfBits) - 1; - } - - public static int powerOfTwo(int power) { - int result = 1; - - for (int i = 0; i < power; i++) { - result *= 2; - } - - return result; - } - - public static String binaryString(byte b, ByteOrder byteOrder) { - String str = String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(' ', '0'); - - return byteOrder.equals(ByteOrder.BIG_ENDIAN) ? str : reverse(str); - } - - public static String reverse(String str) { - return new StringBuilder(str).reverse().toString(); - } - - @Override - public void write(DataOutputStream o) throws IOException { - o.writeByte(depth + Byte.MIN_VALUE); - o.write(data); - } - - @Override - public void read(DataInputStream i) throws IOException { - depth = i.readByte() - Byte.MIN_VALUE; - int neededBits = depth * size; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(depth); - i.read(data); - } - - public int size() { - return size; - } - - public byte get(int index) { - synchronized (lock) { - bitIndex = index * depth; - byteIndex = bitIndex >> 3; - bitInByte = bitIndex & 7; - int value = data[byteIndex] >> bitInByte; - - if (bitInByte + depth > 8) { - value |= data[byteIndex + 1] << bitInByte; - } - - return (byte) (value & mask); - } - } - - public byte get(int x, int y, int z) { - return get(index(x, y, z)); - } - - public int index(int x, int y, int z) { - return y << 8 | z << 4 | x; - } - - public void set(int x, int y, int z, int nibble) { - set(index(x, y, z), nibble); - } - - public void set(int index, int nibble) { - set(index, (byte) nibble); - } - - public void set(int index, byte nybble) { - synchronized (lock) { - bitIndex = index * depth; - byteIndex = bitIndex >> 3; - bitInByte = bitIndex & 7; - data[byteIndex] = (byte) (((~(data[byteIndex] & (mask << bitInByte)) & data[byteIndex]) | ((nybble & mask) << bitInByte)) & 0xff); - - if (bitInByte + depth > 8) { - data[byteIndex + 1] = (byte) (((~(data[byteIndex + 1] & MASKS[bitInByte + depth - 8]) & data[byteIndex + 1]) | ((nybble & mask) >> (8 - bitInByte))) & 0xff); - } - } - } - - public String toBitsString() { - return toBitsString(ByteOrder.BIG_ENDIAN); - } - - public String toBitsString(ByteOrder byteOrder) { - StringJoiner joiner = new StringJoiner(" "); - - for (byte datum : data) { - joiner.add(binaryString(datum, byteOrder)); - } - - return joiner.toString(); - } - - public void clear() { - Arrays.fill(data, (byte) 0); - } - - public void setAll(byte nibble) { - for (int i = 0; i < size; i++) { - set(i, nibble); - } - } - - public void setAll(int nibble) { - for (int i = 0; i < size; i++) { - set(i, (byte) nibble); - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/NibbleDataPalette.java b/src/main/java/com/volmit/adapt/util/data/NibbleDataPalette.java deleted file mode 100644 index 49e1304f1..000000000 --- a/src/main/java/com/volmit/adapt/util/data/NibbleDataPalette.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.collection.KList; - -import com.volmit.adapt.util.collection.KList; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public abstract class NibbleDataPalette implements Writable { - private static final int DEFAULT_BITS_PER_BLOCK = 4; - private static final int CAPACITY = 4096; - private int bpb; - private NibbleArray data; - private KList palette; - - public NibbleDataPalette(T defaultValue) { - palette = new KList<>(); - bpb = DEFAULT_BITS_PER_BLOCK; - data = new NibbleArray(bpb, CAPACITY); - data.setAll(Byte.MIN_VALUE); - getPaletteId(defaultValue); - } - - public abstract T readType(DataInputStream i) throws IOException; - - public abstract void writeType(T t, DataOutputStream o) throws IOException; - - @Override - public void write(DataOutputStream o) throws IOException { - o.writeByte(bpb + Byte.MIN_VALUE); - o.writeByte(palette.size() + Byte.MIN_VALUE); - - for (T i : palette) { - writeType(i, o); - } - - data.write(o); - } - - @Override - public void read(DataInputStream i) throws IOException { - bpb = i.readByte() - Byte.MIN_VALUE; - palette = new KList<>(); - int v = i.readByte() - Byte.MIN_VALUE; - - for (int j = 0; j < v; j++) { - palette.add(readType(i)); - } - - data = new NibbleArray(CAPACITY, i); - } - - private void expand() { - if (bpb < 8) { - changeBitsPerBlock(bpb + 1); - } else { - throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!"); - } - } - - public final void optimize() { - int targetBits = bpb; - int needed = palette.size(); - - for (int i = 1; i < bpb; i++) { - if (Math.pow(2, i) > needed) { - targetBits = i; - break; - } - } - - changeBitsPerBlock(targetBits); - } - - private void changeBitsPerBlock(int bits) { - bpb = bits; - data = new NibbleArray(bpb, CAPACITY, data); - } - - public final void set(int x, int y, int z, T d) { - data.set(getCoordinateIndex(x, y, z), getPaletteId(d)); - } - - public final T get(int x, int y, int z) { - return palette.get(data.get(getCoordinateIndex(x, y, z))); - } - - private int getPaletteId(T d) { - int index = palette.indexOf(d); - - if (index == -1) { - index = palette.size(); - palette.add(d); - - if (palette.size() > Math.pow(2, bpb)) { - expand(); - } - } - - return index + Byte.MIN_VALUE; - } - - private int getCoordinateIndex(int x, int y, int z) { - return y << 8 | z << 4 | x; - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/Recycler.java b/src/main/java/com/volmit/adapt/util/data/Recycler.java deleted file mode 100644 index 1cb03976b..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Recycler.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -public class Recycler { - private final List> pool; - private final Supplier factory; - - public Recycler(Supplier factory) { - pool = new CopyOnWriteArrayList<>(); - this.factory = factory; - } - - public int getFreeObjects() { - int m = 0; - RecycledObject o; - for (RecycledObject tRecycledObject : pool) { - o = tRecycledObject; - - if (!o.isUsed()) { - m++; - } - } - - return m; - } - - public int getUsedOjects() { - int m = 0; - RecycledObject o; - for (RecycledObject tRecycledObject : pool) { - o = tRecycledObject; - - if (o.isUsed()) { - m++; - } - } - - return m; - } - - public void dealloc(T t) { - RecycledObject o; - - for (RecycledObject tRecycledObject : pool) { - o = tRecycledObject; - if (o.isUsed() && System.identityHashCode(t) == System.identityHashCode(o.getObject())) { - o.dealloc(); - return; - } - } - } - - public T alloc() { - RecycledObject o; - - for (RecycledObject tRecycledObject : pool) { - o = tRecycledObject; - if (o.alloc()) { - return o.getObject(); - } - } - - expand(); - - return alloc(); - } - - public boolean contract() { - return contract(Math.max(getFreeObjects() / 2, Runtime.getRuntime().availableProcessors())); - } - - public boolean contract(int maxFree) { - int remove = getFreeObjects() - maxFree; - - if (remove > 0) { - RecycledObject o; - - for (int i = pool.size() - 1; i > 0; i--) { - o = pool.get(i); - if (!o.isUsed()) { - pool.remove(i); - remove--; - - if (remove <= 0) { - return true; - } - } - } - } - - return false; - } - - public void expand() { - if (pool.isEmpty()) { - expand(Runtime.getRuntime().availableProcessors()); - return; - } - - expand(getUsedOjects() + Runtime.getRuntime().availableProcessors()); - } - - public void expand(int by) { - for (int i = 0; i < by; i++) { - pool.add(new RecycledObject<>(factory.get())); - } - } - - public int size() { - return pool.size(); - } - - public void deallocAll() { - pool.clear(); - } - - public static class RecycledObject { - private final T object; - private final AtomicBoolean used; - - public RecycledObject(T object) { - this.object = object; - used = new AtomicBoolean(false); - } - - public T getObject() { - return object; - } - - public boolean isUsed() { - return used.get(); - } - - public void dealloc() { - used.set(false); - } - - public boolean alloc() { - if (used.get()) { - return false; - } - - used.set(true); - return true; - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/Varint.java b/src/main/java/com/volmit/adapt/util/data/Varint.java deleted file mode 100644 index 501efa5b3..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Varint.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -/** - *

Encodes signed and unsigned values using a common variable-length - * scheme, found for example in - * - * Google's Protocol Buffers. It uses fewer bytes to encode smaller values, - * but will use slightly more bytes to encode large values.

- *

- *

Signed values are further encoded using so-called zig-zag encoding - * in order to make them "compatible" with variable-length encoding.

- */ -public final class Varint { - - private Varint() { - } - - /** - * Encodes a value using the variable-length encoding from - * - * Google Protocol Buffers. It uses zig-zag encoding to efficiently - * encode signed values. If values are known to be nonnegative, - * {@link #writeUnsignedVarLong(long, DataOutput)} should be used. - * - * @param value value to encode - * @param out to writeNodeData bytes to - * @throws IOException if {@link DataOutput} throws {@link IOException} - */ - public static void writeSignedVarLong(long value, DataOutput out) throws IOException { - // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types - writeUnsignedVarLong((value << 1) ^ (value >> 63), out); - } - - /** - * Encodes a value using the variable-length encoding from - * - * Google Protocol Buffers. Zig-zag is not used, so input must not be negative. - * If values can be negative, use {@link #writeSignedVarLong(long, DataOutput)} - * instead. This method treats negative input as like a large unsigned value. - * - * @param value value to encode - * @param out to writeNodeData bytes to - * @throws IOException if {@link DataOutput} throws {@link IOException} - */ - public static void writeUnsignedVarLong(long value, DataOutput out) throws IOException { - while ((value & 0xFFFFFFFFFFFFFF80L) != 0L) { - out.writeByte(((int) value & 0x7F) | 0x80); - value >>>= 7; - } - out.writeByte((int) value & 0x7F); - } - - /** - * @see #writeSignedVarLong(long, DataOutput) - */ - public static void writeSignedVarInt(int value, DataOutput out) throws IOException { - // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types - writeUnsignedVarInt((value << 1) ^ (value >> 31), out); - } - - /** - * @see #writeUnsignedVarLong(long, DataOutput) - */ - public static void writeUnsignedVarInt(int value, DataOutput out) throws IOException { - while ((value & 0xFFFFFF80) != 0L) { - out.writeByte((value & 0x7F) | 0x80); - value >>>= 7; - } - out.writeByte(value & 0x7F); - } - - public static byte[] writeSignedVarInt(int value) { - // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types - return writeUnsignedVarInt((value << 1) ^ (value >> 31)); - } - - /** - * @see #writeUnsignedVarLong(long, DataOutput) - *

- * This one does not use streams and is much faster. - * Makes a single object each time, and that object is a primitive array. - */ - public static byte[] writeUnsignedVarInt(int value) { - byte[] byteArrayList = new byte[10]; - int i = 0; - while ((value & 0xFFFFFF80) != 0L) { - byteArrayList[i++] = ((byte) ((value & 0x7F) | 0x80)); - value >>>= 7; - } - byteArrayList[i] = ((byte) (value & 0x7F)); - byte[] out = new byte[i + 1]; - for (; i >= 0; i--) { - out[i] = byteArrayList[i]; - } - return out; - } - - /** - * @param in to read bytes from - * @return decode value - * @throws IOException if {@link DataInput} throws {@link IOException} - * @throws IllegalArgumentException if variable-length value does not terminate - * after 9 bytes have been read - * @see #writeSignedVarLong(long, DataOutput) - */ - public static long readSignedVarLong(DataInput in) throws IOException { - long raw = readUnsignedVarLong(in); - // This undoes the trick in writeSignedVarLong() - long temp = (((raw << 63) >> 63) ^ raw) >> 1; - // This extra step lets us deal with the largest signed values by treating - // negative results from read unsigned methods as like unsigned values - // Must re-flip the top bit if the original read value had it set. - return temp ^ (raw & (1L << 63)); - } - - /** - * @param in to read bytes from - * @return decode value - * @throws IOException if {@link DataInput} throws {@link IOException} - * @throws IllegalArgumentException if variable-length value does not terminate - * after 9 bytes have been read - * @see #writeUnsignedVarLong(long, DataOutput) - */ - public static long readUnsignedVarLong(DataInput in) throws IOException { - long value = 0L; - int i = 0; - long b; - while (((b = in.readByte()) & 0x80L) != 0) { - value |= (b & 0x7F) << i; - i += 7; - if (i > 63) { - throw new IllegalArgumentException("Variable length quantity is too long"); - } - } - return value | (b << i); - } - - /** - * @throws IllegalArgumentException if variable-length value does not terminate - * after 5 bytes have been read - * @throws IOException if {@link DataInput} throws {@link IOException} - * @see #readSignedVarLong(DataInput) - */ - public static int readSignedVarInt(DataInput in) throws IOException { - int raw = readUnsignedVarInt(in); - // This undoes the trick in writeSignedVarInt() - int temp = (((raw << 31) >> 31) ^ raw) >> 1; - // This extra step lets us deal with the largest signed values by treating - // negative results from read unsigned methods as like unsigned values. - // Must re-flip the top bit if the original read value had it set. - return temp ^ (raw & (1 << 31)); - } - - /** - * @throws IllegalArgumentException if variable-length value does not terminate - * after 5 bytes have been read - * @throws IOException if {@link DataInput} throws {@link IOException} - * @see #readUnsignedVarLong(DataInput) - */ - public static int readUnsignedVarInt(DataInput in) throws IOException { - int value = 0; - int i = 0; - int b; - while (((b = in.readByte()) & 0x80) != 0) { - value |= (b & 0x7F) << i; - i += 7; - if (i > 35) { - throw new IllegalArgumentException("Variable length quantity is too long"); - } - } - return value | (b << i); - } - - public static int readSignedVarInt(byte[] bytes) { - int raw = readUnsignedVarInt(bytes); - // This undoes the trick in writeSignedVarInt() - int temp = (((raw << 31) >> 31) ^ raw) >> 1; - // This extra step lets us deal with the largest signed values by treating - // negative results from read unsigned methods as like unsigned values. - // Must re-flip the top bit if the original read value had it set. - return temp ^ (raw & (1 << 31)); - } - - public static int readUnsignedVarInt(byte[] bytes) { - int value = 0; - int i = 0; - byte rb = Byte.MIN_VALUE; - for (byte b : bytes) { - rb = b; - if ((b & 0x80) == 0) { - break; - } - value |= (b & 0x7f) << i; - i += 7; - if (i > 35) { - throw new IllegalArgumentException("Variable length quantity is too long"); - } - } - return value | (rb << i); - } - -} diff --git a/src/main/java/com/volmit/adapt/util/data/WeightedRandom.java b/src/main/java/com/volmit/adapt/util/data/WeightedRandom.java deleted file mode 100644 index cdc73fc55..000000000 --- a/src/main/java/com/volmit/adapt/util/data/WeightedRandom.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -//import com.volmit.react.util.collection.KList; -//import com.volmit.react.util.collection.KeyPair; - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.collection.KeyPair; - -import java.util.Random; - -public class WeightedRandom { - - private final KList> weightedObjects = new KList<>(); - private final Random random; - private int totalWeight = 0; - - public WeightedRandom(Random random) { - this.random = random; - } - - public WeightedRandom() { - this.random = new Random(); - } - - public void put(T object, int weight) { - weightedObjects.add(new KeyPair<>(object, weight)); - totalWeight += weight; - } - - public T pullRandom() { - int pull = random.nextInt(totalWeight); - int index = 0; - while (pull > 0) { - pull -= weightedObjects.get(index).getV(); - index++; - } - return weightedObjects.get(index).getK(); - } - - public int getSize() { - return weightedObjects.size(); - } - - public void shuffle() { - weightedObjects.shuffle(random); - } -} diff --git a/src/main/java/com/volmit/adapt/util/data/Writable.java b/src/main/java/com/volmit/adapt/util/data/Writable.java deleted file mode 100644 index a8473362d..000000000 --- a/src/main/java/com/volmit/adapt/util/data/Writable.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.data; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public interface Writable { - void write(DataOutputStream o) throws IOException; - - void read(DataInputStream i) throws IOException; -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeContext.java b/src/main/java/com/volmit/adapt/util/decree/DecreeContext.java deleted file mode 100644 index bea9eaa0b..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeContext.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import art.arcane.chrono.ChronoLatch; -import com.volmit.adapt.util.VolmitSender; -import com.volmit.adapt.util.collection.KMap; - -public class DecreeContext { - private static final ChronoLatch cl = new ChronoLatch(60000); - private static final KMap context = new KMap<>(); - - public static VolmitSender get() { - return context.get(Thread.currentThread()); - } - - public static void touch(VolmitSender c) { - synchronized (context) { - context.put(Thread.currentThread(), c); - - if (cl.flip()) { - for (Thread i : context.k()) { - if (!i.isAlive()) { - context.remove(i); - } - } - } - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeContextHandler.java b/src/main/java/com/volmit/adapt/util/decree/DecreeContextHandler.java deleted file mode 100644 index 6d83bfa05..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeContextHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.VolmitSender; - -import java.util.HashMap; -import java.util.Map; - -public interface DecreeContextHandler { - Map, DecreeContextHandler> contextHandlers = buildContextHandlers(); - - static Map, DecreeContextHandler> buildContextHandlers() { - Map, DecreeContextHandler> contextHandlers = new HashMap<>(); - - try { - Adapt.initialize("com.volmit.adapt.util.decree.context").forEach((i) - -> contextHandlers.put(((DecreeContextHandler) i).getType(), (DecreeContextHandler) i)); - } catch (Throwable e) { - e.printStackTrace(); - } - - return contextHandlers; - } - - Class getType(); - - T handle(VolmitSender sender); -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeNode.java b/src/main/java/com/volmit/adapt/util/decree/DecreeNode.java deleted file mode 100644 index cb51755b3..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeNode.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.annotations.Decree; -import com.volmit.adapt.util.decree.annotations.Param; -import lombok.Data; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.List; - -@Data -public class DecreeNode { - private final Method method; - private final Object instance; - private final Decree decree; - - public DecreeNode(Object instance, Method method) { - this.instance = instance; - this.method = method; - this.decree = method.getDeclaredAnnotation(Decree.class); - if (decree == null) { - throw new RuntimeException("Cannot instantiate DecreeNode on method " + method.getName() + " in " + method.getDeclaringClass().getCanonicalName() + " not annotated by @Decree"); - } - } - - /** - * Get the parameters of this decree node - * - * @return The list of parameters if ALL are annotated by @{@link Param}, else null - */ - public KList getParameters() { - KList required = new KList<>(); - KList optional = new KList<>(); - - for (Parameter i : method.getParameters()) { - DecreeParameter p = new DecreeParameter(i); - if (p.isRequired()) { - required.add(p); - } else { - optional.add(p); - } - } - - required.addAll(optional); - - return required; - } - - public String getName() { - return decree.name().isEmpty() ? method.getName() : decree.name(); - } - - public DecreeOrigin getOrigin() { - return decree.origin(); - } - - public String getDescription() { - return decree.description().isEmpty() ? Decree.DEFAULT_DESCRIPTION : decree.description(); - } - - public KList getNames() { - KList d = new KList<>(); - d.add(getName()); - - for (String i : decree.aliases()) { - if (i.isEmpty()) { - continue; - } - - d.add(i); - } - - - d.removeDuplicates(); - return d; - } - - public boolean isSync() { - return decree.sync(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeOrigin.java b/src/main/java/com/volmit/adapt/util/decree/DecreeOrigin.java deleted file mode 100644 index e5143f0fd..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeOrigin.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import com.volmit.adapt.util.VolmitSender; - -public enum DecreeOrigin { - PLAYER, - CONSOLE, - /** - * Both the player and the console - */ - BOTH; - - /** - * Check if the origin is valid for a sender - * - * @param sender The sender to check - * @return True if valid for origin - */ - public boolean validFor(VolmitSender sender) { - if (sender.isPlayer()) { - return this.equals(PLAYER) || this.equals(BOTH); - } else { - return this.equals(CONSOLE) || this.equals(BOTH); - } - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeParameter.java b/src/main/java/com/volmit/adapt/util/decree/DecreeParameter.java deleted file mode 100644 index 80ddf7d53..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeParameter.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import com.volmit.adapt.util.cache.AtomicCache; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.annotations.Param; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import com.volmit.adapt.util.decree.specialhandlers.DummyHandler; -import lombok.Data; - -import java.lang.reflect.Parameter; - -@Data -public class DecreeParameter { - private final Parameter parameter; - private final Param param; - private transient final AtomicCache> handlerCache = new AtomicCache<>(); - - public DecreeParameter(Parameter parameter) { - this.parameter = parameter; - this.param = parameter.getDeclaredAnnotation(Param.class); - if (param == null) { - throw new RuntimeException("Cannot instantiate DecreeParameter on " + parameter.getName() + " in method " + parameter.getDeclaringExecutable().getName() + "(...) in class " + parameter.getDeclaringExecutable().getDeclaringClass().getCanonicalName() + " not annotated by @Param"); - } - } - - public DecreeParameterHandler getHandler() { - return handlerCache.aquire(() -> { - try { - if (param.customHandler().equals(DummyHandler.class)) { - return DecreeSystem.getHandler(getType()); - } - - return param.customHandler().getConstructor().newInstance(); - } catch (Throwable e) { - e.printStackTrace(); - } - - return null; - }); - } - - public Class getType() { - return parameter.getType(); - } - - public String getName() { - return param.name().isEmpty() ? parameter.getName() : param.name(); - } - - public String getDescription() { - return param.description().isEmpty() ? Param.DEFAULT_DESCRIPTION : param.description(); - } - - public boolean isRequired() { - return !hasDefault(); - } - - public KList getNames() { - KList d = new KList<>(); - - for (String i : param.aliases()) { - if (i.isEmpty()) { - continue; - } - - d.add(i); - } - - d.add(getName()); - d.removeDuplicates(); - - return d; - } - - public Object getDefaultValue() throws DecreeParsingException { - return param.defaultValue().trim().isEmpty() ? null : getHandler().parse(param.defaultValue().trim(), true); - } - - public boolean hasDefault() { - return !param.defaultValue().trim().isEmpty(); - } - - public String example() { - KList ff = getHandler().getPossibilities(); - ff = ff != null ? ff : new KList<>(); - KList f = ff.kConvert((i) -> getHandler().toStringForce(i)); - if (f.isEmpty()) { - f = new KList<>(); - f.add(getHandler().getRandomDefault()); - } - - return f.getRandom(); - } - - public boolean isContextual() { - return param.contextual(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/DecreeParameterHandler.java b/src/main/java/com/volmit/adapt/util/decree/DecreeParameterHandler.java deleted file mode 100644 index 2640c358a..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/DecreeParameterHandler.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree; - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public interface DecreeParameterHandler { - /** - * Should return the possible values for this type - * - * @return Possibilities for this type. - */ - KList getPossibilities(); - - default boolean isDummy() { - return false; - } - - /** - * Converting the type back to a string (inverse of the {@link #parse(String) parse} method) - * - * @param t The input of the designated type to convert to a String - * @return The resulting string - */ - String toString(T t); - - /** - * Forces conversion to the designated type before converting to a string using {@link #toString(T t)} - * - * @param t The object to convert to string (that should be of this type) - * @return The resulting string. - */ - default String toStringForce(Object t) { - return toString((T) t); - } - - /** - * Should parse a String into the designated type - * - * @param in The string to parse - * @return The value extracted from the string, of the designated type - * @throws DecreeParsingException Thrown when the parsing fails (ex: "oop" translated to an integer throws this) - */ - default T parse(String in) throws DecreeParsingException { - return parse(in, false); - } - - /** - * Should parse a String into the designated type. You can force it to not throw a whichexception - * - * @param in The string to parse - * @param force force an option instead of throwing decreewhich - * @return The value extracted from the string, of the designated type - * @throws DecreeParsingException Thrown when the parsing fails (ex: "oop" translated to an integer throws this) - */ - T parse(String in, boolean force) throws DecreeParsingException; - - /** - * Returns whether a certain type is supported by this handler
- * - * @param type The type to check - * @return True if supported, false if not - */ - boolean supports(Class type); - - /** - * The possible entries for the inputted string (support for autocomplete on partial entries) - * - * @param input The inputted string to check against - * @return A {@link List} of possibilities - */ - default KList getPossibilities(String input) { - if (input.trim().isEmpty()) { - KList f = getPossibilities(); - return f == null ? new KList<>() : f; - } - - input = input.trim(); - KList possible = getPossibilities(); - KList matches = new KList<>(); - - if (possible == null || possible.isEmpty()) { - return matches; - } - - if (input.isEmpty()) { - return getPossibilities(); - } - - KList converted = possible.kConvert(v -> toString(v).trim()); - - for (int i = 0; i < converted.size(); i++) { - String g = converted.get(i); - // if - // G == I or - // I in G or - // G in I - if (g.equalsIgnoreCase(input) || g.toLowerCase().contains(input.toLowerCase()) || input.toLowerCase().contains(g.toLowerCase())) { - matches.add(possible.get(i)); - } - } - - return matches; - } - - default String getRandomDefault() { - return "NOEXAMPLE"; - } - - default double getMultiplier(AtomicReference g) { - double multiplier = 1; - String in = g.get(); - boolean valid = true; - while (valid) { - boolean trim = false; - if (in.toLowerCase().endsWith("k")) { - multiplier *= 1000; - trim = true; - } else if (in.toLowerCase().endsWith("m")) { - multiplier *= 1000000; - trim = true; - } else if (in.toLowerCase().endsWith("h")) { - multiplier *= 100; - trim = true; - } else if (in.toLowerCase().endsWith("c")) { - multiplier *= 16; - trim = true; - } else if (in.toLowerCase().endsWith("r")) { - multiplier *= (16 * 32); - trim = true; - } else { - valid = false; - } - - if (trim) { - in = in.substring(0, in.length() - 1); - } - } - - g.set(in); - return multiplier; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/annotations/Decree.java b/src/main/java/com/volmit/adapt/util/decree/annotations/Decree.java deleted file mode 100644 index 932bf597b..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/annotations/Decree.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.annotations; - -import com.volmit.adapt.util.decree.DecreeOrigin; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -public @interface Decree { - - String DEFAULT_DESCRIPTION = "No Description Provided"; - - /** - * The name of this command, which is the Method's name by default - */ - String name() default ""; - - /** - * If the node's functions MUST be run in sync, set this to true.
- * Defaults to false - */ - boolean sync() default false; - - /** - * The description of this command.
- * Is {@link #DEFAULT_DESCRIPTION} by default - */ - String description() default DEFAULT_DESCRIPTION; - - /** - * The origin this command must come from.
- * Must be elements of the {@link DecreeOrigin} enum
- * By default, is {@link DecreeOrigin#BOTH}, meaning both console & player can send the command - */ - DecreeOrigin origin() default DecreeOrigin.BOTH; - - /** - * The aliases of this parameter (instead of just the {@link #name() name} (if specified) or Method Name (name of - * method))
- * Can be initialized as just a string (ex. "alias") or as an array (ex. {"alias1", "alias2"})
- * If someone uses /plugin foo and you specify alias="f" here, /plugin f will do the exact same. - */ - String[] aliases() default ""; -} diff --git a/src/main/java/com/volmit/adapt/util/decree/annotations/Param.java b/src/main/java/com/volmit/adapt/util/decree/annotations/Param.java deleted file mode 100644 index c3a5d6557..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/annotations/Param.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.annotations; - -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.specialhandlers.DummyHandler; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Param { - String DEFAULT_DESCRIPTION = "No Description Provided"; - - /** - * The main name of this command.
- * Required parameter.
- * This is what is used in game, alongside any (if specified) {@link #aliases() aliases} - */ - String name() default ""; - - /** - * The description of this parameter, used in help-popups in game.
- * The default value is {@link #DEFAULT_DESCRIPTION} - */ - String description() default DEFAULT_DESCRIPTION; - - /** - * The default value for this argument.
- * The entered string is parsed to the value similarly to how commandline-text would be.
- * Which indicates the variable MUST be defined by the person running the command.
- * If you define this, the variable automatically becomes non-required, but can still be set. - */ - String defaultValue() default ""; - - /** - * The aliases of this parameter (instead of just the {@link #name() name} (if specified) or Method Name (name of - * method))
- * Can be initialized as just a string (ex. "alias") or as an array (ex. {"alias1", "alias2"})
- * If someone uses /plugin foo bar=baz and you specify alias="b" here, /plugin foo b=baz will do the exact same. - */ - String[] aliases() default ""; - - /** - * Attempts to dynamically pull context from the player, default data or something else for supported types - */ - boolean contextual() default false; - - Class> customHandler() default DummyHandler.class; -} diff --git a/src/main/java/com/volmit/adapt/util/decree/context/WorldContextHandler.java b/src/main/java/com/volmit/adapt/util/decree/context/WorldContextHandler.java deleted file mode 100644 index 6826fa0e5..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/context/WorldContextHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.context; - -import com.volmit.adapt.util.VolmitSender; -import com.volmit.adapt.util.decree.DecreeContextHandler; -import org.bukkit.World; - -public class WorldContextHandler implements DecreeContextHandler { - public Class getType() { - return World.class; - } - - public World handle(VolmitSender sender) { - return sender.isPlayer() ? sender.player().getWorld() : null; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/exceptions/DecreeParsingException.java b/src/main/java/com/volmit/adapt/util/decree/exceptions/DecreeParsingException.java deleted file mode 100644 index 9ec0cde32..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/exceptions/DecreeParsingException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.exceptions; - -/** - * Thrown when a decree parameter is parsed, but parsing fails - */ -public class DecreeParsingException extends Exception { - public DecreeParsingException(String message) { - super(message); - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/BlockVectorHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/BlockVectorHandler.java deleted file mode 100644 index d1d573eb5..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/BlockVectorHandler.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.VolmitSender; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeContext; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.DecreeSystem; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import org.bukkit.FluidCollisionMode; -import org.bukkit.entity.Player; -import org.bukkit.util.BlockVector; - -public class BlockVectorHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - KList vx = new KList<>(); - VolmitSender s = DecreeContext.get(); - - if (s.isPlayer()) { - vx.add(s.player().getLocation().toVector().toBlockVector()); - } - - return vx; - } - - @Override - public String toString(BlockVector v) { - if (v.getY() == 0) { - return Form.f(v.getBlockX(), 2) + "," + Form.f(v.getBlockZ(), 2); - } - - return Form.f(v.getBlockX(), 2) + "," + Form.f(v.getBlockY(), 2) + "," + Form.f(v.getBlockZ(), 2); - } - - @Override - public BlockVector parse(String in, boolean force) throws DecreeParsingException { - try { - if (in.contains(",")) { - String[] comp = in.split("\\Q,\\E"); - - if (comp.length == 2) { - return new BlockVector(Integer.parseInt(comp[0].trim()), 0, Integer.parseInt(comp[1].trim())); - } else if (comp.length == 3) { - return new BlockVector(Integer.parseInt(comp[0].trim()), - Integer.parseInt(comp[1].trim()), - Integer.parseInt(comp[2].trim())); - } else { - throw new DecreeParsingException("Could not parse components for vector. You have " + comp.length + " components. Expected 2 or 3."); - } - } else if (in.equalsIgnoreCase("here") || in.equalsIgnoreCase("me") || in.equalsIgnoreCase("self")) { - if (!DecreeContext.get().isPlayer()) { - throw new DecreeParsingException("You cannot specify me,self,here as a console."); - } - - return DecreeContext.get().player().getLocation().toVector().toBlockVector(); - } else if (in.equalsIgnoreCase("look") || in.equalsIgnoreCase("cursor") || in.equalsIgnoreCase("crosshair")) { - if (!DecreeContext.get().isPlayer()) { - throw new DecreeParsingException("You cannot specify look,cursor,crosshair as a console."); - } - - return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); - } else if (in.trim().toLowerCase().startsWith("player:")) { - String v = in.trim().split("\\Q:\\E")[1]; - - - KList px = DecreeSystem.getHandler(Player.class).getPossibilities(v); - - if (px != null && px.isNotEmpty()) { - return ((Player) px.get(0)).getLocation().toVector().toBlockVector(); - } else if (px == null || px.isEmpty()) { - throw new DecreeParsingException("Cannot find player: " + v); - } - } - } catch (Throwable e) { - throw new DecreeParsingException("Unable to get Vector for \"" + in + "\" because of an uncaught exception: " + e); - } - - return null; - } - - @Override - public boolean supports(Class type) { - return type.equals(BlockVector.class); - } - - @Override - public String getRandomDefault() { - return M.r(0.5) ? "0,0" : "0,0,0"; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/BooleanHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/BooleanHandler.java deleted file mode 100644 index 4129c12aa..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/BooleanHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.M; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; - -public class BooleanHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public String toString(Boolean aByte) { - return aByte.toString(); - } - - @Override - public Boolean parse(String in, boolean force) throws DecreeParsingException { - try { - if (in.equals("null") || in.equals("other") || in.equals("flip")) { - return null; - } - return Boolean.parseBoolean(in); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse boolean \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Boolean.class) || type.equals(boolean.class); - } - - @Override - public String getRandomDefault() { - return M.r(0.5) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/ByteHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/ByteHandler.java deleted file mode 100644 index 65b2a2d82..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/ByteHandler.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; - -public class ByteHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public String toString(Byte aByte) { - return aByte.toString(); - } - - @Override - public Byte parse(String in, boolean force) throws DecreeParsingException { - try { - return Byte.parseByte(in); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse byte \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Byte.class) || type.equals(byte.class); - } - - @Override - public String getRandomDefault() { - return RNG.r.i(0, Byte.MAX_VALUE) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/DoubleHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/DoubleHandler.java deleted file mode 100644 index 0c1accc15..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/DoubleHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class DoubleHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public Double parse(String in, boolean force) throws DecreeParsingException { - try { - AtomicReference r = new AtomicReference<>(in); - double m = getMultiplier(r); - return Double.parseDouble(r.get()) * m; - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse double \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Double.class) || type.equals(double.class); - } - - @Override - public String toString(Double f) { - return f.toString(); - } - - @Override - public String getRandomDefault() { - return Form.f(RNG.r.d(0, 99.99), 1) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/FloatHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/FloatHandler.java deleted file mode 100644 index 2b215fcfe..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/FloatHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class FloatHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public Float parse(String in, boolean force) throws DecreeParsingException { - try { - AtomicReference r = new AtomicReference<>(in); - double m = getMultiplier(r); - return (float) (Float.parseFloat(r.get()) * m); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse float \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Float.class) || type.equals(float.class); - } - - @Override - public String toString(Float f) { - return f.toString(); - } - - @Override - public String getRandomDefault() { - return Form.f(RNG.r.d(0, 99.99), 1) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/IntegerHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/IntegerHandler.java deleted file mode 100644 index f051e5d48..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/IntegerHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class IntegerHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public Integer parse(String in, boolean force) throws DecreeParsingException { - try { - AtomicReference r = new AtomicReference<>(in); - double m = getMultiplier(r); - return (int) (Integer.valueOf(r.get()).doubleValue() * m); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse integer \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Integer.class) || type.equals(int.class); - } - - @Override - public String toString(Integer f) { - return f.toString(); - } - - @Override - public String getRandomDefault() { - return RNG.r.i(0, 99) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/LongHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/LongHandler.java deleted file mode 100644 index 5bb163290..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/LongHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class LongHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public Long parse(String in, boolean force) throws DecreeParsingException { - try { - AtomicReference r = new AtomicReference<>(in); - double m = getMultiplier(r); - if (m == 1) - return Long.parseLong(r.get()); - else - return (long) (Long.valueOf(r.get()).doubleValue() * m); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse long \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Long.class) || type.equals(long.class); - } - - @Override - public String toString(Long f) { - return f.toString(); - } - - @Override - public String getRandomDefault() { - return RNG.r.i(0, 99) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/OptionalWorldHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/OptionalWorldHandler.java deleted file mode 100644 index c993fab35..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/OptionalWorldHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import org.bukkit.Bukkit; -import org.bukkit.World; - -import java.util.ArrayList; -import java.util.List; - -public class OptionalWorldHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - KList options = new KList<>(); - options.add("ALL"); - for (World world : Bukkit.getWorlds()) { - if (!world.getName().toLowerCase().startsWith("adapt/")) { - options.add(world.getName()); - } - } - return options; - } - - @Override - public String toString(String world) { - return world; - } - - @Override - public String parse(String in, boolean force) throws DecreeParsingException { - return in; - } - - @Override - public boolean supports(Class type) { - return false; - } - - @Override - public String getRandomDefault() { - return "ALL"; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/PlayerHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/PlayerHandler.java deleted file mode 100644 index 42d448afa..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/PlayerHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.Adapt; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class PlayerHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { - return new KList<>(Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()); - } - return new KList<>(new ArrayList<>(Bukkit.getOnlinePlayers())); - } - - @Override - public String toString(Player player) { - return player.getName(); - } - - @Override - public Player parse(String in, boolean force) throws DecreeParsingException { - List options = getPossibilities(in); - - if (options.isEmpty()) { - throw new DecreeParsingException("Unable to find Player \"" + in + "\""); - } - try { - return options.stream().filter((i) -> toString(i).equalsIgnoreCase(in)).collect(Collectors.toList()).get(0); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to filter which Biome \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Player.class); - } - - @Override - public String getRandomDefault() { - return "playername"; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/ShortHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/ShortHandler.java deleted file mode 100644 index f296a7ccf..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/ShortHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.RNG; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -public class ShortHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public Short parse(String in, boolean force) throws DecreeParsingException { - try { - AtomicReference r = new AtomicReference<>(in); - double m = getMultiplier(r); - return (short) (Short.valueOf(r.get()).doubleValue() * m); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to parse short \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(Short.class) || type.equals(short.class); - } - - @Override - public String toString(Short f) { - return f.toString(); - } - - @Override - public String getRandomDefault() { - return RNG.r.i(0, 99) + ""; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/StringHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/StringHandler.java deleted file mode 100644 index ba34b2c7b..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/StringHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; - -/** - * Abstraction can sometimes breed stupidity - */ -public class StringHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - return null; - } - - @Override - public String toString(String s) { - return s; - } - - @Override - public String parse(String in, boolean force) throws DecreeParsingException { - return in; - } - - @Override - public boolean supports(Class type) { - return type.equals(String.class); - } - - @Override - public String getRandomDefault() { - return new KList().qadd("text").qadd("string") - .qadd("blah").qadd("derp").qadd("yolo").getRandom(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/VectorHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/VectorHandler.java deleted file mode 100644 index 3f7f59096..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/VectorHandler.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.Form; -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeContext; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.DecreeSystem; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import org.bukkit.FluidCollisionMode; -import org.bukkit.entity.Player; -import org.bukkit.util.BlockVector; -import org.bukkit.util.Vector; - -public class VectorHandler implements DecreeParameterHandler { - - private static final KList randoms = new KList<>( - "here", - "0,0,0", - "0,0", - "look", - "player:" - ); - - @Override - public KList getPossibilities() { - return null; - } - - @Override - public String toString(Vector v) { - if (v.getY() == 0) { - return Form.f(v.getX(), 2) + "," + Form.f(v.getZ(), 2); - } - - return Form.f(v.getX(), 2) + "," + Form.f(v.getY(), 2) + "," + Form.f(v.getZ(), 2); - } - - @Override - public Vector parse(String in, boolean force) throws DecreeParsingException { - try { - if (in.contains(",")) { - String[] comp = in.split("\\Q,\\E"); - - if (comp.length == 2) { - return new BlockVector(Double.parseDouble(comp[0].trim()), 0, Double.parseDouble(comp[1].trim())); - } else if (comp.length == 3) { - return new BlockVector(Double.parseDouble(comp[0].trim()), - Double.parseDouble(comp[1].trim()), - Double.parseDouble(comp[2].trim())); - } else { - throw new DecreeParsingException("Could not parse components for vector. You have " + comp.length + " components. Expected 2 or 3."); - } - } else if (in.equalsIgnoreCase("here") || in.equalsIgnoreCase("me") || in.equalsIgnoreCase("self")) { - if (!DecreeContext.get().isPlayer()) { - throw new DecreeParsingException("You cannot specify me,self,here as a console."); - } - - return DecreeContext.get().player().getLocation().toVector(); - } else if (in.equalsIgnoreCase("look") || in.equalsIgnoreCase("cursor") || in.equalsIgnoreCase("crosshair")) { - if (!DecreeContext.get().isPlayer()) { - throw new DecreeParsingException("You cannot specify look,cursor,crosshair as a console."); - } - - return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); - } else if (in.trim().toLowerCase().startsWith("player:")) { - String v = in.trim().split("\\Q:\\E")[1]; - - - KList px = DecreeSystem.getHandler(Player.class).getPossibilities(v); - - if (px != null && px.isNotEmpty()) { - return ((Player) px.get(0)).getLocation().toVector(); - } else if (px == null || px.isEmpty()) { - throw new DecreeParsingException("Cannot find player: " + v); - } - } - } catch (Throwable e) { - throw new DecreeParsingException("Unable to get Vector for \"" + in + "\" because of an uncaught exception: " + e); - } - - return null; - } - - @Override - public boolean supports(Class type) { - return type.equals(Vector.class); - } - - @Override - public String getRandomDefault() { - return randoms.getRandom(); - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/handlers/WorldHandler.java b/src/main/java/com/volmit/adapt/util/decree/handlers/WorldHandler.java deleted file mode 100644 index c9477f121..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/handlers/WorldHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.decree.handlers; - -import com.volmit.adapt.util.collection.KList; -import com.volmit.adapt.util.decree.DecreeParameterHandler; -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import org.bukkit.Bukkit; -import org.bukkit.World; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class WorldHandler implements DecreeParameterHandler { - @Override - public KList getPossibilities() { - KList options = new KList<>(); - for (World world : Bukkit.getWorlds()) { - if (!world.getName().toLowerCase().startsWith("adapt/")) { - options.add(world); - } - } - return options; - } - - @Override - public String toString(World world) { - return world.getName(); - } - - @Override - public World parse(String in, boolean force) throws DecreeParsingException { - List options = getPossibilities(in); - - if (options.isEmpty()) { - throw new DecreeParsingException("Unable to find World \"" + in + "\""); - } - try { - return options.stream().filter((i) -> toString(i).equalsIgnoreCase(in)).collect(Collectors.toList()).get(0); - } catch (Throwable e) { - throw new DecreeParsingException("Unable to filter which Biome \"" + in + "\""); - } - } - - @Override - public boolean supports(Class type) { - return type.equals(World.class); - } - - @Override - public String getRandomDefault() { - return "world"; - } -} diff --git a/src/main/java/com/volmit/adapt/util/decree/specialhandlers/NullablePlayerHandler.java b/src/main/java/com/volmit/adapt/util/decree/specialhandlers/NullablePlayerHandler.java deleted file mode 100644 index 76b5a56e2..000000000 --- a/src/main/java/com/volmit/adapt/util/decree/specialhandlers/NullablePlayerHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.volmit.adapt.util.decree.specialhandlers; - -import com.volmit.adapt.util.decree.exceptions.DecreeParsingException; -import com.volmit.adapt.util.decree.handlers.PlayerHandler; -import org.bukkit.entity.Player; - -public class NullablePlayerHandler extends PlayerHandler { - - @Override - public Player parse(String in, boolean force) throws DecreeParsingException { - return getPossibilities(in).stream().filter((i) -> toString(i).equalsIgnoreCase(in)).findFirst().orElse(null); - } -} \ No newline at end of file diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer2.java b/src/main/java/com/volmit/adapt/util/function/Consumer2.java deleted file mode 100644 index 347120785..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer2.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@SuppressWarnings({"hiding", "RedundantSuppression"}) -@FunctionalInterface -public interface Consumer2 { - void accept(A a, B b); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer2IO.java b/src/main/java/com/volmit/adapt/util/function/Consumer2IO.java deleted file mode 100644 index 3f572b7f9..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer2IO.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -import java.io.IOException; - -@SuppressWarnings({"hiding", "RedundantSuppression"}) -@FunctionalInterface -public interface Consumer2IO { - void accept(A a, B b) throws IOException; -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer3.java b/src/main/java/com/volmit/adapt/util/function/Consumer3.java deleted file mode 100644 index ca3e69abe..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer3.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@SuppressWarnings("ALL") -@FunctionalInterface -public interface Consumer3 { - void accept(A a, B b, C c); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer4.java b/src/main/java/com/volmit/adapt/util/function/Consumer4.java deleted file mode 100644 index 1c5677cb3..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer4.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Consumer4 { - void accept(A a, B b, C c, D d); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer4IO.java b/src/main/java/com/volmit/adapt/util/function/Consumer4IO.java deleted file mode 100644 index eb861681e..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer4IO.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -import java.io.IOException; - -@FunctionalInterface -public interface Consumer4IO { - void accept(A a, B b, C c, D d) throws IOException; -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer5.java b/src/main/java/com/volmit/adapt/util/function/Consumer5.java deleted file mode 100644 index 485d32582..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer5.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Consumer5 { - void accept(A a, B b, C c, D d, E e); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer6.java b/src/main/java/com/volmit/adapt/util/function/Consumer6.java deleted file mode 100644 index 1fc570c1c..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer6.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Consumer6 { - void accept(A a, B b, C c, D d, E e, F f); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Consumer8.java b/src/main/java/com/volmit/adapt/util/function/Consumer8.java deleted file mode 100644 index 3f31cee03..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Consumer8.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Consumer8 { - void accept(A a, B b, C c, D d, E e, F f, G g, H h); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Function2.java b/src/main/java/com/volmit/adapt/util/function/Function2.java deleted file mode 100644 index d7f4cfa83..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Function2.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Function2 { - R apply(A a, B b); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Function3.java b/src/main/java/com/volmit/adapt/util/function/Function3.java deleted file mode 100644 index ca2aa0bc4..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Function3.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Function3 { - R apply(A a, B b, C c); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Function4.java b/src/main/java/com/volmit/adapt/util/function/Function4.java deleted file mode 100644 index 5786e56e2..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Function4.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface Function4 { - R apply(A a, B b, C c, D d); -} diff --git a/src/main/java/com/volmit/adapt/util/function/NastyFunction.java b/src/main/java/com/volmit/adapt/util/function/NastyFunction.java deleted file mode 100644 index 94d0e864d..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NastyFunction.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface NastyFunction { - R run(T t); -} diff --git a/src/main/java/com/volmit/adapt/util/function/NastyFuture.java b/src/main/java/com/volmit/adapt/util/function/NastyFuture.java deleted file mode 100644 index d16030386..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NastyFuture.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface NastyFuture { - R run(); -} diff --git a/src/main/java/com/volmit/adapt/util/function/NastyRunnable.java b/src/main/java/com/volmit/adapt/util/function/NastyRunnable.java deleted file mode 100644 index 17753fc17..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NastyRunnable.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface NastyRunnable { - void run() throws Throwable; -} diff --git a/src/main/java/com/volmit/adapt/util/function/NastySupplier.java b/src/main/java/com/volmit/adapt/util/function/NastySupplier.java deleted file mode 100644 index 02180b037..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NastySupplier.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface NastySupplier { - T get() throws Throwable; -} diff --git a/src/main/java/com/volmit/adapt/util/function/NoiseInjector.java b/src/main/java/com/volmit/adapt/util/function/NoiseInjector.java deleted file mode 100644 index de4e15894..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NoiseInjector.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface NoiseInjector { - double[] combine(double src, double value); -} diff --git a/src/main/java/com/volmit/adapt/util/function/NoiseProvider.java b/src/main/java/com/volmit/adapt/util/function/NoiseProvider.java deleted file mode 100644 index bed4bbc89..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NoiseProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface NoiseProvider { - double noise(double x, double z); -} diff --git a/src/main/java/com/volmit/adapt/util/function/NoiseProvider3.java b/src/main/java/com/volmit/adapt/util/function/NoiseProvider3.java deleted file mode 100644 index f3d561cd9..000000000 --- a/src/main/java/com/volmit/adapt/util/function/NoiseProvider3.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -@FunctionalInterface -public interface NoiseProvider3 { - double noise(double x, double y, double z); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Supplier2.java b/src/main/java/com/volmit/adapt/util/function/Supplier2.java deleted file mode 100644 index d0ebe27d8..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Supplier2.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface Supplier2 { - void get(T t, TT tt); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Supplier3.java b/src/main/java/com/volmit/adapt/util/function/Supplier3.java deleted file mode 100644 index d69c85ad8..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Supplier3.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface Supplier3 { - void get(T t, TT tt, TTT ttt); -} diff --git a/src/main/java/com/volmit/adapt/util/function/Supplier3R.java b/src/main/java/com/volmit/adapt/util/function/Supplier3R.java deleted file mode 100644 index f05df6dce..000000000 --- a/src/main/java/com/volmit/adapt/util/function/Supplier3R.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 com.volmit.adapt.util.function; - -public interface Supplier3R { - TTTT get(T t, TT tt, TTT ttt); -} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0a8195790..c3a0f114f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ${name} -main: com.volmit.adapt.Adapt +main: art.arcane.adapt.Adapt version: ${version} authors: - NextdoorPsycho diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 68b80524f..4cbb13e64 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -25,7 +25,7 @@ val generateTemplates = tasks.register("generateTemplates") { from(templateSource) into(templateDest) - rename { "com/volmit/adapt/$it" } + rename { "art/arcane/adapt/$it" } expand(inputs.properties) } diff --git a/velocity/src/main/java/com/volmit/adapt/AdaptVelocity.java b/velocity/src/main/java/art/arcane/adapt/AdaptVelocity.java similarity index 98% rename from velocity/src/main/java/com/volmit/adapt/AdaptVelocity.java rename to velocity/src/main/java/art/arcane/adapt/AdaptVelocity.java index cb93e1c9e..67071b6a5 100644 --- a/velocity/src/main/java/com/volmit/adapt/AdaptVelocity.java +++ b/velocity/src/main/java/art/arcane/adapt/AdaptVelocity.java @@ -1,4 +1,4 @@ -package com.volmit.adapt; +package art.arcane.adapt; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -11,7 +11,7 @@ import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; -import com.volmit.adapt.util.redis.VelocityConfig; +import art.arcane.adapt.util.project.redis.VelocityConfig; import com.moandjiezana.toml.Toml; import io.github.slimjar.app.builder.VelocityApplicationBuilder; import org.slf4j.Logger; diff --git a/velocity/src/main/java/com/volmit/adapt/RedisHandler.java b/velocity/src/main/java/art/arcane/adapt/RedisHandler.java similarity index 89% rename from velocity/src/main/java/com/volmit/adapt/RedisHandler.java rename to velocity/src/main/java/art/arcane/adapt/RedisHandler.java index 1338390d7..b5a7682b1 100644 --- a/velocity/src/main/java/com/volmit/adapt/RedisHandler.java +++ b/velocity/src/main/java/art/arcane/adapt/RedisHandler.java @@ -1,10 +1,10 @@ -package com.volmit.adapt; +package art.arcane.adapt; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.player.ServerPreConnectEvent; -import com.volmit.adapt.util.redis.codec.Codec; -import com.volmit.adapt.util.redis.codec.DataRequest; -import com.volmit.adapt.util.redis.codec.Message; +import art.arcane.adapt.util.project.redis.codec.Codec; +import art.arcane.adapt.util.project.redis.codec.DataRequest; +import art.arcane.adapt.util.project.redis.codec.Message; import io.lettuce.core.RedisClient; import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; import lombok.extern.java.Log; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/RedisConfig.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/RedisConfig.java similarity index 95% rename from velocity/src/main/java/com/volmit/adapt/util/redis/RedisConfig.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/RedisConfig.java index ff47e4cc3..d3727fc95 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/RedisConfig.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/RedisConfig.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis; +package art.arcane.adapt.util.project.redis; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/VelocityConfig.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/VelocityConfig.java similarity index 80% rename from velocity/src/main/java/com/volmit/adapt/util/redis/VelocityConfig.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/VelocityConfig.java index 1679e6851..5598b78c7 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/VelocityConfig.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/VelocityConfig.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis; +package art.arcane.adapt.util.project.redis; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/ByteBufferInputStream.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/ByteBufferInputStream.java similarity index 95% rename from velocity/src/main/java/com/volmit/adapt/util/redis/codec/ByteBufferInputStream.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/ByteBufferInputStream.java index bfecdb135..87a4c8c57 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/ByteBufferInputStream.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/ByteBufferInputStream.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis.codec; +package art.arcane.adapt.util.project.redis.codec; import lombok.AllArgsConstructor; import lombok.NonNull; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/Codec.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Codec.java similarity index 97% rename from velocity/src/main/java/com/volmit/adapt/util/redis/codec/Codec.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Codec.java index bb6873f13..d45f77e60 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/Codec.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Codec.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis.codec; +package art.arcane.adapt.util.project.redis.codec; import com.google.common.io.ByteStreams; import io.lettuce.core.codec.RedisCodec; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataMessage.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataMessage.java similarity index 93% rename from velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataMessage.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataMessage.java index c588473c6..779b94d47 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataMessage.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataMessage.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis.codec; +package art.arcane.adapt.util.project.redis.codec; import lombok.NonNull; import org.jetbrains.annotations.NotNull; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataRequest.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataRequest.java similarity index 92% rename from velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataRequest.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataRequest.java index f8baca76c..6602da364 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/DataRequest.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/DataRequest.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis.codec; +package art.arcane.adapt.util.project.redis.codec; import lombok.NonNull; import org.jetbrains.annotations.NotNull; diff --git a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/Message.java b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Message.java similarity index 89% rename from velocity/src/main/java/com/volmit/adapt/util/redis/codec/Message.java rename to velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Message.java index 6ba1a9e2d..48cb66dc2 100644 --- a/velocity/src/main/java/com/volmit/adapt/util/redis/codec/Message.java +++ b/velocity/src/main/java/art/arcane/adapt/util/project/redis/codec/Message.java @@ -1,4 +1,4 @@ -package com.volmit.adapt.util.redis.codec; +package art.arcane.adapt.util.project.redis.codec; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/velocity/src/main/templates/BuildConstants.java b/velocity/src/main/templates/BuildConstants.java index cbed8b0fd..fb42b88e3 100644 --- a/velocity/src/main/templates/BuildConstants.java +++ b/velocity/src/main/templates/BuildConstants.java @@ -1,4 +1,4 @@ -package com.volmit.adapt; +package art.arcane.adapt; // The constants are replaced before compilation public class BuildConstants { From a5746bb2db78eba252d685c8d1c1532e90de3c4e Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 14 Feb 2026 04:52:14 -0500 Subject: [PATCH 02/25] Fix invis items on uni --- build.gradle.kts | 2 +- .../arcane/adapt/api/world/AdaptPlayer.java | 49 +++++++++++++++++-- .../arcane/adapt/api/world/AdaptServer.java | 34 ++++++++++++- .../arcane/adapt/api/world/PlayerData.java | 7 +-- .../stealth/StealthShadowDecoy.java | 3 ++ 5 files changed, 82 insertions(+), 13 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 93b1b58a5..474602d4d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -201,7 +201,7 @@ slimJar { )) relocate("manifold", "$lib.manifold") - relocate("art.arcane", "$lib.arcane") + relocate("art.arcane.volmlib", "$lib.arcane.volmlib") relocate("Fukkit.extensions", "$lib.extensions") relocate("Amulet.extensions", "$lib.extensions") relocate("com.fren_gor.ultimateAdvancementAPI", "$lib.advancements") diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index 41004f7ff..c5638dc98 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -37,7 +37,9 @@ import org.bukkit.util.Vector; import java.io.File; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import art.arcane.adapt.util.common.format.C; @@ -48,6 +50,8 @@ @EqualsAndHashCode(callSuper = false) @Data public class AdaptPlayer extends TickedObject { + private static final Set LOAD_FAILURE_GUARD = ConcurrentHashMap.newKeySet(); + private final Player player; private final PlayerData data; private ChronoLatch savelatch; @@ -136,6 +140,10 @@ public PlayerSkillLine getSkillLine(String l) { } private void save() { + save(false); + } + + private void save(boolean synchronous) { UUID uuid = player.getUniqueId(); File playerDataFile = getPlayerDataFile(uuid); @@ -144,7 +152,26 @@ private void save() { return; } + if (LOAD_FAILURE_GUARD.contains(uuid)) { + Adapt.warn("Skipping save for " + uuid + " because player data failed to load earlier. Existing file is preserved."); + return; + } + String json = this.data.toJson(AdaptConfig.get().isUseSql()); + if (synchronous) { + if (AdaptConfig.get().isUseSql()) { + if (Adapt.instance.getRedisSync() != null) { + Adapt.instance.getRedisSync().publish(uuid, json); + } + if (Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().updateData(uuid, json); + } + } else { + J.attempt(() -> IO.writeAll(playerDataFile, json)); + } + return; + } + PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); if (queue != null) { queue.queueSave(uuid, json, playerDataFile); @@ -166,7 +193,7 @@ private void save() { @Override public void unregister() { super.unregister(); - save(); + save(true); } public void delete(UUID uuid) { @@ -199,6 +226,7 @@ public static PlayerData loadPlayerData(UUID uuid) { var opt = Adapt.instance.getRedisSync().cachedData(uuid); if (opt.isPresent()) { Adapt.verbose("Using cached data for player: " + uuid); + LOAD_FAILURE_GUARD.remove(uuid); return opt.get(); } } @@ -206,7 +234,14 @@ public static PlayerData loadPlayerData(UUID uuid) { if (Adapt.instance.getSqlManager() != null) { String sqlData = Adapt.instance.getSqlManager().fetchData(uuid); if (sqlData != null) { - return PlayerData.fromJson(sqlData); + try { + PlayerData parsed = PlayerData.fromJson(sqlData); + LOAD_FAILURE_GUARD.remove(uuid); + return parsed; + } catch (Throwable e) { + LOAD_FAILURE_GUARD.add(uuid); + Adapt.warn("Failed to parse SQL player data for " + uuid + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); + } } upload = true; } @@ -224,12 +259,16 @@ public static PlayerData loadPlayerData(UUID uuid) { Adapt.instance.getSqlManager().updateData(uuid, text); } } - return PlayerData.fromJson(text); - } catch (Throwable ignored) { - Adapt.verbose("Failed to load player data for " + uuid); + PlayerData parsed = PlayerData.fromJson(text); + LOAD_FAILURE_GUARD.remove(uuid); + return parsed; + } catch (Throwable e) { + LOAD_FAILURE_GUARD.add(uuid); + Adapt.warn("Failed to load player data for " + uuid + " from " + f.getAbsolutePath() + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); } } + LOAD_FAILURE_GUARD.remove(uuid); return new PlayerData(); } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java index 09f925d14..8a649fd9b 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java @@ -148,6 +148,17 @@ public void takeSpatial(AdaptPlayer p) { } public void join(Player p) { + AdaptPlayer existing = players.get(p.getUniqueId()); + if (existing != null) { + if (existing.getPlayer() == p) { + existing.loggedIn(); + refreshOnlinePlayerSnapshots(); + return; + } + + players.remove(p.getUniqueId()); + } + PlayerData prefetched = takePrefetchedData(p.getUniqueId()); AdaptPlayer a = new AdaptPlayer(p, prefetched); players.put(p.getUniqueId(), a); @@ -159,7 +170,8 @@ public void quit(UUID p) { AdaptPlayer a = players.get(p); if (a == null) return; a.unregister(); - players.remove(p); + // Keep the entry briefly after quit so late quit listeners/tasks do not + // re-create a new AdaptPlayer for an offline player. prefetchedPlayerData.invalidate(p); refreshOnlinePlayerSnapshots(); } @@ -273,7 +285,25 @@ public void onTick() { try { int sizeBefore = players.size(); - players.values().removeIf(AdaptPlayer::shouldUnload); + Iterator> iterator = players.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + AdaptPlayer player = entry.getValue(); + if (player == null) { + iterator.remove(); + prefetchedPlayerData.invalidate(entry.getKey()); + continue; + } + + if (!player.shouldUnload()) { + continue; + } + + player.unregister(); + iterator.remove(); + prefetchedPlayerData.invalidate(entry.getKey()); + } + if (players.size() != sizeBefore) { refreshOnlinePlayerSnapshots(); } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java index 568d0ca5e..8365aefe9 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -113,11 +113,8 @@ public void update(AdaptPlayer p) { String lineId = entry.getKey(); Skill loadedSkill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(lineId); if (loadedSkill == null) { - if (Adapt.instance.getAdaptServer().getSkillRegistry().isKnownSkill(lineId)) { - continue; - } - skillLines.remove(lineId, entry.getValue()); - Adapt.warn("Removed unknown skill line '" + lineId + "' from " + p.getPlayer().getName()); + // Never prune unknown lines automatically; missing skills can be transient + // during startup/reload or due temporary config disables. continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index 8b645192c..d771b4dd7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -1148,6 +1148,9 @@ public void sendOwnerEquipment(Player owner, boolean hide) { } for (Player viewer : new ArrayList<>(owner.getWorld().getPlayers())) { + if (viewer.getUniqueId().equals(owner.getUniqueId())) { + continue; + } sendPacket(viewer, packet); } } catch (Throwable ignored) { From 5bdb60582ff1d6b56d654c793f0f81c5e8a12f22 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 14 Feb 2026 05:44:19 -0500 Subject: [PATCH 03/25] shakey shakey --- .../art/arcane/adapt/api/tick/Ticker.java | 17 ++ .../service/AdaptIntegrationService.java | 197 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java index ee508d098..3f7c368b8 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -86,6 +86,23 @@ public long getMetricsWindowMs() { return Math.max(0, System.currentTimeMillis() - windowStartMs.get()); } + public double getWindowLoadPercent() { + long windowMs = getMetricsWindowMs(); + if (windowMs <= 0L) { + return 0D; + } + + double totalMs = metrics.values().stream() + .mapToDouble(metric -> metric.totalNanos.get() / 1_000_000D) + .sum(); + double percent = (totalMs / (double) windowMs) * 100D; + if (!Double.isFinite(percent)) { + return 0D; + } + + return Math.max(0D, percent); + } + public List topMetrics(int limit) { int safeLimit = Math.max(1, limit); return metrics.entrySet().stream() diff --git a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java new file mode 100644 index 000000000..57048ed9e --- /dev/null +++ b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java @@ -0,0 +1,197 @@ +package art.arcane.adapt.service; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.volmlib.integration.IntegrationHandshakeRequest; +import art.arcane.volmlib.integration.IntegrationHandshakeResponse; +import art.arcane.volmlib.integration.IntegrationHeartbeat; +import art.arcane.volmlib.integration.IntegrationMetricDescriptor; +import art.arcane.volmlib.integration.IntegrationMetricSample; +import art.arcane.volmlib.integration.IntegrationMetricSchema; +import art.arcane.volmlib.integration.IntegrationProtocolNegotiator; +import art.arcane.volmlib.integration.IntegrationProtocolVersion; +import art.arcane.volmlib.integration.IntegrationServiceContract; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.plugin.ServicePriority; + +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +public class AdaptIntegrationService implements AdaptService, IntegrationServiceContract { + private static final long ABILITY_WINDOW_MS = 60_000L; + private static final Set SUPPORTED_PROTOCOLS = Set.of( + new IntegrationProtocolVersion(1, 0), + new IntegrationProtocolVersion(1, 1) + ); + private static final Set CAPABILITIES = Set.of( + "handshake", + "heartbeat", + "metrics", + "adapt-runtime-metrics" + ); + + private final ArrayDeque abilityOps = new ArrayDeque<>(); + private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1); + + @Override + public void onEnable() { + Bukkit.getServicesManager().register(IntegrationServiceContract.class, this, Adapt.instance, ServicePriority.Normal); + Adapt.verbose("Integration provider registered for Adapt"); + } + + @Override + public void onDisable() { + Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this); + synchronized (abilityOps) { + abilityOps.clear(); + } + } + + @EventHandler + public void onAdaptationUse(AdaptAdaptationUseEvent event) { + long now = System.currentTimeMillis(); + synchronized (abilityOps) { + abilityOps.addLast(now); + trimAbilityOps(now); + } + } + + @Override + public String pluginId() { + return "adapt"; + } + + @Override + public String pluginVersion() { + return Adapt.instance.getDescription().getVersion(); + } + + @Override + public Set supportedProtocols() { + return SUPPORTED_PROTOCOLS; + } + + @Override + public Set capabilities() { + return CAPABILITIES; + } + + @Override + public Set metricDescriptors() { + return IntegrationMetricSchema.descriptors().stream() + .filter(descriptor -> descriptor.key().startsWith("adapt.")) + .collect(java.util.stream.Collectors.toSet()); + } + + @Override + public IntegrationHandshakeResponse handshake(IntegrationHandshakeRequest request) { + long now = System.currentTimeMillis(); + if (request == null) { + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + false, + null, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "missing request", + now + ); + } + + Optional negotiated = IntegrationProtocolNegotiator.negotiate( + SUPPORTED_PROTOCOLS, + request.supportedProtocols() + ); + if (negotiated.isEmpty()) { + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + false, + null, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "no-common-protocol", + now + ); + } + + negotiatedProtocol = negotiated.get(); + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + true, + negotiatedProtocol, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "ok", + now + ); + } + + @Override + public IntegrationHeartbeat heartbeat() { + long now = System.currentTimeMillis(); + return new IntegrationHeartbeat(negotiatedProtocol, true, now, "ok"); + } + + @Override + public Map sampleMetrics(Set metricKeys) { + Set requested = metricKeys == null || metricKeys.isEmpty() + ? IntegrationMetricSchema.adaptKeys() + : metricKeys; + long now = System.currentTimeMillis(); + Map out = new HashMap<>(); + + for (String key : requested) { + switch (key) { + case IntegrationMetricSchema.ADAPT_SESSION_LOAD -> out.put(key, sampleSessionLoad(now)); + case IntegrationMetricSchema.ADAPT_ABILITY_OPS -> out.put(key, sampleAbilityOps(now)); + case IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY -> out.put(key, IntegrationMetricSample.unavailable( + IntegrationMetricSchema.descriptor(key), + "world-policy-latency-not-instrumented", + now + )); + default -> out.put(key, IntegrationMetricSample.unavailable( + IntegrationMetricSchema.descriptor(key), + "unsupported-key", + now + )); + } + } + + return out; + } + + private IntegrationMetricSample sampleSessionLoad(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_SESSION_LOAD); + if (Adapt.instance.getTicker() == null) { + return IntegrationMetricSample.unavailable(descriptor, "ticker-not-ready", now); + } + + double load = Adapt.instance.getTicker().getWindowLoadPercent(); + return IntegrationMetricSample.available(descriptor, load, now); + } + + private IntegrationMetricSample sampleAbilityOps(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_OPS); + long count; + synchronized (abilityOps) { + trimAbilityOps(now); + count = abilityOps.size(); + } + + return IntegrationMetricSample.available(descriptor, count, now); + } + + private void trimAbilityOps(long now) { + while (!abilityOps.isEmpty() && (now - abilityOps.peekFirst()) > ABILITY_WINDOW_MS) { + abilityOps.removeFirst(); + } + } +} From c7d854d0ee4018d5cfc884932135fb37d4e658c4 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 14 Feb 2026 16:54:00 -0500 Subject: [PATCH 04/25] more stats --- .../adapt/api/adaptation/Adaptation.java | 25 +++++-- .../WorldPolicyLatencyTelemetry.java | 68 +++++++++++++++++++ .../service/AdaptIntegrationService.java | 63 ++++++++++++----- 3 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 142439aa0..b50ca2517 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -26,6 +26,7 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.protection.Protector; +import art.arcane.adapt.api.protection.WorldPolicyLatencyTelemetry; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.Ticked; @@ -45,6 +46,7 @@ import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -359,31 +361,40 @@ default Set getProtectors() { } default boolean canBlockBreak(Player player, Location blockLocation) { - return getProtectors().stream().allMatch(protector -> protector.canBlockBreak(player, blockLocation, this)); + return evaluateWorldPolicy(protector -> protector.canBlockBreak(player, blockLocation, this)); } default boolean canBlockPlace(Player player, Location blockLocation) { - return getProtectors().stream().allMatch(protector -> protector.canBlockPlace(player, blockLocation, this)); + return evaluateWorldPolicy(protector -> protector.canBlockPlace(player, blockLocation, this)); } default boolean canPVP(Player player, Location victimLocation) { - return getProtectors().stream().allMatch(protector -> protector.canPVP(player, victimLocation, this)); + return evaluateWorldPolicy(protector -> protector.canPVP(player, victimLocation, this)); } default boolean canPVE(Player player, Location victimLocation) { - return getProtectors().stream().allMatch(protector -> protector.canPVE(player, victimLocation, this)); + return evaluateWorldPolicy(protector -> protector.canPVE(player, victimLocation, this)); } default boolean canInteract(Player player, Location targetLocation) { - return getProtectors().stream().allMatch(protector -> protector.canInteract(player, targetLocation, this)); + return evaluateWorldPolicy(protector -> protector.canInteract(player, targetLocation, this)); } default boolean canAccessChest(Player player, Location chestLocation) { - return getProtectors().stream().allMatch(protector -> protector.canAccessChest(player, chestLocation, this)); + return evaluateWorldPolicy(protector -> protector.canAccessChest(player, chestLocation, this)); } default boolean checkRegion(Player player) { - return getProtectors().stream().allMatch(protector -> protector.checkRegion(player, player.getLocation(), this)); + return evaluateWorldPolicy(protector -> protector.checkRegion(player, player.getLocation(), this)); + } + + private boolean evaluateWorldPolicy(Predicate evaluator) { + long start = System.nanoTime(); + try { + return getProtectors().stream().allMatch(evaluator); + } finally { + WorldPolicyLatencyTelemetry.recordNanos(System.nanoTime() - start); + } } default boolean hasUsageConflict(Player p) { diff --git a/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java b/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java new file mode 100644 index 000000000..9a762aefe --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java @@ -0,0 +1,68 @@ +package art.arcane.adapt.api.protection; + +import java.util.ArrayDeque; + +public final class WorldPolicyLatencyTelemetry { + private static final long WINDOW_MS = 60_000L; + private static final int MAX_SAMPLES = 200_000; + + private static final ArrayDeque SAMPLES = new ArrayDeque<>(); + private static long totalNanos = 0L; + + private WorldPolicyLatencyTelemetry() { + } + + public static void recordNanos(long durationNanos) { + if (durationNanos < 0L) { + return; + } + + long now = System.currentTimeMillis(); + synchronized (SAMPLES) { + trim(now); + SAMPLES.addLast(new Sample(now, durationNanos)); + totalNanos += durationNanos; + + while (SAMPLES.size() > MAX_SAMPLES) { + Sample oldest = SAMPLES.removeFirst(); + totalNanos -= oldest.durationNanos; + } + + if (totalNanos < 0L) { + totalNanos = 0L; + } + } + } + + public static double averageMillis(long now) { + synchronized (SAMPLES) { + trim(now); + if (SAMPLES.isEmpty()) { + return 0D; + } + + return (totalNanos / 1_000_000D) / (double) SAMPLES.size(); + } + } + + public static void clear() { + synchronized (SAMPLES) { + SAMPLES.clear(); + totalNanos = 0L; + } + } + + private static void trim(long now) { + while (!SAMPLES.isEmpty() && (now - SAMPLES.peekFirst().timestampMs) > WINDOW_MS) { + Sample oldest = SAMPLES.removeFirst(); + totalNanos -= oldest.durationNanos; + } + + if (totalNanos < 0L) { + totalNanos = 0L; + } + } + + private record Sample(long timestampMs, long durationNanos) { + } +} diff --git a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java index 57048ed9e..784990c9d 100644 --- a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java +++ b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java @@ -1,6 +1,7 @@ package art.arcane.adapt.service; import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.protection.WorldPolicyLatencyTelemetry; import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.volmlib.integration.IntegrationHandshakeRequest; @@ -14,6 +15,7 @@ import art.arcane.volmlib.integration.IntegrationServiceContract; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.plugin.ServicePriority; import java.util.ArrayDeque; @@ -35,7 +37,8 @@ public class AdaptIntegrationService implements AdaptService, IntegrationService "adapt-runtime-metrics" ); - private final ArrayDeque abilityOps = new ArrayDeque<>(); + private final ArrayDeque abilitySuccessfulOps = new ArrayDeque<>(); + private final ArrayDeque abilityCheckOps = new ArrayDeque<>(); private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1); @Override @@ -47,17 +50,27 @@ public void onEnable() { @Override public void onDisable() { Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this); - synchronized (abilityOps) { - abilityOps.clear(); + synchronized (abilitySuccessfulOps) { + abilitySuccessfulOps.clear(); } + synchronized (abilityCheckOps) { + abilityCheckOps.clear(); + } + WorldPolicyLatencyTelemetry.clear(); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onAdaptationUse(AdaptAdaptationUseEvent event) { long now = System.currentTimeMillis(); - synchronized (abilityOps) { - abilityOps.addLast(now); - trimAbilityOps(now); + synchronized (abilityCheckOps) { + abilityCheckOps.addLast(now); + trimAbilityOps(abilityCheckOps, now); + } + if (!event.isCancelled()) { + synchronized (abilitySuccessfulOps) { + abilitySuccessfulOps.addLast(now); + trimAbilityOps(abilitySuccessfulOps, now); + } } } @@ -152,11 +165,8 @@ public Map sampleMetrics(Set metricKeys switch (key) { case IntegrationMetricSchema.ADAPT_SESSION_LOAD -> out.put(key, sampleSessionLoad(now)); case IntegrationMetricSchema.ADAPT_ABILITY_OPS -> out.put(key, sampleAbilityOps(now)); - case IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY -> out.put(key, IntegrationMetricSample.unavailable( - IntegrationMetricSchema.descriptor(key), - "world-policy-latency-not-instrumented", - now - )); + case IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS -> out.put(key, sampleAbilityCheckOps(now)); + case IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY -> out.put(key, sampleWorldPolicyLatency(now)); default -> out.put(key, IntegrationMetricSample.unavailable( IntegrationMetricSchema.descriptor(key), "unsupported-key", @@ -181,17 +191,34 @@ private IntegrationMetricSample sampleSessionLoad(long now) { private IntegrationMetricSample sampleAbilityOps(long now) { IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_OPS); long count; - synchronized (abilityOps) { - trimAbilityOps(now); - count = abilityOps.size(); + synchronized (abilitySuccessfulOps) { + trimAbilityOps(abilitySuccessfulOps, now); + count = abilitySuccessfulOps.size(); + } + + return IntegrationMetricSample.available(descriptor, count, now); + } + + private IntegrationMetricSample sampleAbilityCheckOps(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS); + long count; + synchronized (abilityCheckOps) { + trimAbilityOps(abilityCheckOps, now); + count = abilityCheckOps.size(); } return IntegrationMetricSample.available(descriptor, count, now); } - private void trimAbilityOps(long now) { - while (!abilityOps.isEmpty() && (now - abilityOps.peekFirst()) > ABILITY_WINDOW_MS) { - abilityOps.removeFirst(); + private IntegrationMetricSample sampleWorldPolicyLatency(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY); + double averageMs = WorldPolicyLatencyTelemetry.averageMillis(now); + return IntegrationMetricSample.available(descriptor, averageMs, now); + } + + private void trimAbilityOps(ArrayDeque samples, long now) { + while (!samples.isEmpty() && (now - samples.peekFirst()) > ABILITY_WINDOW_MS) { + samples.removeFirst(); } } } From 38ed1ba8a17f75ebdb92f60d575beef6bb024d5d Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 14 Feb 2026 23:55:32 -0500 Subject: [PATCH 05/25] CMDS --- build.gradle.kts | 6 +- src/main/java/art/arcane/adapt/Adapt.java | 14 +- .../art/arcane/adapt/service/CommandSVC.java | 232 +++++-- .../art/arcane/adapt/service/HotloadSVC.java | 296 ++------- .../adapt/util/common/decree/DecreeNode.java | 17 - .../util/common/decree/DecreeParameter.java | 28 - .../util/common/decree/DecreeSystem.java | 95 +-- .../decree/virtual/VirtualDecreeCommand.java | 588 ------------------ .../adapt/util/common/misc/CustomModel.java | 8 +- .../util/common/plugin/VolmitSender.java | 122 ++-- .../project/config/ConfigFileSupport.java | 19 +- 11 files changed, 349 insertions(+), 1076 deletions(-) delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java diff --git a/build.gradle.kts b/build.gradle.kts index 474602d4d..49bfd45f6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,7 @@ plugins { alias(libs.plugins.kotlin.jvm) } -version = "1.18.0-1.20.2-1.21.11" +version = "2.0.0-1.20.2-1.21.11-Dev1" val apiVersion = "1.20" val main = "art.arcane.adapt.Adapt" @@ -157,7 +157,9 @@ dependencies { compileOnly(libs.spigot) // Cancer - slimApi(libs.fukkit) + slimApi(libs.fukkit) { + exclude(group = "org.spigotmc", module = "spigot-api") + } slimApi(libs.amulet) slimApi(libs.chrono) slimApi(libs.spatial) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index c4dc629bb..2ecb11626 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -40,6 +40,7 @@ import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.adapt.util.config.ConfigMigrationManager; +import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.adapt.util.project.redis.RedisSync; import art.arcane.adapt.util.secret.SecretSplash; import de.crazydev22.platformutils.AudienceProvider; @@ -107,15 +108,23 @@ public class Adapt extends VolmitPlugin { private final KList postShutdown = new KList<>(); private static VolmitSender sender; private static final long STARTUP_SLOW_PHASE_MS = 1500L; + private static final boolean SLIMJAR_DEBUG = Boolean.getBoolean("adapt.debug-slimjar"); + private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("adapt.disable-remapper"); public Adapt() { instance = this; + long libraryLoadStart = System.currentTimeMillis(); getLogger().info("Loading Libraries..."); new SpigotApplicationBuilder(this) - .remap(true) + .debug(SLIMJAR_DEBUG) + .remap(!DISABLE_REMAPPER) .build(); - getLogger().info("Libraries Loaded!"); + long libraryLoadElapsed = System.currentTimeMillis() - libraryLoadStart; + if (DISABLE_REMAPPER) { + getLogger().warning("SlimJar remapper disabled via -Dadapt.disable-remapper=true."); + } + getLogger().info("Libraries Loaded! (" + libraryLoadElapsed + "ms)"); adaptEffectManager = new EffectManager(this); } @@ -203,6 +212,7 @@ public void start() { initializeAdaptationListings(); services.values().forEach(AdaptService::onEnable); services.values().forEach(this::registerListener); + ConfigFileSupport.flushCreatedConfigSummary(); } private static void runStartupPhaseVoid(String phase, Runnable action) { diff --git a/src/main/java/art/arcane/adapt/service/CommandSVC.java b/src/main/java/art/arcane/adapt/service/CommandSVC.java index 24832b6a3..d6bbcc7fc 100644 --- a/src/main/java/art/arcane/adapt/service/CommandSVC.java +++ b/src/main/java/art/arcane/adapt/service/CommandSVC.java @@ -20,35 +20,60 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.command.CommandAdapt; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.plugin.VolmitSender; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.cache.AtomicCache; -import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.decree.DecreeContext; +import art.arcane.adapt.util.decree.DecreeContextHandler; import art.arcane.adapt.util.decree.DecreeSystem; -import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; +import art.arcane.volmlib.util.director.compat.DirectorDecreeEngineFactory; +import art.arcane.volmlib.util.director.context.DirectorContextRegistry; +import art.arcane.volmlib.util.director.runtime.DirectorExecutionMode; +import art.arcane.volmlib.util.director.runtime.DirectorExecutionResult; +import art.arcane.volmlib.util.director.runtime.DirectorInvocation; +import art.arcane.volmlib.util.director.runtime.DirectorInvocationHook; +import art.arcane.volmlib.util.director.runtime.DirectorRuntimeEngine; +import art.arcane.volmlib.util.director.runtime.DirectorRuntimeNode; +import art.arcane.volmlib.util.director.runtime.DirectorSender; +import art.arcane.volmlib.util.director.visual.DirectorVisualCommand; +import art.arcane.volmlib.util.math.RNG; +import org.bukkit.Sound; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; -import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Locale; -import java.util.concurrent.CompletableFuture; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; -public class CommandSVC implements AdaptService, DecreeSystem { - private final KMap> futures = new KMap<>(); - private final transient AtomicCache commandCache = new AtomicCache<>(); - private CompletableFuture consoleFuture = null; +public class CommandSVC implements AdaptService, CommandExecutor, TabCompleter, DirectorInvocationHook { + private static final String ROOT_COMMAND = "adapt"; + private static final String ROOT_PERMISSION = "adapt.main"; + + private final transient AtomicCache directorCache = new AtomicCache<>(); + private final transient AtomicCache helpCache = new AtomicCache<>(); @Override public void onEnable() { Adapt.verbose("Initializing Commands..."); - PluginCommand command = Adapt.instance.getCommand("adapt"); + PluginCommand command = Adapt.instance.getCommand(ROOT_COMMAND); if (command == null) { - Adapt.warn("Failed to find command 'adapt'"); + Adapt.warn("Failed to find command '" + ROOT_COMMAND + "'"); return; } + command.setExecutor(this); - J.a(() -> getRoot().cacheAll()); + command.setTabCompleter(this); + J.a(this::getDirector); } @Override @@ -56,42 +81,175 @@ public void onDisable() { } - @EventHandler - public void on(PlayerCommandPreprocessEvent e) { - String msg = e.getMessage().startsWith("/") ? e.getMessage().substring(1) : e.getMessage(); + public DirectorRuntimeEngine getDirector() { + return directorCache.aquireNastyPrint(() -> DirectorDecreeEngineFactory.create( + new CommandAdapt(), + null, + buildDirectorContexts(), + this::dispatchDirector, + this, + DecreeSystem.handlers + )); + } - if (msg.startsWith("adaptdecree ")) { - String[] args = msg.split("\\Q \\E"); - CompletableFuture future = futures.get(args[1]); + private DirectorContextRegistry buildDirectorContexts() { + DirectorContextRegistry contexts = new DirectorContextRegistry(); - if (future != null) { - future.complete(args[2]); - e.setCancelled(true); - } + for (Map.Entry, DecreeContextHandler> entry : DecreeContextHandler.contextHandlers.entrySet()) { + registerContextHandler(contexts, entry.getKey(), entry.getValue()); } + + return contexts; } - @EventHandler - public void on(ServerCommandEvent e) { - if (consoleFuture != null && !consoleFuture.isCancelled() && !consoleFuture.isDone()) { - if (!e.getCommand().contains(" ")) { - String pick = e.getCommand().trim().toLowerCase(Locale.ROOT); - consoleFuture.complete(pick); - e.setCancelled(true); + @SuppressWarnings({"rawtypes", "unchecked"}) + private void registerContextHandler(DirectorContextRegistry contexts, Class type, DecreeContextHandler handler) { + contexts.register((Class) type, (invocation, map) -> { + if (invocation.getSender() instanceof BukkitDirectorSender sender) { + return ((DecreeContextHandler) handler).handle(new VolmitSender(sender.sender())); } + + return null; + }); + } + + private void dispatchDirector(DirectorExecutionMode mode, Runnable runnable) { + if (mode == DirectorExecutionMode.SYNC) { + J.s(runnable); + } else { + runnable.run(); } } @Override - public VirtualDecreeCommand getRoot() { - return commandCache.aquireNastyPrint(() -> VirtualDecreeCommand.createRoot(new CommandAdapt())); + public void beforeInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { + if (invocation.getSender() instanceof BukkitDirectorSender sender) { + DecreeContext.touch(new VolmitSender(sender.sender())); + } + } + + @Override + public void afterInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { + DecreeContext.remove(); + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { + return List.of(); + } + + List v = runDirectorTab(sender, alias, args); + + if (sender instanceof Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); + } + + return v; } - public void post(String password, CompletableFuture future) { - futures.put(password, future); + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { + return false; + } + + Adapt.verbose("Received Command from %s: /%s".formatted(sender.getName(), label + String.join(" ", args))); + if (!sender.hasPermission(ROOT_PERMISSION)) { + sender.sendMessage("You lack the Permission '" + ROOT_PERMISSION + "'"); + return true; + } + + executeCommand(sender, label, args); + return true; } - public void postConsole(CompletableFuture future) { - consoleFuture = future; + private void executeCommand(CommandSender sender, String label, String[] args) { + if (sendHelpIfRequested(sender, args)) { + playSuccessSound(sender); + return; + } + + DirectorExecutionResult result = runDirector(sender, label, args); + + if (result.isSuccess()) { + playSuccessSound(sender); + return; + } + + playFailureSound(sender); + if (result.getMessage() == null || result.getMessage().trim().isEmpty()) { + sender.sendMessage(C.RED + "Unknown Adapt Command"); + } + } + + private boolean sendHelpIfRequested(CommandSender sender, String[] args) { + Optional request = DirectorVisualCommand.resolveHelp(getHelpRoot(), Arrays.asList(args)); + if (request.isEmpty()) { + return false; + } + + VolmitSender volmitSender = new VolmitSender(sender); + volmitSender.sendDecreeHelp(request.get().command(), request.get().page()); + return true; + } + + private DirectorVisualCommand getHelpRoot() { + return helpCache.aquireNastyPrint(() -> DirectorVisualCommand.createRoot(getDirector())); + } + + private DirectorExecutionResult runDirector(CommandSender sender, String label, String[] args) { + try { + return getDirector().execute(new DirectorInvocation(new BukkitDirectorSender(sender), label, Arrays.asList(args))); + } catch (Throwable e) { + Adapt.warn("Director command execution failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); + return DirectorExecutionResult.notHandled(); + } + } + + private List runDirectorTab(CommandSender sender, String alias, String[] args) { + try { + return getDirector().tabComplete(new DirectorInvocation(new BukkitDirectorSender(sender), alias, Arrays.asList(args))); + } catch (Throwable e) { + Adapt.warn("Director tab completion failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); + return List.of(); + } + } + + private void playFailureSound(CommandSender sender) { + if (sender instanceof Player player) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, 0.77f, 0.25f); + sp.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.2f, 0.45f); + } + } + + private void playSuccessSound(CommandSender sender) { + if (sender instanceof Player player) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_BREAK, 0.77f, 1.65f); + sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.125f, 2.99f); + } + } + + private record BukkitDirectorSender(CommandSender sender) implements DirectorSender { + @Override + public String getName() { + return sender.getName(); + } + + @Override + public boolean isPlayer() { + return sender instanceof Player; + } + + @Override + public void sendMessage(String message) { + if (message != null && !message.trim().isEmpty()) { + sender.sendMessage(message); + } + } } } diff --git a/src/main/java/art/arcane/adapt/service/HotloadSVC.java b/src/main/java/art/arcane/adapt/service/HotloadSVC.java index 1849fcefd..c620b77f0 100644 --- a/src/main/java/art/arcane/adapt/service/HotloadSVC.java +++ b/src/main/java/art/arcane/adapt/service/HotloadSVC.java @@ -1,8 +1,6 @@ package art.arcane.adapt.service; -import art.arcane.amulet.io.FolderWatcher; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; @@ -16,6 +14,7 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.project.config.ConfigRewriteReporter; import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.hotload.ConfigHotloadEngine; import art.arcane.volmlib.util.io.IO; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.io.Json; @@ -28,15 +27,11 @@ import java.io.File; import java.nio.file.Files; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; -import java.util.Set; import java.util.UUID; import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; @@ -44,33 +39,34 @@ public class HotloadSVC implements AdaptService { private static final long WATCHER_POLL_MS = 500; private static final int MAX_DIFF_MESSAGES_PER_FILE = 12; - private static final String MISSING = ""; - private static final String REMOVED = ""; - private FolderWatcher configWatcher; private TickedObject configTicker; - private File adaptFolder; private File adaptConfigFile; private File adaptConfigLegacyFile; private File modelsFile; private File modelsLegacyFile; private File skillsFolder; private File adaptationsFolder; - private final Map knownSignatures = new HashMap<>(); - private final Map knownContents = new HashMap<>(); + private final ConfigHotloadEngine hotloadEngine = new ConfigHotloadEngine( + this::isManagedConfigFile, + this::listKnownConfigFiles, + this::readFileContent, + this::normalizeContent + ); @Override public void onEnable() { - adaptFolder = Adapt.instance.getDataFolder("adapt"); adaptConfigFile = Adapt.instance.getDataFile("adapt", "adapt.toml"); adaptConfigLegacyFile = Adapt.instance.getDataFile("adapt", "adapt.json"); modelsFile = Adapt.instance.getDataFile("adapt", "models.toml"); modelsLegacyFile = Adapt.instance.getDataFile("adapt", "models.json"); skillsFolder = Adapt.instance.getDataFolder("adapt", "skills"); adaptationsFolder = Adapt.instance.getDataFolder("adapt", "adaptations"); - configWatcher = new FolderWatcher(adaptFolder); - configWatcher.checkModified(); - primeKnownSnapshots(); + hotloadEngine.configure( + WATCHER_POLL_MS, + List.of(adaptConfigFile, adaptConfigLegacyFile, modelsFile, modelsLegacyFile), + List.of(skillsFolder, adaptationsFolder) + ); Adapt.info("Config hotload watcher enabled for all /adapt/*.json and /adapt/*.toml files."); configTicker = new TickedObject("config", "config-hotload-service", WATCHER_POLL_MS) { @@ -87,33 +83,17 @@ public void onDisable() { configTicker.unregister(); configTicker = null; } - configWatcher = null; - knownSignatures.clear(); - knownContents.clear(); + hotloadEngine.clear(); } private void pollConfigChanges() { - if (configWatcher == null) { - return; - } - - Set touched = new HashSet<>(); - if (configWatcher.checkModified()) { - touched.addAll(configWatcher.getCreated()); - touched.addAll(configWatcher.getChanged()); - touched.addAll(configWatcher.getDeleted()); - } - touched.addAll(scanForMissedChanges()); + var touched = hotloadEngine.pollTouchedFiles(); if (touched.isEmpty()) { return; } boolean refreshedSomething = false; for (File file : touched) { - if (file == null || !ConfigFileSupport.isSupportedConfigFile(file)) { - continue; - } - refreshedSomething = processConfigChange(file) || refreshedSomething; } @@ -123,29 +103,13 @@ private void pollConfigChanges() { } private boolean processConfigChange(File file) { - String path = file.getAbsolutePath(); - String before = knownContents.get(path); - String nowRaw = readFileContent(file); - String now = normalizeContent(nowRaw); - - if (Objects.equals(before, now)) { - updateKnownSnapshot(file, now); - return false; - } - - boolean applied = applyConfigChange(file); - String after = normalizeContent(readFileContent(file)); - updateKnownSnapshot(file, after); - if (!applied) { - return false; - } - - if (isModelsConfigFile(file)) { - return true; - } + return hotloadEngine.processFileChange(file, this::applyConfigChange, delta -> { + if (isModelsConfigFile(file)) { + return; + } - notifyOps(file, before, after); - return true; + notifyOps(file, delta.before(), delta.after()); + }); } private boolean applyConfigChange(File file) { @@ -292,6 +256,13 @@ private boolean isAdaptationConfigFile(File file) { return isDirectChild(adaptationsFolder, file) && ConfigFileSupport.isSupportedConfigFile(file); } + private boolean isManagedConfigFile(File file) { + return isAdaptConfigFile(file) + || isModelsConfigFile(file) + || isSkillConfigFile(file) + || isAdaptationConfigFile(file); + } + private boolean isDirectChild(File parent, File child) { if (parent == null || child == null) { return false; @@ -327,101 +298,52 @@ private String toConfigName(String fileName) { return ConfigFileSupport.configNameFromFileName(fileName); } - private void primeKnownSnapshots() { - knownSignatures.clear(); - knownContents.clear(); - for (File file : listKnownConfigFiles()) { - updateKnownSnapshot(file, normalizeContent(readFileContent(file))); - } - } - - private Set scanForMissedChanges() { - Set changed = new HashSet<>(); - Set seenPaths = new HashSet<>(); - for (File file : listKnownConfigFiles()) { - String path = file.getAbsolutePath(); - seenPaths.add(path); - String now = signature(file); - String previous = knownSignatures.put(path, now); - if (previous != null && !previous.equals(now)) { - changed.add(file); - } - } + private List listKnownConfigFiles() { + List files = new ArrayList<>(); + Map added = new HashMap<>(); - for (String path : new HashSet<>(knownSignatures.keySet())) { - if (seenPaths.contains(path)) { - continue; - } + addIfManaged(files, added, adaptConfigFile); + addIfManaged(files, added, adaptConfigLegacyFile); + addIfManaged(files, added, modelsFile); + addIfManaged(files, added, modelsLegacyFile); - String previous = knownSignatures.put(path, "missing"); - if (previous != null && !"missing".equals(previous)) { - changed.add(new File(path)); - } - } + addDirectChildren(skillsFolder, files, added); + addDirectChildren(adaptationsFolder, files, added); - return changed; + return files; } - private List listKnownConfigFiles() { - List files = new ArrayList<>(); - Set added = new HashSet<>(); - - addIfConfig(files, added, adaptConfigFile); - addIfConfig(files, added, adaptConfigLegacyFile); - addIfConfig(files, added, modelsFile); - addIfConfig(files, added, modelsLegacyFile); + private void addDirectChildren(File folder, List out, Map added) { + if (folder == null || !folder.exists() || !folder.isDirectory()) { + return; + } - if (adaptFolder == null || !adaptFolder.exists() || !adaptFolder.isDirectory()) { - return files; + File[] children = folder.listFiles(); + if (children == null || children.length == 0) { + return; } - ArrayDeque queue = new ArrayDeque<>(); - queue.add(adaptFolder); - while (!queue.isEmpty()) { - File next = queue.removeFirst(); - File[] children = next.listFiles(); - if (children == null || children.length == 0) { + for (File child : children) { + if (child == null || !child.isFile()) { continue; } - - for (File child : children) { - if (child == null) { - continue; - } - - if (child.isDirectory()) { - queue.add(child); - continue; - } - - addIfConfig(files, added, child); - } + addIfManaged(out, added, child); } - - return files; } - private void addIfConfig(List out, Set added, File file) { - if (file == null || !ConfigFileSupport.isSupportedConfigFile(file)) { + private void addIfManaged(List out, Map added, File file) { + if (file == null || !isManagedConfigFile(file)) { return; } String path = file.getAbsolutePath(); - if (!added.add(path)) { + if (added.putIfAbsent(path, file) != null) { return; } out.add(file); } - private String signature(File file) { - if (file == null || !file.exists()) { - return "missing"; - } - - return file.lastModified() + ":" + file.length(); - } - private String readFileContent(File file) { if (file == null || !file.exists() || !file.isFile()) { return null; @@ -434,20 +356,6 @@ private String readFileContent(File file) { } } - private void updateKnownSnapshot(File file, String normalizedContent) { - if (file == null) { - return; - } - - String path = file.getAbsolutePath(); - knownSignatures.put(path, signature(file)); - if (normalizedContent == null) { - knownContents.remove(path); - } else { - knownContents.put(path, normalizedContent); - } - } - private String normalizeContent(String text) { if (text == null) { return null; @@ -464,7 +372,11 @@ private JsonElement parseStructured(String raw, File file) { } private void notifyOps(File file, String before, String after) { - List diffs = computeDiff(before, after); + List diffs = ConfigHotloadEngine.computeStructuredDiff( + before, + after, + raw -> parseStructured(raw, null) + ); if (diffs.isEmpty()) { return; } @@ -473,8 +385,8 @@ private void notifyOps(File file, String before, String after) { List messages = new ArrayList<>(); int shown = Math.min(MAX_DIFF_MESSAGES_PER_FILE, diffs.size()); for (int i = 0; i < shown; i++) { - DiffEntry diff = diffs.get(i); - messages.add(formatHotloadMessage(relative, diff.key, diff.oldValue, diff.newValue)); + ConfigHotloadEngine.DiffEntry diff = diffs.get(i); + messages.add(formatHotloadMessage(relative, diff.key(), diff.oldValue(), diff.newValue())); } if (diffs.size() > shown) { @@ -494,79 +406,6 @@ private void notifyOps(File file, String before, String after) { }); } - private List computeDiff(String before, String after) { - Map left = flattenForDiff(before); - Map right = flattenForDiff(after); - Set keys = new HashSet<>(left.keySet()); - keys.addAll(right.keySet()); - - List ordered = new ArrayList<>(keys); - ordered.sort(String::compareTo); - - List changes = new ArrayList<>(); - for (String key : ordered) { - boolean inLeft = left.containsKey(key); - boolean inRight = right.containsKey(key); - String oldValue = inLeft ? left.get(key) : MISSING; - String newValue = inRight ? right.get(key) : REMOVED; - if (Objects.equals(oldValue, newValue)) { - continue; - } - changes.add(new DiffEntry(key, oldValue, newValue)); - } - - return changes; - } - - private Map flattenForDiff(String raw) { - JsonElement element = parseStructured(raw, null); - if (element == null) { - Map fallback = new HashMap<>(); - if (raw != null && !raw.isBlank()) { - fallback.put("$", formatValue(raw)); - } - return fallback; - } - - Map out = new HashMap<>(); - flattenJson("$", element, out); - return out; - } - - private void flattenJson(String path, JsonElement element, Map out) { - if (element == null || element.isJsonNull()) { - out.put(path, "null"); - return; - } - - if (element.isJsonPrimitive()) { - out.put(path, element.toString()); - return; - } - - if (element.isJsonArray()) { - if (element.getAsJsonArray().size() == 0) { - out.put(path, "[]"); - return; - } - - for (int i = 0; i < element.getAsJsonArray().size(); i++) { - flattenJson(path + "[" + i + "]", element.getAsJsonArray().get(i), out); - } - return; - } - - JsonObject object = element.getAsJsonObject(); - if (object.entrySet().isEmpty()) { - out.put(path, "{}"); - return; - } - - for (Map.Entry entry : object.entrySet()) { - flattenJson(path + "." + entry.getKey(), entry.getValue(), out); - } - } - private String formatHotloadMessage(String file, String key, String oldValue, String newValue) { return C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + C.GREEN + "Adapt Hotloaded: " @@ -576,15 +415,7 @@ private String formatHotloadMessage(String file, String key, String oldValue, St } private String formatValue(String value) { - if (value == null) { - return "null"; - } - - String compact = value.replace("\r", "\\r").replace("\n", "\\n"); - if (compact.length() > 120) { - return compact.substring(0, 117) + "..."; - } - return compact; + return ConfigHotloadEngine.compactValue(value, 120); } private String relativizeToDataFolder(File file) { @@ -673,15 +504,4 @@ private void reopenFromTag(Player player, String tag) { skill.openGui(player); } - private static class DiffEntry { - private final String key; - private final String oldValue; - private final String newValue; - - private DiffEntry(String key, String oldValue, String newValue) { - this.key = key; - this.oldValue = oldValue; - this.newValue = newValue; - } - } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java deleted file mode 100644 index cec0234fa..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package art.arcane.adapt.util.decree; - -import art.arcane.volmlib.util.decree.DecreeNodeBase; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; - -public class DecreeNode extends DecreeNodeBase { - public DecreeNode(Object instance, Method method) { - super(instance, method); - } - - @Override - protected DecreeParameter createParameter(Parameter parameter) { - return new DecreeParameter(parameter); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java deleted file mode 100644 index 149283120..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameter.java +++ /dev/null @@ -1,28 +0,0 @@ -package art.arcane.adapt.util.decree; - -import art.arcane.volmlib.util.decree.DecreeParameterBase; -import art.arcane.volmlib.util.decree.specialhandlers.NoParameterHandler; -import art.arcane.adapt.util.decree.specialhandlers.DummyHandler; - -import java.lang.reflect.Parameter; - -public class DecreeParameter extends DecreeParameterBase { - public DecreeParameter(Parameter parameter) { - super(parameter); - } - - @Override - protected boolean useSystemHandler(Class customHandler) { - return customHandler.equals(NoParameterHandler.class) || customHandler.equals(DummyHandler.class); - } - - @Override - protected art.arcane.volmlib.util.decree.DecreeParameterHandler getSystemHandler(Class type) { - return DecreeSystem.getHandler(type); - } - - @Override - public DecreeParameterHandler getHandler() { - return (DecreeParameterHandler) super.getHandler(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java index 120b6a71e..ff4b7183e 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java @@ -18,39 +18,13 @@ */ package art.arcane.adapt.util.decree; -import art.arcane.volmlib.util.math.RNG; - import art.arcane.volmlib.util.decree.DecreeSystemSupport; import art.arcane.adapt.Adapt; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.collection.KList; -import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; -import org.bukkit.Sound; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.plugin.Permission; -import art.arcane.adapt.util.common.plugin.VolmitSender; - -public interface DecreeSystem extends CommandExecutor, TabCompleter { - KList> handlers = Adapt.initialize("art.arcane.adapt.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); - - static KList enhanceArgs(String[] args) { - return new KList<>(DecreeSystemSupport.enhanceArgs(args)); - } +public final class DecreeSystem { + public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); - static KList enhanceArgs(String[] args, boolean trim) { - return new KList<>(DecreeSystemSupport.enhanceArgs(args, trim)); + private DecreeSystem() { } /** @@ -59,7 +33,7 @@ static KList enhanceArgs(String[] args, boolean trim) { * @param type The type to handle * @return The corresponding {@link DecreeParameterHandler}, or null */ - static DecreeParameterHandler getHandler(Class type) { + public static DecreeParameterHandler getHandler(Class type) { DecreeParameterHandler handler = DecreeSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); if (handler != null) { return handler; @@ -68,65 +42,4 @@ static DecreeParameterHandler getHandler(Class type) { Adapt.error("Unhandled type in Decree Parameter: " + type.getName() + ". This is bad!"); return null; } - - /** - * The root class to start command searching from - */ - VirtualDecreeCommand getRoot(); - - default boolean call(VolmitSender sender, String[] args) { - DecreeContext.touch(sender); - try { - return getRoot().invoke(sender, enhanceArgs(args)); - } finally { - DecreeContext.remove(); - } - } - - @Nullable - @Override - default List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - Adapt.verbose("Received Tab Complete from %s for %s".formatted(sender.getName(), "/" + alias + String.join(" ", args))); - DecreeContext.touch(new VolmitSender(sender)); - try { - KList enhanced = new KList<>(args); - KList v = getRoot().tabComplete(enhanced, enhanced.toString(" ")); - v.removeDuplicates(); - - if (sender instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); - } - - return v; - } finally { - DecreeContext.remove(); - } - } - - @Override - default boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - Adapt.verbose("Received Command from %s: /%s".formatted(sender.getName(), label + String.join(" ", args))); - if (!sender.hasPermission("adapt.main")) { - sender.sendMessage("You lack the Permission 'adapt.main'"); - return true; - } - - if (!call(new VolmitSender(sender), args)) { - if (sender instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, 0.77f, 0.25f); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.2f, 0.45f); - } - - sender.sendMessage(C.RED + "Unknown Adapt Command"); - } else { - if (sender instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_BREAK, 0.77f, 1.65f); - sp.play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.125f, 2.99f); - } - } - return true; - } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java b/src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java deleted file mode 100644 index dbf5607cc..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/virtual/VirtualDecreeCommand.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 art.arcane.adapt.util.decree.virtual; - -import art.arcane.amulet.format.Form; -import art.arcane.chrono.ChronoLatch; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.project.command.CommandDummy; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.adapt.util.decree.DecreeContext; -import art.arcane.adapt.util.decree.DecreeContextHandler; -import art.arcane.adapt.util.decree.DecreeNode; -import art.arcane.adapt.util.decree.DecreeParameter; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; -import lombok.Data; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -@Data -public class VirtualDecreeCommand { - private final Class type; - private final VirtualDecreeCommand parent; - private final KList nodes; - private final DecreeNode node; - String[] gradients = new String[]{ - "", - "", - "", - "", - "", - "" - }; - private ChronoLatch cl = new ChronoLatch(1000); - - private VirtualDecreeCommand(Class type, VirtualDecreeCommand parent, KList nodes, DecreeNode node) { - this.parent = parent; - this.type = type; - this.nodes = nodes; - this.node = node; - } - - public static VirtualDecreeCommand createRoot(Object v) throws Throwable { - return createRoot(null, v); - } - - public static VirtualDecreeCommand createRoot(VirtualDecreeCommand parent, Object v) throws Throwable { - VirtualDecreeCommand c = new VirtualDecreeCommand(v.getClass(), parent, new KList<>(), null); - - for (Field i : v.getClass().getDeclaredFields()) { - if (Modifier.isStatic(i.getModifiers()) || Modifier.isFinal(i.getModifiers()) || Modifier.isTransient(i.getModifiers()) || Modifier.isVolatile(i.getModifiers())) { - continue; - } - - if (!i.getType().isAnnotationPresent(Decree.class)) { - continue; - } - - i.setAccessible(true); - Object childRoot = i.get(v); - - if (childRoot == null) { - childRoot = i.getType().getConstructor().newInstance(); - i.set(v, childRoot); - } - - c.getNodes().add(createRoot(c, childRoot)); - } - - for (Method i : v.getClass().getDeclaredMethods()) { - if (Modifier.isStatic(i.getModifiers()) || Modifier.isFinal(i.getModifiers()) || Modifier.isPrivate(i.getModifiers())) { - continue; - } - - if (!i.isAnnotationPresent(Decree.class)) { - continue; - } - - c.getNodes().add(new VirtualDecreeCommand(v.getClass(), c, new KList<>(), new DecreeNode(v, i))); - } - - return c; - } - - public void cacheAll() { - VolmitSender sender = new VolmitSender(new CommandDummy()); - - if (isNode()) { - sender.sendDecreeHelpNode(this); - } - - for (VirtualDecreeCommand j : nodes) { - j.cacheAll(); - } - } - - public String getPath() { - KList n = new KList<>(); - VirtualDecreeCommand cursor = this; - - while (cursor.getParent() != null) { - cursor = cursor.getParent(); - n.add(cursor.getName()); - } - - return "/" + n.reverse().qadd(getName()).toString(" "); - } - - public String getParentPath() { - return getParent().getPath(); - } - - public String getName() { - return isNode() ? getNode().getName() : getType().getDeclaredAnnotation(Decree.class).name(); - } - - public String getDescription() { - return isNode() ? getNode().getDescription() : getType().getDeclaredAnnotation(Decree.class).description(); - } - - public KList getNames() { - if (isNode()) { - return getNode().getNames(); - } - - Decree dc = getType().getDeclaredAnnotation(Decree.class); - KList d = new KList<>(); - d.add(dc.name()); - for (String i : dc.aliases()) { - if (i.isEmpty()) { - continue; - } - - d.add(i); - } - - d.removeDuplicates(); - - return d; - } - - public boolean isNode() { - return node != null; - } - - public KList tabComplete(KList args, String raw) { - KList skip = new KList<>(); - KList tabs = new KList<>(); - invokeTabComplete(args, skip, tabs, raw); - return tabs; - } - - private boolean invokeTabComplete(KList args, KList skip, KList tabs, String raw) { - if (isNode()) { - tab(args, tabs); - skip.add(hashCode()); - return false; - } - - if (args.isEmpty()) { - tab(args, tabs); - return true; - } - - String head = args.get(0); - - if (args.size() > 1 || head.endsWith(" ")) { - VirtualDecreeCommand match = matchNode(head, skip); - - if (match != null) { - args.pop(); - return match.invokeTabComplete(args, skip, tabs, raw); - } - - skip.add(hashCode()); - } else { - tab(args, tabs); - } - - return false; - } - - private void tab(List args, List tabs) { - String last = null; - List ignore = new ArrayList<>(); - Runnable la = () -> { - - }; - for (String a : args) { - la.run(); - last = a; - la = () -> { - if (isNode()) { - String sea = a.contains("=") ? a.split("\\Q=\\E")[0] : a; - sea = sea.trim(); - - searching: - for (DecreeParameter i : getNode().getParameters()) { - for (String m : i.getNames()) { - if (m.equalsIgnoreCase(sea) || m.toLowerCase().contains(sea.toLowerCase()) || sea.toLowerCase().contains(m.toLowerCase())) { - ignore.add(i); - continue searching; - } - } - } - } - }; - } - - if (last != null) { - if (isNode()) { - for (DecreeParameter i : getNode().getParameters()) { - if (ignore.contains(i)) { - continue; - } - - int g = 0; - - if (last.contains("=")) { - String[] vv = last.trim().split("\\Q=\\E"); - String vx = vv.length == 2 ? vv[1] : ""; - for (String f : i.getHandler().getPossibilities(vx).convert((v) -> i.getHandler().toStringForce(v))) { - g++; - tabs.add(i.getName() + "=" + f); - } - } else { - for (String f : i.getHandler().getPossibilities("").convert((v) -> i.getHandler().toStringForce(v))) { - g++; - tabs.add(i.getName() + "=" + f); - } - } - - if (g == 0) { - tabs.add(i.getName() + "="); - } - } - } else { - for (VirtualDecreeCommand i : getNodes()) { - String m = i.getName(); - if (m.equalsIgnoreCase(last) || m.toLowerCase().contains(last.toLowerCase()) || last.toLowerCase().contains(m.toLowerCase())) { - tabs.addAll(i.getNames()); - } - } - } - } - } - - /** - * Maps the input a player typed to the parameters of this command - * - * @param sender The sender - * @param in The input - * @return A map of all the parameter names and their values - */ - private KMap> map(VolmitSender sender, List in) { - KMap> data = new KMap<>(); - List nowhich = new ArrayList<>(); - - List unknownInputs = new ArrayList<>(in.stream().filter(s -> !s.contains("=")).collect(Collectors.toList())); - List knownInputs = new ArrayList<>(in.stream().filter(s -> s.contains("=")).collect(Collectors.toList())); - - //Loop known inputs - for (int x = 0; x < knownInputs.size(); x++) { - String stringParam = knownInputs.get(x); - int original = in.indexOf(stringParam); - - String[] v = stringParam.split("\\Q=\\E"); - String key = v[0]; - String value = v[1]; - DecreeParameter param = null; - - //Find decree parameter from string param - for (DecreeParameter j : getNode().getParameters()) { - for (String k : j.getNames()) { - if (k.equalsIgnoreCase(key)) { - param = j; - break; - } - } - } - - //If it failed, see if we can find it by checking if the names contain the param - if (param == null) { - for (DecreeParameter j : getNode().getParameters()) { - for (String k : j.getNames()) { - if (k.toLowerCase().contains(key.toLowerCase()) || key.toLowerCase().contains(k.toLowerCase())) { - param = j; - break; - } - } - } - } - - //Still failed to find, error them - if (param == null) { - Adapt.debug("Can't find parameter key for " + key + "=" + value + " in " + getPath()); - sender.sendMessage(C.YELLOW + "Unknown Parameter: " + key); - unknownInputs.add(value); //Add the value to the unknowns and see if we can assume it later - continue; - } - - key = param.getName(); - - try { - data.put(key, Optional.ofNullable(param.getHandler().parse(value, nowhich.contains(original)))); //Parse and put - } catch (DecreeParsingException e) { - Adapt.debug("Can't parse parameter value for " + key + "=" + value + " in " + getPath() + " using handler " + param.getHandler().getClass().getSimpleName()); - sender.sendMessage(C.RED + "Cannot convert \"" + value + "\" into a " + param.getType().getSimpleName()); - e.printStackTrace(); - return null; - } - } - - //Make a list of decree params that haven't been identified - List decreeParameters = new KList<>(getNode().getParameters().stream().filter(param -> !data.contains(param.getName())).collect(Collectors.toList())); - - //Loop Unknown inputs - for (int x = 0; x < unknownInputs.size(); x++) { - String stringParam = unknownInputs.get(x); - int original = in.indexOf(stringParam); - try { - DecreeParameter par = decreeParameters.get(x); - - try { - data.put(par.getName(), Optional.ofNullable(par.getHandler().parse(stringParam, nowhich.contains(original)))); - } catch (DecreeParsingException e) { - Adapt.debug("Can't parse parameter value for " + par.getName() + "=" + stringParam + " in " + getPath() + " using handler " + par.getHandler().getClass().getSimpleName()); - sender.sendMessage(C.RED + "Cannot convert \"" + stringParam + "\" into a " + par.getType().getSimpleName()); - e.printStackTrace(); - return null; - } - } catch (IndexOutOfBoundsException e) { - sender.sendMessage(C.YELLOW + "Unknown Parameter: " + stringParam + " (" + Form.getNumberSuffixThStRd(x + 1) + " argument)"); - } - } - - return data; - } - - public boolean invoke(VolmitSender sender, KList realArgs) { - return invoke(sender, realArgs, new KList<>()); - } - - public boolean invoke(VolmitSender sender, KList args, List skip) { - DecreeOrigin origin = type.getDeclaredAnnotation(Decree.class).origin(); - if (!origin.validFor(sender.isPlayer())) { - sender.sendMessage(C.RED + "This command has to be sent from another origin: " + C.GOLD + origin); - return false; - } - - Adapt.debug("@ " + getPath() + " with " + args.toString(", ")); - if (isNode()) { - Adapt.debug("Invoke " + getPath() + "(" + args.toString(",") + ") at "); - if (invokeNode(sender, map(sender, args))) { - return true; - } - - skip.add(hashCode()); - return false; - } - - if (args.isEmpty()) { - sender.sendDecreeHelp(this); - - return true; - } else if (args.size() == 1) { - for (String i : args) { - if (i.startsWith("help=")) { - sender.sendDecreeHelp(this, Integer.parseInt(i.split("\\Q=\\E")[1]) - 1); - return true; - } - } - } - - String head = args.get(0); - VirtualDecreeCommand match = matchNode(head, skip); - - if (match != null) { - args.pop(); - return match.invoke(sender, args, skip); - } - - skip.add(hashCode()); - - return false; - } - - private boolean invokeNode(VolmitSender sender, KMap> map) { - if (map == null) { - return false; - } - - Object[] params = new Object[getNode().getMethod().getParameterCount()]; - int vm = 0; - for (DecreeParameter i : getNode().getParameters()) { - Object value = map.getOrDefault(i.getName(), Optional.empty()).orElse(null); - - try { - if (value == null && i.hasDefault()) { - value = i.getDefaultValue(); - } - } catch (DecreeParsingException e) { - Adapt.debug("Can't parse parameter value for " + i.getName() + "=" + i.getParam().defaultValue() + " in " + getPath() + " using handler " + i.getHandler().getClass().getSimpleName()); - sender.sendMessage(C.RED + "Cannot convert \"" + i.getParam().defaultValue() + "\" into a " + i.getType().getSimpleName()); - return false; - } - - if (sender.isPlayer() && i.isContextual() && value == null) { - Adapt.debug("Contextual!"); - DecreeContextHandler ch = DecreeContextHandler.contextHandlers.get(i.getType()); - - if (ch != null) { - value = ch.handle(sender); - - if (value != null) { - Adapt.debug("Parameter \"" + i.getName() + "\" derived a value of \"" + i.getHandler().toStringForce(value) + "\" from " + ch.getClass().getSimpleName()); - } else { - Adapt.debug("Parameter \"" + i.getName() + "\" could not derive a value from \"" + ch.getClass().getSimpleName()); - } - } else { - Adapt.debug("Parameter \"" + i.getName() + "\" is contextual but has no context handler for \"" + i.getType().getCanonicalName() + "\""); - } - } - - if (i.hasDefault() && value == null) { - try { - Adapt.debug("Parameter \"" + i.getName() + "\" is using default value \"" + i.getParam().defaultValue() + "\""); - value = i.getDefaultValue(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - if (i.isRequired() && value == null) { - sender.sendMessage(C.RED + "Missing argument \"" + i.getName() + "\" (" + i.getType().getSimpleName() + ") as the " + Form.getNumberSuffixThStRd(vm + 1) + " argument."); - sender.sendDecreeHelpNode(this); - return false; - } - - params[vm] = value; - vm++; - } - - DecreeContext.touch(sender); - try { - Runnable rx = () -> { - DecreeContext.touch(sender); - try { - getNode().getMethod().setAccessible(true); - getNode().getMethod().invoke(getNode().getInstance(), params); - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException("Failed to execute "); // TODO: - } finally { - DecreeContext.remove(); - } - }; - - if (getNode().isSync()) { - J.s(rx); - } else { - rx.run(); - } - } finally { - DecreeContext.remove(); - } - - return true; - } - - public KList matchAllNodes(String in) { - KList g = new KList<>(); - - if (in.trim().isEmpty()) { - g.addAll(nodes); - return g; - } - - for (VirtualDecreeCommand i : nodes) { - if (i.matches(in)) { - g.add(i); - } - } - - for (VirtualDecreeCommand i : nodes) { - if (i.deepMatches(in)) { - g.add(i); - } - } - - g.removeDuplicates(); - return g; - } - - public VirtualDecreeCommand matchNode(String in, List skip) { - if (in.trim().isEmpty()) { - return null; - } - - for (VirtualDecreeCommand i : nodes) { - if (skip.contains(i.hashCode())) { - continue; - } - - if (i.matches(in)) { - return i; - } - } - - for (VirtualDecreeCommand i : nodes) { - if (skip.contains(i.hashCode())) { - continue; - } - - if (i.deepMatches(in)) { - return i; - } - } - - return null; - } - - public boolean deepMatches(String in) { - List a = getNames(); - - for (String i : a) { - if (i.toLowerCase().contains(in.toLowerCase()) || in.toLowerCase().contains(i.toLowerCase())) { - return true; - } - } - - return false; - } - - @Override - public int hashCode() { - return Objects.hash(getName(), getDescription(), getType(), getPath()); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof VirtualDecreeCommand)) { - return false; - } - return this.hashCode() == obj.hashCode(); - } - - public boolean matches(String in) { - List a = getNames(); - - for (String i : a) { - if (i.equalsIgnoreCase(in)) { - return true; - } - } - - return false; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java index fb1b6be48..b78b97e8c 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java @@ -151,6 +151,12 @@ public void readFile() throws IOException { synchronized (lock) { if (modelsFile.exists()) { String raw = IO.readAll(modelsFile); + if (raw == null || raw.isBlank()) { + json = new JsonObject(); + ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); + return; + } + JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, modelsFile); if (parsed == null || !parsed.isJsonObject()) { throw new IOException("Invalid models.toml"); @@ -176,7 +182,7 @@ public void readFile() throws IOException { json = new JsonObject(); IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(json)); - Adapt.info("Created missing models config [adapt/models.toml] from defaults."); + ConfigFileSupport.recordMissingConfigCreated(); } } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index c4b9bf587..1b2936459 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -19,19 +19,13 @@ package art.arcane.adapt.util.common.plugin; import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.math.RNG; - - -//import art.arcane.Adapt.Adapt; -//import art.arcane.Adapt.util.collection.List; -//import art.arcane.Adapt.util.collection.Map; -//import art.arcane.Adapt.util.decree.DecreeParameter; -//import art.arcane.Adapt.util.decree.virtual.VirtualDecreeCommand; import static art.arcane.amulet.MagicalSugar.*; import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.collection.KList; -import art.arcane.adapt.util.decree.DecreeParameter; -import art.arcane.adapt.util.decree.virtual.VirtualDecreeCommand; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.director.visual.DirectorVisualCommand; +import art.arcane.volmlib.util.director.visual.DirectorVisualCommand.DirectorVisualParameter; +import art.arcane.volmlib.util.math.RNG; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; @@ -66,7 +60,7 @@ */ public class VolmitSender implements CommandSender { @Getter - private static final Map helpCache = new HashMap<>(); + private static final KMap helpCache = new KMap<>(); private final CommandSender s; public boolean useConsoleCustomColors = true; public boolean useCustomColorsIngame = true; @@ -258,12 +252,12 @@ public void sendProgress(double percent, String thing) { int l = 44; int g = (int) (1D * l); sendTitle(C.ADAPT + thing + " ", 0, 500, 250); - sendActionNoProcessing("" + "" + pulse("#00BFFF", "#003366", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); } else { int l = 44; int g = (int) (percent * l); sendTitle(C.ADAPT + thing + " " + C.BLUE + "" + Form.pc(percent, 0), 0, 500, 250); - sendActionNoProcessing("" + "" + pulse("#00BFFF", "#003366", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); } } @@ -446,19 +440,19 @@ public Spigot spigot() { return s.spigot(); } - private String pickRandoms(int max, VirtualDecreeCommand i) { + private String pickRandoms(int max, DirectorVisualCommand i) { KList m = new KList<>(); for (int ix = 0; ix < max; ix++) { m.add((i.isNode() ? (i.getNode().getParameters().isNotEmpty()) - ? "<#B0E0E6>✦ <#00FA9A>" + ? "<#ffd1d1>✦ <#ff6b6b>" + i.getParentPath() - + " <#00CED1>" + + " <#ff8a8a>" + i.getName() + " " + i.getNode().getParameters().shuffleCopy(RNG.r).kConvert((f) -> (f.isRequired() || RNG.r.b(0.5) ? "<#f2e15e>" + f.getNames().getRandom() + "=" - + "<#9370DB>" + f.example() + + "<#ff9ea0>" + f.example() : "")) .toString(" ") : "" @@ -478,9 +472,9 @@ public void sendHeader(String name, int overrideLength) { String se = "]"; if (name.trim().isEmpty()) { - sendMessageRaw("" + sf + s + "" + s + se); + sendMessageRaw("" + sf + s + "" + s + se); } else { - sendMessageRaw("" + sf + s + si + " " + name + " " + so + s + se); + sendMessageRaw("" + sf + s + si + " " + name + " " + so + s + se); } } @@ -488,31 +482,29 @@ public void sendHeader(String name) { sendHeader(name, 40); } - public void sendDecreeHelp(VirtualDecreeCommand v) { + public void sendDecreeHelp(DirectorVisualCommand v) { sendDecreeHelp(v, 0); } - public void sendDecreeHelp(VirtualDecreeCommand v, int page) { + public void sendDecreeHelp(DirectorVisualCommand v, int page) { if (!isPlayer()) { - for (VirtualDecreeCommand i : v.getNodes()) { + for (DirectorVisualCommand i : v.getNodes()) { sendDecreeHelpNode(i); } return; } - int m = v.getNodes().size(); - sendMessageRaw("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); if (v.getNodes().isNotEmpty()) { sendHeader(v.getPath() + (page > 0 ? (" {" + (page + 1) + "}") : "")); if (isPlayer() && v.getParent() != null) { - sendMessageRaw("Click to go back to <#4682B4>" + Form.capitalize(v.getParent().getName()) + " Help" + "'><#B30000>〈 Back"); + sendMessageRaw("Click to go back to <#ff8a8a>" + Form.capitalize(v.getParent().getName()) + " Help" + "'><#ff6b6b>〈 Back"); } AtomicBoolean next = new AtomicBoolean(false); - for (VirtualDecreeCommand i : paginate(v.getNodes(), 17, page, next)) { + for (DirectorVisualCommand i : paginate(v.getNodes(), 17, page, next)) { sendDecreeHelpNode(i); } @@ -520,13 +512,13 @@ public void sendDecreeHelp(VirtualDecreeCommand v, int page) { int l = 75 - (page > 0 ? 10 : 0) - (next.get() ? 10 : 0); if (page > 0) { - s += "Click to go back to page " + page + "'>〈 Page " + page + " "; + s += "Click to go back to page " + page + "'>〈 Page " + page + " "; } - s += "" + Form.repeat(" ", l) + ""; + s += "" + Form.repeat(" ", l) + ""; if (next.get()) { - s += " Click to go to back to page " + (page + 2) + "'>Page " + (page + 2) + " ❭"; + s += " Click to go to back to page " + (page + 2) + "'>Page " + (page + 2) + " ❭"; } sendMessageRaw(s); @@ -535,17 +527,15 @@ public void sendDecreeHelp(VirtualDecreeCommand v, int page) { } } - public void sendDecreeHelpNode(VirtualDecreeCommand i) { + public void sendDecreeHelpNode(DirectorVisualCommand i) { if (isPlayer() || s instanceof CommandDummy) { sendMessageRaw(helpCache.computeIfAbsent(i.getPath(), (k) -> { String newline = "\n"; - /// Command - // Contains main command & aliases - String realText = i.getPath() + " >" + "<#E6F2FF>⇀ " + i.getName(); - String hoverTitle = i.getNames().copy().reverse().kConvert((f) -> "<#00BFFF>" + f).toString(", "); - String description = "<#3fe05a>✎ <#6ad97d>" + i.getDescription(); - String usage = "<#FF0000>✒ <#A52A2A>"; + String realText = i.getPath() + " >" + "<#ffe6e6>⇀ " + i.getName(); + String hoverTitle = i.getNames().copy().reverse().kConvert((f) -> "<#ff5c5c>" + f).toString(", "); + String description = "<#ff9ea0>✎ <#ffd1d1>" + i.getDescription(); + String usage = "<#FF0000>✒ <#8b1a1a>"; String onClick; if (i.isNode()) { @@ -564,36 +554,34 @@ public void sendDecreeHelpNode(VirtualDecreeCommand i) { String suggestion = ""; String suggestions = ""; if (i.isNode() && i.getNode().getParameters().isNotEmpty()) { - suggestion += newline + "<#B0E0E6>✦ <#00FA9A>" + i.getParentPath() + " <#00CED1>" + i.getName() + " " - + i.getNode().getParameters().kConvert((f) -> "<#9370DB>" + f.example()).toString(" "); + suggestion += newline + "<#ffd1d1>✦ <#ff6b6b>" + i.getParentPath() + " <#ff8a8a>" + i.getName() + " " + + i.getNode().getParameters().kConvert((f) -> "<#ff9ea0>" + f.example()).toString(" "); suggestions += newline + "" + pickRandoms(Math.min(i.getNode().getParameters().size() + 1, 5), i); } - /// Params StringBuilder nodes = new StringBuilder(); if (i.isNode()) { - for (DecreeParameter p : i.getNode().getParameters()) { - - String nTitle = "" + p.getName(); + for (DirectorVisualParameter p : i.getNode().getParameters()) { + String nTitle = "" + p.getName(); String nHoverTitle = p.getNames().kConvert((ff) -> "<#ff9900>" + ff).toString(", "); - String nDescription = "<#2E8B57>✎ <#3CB371>" + p.getDescription(); + String nDescription = "<#ff9ea0>✎ <#ffd1d1>" + p.getDescription(); String nUsage; String fullTitle; Adapt.debug("Contextual: " + p.isContextual() + " / player: " + isPlayer()); if (p.isContextual() && (isPlayer() || s instanceof CommandDummy)) { - fullTitle = "<#FFD700>[" + nTitle + "<#FFD700>] "; - nUsage = "<#ff9900>➱ <#FFD700>The value may be derived from environment context."; + fullTitle = "<#ffcc66>[" + nTitle + "<#ffcc66>] "; + nUsage = "<#ff9900>➱ <#ffcc66>The value may be derived from environment context."; } else if (p.isRequired()) { fullTitle = "[" + nTitle + "] "; - nUsage = "<#CD5C5C>⚠ <#F08080>This parameter is required."; + nUsage = "<#ff6b6b>⚠ <#ffd1d1>This parameter is required."; } else if (p.hasDefault()) { fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; - nUsage = "<#1E90FF>✔ <#87CEEB>Defaults to \"" + p.getParam().defaultValue() + "\" if undefined."; + nUsage = "<#ff8a8a>✔ <#ffd1d1>Defaults to \"" + p.getParam().defaultValue() + "\" if undefined."; } else { fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; - nUsage = "<#8A2BE2>✔ <#87CEEB>This parameter is optional."; + nUsage = "<#ff6b6b>✔ <#ffd1d1>This parameter is optional."; } - String type = "<#cc00ff>✢ <#ff33cc>This parameter is of type " + p.getType().getSimpleName() + "."; + String type = "<#ff6b6b>✢ <#ff9ea0>This parameter is of type " + p.getType().getSimpleName() + "."; nodes .append(""); } } else { - nodes = new StringBuilder(" - Category of Commands"); + nodes = new StringBuilder(" - Category of Commands"); } - /// Wrapper - String wrapper = - "" + - "" + - "" + - " " + - nodes; - - return wrapper; + return "" + + "" + + "" + + " " + + nodes; })); } else { sendMessage(i.getPath()); diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java index 23ece7785..a15b5e728 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java @@ -10,10 +10,12 @@ import java.io.IOException; import java.nio.file.Files; import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; public final class ConfigFileSupport { private static final long MAX_CONFIG_BYTES_DEFAULT = 2L * 1024L * 1024L; private static final long MAX_CONFIG_BYTES_SKILL_OR_ADAPTATION = 256L * 1024L; + private static final AtomicInteger CREATED_MISSING_CONFIGS = new AtomicInteger(); private ConfigFileSupport() { } @@ -87,12 +89,23 @@ public static T load( } IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); - if (createdMessage != null && !createdMessage.isBlank()) { - Adapt.info(createdMessage); - } + recordMissingConfigCreated(); return fallback; } + public static void recordMissingConfigCreated() { + CREATED_MISSING_CONFIGS.incrementAndGet(); + } + + public static void flushCreatedConfigSummary() { + int created = CREATED_MISSING_CONFIGS.getAndSet(0); + if (created <= 0) { + return; + } + + Adapt.info("Created " + created + " missing config " + (created == 1 ? "entry" : "entries") + " from defaults."); + } + public static String normalize(String text) { if (text == null) { return ""; From 01f1b943769e2b2c42d4e514dccab71f59bd5c92 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 15 Feb 2026 04:50:13 -0500 Subject: [PATCH 06/25] Fixes --- src/main/java/art/arcane/adapt/Adapt.java | 10 +- .../art/arcane/adapt/api/skill/Skill.java | 10 +- .../adapt/api/world/PlayerSkillLine.java | 10 +- .../arcane/adapt/content/gui/SkillsGui.java | 35 ++++-- .../util/common/format/AdventureCompat.java | 106 ++++++++++++++++++ .../adapt/util/common/format/Localizer.java | 5 +- .../util/common/plugin/VolmitSender.java | 22 ++-- 7 files changed, 166 insertions(+), 32 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 2ecb11626..53fa926cf 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -355,11 +355,11 @@ private void startupPrint() { Random r = new Random(); int game = r.nextInt(100); if (game < 90) { - Adapt.info("\n" + C.GRAY + " █████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "█████" + C.DARK_RED + "╗ " + C.GRAY + "██████" + C.DARK_RED + "╗ " + C.GRAY + "████████" + C.DARK_RED + "╗\n" + - C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "╗╚══" + C.GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + - C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "███████" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔══" + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + - C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██████" + C.DARK_RED + "╔╝" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║" + C.GRAY + "██" + C.DARK_RED + "║ " + C.GRAY + "██" + C.DARK_RED + "║ \n" + + Adapt.info("\n" + C.DARK_GRAY + " █████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "█████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "████████" + C.DARK_RED + "╗\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║ \n" + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); } else { info(SecretSplash.getSecretSplash().getRandom()); diff --git a/src/main/java/art/arcane/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java index 8f4416436..b6a9898b7 100644 --- a/src/main/java/art/arcane/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -230,7 +230,10 @@ default String getDisplayName(int level) { if (!this.isEnabled()) { return C.DARK_GRAY + Form.capitalize(getName()); } - return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; + if (level > 0) { + return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; + } + return getDisplayName(); } default CustomModel getModel() { @@ -372,7 +375,10 @@ default void openGui(Player player, int page) { } visibleAdaptations.add(adaptation); } - visibleAdaptations.sort(Comparator.comparing(adaptation -> normalizeSortKey(adaptation.getDisplayName()))); + visibleAdaptations.sort( + Comparator.comparing((Adaptation adaptation) -> normalizeSortKey(adaptation.getDisplayName())) + .thenComparing(Adaptation::getName, String.CASE_INSENSITIVE_ORDER) + ); boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); GuiLayout.PagePlan plan = GuiLayout.plan(visibleAdaptations.size(), reserveNavigation); diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index 5a802d855..7a6d9f0fe 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -327,13 +327,19 @@ public int getAdaptationLevel(String id) { } public void setAdaptation(Adaptation a, int level) { - if (level <= 1) { + if (a == null) { + return; + } + + int clamped = Math.max(0, Math.min(level, a.getMaxLevel())); + if (clamped <= 0) { adaptations.remove(a.getName()); + return; } PlayerAdaptation v = new PlayerAdaptation(); v.setId(a.getName()); - v.setLevel(Math.min(level, a.getMaxLevel())); + v.setLevel(clamped); adaptations.put(a.getName(), v); } diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 705b1977c..8687f43c4 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -24,8 +24,6 @@ import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.api.xp.XP; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -68,27 +66,33 @@ public static void open(Player player, int page) { } List entries = new ArrayList<>(); - for (PlayerSkillLine line : adaptPlayer.getData().getSkillLines().sortV()) { - Skill skill = line.getRawSkill(adaptPlayer); + for (Skill skill : adaptPlayer.getServer().getSkillRegistry().getSkills()) { if (skill == null) { continue; } if (!skill.isEnabled()) { continue; } + PlayerSkillLine line = adaptPlayer.getData().getSkillLineNullable(skill.getName()); + if (line == null) { + continue; + } if (skill.hasBlacklistPermission(adaptPlayer.getPlayer(), skill) || line.getLevel() < 0) { continue; } - int adaptationLevel = 0; - for (PlayerAdaptation adaptation : line.getAdaptations().sortV()) { - adaptationLevel += adaptation.getLevel(); + int adaptationLevel = sumAdaptationLevels(line); + if (!hasVisibleProgress(line, adaptationLevel)) { + continue; } entries.add(new SkillPageEntry(skill, line, adaptationLevel)); } - entries.sort(Comparator.comparing(entry -> normalizeSortKey(entry.skill().getDisplayName()))); + entries.sort( + Comparator.comparing((SkillPageEntry entry) -> normalizeSortKey(entry.skill().getDisplayName())) + .thenComparing(entry -> entry.skill().getName(), String.CASE_INSENSITIVE_ORDER) + ); boolean reserveNavigation = false; GuiLayout.PagePlan plan = GuiLayout.plan(entries.size(), reserveNavigation); @@ -148,6 +152,21 @@ public static void open(Player player, int page) { private record SkillPageEntry(Skill skill, PlayerSkillLine line, int adaptationLevel) { } + private static int sumAdaptationLevels(PlayerSkillLine line) { + int total = 0; + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation == null) { + continue; + } + total += Math.max(0, adaptation.getLevel()); + } + return total; + } + + private static boolean hasVisibleProgress(PlayerSkillLine line, int adaptationLevel) { + return line.getXp() > 0D || line.getKnowledge() > 0L || adaptationLevel > 0; + } + private static String normalizeSortKey(String value) { if (value == null) { return ""; diff --git a/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java b/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java new file mode 100644 index 000000000..0ecb09c2b --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java @@ -0,0 +1,106 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.util.common.format; + +import art.arcane.adapt.Adapt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; + +public final class AdventureCompat { + private static final AtomicBoolean FALLBACK_NOTIFIED = new AtomicBoolean(false); + private static volatile boolean miniMessageCompatible = true; + + private AdventureCompat() { + } + + public static Component deserialize(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.miniMessage().deserialize(source); + } catch (Throwable e) { + markIncompatible(e); + } + } + + return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); + } + + public static Component deserializeNoProcessing(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.builder().postProcessor(c -> c).build().deserialize(source); + } catch (Throwable e) { + markIncompatible(e); + } + } + + return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); + } + + public static String stripTags(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.miniMessage().stripTags(source); + } catch (Throwable e) { + markIncompatible(e); + } + } + + return stripTagsFallback(source); + } + + public static String toLegacySection(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(source)); + } catch (Throwable e) { + markIncompatible(e); + } + } + + return C.translateAlternateColorCodes('&', stripTagsFallback(source)); + } + + private static String normalize(String message) { + return message == null ? "" : message; + } + + private static String stripTagsFallback(String message) { + return message.replaceAll("<[^>]+>", ""); + } + + private static void markIncompatible(Throwable e) { + miniMessageCompatible = false; + if (FALLBACK_NOTIFIED.compareAndSet(false, true)) { + String reason = e == null ? "unknown" : e.getClass().getSimpleName(); + Adapt.warn("MiniMessage compatibility fallback enabled (" + reason + ")."); + if (e != null) { + Adapt.verbose("MiniMessage fallback reason: " + e.toString().toLowerCase(Locale.ROOT)); + } + } + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java index c34550011..3ef90ac6d 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java +++ b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java @@ -25,8 +25,6 @@ import art.arcane.volmlib.util.io.IO; import art.arcane.adapt.util.config.ConfigFileSupport; import lombok.SneakyThrows; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import java.io.File; import java.io.InputStream; @@ -110,8 +108,7 @@ public static String dLocalize(String key, Object... params) { s = C.aura(s, -20, 7, 8, 0.36); } - return LegacyComponentSerializer.legacySection() - .serialize(MiniMessage.miniMessage().deserialize(s)); + return AdventureCompat.toLegacySection(s); } private static void syncLanguageResource(File langFolder, String languageCode) throws Exception { diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index 1b2936459..d079ee3fb 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -29,7 +29,6 @@ import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.title.Title; import org.bukkit.Server; import org.bukkit.Sound; @@ -49,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.AdventureCompat; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.project.command.Command; import art.arcane.adapt.util.project.command.CommandDummy; @@ -283,38 +283,38 @@ public boolean canUseCustomColors(VolmitSender volmitSender) { private Component createNoPrefixComponent(String message) { if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(message)); - return MiniMessage.miniMessage().deserialize(t); + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(message)); + return AdventureCompat.deserialize(t); } String t = C.translateAlternateColorCodes('&', message); String a = C.aura(t, spinh, spins, spinb, 0.36); - return MiniMessage.miniMessage().deserialize(a); + return AdventureCompat.deserialize(a); } private Component createNoPrefixComponentNoProcessing(String message) { - return MiniMessage.builder().postProcessor(c -> c).build().deserialize(message); + return AdventureCompat.deserializeNoProcessing(message); } private Component createComponent(String message) { if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); - return MiniMessage.miniMessage().deserialize(t); + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); + return AdventureCompat.deserialize(t); } String t = C.translateAlternateColorCodes('&', getTag() + message); String a = C.aura(t, spinh, spins, spinb); - return MiniMessage.miniMessage().deserialize(a); + return AdventureCompat.deserialize(a); } private Component createComponentRaw(String message) { if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message)); - return MiniMessage.miniMessage().deserialize(t); + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); + return AdventureCompat.deserialize(t); } String t = C.translateAlternateColorCodes('&', getTag() + message); - return MiniMessage.miniMessage().deserialize(t); + return AdventureCompat.deserialize(t); } public void showWaiting(String passive, CompletableFuture f) { From 0f26e2bea9e33e2eb8fde8c2fb121ad06aae05cb Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 15 Feb 2026 17:43:43 -0500 Subject: [PATCH 07/25] f --- build.gradle.kts | 2 +- .../arcane/adapt/command/CommandAdapt.java | 35 +++++++++++++++---- .../adaptation/ranged/RangedRicochetBolt.java | 4 +-- .../context/AdaptationListingHandler.java | 19 +++++++--- .../NullablePlayerHandler.java | 12 +++++-- src/main/resources/en_US.toml | 6 ++-- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 49bfd45f6..7acb976bd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,7 @@ registerCustomOutputTask("CrazyDev22", "C://Users/Julian/Desktop/server/plugins" registerCustomOutputTask("Pixel", "D://Iris Dimension Engine//1.20.4 - Development//plugins") // ========================== UNIX ============================== registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins") -registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/plugins") +registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/plugin-jars") registerCustomOutputTaskUnix("the456gamer", "/home/the456gamer/projects/minecraft/adapt-testserver/plugins/update/", false) // ============================================================== diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index beb74569c..413cf0305 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -23,7 +23,9 @@ import org.bukkit.entity.Player; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import art.arcane.adapt.util.common.plugin.Permission; import art.arcane.adapt.util.project.command.Command; @@ -179,7 +181,7 @@ public void experience( if (skillName.equals("[all]")) { Map experienceMap = new HashMap<>(); - for (Skill skill : SkillRegistry.skills.sortV()) { + for (Skill skill : allSkillSnapshot()) { experienceMap.put(skill.getName(), (double) amount); } targetPlayer.getInventory().addItem(ExperienceOrb.with(experienceMap)); @@ -188,12 +190,18 @@ public void experience( } if (skillName.equals("[random]")) { - targetPlayer.getInventory().addItem(ExperienceOrb.with(SkillRegistry.skills.sortV().getRandom().getName(), amount)); + List> skills = allSkillSnapshot(); + if (skills.isEmpty()) { + FConst.error("No skills are registered.").send(sender()); + return; + } + + targetPlayer.getInventory().addItem(ExperienceOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); FConst.success("Giving random orb").send(sender()); return; } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillName.name()); + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); if (skill != null) { targetPlayer.getInventory().addItem(ExperienceOrb.with(skill.getName(), amount)); FConst.success("Giving " + skill.getName() + " orb").send(sender()); @@ -226,7 +234,7 @@ public void knowledge( if (skillName.equals("[all]")) { Map knowledgeMap = new HashMap<>(); - for (Skill skill : SkillRegistry.skills.sortV()) { + for (Skill skill : allSkillSnapshot()) { knowledgeMap.put(skill.getName(), amount); } targetPlayer.getInventory().addItem(KnowledgeOrb.with(knowledgeMap)); @@ -235,12 +243,18 @@ public void knowledge( } if (skillName.equals("[random]")){ - targetPlayer.getInventory().addItem(KnowledgeOrb.with(SkillRegistry.skills.sortV().getRandom().getName(), amount)); + List> skills = allSkillSnapshot(); + if (skills.isEmpty()) { + FConst.error("No skills are registered.").send(sender()); + return; + } + + targetPlayer.getInventory().addItem(KnowledgeOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); FConst.success("Giving random orb").send(sender()); return; } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillName.name()); + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); if(skill != null){ targetPlayer.getInventory().addItem(KnowledgeOrb.with(skill.getName(), amount)); FConst.success("Giving " + skill.getName() + " orb").send(sender()); @@ -333,4 +347,13 @@ public void migrateConfigs() { FConst.success("Canonicalized TOML configs. skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson).send(sender()); } + private List> allSkillSnapshot() { + if (Adapt.instance != null + && Adapt.instance.getAdaptServer() != null + && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { + return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + } + + return SkillRegistry.skills.sortV(); + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java index 54883d556..030a2bd78 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java @@ -383,7 +383,7 @@ public boolean isPermanent() { } @NoArgsConstructor - @ConfigDescription("Arrows ricochet from block impacts with chained bounces, scaling speed, and bonus damage.") + @ConfigDescription("Projectiles ricochet from block impacts with chained bounces, scaling speed, and bonus damage.") protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = false; @@ -443,7 +443,7 @@ protected static class Config { double xpPerRicochet = 6; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet Step for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerRicochetStep = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) in addition to arrows/tridents.", impact = "True enables universal ricochet across most player-thrown projectiles.") + @art.arcane.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) so all supported player projectiles can bounce.", impact = "True enables universal ricochet across most player-thrown projectiles.") boolean applyToAllProjectiles = true; } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java index 413da8015..02a1730f7 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java @@ -1,5 +1,6 @@ package art.arcane.adapt.util.decree.context; +import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.skill.SkillRegistry; @@ -56,16 +57,26 @@ public static KList getAdaptionSkillListings() { adaptationSkillLists.add(t1); AdaptationSkillList t2 = new AdaptationSkillList("[random]"); adaptationSkillLists.add(t2); - for (Skill skill : SkillRegistry.skills.sortV()) { - if (!skill.isEnabled()) { - continue; - } + + for (Skill skill : allSkills()) { AdaptationSkillList t3 = new AdaptationSkillList(skill.getName()); adaptationSkillLists.add(t3); } + + adaptationSkillLists.removeDuplicates(); return adaptationSkillLists; } + private static KList> allSkills() { + if (Adapt.instance != null + && Adapt.instance.getAdaptServer() != null + && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { + return new KList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); + } + + return SkillRegistry.skills.sortV(); + } + public static KList getAdaptationProviders() { if (adaptationProviders.isNotEmpty()) return adaptationProviders; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java index 4951dd41a..19b30a686 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java @@ -1,6 +1,5 @@ package art.arcane.adapt.util.decree.specialhandlers; -import art.arcane.volmlib.util.decree.handlers.base.NullablePlayerHandlerBase; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.handlers.PlayerHandler; @@ -9,6 +8,15 @@ public class NullablePlayerHandler extends PlayerHandler implements DecreeParameterHandler { @Override public Player parse(String in, boolean force) throws DecreeParsingException { - return NullablePlayerHandlerBase.parseNullable(this, in); + if (in == null) { + return null; + } + + String value = in.trim(); + if (value.isEmpty() || value.equals("---") || value.equalsIgnoreCase("null")) { + return null; + } + + return super.parse(value, force); } } diff --git a/src/main/resources/en_US.toml b/src/main/resources/en_US.toml index 8fb928ad9..16556f1d0 100644 --- a/src/main/resources/en_US.toml +++ b/src/main/resources/en_US.toml @@ -2219,7 +2219,7 @@ [ranged.ricochet_bolt] name = "Ricochet Bolt" - description = "Arrows ricochet off solid blocks with chained bounces." + description = "Projectiles ricochet off solid blocks with chained bounces." lore1 = "Max Ricochets" lore2 = "Speed Bonus Per Ricochet" lore3 = "Bonus Damage Per Ricochet" @@ -3392,11 +3392,11 @@ [advancement.challenge_ranged_ricochet_kills_50] title = "Trick Shot" - description = "Kill 50 mobs with ricocheted arrows" + description = "Kill 50 mobs with ricocheted projectiles" [advancement.challenge_ranged_ricochet_kills_500] title = "Pinball Wizard" - description = "Kill 500 mobs with ricocheted arrows" + description = "Kill 500 mobs with ricocheted projectiles" # --- RIFT --- From 57b3a7e2387040552f4547d4134ca6cccbede7ee Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 16 Feb 2026 07:49:21 -0500 Subject: [PATCH 08/25] "Folia" --- build.gradle.kts | 1 + .../java/art/arcane/adapt/api/Component.java | 332 ++++++------- .../adapt/api/adaptation/Adaptation.java | 16 +- .../api/advancement/AdaptAdvancement.java | 16 +- .../api/advancement/AdvancementManager.java | 68 ++- .../adapt/api/potion/BrewingManager.java | 38 +- .../arcane/adapt/api/potion/BrewingTask.java | 17 +- .../arcane/adapt/api/skill/SimpleSkill.java | 51 +- .../art/arcane/adapt/api/skill/Skill.java | 9 +- .../arcane/adapt/api/skill/SkillRegistry.java | 96 +++- .../arcane/adapt/api/tick/TickedObject.java | 53 +- .../agility/AgilityRollLanding.java | 2 +- .../architect/ArchitectElevator.java | 3 +- .../architect/ArchitectFoundation.java | 6 +- .../architect/ArchitectWirelessRedstone.java | 10 +- .../adaptation/axe/AxeLeafVeinminer.java | 2 +- .../adaptation/axe/AxeWoodVeinminer.java | 2 +- .../blocking/BlockingMirrorBlock.java | 3 +- .../blocking/BlockingMultiArmor.java | 6 +- .../brewing/BrewingSuperHeated.java | 113 +++-- .../chronos/ChronosInstantRecall.java | 220 +++++---- .../adaptation/chronos/ChronosSoundFX.java | 16 +- .../chronos/ChronosTemporalEcho.java | 2 +- .../adaptation/chronos/ChronosTimeBomb.java | 2 +- .../discovery/DiscoveryVillagerAtt.java | 6 +- .../excavation/ExcavationOmniTool.java | 18 +- .../excavation/ExcavationSpelunker.java | 2 +- .../herbalism/HerbalismGrowthAura.java | 2 +- .../herbalism/HerbalismReplant.java | 6 +- .../herbalism/HerbalismSporeBloom.java | 66 +-- .../pickaxe/PickaxeQuarrySense.java | 14 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 2 +- .../adaptation/ranged/RangedWebBomb.java | 6 +- .../content/adaptation/rift/RiftBlink.java | 8 +- .../content/adaptation/rift/RiftDescent.java | 4 +- .../adaptation/rift/RiftEnderTaglock.java | 4 +- .../content/adaptation/rift/RiftGate.java | 56 +-- .../rift/RiftInflatedPocketDimension.java | 2 +- .../adaptation/seaborrne/SeaborneOxygen.java | 38 +- .../seaborrne/SeabornePressureDiver.java | 38 +- .../adaptation/seaborrne/SeaborneSpeed.java | 38 +- .../seaborrne/SeaborneTidecaller.java | 4 +- .../seaborrne/SeaborneTurtlesMiningSpeed.java | 30 +- .../seaborrne/SeaborneTurtlesVision.java | 30 +- .../adaptation/stealth/StealthGhostArmor.java | 2 +- .../adaptation/stealth/StealthSight.java | 2 +- .../adaptation/stealth/StealthSnatch.java | 7 +- .../adaptation/stealth/StealthSpeed.java | 9 +- .../adaptation/sword/SwordsMachete.java | 2 +- .../sword/effects/DamagingBleedEffect.java | 2 +- .../adaptation/taming/TamingBeastRecall.java | 3 +- .../adaptation/taming/TamingDamage.java | 33 ++ .../adaptation/taming/TamingHealthBoost.java | 43 +- .../taming/TamingPackLeaderAura.java | 59 ++- .../tragoul/TragoulBoneHarvest.java | 2 +- .../adaptation/tragoul/TragoulGlobe.java | 2 +- .../adaptation/tragoul/TragoulHealing.java | 4 +- .../adaptation/tragoul/TragoulLance.java | 6 +- .../arcane/adapt/content/gui/ConfigGui.java | 21 +- .../arcane/adapt/content/gui/SkillsGui.java | 2 +- .../adapt/content/skill/SkillAgility.java | 4 +- .../adapt/content/skill/SkillChronos.java | 5 +- .../adapt/content/skill/SkillDiscovery.java | 14 +- .../adapt/content/skill/SkillHerbalism.java | 2 +- .../adapt/content/skill/SkillSeaborne.java | 2 +- .../util/common/inventorygui/GuiConfirm.java | 2 +- .../adapt/util/common/plugin/Metrics.java | 11 +- .../util/common/plugin/VolmitPlugin.java | 15 +- .../adapt/util/common/scheduling/J.java | 451 ++++++++++++++---- .../util/project/command/VirtualCommand.java | 3 +- .../util/project/secret/SecretSplash.java | 3 +- .../util/AdvancementUtils.java | 425 +++++++++++++++++ src/main/resources/plugin.yml | 1 + 73 files changed, 1878 insertions(+), 717 deletions(-) create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java diff --git a/build.gradle.kts b/build.gradle.kts index 7acb976bd..083f8425a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -173,6 +173,7 @@ dependencies { slimApi(libs.lettuce) slimApi(libs.particle) slimApi(libs.ultimateAdvancementApi) + implementation(libs.ultimateAdvancementApi) slimApi(libs.customBlockData) slimApi(libs.lur) slimApi(libs.lang3) diff --git a/src/main/java/art/arcane/adapt/api/Component.java b/src/main/java/art/arcane/adapt/api/Component.java index f93164737..4479f8c2c 100644 --- a/src/main/java/art/arcane/adapt/api/Component.java +++ b/src/main/java/art/arcane/adapt/api/Component.java @@ -43,7 +43,6 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; @@ -52,6 +51,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.function.IntConsumer; import java.util.function.Predicate; import static art.arcane.adapt.util.reflect.registries.Particles.ENCHANTMENT_TABLE; @@ -252,11 +252,37 @@ default double getValue(Block block) { return MaterialValue.getValue(block.getType()); } + default void runVisualLoop(int durationTicks, IntConsumer onTick) { + if (durationTicks <= 0) { + return; + } + + int[] tick = {0}; + Runnable[] loop = new Runnable[1]; + loop[0] = () -> { + if (tick[0] >= durationTicks) { + return; + } + + onTick.accept(tick[0]); + tick[0]++; + if (tick[0] < durationTicks) { + J.s(loop[0], 1); + } + }; + J.s(loop[0]); + } + default void vfxMovingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double density) { if (!areParticlesEnabled()) { return; } + if (ticks <= 0) { + return; + } + + int durationTicks = ticks; World world = startLocation.getWorld(); double startX = startLocation.getX(); double startY = startLocation.getY(); @@ -264,39 +290,29 @@ default void vfxMovingSphere(Location startLocation, Location endLocation, int t double endX = endLocation.getX(); double endY = endLocation.getY(); double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / ticks; - double deltaY = (endY - startY) / ticks; - double deltaZ = (endZ - startZ) / ticks; + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); - new BukkitRunnable() { - int tick = 0; - - public void run() { - if (tick >= ticks) { - cancel(); - return; + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; + Location particleLocation = new Location(world, x, y, z); + + for (double i = 0; i < Math.PI; i += Math.PI / density) { + double radius = Math.sin(i) * size; + double yCoord = Math.cos(i) * size; + for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { + double xCoord = Math.sin(j) * radius; + double zCoord = Math.cos(j) * radius; + + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); } - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - Location particleLocation = new Location(world, x, y, z); - - for (double i = 0; i < Math.PI; i += Math.PI / density) { - double radius = Math.sin(i) * size; - double yCoord = Math.cos(i) * size; - for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { - double xCoord = Math.sin(j) * radius; - double zCoord = Math.cos(j) * radius; - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } - } - - tick++; } - }.runTaskTimer(Adapt.instance, 0, 1); + }); } default void vfxMovingSwirlingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double swirlRadius, double density) { @@ -304,6 +320,11 @@ default void vfxMovingSwirlingSphere(Location startLocation, Location endLocatio return; } + if (ticks <= 0) { + return; + } + + int durationTicks = ticks; World world = startLocation.getWorld(); double startX = startLocation.getX(); double startY = startLocation.getY(); @@ -311,45 +332,35 @@ default void vfxMovingSwirlingSphere(Location startLocation, Location endLocatio double endX = endLocation.getX(); double endY = endLocation.getY(); double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / ticks; - double deltaY = (endY - startY) / ticks; - double deltaZ = (endZ - startZ) / ticks; + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); - new BukkitRunnable() { - int tick = 0; + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; - public void run() { - if (tick >= ticks) { - cancel(); - return; - } - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - - // Add swirling effect - double swirlAngle = 2 * Math.PI * tick / ticks; - x += swirlRadius * Math.cos(swirlAngle); - z += swirlRadius * Math.sin(swirlAngle); - - Location particleLocation = new Location(world, x, y, z); - - for (double i = 0; i < Math.PI; i += Math.PI / density) { - double radius = Math.sin(i) * size; - double yCoord = Math.cos(i) * size; - for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { - double xCoord = Math.sin(j) * radius; - double zCoord = Math.cos(j) * radius; - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } - } + // Add swirling effect + double swirlAngle = 2 * Math.PI * tick / durationTicks; + x += swirlRadius * Math.cos(swirlAngle); + z += swirlRadius * Math.sin(swirlAngle); + + Location particleLocation = new Location(world, x, y, z); + + for (double i = 0; i < Math.PI; i += Math.PI / density) { + double radius = Math.sin(i) * size; + double yCoord = Math.cos(i) * size; + for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { + double xCoord = Math.sin(j) * radius; + double zCoord = Math.cos(j) * radius; - tick++; + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); + } } - }.runTaskTimer(Adapt.instance, 0, 1); + }); } default void vfxPlayerBoundingBoxOutline(Player player, Color color, int ticks, int particleCount) { @@ -360,48 +371,37 @@ default void vfxPlayerBoundingBoxOutline(Player player, Color color, int ticks, World world = player.getWorld(); Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); - new BukkitRunnable() { - int tick = 0; - - public void run() { - if (tick >= ticks) { - cancel(); - return; - } - - BoundingBox boundingBox = player.getBoundingBox(); - double minX = boundingBox.getMinX(); - double minY = boundingBox.getMinY(); - double minZ = boundingBox.getMinZ(); - double maxX = boundingBox.getMaxX(); - double maxY = boundingBox.getMaxY(); - double maxZ = boundingBox.getMaxZ(); - - for (int i = 0; i < particleCount; i++) { - double t = (double) i / (particleCount - 1); - - // Edges along X-axis - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, maxZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, maxZ, 0, 0, 0, 0, dustOptions); - - // Edges along Y-axis - world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); - - // Edges along Z-axis - world.spawnParticle(REDSTONE, minX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - } - - tick++; + runVisualLoop(ticks, tick -> { + BoundingBox boundingBox = player.getBoundingBox(); + double minX = boundingBox.getMinX(); + double minY = boundingBox.getMinY(); + double minZ = boundingBox.getMinZ(); + double maxX = boundingBox.getMaxX(); + double maxY = boundingBox.getMaxY(); + double maxZ = boundingBox.getMaxZ(); + + for (int i = 0; i < particleCount; i++) { + double t = (double) i / (particleCount - 1); + + // Edges along X-axis + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, maxZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, maxZ, 0, 0, 0, 0, dustOptions); + + // Edges along Y-axis + world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); + + // Edges along Z-axis + world.spawnParticle(REDSTONE, minX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); } - }.runTaskTimer(Adapt.instance, 0, 1); + }); } default void vfxVortexSphere(Location startLocation, Location endLocation, int ticks, Color color, double radius) { @@ -409,6 +409,11 @@ default void vfxVortexSphere(Location startLocation, Location endLocation, int t return; } + if (ticks <= 0) { + return; + } + + int durationTicks = ticks; World world = startLocation.getWorld(); Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); @@ -418,40 +423,29 @@ default void vfxVortexSphere(Location startLocation, Location endLocation, int t double endX = endLocation.getX(); double endY = endLocation.getY(); double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / ticks; - double deltaY = (endY - startY) / ticks; - double deltaZ = (endZ - startZ) / ticks; - - new BukkitRunnable() { - int tick = 0; - - public void run() { - if (tick >= ticks) { - cancel(); - return; - } - - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - Location particleLocation = new Location(world, x, y, z); - - double currentRadius = radius * (1 - (double) tick / ticks); - - for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 10) { - for (double phi = 0; phi < Math.PI; phi += Math.PI / 10) { - double xCoord = currentRadius * Math.sin(phi) * Math.cos(theta); - double yCoord = currentRadius * Math.sin(phi) * Math.sin(theta); - double zCoord = currentRadius * Math.cos(phi); - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; + + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; + Location particleLocation = new Location(world, x, y, z); + + double currentRadius = radius * (1 - (double) tick / durationTicks); + + for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 10) { + for (double phi = 0; phi < Math.PI; phi += Math.PI / 10) { + double xCoord = currentRadius * Math.sin(phi) * Math.cos(theta); + double yCoord = currentRadius * Math.sin(phi) * Math.sin(theta); + double zCoord = currentRadius * Math.cos(phi); + + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); } - - tick++; } - }.runTaskTimer(Adapt.instance, 0, 1); + }); } @@ -555,7 +549,19 @@ default void vfxParticleLine(Location start, Location end, Particle particle, in start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); continue; } - if (operationPerPoint.test(l)) { + boolean allowed; + try { + allowed = operationPerPoint.test(l); + } catch (IllegalStateException ex) { + // Folia region ownership checks can reject block probes off-thread. + // Skip this point instead of failing the entire visual effect. + Adapt.verbose("Skipping particle line point due to region ownership rejection: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + allowed = false; + } + + if (allowed) { start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); } } @@ -721,24 +727,13 @@ default void vfxLoadingRing(Location center, double radius, Color color, int dur World world = center.getWorld(); Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); - new BukkitRunnable() { - int tick = 0; - - public void run() { - if (tick >= durationTicks) { - cancel(); - return; - } - - double angle = 2 * Math.PI * tick / durationTicks; - double x = radius * Math.cos(angle); - double z = radius * Math.sin(angle); - Location particleLocation = center.clone().add(x, 0, z); - world.spawnParticle(REDSTONE, particleLocation, particleCount, 0, 0, 0, dustOptions); - - tick++; - } - }.runTaskTimer(Adapt.instance, 0, 1); + runVisualLoop(durationTicks, tick -> { + double angle = 2 * Math.PI * tick / durationTicks; + double x = radius * Math.cos(angle); + double z = radius * Math.sin(angle); + Location particleLocation = center.clone().add(x, 0, z); + world.spawnParticle(REDSTONE, particleLocation, particleCount, 0, 0, 0, dustOptions); + }); } default void vfxLoadingRing(Location center, double radius, Particle particle, int durationTicks, int particleCount) { @@ -748,24 +743,13 @@ default void vfxLoadingRing(Location center, double radius, Particle particle, i World world = center.getWorld(); - new BukkitRunnable() { - int tick = 0; - - public void run() { - if (tick >= durationTicks) { - cancel(); - return; - } - - double angle = 2 * Math.PI * tick / durationTicks; - double x = radius * Math.cos(angle); - double z = radius * Math.sin(angle); - Location particleLocation = center.clone().add(x, 0, z); - world.spawnParticle(particle, particleLocation, particleCount, 0, 0, 0); - - tick++; - } - }.runTaskTimer(Adapt.instance, 0, 1); + runVisualLoop(durationTicks, tick -> { + double angle = 2 * Math.PI * tick / durationTicks; + double x = radius * Math.cos(angle); + double z = radius * Math.sin(angle); + Location particleLocation = center.clone().add(x, 0, z); + world.spawnParticle(particle, particleLocation, particleCount, 0, 0, 0); + }); } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index b50ca2517..6305c5a2b 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -171,9 +171,12 @@ private static Boolean readBooleanField(Object source, String fieldName) { return bool; } return null; - } catch (NoSuchFieldException ignored) { + } catch (NoSuchFieldException ex) { current = current.getSuperclass(); - } catch (Throwable ignored) { + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return null; } } @@ -445,6 +448,10 @@ default int getActiveLevel(Player p) { return 0; } + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } + int level = getLevel(p); if (level <= 0) { return 0; @@ -496,6 +503,9 @@ default int getLevel(Player p) { if (p == null) { return 0; } + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } if (!p.getClass().getSimpleName().equals("CraftPlayer")) { Adapt.verbose("Simple name: " + p.getClass().getSimpleName()); return 0; @@ -641,7 +651,7 @@ default void openGui(Player player, int page) { if (!getSkill().isEnabled()) { return; } - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { int targetPage = page; J.s(() -> openGui(player, targetPage)); return; diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java index 937d6f297..8833bea3f 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java @@ -117,13 +117,25 @@ public MainAdvancement(@NotNull AdvancementTab advancementTab, @NotNull String k @Override public void grant(@NotNull Player player, boolean giveRewards) { super.grant(player, giveRewards); - getAdvancementTab().showTab(player); + try { + getAdvancementTab().showTab(player); + } catch (Throwable t) { + Adapt.verbose("Failed to show advancement tab '" + getKey() + "' for " + player.getName() + ": " + + t.getClass().getSimpleName() + + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); + } } @Override public void revoke(@NotNull Player player) { super.revoke(player); - getAdvancementTab().hideTab(player); + try { + getAdvancementTab().hideTab(player); + } catch (Throwable t) { + Adapt.verbose("Failed to hide advancement tab '" + getKey() + "' for " + player.getName() + ": " + + t.getClass().getSimpleName() + + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); + } } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index 1b8d85d44..7eb1084ae 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -10,7 +10,6 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.scheduling.J; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.*; @@ -57,36 +56,35 @@ public void grant(AdaptPlayer player, String key, boolean toast) { return; } - J.s(() -> { - Player target = player.getPlayer(); - if (target == null || !target.isOnline()) { + J.runEntity(p, () -> { + if (!p.isOnline()) { return; } try { - advancement.grant(target, true); + advancement.grant(p, true); } catch (Throwable t) { if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + target.getName() + "."); + Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + p.getName() + "."); return; } - Adapt.warn("Failed to grant advancement '" + key + "' for " + target.getName() + ": " + t.getMessage()); + Adapt.warn("Failed to grant advancement '" + key + "' for " + p.getName() + ": " + summarizeThrowable(t)); } - }, 5); - if (toast) { - try { - advancement.displayToastToPlayer(p); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + p.getName() + "."); - return; - } + if (toast) { + try { + advancement.displayToastToPlayer(p); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + p.getName() + "."); + return; + } - Adapt.warn("Failed to display advancement toast '" + key + "' for " + p.getName() + ": " + t.getMessage()); + Adapt.warn("Failed to display advancement toast '" + key + "' for " + p.getName() + ": " + summarizeThrowable(t)); + } } - } + }, 5); } private boolean isUserNotLoadedError(Throwable throwable) { @@ -102,9 +100,41 @@ private boolean isUserNotLoadedError(Throwable throwable) { return false; } + private String summarizeThrowable(Throwable throwable) { + if (throwable == null) { + return "unknown"; + } + + Throwable root = throwable; + while (root.getCause() != null && root.getCause() != root) { + root = root.getCause(); + } + + StringBuilder summary = new StringBuilder(throwable.getClass().getSimpleName()); + appendMessage(summary, throwable.getMessage()); + + if (root != throwable) { + summary.append(" | cause=").append(root.getClass().getSimpleName()); + appendMessage(summary, root.getMessage()); + } + + return summary.toString(); + } + + private void appendMessage(StringBuilder builder, String message) { + if (message != null && !message.isBlank()) { + builder.append(": ").append(message); + } + } + public void unlockExisting(AdaptPlayer player) { if (!AdaptConfig.get().isAdvancements() || !enabled.get()) return; - J.s(() -> { + Player target = player.getPlayer(); + if (target == null || !target.isOnline()) { + return; + } + + J.runEntity(target, () -> { instance.getAdaptServer() .getSkillRegistry() .getSkills() diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java index 0ea087b4f..12c29f481 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java @@ -24,11 +24,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; public class BrewingManager implements Listener { private static final Map> recipes = Maps.newHashMap(); - private static final Map activeTasks = Maps.newHashMap(); + private static final Map activeTasks = new ConcurrentHashMap<>(); public static void registerRecipe(String adaptation, BrewingRecipe recipe) { recipes.putIfAbsent(recipe, Lists.newArrayList(adaptation)); @@ -56,35 +57,46 @@ public void onInventoryClick(InventoryClickEvent e) { Adapt.verbose("Brewing Stand Ingredient Clicked"); e.setCancelled(true); } - J.s(() -> { + Player clicker = (Player) e.getWhoClicked(); + J.runEntity(clicker, () -> { if (doTheThing) { inv.setIngredient(e.getCursor()); e.setCursor(null); } + BrewingStand stand = inv.getHolder(); - AdaptPlayer p = Adapt.instance.getAdaptServer().getPlayer((Player) e.getWhoClicked()); - Optional recipe = recipes.keySet().stream().filter(r -> BrewingTask.isValid(r, stand.getLocation())).findFirst(); + if (stand == null) { + return; + } + + Location standLocation = stand.getLocation(); + AdaptPlayer p = Adapt.instance.getAdaptServer().getPlayer(clicker); + Optional recipe = recipes.keySet().stream().filter(r -> BrewingTask.isValid(r, standLocation)).findFirst(); recipe.ifPresent(r -> { - if (activeTasks.containsKey(stand.getLocation())) { - BrewingTask t = activeTasks.get(stand.getLocation()); - if (!t.getRecipe().getId().equals(r.getId())) { - activeTasks.remove(stand.getLocation()).cancel(); + BrewingTask active = activeTasks.get(standLocation); + if (active != null) { + if (!active.getRecipe().getId().equals(r.getId())) { + activeTasks.remove(standLocation).cancel(); if (recipes.get(r).stream().noneMatch(p::hasAdaptation)) { return; } - activeTasks.put(stand.getLocation(), new BrewingTask(r, stand.getLocation())); + activeTasks.put(standLocation, new BrewingTask(r, standLocation)); } } else { if (recipes.get(r).stream().noneMatch(p::hasAdaptation)) { return; } - activeTasks.put(stand.getLocation(), new BrewingTask(r, stand.getLocation())); + activeTasks.put(standLocation, new BrewingTask(r, standLocation)); } }); - if (recipe.isEmpty() && activeTasks.containsKey(stand.getLocation())) { - activeTasks.remove(stand.getLocation()).cancel(); + + if (recipe.isEmpty()) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); + } } - }); + }, 1); } @EventHandler diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java index 827bc9576..884c335b1 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java @@ -1,7 +1,7 @@ package art.arcane.adapt.api.potion; -import art.arcane.adapt.Adapt; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import lombok.Getter; import org.bukkit.Location; import org.bukkit.Material; @@ -10,9 +10,8 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; -public class BrewingTask extends BukkitRunnable { +public class BrewingTask implements Runnable { private static final int DEFAULT_BREW_TIME = 400; @@ -21,6 +20,7 @@ public class BrewingTask extends BukkitRunnable { private final Location location; private int brewTime; + private volatile boolean cancelled; public BrewingTask(BrewingRecipe recipe, Location loc) { this.recipe = recipe; @@ -39,7 +39,7 @@ public BrewingTask(BrewingRecipe recipe, Location loc) { block.setBrewingTime(DEFAULT_BREW_TIME); block.update(true); - runTaskTimer(Adapt.instance, 0L, 1L); + J.runAt(location, this::run); } public static ItemStack decrease(ItemStack source, int amount) { @@ -74,6 +74,10 @@ public static boolean isValid(BrewingRecipe recipe, Location loc) { @Override public void run() { + if (cancelled) { + return; + } + BrewingStand block = (BrewingStand) this.location.getBlock().getState(); BrewerInventory inventory = block.getInventory(); if (brewTime <= 0) { @@ -97,6 +101,11 @@ public void run() { brewTime--; block.setBrewingTime(getRemainingTime()); block.update(true); + J.runAt(location, this::run, 1); + } + + public void cancel() { + cancelled = true; } private int getRemainingTime() { diff --git a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index 9718b82dc..d5171d271 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -174,8 +174,10 @@ private Number getNumericField(T source, String fieldName) { if (value instanceof Number number) { return number; } - } catch (Throwable ignored) { - Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName()); + } catch (Throwable ex) { + Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } return null; @@ -186,7 +188,7 @@ private Field getField(Class type, String name) { while (current != null) { try { return current.getDeclaredField(name); - } catch (NoSuchFieldException ignored) { + } catch (NoSuchFieldException ex) { current = current.getSuperclass(); } } @@ -240,39 +242,74 @@ protected boolean shouldReturnForPlayer(Player p) { if (p == null) { return true; } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return true; + } + Adapt.verbose("Checking " + p.getName() + " for " + getName()); return AdaptationGate.shouldSkipPlayer(p, this, getPlayer(p) != null); - } catch (Exception ignored) { + } catch (Exception ex) { + Adapt.verbose("Failed shouldReturnForPlayer check for " + (p == null ? "null" : p.getName()) + + " in skill " + getName() + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return true; } } protected void shouldReturnForPlayer(Player p, Runnable r) { try { + if (p == null || r == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, () -> shouldReturnForPlayer(p, r)); + return; + } + if (shouldReturnForPlayer(p)) { return; } r.run(); - } catch (Exception ignored) { + } catch (Exception ex) { + Adapt.verbose("Failed guarded player runnable for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } } protected void shouldReturnForPlayer(Player p, Cancellable c, Runnable r) { try { + if (p == null || c == null || r == null) { + return; + } + if (c.isCancelled()) { return; } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return; + } + if (shouldReturnForPlayer(p)) { return; } r.run(); - } catch (Exception ignored) { + } catch (Exception ex) { + Adapt.verbose("Failed guarded cancellable player runnable for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } } protected boolean shouldReturnForWorld(World world, Skill skill) { try { return AdaptationGate.shouldSkipWorld(world, skill); - } catch (Exception ignored) { + } catch (Exception ex) { + Adapt.verbose("Failed shouldReturnForWorld check for skill " + (skill == null ? "null" : skill.getName()) + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return true; } } diff --git a/src/main/java/art/arcane/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java index b6a9898b7..25fdf4b8a 100644 --- a/src/main/java/art/arcane/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -154,9 +154,12 @@ private static Boolean readBooleanField(Object source, String fieldName) { return bool; } return null; - } catch (NoSuchFieldException ignored) { + } catch (NoSuchFieldException ex) { current = current.getSuperclass(); - } catch (Throwable ignored) { + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return null; } } @@ -351,7 +354,7 @@ default void openGui(Player player, int page) { if (!player.getClass().getSimpleName().equals("CraftPlayer")) { return; } - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { int targetPage = page; J.s(() -> openGui(player, targetPage)); return; diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index 6f5ff1ab3..b3eb7982d 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -30,6 +30,7 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.content.skill.*; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; import art.arcane.adapt.util.common.misc.SoundPlayer; @@ -51,7 +52,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.scheduler.BukkitTask; import java.util.ArrayDeque; import java.util.ArrayList; @@ -72,8 +72,9 @@ public class SkillRegistry extends TickedObject { private final KMap>> skillTypes = new KMap<>(); private final Map> adaptationRecipeIndex = new ConcurrentHashMap<>(); private final Deque> deferredBootstrapRecipeRegistration = new ArrayDeque<>(); - private volatile BukkitTask deferredBootstrapRecipeTask; + private volatile boolean deferredBootstrapRecipeTaskScheduled; private volatile boolean bootstrapLoading = true; + private volatile boolean foliaRecipeRegistrationWaitWarned; public SkillRegistry() { super("registry", UUID.randomUUID() + "-sk", 1250); @@ -394,6 +395,19 @@ private void unregisterRecipes(Skill s) { } private void registerRecipes(Skill s) { + if (s == null) { + return; + } + + if (shouldDelayRecipeRegistrationForFolia()) { + enqueueDeferredRecipeRegistration(s); + return; + } + + registerRecipesNow(s); + } + + private void registerRecipesNow(Skill s) { if (!s.isEnabled()) { return; } @@ -410,13 +424,27 @@ private void registerRecipes(Skill s) { }); } + private boolean shouldDelayRecipeRegistrationForFolia() { + if (!J.isFoliaThreading()) { + return false; + } + + return !Bukkit.getOnlinePlayers().isEmpty(); + } + + private synchronized void enqueueDeferredRecipeRegistration(Skill skill) { + if (skill == null) { + return; + } + + deferredBootstrapRecipeRegistration.remove(skill); + deferredBootstrapRecipeRegistration.addLast(skill); + scheduleDeferredBootstrapRecipeRegistration(); + } + @Override public void unregister() { - BukkitTask pendingTask = deferredBootstrapRecipeTask; - if (pendingTask != null) { - pendingTask.cancel(); - deferredBootstrapRecipeTask = null; - } + deferredBootstrapRecipeTaskScheduled = false; deferredBootstrapRecipeRegistration.clear(); for (Skill i : knownSkills.v()) { i.unregister(); @@ -473,34 +501,66 @@ private void removeAdaptationRecipeIndex(AdaptRecipe recipe, Adaptation adapt } private synchronized void scheduleDeferredBootstrapRecipeRegistration() { - if (deferredBootstrapRecipeRegistration.isEmpty() || deferredBootstrapRecipeTask != null) { + if (deferredBootstrapRecipeRegistration.isEmpty() || deferredBootstrapRecipeTaskScheduled) { return; } + deferredBootstrapRecipeTaskScheduled = true; Adapt.info("Deferring recipe registration for " + deferredBootstrapRecipeRegistration.size() + " skills."); - deferredBootstrapRecipeTask = Bukkit.getScheduler().runTaskTimer(Adapt.instance, () -> { + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); + } + + private void runDeferredBootstrapRecipeRegistrationTick() { + if (shouldDelayRecipeRegistrationForFolia()) { + if (!foliaRecipeRegistrationWaitWarned) { + foliaRecipeRegistrationWaitWarned = true; + Adapt.warn("Delaying Adapt recipe registration on Folia while players are online to avoid unsafe recipe reload races. Recipes will register automatically when the server has no online players."); + } + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); + return; + } + + foliaRecipeRegistrationWaitWarned = false; + boolean complete; + + synchronized (this) { + if (!deferredBootstrapRecipeTaskScheduled) { + return; + } + int processed = 0; long started = System.currentTimeMillis(); while (processed < DEFERRED_SKILLS_PER_TICK) { + if (shouldDelayRecipeRegistrationForFolia()) { + break; + } + Skill skill = deferredBootstrapRecipeRegistration.pollFirst(); if (skill == null) { break; } - registerRecipes(skill); + registerRecipesNow(skill); processed++; if (System.currentTimeMillis() - started > 8L) { break; } } - if (deferredBootstrapRecipeRegistration.isEmpty()) { - BukkitTask task = deferredBootstrapRecipeTask; - deferredBootstrapRecipeTask = null; - if (task != null) { - task.cancel(); - } - Adapt.info("Deferred recipe registration completed."); + complete = deferredBootstrapRecipeRegistration.isEmpty(); + if (complete) { + deferredBootstrapRecipeTaskScheduled = false; } - }, 1L, 1L); + } + + if (complete) { + Adapt.info("Deferred recipe registration completed."); + return; + } + + if (shouldDelayRecipeRegistrationForFolia()) { + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); + } else { + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); + } } } diff --git a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java index 8540c3403..98aa4d796 100644 --- a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java +++ b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java @@ -26,6 +26,7 @@ import org.bukkit.event.Listener; import java.lang.reflect.Method; +import java.util.Locale; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -35,6 +36,7 @@ public abstract class TickedObject implements Ticked, Listener { private static final Set LISTENER_INTROSPECTION_WARNED = ConcurrentHashMap.newKeySet(); + private static final Set FOLIA_TICK_VIOLATION_WARNED = ConcurrentHashMap.newKeySet(); private final AtomicLong lastTick; private final AtomicLong interval; @@ -118,7 +120,7 @@ public void setInterval(long ms) { @Override public void tick() { - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { if (pendingSyncTick.compareAndSet(false, true)) { J.s(() -> { try { @@ -142,7 +144,21 @@ public void tick() { lastTick.set(M.ms()); burst.decrementAndGet(); - onTick(); + try { + onTick(); + } catch (IllegalStateException ex) { + if (J.isFoliaThreading() && isFoliaThreadOwnershipViolation(ex)) { + warnFoliaTickViolation(ex); + return; + } + throw ex; + } catch (NullPointerException ex) { + if (J.isFoliaThreading() && isFoliaTransientWorldStateNpe(ex)) { + warnFoliaTickViolation(ex); + return; + } + throw ex; + } } public abstract void onTick(); @@ -252,4 +268,37 @@ private static void warnListenerIntrospectionFailure(Class type, Throwable er Adapt.warn("Skipping listener registration for " + type.getName() + " due to missing/incompatible event class: " + error.getClass().getSimpleName() + (error.getMessage() == null ? "" : " (" + error.getMessage() + ")")); } } + + private boolean isFoliaThreadOwnershipViolation(Throwable throwable) { + if (throwable == null) { + return false; + } + + String message = throwable.getMessage(); + if (message == null) { + return false; + } + + String lower = message.toLowerCase(Locale.ROOT); + return lower.contains("thread failed main thread check") + || lower.contains("cannot read world asynchronously") + || lower.contains("accessing entity state off owning region"); + } + + private boolean isFoliaTransientWorldStateNpe(NullPointerException throwable) { + if (throwable == null || throwable.getMessage() == null) { + return false; + } + + String lower = throwable.getMessage().toLowerCase(Locale.ROOT); + return lower.contains("getcurrentworlddata"); + } + + private void warnFoliaTickViolation(Throwable throwable) { + String message = throwable == null || throwable.getMessage() == null ? throwable.getClass().getSimpleName() : throwable.getMessage(); + String key = getClass().getName() + ":" + throwable.getClass().getName() + ":" + message; + if (FOLIA_TICK_VIOLATION_WARNED.add(key)) { + Adapt.warn("Suppressed unsafe Folia tick execution in " + getClass().getName() + ": " + message); + } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java index 08420998f..99a341ae5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java @@ -192,7 +192,7 @@ private void triggerRollPose(Player p, int level) { proneUntilMillis.put(id, until); p.setSwimming(true); - J.s(() -> { + J.runEntity(p, () -> { if (!p.isOnline() || p.isDead()) { proneUntilMillis.remove(id); return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java index 3f7694de3..d0586d273 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java @@ -34,6 +34,7 @@ import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -385,7 +386,7 @@ private static boolean hasEnoughSpace(Player player, int targetY) { private void teleportPlayer(Player p, Location l) { playTeleportEffects(p); - p.teleport(l); + J.teleport(p, l); SoundPlayer.of(p.getWorld()).play(p, Sound.ENTITY_ENDERMAN_TELEPORT, SOUND_VOLUME, SOUND_PITCH); playTeleportEffects(p); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java index aad7276fc..7ca929bc4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java @@ -252,7 +252,7 @@ public boolean addFoundation(Block block) { return false; - J.s(() -> { + J.runAt(block.getLocation(), () -> { block.setBlockData(BLOCK); activeBlocks.add(block); }); @@ -263,7 +263,7 @@ public boolean addFoundation(Block block) { vfxCuboidOutline(block, Particle.REVERSE_PORTAL); vfxCuboidOutline(block, Particle.ASH); } - J.s(() -> removeFoundation(block), 3 * 20); + J.runAt(block.getLocation(), () -> removeFoundation(block), 3 * 20); return true; } @@ -272,7 +272,7 @@ public void removeFoundation(Block block) { return; } - J.s(() -> { + J.runAt(block.getLocation(), () -> { block.setBlockData(AIR); activeBlocks.remove(block); SoundPlayer spw = SoundPlayer.of(block.getWorld()); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index 81dd6dab1..1210cb414 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -247,7 +247,7 @@ private void linkTorch(Player p, Location l) { private void triggerPulse(Player p, ItemStack item) { Location l = BoundRedstoneTorch.getLocation(item); if (isBound(item) && l != null) { - loadChunkAsync(l, chunk -> { + loadChunkAsync(l, chunk -> J.runAt(l, () -> { Block b = l.getBlock(); BlockData data = b.getBlockData(); if (data instanceof AnaloguePowerable redBlock && b.getType().equals(Material.TARGET)) { @@ -257,7 +257,7 @@ private void triggerPulse(Player p, ItemStack item) { vfxCuboidOutline(l.getBlock(), l.getBlock(), Color.RED, 1); b.setBlockData(redBlock); getPlayer(p).getData().addStat("architect.wireless-redstone.pulses", 1); - J.s(() -> { + J.runAt(l, () -> { redBlock.setPower(0); b.setBlockData(redBlock); }, 2); @@ -265,7 +265,7 @@ private void triggerPulse(Player p, ItemStack item) { SoundPlayer sp = SoundPlayer.of(p); sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); } - }); + })); } } @@ -287,9 +287,9 @@ public void onTick() { ItemStack offhand = p.getInventory().getItemInOffHand(); if ((isRedstoneTorch(hand) && BoundRedstoneTorch.hasItemData(hand)) || ( isRedstoneTorch(offhand) && BoundRedstoneTorch.hasItemData(offhand))) { - J.s(() -> updatePlayerCooldown(p, false)); + J.runEntity(p, () -> updatePlayerCooldown(p, false)); } else { - J.s(() -> updatePlayerCooldown(p, true)); + J.runEntity(p, () -> updatePlayerCooldown(p, true)); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index 1036d97e9..015ace7bc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -148,7 +148,7 @@ public void on(BlockBreakEvent e) { } int leavesBroken = blockMap.size(); - J.s(() -> { + J.runEntity(p, () -> { for (Location l : blockMap.keySet()) { Block b = block.getWorld().getBlockAt(l); PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index 02c19cf9d..fd20be2ad 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -159,7 +159,7 @@ public void on(BlockBreakEvent e) { } int logsVeinmined = blockMap.size(); - J.s(() -> { + J.runEntity(p, () -> { for (Block blocks : blockMap) { PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); PlayerAdaptation adaptation = line != null ? line.getAdaptation("axe-drop-to-inventory") : null; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java index 47198fc87..37caadfef 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java @@ -30,6 +30,7 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -166,7 +167,7 @@ private void reflectProjectile(Player defender, Projectile projectile, int level reflected = defender.getEyeLocation().getDirection().normalize().multiply(getConfig().fallbackReflectedSpeed); } - projectile.teleport(defender.getEyeLocation().add(defender.getEyeLocation().getDirection().multiply(0.55))); + J.teleport(projectile, defender.getEyeLocation().add(defender.getEyeLocation().getDirection().multiply(0.55))); projectile.setShooter(defender); projectile.setVelocity(reflected); projectile.setMetadata(REFLECTED_META, new FixedMetadataValue(Adapt.instance, true)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java index 07cff66b5..0d5a943ce 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java @@ -131,7 +131,7 @@ public void on(PlayerMoveEvent e) { if (isChestplate(chest)) { return; } - J.s(() -> p.getInventory().setChestplate(multiarmor.nextChestplate(chest))); + J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextChestplate(chest))); cooldowns.put(p, System.currentTimeMillis()); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); spw.play(p.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 0.5f, 0.77f); @@ -141,7 +141,7 @@ public void on(PlayerMoveEvent e) { if (isElytra(chest)) { return; } - J.s(() -> p.getInventory().setChestplate(multiarmor.nextElytra(chest))); + J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextElytra(chest))); cooldowns.put(p, System.currentTimeMillis()); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); spw.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.5f, 0.77f); @@ -184,7 +184,7 @@ public void on(PlayerDropItemEvent e) { drops.set(drops.indexOf(i), i); } - J.s(() -> { + J.runEntity(p, () -> { sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); for (ItemStack i : drops) { p.getWorld().dropItem(p.getLocation(), i); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java index 7165659b4..4a564a444 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java @@ -47,9 +47,8 @@ import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryType; -import java.util.HashMap; -import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import art.arcane.adapt.util.common.format.C; @@ -61,7 +60,7 @@ public class BrewingSuperHeated extends SimpleAdaptation { private static final int MAX_CHECKS_BEFORE_REMOVE = 20; - private final Map activeStands = new HashMap<>(); + private final Map activeStands = new ConcurrentHashMap<>(); public BrewingSuperHeated() { super("brewing-super-heated"); @@ -113,11 +112,11 @@ public void on(InventoryMoveItemEvent e) { if (e.isCancelled()) { return; } - J.s(() -> { - if (e.getDestination().getType().equals(InventoryType.BREWING)) { - activeStands.put(e.getDestination().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); - } - }); + if (!e.getDestination().getType().equals(InventoryType.BREWING) || e.getDestination().getLocation() == null) { + return; + } + + activeStands.put(e.getDestination().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); } @EventHandler(priority = EventPriority.HIGHEST) @@ -131,11 +130,9 @@ public void on(BrewEvent e) { getServer().peekData(owner.getOwner()).addStat("brewing.super-heated.brews-accelerated", 1); } } - J.s(() -> { - if (((BrewingStand) e.getBlock().getState()).getBrewingTime() > 0) { - activeStands.put(e.getBlock(), MAX_CHECKS_BEFORE_REMOVE); - } - }); + if (((BrewingStand) e.getBlock().getState()).getBrewingTime() > 0) { + activeStands.put(e.getBlock(), MAX_CHECKS_BEFORE_REMOVE); + } } @EventHandler(priority = EventPriority.HIGHEST) @@ -155,49 +152,59 @@ public void onTick() { return; } - Iterator it = activeStands.keySet().iterator(); - - J.s(() -> { - while (it.hasNext()) { - BlockState s = it.next().getState(); - - if (s instanceof BrewingStand b) { - if (b.getBrewingTime() <= 0) { - J.s(() -> { - BrewingStand bb = (BrewingStand) s.getBlock().getState(); - if (bb.getBrewingTime() <= 0) { - if (activeStands.get(b.getBlock()) == 0) { - activeStands.remove(b.getBlock()); - } - if (activeStands.containsKey(b.getBlock())) { - activeStands.put(b.getBlock(), activeStands.get(b.getBlock()) - 1); - } - } - }); - continue; - } - - BrewingStandOwner owner = WorldData.of(b.getWorld()).get(b.getBlock(), BrewingStandOwner.class); - - if (owner == null) { - it.remove(); - continue; - } - - PlayerData p = getServer().peekData(owner.getOwner()); - - PlayerSkillLine line = p.getSkillLineNullable(getSkill().getName()); - PlayerAdaptation adaptation = line != null ? line.getAdaptation(getName()) : null; - if (adaptation != null && adaptation.getLevel() > 0) { - updateHeat(b, getLevelPercent(adaptation.getLevel())); - } else { - it.remove(); - } + for (Block block : activeStands.keySet()) { + if (block == null) { + continue; + } + + J.runAt(block.getLocation(), () -> tickStand(block)); + } + } + + private void tickStand(Block block) { + BlockState state = block.getState(); + if (!(state instanceof BrewingStand brewingStand)) { + activeStands.remove(block); + return; + } + + if (brewingStand.getBrewingTime() <= 0) { + BrewingStand current = (BrewingStand) block.getState(); + if (current.getBrewingTime() <= 0) { + Integer remainingChecks = activeStands.get(block); + if (remainingChecks == null) { + return; + } + + if (remainingChecks <= 0) { + activeStands.remove(block); } else { - it.remove(); + activeStands.put(block, remainingChecks - 1); } } - }); + return; + } + + BrewingStandOwner owner = WorldData.of(brewingStand.getWorld()).get(block, BrewingStandOwner.class); + if (owner == null) { + activeStands.remove(block); + return; + } + + PlayerData playerData = getServer().peekData(owner.getOwner()); + if (playerData == null) { + activeStands.remove(block); + return; + } + + PlayerSkillLine line = playerData.getSkillLineNullable(getSkill().getName()); + PlayerAdaptation adaptation = line != null ? line.getAdaptation(getName()) : null; + if (adaptation == null || adaptation.getLevel() <= 0) { + activeStands.remove(block); + return; + } + + updateHeat(brewingStand, getLevelPercent(adaptation.getLevel())); } private void updateHeat(BrewingStand b, double factor) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index 6a91f8096..ef1db7b35 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -58,13 +58,14 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import art.arcane.adapt.util.common.inventorygui.Window; import art.arcane.adapt.util.reflect.registries.Particles; import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; public class ChronosInstantRecall extends SimpleAdaptation { private static final EnumSet RECALL_ACTIONS = EnumSet.of( @@ -752,7 +753,7 @@ private void armDoubleJump(Player p, UUID id) { int triggerWindowMillis = Math.max(150, getConfig().doubleJumpWindowMillis); jumpArmUntil.put(id, M.ms() + triggerWindowMillis); p.setAllowFlight(true); - J.s(() -> { + J.runEntity(p, () -> { if (!p.isOnline()) { return; } @@ -890,127 +891,136 @@ && getConfig().rewindUseClientCamera final boolean initialClientCamera = allowClientCamera; final ArmorStand initialCameraAnchor = cameraAnchor; - new BukkitRunnable() { - int step = 0; - Location lastLoc = p.getLocation().clone(); - final boolean[] clientCameraActive = new boolean[]{initialClientCamera}; - final ArmorStand[] cameraRef = new ArmorStand[]{initialCameraAnchor}; - - private void cleanupVisualState(boolean restoreGameMode) { - if (cameraRef[0] != null) { - Entity anchorEntity = cameraRef[0]; - cameraRef[0] = null; - if (anchorEntity.isValid()) { - anchorEntity.remove(); - } + int[] step = {0}; + Location[] lastLoc = {p.getLocation().clone()}; + boolean[] clientCameraActive = {initialClientCamera}; + ArmorStand[] cameraRef = {initialCameraAnchor}; + + Consumer cleanupVisualState = restoreGameMode -> { + if (cameraRef[0] != null) { + Entity anchorEntity = cameraRef[0]; + cameraRef[0] = null; + if (anchorEntity.isValid()) { + anchorEntity.remove(); } + } - if (temporarySpectator) { - p.setSpectatorTarget(null); - if (restoreGameMode && p.getGameMode() == GameMode.SPECTATOR) { - p.setGameMode(originalGameMode); - p.setFlying(false); - } + if (temporarySpectator) { + p.setSpectatorTarget(null); + if (restoreGameMode && p.getGameMode() == GameMode.SPECTATOR) { + p.setGameMode(originalGameMode); + p.setFlying(false); } } + }; - @Override - public void run() { - if (!p.isOnline() || p.isDead()) { - rewinding.remove(id); - cleanupVisualState(true); - cancel(); - return; - } + Runnable[] rewindTask = new Runnable[1]; + rewindTask[0] = () -> { + if (!p.isOnline() || p.isDead()) { + rewinding.remove(id); + cleanupVisualState.accept(true); + return; + } - float progress = animationTicks <= 1 ? 1f : (float) step / (float) (animationTicks - 1); - int index = Math.min(animationPath.size() - 1, step); - Snapshot snapshot = animationPath.get(index); - Location destination = toLocation(snapshot, p.getWorld()); + float progress = animationTicks <= 1 ? 1f : (float) step[0] / (float) (animationTicks - 1); + int index = Math.min(animationPath.size() - 1, step[0]); + Snapshot snapshot = animationPath.get(index); + Location destination = toLocation(snapshot, p.getWorld()); - if (getConfig().showRewindTraceParticles && lastLoc.getWorld() != null && lastLoc.getWorld().equals(destination.getWorld())) { - vfxParticleLine(lastLoc.clone().add(0, 1, 0), destination.clone().add(0, 1, 0), Particle.REVERSE_PORTAL, - Math.max(4, getConfig().rewindTracePoints), 1, 0.08D, 0.08D, 0.08D, 0D, null, true, - l -> l.getBlock().isPassable()); - } + if (getConfig().showRewindTraceParticles && lastLoc[0].getWorld() != null && lastLoc[0].getWorld().equals(destination.getWorld())) { + Predicate traceFilter = J.isFoliaThreading() ? null : l -> l.getBlock().isPassable(); + vfxParticleLine(lastLoc[0].clone().add(0, 1, 0), destination.clone().add(0, 1, 0), Particle.REVERSE_PORTAL, + Math.max(4, getConfig().rewindTracePoints), 1, 0.08D, 0.08D, 0.08D, 0D, null, true, + traceFilter); + } - boolean movedClient = false; - if (clientCameraActive[0] && cameraRef[0] != null && cameraRef[0].isValid()) { - Entity target = p.getSpectatorTarget(); - if (target == null || !target.getUniqueId().equals(cameraRef[0].getUniqueId())) { - p.setSpectatorTarget(cameraRef[0]); - target = p.getSpectatorTarget(); - } - - if (target != null - && target.getUniqueId().equals(cameraRef[0].getUniqueId()) - && destination.getWorld() != null - && destination.getWorld().equals(cameraRef[0].getWorld())) { - cameraRef[0].teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN); - movedClient = true; - } else { - clientCameraActive[0] = false; - } + boolean movedClient = false; + if (clientCameraActive[0] && cameraRef[0] != null && cameraRef[0].isValid()) { + Entity target = p.getSpectatorTarget(); + if (target == null || !target.getUniqueId().equals(cameraRef[0].getUniqueId())) { + p.setSpectatorTarget(cameraRef[0]); + target = p.getSpectatorTarget(); } - if (!movedClient) { - p.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + if (target != null + && target.getUniqueId().equals(cameraRef[0].getUniqueId()) + && destination.getWorld() != null + && destination.getWorld().equals(cameraRef[0].getWorld())) { + J.teleport(cameraRef[0], destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + movedClient = true; + } else { + clientCameraActive[0] = false; } + } + + if (!movedClient) { + J.teleport(p, destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + } + + if (!temporarySpectator || step[0] >= animationPath.size() - 1) { + applySnapshotState(p, snapshot); + } + + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindStep(p, progress); + } - if (!temporarySpectator || step >= animationPath.size() - 1) { - applySnapshotState(p, snapshot); + lastLoc[0] = destination; + step[0]++; + + if (step[0] >= animationTicks) { + cleanupVisualState.accept(true); + Location finalDestination = toLocation(finalSnapshot, p.getWorld()); + if (finalDestination.getWorld() != null) { + J.teleport(p, finalDestination, PlayerTeleportEvent.TeleportCause.PLUGIN); } + applySnapshotState(p, finalSnapshot); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 26, 0.25, 0.35, 0.25, 0.01); + } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.ITEM, p.getLocation().add(0, 1, 0), 18, 0.30, 0.30, 0.30, 0.01, new ItemStack(Material.CLOCK)); + } if (getConfig().playClockSounds) { - ChronosSoundFX.playRewindStep(p, progress); + ChronosSoundFX.playRewindFinish(p); } - - lastLoc = destination; - step++; - - if (step >= animationTicks) { - cleanupVisualState(true); - Location finalDestination = toLocation(finalSnapshot, p.getWorld()); - if (finalDestination.getWorld() != null) { - p.teleport(finalDestination, PlayerTeleportEvent.TeleportCause.PLUGIN); - } - applySnapshotState(p, finalSnapshot); - - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 26, 0.25, 0.35, 0.25, 0.01); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.ITEM, p.getLocation().add(0, 1, 0), 18, 0.30, 0.30, 0.30, 0.01, new ItemStack(Material.CLOCK)); - } - if (getConfig().playClockSounds) { - ChronosSoundFX.playRewindFinish(p); - } - rewinding.remove(id); - getPlayer(p).getData().addStat("chronos.instant-recall.recalls", 1); - if (healthBeforeRecall <= 4 && healthAfterRecall >= 16 - && AdaptConfig.get().isAdvancements() - && !getPlayer(p).getData().isGranted("challenge_chronos_recall_cheat_death")) { - getPlayer(p).getAdvancementHandler().grant("challenge_chronos_recall_cheat_death"); - } - long awardAt = M.ms(); - double xpGain = computeRecallXPGain(id, level, xpContext, awardAt); - if (xpGain > 0D) { - xp(p, p.getLocation(), xpGain); - recallXpStamps.put(id, new RecallXPFarmStamp( - awardAt, - xpContext.fromWorld(), - xpContext.fromX(), - xpContext.fromY(), - xpContext.fromZ(), - xpContext.toWorld(), - xpContext.toX(), - xpContext.toY(), - xpContext.toZ())); - } - cancel(); + rewinding.remove(id); + getPlayer(p).getData().addStat("chronos.instant-recall.recalls", 1); + if (healthBeforeRecall <= 4 && healthAfterRecall >= 16 + && AdaptConfig.get().isAdvancements() + && !getPlayer(p).getData().isGranted("challenge_chronos_recall_cheat_death")) { + getPlayer(p).getAdvancementHandler().grant("challenge_chronos_recall_cheat_death"); + } + long awardAt = M.ms(); + double xpGain = computeRecallXPGain(id, level, xpContext, awardAt); + if (xpGain > 0D) { + xp(p, p.getLocation(), xpGain); + recallXpStamps.put(id, new RecallXPFarmStamp( + awardAt, + xpContext.fromWorld(), + xpContext.fromX(), + xpContext.fromY(), + xpContext.fromZ(), + xpContext.toWorld(), + xpContext.toX(), + xpContext.toY(), + xpContext.toZ())); } + return; + } + + if (J.isFoliaThreading()) { + J.runEntity(p, rewindTask[0], 1); + } else { + J.s(rewindTask[0], 1); } - }.runTaskTimer(Adapt.instance, 0, 1); + }; + if (J.isFoliaThreading()) { + J.runEntity(p, rewindTask[0]); + } else { + J.s(rewindTask[0]); + } } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java index efc4c0ecf..df98b8da0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java @@ -46,10 +46,10 @@ private static void play(Location location, Sound sound, float volume, float pit } }; - if (Bukkit.isPrimaryThread()) { + if (J.isPrimaryThread()) { playTask.run(); } else { - J.s(playTask); + J.runAt(at, playTask); } } @@ -69,12 +69,12 @@ private static void playLater(Location location, Sound sound, float volume, floa } }; - if (delayTicks <= 0 && Bukkit.isPrimaryThread()) { + if (delayTicks <= 0 && J.isPrimaryThread()) { playTask.run(); return; } - J.s(playTask, Math.max(0, delayTicks)); + J.runAt(at, playTask, Math.max(0, delayTicks)); } private static void playOnPlayer(Player player, Sound sound, float volume, float pitch) { @@ -96,10 +96,10 @@ private static void playOnPlayer(Player player, Sound sound, float volume, float } }; - if (Bukkit.isPrimaryThread()) { + if (J.isPrimaryThread()) { playTask.run(); } else { - J.s(playTask); + J.runEntity(player, playTask); } } @@ -122,12 +122,12 @@ private static void playOnPlayerLater(Player player, Sound sound, float volume, } }; - if (delayTicks <= 0 && Bukkit.isPrimaryThread()) { + if (delayTicks <= 0 && J.isPrimaryThread()) { playTask.run(); return; } - J.s(playTask, Math.max(0, delayTicks)); + J.runEntity(player, playTask, Math.max(0, delayTicks)); } public static void playClockReject(Player p) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index d82438f70..055b2f5bc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -104,7 +104,7 @@ public void on(ProjectileLaunchEvent e) { Projectile original = e.getEntity(); Vector originalVelocity = original.getVelocity().clone(); int delay = getEchoDelayTicks(level); - J.s(() -> spawnEcho(p, echoType, originalVelocity, level), delay); + J.runEntity(p, () -> spawnEcho(p, echoType, originalVelocity, level), delay); } private void spawnEcho(Player p, EchoType type, Vector velocity, int level) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index 65201ff4e..c152163c1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -511,7 +511,7 @@ private void spawnFieldSphere(TemporalField field, long now) { @Override public void onTick() { - if (Bukkit.isPrimaryThread()) { + if (J.isPrimaryThread()) { onTickSync(); return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java index 16f0935ef..1d488ffc8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java @@ -169,11 +169,11 @@ public void on(PlayerQuitEvent event) { @Override public void onTick() { - J.s(() -> active.forEach((p, lvl) -> { + active.forEach((p, lvl) -> { var player = Bukkit.getPlayer(p); if (player == null) return; - player.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, lvl, true, true)); - })); + J.runEntity(player, () -> player.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, lvl, true, true))); + }); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index e7ee98e02..1db8f2d7f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -144,7 +144,7 @@ public void on(EntityDamageByEntityEvent e) { if (!validateTool(hand)) { return; } - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); SoundPlayer spw = SoundPlayer.of(p.getWorld()); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); if (inHand != null && inHand.hasDamage()) { @@ -199,10 +199,10 @@ public void on(PlayerInteractEvent e) { SoundPlayer spw = SoundPlayer.of(p.getWorld()); if (ItemListings.farmable.contains(block.getType())) { if (isShovel(hand)) { - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextHoe(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextHoe(hand))); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); } else { - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); } if (imHand != null && imHand.hasDamage()) { @@ -212,7 +212,7 @@ public void on(PlayerInteractEvent e) { } } } else if (ItemListings.burnable.contains(block.getType())) { - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextFnS(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextFnS(hand))); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); if (imHand != null && imHand.hasDamage()) { if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { @@ -259,7 +259,7 @@ public void on(PlayerDropItemEvent e) { drops.set(drops.indexOf(i), i); } - J.s(() -> { + J.runEntity(p, () -> { SoundPlayer sp = SoundPlayer.of(p); sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); for (ItemStack i : drops) { @@ -289,7 +289,7 @@ public void on(BlockDamageEvent e) { if (ItemListings.getAxePreference().contains(b.getType())) { if (!isAxe(hand)) { Adapt.verbose("Omnitool for " + p.getName() + " changed to axe"); - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextAxe(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextAxe(hand))); itemDelegate(e, hand, imHand); } else { Adapt.verbose("Omnitool for " + p.getName() + " is already axe"); @@ -297,7 +297,7 @@ public void on(BlockDamageEvent e) { } else if (ItemListings.getShovelPreference().contains(b.getType())) { if (!isShovel(hand)) { Adapt.verbose("Omnitool for " + p.getName() + " changed to shovel"); - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); itemDelegate(e, hand, imHand); } else { Adapt.verbose("Omnitool for " + p.getName() + " is already shovel"); @@ -305,7 +305,7 @@ public void on(BlockDamageEvent e) { } else if (ItemListings.getSwordPreference().contains(b.getType())) { if (!isSword(hand)) { Adapt.verbose("Omnitool for " + p.getName() + " changed to sword"); - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); itemDelegate(e, hand, imHand); } else { Adapt.verbose("Omnitool for " + p.getName() + " is already sword"); @@ -313,7 +313,7 @@ public void on(BlockDamageEvent e) { } else { // Default to pickaxe if (!isPickaxe(hand)) { Adapt.verbose("Omnitool for " + p.getName() + " changed to pickaxe"); - J.s(() -> p.getInventory().setItemInMainHand(omniTool.nextPickaxe(hand))); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextPickaxe(hand))); itemDelegate(e, hand, imHand); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index 85feece20..c075edc08 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -177,7 +177,7 @@ private void searchForOres(Player p, int radius) { throw new RuntimeException(e); } - J.s(() -> { + J.runEntity(slime, () -> { try { glowingEntities.unsetGlowing(slime, p); } catch (ReflectiveOperationException e) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java index 810c844fd..e0bdcd41b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java @@ -134,7 +134,7 @@ public void onTick() { AdaptPlayer player = getPlayer(p); if (ab.getMaximumAge() > ab.getAge() && player.canConsumeFood(foodCost, 10)) { while (add-- > 0) { - J.s(() -> { + J.runEntity(p, () -> { if (!p.isOnline() || !player.consumeFood(foodCost, 10) || !(a.getBlockData() instanceof Ageable aab) diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index 684955f43..b9f551c86 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -143,7 +143,7 @@ public void on(PlayerInteractEvent e) { c = c.expand(Cuboid.CuboidDirection.West, Math.round(getRadius(lvl))); for (Block i : c) { - J.s(() -> hit(p, i), M.irand(1, 6)); + J.runEntity(p, () -> hit(p, i), M.irand(1, 6)); } spw.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 1f, 0.66f); spw.play(p.getLocation(), Sound.BLOCK_BAMBOO_SAPLING_BREAK, 1f, 0.66f); @@ -177,14 +177,14 @@ private void hit(Player p, Block b) { } } aa.setAge(0); - J.s(() -> b.setBlockData(aa, true)); + J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); } else { p.breakBlock(b); } aa.setAge(0); - J.s(() -> b.setBlockData(aa, true)); + J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); getPlayer(p).getData().addStat("harvest.blocks", 1); getPlayer(p).getData().addStat("harvest.planted", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index 0e22c1ee1..e5f13d7f2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -29,6 +29,7 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -40,7 +41,6 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; import java.util.Comparator; @@ -114,45 +114,45 @@ private void startBloom(org.bukkit.entity.Player player, Block center, Material return; } - new BukkitRunnable() { - int cursor = 0; - int totalChanged = 0; + int intervalTicks = Math.max(1, getSpreadIntervalTicks(level)); + int[] cursor = {0}; + int[] totalChanged = {0}; + Runnable[] bloomTask = new Runnable[1]; + bloomTask[0] = () -> { + if (!player.isOnline() || center.getWorld() == null) { + return; + } - @Override - public void run() { - if (!player.isOnline() || center.getWorld() == null) { - cancel(); - return; - } + int pulseChanged = 0; + int batch = getBlocksPerPulse(level); + for (int i = 0; i < batch && cursor[0] < path.size(); i++) { + pulseChanged += spreadAt(path.get(cursor[0]++), catalyst, spreadSurface); + } - int pulseChanged = 0; - int batch = getBlocksPerPulse(level); - for (int i = 0; i < batch && cursor < path.size(); i++) { - pulseChanged += spreadAt(path.get(cursor++), catalyst, spreadSurface); + if (pulseChanged > 0) { + totalChanged[0] += pulseChanged; + if (areParticlesEnabled()) { + center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.02); } - - if (pulseChanged > 0) { - totalChanged += pulseChanged; - if (areParticlesEnabled()) { - center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.02); - } - if (areParticlesEnabled()) { - center.getWorld().spawnParticle(Particle.CRIMSON_SPORE, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.01); - } - SoundPlayer sp = SoundPlayer.of(center.getWorld()); - sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_FUNGUS_PLACE, 0.45f, 0.75f); - sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.22f, 0.45f + ThreadLocalRandom.current().nextFloat() * 0.45f); + if (areParticlesEnabled()) { + center.getWorld().spawnParticle(Particle.CRIMSON_SPORE, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.01); } + SoundPlayer sp = SoundPlayer.of(center.getWorld()); + sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_FUNGUS_PLACE, 0.45f, 0.75f); + sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.22f, 0.45f + ThreadLocalRandom.current().nextFloat() * 0.45f); + } - if (cursor >= path.size()) { - if (totalChanged > 0) { - getPlayer(player).getData().addStat("herbalism.spore-bloom.blocks-spread", totalChanged); - xp(player, totalChanged * getConfig().xpPerMushroomPlaced); - } - cancel(); + if (cursor[0] >= path.size()) { + if (totalChanged[0] > 0) { + getPlayer(player).getData().addStat("herbalism.spore-bloom.blocks-spread", totalChanged[0]); + xp(player, totalChanged[0] * getConfig().xpPerMushroomPlaced); } + return; } - }.runTaskTimer(Adapt.instance, 0L, getSpreadIntervalTicks(level)); + + J.runAt(center.getLocation(), bloomTask[0], intervalTicks); + }; + J.runAt(center.getLocation(), bloomTask[0]); } private List buildSpiderPath(Block center, double radius, int spokes, int max, int guaranteedReach) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index 77f046496..405873f16 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -205,7 +205,10 @@ private void showOreMarker(Player p, Block ore, int durationTicks) { try { glowingEntities.setGlowing(slime, p, ChatColor.AQUA); - } catch (ReflectiveOperationException ignored) { + } catch (ReflectiveOperationException ex) { + Adapt.verbose("Failed to enable glowing marker for QuarrySense: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); slime.remove(); showFallbackMarker(p, ore, durationTicks); return; @@ -220,10 +223,13 @@ private void showOreMarker(Player p, Block ore, int durationTicks) { p.spawnParticle(Particle.END_ROD, ore.getLocation().add(0.5, 0.5, 0.5), 8, 0.15, 0.15, 0.15, 0.003); } - J.s(() -> { + J.runEntity(slime, () -> { try { glowingEntities.unsetGlowing(slime, p); - } catch (ReflectiveOperationException ignored) { + } catch (ReflectiveOperationException ex) { + Adapt.verbose("Failed to clear glowing marker for QuarrySense: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } slime.remove(); }, durationTicks); @@ -232,7 +238,7 @@ private void showOreMarker(Player p, Block ore, int durationTicks) { private void showFallbackMarker(Player p, Block ore, int durationTicks) { Location loc = ore.getLocation().add(0.5, 0.5, 0.5); for (int t = 0; t <= durationTicks; t += 8) { - J.s(() -> { + J.runEntity(p, () -> { if (!p.isOnline()) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index 582c8a8ee..9532d4ab1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -146,7 +146,7 @@ public void on(BlockBreakEvent e) { getPlayer(p).getAdvancementHandler().grant("challenge_pickaxe_veinminer_20"); } - J.s(() -> { + J.runEntity(p, () -> { for (Location l : blockMap.keySet()) { if (!canBlockBreak(p, l)) { Adapt.verbose("Player " + p.getName() + " doesn't have permission."); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java index 8723c22bd..92ca7a57b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java @@ -169,7 +169,7 @@ public void addWebFoundation(Block block, int seconds) { return; } - J.s(() -> { + J.runAt(block.getLocation(), () -> { block.setBlockData(BLOCK); activeBlocks.add(block); }); @@ -180,7 +180,7 @@ public void addWebFoundation(Block block, int seconds) { vfxCuboidOutline(block, Particle.CLOUD); vfxCuboidOutline(block, Particle.WHITE_ASH); } - J.s(() -> removeFoundation(block), seconds * 16); + J.runAt(block.getLocation(), () -> removeFoundation(block), seconds * 16); } public void removeFoundation(Block block) { @@ -188,7 +188,7 @@ public void removeFoundation(Block block) { return; } - J.s(() -> { + J.runAt(block.getLocation(), () -> { block.setBlockData(AIR); activeBlocks.remove(block); }); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index 5fef923de..08f8acb69 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -131,7 +131,7 @@ private void armDoubleJump(Player p, UUID id) { long expires = M.ms() + triggerWindowMillis; jumpArmUntil.put(id, expires); p.setAllowFlight(true); - J.s(() -> { + J.runEntity(p, () -> { if (!p.isOnline()) { return; } @@ -475,16 +475,16 @@ private boolean attemptBlink(Player p) { } Vector v = p.getVelocity().clone(); - loadChunkAsync(destinationGround, chunk -> J.s(() -> { + loadChunkAsync(destinationGround, chunk -> J.runEntity(p, () -> { Location toLoc = destinationGround.clone().add(0, 1, 0); - AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(false, getPlayer(p), this, locOG, destinationGround.clone()); + AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, locOG, destinationGround.clone()); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } - p.teleport(toLoc, PlayerTeleportEvent.TeleportCause.PLUGIN); + J.teleport(p, toLoc, PlayerTeleportEvent.TeleportCause.PLUGIN); p.setVelocity(v.multiply(3)); })); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java index 4fce92f2d..964cfb490 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java @@ -105,12 +105,12 @@ public void on(PlayerToggleSneakEvent e) { p.removePotionEffect(PotionEffectType.LEVITATION); getPlayer(p).getData().addStat("rift.descent.levitation-cancelled", 1); cooldown.add(p); - J.s(() -> { + J.runEntity(p, () -> { sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); cooldown.remove(p); }, Math.max(1, (int) Math.round(getConfig().cooldown * 20D))); - J.s(() -> { + J.runEntity(p, () -> { p.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, (int) (20 * getConfig().cooldown), 0)); sp.play(p.getLocation(), Sound.ENTITY_ENDER_DRAGON_FLAP, 1f, 1f); }); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java index d43888c27..f0820fe7d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java @@ -262,7 +262,7 @@ public void on(ProjectileHitEvent e) { } destination.getChunk().load(); - target.teleport(destination); + J.teleport(target, destination); if (areParticlesEnabled()) { target.getWorld().spawnParticle(Particle.REVERSE_PORTAL, destination.clone().add(0, 0.75, 0), 18, 0.3, 0.35, 0.3, 0.05); } @@ -273,7 +273,7 @@ public void on(ProjectileHitEvent e) { } getPlayer(p).getData().addStat("rift.ender-taglock.taglocked-teleports", 1); xp(p, getConfig().xpOnTeleport); - J.s(() -> suppressPearlTeleportUntil.remove(p.getUniqueId()), 2); + J.runEntity(p, () -> suppressPearlTeleportUntil.remove(p.getUniqueId()), 2); } private Location resolveDestination(ProjectileHitEvent e) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java index 57dd6d55c..6ec8b5d22 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java @@ -41,7 +41,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitRunnable; import art.arcane.adapt.util.common.format.C; @@ -217,39 +216,36 @@ private void openEye(Player p) { sp.play(l, Sound.BLOCK_LODESTONE_PLACE, 1f, 0.1f); sp.play(l, Sound.BLOCK_BELL_RESONATE, 1f, 0.1f); - new BukkitRunnable() { - private long dur = 4000; - private double radius = 2.0; - private double adder = 0.0; - private final Color color = Color.fromBGR(0, 0, 0); - private boolean initialRingShown = false; - - @Override - public void run() { - if (!p.isOnline()) { - cancel(); - return; - } - - if (!initialRingShown) { - vfxFastRing(p.getLocation(), radius, color); - initialRingShown = true; - } + int[] remainingSteps = {80}; + double[] radius = {2.0}; + double[] adder = {0.0}; + boolean[] initialRingShown = {false}; + final Color color = Color.fromBGR(0, 0, 0); + Runnable[] ringTask = new Runnable[1]; + ringTask[0] = () -> { + if (!p.isOnline()) { + return; + } - dur -= 50; - if (dur <= 0) { - cancel(); - return; - } + if (!initialRingShown[0]) { + vfxFastRing(p.getLocation(), radius[0], color); + initialRingShown[0] = true; + } - adder += 0.02; - radius *= 0.9; - vfxFastRing(p.getLocation().add(0, adder, 0), radius, color); + remainingSteps[0]--; + if (remainingSteps[0] <= 0) { + return; } - }.runTaskTimer(Adapt.instance, 0L, 1L); + + adder[0] += 0.02; + radius[0] *= 0.9; + vfxFastRing(p.getLocation().add(0, adder[0], 0), radius[0], color); + J.runEntity(p, ringTask[0], 1); + }; + J.runEntity(p, ringTask[0]); vfxLevelUp(p); sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); - J.s(() -> { + J.runEntity(p, () -> { AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, p.getLocation(), l); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { @@ -259,7 +255,7 @@ public void run() { getPlayer(p).getData().addStat("rift.teleports", 1); getPlayer(p).getData().addStat("rift.gate.teleports", 1); getPlayer(p).getData().addStat("rift.gate.total-distance", (int) p.getLocation().distance(l)); - p.teleport(l, PlayerTeleportEvent.TeleportCause.PLUGIN); + J.teleport(p, l, PlayerTeleportEvent.TeleportCause.PLUGIN); vfxLevelUp(p); sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); }, 85); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index 53a5b6b03..e5633e2f2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -133,7 +133,7 @@ public void on(BlockPlaceEvent e) { return; } - J.s(() -> { + J.runEntity(p, () -> { ItemStack hand = p.getInventory().getItemInMainHand(); int needed = 0; if (hand.getType().isAir()) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java index 8df5c90e6..129d9719e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java @@ -37,6 +37,7 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; public class SeaborneOxygen extends SimpleAdaptation { @@ -74,11 +75,38 @@ public double getAirBoost(int level) { @Override public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - if (i.getLocation().getBlock().getType() == Material.WATER && hasAdaptation(i)) { - int airTicks = getLevel(i) * getConfig().airPerLevelTics; - i.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, airTicks, getLevel(i))); - getPlayer(i).getData().addStat("seaborne.oxygen.bonus-air-ticks", airTicks); + Player player = adaptPlayer.getPlayer(); + if (player == null || !player.isOnline()) { + continue; + } + + Runnable applyOxygen = () -> { + if (!player.isOnline() || player.getWorld() == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return; + } + + if ((!player.isInWater() && !player.isSwimming()) || !hasAdaptation(player)) { + return; + } + + int level = getLevel(player); + if (level <= 0) { + return; + } + + int airTicks = level * getConfig().airPerLevelTics; + player.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, airTicks, level)); + getPlayer(player).getData().addStat("seaborne.oxygen.bonus-air-ticks", airTicks); + }; + + if (J.isFoliaThreading()) { + J.runEntity(player, applyOxygen); + } else { + applyOxygen.run(); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index ea6122fc0..3b2c7373d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -27,9 +27,9 @@ import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -100,18 +100,38 @@ public void onTick() { long now = System.currentTimeMillis(); for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p) || !p.isInWater()) { + if (p == null || !p.isOnline()) { continue; } - int level = getLevel(p); - if (!isDeepEnough(p, level)) { - continue; - } + Runnable apply = () -> { + if (!p.isOnline()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return; + } + + if (!hasAdaptation(p) || !p.isInWater()) { + return; + } - applyDepthBuffs(p, level); - awardDepthXp(p, now); - getPlayer(p).getData().addStat("seaborne.pressure-diver.deep-blocks-mined", 1); + int level = getLevel(p); + if (!isDeepEnough(p, level)) { + return; + } + + applyDepthBuffs(p, level); + awardDepthXp(p, now); + getPlayer(p).getData().addStat("seaborne.pressure-diver.deep-blocks-mined", 1); + }; + + if (J.isFoliaThreading()) { + J.runEntity(p, apply); + } else { + apply.run(); + } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java index 252d67ed2..15c11ce04 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java @@ -26,9 +26,9 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -78,15 +78,35 @@ public void addStats(int level, Element v) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); - if (player.isInWater() && hasAdaptation(player)) { - if (player.getLocation().getBlock().isLiquid()) { - if (player.getInventory().getBoots() != null && player.getInventory().getBoots().containsEnchantment(Enchantment.DEPTH_STRIDER)) { - continue; - } else { - player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, getLevel(player))); - getPlayer(player).getData().addStat("seaborne.speed.blocks-swum", 1); - } + if (player == null || !player.isOnline()) { + continue; + } + + Runnable apply = () -> { + if (!player.isOnline()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return; + } + + if (!player.isInWater() || !hasAdaptation(player)) { + return; + } + + if (player.getInventory().getBoots() != null && player.getInventory().getBoots().containsEnchantment(Enchantment.DEPTH_STRIDER)) { + return; } + + player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, getLevel(player))); + getPlayer(player).getData().addStat("seaborne.speed.blocks-swum", 1); + }; + + if (J.isFoliaThreading()) { + J.runEntity(player, apply); + } else { + apply.run(); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java index 1f0f1d50d..cf6b72c6d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java @@ -223,7 +223,7 @@ private void tryDash(Player p, TriggerType triggerType) { if (target == null) { return; } - p.teleport(target); + J.teleport(p, target); applyDashMomentum(p, origin, target); } @@ -317,7 +317,7 @@ private void preserveSwimStateAfterDash(Player p, boolean wasSwimming) { } p.setSwimming(true); - J.s(() -> { + J.runEntity(p, () -> { if (p.isOnline() && isInWaterDashState(p)) { p.setSwimming(true); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java index d19b9fc94..b06f8853a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java @@ -27,9 +27,9 @@ import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; @@ -77,11 +77,31 @@ public void addStats(int level, Element v) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); - if (player.isInWater() && hasAdaptation(player)) { - if (player.getLocation().getBlock().isLiquid()) { - player.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 62, 1, false, false)); - getPlayer(player).getData().addStat("seaborne.turtles-mining.blocks-underwater", 1); + if (player == null || !player.isOnline()) { + continue; + } + + Runnable apply = () -> { + if (!player.isOnline()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return; } + + if (!player.isInWater() || !hasAdaptation(player)) { + return; + } + + player.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 62, 1, false, false)); + getPlayer(player).getData().addStat("seaborne.turtles-mining.blocks-underwater", 1); + }; + + if (J.isFoliaThreading()) { + J.runEntity(player, apply); + } else { + apply.run(); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java index 23c1f1392..84d2fe5d6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java @@ -26,9 +26,9 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; @@ -68,11 +68,31 @@ public void addStats(int level, Element v) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); - if (player.isInWater() && hasAdaptation(player)) { - if (player.getLocation().getBlock().isLiquid()) { - player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 62, 0, false, false)); - getPlayer(player).getData().addStat("seaborne.turtles-vision.time-underwater", 1); + if (player == null || !player.isOnline()) { + continue; + } + + Runnable apply = () -> { + if (!player.isOnline()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return; } + + if (!player.isInWater() || !hasAdaptation(player)) { + return; + } + + player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 62, 0, false, false)); + getPlayer(player).getData().addStat("seaborne.turtles-vision.time-underwater", 1); + }; + + if (J.isFoliaThreading()) { + J.runEntity(player, apply); + } else { + apply.run(); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java index 3fcf0fbd3..80d0b98bf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java @@ -133,7 +133,7 @@ public void on(EntityDamageEvent e) { int damageXP = (int) Math.min(10, 2.5 * e.getDamage()); xp(p,damageXP ); getPlayer(p).getData().addStat("stealth.ghost-armor.armor-consumed", 1); - J.s(() -> { + J.runEntity(p, () -> { var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); if (attribute == null) return; attribute.removeModifier(MODIFIER, MODIFIER_KEY); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index 3acca00b2..8ebdb9696 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -103,7 +103,7 @@ public void onTick() { for (Player p : sneaking) { if (hasAdaptation(p) && !p.isSneaking()) { toRemove.add(p); - J.s(() -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); + J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); } } sneaking.removeAll(toRemove); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index 5926d0cb0..fc7c86af9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -41,6 +41,7 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import art.arcane.adapt.util.common.format.C; @@ -64,7 +65,7 @@ public StealthSnatch() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - holds = new HashSet<>(); + holds = ConcurrentHashMap.newKeySet(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CHEST) .key("challenge_stealth_snatch_2500") @@ -139,7 +140,7 @@ private void snatch(Player player) { getPlayer(player).getData().addStat("stealth.snatch.items-snatched", 1); //sendCollected(player, droppedItemEntity); int id = droppedItemEntity.getEntityId(); - J.s(() -> holds.remove(Integer.valueOf(id))); + J.runEntity(player, () -> holds.remove(Integer.valueOf(id)), 1); } } } @@ -169,7 +170,7 @@ public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); if (i.isSneaking()) { - J.s(() -> snatch(i)); + J.runEntity(i, () -> snatch(i)); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index 3c4072f81..8bf32abb8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -17,6 +17,7 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.stealth; +import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -46,6 +47,7 @@ import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.common.scheduling.J; public class StealthSpeed extends SimpleAdaptation { private static final Sound DEFAULT_ACTIVATION_SOUND = Sound.PARTICLE_SOUL_ESCAPE; @@ -249,8 +251,9 @@ private Vector resolveAutoStepDirection(Player p) { } } } - } catch (NoSuchMethodError ignored) { + } catch (NoSuchMethodError ex) { // Runtime does not expose input API. Use velocity-based fallback. + Adapt.verbose("Player input API is unavailable on this runtime; using velocity fallback for stealth auto-step."); } } @@ -279,7 +282,7 @@ private boolean tryStepUp(Player p, org.bukkit.Location front, Vector direction) return false; } - p.teleport(destination); + J.teleport(p, destination); return true; } @@ -303,7 +306,7 @@ private boolean tryStepDown(Player p, org.bukkit.Location front, Vector directio return false; } - p.teleport(destination); + J.teleport(p, destination); p.setFallDistance(0); return true; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index 98352b775..8555ff6dd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -171,7 +171,7 @@ public void on(PlayerInteractEvent e) { if (!ee.isCancelled()) { dmg += 1; - J.s(() -> { + J.runAt(i.getLocation(), () -> { i.breakNaturally(); spw.play(i.getLocation(), Sound.BLOCK_GRASS_BREAK, 0.4f, (float) (ThreadLocalRandom.current().nextDouble() * 1.85D)); }, RNG.r.i(0, (getMaxLevel() - lvl * 2) + 1)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java index f78628d80..aa2632f5c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java @@ -36,6 +36,6 @@ public DamagingBleedEffect(EffectManager effectManager, double damage, LivingEnt @Override public void onRun() { super.onRun(); - J.s(() -> target.damage(damage)); + J.runEntity(target, () -> target.damage(damage)); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index 3f71e130e..3354a7b08 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -28,6 +28,7 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; @@ -108,7 +109,7 @@ public void on(PlayerInteractEvent e) { return; } - tameable.teleport(safe); + J.teleport(tameable, safe); tameable.setFallDistance(0); p.setCooldown(Material.LEAD, getCooldownTicks(level)); e.setCancelled(true); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java index b3943e928..3ae5bf97f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java @@ -29,6 +29,7 @@ import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -36,6 +37,7 @@ import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.attribute.AttributeModifier; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Tameable; import org.bukkit.event.EventHandler; @@ -55,6 +57,7 @@ public class TamingDamage extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-damage-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-damage-boost"); + private static final double FOLIA_SCAN_RADIUS = 48D; public TamingDamage() { super("tame-damage"); @@ -98,6 +101,11 @@ private double getDamageBoost(int level) { @Override public void onTick() { + if (J.isFoliaThreading()) { + onFoliaTick(); + return; + } + Map ownerLevels = new HashMap<>(); for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); @@ -114,6 +122,31 @@ public void onTick() { } } + private void onFoliaTick() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getLevel(owner); + J.runEntity(owner, () -> updateNearbyOwnedTameables(owner, level)); + } + } + + private void updateNearbyOwnedTameables(Player owner, int level) { + if (owner == null || !owner.isOnline()) { + return; + } + + for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + + update(tameable, level); + } + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(EntityDeathEvent e) { if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java index b0b0ba4b4..077434ee5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java @@ -29,6 +29,7 @@ import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -36,6 +37,7 @@ import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.attribute.AttributeModifier; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Tameable; @@ -51,6 +53,7 @@ public class TamingHealthBoost extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-health-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-health-boost"); + private static final double FOLIA_SCAN_RADIUS = 48D; public TamingHealthBoost() { super("tame-health"); @@ -85,10 +88,15 @@ private double getHealthBoost(int level) { @Override public void onTick() { + if (J.isFoliaThreading()) { + onFoliaTick(); + return; + } + Map ownerStates = new HashMap<>(); for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); - ownerStates.put(owner.getUniqueId(), new OwnerState(adaptPlayer, getLevel(owner))); + ownerStates.put(owner.getUniqueId(), new OwnerState(adaptPlayer, owner, getLevel(owner))); } for (World world : Bukkit.getServer().getWorlds()) { @@ -99,14 +107,43 @@ public void onTick() { int level = state == null ? 0 : state.level(); update(tameable, level); if (level > 0 && state != null) { - state.owner().getData().addStat("taming.health-boost.ticks-active", 1); + state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); } } } } } - private record OwnerState(AdaptPlayer owner, int level) { + private void onFoliaTick() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + OwnerState state = new OwnerState(adaptPlayer, owner, getLevel(owner)); + J.runEntity(owner, () -> updateNearbyOwnedTameables(state)); + } + } + + private void updateNearbyOwnedTameables(OwnerState state) { + Player owner = state.owner(); + if (owner == null || !owner.isOnline()) { + return; + } + + for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + + update(tameable, state.level()); + if (state.level() > 0) { + state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); + } + } + } + + private record OwnerState(AdaptPlayer ownerData, Player owner, int level) { } private void update(Tameable j, int level) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index e8b066081..3cd9df132 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -31,7 +31,6 @@ import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Entity; @@ -74,7 +73,26 @@ public void addStats(int level, Element v) { @Override public void onTick() { - if (!Bukkit.isPrimaryThread()) { + if (J.isFoliaThreading()) { + List owners = new ArrayList<>(); + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getActiveLevel(owner); + if (level <= 0) { + continue; + } + + double radius = getRadius(level); + owners.add(new OwnerAuraState(adaptPlayer, owner, radius, radius * radius, getAmplifier(level))); + } + + for (OwnerAuraState state : owners) { + J.runEntity(state.owner(), () -> applyAura(state)); + } + return; + } + + if (!J.isPrimaryThread()) { J.s(this::onTick); return; } @@ -92,22 +110,31 @@ public void onTick() { } for (OwnerAuraState state : owners) { - Location ownerLocation = state.owner.getLocation(); - for (Entity nearby : state.owner.getWorld().getNearbyEntities(ownerLocation, state.radius, state.radius, state.radius)) { - if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { - continue; - } - if (!(tameable.getOwner() instanceof Player owner) || !owner.getUniqueId().equals(state.owner.getUniqueId())) { - continue; - } - if (ownerLocation.distanceSquared(tameable.getLocation()) > state.radiusSquared) { - continue; - } + applyAura(state); + } + } + + private void applyAura(OwnerAuraState state) { + Player owner = state.owner(); + if (owner == null || !owner.isOnline()) { + return; + } - tameable.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, getConfig().effectTicks, state.amplifier, false, false)); - tameable.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().effectTicks, state.amplifier, false, false)); - state.ownerData.getData().addStat("taming.pack-leader.buffed-ticks", 1); + Location ownerLocation = owner.getLocation(); + for (Entity nearby : owner.getNearbyEntities(state.radius(), state.radius(), state.radius())) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player petOwner) || !petOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; } + if (ownerLocation.distanceSquared(tameable.getLocation()) > state.radiusSquared()) { + continue; + } + + tameable.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, getConfig().effectTicks, state.amplifier(), false, false)); + tameable.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().effectTicks, state.amplifier(), false, false)); + state.ownerData().getData().addStat("taming.pack-leader.buffed-ticks", 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java index 351e9d91e..ec65f86b0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java @@ -174,7 +174,7 @@ private void spawnGlobe(Player owner, EntityDeathEvent e, boolean blood, int lev } int life = getGlobeLifetimeTicks(level); - J.s(() -> { + J.runEntity(dropped, () -> { bloodGlobes.remove(dropped.getUniqueId()); boneGlobes.remove(dropped.getUniqueId()); if (dropped.isValid()) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java index 36dbc376c..d8816ea5b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java @@ -127,7 +127,7 @@ public void on(EntityDamageByEntityEvent e) { } if (areParticlesEnabled()) { - J.s(() -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); + J.runEntity(p, () -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java index 110e40868..c094f77e1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java @@ -28,10 +28,10 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.entity.Player; @@ -123,7 +123,7 @@ private boolean isOnCooldown(Player p) { private void startHealingWindow(Player p) { long currentTime = System.currentTimeMillis(); healingWindow.put(p, currentTime + getConfig().windowDuration); - Bukkit.getScheduler().runTaskLater(Adapt.instance, () -> { + J.runEntity(p, () -> { healingWindow.remove(p); cooldowns.put(p, currentTime + getConfig().windowDuration + getConfig().cooldownDuration); }, getConfig().windowDuration / 50); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java index 65536509b..be3289c7e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java @@ -25,9 +25,9 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.entity.Entity; @@ -124,7 +124,7 @@ private void triggerSeeker(Player p, Entity origin, double damage, int remaining p.damage(selfDamage, p); LivingEntity finalNearest = nearest; - Bukkit.getScheduler().runTaskLater(Adapt.instance, () -> { + J.runEntity(finalNearest, () -> { double remainingHealth = finalNearest.getHealth() - damage; finalNearest.damage(damage, p); if (remainingHealth <= 0) { @@ -179,4 +179,4 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Self Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double selfDamageMultiplier = 0.5; } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index 9ed2aa06a..0bf825c5a 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -84,7 +84,7 @@ public static void open(Player player, String sectionPath, int page) { return; } - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { String path = sectionPath; int targetPage = page; J.s(() -> open(player, path, targetPage)); @@ -1130,7 +1130,7 @@ private static Field findField(Class type, String name) { Field field = current.getDeclaredField(name); field.setAccessible(true); return field; - } catch (NoSuchFieldException ignored) { + } catch (NoSuchFieldException ex) { current = current.getSuperclass(); } } @@ -1173,7 +1173,10 @@ private static Object instantiate(Class type) { try { return normalized.getDeclaredConstructor().newInstance(); - } catch (Throwable ignored) { + } catch (Throwable ex) { + Adapt.verbose("Failed to instantiate config type " + normalized.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return null; } } @@ -1183,7 +1186,11 @@ private static boolean setFieldValue(Field field, Object target, Object value) { field.setAccessible(true); field.set(target, value); return true; - } catch (Throwable ignored) { + } catch (Throwable ex) { + Adapt.verbose("Failed to set field '" + field.getName() + "' on " + + (target == null ? "null" : target.getClass().getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return false; } } @@ -1192,7 +1199,11 @@ private static Object getFieldValue(Field field, Object target) { try { field.setAccessible(true); return field.get(target); - } catch (Throwable ignored) { + } catch (Throwable ex) { + Adapt.verbose("Failed to read field '" + field.getName() + "' on " + + (target == null ? "null" : target.getClass().getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return null; } } diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 8687f43c4..c02aace06 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -53,7 +53,7 @@ public static void open(Player player) { } public static void open(Player player, int page) { - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { int targetPage = page; J.s(() -> open(player, targetPage)); return; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java index 4a6fec166..b53e98dda 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java @@ -239,12 +239,12 @@ public void onTick() { } // Check for jumping - if (i.getLocation().subtract(0, 1, 0).getBlock().getType().isAir() && !i.isFlying() && !i.isSneaking()) { + if (!i.isOnGround() && !i.isFlying() && !i.isSneaking()) { xpSilent(i, getConfig().jumpXpPassive, "agility:jump"); } // Check for climbing ladders - if (i.getLocation().getBlock().getType() == Material.LADDER && !i.isFlying() && !i.isSneaking()) { + if (i.isClimbing() && !i.isFlying() && !i.isSneaking()) { xpSilent(i, getConfig().climbXpPassive, "agility:climb"); } }); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index 5e74f323a..f5c32d70a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -33,6 +33,7 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -376,7 +377,7 @@ public void on(PlayerBedEnterEvent e) { sleepCooldowns.put(uuid, now); getPlayer(p).getData().addStat("chronos.beds.used", 1); - Bukkit.getScheduler().runTaskLater(Adapt.instance, () -> { + J.runEntity(p, () -> { if (!p.isOnline()) { return; } @@ -387,7 +388,7 @@ public void on(PlayerBedEnterEvent e) { } else { xp(p, p.getLocation(), getConfig().sleepAttemptXP); } - }, 40L); + }, 40); }); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java index 769dcae94..2c960a886 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java @@ -214,8 +214,10 @@ public void on(CraftItemEvent e) { if (key != null) { seeCraftedRecipe(p, key.toString()); } - } catch (Throwable ignored) { - Adapt.verbose("No recipe key found for " + e.getRecipe().getResult().getType().name()); + } catch (Throwable ex) { + Adapt.verbose("No recipe key found for " + e.getRecipe().getResult().getType().name() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } }); } @@ -249,7 +251,7 @@ public void on(PlayerExpChangeEvent e) { private void scheduleSeeWorld(Player p) { try { - J.s(() -> seeWorld(p, p.getWorld()), 15); + J.runEntity(p, () -> seeWorld(p, p.getWorld()), 15); } catch (Exception e) { Adapt.error("Failed to discover world " + p.getWorld().getName()); } @@ -381,8 +383,10 @@ private void seeTargetBlock(Player i) { seeBlock(i, b.getBlockData(), b.getLocation()); seeBiome(i, b.getBiome()); } - } catch (Throwable ignored) { - Adapt.verbose("Failed to get target block for " + i.getName()); + } catch (Throwable ex) { + Adapt.verbose("Failed to get target block for " + i.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index b454cb4b9..ad840fb66 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -310,7 +310,7 @@ private void handleComposterInteraction(PlayerInteractEvent e, Player p) { if (!(b.getBlockData() instanceof Levelled oldData)) return; int ol = oldData.getLevel(); - J.s(() -> { + J.runAt(b.getLocation(), () -> { if (!(b.getBlockData() instanceof Levelled newData)) return; int nl = newData.getLevel(); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java index f5b76cce8..bc3a7a4f2 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java @@ -193,7 +193,7 @@ public void onTick() { for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); shouldReturnForPlayer(i, () -> { - if (i.getWorld().getBlockAt(i.getLocation()).isLiquid() && i.isSwimming() && i.getPlayer() != null && i.getPlayer().getRemainingAir() < i.getMaximumAir()) { + if ((i.isInWater() || i.isSwimming()) && i.getRemainingAir() < i.getMaximumAir()) { Adapt.verbose("seaborne Tick"); checkStatTrackers(adaptPlayer); xpSilent(i, getConfig().swimXP, "seaborne:swim"); diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index 728944ca6..e9eeafdbc 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -24,7 +24,7 @@ public static void open( return; } - if (!Bukkit.isPrimaryThread()) { + if (!J.isPrimaryThread()) { J.s(() -> open(player, title, message, onConfirm, onCancel)); return; } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java index 40265eb92..29eed1130 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java @@ -19,6 +19,7 @@ package art.arcane.adapt.util.common.plugin; import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -79,8 +80,10 @@ public Metrics(JavaPlugin plugin, int serviceId) { .copyDefaults(true); try { config.save(configFile); - } catch (IOException ignored) { - Adapt.verbose("Failed to save bStats config file."); + } catch (IOException ex) { + Adapt.verbose("Failed to save bStats config file: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } } // Load the data @@ -97,7 +100,7 @@ public Metrics(JavaPlugin plugin, int serviceId) { enabled, this::appendPlatformData, this::appendServiceData, - submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + submitDataTask -> J.s(submitDataTask), plugin::isEnabled, (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), (message) -> this.plugin.getLogger().log(Level.INFO, message), @@ -859,4 +862,4 @@ public String toString() { } } } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java index e36d8b369..24a338d8e 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java @@ -44,6 +44,7 @@ public abstract class VolmitPlugin extends JavaPlugin implements Listener { public static boolean bad = false; + private int controllerTickerTaskId = -1; private KMap controllers; private List cachedControllers; private KMap, IController> cachedClassControllers; @@ -67,7 +68,7 @@ public void v(Object l) { public void onEnable() { registerInstance(); registerControllers(); - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::tickControllers, 0, 0); + controllerTickerTaskId = J.sr(this::tickControllers, 1); J.a(this::outputInfo); registerListener(this); start(); @@ -89,9 +90,9 @@ private void outputInfo() { IO.delete(getDataFolder("info")); getDataFolder("info").mkdirs(); outputPluginInfo(); - } catch (Throwable ignored) { - Adapt.verbose("Failed to output info"); - + } catch (Throwable ex) { + Adapt.verbose("Failed to output plugin info: " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } } @@ -110,7 +111,11 @@ private void outputPluginInfo() throws IOException { @Override public void onDisable() { stop(); - Bukkit.getScheduler().cancelTasks(this); + if (controllerTickerTaskId != -1) { + J.csr(controllerTickerTaskId); + controllerTickerTaskId = -1; + } + J.cancelPluginTasks(); unregisterListener(this); unregisterAll(); } diff --git a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java index 46597da72..4ca169ef3 100644 --- a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java +++ b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java @@ -18,28 +18,40 @@ package art.arcane.adapt.util.common.scheduling; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.function.NastyFunction; +import art.arcane.adapt.util.common.function.NastyFuture; +import art.arcane.adapt.util.common.function.NastyRunnable; +import art.arcane.adapt.util.common.parallel.MultiBurst; import art.arcane.volmlib.util.math.FinalInteger; import art.arcane.volmlib.util.scheduling.AR; +import art.arcane.volmlib.util.scheduling.FoliaScheduler; import art.arcane.volmlib.util.scheduling.JSupport; import art.arcane.volmlib.util.scheduling.SR; import art.arcane.volmlib.util.scheduling.SchedulerBridge; import art.arcane.volmlib.util.scheduling.StartupQueueSupport; -import art.arcane.adapt.Adapt; import org.bukkit.Bukkit; - +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.plugin.IllegalPluginAccessException; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.Method; +import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import art.arcane.adapt.util.common.function.NastyFunction; -import art.arcane.adapt.util.common.function.NastyFuture; -import art.arcane.adapt.util.common.function.NastyRunnable; -import art.arcane.adapt.util.common.parallel.MultiBurst; - public class J { - private static final int tid = 0; + private static final long TICK_MS = 50L; + private static final AtomicInteger TASK_IDS = new AtomicInteger(1); + private static final Map REPEATING_CANCELLERS = new ConcurrentHashMap<>(); private static final StartupQueueSupport STARTUP_QUEUE = new StartupQueueSupport(); static { @@ -105,78 +117,256 @@ public static void executeAfterStartupQueue() { JSupport.executeAfterStartupQueue(STARTUP_QUEUE, J::s, J::a); } - /** - * Schedule a sync task to be run right after startup. If the server has already - * started ticking, it will simply run it in a sync task. - *

- * If you dont know if you should queue this or not, do so, it's pretty - * forgiving. - * - * @param r the runnable - */ public static void ass(Runnable r) { JSupport.enqueueAfterStartupSync(STARTUP_QUEUE, r, J::s); } - /** - * Schedule an async task to be run right after startup. If the server has - * already started ticking, it will simply run it in an async task. - *

- * If you dont know if you should queue this or not, do so, it's pretty - * forgiving. - * - * @param r the runnable - */ public static void asa(Runnable r) { JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a); } - /** - * Queue a sync task - * - * @param r the runnable - */ + public static boolean isPrimaryThread() { + return FoliaScheduler.isPrimaryThread(); + } + + public static boolean isFoliaThreading() { + return FoliaScheduler.isFoliaThreading(Bukkit.getServer()); + } + + public static boolean isOwnedByCurrentRegion(Entity entity) { + return FoliaScheduler.isOwnedByCurrentRegion(entity); + } + + public static boolean runEntity(Entity entity, Runnable runnable) { + if (entity == null || runnable == null || !isPluginActive()) { + return false; + } + + if (isFoliaThreading()) { + if (isOwnedByCurrentRegion(entity)) { + runnable.run(); + return true; + } + + return runEntityImmediate(entity, runnable); + } + + if (isPrimaryThread()) { + runnable.run(); + return true; + } + + return runEntityImmediate(entity, runnable); + } + + public static boolean runEntity(Entity entity, Runnable runnable, int delayTicks) { + if (entity == null || runnable == null || !isPluginActive()) { + return false; + } + + if (delayTicks <= 0) { + return runEntity(entity, runnable); + } + + if (isFoliaThreading() && runEntityDelayed(entity, runnable, delayTicks)) { + return true; + } + + s(() -> runEntity(entity, runnable), delayTicks); + return true; + } + + public static boolean teleport(Entity entity, Location location) { + return teleport(entity, location, null); + } + + public static boolean teleport(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { + if (entity == null || location == null) { + return false; + } + + if (isFoliaThreading()) { + Object asyncWithCause = null; + if (cause != null) { + asyncWithCause = invokeNoThrow( + entity, + "teleportAsync", + new Class[]{Location.class, PlayerTeleportEvent.TeleportCause.class}, + location, + cause + ); + } + + if (asyncWithCause != null) { + return true; + } + + Object async = invokeNoThrow(entity, "teleportAsync", new Class[]{Location.class}, location); + if (async != null) { + return true; + } + } + + try { + if (cause != null) { + return entity.teleport(location, cause); + } + + return entity.teleport(location); + } catch (UnsupportedOperationException e) { + Adapt.warn("Failed to teleport entity synchronously on this server; teleportAsync was unavailable. Entity=" + + entity.getUniqueId() + " world=" + (location.getWorld() == null ? "null" : location.getWorld().getName())); + return false; + } + } + + public static boolean runAt(Location location, Runnable runnable) { + if (location == null || runnable == null) { + return false; + } + + if (runRegionImmediate(location, runnable)) { + return true; + } + + if (isFoliaThreading()) { + World world = location.getWorld(); + Adapt.verbose("Failed to schedule immediate region task at " + + (world == null ? "null" : world.getName()) + + "@" + (location.getBlockX() >> 4) + "," + (location.getBlockZ() >> 4) + " on Folia."); + return false; + } + + s(runnable); + return true; + } + + public static boolean runAt(Location location, Runnable runnable, int delayTicks) { + if (location == null || runnable == null) { + return false; + } + + if (delayTicks <= 0) { + return runAt(location, runnable); + } + + if (runRegionDelayed(location, runnable, delayTicks)) { + return true; + } + + if (isFoliaThreading()) { + World world = location.getWorld(); + Adapt.verbose("Failed to schedule delayed region task at " + + (world == null ? "null" : world.getName()) + + "@" + (location.getBlockX() >> 4) + "," + (location.getBlockZ() >> 4) + + " (" + delayTicks + "t) on Folia."); + return false; + } + + s(runnable, delayTicks); + return true; + } + + public static void cancelPluginTasks() { + Plugin plugin = Adapt.instance; + if (plugin == null) { + return; + } + + for (Runnable cancelAction : REPEATING_CANCELLERS.values()) { + try { + cancelAction.run(); + } catch (Throwable ex) { + Adapt.verbose("Failed to run cancel action: " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + REPEATING_CANCELLERS.clear(); + + FoliaScheduler.cancelTasks(plugin); + + try { + Bukkit.getScheduler().cancelTasks(plugin); + } catch (UnsupportedOperationException | IllegalPluginAccessException ex) { + // Folia blocks BukkitScheduler usage. + Adapt.verbose("Skipping BukkitScheduler#cancelTasks for Adapt on this server."); + } + } + public static void s(Runnable r) { - Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r); + if (!isPluginActive()) { + return; + } + + if (!runGlobalImmediate(r)) { + try { + Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r); + } catch (IllegalPluginAccessException e) { + if (!isPluginActive()) { + return; + } + + throw new IllegalStateException("Failed to schedule global sync task while plugin is enabled.", e); + } catch (UnsupportedOperationException e) { + throw new IllegalStateException("Failed to schedule global sync task on this server (Folia scheduler unavailable, BukkitScheduler unsupported).", e); + } + } } - /** - * Queue a sync task - * - * @param r the runnable - * @param delay the delay to wait in ticks before running - */ public static void s(Runnable r, int delay) { - Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r, delay); + if (delay <= 0) { + s(r); + return; + } + + if (!isPluginActive()) { + return; + } + + if (!runGlobalDelayed(r, delay)) { + try { + Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r, delay); + } catch (IllegalPluginAccessException e) { + if (!isPluginActive()) { + return; + } + + throw new IllegalStateException("Failed to schedule delayed global sync task while plugin is enabled.", e); + } catch (UnsupportedOperationException e) { + throw new IllegalStateException("Failed to schedule delayed global sync task on this server (Folia scheduler unavailable, BukkitScheduler unsupported).", e); + } + } } - /** - * Cancel a sync repeating task - * - * @param id the task id - */ public static void csr(int id) { - Bukkit.getScheduler().cancelTask(id); + cancelRepeatingTask(id); } - /** - * Start a sync repeating task - * - * @param r the runnable - * @param interval the interval - * @return the task id - */ public static int sr(Runnable r, int interval) { - return Bukkit.getScheduler().scheduleSyncRepeatingTask(Adapt.instance, r, 0, interval); + int safeInterval = Math.max(1, interval); + RepeatingState state = new RepeatingState(); + int taskId = trackRepeatingTask(() -> state.cancelled = true); + + Runnable[] loop = new Runnable[1]; + loop[0] = () -> { + if (state.cancelled || !isPluginActive()) { + REPEATING_CANCELLERS.remove(taskId); + return; + } + + r.run(); + if (state.cancelled || !isPluginActive()) { + REPEATING_CANCELLERS.remove(taskId); + return; + } + + s(loop[0], safeInterval); + }; + + s(loop[0]); + return taskId; } - /** - * Start a sync repeating task for a limited amount of ticks - * - * @param r the runnable - * @param interval the interval in ticks - * @param intervals the maximum amount of intervals to run - */ public static void sr(Runnable r, int interval, int intervals) { FinalInteger fi = new FinalInteger(0); @@ -193,45 +383,56 @@ public void run() { }; } - /** - * Call an async task dealyed - * - * @param r the runnable - * @param delay the delay to wait before running - */ - @SuppressWarnings("deprecation") public static void a(Runnable r, int delay) { - Bukkit.getScheduler().scheduleAsyncDelayedTask(Adapt.instance, r, delay); + if (!isPluginActive()) { + return; + } + + if (delay <= 0) { + if (!runAsyncImmediate(r)) { + a(r); + } + return; + } + + if (!runAsyncDelayed(r, delay)) { + a(() -> { + if (sleep(ticksToMilliseconds(delay))) { + r.run(); + } + }); + } } - /** - * Cancel an async repeat task - * - * @param id the id - */ public static void car(int id) { - Bukkit.getScheduler().cancelTask(id); + cancelRepeatingTask(id); } - /** - * Start an async repeat task - * - * @param r the runnable - * @param interval the interval in ticks - * @return the task id - */ - @SuppressWarnings("deprecation") public static int ar(Runnable r, int interval) { - return Bukkit.getScheduler().scheduleAsyncRepeatingTask(Adapt.instance, r, 0, interval); + int safeInterval = Math.max(1, interval); + RepeatingState state = new RepeatingState(); + int taskId = trackRepeatingTask(() -> state.cancelled = true); + + Runnable[] loop = new Runnable[1]; + loop[0] = () -> { + if (state.cancelled || !isPluginActive()) { + REPEATING_CANCELLERS.remove(taskId); + return; + } + + r.run(); + if (state.cancelled || !isPluginActive()) { + REPEATING_CANCELLERS.remove(taskId); + return; + } + + a(loop[0], safeInterval); + }; + + a(loop[0], 0); + return taskId; } - /** - * Start an async repeating task for a limited time - * - * @param r the runnable - * @param interval the interval - * @param intervals the intervals to run - */ public static void ar(Runnable r, int interval, int intervals) { FinalInteger fi = new FinalInteger(0); @@ -247,4 +448,74 @@ public void run() { } }; } + + private static int trackRepeatingTask(Runnable cancelAction) { + int id = TASK_IDS.getAndIncrement(); + REPEATING_CANCELLERS.put(id, cancelAction); + return id; + } + + private static void cancelRepeatingTask(int id) { + Runnable cancelAction = REPEATING_CANCELLERS.remove(id); + if (cancelAction != null) { + cancelAction.run(); + } + } + + private static long ticksToMilliseconds(int ticks) { + return Math.max(0L, ticks) * TICK_MS; + } + + private static boolean runGlobalImmediate(Runnable runnable) { + return FoliaScheduler.runGlobal(Adapt.instance, runnable); + } + + private static boolean runGlobalDelayed(Runnable runnable, int delayTicks) { + return FoliaScheduler.runGlobal(Adapt.instance, runnable, Math.max(0, delayTicks)); + } + + private static boolean runRegionImmediate(Location location, Runnable runnable) { + return FoliaScheduler.runRegion(Adapt.instance, location, runnable); + } + + private static boolean runRegionDelayed(Location location, Runnable runnable, int delayTicks) { + return FoliaScheduler.runRegion(Adapt.instance, location, runnable, Math.max(0, delayTicks)); + } + + private static boolean runAsyncImmediate(Runnable runnable) { + return FoliaScheduler.runAsync(Adapt.instance, runnable); + } + + private static boolean runAsyncDelayed(Runnable runnable, int delayTicks) { + return FoliaScheduler.runAsync(Adapt.instance, runnable, Math.max(0, delayTicks)); + } + + private static boolean runEntityImmediate(Entity entity, Runnable runnable) { + return FoliaScheduler.runEntity(Adapt.instance, entity, runnable); + } + + private static boolean runEntityDelayed(Entity entity, Runnable runnable, int delayTicks) { + return FoliaScheduler.runEntity(Adapt.instance, entity, runnable, Math.max(0, delayTicks)); + } + + private static Object invokeNoThrow(Object target, String methodName, Class[] parameterTypes, Object... args) { + try { + Method method = target.getClass().getMethod(methodName, parameterTypes); + return method.invoke(target, args); + } catch (Throwable ex) { + Adapt.verbose("Reflective call failed for method '" + methodName + "' on " + target.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; + } + } + + private static final class RepeatingState { + private volatile boolean cancelled; + } + + private static boolean isPluginActive() { + Adapt adapt = Adapt.instance; + return adapt != null && adapt.isEnabled(); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java index a4f9e75c3..07a12a041 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java @@ -22,6 +22,7 @@ import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.reflect.V; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.command.CommandSender; @@ -174,7 +175,7 @@ private boolean checkPermissions(CommandSender sender, ICommand command2) { for (String i : command.getRequiredPermissions()) { if (!sender.hasPermission(i)) { failed = true; - Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, () -> Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "- " + C.WHITE + i), 0); + J.s(() -> Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "- " + C.WHITE + i)); } } diff --git a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java index 12289cb9f..c033c6224 100644 --- a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java +++ b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java @@ -58,7 +58,8 @@ public class SecretSplash { C.GRAY + "░▓█▒░██▓░▒████▒░██████▒▒██▒ ░ ░ ▒██▒ ░██▒░▒████▒ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + C.GRAY + " ▒ ░░▒░▒░░ ▒░ ░░ ▒░▓ ░▒▓▒░ ░ ░ ░ ▒░ ░ ░░░ ▒░ ░ ", - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣶⣿⣿⣷⣶⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" + + C.GRAY + "⠀⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣶⣿⣿⣷⣶⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣾⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀ ⠀\n" + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡟⠁⣰⣿⣿⣿⡿⠿⠻⠿⣿⣿⣿⣿⣧⠀ ⠀⠀⠀\n" + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⣴⣿⣿⣿⠉⠀⠀⠀⠀⠀⠈⢻⣿⣿⣇⠀⠀⠀ \n" + diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java new file mode 100644 index 000000000..2da16ee6d --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java @@ -0,0 +1,425 @@ +package com.fren_gor.ultimateAdvancementAPI.util; + +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.scheduling.FoliaScheduler; +import com.fren_gor.ultimateAdvancementAPI.AdvancementMain; +import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; +import com.fren_gor.ultimateAdvancementAPI.UltimateAdvancementAPI; +import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; +import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementDisplay; +import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementFrameType; +import com.fren_gor.ultimateAdvancementAPI.database.TeamProgression; +import com.fren_gor.ultimateAdvancementAPI.exceptions.AsyncExecutionException; +import com.fren_gor.ultimateAdvancementAPI.exceptions.UserNotLoadedException; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.VanillaAdvancementDisablerWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementDisplayWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementFrameTypeWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.packets.PacketPlayOutAdvancementsWrapper; +import com.google.common.base.Preconditions; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.ComponentBuilder.FormatRetention; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.GameRule; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class AdvancementUtils { + + /** + * The {@code show_advancement_messages} game rule (previously called {@code announceAdvancements}). + */ + public static final GameRule SHOW_ADVANCEMENT_MESSAGES_GAMERULE = getShowAdvancementMessagesGamerule(); + + public static final MinecraftKeyWrapper ROOT_KEY, NOTIFICATION_KEY; + private static final String ADV_DESCRIPTION = "\n§7A notification."; + private static final AdvancementWrapper ROOT; + + static { + try { + ROOT_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "root"); + NOTIFICATION_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "notification"); + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(new ItemStack(Material.GRASS_BLOCK), "§f§lNotifications§1§2§3§4§5§6§7§8§9§0", "§7Notification page.\n§7Close and reopen advancements to hide.", AdvancementFrameTypeWrapper.TASK, 0, 0, "textures/block/stone.png"); + ROOT = AdvancementWrapper.craftRootAdvancement(ROOT_KEY, display, 1); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + /** + * Displays a custom toast to a player. + * + * @param player A player to show the toast. + * @param icon The displayed item of the toast. + * @param title The displayed title of the toast. + * @param frame The {@link AdvancementFrameType} of the toast. + * @see UltimateAdvancementAPI#displayCustomToast(Player, ItemStack, String, AdvancementFrameType) + */ + public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull AdvancementFrameType frame) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(icon, "Icon is null."); + Preconditions.checkNotNull(title, "Title is null."); + Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); + Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); + + try { + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, ADV_DESCRIPTION, frame.getNMSWrapper(), 1, 0, true, false, false); + AdvancementWrapper notification = AdvancementWrapper.craftBaseAdvancement(NOTIFICATION_KEY, ROOT, display, 1); + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of( + ROOT, 1, + notification, 1 + )).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(ROOT_KEY, NOTIFICATION_KEY)).sendTo(player); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + /*public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull AdvancementFrameType frame, @NotNull Advancement base) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(icon, "Icon is null."); + Preconditions.checkNotNull(title, "Title is null."); + Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); + Preconditions.checkNotNull(base, "Advancement is null."); + Preconditions.checkArgument(base.isValid(), "Advancement isn't valid."); + Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); + + final MinecraftKeyWrapper key = getUniqueKey(base.getAdvancementTab()).getNMSWrapper(); + + try { + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, ADV_DESCRIPTION, frame.getNMSWrapper(), base.getDisplay().getX() + 1, base.getDisplay().getY(), true, false, false); + AdvancementWrapper adv = AdvancementWrapper.craftBaseAdvancement(key, base.getNMSWrapper(), display, 1); + + PacketPlayOutSelectAdvancementTabWrapper.craftSelectNone().sendTo(player); + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of(adv, 1)).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(key)).sendTo(player); + PacketPlayOutSelectAdvancementTabWrapper.craftSelect(key).sendTo(player); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + }*/ + + public static void displayToastDuringUpdate(@NotNull Player player, @NotNull Advancement advancement) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(advancement, "Advancement is null."); + Preconditions.checkArgument(advancement.isValid(), "Advancement isn't valid."); + + final AdvancementDisplay display = advancement.getDisplay(); + final MinecraftKeyWrapper keyWrapper = getUniqueKey(advancement.getAdvancementTab()).getNMSWrapper(); + + try { + AdvancementDisplayWrapper displayWrapper = AdvancementDisplayWrapper.craft(display.getIcon(), display.getTitle(), ADV_DESCRIPTION, display.getFrame().getNMSWrapper(), 0, 0, true, false, false); + AdvancementWrapper advWrapper = AdvancementWrapper.craftBaseAdvancement(keyWrapper, advancement.getNMSWrapper(), displayWrapper, 1); + + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of(advWrapper, 1)).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(keyWrapper)).sendTo(player); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + @NotNull + private static AdvancementKey getUniqueKey(@NotNull AdvancementTab tab) { + final String namespace = tab.getNamespace(); + StringBuilder builder = new StringBuilder("i"); + AdvancementKey key; + while (tab.getAdvancement(key = new AdvancementKey(namespace, builder.toString())) != null) { + builder.append('i'); + } + return key; + } + + /** + * Disables vanilla advancements. + * + * @throws Exception If disabling fails. + * @see UltimateAdvancementAPI#disableVanillaAdvancements() + */ + public static void disableVanillaAdvancements() throws Exception { + VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(true, false); + } + + /** + * Disables vanilla recipe advancements (i.e. the advancements which unlock recipes). + * + * @throws Exception If disabling fails. + * @see UltimateAdvancementAPI#disableVanillaRecipeAdvancements() + */ + public static void disableVanillaRecipeAdvancements() throws Exception { + VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(false, true); + } + + @NotNull + public static BaseComponent[] fromStringList(@NotNull List list) { + return fromStringList(null, list); + } + + @NotNull + public static BaseComponent[] fromStringList(@Nullable String title, @NotNull List list) { + Preconditions.checkNotNull(list); + ComponentBuilder builder = new ComponentBuilder(); + if (title != null) { + builder.append(TextComponent.fromLegacyText(title), FormatRetention.NONE); + if (list.isEmpty()) { + return builder.create(); + } + builder.append("\n", FormatRetention.NONE); + } else if (list.isEmpty()) { + return builder.create(); + } + int i = 0; + for (String s : list) { + builder.append(TextComponent.fromLegacyText(s), FormatRetention.NONE); + if (++i < list.size()) // Don't append \n at the end + builder.append("\n", FormatRetention.NONE); + } + return builder.create(); + } + + public static boolean startsWithEmptyLine(@NotNull String text) { + Preconditions.checkNotNull(text); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '§') { + i++; // Skip next character since it is a color code + } else { + return c == '\n'; + } + } + return false; + } + + @Contract("_ -> param1") + public static int validateProgressionValue(int progression) { + if (progression < 0) { + throw new IllegalArgumentException("Progression value cannot be < 0"); + } + return progression; + } + + public static void validateProgressionValueStrict(int progression, int maxProgression) { + validateProgressionValue(progression); + if (progression > maxProgression) { + throw new IllegalArgumentException("Progression value cannot be greater than the maximum progression (" + maxProgression + ')'); + } + } + + public static void validateIncrement(int increment) { + if (increment <= 0) { + throw new IllegalArgumentException("Increment cannot be zero or less."); + } + } + + @Contract("null -> fail; !null -> param1") + public static TeamProgression validateTeamProgression(TeamProgression pro) { + Preconditions.checkNotNull(pro, "TeamProgression is null."); + Preconditions.checkArgument(pro.isValid(), "Invalid TeamProgression."); + return pro; + } + + public static void checkTeamProgressionNotNull(TeamProgression progression) { + if (progression == null) { + throw new UserNotLoadedException(); + } + } + + public static void checkTeamProgressionNotNull(TeamProgression progression, UUID uuid) { + if (progression == null) { + throw new UserNotLoadedException(uuid); + } + } + + public static void checkSync() { + if (J.isFoliaThreading() || hasFoliaScheduler()) { + return; + } + + if (!Bukkit.isPrimaryThread()) + throw new AsyncExecutionException("Illegal async method call. This method can be called only from the main thread."); + } + + public static void runSync(@NotNull AdvancementMain main, @NotNull Runnable runnable) { + runSync(main.getOwningPlugin(), runnable); + } + + public static void runSync(@NotNull Plugin plugin, @NotNull Runnable runnable) { + runSync(plugin, 1, runnable); + } + + public static void runSync(@NotNull AdvancementMain main, long delay, @NotNull Runnable runnable) { + runSync(main.getOwningPlugin(), delay, runnable); + } + + public static void runSync(@NotNull Plugin plugin, long delay, @NotNull Runnable runnable) { + Preconditions.checkNotNull(plugin, "Plugin is null."); + Preconditions.checkNotNull(runnable, "Runnable is null."); + int safeDelay = sanitizeDelay(delay); + if (hasFoliaScheduler()) { + if (scheduleFoliaSync(plugin, runnable, safeDelay)) { + return; + } + + if (safeDelay <= 0) { + J.s(runnable); + } else { + J.s(runnable, safeDelay); + } + return; + } + + try { + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable, safeDelay); + } catch (UnsupportedOperationException ex) { + // Folia rejects BukkitScheduler sync calls; retry with Adapt's Folia-aware scheduler path. + if (safeDelay <= 0) { + J.s(runnable); + } else { + J.s(runnable, safeDelay); + } + } + } + + private static int sanitizeDelay(long delay) { + if (delay <= 0) { + return 0; + } + + if (delay >= Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + + return (int) delay; + } + + private static boolean scheduleFoliaSync(@NotNull Plugin plugin, @NotNull Runnable runnable, int safeDelay) { + Player player = extractPlayer(runnable); + if (player != null && player.isOnline() && FoliaScheduler.runEntity(plugin, player, runnable, safeDelay)) { + return true; + } + + return FoliaScheduler.runGlobal(plugin, runnable, safeDelay); + } + + private static boolean hasFoliaScheduler() { + return FoliaScheduler.isFolia(Bukkit.getServer()); + } + + @Nullable + private static Player extractPlayer(@NotNull Runnable runnable) { + Class current = runnable.getClass(); + while (current != null && current != Object.class) { + for (Field field : current.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + + try { + field.setAccessible(true); + Object value = field.get(runnable); + Player player = asPlayer(value); + if (player != null) { + return player; + } + } catch (Throwable ex) { + // Ignore inaccessible synthetic fields while walking runnable captures. + } + } + + current = current.getSuperclass(); + } + + return null; + } + + @Nullable + private static Player asPlayer(@Nullable Object value) { + if (value instanceof Player player) { + return player; + } + + if (value instanceof OfflinePlayer offlinePlayer) { + return offlinePlayer.getPlayer(); + } + + if (value instanceof UUID uuid) { + return Bukkit.getPlayer(uuid); + } + + return null; + } + + @NotNull + public static UUID uuidFromPlayer(@NotNull Player player) { + Preconditions.checkNotNull(player, "Player is null."); + return player.getUniqueId(); + } + + @NotNull + public static UUID uuidFromPlayer(@NotNull OfflinePlayer player) { + Preconditions.checkNotNull(player, "OfflinePlayer is null."); + return player.getUniqueId(); + } + + @NotNull + public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull Advancement advancement) { + return progressionFromPlayer(player, advancement.getAdvancementTab()); + } + + @NotNull + public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull Advancement advancement) { + return progressionFromUUID(uuid, advancement.getAdvancementTab()); + } + + @NotNull + public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull AdvancementTab tab) { + return progressionFromUUID(uuidFromPlayer(player), tab); + } + + @NotNull + public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull AdvancementTab tab) { + Preconditions.checkNotNull(uuid, "UUID is null."); + return tab.getDatabaseManager().getTeamProgression(uuid); + } + + @SuppressWarnings("unchecked") + private static GameRule getShowAdvancementMessagesGamerule() { + try { + // Spigot 1.21.11+ + return (GameRule) GameRule.class.getDeclaredField("SHOW_ADVANCEMENT_MESSAGES").get(null); + } catch (NoSuchFieldException e) { + // Spigot <= 1.21.10, Paper all versions + try { + return (GameRule) GameRule.class.getDeclaredField("ANNOUNCE_ADVANCEMENTS").get(null); + } catch (NoSuchFieldException ex) { + return null; + } catch (ReflectiveOperationException inner) { + throw new RuntimeException(inner); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private AdvancementUtils() { + throw new UnsupportedOperationException("Utility class."); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c3a0f114f..4a6310fd0 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,6 +6,7 @@ authors: - Cyberpwn - Vatuu api-version: '${apiVersion}' +folia-supported: true softdepend: - PlaceholderAPI - WorldGuard From d3deef1e27c93df479c0bac1e9588fdd43bf872b Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 16 Feb 2026 20:37:56 -0500 Subject: [PATCH 09/25] Another Overhaul Performance, Readability and, design focused this time --- build.gradle.kts | 2 +- src/main/java/art/arcane/adapt/Adapt.java | 49 +- .../java/art/arcane/adapt/AdaptConfig.java | 2 +- .../java/art/arcane/adapt/PapiExpansion.java | 2 +- .../java/art/arcane/adapt/api/Component.java | 8 +- .../adapt/api/adaptation/Adaptation.java | 1125 ++++++++--------- .../adaptation/AdaptationEventRegistrar.java | 119 ++ .../api/adaptation/AdaptationGuiSupport.java | 461 +++++++ .../adaptation/AdaptationRuntimeGuards.java | 751 +++++++++++ .../adaptation/ReceiveCancelledEvents.java | 33 + .../api/adaptation/SimpleAdaptation.java | 12 +- .../api/advancement/AdaptAdvancement.java | 6 +- .../api/advancement/AdvancementManager.java | 185 ++- .../art/arcane/adapt/api/data/WorldData.java | 6 +- .../notification/AdvancementNotification.java | 3 +- .../adapt/api/notification/Notifier.java | 2 +- .../api/notification/SoundNotification.java | 1 - .../adapt/api/potion/BrewingManager.java | 95 +- .../adapt/api/potion/PotionBuilder.java | 2 +- .../adapt/api/protection/Protector.java | 2 - .../api/protection/ProtectorRegistry.java | 46 +- .../adapt/api/runtime/AdaptationGate.java | 1 + .../arcane/adapt/api/skill/SimpleSkill.java | 89 +- .../art/arcane/adapt/api/skill/Skill.java | 471 ++----- .../adapt/api/skill/SkillEventRegistrar.java | 120 ++ .../adapt/api/skill/SkillGuiSupport.java | 285 +++++ .../arcane/adapt/api/skill/SkillRegistry.java | 34 +- .../adapt/api/skill/SkillRuntimeGuards.java | 222 ++++ .../api/telemetry/AbilityCheckTelemetry.java | 76 ++ .../arcane/adapt/api/tick/TickedObject.java | 1 - .../art/arcane/adapt/api/tick/Ticker.java | 23 +- .../arcane/adapt/api/value/MaterialValue.java | 6 +- .../arcane/adapt/api/version/IAttribute.java | 5 +- .../adapt/api/version/RuntimeAttribute.java | 49 +- .../arcane/adapt/api/world/AdaptPlayer.java | 36 +- .../arcane/adapt/api/world/AdaptServer.java | 13 +- .../adapt/api/world/AdvancementHandler.java | 3 +- .../adapt/api/world/PlayerAdaptation.java | 3 - .../arcane/adapt/api/world/PlayerData.java | 26 +- .../api/world/PlayerDataPersistenceQueue.java | 6 +- .../adapt/api/world/PlayerSkillLine.java | 2 +- .../arcane/adapt/command/CommandAdapt.java | 9 +- .../arcane/adapt/command/CommandClear.java | 4 +- .../arcane/adapt/command/CommandDebug.java | 5 +- .../arcane/adapt/command/CommandDefault.java | 4 +- .../arcane/adapt/command/CommandReset.java | 2 +- .../adaptation/agility/AgilityArmorUp.java | 122 +- .../agility/AgilityLadderSlide.java | 82 +- .../agility/AgilityParkourMomentum.java | 130 +- .../agility/AgilityRollLanding.java | 100 +- .../adaptation/agility/AgilitySuperJump.java | 65 +- .../adaptation/agility/AgilityWallJump.java | 221 ++-- .../adaptation/agility/AgilityWindUp.java | 131 +- .../architect/ArchitectElevator.java | 16 +- .../architect/ArchitectFoundation.java | 237 ++-- .../adaptation/architect/ArchitectGlass.java | 47 +- .../architect/ArchitectPlacement.java | 544 +++++--- .../architect/ArchitectSmartShape.java | 59 +- .../architect/ArchitectWirelessRedstone.java | 83 +- .../adapt/content/adaptation/axe/AxeChop.java | 16 +- .../adaptation/axe/AxeCraftLogSwap.java | 10 +- .../adaptation/axe/AxeDropToInventory.java | 21 +- .../adaptation/axe/AxeGroundSmash.java | 73 +- .../adaptation/axe/AxeLeafVeinminer.java | 23 +- .../content/adaptation/axe/AxeTimberMark.java | 28 +- .../adaptation/axe/AxeWoodVeinminer.java | 21 +- .../blocking/BlockingBastionStance.java | 27 +- .../blocking/BlockingBulwarkBash.java | 31 +- .../blocking/BlockingChainArmorer.java | 8 +- .../blocking/BlockingCounterGuard.java | 19 +- .../blocking/BlockingHorseArmorer.java | 8 +- .../blocking/BlockingMirrorBlock.java | 19 +- .../blocking/BlockingMultiArmor.java | 44 +- .../blocking/BlockingSaddlecrafter.java | 8 +- .../adaptation/brewing/BrewingAbsorption.java | 8 +- .../adaptation/brewing/BrewingBlindness.java | 6 +- .../adaptation/brewing/BrewingDarkness.java | 6 +- .../adaptation/brewing/BrewingDecay.java | 6 +- .../adaptation/brewing/BrewingFatigue.java | 8 +- .../adaptation/brewing/BrewingHaste.java | 8 +- .../brewing/BrewingHealthBoost.java | 8 +- .../adaptation/brewing/BrewingHunger.java | 6 +- .../adaptation/brewing/BrewingLingering.java | 23 +- .../adaptation/brewing/BrewingNausea.java | 8 +- .../adaptation/brewing/BrewingResistance.java | 6 +- .../adaptation/brewing/BrewingSaturation.java | 8 +- .../brewing/BrewingSuperHeated.java | 28 +- .../chronos/ChronosAberrantTouch.java | 29 +- .../chronos/ChronosInstantRecall.java | 58 +- .../adaptation/chronos/ChronosSoundFX.java | 1 - .../chronos/ChronosTemporalEcho.java | 21 +- .../adaptation/chronos/ChronosTimeBomb.java | 98 +- .../chronos/ChronosTimeInABottle.java | 25 +- .../crafting/CraftingBackpacks.java | 5 +- .../crafting/CraftingDeconstruction.java | 9 +- .../adaptation/crafting/CraftingLeather.java | 4 +- .../crafting/CraftingReconstruction.java | 8 +- .../adaptation/crafting/CraftingSkulls.java | 5 +- .../adaptation/crafting/CraftingStations.java | 4 +- .../adaptation/crafting/CraftingXP.java | 21 +- .../discovery/DiscoveryArchaeologist.java | 31 +- .../adaptation/discovery/DiscoveryArmor.java | 22 +- .../discovery/DiscoveryBetterMending.java | 11 +- .../discovery/DiscoveryCartographerPulse.java | 12 +- .../adaptation/discovery/DiscoveryUnity.java | 16 +- .../discovery/DiscoveryVillagerAtt.java | 33 +- .../discovery/DiscoveryXpResist.java | 26 +- .../enchanting/EnchantingAnvilSavant.java | 14 +- .../EnchantingBookshelfAttunement.java | 5 +- .../EnchantingGrindstoneRecovery.java | 13 +- .../enchanting/EnchantingLapisReturn.java | 16 +- .../enchanting/EnchantingOfferReroll.java | 11 +- .../enchanting/EnchantingQuickEnchant.java | 25 +- .../enchanting/EnchantingXPReturn.java | 24 +- .../excavation/ExcavationDropToInventory.java | 20 +- .../excavation/ExcavationHaste.java | 16 +- .../excavation/ExcavationOmniTool.java | 46 +- .../excavation/ExcavationSeismicPing.java | 22 +- .../excavation/ExcavationSpelunker.java | 31 +- .../herbalism/HerbalismBeeShepherd.java | 15 +- .../herbalism/HerbalismCompostCascade.java | 13 +- .../herbalism/HerbalismCraftableCobweb.java | 8 +- .../HerbalismCraftableMushroomBlocks.java | 8 +- .../herbalism/HerbalismDropToInventory.java | 10 +- .../herbalism/HerbalismGrowthAura.java | 25 +- .../herbalism/HerbalismHungryHippo.java | 8 +- .../herbalism/HerbalismHungryShield.java | 10 +- .../adaptation/herbalism/HerbalismLuck.java | 10 +- .../herbalism/HerbalismMyconid.java | 8 +- .../herbalism/HerbalismReplant.java | 19 +- .../herbalism/HerbalismRootedFooting.java | 17 +- .../herbalism/HerbalismSeedSower.java | 10 +- .../herbalism/HerbalismSporeBloom.java | 21 +- .../herbalism/HerbalismTerralid.java | 8 +- .../adaptation/hunter/HunterAdrenaline.java | 26 +- .../hunter/HunterDropToInventory.java | 30 +- .../adaptation/hunter/HunterInvis.java | 8 +- .../adaptation/hunter/HunterJumpBoost.java | 10 +- .../content/adaptation/hunter/HunterLuck.java | 8 +- .../adaptation/hunter/HunterRegen.java | 8 +- .../adaptation/hunter/HunterResistance.java | 10 +- .../adaptation/hunter/HunterSpeed.java | 10 +- .../adaptation/hunter/HunterStrength.java | 10 +- .../hunter/HunterTrophySkinner.java | 9 +- .../adaptation/nether/NetherBlazeLeech.java | 49 +- .../adaptation/nether/NetherFireResist.java | 35 +- .../adaptation/nether/NetherGhastWard.java | 85 +- .../adaptation/nether/NetherLavaWalker.java | 69 +- .../adaptation/nether/NetherPiglinBroker.java | 15 +- .../adaptation/nether/NetherSkullYeet.java | 122 +- .../adaptation/nether/NetherWitherResist.java | 20 +- .../adaptation/pickaxe/PickaxeAutosmelt.java | 13 +- .../adaptation/pickaxe/PickaxeChisel.java | 17 +- .../pickaxe/PickaxeDropToInventory.java | 17 +- .../pickaxe/PickaxeQuarrySense.java | 20 +- .../pickaxe/PickaxeSilkSpawner.java | 12 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 28 +- .../ranged/RangedArrowRecovery.java | 28 +- .../adaptation/ranged/RangedFloaters.java | 27 +- .../adaptation/ranged/RangedForce.java | 58 +- .../adaptation/ranged/RangedLungeShot.java | 20 +- .../adaptation/ranged/RangedPiercing.java | 41 +- .../adaptation/ranged/RangedPinningShot.java | 22 +- .../adaptation/ranged/RangedRicochetBolt.java | 35 +- .../ranged/RangedTrajectorySight.java | 29 +- .../adaptation/ranged/RangedWebBomb.java | 97 +- .../content/adaptation/rift/RiftAccess.java | 20 +- .../content/adaptation/rift/RiftBlink.java | 34 +- .../content/adaptation/rift/RiftDescent.java | 30 +- .../adaptation/rift/RiftEnderTaglock.java | 64 +- .../adaptation/rift/RiftEnderchest.java | 5 +- .../content/adaptation/rift/RiftGate.java | 18 +- .../rift/RiftInflatedPocketDimension.java | 13 +- .../content/adaptation/rift/RiftResist.java | 5 +- .../content/adaptation/rift/RiftVisage.java | 5 +- .../adaptation/rift/RiftVoidMagnet.java | 12 +- .../seaborrne/SeaborneFishersFantasy.java | 35 +- .../adaptation/seaborrne/SeaborneOxygen.java | 36 +- .../seaborrne/SeabornePressureDiver.java | 50 +- .../adaptation/seaborrne/SeaborneSpeed.java | 23 +- .../seaborrne/SeaborneTidecaller.java | 121 +- .../seaborrne/SeaborneTurtlesMiningSpeed.java | 21 +- .../seaborrne/SeaborneTurtlesVision.java | 21 +- .../adaptation/stealth/StealthEnderVeil.java | 18 +- .../adaptation/stealth/StealthGhostArmor.java | 23 +- .../stealth/StealthShadowDecoy.java | 47 +- .../adaptation/stealth/StealthSight.java | 83 +- .../adaptation/stealth/StealthSilentStep.java | 65 +- .../adaptation/stealth/StealthSnatch.java | 24 +- .../adaptation/stealth/StealthSpeed.java | 29 +- .../adaptation/sword/SwordsBloodyBlade.java | 38 +- .../sword/SwordsCrimsonCyclone.java | 36 +- .../adaptation/sword/SwordsDualWield.java | 11 +- .../sword/SwordsExecutionersEdge.java | 21 +- .../adaptation/sword/SwordsMachete.java | 24 +- .../adaptation/sword/SwordsPoisonedBlade.java | 34 +- .../adaptation/sword/SwordsRiposteWindow.java | 31 +- .../adaptation/taming/TamingBeastRecall.java | 11 +- .../adaptation/taming/TamingDamage.java | 23 +- .../adaptation/taming/TamingHealthBoost.java | 17 +- .../taming/TamingHealthRegeneration.java | 49 +- .../taming/TamingMountedTactics.java | 41 +- .../taming/TamingPackLeaderAura.java | 5 +- .../adaptation/taming/TamingSharedPain.java | 11 +- .../adaptation/tragoul/TragoulBloodPact.java | 126 +- .../tragoul/TragoulBoneHarvest.java | 120 +- .../adaptation/tragoul/TragoulGlobe.java | 79 +- .../adaptation/tragoul/TragoulHealing.java | 74 +- .../adaptation/tragoul/TragoulLance.java | 43 +- .../adaptation/tragoul/TragoulThorns.java | 98 +- .../unarmed/UnarmedBatteringCharge.java | 24 +- .../adaptation/unarmed/UnarmedComboChain.java | 18 +- .../unarmed/UnarmedGlassCannon.java | 34 +- .../adaptation/unarmed/UnarmedPower.java | 34 +- .../unarmed/UnarmedSuckerPunch.java | 72 +- .../adapt/content/event/AdaptEvent.java | 1 - .../arcane/adapt/content/gui/ConfigGui.java | 26 +- .../arcane/adapt/content/gui/SkillsGui.java | 18 +- .../adapt/content/item/ChronoTimeBottle.java | 2 +- .../adapt/content/item/ExperienceOrb.java | 3 +- .../adapt/content/item/ItemListings.java | 2 +- .../content/item/multiItems/MultiArmor.java | 2 - .../content/item/multiItems/MultiItem.java | 2 +- .../content/item/multiItems/OmniTool.java | 2 - .../protector/FactionsClaimProtector.java | 2 +- .../protector/GriefDefenderProtector.java | 4 +- .../protector/LocketteProProtector.java | 3 - .../content/protector/ResidenceProtector.java | 8 +- .../protector/WorldGuardProtector.java | 8 +- .../adapt/content/skill/SkillAgility.java | 25 +- .../adapt/content/skill/SkillArchitect.java | 19 +- .../arcane/adapt/content/skill/SkillAxes.java | 19 +- .../adapt/content/skill/SkillBlocking.java | 25 +- .../adapt/content/skill/SkillBrewing.java | 31 +- .../adapt/content/skill/SkillChronos.java | 178 ++- .../adapt/content/skill/SkillCrafting.java | 21 +- .../adapt/content/skill/SkillDiscovery.java | 29 +- .../adapt/content/skill/SkillEnchanting.java | 24 +- .../adapt/content/skill/SkillExcavation.java | 23 +- .../adapt/content/skill/SkillHerbalism.java | 37 +- .../adapt/content/skill/SkillHunter.java | 21 +- .../adapt/content/skill/SkillNether.java | 65 +- .../adapt/content/skill/SkillPickaxes.java | 19 +- .../adapt/content/skill/SkillRanged.java | 23 +- .../arcane/adapt/content/skill/SkillRift.java | 42 +- .../adapt/content/skill/SkillSeaborne.java | 40 +- .../adapt/content/skill/SkillStealth.java | 16 +- .../adapt/content/skill/SkillSwords.java | 24 +- .../adapt/content/skill/SkillTaming.java | 30 +- .../adapt/content/skill/SkillTragOul.java | 26 +- .../adapt/content/skill/SkillUnarmed.java | 22 +- .../adapt/engine/framework/MeteredCache.java | 36 - .../service/AdaptIntegrationService.java | 60 +- .../art/arcane/adapt/service/CommandSVC.java | 16 +- .../arcane/adapt/service/ConfigInputSVC.java | 2 +- .../art/arcane/adapt/service/HotloadSVC.java | 23 +- .../util/common/collection/Dictionary.java | 4 - .../adapt/util/common/collection/GBiset.java | 2 - .../art/arcane/adapt/util/common/data/B.java | 4 +- .../util/common/decree/DecreeContext.java | 2 +- .../common/decree/DecreeContextHandler.java | 4 +- .../util/common/decree/DecreeExecutor.java | 2 +- .../util/common/decree/DecreeSystem.java | 3 +- .../decree/context/WorldContextHandler.java | 2 +- .../handlers/AdaptationListHandler.java | 2 +- .../handlers/AdaptationProviderHandler.java | 2 +- .../handlers/AdaptationSkillListHandler.java | 2 +- .../decree/handlers/BooleanHandler.java | 2 +- .../common/decree/handlers/ByteHandler.java | 2 +- .../common/decree/handlers/DoubleHandler.java | 2 +- .../common/decree/handlers/FloatHandler.java | 2 +- .../decree/handlers/IntegerHandler.java | 2 +- .../common/decree/handlers/LongHandler.java | 2 +- .../decree/handlers/OptionalWorldHandler.java | 2 +- .../decree/handlers/ParticleHandler.java | 2 +- .../common/decree/handlers/PlayerHandler.java | 2 +- .../common/decree/handlers/ShortHandler.java | 2 +- .../decree/handlers/SkillProviderHandler.java | 2 +- .../common/decree/handlers/SoundHandler.java | 2 +- .../common/decree/handlers/StringHandler.java | 2 +- .../common/decree/handlers/WorldHandler.java | 2 +- .../decree/specialhandlers/DummyHandler.java | 2 +- .../NullablePlayerHandler.java | 2 +- .../arcane/adapt/util/common/format/C.java | 3 +- .../adapt/util/common/format/Localizer.java | 6 +- .../adapt/util/common/function/Consumer2.java | 2 - .../adapt/util/common/function/Consumer3.java | 3 - .../adapt/util/common/function/Consumer4.java | 3 - .../adapt/util/common/function/Consumer5.java | 3 - .../adapt/util/common/function/Consumer6.java | 3 - .../adapt/util/common/function/Consumer7.java | 3 - .../adapt/util/common/function/Consumer8.java | 3 - .../adapt/util/common/function/Function2.java | 2 - .../adapt/util/common/function/Function3.java | 3 - .../adapt/util/common/function/Function4.java | 3 - .../util/common/inventorygui/Element.java | 5 +- .../common/inventorygui/ElementEvent.java | 2 - .../util/common/inventorygui/GuiConfirm.java | 6 +- .../util/common/inventorygui/GuiTheme.java | 3 +- .../adapt/util/common/inventorygui/Items.java | 4 +- .../util/common/inventorygui/UIElement.java | 13 +- .../inventorygui/UIStaticDecorator.java | 3 +- .../util/common/inventorygui/UIWindow.java | 3 +- .../adapt/util/common/io/BukkitGson.java | 2 +- .../adapt/util/common/io/ReactiveFolder.java | 3 +- .../adapt/util/common/io/SQLManager.java | 7 +- .../adapt/util/common/mantle/Mantle.java | 10 +- .../adapt/util/common/mantle/MantleChunk.java | 2 +- .../util/common/mantle/TectonicPlate.java | 2 +- .../adapt/util/common/mantle/io/IOWorker.java | 6 +- .../adapt/util/common/math/DataPalette.java | 4 +- .../adapt/util/common/math/Direction.java | 3 +- .../adapt/util/common/math/MathHelper.java | 1 - .../adapt/util/common/math/VectorMath.java | 5 +- .../util/common/misc/AdvancementUtils.java | 4 +- .../arcane/adapt/util/common/misc/Area.java | 3 - .../adapt/util/common/misc/Chunker.java | 5 +- .../adapt/util/common/misc/CustomModel.java | 9 +- .../adapt/util/common/misc/DirtyString.java | 3 +- .../adapt/util/common/misc/Impulse.java | 3 +- .../adapt/util/common/misc/SoundPlayer.java | 2 - .../adapt/util/common/nbt/NibbleArray.java | 4 +- .../util/common/parallel/MultiBurst.java | 1 + .../util/common/plugin/VolmitPlugin.java | 16 +- .../util/common/plugin/VolmitSender.java | 19 +- .../util/common/reflect/WrappedField.java | 2 - .../reflect/WrappedReturningMethod.java | 2 - .../reflect/events/ReflectiveEvents.java | 4 +- .../adapt/util/common/scheduling/J.java | 7 +- .../adapt/util/project/command/Feedback.java | 1 - .../util/project/command/MortarCommand.java | 5 +- .../project/command/MortarPermission.java | 3 +- .../util/project/command/MortarSender.java | 3 +- .../util/project/command/VirtualCommand.java | 9 +- .../project/config/ConfigDocumentation.java | 6 +- .../project/config/ConfigFileSupport.java | 4 +- .../project/config/ConfigRewriteReporter.java | 12 +- .../adapt/util/project/config/TomlCodec.java | 8 +- .../adapt/util/project/redis/RedisSync.java | 4 +- .../util/AdvancementUtils.java | 7 +- 340 files changed, 6444 insertions(+), 5328 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java create mode 100644 src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java create mode 100644 src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java create mode 100644 src/main/java/art/arcane/adapt/api/adaptation/ReceiveCancelledEvents.java create mode 100644 src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java create mode 100644 src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java create mode 100644 src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java create mode 100644 src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java delete mode 100644 src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java diff --git a/build.gradle.kts b/build.gradle.kts index 083f8425a..0dc1a7692 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -172,7 +172,7 @@ dependencies { slimApi(libs.adventure.legacy) slimApi(libs.lettuce) slimApi(libs.particle) - slimApi(libs.ultimateAdvancementApi) + // Keep UAA in the main shaded jar so Folia patches in source overrides take precedence. implementation(libs.ultimateAdvancementApi) slimApi(libs.customBlockData) slimApi(libs.lur) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 53fa926cf..ec98323d5 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -18,31 +18,38 @@ package art.arcane.adapt; -import com.jeff_media.customblockdata.CustomBlockData; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdvancementManager; import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingManager; import art.arcane.adapt.api.protection.ProtectorRegistry; +import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.Ticker; import art.arcane.adapt.api.value.MaterialValue; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptServer; import art.arcane.adapt.api.world.PlayerDataPersistenceQueue; -import art.arcane.adapt.api.adaptation.Adaptation; -import art.arcane.adapt.api.adaptation.SimpleAdaptation; -import art.arcane.adapt.api.skill.SimpleSkill; -import art.arcane.adapt.api.skill.Skill; -import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.content.protector.*; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.io.JarScanner; -import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.adapt.util.config.ConfigMigrationManager; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.io.SQLManager; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.plugin.Metrics; +import art.arcane.adapt.util.common.plugin.VolmitPlugin; +import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.util.config.ConfigMigrationManager; import art.arcane.adapt.util.project.redis.RedisSync; import art.arcane.adapt.util.secret.SecretSplash; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.io.JarScanner; +import com.jeff_media.customblockdata.CustomBlockData; import de.crazydev22.platformutils.AudienceProvider; import de.crazydev22.platformutils.Platform; import de.crazydev22.platformutils.PlatformUtils; @@ -53,27 +60,17 @@ import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; import java.io.*; import java.lang.annotation.Annotation; import java.net.URL; import java.text.MessageFormat; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; import java.util.function.Supplier; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.io.SQLManager; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.plugin.AdaptService; -import art.arcane.adapt.util.common.plugin.Metrics; -import art.arcane.adapt.util.common.plugin.VolmitPlugin; -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.adapt.util.common.scheduling.J; - import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; public class Adapt extends VolmitPlugin { diff --git a/src/main/java/art/arcane/adapt/AdaptConfig.java b/src/main/java/art/arcane/adapt/AdaptConfig.java index f632112ae..a7a2e2399 100644 --- a/src/main/java/art/arcane/adapt/AdaptConfig.java +++ b/src/main/java/art/arcane/adapt/AdaptConfig.java @@ -19,8 +19,8 @@ package art.arcane.adapt; import art.arcane.adapt.api.xp.Curves; -import art.arcane.adapt.util.project.redis.RedisConfig; import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.util.project.redis.RedisConfig; import lombok.Getter; import lombok.Setter; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/PapiExpansion.java b/src/main/java/art/arcane/adapt/PapiExpansion.java index 2afaa208d..d0f5e2eb9 100644 --- a/src/main/java/art/arcane/adapt/PapiExpansion.java +++ b/src/main/java/art/arcane/adapt/PapiExpansion.java @@ -1,11 +1,11 @@ package art.arcane.adapt; -import com.google.common.collect.Maps; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.Localizer; +import com.google.common.collect.Maps; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/art/arcane/adapt/api/Component.java b/src/main/java/art/arcane/adapt/api/Component.java index 4479f8c2c..7a07873f4 100644 --- a/src/main/java/art/arcane/adapt/api/Component.java +++ b/src/main/java/art/arcane/adapt/api/Component.java @@ -18,16 +18,16 @@ package art.arcane.adapt.api; -import com.francobm.magicosmetics.api.CosmeticType; -import com.francobm.magicosmetics.api.MagicAPI; -import com.google.common.collect.Lists; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.value.MaterialValue; import art.arcane.adapt.api.xp.XP; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import com.francobm.magicosmetics.api.CosmeticType; +import com.francobm.magicosmetics.api.MagicAPI; +import com.google.common.collect.Lists; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 6305c5a2b..eb9d492e4 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -17,11 +17,7 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.api.adaptation; -import art.arcane.volmlib.util.format.Form; -import com.google.common.collect.ImmutableSet; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.Component; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.potion.BrewingRecipe; @@ -31,362 +27,599 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.Ticked; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.PlayerAdaptation; -import art.arcane.adapt.api.world.PlayerData; -import art.arcane.adapt.api.world.PlayerSkillLine; -import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; -import art.arcane.volmlib.util.io.IO; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.volmlib.util.math.M; -import org.bukkit.*; -import org.bukkit.block.Block; +import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; -import java.lang.reflect.Field; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntConsumer; import java.util.function.Predicate; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.GuiEffects; -import art.arcane.adapt.util.common.inventorygui.GuiLayout; -import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.adapt.util.common.inventorygui.UIElement; -import art.arcane.adapt.util.common.inventorygui.UIWindow; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - +/** + * Public API for one adaptation under a skill line. + */ public interface Adaptation extends Ticked, Component { - Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); - Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); - long PERMANENT_LEARN_CONFIRM_WINDOW_MS = 6_000L; + /** + * @return maximum unlockable level for this adaptation. + */ int getMaxLevel(); + /** + * Grants adaptation-attributed xp to a player. + */ default void xp(Player p, double amount) { xp(p, amount, null); } + /** + * Grants adaptation-attributed xp to a player with an optional reward key suffix. + */ default void xp(Player p, double amount, String rewardKey) { getSkill().xp(p, amount, adaptationRewardKey(rewardKey)); } + /** + * Grants adaptation-attributed xp and renders it at a world location. + */ default void xp(Player p, Location l, double amount) { xp(p, l, amount, null); } + /** + * Grants adaptation-attributed xp at a location with an optional reward key suffix. + */ default void xp(Player p, Location l, double amount, String rewardKey) { getSkill().xp(p, l, amount, adaptationRewardKey(rewardKey)); } + /** + * Grants silent adaptation-attributed xp with an optional reward key suffix. + */ default void xpSilent(Player p, double amount, String rewardKey) { getSkill().xpSilent(p, amount, adaptationRewardKey(rewardKey)); } + /** + * Grants silent adaptation-attributed xp. + */ default void xpSilent(Player p, double amount) { xpSilent(p, amount, null); } - default String adaptationRewardKey(String rewardKey) { - String suffix = rewardKey == null ? "" : rewardKey.trim(); - if (suffix.isEmpty()) { - suffix = "use"; - } - return "adaptation:" + getName() + ":" + suffix; + /** + * Ensures this logic runs on the player's owned thread/region (Folia-safe). + */ + default void withPlayerThread(Player p, Runnable runnable) { + AdaptationRuntimeGuards.withPlayerThread(this, p, runnable); } - @Override - default boolean areParticlesEnabled() { - if (!Component.super.areParticlesEnabled()) { - return false; - } + /** + * Thread/region guard with optional cancellable event gating. + */ + default void withPlayerThread(Player p, Cancellable cancellable, Runnable runnable) { + AdaptationRuntimeGuards.withPlayerThread(this, p, cancellable, runnable); + } - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - if (effects != null && effects.getAdaptationParticleOverrides() != null && !effects.getAdaptationParticleOverrides().isEmpty()) { - String key = getName(); - Boolean override = effects.getAdaptationParticleOverrides().get(key); - if (override == null && key != null) { - override = effects.getAdaptationParticleOverrides().get(key.toLowerCase(Locale.ROOT)); - } - if (override != null && !override) { - return false; - } - } + /** + * Runs only when player-thread-safe and the player owns this adaptation. + */ + default void withAdaptedPlayer(Player p, Runnable runnable) { + AdaptationRuntimeGuards.withAdaptedPlayer(this, p, runnable); + } - Object config = getConfig(); - if (config == null) { - return true; - } + /** + * Runs only when event is not cancelled, player is thread-safe, and adaptation is owned. + */ + default void withAdaptedPlayer(Player p, Cancellable cancellable, Runnable runnable) { + AdaptationRuntimeGuards.withAdaptedPlayer(this, p, cancellable, runnable); + } - Boolean directToggle = readBooleanField(config, "showParticles"); - if (directToggle != null) { - return directToggle; - } + /** + * Resolves active runtime level and invokes consumer only when level > 0. + */ + default void withActiveLevel(Player p, IntConsumer consumer) { + AdaptationRuntimeGuards.withActiveLevel(this, p, consumer); + } - Boolean genericToggle = readBooleanField(config, "showParticleEffects"); - if (genericToggle != null) { - return genericToggle; - } + /** + * Active-level helper with cancellable event gating. + */ + default void withActiveLevel(Player p, Cancellable cancellable, IntConsumer consumer) { + AdaptationRuntimeGuards.withActiveLevel(this, p, cancellable, consumer); + } - return true; + /** + * Builds an adaptation-scoped reward key (`adaptation::`). + */ + default String adaptationRewardKey(String rewardKey) { + return AdaptationRuntimeGuards.adaptationRewardKey(this, rewardKey); } + /** + * Adaptation-aware particle toggle check (global + skill + adaptation config aware). + */ @Override - default boolean areSoundsEnabled() { - if (!Component.super.areSoundsEnabled()) { - return false; - } - - Object config = getConfig(); - if (config == null) { - return true; - } - - Boolean directToggle = readBooleanField(config, "showSounds"); - if (directToggle != null) { - return directToggle; - } - - return true; + default boolean areParticlesEnabled() { + return AdaptationGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); } - private static Boolean readBooleanField(Object source, String fieldName) { - if (source == null || fieldName == null || fieldName.isBlank()) { - return null; - } - - Class current = source.getClass(); - while (current != null) { - try { - Field field = current.getDeclaredField(fieldName); - field.setAccessible(true); - Object value = field.get(source); - if (value instanceof Boolean bool) { - return bool; - } - return null; - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } catch (Throwable ex) { - Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } - } - - return null; + /** + * Adaptation-aware sound toggle check (global + skill + adaptation config aware). + */ + @Override + default boolean areSoundsEnabled() { + return AdaptationGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); } + /** + * Grants small anti-abuse baseline xp for successful adaptation use. + */ default void awardUsageBaselineXp(Player p, int level) { - if (p == null || level <= 0 || !p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - - AdaptConfig.AdaptationXp cfg = AdaptConfig.get().getAdaptationXp(); - if (cfg == null || !cfg.isUsageBaselineEnabled()) { - return; - } - - long now = M.ms(); - long cooldown = Math.max(250L, cfg.getUsageBaselineCooldownMillis()); - String key = p.getUniqueId() + "|" + getName(); - Long next = USAGE_BASELINE_XP_COOLDOWNS.get(key); - if (next != null && next > now) { - return; - } - - if (USAGE_BASELINE_XP_COOLDOWNS.size() > 6000) { - USAGE_BASELINE_XP_COOLDOWNS.entrySet().removeIf(i -> i.getValue() <= now); - } - - double reward = cfg.getUsageBaselineXp() + ((Math.max(1, level) - 1) * cfg.getUsageBaselineXpPerLevel()); - if (reward <= 0) { - return; - } - - USAGE_BASELINE_XP_COOLDOWNS.put(key, now + cooldown); - xpSilent(p, reward, "baseline-use"); + AdaptationRuntimeGuards.awardUsageBaselineXp(this, p, level); } + /** + * Reads adaptation-scoped per-player storage, falling back to default value. + */ default F getStorage(Player p, String key, F defaultValue) { - PlayerData data = getPlayer(p).getData(); - PlayerSkillLine line = data.getSkillLineNullable(getSkill().getName()); - if (line == null) return defaultValue; - PlayerAdaptation adaptation = line.getAdaptation(getName()); - if (adaptation == null) return defaultValue; - Object o = adaptation.getStorage().get(key); - return o == null ? defaultValue : (F) o; + return AdaptationRuntimeGuards.getStorage(this, p, key, defaultValue); } + /** + * Reads adaptation-scoped per-player storage. + */ default F getStorage(Player p, String key) { return getStorage(p, key, null); } + /** + * Writes adaptation-scoped per-player storage. + */ default boolean setStorage(Player p, String key, Object value) { - PlayerData data = getPlayer(p).getData(); - PlayerSkillLine line = data.getSkillLineNullable(getSkill().getName()); - if (line == null) return false; - PlayerAdaptation adaptation = line.getAdaptation(getName()); - if (adaptation == null) return false; - if (value == null) { - adaptation.getStorage().remove(key); - return true; - } - - adaptation.getStorage().put(key, value); - return true; + return AdaptationRuntimeGuards.setStorage(this, p, key, value); } + /** + * Fires use checks/events for this adaptation against an AdaptPlayer context. + */ default boolean canUse(AdaptPlayer player) { - Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + getName() + "..."); - AdaptAdaptationUseEvent e = new AdaptAdaptationUseEvent(!Bukkit.isPrimaryThread(), player, this); - Bukkit.getServer().getPluginManager().callEvent(e); - return (!e.isCancelled()); + return AdaptationRuntimeGuards.canUse(this, player); } + /** + * Fires use checks/events for this adaptation against a Bukkit player. + */ default boolean canUse(Player player) { - return canUse(getPlayer(player)); + return AdaptationRuntimeGuards.canUse(this, player); } + /** + * Returns true when this player is blacklisted from this adaptation via permission. + */ default boolean hasBlacklistPermission(Player p, Adaptation a) { - if (p.isOp()) { // If the player is an operator, bypass the permission check - return false; - } - String blacklistPermission = "adapt.blacklist." + a.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); - - return p.hasPermission(blacklistPermission); + return AdaptationRuntimeGuards.hasBlacklistPermission(this, p, a); } + /** + * Typed storage convenience helper for strings. + */ default String getStorageString(Player p, String key, String defaultValue) { return getStorage(p, key, defaultValue); } + /** + * Typed storage convenience helper for strings. + */ default String getStorageString(Player p, String key) { return getStorage(p, key); } + /** + * Typed storage convenience helper for integers. + */ default Integer getStorageInt(Player p, String key, Integer defaultValue) { return getStorage(p, key, defaultValue); } + /** + * Typed storage convenience helper for integers. + */ default Integer getStorageInt(Player p, String key) { return getStorage(p, key); } + /** + * Typed storage convenience helper for doubles. + */ default Double getStorageDouble(Player p, String key, Double defaultValue) { return getStorage(p, key, defaultValue); } + /** + * Typed storage convenience helper for doubles. + */ default Double getStorageDouble(Player p, String key) { return getStorage(p, key); } + /** + * Typed storage convenience helper for booleans. + */ default Boolean getStorageBoolean(Player p, String key, Boolean defaultValue) { return getStorage(p, key, defaultValue); } + /** + * Typed storage convenience helper for booleans. + */ default Boolean getStorageBoolean(Player p, String key) { return getStorage(p, key); } + /** + * Typed storage convenience helper for longs. + */ default Long getStorageLong(Player p, String key, Long defaultValue) { return getStorage(p, key, defaultValue); } + /** + * Typed storage convenience helper for longs. + */ default Long getStorageLong(Player p, String key) { return getStorage(p, key); } + /** + * Returns the concrete config class used by this adaptation. + */ Class getConfigurationClass(); + /** + * Registers the config class used for load/reload of this adaptation. + */ void registerConfiguration(Class type); + /** + * @return true when this adaptation is runtime-enabled. + */ boolean isEnabled(); + /** + * @return true when this adaptation cannot be unlearned. + */ boolean isPermanent(); + /** + * Returns the live config instance for this adaptation. + */ T getConfig(); + /** + * Builds the advancement tree node for this adaptation. + */ AdaptAdvancement buildAdvancements(); + /** + * Adds per-level stat lines to GUI elements. + */ void addStats(int level, Element v); + /** + * @return base upgrade cost. + */ int getBaseCost(); + /** + * @return localized adaptation description. + */ String getDescription(); + /** + * @return base icon for this adaptation. + */ Material getIcon(); + /** + * @return owning skill of this adaptation. + */ Skill getSkill(); + /** + * Assigns the owning skill for this adaptation. + */ void setSkill(Skill skill); + /** + * @return internal adaptation key. + */ String getName(); + /** + * @return initial unlock/upgrade cost component. + */ int getInitialCost(); + /** + * @return per-level cost multiplier. + */ double getCostFactor(); + /** + * @return recipes registered by this adaptation. + */ List getRecipes(); + /** + * @return brewing recipes registered by this adaptation. + */ List getBrewingRecipes(); + /** + * Called while building advancement trees to append custom nodes. + */ void onRegisterAdvancements(List advancements); + /** + * Returns the active protector set after applying this adaptation's override config. + */ default Set getProtectors() { - Set protectors = new HashSet<>(Adapt.instance.getProtectorRegistry().getDefaultProtectors()); - Map overrides = AdaptConfig.get().getProtectionOverrides().getOrDefault(this.getName(), Collections.emptyMap()); - overrides.forEach((protector, enabled) -> { - if (enabled) { - Protector p = Adapt.instance.getProtectorRegistry().getAllProtectors() - .stream() - .filter(pr -> pr.getName().equals(protector)) - .findFirst() - .orElse(null); - if (p == null) { - Adapt.error("Could not find protector " + protector + " for adaptation " + this.getName() + ". Skipping..."); - } else { - protectors.add(p); - } - } else { - protectors.removeIf(pr -> pr.getName().equals(protector)); - } - }); - return ImmutableSet.copyOf(protectors); + return AdaptationRuntimeGuards.getProtectors(this); } + /** + * World-policy check for breaking a block. + */ default boolean canBlockBreak(Player player, Location blockLocation) { return evaluateWorldPolicy(protector -> protector.canBlockBreak(player, blockLocation, this)); } + /** + * World-policy check for placing a block. + */ default boolean canBlockPlace(Player player, Location blockLocation) { return evaluateWorldPolicy(protector -> protector.canBlockPlace(player, blockLocation, this)); } + /** + * World-policy check for PVP damage. + */ default boolean canPVP(Player player, Location victimLocation) { return evaluateWorldPolicy(protector -> protector.canPVP(player, victimLocation, this)); } + /** + * World-policy check for PVE damage. + */ default boolean canPVE(Player player, Location victimLocation) { return evaluateWorldPolicy(protector -> protector.canPVE(player, victimLocation, this)); } + /** + * World-policy check for generic interaction. + */ default boolean canInteract(Player player, Location targetLocation) { return evaluateWorldPolicy(protector -> protector.canInteract(player, targetLocation, this)); } + /** + * Returns true when attacker can damage target under current world policies. + */ + default boolean canDamageTarget(Player attacker, Entity target) { + return AdaptationRuntimeGuards.canDamageTarget(this, attacker, target); + } + + /** + * Returns active level only when player is in survival. + */ + default int getActiveSurvivalLevel(Player player) { + return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player); + } + + /** + * Returns active level only when the player meets the supplied requirement. + */ + default int getActiveLevel(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.getActiveLevel(this, player, requirement); + } + + /** + * Returns active-survival level only when the player meets the supplied requirement. + */ + default int getActiveSurvivalLevel(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player, requirement); + } + + /** + * Returns active level gated by interaction permission at a location. + */ + default int getActiveInteractLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveInteractLevel(this, player, location); + } + + /** + * Returns active level gated by block-break permission at a location. + */ + default int getActiveBlockBreakLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveBlockBreakLevel(this, player, location); + } + + /** + * Returns active level gated by block-place permission at a location. + */ + default int getActiveBlockPlaceLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveBlockPlaceLevel(this, player, location); + } + + /** + * Returns active level gated by PVP/PVE policy for a specific target. + */ + default int getActiveDamageLevel(Player attacker, Entity target) { + return AdaptationRuntimeGuards.getActiveDamageLevel(this, attacker, target); + } + + /** + * Resolves a context only when adaptation is active and interact is allowed. + */ + default BlockActionContext resolveInteractContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location); + } + + /** + * Resolves interact context and applies an additional player requirement. + */ + default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement); + } + + /** + * Resolves interact context with optional requirement and survival-only gating. + */ + default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context only when adaptation is active and block break is allowed. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location); + } + + /** + * Resolves block-break context and applies an additional player requirement. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement); + } + + /** + * Resolves block-break context with optional requirement and survival-only gating. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context only when adaptation is active and block place is allowed. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location); + } + + /** + * Resolves block-place context and applies an additional player requirement. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement); + } + + /** + * Resolves block-place context with optional requirement and survival-only gating. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context requiring both interact and block-break permission. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location); + } + + /** + * Resolves interact+break context and applies an additional player requirement. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement); + } + + /** + * Resolves interact+break context with optional requirement and survival-only gating. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement, survivalOnly); + } + + /** + * Returns a validated main-hand item when adaptation is active and requirement passes. + */ + default ItemStack readyMainHand(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.readyMainHand(this, player, requirement); + } + + /** + * Returns a validated active main-hand item without extra requirement checks. + */ + default ItemStack readyMainHand(Player player) { + return readyMainHand(player, null); + } + + /** + * Resolves melee combat context from an entity-damage event. + */ + default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + return AdaptationRuntimeGuards.resolveMeleeContext(this, event, mainHandRequirement); + } + + /** + * Resolves melee combat context from an entity-damage event. + */ + default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event) { + return resolveMeleeContext(event, null); + } + + /** + * Resolves generic player attack context from an entity-damage event. + */ + default AttackContext resolveAttackContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + return AdaptationRuntimeGuards.resolveAttackContext(this, event, mainHandRequirement); + } + + /** + * Resolves generic player attack context from an entity-damage event. + */ + default AttackContext resolveAttackContext(EntityDamageByEntityEvent event) { + return resolveAttackContext(event, null); + } + + /** + * Resolves projectile combat context from an entity-damage event. + */ + default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event, Predicate projectileRequirement) { + return AdaptationRuntimeGuards.resolveProjectileContext(this, event, projectileRequirement); + } + + /** + * Resolves projectile combat context from an entity-damage event. + */ + default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event) { + return resolveProjectileContext(event, null); + } + + /** + * World-policy check for chest access. + */ default boolean canAccessChest(Player player, Location chestLocation) { return evaluateWorldPolicy(protector -> protector.canAccessChest(player, chestLocation, this)); } + /** + * World-policy check for generic region access at the player's current location. + */ default boolean checkRegion(Player player) { return evaluateWorldPolicy(protector -> protector.checkRegion(player, player.getLocation(), this)); } @@ -394,136 +627,79 @@ default boolean checkRegion(Player player) { private boolean evaluateWorldPolicy(Predicate evaluator) { long start = System.nanoTime(); try { - return getProtectors().stream().allMatch(evaluator); + for (Protector protector : getProtectors()) { + if (!evaluator.test(protector)) { + return false; + } + } + return true; } finally { WorldPolicyLatencyTelemetry.recordNanos(System.nanoTime() - start); } } + /** + * Returns true when this adaptation currently conflicts with another active adaptation. + */ default boolean hasUsageConflict(Player p) { - Map> conflicts = AdaptConfig.get().getAdaptationUsageConflicts(); - if (conflicts == null || conflicts.isEmpty()) { - return false; - } - - String me = getName().toLowerCase(Locale.ROOT); - Set denied = new HashSet<>(); - for (Map.Entry> entry : conflicts.entrySet()) { - if (entry.getKey() != null && entry.getValue() != null && entry.getKey().equalsIgnoreCase(me)) { - entry.getValue().stream() - .filter(Objects::nonNull) - .map(i -> i.toLowerCase(Locale.ROOT)) - .forEach(denied::add); - } - } - - for (Map.Entry> entry : conflicts.entrySet()) { - if (entry.getKey() == null || entry.getValue() == null) { - continue; - } - - boolean containsThisAdaptation = entry.getValue().stream() - .filter(Objects::nonNull) - .map(i -> i.toLowerCase(Locale.ROOT)) - .anyMatch(me::equals); - if (containsThisAdaptation) { - denied.add(entry.getKey().toLowerCase(Locale.ROOT)); - } - } - - denied.remove(me); - for (String conflict : denied) { - if (getPlayer(p).hasAdaptation(conflict)) { - Adapt.verbose("Player " + p.getName() + " has conflicting adaptation " + conflict + " and cannot use " + getName()); - return true; - } - } - - return false; + return AdaptationRuntimeGuards.hasUsageConflict(this, p); } + /** + * Runtime-ready level check (ownership + enabled + world/protection/conflict checks). + */ default int getActiveLevel(Player p) { - try { - if (p == null || p.isDead()) { // Check if player is not invalid - return 0; - } + return AdaptationRuntimeGuards.getActiveLevel(this, p); + } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return 0; - } + /** + * Ownership check only (learned level > 0), without runtime gating. + */ + default boolean hasAdaptation(Player p) { + return getLevel(p) > 0; + } - int level = getLevel(p); - if (level <= 0) { - return 0; - } + /** + * Runtime-usable state (ownership + active world/permission/conflict checks). + */ + default boolean hasActiveAdaptation(Player p) { + return getActiveLevel(p) > 0; + } - if (AdaptConfig.get().blacklistedWorlds.contains(p.getWorld().getName())) { - Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + this.getName()); - return 0; - } - if (p.getGameMode().equals(GameMode.CREATIVE) || p.getGameMode().equals(GameMode.SPECTATOR)) { - Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + this.getName()); - return 0; - } - if (!checkRegion(p)) { - Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + this.getName() + " permission."); - return 0; - } + /** + * Generic attack context payload. + */ + record AttackContext(Player attacker, Entity target, ItemStack mainHand, int level) { + } - if (hasBlacklistPermission(p, this)) { - Adapt.verbose("Player " + p.getName() + " has blacklist permission for adaptation " + this.getName()); - return 0; - } - if (hasUsageConflict(p)) { - return 0; - } - if (!canUse(p)) { - Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + this.getName()); - return 0; - } - awardUsageBaselineXp(p, level); - Adapt.verbose("Player " + p.getName() + " used adaptation " + this.getName()); - return level; - } catch (Exception e) { - if (e instanceof IndexOutOfBoundsException) { // This is that fucking bug with Citizens Spoofing Players. I hate it. - Adapt.verbose("Citizens/PacketSpoofing is Messing stuff up again. I hate it."); - Adapt.verbose(e.getMessage()); - } else { - e.printStackTrace(); - } - return 0; - } + /** + * Block-action context payload. + */ + record BlockActionContext(Player player, Location location, int level) { } - default boolean hasAdaptation(Player p) { - return getActiveLevel(p) > 0; + /** + * Melee-specific context payload. + */ + record MeleeContext(Player attacker, LivingEntity target, ItemStack mainHand, int level) { + } + + /** + * Projectile-specific context payload. + */ + record ProjectileContext(Player attacker, LivingEntity target, Projectile projectile, int level) { } + /** + * Raw learned level (ignores runtime gating such as world/protection/conflict). + */ default int getLevel(Player p) { - if (p == null) { - return 0; - } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return 0; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - Adapt.verbose("Simple name: " + p.getClass().getSimpleName()); - return 0; - } - if (!this.isEnabled()) { - return 0; - } - if (!this.getSkill().isEnabled()) { - return 0; - } - AdaptPlayer adaptPlayer = getPlayer(p); - PlayerSkillLine line = adaptPlayer.getData().getSkillLine(getSkill().getName()); - if (line == null) { - return 0; - } - return line.getAdaptationLevel(getName()); + return AdaptationRuntimeGuards.getLevel(this, p); } + /** + * Learned level normalized to 0..1. + */ default double getLevelPercent(Player p) { if (!this.isEnabled()) { return 0; @@ -537,18 +713,30 @@ default double getLevelPercent(Player p) { return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), getLevel(p))), 1); } + /** + * Level normalized to 0..1 using an explicit level value. + */ default double getLevelPercent(int p) { return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), p)), 1); } + /** + * Cost for purchasing exactly this level step. + */ default int getCostFor(int level) { return (int) (Math.max(1, getBaseCost() + (getBaseCost() * (level * getCostFactor())))) + (level == 1 ? getInitialCost() : 0); } + /** + * Power cost delta for level transitions. + */ default int getPowerCostFor(int level, int myLevel) { return level - myLevel; } + /** + * Cumulative cost to move from current level to target level. + */ default int getCostFor(int level, int myLevel) { if (myLevel >= level) { return 0; @@ -564,6 +752,9 @@ default int getCostFor(int level, int myLevel) { return c; } + /** + * Cumulative refund amount when reducing from current level to target level. + */ default int getRefundCostFor(int level, int myLevel) { if (myLevel <= level) { return 0; @@ -578,333 +769,101 @@ default int getRefundCostFor(int level, int myLevel) { return c; } + /** + * UI display name for this adaptation. + */ default String getDisplayName() { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalizeWords(getName().replaceAll("\\Q" + getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); - } - if (!this.getSkill().isEnabled()) { - return C.DARK_GRAY + Form.capitalizeWords(getName().replaceAll("\\Q" + getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); - } - return C.RESET + "" + C.BOLD + getSkill().getColor().toString() + Form.capitalizeWords(getName().replaceAll("\\Q" + getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + return AdaptationGuiSupport.getDisplayName(this); } + /** + * UI display name with level suffix. + */ default String getDisplayName(int level) { - if (!this.isEnabled()) { - return getDisplayName(); - } - if (!this.getSkill().isEnabled()) { - return getDisplayName(); - } - if (level >= 1) { - return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + Form.toRoman(level) + C.RESET; - } - - return getDisplayName(); + return AdaptationGuiSupport.getDisplayName(this, level); } + /** + * UI display name with numeric level but no roman formatting. + */ default String getDisplayNameNoRoman(int level) { - if (level >= 1) { - return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; - } - - return getDisplayName(); + return AdaptationGuiSupport.getDisplayNameNoRoman(this, level); } + /** + * Returns targeted block face from player look direction. + */ default BlockFace getBlockFace(Player player, int maxrange) { - List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, maxrange); - if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) return null; - Block targetBlock = lastTwoTargetBlocks.get(1); - Block adjacentBlock = lastTwoTargetBlocks.get(0); - return targetBlock.getFace(adjacentBlock); + return AdaptationGuiSupport.getBlockFace(player, maxrange); } + /** + * Returns generated custom model binding for this adaptation icon. + */ default CustomModel getModel() { - return CustomModel.get(getIcon(), "adaptation", getName(), "icon"); + return AdaptationGuiSupport.getModel(this); } + /** + * Returns generated custom model binding for a specific adaptation level. + */ default CustomModel getModel(int level) { - var model = CustomModel.get(getIcon(), "adaptation", getName(), "level-" + level); - if (model.material() == getIcon() && model.model() == 0) - model = CustomModel.get(Material.PAPER, "snippets", "gui", "level", String.valueOf(level)); - if (model.material() == Material.PAPER && model.model() == 0) - model = getModel(); - return model; + return AdaptationGuiSupport.getModel(this, level); } + /** + * Opens adaptation GUI and optionally applies blacklist permission checks. + */ default boolean openGui(Player player, boolean checkPermissions) { - if (hasBlacklistPermission(player, this)) { - return false; - } else { - openGui(player); - return true; - } + return AdaptationGuiSupport.openGui(this, player, checkPermissions); } + /** + * Opens page 0 of this adaptation GUI. + */ default void openGui(Player player) { - openGui(player, 0); + AdaptationGuiSupport.openGui(this, player); } + /** + * Opens a specific page of this adaptation GUI. + */ default void openGui(Player player, int page) { - if (!isEnabled()) { - return; - } - if (!getSkill().isEnabled()) { - return; - } - if (!J.isPrimaryThread()) { - int targetPage = page; - J.s(() -> openGui(player, targetPage)); - return; - } - - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); - - boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); - GuiLayout.PagePlan plan = GuiLayout.plan(getMaxLevel(), reserveNavigation); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(getMaxLevel(), start + plan.itemsPerPage()); - - int mylevel = getPlayer(player).getSkillLine(getSkill().getName()).getAdaptationLevel(getName()); - - long k = getPlayer(player).getData().getSkillLine(getSkill().getName()).getKnowledge(); - - Window w = new UIWindow(player); - GuiTheme.apply(w, "skill/" + getSkill().getName() + "/" + getName()); - w.setViewportHeight(plan.rows()); - - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - int lvl = rowStart + i + 1; - int pos = GuiLayout.centeredPosition(i, rowCount); - int c = getCostFor(lvl, mylevel); - int rc = getRefundCostFor(lvl - 1, mylevel); - int pc = getPowerCostFor(lvl, mylevel); - boolean pendingPermanentConfirm = isPermanentLearnConfirmationPending(player, lvl); - Element de = new UIElement("lp-" + lvl + "g") - .setMaterial(new MaterialBlock(getIcon())) - .setModel(getModel(lvl)) - .setName(getDisplayName(lvl)) - .setEnchanted(mylevel >= lvl) - .setProgress(1D) - .addLore(Form.wrapWordsPrefixed(getDescription(), "" + C.GRAY, 40)) - .addLore(mylevel >= lvl ? ("") : ("" + C.WHITE + c + C.GRAY + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost") + " " + (AdaptConfig.get().isHardcoreNoRefunds() ? C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds") : ""))) - .addLore(mylevel >= lvl ? AdaptConfig.get().isHardcoreNoRefunds() ? (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds")) : (isPermanent() ? "" : (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearn_refund") + " " + C.GREEN + rc + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost"))) : (k >= c ? (C.BLUE + Localizer.dLocalize("snippets.adapt_menu.click_learn") + " " + getDisplayName(lvl)) : (k == 0 ? (C.RED + Localizer.dLocalize("snippets.adapt_menu.no_knowledge")) : (C.RED + "(" + Localizer.dLocalize("snippets.adapt_menu.you_only_have") + " " + C.WHITE + k + C.RED + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_available") + ")")))) - .addLore(mylevel < lvl && getPlayer(player).getData().hasPowerAvailable(pc) ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : mylevel >= lvl ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : C.RED + Localizer.dLocalize("snippets.adapt_menu.not_enough_power") + "\n" + C.RED + Localizer.dLocalize("snippets.adapt_menu.how_to_level_up")) - .addLore((isPermanent() ? C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") : "")) - .addLore(isPermanent() && mylevel < lvl - ? (pendingPermanentConfirm - ? C.GOLD + "" + C.BOLD + "Click again now to confirm permanent learn." - : C.YELLOW + "Double-click required to confirm permanent learn.") - : "") - .onLeftClick((e) -> { - if (mylevel >= lvl) { - unlearn(player, lvl, false); - spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); - w.close(); - if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { - if (isPermanent()) { - spw.play(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 0.5f, 1.355f); - player.sendTitle(" ", C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") + " " + getDisplayName(mylevel), 1, 10, 11); - } else { - player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearned") + " " + getDisplayName(mylevel), 1, 10, 11); - } - } - J.s(() -> openGui(player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); - return; - } - - if (k >= c && getPlayer(player).getData().hasPowerAvailable(pc)) { - if (isPermanent() && !consumePermanentLearnConfirmation(player, lvl)) { - spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); - player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); - J.s(() -> openGui(player, currentPage), 1); - return; - } - - if (getPlayer(player).getData().getSkillLine(getSkill().getName()).spendKnowledge(c)) { - getPlayer(player).getData().getSkillLine(getSkill().getName()).setAdaptation(this, lvl); - spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.9f, 1.355f); - spw.play(player.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.7f, 0.355f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_POWER_SELECT, 0.4f, 0.155f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 0.2f, 1.455f); - if (isPermanent()) { - spw.play(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.355f); - spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); - } - w.close(); - if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { - player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + getDisplayName(lvl), 1, 5, 11); - } - J.s(() -> openGui(player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); - } else { - spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); - } - } else { - spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); - } - }); - de.addLore(" "); - addStats(lvl, de); - reveal.add(new GuiEffects.Placement(pos, row, de)); - } - } - GuiEffects.applyReveal(w, reveal); - - if (plan.hasNavigationRow()) { - int navRow = plan.rows() - 1; - int jumpPages = 5; - int jumpBack = Math.max(0, currentPage - jumpPages); - int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); - if (currentPage > 0) { - w.setElement(-4, navRow, new UIElement("adapt-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openGui(player, currentPage - 1)) - .onRightClick((e) -> openGui(player, jumpBack))); - w.setElement(-3, navRow, new UIElement("adapt-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> openGui(player, 0))); - } - if (currentPage < plan.pageCount() - 1) { - w.setElement(4, navRow, new UIElement("adapt-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openGui(player, currentPage + 1)) - .onRightClick((e) -> openGui(player, jumpForward))); - w.setElement(3, navRow, new UIElement("adapt-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> openGui(player, plan.pageCount() - 1))); - } - - int from = getMaxLevel() <= 0 ? 0 : (start + 1); - int to = getMaxLevel() <= 0 ? 0 : end; - w.setElement(-1, navRow, new UIElement("adapt-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + getMaxLevel()) - .setProgress(1D)); - - if (AdaptConfig.get().isGuiBackButton()) { - w.setElement(0, navRow, new UIElement("back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> onGuiClose(player, true))); - } - - } - - AdaptPlayer a = Adapt.instance.getAdaptServer().getPlayer(player); - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(getDisplayName() + " " + C.DARK_GRAY + " " + Form.f(a.getSkillLine(getSkill().getName()).getKnowledge()) + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge") + pageSuffix); - w.onClosed((vv) -> J.s(() -> onGuiClose(player, !AdaptConfig.get().isEscClosesAllGuis()))); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); - } - - private void onGuiClose(Player player, boolean openPrevGui) { - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); - if (openPrevGui) { - getSkill().openGui(player); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); - } - } - - private static String permanentConfirmPrefix(Player player, Adaptation adaptation) { - return player.getUniqueId() + "|" + adaptation.getName() + "|"; - } - - private static String permanentConfirmKey(Player player, Adaptation adaptation, int level) { - return permanentConfirmPrefix(player, adaptation) + level; - } - - private static boolean isPermanentLearnConfirmationPending(Player player, Adaptation adaptation, int level) { - if (player == null || adaptation == null) { - return false; - } - - Long until = PERMANENT_LEARN_CONFIRMATIONS.get(permanentConfirmKey(player, adaptation, level)); - return until != null && until >= M.ms(); + AdaptationGuiSupport.openGui(this, player, page); } + /** + * Checks whether permanent learn confirmation is pending for this player/level. + */ default boolean isPermanentLearnConfirmationPending(Player player, int level) { - return isPermanentLearnConfirmationPending(player, this, level); + return AdaptationGuiSupport.isPermanentLearnConfirmationPending(player, this, level); } + /** + * Consumes pending permanent learn confirmation for this player/level. + */ default boolean consumePermanentLearnConfirmation(Player player, int level) { - if (player == null) { - return false; - } - - long now = M.ms(); - PERMANENT_LEARN_CONFIRMATIONS.entrySet().removeIf(e -> e.getValue() < now); - - String key = permanentConfirmKey(player, this, level); - Long until = PERMANENT_LEARN_CONFIRMATIONS.get(key); - if (until != null && until >= now) { - PERMANENT_LEARN_CONFIRMATIONS.remove(key); - return true; - } - - String prefix = permanentConfirmPrefix(player, this); - PERMANENT_LEARN_CONFIRMATIONS.keySet().removeIf(existing -> existing.startsWith(prefix)); - PERMANENT_LEARN_CONFIRMATIONS.put(key, now + PERMANENT_LEARN_CONFIRM_WINDOW_MS); - return false; + return AdaptationGuiSupport.consumePermanentLearnConfirmation(player, this, level); } + /** + * Unlearns levels from this adaptation. + */ default void unlearn(Player player, int lvl, boolean force) { - if (isPermanent() && !force) { - return; - } - int myLevel = getPlayer(player).getSkillLine(getSkill().getName()).getAdaptationLevel(getName()); - int rc = getRefundCostFor(lvl - 1, myLevel); - if (!AdaptConfig.get().isHardcoreNoRefunds()) { - getPlayer(player).getData().getSkillLine(getSkill().getName()).giveKnowledge(rc); - } - getPlayer(player).getData().getSkillLine(getSkill().getName()).setAdaptation(this, lvl - 1); + AdaptationGuiSupport.unlearn(this, player, lvl, force); } + /** + * Learns levels into this adaptation. + */ default void learn(Player player, int lvl, boolean force) { - int myLevel = getPlayer(player).getSkillLine(getSkill().getName()).getAdaptationLevel(getName()); - int c = getCostFor(lvl, myLevel); - if (getPlayer(player).getData().hasPowerAvailable(c) || force) { - if (getPlayer(player).getData().getSkillLine(getSkill().getName()).spendKnowledge(c) || force) { - getPlayer(player).getData().getSkillLine(getSkill().getName()).setAdaptation(this, lvl); - } - } + AdaptationGuiSupport.learn(this, player, lvl, force); } + /** + * Returns true when the provided recipe belongs to this adaptation. + */ default boolean isAdaptationRecipe(Recipe recipe) { - if (!this.isEnabled()) { - return false; - } - if (!this.getSkill().isEnabled()) { - return false; - } - for (AdaptRecipe i : getRecipes()) { - if (i.is(recipe)) { - return true; - } - } - return false; + return AdaptationGuiSupport.isAdaptationRecipe(this, recipe); } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java new file mode 100644 index 000000000..5d2748117 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java @@ -0,0 +1,119 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.adaptation; + +import art.arcane.adapt.Adapt; +import org.bukkit.Bukkit; +import org.bukkit.event.*; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class AdaptationEventRegistrar { + private AdaptationEventRegistrar() { + } + + public static boolean register(Plugin plugin, Listener listener) { + if (!(listener instanceof Adaptation)) { + return false; + } + + boolean registeredAny = false; + for (Method method : collectHandlerMethods(listener.getClass()).values()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { + continue; + } + + Class parameterType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(parameterType)) { + continue; + } + + @SuppressWarnings("unchecked") + Class eventType = (Class) parameterType; + try { + method.setAccessible(true); + } catch (Throwable ex) { + Adapt.warn("Failed enabling access to adaptation handler " + + listener.getClass().getName() + "#" + method.getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + continue; + } + + EventExecutor executor = (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; + } + + try { + method.invoke(target, event); + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable ex) { + throw new EventException(ex); + } + }; + + boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); + Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); + registeredAny = true; + } + + return registeredAny; + } + + private static Map collectHandlerMethods(Class type) { + Map methods = new LinkedHashMap<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Method method : current.getDeclaredMethods()) { + if (!method.isAnnotationPresent(EventHandler.class)) { + continue; + } + methods.putIfAbsent(signature(method), method); + } + current = current.getSuperclass(); + } + return methods; + } + + private static String signature(Method method) { + return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + } + + private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { + if (!Cancellable.class.isAssignableFrom(eventType)) { + return annotation.ignoreCancelled(); + } + + if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { + return false; + } + + return true; + } +} diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java new file mode 100644 index 000000000..99bc1577f --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -0,0 +1,461 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.adaptation; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.*; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Recipe; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +final class AdaptationGuiSupport { + private static final Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); + private static final long PERMANENT_LEARN_CONFIRM_WINDOW_MS = 6_000L; + + private AdaptationGuiSupport() { + } + + static boolean areParticlesEnabled(Adaptation adaptation, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } + + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + if (effects != null && effects.getAdaptationParticleOverrides() != null && !effects.getAdaptationParticleOverrides().isEmpty()) { + String key = adaptation.getName(); + Boolean override = effects.getAdaptationParticleOverrides().get(key); + if (override == null && key != null) { + override = effects.getAdaptationParticleOverrides().get(key.toLowerCase(Locale.ROOT)); + } + if (override != null && !override) { + return false; + } + } + + Object config = adaptation.getConfig(); + if (config == null) { + return true; + } + + Boolean directToggle = readBooleanField(config, "showParticles"); + if (directToggle != null) { + return directToggle; + } + + Boolean genericToggle = readBooleanField(config, "showParticleEffects"); + if (genericToggle != null) { + return genericToggle; + } + + return true; + } + + static boolean areSoundsEnabled(Adaptation adaptation, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } + + Object config = adaptation.getConfig(); + if (config == null) { + return true; + } + + Boolean directToggle = readBooleanField(config, "showSounds"); + if (directToggle != null) { + return directToggle; + } + + return true; + } + + static String getDisplayName(Adaptation adaptation) { + if (!adaptation.isEnabled()) { + return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + } + if (!adaptation.getSkill().isEnabled()) { + return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + } + return C.RESET + "" + C.BOLD + adaptation.getSkill().getColor().toString() + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + } + + static String getDisplayName(Adaptation adaptation, int level) { + if (!adaptation.isEnabled()) { + return adaptation.getDisplayName(); + } + if (!adaptation.getSkill().isEnabled()) { + return adaptation.getDisplayName(); + } + if (level >= 1) { + return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + Form.toRoman(level) + C.RESET; + } + + return adaptation.getDisplayName(); + } + + static String getDisplayNameNoRoman(Adaptation adaptation, int level) { + if (level >= 1) { + return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; + } + + return adaptation.getDisplayName(); + } + + static BlockFace getBlockFace(Player player, int maxrange) { + List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, maxrange); + if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) return null; + Block targetBlock = lastTwoTargetBlocks.get(1); + Block adjacentBlock = lastTwoTargetBlocks.get(0); + return targetBlock.getFace(adjacentBlock); + } + + static CustomModel getModel(Adaptation adaptation) { + return CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "icon"); + } + + static CustomModel getModel(Adaptation adaptation, int level) { + var model = CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "level-" + level); + if (model.material() == adaptation.getIcon() && model.model() == 0) + model = CustomModel.get(Material.PAPER, "snippets", "gui", "level", String.valueOf(level)); + if (model.material() == Material.PAPER && model.model() == 0) + model = adaptation.getModel(); + return model; + } + + static boolean openGui(Adaptation adaptation, Player player, boolean checkPermissions) { + if (checkPermissions && adaptation.hasBlacklistPermission(player, adaptation)) { + return false; + } else { + openGui(adaptation, player); + return true; + } + } + + static void openGui(Adaptation adaptation, Player player) { + openGui(adaptation, player, 0); + } + + static void openGui(Adaptation adaptation, Player player, int page) { + if (!adaptation.isEnabled()) { + return; + } + if (!adaptation.getSkill().isEnabled()) { + return; + } + if (!J.isPrimaryThread()) { + int targetPage = page; + J.s(() -> openGui(adaptation, player, targetPage)); + return; + } + + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); + + boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); + GuiLayout.PagePlan plan = GuiLayout.plan(adaptation.getMaxLevel(), reserveNavigation); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(adaptation.getMaxLevel(), start + plan.itemsPerPage()); + + int mylevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + + long k = adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).getKnowledge(); + + Window w = new UIWindow(player); + GuiTheme.apply(w, "skill/" + adaptation.getSkill().getName() + "/" + adaptation.getName()); + w.setViewportHeight(plan.rows()); + + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; + } + + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + int lvl = rowStart + i + 1; + int pos = GuiLayout.centeredPosition(i, rowCount); + int c = adaptation.getCostFor(lvl, mylevel); + int rc = adaptation.getRefundCostFor(lvl - 1, mylevel); + int pc = adaptation.getPowerCostFor(lvl, mylevel); + boolean pendingPermanentConfirm = isPermanentLearnConfirmationPending(player, adaptation, lvl); + Element de = new UIElement("lp-" + lvl + "g") + .setMaterial(new MaterialBlock(adaptation.getIcon())) + .setModel(adaptation.getModel(lvl)) + .setName(adaptation.getDisplayName(lvl)) + .setEnchanted(mylevel >= lvl) + .setProgress(1D) + .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 40)) + .addLore(mylevel >= lvl ? ("") : ("" + C.WHITE + c + C.GRAY + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost") + " " + (AdaptConfig.get().isHardcoreNoRefunds() ? C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds") : ""))) + .addLore(mylevel >= lvl ? AdaptConfig.get().isHardcoreNoRefunds() ? (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds")) : (adaptation.isPermanent() ? "" : (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearn_refund") + " " + C.GREEN + rc + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost"))) : (k >= c ? (C.BLUE + Localizer.dLocalize("snippets.adapt_menu.click_learn") + " " + adaptation.getDisplayName(lvl)) : (k == 0 ? (C.RED + Localizer.dLocalize("snippets.adapt_menu.no_knowledge")) : (C.RED + "(" + Localizer.dLocalize("snippets.adapt_menu.you_only_have") + " " + C.WHITE + k + C.RED + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_available") + ")")))) + .addLore(mylevel < lvl && adaptation.getPlayer(player).getData().hasPowerAvailable(pc) ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : mylevel >= lvl ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : C.RED + Localizer.dLocalize("snippets.adapt_menu.not_enough_power") + "\n" + C.RED + Localizer.dLocalize("snippets.adapt_menu.how_to_level_up")) + .addLore((adaptation.isPermanent() ? C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") : "")) + .addLore(adaptation.isPermanent() && mylevel < lvl + ? (pendingPermanentConfirm + ? C.GOLD + "" + C.BOLD + "Click again now to confirm permanent learn." + : C.YELLOW + "Double-click required to confirm permanent learn.") + : "") + .onLeftClick((e) -> { + if (mylevel >= lvl) { + adaptation.unlearn(player, lvl, false); + spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); + w.close(); + if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { + if (adaptation.isPermanent()) { + spw.play(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 0.5f, 1.355f); + player.sendTitle(" ", C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") + " " + adaptation.getDisplayName(mylevel), 1, 10, 11); + } else { + player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearned") + " " + adaptation.getDisplayName(mylevel), 1, 10, 11); + } + } + J.s(() -> openGui(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); + return; + } + + if (k >= c && adaptation.getPlayer(player).getData().hasPowerAvailable(pc)) { + if (adaptation.isPermanent() && !consumePermanentLearnConfirmation(player, adaptation, lvl)) { + spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); + player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); + J.s(() -> openGui(adaptation, player, currentPage), 1); + return; + } + + if (adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).spendKnowledge(c)) { + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl); + spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.9f, 1.355f); + spw.play(player.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.7f, 0.355f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_POWER_SELECT, 0.4f, 0.155f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 0.2f, 1.455f); + if (adaptation.isPermanent()) { + spw.play(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.355f); + spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); + } + w.close(); + if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { + player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + adaptation.getDisplayName(lvl), 1, 5, 11); + } + J.s(() -> openGui(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); + } else { + spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); + } + } else { + spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); + } + }); + de.addLore(" "); + adaptation.addStats(lvl, de); + reveal.add(new GuiEffects.Placement(pos, row, de)); + } + } + GuiEffects.applyReveal(w, reveal); + + if (plan.hasNavigationRow()) { + int navRow = plan.rows() - 1; + int jumpPages = 5; + int jumpBack = Math.max(0, currentPage - jumpPages); + int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); + if (currentPage > 0) { + w.setElement(-4, navRow, new UIElement("adapt-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") + .onLeftClick((e) -> openGui(adaptation, player, currentPage - 1)) + .onRightClick((e) -> openGui(adaptation, player, jumpBack))); + w.setElement(-3, navRow, new UIElement("adapt-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> openGui(adaptation, player, 0))); + } + if (currentPage < plan.pageCount() - 1) { + w.setElement(4, navRow, new UIElement("adapt-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") + .onLeftClick((e) -> openGui(adaptation, player, currentPage + 1)) + .onRightClick((e) -> openGui(adaptation, player, jumpForward))); + w.setElement(3, navRow, new UIElement("adapt-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> openGui(adaptation, player, plan.pageCount() - 1))); + } + + int from = adaptation.getMaxLevel() <= 0 ? 0 : (start + 1); + int to = adaptation.getMaxLevel() <= 0 ? 0 : end; + w.setElement(-1, navRow, new UIElement("adapt-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + adaptation.getMaxLevel()) + .setProgress(1D)); + + if (AdaptConfig.get().isGuiBackButton()) { + w.setElement(0, navRow, new UIElement("back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) + .onLeftClick((e) -> onGuiClose(adaptation, player, true))); + } + + } + + AdaptPlayer a = Adapt.instance.getAdaptServer().getPlayer(player); + String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; + w.setTitle(adaptation.getDisplayName() + " " + C.DARK_GRAY + " " + Form.f(a.getSkillLine(adaptation.getSkill().getName()).getKnowledge()) + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge") + pageSuffix); + w.onClosed((vv) -> J.s(() -> onGuiClose(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); + w.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } + + static boolean isPermanentLearnConfirmationPending(Player player, Adaptation adaptation, int level) { + if (player == null || adaptation == null) { + return false; + } + + Long until = PERMANENT_LEARN_CONFIRMATIONS.get(permanentConfirmKey(player, adaptation, level)); + return until != null && until >= M.ms(); + } + + static boolean consumePermanentLearnConfirmation(Player player, Adaptation adaptation, int level) { + if (player == null) { + return false; + } + + long now = M.ms(); + PERMANENT_LEARN_CONFIRMATIONS.entrySet().removeIf(e -> e.getValue() < now); + + String key = permanentConfirmKey(player, adaptation, level); + Long until = PERMANENT_LEARN_CONFIRMATIONS.get(key); + if (until != null && until >= now) { + PERMANENT_LEARN_CONFIRMATIONS.remove(key); + return true; + } + + String prefix = permanentConfirmPrefix(player, adaptation); + PERMANENT_LEARN_CONFIRMATIONS.keySet().removeIf(existing -> existing.startsWith(prefix)); + PERMANENT_LEARN_CONFIRMATIONS.put(key, now + PERMANENT_LEARN_CONFIRM_WINDOW_MS); + return false; + } + + static void unlearn(Adaptation adaptation, Player player, int lvl, boolean force) { + if (adaptation.isPermanent() && !force) { + return; + } + int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + int rc = adaptation.getRefundCostFor(lvl - 1, myLevel); + if (!AdaptConfig.get().isHardcoreNoRefunds()) { + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).giveKnowledge(rc); + } + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl - 1); + } + + static void learn(Adaptation adaptation, Player player, int lvl, boolean force) { + int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + int c = adaptation.getCostFor(lvl, myLevel); + if (adaptation.getPlayer(player).getData().hasPowerAvailable(c) || force) { + if (adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).spendKnowledge(c) || force) { + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl); + } + } + } + + static boolean isAdaptationRecipe(Adaptation adaptation, Recipe recipe) { + if (!adaptation.isEnabled()) { + return false; + } + if (!adaptation.getSkill().isEnabled()) { + return false; + } + for (AdaptRecipe i : adaptation.getRecipes()) { + if (i.is(recipe)) { + return true; + } + } + return false; + } + + private static Boolean readBooleanField(Object source, String fieldName) { + if (source == null || fieldName == null || fieldName.isBlank()) { + return null; + } + + Class current = source.getClass(); + while (current != null) { + try { + Field field = current.getDeclaredField(fieldName); + field.setAccessible(true); + Object value = field.get(source); + if (value instanceof Boolean bool) { + return bool; + } + return null; + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; + } + } + + return null; + } + + private static void onGuiClose(Adaptation adaptation, Player player, boolean openPrevGui) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); + if (openPrevGui) { + adaptation.getSkill().openGui(player); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static String permanentConfirmPrefix(Player player, Adaptation adaptation) { + return player.getUniqueId() + "|" + adaptation.getName() + "|"; + } + + private static String permanentConfirmKey(Player player, Adaptation adaptation, int level) { + return permanentConfirmPrefix(player, adaptation) + level; + } +} diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java new file mode 100644 index 000000000..5dd4bfc7c --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -0,0 +1,751 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.adaptation; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.protection.Protector; +import art.arcane.adapt.api.telemetry.AbilityCheckTelemetry; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerAdaptation; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.math.M; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntConsumer; +import java.util.function.Predicate; + +final class AdaptationRuntimeGuards { + private static final Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); + private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); + private static final Map PROTECTOR_CACHE = new ConcurrentHashMap<>(); + private static final Map USAGE_CONFLICT_CACHE = new ConcurrentHashMap<>(); + private static final int ACTIVE_LEVEL_CACHE_SOFT_LIMIT = 16_384; + private static final int ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS = 64; + private static final int ACTIVE_LEVEL_CACHE_RETENTION_TICKS = 2; + private static volatile long lastActiveCacheSweepTick = Long.MIN_VALUE; + + private AdaptationRuntimeGuards() { + } + + static void withPlayerThread(Adaptation adaptation, Player p, Runnable runnable) { + try { + if (p == null || runnable == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, () -> withPlayerThread(adaptation, p, runnable)); + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded player runnable for adaptation " + adaptation.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withPlayerThread(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { + try { + if (p == null || cancellable == null || runnable == null) { + return; + } + + if (cancellable.isCancelled()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, () -> withPlayerThread(adaptation, p, cancellable, runnable)); + return; + } + + if (cancellable.isCancelled()) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded cancellable player runnable for adaptation " + adaptation.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withAdaptedPlayer(Adaptation adaptation, Player p, Runnable runnable) { + withPlayerThread(adaptation, p, () -> { + if (!adaptation.hasActiveAdaptation(p)) { + return; + } + runnable.run(); + }); + } + + static void withAdaptedPlayer(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { + withPlayerThread(adaptation, p, cancellable, () -> { + if (!adaptation.hasActiveAdaptation(p)) { + return; + } + runnable.run(); + }); + } + + static void withActiveLevel(Adaptation adaptation, Player p, IntConsumer consumer) { + withPlayerThread(adaptation, p, () -> { + int level = getActiveLevel(adaptation, p); + if (level <= 0) { + return; + } + consumer.accept(level); + }); + } + + static void withActiveLevel(Adaptation adaptation, Player p, Cancellable cancellable, IntConsumer consumer) { + withPlayerThread(adaptation, p, cancellable, () -> { + int level = getActiveLevel(adaptation, p); + if (level <= 0) { + return; + } + consumer.accept(level); + }); + } + + static String adaptationRewardKey(Adaptation adaptation, String rewardKey) { + String suffix = rewardKey == null ? "" : rewardKey.trim(); + if (suffix.isEmpty()) { + suffix = "use"; + } + return "adaptation:" + adaptation.getName() + ":" + suffix; + } + + static void awardUsageBaselineXp(Adaptation adaptation, Player p, int level) { + if (p == null || level <= 0 || !p.getClass().getSimpleName().equals("CraftPlayer")) { + return; + } + + AdaptConfig.AdaptationXp cfg = AdaptConfig.get().getAdaptationXp(); + if (cfg == null || !cfg.isUsageBaselineEnabled()) { + return; + } + + long now = M.ms(); + long cooldown = Math.max(250L, cfg.getUsageBaselineCooldownMillis()); + String key = p.getUniqueId() + "|" + adaptation.getName(); + Long next = USAGE_BASELINE_XP_COOLDOWNS.get(key); + if (next != null && next > now) { + return; + } + + if (USAGE_BASELINE_XP_COOLDOWNS.size() > 6000) { + USAGE_BASELINE_XP_COOLDOWNS.entrySet().removeIf(i -> i.getValue() <= now); + } + + double reward = cfg.getUsageBaselineXp() + ((Math.max(1, level) - 1) * cfg.getUsageBaselineXpPerLevel()); + if (reward <= 0) { + return; + } + + USAGE_BASELINE_XP_COOLDOWNS.put(key, now + cooldown); + adaptation.xpSilent(p, reward, "baseline-use"); + } + + static boolean canUse(Adaptation adaptation, AdaptPlayer player) { + Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + adaptation.getName() + "..."); + AdaptAdaptationUseEvent e = new AdaptAdaptationUseEvent(!Bukkit.isPrimaryThread(), player, adaptation); + Bukkit.getServer().getPluginManager().callEvent(e); + return (!e.isCancelled()); + } + + static boolean canUse(Adaptation adaptation, Player player) { + return canUse(adaptation, adaptation.getPlayer(player)); + } + + static boolean hasBlacklistPermission(Adaptation adaptation, Player p, Adaptation targetAdaptation) { + if (p.isOp()) { + return false; + } + Adaptation target = targetAdaptation == null ? adaptation : targetAdaptation; + String blacklistPermission = "adapt.blacklist." + target.getName().replaceAll("-", ""); + Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); + + return p.hasPermission(blacklistPermission); + } + + static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity target) { + if (attacker == null || target == null) { + return false; + } + + if (target instanceof Player victim) { + return adaptation.canPVP(attacker, victim.getLocation()); + } + + return adaptation.canPVE(attacker, target.getLocation()); + } + + static int getActiveSurvivalLevel(Adaptation adaptation, Player player) { + if (player == null || player.getGameMode() != GameMode.SURVIVAL) { + return 0; + } + + return getActiveLevel(adaptation, player); + } + + static int getActiveLevel(Adaptation adaptation, Player player, Predicate requirement) { + int level = getActiveLevel(adaptation, player); + if (level <= 0) { + return 0; + } + + if (requirement != null && !requirement.test(player)) { + return 0; + } + + return level; + } + + static int getActiveSurvivalLevel(Adaptation adaptation, Player player, Predicate requirement) { + int level = getActiveSurvivalLevel(adaptation, player); + if (level <= 0) { + return 0; + } + + if (requirement != null && !requirement.test(player)) { + return 0; + } + + return level; + } + + static int getActiveInteractLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; + } + + return adaptation.canInteract(player, location) ? level : 0; + } + + static int getActiveBlockBreakLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; + } + + return adaptation.canBlockBreak(player, location) ? level : 0; + } + + static int getActiveBlockPlaceLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; + } + + return adaptation.canBlockPlace(player, location) ? level : 0; + } + + static int getActiveDamageLevel(Adaptation adaptation, Player attacker, Entity target) { + int level = getActiveLevel(adaptation, attacker); + if (level <= 0 || target == null) { + return 0; + } + + return canDamageTarget(adaptation, attacker, target) ? level : 0; + } + + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location) { + return resolveInteractContext(adaptation, player, location, null, false); + } + + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveInteractContext(adaptation, player, location, requirement, false); + } + + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canInteract(player, location)) { + return null; + } + + return new Adaptation.BlockActionContext(player, location, level); + } + + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location) { + return resolveBlockBreakContext(adaptation, player, location, null, false); + } + + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveBlockBreakContext(adaptation, player, location, requirement, false); + } + + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canBlockBreak(player, location)) { + return null; + } + + return new Adaptation.BlockActionContext(player, location, level); + } + + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location) { + return resolveBlockPlaceContext(adaptation, player, location, null, false); + } + + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveBlockPlaceContext(adaptation, player, location, requirement, false); + } + + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canBlockPlace(player, location)) { + return null; + } + + return new Adaptation.BlockActionContext(player, location, level); + } + + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location) { + return resolveInteractBreakContext(adaptation, player, location, null, false); + } + + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveInteractBreakContext(adaptation, player, location, requirement, false); + } + + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + Adaptation.BlockActionContext context = resolveInteractContext(adaptation, player, location, requirement, survivalOnly); + if (context == null || !adaptation.canBlockBreak(context.player(), context.location())) { + return null; + } + + return context; + } + + static ItemStack readyMainHand(Adaptation adaptation, Player player, Predicate requirement) { + if (player == null) { + return null; + } + + ItemStack hand = player.getInventory().getItemInMainHand(); + if (!adaptation.isItem(hand)) { + return null; + } + + if (requirement != null && (!requirement.test(hand) || player.hasCooldown(hand.getType()))) { + return null; + } + + return hand; + } + + static Adaptation.MeleeContext resolveMeleeContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + Adaptation.AttackContext attack = resolveAttackContext(adaptation, event, mainHandRequirement); + if (attack == null || !(attack.target() instanceof LivingEntity target)) { + return null; + } + + return new Adaptation.MeleeContext(attack.attacker(), target, attack.mainHand(), attack.level()); + } + + static Adaptation.AttackContext resolveAttackContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + if (event == null || !(event.getDamager() instanceof Player attacker)) { + return null; + } + + int level = getActiveLevel(adaptation, attacker); + if (level <= 0) { + return null; + } + + ItemStack hand = readyMainHand(adaptation, attacker, mainHandRequirement); + if (mainHandRequirement != null && hand == null) { + return null; + } + + Entity target = event.getEntity(); + if (!canDamageTarget(adaptation, attacker, target)) { + return null; + } + + return new Adaptation.AttackContext(attacker, target, hand, level); + } + + static Adaptation.ProjectileContext resolveProjectileContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate projectileRequirement) { + if (event == null + || !(event.getDamager() instanceof Projectile projectile) + || !(projectile.getShooter() instanceof Player attacker) + || !(event.getEntity() instanceof LivingEntity target)) { + return null; + } + + if (projectileRequirement != null && !projectileRequirement.test(projectile)) { + return null; + } + + int level = getActiveLevel(adaptation, attacker); + if (level <= 0) { + return null; + } + + if (!canDamageTarget(adaptation, attacker, target)) { + return null; + } + + return new Adaptation.ProjectileContext(attacker, target, projectile, level); + } + + static boolean hasUsageConflict(Adaptation adaptation, Player p) { + if (adaptation == null || p == null) { + return false; + } + + Set denied = resolveUsageConflicts(adaptation); + if (denied.isEmpty()) { + return false; + } + + AdaptPlayer adaptPlayer = adaptation.getPlayer(p); + for (String conflict : denied) { + if (adaptPlayer.hasAdaptation(conflict)) { + Adapt.verbose("Player " + p.getName() + " has conflicting adaptation " + conflict + " and cannot use " + adaptation.getName()); + return true; + } + } + + return false; + } + + static Set getProtectors(Adaptation adaptation) { + if (adaptation == null) { + return Collections.emptySet(); + } + + List defaults = Adapt.instance.getProtectorRegistry().getDefaultProtectors(); + List allProtectors = Adapt.instance.getProtectorRegistry().getAllProtectors(); + Map overrides = AdaptConfig.get().getProtectionOverrides().getOrDefault(adaptation.getName(), Collections.emptyMap()); + int signature = buildProtectorSignature(defaults, allProtectors, overrides); + String cacheKey = adaptation.getName(); + ProtectorCacheEntry cached = PROTECTOR_CACHE.get(cacheKey); + if (cached != null && cached.signature() == signature) { + return cached.protectors(); + } + + Map byName = new HashMap<>(); + for (Protector protector : allProtectors) { + byName.put(protector.getName(), protector); + } + + Set resolved = new HashSet<>(defaults); + for (Map.Entry entry : overrides.entrySet()) { + String protectorName = entry.getKey(); + Boolean enabled = entry.getValue(); + if (protectorName == null || enabled == null) { + continue; + } + + if (enabled) { + Protector protector = byName.get(protectorName); + if (protector == null) { + Adapt.error("Could not find protector " + protectorName + " for adaptation " + adaptation.getName() + ". Skipping..."); + continue; + } + resolved.add(protector); + continue; + } + + resolved.removeIf(existing -> existing.getName().equals(protectorName)); + } + + Set immutable = Collections.unmodifiableSet(new HashSet<>(resolved)); + PROTECTOR_CACHE.put(cacheKey, new ProtectorCacheEntry(signature, immutable)); + return immutable; + } + + static int getActiveLevel(Adaptation adaptation, Player p) { + try { + if (p == null || p.isDead()) { + return 0; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } + + AbilityCheckTelemetry.recordCheckAttempt(); + long tick = M.tick; + String key = cacheKey(adaptation, p); + ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); + if (cached != null && cached.tick() == tick) { + if (cached.level() > 0) { + AbilityCheckTelemetry.recordSuccessfulCheck(); + } + return cached.level(); + } + + int level = resolveActiveLevelUncached(adaptation, p); + ACTIVE_LEVEL_CACHE.put(key, new ActiveLevelCacheEntry(tick, level)); + sweepActiveLevelCache(tick); + + if (level > 0) { + AbilityCheckTelemetry.recordSuccessfulCheck(); + } + return level; + } catch (Exception e) { + if (e instanceof IndexOutOfBoundsException) { + Adapt.verbose("Citizens/PacketSpoofing is Messing stuff up again. I hate it."); + Adapt.verbose(e.getMessage()); + } else { + e.printStackTrace(); + } + return 0; + } + } + + static int getLevel(Adaptation adaptation, Player p) { + if (p == null) { + return 0; + } + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } + if (!p.getClass().getSimpleName().equals("CraftPlayer")) { + Adapt.verbose("Simple name: " + p.getClass().getSimpleName()); + return 0; + } + if (!adaptation.isEnabled()) { + return 0; + } + if (!adaptation.getSkill().isEnabled()) { + return 0; + } + AdaptPlayer adaptPlayer = adaptation.getPlayer(p); + PlayerSkillLine line = adaptPlayer.getData().getSkillLine(adaptation.getSkill().getName()); + if (line == null) { + return 0; + } + return line.getAdaptationLevel(adaptation.getName()); + } + + static F getStorage(Adaptation adaptation, Player p, String key, F defaultValue) { + PlayerData data = adaptation.getPlayer(p).getData(); + PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); + if (line == null) return defaultValue; + PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); + if (playerAdaptation == null) return defaultValue; + Object o = playerAdaptation.getStorage().get(key); + return o == null ? defaultValue : (F) o; + } + + static boolean setStorage(Adaptation adaptation, Player p, String key, Object value) { + PlayerData data = adaptation.getPlayer(p).getData(); + PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); + if (line == null) return false; + PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); + if (playerAdaptation == null) return false; + if (value == null) { + playerAdaptation.getStorage().remove(key); + return true; + } + + playerAdaptation.getStorage().put(key, value); + return true; + } + + private static int resolveActionLevel(Adaptation adaptation, Player player, Predicate requirement, boolean survivalOnly) { + if (survivalOnly) { + return getActiveSurvivalLevel(adaptation, player, requirement); + } + + return getActiveLevel(adaptation, player, requirement); + } + + private static int resolveActiveLevelUncached(Adaptation adaptation, Player p) { + int level = adaptation.getLevel(p); + if (level <= 0) { + return 0; + } + + if (AdaptConfig.get().blacklistedWorlds.contains(p.getWorld().getName())) { + Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + adaptation.getName()); + return 0; + } + if (p.getGameMode().equals(GameMode.CREATIVE) || p.getGameMode().equals(GameMode.SPECTATOR)) { + Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + adaptation.getName()); + return 0; + } + if (!adaptation.checkRegion(p)) { + Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + adaptation.getName() + " permission."); + return 0; + } + + if (adaptation.hasBlacklistPermission(p, adaptation)) { + Adapt.verbose("Player " + p.getName() + " has blacklist permission for adaptation " + adaptation.getName()); + return 0; + } + if (hasUsageConflict(adaptation, p)) { + return 0; + } + if (!adaptation.canUse(p)) { + Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + adaptation.getName()); + return 0; + } + + awardUsageBaselineXp(adaptation, p, level); + Adapt.verbose("Player " + p.getName() + " used adaptation " + adaptation.getName()); + return level; + } + + private static String cacheKey(Adaptation adaptation, Player player) { + return player.getUniqueId() + "|" + adaptation.getName(); + } + + private static void sweepActiveLevelCache(long tick) { + if (ACTIVE_LEVEL_CACHE.size() <= ACTIVE_LEVEL_CACHE_SOFT_LIMIT) { + return; + } + + long previousSweep = lastActiveCacheSweepTick; + if (previousSweep != Long.MIN_VALUE && tick - previousSweep < ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS) { + return; + } + lastActiveCacheSweepTick = tick; + + long minTick = tick - ACTIVE_LEVEL_CACHE_RETENTION_TICKS; + ACTIVE_LEVEL_CACHE.entrySet().removeIf(entry -> entry.getValue().tick() < minTick); + } + + private static Set resolveUsageConflicts(Adaptation adaptation) { + Map> conflicts = AdaptConfig.get().getAdaptationUsageConflicts(); + if (conflicts == null || conflicts.isEmpty()) { + return Collections.emptySet(); + } + + String me = adaptation.getName().toLowerCase(Locale.ROOT); + int signature = buildUsageConflictSignature(conflicts); + UsageConflictCacheEntry cached = USAGE_CONFLICT_CACHE.get(me); + if (cached != null && cached.signature() == signature) { + return cached.denied(); + } + + Set denied = new HashSet<>(); + for (Map.Entry> entry : conflicts.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + if (key == null || values == null) { + continue; + } + + if (key.equalsIgnoreCase(me)) { + for (String value : values) { + if (value != null) { + denied.add(value.toLowerCase(Locale.ROOT)); + } + } + continue; + } + + boolean containsThisAdaptation = false; + for (String value : values) { + if (value != null && me.equals(value.toLowerCase(Locale.ROOT))) { + containsThisAdaptation = true; + break; + } + } + if (containsThisAdaptation) { + denied.add(key.toLowerCase(Locale.ROOT)); + } + } + + denied.remove(me); + Set immutable = Collections.unmodifiableSet(new HashSet<>(denied)); + USAGE_CONFLICT_CACHE.put(me, new UsageConflictCacheEntry(signature, immutable)); + return immutable; + } + + private static int buildUsageConflictSignature(Map> conflicts) { + int hash = 1; + for (Map.Entry> entry : conflicts.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + int local = 0; + if (key != null) { + local += key.toLowerCase(Locale.ROOT).hashCode(); + } + if (values != null) { + local += values.size() * 17; + for (String value : values) { + if (value != null) { + local += value.toLowerCase(Locale.ROOT).hashCode(); + } + } + } + hash += local; + } + return hash; + } + + private static int buildProtectorSignature(List defaults, List allProtectors, Map overrides) { + int hash = 1; + hash += defaults.size() * 31; + for (Protector protector : defaults) { + hash += protector.getName().hashCode(); + } + hash += allProtectors.size() * 17; + for (Protector protector : allProtectors) { + hash += protector.getName().hashCode(); + } + hash += overrides.size() * 13; + for (Map.Entry entry : overrides.entrySet()) { + String name = entry.getKey(); + Boolean enabled = entry.getValue(); + if (name != null) { + hash += name.hashCode(); + } + if (enabled != null) { + hash += enabled ? 1 : 2; + } + } + return hash; + } + + private record ActiveLevelCacheEntry(long tick, int level) { + } + + private record ProtectorCacheEntry(int signature, Set protectors) { + } + + private record UsageConflictCacheEntry(int signature, Set denied) { + } +} diff --git a/src/main/java/art/arcane/adapt/api/adaptation/ReceiveCancelledEvents.java b/src/main/java/art/arcane/adapt/api/adaptation/ReceiveCancelledEvents.java new file mode 100644 index 000000000..0b60a0307 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/adaptation/ReceiveCancelledEvents.java @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.adaptation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Opt-out marker for adaptation event handlers that must run even when the + * event is already cancelled. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ReceiveCancelledEvents { +} diff --git a/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java index 9b81d1f98..50aa38bd6 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java @@ -27,16 +27,15 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.TickedObject; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.volmlib.util.collection.KList; import lombok.Data; import lombok.EqualsAndHashCode; import org.bukkit.Material; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.collection.KList; - import java.io.File; import java.io.IOException; import java.lang.reflect.Field; @@ -44,9 +43,6 @@ import java.util.List; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; - @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleAdaptation extends TickedObject implements Adaptation { diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java index 8833bea3f..24df2343a 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java @@ -19,15 +19,15 @@ package art.arcane.adapt.api.advancement; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.collection.KList; import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementDisplay; import com.fren_gor.ultimateAdvancementAPI.database.TeamProgression; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.volmlib.util.collection.KList; import lombok.Builder; import lombok.Data; import lombok.Singular; diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index 7eb1084ae..eaf2b5d21 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -1,18 +1,22 @@ package art.arcane.adapt.api.advancement; -import com.fren_gor.ultimateAdvancementAPI.AdvancementMain; -import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; -import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; -import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; -import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdvancementHandler; import art.arcane.adapt.util.common.scheduling.J; +import com.fren_gor.ultimateAdvancementAPI.AdvancementMain; +import com.fren_gor.ultimateAdvancementAPI.AdvancementTab; +import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; +import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; +import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import static art.arcane.adapt.Adapt.instance; @@ -22,6 +26,7 @@ public class AdvancementManager { private final Map advancements; private final AtomicBoolean loaded = new AtomicBoolean(false); private final AtomicBoolean enabled = new AtomicBoolean(false); + private final AtomicBoolean runtimeSchedulerUnsupported = new AtomicBoolean(false); public AdvancementManager() { AdvancementMain loadedMain = null; @@ -35,7 +40,7 @@ public AdvancementManager() { } main = loadedMain; - advancements = new HashMap<>(); + advancements = new ConcurrentHashMap<>(); } AdvancementTab createAdvancementTab(String namespace) { @@ -49,7 +54,7 @@ AdvancementTab createAdvancementTab(String namespace) { public void grant(AdaptPlayer player, String key, boolean toast) { player.getData().ensureGranted(key); Player p = player.getPlayer(); - if (!AdaptConfig.get().isAdvancements() || !enabled.get() || p == null || !p.isOnline()) return; + if (!AdaptConfig.get().isAdvancements() || !enabled.get() || runtimeSchedulerUnsupported.get() || p == null || !p.isOnline()) return; Advancement advancement = advancements.get(key); if (advancement == null) { Adapt.verbose("Advancement key '" + key + "' is not registered; skipping grant."); @@ -61,30 +66,105 @@ public void grant(AdaptPlayer player, String key, boolean toast) { return; } - try { - advancement.grant(p, true); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + p.getName() + "."); + attemptGrant(p, advancement, key, toast, true); + }, 5); + } + + private void attemptGrant(Player player, Advancement advancement, String key, boolean toast, boolean allowRetryOnGlobal) { + if (player == null || !player.isOnline()) { + return; + } + + try { + advancement.grant(player, true); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; + } + + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; + } + + if (allowRetryOnGlobal) { + J.s(() -> attemptGrant(player, advancement, key, toast, false), 1); return; } + } + + Adapt.warn("Failed to grant advancement '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + return; + } - Adapt.warn("Failed to grant advancement '" + key + "' for " + p.getName() + ": " + summarizeThrowable(t)); + if (!toast) { + return; + } + + try { + advancement.displayToastToPlayer(player); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; } - if (toast) { - try { - advancement.displayToastToPlayer(p); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + p.getName() + "."); - return; - } + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; + } - Adapt.warn("Failed to display advancement toast '" + key + "' for " + p.getName() + ": " + summarizeThrowable(t)); + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false), 1); + return; } } - }, 5); + + Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + } + } + + private void attemptToast(Player player, Advancement advancement, String key, boolean allowRetryOnGlobal) { + if (player == null || !player.isOnline()) { + return; + } + + try { + advancement.displayToastToPlayer(player); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; + } + + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; + } + + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false), 1); + return; + } + } + + Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + } + } + + private void markRuntimeSchedulerUnsupported(Throwable throwable) { + if (!runtimeSchedulerUnsupported.compareAndSet(false, true)) { + return; + } + + Adapt.info("UltimateAdvancementAPI live packet grants/toasts are unavailable on this Folia runtime; stored advancement grants will continue without live packets/toasts."); + if (throwable != null) { + Adapt.verbose("UltimateAdvancementAPI fallback cause: " + summarizeThrowable(throwable)); + } } private boolean isUserNotLoadedError(Throwable throwable) { @@ -100,6 +180,31 @@ private boolean isUserNotLoadedError(Throwable throwable) { return false; } + private boolean isSchedulerContextMismatch(Throwable throwable) { + Throwable current = throwable; + while (current != null) { + if (current instanceof UnsupportedOperationException) { + return true; + } + + String message = current.getMessage(); + if (message != null) { + String lower = message.toLowerCase(Locale.ROOT); + if (lower.contains("thread") + || lower.contains("scheduler") + || lower.contains("region") + || lower.contains("primary thread") + || lower.contains("asynchronously")) { + return true; + } + } + + current = current.getCause(); + } + + return false; + } + private String summarizeThrowable(Throwable throwable) { if (throwable == null) { return "unknown"; @@ -127,13 +232,22 @@ private void appendMessage(StringBuilder builder, String message) { } } - public void unlockExisting(AdaptPlayer player) { + public void unlockExisting(AdaptPlayer player, AdvancementHandler handler) { if (!AdaptConfig.get().isAdvancements() || !enabled.get()) return; + if (player == null || handler == null) { + return; + } + Player target = player.getPlayer(); if (target == null || !target.isOnline()) { return; } + if (runtimeSchedulerUnsupported.get()) { + handler.setReady(true); + return; + } + J.runEntity(target, () -> { instance.getAdaptServer() .getSkillRegistry() @@ -142,7 +256,7 @@ public void unlockExisting(AdaptPlayer player) { .map(Skill::buildAdvancements) .forEach(aa -> unlockExisting(player, aa)); - player.getAdvancementHandler().setReady(true); + handler.setReady(true); }, 20); } @@ -163,6 +277,8 @@ public void enable() { return; } + runtimeSchedulerUnsupported.set(false); + if (loaded.compareAndSet(false, true)) main.load(); @@ -175,6 +291,10 @@ public void enable() { main.enableSQLite(instance.getDataFile("data", "advancements.db")); } + if (J.isFoliaThreading() && isLegacyAsyncSchedulerUnsupported()) { + markRuntimeSchedulerUnsupported(null); + } + for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { AdaptAdvancement aa = i.buildAdvancements(); Set set = new HashSet<>(); @@ -198,11 +318,26 @@ public void disable() { if (main == null) { enabled.set(false); loaded.set(false); + runtimeSchedulerUnsupported.set(false); return; } main.disable(); enabled.set(false); loaded.set(false); + runtimeSchedulerUnsupported.set(false); + } + + private boolean isLegacyAsyncSchedulerUnsupported() { + try { + BukkitTask probe = Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { + }, 1L, 1L); + probe.cancel(); + return false; + } catch (UnsupportedOperationException ignored) { + return true; + } catch (Throwable ignored) { + return false; + } } } diff --git a/src/main/java/art/arcane/adapt/api/data/WorldData.java b/src/main/java/art/arcane/adapt/api/data/WorldData.java index 3fc65aaba..140deab33 100644 --- a/src/main/java/art/arcane/adapt/api/data/WorldData.java +++ b/src/main/java/art/arcane/adapt/api/data/WorldData.java @@ -18,13 +18,13 @@ package art.arcane.adapt.api.data; -import art.arcane.adapt.util.mantle.Mantle; -import art.arcane.spatial.matter.ClassReader; -import art.arcane.spatial.matter.SpatialMatter; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.data.unit.Earnings; import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.mantle.Mantle; +import art.arcane.spatial.matter.ClassReader; +import art.arcane.spatial.matter.SpatialMatter; import art.arcane.volmlib.util.collection.KMap; import org.bukkit.World; import org.bukkit.block.Block; diff --git a/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java index 77ce74059..6ca2794ad 100644 --- a/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java @@ -19,9 +19,8 @@ package art.arcane.adapt.api.notification; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; -import art.arcane.adapt.util.common.misc.AdvancementUtils; import art.arcane.adapt.api.world.AdaptPlayer; - +import art.arcane.adapt.util.common.misc.AdvancementUtils; import art.arcane.adapt.util.common.misc.CustomModel; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/art/arcane/adapt/api/notification/Notifier.java b/src/main/java/art/arcane/adapt/api/notification/Notifier.java index 4d60e05ba..0c1d0bb86 100644 --- a/src/main/java/art/arcane/adapt/api/notification/Notifier.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notifier.java @@ -23,9 +23,9 @@ import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; +import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KMap; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java index 9a861ea62..5a3ac3d10 100644 --- a/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java @@ -19,7 +19,6 @@ package art.arcane.adapt.api.notification; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.misc.SoundPlayer; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java index 12c29f481..86b659aa7 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java @@ -1,7 +1,5 @@ package art.arcane.adapt.api.potion; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.scheduling.J; @@ -21,23 +19,21 @@ import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.potion.PotionType; -import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class BrewingManager implements Listener { - private static final Map> recipes = Maps.newHashMap(); + private static final Map> recipes = new ConcurrentHashMap<>(); private static final Map activeTasks = new ConcurrentHashMap<>(); public static void registerRecipe(String adaptation, BrewingRecipe recipe) { - recipes.putIfAbsent(recipe, Lists.newArrayList(adaptation)); - recipes.computeIfPresent(recipe, (k, v) -> { - if (!v.contains(adaptation)) - v.add(adaptation); - return v; - }); + if (adaptation == null || adaptation.isBlank() || recipe == null) { + return; + } + + recipes.computeIfAbsent(recipe, unused -> ConcurrentHashMap.newKeySet()).add(adaptation); } @EventHandler @@ -71,37 +67,50 @@ public void onInventoryClick(InventoryClickEvent e) { Location standLocation = stand.getLocation(); AdaptPlayer p = Adapt.instance.getAdaptServer().getPlayer(clicker); - Optional recipe = recipes.keySet().stream().filter(r -> BrewingTask.isValid(r, standLocation)).findFirst(); - recipe.ifPresent(r -> { - BrewingTask active = activeTasks.get(standLocation); - if (active != null) { - if (!active.getRecipe().getId().equals(r.getId())) { - activeTasks.remove(standLocation).cancel(); - if (recipes.get(r).stream().noneMatch(p::hasAdaptation)) { - return; - } - activeTasks.put(standLocation, new BrewingTask(r, standLocation)); - } - } else { - if (recipes.get(r).stream().noneMatch(p::hasAdaptation)) { - return; + BrewingRecipe recipe = findMatchingRecipe(standLocation); + if (recipe == null) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); + } + return; + } + + Set requiredAdaptations = recipes.get(recipe); + BrewingTask active = activeTasks.get(standLocation); + if (!playerHasRequiredAdaptation(p, requiredAdaptations)) { + if (active != null && !active.getRecipe().getId().equals(recipe.getId())) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); } - activeTasks.put(standLocation, new BrewingTask(r, standLocation)); } - }); + return; + } - if (recipe.isEmpty()) { + if (active != null && active.getRecipe().getId().equals(recipe.getId())) { + return; + } + + if (active != null) { BrewingTask removed = activeTasks.remove(standLocation); if (removed != null) { removed.cancel(); } } + + activeTasks.put(standLocation, new BrewingTask(recipe, standLocation)); }, 1); } @EventHandler public void onBrew(BrewEvent e) { - Material m = e.getContents().getIngredient().getType(); + ItemStack ingredient = e.getContents().getIngredient(); + if (ingredient == null) { + return; + } + + Material m = ingredient.getType(); if (m != Material.GUNPOWDER && m != Material.DRAGON_BREATH) { return; } @@ -129,4 +138,32 @@ public void onBrew(BrewEvent e) { e.getResults().set(i, newStack); } } + + private BrewingRecipe findMatchingRecipe(Location standLocation) { + if (standLocation == null) { + return null; + } + + for (BrewingRecipe recipe : recipes.keySet()) { + if (BrewingTask.isValid(recipe, standLocation)) { + return recipe; + } + } + + return null; + } + + private boolean playerHasRequiredAdaptation(AdaptPlayer player, Set requiredAdaptations) { + if (player == null || requiredAdaptations == null || requiredAdaptations.isEmpty()) { + return false; + } + + for (String adaptation : requiredAdaptations) { + if (player.hasAdaptation(adaptation)) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java index 434d603c5..1fa2083ab 100644 --- a/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java +++ b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java @@ -1,8 +1,8 @@ package art.arcane.adapt.api.potion; -import com.google.common.collect.Lists; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.reflect.registries.PotionTypes; +import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Getter; import net.kyori.adventure.text.Component; diff --git a/src/main/java/art/arcane/adapt/api/protection/Protector.java b/src/main/java/art/arcane/adapt/api/protection/Protector.java index 14d2ed3be..f415bb5cc 100644 --- a/src/main/java/art/arcane/adapt/api/protection/Protector.java +++ b/src/main/java/art/arcane/adapt/api/protection/Protector.java @@ -22,8 +22,6 @@ import org.bukkit.Location; import org.bukkit.entity.Player; -import java.util.UUID; - public interface Protector { default boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { diff --git a/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java index 095c09d25..b303e5b02 100644 --- a/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java +++ b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java @@ -18,35 +18,67 @@ package art.arcane.adapt.api.protection; -import com.google.common.collect.ImmutableList; import art.arcane.adapt.Adapt; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class ProtectorRegistry { private final List protectors = new ArrayList<>(); + private volatile List allProtectorsSnapshot = List.of(); + private volatile List defaultProtectorsSnapshot = List.of(); + + public synchronized void registerProtector(Protector protector) { + if (protector == null || protectors.contains(protector)) { + return; + } - public void registerProtector(Protector protector) { Adapt.verbose("Protector: \"" + protector.getName() + "\" registered."); protectors.add(protector); + rebuildSnapshots(); } - public void unregisterProtector(Protector protector) { + public synchronized void unregisterProtector(Protector protector) { + if (protector == null) { + return; + } + protector.unregister(); - protectors.remove(protector); + if (protectors.remove(protector)) { + rebuildSnapshots(); + } } public List getDefaultProtectors() { - return protectors.stream().filter(Protector::isEnabledByDefault).collect(ImmutableList.toImmutableList()); + return defaultProtectorsSnapshot; } public List getAllProtectors() { - return ImmutableList.copyOf(protectors); + return allProtectorsSnapshot; } - public void unregisterAll() { + public synchronized void unregisterAll() { protectors.forEach(Protector::unregister); protectors.clear(); + rebuildSnapshots(); + } + + private void rebuildSnapshots() { + List all = new ArrayList<>(protectors.size()); + List defaults = new ArrayList<>(Math.max(1, protectors.size() / 2)); + + for (Protector protector : protectors) { + if (protector == null) { + continue; + } + all.add(protector); + if (protector.isEnabledByDefault()) { + defaults.add(protector); + } + } + + allProtectorsSnapshot = Collections.unmodifiableList(all); + defaultProtectorsSnapshot = Collections.unmodifiableList(defaults); } } diff --git a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java index 1c2522882..06d232786 100644 --- a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java +++ b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java @@ -56,6 +56,7 @@ public static boolean isWorldBlacklisted(Player player) { if (player == null) { return true; } + return AdaptConfig.get().blacklistedWorlds.contains(player.getWorld().getName()); } diff --git a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index d5171d271..2f6d7b1e2 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -19,18 +19,16 @@ package art.arcane.adapt.api.skill; import art.arcane.adapt.Adapt; -import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.runtime.AdaptationGate; import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.volmlib.util.collection.KList; import lombok.Data; @@ -46,9 +44,6 @@ import java.lang.reflect.Field; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.scheduling.J; - @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleSkill extends TickedObject implements Skill { @@ -238,88 +233,27 @@ public boolean checkValidEntity(EntityType e) { } protected boolean shouldReturnForPlayer(Player p) { - try { - if (p == null) { - return true; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return true; - } - - Adapt.verbose("Checking " + p.getName() + " for " + getName()); - return AdaptationGate.shouldSkipPlayer(p, this, getPlayer(p) != null); - } catch (Exception ex) { - Adapt.verbose("Failed shouldReturnForPlayer check for " + (p == null ? "null" : p.getName()) - + " in skill " + getName() + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return true; - } + return SkillRuntimeGuards.shouldSkipPlayer(this, p); } - protected void shouldReturnForPlayer(Player p, Runnable r) { - try { - if (p == null || r == null) { - return; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - J.runEntity(p, () -> shouldReturnForPlayer(p, r)); - return; - } - if (shouldReturnForPlayer(p)) { - return; - } - r.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded player runnable for skill " + getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + protected void shouldReturnForPlayer(Player p, Runnable r) { + SkillRuntimeGuards.withPlayer(this, p, r); } protected void shouldReturnForPlayer(Player p, Cancellable c, Runnable r) { - try { - if (p == null || c == null || r == null) { - return; - } - - if (c.isCancelled()) { - return; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return; - } - - if (shouldReturnForPlayer(p)) { - return; - } - r.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded cancellable player runnable for skill " + getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + SkillRuntimeGuards.withPlayer(this, p, c, r); } protected boolean shouldReturnForWorld(World world, Skill skill) { - try { - return AdaptationGate.shouldSkipWorld(world, skill); - } catch (Exception ex) { - Adapt.verbose("Failed shouldReturnForWorld check for skill " + (skill == null ? "null" : skill.getName()) - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return true; - } + return SkillRuntimeGuards.shouldSkipWorld(skill, world); } protected boolean isWorldBlacklisted(Player p) { - return AdaptationGate.isWorldBlacklisted(p); + return SkillRuntimeGuards.isWorldBlacklisted(p); } protected boolean isInCreativeOrSpectator(Player p) { - return AdaptationGate.isInCreativeOrSpectator(p); + return SkillRuntimeGuards.isInCreativeOrSpectator(p); } @Override @@ -369,10 +303,7 @@ protected void registerMilestone(String advancementKey, String stat, double goal protected void checkStatTrackersForOnlinePlayers() { for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player player = adaptPlayer.getPlayer(); - if (shouldReturnForPlayer(player)) { - continue; - } - checkStatTrackers(adaptPlayer); + shouldReturnForPlayer(player, () -> checkStatTrackers(adaptPlayer)); } } diff --git a/src/main/java/art/arcane/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java index 25fdf4b8a..f78f23c7f 100644 --- a/src/main/java/art/arcane/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -17,204 +17,145 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.api.skill; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.Component; import art.arcane.adapt.api.adaptation.Adaptation; -import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.tick.Ticked; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.xp.XP; -import art.arcane.adapt.content.gui.SkillsGui; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.volmlib.util.collection.KList; -import org.bukkit.Bukkit; +import art.arcane.volmlib.util.format.Form; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.Sound; import org.bukkit.entity.Player; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.GuiEffects; -import art.arcane.adapt.util.common.inventorygui.GuiLayout; -import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.adapt.util.common.inventorygui.UIElement; -import art.arcane.adapt.util.common.inventorygui.UIWindow; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - +/** + * Public API for a skill line and its shared behavior. + */ public interface Skill extends Ticked, Component { + /** + * Builds the root advancement tree for this skill (including child adaptations). + */ AdaptAdvancement buildAdvancements(); + /** + * Returns the concrete config class used by this skill. + */ Class getConfigurationClass(); + /** + * Registers the config class used for load/reload of this skill. + */ void registerConfiguration(Class type); + /** + * @return true when this skill is runtime-enabled. + */ boolean isEnabled(); + /** + * Returns the live config instance for this skill. + */ T getConfig(); + /** + * @return internal skill key (e.g. "swords"). + */ String getName(); + /** + * @return icon glyph/emoji prefix used in UI. + */ String getEmojiName(); + /** + * @return base icon for this skill. + */ Material getIcon(); + /** + * @return localized skill description. + */ String getDescription(); + /** + * @return recipes registered by this skill. + */ KList getRecipes(); + /** + * Registers an adaptation under this skill. + */ void registerAdaptation(Adaptation a); + /** + * Registers a stat tracker/milestone for this skill. + */ void registerStatTracker(AdaptStatTracker tracker); + /** + * @return all stat trackers for this skill. + */ KList getStatTrackers(); + /** + * Skill-level particle toggle check (global + per-skill config aware). + */ @Override default boolean areParticlesEnabled() { - if (!Component.super.areParticlesEnabled()) { - return false; - } - - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - if (effects != null && effects.getSkillParticleOverrides() != null && !effects.getSkillParticleOverrides().isEmpty()) { - String key = getName(); - Boolean override = effects.getSkillParticleOverrides().get(key); - if (override == null && key != null) { - override = effects.getSkillParticleOverrides().get(key.toLowerCase(Locale.ROOT)); - } - if (override != null && !override) { - return false; - } - } - - Object config = getConfig(); - if (config != null) { - Boolean directToggle = readBooleanField(config, "showParticles"); - if (directToggle != null && !directToggle) { - return false; - } - - Boolean genericToggle = readBooleanField(config, "showParticleEffects"); - if (genericToggle != null && !genericToggle) { - return false; - } - } - - return true; + return SkillGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); } + /** + * Skill-level sound toggle check (global + per-skill config aware). + */ @Override default boolean areSoundsEnabled() { - if (!Component.super.areSoundsEnabled()) { - return false; - } - - Object config = getConfig(); - if (config != null) { - Boolean directToggle = readBooleanField(config, "showSounds"); - if (directToggle != null && !directToggle) { - return false; - } - } - - return true; - } - - private static Boolean readBooleanField(Object source, String fieldName) { - if (source == null || fieldName == null || fieldName.isBlank()) { - return null; - } - - Class current = source.getClass(); - while (current != null) { - try { - Field field = current.getDeclaredField(fieldName); - field.setAccessible(true); - Object value = field.get(source); - if (value instanceof Boolean bool) { - return bool; - } - return null; - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } catch (Throwable ex) { - Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } - } - - return null; + return SkillGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); } + /** + * Evaluates and grants eligible stat-tracker advancements for one player. + */ default void checkStatTrackers(AdaptPlayer player) { - if (!this.isEnabled()) { - return; - } - if (!player.getPlayer().getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - if (!AdaptConfig.get().isAdvancements()) { - return; - } - PlayerData d = player.getData(); - - for (AdaptStatTracker i : getStatTrackers()) { - if (!d.isGranted(i.getAdvancement()) && d.getStat(i.getStat()) >= i.getGoal()) { - player.getAdvancementHandler().grant(i.getAdvancement()); - xp(player.getPlayer(), i.getReward()); - } - } - - for (Adaptation adaptation : getAdaptations()) { - if (!(adaptation instanceof SimpleAdaptation sa)) continue; - if (!adaptation.isEnabled()) continue; - for (AdaptStatTracker tracker : sa.getStatTrackers()) { - if (!d.isGranted(tracker.getAdvancement()) && d.getStat(tracker.getStat()) >= tracker.getGoal()) { - player.getAdvancementHandler().grant(tracker.getAdvancement()); - xp(player.getPlayer(), tracker.getReward()); - } - } - } + SkillRuntimeGuards.checkStatTrackers(this, player); } + /** + * @return all adaptations currently attached to this skill. + */ KList> getAdaptations(); + /** + * @return primary chat/UI color for this skill. + */ C getColor(); + /** + * @return minimum xp tuning value used by this skill. + */ double getMinXp(); + /** + * Called while building advancement trees to append custom nodes. + */ void onRegisterAdvancements(KList advancements); + /** + * Returns true when this player is blacklisted from this skill via permission. + */ default boolean hasBlacklistPermission(Player p, Skill s) { - if (p.isOp()) { // If the player is an operator, bypass the permission check - return false; - } - String blacklistPermission = "adapt.blacklist." + s.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); - return p.hasPermission(blacklistPermission); + return SkillRuntimeGuards.hasBlacklistPermission(p, s); } + /** + * Formatted display name with color + emoji. + */ default String getDisplayName() { if (!this.isEnabled()) { return C.DARK_GRAY + Form.capitalize(getName()); @@ -222,6 +163,9 @@ default String getDisplayName() { return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName() + " " + Form.capitalize(getName()); } + /** + * Compact formatted display name with color + emoji. + */ default String getShortName() { if (!this.isEnabled()) { return C.DARK_GRAY + Form.capitalize(getName()); @@ -229,6 +173,9 @@ default String getShortName() { return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName(); } + /** + * Display name with optional level suffix. + */ default String getDisplayName(int level) { if (!this.isEnabled()) { return C.DARK_GRAY + Form.capitalize(getName()); @@ -239,103 +186,90 @@ default String getDisplayName(int level) { return getDisplayName(); } + /** + * Returns the generated custom model binding for this skill icon. + */ default CustomModel getModel() { return CustomModel.get(getIcon(), "skill", getName()); } + /** + * Grants visible xp at the player's location. + */ default void xp(Player p, double xp) { xp(p, xp, null); } + /** + * Grants visible xp at the player's location using an optional reward key. + */ default void xp(Player p, double xp, String rewardKey) { - if (!this.isEnabled()) { - return; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - xp(p, p.getLocation(), xp, rewardKey); + xp(p, p == null ? null : p.getLocation(), xp, rewardKey); } + /** + * Grants visible xp at a specific location. + */ default void xp(Player p, Location at, double xp) { xp(p, at, xp, null); } + /** + * Grants visible xp at a specific location using an optional reward key. + */ default void xp(Player p, Location at, double xp, String rewardKey) { - if (!this.isEnabled()) { - return; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - try { - XP.xp(p, this, xp, rewardKey); - if (xp > 50) { - vfxXP(p, at, (int) xp); - } - Adapt.verbose("Gave " + p.getName() + " " + xp + " xp in " + getName() + " " + this.getClass()); - } catch (Exception e) { - Adapt.verbose("Failed to give xp to " + p.getName() + " for " + getName() + " (" + xp + ")"); - } + SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, false, true); } + /** + * Grants silent xp at a specific location (with optional burst visuals). + */ default void xpS(Player p, Location at, double xp) { xpS(p, at, xp, null); } + /** + * Grants silent xp at a specific location using an optional reward key. + */ default void xpS(Player p, Location at, double xp, String rewardKey) { - if (!this.isEnabled()) { - return; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - try { - XP.xpSilent(p, this, xp, rewardKey); - if (xp > 50) { - vfxXP(p, at, (int) xp); - } - Adapt.verbose("Gave " + p.getName() + " " + xp + " xp in " + getName() + " " + this.getClass()); - } catch (Exception e) { - Adapt.verbose("Failed to give xp to " + p.getName() + " for " + getName() + " (" + xp + ")"); - } + SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, true, true); } + /** + * Grants silent xp without visuals. + */ default void xpSilent(Player p, double xp) { xpSilent(p, xp, null); } + /** + * Grants silent xp without visuals using an optional reward key. + */ default void xpSilent(Player p, double xp, String rewardKey) { - if (!this.isEnabled()) { - return; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - try { - XP.xpSilent(p, this, xp, rewardKey); - } catch ( - Exception ignored) { // Player was Given XP (Likely Teleportation) before i can see it because some plugin has higher priority than me and moves a player. so im not going to throw an error, as i know why it's happening. - Adapt.verbose("Player was Given XP (Likely Teleportation) before i can see it because some plugin has higher priority than me and moves a player. so im not going to throw an error, as i know why it's happening."); - } + SkillRuntimeGuards.grantXpSilent(this, p, xp, rewardKey); } - + /** + * Emits spatial xp pulses around a world location. + */ default void xp(Location at, double xp, int rad, long duration) { XP.spatialXP(at, this, xp, rad, duration); vfxXP(at); } + /** + * Grants knowledge points for this skill. + */ default void knowledge(Player p, long k) { - if (!this.isEnabled()) { - return; - } - XP.knowledge(p, this, k); + SkillRuntimeGuards.grantKnowledge(this, p, k); } + /** + * Opens the skill GUI and optionally enforces blacklist permission checks. + */ default boolean openGui(Player player, boolean checkPermissions) { - if (hasBlacklistPermission(player, this)) { + if (checkPermissions && hasBlacklistPermission(player, this)) { return false; } else { openGui(player); @@ -343,160 +277,17 @@ default boolean openGui(Player player, boolean checkPermissions) { } } + /** + * Opens page 0 of this skill GUI. + */ default void openGui(Player player) { openGui(player, 0); } + /** + * Opens a specific page of this skill GUI. + */ default void openGui(Player player, int page) { - if (!this.isEnabled()) { - return; - } - if (!player.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - if (!J.isPrimaryThread()) { - int targetPage = page; - J.s(() -> openGui(player, targetPage)); - return; - } - - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); - - List> visibleAdaptations = new ArrayList<>(); - for (Adaptation adaptation : getAdaptations()) { - if (!adaptation.isEnabled()) { - continue; - } - if (!adaptation.getSkill().isEnabled()) { - continue; - } - if (adaptation.hasBlacklistPermission(player, adaptation)) { - continue; - } - visibleAdaptations.add(adaptation); - } - visibleAdaptations.sort( - Comparator.comparing((Adaptation adaptation) -> normalizeSortKey(adaptation.getDisplayName())) - .thenComparing(Adaptation::getName, String.CASE_INSENSITIVE_ORDER) - ); - - boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); - GuiLayout.PagePlan plan = GuiLayout.plan(visibleAdaptations.size(), reserveNavigation); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(visibleAdaptations.size(), start + plan.itemsPerPage()); - - Window w = new UIWindow(player); - GuiTheme.apply(w, "skill/" + getName()); - w.setViewportHeight(plan.rows()); - - if (visibleAdaptations.isEmpty()) { - w.setElement(0, 0, new UIElement("ada-empty") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.GRAY + "No adaptations available")); - } else { - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - Adaptation adaptation = visibleAdaptations.get(rowStart + i); - int lvl = getPlayer(player).getData().getSkillLine(getName()).getAdaptationLevel(adaptation.getName()); - int pos = GuiLayout.centeredPosition(i, rowCount); - Element element = new UIElement("ada-" + adaptation.getName()) - .setMaterial(new MaterialBlock(adaptation.getIcon())) - .setModel(adaptation.getModel()) - .setName(adaptation.getDisplayName(lvl)) - .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) - .addLore(lvl == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(lvl))) - .setProgress(1D) - .onLeftClick((e) -> adaptation.openGui(player)); - reveal.add(new GuiEffects.Placement(pos, row, element)); - } - } - GuiEffects.applyReveal(w, reveal); - } - - if (plan.hasNavigationRow()) { - int navRow = plan.rows() - 1; - int jumpPages = 5; - int jumpBack = Math.max(0, currentPage - jumpPages); - int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); - if (currentPage > 0) { - w.setElement(-4, navRow, new UIElement("skill-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openGui(player, currentPage - 1)) - .onRightClick((e) -> openGui(player, jumpBack))); - w.setElement(-3, navRow, new UIElement("skill-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> openGui(player, 0))); - } - if (currentPage < plan.pageCount() - 1) { - w.setElement(4, navRow, new UIElement("skill-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openGui(player, currentPage + 1)) - .onRightClick((e) -> openGui(player, jumpForward))); - w.setElement(3, navRow, new UIElement("skill-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> openGui(player, plan.pageCount() - 1))); - } - - int from = visibleAdaptations.isEmpty() ? 0 : (start + 1); - int to = visibleAdaptations.isEmpty() ? 0 : end; - w.setElement(-1, navRow, new UIElement("skill-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + visibleAdaptations.size()) - .setProgress(1D)); - - if (AdaptConfig.get().isGuiBackButton()) { - w.setElement(0, navRow, new UIElement("back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> onGuiClose(player, true))); - } - - } - - AdaptPlayer a = Adapt.instance.getAdaptServer().getPlayer(player); - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(getDisplayName(a.getSkillLine(getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(a.getSkillLine(getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(a.getSkillLine(getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (a.getSkillLine(getName()).getLevel() + 1) + ")" + pageSuffix); - w.onClosed((vv) -> J.s(() -> onGuiClose(player, !AdaptConfig.get().isEscClosesAllGuis()))); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); - } - - private void onGuiClose(Player player, boolean openPrevGui) { - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); - if (openPrevGui) { - SkillsGui.open(player); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); - } - } - - private static String normalizeSortKey(String value) { - if (value == null) { - return ""; - } - - String normalized = C.stripColor(value).toLowerCase(Locale.ROOT).trim(); - return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + SkillGuiSupport.openGui(this, player, page); } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java new file mode 100644 index 000000000..82f9d06b4 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.ReceiveCancelledEvents; +import org.bukkit.Bukkit; +import org.bukkit.event.*; +import org.bukkit.plugin.EventExecutor; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class SkillEventRegistrar { + private SkillEventRegistrar() { + } + + public static boolean register(Plugin plugin, Listener listener) { + if (!(listener instanceof Skill)) { + return false; + } + + boolean registeredAny = false; + for (Method method : collectHandlerMethods(listener.getClass()).values()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { + continue; + } + + Class parameterType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(parameterType)) { + continue; + } + + @SuppressWarnings("unchecked") + Class eventType = (Class) parameterType; + try { + method.setAccessible(true); + } catch (Throwable ex) { + Adapt.warn("Failed enabling access to skill handler " + + listener.getClass().getName() + "#" + method.getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + continue; + } + + EventExecutor executor = (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; + } + + try { + method.invoke(target, event); + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable ex) { + throw new EventException(ex); + } + }; + + boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); + Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); + registeredAny = true; + } + + return registeredAny; + } + + private static Map collectHandlerMethods(Class type) { + Map methods = new LinkedHashMap<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Method method : current.getDeclaredMethods()) { + if (!method.isAnnotationPresent(EventHandler.class)) { + continue; + } + methods.putIfAbsent(signature(method), method); + } + current = current.getSuperclass(); + } + return methods; + } + + private static String signature(Method method) { + return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + } + + private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { + if (!Cancellable.class.isAssignableFrom(eventType)) { + return annotation.ignoreCancelled(); + } + + if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { + return false; + } + + return true; + } +} diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java new file mode 100644 index 000000000..3b3818388 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -0,0 +1,285 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.content.gui.SkillsGui; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.adapt.util.common.inventorygui.UIElement; +import art.arcane.adapt.util.common.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.format.Form; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +final class SkillGuiSupport { + private SkillGuiSupport() { + } + + static boolean areParticlesEnabled(Skill skill, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } + + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + if (effects != null && effects.getSkillParticleOverrides() != null && !effects.getSkillParticleOverrides().isEmpty()) { + String key = skill.getName(); + Boolean override = effects.getSkillParticleOverrides().get(key); + if (override == null && key != null) { + override = effects.getSkillParticleOverrides().get(key.toLowerCase(Locale.ROOT)); + } + if (override != null && !override) { + return false; + } + } + + Object config = skill.getConfig(); + if (config != null) { + Boolean directToggle = readBooleanField(config, "showParticles"); + if (directToggle != null && !directToggle) { + return false; + } + + Boolean genericToggle = readBooleanField(config, "showParticleEffects"); + if (genericToggle != null && !genericToggle) { + return false; + } + } + + return true; + } + + static boolean areSoundsEnabled(Skill skill, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } + + Object config = skill.getConfig(); + if (config != null) { + Boolean directToggle = readBooleanField(config, "showSounds"); + if (directToggle != null && !directToggle) { + return false; + } + } + + return true; + } + + static void openGui(Skill skill, Player player, int page) { + if (skill == null || !skill.isEnabled() || !SkillRuntimeGuards.isRuntimePlayer(player)) { + return; + } + + if (!J.isPrimaryThread()) { + int targetPage = page; + J.s(() -> openGui(skill, player, targetPage)); + return; + } + + AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(player); + if (adaptPlayer == null) { + return; + } + + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + + List> visibleAdaptations = new ArrayList<>(); + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.isEnabled()) { + continue; + } + if (!adaptation.getSkill().isEnabled()) { + continue; + } + if (adaptation.hasBlacklistPermission(player, adaptation)) { + continue; + } + visibleAdaptations.add(adaptation); + } + visibleAdaptations.sort( + Comparator.comparing((Adaptation adaptation) -> normalizeSortKey(adaptation.getDisplayName())) + .thenComparing(Adaptation::getName, String.CASE_INSENSITIVE_ORDER) + ); + + boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); + GuiLayout.PagePlan plan = GuiLayout.plan(visibleAdaptations.size(), reserveNavigation); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(visibleAdaptations.size(), start + plan.itemsPerPage()); + + Window window = new UIWindow(player); + GuiTheme.apply(window, "skill/" + skill.getName()); + window.setViewportHeight(plan.rows()); + + if (visibleAdaptations.isEmpty()) { + window.setElement(0, 0, new UIElement("ada-empty") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.GRAY + "No adaptations available")); + } else { + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; + } + + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + Adaptation adaptation = visibleAdaptations.get(rowStart + i); + int level = adaptPlayer.getData().getSkillLine(skill.getName()).getAdaptationLevel(adaptation.getName()); + int pos = GuiLayout.centeredPosition(i, rowCount); + Element element = new UIElement("ada-" + adaptation.getName()) + .setMaterial(new MaterialBlock(adaptation.getIcon())) + .setModel(adaptation.getModel()) + .setName(adaptation.getDisplayName(level)) + .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) + .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) + .setProgress(1D) + .onLeftClick((e) -> adaptation.openGui(player)); + reveal.add(new GuiEffects.Placement(pos, row, element)); + } + } + GuiEffects.applyReveal(window, reveal); + } + + if (plan.hasNavigationRow()) { + int navRow = plan.rows() - 1; + int jumpPages = 5; + int jumpBack = Math.max(0, currentPage - jumpPages); + int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); + if (currentPage > 0) { + window.setElement(-4, navRow, new UIElement("skill-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") + .onLeftClick((e) -> openGui(skill, player, currentPage - 1)) + .onRightClick((e) -> openGui(skill, player, jumpBack))); + window.setElement(-3, navRow, new UIElement("skill-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> openGui(skill, player, 0))); + } + if (currentPage < plan.pageCount() - 1) { + window.setElement(4, navRow, new UIElement("skill-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") + .onLeftClick((e) -> openGui(skill, player, currentPage + 1)) + .onRightClick((e) -> openGui(skill, player, jumpForward))); + window.setElement(3, navRow, new UIElement("skill-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> openGui(skill, player, plan.pageCount() - 1))); + } + + int from = visibleAdaptations.isEmpty() ? 0 : (start + 1); + int to = visibleAdaptations.isEmpty() ? 0 : end; + window.setElement(-1, navRow, new UIElement("skill-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + visibleAdaptations.size()) + .setProgress(1D)); + + if (AdaptConfig.get().isGuiBackButton()) { + window.setElement(0, navRow, new UIElement("back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) + .onLeftClick((e) -> onGuiClose(player, true))); + } + + } + + String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; + window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")" + pageSuffix); + window.onClosed((vv) -> J.s(() -> onGuiClose(player, !AdaptConfig.get().isEscClosesAllGuis()))); + window.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); + } + + private static void onGuiClose(Player player, boolean openPrevGui) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + if (openPrevGui) { + SkillsGui.open(player); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static String normalizeSortKey(String value) { + if (value == null) { + return ""; + } + + String normalized = C.stripColor(value).toLowerCase(Locale.ROOT).trim(); + return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + } + + private static Boolean readBooleanField(Object source, String fieldName) { + if (source == null || fieldName == null || fieldName.isBlank()) { + return null; + } + + Class current = source.getClass(); + while (current != null) { + try { + Field field = current.getDeclaredField(fieldName); + field.setAccessible(true); + Object value = field.get(source); + if (value instanceof Boolean bool) { + return bool; + } + return null; + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; + } + } + + return null; + } +} diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index b3eb7982d..310251ee1 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -22,6 +22,7 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.potion.BrewingManager; +import art.arcane.adapt.api.protection.Protector; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.api.world.AdaptPlayer; @@ -30,18 +31,13 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.content.skill.*; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.adapt.util.reflect.registries.Particles; -import org.bukkit.Bukkit; -import org.bukkit.Keyed; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -53,14 +49,8 @@ import org.bukkit.inventory.Recipe; import org.bukkit.persistence.PersistentDataType; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class SkillRegistry extends TickedObject { @@ -113,7 +103,17 @@ public void on(PlayerExpChangeEvent e) { } private boolean canInteract(Player player, Location targetLocation) { - return Adapt.instance.getProtectorRegistry().getAllProtectors().stream().allMatch(protector -> protector.canInteract(player, targetLocation, null)); + if (player == null || targetLocation == null) { + return false; + } + + for (Protector protector : Adapt.instance.getProtectorRegistry().getAllProtectors()) { + if (!protector.canInteract(player, targetLocation, null)) { + return false; + } + } + + return true; } @EventHandler(priority = EventPriority.MONITOR) diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java new file mode 100644 index 000000000..2931e059b --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java @@ -0,0 +1,222 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.skill; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.runtime.AdaptationGate; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.AdaptStatTracker; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.util.common.scheduling.J; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; + +final class SkillRuntimeGuards { + private SkillRuntimeGuards() { + } + + static void checkStatTrackers(Skill skill, AdaptPlayer player) { + if (skill == null || player == null || !skill.isEnabled()) { + return; + } + if (!AdaptConfig.get().isAdvancements()) { + return; + } + if (!isRuntimePlayer(player.getPlayer())) { + return; + } + + PlayerData data = player.getData(); + + for (AdaptStatTracker tracker : skill.getStatTrackers()) { + if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { + player.getAdvancementHandler().grant(tracker.getAdvancement()); + skill.xp(player.getPlayer(), tracker.getReward()); + } + } + + for (Adaptation adaptation : skill.getAdaptations()) { + if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { + continue; + } + if (!adaptation.isEnabled()) { + continue; + } + for (AdaptStatTracker tracker : simpleAdaptation.getStatTrackers()) { + if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { + player.getAdvancementHandler().grant(tracker.getAdvancement()); + skill.xp(player.getPlayer(), tracker.getReward()); + } + } + } + } + + static boolean hasBlacklistPermission(Player player, Skill skill) { + if (player == null || skill == null) { + return true; + } + if (player.isOp()) { + return false; + } + String blacklistPermission = "adapt.blacklist." + skill.getName().replaceAll("-", ""); + Adapt.verbose("Checking if player " + player.getName() + " has blacklist permission " + blacklistPermission); + return player.hasPermission(blacklistPermission); + } + + static boolean shouldSkipPlayer(Skill skill, Player player) { + try { + if (skill == null || player == null) { + return true; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return true; + } + + return AdaptationGate.shouldSkipPlayer(player, skill, skill.getPlayer(player) != null); + } catch (Exception ex) { + Adapt.verbose("Failed shouldSkipPlayer check for " + (player == null ? "null" : player.getName()) + + " in skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return true; + } + } + + static void withPlayer(Skill skill, Player player, Runnable runnable) { + try { + if (skill == null || player == null || runnable == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + J.runEntity(player, () -> withPlayer(skill, player, runnable)); + return; + } + + if (shouldSkipPlayer(skill, player)) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withPlayer(Skill skill, Player player, Cancellable cancellable, Runnable runnable) { + try { + if (skill == null || player == null || cancellable == null || runnable == null) { + return; + } + + if (cancellable.isCancelled()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + J.runEntity(player, () -> withPlayer(skill, player, cancellable, runnable)); + return; + } + + if (cancellable.isCancelled() || shouldSkipPlayer(skill, player)) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded cancellable player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static boolean shouldSkipWorld(Skill skill, World world) { + try { + return AdaptationGate.shouldSkipWorld(world, skill); + } catch (Exception ex) { + Adapt.verbose("Failed shouldSkipWorld check for skill " + (skill == null ? "null" : skill.getName()) + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return true; + } + } + + static boolean isWorldBlacklisted(Player player) { + return AdaptationGate.isWorldBlacklisted(player); + } + + static boolean isInCreativeOrSpectator(Player player) { + return AdaptationGate.isInCreativeOrSpectator(player); + } + + static boolean canGrantXp(Skill skill, Player player) { + return skill != null && skill.isEnabled() && isRuntimePlayer(player); + } + + static void grantXp(Skill skill, Player player, Location location, double xp, String rewardKey, boolean silent, boolean visualBurst) { + if (!canGrantXp(skill, player)) { + return; + } + try { + if (silent) { + XP.xpSilent(player, skill, xp, rewardKey); + } else { + XP.xp(player, skill, xp, rewardKey); + } + + if (visualBurst && location != null && xp > 50) { + skill.vfxXP(player, location, (int) xp); + } + Adapt.verbose("Gave " + player.getName() + " " + xp + " xp in " + skill.getName() + " " + skill.getClass()); + } catch (Exception ex) { + Adapt.verbose("Failed to give xp to " + player.getName() + " for " + skill.getName() + " (" + xp + ")"); + } + } + + static void grantXpSilent(Skill skill, Player player, double xp, String rewardKey) { + if (!canGrantXp(skill, player)) { + return; + } + try { + XP.xpSilent(player, skill, xp, rewardKey); + } catch (Exception ignored) { + Adapt.verbose("Player was Given XP (Likely Teleportation) before i can see it because some plugin has higher priority than me and moves a player. so im not going to throw an error, as i know why it's happening."); + } + } + + static void grantKnowledge(Skill skill, Player player, long knowledge) { + if (skill == null || !skill.isEnabled() || player == null) { + return; + } + XP.knowledge(player, skill, knowledge); + } + + static boolean isRuntimePlayer(Player player) { + return player != null && player.getClass().getSimpleName().equals("CraftPlayer"); + } +} diff --git a/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java new file mode 100644 index 000000000..ae217f961 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java @@ -0,0 +1,76 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.telemetry; + +import java.util.ArrayDeque; + +public final class AbilityCheckTelemetry { + private static final long WINDOW_MS = 60_000L; + private static final Object LOCK = new Object(); + private static final ArrayDeque checkOps = new ArrayDeque<>(); + private static final ArrayDeque successfulOps = new ArrayDeque<>(); + + private AbilityCheckTelemetry() { + } + + public static void recordCheckAttempt() { + long now = System.currentTimeMillis(); + synchronized (LOCK) { + checkOps.addLast(now); + trim(checkOps, now); + trim(successfulOps, now); + } + } + + public static void recordSuccessfulCheck() { + long now = System.currentTimeMillis(); + synchronized (LOCK) { + successfulOps.addLast(now); + trim(checkOps, now); + trim(successfulOps, now); + } + } + + public static long checksPerMinute(long now) { + synchronized (LOCK) { + trim(checkOps, now); + return checkOps.size(); + } + } + + public static long successfulChecksPerMinute(long now) { + synchronized (LOCK) { + trim(successfulOps, now); + return successfulOps.size(); + } + } + + public static void clear() { + synchronized (LOCK) { + checkOps.clear(); + successfulOps.clear(); + } + } + + private static void trim(ArrayDeque samples, long now) { + while (!samples.isEmpty() && (now - samples.peekFirst()) > WINDOW_MS) { + samples.removeFirst(); + } + } +} diff --git a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java index 98aa4d796..35837b921 100644 --- a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java +++ b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java @@ -21,7 +21,6 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.math.M; -import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java index 3f7c368b8..1da60778f 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -21,6 +21,7 @@ import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.collection.KList; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; @@ -92,9 +93,10 @@ public double getWindowLoadPercent() { return 0D; } - double totalMs = metrics.values().stream() - .mapToDouble(metric -> metric.totalNanos.get() / 1_000_000D) - .sum(); + double totalMs = 0D; + for (TickMetric metric : metrics.values()) { + totalMs += metric.totalNanos.get() / 1_000_000D; + } double percent = (totalMs / (double) windowMs) * 100D; if (!Double.isFinite(percent)) { return 0D; @@ -105,11 +107,16 @@ public double getWindowLoadPercent() { public List topMetrics(int limit) { int safeLimit = Math.max(1, limit); - return metrics.entrySet().stream() - .sorted(Comparator.comparingLong((Map.Entry e) -> e.getValue().totalNanos.get()).reversed()) - .limit(safeLimit) - .map(entry -> formatMetric(entry.getKey(), entry.getValue())) - .toList(); + ArrayList> entries = new ArrayList<>(metrics.entrySet()); + entries.sort(Comparator.comparingLong((Map.Entry e) -> e.getValue().totalNanos.get()).reversed()); + + int outputSize = Math.min(safeLimit, entries.size()); + ArrayList top = new ArrayList<>(outputSize); + for (int i = 0; i < outputSize; i++) { + Map.Entry entry = entries.get(i); + top.add(formatMetric(entry.getKey(), entry.getValue())); + } + return top; } private void tick() { diff --git a/src/main/java/art/arcane/adapt/api/value/MaterialValue.java b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java index 7e1c2911b..cfd6fa24c 100644 --- a/src/main/java/art/arcane/adapt/api/value/MaterialValue.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java @@ -17,13 +17,13 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.api.value; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import lombok.Getter; import org.bukkit.Bukkit; @@ -32,8 +32,6 @@ import java.io.File; import java.io.IOException; -import art.arcane.adapt.util.common.io.Json; - import java.util.*; @Getter diff --git a/src/main/java/art/arcane/adapt/api/version/IAttribute.java b/src/main/java/art/arcane/adapt/api/version/IAttribute.java index 25e378f6b..819f45e15 100644 --- a/src/main/java/art/arcane/adapt/api/version/IAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/IAttribute.java @@ -1,7 +1,10 @@ package art.arcane.adapt.api.version; import art.arcane.volmlib.util.collection.KList; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; import org.bukkit.NamespacedKey; import org.bukkit.attribute.AttributeModifier; diff --git a/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java index b4f6f430a..baf8f272f 100644 --- a/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java @@ -7,8 +7,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; public record RuntimeAttribute(AttributeInstance instance) implements IAttribute { private static final Method GET_KEY_METHOD = findMethod("getKey"); @@ -42,27 +43,47 @@ public void addModifier(UUID uuid, NamespacedKey key, double amount, AttributeMo @Override public boolean hasModifier(UUID uuid, NamespacedKey key) { - return instance.getModifiers() - .stream() - .anyMatch(modifier -> matches(modifier, uuid, key)); + for (AttributeModifier modifier : instance.getModifiers()) { + if (matches(modifier, uuid, key)) { + return true; + } + } + + return false; } @Override public void removeModifier(UUID uuid, NamespacedKey key) { - instance.getModifiers() - .stream() - .filter(modifier -> matches(modifier, uuid, key)) - .toList() - .forEach(instance::removeModifier); + List toRemove = null; + for (AttributeModifier modifier : instance.getModifiers()) { + if (!matches(modifier, uuid, key)) { + continue; + } + + if (toRemove == null) { + toRemove = new ArrayList<>(); + } + toRemove.add(modifier); + } + + if (toRemove == null) { + return; + } + + for (AttributeModifier modifier : toRemove) { + instance.removeModifier(modifier); + } } @Override public KList getModifier(UUID uuid, NamespacedKey key) { - return instance.getModifiers() - .stream() - .filter(modifier -> matches(modifier, uuid, key)) - .map(RuntimeAttribute::wrap) - .collect(Collectors.toCollection(KList::new)); + KList modifiers = new KList<>(); + for (AttributeModifier modifier : instance.getModifiers()) { + if (matches(modifier, uuid, key)) { + modifiers.add(wrap(modifier)); + } + } + return modifiers; } private static AttributeModifier createModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index c5638dc98..9c33fa2bd 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.api.world; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; @@ -25,6 +24,11 @@ import art.arcane.adapt.api.notification.Notifier; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.RollingSequence; @@ -42,11 +46,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.scheduling.J; - @EqualsAndHashCode(callSuper = false) @Data public class AdaptPlayer extends TickedObject { @@ -65,6 +64,7 @@ public class AdaptPlayer extends TickedObject { private Location lastpos; private long lastSeen; private volatile boolean pendingDataDeletion; + private volatile boolean runtimeReady; public AdaptPlayer(Player p) { this(p, null); @@ -83,6 +83,7 @@ public AdaptPlayer(Player p, PlayerData prefetchedData) { lastloc = M.ms(); lastSeen = M.ms(); velocity = new Vector(); + runtimeReady = true; } public boolean canConsumeFood(double cost, int minFood) { @@ -274,6 +275,23 @@ public static PlayerData loadPlayerData(UUID uuid) { @Override public void onTick() { + if (!runtimeReady) { + return; + } + + if (updatelatch == null) { + updatelatch = new ChronoLatch(1000); + } + if (savelatch == null) { + savelatch = new ChronoLatch(60000); + } + if (speed == null) { + speed = new RollingSequence(7); + } + if (velocity == null) { + velocity = new Vector(); + } + if (updatelatch.flip()) { getData().update(this); } @@ -303,6 +321,10 @@ public void onTick() { } public double getSpeed() { + if (!runtimeReady || speed == null) { + return 0D; + } + return speed.getAverage(); } @@ -321,7 +343,7 @@ public boolean hasAdaptation(String id) { return false; } - PlayerSkillLine line = getData().getSkillLine(skillLine); + PlayerSkillLine line = getData().getSkillLineNullable(skillLine); if (line == null) { return false; } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java index 8a649fd9b..328904187 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java @@ -18,8 +18,6 @@ package art.arcane.adapt.api.world; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; @@ -34,8 +32,14 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.content.item.ExperienceOrb; import art.arcane.adapt.content.item.KnowledgeOrb; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; @@ -58,11 +62,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.io.Json; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class AdaptServer extends TickedObject { private final ReentrantLock clearLock = new ReentrantLock(); private final Map players = new ConcurrentHashMap<>(); diff --git a/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java index 891ba1687..a357ced67 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java +++ b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java @@ -30,7 +30,8 @@ public class AdvancementHandler { public AdvancementHandler(AdaptPlayer player) { this.player = player; - instance.getManager().unlockExisting(player); + ready = false; + instance.getManager().unlockExisting(player, this); } public void grant(String key, boolean toast) { diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java index eaee66943..4ebd9c292 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java @@ -21,9 +21,6 @@ import art.arcane.volmlib.util.collection.KMap; import lombok.Data; -import java.util.HashMap; -import java.util.Map; - @Data public class PlayerAdaptation { private String id; diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java index 8365aefe9..358414b53 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -27,12 +27,12 @@ import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.api.xp.XPMultiplier; import art.arcane.adapt.util.common.format.C; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.io.Json; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.io.Json; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.collection.KSet; +import art.arcane.volmlib.util.format.Form; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -218,7 +218,20 @@ public boolean hasPowerAvailable(int amount) { } public int getUsedPower() { - return skillLines.values().stream().mapToInt(i -> i.getAdaptations().values().stream().mapToInt(PlayerAdaptation::getLevel).sum()).sum(); + int usedPower = 0; + for (PlayerSkillLine line : skillLines.values()) { + if (line == null) { + continue; + } + + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation == null) { + continue; + } + usedPower += adaptation.getLevel(); + } + } + return usedPower; } public int getLevel() { @@ -316,7 +329,10 @@ public void clearDiscoveries() { } public void pruneAdaptationsForPowerBudget() { - while (getUsedPower() > getMaxPower()) { + int usedPower = getUsedPower(); + int maxPower = getMaxPower(); + + while (usedPower > maxPower) { String worstSkill = null; String worstAdaptation = null; int worstLevel = Integer.MAX_VALUE; @@ -339,8 +355,10 @@ public void pruneAdaptationsForPowerBudget() { PlayerAdaptation adapt = skillLines.get(worstSkill).getAdaptations().get(worstAdaptation); if (adapt.getLevel() <= 1) { skillLines.get(worstSkill).getAdaptations().remove(worstAdaptation); + usedPower -= 1; } else { adapt.setLevel(adapt.getLevel() - 1); + usedPower -= 1; } } } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java index 705b9902e..631917b8f 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java @@ -6,11 +6,7 @@ import java.io.File; import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; public class PlayerDataPersistenceQueue implements AutoCloseable { diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index 7a6d9f0fe..169e4eb65 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -28,9 +28,9 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.api.xp.XPMultiplier; -import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.math.M; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Sound; diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index 413cf0305..3311c897a 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -3,9 +3,9 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.skill.SkillRegistry; -import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptServer; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.content.gui.ConfigGui; @@ -15,11 +15,11 @@ import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.config.ConfigMigrationManager; import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.decree.DecreeOrigin; import art.arcane.volmlib.util.decree.annotations.Decree; import art.arcane.volmlib.util.decree.annotations.Param; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; import java.util.HashMap; @@ -27,9 +27,6 @@ import java.util.Map; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.plugin.Permission; -import art.arcane.adapt.util.project.command.Command; - @Decree(name = "adapt", description = "Basic Command") public class CommandAdapt implements DecreeExecutor { private CommandDebug debug; diff --git a/src/main/java/art/arcane/adapt/command/CommandClear.java b/src/main/java/art/arcane/adapt/command/CommandClear.java index c10384c47..6f46239e5 100644 --- a/src/main/java/art/arcane/adapt/command/CommandClear.java +++ b/src/main/java/art/arcane/adapt/command/CommandClear.java @@ -4,14 +4,12 @@ import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.decree.DecreeOrigin; import art.arcane.volmlib.util.decree.annotations.Decree; import art.arcane.volmlib.util.decree.annotations.Param; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; -import art.arcane.adapt.util.common.plugin.Permission; - @Decree(name = "clear", origin = DecreeOrigin.BOTH, description = "Clear player progression data") public class CommandClear implements DecreeExecutor { diff --git a/src/main/java/art/arcane/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java index 012f7987f..2839eaa98 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -2,8 +2,8 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; -import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.command.FConst; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.decree.DecreeExecutor; import art.arcane.volmlib.util.decree.DecreeOrigin; import art.arcane.volmlib.util.decree.annotations.Decree; @@ -14,9 +14,6 @@ import java.util.List; -import art.arcane.adapt.util.common.plugin.Permission; -import art.arcane.adapt.util.project.command.Command; - @Decree(name = "debug", origin = DecreeOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) public class CommandDebug implements DecreeExecutor { diff --git a/src/main/java/art/arcane/adapt/command/CommandDefault.java b/src/main/java/art/arcane/adapt/command/CommandDefault.java index 0bd3c4d09..86d0bc167 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDefault.java +++ b/src/main/java/art/arcane/adapt/command/CommandDefault.java @@ -4,14 +4,14 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.adaptation.SimpleAdaptation; -import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.adapt.util.decree.context.AdaptationListingHandler; import art.arcane.volmlib.util.decree.DecreeOrigin; import art.arcane.volmlib.util.decree.annotations.Decree; import art.arcane.volmlib.util.decree.annotations.Param; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; import java.io.File; import java.io.IOException; diff --git a/src/main/java/art/arcane/adapt/command/CommandReset.java b/src/main/java/art/arcane/adapt/command/CommandReset.java index f3efec445..2002a0cc2 100644 --- a/src/main/java/art/arcane/adapt/command/CommandReset.java +++ b/src/main/java/art/arcane/adapt/command/CommandReset.java @@ -4,10 +4,10 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.decree.DecreeExecutor; +import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.decree.DecreeOrigin; import art.arcane.volmlib.util.decree.annotations.Decree; import art.arcane.volmlib.util.decree.annotations.Param; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; import org.bukkit.entity.Player; import java.util.HashMap; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java index d96e4877d..cceb533f7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.agility; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -25,13 +24,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Particle; @@ -40,19 +40,14 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.HashMap; import java.util.Map; import java.util.UUID; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.reflect.registries.Particles; +import java.util.concurrent.ConcurrentHashMap; public class AgilityArmorUp extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-armor-up".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:armor-up"); - private final Map ticksRunning; + private final Map ticksRunning; public AgilityArmorUp() { @@ -64,8 +59,8 @@ public AgilityArmorUp() { setBaseCost(getConfig().baseCost); setCostFactor(getConfig().costFactor); setInitialCost(getConfig().initialCost); - setInterval(350); - ticksRunning = new HashMap<>(); + setInterval(50); + ticksRunning = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.IRON_CHESTPLATE) .key("challenge_agility_armor_up_30min") @@ -94,8 +89,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - ticksRunning.remove(p); + ticksRunning.remove(e.getPlayer().getUniqueId()); } private double getWindupTicks(double factor) { @@ -110,54 +104,64 @@ private double getWindupArmor(double factor) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); - if (attribute == null) continue; - - try { - attribute.removeModifier(MODIFIER, MODIFIER_KEY); - } catch (Exception e) { - Adapt.verbose("Failed to remove windup modifier: " + e.getMessage()); + if (p == null || !p.isOnline()) { + continue; } + withPlayerThread(p, () -> updatePlayer(p)); + } + } - if (p.isSwimming() || p.isFlying() || p.isGliding() || p.isSneaking()) { - ticksRunning.remove(p); - continue; + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; + } + + UUID id = p.getUniqueId(); + var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + if (attribute == null) { + ticksRunning.remove(id); + return; + } + + try { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } catch (Exception e) { + Adapt.verbose("Failed to remove windup modifier: " + e.getMessage()); + } + + if (!hasActiveAdaptation(p) || p.isSwimming() || p.isFlying() || p.isGliding() || p.isSneaking()) { + ticksRunning.remove(id); + return; + } + + if (!p.isSprinting()) { + ticksRunning.remove(id); + return; + } + + ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); + int tr = ticksRunning.getOrDefault(id, 0); + if (tr <= 0) { + return; + } + + double factor = getLevelPercent(p); + double ticksToMax = getWindupTicks(factor); + double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); + double armorInc = M.lerp(0, getWindupArmor(factor), progress); + + if (areParticlesEnabled()) { + if (M.r(0.2 * progress)) { + p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation(), 1); } - if (p.isSprinting() && hasAdaptation(p)) { - ticksRunning.compute(p, (k, v) -> { - if (v == null) { - return 1; - } - return v + 1; - }); - - Integer tr = ticksRunning.get(p); - - if (tr == null || tr <= 0) { - continue; - } - - double factor = getLevelPercent(p); - double ticksToMax = getWindupTicks(factor); - double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); - double armorInc = M.lerp(0, getWindupArmor(factor), progress); - - if (areParticlesEnabled()) { - if (M.r(0.2 * progress)) { - p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation(), 1); - } - - if (M.r(0.25 * progress)) { - p.getWorld().spawnParticle(Particle.WAX_ON, p.getLocation(), 1, 0, 0, 0, 0); - } - } - attribute.setModifier(MODIFIER, MODIFIER_KEY, armorInc * 10, AttributeModifier.Operation.MULTIPLY_SCALAR_1); - getPlayer(p).getData().addStat("agility.armor-up.ticks-armored", 1); - } else { - ticksRunning.remove(p); + if (M.r(0.25 * progress)) { + p.getWorld().spawnParticle(Particle.WAX_ON, p.getLocation(), 1, 0, 0, 0, 0); } } + + attribute.setModifier(MODIFIER, MODIFIER_KEY, armorInc * 10, AttributeModifier.Operation.MULTIPLY_SCALAR_1); + getPlayer(p).getData().addStat("agility.armor-up.ticks-armored", 1); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java index fd7e375d8..0ee68c63e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -40,9 +39,9 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class AgilityLadderSlide extends SimpleAdaptation { private final Map upwardStates; @@ -58,7 +57,7 @@ public AgilityLadderSlide() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setInterval(50); - upwardStates = new HashMap<>(); + upwardStates = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.LADDER) .key("challenge_agility_ladder_500") @@ -92,49 +91,48 @@ public void on(PlayerMoveEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || p.isFlying() || p.isGliding() || p.isSwimming()) { - clearUpwardState(p); - return; - } + withPlayerThread(p, e, () -> { + var context = resolveInteractContext(p, p.getLocation(), player -> !player.isFlying() && !player.isGliding() && !player.isSwimming()); + if (context == null) { + clearUpwardState(p); + return; + } - Location location = p.getLocation(); - if (!canInteract(p, location)) { - clearUpwardState(p); - return; - } + Location location = context.location(); - Block activeLadder = getActiveLadderBlock(location); - if (activeLadder == null) { - clearUpwardState(p); - return; - } + Block activeLadder = getActiveLadderBlock(location); + if (activeLadder == null) { + clearUpwardState(p); + return; + } - double dy = e.getTo().getY() - e.getFrom().getY(); - boolean lookingUp = p.getLocation().getPitch() <= -Math.abs(getConfig().lookUpPitchThreshold); - if (!lookingUp) { - clearUpwardState(p); - return; - } + double dy = e.getTo().getY() - e.getFrom().getY(); + boolean lookingUp = p.getLocation().getPitch() <= -Math.abs(getConfig().lookUpPitchThreshold); + if (!lookingUp) { + clearUpwardState(p); + return; + } - double epsilon = Math.abs(getConfig().movementDirectionEpsilonUpward); - Vector velocity = p.getVelocity(); - if (p.isSneaking()) { - clearUpwardState(p); - applyVerticalVelocity(p, velocity, 0); - return; - } + double epsilon = Math.abs(getConfig().movementDirectionEpsilonUpward); + Vector velocity = p.getVelocity(); + if (p.isSneaking()) { + clearUpwardState(p); + applyVerticalVelocity(p, velocity, 0); + return; + } - boolean movingUp = dy > epsilon; - if (!movingUp) { - clearUpwardState(p); - return; - } + boolean movingUp = dy > epsilon; + if (!movingUp) { + clearUpwardState(p); + return; + } - double baseUp = Math.max(0, getConfig().normalUpwardLadderSpeed); - double targetUp = isNearLadderEnd(activeLadder, true) ? baseUp : getUpwardSpeed(); - applySmoothUpwardVelocity(p, velocity, baseUp, targetUp); - p.setFallDistance(0); - getPlayer(p).getData().addStat("agility.ladder-slide.blocks-climbed", 1); + double baseUp = Math.max(0, getConfig().normalUpwardLadderSpeed); + double targetUp = isNearLadderEnd(activeLadder, true) ? baseUp : getUpwardSpeed(); + applySmoothUpwardVelocity(p, velocity, baseUp, targetUp); + p.setFallDistance(0); + getPlayer(p).getData().addStat("agility.ladder-slide.blocks-climbed", 1); + }); } @EventHandler diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java index 156845eb9..3b35d9bc6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java @@ -23,9 +23,8 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -42,14 +41,14 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class AgilityParkourMomentum extends SimpleAdaptation { - private final Map momentum = new HashMap<>(); - private final Map wasOnGround = new HashMap<>(); - private final Map speedBoosting = new HashMap<>(); + private final Map momentum = new ConcurrentHashMap<>(); + private final Map wasOnGround = new ConcurrentHashMap<>(); + private final Map speedBoosting = new ConcurrentHashMap<>(); public AgilityParkourMomentum() { super("agility-parkour-momentum"); @@ -95,83 +94,92 @@ public void on(PlayerMoveEvent e) { } Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (!hasAdaptation(p)) { - momentum.remove(id); - wasOnGround.remove(id); - return; - } + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + momentum.remove(id); + wasOnGround.remove(id); + return; + } - boolean onGroundNow = p.isOnGround(); - boolean onGroundBefore = wasOnGround.getOrDefault(id, onGroundNow); - int current = momentum.getOrDefault(id, 0); + boolean onGroundNow = p.isOnGround(); + boolean onGroundBefore = wasOnGround.getOrDefault(id, onGroundNow); + int current = momentum.getOrDefault(id, 0); - if (!onGroundBefore && onGroundNow) { - if (isMomentumLanding(p) && isOnLedge(p)) { - current += getConfig().landingGain; - getPlayer(p).getData().addStat("agility.parkour-momentum.ledge-landings", 1); - } else { - current -= getConfig().failedLandingPenalty; + if (!onGroundBefore && onGroundNow) { + if (isMomentumLanding(p) && isOnLedge(p)) { + current += getConfig().landingGain; + getPlayer(p).getData().addStat("agility.parkour-momentum.ledge-landings", 1); + } else { + current -= getConfig().failedLandingPenalty; + } + } else if (onGroundNow && !p.isSprinting()) { + current -= getConfig().groundDecayOnMove; } - } else if (onGroundNow && !p.isSprinting()) { - current -= getConfig().groundDecayOnMove; - } - current = clampMomentum(current, getMaxMomentum(getLevel(p))); - momentum.put(id, current); - wasOnGround.put(id, onGroundNow); + current = clampMomentum(current, getMaxMomentum(level)); + momentum.put(id, current); + wasOnGround.put(id, onGroundNow); + }); } @Override public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - UUID id = p.getUniqueId(); - if (!hasAdaptation(p)) { - momentum.remove(id); - wasOnGround.remove(id); - invalidateMomentumSpeed(p, id, true); + if (p == null || !p.isOnline()) { continue; } - int level = getLevel(p); - int maxMomentum = getMaxMomentum(level); - int current = momentum.getOrDefault(id, 0); - if (current <= 0) { - invalidateMomentumSpeed(p, id, false); - continue; - } + withPlayerThread(p, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + momentum.remove(id); + wasOnGround.remove(id); + invalidateMomentumSpeed(p, id, true); + return; + } - if (p.isOnGround() && !isOnLedge(p)) { - current -= getConfig().offLedgeDecayPerTick; - momentum.put(id, clampMomentum(current, maxMomentum)); - brakeMomentumSpeed(p, id); - continue; - } + int maxMomentum = getMaxMomentum(level); + int current = momentum.getOrDefault(id, 0); + if (current <= 0) { + invalidateMomentumSpeed(p, id, false); + return; + } - int speedAmp = Math.max(0, Math.min(getMaxSpeedAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxSpeedAmplifier(level) + 1)) - 1)); - int jumpAmp = Math.max(0, Math.min(getMaxJumpAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxJumpAmplifier(level) + 1)) - 1)); + if (p.isOnGround() && !isOnLedge(p)) { + current -= getConfig().offLedgeDecayPerTick; + momentum.put(id, clampMomentum(current, maxMomentum)); + brakeMomentumSpeed(p, id); + return; + } - p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, 25, jumpAmp, false, false)); + int speedAmp = Math.max(0, Math.min(getMaxSpeedAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxSpeedAmplifier(level) + 1)) - 1)); + int jumpAmp = Math.max(0, Math.min(getMaxJumpAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxJumpAmplifier(level) + 1)) - 1)); - if (speedAmp <= 0) { - brakeMomentumSpeed(p, id); - } else if (!isVelocityEligible(p)) { - invalidateMomentumSpeed(p, id, true); - } else { - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { + p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, 25, jumpAmp, false, false)); + + if (speedAmp <= 0) { brakeMomentumSpeed(p, id); + } else if (!isVelocityEligible(p)) { + invalidateMomentumSpeed(p, id, true); } else { - applyMomentumSpeed(p, id, input, speedAmp); + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeMomentumSpeed(p, id); + } else { + applyMomentumSpeed(p, id, input, speedAmp); + } } - } - if (p.isOnGround() && !p.isSprinting()) { - current -= getConfig().passiveGroundDecayPerTick; - } + if (p.isOnGround() && !p.isSprinting()) { + current -= getConfig().passiveGroundDecayPerTick; + } - momentum.put(id, clampMomentum(current, maxMomentum)); + momentum.put(id, clampMomentum(current, maxMomentum)); + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java index 99a341ae5..7770a55cc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java @@ -23,14 +23,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,15 +41,13 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerToggleSneakEvent; -import java.util.HashMap; import java.util.Map; import java.util.UUID; - -import art.arcane.adapt.util.common.inventorygui.Window; +import java.util.concurrent.ConcurrentHashMap; public class AgilityRollLanding extends SimpleAdaptation { - private final Map rollInputs = new HashMap<>(); - private final Map proneUntilMillis = new HashMap<>(); + private final Map rollInputs = new ConcurrentHashMap<>(); + private final Map proneUntilMillis = new ConcurrentHashMap<>(); public AgilityRollLanding() { super("agility-roll-landing"); @@ -107,65 +104,70 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(PlayerToggleSneakEvent e) { Player p = e.getPlayer(); - if (e.isSneaking()) { - recordRollInput(p, null, null); - } + withAdaptedPlayer(p, e, () -> { + if (e.isSneaking()) { + recordRollInput(p, null, null); + } + }); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(PlayerMoveEvent e) { - recordRollInput(e.getPlayer(), e.getFrom().getY(), e.getTo() == null ? null : e.getTo().getY()); + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> recordRollInput(p, e.getFrom().getY(), e.getTo() == null ? null : e.getTo().getY())); } @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { + if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { return; } - if (!hasAdaptation(p) || p.hasCooldown(Material.HAY_BLOCK)) { - return; - } + withAdaptedPlayer(p, e, () -> { + if (p.hasCooldown(Material.HAY_BLOCK)) { + return; + } - long now = System.currentTimeMillis(); - long input = rollInputs.getOrDefault(p.getUniqueId(), 0L); - int level = getLevel(p); - if (now - input > getInputWindowMillis(level)) { - return; - } + long now = System.currentTimeMillis(); + long input = rollInputs.getOrDefault(p.getUniqueId(), 0L); + int level = getActiveLevel(p); + if (now - input > getInputWindowMillis(level)) { + return; + } - double absorbCap = e.getDamage() * getFallReduction(level); - int hungerNeeded = (int) Math.ceil(absorbCap * getHungerPerDamage(level)); - if (hungerNeeded <= 0 || p.getFoodLevel() <= 0) { - return; - } + double absorbCap = e.getDamage() * getFallReduction(level); + int hungerNeeded = (int) Math.ceil(absorbCap * getHungerPerDamage(level)); + if (hungerNeeded <= 0 || p.getFoodLevel() <= 0) { + return; + } - int usableFood = Math.min(p.getFoodLevel(), hungerNeeded); - double absorbed = usableFood / getHungerPerDamage(level); - if (absorbed <= 0) { - return; - } + int usableFood = Math.min(p.getFoodLevel(), hungerNeeded); + double absorbed = usableFood / getHungerPerDamage(level); + if (absorbed <= 0) { + return; + } - p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); - e.setDamage(Math.max(0, e.getDamage() - absorbed)); - if (e.getDamage() <= 0.01) { - e.setCancelled(true); - } + p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); + e.setDamage(Math.max(0, e.getDamage() - absorbed)); + if (e.getDamage() <= 0.01) { + e.setCancelled(true); + } - p.setCooldown(Material.HAY_BLOCK, getCooldownTicks(level)); - triggerRollPose(p, level); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_SMALL_FALL, 0.8f, 0.7f); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.0f, 0.89f); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_WOOL_BREAK, 0.55f, 0.9f); - getPlayer(p).getData().addStat("agility.roll-landing.damage-prevented", absorbed); - if (p.getFallDistance() >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_fearless")) { - getPlayer(p).getAdvancementHandler().grant("challenge_agility_fearless"); - } - xp(p, absorbed * getConfig().xpPerDamagePrevented); + p.setCooldown(Material.HAY_BLOCK, getCooldownTicks(level)); + triggerRollPose(p, level); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_SMALL_FALL, 0.8f, 0.7f); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.0f, 0.89f); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_WOOL_BREAK, 0.55f, 0.9f); + getPlayer(p).getData().addStat("agility.roll-landing.damage-prevented", absorbed); + if (p.getFallDistance() >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_fearless")) { + getPlayer(p).getAdvancementHandler().grant("challenge_agility_fearless"); + } + xp(p, absorbed * getConfig().xpPerDamagePrevented); + }); } private void recordRollInput(Player p, Double fromY, Double toY) { - if (!hasAdaptation(p) || !p.isSneaking()) { + if (!p.isSneaking()) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java index 1531c94f8..92326c9b4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java @@ -17,18 +17,20 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.agility; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; -import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -41,17 +43,12 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; - - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class AgilitySuperJump extends SimpleAdaptation { - private final Map lastJump; + private final Map lastJump; public AgilitySuperJump() { super("agility-super-jump"); @@ -64,7 +61,7 @@ public AgilitySuperJump() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setInterval(9999); - lastJump = new HashMap<>(); + lastJump = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.LEATHER_BOOTS) .key("challenge_agility_super_jump_100") @@ -98,37 +95,33 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerToggleSneakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { - return; - } - - if (e.isSneaking() && p.isOnGround()) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.3f, 0.35f); - } + withAdaptedPlayer(p, e, () -> { + if (e.isSneaking() && p.isOnGround()) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.3f, 0.35f); + } + }); } @EventHandler public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - lastJump.remove(p); + lastJump.remove(p.getUniqueId()); } @EventHandler public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); if (p.isSwimming() || p.isFlying() || p.isGliding() || p.isSprinting()) { return; } - if (p.isSneaking() && hasAdaptation(p) && canUse(getPlayer(p))) { + withAdaptedPlayer(p, e, () -> { + if (!p.isSneaking() || !canUse(getPlayer(p))) { + return; + } + Vector velocity = p.getVelocity(); if (velocity.getY() > 0) { @@ -139,10 +132,10 @@ public void on(PlayerMoveEvent e) { jumpVelocity += (double) ((float) jumpPotion.getAmplifier() + 1) * 0.1F; } - if (lastJump.get(p) != null && M.ms() - lastJump.get(p) < 1000) { + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) < 1000) { return; - } else if (lastJump.get(p) != null && M.ms() - lastJump.get(p) > 1500) { - lastJump.remove(p); + } else if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) > 1500) { + lastJump.remove(p.getUniqueId()); } if (p.getLocation().getBlock().getType() != Material.LADDER && velocity.getY() > jumpVelocity && p.isOnline()) { SoundPlayer spw = SoundPlayer.of(p.getWorld()); @@ -152,11 +145,11 @@ public void on(PlayerMoveEvent e) { p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData()); } p.setVelocity(p.getVelocity().setY(getJumpHeight(getLevel(p)))); - lastJump.put(p, M.ms()); + lastJump.put(p.getUniqueId(), M.ms()); getPlayer(p).getData().addStat("agility.super-jump.jumps", 1); } } - } + }); } @Override @@ -196,4 +189,4 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Level Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double jumpLevelMultiplier = 0.23; } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java index e1547a16a..24cddf145 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java @@ -17,20 +17,21 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.agility; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; @@ -39,21 +40,18 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.misc.SoundPlayer; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class AgilityWallJump extends SimpleAdaptation { - private final Map airjumps; - private final Map horizontalIntent; - private final Map horizontalIntentTime; + private final Map airjumps; + private final Map horizontalIntent; + private final Map horizontalIntentTime; + private final Map sneakState; public AgilityWallJump() { super("agility-wall-jump"); @@ -66,9 +64,10 @@ public AgilityWallJump() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setInterval(50); - airjumps = new HashMap<>(); - horizontalIntent = new HashMap<>(); - horizontalIntentTime = new HashMap<>(); + airjumps = new ConcurrentHashMap<>(); + horizontalIntent = new ConcurrentHashMap<>(); + horizontalIntentTime = new ConcurrentHashMap<>(); + sneakState = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.LADDER) .key("challenge_agility_wall_jump_500") @@ -96,10 +95,17 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + airjumps.remove(id); + horizontalIntent.remove(id); + horizontalIntentTime.remove(id); + sneakState.remove(id); + } + + @EventHandler + public void on(PlayerToggleSneakEvent e) { Player p = e.getPlayer(); - airjumps.remove(p); - horizontalIntent.remove(p); - horizontalIntentTime.remove(p); + withPlayerThread(p, e, () -> sneakState.put(p.getUniqueId(), e.isSneaking())); } private int getMaxJumps(int level) { @@ -112,16 +118,15 @@ private double getJumpHeight(int level) { @EventHandler public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!canInteract(p, p.getLocation())) { + UUID id = p.getUniqueId(); + sneakState.put(id, p.isSneaking()); + if (resolveInteractContext(p, p.getLocation()) == null) { return; } - if (airjumps.containsKey(p)) { + if (airjumps.containsKey(id)) { if (p.isOnGround() && !p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData().getMaterial().isAir()) { - airjumps.remove(p); + airjumps.remove(id); } } @@ -133,8 +138,8 @@ public void on(PlayerMoveEvent e) { delta.setY(0); double movementThresholdSq = getConfig().inputMovementThreshold * getConfig().inputMovementThreshold; if (delta.lengthSquared() >= movementThresholdSq) { - horizontalIntent.put(p, delta.normalize()); - horizontalIntentTime.put(p, M.ms()); + horizontalIntent.put(id, delta.normalize()); + horizontalIntentTime.put(id, M.ms()); } } @@ -142,98 +147,107 @@ public void on(PlayerMoveEvent e) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - int level = getLevel(p); - if (level <= 0) { + if (p == null || !p.isOnline()) { continue; } + withPlayerThread(p, () -> updatePlayer(p)); + } + } - Double j = airjumps.get(p); + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; + } - if (j != null && j - 0.25 >= getMaxJumps(level)) { - p.setGravity(true); - continue; - } + UUID id = p.getUniqueId(); + int level = getActiveInteractLevel(p, p.getLocation()); + if (level <= 0) { + return; + } - if (p.isOnGround()) { - airjumps.remove(p); - if (!p.hasGravity()) { - p.setGravity(true); - } - continue; - } + Double j = airjumps.get(id); - if (!canInteract(p, p.getLocation())) { - continue; + if (j != null && j - 0.25 >= getMaxJumps(level)) { + p.setGravity(true); + return; + } + + if (p.isOnGround()) { + airjumps.remove(id); + if (!p.hasGravity()) { + p.setGravity(true); } + return; + } - Block stickBlock = stickToWall(p); - if (p.isFlying() || !p.isSneaking() || p.getFallDistance() < 0.3) { - boolean jumped = false; - - if (!p.hasGravity() && p.getFallDistance() > 0.45 && stickBlock != null) { - j = j == null ? 0 : j; - j++; - - if (j - 0.25 <= getMaxJumps(level)) { - jumped = true; - Vector launch = p.getVelocity().clone().setY(getJumpHeight(level)); - if (isBackwardLaunch(p)) { - Vector direction = p.getLocation().getDirection().clone().setY(0); - if (direction.lengthSquared() > 0.000001) { - direction.normalize().multiply(-getConfig().backwardPushSpeed); - launch.setX(direction.getX()); - launch.setZ(direction.getZ()); - } - } - p.setVelocity(launch); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, stickBlock.getBlockData()); - } - getPlayer(p).getData().addStat("agility.wall-jump.air-jumps", 1); - if (j >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_parkour_master")) { - getPlayer(p).getAdvancementHandler().grant("challenge_agility_parkour_master"); + Block stickBlock = stickToWall(p); + if (p.isFlying() || !isSneaking(id, p)) { + boolean jumped = false; + + if (!p.hasGravity() && p.getFallDistance() > 0.45 && stickBlock != null) { + j = j == null ? 0 : j; + j++; + + if (j - 0.25 <= getMaxJumps(level)) { + jumped = true; + Vector launch = p.getVelocity().clone().setY(getJumpHeight(level)); + if (isBackwardLaunch(p)) { + Vector direction = p.getLocation().getDirection().clone().setY(0); + if (direction.lengthSquared() > 0.000001) { + direction.normalize().multiply(-getConfig().backwardPushSpeed); + launch.setX(direction.getX()); + launch.setZ(direction.getZ()); } } - airjumps.put(p, j); - } - - if (!jumped && !p.hasGravity()) { - p.setGravity(true); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.439f); - } - continue; - } - - if (stickBlock != null) { - if (p.hasGravity()) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.89f); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_CHAIN, 1f, 1.39f); + p.setVelocity(launch); if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.2, 0.1, 0.1, stickBlock.getBlockData()); + p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, stickBlock.getBlockData()); + } + getPlayer(p).getData().addStat("agility.wall-jump.air-jumps", 1); + if (j >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_parkour_master")) { + getPlayer(p).getAdvancementHandler().grant("challenge_agility_parkour_master"); } } - - applyWallStickForce(p, stickBlock); - p.setGravity(false); - Vector c = p.getVelocity(); - p.setVelocity(p.getVelocity().setY((c.getY() * 0.35) - 0.0025)); - Double vv = airjumps.get(p); - vv = vv == null ? 0 : vv; - vv += 0.0127; - airjumps.put(p, vv); + airjumps.put(id, j); } - if (stickBlock == null && !p.hasGravity()) { + if (!jumped && !p.hasGravity()) { p.setGravity(true); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.439f); + } + return; + } + + if (stickBlock != null) { + if (p.hasGravity()) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.89f); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_CHAIN, 1f, 1.39f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.2, 0.1, 0.1, stickBlock.getBlockData()); + } } + + applyWallStickForce(p, stickBlock); + p.setGravity(false); + Vector c = p.getVelocity(); + p.setVelocity(p.getVelocity().setY((c.getY() * 0.35) - 0.0025)); + Double vv = airjumps.get(id); + vv = vv == null ? 0 : vv; + vv += 0.0127; + airjumps.put(id, vv); + } + + if (stickBlock == null && !p.hasGravity()) { + p.setGravity(true); } } private boolean isBackwardLaunch(Player p) { - Long at = horizontalIntentTime.get(p); - Vector intent = horizontalIntent.get(p); + UUID id = p.getUniqueId(); + Long at = horizontalIntentTime.get(id); + Vector intent = horizontalIntent.get(id); if (at == null || intent == null || M.ms() - at > getConfig().inputWindowMs) { return false; } @@ -247,6 +261,11 @@ private boolean isBackwardLaunch(Player p) { return intent.dot(facing) <= -Math.abs(getConfig().backwardIntentDotThreshold); } + private boolean isSneaking(UUID id, Player p) { + Boolean cached = sneakState.get(id); + return cached != null ? cached : p.isSneaking(); + } + private Block stickToWall(Player p) { for (Block wall : getBlocks(p)) { if (wall.getBlockData().getMaterial().isSolid()) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index 44d1033b3..8c18154a6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -17,19 +17,21 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.agility; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; import art.arcane.adapt.util.reflect.events.api.entity.EntityMountEvent; -import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -40,14 +42,9 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.reflect.registries.Particles; +import java.util.concurrent.ConcurrentHashMap; public class AgilityWindUp extends SimpleAdaptation { private final Map ticksRunning; @@ -62,9 +59,9 @@ public AgilityWindUp() { setBaseCost(getConfig().baseCost); setCostFactor(getConfig().costFactor); setInitialCost(getConfig().initialCost); - setInterval(120); - ticksRunning = new HashMap<>(); - speedBoosting = new HashMap<>(); + setInterval(50); + ticksRunning = new ConcurrentHashMap<>(); + speedBoosting = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.POWERED_RAIL) .key("challenge_agility_wind_up_10min") @@ -132,63 +129,74 @@ private double getWindupSpeed(double factor) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - UUID id = p.getUniqueId(); - if (!hasAdaptation(p) || !isVelocityEligible(p)) { - ticksRunning.remove(id); - invalidateWindupSpeed(p, id, true); + if (p == null || !p.isOnline()) { continue; } + withPlayerThread(p, () -> updatePlayer(p)); + } + } - if (!p.isSprinting()) { - ticksRunning.remove(id); - brakeWindupSpeed(p, id); - continue; - } + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; + } - ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); - int tr = ticksRunning.getOrDefault(id, 0); - if (tr <= 0) { - continue; - } + UUID id = p.getUniqueId(); + if (!hasActiveAdaptation(p) || !isVelocityEligible(p)) { + ticksRunning.remove(id); + invalidateWindupSpeed(p, id, false); + return; + } + + if (!p.isSprinting()) { + ticksRunning.remove(id); + invalidateWindupSpeed(p, id, false); + return; + } - double factor = getLevelPercent(p); - double ticksToMax = getWindupTicks(factor); - double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); - double speedIncrease = M.lerp(0, getWindupSpeed(factor), progress); + ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); + int tr = ticksRunning.getOrDefault(id, 0); + if (tr <= 0) { + return; + } - if (areParticlesEnabled()) { - if (M.r(0.2 * progress)) { - p.getWorld().spawnParticle(Particle.LAVA, p.getLocation(), 1); - } + double factor = getLevelPercent(p); + double ticksToMax = getWindupTicks(factor); + double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); + double speedIncrease = M.lerp(0, getWindupSpeed(factor), progress); - if (M.r(0.25 * progress)) { - p.getWorld().spawnParticle(Particle.FLAME, p.getLocation(), 1, 0, 0, 0, 0); - } + if (areParticlesEnabled()) { + if (M.r(0.2 * progress)) { + p.getWorld().spawnParticle(Particle.LAVA, p.getLocation(), 1); } - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeWindupSpeed(p, id); - } else { - applyWindupSpeed(p, id, input, speedIncrease); + if (M.r(0.25 * progress)) { + p.getWorld().spawnParticle(Particle.FLAME, p.getLocation(), 1, 0, 0, 0, 0); } + } - if (progress >= 1.0) { - getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); - } + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + speedBoosting.put(id, false); + return; + } + applyWindupSpeed(p, id, input, speedIncrease); + + if (progress >= 1.0) { + getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); } } private void applyWindupSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot input, double speedIncrease) { Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - brakeWindupSpeed(p, id); + speedBoosting.put(id, false); return; } - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, - Math.max(0, getConfig().baseHorizontalSpeed * (1.0 + Math.max(0, speedIncrease)))); Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + double computedTargetSpeed = Math.max(0, getConfig().baseHorizontalSpeed * (1.0 + Math.max(0, speedIncrease))); + double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, Math.max(horizontal.length(), computedTargetSpeed)); Vector targetHorizontal = direction.multiply(targetSpeed); Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); @@ -196,29 +204,6 @@ private void applyWindupSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot inp speedBoosting.put(id, true); } - private void brakeWindupSpeed(Player p, UUID id) { - if (!speedBoosting.getOrDefault(id, false)) { - return; - } - - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - double stopThreshold = Math.max(0, getConfig().stopThreshold); - if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - speedBoosting.put(id, false); - return; - } - - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); - if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - speedBoosting.put(id, false); - return; - } - - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - } - private void invalidateWindupSpeed(Player p, UUID id, boolean invalidState) { if (!speedBoosting.getOrDefault(id, false)) { return; @@ -290,7 +275,7 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") double stopThreshold = 0.01; @art.arcane.adapt.util.config.ConfigDoc(value = "If true, windup velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") - boolean hardStopOnInvalidState = true; + boolean hardStopOnInvalidState = false; @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") double fallbackInputVelocityThreshold = 0.0008; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java index d0586d273..b1e94b495 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java @@ -18,9 +18,6 @@ package art.arcane.adapt.content.adaptation.architect; -import com.jeff_media.customblockdata.CustomBlockData; -import com.jeff_media.customblockdata.events.CustomBlockDataMoveEvent; -import com.jeff_media.customblockdata.events.CustomBlockDataRemoveEvent; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -29,13 +26,15 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import com.jeff_media.customblockdata.CustomBlockData; +import com.jeff_media.customblockdata.events.CustomBlockDataMoveEvent; +import com.jeff_media.customblockdata.events.CustomBlockDataRemoveEvent; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -58,7 +57,6 @@ import org.bukkit.util.VoxelShape; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -72,7 +70,7 @@ public class ArchitectElevator extends SimpleAdaptation players = new HashSet<>(); + private final Set players = java.util.concurrent.ConcurrentHashMap.newKeySet(); public ArchitectElevator() { super("architect-elevator"); @@ -197,7 +195,7 @@ public void on(BlockPlaceEvent event) { } public int getMaxDistance(Player player) { - int level = getLevel(player); + int level = getActiveLevel(player); if (level == 0) return 0; Config config = getConfig(); return config.baseDistance * (level * config.multiplier); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java index 7ca929bc4..4c41f7291 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java @@ -23,15 +23,20 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Painting; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -41,28 +46,21 @@ import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerToggleSneakEvent; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; - -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Painting; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class ArchitectFoundation extends SimpleAdaptation { private static final BlockData AIR = Material.AIR.createBlockData(); private static final BlockData BLOCK = Material.TINTED_GLASS.createBlockData(); - private final Map blockPower; - private final Map cooldowns; - private final Set active; + private final Map blockPower; + private final Map cooldowns; + private final Set active; private final Set activeBlocks; public ArchitectFoundation() { @@ -76,10 +74,10 @@ public ArchitectFoundation() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - blockPower = new HashMap<>(); - cooldowns = new HashMap<>(); - active = new HashSet<>(); - activeBlocks = new HashSet<>(); + blockPower = new ConcurrentHashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + active = ConcurrentHashMap.newKeySet(); + activeBlocks = ConcurrentHashMap.newKeySet(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.SCAFFOLDING) .key("challenge_architect_foundation_1k") @@ -109,59 +107,53 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!p.isSneaking()) { - return; - } - if (!hasAdaptation(p)) { - return; - } - if (!canBlockPlace(p, p.getLocation())) { - return; - } - if (!e.getFrom().getBlock().equals(e.getTo().getBlock())) { - return; - } - if (!this.active.contains(p)) { - return; - } - int power = blockPower.get(p); - - if (power <= 0) { - return; - } - - Location l = e.getTo(); - World world = l.getWorld(); - Set locs = new HashSet<>(); - locs.add(world.getBlockAt(l.clone().add(0.3, -1, -0.3))); - locs.add(world.getBlockAt(l.clone().add(-0.3, -1, -0.3))); - locs.add(world.getBlockAt(l.clone().add(0.3, -1, 0.3))); - locs.add(world.getBlockAt(l.clone().add(-0.3, -1, +0.3))); - - for (Block b : locs) { - if (addFoundation(b)) { - power--; - getPlayer(p).getData().addStat("architect.foundation.blocks-placed", 1); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + if (!p.isSneaking()) { + return; + } + if (getActiveBlockPlaceLevel(p, p.getLocation()) <= 0) { + return; + } + if (e.getTo() == null || !e.getFrom().getBlock().equals(e.getTo().getBlock())) { + return; } + if (!this.active.contains(id)) { + return; + } + int power = blockPower.getOrDefault(id, 0); if (power <= 0) { - break; + return; + } + + Location l = e.getTo(); + World world = l.getWorld(); + Set locs = new HashSet<>(); + locs.add(world.getBlockAt(l.clone().add(0.3, -1, -0.3))); + locs.add(world.getBlockAt(l.clone().add(-0.3, -1, -0.3))); + locs.add(world.getBlockAt(l.clone().add(0.3, -1, 0.3))); + locs.add(world.getBlockAt(l.clone().add(-0.3, -1, +0.3))); + + for (Block b : locs) { + if (addFoundation(b)) { + power--; + getPlayer(p).getData().addStat("architect.foundation.blocks-placed", 1); + } + + if (power <= 0) { + break; + } } - } - blockPower.put(p, power); + blockPower.put(id, power); + }); } // prevent piston from moving blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockPistonExtendEvent e) { - if (e.isCancelled()) { - return; - } e.getBlocks().forEach(b -> { if (activeBlocks.contains(b)) { Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); @@ -173,9 +165,6 @@ public void on(BlockPistonExtendEvent e) { // prevent piston from pulling blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockPistonRetractEvent e) { - if (e.isCancelled()) { - return; - } e.getBlocks().forEach(b -> { if (activeBlocks.contains(b)) { Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); @@ -187,9 +176,6 @@ public void on(BlockPistonRetractEvent e) { // prevent TNT from destroying blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockExplodeEvent e) { - if (e.isCancelled()) { - return; - } if (activeBlocks.contains(e.getBlock())) { Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); e.setCancelled(true); @@ -207,37 +193,34 @@ public void on(BlockBreakEvent e) { // prevent Entities from destroying blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityExplodeEvent e) { - if (e.isCancelled()) { - return; - } e.blockList().removeIf(activeBlocks::contains); } @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerToggleSneakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p) || p.getGameMode().equals(GameMode.CREATIVE) - || p.getGameMode().equals(GameMode.SPECTATOR)) { - return; - } - - boolean ready = !hasCooldown(p); - boolean active = this.active.contains(p); - - if (e.isSneaking() && ready && !active) { - this.active.add(p); - cooldowns.put(p, Long.MAX_VALUE); - // effect start placing - } else if (!e.isSneaking() && active) { - this.active.remove(p); - cooldowns.put(p, M.ms() + getConfig().cooldown); - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1.0f, 10.0f); - sp.play(p.getLocation(), Sound.BLOCK_SCULK_CATALYST_BREAK, 1.0f, 0.81f); - } + withAdaptedPlayer(p, e, () -> { + if (p.getGameMode().equals(GameMode.CREATIVE) + || p.getGameMode().equals(GameMode.SPECTATOR)) { + return; + } + UUID id = p.getUniqueId(); + + boolean ready = !hasCooldown(id); + boolean active = this.active.contains(id); + + if (e.isSneaking() && ready && !active) { + this.active.add(id); + cooldowns.put(id, Long.MAX_VALUE); + // effect start placing + } else if (!e.isSneaking() && active) { + this.active.remove(id); + cooldowns.put(id, M.ms() + getConfig().cooldown); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1.0f, 10.0f); + sp.play(p.getLocation(), Sound.BLOCK_SCULK_CATALYST_BREAK, 1.0f, 0.81f); + } + }); } public boolean addFoundation(Block block) { @@ -291,39 +274,55 @@ public int getBlockPower(double factor) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); - if (!hasAdaptation(i)) { + if (i == null || !i.isOnline()) { continue; } - - boolean ready = !hasCooldown(i); - int availablePower = getBlockPower(getLevelPercent(i)); - blockPower.compute(i, (k, v) -> { - if ((k == null || v == null) || (ready && v != availablePower)) { - if (i == null) { - return 0; - } - final var world = i.getWorld(); - final var location = i.getLocation(); - - SoundPlayer spw = SoundPlayer.of(world); - spw.play(location, Sound.BLOCK_BEACON_ACTIVATE, 1.0f, 10.0f); - spw.play(location, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1.0f, 0.81f); - - return availablePower; - } - return v; - }); + withPlayerThread(i, () -> refreshPlayerPower(i)); } } - private boolean hasCooldown(Player i) { - if (cooldowns.containsKey(i)) { - if (M.ms() >= cooldowns.get(i)) { - cooldowns.remove(i); + private void refreshPlayerPower(Player i) { + UUID id = i.getUniqueId(); + if (!hasActiveAdaptation(i)) { + active.remove(id); + blockPower.remove(id); + cooldowns.remove(id); + return; + } + + boolean ready = !hasCooldown(id); + int availablePower = getBlockPower(getLevelPercent(i)); + blockPower.compute(id, (k, v) -> { + if (v == null || (ready && v != availablePower)) { + final var world = i.getWorld(); + final var location = i.getLocation(); + + SoundPlayer spw = SoundPlayer.of(world); + spw.play(location, Sound.BLOCK_BEACON_ACTIVATE, 1.0f, 10.0f); + spw.play(location, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1.0f, 0.81f); + + return availablePower; } + return v; + }); + } + + private boolean hasCooldown(UUID id) { + Long cooldown = cooldowns.get(id); + if (cooldown != null && M.ms() >= cooldown) { + cooldowns.remove(id); + cooldown = null; } - return cooldowns.containsKey(i); + return cooldown != null; + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + blockPower.remove(id); + cooldowns.remove(id); + active.remove(id); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java index 0d84620de..07a960feb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java @@ -22,9 +22,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -36,13 +37,6 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Particles; - public class ArchitectGlass extends SimpleAdaptation { public ArchitectGlass() { super("architect-glass"); @@ -83,27 +77,26 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (hasAdaptation(p) && (p.getInventory().getItemInMainHand().getType() == Material.AIR || !isTool(p.getInventory().getItemInMainHand())) && !e.isCancelled()) { - if (!canBlockBreak(p, e.getBlock().getLocation())) { - return; - } - if (e.getBlock().getType().toString().contains("GLASS") && !e.getBlock().getType().toString().contains("TINTED_GLASS")) { - e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), new ItemStack(e.getBlock().getType(), 1)); - SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); - spw.play(e.getBlock().getLocation(), Sound.BLOCK_LARGE_AMETHYST_BUD_BREAK, 1.0f, 1.0f); - if (areParticlesEnabled()) { + withAdaptedPlayer(p, e, () -> { + if (p.getInventory().getItemInMainHand().getType() == Material.AIR || !isTool(p.getInventory().getItemInMainHand())) { + if (!canBlockBreak(p, e.getBlock().getLocation())) { + return; + } + if (e.getBlock().getType().toString().contains("GLASS") && !e.getBlock().getType().toString().contains("TINTED_GLASS")) { + e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), new ItemStack(e.getBlock().getType(), 1)); + SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); + spw.play(e.getBlock().getLocation(), Sound.BLOCK_LARGE_AMETHYST_BUD_BREAK, 1.0f, 1.0f); + if (areParticlesEnabled()) { - e.getBlock().getWorld().spawnParticle(Particle.SCRAPE, e.getBlock().getLocation(), 1); - vfxCuboidOutline(e.getBlock(), Particle.REVERSE_PORTAL); + e.getBlock().getWorld().spawnParticle(Particle.SCRAPE, e.getBlock().getLocation(), 1); + vfxCuboidOutline(e.getBlock(), Particle.REVERSE_PORTAL); + } + e.getBlock().breakNaturally(); + getPlayer(p).getData().addStat("architect.glass.blocks-recovered", 1); } - e.getBlock().breakNaturally(); - getPlayer(p).getData().addStat("architect.glass.blocks-recovered", 1); } - } + }); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java index 128fea317..b4cfc29b4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java @@ -23,18 +23,23 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Container; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Display; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -44,17 +49,12 @@ import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.inventory.ItemStack; -import java.util.List; -import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Particles; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class ArchitectPlacement extends SimpleAdaptation { - private final KMap> totalMap = new KMap<>(); + private final Map> totalMap = new ConcurrentHashMap<>(); + private final Map> previewDisplays = new ConcurrentHashMap<>(); public ArchitectPlacement() { super("architect-placement"); @@ -102,170 +102,213 @@ private BlockFace getBlockFace(Player player) { @EventHandler public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - totalMap.remove(p); + UUID id = e.getPlayer().getUniqueId(); + totalMap.remove(id); + clearPreviewDisplays(id); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockPlaceEvent e) { Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p) || !p.isSneaking()) - return; - - var blocks = totalMap.get(p); - if (blocks == null || blocks.isEmpty()) - return; + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + SoundPlayer sp = SoundPlayer.of(p); + if (getActiveLevel(p, Player::isSneaking) <= 0) { + return; + } - ItemStack hand = e.getItemInHand(); - if (!hand.getType().isBlock() || blocks.keys().nextElement().getType() != hand.getType()) - return; + var blocks = totalMap.get(id); + if (blocks == null || blocks.isEmpty()) { + return; + } - double v = getValue(e.getBlock()); - Block ignored = blocks.keySet() - .stream() - .filter(b -> b.getRelative(blocks.get(b)).equals(e.getBlock())) - .findFirst() - .orElse(null); + ItemStack hand = e.getItemInHand(); + Block first = blocks.keySet().stream().findFirst().orElse(null); + if (!hand.getType().isBlock() || first == null || first.getType() != hand.getType()) { + return; + } - if (hand.getAmount() < blocks.size()) { - Adapt.messagePlayer(p, C.RED + Localizer.dLocalize("architect.placement.lore1") + " " + C.GREEN + blocks.size() + C.RED + " " + Localizer.dLocalize("architect.placement.lore2")); - return; - } + double v = getValue(e.getBlock()); + Block ignored = blocks.keySet() + .stream() + .filter(b -> b.getRelative(blocks.get(b)).equals(e.getBlock())) + .findFirst() + .orElse(null); - if (ignored != null) blocks.remove(ignored); - for (Block b : blocks.keySet()) { // Block Placer - Block relative = b.getRelative(blocks.get(b)); - if (!relative.getType().isAir()) - continue; + if (hand.getAmount() < blocks.size()) { + Adapt.messagePlayer(p, C.RED + Localizer.dLocalize("architect.placement.lore1") + " " + C.GREEN + blocks.size() + C.RED + " " + Localizer.dLocalize("architect.placement.lore2")); + return; + } - if (!canBlockPlace(p, relative.getLocation())) { - Adapt.verbose("Player " + p.getName() + " doesn't have permission."); - continue; + if (ignored != null) { + blocks.remove(ignored); } + for (Map.Entry entry : new ArrayList<>(blocks.entrySet())) { // Block Placer + Block b = entry.getKey(); + BlockFace face = entry.getValue(); + if (b == null || face == null) { + continue; + } - relative.setBlockData(b.getBlockData()); - getPlayer(p).getData().addStat("blocks.placed", 1); - getPlayer(p).getData().addStat("blocks.placed.value", v); - getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); - sp.play(b.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); - xp(p, 2); + Block relative = b.getRelative(face); + if (!relative.getType().isAir()) { + continue; + } - hand.setAmount(hand.getAmount() - 1); - } + if (!canBlockPlace(p, relative.getLocation())) { + Adapt.verbose("Player " + p.getName() + " doesn't have permission."); + continue; + } - if (ignored != null) { - e.getBlock().setBlockData(ignored.getBlockData()); - getPlayer(p).getData().addStat("blocks.placed", 1); - getPlayer(p).getData().addStat("blocks.placed.value", v); - getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); - sp.play(ignored.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); - xp(p, 2); + relative.setBlockData(b.getBlockData()); + getPlayer(p).getData().addStat("blocks.placed", 1); + getPlayer(p).getData().addStat("blocks.placed.value", v); + getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); + sp.play(b.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); + xp(p, 2); - hand.setAmount(hand.getAmount() - 1); - } else e.setCancelled(true); + hand.setAmount(hand.getAmount() - 1); + } - totalMap.remove(p); - if (hand.getAmount() > 0) { - runPlayerViewport(getBlockFace(p), p.getTargetBlock(null, 5), p.getInventory().getItemInMainHand().getType(), p); - } + if (ignored != null) { + e.getBlock().setBlockData(ignored.getBlockData()); + getPlayer(p).getData().addStat("blocks.placed", 1); + getPlayer(p).getData().addStat("blocks.placed.value", v); + getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); + sp.play(ignored.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); + xp(p, 2); + + hand.setAmount(hand.getAmount() - 1); + } else { + e.setCancelled(true); + } + + totalMap.remove(id); + clearPreviewDisplays(id); + if (hand.getAmount() > 0) { + runPlayerViewport(getBlockFace(p), p.getTargetBlock(null, 5), p.getInventory().getItemInMainHand().getType(), p); + } + }); } @EventHandler public void on(PlayerToggleSneakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (hasAdaptation(p) && p.isSneaking()) { - totalMap.remove(p); - } - - if (hasAdaptation(p) && !p.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { - Block block = p.getTargetBlock(null, 5); // 5 is the range of player - if (block instanceof Container) { // return if block is a container + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + totalMap.remove(id); + clearPreviewDisplays(id); return; } - Material handMaterial = p.getInventory().getItemInMainHand().getType(); - if (handMaterial.isAir()) { - return; + + if (e.isSneaking()) { + totalMap.remove(id); + clearPreviewDisplays(id); } - BlockFace viewPortBlock = getBlockFace(p); - runPlayerViewport(viewPortBlock, block, handMaterial, p); - } + + if (!e.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { + Block block = p.getTargetBlock(null, 5); // 5 is the range of player + if (block instanceof Container) { // return if block is a container + return; + } + Material handMaterial = p.getInventory().getItemInMainHand().getType(); + if (handMaterial.isAir()) { + return; + } + BlockFace viewPortBlock = getBlockFace(p); + runPlayerViewport(viewPortBlock, block, handMaterial, p); + } + }); } @EventHandler public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (hasAdaptation(p) && !p.isSneaking()) { - totalMap.remove(p); - } - - if (hasAdaptation(p) && p.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { - Block block = p.getTargetBlock(null, 5); // 5 is the range of player - if (block instanceof Container) { // return if block is a container + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + totalMap.remove(id); + clearPreviewDisplays(id); return; } - Material handMaterial = p.getInventory().getItemInMainHand().getType(); - if (handMaterial.isAir()) { - return; + + if (!p.isSneaking()) { + totalMap.remove(id); + clearPreviewDisplays(id); } - BlockFace viewPortBlock = getBlockFace(p); - runPlayerViewport(viewPortBlock, block, handMaterial, p); - } + if (p.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { + Block block = p.getTargetBlock(null, 5); // 5 is the range of player + if (block instanceof Container) { // return if block is a container + return; + } + Material handMaterial = p.getInventory().getItemInMainHand().getType(); + if (handMaterial.isAir()) { + return; + } + BlockFace viewPortBlock = getBlockFace(p); + runPlayerViewport(viewPortBlock, block, handMaterial, p); + } + }); } public void runPlayerViewport(BlockFace viewPortBlock, Block block, Material handMaterial, Player p) { - if (viewPortBlock != null && (viewPortBlock.getDirection().equals(BlockFace.NORTH.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.SOUTH.getDirection()))) { // North & South = X + UUID id = p.getUniqueId(); + if (viewPortBlock == null || block == null || handMaterial == null || handMaterial.isAir()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; + } + + Map map = new ConcurrentHashMap<>(); + + if (viewPortBlock.getDirection().equals(BlockFace.NORTH.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.SOUTH.getDirection())) { // North & South = X for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { // 1 is the radius of the blocks for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { - if (handMaterial == block.getWorld().getBlockAt(x, y, block.getZ()).getType()) { - if (totalMap.get(p) == null) { - KMap map = new KMap<>(); - map.put(block.getWorld().getBlockAt(x, y, block.getZ()), viewPortBlock); - totalMap.put(p, map); - } else if (totalMap.get(p).size() <= getConfig().maxBlocks) { - totalMap.get(p).put(block.getWorld().getBlockAt(x, y, block.getZ()), viewPortBlock); - } - } + addViewportEntry(map, block.getWorld().getBlockAt(x, y, block.getZ()), viewPortBlock, handMaterial); } } - } else if (viewPortBlock != null && (viewPortBlock.getDirection().equals(BlockFace.EAST.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.WEST.getDirection()))) { // East & West = Z + } else if (viewPortBlock.getDirection().equals(BlockFace.EAST.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.WEST.getDirection())) { // East & West = Z for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { - if (handMaterial == block.getWorld().getBlockAt(block.getX(), y, z).getType()) { - if (totalMap.get(p) == null) { - KMap map = new KMap<>(); - map.put(block.getWorld().getBlockAt(block.getX(), y, z), viewPortBlock); - totalMap.put(p, map); - } else if (totalMap.get(p).size() <= getConfig().maxBlocks) { - totalMap.get(p).put(block.getWorld().getBlockAt(block.getX(), y, z), viewPortBlock); - } - } + addViewportEntry(map, block.getWorld().getBlockAt(block.getX(), y, z), viewPortBlock, handMaterial); } } - } else if (viewPortBlock != null && (viewPortBlock.getDirection().equals(BlockFace.UP.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.DOWN.getDirection()))) { // Up & Down = Y + } else if (viewPortBlock.getDirection().equals(BlockFace.UP.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.DOWN.getDirection())) { // Up & Down = Y for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { - if (handMaterial == block.getWorld().getBlockAt(x, block.getY(), z).getType()) { - if (totalMap.get(p) == null) { - KMap map = new KMap<>(); - map.put(block.getWorld().getBlockAt(x, block.getY(), z), viewPortBlock); - totalMap.put(p, map); - } else if (totalMap.get(p).size() <= getConfig().maxBlocks) { - totalMap.get(p).put(block.getWorld().getBlockAt(x, block.getY(), z), viewPortBlock); - } - } + addViewportEntry(map, block.getWorld().getBlockAt(x, block.getY(), z), viewPortBlock, handMaterial); } } } + + if (map.isEmpty()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; + } + + totalMap.put(id, map); + } + + private void addViewportEntry(Map map, Block target, BlockFace viewPortBlock, Material handMaterial) { + if (target == null || viewPortBlock == null || handMaterial == null) { + return; + } + + int maxBlocks = Math.max(1, getConfig().maxBlocks); + if (map.size() >= maxBlocks) { + return; + } + + if (target.getType() == handMaterial) { + map.put(target, viewPortBlock); + } } @Override @@ -281,24 +324,239 @@ public boolean isPermanent() { @Override public void onTick() { - if (!totalMap.isEmpty()) { - for (Player p : totalMap.keySet()) { // Get every player that has a map - if (!hasAdaptation(p) || !p.isSneaking()) { - totalMap.clear(); - return; + if (previewDisplays.isEmpty() && totalMap.isEmpty()) { + return; + } + + for (UUID playerId : new HashSet<>(previewDisplays.keySet())) { + if (!totalMap.containsKey(playerId)) { + clearPreviewDisplays(playerId); + } + } + + if (totalMap.isEmpty()) { + return; + } + + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline() || !totalMap.containsKey(p.getUniqueId())) { + continue; + } + withPlayerThread(p, () -> renderPreview(p)); + } + } + + private void renderPreview(Player p) { + UUID id = p.getUniqueId(); + Map blockRender = totalMap.get(id); + if (getActiveLevel(p, Player::isSneaking) <= 0 || blockRender == null || blockRender.isEmpty()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; + } + + Set activePreviews = new HashSet<>(); + boolean displayPreview = getConfig().useDisplayEntities; + + for (Map.Entry entry : new ArrayList<>(blockRender.entrySet())) { + Block b = entry.getKey(); + BlockFace bf = entry.getValue(); + if (b == null || bf == null || b instanceof Container) { + continue; + } + + Block transposedBlock = b.getRelative(bf); + if (displayPreview) { + if (!transposedBlock.getType().isAir()) { + continue; } - Map blockRender = totalMap.get(p); - for (Block b : blockRender.keySet()) { // Get the blocks in that map that bind with a BlockFace - if (b instanceof Container) { // return if block is a container - return; - } - BlockFace bf = blockRender.get(b); // Get that blockface - Block transposedBlock = b.getRelative(bf); - if (areParticlesEnabled()) { - vfxCuboidOutline(transposedBlock, Particle.REVERSE_PORTAL); - } + + PreviewKey key = PreviewKey.of(transposedBlock); + activePreviews.add(key); + ensurePreviewDisplay(id, key, b.getBlockData()); + } else if (areParticlesEnabled()) { + vfxCuboidOutline(transposedBlock, Particle.REVERSE_PORTAL); + } + } + + if (displayPreview) { + clearStalePreviewDisplays(id, activePreviews); + } else { + clearPreviewDisplays(id); + } + } + + private void ensurePreviewDisplay(UUID playerId, PreviewKey key, org.bukkit.block.data.BlockData sourceData) { + if (key == null || sourceData == null) { + return; + } + + Map displays = previewDisplays.computeIfAbsent(playerId, unused -> new ConcurrentHashMap<>()); + BlockDisplay existing = displays.get(key); + if (existing != null) { + if (existing.isValid()) { + if (J.isFoliaThreading()) { + J.runEntity(existing, () -> { + if (!existing.isValid()) { + displays.remove(key, existing); + return; + } + existing.setBlock(sourceData); + showPreviewToOwner(playerId, existing); + }); + } else { + existing.setBlock(sourceData); + showPreviewToOwner(playerId, existing); + } + return; + } + displays.remove(key); + } + + Runnable spawnTask = () -> { + if (!totalMap.containsKey(playerId)) { + return; + } + + org.bukkit.World world = Bukkit.getWorld(key.worldId()); + if (world == null) { + return; + } + + Block targetBlock = world.getBlockAt(key.x(), key.y(), key.z()); + BlockDisplay live = displays.get(key); + if (live != null && live.isValid()) { + live.setBlock(sourceData); + showPreviewToOwner(playerId, live); + return; + } + + BlockDisplay spawned = world.spawn(targetBlock.getLocation(), BlockDisplay.class, display -> { + display.setPersistent(false); + display.setInvulnerable(true); + display.setGravity(false); + display.setSilent(true); + display.setVisibleByDefault(false); + display.setInterpolationDuration(2); + display.setTeleportDuration(1); + display.setViewRange((float) Math.max(0.25, getConfig().displayEntityViewRange)); + display.setShadowRadius(0f); + display.setShadowStrength(0f); + display.setBrightness(new Display.Brightness(15, 15)); + display.setBlock(sourceData); + }); + displays.put(key, spawned); + showPreviewToOwner(playerId, spawned); + hidePreviewFromOthers(playerId, spawned); + }; + + if (J.isFoliaThreading()) { + org.bukkit.World world = Bukkit.getWorld(key.worldId()); + if (world == null) { + return; + } + J.runAt(new org.bukkit.Location(world, key.x() + 0.5, key.y(), key.z() + 0.5), spawnTask); + return; + } + + spawnTask.run(); + } + + private void clearStalePreviewDisplays(UUID playerId, Set activePreviews) { + Map displays = previewDisplays.get(playerId); + if (displays == null || displays.isEmpty()) { + return; + } + + for (PreviewKey key : new HashSet<>(displays.keySet())) { + if (activePreviews.contains(key)) { + continue; + } + + BlockDisplay removed = displays.remove(key); + removeDisplayEntity(removed); + } + + if (displays.isEmpty()) { + previewDisplays.remove(playerId); + } + } + + private void clearPreviewDisplays(UUID playerId) { + Map displays = previewDisplays.remove(playerId); + if (displays == null || displays.isEmpty()) { + return; + } + + for (BlockDisplay display : displays.values()) { + removeDisplayEntity(display); + } + } + + private void removeDisplayEntity(Entity entity) { + if (entity == null) { + return; + } + + if (J.isFoliaThreading()) { + J.runEntity(entity, () -> { + if (entity.isValid()) { + entity.remove(); + } + }); + } else if (entity.isValid()) { + entity.remove(); + } + } + + private void showPreviewToOwner(UUID playerId, Entity entity) { + if (entity == null || !entity.isValid()) { + return; + } + + Player owner = Bukkit.getPlayer(playerId); + if (owner == null || !owner.isOnline()) { + return; + } + + if (J.isFoliaThreading()) { + J.runEntity(owner, () -> { + if (entity.isValid()) { + owner.showEntity(Adapt.instance, entity); } + }); + return; + } + + owner.showEntity(Adapt.instance, entity); + } + + private void hidePreviewFromOthers(UUID ownerId, Entity entity) { + if (entity == null || !entity.isValid()) { + return; + } + + for (Player viewer : Bukkit.getOnlinePlayers()) { + if (viewer == null || !viewer.isOnline() || viewer.getUniqueId().equals(ownerId)) { + continue; } + + if (J.isFoliaThreading()) { + J.runEntity(viewer, () -> { + if (entity.isValid()) { + viewer.hideEntity(Adapt.instance, entity); + } + }); + } else { + viewer.hideEntity(Adapt.instance, entity); + } + } + } + + private record PreviewKey(UUID worldId, int x, int y, int z) { + private static PreviewKey of(Block block) { + return new PreviewKey(block.getWorld().getUID(), block.getX(), block.getY(), block.getZ()); } } @@ -313,6 +571,10 @@ protected static class Config { boolean enabled = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Placement adaptation.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Use owner-only block display previews instead of particles for the wand guide.", impact = "True shows ghost blocks only to the wand user; false keeps particle outlines.") + boolean useDisplayEntities = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "View range used for wand preview display entities.", impact = "Lower values hide previews sooner; higher values keep them visible from farther away.") + double displayEntityViewRange = 0.75; @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") int baseCost = 6; @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java index 6152ff865..0d74bc92d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java @@ -22,15 +22,17 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; +import org.bukkit.Axis; import org.bukkit.Material; +import org.bukkit.Sound; 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.Orientable; @@ -42,15 +44,8 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; -import org.bukkit.Axis; -import org.bukkit.Sound; -import org.bukkit.block.BlockFace; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Set; +import java.util.*; public class ArchitectSmartShape extends SimpleAdaptation { private static final List ROTATION_ORDER = Arrays.asList( @@ -120,31 +115,33 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { - return; - } + withAdaptedPlayer(p, e, () -> { + if (!p.isSneaking()) { + return; + } - ItemStack hand = p.getInventory().getItemInMainHand(); - if (isItem(hand) && hand.getType() != Material.AIR) { - return; - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (isItem(hand) && hand.getType() != Material.AIR) { + return; + } - Block target = e.getClickedBlock(); - if (!canBlockPlace(p, target.getLocation())) { - return; - } + Block target = e.getClickedBlock(); + if (!canBlockPlace(p, target.getLocation())) { + return; + } - BlockData data = target.getBlockData().clone(); - int options = rotateData(data); - if (options <= 0) { - return; - } + BlockData data = target.getBlockData().clone(); + int options = rotateData(data); + if (options <= 0) { + return; + } - target.setBlockData(data, true); - e.setCancelled(true); - SoundPlayer.of(p.getWorld()).play(target.getLocation(), Sound.ITEM_AXE_STRIP, 0.45f, 1.8f); - xp(p, Math.max(getConfig().minXpPerRotate, options * getConfig().xpPerOrientationOption)); - getPlayer(p).getData().addStat("architect.smart-shape.rotations", 1); + target.setBlockData(data, true); + e.setCancelled(true); + SoundPlayer.of(p.getWorld()).play(target.getLocation(), Sound.ITEM_AXE_STRIP, 0.45f, 1.8f); + xp(p, Math.max(getConfig().minXpPerRotate, options * getConfig().xpPerOrientationOption)); + getPlayer(p).getData().addStat("architect.smart-shape.rotations", 1); + }); } private int rotateData(BlockData data) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index 1210cb414..cb752efe4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -18,8 +18,6 @@ package art.arcane.adapt.content.adaptation.architect; -import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; - import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -27,21 +25,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.BoundRedstoneTorch; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; - -import java.util.HashMap; -import java.util.Map; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.AnaloguePowerable; import org.bukkit.block.data.BlockData; @@ -54,17 +47,13 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; +import java.util.Map; +import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.reflect.events.api.Event; -import art.arcane.adapt.util.reflect.registries.Particles; +import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class ArchitectWirelessRedstone extends SimpleAdaptation { - private final Map cooldowns; + private final Map cooldowns; public ArchitectWirelessRedstone() { super("architect-wireless-redstone"); @@ -84,7 +73,7 @@ public ArchitectWirelessRedstone() { .ingredient(Material.ENDER_PEARL) .result(BoundRedstoneTorch.io.withData(new BoundRedstoneTorch.Data(null))) .build()); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.REDSTONE) .key("challenge_architect_wireless_100") @@ -139,25 +128,22 @@ public void onPlayerInteract(PlayerInteractEvent event) { } Player player = event.getPlayer(); + withPlayerThread(player, event, () -> { + if (resolveInteractContext(player, player.getLocation()) == null) { + return; + } - if (!hasAdaptation(player)) { - return; - } - - boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; - boolean inCreative = player.getGameMode() == GameMode.CREATIVE; - if (inCreative && !canUseInCreative) { - return; - } - - if (!canInteract(event.getPlayer(), event.getPlayer().getLocation())) { - return; - } + boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; + boolean inCreative = player.getGameMode() == GameMode.CREATIVE; + if (inCreative && !canUseInCreative) { + return; + } - switch (event.getAction()) { - case LEFT_CLICK_BLOCK -> handleLeftClickBlock(event, player); - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> handleRightClick(event, player); - } + switch (event.getAction()) { + case LEFT_CLICK_BLOCK -> handleLeftClickBlock(event, player); + case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> handleRightClick(event, player); + } + }); } @@ -202,7 +188,7 @@ private void handleRightClick(PlayerInteractEvent event, Player player) { SoundPlayer sp = SoundPlayer.of(player); sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); } else { - cooldowns.put(player, System.currentTimeMillis() + getConfig().cooldown); + cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + getConfig().cooldown); updatePlayerCooldown(player, false); triggerPulse(player, event.getItem()); } @@ -214,12 +200,12 @@ public void updatePlayerCooldown(Player player, boolean reset) { private boolean hasCooldown(Player i) { - if (cooldowns.containsKey(i)) { - if (M.ms() >= cooldowns.get(i)) { - cooldowns.remove(i); + if (cooldowns.containsKey(i.getUniqueId())) { + if (M.ms() >= cooldowns.get(i.getUniqueId())) { + cooldowns.remove(i.getUniqueId()); } } - return cooldowns.containsKey(i); + return cooldowns.containsKey(i.getUniqueId()); } @@ -283,13 +269,16 @@ public boolean isEnabled() { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } ItemStack hand = p.getInventory().getItemInMainHand(); ItemStack offhand = p.getInventory().getItemInOffHand(); if ((isRedstoneTorch(hand) && BoundRedstoneTorch.hasItemData(hand)) || ( isRedstoneTorch(offhand) && BoundRedstoneTorch.hasItemData(offhand))) { - J.runEntity(p, () -> updatePlayerCooldown(p, false)); + withPlayerThread(p, () -> updatePlayerCooldown(p, false)); } else { - J.runEntity(p, () -> updatePlayerCooldown(p, true)); + withPlayerThread(p, () -> updatePlayerCooldown(p, true)); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java index b8a5be247..a48148676 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.axe; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; @@ -25,10 +24,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -41,11 +42,6 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class AxeChop extends SimpleAdaptation { public AxeChop() { @@ -101,7 +97,7 @@ public void on(PlayerInteractEvent e) { return; } - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isAxe(p.getInventory().getItemInMainHand()) && hasAdaptation(p)) { + if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isAxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java index 418c4b2a8..40f94303d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java @@ -23,11 +23,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Materials; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -35,7 +35,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.inventory.CraftItemEvent; import org.bukkit.inventory.ItemStack; -import art.arcane.adapt.util.reflect.registries.Materials; public class AxeCraftLogSwap extends SimpleAdaptation { @@ -1032,10 +1031,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { return; } if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().startsWith("axe-swap")) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java index 9c846ae98..93edb57b3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java @@ -22,16 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; import lombok.NoArgsConstructor; -import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Item; @@ -76,20 +74,13 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } + Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { - return; - } - if (p.getGameMode() != GameMode.SURVIVAL) { - return; - } - if (!canBlockBreak(p, e.getBlock().getLocation())) { + if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { return; } + + SoundPlayer sp = SoundPlayer.of(p); if (ItemListings.toolAxes.contains(p.getInventory().getItemInMainHand().getType())) { List items = new KList<>(e.getItems()); e.getItems().clear(); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java index 98e5f477b..e5c02d4ff 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java @@ -17,17 +17,19 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.axe; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.Impulse; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -39,12 +41,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.Impulse; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class AxeGroundSmash extends SimpleAdaptation { public AxeGroundSmash() { super("axe-ground-smash"); @@ -87,43 +83,40 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var combat = resolveMeleeContext(e, this::isAxe); + if (combat == null) { return; } - if (e.getDamager() instanceof Player p && hasAdaptation(p) && p.isSneaking()) { - if (!isAxe(p.getInventory().getItemInMainHand())) { - return; - } - - double f = getLevelPercent(p); - if (p.hasCooldown(p.getInventory().getItemInMainHand().getType())) { - return; - } + Player p = combat.attacker(); + if (!p.isSneaking()) { + return; + } - p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(f)); - double radius = getRadius(f); - new Impulse(radius) - .damage(getDamage(f), getFalloffDamage(f)) - .force(getForce(f)) - .punch(e.getEntity().getLocation()); - int mobsHit = 0; - for (Entity nearby : e.getEntity().getWorld().getNearbyEntities(e.getEntity().getLocation(), radius, radius, radius)) { - if (nearby instanceof LivingEntity && nearby != p) { - mobsHit++; - } + double f = getLevelPercent(combat.level()); + + p.setCooldown(combat.mainHand().getType(), getCooldownTime(f)); + double radius = getRadius(f); + new Impulse(radius) + .damage(getDamage(f), getFalloffDamage(f)) + .force(getForce(f)) + .punch(e.getEntity().getLocation()); + int mobsHit = 0; + for (Entity nearby : e.getEntity().getWorld().getNearbyEntities(e.getEntity().getLocation(), radius, radius, radius)) { + if (nearby instanceof LivingEntity && nearby != p) { + mobsHit++; } - if (mobsHit > 0) { - getPlayer(p).getData().addStat("axe.ground-smash.mobs-hit", mobsHit); - if (mobsHit >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_ground_smash_5")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_ground_smash_5"); - } + } + if (mobsHit > 0) { + getPlayer(p).getData().addStat("axe.ground-smash.mobs-hit", mobsHit); + if (mobsHit >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_ground_smash_5")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_ground_smash_5"); } - SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.6f, 0.4f); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.5f, 0.1f); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_TURTLE_EGG_CRACK, SoundCategory.HOSTILE, 1f, 0.4f); } + SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.6f, 0.4f); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.5f, 0.1f); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_TURTLE_EGG_CRACK, SoundCategory.HOSTILE, 1f, 0.4f); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index 015ace7bc..030548cc3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -22,13 +22,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -41,12 +43,6 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - import java.util.*; import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; @@ -91,9 +87,6 @@ private int getRadius(int lvl) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } if (VEIN_MINED.get(e.getBlock())) { return; @@ -101,7 +94,7 @@ public void on(BlockBreakEvent e) { Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -121,7 +114,7 @@ public void on(BlockBreakEvent e) { VEIN_MINED.add(e.getBlock()); Block block = e.getBlock(); - Map blockMap = new HashMap<>(); + Map blockMap = new java.util.concurrent.ConcurrentHashMap<>(); Deque stack = new LinkedList<>(); stack.push(block); int radius = getRadius(getLevel(p)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java index 984236a58..f9617cefe 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java @@ -23,21 +23,20 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.Event; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -45,7 +44,6 @@ import org.bukkit.inventory.EquipmentSlot; import java.util.ArrayDeque; -import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -96,7 +94,7 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled() || e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { return; } @@ -105,7 +103,8 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || !isAxe(p.getInventory().getItemInMainHand())) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { return; } @@ -115,7 +114,7 @@ public void on(PlayerInteractEvent e) { } setStorage(p, "timberMarkBlock", clicked.getLocation().toString()); - setStorage(p, "timberMarkUntil", System.currentTimeMillis() + getMarkDurationMillis(getLevel(p))); + setStorage(p, "timberMarkUntil", System.currentTimeMillis() + getMarkDurationMillis(level)); e.setUseInteractedBlock(Event.Result.DENY); e.setUseItemInHand(Event.Result.DENY); e.setCancelled(true); @@ -124,12 +123,10 @@ public void on(PlayerInteractEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !isAxe(p.getInventory().getItemInMainHand())) { + int level = getActiveLevel(p); + if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { return; } @@ -144,7 +141,7 @@ public void on(BlockBreakEvent e) { } Material type = e.getBlock().getType(); - int maxBlocks = getMaxBlocks(getLevel(p)); + int maxBlocks = getMaxBlocks(level); Set connected = floodLogs(e.getBlock(), type, maxBlocks); int logsFelled = 0; for (Block b : connected) { @@ -161,7 +158,6 @@ public void on(BlockBreakEvent e) { logsFelled++; } - int level = getLevel(p); Set leaves = floodLeaves(connected, getMaxLeaves(level)); for (Block leaf : leaves) { if (!canBlockBreak(p, leaf.getLocation())) { @@ -182,7 +178,7 @@ public void on(BlockBreakEvent e) { } private Set floodLogs(Block start, Material type, int maxBlocks) { - Set visited = new HashSet<>(); + Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); ArrayDeque queue = new ArrayDeque<>(); queue.add(start); visited.add(start); @@ -209,7 +205,7 @@ private Set floodLogs(Block start, Material type, int maxBlocks) { } private Set floodLeaves(Set logs, int maxLeaves) { - Set visited = new HashSet<>(); + Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); ArrayDeque queue = new ArrayDeque<>(); for (Block log : logs) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index fd20be2ad..0bb4e5716 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -24,13 +24,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -46,12 +48,6 @@ import java.util.HashSet; import java.util.Set; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class AxeWoodVeinminer extends SimpleAdaptation { @@ -102,15 +98,12 @@ private int getRadius(int lvl) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } if (VEIN_MINED.get(e.getBlock())) { return; } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java index b12cb6240..97534ea44 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java @@ -23,17 +23,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; @@ -85,7 +83,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player defender) || !isBastionStance(defender)) { + if (!(e.getEntity() instanceof Player defender) || !isBastionStance(defender)) { return; } @@ -93,7 +91,10 @@ public void on(EntityDamageByEntityEvent e) { return; } - int level = getLevel(defender); + int level = getActiveLevel(defender); + if (level <= 0) { + return; + } // Track session counter for special achievement int sessionCount = getStorageInt(defender, "bastionSessionCount", 0) + 1; @@ -119,17 +120,22 @@ public void on(EntityDamageByEntityEvent e) { @EventHandler(priority = EventPriority.HIGH) public void on(PlayerVelocityEvent e) { Player p = e.getPlayer(); - if (!isBastionStance(p)) { + int level = getActiveLevel(p); + if (!isBastionStance(p, level)) { return; } - double factor = 1D - getKnockbackReduction(getLevel(p)); + double factor = 1D - getKnockbackReduction(level); Vector v = e.getVelocity(); e.setVelocity(new Vector(v.getX() * factor, v.getY(), v.getZ() * factor)); } private boolean isBastionStance(Player p) { - return hasAdaptation(p) && p.isBlocking() && p.isSneaking() && hasShield(p); + return isBastionStance(p, getActiveLevel(p)); + } + + private boolean isBastionStance(Player p, int level) { + return level > 0 && p.isBlocking() && p.isSneaking() && hasShield(p); } private boolean hasShield(Player p) { @@ -154,7 +160,8 @@ private double getProjectileNegateChance(int level) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (hasAdaptation(p) && !isBastionStance(p)) { + int level = getActiveLevel(p); + if (level > 0 && !isBastionStance(p, level)) { setStorage(p, "bastionSessionCount", 0); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java index a0733cd63..b25b97ec3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java @@ -23,13 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -45,14 +44,11 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.inventorygui.Window; - public class BlockingBulwarkBash extends SimpleAdaptation { - private final Map lastSprintMillis = new HashMap<>(); + private final Map lastSprintMillis = new java.util.concurrent.ConcurrentHashMap<>(); public BlockingBulwarkBash() { super("blocking-bulwark-bash"); @@ -105,10 +101,13 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target) || !hasAdaptation(p)) { + var combat = resolveMeleeContext(e); + if (combat == null) { return; } + Player p = combat.attacker(); + LivingEntity target = combat.target(); if (p.getInventory().getItemInOffHand().getType() != Material.SHIELD || p.hasCooldown(Material.SHIELD)) { return; } @@ -117,15 +116,7 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, target.getLocation())) { - return; - } - - int level = getLevel(p); + int level = combat.level(); int affected = 0; double radius = getRange(level); for (org.bukkit.entity.Entity nearby : target.getWorld().getNearbyEntities(target.getLocation(), radius, radius, radius)) { @@ -133,11 +124,7 @@ public void on(EntityDamageByEntityEvent e) { continue; } - if (hit instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - continue; - } - } else if (!canPVE(p, hit.getLocation())) { + if (!canDamageTarget(p, hit)) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java index 0e9516ac5..c77868f83 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -98,10 +97,7 @@ public BlockingChainArmorer() { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getWhoClicked() instanceof Player p && hasAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { getPlayer(p).getData().addStat("blocking.chain-armorer.pieces-crafted", 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java index 51c90cab1..2c2da58f0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java @@ -23,13 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.format.Localizer; import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Entity; @@ -41,8 +40,6 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.ItemStack; -import art.arcane.adapt.util.reflect.Reflect; - public class BlockingCounterGuard extends SimpleAdaptation { public BlockingCounterGuard() { super("blocking-counter-guard"); @@ -83,15 +80,15 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player defender)) { + if (!(e.getEntity() instanceof Player defender)) { return; } - if (!hasAdaptation(defender) || !hasShield(defender)) { + if (!hasActiveAdaptation(defender) || !hasShield(defender)) { return; } - int level = getLevel(defender); + int level = getActiveLevel(defender); int stacks = getStorageInt(defender, "counterStacks", 0); if (defender.isBlocking()) { @@ -112,11 +109,7 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (attacker instanceof Player p && !canPVP(defender, p.getLocation())) { - return; - } - - if (!(attacker instanceof Player) && !canPVE(defender, attacker.getLocation())) { + if (!canDamageTarget(defender, attacker)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java index 749c34477..b9848c844 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -104,10 +103,7 @@ public BlockingHorseArmorer() { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getWhoClicked() instanceof Player p && hasAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { getPlayer(p).getData().addStat("blocking.horse-armorer.armor-crafted", 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java index 37caadfef..87be1143b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java @@ -24,14 +24,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -48,8 +47,6 @@ import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.reflect.Reflect; - public class BlockingMirrorBlock extends SimpleAdaptation { private static final String REFLECTED_META = "adapt-mirror-reflected"; private static final String DAMAGE_FACTOR_META = "adapt-mirror-damage-factor"; @@ -93,7 +90,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Projectile projectile)) { + if (!(e.getDamager() instanceof Projectile projectile)) { return; } @@ -103,7 +100,7 @@ public void on(EntityDamageByEntityEvent e) { return; } - int level = getLevel(defender); + int level = getActiveLevel(defender); long now = System.currentTimeMillis(); long next = getStorageLong(defender, "mirrorBlockNext", 0L); if (next > now) { @@ -148,11 +145,7 @@ private void applyReflectedDamageModifier(EntityDamageByEntityEvent e, Projectil return; } - if (e.getEntity() instanceof Player victim) { - if (!canPVP(shooter, victim.getLocation())) { - return; - } - } else if (!canPVE(shooter, e.getEntity().getLocation())) { + if (!canDamageTarget(shooter, e.getEntity())) { return; } @@ -175,7 +168,7 @@ private void reflectProjectile(Player defender, Projectile projectile, int level } private boolean isMirrorReady(Player p) { - return hasAdaptation(p) && p.isBlocking() && hasShield(p); + return hasActiveAdaptation(p) && p.isBlocking() && hasShield(p); } private boolean hasShield(Player p) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java index 0d5a943ce..4dd7547f4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java @@ -22,11 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.content.item.multiItems.MultiArmor; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -44,20 +46,13 @@ import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; -import java.util.HashMap; import java.util.List; import java.util.Map; - - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; +import java.util.UUID; public class BlockingMultiArmor extends SimpleAdaptation { private static final MultiArmor multiarmor = new MultiArmor(); - private final Map cooldowns; + private final Map cooldowns; public BlockingMultiArmor() { @@ -71,7 +66,7 @@ public BlockingMultiArmor() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.ELYTRA) .key("challenge_blocking_multi_200") @@ -113,17 +108,14 @@ public void onTick() { @EventHandler(priority = EventPriority.HIGH) public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); ItemStack chest = p.getInventory().getChestplate(); - if (chest != null && hasAdaptation(p) && validateArmor(chest)) { - Long cooldown = cooldowns.get(p); + if (chest != null && hasActiveAdaptation(p) && validateArmor(chest)) { + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null) { if (cooldown + 3000 > System.currentTimeMillis()) return; - else cooldowns.remove(p); + else cooldowns.remove(p.getUniqueId()); } SoundPlayer spw = SoundPlayer.of(p.getWorld()); @@ -132,7 +124,7 @@ public void on(PlayerMoveEvent e) { return; } J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextChestplate(chest))); - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); spw.play(p.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 0.5f, 0.77f); getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); @@ -142,7 +134,7 @@ public void on(PlayerMoveEvent e) { return; } J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextElytra(chest))); - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); spw.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.5f, 0.77f); getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); @@ -155,7 +147,7 @@ public void on(PlayerMoveEvent e) { public void on(PlayerDropItemEvent e) { Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (p.isSneaking()) { @@ -198,7 +190,9 @@ public void on(PlayerDropItemEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(InventoryClickEvent e) { - if (!hasAdaptation((Player) e.getWhoClicked())) { + Player player = (Player) e.getWhoClicked(); + int level = getActiveLevel(player); + if (level <= 0) { return; } if (e.getClickedInventory() != null @@ -212,9 +206,9 @@ public void on(InventoryClickEvent e) { if (multiarmor.explode(cursor).size() > 1 || multiarmor.explode(clicked).size() > 1) { - if (multiarmor.explode(cursor).size() >= getSlots(getLevel((Player) e.getWhoClicked())) || multiarmor.explode(clicked).size() >= getSlots(getLevel((Player) e.getWhoClicked()))) { + if (multiarmor.explode(cursor).size() >= getSlots(level) || multiarmor.explode(clicked).size() >= getSlots(level)) { e.setCancelled(true); - SoundPlayer sp = SoundPlayer.of((Player) e.getWhoClicked()); + SoundPlayer sp = SoundPlayer.of(player); sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java index e94938da6..e97c8ca18 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -72,10 +71,7 @@ public BlockingSaddlecrafter() { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getWhoClicked() instanceof Player p && hasAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { getPlayer(p).getData().addStat("blocking.saddlecrafter.saddles-crafted", 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java index e768c4353..5f723af55 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java @@ -25,13 +25,12 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -97,9 +96,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.absorption.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java index d1e9b466b..4cbb0136f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java @@ -25,11 +25,10 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.blindness.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java index fce1398c8..e62ed069f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java @@ -25,11 +25,10 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -84,9 +83,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.darkness.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java index cb5960234..9c447f1c5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java @@ -25,11 +25,10 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.decay.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java index 2707c5be3..fd9cd8df3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java @@ -25,13 +25,12 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.fatigue.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java index 6256ae252..68ca6e635 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java @@ -25,14 +25,13 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import art.arcane.adapt.util.reflect.registries.PotionTypes; -import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.haste.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java index 3452d43ea..6704bf49a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java @@ -25,13 +25,12 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.health-boost.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java index c86344192..7fd0dfe54 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java @@ -25,11 +25,10 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.hunger.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java index 255b3811a..f4b5da082 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.brewing; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -25,15 +24,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.data.WorldData; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.content.matter.BrewingStandOwner; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.adapt.util.reflect.registries.ItemFlags; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.function.Function3; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.ItemFlags; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -60,12 +62,6 @@ import java.util.Map; import java.util.function.Function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.function.Function3; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class BrewingLingering extends SimpleAdaptation { private static final Function getColor; private static final Function> getEffectAttributes; @@ -119,9 +115,6 @@ public double getPercentBoost(double factor) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java index f0c81f0c6..60b91d170 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java @@ -25,13 +25,12 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.nausea.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java index febfd7594..21b8bc866 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java @@ -25,11 +25,10 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.resistance.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java index 334a04471..2a3e66faf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java @@ -25,13 +25,12 @@ import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.potion.BrewingRecipe; import art.arcane.adapt.api.potion.PotionBuilder; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -96,9 +95,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { getServer().peekData(owner.getOwner()).addStat("brewing.saturation.potions-brewed", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java index 4a564a444..6cd60da78 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java @@ -17,22 +17,25 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.brewing; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.math.RNG; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.data.WorldData; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.content.matter.BrewingStandOwner; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -50,13 +53,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class BrewingSuperHeated extends SimpleAdaptation { private static final int MAX_CHECKS_BEFORE_REMOVE = 20; @@ -109,9 +105,6 @@ public double getFireBoost(double factor) { @EventHandler(priority = EventPriority.HIGHEST) public void on(InventoryMoveItemEvent e) { - if (e.isCancelled()) { - return; - } if (!e.getDestination().getType().equals(InventoryType.BREWING) || e.getDestination().getLocation() == null) { return; } @@ -121,9 +114,6 @@ public void on(InventoryMoveItemEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BrewEvent e) { - if (e.isCancelled()) { - return; - } if (activeStands.containsKey(e.getBlock())) { BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); if (owner != null) { @@ -137,7 +127,7 @@ public void on(BrewEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(InventoryClickEvent e) { - if (e.getClickedInventory() == null || e.isCancelled()) { + if (e.getClickedInventory() == null) { return; } if (e.getView().getTopInventory().getType().equals(InventoryType.BREWING)) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java index 8255fc539..dc9f149f4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -39,7 +38,6 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -58,8 +56,8 @@ public ChronosAberrantTouch() { setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); setInterval(1000); - cooldowns = new HashMap<>(); - targetStacks = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + targetStacks = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CLOCK) .key("challenge_chronos_aberrant_500") @@ -101,11 +99,7 @@ private int getDurationAddedTicks(int level) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player attacker) || !hasAdaptation(attacker)) { - return; - } - - if (!(e.getEntity() instanceof LivingEntity target)) { + if (!(e.getDamager() instanceof Player attacker)) { return; } @@ -115,21 +109,18 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (target instanceof Player playerTarget) { - if (!canPVP(attacker, playerTarget.getLocation())) { - return; - } - } else { - if (!canPVE(attacker, target.getLocation())) { - return; - } + var combat = resolveMeleeContext(e); + if (combat == null) { + return; } + attacker = combat.attacker(); + LivingEntity target = combat.target(); if (!getPlayer(attacker).consumeFood(getConfig().hungerCost, getConfig().minimumFoodLevel)) { return; } - int level = getLevel(attacker); + int level = combat.level(); int amplifierCap = target instanceof Player ? getConfig().playerAmplifierCap : getPvEAmplifierCap(level); int durationCap = target instanceof Player ? getConfig().playerDurationCapTicks : getPvEDurationCapTicks(level); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index ef1db7b35..dcfe34f94 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -18,28 +18,21 @@ package art.arcane.adapt.content.adaptation.chronos; -import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.M; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; @@ -48,22 +41,15 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.event.player.PlayerToggleSneakEvent; -import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.reflect.registries.Particles; - import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; @@ -74,7 +60,7 @@ public class ChronosInstantRecall extends SimpleAdaptation TELEPORT_XP_SUPPRESS_UNTIL = new HashMap<>(); + private static final Map TELEPORT_XP_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); private final Map> snapshots; private final Map lastSnapshot; @@ -97,15 +83,15 @@ public ChronosInstantRecall() { setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); setInterval(50); - snapshots = new HashMap<>(); - lastSnapshot = new HashMap<>(); - cooldowns = new HashMap<>(); - cooldownReadyNotify = new HashSet<>(); - rewindProtection = new HashMap<>(); - rewinding = new HashSet<>(); - recallXpStamps = new HashMap<>(); - jumpArmUntil = new HashMap<>(); - lastOnGround = new HashMap<>(); + snapshots = new ConcurrentHashMap<>(); + lastSnapshot = new ConcurrentHashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + cooldownReadyNotify = ConcurrentHashMap.newKeySet(); + rewindProtection = new ConcurrentHashMap<>(); + rewinding = ConcurrentHashMap.newKeySet(); + recallXpStamps = new ConcurrentHashMap<>(); + jumpArmUntil = new ConcurrentHashMap<>(); + lastOnGround = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CLOCK) .key("challenge_chronos_recall_50") @@ -648,7 +634,7 @@ private EquipmentSlot resolveRecallHand(Player p, EquipmentSlot eventHand) { } private boolean isRecallEligible(Player p) { - return hasAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; } private boolean isLeftClick(Action action) { @@ -832,7 +818,7 @@ private void attemptRecall(Player p) { return; } - int level = getLevel(p); + int level = getActiveLevel(p); long rewindMillis = getRewindDurationMillis(level); Snapshot anchor = findSnapshot(p, rewindMillis); if (anchor == null) { @@ -1090,12 +1076,10 @@ public void onDoubleJumpMove(PlayerMoveEvent e) { public void onTick() { long now = M.ms(); - Iterator ready = cooldownReadyNotify.iterator(); - while (ready.hasNext()) { - UUID id = ready.next(); + for (UUID id : new HashSet<>(cooldownReadyNotify)) { Player p = Bukkit.getPlayer(id); if (p == null) { - ready.remove(); + cooldownReadyNotify.remove(id); continue; } @@ -1104,7 +1088,7 @@ public void onTick() { if (getConfig().playClockSounds) { ChronosSoundFX.playCooldownReady(p); } - ready.remove(); + cooldownReadyNotify.remove(id); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java index df98b8da0..d9e2dfd11 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java @@ -20,7 +20,6 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.util.common.scheduling.J; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.World; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index 055b2f5bc..a7a321988 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -23,36 +23,29 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; -import org.bukkit.entity.Arrow; -import org.bukkit.entity.Egg; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.Snowball; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class ChronosTemporalEcho extends SimpleAdaptation { private static final String ECHO_META = "adapt-chronos-temporal-echo"; - private final Map cooldowns = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); public ChronosTemporalEcho() { super("chronos-temporal-echo"); @@ -85,7 +78,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity().getShooter() instanceof Player p) || !hasAdaptation(p) || e.getEntity().hasMetadata(ECHO_META)) { + if (!(e.getEntity().getShooter() instanceof Player p) || !hasActiveAdaptation(p) || e.getEntity().hasMetadata(ECHO_META)) { return; } @@ -94,7 +87,7 @@ public void on(ProjectileLaunchEvent e) { return; } - int level = getLevel(p); + int level = getActiveLevel(p); long now = System.currentTimeMillis(); if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index c152163c1..92a73bf69 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -24,25 +24,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.M; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.ThrownPotion; +import org.bukkit.*; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; @@ -57,12 +48,10 @@ import org.bukkit.util.Vector; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import art.arcane.adapt.util.common.math.Sphere; -import art.arcane.adapt.util.common.misc.Impulse; -import art.arcane.adapt.util.reflect.registries.Particles; - public class ChronosTimeBomb extends SimpleAdaptation { private static final EnumSet SUPPORTED_ACTIONS = EnumSet.of( Action.RIGHT_CLICK_AIR, @@ -99,12 +88,12 @@ public ChronosTimeBomb() { .result(ChronoTimeBombItem.withData()) .build()); - cooldowns = new HashMap<>(); - cooldownReadyNotify = new HashSet<>(); - fields = new ArrayList<>(); - activeBombProjectiles = new HashMap<>(); - frozenEntities = new HashMap<>(); - frozenPlayers = new HashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + cooldownReadyNotify = ConcurrentHashMap.newKeySet(); + fields = new CopyOnWriteArrayList<>(); + activeBombProjectiles = new ConcurrentHashMap<>(); + frozenEntities = new ConcurrentHashMap<>(); + frozenPlayers = new ConcurrentHashMap<>(); syncTickQueued = new AtomicBoolean(false); registerAdvancement(AdaptAdvancement.builder() .icon(Material.ICE) @@ -176,7 +165,7 @@ public void on(PlayerInteractEvent e) { return; } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { e.setCancelled(true); return; } @@ -212,7 +201,8 @@ public void on(ProjectileLaunchEvent e) { return; } - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { e.setCancelled(true); return; } @@ -227,7 +217,6 @@ public void on(ProjectileLaunchEvent e) { return; } - int level = getLevel(p); cooldowns.put(p.getUniqueId(), now + getCooldownMillis()); cooldownReadyNotify.add(p.getUniqueId()); activeBombProjectiles.put(potion.getUniqueId(), new ArmedBombProjectile(p.getUniqueId(), level, now)); @@ -372,7 +361,7 @@ private boolean shouldFreezePlayer(Player player) { } Player owner = Bukkit.getPlayer(field.owner()); - if (owner != null && !canPVP(owner, player.getLocation())) { + if (owner != null && !canDamageTarget(owner, player)) { continue; } @@ -450,14 +439,8 @@ private void applyField(TemporalField field, long now) { } if (owner != null) { - if (living instanceof Player targetPlayer) { - if (!canPVP(owner, targetPlayer.getLocation())) { - continue; - } - } else { - if (!canPVE(owner, living.getLocation())) { - continue; - } + if (!canDamageTarget(owner, living)) { + continue; } } @@ -533,12 +516,10 @@ private void onTickSync() { long now = M.ms(); cleanupBombProjectiles(now); - Iterator ready = cooldownReadyNotify.iterator(); - while (ready.hasNext()) { - UUID id = ready.next(); + for (UUID id : new HashSet<>(cooldownReadyNotify)) { Player p = Bukkit.getPlayer(id); if (p == null) { - ready.remove(); + cooldownReadyNotify.remove(id); continue; } @@ -547,7 +528,7 @@ private void onTickSync() { if (getConfig().playClockSounds) { ChronosSoundFX.playCooldownReady(p); } - ready.remove(); + cooldownReadyNotify.remove(id); } } @@ -558,12 +539,11 @@ private void onTickSync() { applyField(field, now); } - Iterator> frozenPlayerIterator = frozenPlayers.entrySet().iterator(); - while (frozenPlayerIterator.hasNext()) { - Map.Entry entry = frozenPlayerIterator.next(); - Player player = Bukkit.getPlayer(entry.getKey()); + for (Map.Entry entry : new ArrayList<>(frozenPlayers.entrySet())) { + UUID playerId = entry.getKey(); + Player player = Bukkit.getPlayer(playerId); if (player == null || !player.isOnline() || player.isDead()) { - frozenPlayerIterator.remove(); + frozenPlayers.remove(playerId); continue; } @@ -572,16 +552,15 @@ private void onTickSync() { continue; } - unfreezePlayer(entry.getKey(), entry.getValue()); - frozenPlayerIterator.remove(); + unfreezePlayer(playerId, entry.getValue()); + frozenPlayers.remove(playerId); } - Iterator> frozenIterator = frozenEntities.entrySet().iterator(); - while (frozenIterator.hasNext()) { - Map.Entry entry = frozenIterator.next(); - Entity entity = Bukkit.getEntity(entry.getKey()); + for (Map.Entry entry : new ArrayList<>(frozenEntities.entrySet())) { + UUID entityId = entry.getKey(); + Entity entity = Bukkit.getEntity(entityId); if (entity == null || entity.isDead() || !entity.isValid()) { - frozenIterator.remove(); + frozenEntities.remove(entityId); continue; } @@ -590,23 +569,22 @@ private void onTickSync() { continue; } - unfreezeEntity(entry.getKey(), entry.getValue()); - frozenIterator.remove(); + unfreezeEntity(entityId, entry.getValue()); + frozenEntities.remove(entityId); } } private void cleanupBombProjectiles(long now) { - Iterator> iterator = activeBombProjectiles.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - Entity entity = Bukkit.getEntity(entry.getKey()); + for (Map.Entry entry : new ArrayList<>(activeBombProjectiles.entrySet())) { + UUID projectileId = entry.getKey(); + Entity entity = Bukkit.getEntity(projectileId); if (entity == null || !entity.isValid() || entity.isDead()) { - iterator.remove(); + activeBombProjectiles.remove(projectileId); continue; } if (now - entry.getValue().launchedAt() > PROJECTILE_TRACK_TTL_MS) { - iterator.remove(); + activeBombProjectiles.remove(projectileId); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java index 61ebe8628..e573ddf3d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java @@ -23,11 +23,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ChronoTimeBottle; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Keyed; @@ -47,8 +46,8 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.inventory.CraftItemEvent; -import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -57,8 +56,6 @@ import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.reflect.registries.Particles; - public class ChronosTimeInABottle extends SimpleAdaptation { private static final String RECIPE_KEY = "chronos-time-in-a-bottle"; @@ -342,16 +339,15 @@ public void on(PlayerInteractEvent e) { // Chrono bottles are never drinkable; always deny vanilla potion use. e.setUseItemInHand(Event.Result.DENY); - if (!hasAdaptation(p) || action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { return; } Block clicked = e.getClickedBlock(); - if (!canInteract(p, clicked.getLocation())) { + int level = getActiveInteractLevel(p, clicked.getLocation()); + if (level <= 0) { return; } - - int level = getLevel(p); double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); if (storedSeconds <= 0) { return; @@ -385,7 +381,7 @@ public void on(PlayerInteractEntityEvent e) { Player p = e.getPlayer(); ItemStack hand = p.getInventory().getItemInMainHand(); - if (!ChronoTimeBottle.isBindableItem(hand) || !hasAdaptation(p)) { + if (!ChronoTimeBottle.isBindableItem(hand)) { return; } @@ -393,7 +389,8 @@ public void on(PlayerInteractEntityEvent e) { return; } - if (!canInteract(p, e.getRightClicked().getLocation())) { + int level = getActiveInteractLevel(p, e.getRightClicked().getLocation()); + if (level <= 0) { return; } @@ -401,8 +398,6 @@ public void on(PlayerInteractEntityEvent e) { if (currentAge == 0) { return; } - - int level = getLevel(p); double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); if (storedSeconds <= 0) { return; @@ -642,11 +637,11 @@ private enum GrowthProfile { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { continue; } - int level = getLevel(p); double chargePerSecond = getConfig().chargePerSecond + (level * getConfig().chargePerSecondPerLevel); for (ItemStack stack : p.getInventory().getContents()) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java index 3051b20b4..34c541b6a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java @@ -23,8 +23,8 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -81,9 +81,8 @@ public void addStats(int level, Element v) { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) return; Player p = (Player) e.getWhoClicked(); - if (!hasAdaptation(p)) return; + if (!hasActiveAdaptation(p)) return; if (e.getRecipe() != null && e.getRecipe().getResult().getType() == Material.BUNDLE) { getPlayer(p).getData().addStat("crafting.backpacks.bundles-crafted", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java index facbc7d22..d4e02166a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java @@ -21,8 +21,8 @@ import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -40,7 +40,8 @@ import org.bukkit.inventory.meta.Damageable; import org.bukkit.util.RayTraceResult; -import java.util.*; +import java.util.Map; +import java.util.Objects; public class CraftingDeconstruction extends SimpleAdaptation { public CraftingDeconstruction() { @@ -113,7 +114,7 @@ public ItemStack getDeconstructionOffering(ItemStack forStuff) { } } else { ShapedRecipe r = (ShapedRecipe) selectedRecipe; - Map ings = new HashMap<>(); + Map ings = new java.util.concurrent.ConcurrentHashMap<>(); r.getIngredientMap().values().stream().filter(Objects::nonNull).forEach(i -> ings.merge(i.getType(), i.getAmount(), Integer::sum)); for (Map.Entry entry : ings.entrySet()) { @@ -142,7 +143,7 @@ public ItemStack getDeconstructionOffering(ItemStack forStuff) { public void on(PlayerInteractEvent e) { Player player = e.getPlayer(); ItemStack mainHandItem = player.getInventory().getItemInMainHand(); - if (!hasAdaptation(player)) { + if (!hasActiveAdaptation(player)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java index b18512060..52031c1df 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java @@ -22,8 +22,8 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -68,7 +68,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerInteractEvent e) { if (e.getItem() != null && e.getItem().getType() == Material.ROTTEN_FLESH && e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.CAMPFIRE) { - if (!hasAdaptation(e.getPlayer())) { + if (getActiveLevel(e.getPlayer()) <= 0) { e.setCancelled(true); } else { getPlayer(e.getPlayer()).getData().addStat("crafting.leather.leather-crafted", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java index ec7cd1ba3..8749fdf1e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -326,9 +325,8 @@ public void on(PlayerInteractEvent e) { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) return; Player p = (Player) e.getWhoClicked(); - if (!hasAdaptation(p)) return; + if (!hasActiveAdaptation(p)) return; if (e.getRecipe() != null && (e.getRecipe().getResult().getType().name().contains("ORE") || e.getRecipe().getResult().getType() == Material.ANCIENT_DEBRIS)) { getPlayer(p).getData().addStat("crafting.reconstruction.ores-reconstructed", 1); } @@ -365,4 +363,4 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 1; } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java index d321c3976..c4111eec2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java @@ -23,8 +23,8 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -128,9 +128,8 @@ public void addStats(int level, Element v) { @EventHandler public void on(CraftItemEvent e) { - if (e.isCancelled()) return; Player p = (Player) e.getWhoClicked(); - if (!hasAdaptation(p)) return; + if (!hasActiveAdaptation(p)) return; if (e.getRecipe() != null) { Material result = e.getRecipe().getResult().getType(); if (result == Material.SKELETON_SKULL || result == Material.WITHER_SKELETON_SKULL diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java index 684d30553..070fff118 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java @@ -21,8 +21,8 @@ import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -76,7 +76,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerInteractEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java index c54ad7bd0..c7a68bf71 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java @@ -21,8 +21,8 @@ import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -32,12 +32,12 @@ import org.bukkit.event.inventory.CraftItemEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class CraftingXP extends SimpleAdaptation { - private final Map cooldown = new HashMap<>(); + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); public CraftingXP() { @@ -76,26 +76,23 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - cooldown.remove(p); + cooldown.remove(p.getUniqueId()); } @EventHandler(priority = EventPriority.LOW) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = (Player) e.getWhoClicked(); - if (e.getInventory().getResult() != null && !e.isCancelled() && hasAdaptation(p) && e.getInventory().getResult().getAmount() > 0) { + if (e.getInventory().getResult() != null && hasActiveAdaptation(p) && e.getInventory().getResult().getAmount() > 0) { if (e.getInventory().getResult() != null && e.getCursor() != null && e.getCursor().getAmount() < 64) { if (p.getInventory().addItem(e.getCurrentItem()).isEmpty()) { p.getInventory().removeItem(e.getCurrentItem()); - if (cooldown.containsKey(p) && cooldown.get(p) + 20000 < System.currentTimeMillis()) { - cooldown.remove(p); - } else if (cooldown.containsKey(p) && cooldown.get(p) + 20000 > System.currentTimeMillis()) { + if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { + cooldown.remove(p.getUniqueId()); + } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { return; } - cooldown.put(p, System.currentTimeMillis()); + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(getLevel(p) * 2); getPlayer(p).getData().addStat("crafting.xp.items-crafted", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java index 85e60fb9a..bef482852 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java @@ -23,23 +23,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; @@ -48,7 +43,6 @@ import org.bukkit.plugin.EventExecutor; import java.lang.reflect.Method; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; @@ -58,8 +52,8 @@ public class DiscoveryArchaeologist extends SimpleAdaptation { private static final String BLOCK_BRUSH_EVENT_CLASS = "org.bukkit.event.block.BlockBrushEvent"; private static final long BRUSH_FALLBACK_WINDOW_MILLIS = 25000L; - private final Map cooldowns = new HashMap<>(); - private final Map pendingBrushes = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map pendingBrushes = new java.util.concurrent.ConcurrentHashMap<>(); private final AtomicBoolean brushEventFailureWarned = new AtomicBoolean(false); private final BrushEventBridge brushEventBridge; @@ -127,7 +121,7 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !canBlockBreak(p, block.getLocation())) { + if (getActiveBlockBreakLevel(p, block.getLocation()) <= 0) { return; } @@ -173,7 +167,8 @@ private void handleBrush(Player p, Block block, Material originalType, Material return; } - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -190,7 +185,6 @@ private void handleBrush(Player p, Block block, Material originalType, Material return; } - int level = getLevel(p); long now = System.currentTimeMillis(); long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); if (now < nextReady) { @@ -332,7 +326,12 @@ public void onTick() { } Player p = Bukkit.getPlayer(entry.getKey()); - if (p == null || !p.isOnline() || !hasAdaptation(p)) { + if (p == null || !p.isOnline()) { + iterator.remove(); + continue; + } + + if (!hasActiveAdaptation(p)) { iterator.remove(); continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java index 393ffff99..5b654b367 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java @@ -24,15 +24,17 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.IAttribute; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.math.Sphere; +import art.arcane.adapt.util.common.math.VectorMath; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.adapt.util.reflect.registries.Particles; -import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -46,12 +48,6 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.math.Sphere; -import art.arcane.adapt.util.common.math.VectorMath; - public class DiscoveryArmor extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-discovery-armor".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:discovery-armor"); @@ -156,7 +152,7 @@ public void onTick() { var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); if (attribute == null) continue; - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { attribute.removeModifier(MODIFIER, MODIFIER_KEY); } else { double oldArmor = attribute.getModifier(MODIFIER, MODIFIER_KEY) diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java index d35cf1373..eab5a542c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -83,7 +82,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled() || e.getHand() != EquipmentSlot.HAND) { + if (e.getHand() != EquipmentSlot.HAND) { return; } @@ -93,7 +92,8 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { return; } @@ -107,7 +107,6 @@ public void on(PlayerInteractEvent e) { return; } - int level = getLevel(p); int availableXp = getTotalExp(p); if (availableXp <= 0) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java index cbf2cf5a4..aee390f56 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,12 +43,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class DiscoveryCartographerPulse extends SimpleAdaptation { - private final Map cooldowns = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); public DiscoveryCartographerPulse() { super("discovery-cartographer-pulse"); @@ -100,11 +98,11 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || p.getInventory().getItemInMainHand().getType() != Material.COMPASS) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.COMPASS) { return; } - int level = getLevel(p); long now = System.currentTimeMillis(); if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java index b616acfc7..f9ff0c985 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.discovery; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -25,11 +24,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerSkillLine; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -40,11 +41,6 @@ import java.util.List; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public class DiscoveryUnity extends SimpleAdaptation { @@ -90,7 +86,7 @@ public void on(PlayerExpChangeEvent e) { Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); AdaptPlayer ap = getPlayer(p); - if (hasAdaptation(p) && e.getAmount() > 0) { + if (hasActiveAdaptation(p) && e.getAmount() > 0) { xp(p, 5); sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.9f); //get a random skill that they have unlocked already diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java index 1d488ffc8..e8c476c53 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java @@ -17,19 +17,22 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.discovery; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.ReceiveCancelledEvents; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; -import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -48,12 +51,6 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class DiscoveryVillagerAtt extends SimpleAdaptation { private final KMap active = new KMap<>(); @@ -107,21 +104,18 @@ private int getXpTaken(double level) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerInteractEntityEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (e.getRightClicked() instanceof Villager v && hasAdaptation(p)) { - if (ThreadLocalRandom.current().nextDouble() <= getEffectiveness(getLevelPercent(getLevel(p)))) { - if (p.getLevel() - getXpTaken(getLevel(p)) > 0) { + int level = getActiveLevel(p); + if (e.getRightClicked() instanceof Villager v && level > 0) { + if (ThreadLocalRandom.current().nextDouble() <= getEffectiveness(getLevelPercent(level))) { + if (p.getLevel() - getXpTaken(level) > 0) { BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood blood.material = Material.EMERALD; blood.setEntity(v); - p.setLevel((p.getLevel() - getXpTaken(getLevel(p)))); + p.setLevel((p.getLevel() - getXpTaken(level))); sp.play(p.getLocation(), Sound.ENTITY_VILLAGER_CELEBRATE, 1f, 1f); sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); - int level = getLevel(p); active.put(p.getUniqueId(), level); p.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, level, true, true)); getPlayer(p).getData().addStat("discovery.villager-att.improved-trades", 1); @@ -137,6 +131,7 @@ public void on(PlayerInteractEntityEvent e) { } @EventHandler(priority = EventPriority.MONITOR) + @ReceiveCancelledEvents public void on(InventoryOpenEvent event) { if (!(event.getPlayer() instanceof Player p)) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java index 01f57520c..1f291bec0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java @@ -17,17 +17,19 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.discovery; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -37,15 +39,9 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageEvent; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class DiscoveryXpResist extends SimpleAdaptation { private static final long COOLDOWN_MILLIS = 15000L; private final Map cooldowns; @@ -61,7 +57,7 @@ public DiscoveryXpResist() { setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); setMaxLevel(getConfig().maxLevel); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.TOTEM_OF_UNDYING) .key("challenge_discovery_xp_resist_25") @@ -109,7 +105,12 @@ private int getXpTaken(double level) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -118,7 +119,6 @@ public void on(EntityDamageEvent e) { } SoundPlayer sp = SoundPlayer.of(p); - int level = getLevel(p); int xpCost = getXpTaken(level); if (p.getLevel() < xpCost) { vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.RED); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java index fe7bd9ff2..581caa312 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -75,7 +74,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PrepareAnvilEvent e) { - if (!(e.getView().getPlayer() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getView().getPlayer() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -88,7 +92,7 @@ public void on(PrepareAnvilEvent e) { return; } - int reduced = Math.max(getConfig().minimumCost, (int) Math.ceil(current * (1D - getCostReduction(getLevel(p))))); + int reduced = Math.max(getConfig().minimumCost, (int) Math.ceil(current * (1D - getCostReduction(level)))); writeRepairCost(inventory, reduced); int saved = current - reduced; if (saved > 0) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java index f35b51c1f..0a81cb25b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java @@ -22,10 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -66,7 +65,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PrepareItemEnchantEvent e) { Player p = e.getEnchanter(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java index 708ead9c5..006915497 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -87,7 +86,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(InventoryClickEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -100,7 +104,6 @@ public void on(InventoryClickEvent e) { return; } - int level = getLevel(p); if (ThreadLocalRandom.current().nextDouble() > getRecoverChance(level)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java index 9e5b7cfc3..4f56c19df 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java @@ -22,10 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -36,13 +35,12 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class EnchantingLapisReturn extends SimpleAdaptation { - private final Map cooldown = new HashMap<>(); + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); public EnchantingLapisReturn() { super("enchanting-lapis-return"); @@ -88,12 +86,10 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGH) public void on(EnchantItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getEnchanter(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -107,8 +103,8 @@ public void on(EnchantItemEvent e) { } cooldown.put(playerId, now + 20000L); - p.getWorld().dropItemNaturally(p.getLocation(), new ItemStack(Material.LAPIS_LAZULI, getLevel(p))); - getPlayer(p).getData().addStat("enchanting.lapis-return.lapis-saved", getLevel(p)); + p.getWorld().dropItemNaturally(p.getLocation(), new ItemStack(Material.LAPIS_LAZULI, level)); + getPlayer(p).getData().addStat("enchanting.lapis-return.lapis-saved", level); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java index a89028836..c388c167f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -82,16 +81,16 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled() || e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { return; } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.ENCHANTING_TABLE) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || e.getClickedBlock().getType() != Material.ENCHANTING_TABLE) { return; } - int level = getLevel(p); int lapisCost = getLapisCost(level); if (p.getFoodLevel() < 0) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java index d8887f983..4627dfd33 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java @@ -23,13 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KMap; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KMap; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -88,12 +87,20 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(InventoryClickEvent e) { - if (e.getClickedInventory() == null || e.isCancelled()) { + if (e.getClickedInventory() == null) { return; } - if (e.getWhoClicked() instanceof Player p - && hasAdaptation(p) - && e.getAction().equals(InventoryAction.SWAP_WITH_CURSOR) + + if (!(e.getWhoClicked() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (e.getAction().equals(InventoryAction.SWAP_WITH_CURSOR) && e.getClick().equals(ClickType.LEFT) && (e.getSlotType().equals(InventoryType.SlotType.CONTAINER) || e.getSlotType().equals(InventoryType.SlotType.ARMOR) @@ -134,8 +141,8 @@ && hasAdaptation(p) } SoundPlayer sp = SoundPlayer.of(p); - if (power > getTotalLevelCount(getLevel(p))) { - Adapt.actionbar(p, C.RED + Localizer.dLocalize("enchanting.quick_enchant.lore2") + getTotalLevelCount(getLevel(p)) + " " + Localizer.dLocalize("enchanting.quick_enchant.lore3")); + if (power > getTotalLevelCount(level)) { + Adapt.actionbar(p, C.RED + Localizer.dLocalize("enchanting.quick_enchant.lore2") + getTotalLevelCount(level) + " " + Localizer.dLocalize("enchanting.quick_enchant.lore3")); sp.play(p.getLocation(), Sound.BLOCK_CONDUIT_DEACTIVATE, 0.5f, 1.7f); return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java index a3488a05b..0fe511a96 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java @@ -22,10 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -35,11 +34,11 @@ import org.bukkit.event.enchantment.EnchantItemEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class EnchantingXPReturn extends SimpleAdaptation { - private final Map cooldown = new HashMap<>(); + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); public EnchantingXPReturn() { super("enchanting-xp-return"); @@ -72,27 +71,24 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - cooldown.remove(p); + cooldown.remove(p.getUniqueId()); } @EventHandler(priority = EventPriority.HIGHEST) public void on(EnchantItemEvent e) { - if (e.isCancelled()) { - return; - } - int level = getLevel(e.getEnchanter()); Player p = e.getEnchanter(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { return; } - if (cooldown.containsKey(p) && cooldown.get(p) + 20000 < System.currentTimeMillis()) { - cooldown.remove(p); - } else if (cooldown.containsKey(p) && cooldown.get(p) + 20000 > System.currentTimeMillis()) { + if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { + cooldown.remove(p.getUniqueId()); + } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { return; } - cooldown.put(p, System.currentTimeMillis()); + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); int xpAmount = getConfig().xpReturn * (level * level); p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(xpAmount); getPlayer(p).getData().addStat("enchanting.xp-return.levels-saved", xpAmount); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java index 60ad0dd39..87ad3f3cf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java @@ -22,16 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; import lombok.NoArgsConstructor; -import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Item; @@ -77,21 +75,9 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { - return; - } - if (p.getGameMode() != GameMode.SURVIVAL) { - return; - } - if (!canInteract(p, e.getBlock().getLocation())) { - return; - } - if (!canBlockBreak(p, e.getBlock().getLocation())) { + if (resolveInteractBreakContext(p, e.getBlock().getLocation(), null, true) == null) { return; } if (ItemListings.toolShovels.contains(p.getInventory().getItemInMainHand().getType())) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java index feeb5eea5..5bb098950 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -76,17 +75,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDamageEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { - return; - } - if (!canInteract(p, e.getBlock().getLocation())) { + var context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { return; } - p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 15, getLevel(p), false, false, true)); + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 15, context.level(), false, false, true)); getPlayer(p).getData().addStat("excavation.haste.blocks-while-hasted", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index 1db8f2d7f..205b9cf96 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -23,11 +23,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.content.item.multiItems.OmniTool; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -53,13 +55,6 @@ import java.util.List; import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class ExcavationOmniTool extends SimpleAdaptation { private static final OmniTool omniTool = new OmniTool(); @@ -124,20 +119,10 @@ public void on(EntityDamageByEntityEvent e) { e.setCancelled(true); return; } - - if (e.isCancelled()) { - return; - } - if (!hasAdaptation(p) && validateTool(p.getInventory().getItemInMainHand())) { + if (!hasActiveAdaptation(p) && validateTool(p.getInventory().getItemInMainHand())) { e.setCancelled(true); return; } - if (!hasAdaptation(p)) { - if (validateTool(p.getInventory().getItemInMainHand())) { - e.setCancelled(true); - } - return; - } ItemStack hand = p.getInventory().getItemInMainHand(); Damageable inHand = (Damageable) hand.getItemMeta(); @@ -170,7 +155,7 @@ public void on(BlockBreakEvent e) { //deny if they dont have the adaptation - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { e.setCancelled(true); return; } @@ -187,7 +172,7 @@ public void on(PlayerInteractEvent e) { return; } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { @@ -230,7 +215,7 @@ public void on(PlayerInteractEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerDropItemEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (p.isSneaking()) { @@ -278,10 +263,7 @@ public void on(BlockDamageEvent e) { ItemStack hand = p.getInventory().getItemInMainHand(); if (validateTool(hand)) { - if (e.isCancelled()) { - return; - } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -323,7 +305,9 @@ public void on(BlockDamageEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(InventoryClickEvent e) { - if (!hasAdaptation((Player) e.getWhoClicked())) { + Player player = (Player) e.getWhoClicked(); + int level = getActiveLevel(player); + if (level <= 0) { return; } if (e.getClickedInventory() != null && e.getClick().equals(ClickType.SHIFT_LEFT) && e.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { @@ -331,9 +315,9 @@ public void on(InventoryClickEvent e) { ItemStack clicked = e.getClickedInventory().getItem(e.getSlot()).clone(); if (omniTool.explode(cursor).size() > 1 || omniTool.explode(clicked).size() > 1) { - if (omniTool.explode(cursor).size() >= getSlots(getLevel((Player) e.getWhoClicked())) || omniTool.explode(clicked).size() >= getSlots(getLevel((Player) e.getWhoClicked()))) { + if (omniTool.explode(cursor).size() >= getSlots(level) || omniTool.explode(clicked).size() >= getSlots(level)) { e.setCancelled(true); - SoundPlayer sp = SoundPlayer.of((Player) e.getWhoClicked()); + SoundPlayer sp = SoundPlayer.of(player); sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java index d6d87bc98..507e90433 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java @@ -22,20 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Color; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -45,13 +39,12 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class ExcavationSeismicPing extends SimpleAdaptation { - private final Map cooldowns = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); public ExcavationSeismicPing() { super("excavation-seismic-ping"); @@ -90,15 +83,16 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockBreakEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p) || !isExcavationTool(p.getInventory().getItemInMainHand())) { + if (!isExcavationTool(p.getInventory().getItemInMainHand())) { return; } - if (!canBlockBreak(p, e.getBlock().getLocation())) { + var context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { return; } - int level = getLevel(p); + int level = context.level(); long now = System.currentTimeMillis(); long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); if (now < nextReady) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index c075edc08..0da602162 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -23,13 +23,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import fr.skytasul.glowingentities.GlowingEntities; -import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -45,17 +47,11 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; +import java.util.UUID; public class ExcavationSpelunker extends SimpleAdaptation { - private final Map cooldowns; + private final Map cooldowns; public ExcavationSpelunker() { super("excavation-spelunker"); @@ -68,7 +64,7 @@ public ExcavationSpelunker() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.SPYGLASS) .key("challenge_excavation_spelunker_1k") @@ -100,18 +96,19 @@ public void addStats(int level, Element v) { public void on(PlayerToggleSneakEvent e) { Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); + int level = getActiveLevel(p, Player::isSneaking); // Check if player is sneaking, has Glowberries in main hand, and an ore in offhand - if (p.isSneaking() && hasGlowberries(p) && hasOreInOffhand(p) && hasAdaptation(p)) { + if (level > 0 && hasGlowberries(p) && hasOreInOffhand(p)) { // Check if player is on cooldown - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown > System.currentTimeMillis()) { sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); return; } - int radius = getConfig().rangeMultiplier * getLevel(p); + int radius = getConfig().rangeMultiplier * level; consumeGlowberry(p); searchForOres(p, radius); - cooldowns.put(p, (long) (System.currentTimeMillis() + (1000 * getConfig().cooldown))); + cooldowns.put(p.getUniqueId(), (long) (System.currentTimeMillis() + (1000 * getConfig().cooldown))); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java index acdbd8c3d..ca1e26761 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java @@ -22,15 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; @@ -42,15 +40,12 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.reflect.registries.Particles; - public class HerbalismBeeShepherd extends SimpleAdaptation { - private final Map lastPulse = new HashMap<>(); + private final Map lastPulse = new java.util.concurrent.ConcurrentHashMap<>(); public HerbalismBeeShepherd() { super("herbalism-bee-shepherd"); @@ -86,11 +81,11 @@ public void onTick() { long now = System.currentTimeMillis(); for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p) || !isHoldingFlower(p)) { + if (!hasActiveAdaptation(p) || !isHoldingFlower(p)) { continue; } - int level = getLevel(p); + int level = getActiveLevel(p); if (now - lastPulse.getOrDefault(p.getUniqueId(), 0L) < getPulseMillis(level)) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index 6c8e6edd7..8a7ed7016 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -51,8 +50,6 @@ import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.inventorygui.Items; - public class HerbalismCompostCascade extends SimpleAdaptation { public HerbalismCompostCascade() { super("herbalism-compost-cascade"); @@ -95,12 +92,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled() || e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { return; } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.COMPOSTER || p.hasCooldown(Material.COMPOSTER)) { + if (!hasActiveAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.COMPOSTER || p.hasCooldown(Material.COMPOSTER)) { return; } @@ -113,7 +110,7 @@ public void on(PlayerInteractEvent e) { return; } - int level = getLevel(p); + int level = getActiveLevel(p); double fillChance = getFillChance(level); int maxItems = getMaxItems(level); double radius = getRadius(level); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java index c6e6d4a2e..119d2cc84 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -80,10 +79,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { return; } if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-cobwebBlock")) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java index 7feb2e246..cce5532cf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -97,10 +96,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { return; } if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && (recipe.getKey().getKey().equals("herbalism-redmushblock") || recipe.getKey().getKey().equals("herbalism-brownmushblock"))) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java index d0fe06a9b..a85181d3a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java @@ -22,14 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -76,12 +75,9 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (p.getGameMode() != GameMode.SURVIVAL) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java index e0bdcd41b..0fe5525e9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java @@ -17,38 +17,33 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.herbalism; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.math.RNG; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.block.data.Ageable; import org.bukkit.entity.Player; import org.bukkit.util.Vector; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class HerbalismGrowthAura extends SimpleAdaptation { public HerbalismGrowthAura() { super("herbalism-growth-aura"); @@ -106,7 +101,7 @@ public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); try { - if (hasAdaptation(p)) { + if (hasActiveAdaptation(p)) { double rad = getRadius(getLevelPercent(p)); double strength = getStrength(getLevel(p)); ThreadLocalRandom random = ThreadLocalRandom.current(); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java index 864d2d8f5..3186e2e0e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java @@ -22,11 +22,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -69,12 +68,9 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.NORMAL) public void on(PlayerItemConsumeEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (ItemListings.getFood().contains(e.getItem().getType())) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java index 3699050e9..e05dbd155 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -86,10 +85,7 @@ private double getEffectiveness(double factor) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof Player p && hasAdaptation(p)) { + if (e.getEntity() instanceof Player p && hasActiveAdaptation(p)) { double f = getEffectiveness(getLevelPercent(p)); double h = e.getDamage() * f; double d = e.getDamage() - h; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java index 296ce0cfe..e493eefcd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Materials; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -86,11 +85,8 @@ private double getEffectiveness(double factor) { @EventHandler(priority = EventPriority.NORMAL) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java index 8195a7a44..1d4e41079 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -75,10 +74,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { return; } if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-myconid")) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index b9f551c86..59e20b878 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -22,15 +22,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.content.skill.SkillHerbalism; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.data.Cuboid; -import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.registries.Particles; -import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -45,12 +48,6 @@ import java.util.Collection; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class HerbalismReplant extends SimpleAdaptation { public HerbalismReplant() { @@ -157,7 +154,7 @@ public void on(PlayerInteractEvent e) { } private void hit(Player p, Block b) { - if (b != null && b.getBlockData() instanceof Ageable aa && hasAdaptation(p)) { + if (b != null && b.getBlockData() instanceof Ageable aa && hasActiveAdaptation(p)) { if (aa.getAge() != aa.getMaximumAge()) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java index ee3b50ef4..c6693c31c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -71,15 +70,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled()) { - return; - } if (e.getAction() != Action.PHYSICAL || e.getClickedBlock() == null || !(e.getPlayer() instanceof Player p)) { return; } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -91,15 +87,16 @@ public void on(PlayerInteractEvent e) { @EventHandler(priority = EventPriority.HIGH) public void on(EntityDamageEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { + if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { return; } - if (!hasAdaptation(p) || !isNatureGround(p)) { + int level = getActiveLevel(p); + if (level <= 0 || !isNatureGround(p)) { return; } - double absorbCap = e.getDamage() * getFallAbsorb(getLevel(p)); + double absorbCap = e.getDamage() * getFallAbsorb(level); int foodRequired = (int) Math.ceil(absorbCap * getConfig().foodPerDamage); if (foodRequired <= 0 || p.getFoodLevel() <= 0) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java index c6fee6593..3437f94e9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -84,12 +83,9 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + if (!hasActiveAdaptation(p) || !p.isSneaking()) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index e5f13d7f2..1eb139880 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -18,19 +18,17 @@ package art.arcane.adapt.content.adaptation.herbalism; -import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -42,18 +40,11 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; public class HerbalismSporeBloom extends SimpleAdaptation { - private final Map cooldowns = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); public HerbalismSporeBloom() { super("herbalism-spore-bloom"); @@ -90,7 +81,7 @@ public void on(BlockPlaceEvent e) { return; } - if (!hasAdaptation(e.getPlayer()) || !e.getPlayer().isSneaking()) { + if (getActiveLevel(e.getPlayer()) <= 0 || !e.getPlayer().isSneaking()) { return; } @@ -321,7 +312,7 @@ private boolean attemptBloom(org.bukkit.entity.Player player, Block center, Mate return false; } - int level = getLevel(player); + int level = getActiveLevel(player); long now = System.currentTimeMillis(); long ready = cooldowns.getOrDefault(player.getUniqueId(), 0L); if (now < ready) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java index a6cc6e0b5..392bcf7a2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -80,10 +79,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } - if (!(e.getWhoClicked() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { return; } if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-terralid")) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java index 69939e7f7..7db81cb90 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -77,21 +76,22 @@ private double getDamage(int level) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var attack = resolveAttackContext(e); + if (attack == null) { return; } - if (e.getDamager() instanceof Player p && hasAdaptation(p) && getLevel((Player) e.getDamager()) > 0) { - double damageMax = getDamage(getLevel(p)); - double hpp = ((Player) e.getDamager()).getHealth() / ((Player) e.getDamager()).getMaxHealth(); - if (hpp >= 1) { - return; - } + Player p = attack.attacker(); + double damageMax = getDamage(attack.level()); + double hpp = p.getHealth() / p.getMaxHealth(); - damageMax *= (1D - hpp); - e.setDamage(e.getDamage() * (damageMax + 1D)); - getPlayer(p).getData().addStat("hunter.adrenaline.low-health-kills", 1); + if (hpp >= 1) { + return; } + + damageMax *= (1D - hpp); + e.setDamage(e.getDamage() * (damageMax + 1D)); + getPlayer(p).getData().addStat("hunter.adrenaline.low-health-kills", 1); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java index e5920179e..e825947a7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java @@ -22,16 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; import lombok.NoArgsConstructor; -import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.EntityType; @@ -79,23 +77,14 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } + Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { - return; - } - if (p.getGameMode() != GameMode.SURVIVAL) { - return; - } - if (!canInteract(p, e.getBlock().getLocation())) { - return; - } - if (!canPVP(p, e.getBlock().getLocation())) { + if (resolveInteractContext(p, e.getBlock().getLocation(), null, true) == null + || !canPVP(p, e.getBlock().getLocation())) { return; } + + SoundPlayer sp = SoundPlayer.of(p); if (ItemListings.toolSwords.contains(p.getInventory().getItemInMainHand().getType())) { List items = new KList<>(e.getItems()); e.getItems().clear(); @@ -116,10 +105,7 @@ public void on(EntityDeathEvent e) { return; } Player p = k.getKiller(); - if (!hasAdaptation(p)) { - return; - } - if (e.getEntity() instanceof Player) { + if (e.getEntity() instanceof Player || getActiveDamageLevel(p, e.getEntity()) <= 0) { return; } if (e.getEntity().getKiller() != null && e.getEntity().getKiller().getClass().getSimpleName().equals("CraftPlayer")) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java index 401e8b2aa..404fbc4b9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -73,10 +72,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java index 40135f4e4..94449efbf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java @@ -23,12 +23,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -73,10 +72,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java index 53997682d..123b19c79 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -72,10 +71,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java index f036be153..ffa446efa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -72,10 +71,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java index e107b7310..2cea2edc6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java @@ -23,12 +23,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -73,10 +72,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java index 7ce22d3b1..0bdfe684d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -39,12 +39,11 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class HunterSpeed extends SimpleAdaptation { - private final Map speedBursts = new HashMap<>(); + private final Map speedBursts = new java.util.concurrent.ConcurrentHashMap<>(); public HunterSpeed() { super("hunter-speed"); @@ -82,10 +81,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java index 2cac62a1a..7a44e908d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java @@ -23,12 +23,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -73,10 +72,7 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasAdaptation(p)) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java index 64fe8dd2a..5cb5b98e7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -85,11 +84,11 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(EntityDeathEvent e) { Player killer = e.getEntity().getKiller(); - if (killer == null || !hasAdaptation(killer) || e.getEntity() instanceof Player || !canPVE(killer, e.getEntity().getLocation())) { + if (killer == null || !hasActiveAdaptation(killer) || e.getEntity() instanceof Player || !canDamageTarget(killer, e.getEntity())) { return; } - int level = getLevel(killer); + int level = getActiveLevel(killer); PrecisionContext precision = readPrecisionContext(e, killer, level); if (!precision.precise()) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java index bf76416ae..8ac07f79b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -84,44 +83,46 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof Player p)) { return; } - if (!isFireCause(e.getCause()) || !isReady(p, getLevel(p))) { - return; - } + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (!isFireCause(e.getCause()) || !isReady(p, level)) { + return; + } - if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(getLevel(p))) { - return; - } + if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { + return; + } - applyLeech(p, getLevel(p), true); + applyLeech(p, level, true); + }); } @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p) || !(e.getEntity() instanceof LivingEntity target)) { + if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { return; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { + withAdaptedPlayer(p, e, () -> { + if (!canDamageTarget(p, target)) { return; } - } else if (!canPVE(p, target.getLocation())) { - return; - } - if (target.getFireTicks() <= 0 || !isReady(p, getLevel(p))) { - return; - } + int level = getActiveLevel(p); + if (target.getFireTicks() <= 0 || !isReady(p, level)) { + return; + } - if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(getLevel(p))) { - return; - } + if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { + return; + } - applyLeech(p, getLevel(p), false); + applyLeech(p, level, false); + }); } private boolean isFireCause(EntityDamageEvent.DamageCause cause) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java index 8f3d2398a..e6399682f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java @@ -16,16 +16,16 @@ - along with this program. If not, see . -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.nether; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -36,10 +36,6 @@ import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; - public class NetherFireResist extends SimpleAdaptation { public NetherFireResist() { super("nether-fire-resist"); @@ -79,27 +75,22 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGH) public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity() instanceof Player p)) { return; } - if (!hasAdaptation(p)) { - return; - } - - if (e.getCause() != EntityDamageEvent.DamageCause.FIRE && e.getCause() != EntityDamageEvent.DamageCause.FIRE_TICK) { - return; - } + withAdaptedPlayer(p, e, () -> { + if (e.getCause() != EntityDamageEvent.DamageCause.FIRE && e.getCause() != EntityDamageEvent.DamageCause.FIRE_TICK) { + return; + } - if (ThreadLocalRandom.current().nextDouble() < getFireResist(getLevel(p))) { - e.setCancelled(true); - getPlayer(p).getData().addStat("nether.fire-resist.negated", 1); - } + if (ThreadLocalRandom.current().nextDouble() < getFireResist(getLevel(p))) { + e.setCancelled(true); + getPlayer(p).getData().addStat("nether.fire-resist.negated", 1); + } + }); } public double getFireResist(double level) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java index 36d792aea..3900cf102 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java @@ -22,19 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; -import org.bukkit.entity.AbstractArrow; -import org.bukkit.entity.Fireball; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Player; -import org.bukkit.entity.WitherSkeleton; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -72,51 +67,63 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof Player p) || !hasAdaptation(p) || !isNether(p)) { + if (!(e.getEntity() instanceof Player p)) { return; } - int level = getLevel(p); - if (e.getDamager() instanceof Fireball fireball && fireball.getShooter() instanceof Ghast) { - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getGhastProjectileReduction(level)))); - p.setFireTicks(Math.min(p.getFireTicks(), getMaxFireTicks(level))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + withAdaptedPlayer(p, e, () -> { + if (!isNether(p)) { + return; } - return; - } - if (e.getDamager() instanceof AbstractArrow arrow && arrow.getShooter() instanceof WitherSkeleton) { - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getWitherSkeletonReduction(level)))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + int level = getActiveLevel(p); + if (e.getDamager() instanceof Fireball fireball && fireball.getShooter() instanceof Ghast) { + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getGhastProjectileReduction(level)))); + p.setFireTicks(Math.min(p.getFireTicks(), getMaxFireTicks(level))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + } + return; } - } + + if (e.getDamager() instanceof AbstractArrow arrow && arrow.getShooter() instanceof WitherSkeleton) { + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getWitherSkeletonReduction(level)))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + } + } + }); } @EventHandler(priority = EventPriority.HIGH) public void on(EntityDamageEvent e) { - if (e.isCancelled() || e instanceof EntityDamageByEntityEvent || !(e.getEntity() instanceof Player p) || !hasAdaptation(p) || !isNether(p)) { + if (e instanceof EntityDamageByEntityEvent || !(e.getEntity() instanceof Player p)) { return; } - if (e.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION && e.getCause() != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION) { - return; - } + withAdaptedPlayer(p, e, () -> { + if (!isNether(p)) { + return; + } - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getExplosionReduction(getLevel(p))))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); - } + if (e.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION && e.getCause() != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION) { + return; + } + + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getExplosionReduction(getLevel(p))))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + } + }); } private boolean isNether(Player p) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java index 5bb9b718b..ae9a5b6a1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -80,37 +79,39 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGH) public void on(PlayerMoveEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.getWorld().getEnvironment().name().contains("NETHER")) { - return; - } - - if (p.isFlying() || p.isGliding() || p.isInsideVehicle() || p.getFoodLevel() <= 0) { - return; - } - - Block feet = p.getLocation().getBlock(); - Block below = p.getLocation().clone().add(0, -1, 0).getBlock(); - if (!(isLava(feet) || isLava(below))) { - return; - } - - int level = getLevel(p); - if (getStorageLong(p, "lavaWalkerCooldown", 0L) > System.currentTimeMillis()) { - return; - } - - Vector velocity = p.getVelocity(); - Vector dir = p.getLocation().getDirection().setY(0).normalize().multiply(getStride(level)); - p.setVelocity(new Vector(dir.getX(), Math.max(0.16, velocity.getY()), dir.getZ())); - p.setFallDistance(0); - p.setFireTicks(0); - p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, getConfig().fireResistTicks, 0, false, false)); - - int hungerCost = getHungerCost(level); - p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); - setStorage(p, "lavaWalkerCooldown", System.currentTimeMillis() + getCooldownMillis(level)); - xp(p, getConfig().xpPerStride); - getPlayer(p).getData().addStat("nether.lava-walker.blocks-walked", 1); + withAdaptedPlayer(p, e, () -> { + if (!p.getWorld().getEnvironment().name().contains("NETHER")) { + return; + } + + if (p.isFlying() || p.isGliding() || p.isInsideVehicle() || p.getFoodLevel() <= 0) { + return; + } + + Block feet = p.getLocation().getBlock(); + Block below = p.getLocation().clone().add(0, -1, 0).getBlock(); + if (!(isLava(feet) || isLava(below))) { + return; + } + + int level = getActiveLevel(p); + if (getStorageLong(p, "lavaWalkerCooldown", 0L) > System.currentTimeMillis()) { + return; + } + + Vector velocity = p.getVelocity(); + Vector dir = p.getLocation().getDirection().setY(0).normalize().multiply(getStride(level)); + p.setVelocity(new Vector(dir.getX(), Math.max(0.16, velocity.getY()), dir.getZ())); + p.setFallDistance(0); + p.setFireTicks(0); + p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, getConfig().fireResistTicks, 0, false, false)); + + int hungerCost = getHungerCost(level); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + setStorage(p, "lavaWalkerCooldown", System.currentTimeMillis() + getCooldownMillis(level)); + xp(p, getConfig().xpPerStride); + getPlayer(p).getData().addStat("nether.lava-walker.blocks-walked", 1); + }); } private boolean isLava(Block b) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java index 0cf4ad9da..f6176c336 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java @@ -22,19 +22,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.entity.Piglin; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.PiglinBarterEvent; @@ -89,7 +88,11 @@ public void on(PiglinBarterEvent e) { return; } - int level = getLevel(broker); + int level = getActiveLevel(broker); + if (level <= 0) { + return; + } + List outcome = e.getOutcome(); if (outcome.isEmpty()) { return; @@ -128,7 +131,7 @@ private Player findBroker(Piglin piglin, double range) { if (!(nearby instanceof Player p)) { continue; } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java index 5f46de97c..b1874cc5c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java @@ -23,10 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.math.M; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.GameMode; @@ -46,17 +48,13 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class NetherSkullYeet extends SimpleAdaptation { - private final Map lastJump = new HashMap<>(); + private final Map lastJump = new ConcurrentHashMap<>(); public NetherSkullYeet() { super("nether-skull-toss"); @@ -111,63 +109,62 @@ private int getCooldownDuration(Player p) { @EventHandler public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - lastJump.remove(p); + lastJump.remove(p.getUniqueId()); } @EventHandler public void onRightClick(PlayerInteractEvent e) { - if (!hasAdaptation(e.getPlayer())) { - return; - } - if (e.useItemInHand() == Event.Result.DENY) { - return; - } + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (e.useItemInHand() == Event.Result.DENY) { + return; + } - if (e.getAction() != Action.LEFT_CLICK_AIR && e.getAction() != Action.LEFT_CLICK_BLOCK) { - return; - } - if (e.getHand() != EquipmentSlot.HAND || e.getItem() == null || e.getMaterial() != Material.WITHER_SKELETON_SKULL) { - return; - } + if (e.getAction() != Action.LEFT_CLICK_AIR && e.getAction() != Action.LEFT_CLICK_BLOCK) { + return; + } + if (e.getHand() != EquipmentSlot.HAND || e.getItem() == null || e.getMaterial() != Material.WITHER_SKELETON_SKULL) { + return; + } - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); + SoundPlayer sp = SoundPlayer.of(p); - if (lastJump.get(p) != null && M.ms() - lastJump.get(p) <= getCooldownDuration(p)) { - sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); - return; - } + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); + return; + } - if (lastJump.get(p) != null && M.ms() - lastJump.get(p) <= getCooldownDuration(p)) { - return; - } + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { + return; + } - if (p.hasCooldown(p.getInventory().getItemInMainHand().getType())) { - e.setCancelled(true); - sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); - return; - } else { - p.setCooldown(Material.WITHER_SKELETON_SKULL, getCooldownDuration(p)); - } + if (p.hasCooldown(p.getInventory().getItemInMainHand().getType())) { + e.setCancelled(true); + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); + return; + } else { + p.setCooldown(Material.WITHER_SKELETON_SKULL, getCooldownDuration(p)); + } - if (p.getGameMode() != GameMode.CREATIVE) { - e.getItem().setAmount(e.getItem().getAmount() - 1); - lastJump.put(p, M.ms()); - } + if (p.getGameMode() != GameMode.CREATIVE) { + e.getItem().setAmount(e.getItem().getAmount() - 1); + lastJump.put(p.getUniqueId(), M.ms()); + } - Vector dir = p.getEyeLocation().getDirection(); - Location spawn = p.getEyeLocation().add(new Vector(.5, -.5, .5)).add(dir); - p.getWorld().spawn(spawn, WitherSkull.class, entity -> { - sp.play(entity, Sound.ENTITY_WITHER_SHOOT, 1, 1); - entity.setRotation(p.getEyeLocation().getYaw(), p.getEyeLocation().getPitch()); - entity.setCharged(false); - entity.setBounce(false); - entity.setDirection(dir); - entity.setShooter(p); - xp(p, 100); + Vector dir = p.getEyeLocation().getDirection(); + Location spawn = p.getEyeLocation().add(new Vector(.5, -.5, .5)).add(dir); + p.getWorld().spawn(spawn, WitherSkull.class, entity -> { + sp.play(entity, Sound.ENTITY_WITHER_SHOOT, 1, 1); + entity.setRotation(p.getEyeLocation().getYaw(), p.getEyeLocation().getPitch()); + entity.setCharged(false); + entity.setBounce(false); + entity.setDirection(dir); + entity.setShooter(p); + xp(p, 100); + }); + getPlayer(p).getData().addStat("nether.skull-yeet.skulls-thrown", 1); }); - getPlayer(p).getData().addStat("nether.skull-yeet.skulls-thrown", 1); } @EventHandler @@ -175,14 +172,15 @@ public void onEntityDeath(EntityDeathEvent e) { LivingEntity dead = e.getEntity(); if (dead.getLastDamageCause() instanceof EntityDamageByEntityEvent dbe && dbe.getDamager() instanceof WitherSkull skull - && skull.getShooter() instanceof Player p - && hasAdaptation(p)) { - getPlayer(p).getData().addStat("nether.skull-yeet.skull-kills", 1); - - double distance = p.getLocation().distance(dead.getLocation()); - if (distance >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_nether_skull_long_bomb")) { - getPlayer(p).getAdvancementHandler().grant("challenge_nether_skull_long_bomb"); - } + && skull.getShooter() instanceof Player p) { + withAdaptedPlayer(p, () -> { + getPlayer(p).getData().addStat("nether.skull-yeet.skull-kills", 1); + + double distance = p.getLocation().distance(dead.getLocation()); + if (distance >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_nether_skull_long_bomb")) { + getPlayer(p).getAdvancementHandler().grant("challenge_nether_skull_long_bomb"); + } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java index 4f71d7d83..e1aa6e143 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java @@ -22,10 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.Data; import lombok.NoArgsConstructor; @@ -80,17 +79,14 @@ public void addStats(int level, Element v) { @EventHandler public void onEntityDamage(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } if (e.getCause() == EntityDamageEvent.DamageCause.WITHER && e.getEntity() instanceof Player p) { - if (!hasAdaptation(p)) - return; - double chance = getTotalChange(p); - if (ThreadLocalRandom.current().nextInt(101) <= chance) { - e.setCancelled(true); - getPlayer(p).getData().addStat("nether.wither-resist.negated", 1); - } + withAdaptedPlayer(p, e, () -> { + double chance = getTotalChange(p); + if (ThreadLocalRandom.current().nextInt(101) <= chance) { + e.setCancelled(true); + getPlayer(p).getData().addStat("nether.wither-resist.negated", 1); + } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java index 82f0e196c..738cd8aff 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java @@ -23,16 +23,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Enchantments; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Enchantments; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -219,16 +218,10 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGH) public void on(BlockBreakEvent e) { Player p = e.getPlayer(); - if (e.isCancelled()) { - return; - } - if (!hasAdaptation(p)) { - return; - } if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE") && !ItemListings.getSmeltOre().contains(e.getBlock().getType())) { return; } - if (!canBlockBreak(p, e.getBlock().getLocation())) { + if (resolveBlockBreakContext(p, e.getBlock().getLocation()) == null) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java index 8043dd176..3f5cc1c8f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java @@ -17,17 +17,19 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.pickaxe; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -40,11 +42,6 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class PickaxeChisel extends SimpleAdaptation { public PickaxeChisel() { super("pickaxe-chisel"); @@ -95,7 +92,7 @@ private int getDamagePerBlock(double levelPercent) { public void on(PlayerInteractEvent e) { Player p = e.getPlayer(); - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isPickaxe(p.getInventory().getItemInMainHand()) && hasAdaptation(p)) { + if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isPickaxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { if (p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH) || p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.MENDING)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java index bf92d946e..c82e94391 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java @@ -22,16 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; import lombok.NoArgsConstructor; -import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Item; @@ -76,18 +74,9 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockDropItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { - return; - } - if (p.getGameMode() != GameMode.SURVIVAL) { - return; - } - if (!canBlockBreak(p, e.getBlock().getLocation())) { + if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { return; } if (ItemListings.toolPickaxes.contains(p.getInventory().getItemInMainHand().getType())) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index 405873f16..d012dca9a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -23,21 +23,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.entity.Slime; @@ -91,9 +86,6 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled()) { - return; - } Action action = e.getAction(); if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { @@ -105,7 +97,8 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { return; } @@ -114,7 +107,6 @@ public void on(PlayerInteractEvent e) { return; } - int level = getLevel(p); if (areParticlesEnabled()) { p.spawnParticle(Particle.ENCHANT, p.getEyeLocation(), 14, 0.2, 0.25, 0.2, 0.15); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java index 7862415aa..b99914f81 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java @@ -4,13 +4,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.RNG; -import art.arcane.volmlib.util.collection.KList; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -61,9 +60,10 @@ public PickaxeSilkSpawner() { public void onBlockBreak(BlockBreakEvent event) { var player = event.getPlayer(); var block = event.getBlock(); - if (!event.isDropItems() || !hasAdaptation(player) || block.getType() != Material.SPAWNER || !canBlockBreak(player, event.getBlock().getLocation())) + var context = resolveBlockBreakContext(player, block.getLocation()); + if (!event.isDropItems() || block.getType() != Material.SPAWNER || context == null) return; - var level = getLevel(player); + var level = context.level(); if (level == 1 && !player.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { return; } else if (level > 1 && !player.isSneaking()) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index 9532d4ab1..ea415a414 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -24,12 +24,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.content.item.ItemListings; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Location; @@ -45,13 +47,6 @@ import java.util.HashMap; import java.util.Map; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.reflect.registries.Particles; - import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class PickaxeVeinminer extends SimpleAdaptation { @@ -97,18 +92,13 @@ private int getRadius(int lvl) { @EventHandler public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } if (VEIN_MINED.get(e.getBlock())) { return; } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { - return; - } - if (!p.isSneaking()) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { return; } @@ -120,10 +110,10 @@ public void on(BlockBreakEvent e) { VEIN_MINED.add(e.getBlock()); Block block = e.getBlock(); - Map blockMap = new HashMap<>(); + Map blockMap = new java.util.concurrent.ConcurrentHashMap<>(); blockMap.put(block.getLocation(), block); - int radius = getRadius(getLevel(p)); + int radius = getRadius(level); for (int i = 0; i < radius; i++) { for (int x = -i; x <= i; x++) { for (int y = -i; y <= i; y++) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java index df2d4a12b..00692b89f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java @@ -5,13 +5,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Enchantments; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Arrow; import org.bukkit.entity.Player; @@ -20,13 +20,14 @@ import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public class RangedArrowRecovery extends SimpleAdaptation { - private final Map shotArrows; + private final Map shotArrows; public RangedArrowRecovery() { super("ranged-recovery"); @@ -38,7 +39,7 @@ public RangedArrowRecovery() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - shotArrows = new HashMap<>(); + shotArrows = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.ARROW) .key("challenge_ranged_arrow_500") @@ -61,10 +62,10 @@ public RangedArrowRecovery() { @EventHandler public void onEntityShootBow(EntityShootBowEvent event) { - if (event.getEntity() instanceof Player player && hasAdaptation(player)) { + if (event.getEntity() instanceof Player player && hasActiveAdaptation(player)) { if (!event.getBow().containsEnchantment(Enchantments.ARROW_INFINITE)) { if (event.getProjectile() instanceof Arrow arrow) { - shotArrows.put(arrow, player); + shotArrows.put(arrow.getUniqueId(), player.getUniqueId()); } } } @@ -73,9 +74,10 @@ public void onEntityShootBow(EntityShootBowEvent event) { @EventHandler public void onProjectileHit(ProjectileHitEvent event) { if (event.getEntity() instanceof Arrow arrow) { - Player shooter = shotArrows.get(arrow); - if (shooter != null && hasAdaptation(shooter)) { - int level = getLevel(shooter); + UUID shooterId = shotArrows.get(arrow.getUniqueId()); + Player shooter = shooterId == null ? null : Bukkit.getPlayer(shooterId); + int level = shooter == null ? 0 : getActiveLevel(shooter); + if (level > 0) { double chance = getConfig().hitChance[level - 1] / 100.0; if (RANDOM.nextDouble() < chance) { ItemStack arrowStack = new ItemStack(Material.ARROW, 1); @@ -84,7 +86,7 @@ public void onProjectileHit(ProjectileHitEvent event) { Adapt.info("Arrow added to inventory."); } } - shotArrows.remove(arrow); + shotArrows.remove(arrow.getUniqueId()); } } @@ -130,4 +132,4 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Chance for the Ranged Arrow Recovery adaptation.", impact = "Add or remove entries to control which values are included.") double[] hitChance = {10, 20, 30, 40, 50, 60, 70, 80}; } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java index 3c4cd4667..251107732 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java @@ -22,20 +22,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; 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.entity.EntityDamageByEntityEvent; @@ -44,8 +42,6 @@ import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.reflect.registries.Particles; - public class RangedFloaters extends SimpleAdaptation { public RangedFloaters() { super("ranged-floaters"); @@ -78,23 +74,14 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGH) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } - - if (!(e.getDamager() instanceof Projectile projectile)) { - return; - } - - if (!(projectile.getShooter() instanceof Player p) || !hasAdaptation(p)) { - return; - } - - if (!(e.getEntity() instanceof LivingEntity target)) { + var combat = resolveProjectileContext(e); + if (combat == null) { return; } - int level = getLevel(p); + Player p = combat.attacker(); + LivingEntity target = combat.target(); + int level = combat.level(); if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java index 6c79b4a7c..36d7d1e1d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java @@ -17,32 +17,27 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.ranged; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; - public class RangedForce extends SimpleAdaptation { public RangedForce() { @@ -86,36 +81,35 @@ private double getSpeed(double factor) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var combat = resolveProjectileContext(e); + if (combat == null) { return; } - if (e.getDamager() instanceof Projectile r && r.getShooter() instanceof Player p && hasAdaptation(p)) { - Location a = e.getEntity().getLocation().clone(); - Location b = p.getLocation().clone(); - a.setY(0); - b.setY(0); - xp(p, 5); - double distSq = a.distanceSquared(b); - - if (distSq > 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_force_30")) { - getPlayer(p).getAdvancementHandler().grant("challenge_force_30"); - xp(p, getConfig().challengeRewardLongShotReward, "challenge-long-shot"); - } - if (distSq > 900) { - getPlayer(p).getData().addStat("ranged.force.long-range-hits", 1); - } + Player p = combat.attacker(); + Location a = e.getEntity().getLocation().clone(); + Location b = p.getLocation().clone(); + a.setY(0); + b.setY(0); + xp(p, 5); + double distSq = a.distanceSquared(b); + + if (distSq > 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_force_30")) { + getPlayer(p).getAdvancementHandler().grant("challenge_force_30"); + xp(p, getConfig().challengeRewardLongShotReward, "challenge-long-shot"); + } + + if (distSq > 900) { + getPlayer(p).getData().addStat("ranged.force.long-range-hits", 1); } } @EventHandler public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity().getShooter() instanceof Player p) { - if (hasAdaptation(p)) { - double factor = getLevelPercent(p); + int level = getActiveLevel(p); + if (level > 0) { + double factor = getLevelPercent(level); e.getEntity().setVelocity(e.getEntity().getVelocity().clone().multiply(1 + getSpeed(factor))); SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); spw.play(e.getEntity().getLocation(), Sound.ENTITY_SNOWBALL_THROW, 0.5f + ((float) factor * 0.25f), 0.7f + (float) (factor / 2f)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java index 9ea123892..91b83c6b3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java @@ -17,16 +17,17 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.ranged; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -37,12 +38,6 @@ import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.util.Vector; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Particles; - public class RangedLungeShot extends SimpleAdaptation { public RangedLungeShot() { super("ranged-lunge-shot"); @@ -86,12 +81,9 @@ public void addStats(int level, Element v) { @EventHandler public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity().getShooter() instanceof Player p) { if (e.getEntity() instanceof AbstractArrow a) { - if (hasAdaptation(p)) { + if (hasActiveAdaptation(p)) { if (!p.isOnGround()) { Vector velocity = p.getPlayer().getLocation().getDirection().normalize().multiply(getSpeed(getLevelPercent(p))); p.setVelocity(p.getVelocity().subtract(velocity)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java index 5216b0ba0..2a0c58224 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java @@ -23,26 +23,23 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.AbstractArrow; import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class RangedPiercing extends SimpleAdaptation { - private final Map arrowHitCounts = new HashMap<>(); + private final Map arrowHitCounts = new java.util.concurrent.ConcurrentHashMap<>(); public RangedPiercing() { super("ranged-piercing"); @@ -81,14 +78,12 @@ public void addStats(int level, Element v) { @EventHandler public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity().getShooter() instanceof Player p) { if (e.getEntity() instanceof AbstractArrow a) { xp(p, 5); - if (hasAdaptation(p)) { - a.setPierceLevel(((AbstractArrow) e.getEntity()).getPierceLevel() + getLevel(p)); + int level = getActiveLevel(p); + if (level > 0) { + a.setPierceLevel(((AbstractArrow) e.getEntity()).getPierceLevel() + level); } } } @@ -96,20 +91,22 @@ public void on(ProjectileLaunchEvent e) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var combat = resolveProjectileContext(e, projectile -> projectile instanceof AbstractArrow); + if (combat == null) { return; } - if (e.getDamager() instanceof Projectile projectile && projectile instanceof AbstractArrow arrow && arrow.getShooter() instanceof Player p && hasAdaptation(p)) { - if (arrow.getPierceLevel() > 0) { - UUID arrowId = arrow.getUniqueId(); - int hits = arrowHitCounts.getOrDefault(arrowId, 0) + 1; - arrowHitCounts.put(arrowId, hits); - if (hits > 1) { - getPlayer(p).getData().addStat("ranged.piercing.extra-hits", 1); - } - if (hits >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_ranged_piercing_4")) { - getPlayer(p).getAdvancementHandler().grant("challenge_ranged_piercing_4"); - } + + AbstractArrow arrow = (AbstractArrow) combat.projectile(); + if (arrow.getPierceLevel() > 0) { + UUID arrowId = arrow.getUniqueId(); + int hits = arrowHitCounts.getOrDefault(arrowId, 0) + 1; + arrowHitCounts.put(arrowId, hits); + Player p = combat.attacker(); + if (hits > 1) { + getPlayer(p).getData().addStat("ranged.piercing.extra-hits", 1); + } + if (hits >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_ranged_piercing_4")) { + getPlayer(p).getAdvancementHandler().grant("challenge_ranged_piercing_4"); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java index 72c844fc0..8e77a6217 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -43,13 +42,12 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class RangedPinningShot extends SimpleAdaptation { - private final Map targetProcTimes = new HashMap<>(); + private final Map targetProcTimes = new java.util.concurrent.ConcurrentHashMap<>(); public RangedPinningShot() { super("ranged-pinning-shot"); @@ -82,21 +80,21 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !hasAdaptation(p) || !(e.getEntity() instanceof LivingEntity target)) { + if (!(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, target.getLocation())) { + if (!canDamageTarget(p, target)) { return; } long now = System.currentTimeMillis(); cleanupExpired(now); - int level = getLevel(p); long reapply = getReapplyCooldownMillis(level); Long last = targetProcTimes.get(target.getUniqueId()); if (last != null && last + reapply > now) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java index 030a2bd78..7ce72947b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java @@ -23,30 +23,19 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.block.BlockFace; -import org.bukkit.entity.AbstractArrow; -import org.bukkit.entity.Arrow; -import org.bukkit.entity.Egg; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.Snowball; -import org.bukkit.entity.SpectralArrow; -import org.bukkit.entity.ThrownExpBottle; -import org.bukkit.entity.ThrownPotion; -import org.bukkit.entity.Trident; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -56,8 +45,6 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.util.Vector; -import art.arcane.adapt.util.common.math.Direction; - public class RangedRicochetBolt extends SimpleAdaptation { private static final String RICOCHET_COUNT_META = "adapt-ricochet-count"; private static final String RICOCHET_MAX_META = "adapt-ricochet-max"; @@ -103,7 +90,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(ProjectileHitEvent e) { - if (!(e.getEntity() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -111,7 +103,6 @@ public void on(ProjectileHitEvent e) { return; } - int level = getLevel(p); int ricochetCount = Math.max(0, getMetadataInt(projectile, RICOCHET_COUNT_META, 0)); int maxRicochets = Math.max(1, getMetadataInt(projectile, RICOCHET_MAX_META, getMaxRicochets(level))); if (ricochetCount >= maxRicochets) { @@ -178,11 +169,7 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (e.getEntity() instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, e.getEntity().getLocation())) { + if (!canDamageTarget(p, e.getEntity())) { return; } @@ -194,7 +181,7 @@ public void on(EntityDamageByEntityEvent e) { @EventHandler public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() instanceof Player p && hasAdaptation(p)) { + if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Projectile projectile && projectile.hasMetadata(RICOCHET_COUNT_META) diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index fcbd606fe..0137aac21 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -19,28 +19,21 @@ package art.arcane.adapt.content.adaptation.ranged; import art.arcane.adapt.Adapt; -import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.Skill; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Color; -import org.bukkit.FluidCollisionMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; +import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; @@ -58,14 +51,13 @@ import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class RangedTrajectorySight extends SimpleAdaptation { private static final double EPSILON = 0.0000001D; - private final Map drawStartedMillis = new HashMap<>(); - private final Map previewGlowTargets = new HashMap<>(); + private final Map drawStartedMillis = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map previewGlowTargets = new java.util.concurrent.ConcurrentHashMap<>(); private volatile RangedForce cachedRangedForce; private volatile RangedRicochetBolt cachedRicochetBolt; @@ -111,7 +103,7 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -141,7 +133,7 @@ public void on(EntityShootBowEvent e) { @EventHandler public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() instanceof Player p && hasAdaptation(p)) { + if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof Player) { @@ -154,7 +146,8 @@ public void on(EntityDeathEvent e) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { drawStartedMillis.remove(p.getUniqueId()); clearPreviewGlow(p); continue; @@ -177,7 +170,7 @@ public void onTick() { continue; } - UUID predictedHit = renderTrajectory(p, getSegments(getLevel(p)), shot); + UUID predictedHit = renderTrajectory(p, getSegments(level), shot); updatePreviewGlow(p, predictedHit); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java index 92ca7a57b..9a825eed2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java @@ -25,10 +25,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.BoundSnowBall; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import lombok.NoArgsConstructor; @@ -38,7 +40,6 @@ import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Snowball; import org.bukkit.event.EventHandler; @@ -51,18 +52,13 @@ import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class RangedWebBomb extends SimpleAdaptation { private static final BlockData AIR = Material.AIR.createBlockData(); private static final BlockData BLOCK = Material.COBWEB.createBlockData(); - private final Map activeSnowballs; + private final Map activeSnowballs; private final Set activeBlocks; public RangedWebBomb() { @@ -86,8 +82,8 @@ public RangedWebBomb() { "III")) .result(BoundSnowBall.io.withData(new BoundSnowBall.Data(null))) .build()); - activeBlocks = new HashSet<>(); - activeSnowballs = new HashMap<>(); + activeBlocks = ConcurrentHashMap.newKeySet(); + activeSnowballs = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.COBWEB) .key("challenge_ranged_web_200") @@ -108,9 +104,23 @@ public void addStats(int level, Element v) { @EventHandler public void on(ProjectileHitEvent e) { - if (e.isCancelled()) { + if (!(e.getEntity() instanceof Snowball snowball)) { + return; + } + UUID shooterId = activeSnowballs.remove(snowball.getUniqueId()); + if (shooterId == null) { return; } + Player p = Bukkit.getPlayer(shooterId); + if (p == null || !p.isOnline()) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + Block block; if (e.getHitEntity() != null) { @@ -121,43 +131,34 @@ public void on(ProjectileHitEvent e) { block = e.getEntity().getLocation().add(0, 1, 0).getBlock(); } - if (e.getEntity().getShooter() instanceof Player p && e.getEntity() instanceof Snowball snowball && hasAdaptation(p)) { - vfxCuboidOutline(block, Particle.REVERSE_PORTAL); - Adapt.verbose("Snowball Got: " + snowball.getEntityId() + " " + snowball.getUniqueId()); - if (activeSnowballs.containsKey(Bukkit.getEntity(snowball.getUniqueId()))) { - Adapt.verbose("Detected snowball hit"); - if (e.getHitEntity() != null) { - getPlayer(p).getData().addStat("ranged.web-bomb.mobs-trapped", 1); - } - activeSnowballs.remove(snowball); - snowball.remove(); - Set locs = new HashSet<>(); - locs.add(block.getLocation().add(0, 1, 0).getBlock()); - locs.add(block.getLocation().add(0, -1, 0).getBlock()); - locs.add(block.getLocation().add(0, 0, 1).getBlock()); - locs.add(block.getLocation().add(0, 0, -1).getBlock()); - locs.add(block.getLocation().add(1, 0, 0).getBlock()); - locs.add(block.getLocation().add(-1, 0, 0).getBlock()); - - for (Block i : locs) { - addWebFoundation(i, getLevel(p)); - } - - } + vfxCuboidOutline(block, Particle.REVERSE_PORTAL); + Adapt.verbose("Snowball Got: " + snowball.getEntityId() + " " + snowball.getUniqueId()); + Adapt.verbose("Detected snowball hit"); + if (e.getHitEntity() != null) { + getPlayer(p).getData().addStat("ranged.web-bomb.mobs-trapped", 1); + } + snowball.remove(); + Set locs = new HashSet<>(); + locs.add(block.getLocation().add(0, 1, 0).getBlock()); + locs.add(block.getLocation().add(0, -1, 0).getBlock()); + locs.add(block.getLocation().add(0, 0, 1).getBlock()); + locs.add(block.getLocation().add(0, 0, -1).getBlock()); + locs.add(block.getLocation().add(1, 0, 0).getBlock()); + locs.add(block.getLocation().add(-1, 0, 0).getBlock()); + + for (Block i : locs) { + addWebFoundation(i, level); } } @EventHandler public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity().getShooter() instanceof Player p && e.getEntity() instanceof Snowball snowball && hasAdaptation(p)) { + if (e.getEntity().getShooter() instanceof Player p && e.getEntity() instanceof Snowball snowball && hasActiveAdaptation(p)) { Adapt.verbose("Snowball Launched: " + snowball.getEntityId() + " " + snowball.getUniqueId()); if (BoundSnowBall.isBindableItem(snowball.getItem())) { Adapt.verbose("Snowball is bound"); - activeSnowballs.put(snowball, p); + activeSnowballs.put(snowball.getUniqueId(), p.getUniqueId()); } else { Adapt.verbose("Snowball is not bound"); } @@ -203,9 +204,6 @@ public void removeFoundation(Block block) { //prevent piston from moving blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockPistonExtendEvent e) { - if (e.isCancelled()) { - return; - } e.getBlocks().forEach(b -> { if (activeBlocks.contains(b)) { Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); @@ -217,9 +215,6 @@ public void on(BlockPistonExtendEvent e) { //prevent piston from pulling blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockPistonRetractEvent e) { - if (e.isCancelled()) { - return; - } e.getBlocks().forEach(b -> { if (activeBlocks.contains(b)) { Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); @@ -231,9 +226,6 @@ public void on(BlockPistonRetractEvent e) { //prevent TNT from destroying blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(BlockExplodeEvent e) { - if (e.isCancelled()) { - return; - } if (activeBlocks.contains(e.getBlock())) { Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); e.setCancelled(true); @@ -251,9 +243,6 @@ public void on(BlockBreakEvent e) { //prevent Entities from destroying blocks // Dupe fix @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityExplodeEvent e) { - if (e.isCancelled()) { - return; - } e.blockList().removeIf(activeBlocks::contains); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java index 765f6dc45..adf83811a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java @@ -25,10 +25,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.BoundEnderPearl; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -54,12 +55,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Particles; - import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class RiftAccess extends SimpleAdaptation { @@ -130,7 +125,7 @@ public void on(PlayerInteractEvent e) { // If the main hand is holding a bound enderpearl if (mainHandBound) { e.setCancelled(true); - if (hasAdaptation(p)) { + if (hasActiveAdaptation(p)) { Adapt.verbose("Player using bound enderpearl."); handleEnderPearlInteraction(e, p, block); } @@ -250,13 +245,11 @@ private void removeEntryIfViewsEmpty(Iterator @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBurnEvent event) { - if (event.isCancelled()) return; invClose(event.getBlock()); } @EventHandler(priority = EventPriority.MONITOR) public void on(BlockPistonRetractEvent event) { - if (event.isCancelled()) return; for (Block b : event.getBlocks()) { invClose(b); } @@ -264,7 +257,6 @@ public void on(BlockPistonRetractEvent event) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockPistonExtendEvent event) { - if (event.isCancelled()) return; for (Block b : event.getBlocks()) { invClose(b); } @@ -272,7 +264,6 @@ public void on(BlockPistonExtendEvent event) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockExplodeEvent event) { - if (event.isCancelled()) return; for (Block b : event.blockList()) { invClose(b); } @@ -280,7 +271,6 @@ public void on(BlockExplodeEvent event) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent event) { - if (event.isCancelled()) return; invClose(event.getBlock()); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index 08f8acb69..5871e55e7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -22,51 +22,37 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; -import org.bukkit.event.block.Action; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.event.player.PlayerToggleSneakEvent; -import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.reflect.registries.Particles; - import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class RiftBlink extends SimpleAdaptation { - private final Map lastBlink = new HashMap<>(); - private final Map jumpArmUntil = new HashMap<>(); - private final Map lastOnGround = new HashMap<>(); + private final Map lastBlink = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map jumpArmUntil = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastOnGround = new java.util.concurrent.ConcurrentHashMap<>(); public RiftBlink() { super("rift-blink"); @@ -108,7 +94,7 @@ private long getCooldownDuration() { } private boolean isBlinkEligible(Player p) { - return hasAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; } private boolean isOnCooldown(UUID id) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java index 964cfb490..3161071e4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java @@ -22,9 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -36,17 +38,12 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import java.util.ArrayList; -import java.util.List; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class RiftDescent extends SimpleAdaptation { - private final List cooldown = new ArrayList<>(); + private final Set cooldown = ConcurrentHashMap.newKeySet(); public RiftDescent() { super("rift-descent"); @@ -92,10 +89,11 @@ public void on(PlayerToggleSneakEvent e) { if (p.getPotionEffect(PotionEffectType.LEVITATION) == null) { return; } - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } - if (cooldown.contains(p)) { + UUID playerId = p.getUniqueId(); + if (cooldown.contains(playerId)) { return; } @@ -104,10 +102,10 @@ public void on(PlayerToggleSneakEvent e) { if (!e.isSneaking() && (levi != null)) { p.removePotionEffect(PotionEffectType.LEVITATION); getPlayer(p).getData().addStat("rift.descent.levitation-cancelled", 1); - cooldown.add(p); + cooldown.add(playerId); J.runEntity(p, () -> { sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); - cooldown.remove(p); + cooldown.remove(playerId); }, Math.max(1, (int) Math.round(getConfig().cooldown * 20D))); J.runEntity(p, () -> { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java index f0820fe7d..ed4e5f9a8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java @@ -23,33 +23,17 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.BoundEnderPearl; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Particle; -import org.bukkit.Sound; -import org.bukkit.entity.Animals; -import org.bukkit.entity.Ambient; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Mob; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Player; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Villager; -import org.bukkit.entity.WaterMob; +import org.bukkit.*; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; @@ -74,9 +58,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.nbt.Tag; - public class RiftEnderTaglock extends SimpleAdaptation { private static final String PROJECTILE_TARGET_META = "adapt-rift-taglock-target"; private final NamespacedKey targetKey; @@ -133,11 +114,12 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target) || !hasAdaptation(p)) { + if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { return; } - if (!p.isSneaking()) { + int level = getActiveDamageLevel(p, target); + if (level <= 0 || !p.isSneaking()) { return; } @@ -146,19 +128,10 @@ public void on(EntityDamageByEntityEvent e) { return; } - int level = getLevel(p); if (!isTaggable(target, level)) { return; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, target.getLocation())) { - return; - } - e.setCancelled(true); tagIntoPearl(p, hand, target); SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.55f, 1.4f); @@ -182,7 +155,8 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { return; } @@ -206,7 +180,7 @@ public void on(PlayerInteractEvent e) { EnderPearl pearl = p.launchProjectile(EnderPearl.class); pearl.setMetadata(PROJECTILE_TARGET_META, new FixedMetadataValue(Adapt.instance, target.toString())); suppressPearlTeleportUntil.put(p.getUniqueId(), System.currentTimeMillis() + getSuppressPearlTeleportWindowMillis()); - p.setCooldown(Material.ENDER_PEARL, getThrowCooldownTicks(getLevel(p))); + p.setCooldown(Material.ENDER_PEARL, getThrowCooldownTicks(level)); SoundPlayer.of(p).play(p.getLocation(), Sound.ENTITY_ENDER_EYE_LAUNCH, 0.65f, 1.25f); xp(p, getConfig().xpOnThrow); } @@ -231,7 +205,11 @@ public void on(PlayerTeleportEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(ProjectileHitEvent e) { - if (!(e.getEntity() instanceof EnderPearl pearl) || !(pearl.getShooter() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof EnderPearl pearl) || !(pearl.getShooter() instanceof Player p)) { + return; + } + + if (!hasActiveAdaptation(p)) { return; } @@ -253,11 +231,15 @@ public void on(ProjectileHitEvent e) { } Location destination = resolveDestination(e); - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation()) || !canPVP(p, destination)) { + if (!canDamageTarget(p, target)) { + return; + } + + if (target instanceof Player) { + if (!canPVP(p, destination)) { return; } - } else if (!canPVE(p, target.getLocation()) || !canPVE(p, destination)) { + } else if (!canPVE(p, destination)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java index 4462377e2..875391030 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -74,7 +73,7 @@ public void on(PlayerInteractEvent e) { SoundPlayer sp = SoundPlayer.of(p); ItemStack hand = p.getInventory().getItemInMainHand(); - if (hand.getType() != Material.ENDER_CHEST || !hasAdaptation(p)) { + if (hand.getType() != Material.ENDER_CHEST || !hasActiveAdaptation(p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java index 6ec8b5d22..c623fc356 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java @@ -24,11 +24,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; import art.arcane.adapt.content.item.BoundEyeOfEnder; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -42,14 +44,6 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.reflect.registries.Particles; - public class RiftGate extends SimpleAdaptation { public RiftGate() { super("rift-gate"); @@ -112,7 +106,7 @@ public void on(PlayerInteractEvent e) { if (p.getInventory().getItemInMainHand().getType().equals(Material.ENDER_EYE) && !p.hasCooldown(Material.ENDER_EYE) - && hasAdaptation(p) + && hasActiveAdaptation(p) && BoundEyeOfEnder.isBindableItem(hand)) { e.setCancelled(true); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index e5633e2f2..f87227ff4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -44,8 +43,6 @@ import java.util.Map; -import art.arcane.adapt.util.common.math.Dimension; - public class RiftInflatedPocketDimension extends SimpleAdaptation { public RiftInflatedPocketDimension() { super("rift-inflated-pocket-dimension"); @@ -96,7 +93,7 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -124,7 +121,7 @@ public void on(PlayerInteractEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockPlaceEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } @@ -160,7 +157,7 @@ public void on(BlockPlaceEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(PlayerDropItemEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + if (getActiveLevel(p, Player::isSneaking) <= 0) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java index 48f043fdc..54dc4aea0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java @@ -23,10 +23,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; @@ -89,7 +88,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } ItemStack hand = p.getInventory().getItemInMainHand(); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java index 72689be4e..ebcf9a4a1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java @@ -4,10 +4,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -60,7 +59,7 @@ public void onEntityTarget(EntityTargetEvent event) { Entity entity = event.getEntity(); if (entity instanceof Enderman) { if (event.getTarget() instanceof Player player) { - if (hasAdaptation(player) && hasEnderPearl(player)) { + if (hasActiveAdaptation(player) && hasEnderPearl(player)) { event.setCancelled(true); getPlayer(player).getData().addStat("rift.visage.stares-survived", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index a473b1dee..2adcca51f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -22,15 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; @@ -41,8 +39,6 @@ import java.util.Map; -import art.arcane.adapt.util.common.inventorygui.Items; - public class RiftVoidMagnet extends SimpleAdaptation { public RiftVoidMagnet() { super("rift-void-magnet"); @@ -86,11 +82,11 @@ public void addStats(int level, Element v) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || p.getTicksLived() % getPulseTicks(getLevel(p)) != 0) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getTicksLived() % getPulseTicks(level) != 0) { continue; } - int level = getLevel(p); int moved = collectNearbyItems(p, level); if (moved <= 0) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java index bd1672912..5365c0ac6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java @@ -23,11 +23,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -79,27 +78,23 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerFishEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { - return; - } - if (e.getState() == PlayerFishEvent.State.CAUGHT_FISH) { - getPlayer(p).getData().addStat("seaborne.fishers-fantasy.fish-caught", 1); - int level = getLevel(p); - ThreadLocalRandom random = ThreadLocalRandom.current(); - for (int i = 0; i < level; i++) { - ItemStack item = new ItemStack(ItemListings.getFishingDrops().getRandom(), 1); - if (random.nextBoolean()) { - p.getWorld().dropItemNaturally(p.getLocation(), item); - p.getWorld().spawn(p.getLocation(), ExperienceOrb.class); - Adapt.verbose("Fishing Gift Donated!"); - xp(p, 15 * level); + withAdaptedPlayer(p, e, () -> { + if (e.getState() == PlayerFishEvent.State.CAUGHT_FISH) { + getPlayer(p).getData().addStat("seaborne.fishers-fantasy.fish-caught", 1); + int level = getActiveLevel(p); + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (int i = 0; i < level; i++) { + ItemStack item = new ItemStack(ItemListings.getFishingDrops().getRandom(), 1); + if (random.nextBoolean()) { + p.getWorld().dropItemNaturally(p.getLocation(), item); + p.getWorld().spawn(p.getLocation(), ExperienceOrb.class); + Adapt.verbose("Fishing Gift Donated!"); + xp(p, 15 * level); + } } } - } + }); } @Override diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java index 129d9719e..f6ea23d73 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java @@ -17,28 +17,22 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.seaborrne; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.scheduling.J; - public class SeaborneOxygen extends SimpleAdaptation { public SeaborneOxygen() { @@ -80,34 +74,20 @@ public void onTick() { continue; } - Runnable applyOxygen = () -> { + withPlayerThread(player, () -> { if (!player.isOnline() || player.getWorld() == null) { return; } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - return; - } - - if ((!player.isInWater() && !player.isSwimming()) || !hasAdaptation(player)) { - return; - } - - int level = getLevel(player); - if (level <= 0) { + int level = getActiveLevel(player); + if (level <= 0 || (!player.isInWater() && !player.isSwimming())) { return; } int airTicks = level * getConfig().airPerLevelTics; player.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, airTicks, level)); getPlayer(player).getData().addStat("seaborne.oxygen.bonus-air-ticks", airTicks); - }; - - if (J.isFoliaThreading()) { - J.runEntity(player, applyOxygen); - } else { - applyOxygen.run(); - } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index 3b2c7373d..7cce3c5b1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -22,13 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -39,13 +37,13 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; public class SeabornePressureDiver extends SimpleAdaptation { - private final Map xpCooldowns = new HashMap<>(); + private final Map xpCooldowns = new ConcurrentHashMap<>(); public SeabornePressureDiver() { super("seaborne-pressure-diver"); @@ -83,16 +81,26 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p) || !hasAdaptation(p) || !p.isInWater()) { + if (!(e.getEntity() instanceof Player p)) { return; } - int level = getLevel(p); - if (!isDeepEnough(p, level)) { - return; - } + withPlayerThread(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (!p.isInWater()) { + return; + } + + if (!isDeepEnough(p, level)) { + return; + } - e.setDamage(e.getDamage() * (1D - getDamageReduction(level))); + e.setDamage(e.getDamage() * (1D - getDamageReduction(level))); + }); } @Override @@ -104,20 +112,16 @@ public void onTick() { continue; } - Runnable apply = () -> { + withPlayerThread(p, () -> { if (!p.isOnline()) { return; } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return; - } - - if (!hasAdaptation(p) || !p.isInWater()) { + int level = getActiveLevel(p); + if (level <= 0 || !p.isInWater()) { return; } - int level = getLevel(p); if (!isDeepEnough(p, level)) { return; } @@ -125,13 +129,7 @@ public void onTick() { applyDepthBuffs(p, level); awardDepthXp(p, now); getPlayer(p).getData().addStat("seaborne.pressure-diver.deep-blocks-mined", 1); - }; - - if (J.isFoliaThreading()) { - J.runEntity(p, apply); - } else { - apply.run(); - } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java index 15c11ce04..3052a7188 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java @@ -22,11 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -82,16 +80,13 @@ public void onTick() { continue; } - Runnable apply = () -> { + withPlayerThread(player, () -> { if (!player.isOnline()) { return; } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - return; - } - - if (!player.isInWater() || !hasAdaptation(player)) { + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { return; } @@ -99,15 +94,9 @@ public void onTick() { return; } - player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, getLevel(player))); + player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, level)); getPlayer(player).getData().addStat("seaborne.speed.blocks-swum", 1); - }; - - if (J.isFoliaThreading()) { - J.runEntity(player, apply); - } else { - apply.run(); - } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java index cf6b72c6d..73133b8e1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java @@ -22,19 +22,18 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; +import org.bukkit.FluidCollisionMode; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; -import org.bukkit.FluidCollisionMode; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -170,76 +169,78 @@ public void on(PlayerAnimationEvent e) { } private void tryDash(Player p, TriggerType triggerType) { - if (!hasAdaptation(p) || p.hasCooldown(Material.HEART_OF_THE_SEA)) { - return; - } - - if (triggerType == TriggerType.SNEAK && !getConfig().enableSneakTrigger) { - return; - } - - if (triggerType == TriggerType.ATTACK) { - if (!getConfig().enableAttackTrigger) { + withAdaptedPlayer(p, () -> { + if (p.hasCooldown(Material.HEART_OF_THE_SEA)) { return; } - if (getConfig().attackTriggerRequiresSneak && !p.isSneaking()) { + if (triggerType == TriggerType.SNEAK && !getConfig().enableSneakTrigger) { return; } - if (getConfig().attackTriggerWaterOnly && !isInWaterDashState(p)) { - return; + if (triggerType == TriggerType.ATTACK) { + if (!getConfig().enableAttackTrigger) { + return; + } + + if (getConfig().attackTriggerRequiresSneak && !p.isSneaking()) { + return; + } + + if (getConfig().attackTriggerWaterOnly && !isInWaterDashState(p)) { + return; + } } - } - if (!isDashEnvironmentValid(p)) { - return; - } + if (!isDashEnvironmentValid(p)) { + return; + } - int level = getLevel(p); - Vector direction = resolveDashDirection(p); - if (direction.lengthSquared() <= 0.000001) { - return; - } + int level = getActiveLevel(p); + Vector direction = resolveDashDirection(p); + if (direction.lengthSquared() <= 0.000001) { + return; + } - if (isBlockedAhead(p, direction)) { - return; - } + if (isBlockedAhead(p, direction)) { + return; + } - boolean wasSwimming = p.isSwimming(); - org.bukkit.Location target = null; - org.bukkit.Location origin = null; + boolean wasSwimming = p.isSwimming(); + org.bukkit.Location target; + org.bukkit.Location origin = null; - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.SPLASH, p.getLocation().add(0, 1, 0), 20, 0.25, 0.35, 0.25, 0.08); - } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SPLASH, p.getLocation().add(0, 1, 0), 20, 0.25, 0.35, 0.25, 0.08); + } - if (getConfig().useVelocityDash) { - applyVelocityDash(p, direction, level); - target = p.getLocation().clone().add(direction.clone().multiply(Math.max(0.35, getDashDistance(level) * 0.35))); - } else { - origin = p.getLocation().clone(); - target = findSafeDashTarget(p, getDashDistance(level)); - if (target == null) { - return; + if (getConfig().useVelocityDash) { + applyVelocityDash(p, direction, level); + target = p.getLocation().clone().add(direction.clone().multiply(Math.max(0.35, getDashDistance(level) * 0.35))); + } else { + origin = p.getLocation().clone(); + target = findSafeDashTarget(p, getDashDistance(level)); + if (target == null) { + return; + } + J.teleport(p, target); + applyDashMomentum(p, origin, target); } - J.teleport(p, target); - applyDashMomentum(p, origin, target); - } - preserveSwimStateAfterDash(p, wasSwimming); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.SPLASH, target.clone().add(0, 1, 0), 30, 0.35, 0.45, 0.35, 0.08); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.BUBBLE, target.clone().add(0, 0.9, 0), 18, 0.4, 0.35, 0.4, 0.05); - } - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ITEM_TRIDENT_RIPTIDE_2, 0.75f, 1.2f); - sp.play(target, Sound.ENTITY_DOLPHIN_SPLASH, 0.65f, 1.15f); - p.setCooldown(Material.HEART_OF_THE_SEA, getCooldownTicks(level)); - xp(p, getConfig().xpPerBurst); - getPlayer(p).getData().addStat("seaborne.tidecaller.dashes", 1); + preserveSwimStateAfterDash(p, wasSwimming); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SPLASH, target.clone().add(0, 1, 0), 30, 0.35, 0.45, 0.35, 0.08); + } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.BUBBLE, target.clone().add(0, 0.9, 0), 18, 0.4, 0.35, 0.4, 0.05); + } + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ITEM_TRIDENT_RIPTIDE_2, 0.75f, 1.2f); + sp.play(target, Sound.ENTITY_DOLPHIN_SPLASH, 0.65f, 1.15f); + p.setCooldown(Material.HEART_OF_THE_SEA, getCooldownTicks(level)); + xp(p, getConfig().xpPerBurst); + getPlayer(p).getData().addStat("seaborne.tidecaller.dashes", 1); + }); } private void applyVelocityDash(Player p, Vector direction, int level) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java index b06f8853a..0c27a4d56 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java @@ -22,12 +22,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -81,28 +79,19 @@ public void onTick() { continue; } - Runnable apply = () -> { + withPlayerThread(player, () -> { if (!player.isOnline()) { return; } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - return; - } - - if (!player.isInWater() || !hasAdaptation(player)) { + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { return; } player.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 62, 1, false, false)); getPlayer(player).getData().addStat("seaborne.turtles-mining.blocks-underwater", 1); - }; - - if (J.isFoliaThreading()) { - J.runEntity(player, apply); - } else { - apply.run(); - } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java index 84d2fe5d6..40af171c9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java @@ -22,11 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -72,28 +70,19 @@ public void onTick() { continue; } - Runnable apply = () -> { + withPlayerThread(player, () -> { if (!player.isOnline()) { return; } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - return; - } - - if (!player.isInWater() || !hasAdaptation(player)) { + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { return; } player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 62, 0, false, false)); getPlayer(player).getData().addStat("seaborne.turtles-vision.time-underwater", 1); - }; - - if (J.isFoliaThreading()) { - J.runEntity(player, apply); - } else { - apply.run(); - } + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java index 17b424a3f..46816c996 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java @@ -4,10 +4,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; @@ -69,12 +68,16 @@ public void onTarget(EntityTargetLivingEntityEvent event) { if (target == null || target.getType() != EntityType.PLAYER || event.getEntityType() != EntityType.ENDERMAN - || !(event.getTarget() instanceof Player player) - || !hasAdaptation(player)) { + || !(event.getTarget() instanceof Player player)) { + return; + } + + int level = getActiveLevel(player); + if (level <= 0) { return; } - if (getLevel(player) > 1 || player.isSneaking()) { + if (level > 1 || player.isSneaking()) { event.setCancelled(true); getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); } @@ -83,11 +86,12 @@ public void onTarget(EntityTargetLivingEntityEvent event) { @ReflectiveHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onTarget(EndermanAttackPlayerEvent event) { var player = event.getPlayer(); - if (!hasAdaptation(player)) { + int level = getActiveLevel(player); + if (level <= 0) { return; } - if (getLevel(player) > 1 || player.isSneaking()) { + if (level > 1 || player.isSneaking()) { event.setCancelled(true); getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java index 80d0b98bf..df7b153d6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.stealth; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; @@ -25,13 +24,15 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.IAttribute; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.attribute.AttributeModifier; @@ -42,11 +43,6 @@ import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.scheduling.J; - public class StealthGhostArmor extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-ghost-armor".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:ghost-armor"); @@ -102,7 +98,7 @@ public void onTick() { Player p = adaptPlayer.getPlayer(); var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { attribute.removeModifier(MODIFIER, MODIFIER_KEY); continue; } @@ -125,10 +121,7 @@ public void onTick() { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof Player p && hasAdaptation(p) && !e.isCancelled() && e.getDamage() > 0) { + if (e.getEntity() instanceof Player p && hasActiveAdaptation(p) && e.getDamage() > 0) { // Check if 2.5 * e.getDamage() is greater than 10 if so just set it to 10 otherwise use the value of 2.5 * e.getDamage() int damageXP = (int) Math.min(10, 2.5 * e.getDamage()); xp(p,damageXP ); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index d771b4dd7..e96259a27 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -23,25 +23,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; -import org.bukkit.World; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Mob; -import org.bukkit.entity.Player; +import org.bukkit.*; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -60,24 +50,15 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; public class StealthShadowDecoy extends SimpleAdaptation { private static final PacketDecoyBridge PACKET_DECOY = PacketDecoyBridge.create(); - private final Map cooldowns = new HashMap<>(); - private final Map activeDecoys = new HashMap<>(); - private final Map anchorOwners = new HashMap<>(); - private final Map ownerEquipmentMaskSync = new HashMap<>(); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map activeDecoys = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map anchorOwners = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerEquipmentMaskSync = new java.util.concurrent.ConcurrentHashMap<>(); public StealthShadowDecoy() { super("stealth-shadow-decoy"); @@ -206,11 +187,15 @@ private void reactToDecoyHit(DecoyState state, ArmorStand stand, LivingEntity at @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(PlayerToggleSneakEvent e) { Player p = e.getPlayer(); - if (e.isSneaking() || !hasAdaptation(p)) { + if (e.isSneaking()) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { return; } - int level = getLevel(p); long now = System.currentTimeMillis(); if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { return; @@ -504,7 +489,7 @@ private PacketPlayerDecoy(PacketDecoyBridge bridge, World world, UUID profileId, this.entityId = entityId; this.nmsEntity = nmsEntity; this.removeTabAt = System.currentTimeMillis() + Math.max(0, tabListRemoveDelayTicks) * 50L; - this.knownViewers = new HashSet<>(); + this.knownViewers = java.util.concurrent.ConcurrentHashMap.newKeySet(); this.removedFromTab = false; this.spawnPlayerInfoPacket = null; this.spawnAddEntityPacket = null; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index 8ebdb9696..2e3eb9d1c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -22,11 +22,14 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -35,17 +38,13 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import java.util.ArrayList; -import java.util.List; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class StealthSight extends SimpleAdaptation { - private final List sneaking; + private final Set sneaking; public StealthSight() { @@ -59,7 +58,7 @@ public StealthSight() { setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); setMaxLevel(getConfig().maxLevel); - sneaking = new ArrayList<>(); + sneaking = ConcurrentHashMap.newKeySet(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.ENDER_EYE) .key("challenge_stealth_sight_72k") @@ -78,35 +77,53 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerToggleSneakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasAdaptation(p)) { - return; - } - sneaking.add(p); - if (!p.isSneaking()) { - sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 1, 0.99f); - p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false)); - getPlayer(p).getData().addStat("stealth.sight.time-in-darkness", 1); - } else { + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + sneaking.remove(id); + p.removePotionEffect(PotionEffectType.NIGHT_VISION); + return; + } + + if (e.isSneaking()) { + sneaking.add(id); + sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 1, 0.99f); + p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false)); + getPlayer(p).getData().addStat("stealth.sight.time-in-darkness", 1); + return; + } + + sneaking.remove(id); p.removePotionEffect(PotionEffectType.NIGHT_VISION); - } + }); } @Override public void onTick() { - List toRemove = new ArrayList<>(); - for (Player p : sneaking) { - if (hasAdaptation(p) && !p.isSneaking()) { - toRemove.add(p); - J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); + Set snapshot = new HashSet<>(sneaking); + for (UUID id : snapshot) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + sneaking.remove(id); + continue; + } + + Runnable check = () -> { + if (getActiveLevel(p, Player::isSneaking) <= 0) { + sneaking.remove(id); + J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); + } + }; + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, check); + } else { + check.run(); } } - sneaking.removeAll(toRemove); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java index 2d36340df..bb8a92d8e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java @@ -24,22 +24,17 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Mob; -import org.bukkit.entity.Player; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -50,17 +45,12 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class StealthSilentStep extends SimpleAdaptation { - private final Map dimmed = new HashMap<>(); - private final Map> recentBackstabs = new HashMap<>(); - private final Map> threatGlows = new HashMap<>(); + private final Map dimmed = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map> recentBackstabs = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map> threatGlows = new java.util.concurrent.ConcurrentHashMap<>(); public StealthSilentStep() { super("stealth-silent-step"); @@ -112,7 +102,7 @@ public void on(EntityTargetLivingEntityEvent e) { return; } - if (!hasAdaptation(p) || !p.isSneaking()) { + if (getActiveLevel(p, Player::isSneaking) <= 0) { return; } @@ -128,16 +118,14 @@ public void on(EntityTargetLivingEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerMoveEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { return; } - double radius = getStealthRadius(getLevel(p)); + double radius = getStealthRadius(level); for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { if (!(entity instanceof Mob mob)) { continue; @@ -156,28 +144,19 @@ public void on(PlayerMoveEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player attacker) || !hasAdaptation(attacker)) { - return; - } - - if (!(e.getEntity() instanceof LivingEntity target)) { - return; - } - - if (target instanceof Player victim) { - if (!canPVP(attacker, victim.getLocation())) { - return; - } - } else if (!canPVE(attacker, target.getLocation())) { + var combat = resolveMeleeContext(e); + if (combat == null) { return; } + Player attacker = combat.attacker(); + LivingEntity target = combat.target(); boolean unseen = attacker.hasPotionEffect(PotionEffectType.INVISIBILITY) || !isLookingAt(target, attacker); if (target == attacker || !unseen) { return; } - int level = getLevel(attacker); + int level = combat.level(); double multiplier = (target instanceof Player) ? getPlayerBackstabMultiplier(level) : getMobBackstabMultiplier(level); e.setDamage(e.getDamage() * multiplier); xp(attacker, e.getDamage() * getConfig().xpPerBonusDamage); @@ -198,13 +177,13 @@ public void on(EntityDamageByEntityEvent e) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking()) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { clearDimming(p); clearThreatGlows(p); continue; } - int level = getLevel(p); p.setFallDistance(Math.min(p.getFallDistance(), getConfig().maxSilentFallDistance)); ThreatSnapshot threatSnapshot = collectThreatSnapshot(p, level); if (threatSnapshot.canDetect.isEmpty()) { @@ -271,7 +250,7 @@ private void updateThreatGlows(Player p, ThreatSnapshot snapshot) { } UUID viewerId = p.getUniqueId(); - Map active = threatGlows.computeIfAbsent(viewerId, k -> new HashMap<>()); + Map active = threatGlows.computeIfAbsent(viewerId, k -> new java.util.concurrent.ConcurrentHashMap<>()); List stale = new ArrayList<>(); for (UUID entityId : active.keySet()) { @@ -508,9 +487,9 @@ private enum ThreatLevel { } private static class ThreatSnapshot { - private final Map threats = new HashMap<>(); - private final Map entities = new HashMap<>(); - private final Map canDetect = new HashMap<>(); + private final Map threats = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map entities = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map canDetect = new java.util.concurrent.ConcurrentHashMap<>(); private void add(Entity entity, ThreatLevel level) { if (entity == null || level == ThreatLevel.NONE) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index fc7c86af9..e4c2f8410 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -17,18 +17,20 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.stealth; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.inventorygui.Inventories; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Entity; @@ -44,13 +46,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.Inventories; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; - public class StealthSnatch extends SimpleAdaptation { private final Set holds; @@ -93,11 +88,8 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerToggleSneakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return; } if (!canAccessChest(p, p.getLocation())) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index 8bf32abb8..8e9f17d1c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -17,38 +17,31 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.stealth; -import art.arcane.adapt.Adapt; -import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; -import org.bukkit.GameMode; -import org.bukkit.Input; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.math.VelocitySpeed; -import art.arcane.adapt.util.common.scheduling.J; - public class StealthSpeed extends SimpleAdaptation { private static final Sound DEFAULT_ACTIVATION_SOUND = Sound.PARTICLE_SOUL_ESCAPE; private final Map states; @@ -63,7 +56,7 @@ public StealthSpeed() { setInterval(getConfig().setInterval); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - states = new HashMap<>(); + states = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.LEATHER_BOOTS) .key("challenge_stealth_speed_5k") @@ -162,7 +155,7 @@ private void clearBoost(Player p, RuntimeState state) { } private boolean isEligible(Player p) { - if (!hasAdaptation(p)) { + if (!hasActiveAdaptation(p)) { return false; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java index 089371c6f..7da9f94b1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java @@ -23,16 +23,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; @@ -42,18 +42,14 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; -import art.arcane.adapt.util.reflect.registries.Particles; - public class SwordsBloodyBlade extends SimpleAdaptation { - private final Map cooldowns; - private final Set bleedingEntities = new HashSet<>(); - private final Map bleedSource = new HashMap<>(); + private final Map cooldowns; + private final Set bleedingEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map bleedSource = new java.util.concurrent.ConcurrentHashMap<>(); public SwordsBloodyBlade() { super("sword-bloody-blade"); @@ -66,7 +62,7 @@ public SwordsBloodyBlade() { setInterval(5534); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.IRON_SWORD) .key("challenge_swords_bloody_500") @@ -105,20 +101,13 @@ public long getDurationOfEffect(int level) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getDamager() instanceof Player p && hasAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { - Long cooldown = cooldowns.get(p); + if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown > System.currentTimeMillis()) return; Entity victim = e.getEntity(); - cooldowns.put(p, System.currentTimeMillis() + getCooldown(getLevel(p))); - if (victim instanceof Player pvic) { - if (!canPVP(p, pvic.getLocation())) return; - } else { - if (!canPVE(p, victim.getLocation())) return; - } + cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); + if (!canDamageTarget(p, victim)) return; if (areParticlesEnabled()) { BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getConfig().damagePerBleedProc, l) : new BleedEffect(Adapt.instance.adaptEffectManager); blood.setEntity(victim); @@ -143,7 +132,7 @@ public void on(EntityDamageByEntityEvent e) { blood.start(); } bleedingEntities.add(victim.getUniqueId()); - bleedSource.put(victim.getUniqueId(), p); + bleedSource.put(victim.getUniqueId(), p.getUniqueId()); getPlayer(p).getData().addStat("swords.bloody-blade.bleed-damage", 1); } @@ -153,7 +142,8 @@ public void on(EntityDamageByEntityEvent e) { public void on(EntityDeathEvent e) { UUID victimId = e.getEntity().getUniqueId(); if (bleedingEntities.remove(victimId)) { - Player source = bleedSource.remove(victimId); + UUID sourceId = bleedSource.remove(victimId); + Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); if (source != null && source.isOnline()) { getPlayer(source).getData().addStat("swords.bloody-blade.bleed-kills", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java index 55d8b8c86..dbad1be17 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java @@ -24,14 +24,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -46,8 +45,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; -import art.arcane.adapt.util.reflect.registries.Particles; - public class SwordsCrimsonCyclone extends SimpleAdaptation { public SwordsCrimsonCyclone() { super("sword-crimson-cyclone"); @@ -97,28 +94,19 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity primaryTarget)) { - return; - } - - if (!hasAdaptation(p)) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!isSword(hand) || p.hasCooldown(hand.getType()) || !isCritTrigger(p)) { + var combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { return; } - if (primaryTarget instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, primaryTarget.getLocation())) { + Player p = combat.attacker(); + LivingEntity primaryTarget = combat.target(); + ItemStack hand = combat.mainHand(); + if (!isCritTrigger(p)) { return; } - int level = getLevel(p); + int level = combat.level(); int hungerCost = getHungerCost(level); if (p.getFoodLevel() < hungerCost) { return; @@ -150,11 +138,7 @@ public void on(EntityDamageByEntityEvent e) { continue; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - continue; - } - } else if (!canPVE(p, target.getLocation())) { + if (!canDamageTarget(p, target)) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java index 3f1c8ccb0..948359773 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -76,10 +75,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p)) { + var combat = resolveMeleeContext(e); + if (combat == null) { return; } + Player p = combat.attacker(); ItemStack main = p.getInventory().getItemInMainHand(); ItemStack off = p.getInventory().getItemInOffHand(); if (!isItem(main) || !isItem(off) || main.getType() == Material.AIR || off.getType() == Material.AIR) { @@ -87,7 +88,7 @@ public void on(EntityDamageByEntityEvent e) { } boolean sameWeapon = main.getType() == off.getType(); - double multiplier = sameWeapon ? getSameMultiplier(getLevel(p)) : getMixedMultiplier(getLevel(p)); + double multiplier = sameWeapon ? getSameMultiplier(combat.level()) : getMixedMultiplier(combat.level()); double originalDamage = e.getDamage(); e.setDamage(originalDamage * multiplier); double bonusDamage = e.getDamage() - originalDamage; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java index 2cfebca37..e5fe91546 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java @@ -23,12 +23,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.attribute.Attribute; @@ -87,17 +86,13 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p) || !isSword(p.getInventory().getItemInMainHand()) || !(e.getEntity() instanceof LivingEntity target)) { + var combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { return; } - if (target instanceof Player victim) { - if (!canPVP(p, victim.getLocation())) { - return; - } - } else if (!canPVE(p, target.getLocation())) { - return; - } + Player p = combat.attacker(); + LivingEntity target = combat.target(); double maxHealth = getMaxHealth(target); if (maxHealth <= 0) { @@ -105,12 +100,12 @@ public void on(EntityDamageByEntityEvent e) { } double hpPercent = Math.max(0, target.getHealth() / maxHealth); - double threshold = getThreshold(getLevel(p)); + double threshold = getThreshold(combat.level()); if (hpPercent > threshold) { return; } - double multiplier = 1D + getBonusDamage(getLevel(p)); + double multiplier = 1D + getBonusDamage(combat.level()); e.setDamage(e.getDamage() * multiplier); xp(p, e.getDamage() * getConfig().xpPerBuffedDamage); getPlayer(p).getData().addStat("swords.executioners-edge.executions", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index 8555ff6dd..10cd9e425 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -17,19 +17,22 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.sword; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.math.RNG; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.data.Cuboid; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.volmlib.util.data.Cuboid; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -46,13 +49,6 @@ import java.util.concurrent.ThreadLocalRandom; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.reflect.registries.Particles; - public class SwordsMachete extends SimpleAdaptation { public SwordsMachete() { super("sword-machete"); @@ -104,7 +100,7 @@ public void on(PlayerInteractEvent e) { int dmg = 0; ItemStack is = e.getItem(); if (isSword(is)) { - if (is != null && !p.hasCooldown(is.getType()) && hasAdaptation(p)) { + if (is != null && !p.hasCooldown(is.getType()) && hasActiveAdaptation(p)) { Location ctr = p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(2.25)).add(0, -0.5, 0); int lvl = getLevel(p); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java index bfd96281e..24fe5a7b9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java @@ -23,16 +23,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; @@ -43,16 +43,14 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.potion.PotionEffectType; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; public class SwordsPoisonedBlade extends SimpleAdaptation { - private final Map cooldowns; - private final Set poisonedEntities = new HashSet<>(); - private final Map poisonSource = new HashMap<>(); + private final Map cooldowns; + private final Set poisonedEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map poisonSource = new java.util.concurrent.ConcurrentHashMap<>(); public SwordsPoisonedBlade() { super("sword-poison-blade"); @@ -65,7 +63,7 @@ public SwordsPoisonedBlade() { setInterval(4984); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.SPIDER_EYE) .key("challenge_swords_poison_500") @@ -103,17 +101,14 @@ public long getDurationOfEffect(int level) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getDamager() instanceof Player p && hasAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { - Long cooldown = cooldowns.get(p); + if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown > System.currentTimeMillis()) return; Entity victim = e.getEntity(); - cooldowns.put(p, System.currentTimeMillis() + getCooldown(getLevel(p))); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); + if (!canDamageTarget(p, victim)) return; if (victim instanceof Player pvic) { - if (!canPVP(p, pvic.getLocation())) return; BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); blood.setEntity(pvic); blood.material = Material.LARGE_FERN; @@ -124,7 +119,6 @@ public void on(EntityDamageByEntityEvent e) { blood.start(); addPotionStacks(pvic, PotionEffectType.POISON, 2, 50 * getLevel(p), true); } else { - if (!canPVE(p, victim.getLocation())) return; BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, 1, l) : new BleedEffect(Adapt.instance.adaptEffectManager); blood.setEntity(victim); blood.material = Material.LARGE_FERN; @@ -135,7 +129,7 @@ public void on(EntityDamageByEntityEvent e) { blood.start(); } poisonedEntities.add(victim.getUniqueId()); - poisonSource.put(victim.getUniqueId(), p); + poisonSource.put(victim.getUniqueId(), p.getUniqueId()); getPlayer(p).getData().addStat("swords.poisoned-blade.poison-applied", 1); } @@ -145,7 +139,8 @@ public void on(EntityDamageByEntityEvent e) { public void on(EntityDeathEvent e) { UUID victimId = e.getEntity().getUniqueId(); if (poisonedEntities.remove(victimId)) { - Player source = poisonSource.remove(victimId); + UUID sourceId = poisonSource.remove(victimId); + Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); if (source != null && source.isOnline()) { getPlayer(source).getData().addStat("swords.poisoned-blade.poison-kills", 1); } @@ -189,4 +184,3 @@ protected static class Config { double costFactor = 0.325; } } - diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java index b59fb1a55..bb98ffe38 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java @@ -23,32 +23,27 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; -import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.inventorygui.Window; - public class SwordsRiposteWindow extends SimpleAdaptation { - private final Map riposteUntil = new HashMap<>(); + private final Map riposteUntil = new java.util.concurrent.ConcurrentHashMap<>(); public SwordsRiposteWindow() { super("sword-riposte-window"); @@ -106,7 +101,7 @@ public void on(EntityDamageByEntityEvent e) { armRiposte(defender); } - if (!(e.getDamager() instanceof Player attacker) || !hasAdaptation(attacker) || !isSword(attacker.getInventory().getItemInMainHand())) { + if (!(e.getDamager() instanceof Player attacker)) { return; } @@ -116,19 +111,13 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (!(e.getEntity() instanceof LivingEntity target)) { - return; - } - - if (target instanceof Player victim) { - if (!canPVP(attacker, victim.getLocation())) { - return; - } - } else if (!canPVE(attacker, target.getLocation())) { + var combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { return; } - e.setDamage(e.getDamage() * (1D + getDamageBonus(getLevel(attacker)))); + attacker = combat.attacker(); + e.setDamage(e.getDamage() * (1D + getDamageBonus(combat.level()))); riposteUntil.remove(attacker.getUniqueId()); if (areParticlesEnabled()) { attacker.getWorld().spawnParticle(Particle.SWEEP_ATTACK, attacker.getLocation().add(0, 1, 0), 1, 0, 0, 0, 0); @@ -158,11 +147,11 @@ public void on(EntityDamageByEntityEvent e) { private void armRiposte(Player defender) { boolean hasShield = defender.getInventory().getItemInOffHand().getType() == Material.SHIELD || defender.getInventory().getItemInMainHand().getType() == Material.SHIELD; - if (!hasAdaptation(defender) || !defender.isBlocking() || !hasShield) { + int level = getActiveLevel(defender); + if (level <= 0 || !defender.isBlocking() || !hasShield) { return; } - int level = getLevel(defender); riposteUntil.put(defender.getUniqueId(), System.currentTimeMillis() + getWindowMillis(level)); SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.6f, 0.9f); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index 3354a7b08..ea14ac8fb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -22,14 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -84,7 +83,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.isCancelled() || e.getHand() != EquipmentSlot.HAND) { + if (e.getHand() != EquipmentSlot.HAND) { return; } @@ -94,11 +93,11 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || !p.isSneaking() || p.getInventory().getItemInMainHand().getType() != Material.LEAD || p.hasCooldown(Material.LEAD)) { + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.LEAD || p.hasCooldown(Material.LEAD)) { return; } - int level = getLevel(p); Tameable tameable = findNearestOwnedTameable(p, getSearchRadius(level)); if (tameable == null) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java index 3ae5bf97f..cad1997e9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.taming; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; @@ -25,12 +24,13 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -46,14 +46,9 @@ import org.bukkit.event.entity.EntityDeathEvent; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; - public class TamingDamage extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-damage-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-damage-boost"); @@ -106,10 +101,10 @@ public void onTick() { return; } - Map ownerLevels = new HashMap<>(); + Map ownerLevels = new java.util.concurrent.ConcurrentHashMap<>(); for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); - ownerLevels.put(owner.getUniqueId(), getLevel(owner)); + ownerLevels.put(owner.getUniqueId(), getActiveLevel(owner)); } for (World world : Bukkit.getServer().getWorlds()) { @@ -125,7 +120,7 @@ public void onTick() { private void onFoliaTick() { for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); - int level = getLevel(owner); + int level = getActiveLevel(owner); J.runEntity(owner, () -> updateNearbyOwnedTameables(owner, level)); } } @@ -153,7 +148,7 @@ public void on(EntityDeathEvent e) { && dmgEvent.getDamager() instanceof Tameable tam && tam.isTamed() && tam.getOwner() instanceof Player p - && hasAdaptation(p)) { + && hasActiveAdaptation(p)) { getPlayer(p).getData().addStat("taming.damage.pet-kills", 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java index 077434ee5..a5bfd8e0b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.taming; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; @@ -25,12 +24,13 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -42,14 +42,9 @@ import org.bukkit.entity.Tameable; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; - public class TamingHealthBoost extends SimpleAdaptation { private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-health-boost".getBytes()); private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-health-boost"); @@ -93,7 +88,7 @@ public void onTick() { return; } - Map ownerStates = new HashMap<>(); + Map ownerStates = new java.util.concurrent.ConcurrentHashMap<>(); for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); ownerStates.put(owner.getUniqueId(), new OwnerState(adaptPlayer, owner, getLevel(owner))); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java index 3591e2b27..c8453a923 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java @@ -17,7 +17,6 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.taming; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; @@ -25,11 +24,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -40,19 +41,13 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.potion.PotionEffectType; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.reflect.registries.Particles; - import static org.bukkit.Particle.HEART; public class TamingHealthRegeneration extends SimpleAdaptation { - private final Map lastDamage = new HashMap<>(); + private final Map lastDamage = new java.util.concurrent.ConcurrentHashMap<>(); public TamingHealthRegeneration() { super("tame-health-regeneration"); @@ -83,12 +78,13 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity() instanceof Tameable tam - && tam.getOwner() instanceof Player p - && hasAdaptation(p)) { + && tam.getOwner() instanceof Player p) { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + if (lastDamage.containsKey(tam.getUniqueId())) { Adapt.verbose("Tamed Entity " + tam.getUniqueId() + " last damaged " + (M.ms() - lastDamage.get(tam.getUniqueId())) + "ms ago"); return; @@ -97,18 +93,15 @@ && hasAdaptation(p)) { double mh = attribute == null ? tam.getHealth() : attribute.getValue(); if (tam.isTamed() && tam.getOwner() instanceof Player && tam.getHealth() < mh) { Adapt.verbose("Successfully healed tamed entity " + tam.getUniqueId()); - int level = getLevel(p); - if (level > 0) { - Adapt.verbose("[PRE] Current Health: " + tam.getHealth() + " Max Health: " + mh); - tam.addPotionEffect(PotionEffectType.REGENERATION.createEffect(25 * getLevel(p), 3)); - getPlayer(p).getData().addStat("taming.health-regen.health-regened", 1); - - if (areParticlesEnabled()) { - Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " with particles"); - tam.getWorld().spawnParticle(HEART, tam.getLocation().add(0, 1, 0), 2 * p.getLevel()); - } else { - Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " without particles"); - } + Adapt.verbose("[PRE] Current Health: " + tam.getHealth() + " Max Health: " + mh); + tam.addPotionEffect(PotionEffectType.REGENERATION.createEffect(25 * level, 3)); + getPlayer(p).getData().addStat("taming.health-regen.health-regened", 1); + + if (areParticlesEnabled()) { + Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " with particles"); + tam.getWorld().spawnParticle(HEART, tam.getLocation().add(0, 1, 0), 2 * p.getLevel()); + } else { + Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " without particles"); } } lastDamage.put(e.getEntity().getUniqueId(), M.ms()); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java index f625ed161..5c9e09c75 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java @@ -23,19 +23,15 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Pig; -import org.bukkit.entity.Player; -import org.bukkit.entity.Strider; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -44,12 +40,11 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class TamingMountedTactics extends SimpleAdaptation { - private final Map lastMountedLocation = new HashMap<>(); + private final Map lastMountedLocation = new java.util.concurrent.ConcurrentHashMap<>(); public TamingMountedTactics() { super("tame-mounted-tactics"); @@ -92,11 +87,11 @@ public void addStats(int level, Element v) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { continue; } - int level = getLevel(p); Entity vehicle = p.getVehicle(); if (vehicle != null) { Location last = lastMountedLocation.get(p.getUniqueId()); @@ -161,28 +156,30 @@ private boolean hasForwardInput(Player p) { public void on(EntityDeathEvent e) { if (e.getEntity().getKiller() instanceof Player p && p.getVehicle() != null - && hasAdaptation(p)) { + && hasActiveAdaptation(p)) { getPlayer(p).getData().addStat("taming.mounted-tactics.mounted-kills", 1); } } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player attacker && hasAdaptation(attacker) && attacker.getVehicle() != null) { - if (e.getEntity() instanceof Player victim) { - if (!canPVP(attacker, victim.getLocation())) { + if (e.getDamager() instanceof Player attacker && attacker.getVehicle() != null) { + int level = getActiveLevel(attacker); + if (level > 0) { + if (!canDamageTarget(attacker, e.getEntity())) { return; } - } else if (!canPVE(attacker, e.getEntity().getLocation())) { - return; - } - e.setDamage(e.getDamage() * (1D + getMountedDamageBonus(getLevel(attacker)))); - xp(attacker, e.getDamage() * getConfig().xpPerMountedDamage); + e.setDamage(e.getDamage() * (1D + getMountedDamageBonus(level))); + xp(attacker, e.getDamage() * getConfig().xpPerMountedDamage); + } } - if (e.getEntity() instanceof Player defender && hasAdaptation(defender) && defender.getVehicle() != null) { - e.setDamage(e.getDamage() * (1D - getMountedDamageReduction(getLevel(defender)))); + if (e.getEntity() instanceof Player defender && defender.getVehicle() != null) { + int level = getActiveLevel(defender); + if (level > 0) { + e.setDamage(e.getDamage() * (1D - getMountedDamageReduction(level))); + } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index 3cd9df132..28161fdfa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -23,13 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java index 9a9b6fda6..af8975051 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java @@ -22,13 +22,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -78,15 +77,15 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Tameable tameable) || !tameable.isTamed() || !(tameable.getOwner() instanceof Player owner) || !hasAdaptation(owner)) { + if (!(e.getEntity() instanceof Tameable tameable) || !tameable.isTamed() || !(tameable.getOwner() instanceof Player owner)) { return; } - if (!canPVE(owner, tameable.getLocation())) { + int level = getActiveDamageLevel(owner, tameable); + if (level <= 0) { return; } - int level = getLevel(owner); double redirect = e.getDamage() * getRedirectPercent(level); if (redirect <= 0) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java index 895050979..fffac6b7b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java @@ -24,12 +24,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -47,12 +47,8 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; public class TragoulBloodPact extends SimpleAdaptation { @@ -66,9 +62,9 @@ public class TragoulBloodPact extends SimpleAdaptation PotionEffectType.NIGHT_VISION }; - private final Map procCooldowns = new HashMap<>(); - private final Map lowHealthProcs = new HashMap<>(); - private final Map speedBursts = new HashMap<>(); + private final Map procCooldowns = new ConcurrentHashMap<>(); + private final Map lowHealthProcs = new ConcurrentHashMap<>(); + private final Map speedBursts = new ConcurrentHashMap<>(); public TragoulBloodPact() { super("tragoul-blood-pact"); @@ -130,48 +126,52 @@ public void on(PlayerDeathEvent e) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof Player p)) { return; } - int level = getLevel(p); - if (level <= 0 || e.getFinalDamage() < getMinTriggerDamage()) { - return; - } + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || e.getFinalDamage() < getMinTriggerDamage()) { + return; + } - long now = System.currentTimeMillis(); - long until = procCooldowns.getOrDefault(p.getUniqueId(), 0L); - if (now < until) { - return; - } + long now = System.currentTimeMillis(); + long until = procCooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < until) { + return; + } - if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { - return; - } + if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { + return; + } - procCooldowns.put(p.getUniqueId(), now + getProcCooldownMillis(level)); - getPlayer(p).getData().addStat("tragoul.blood-pact.health-sacrificed", (int) e.getFinalDamage()); - if (p.getHealth() - e.getFinalDamage() <= 6.0) { - lowHealthProcs.put(p.getUniqueId(), true); - } - applyRandomBuffs(p, level, e.getFinalDamage()); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, p.getLocation().add(0, 1.0, 0), 22, 0.28, 0.42, 0.28, 0.02); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.62f, 1.25f); - xp(p, getConfig().xpPerProc); + procCooldowns.put(p.getUniqueId(), now + getProcCooldownMillis(level)); + getPlayer(p).getData().addStat("tragoul.blood-pact.health-sacrificed", (int) e.getFinalDamage()); + if (p.getHealth() - e.getFinalDamage() <= 6.0) { + lowHealthProcs.put(p.getUniqueId(), true); + } + applyRandomBuffs(p, level, e.getFinalDamage()); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, p.getLocation().add(0, 1.0, 0), 22, 0.28, 0.42, 0.28, 0.02); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.62f, 1.25f); + xp(p, getConfig().xpPerProc); + }); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(EntityDeathEvent e) { if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent) { - if (dmgEvent.getDamager() instanceof Player p && hasAdaptation(p)) { - if (p.hasPotionEffect(PotionEffectType.ABSORPTION) || p.hasPotionEffect(PotionEffectType.RESISTANCE)) { - getPlayer(p).getData().addStat("tragoul.blood-pact.empowered-kills", 1); - if (lowHealthProcs.getOrDefault(p.getUniqueId(), false) && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_pact_all_in")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_pact_all_in"); + if (dmgEvent.getDamager() instanceof Player p) { + withAdaptedPlayer(p, () -> { + if (p.hasPotionEffect(PotionEffectType.ABSORPTION) || p.hasPotionEffect(PotionEffectType.RESISTANCE)) { + getPlayer(p).getData().addStat("tragoul.blood-pact.empowered-kills", 1); + if (lowHealthProcs.getOrDefault(p.getUniqueId(), false) && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_pact_all_in")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_pact_all_in"); + } } - } + }); } } } @@ -263,30 +263,36 @@ public void onTick() { private void applySpeedBursts(long now) { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - UUID id = p.getUniqueId(); - SpeedBurst burst = speedBursts.get(id); - if (burst == null) { + if (p == null || !p.isOnline()) { continue; } - if (burst.expiresAt <= now) { - invalidateSpeedBurst(p, burst, false); - speedBursts.remove(id); - continue; - } + withPlayerThread(p, () -> { + UUID id = p.getUniqueId(); + SpeedBurst burst = speedBursts.get(id); + if (burst == null) { + return; + } - if (!isVelocityEligible(p)) { - invalidateSpeedBurst(p, burst, true); - continue; - } + if (burst.expiresAt <= now) { + invalidateSpeedBurst(p, burst, false); + speedBursts.remove(id); + return; + } - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeSpeedBurst(p, burst); - continue; - } + if (!isVelocityEligible(p)) { + invalidateSpeedBurst(p, burst, true); + return; + } + + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeSpeedBurst(p, burst); + return; + } - applySpeedBurst(p, burst, input); + applySpeedBurst(p, burst, input); + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java index ec65f86b0..7791eacad 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java @@ -23,13 +23,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -48,14 +48,8 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; public class TragoulBoneHarvest extends SimpleAdaptation { @@ -69,9 +63,9 @@ public class TragoulBoneHarvest extends SimpleAdaptation bloodGlobes = new HashSet<>(); - private final Set boneGlobes = new HashSet<>(); - private final Map speedBursts = new HashMap<>(); + private final Set bloodGlobes = ConcurrentHashMap.newKeySet(); + private final Set boneGlobes = ConcurrentHashMap.newKeySet(); + private final Map speedBursts = new ConcurrentHashMap<>(); public TragoulBoneHarvest() { super("tragoul-bone-harvest"); @@ -113,38 +107,46 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(EntityDeathEvent e) { Player killer = e.getEntity().getKiller(); - if (killer == null || !hasAdaptation(killer) || !canPVE(killer, e.getEntity().getLocation())) { + if (killer == null) { return; } - int level = getLevel(killer); - ThreadLocalRandom random = ThreadLocalRandom.current(); - if (random.nextDouble() > getGlobeChance(level)) { - return; - } + withAdaptedPlayer(killer, () -> { + if (!canDamageTarget(killer, e.getEntity())) { + return; + } + + int level = getActiveLevel(killer); + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (random.nextDouble() > getGlobeChance(level)) { + return; + } - spawnGlobe(killer, e, random.nextBoolean(), level); + spawnGlobe(killer, e, random.nextBoolean(), level); + }); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(EntityPickupItemEvent e) { - if (!(e.getEntity() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getEntity() instanceof Player p)) { return; } - UUID id = e.getItem().getUniqueId(); - boolean blood = bloodGlobes.contains(id); - boolean bone = boneGlobes.contains(id); - if (!blood && !bone) { - return; - } + withAdaptedPlayer(p, e, () -> { + UUID id = e.getItem().getUniqueId(); + boolean blood = bloodGlobes.contains(id); + boolean bone = boneGlobes.contains(id); + if (!blood && !bone) { + return; + } - e.setCancelled(true); - e.getItem().remove(); - bloodGlobes.remove(id); - boneGlobes.remove(id); - applyBuff(p, blood, getLevel(p)); - getPlayer(p).getData().addStat("tragoul.bone-harvest.orbs-collected", 1); + e.setCancelled(true); + e.getItem().remove(); + bloodGlobes.remove(id); + boneGlobes.remove(id); + applyBuff(p, blood, getLevel(p)); + getPlayer(p).getData().addStat("tragoul.bone-harvest.orbs-collected", 1); + }); } @EventHandler(priority = EventPriority.MONITOR) @@ -244,30 +246,36 @@ public void onTick() { long now = System.currentTimeMillis(); for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - UUID id = p.getUniqueId(); - SpeedBurst burst = speedBursts.get(id); - if (burst == null) { - continue; - } - - if (burst.expiresAt <= now) { - invalidateSpeedBurst(p, burst, false); - speedBursts.remove(id); - continue; - } - - if (!isVelocityEligible(p)) { - invalidateSpeedBurst(p, burst, true); - continue; - } - - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeSpeedBurst(p, burst); + if (p == null || !p.isOnline()) { continue; } - applySpeedBurst(p, burst, input); + withPlayerThread(p, () -> { + UUID id = p.getUniqueId(); + SpeedBurst burst = speedBursts.get(id); + if (burst == null) { + return; + } + + if (burst.expiresAt <= now) { + invalidateSpeedBurst(p, burst, false); + speedBursts.remove(id); + return; + } + + if (!isVelocityEligible(p)) { + invalidateSpeedBurst(p, burst, true); + return; + } + + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeSpeedBurst(p, burst); + return; + } + + applySpeedBurst(p, burst, input); + }); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java index d8816ea5b..0bb592304 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java @@ -23,11 +23,10 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -39,13 +38,12 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import java.util.HashMap; import java.util.Map; - -import art.arcane.adapt.util.reflect.registries.Particles; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class TragoulGlobe extends SimpleAdaptation { - private final Map cooldowns; + private final Map cooldowns; public TragoulGlobe() { super("tragoul-globe"); @@ -58,7 +56,7 @@ public TragoulGlobe() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.GLASS) .key("challenge_tragoul_globe_1k") @@ -87,48 +85,55 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p)) { + if (!(e.getDamager() instanceof Player p)) { return; } - Long cooldownTime = cooldowns.get(p); - if (cooldownTime != null && cooldownTime + (1000 * getConfig().cooldown) > System.currentTimeMillis()) { - return; - } + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, e.getEntity())) { + return; + } + + Long cooldownTime = cooldowns.get(p.getUniqueId()); + if (cooldownTime != null && cooldownTime + (1000 * getConfig().cooldown) > System.currentTimeMillis()) { + return; + } - cooldowns.put(p, System.currentTimeMillis()); - double range = (getConfig().rangePerLevel * getLevel(p)) + getConfig().initalRange; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + double range = (getConfig().rangePerLevel * level) + getConfig().initalRange; - int entitiesCount = 0; - for (Entity entity : p.getNearbyEntities(range, range, range)) { - if (entity instanceof LivingEntity && !entity.equals(p)) { - entitiesCount++; + int entitiesCount = 0; + for (Entity entity : p.getNearbyEntities(range, range, range)) { + if (entity instanceof LivingEntity && !entity.equals(p)) { + entitiesCount++; + } } - } - if (entitiesCount <= 1) { - return; - } + if (entitiesCount <= 1) { + return; + } - double damagePerEntity = e.getDamage() / entitiesCount + (getConfig().bonusDamagePerLevel * getLevel(p)); - e.setDamage(damagePerEntity); + double damagePerEntity = e.getDamage() / entitiesCount + (getConfig().bonusDamagePerLevel * level); + e.setDamage(damagePerEntity); - int mobsSharedWith = 0; - for (Entity entity : p.getNearbyEntities(range, range, range)) { - if (entity instanceof LivingEntity && !entity.equals(p)) { - ((LivingEntity) entity).damage(damagePerEntity, p); - mobsSharedWith++; + int mobsSharedWith = 0; + for (Entity entity : p.getNearbyEntities(range, range, range)) { + if (entity instanceof LivingEntity && !entity.equals(p) && canDamageTarget(p, entity)) { + ((LivingEntity) entity).damage(damagePerEntity, p); + mobsSharedWith++; + } } - } - getPlayer(p).getData().addStat("tragoul.globe.mobs-shared-with", mobsSharedWith); - if (mobsSharedWith >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_globe_5")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_globe_5"); - } + getPlayer(p).getData().addStat("tragoul.globe.mobs-shared-with", mobsSharedWith); + if (mobsSharedWith >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_globe_5")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_globe_5"); + } - if (areParticlesEnabled()) { - J.runEntity(p, () -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); - } + if (areParticlesEnabled()) { + J.runEntity(p, () -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); + } + }); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java index c094f77e1..08326f9dc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java @@ -24,10 +24,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; @@ -38,16 +37,13 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import java.util.HashMap; import java.util.Map; - - -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.reflect.registries.Particles; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class TragoulHealing extends SimpleAdaptation { - private final Map cooldowns; - private final Map healingWindow; + private final Map cooldowns; + private final Map healingWindow; public TragoulHealing() { super("tragoul-healing"); @@ -60,8 +56,8 @@ public TragoulHealing() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); - healingWindow = new HashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + healingWindow = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.REDSTONE) .key("challenge_tragoul_healing_500") @@ -91,41 +87,47 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && hasAdaptation(p)) { - if (isOnCooldown(p)) { - return; - } - - if (!healingWindow.containsKey(p)) { - Adapt.verbose("Starting healing window for " + p.getName()); - startHealingWindow(p); - } - - if (areParticlesEnabled()) { - vfxParticleLine(p.getLocation(), e.getEntity().getLocation(), 25, Particle.WHITE_ASH); - } - - double healPercentage = getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (getLevel(p) - 1) / (getConfig().maxLevel - 1); - double healAmount = e.getDamage() * healPercentage; - Adapt.verbose("Healing " + p.getName() + " for " + healAmount + " (" + healPercentage * 100 + "% of " + e.getDamage() + " damage)"); - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_MAX_HEALTH); - p.setHealth(Math.min(attribute == null ? p.getHealth() : attribute.getValue(), p.getHealth() + healAmount)); - getPlayer(p).getData().addStat("tragoul.healing.health-stolen", (int) healAmount); - + if (e.getDamager() instanceof Player p) { + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, e.getEntity())) { + return; + } + + if (isOnCooldown(p)) { + return; + } + + if (!healingWindow.containsKey(p.getUniqueId())) { + Adapt.verbose("Starting healing window for " + p.getName()); + startHealingWindow(p); + } + + if (areParticlesEnabled()) { + vfxParticleLine(p.getLocation(), e.getEntity().getLocation(), 25, Particle.WHITE_ASH); + } + + double healPercentage = getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (level - 1) / (getConfig().maxLevel - 1); + double healAmount = e.getDamage() * healPercentage; + Adapt.verbose("Healing " + p.getName() + " for " + healAmount + " (" + healPercentage * 100 + "% of " + e.getDamage() + " damage)"); + var attribute = Version.get().getAttribute(p, Attributes.GENERIC_MAX_HEALTH); + p.setHealth(Math.min(attribute == null ? p.getHealth() : attribute.getValue(), p.getHealth() + healAmount)); + getPlayer(p).getData().addStat("tragoul.healing.health-stolen", (int) healAmount); + }); } } private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); return cooldown != null && cooldown > System.currentTimeMillis(); } private void startHealingWindow(Player p) { long currentTime = System.currentTimeMillis(); - healingWindow.put(p, currentTime + getConfig().windowDuration); + healingWindow.put(p.getUniqueId(), currentTime + getConfig().windowDuration); J.runEntity(p, () -> { - healingWindow.remove(p); - cooldowns.put(p, currentTime + getConfig().windowDuration + getConfig().cooldownDuration); + healingWindow.remove(p.getUniqueId()); + cooldowns.put(p.getUniqueId(), currentTime + getConfig().windowDuration + getConfig().cooldownDuration); }, getConfig().windowDuration / 50); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java index be3289c7e..86e128721 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java @@ -21,10 +21,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; @@ -38,11 +37,12 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class TragoulLance extends SimpleAdaptation { - private final Map cooldowns; + private final Map cooldowns; public TragoulLance() { super("tragoul-lance"); @@ -54,7 +54,7 @@ public TragoulLance() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.IRON_SWORD) .key("challenge_tragoul_lance_200") @@ -79,19 +79,26 @@ public TragoulLance() { @EventHandler (priority = EventPriority.LOWEST) public void onEntityDeath(EntityDeathEvent event) { if (event.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && hasAdaptation(p)) { - Long cooldown = cooldowns.get(p); - if (cooldown != null && cooldown + 5000 > System.currentTimeMillis()) - return; - - cooldowns.put(p, System.currentTimeMillis()); - int level = getLevel(p); - double baseSeekerRange = 5 + 4 * level; - double damageDealt = e.getDamage(); - double seekerDamage = getConfig().seekerDamageMultiplier * damageDealt; - - triggerSeeker(p, event.getEntity(), seekerDamage, level, baseSeekerRange); - getPlayer(p).getData().addStat("tragoul.lance.lance-kills", 1); + if (e.getDamager() instanceof Player p) { + withAdaptedPlayer(p, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, event.getEntity())) { + return; + } + + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + 5000 > System.currentTimeMillis()) { + return; + } + + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + double baseSeekerRange = 5 + 4 * level; + double damageDealt = e.getDamage(); + double seekerDamage = getConfig().seekerDamageMultiplier * damageDealt; + + triggerSeeker(p, event.getEntity(), seekerDamage, level, baseSeekerRange); + getPlayer(p).getData().addStat("tragoul.lance.lance-kills", 1); + }); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java index 4d7bbb3e3..4e0627dd7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java @@ -24,29 +24,28 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; +import org.bukkit.Particle; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByEntityEvent; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; -import art.arcane.adapt.util.reflect.Reflect; -import art.arcane.adapt.util.reflect.registries.Particles; - public class TragoulThorns extends SimpleAdaptation { - private final Map cooldowns; + private final Map cooldowns; public TragoulThorns() { super("tragoul-thorns"); @@ -59,7 +58,7 @@ public TragoulThorns() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - cooldowns = new HashMap<>(); + cooldowns = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CACTUS) .key("challenge_tragoul_thorns_500") @@ -97,40 +96,69 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } - if (e.getEntity() instanceof Player p && hasAdaptation(p)) { - Long cooldown = cooldowns.get(p); - if (cooldown != null && cooldown + 1500 > System.currentTimeMillis()) - return; + if (e.getEntity() instanceof Player p) { + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } - cooldowns.put(p, System.currentTimeMillis()); + UUID id = p.getUniqueId(); + Long cooldown = cooldowns.get(id); + if (cooldown != null && cooldown + 1500 > System.currentTimeMillis()) { + return; + } - LivingEntity le = null; + cooldowns.put(id, System.currentTimeMillis()); - if (e.getDamager() instanceof LivingEntity) { - le = (LivingEntity) e.getDamager(); - } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity) { - le = (LivingEntity) projectile.getShooter(); - } + LivingEntity le = null; - if (le != null) { - if (areParticlesEnabled()) { - BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood - blood.setEntity(le); - blood.height = -1; - blood.iterations = 1; - blood.start(); + if (e.getDamager() instanceof LivingEntity) { + le = (LivingEntity) e.getDamager(); + } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity) { + le = (LivingEntity) projectile.getShooter(); } - double reflectedDamage = getConfig().damageMultiplierPerLevel * getLevel(p); - double healthBefore = le.getHealth(); - le.damage(reflectedDamage, p); - getPlayer(p).getData().addStat("tragoul.thorns.damage-reflected", (int) reflectedDamage); - if (healthBefore <= reflectedDamage && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_thorns_kill")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_thorns_kill"); + + if (le != null && canDamageTarget(p, le)) { + if (areParticlesEnabled()) { + playThornsParticles(le); + } + double reflectedDamage = getConfig().damageMultiplierPerLevel * level; + double healthBefore = le.getHealth(); + le.damage(reflectedDamage, p); + getPlayer(p).getData().addStat("tragoul.thorns.damage-reflected", (int) reflectedDamage); + if (healthBefore <= reflectedDamage && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_thorns_kill")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_thorns_kill"); + } } + }); + } + } + + private void playThornsParticles(LivingEntity target) { + Runnable burst = () -> { + if (target == null || !target.isValid()) { + return; } + + target.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR, target.getLocation().clone().add(0, 1, 0), 8, 0.35, 0.45, 0.35, 0.01); + target.getWorld().spawnParticle(Particle.CRIT, target.getLocation().clone().add(0, 1, 0), 10, 0.4, 0.5, 0.4, 0.06); + }; + + if (J.isFoliaThreading()) { + J.runEntity(target, burst); + return; + } + + try { + BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(target); + blood.height = -1; + blood.iterations = 1; + blood.start(); + } catch (UnsupportedOperationException | IllegalStateException ex) { + Adapt.verbose("Falling back to native thorns particles: " + ex.getMessage()); + burst.run(); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index 3d2f7efc7..a48275f3e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -22,15 +22,13 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; @@ -45,12 +43,11 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import java.util.HashMap; import java.util.Map; import java.util.UUID; public class UnarmedBatteringCharge extends SimpleAdaptation { - private final Map primedState = new HashMap<>(); + private final Map primedState = new java.util.concurrent.ConcurrentHashMap<>(); public UnarmedBatteringCharge() { super("unarmed-battering-charge"); @@ -92,7 +89,13 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p) || p.isInsideVehicle()) { + var attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (p.isInsideVehicle()) { return; } @@ -110,7 +113,7 @@ public void on(EntityDamageByEntityEvent e) { return; } - int level = getLevel(p); + int level = attack.level(); e.setDamage(e.getDamage() + getDamageBonus(level)); Entity target = e.getEntity(); target.setVelocity(target.getVelocity().add(p.getLocation().getDirection().normalize().multiply(getKnockback(level)))); @@ -140,7 +143,7 @@ public void on(EntityDeathEvent e) { } if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Player p - && hasAdaptation(p) + && hasActiveAdaptation(p) && isChargeLoadout(p)) { getPlayer(p).getData().addStat("unarmed.battering-charge.charge-kills", 1); } @@ -203,7 +206,8 @@ private int getCooldownTicks(int level) { public void onTick() { for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (!hasAdaptation(p)) { + int level = getActiveLevel(p); + if (level <= 0) { primedState.remove(p.getUniqueId()); continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java index fab255f06..974b4955d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java @@ -23,13 +23,12 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -43,14 +42,11 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import art.arcane.adapt.util.common.inventorygui.Window; - public class UnarmedComboChain extends SimpleAdaptation { - private final Map combos = new HashMap<>(); + private final Map combos = new java.util.concurrent.ConcurrentHashMap<>(); public UnarmedComboChain() { super("unarmed-combo-chain"); @@ -99,10 +95,12 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getDamager() instanceof Player p) || !hasAdaptation(p)) { + var attack = resolveAttackContext(e); + if (attack == null) { return; } + Player p = attack.attacker(); ItemStack hand = p.getInventory().getItemInMainHand(); if (isMelee(hand)) { combos.remove(p.getUniqueId()); @@ -110,7 +108,7 @@ public void on(EntityDamageByEntityEvent e) { } long now = System.currentTimeMillis(); - int level = getLevel(p); + int level = attack.level(); ComboState state = combos.computeIfAbsent(p.getUniqueId(), id -> new ComboState()); if (now - state.lastHitMillis > getComboWindowMillis(level)) { @@ -143,7 +141,7 @@ public void on(PlayerInteractEvent e) { } Player p = e.getPlayer(); - if (!hasAdaptation(p) || isMelee(p.getInventory().getItemInMainHand())) { + if (!hasActiveAdaptation(p) || isMelee(p.getInventory().getItemInMainHand())) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java index e5cbdc038..44335c5fd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -78,27 +77,24 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var attack = resolveAttackContext(e); + if (attack == null) { return; } - if (e.getDamager() instanceof Player p) { - if (!hasAdaptation(p)) { - return; - } + Player p = attack.attacker(); - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } - double armor = getArmorValue(p); - double damage = e.getDamage(); + double armor = getArmorValue(p); + double damage = e.getDamage(); - if (armor == 0) { - e.setDamage(damage * getConfig().maxDamageFactor); - } else { - e.setDamage(damage - (damage * armor)); - } + if (armor == 0) { + e.setDamage(damage * getConfig().maxDamageFactor); + } else { + e.setDamage(damage - (damage * armor)); } } @@ -109,7 +105,7 @@ public void on(EntityDeathEvent e) { } if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Player p - && hasAdaptation(p) + && hasActiveAdaptation(p) && !isTool(p.getInventory().getItemInMainHand()) && !isTool(p.getInventory().getItemInOffHand()) && getArmorValue(p) == 0) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java index 3e3b80442..a188877db 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java @@ -22,12 +22,11 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -76,26 +75,23 @@ public void addStats(int level, Element v) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var attack = resolveAttackContext(e); + if (attack == null) { return; } - if (e.getDamager() instanceof Player p) { - if (!hasAdaptation(p)) { - return; - } - - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } - double factor = getLevelPercent(p); - if (factor <= 0) { - return; - } - e.setDamage(e.getDamage() * (1 + getUnarmedDamage(getLevel(p)))); - xp(p, 0.321 * factor * e.getDamage(), "unarmed-hit"); + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + double factor = getLevelPercent(attack.level()); + if (factor <= 0) { + return; } + e.setDamage(e.getDamage() * (1 + getUnarmedDamage(attack.level()))); + xp(p, 0.321 * factor * e.getDamage(), "unarmed-hit"); + } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -105,7 +101,7 @@ public void on(EntityDeathEvent e) { } if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Player p - && hasAdaptation(p) + && hasActiveAdaptation(p) && !isTool(p.getInventory().getItemInMainHand()) && !isTool(p.getInventory().getItemInOffHand())) { getPlayer(p).getData().addStat("unarmed.power.unarmed-kills", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java index 131d02209..f7ec9cf9d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java @@ -17,16 +17,17 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.content.adaptation.unarmed; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -38,12 +39,6 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.reflect.registries.Particles; - public class UnarmedSuckerPunch extends SimpleAdaptation { public UnarmedSuckerPunch() { super("unarmed-sucker-punch"); @@ -90,42 +85,39 @@ private double getDamage(double f) { @EventHandler public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + var attack = resolveAttackContext(e); + if (attack == null) { return; } - if (e.getDamager() instanceof Player p) { - if (!hasAdaptation(p)) { - return; - } - if (p.getInventory().getItemInMainHand().getType() != Material.AIR && p.getInventory().getItemInOffHand().getType() != Material.AIR) { - return; - } - double factor = getLevelPercent(p); + Player p = attack.attacker(); + if (p.getInventory().getItemInMainHand().getType() != Material.AIR && p.getInventory().getItemInOffHand().getType() != Material.AIR) { + return; + } + double factor = getLevelPercent(attack.level()); - if (!p.isSprinting()) { - return; - } + if (!p.isSprinting()) { + return; + } - if (factor <= 0) { - return; - } + if (factor <= 0) { + return; + } - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } - e.setDamage(e.getDamage() * getDamage(factor)); - SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_PLAYER_ATTACK_STRONG, 1f, 1.8f); - spw.play(e.getEntity().getLocation(), Sound.BLOCK_BASALT_BREAK, 1f, 0.6f); - xp(p, 6.221 * e.getDamage(), "sucker-punch"); - getPlayer(p).getData().addStat("unarmed.sucker-punch.sucker-punches", 1); - if (e.getDamage() > 5) { - xp(p, 0.42 * e.getDamage(), "bonus-damage"); - if (areParticlesEnabled()) { - e.getEntity().getWorld().spawnParticle(Particle.FLASH, e.getEntity().getLocation(), 1); - } + e.setDamage(e.getDamage() * getDamage(factor)); + SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_PLAYER_ATTACK_STRONG, 1f, 1.8f); + spw.play(e.getEntity().getLocation(), Sound.BLOCK_BASALT_BREAK, 1f, 0.6f); + xp(p, 6.221 * e.getDamage(), "sucker-punch"); + getPlayer(p).getData().addStat("unarmed.sucker-punch.sucker-punches", 1); + if (e.getDamage() > 5) { + xp(p, 0.42 * e.getDamage(), "bonus-damage"); + if (areParticlesEnabled()) { + e.getEntity().getWorld().spawnParticle(Particle.FLASH, e.getEntity().getLocation(), 1); } } } @@ -137,7 +129,7 @@ public void on(EntityDeathEvent e) { } if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg && dmg.getDamager() instanceof Player p - && hasAdaptation(p) + && hasActiveAdaptation(p) && p.isSprinting() && !isTool(p.getInventory().getItemInMainHand()) && !isTool(p.getInventory().getItemInOffHand())) { diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java index 28c2c0709..38c9e5b6a 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java @@ -23,7 +23,6 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; public class AdaptEvent extends Event implements Cancellable { private static final HandlerList HANDLERS = new HandlerList(); diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index 0bf825c5a..0ca0e8f4a 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -7,23 +7,16 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.service.ConfigInputSVC; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.GuiEffects; -import art.arcane.adapt.util.common.inventorygui.GuiLayout; -import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.volmlib.util.io.IO; -import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.M; +import art.arcane.adapt.util.common.inventorygui.*; import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.common.inventorygui.UIElement; -import art.arcane.adapt.util.common.inventorygui.UIWindow; -import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDocumentation; import art.arcane.adapt.util.config.TomlCodec; -import org.bukkit.Bukkit; +import art.arcane.volmlib.util.io.IO; +import art.arcane.volmlib.util.math.M; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -31,14 +24,7 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BooleanSupplier; diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index c02aace06..fa2de3430 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -24,7 +24,11 @@ import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.api.xp.XP; -import org.bukkit.Bukkit; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.inventorygui.*; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -33,18 +37,6 @@ import java.util.List; import java.util.Locale; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; -import art.arcane.adapt.util.common.inventorygui.GuiEffects; -import art.arcane.adapt.util.common.inventorygui.GuiLayout; -import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.adapt.util.common.inventorygui.UIElement; -import art.arcane.adapt.util.common.inventorygui.UIWindow; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.scheduling.J; - public class SkillsGui { private static final int PAGE_JUMP = 5; diff --git a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java index aa538b912..c4131a790 100644 --- a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java +++ b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java @@ -20,9 +20,9 @@ import art.arcane.adapt.api.item.DataItem; import art.arcane.adapt.util.common.format.C; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.reflect.registries.ItemFlags; +import art.arcane.volmlib.util.format.Form; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java index b85c9af70..37c22b962 100644 --- a/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java +++ b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java @@ -20,10 +20,9 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.item.DataItem; -import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.util.common.format.C; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.volmlib.util.format.Form; import lombok.AllArgsConstructor; import lombok.Data; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/item/ItemListings.java b/src/main/java/art/arcane/adapt/content/item/ItemListings.java index 7d6cb2355..3294632c2 100644 --- a/src/main/java/art/arcane/adapt/content/item/ItemListings.java +++ b/src/main/java/art/arcane/adapt/content/item/ItemListings.java @@ -20,8 +20,8 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.volmlib.util.collection.KList; import lombok.Getter; import org.bukkit.ChatColor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java index 0f5117dc7..9b51f58f8 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java @@ -26,8 +26,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.inventorygui.Items; - public class MultiArmor implements MultiItem { @Override public boolean supportsItem(ItemStack itemStack) { diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java index 473927477..5891866a6 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java @@ -20,8 +20,8 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.nms.NMS; -import art.arcane.adapt.util.common.io.BukkitGson; import art.arcane.adapt.util.common.inventorygui.WindowResolution; +import art.arcane.adapt.util.common.io.BukkitGson; import art.arcane.volmlib.util.collection.KList; import lombok.*; import org.bukkit.NamespacedKey; diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java index 1b5d1639f..5a6c3fded 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java @@ -26,8 +26,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.inventorygui.Items; - public class OmniTool implements MultiItem { @Override public boolean supportsItem(ItemStack itemStack) { diff --git a/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java index bc986db33..231db4ce7 100644 --- a/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java @@ -18,10 +18,10 @@ package art.arcane.adapt.content.protector; -import com.massivecraft.factions.*; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.protection.Protector; +import com.massivecraft.factions.*; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java index 82b7d5ced..84e762e31 100644 --- a/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java @@ -18,11 +18,11 @@ package art.arcane.adapt.content.protector; -import com.griefdefender.api.GriefDefender; -import com.griefdefender.api.claim.Claim; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.protection.Protector; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.claim.Claim; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java index 62fe05236..82c810efd 100644 --- a/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java @@ -1,11 +1,8 @@ package art.arcane.adapt.content.protector; -import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.protection.Protector; -import me.angeschossen.chestprotect.api.addons.ChestProtectAddon; -import me.angeschossen.chestprotect.api.protection.block.BlockProtection; import me.crafter.mc.lockettepro.LocketteProAPI; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java index e45c88d59..21113939b 100644 --- a/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java @@ -1,13 +1,13 @@ package art.arcane.adapt.content.protector; -import com.bekvon.bukkit.residence.Residence; -import com.bekvon.bukkit.residence.containers.Flags; -import com.bekvon.bukkit.residence.protection.ClaimedResidence; -import com.bekvon.bukkit.residence.protection.FlagPermissions; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.protection.Protector; import art.arcane.adapt.util.common.scheduling.J; +import com.bekvon.bukkit.residence.Residence; +import com.bekvon.bukkit.residence.containers.Flags; +import com.bekvon.bukkit.residence.protection.ClaimedResidence; +import com.bekvon.bukkit.residence.protection.FlagPermissions; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java index 67d92bd85..e663025b7 100644 --- a/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java @@ -18,6 +18,10 @@ package art.arcane.adapt.content.protector; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.protection.Protector; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; @@ -28,10 +32,6 @@ import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; import com.sk89q.worldguard.protection.regions.RegionContainer; import com.sk89q.worldguard.protection.regions.RegionQuery; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.AdaptConfig; -import art.arcane.adapt.api.adaptation.Adaptation; -import art.arcane.adapt.api.protection.Protector; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java index b53e98dda..dbe9c5b3c 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java @@ -18,38 +18,23 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.agility.AgilityArmorUp; -import art.arcane.adapt.content.adaptation.agility.AgilityLadderSlide; -import art.arcane.adapt.content.adaptation.agility.AgilityParkourMomentum; -import art.arcane.adapt.content.adaptation.agility.AgilityRollLanding; -import art.arcane.adapt.content.adaptation.agility.AgilitySuperJump; -import art.arcane.adapt.content.adaptation.agility.AgilityWallJump; -import art.arcane.adapt.content.adaptation.agility.AgilityWindUp; +import art.arcane.adapt.content.adaptation.agility.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerMoveEvent; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - public class SkillAgility extends SimpleSkill { - private Map lastLocations; - public SkillAgility() { super("agility", Localizer.dLocalize("skill.agility.icon")); registerConfiguration(Config.class); @@ -189,15 +174,11 @@ public SkillAgility() { .build()); registerMilestone("challenge_agility_sneak_500", "move.sneak", 500, getConfig().challengeSprint5kReward); registerMilestone("challenge_agility_sneak_5k", "move.sneak", 5000, getConfig().challengeSprint5kReward * 2); - lastLocations = new HashMap<>(); } @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerMoveEvent e) { Player p = e.getPlayer(); - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(p, e, () -> { if (e.getFrom().getWorld() != null && e.getTo() != null && e.getFrom().getWorld().equals(e.getTo().getWorld())) { double d = e.getFrom().distance(e.getTo()); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index aada1d940..22833169c 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -18,19 +18,17 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.architect.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -40,9 +38,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillArchitect extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillArchitect() { super("architect", Localizer.dLocalize("skill.architect.icon")); @@ -191,9 +190,6 @@ public SkillArchitect() { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockPlaceEvent e) { Player p = e.getPlayer(); - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(p, e, () -> { if (!isStorage(e.getBlock().getType().createBlockData())) { double v = getValue(e.getBlock()) * getConfig().xpValueMultiplier; @@ -218,9 +214,6 @@ public void on(BlockPlaceEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { Player p = e.getPlayer(); - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(p, e, () -> { AdaptPlayer adaptPlayer = getPlayer(p); adaptPlayer.getData().addStat("blocks.broken", 1); @@ -234,10 +227,10 @@ public void onTick() { } private void handleBlockCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); action.run(); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java index 3de9d530d..543cefeae 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java @@ -18,18 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.axe.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -40,9 +38,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillAxes extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillAxes() { super("axes", Localizer.dLocalize("skill.axes.icon")); @@ -189,9 +188,6 @@ public SkillAxes() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { if (!getConfig().getXpForAttackingWithTools) { return; @@ -216,9 +212,6 @@ public void on(EntityDamageByEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(p, () -> { if (isAxe(p.getInventory().getItemInMainHand())) { @@ -237,10 +230,10 @@ public void on(BlockBreakEvent e) { } private void handleCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); action.run(); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java index 7fdc1253e..5d31392a8 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java @@ -18,26 +18,17 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.blocking.BlockingChainArmorer; -import art.arcane.adapt.content.adaptation.blocking.BlockingBastionStance; -import art.arcane.adapt.content.adaptation.blocking.BlockingBulwarkBash; -import art.arcane.adapt.content.adaptation.blocking.BlockingCounterGuard; -import art.arcane.adapt.content.adaptation.blocking.BlockingHorseArmorer; -import art.arcane.adapt.content.adaptation.blocking.BlockingMirrorBlock; -import art.arcane.adapt.content.adaptation.blocking.BlockingMultiArmor; -import art.arcane.adapt.content.adaptation.blocking.BlockingSaddlecrafter; +import art.arcane.adapt.content.adaptation.blocking.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -48,9 +39,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillBlocking extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillBlocking() { super("blocking", Localizer.dLocalize("skill.blocking.icon")); @@ -198,19 +190,16 @@ public SkillBlocking() { } private void handleCooldown(Player p, Runnable runnable) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); runnable.run(); } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity() instanceof Player p) { SoundPlayer sp = SoundPlayer.of(p); shouldReturnForPlayer(p, e, () -> { diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java index 682cd568f..e4c29b555 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java @@ -18,20 +18,19 @@ package art.arcane.adapt.content.skill; -import art.arcane.spatial.matter.SpatialMatter; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.data.WorldData; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.brewing.*; import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.content.matter.BrewingStandOwnerMatter; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.spatial.matter.SpatialMatter; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -45,13 +44,12 @@ import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.Bukkit; - import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillBrewing extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillBrewing() { super("brewing", Localizer.dLocalize("skill.brewing.icon")); @@ -232,19 +230,16 @@ public SkillBrewing() { } private void handleCooldown(Player p, Runnable runnable) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); runnable.run(); } @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerItemConsumeEvent e) { Player p = e.getPlayer(); - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(p, e, () -> { if (e.getItem().getItemMeta() instanceof PotionMeta o && !e.getItem().toString().contains("potion-type=minecraft:water") @@ -265,9 +260,6 @@ public void on(PlayerItemConsumeEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PotionSplashEvent e) { - if (e.isCancelled()) { - return; - } if (e.getPotion().getShooter() instanceof Player p) { shouldReturnForPlayer(p, e, () -> { AdaptPlayer a = getPlayer(p); @@ -281,9 +273,6 @@ public void on(PotionSplashEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockPlaceEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getBlock().getType().equals(Material.BREWING_STAND)) { WorldData.of(e.getBlock().getWorld()).set(e.getBlock(), new BrewingStandOwner(e.getPlayer().getUniqueId())); @@ -294,8 +283,7 @@ public void on(BlockPlaceEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(InventoryOpenEvent e) { - if (e.isCancelled() - || !(e.getPlayer() instanceof Player player) + if ( !(e.getPlayer() instanceof Player player) || !(e.getInventory() instanceof BrewerInventory inv)) { return; } @@ -315,9 +303,6 @@ public void on(InventoryOpenEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> { if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { return; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index f5c32d70a..a18fe636a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -18,24 +18,17 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.chronos.ChronosAberrantTouch; -import art.arcane.adapt.content.adaptation.chronos.ChronosInstantRecall; -import art.arcane.adapt.content.adaptation.chronos.ChronosTemporalEcho; -import art.arcane.adapt.content.adaptation.chronos.ChronosTimeBomb; -import art.arcane.adapt.content.adaptation.chronos.ChronosTimeInABottle; +import art.arcane.adapt.content.adaptation.chronos.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.scheduling.J; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.EnderPearl; @@ -53,15 +46,8 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import art.arcane.adapt.util.common.inventorygui.Window; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class SkillChronos extends SimpleSkill { private final Map lastPositions; @@ -90,17 +76,17 @@ public SkillChronos() { registerAdaptation(new ChronosInstantRecall()); registerAdaptation(new ChronosTimeBomb()); registerAdaptation(new ChronosTemporalEcho()); - lastPositions = new HashMap<>(); - positionHistory = new HashMap<>(); - recentActionTypes = new HashMap<>(); - actionTypeResetTimestamps = new HashMap<>(); - lastActivityTimestamps = new HashMap<>(); - sleepCooldowns = new HashMap<>(); - sleepEntryWorldTime = new HashMap<>(); - speedPotionTrackers = new HashMap<>(); - enderPearlCooldowns = new HashMap<>(); - survivalStreakStart = new HashMap<>(); - lastSurvivalCheck = new HashMap<>(); + lastPositions = new ConcurrentHashMap<>(); + positionHistory = new ConcurrentHashMap<>(); + recentActionTypes = new ConcurrentHashMap<>(); + actionTypeResetTimestamps = new ConcurrentHashMap<>(); + lastActivityTimestamps = new ConcurrentHashMap<>(); + sleepCooldowns = new ConcurrentHashMap<>(); + sleepEntryWorldTime = new ConcurrentHashMap<>(); + speedPotionTrackers = new ConcurrentHashMap<>(); + enderPearlCooldowns = new ConcurrentHashMap<>(); + survivalStreakStart = new ConcurrentHashMap<>(); + lastSurvivalCheck = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CLOCK) .key("challenge_chronos_1h") @@ -229,10 +215,10 @@ private void trackAction(UUID uuid, String actionType) { Long resetTime = actionTypeResetTimestamps.get(uuid); if (resetTime == null || now - resetTime > getConfig().activityWindow) { - recentActionTypes.put(uuid, new HashSet<>()); + recentActionTypes.put(uuid, ConcurrentHashMap.newKeySet()); actionTypeResetTimestamps.put(uuid, now); } - recentActionTypes.computeIfAbsent(uuid, k -> new HashSet<>()).add(actionType); + recentActionTypes.computeIfAbsent(uuid, k -> ConcurrentHashMap.newKeySet()).add(actionType); } private boolean isAfk(UUID uuid) { @@ -260,7 +246,7 @@ private boolean isAfk(UUID uuid) { } variance /= count; - Set actions = recentActionTypes.getOrDefault(uuid, new HashSet<>()); + Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); return variance < getConfig().afkVarianceThreshold && actions.size() < getConfig().afkMinActionTypes; } @@ -282,75 +268,73 @@ public void onTick() { for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); - if (shouldReturnForPlayer(p)) { - continue; - } + shouldReturnForPlayer(p, () -> { + UUID uuid = p.getUniqueId(); + Location current = p.getLocation(); + Location last = lastPositions.get(uuid); + + // Update position history + Deque history = positionHistory.computeIfAbsent(uuid, k -> new ArrayDeque<>()); + history.addLast(current.clone()); + while (history.size() > getConfig().positionHistorySize) { + history.removeFirst(); + } - UUID uuid = p.getUniqueId(); - Location current = p.getLocation(); - Location last = lastPositions.get(uuid); - - // Update position history - Deque history = positionHistory.computeIfAbsent(uuid, k -> new ArrayDeque<>()); - history.addLast(current.clone()); - while (history.size() > getConfig().positionHistorySize) { - history.removeFirst(); - } + double moved = (last != null && last.getWorld() != null && last.getWorld().equals(current.getWorld())) + ? last.distance(current) + : 0; - double moved = (last != null && last.getWorld() != null && last.getWorld().equals(current.getWorld())) - ? last.distance(current) - : 0; + // Track movement as an action type + if (moved >= getConfig().minimumMovementForActiveCheck) { + trackAction(uuid, "movement"); + } - // Track movement as an action type - if (moved >= getConfig().minimumMovementForActiveCheck) { - trackAction(uuid, "movement"); - } + double afkMult = getAfkMultiplier(uuid); - double afkMult = getAfkMultiplier(uuid); + // Movement XP (existing behavior, now with AFK penalty) + if (moved >= getConfig().minimumMovementForActiveCheck) { + adaptPlayer.getData().addStat("minutes.online", 10); + adaptPlayer.getData().addStat("chronos.active.distance", moved); + double bonus = (moved / getConfig().distancePerBonusXP) * getConfig().activeMovementXP; + xpSilent(p, Math.min(getConfig().activeMovementXPCapPerTick, bonus) * afkMult, "chronos:movement"); + } - // Movement XP (existing behavior, now with AFK penalty) - if (moved >= getConfig().minimumMovementForActiveCheck) { - adaptPlayer.getData().addStat("minutes.online", 10); - adaptPlayer.getData().addStat("chronos.active.distance", moved); - double bonus = (moved / getConfig().distancePerBonusXP) * getConfig().activeMovementXP; - xpSilent(p, Math.min(getConfig().activeMovementXPCapPerTick, bonus) * afkMult, "chronos:movement"); - } + // Passive active-play XP + Long lastActivity = lastActivityTimestamps.get(uuid); + if (lastActivity != null && now - lastActivity < getConfig().activityWindow) { + double passiveXP = getConfig().passiveActiveXP; - // Passive active-play XP - Long lastActivity = lastActivityTimestamps.get(uuid); - if (lastActivity != null && now - lastActivity < getConfig().activityWindow) { - double passiveXP = getConfig().passiveActiveXP; + // Night activity multiplier + if (isNight(p)) { + passiveXP *= getConfig().nightActivityMultiplier; + } - // Night activity multiplier - if (isNight(p)) { - passiveXP *= getConfig().nightActivityMultiplier; - } + // Activity variety bonus + Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); + if (actions.size() >= getConfig().activityTypesForBonus) { + passiveXP *= getConfig().activityBonusMultiplier; + } - // Activity variety bonus - Set actions = recentActionTypes.getOrDefault(uuid, new HashSet<>()); - if (actions.size() >= getConfig().activityTypesForBonus) { - passiveXP *= getConfig().activityBonusMultiplier; + xpSilent(p, passiveXP * afkMult, "chronos:passive"); } - xpSilent(p, passiveXP * afkMult, "chronos:passive"); - } - - // Survival streak XP - survivalStreakStart.putIfAbsent(uuid, now); - Long lastCheck = lastSurvivalCheck.get(uuid); - if (lastCheck == null || now - lastCheck >= 60000) { - lastSurvivalCheck.put(uuid, now); - long aliveMs = now - survivalStreakStart.getOrDefault(uuid, now); - double aliveHours = aliveMs / 3600000.0; - double streakBonus = 1.0 + Math.min( - aliveHours * getConfig().survivalStreakBonusPerHour, - getConfig().survivalStreakHourCap * getConfig().survivalStreakBonusPerHour - ); - xpSilent(p, getConfig().survivalXPPerMinute * streakBonus * afkMult, "chronos:survival"); - } + // Survival streak XP + survivalStreakStart.putIfAbsent(uuid, now); + Long lastCheck = lastSurvivalCheck.get(uuid); + if (lastCheck == null || now - lastCheck >= 60000) { + lastSurvivalCheck.put(uuid, now); + long aliveMs = now - survivalStreakStart.getOrDefault(uuid, now); + double aliveHours = aliveMs / 3600000.0; + double streakBonus = 1.0 + Math.min( + aliveHours * getConfig().survivalStreakBonusPerHour, + getConfig().survivalStreakHourCap * getConfig().survivalStreakBonusPerHour + ); + xpSilent(p, getConfig().survivalXPPerMinute * streakBonus * afkMult, "chronos:survival"); + } - checkStatTrackers(adaptPlayer); - lastPositions.put(uuid, current.clone()); + checkStatTrackers(adaptPlayer); + lastPositions.put(uuid, current.clone()); + }); } } @@ -358,9 +342,6 @@ public void onTick() { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerBedEnterEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(p, () -> { UUID uuid = p.getUniqueId(); @@ -396,9 +377,6 @@ public void on(PlayerBedEnterEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerItemConsumeEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(p, () -> { ItemStack item = e.getItem(); @@ -462,9 +440,6 @@ public void on(PlayerItemConsumeEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity() instanceof EnderPearl pearl)) { return; } @@ -488,9 +463,6 @@ public void on(ProjectileLaunchEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerTeleportEvent e) { - if (e.isCancelled()) { - return; - } if (e.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { return; } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java index e2db69e92..344a3d5c1 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java @@ -18,17 +18,15 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.crafting.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -41,9 +39,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillCrafting extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillCrafting() { super("crafting", Localizer.dLocalize("skill.crafting.icon")); @@ -192,16 +191,13 @@ public SkillCrafting() { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = (Player) e.getWhoClicked(); shouldReturnForPlayer(p, e, () -> { if (!isValidCraftEvent(e)) { return; } int recipeAmount = calculateRecipeAmount(e); - if (recipeAmount > 0 && !e.isCancelled()) { + if (recipeAmount > 0) { double v = recipeAmount * getValue(e.getRecipe().getResult()) * getConfig().craftingValueXPMultiplier; getPlayer(p).getData().addStat("crafted.items", recipeAmount); getPlayer(p).getData().addStat("crafted.value", v); @@ -220,9 +216,6 @@ public void on(CraftItemEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(FurnaceSmeltEvent e) { - if (e.isCancelled()) { - return; - } if (shouldReturnForWorld(e.getBlock().getWorld(), this)) { return; } @@ -238,10 +231,10 @@ public void onTick() { private boolean isValidCraftEvent(CraftItemEvent e) { Player p = (Player) e.getWhoClicked(); - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return false; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); ItemStack result = e.getInventory().getResult(); ItemStack cursor = e.getCursor(); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java index 2c960a886..a57e9f0d8 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java @@ -24,21 +24,14 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.Discovery; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryArmor; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryArchaeologist; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryBetterMending; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryCartographerPulse; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryUnity; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryVillagerAtt; -import art.arcane.adapt.content.adaptation.discovery.DiscoveryXpResist; +import art.arcane.adapt.content.adaptation.discovery.*; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.volmlib.util.format.Form; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Biome; @@ -185,17 +178,11 @@ public void on(PlayerChangedWorldEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerInteractAtEntityEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> seeEntity(e.getPlayer(), e.getRightClicked())); } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityPickupItemEvent e) { - if (e.isCancelled()) { - return; - } if (e.getEntity() instanceof Player p) { shouldReturnForPlayer(p, e, () -> seeItem(p, e.getItem().getItemStack())); } @@ -204,9 +191,6 @@ public void on(EntityPickupItemEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(CraftItemEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getWhoClicked() instanceof Player p)) return; shouldReturnForPlayer(p, e, () -> { try { @@ -370,9 +354,10 @@ public void onTick() { if (!this.isEnabled()) return; for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); - if (shouldReturnForPlayer(i)) continue; - checkStatTrackers(adaptPlayer); - seeTargetBlock(i); + shouldReturnForPlayer(i, () -> { + checkStatTrackers(adaptPlayer); + seeTargetBlock(i); + }); } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java index 824ecf3e6..b7d476b7a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java @@ -18,24 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingLapisReturn; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingAnvilSavant; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingBookshelfAttunement; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingGrindstoneRecovery; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingOfferReroll; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingQuickEnchant; -import art.arcane.adapt.content.adaptation.enchanting.EnchantingXPReturn; +import art.arcane.adapt.content.adaptation.enchanting.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -44,9 +36,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillEnchanting extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillEnchanting() { super("enchanting", Localizer.dLocalize("skill.enchanting.icon")); @@ -197,9 +190,6 @@ public SkillEnchanting() { @EventHandler(priority = EventPriority.MONITOR) public void on(EnchantItemEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getEnchanter(); shouldReturnForPlayer(p, e, () -> { handleEnchantItemEvent(p, e); @@ -217,10 +207,10 @@ private void handleEnchantItemEvent(Player p, EnchantItemEvent e) { } adaptPlayer.getData().addStat("enchanting.total.levels", e.getExpLevelCost()); - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(p, getConfig().enchantPowerXPMultiplier * e.getEnchantsToAdd().values().stream().mapToInt((i) -> i).sum()); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index b262b61c8..0b49e76ec 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -18,21 +18,19 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.excavation.ExcavationDropToInventory; import art.arcane.adapt.content.adaptation.excavation.ExcavationHaste; import art.arcane.adapt.content.adaptation.excavation.ExcavationOmniTool; import art.arcane.adapt.content.adaptation.excavation.ExcavationSpelunker; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -43,9 +41,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillExcavation extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillExcavation() { super("excavation", Localizer.dLocalize("skill.excavation.icon")); @@ -190,9 +189,6 @@ public SkillExcavation() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { if (!getConfig().getXpForAttackingWithTools) { return; @@ -205,10 +201,10 @@ private void handleEntityDamageByPlayer(Player p, EntityDamageByEntityEvent e) { AdaptPlayer a = getPlayer(p); ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); if (isShovel(hand)) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); getPlayer(p).getData().addStat("excavation.swings", 1); getPlayer(p).getData().addStat("excavation.damage", e.getDamage()); xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().axeDamageXPMultiplier * e.getDamage()); @@ -217,9 +213,6 @@ private void handleEntityDamageByPlayer(Player p, EntityDamageByEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(p, e, () -> { if (isShovel(p.getInventory().getItemInMainHand())) { @@ -237,10 +230,10 @@ private void handleBlockBreakWithShovel(Player p, BlockBreakEvent e) { || blockType == Material.CLAY || blockType == Material.SOUL_SAND || blockType == Material.SOUL_SOIL) { getPlayer(p).getData().addStat("excavation.gravel", 1); } - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); double v = getValue(e.getBlock().getType()); xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v)); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index ad840fb66..0473532fb 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -18,18 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.herbalism.*; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.format.Localizer; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.Ageable; @@ -46,9 +44,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillHerbalism extends SimpleSkill { - private final Map cooldown = new HashMap<>(); + private final Map cooldown = new HashMap<>(); public SkillHerbalism() { super("herbalism", Localizer.dLocalize("skill.herbalism.icon")); @@ -203,14 +202,11 @@ public SkillHerbalism() { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - cooldown.remove(p); + cooldown.remove(p.getUniqueId()); } @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerItemConsumeEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getItem().getItemMeta() instanceof PotionMeta o) { @@ -228,9 +224,6 @@ public void on(PlayerItemConsumeEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerShearEntityEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { getPlayer(p).getData().addStat("herbalism.sheared", 1); @@ -240,25 +233,16 @@ public void on(PlayerShearEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerHarvestBlockEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getHarvestedBlock(), "harvest.blocks")); } @EventHandler(priority = EventPriority.MONITOR) public void on(BlockPlaceEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.planted")); } @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerInteractEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.useItemInHand().equals(Event.Result.DENY)) { @@ -276,22 +260,19 @@ public void on(PlayerInteractEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.blocks")); } private void handleHerbCooldown(Player p, Runnable action) { - if (cooldown.containsKey(p)) { - if (cooldown.get(p) + getConfig().harvestXpCooldown > System.currentTimeMillis()) { + if (cooldown.containsKey(p.getUniqueId())) { + if (cooldown.get(p.getUniqueId()) + getConfig().harvestXpCooldown > System.currentTimeMillis()) { return; } else { - cooldown.remove(p); + cooldown.remove(p.getUniqueId()); } } - cooldown.put(p, System.currentTimeMillis()); + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); action.run(); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java index 380ae2ccf..643d24653 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java @@ -18,19 +18,17 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.hunter.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; @@ -47,9 +45,10 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.UUID; public class SkillHunter extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillHunter() { super("hunter", Localizer.dLocalize("skill.hunter.icon")); @@ -200,18 +199,15 @@ private void handleCooldownAndXp(Player p, double xpAmount) { } private void handleCooldownAndXp(Player p, double xpAmount, String rewardKey) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(p, xpAmount, rewardKey); } @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getBlock().getType().equals(Material.TURTLE_EGG)) { @@ -223,9 +219,6 @@ public void on(BlockBreakEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerInteractEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) { @@ -269,7 +262,7 @@ public void on(EntityDeathEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(CreatureSpawnEvent e) { - if (!isEnabled() || e.isCancelled()) { + if (!isEnabled()) { return; } if (e.getSpawnReason().equals(CreatureSpawnEvent.SpawnReason.SPAWNER)) { diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java index 50e4c352c..b1b6723aa 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java @@ -18,25 +18,17 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.nether.NetherFireResist; -import art.arcane.adapt.content.adaptation.nether.NetherBlazeLeech; -import art.arcane.adapt.content.adaptation.nether.NetherGhastWard; -import art.arcane.adapt.content.adaptation.nether.NetherLavaWalker; -import art.arcane.adapt.content.adaptation.nether.NetherPiglinBroker; -import art.arcane.adapt.content.adaptation.nether.NetherSkullYeet; -import art.arcane.adapt.content.adaptation.nether.NetherWitherResist; +import art.arcane.adapt.content.adaptation.nether.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.Data; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -178,27 +170,23 @@ public SkillNether() { registerMilestone("challenge_roses_100", "nether.roses.broken", 100, getConfig().getChallengeRosesReward() * 2); } - private boolean shouldReturnForEventWithCause(Player p, EntityDamageEvent.DamageCause cause) { - return shouldReturnForPlayer(p) || cause != EntityDamageEvent.DamageCause.WITHER; + private boolean isWitherDamageCause(EntityDamageEvent.DamageCause cause) { + return cause == EntityDamageEvent.DamageCause.WITHER; } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageEvent e) { - if (e.isCancelled()) { + if (!this.isEnabled() || !(e.getEntity() instanceof Player p) || !isWitherDamageCause(e.getCause()) || e instanceof EntityDamageByBlockEvent) { return; } - if (!this.isEnabled() || e.isCancelled() || !(e.getEntity() instanceof Player p) || shouldReturnForEventWithCause(p, e.getCause()) || e instanceof EntityDamageByBlockEvent) { - return; - } - getPlayer(p).getData().addStat("nether.wither.damage", e.getDamage()); - xp(p, getConfig().getWitherDamageXp()); + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("nether.wither.damage", e.getDamage()); + xp(p, getConfig().getWitherDamageXp()); + }); } @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getBlock().getType() == Material.WITHER_ROSE && witherRoseCooldown == 0) { @@ -213,29 +201,28 @@ public void on(BlockBreakEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDeathEvent e) { Player p = e.getEntity().getKiller(); - if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer") || shouldReturnForPlayer(p)) { + if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { return; } - if (e.getEntityType() == EntityType.WITHER_SKELETON) { - getPlayer(p).getData().addStat("nether.kills", 1); - getPlayer(p).getData().addStat("nether.skeleton.kills", 1); - xp(p, getConfig().getWitherSkeletonKillXp()); - } else if (e.getEntityType() == EntityType.WITHER) { - getPlayer(p).getData().addStat("nether.kills", 1); - getPlayer(p).getData().addStat("nether.boss.kills", 1); - xp(p, getConfig().getWitherKillXp()); - } + shouldReturnForPlayer(p, () -> { + if (e.getEntityType() == EntityType.WITHER_SKELETON) { + getPlayer(p).getData().addStat("nether.kills", 1); + getPlayer(p).getData().addStat("nether.skeleton.kills", 1); + xp(p, getConfig().getWitherSkeletonKillXp()); + } else if (e.getEntityType() == EntityType.WITHER) { + getPlayer(p).getData().addStat("nether.kills", 1); + getPlayer(p).getData().addStat("nether.boss.kills", 1); + xp(p, getConfig().getWitherKillXp()); + } + }); } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { + if (!(e.getDamager() instanceof Player p) || !isWitherDamageCause(e.getCause())) { return; } - if (!(e.getDamager() instanceof Player p) || shouldReturnForEventWithCause(p, e.getCause())) { - return; - } - xp(p, getConfig().getWitherAttackXp()); + shouldReturnForPlayer(p, e, () -> xp(p, getConfig().getWitherAttackXp())); } @Override @@ -248,9 +235,7 @@ public void onTick() { } for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player i = adaptPlayer.getPlayer(); - if (!shouldReturnForPlayer(i)) { - checkStatTrackers(adaptPlayer); - } + shouldReturnForPlayer(i, () -> checkStatTrackers(adaptPlayer)); } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index aa0378a27..57e90c611 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -18,18 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.pickaxe.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -42,9 +40,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillPickaxes extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillPickaxes() { super("pickaxe", Localizer.dLocalize("skill.pickaxe.icon")); @@ -197,9 +196,6 @@ public SkillPickaxes() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getDamager() instanceof Player ? (Player) e.getDamager() : null; if (!getConfig().getXpForAttackingWithTools || p == null) { return; @@ -220,9 +216,6 @@ public void on(EntityDamageByEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(p, () -> { ItemStack mainHand = p.getInventory().getItemInMainHand(); @@ -284,10 +277,10 @@ public double getValue(Material type) { private void handleCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); action.run(); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java index d2ef41b65..48a2e7be0 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java @@ -18,17 +18,15 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.ranged.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.FishHook; import org.bukkit.entity.Player; @@ -44,9 +42,10 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.UUID; public class SkillRanged extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillRanged() { super("ranged", Localizer.dLocalize("skill.ranged.icon")); @@ -188,9 +187,6 @@ public SkillRanged() { @EventHandler(priority = EventPriority.MONITOR) public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity().getShooter() instanceof Player p)) { return; } @@ -201,19 +197,16 @@ public void on(ProjectileLaunchEvent e) { getPlayer(p).getData().addStat("ranged.shotsfired", 1); getPlayer(p).getData().addStat("ranged.shotsfired." + e.getEntity().getType().name().toLowerCase(Locale.ROOT), 1); - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(p, getConfig().shootXP); }); } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getDamager() instanceof Projectile) || !(((Projectile) e.getDamager()).getShooter() instanceof Player p) || !checkValidEntity(e.getEntity().getType())) { return; } @@ -231,10 +224,10 @@ public void on(EntityDamageByEntityEvent e) { } getPlayer(p).getData().addStat("ranged.damage", e.getDamage()); getPlayer(p).getData().addStat("ranged.damage." + e.getDamager().getType().name().toLowerCase(Locale.ROOT), e.getDamage()); - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(p, e.getEntity().getLocation(), (getConfig().hitDamageXPMultiplier * e.getDamage()) + (e.getEntity().getLocation().distance(p.getLocation()) * getConfig().hitDistanceXPMultiplier)); }); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java index 8465f361e..677631c60 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java @@ -18,21 +18,20 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.chronos.ChronosInstantRecall; import art.arcane.adapt.content.adaptation.rift.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.collection.KMap; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.adapt.util.reflect.registries.EntityTypes; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -45,8 +44,10 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import java.util.UUID; + public class SkillRift extends SimpleSkill { - private final KMap lasttp; + private final KMap lasttp; public SkillRift() { super("rift", Localizer.dLocalize("skill.rift.icon")); @@ -189,9 +190,6 @@ public SkillRift() { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerTeleportEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); if (ChronosInstantRecall.isRecallTeleportSuppressed(p)) { return; @@ -199,18 +197,15 @@ public void on(PlayerTeleportEvent e) { shouldReturnForPlayer(e.getPlayer(), e, () -> { getPlayer(p).getData().addStat("rift.teleports", 1); - if (!lasttp.containsKey(p)) { + if (!lasttp.containsKey(p.getUniqueId())) { xpSilent(p, getConfig().teleportXP, "rift:teleport"); - lasttp.put(p, M.ms()); + lasttp.put(p.getUniqueId(), M.ms()); } }); } @EventHandler(priority = EventPriority.MONITOR) public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity().getShooter() instanceof Player p)) { return; } @@ -254,9 +249,6 @@ private void handleEntityDamageByEntity(Entity entity, Player p, double damage) @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Player p) { shouldReturnForPlayer(p, e, () -> handleEntityDamageByEntity(e.getEntity(), p, e.getDamage())); } else if (e.getDamager() instanceof Projectile j && j.getShooter() instanceof Player p) { @@ -278,7 +270,7 @@ public void on(EntityDeathEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - lasttp.remove(p); + lasttp.remove(p.getUniqueId()); } @Override @@ -286,10 +278,16 @@ public void onTick() { if (!this.isEnabled()) { return; } - for (Player i : lasttp.k()) { - shouldReturnForPlayer(i, () -> { - if (M.ms() - lasttp.get(i) > getConfig().teleportXPCooldown) { - lasttp.remove(i); + for (UUID playerId : lasttp.k()) { + Player player = Bukkit.getPlayer(playerId); + if (player == null || !player.isOnline()) { + lasttp.remove(playerId); + continue; + } + + shouldReturnForPlayer(player, () -> { + if (M.ms() - lasttp.get(playerId) > getConfig().teleportXPCooldown) { + lasttp.remove(playerId); } }); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java index bc3a7a4f2..75924b1dc 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java @@ -18,23 +18,24 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.seaborrne.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.reflect.registries.Attributes; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.entity.*; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockBreakEvent; @@ -44,9 +45,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillSeaborne extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillSeaborne() { super("seaborne", Localizer.dLocalize("skill.seaborne.icon")); @@ -177,12 +179,12 @@ public SkillSeaborne() { } private boolean isOnCooldown(Player p, long cooldown) { - Long lastCooldown = cooldowns.get(p); + Long lastCooldown = cooldowns.get(p.getUniqueId()); return lastCooldown != null && lastCooldown + cooldown > System.currentTimeMillis(); } private void setCooldown(Player p) { - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); } @Override @@ -205,9 +207,6 @@ public void onTick() { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerFishEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (e.getState().equals(PlayerFishEvent.State.CAUGHT_FISH)) { @@ -221,9 +220,6 @@ public void on(PlayerFishEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(BlockBreakEvent e) { - if (e.isCancelled()) { - return; - } Player p = e.getPlayer(); shouldReturnForPlayer(e.getPlayer(), e, () -> { if (isOnCooldown(p, getConfig().seaPickleCooldown)) { @@ -244,19 +240,21 @@ public void on(BlockBreakEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDeathEvent e) { Player p = e.getEntity().getKiller(); - if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer") || shouldReturnForPlayer(p)) { + if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { return; } - if (e.getEntityType() == EntityType.DROWNED) { - getPlayer(p).getData().addStat("seaborne.drowned.kills", 1); - } else if (e.getEntityType() == EntityType.GUARDIAN || e.getEntityType() == EntityType.ELDER_GUARDIAN) { - getPlayer(p).getData().addStat("seaborne.guardian.kills", 1); - } + shouldReturnForPlayer(p, () -> { + if (e.getEntityType() == EntityType.DROWNED) { + getPlayer(p).getData().addStat("seaborne.drowned.kills", 1); + } else if (e.getEntityType() == EntityType.GUARDIAN || e.getEntityType() == EntityType.ELDER_GUARDIAN) { + getPlayer(p).getData().addStat("seaborne.guardian.kills", 1); + } + }); } @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled() || !(e.getEntity() instanceof LivingEntity entity)) + if (!(e.getEntity() instanceof LivingEntity entity)) return; if (e.getEntity().getType() == EntityType.DROWNED && e.getDamager() instanceof Player p) { diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java index 3d69c8038..5290eab4f 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java @@ -18,22 +18,19 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.adaptation.stealth.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -42,9 +39,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillStealth extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillStealth() { super("stealth", Localizer.dLocalize("skill.stealth.icon")); @@ -184,9 +182,6 @@ public SkillStealth() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Player p && p.isSneaking()) { shouldReturnForPlayer(p, e, () -> { getPlayer(p).getData().addStat("stealth.damage.sneaking", e.getDamage()); @@ -211,9 +206,6 @@ public void on(EntityDeathEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(ProjectileLaunchEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity().getShooter() instanceof Player p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java index 27b76b515..4cdebb137 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java @@ -18,24 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.sword.SwordsBloodyBlade; -import art.arcane.adapt.content.adaptation.sword.SwordsDualWield; -import art.arcane.adapt.content.adaptation.sword.SwordsCrimsonCyclone; -import art.arcane.adapt.content.adaptation.sword.SwordsExecutionersEdge; -import art.arcane.adapt.content.adaptation.sword.SwordsMachete; -import art.arcane.adapt.content.adaptation.sword.SwordsPoisonedBlade; -import art.arcane.adapt.content.adaptation.sword.SwordsRiposteWindow; +import art.arcane.adapt.content.adaptation.sword.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -46,9 +38,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillSwords extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillSwords() { super("swords", Localizer.dLocalize("skill.swords.icon")); @@ -188,9 +181,6 @@ public SkillSwords() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { shouldReturnForPlayer(p, e, () -> { AdaptPlayer a = getPlayer(p); @@ -228,12 +218,12 @@ public void on(EntityDeathEvent e) { } private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); } private void setCooldown(Player p) { - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java index 15ffaf351..f65fb6ff5 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java @@ -18,23 +18,15 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.taming.TamingDamage; -import art.arcane.adapt.content.adaptation.taming.TamingBeastRecall; -import art.arcane.adapt.content.adaptation.taming.TamingHealthBoost; -import art.arcane.adapt.content.adaptation.taming.TamingHealthRegeneration; -import art.arcane.adapt.content.adaptation.taming.TamingMountedTactics; -import art.arcane.adapt.content.adaptation.taming.TamingPackLeaderAura; -import art.arcane.adapt.content.adaptation.taming.TamingSharedPain; +import art.arcane.adapt.content.adaptation.taming.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -48,9 +40,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillTaming extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillTaming() { super("taming", Localizer.dLocalize("skill.taming.icon")); @@ -182,9 +175,6 @@ public SkillTaming() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityBreedEvent e) { - if (e.isCancelled()) { - return; - } for (Entity nearby : e.getEntity().getNearbyEntities(15, 15, 15)) { if (!(nearby instanceof Player p)) { continue; @@ -201,9 +191,6 @@ public void on(EntityBreedEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (e.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { shouldReturnForPlayer(p, e, () -> { getPlayer(p).getData().addStat("taming.pet.damage", e.getDamage()); @@ -217,9 +204,6 @@ public void on(EntityDamageByEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityTameEvent e) { - if (e.isCancelled()) { - return; - } if (e.getOwner() instanceof Player p) { shouldReturnForPlayer(p, e, () -> { getPlayer(p).getData().addStat("taming.tamed", 1); @@ -244,12 +228,12 @@ public void on(EntityDeathEvent e) { } private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); } private void setCooldown(Player p) { - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 3ee1f4d4f..31cbb47bb 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -20,28 +20,21 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; -import art.arcane.adapt.content.adaptation.tragoul.TragoulGlobe; -import art.arcane.adapt.content.adaptation.tragoul.TragoulBloodPact; -import art.arcane.adapt.content.adaptation.tragoul.TragoulBoneHarvest; -import art.arcane.adapt.content.adaptation.tragoul.TragoulHealing; -import art.arcane.adapt.content.adaptation.tragoul.TragoulLance; -import art.arcane.adapt.content.adaptation.tragoul.TragoulThorns; +import art.arcane.adapt.content.adaptation.tragoul.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.reflect.registries.Particles; import de.slikey.effectlib.effect.CloudEffect; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; @@ -54,9 +47,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillTragOul extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillTragOul() { super("tragoul", Localizer.dLocalize("skill.tragoul.icon")); @@ -195,9 +189,6 @@ public SkillTragOul() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity() instanceof Player p)) { return; } @@ -212,10 +203,10 @@ public void on(EntityDamageByEntityEvent e) { AdaptPlayer a = getPlayer(p); a.getData().addStat("trag.hitsrecieved", 1); a.getData().addStat("trag.damage", e.getDamage()); - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(a.getPlayer(), getConfig().damageReceivedXpMultiplier * e.getDamage()); if (p.getHealth() - e.getFinalDamage() > 0 && p.getHealth() - e.getFinalDamage() <= 8) { xp(a.getPlayer(), getConfig().lowHealthSurvivalXP); @@ -275,9 +266,6 @@ public void on(PlayerDeathEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getEntity() instanceof Player p)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java index 2810d9381..a60d37bc4 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java @@ -18,22 +18,16 @@ package art.arcane.adapt.content.skill; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.api.world.AdaptStatTracker; -import art.arcane.adapt.content.adaptation.unarmed.UnarmedGlassCannon; -import art.arcane.adapt.content.adaptation.unarmed.UnarmedBatteringCharge; -import art.arcane.adapt.content.adaptation.unarmed.UnarmedComboChain; -import art.arcane.adapt.content.adaptation.unarmed.UnarmedPower; -import art.arcane.adapt.content.adaptation.unarmed.UnarmedSuckerPunch; +import art.arcane.adapt.content.adaptation.unarmed.*; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.CustomModel; import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -44,9 +38,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class SkillUnarmed extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; public SkillUnarmed() { super("unarmed", Localizer.dLocalize("skill.unarmed.icon")); @@ -184,9 +179,6 @@ public SkillUnarmed() { @EventHandler(priority = EventPriority.MONITOR) public void on(EntityDamageByEntityEvent e) { - if (e.isCancelled()) { - return; - } if (!(e.getDamager() instanceof Player p)) { return; } @@ -214,10 +206,10 @@ public void on(EntityDamageByEntityEvent e) { if (e.getDamage() > 6) { a.getData().addStat("unarmed.heavy", 1); } - Long cooldown = cooldowns.get(p); + Long cooldown = cooldowns.get(p.getUniqueId()); if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; - cooldowns.put(p, System.currentTimeMillis()); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage()); } }); diff --git a/src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java b/src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java deleted file mode 100644 index 1cf463d9d..000000000 --- a/src/main/java/art/arcane/adapt/engine/framework/MeteredCache.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 art.arcane.adapt.engine.framework; - -import art.arcane.volmlib.util.data.KCache; - -public interface MeteredCache { - long getSize(); - - KCache getRawCache(); - - long getMaxSize(); - - default double getUsage() { - return (double) getSize() / (double) getMaxSize(); - } - - boolean isClosed(); -} diff --git a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java index 784990c9d..fb24104b4 100644 --- a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java +++ b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java @@ -2,30 +2,18 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.protection.WorldPolicyLatencyTelemetry; -import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; +import art.arcane.adapt.api.telemetry.AbilityCheckTelemetry; import art.arcane.adapt.util.common.plugin.AdaptService; -import art.arcane.volmlib.integration.IntegrationHandshakeRequest; -import art.arcane.volmlib.integration.IntegrationHandshakeResponse; -import art.arcane.volmlib.integration.IntegrationHeartbeat; -import art.arcane.volmlib.integration.IntegrationMetricDescriptor; -import art.arcane.volmlib.integration.IntegrationMetricSample; -import art.arcane.volmlib.integration.IntegrationMetricSchema; -import art.arcane.volmlib.integration.IntegrationProtocolNegotiator; -import art.arcane.volmlib.integration.IntegrationProtocolVersion; -import art.arcane.volmlib.integration.IntegrationServiceContract; +import art.arcane.volmlib.integration.*; import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.plugin.ServicePriority; -import java.util.ArrayDeque; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; public class AdaptIntegrationService implements AdaptService, IntegrationServiceContract { - private static final long ABILITY_WINDOW_MS = 60_000L; private static final Set SUPPORTED_PROTOCOLS = Set.of( new IntegrationProtocolVersion(1, 0), new IntegrationProtocolVersion(1, 1) @@ -37,8 +25,6 @@ public class AdaptIntegrationService implements AdaptService, IntegrationService "adapt-runtime-metrics" ); - private final ArrayDeque abilitySuccessfulOps = new ArrayDeque<>(); - private final ArrayDeque abilityCheckOps = new ArrayDeque<>(); private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1); @Override @@ -50,30 +36,10 @@ public void onEnable() { @Override public void onDisable() { Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this); - synchronized (abilitySuccessfulOps) { - abilitySuccessfulOps.clear(); - } - synchronized (abilityCheckOps) { - abilityCheckOps.clear(); - } + AbilityCheckTelemetry.clear(); WorldPolicyLatencyTelemetry.clear(); } - @EventHandler(priority = EventPriority.MONITOR) - public void onAdaptationUse(AdaptAdaptationUseEvent event) { - long now = System.currentTimeMillis(); - synchronized (abilityCheckOps) { - abilityCheckOps.addLast(now); - trimAbilityOps(abilityCheckOps, now); - } - if (!event.isCancelled()) { - synchronized (abilitySuccessfulOps) { - abilitySuccessfulOps.addLast(now); - trimAbilityOps(abilitySuccessfulOps, now); - } - } - } - @Override public String pluginId() { return "adapt"; @@ -190,23 +156,13 @@ private IntegrationMetricSample sampleSessionLoad(long now) { private IntegrationMetricSample sampleAbilityOps(long now) { IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_OPS); - long count; - synchronized (abilitySuccessfulOps) { - trimAbilityOps(abilitySuccessfulOps, now); - count = abilitySuccessfulOps.size(); - } - + long count = AbilityCheckTelemetry.successfulChecksPerMinute(now); return IntegrationMetricSample.available(descriptor, count, now); } private IntegrationMetricSample sampleAbilityCheckOps(long now) { IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS); - long count; - synchronized (abilityCheckOps) { - trimAbilityOps(abilityCheckOps, now); - count = abilityCheckOps.size(); - } - + long count = AbilityCheckTelemetry.checksPerMinute(now); return IntegrationMetricSample.available(descriptor, count, now); } @@ -215,10 +171,4 @@ private IntegrationMetricSample sampleWorldPolicyLatency(long now) { double averageMs = WorldPolicyLatencyTelemetry.averageMillis(now); return IntegrationMetricSample.available(descriptor, averageMs, now); } - - private void trimAbilityOps(ArrayDeque samples, long now) { - while (!samples.isEmpty() && (now - samples.peekFirst()) > ABILITY_WINDOW_MS) { - samples.removeFirst(); - } - } } diff --git a/src/main/java/art/arcane/adapt/service/CommandSVC.java b/src/main/java/art/arcane/adapt/service/CommandSVC.java index d6bbcc7fc..a0f05b8db 100644 --- a/src/main/java/art/arcane/adapt/service/CommandSVC.java +++ b/src/main/java/art/arcane/adapt/service/CommandSVC.java @@ -20,32 +20,22 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.command.CommandAdapt; +import art.arcane.adapt.util.cache.AtomicCache; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.adapt.util.common.plugin.VolmitSender; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.cache.AtomicCache; import art.arcane.adapt.util.decree.DecreeContext; import art.arcane.adapt.util.decree.DecreeContextHandler; import art.arcane.adapt.util.decree.DecreeSystem; import art.arcane.volmlib.util.director.compat.DirectorDecreeEngineFactory; import art.arcane.volmlib.util.director.context.DirectorContextRegistry; -import art.arcane.volmlib.util.director.runtime.DirectorExecutionMode; -import art.arcane.volmlib.util.director.runtime.DirectorExecutionResult; -import art.arcane.volmlib.util.director.runtime.DirectorInvocation; -import art.arcane.volmlib.util.director.runtime.DirectorInvocationHook; -import art.arcane.volmlib.util.director.runtime.DirectorRuntimeEngine; -import art.arcane.volmlib.util.director.runtime.DirectorRuntimeNode; -import art.arcane.volmlib.util.director.runtime.DirectorSender; +import art.arcane.volmlib.util.director.runtime.*; import art.arcane.volmlib.util.director.visual.DirectorVisualCommand; import art.arcane.volmlib.util.math.RNG; import org.bukkit.Sound; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.PluginCommand; -import org.bukkit.command.TabCompleter; +import org.bukkit.command.*; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java index 8264ba679..9f79ad980 100644 --- a/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java +++ b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java @@ -2,8 +2,8 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.content.gui.ConfigGui; -import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/src/main/java/art/arcane/adapt/service/HotloadSVC.java b/src/main/java/art/arcane/adapt/service/HotloadSVC.java index c620b77f0..b066a3286 100644 --- a/src/main/java/art/arcane/adapt/service/HotloadSVC.java +++ b/src/main/java/art/arcane/adapt/service/HotloadSVC.java @@ -1,6 +1,5 @@ package art.arcane.adapt.service; -import com.google.gson.JsonElement; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; @@ -10,29 +9,25 @@ import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.content.gui.ConfigGui; import art.arcane.adapt.content.gui.SkillsGui; -import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.project.config.ConfigRewriteReporter; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.volmlib.util.hotload.ConfigHotloadEngine; -import art.arcane.volmlib.util.io.IO; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.common.io.Json; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.adapt.util.common.plugin.AdaptService; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.adapt.util.project.config.ConfigRewriteReporter; +import art.arcane.volmlib.util.hotload.ConfigHotloadEngine; +import art.arcane.volmlib.util.io.IO; +import com.google.gson.JsonElement; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.entity.Player; import java.io.File; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; +import java.util.*; import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; diff --git a/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java index 9af8190df..6cd1d832a 100644 --- a/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java @@ -22,10 +22,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.data.B; - class Dictionary { //!\"#$%&[] private static final Pattern wordsPattern = Pattern.compile("[A-Z][A-Z][A-Z][A-Z]*"); diff --git a/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java b/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java index 5fce3fb15..af9fb0da9 100644 --- a/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java @@ -21,8 +21,6 @@ import java.io.Serializable; -import art.arcane.adapt.util.data.B; - /** * A Biset * diff --git a/src/main/java/art/arcane/adapt/util/common/data/B.java b/src/main/java/art/arcane/adapt/util/common/data/B.java index 3ffd76f8c..1a992626e 100644 --- a/src/main/java/art/arcane/adapt/util/common/data/B.java +++ b/src/main/java/art/arcane/adapt/util/common/data/B.java @@ -1,10 +1,10 @@ package art.arcane.adapt.util.data; +import art.arcane.adapt.core.nms.container.BlockProperty; +import art.arcane.adapt.util.reflect.registries.Materials; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.data.BSupport; -import art.arcane.adapt.core.nms.container.BlockProperty; -import art.arcane.adapt.util.reflect.registries.Materials; import it.unimi.dsi.fastutil.ints.IntSet; import org.bukkit.Material; import org.bukkit.block.data.BlockData; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java index 050a4fec0..524df5678 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree; -import art.arcane.volmlib.util.decree.context.DecreeContextBase; import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.volmlib.util.decree.context.DecreeContextBase; public class DecreeContext { private static final DecreeContextBase context = new DecreeContextBase<>(); diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java index 2c553de0b..f638c22b5 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java @@ -1,9 +1,9 @@ package art.arcane.adapt.util.decree; -import art.arcane.volmlib.util.decree.context.DecreeContextHandlers; -import art.arcane.volmlib.util.decree.context.DecreeContextHandlerType; import art.arcane.adapt.Adapt; import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.volmlib.util.decree.context.DecreeContextHandlerType; +import art.arcane.volmlib.util.decree.context.DecreeContextHandlers; import java.util.Map; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java index 95f124f13..e73ef9fa6 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java @@ -19,8 +19,8 @@ package art.arcane.adapt.util.decree; -import art.arcane.volmlib.util.decree.DecreeExecutorBase; import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.volmlib.util.decree.DecreeExecutorBase; import org.bukkit.entity.Player; public interface DecreeExecutor extends DecreeExecutorBase { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java index ff4b7183e..5cf9a71f0 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java @@ -18,9 +18,10 @@ */ package art.arcane.adapt.util.decree; -import art.arcane.volmlib.util.decree.DecreeSystemSupport; + import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.decree.DecreeSystemSupport; public final class DecreeSystem { public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); diff --git a/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java index 9cd3e68d5..559f62f01 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.context; -import art.arcane.volmlib.util.decree.context.WorldContextHandlerBase; import art.arcane.adapt.util.common.plugin.VolmitSender; import art.arcane.adapt.util.decree.DecreeContextHandler; +import art.arcane.volmlib.util.decree.context.WorldContextHandlerBase; import org.bukkit.World; public class WorldContextHandler extends WorldContextHandlerBase implements DecreeContextHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java index 42a8f7271..c810c55a2 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationListHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java index 8b10b0a9b..97d8435d6 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationProviderHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java index 3a331c382..a8dbf7025 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class AdaptationSkillListHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java index 2d3a98663..42b6e0b41 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.BooleanHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.BooleanHandlerBase; public class BooleanHandler extends BooleanHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java index a131ee75a..3aceab2f8 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.ByteHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.ByteHandlerBase; public class ByteHandler extends ByteHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java index 2149de645..0ad309151 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.DoubleHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.DoubleHandlerBase; public class DoubleHandler extends DoubleHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java index 44504b984..925d8fb8a 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.FloatHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.FloatHandlerBase; public class FloatHandler extends FloatHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java index de7fac054..3752a123e 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.IntegerHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.IntegerHandlerBase; public class IntegerHandler extends IntegerHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java index ca4589499..bccc76a1c 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.LongHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.LongHandlerBase; public class LongHandler extends LongHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java index 607118824..fdb27d09d 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java @@ -19,8 +19,8 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.OptionalWorldHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.OptionalWorldHandlerBase; public class OptionalWorldHandler extends OptionalWorldHandlerBase implements DecreeParameterHandler { @Override diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java index 87b49c1d4..5e19dbe0c 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import org.bukkit.Particle; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java index 2514a0cfe..323994254 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java @@ -20,8 +20,8 @@ package art.arcane.adapt.util.decree.handlers; import art.arcane.adapt.Adapt; -import art.arcane.volmlib.util.decree.handlers.base.PlayerHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.PlayerHandlerBase; import org.bukkit.Bukkit; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java index 14e8eecc2..c76c9410d 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.ShortHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.ShortHandlerBase; public class ShortHandler extends ShortHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java index d0f6f7f68..b0289d0bd 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; public class SkillProviderHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java index 4f3a03dc7..7be15cc8b 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.collection.KList; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import org.bukkit.Sound; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java index add795f3c..4ef876150 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.StringHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.StringHandlerBase; public class StringHandler extends StringHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java index e1de617f2..0aca961ca 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.handlers; -import art.arcane.volmlib.util.decree.handlers.base.WorldHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.WorldHandlerBase; import org.bukkit.World; public class WorldHandler extends WorldHandlerBase implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java index 07e8234a2..81467210d 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.decree.specialhandlers; -import art.arcane.volmlib.util.decree.handlers.base.DummyHandlerBase; import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.decree.handlers.base.DummyHandlerBase; public class DummyHandler extends DummyHandlerBase implements DecreeParameterHandler { } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java index 19b30a686..4458865ba 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.decree.specialhandlers; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import art.arcane.adapt.util.decree.DecreeParameterHandler; import art.arcane.adapt.util.decree.handlers.PlayerHandler; +import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; import org.bukkit.entity.Player; public class NullablePlayerHandler extends PlayerHandler implements DecreeParameterHandler { diff --git a/src/main/java/art/arcane/adapt/util/common/format/C.java b/src/main/java/art/arcane/adapt/util/common/format/C.java index 830e423c4..c4c50e578 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/C.java +++ b/src/main/java/art/arcane/adapt/util/common/format/C.java @@ -18,6 +18,7 @@ package art.arcane.adapt.util.common.format; +import art.arcane.adapt.util.common.plugin.VolmitSender; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import org.apache.commons.lang3.Validate; @@ -30,8 +31,6 @@ import java.util.Map; import java.util.regex.Pattern; -import art.arcane.adapt.util.common.plugin.VolmitSender; - /** * Colors * diff --git a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java index 3ef90ac6d..48496a651 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java +++ b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java @@ -18,12 +18,12 @@ package art.arcane.adapt.util.common.format; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; -import art.arcane.volmlib.util.io.IO; import art.arcane.adapt.util.config.ConfigFileSupport; +import art.arcane.volmlib.util.io.IO; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import lombok.SneakyThrows; import java.io.File; diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java index d00318dfc..f660c3786 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java @@ -19,8 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer2 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java index c68cbfbb3..908c90210 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer3 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java index ae7d9f4f7..946b75730 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer4 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java index 6cf87234d..15560ced3 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer5 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java index c35cbf840..4887e929b 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer6 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java index dccec4ea0..d57eb0414 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer7 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java index 5425fb8f8..71527d8f7 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer8 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function2.java b/src/main/java/art/arcane/adapt/util/common/function/Function2.java index 5c4975e10..3dd9f501a 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Function2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function2.java @@ -19,8 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Function2 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function3.java b/src/main/java/art/arcane/adapt/util/common/function/Function3.java index 179066e50..53e46f983 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Function3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function3.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Function3 { diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function4.java b/src/main/java/art/arcane/adapt/util/common/function/Function4.java index 69b9baac3..dd0e3f0ce 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Function4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function4.java @@ -19,9 +19,6 @@ package art.arcane.adapt.util.common.function; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.data.B; - @SuppressWarnings("hiding") @FunctionalInterface public interface Function4 { diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java index 00cff7fd0..b88ceaa0e 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java @@ -18,14 +18,13 @@ package art.arcane.adapt.util.common.inventorygui; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.volmlib.util.scheduling.Callback; import org.bukkit.inventory.ItemStack; import java.util.List; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; - public interface Element { MaterialBlock getMaterial(); diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java index 13b4f38b3..ad7f937c7 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java @@ -19,8 +19,6 @@ package art.arcane.adapt.util.common.inventorygui; -import art.arcane.adapt.util.reflect.events.api.Event; - /** * Element Event. * diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index e9eeafdbc..412b63f5f 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -1,13 +1,11 @@ package art.arcane.adapt.util.common.inventorygui; import art.arcane.adapt.Adapt; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; - import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.math.MaterialBlock; import art.arcane.adapt.util.common.scheduling.J; +import org.bukkit.Material; +import org.bukkit.entity.Player; public final class GuiConfirm { private GuiConfirm() { diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java index afccf7045..a9559a165 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java @@ -1,8 +1,7 @@ package art.arcane.adapt.util.common.inventorygui; -import org.bukkit.Material; - import art.arcane.adapt.util.common.math.MaterialBlock; +import org.bukkit.Material; public final class GuiTheme { private GuiTheme() { diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java index c2c2d9cb5..d17b1a2a6 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java @@ -18,15 +18,13 @@ package art.arcane.adapt.util.common.inventorygui; +import art.arcane.adapt.util.common.math.MaterialBlock; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; - -import art.arcane.adapt.util.common.math.MaterialBlock; - /** * Itemstack utilities * diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java index e54dcc5cd..262bacd48 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java @@ -17,21 +17,20 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.util.common.inventorygui; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.scheduling.Callback; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.reflect.registries.Enchantments; import art.arcane.adapt.util.reflect.registries.ItemFlags; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.scheduling.Callback; import org.bukkit.Material; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; - public class UIElement implements Element { private final String id; private final KList lore; diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java index 8efac7ad4..1188404d5 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java @@ -18,9 +18,8 @@ package art.arcane.adapt.util.common.inventorygui; -import org.bukkit.Material; - import art.arcane.adapt.util.common.math.MaterialBlock; +import org.bukkit.Material; public class UIStaticDecorator implements WindowDecorator { private final Element element; diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java index 993a47820..e4df36793 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java @@ -20,6 +20,7 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.scheduling.Callback; import lombok.Getter; import lombok.Setter; @@ -41,8 +42,6 @@ import java.util.Map; import java.util.Set; -import art.arcane.adapt.util.common.scheduling.J; - public class UIWindow implements Window, Listener { private final Player viewer; private final Map elements; diff --git a/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java index 2128b9abb..7e9aec40e 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java +++ b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java @@ -17,8 +17,8 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.util.common.io; -import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.format.Form; import com.google.gson.*; import org.bukkit.Bukkit; import org.bukkit.Location; diff --git a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java index 9d68c8d14..b26edcb8e 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java @@ -18,13 +18,12 @@ package art.arcane.adapt.util.common.io; +import art.arcane.adapt.util.common.function.Consumer3; import art.arcane.amulet.io.FolderWatcher; import java.io.File; import java.util.List; -import art.arcane.adapt.util.common.function.Consumer3; - public class ReactiveFolder { private final File folder; private final Consumer3, List, List> hotload; diff --git a/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java index f9a1329d8..fe8341b74 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java +++ b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java @@ -3,12 +3,7 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import java.util.UUID; public class SQLManager { diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java b/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java index 128634936..ddb142f39 100644 --- a/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java +++ b/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java @@ -1,14 +1,14 @@ package art.arcane.adapt.util.mantle; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.parallel.MultiBurst; +import art.arcane.adapt.util.mantle.io.IOWorker; import art.arcane.spatial.matter.Matter; import art.arcane.spatial.matter.MatterSlice; -import art.arcane.volmlib.util.function.Consumer4; -import art.arcane.volmlib.util.parallel.HyperLockSupport; -import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.function.Consumer4; import art.arcane.volmlib.util.io.IO; -import art.arcane.adapt.util.common.parallel.MultiBurst; -import art.arcane.adapt.util.mantle.io.IOWorker; +import art.arcane.volmlib.util.parallel.HyperLockSupport; import java.io.File; import java.io.IOException; diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java b/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java index e68e18cb5..c87b62388 100644 --- a/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java +++ b/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java @@ -1,11 +1,11 @@ package art.arcane.adapt.util.mantle; +import art.arcane.adapt.Adapt; import art.arcane.spatial.matter.Matter; import art.arcane.spatial.matter.MatterSlice; import art.arcane.spatial.matter.SpatialMatter; import art.arcane.volmlib.util.function.Consumer4; import art.arcane.volmlib.util.io.CountingDataInputStream; -import art.arcane.adapt.Adapt; import org.jetbrains.annotations.Nullable; import java.io.DataOutputStream; diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java b/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java index 1678ebffd..608f11e7e 100644 --- a/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java +++ b/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.mantle; -import art.arcane.volmlib.util.io.CountingDataInputStream; import art.arcane.adapt.Adapt; +import art.arcane.volmlib.util.io.CountingDataInputStream; import java.io.DataOutputStream; import java.io.IOException; diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java b/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java index 38fbcaa1e..28c2c2e78 100644 --- a/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java +++ b/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java @@ -1,14 +1,14 @@ package art.arcane.adapt.util.mantle.io; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.mantle.MantleChunk; +import art.arcane.adapt.util.mantle.TectonicPlate; import art.arcane.spatial.mantle.MantleRegion; import art.arcane.spatial.matter.Matter; import art.arcane.spatial.matter.MatterSlice; import art.arcane.volmlib.util.mantle.io.IOWorkerCodecSupport; import art.arcane.volmlib.util.mantle.io.IOWorkerRuntimeSupport; import art.arcane.volmlib.util.mantle.io.IOWorkerSupport; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.mantle.MantleChunk; -import art.arcane.adapt.util.mantle.TectonicPlate; import java.io.File; import java.io.IOException; diff --git a/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java index 51599387e..b27f8e20c 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java @@ -18,14 +18,14 @@ package art.arcane.adapt.util.common.math; +import art.arcane.adapt.util.common.nbt.NibbleArray; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.nbt.NibbleArray; - public abstract class DataPalette implements Writable { private static final int DEFAULT_BITS_PER_BLOCK = 4; private static final int CAPACITY = 4096; diff --git a/src/main/java/art/arcane/adapt/util/common/math/Direction.java b/src/main/java/art/arcane/adapt/util/common/math/Direction.java index a2ed3b29d..badd9e4f6 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Direction.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Direction.java @@ -18,6 +18,7 @@ package art.arcane.adapt.util.common.math; +import art.arcane.adapt.util.common.collection.GBiset; import art.arcane.volmlib.util.data.Cuboid.CuboidDirection; import art.arcane.volmlib.util.math.DOP; import org.bukkit.Axis; @@ -29,8 +30,6 @@ import java.util.List; import java.util.Map; -import art.arcane.adapt.util.common.collection.GBiset; - /** * Directions * diff --git a/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java index 73305e304..107086c73 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java +++ b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java @@ -477,7 +477,6 @@ public static int k(double var0) { return var0 > 0.0 ? 1 : -1; } - @Deprecated public static float j(float var0, float var1, float var2) { float var3; for (var3 = var1 - var0; var3 < -180.0f; var3 += 360.0f) { diff --git a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java index c000acea8..d2f4ba466 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java @@ -17,8 +17,9 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.util.common.math; -import art.arcane.volmlib.util.format.Form; +import art.arcane.adapt.util.common.collection.GListAdapter; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.CDou; import org.bukkit.Axis; import org.bukkit.Bukkit; @@ -30,8 +31,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.collection.GListAdapter; - /** * Vector utilities * diff --git a/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java index 51f9a0e41..6c09cecf2 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java @@ -1,13 +1,13 @@ package art.arcane.adapt.util.common.misc; +import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import com.fren_gor.ultimateAdvancementAPI.UltimateAdvancementAPI; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementDisplayWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementFrameTypeWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.packets.PacketPlayOutAdvancementsWrapper; -import art.arcane.adapt.Adapt; -import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import com.google.common.base.Preconditions; import org.bukkit.Material; import org.bukkit.entity.Player; diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Area.java b/src/main/java/art/arcane/adapt/util/common/misc/Area.java index db8639c7d..c6a25a4ff 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Area.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Area.java @@ -30,9 +30,6 @@ import java.util.List; import java.util.Random; - -import art.arcane.adapt.util.reflect.registries.Particles; - /** * Used to Create an instance of a spherical area based on a central location * Great for efficiently checking if an entity is within a spherical area. diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java index 1f9629100..e651e8c34 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java @@ -18,8 +18,9 @@ package art.arcane.adapt.util.common.misc; -import art.arcane.volmlib.util.scheduling.ChronoLatch; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.scheduling.Callback; +import art.arcane.volmlib.util.scheduling.ChronoLatch; import art.arcane.volmlib.util.scheduling.Contained; import java.util.List; @@ -28,8 +29,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import art.arcane.adapt.util.common.scheduling.J; - public class Chunker { private final List q; private ExecutorService executor; diff --git a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java index b78b97e8c..876af362f 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java @@ -1,13 +1,14 @@ package art.arcane.adapt.util.common.misc; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.io.IO; -import art.arcane.adapt.util.config.ConfigFileSupport; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -15,8 +16,6 @@ import java.io.File; import java.io.IOException; -import art.arcane.adapt.util.common.io.Json; - import static art.arcane.adapt.Adapt.instance; public record CustomModel(Material material, int model, NamespacedKey modelKey) { diff --git a/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java index 65d9792f5..04d163275 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java @@ -19,10 +19,9 @@ package art.arcane.adapt.util.common.misc; import art.arcane.adapt.Adapt; -import org.bukkit.ChatColor; - import art.arcane.adapt.util.common.format.HiddenStringUtils; import art.arcane.adapt.util.common.io.Json; +import org.bukkit.ChatColor; public class DirtyString { diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java index adf13b3a3..296a0d9f9 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java @@ -18,6 +18,7 @@ package art.arcane.adapt.util.common.misc; +import art.arcane.adapt.util.common.math.VectorMath; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; @@ -26,8 +27,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.math.VectorMath; - public class Impulse { private final List ignore; private double radius; diff --git a/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java index bbc6ab110..600a54ea0 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java @@ -14,8 +14,6 @@ import java.util.Collection; import java.util.List; -import art.arcane.adapt.util.common.scheduling.J; - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class SoundPlayer { diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java index 36b603e83..aa0b9de3f 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java @@ -18,6 +18,8 @@ package art.arcane.adapt.util.common.nbt; +import art.arcane.adapt.util.common.math.Writable; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -25,8 +27,6 @@ import java.util.Arrays; import java.util.StringJoiner; -import art.arcane.adapt.util.common.math.Writable; - public class NibbleArray implements Writable { private static final int[] MASKS = new int[8]; diff --git a/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java index 209daac7b..20b5b8823 100644 --- a/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java +++ b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java @@ -1,6 +1,7 @@ package art.arcane.adapt.util.common.parallel; import art.arcane.adapt.Adapt; + import java.util.concurrent.ExecutorService; import java.util.function.IntSupplier; diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java index 24a338d8e..e9b42fc8c 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java @@ -19,10 +19,15 @@ package art.arcane.adapt.util.common.plugin; import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.adaptation.AdaptationEventRegistrar; +import art.arcane.adapt.api.skill.SkillEventRegistrar; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.project.command.Control; +import art.arcane.adapt.util.project.command.IController; +import art.arcane.adapt.util.reflect.events.ReflectiveEvents; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; -import art.arcane.adapt.util.reflect.events.ReflectiveEvents; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -37,11 +42,6 @@ import java.lang.reflect.Modifier; import java.util.List; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.project.command.Command; -import art.arcane.adapt.util.project.command.Control; -import art.arcane.adapt.util.project.command.IController; - public abstract class VolmitPlugin extends JavaPlugin implements Listener { public static boolean bad = false; private int controllerTickerTaskId = -1; @@ -249,7 +249,9 @@ public void registerListener(Listener l) { if (bad) { return; } - Bukkit.getPluginManager().registerEvents(l, this); + if (!SkillEventRegistrar.register(this, l) && !AdaptationEventRegistrar.register(this, l)) { + Bukkit.getPluginManager().registerEvents(l, this); + } ReflectiveEvents.register(l); } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index d079ee3fb..11e64294b 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -18,13 +18,17 @@ */ package art.arcane.adapt.util.common.plugin; -import art.arcane.volmlib.util.format.Form; -import static art.arcane.amulet.MagicalSugar.*; + import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.AdventureCompat; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.project.command.CommandDummy; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.director.visual.DirectorVisualCommand; import art.arcane.volmlib.util.director.visual.DirectorVisualCommand.DirectorVisualParameter; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.RNG; import lombok.Getter; import lombok.Setter; @@ -41,18 +45,15 @@ import org.bukkit.plugin.Plugin; import java.time.Duration; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.format.AdventureCompat; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.project.command.Command; -import art.arcane.adapt.util.project.command.CommandDummy; - /** * Represents a volume sender. A command sender with extra crap in its * diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java index cf5792e26..83fbd1a65 100644 --- a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java @@ -4,8 +4,6 @@ import java.lang.reflect.Field; -import art.arcane.adapt.util.common.format.C; - public class WrappedField { private final Field field; diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java index 2522412ed..669432670 100644 --- a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java @@ -6,8 +6,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import art.arcane.adapt.util.common.format.C; - public final class WrappedReturningMethod { private final Method method; diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java b/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java index d4c94e66d..36af33d8a 100644 --- a/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java +++ b/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java @@ -1,14 +1,14 @@ package art.arcane.adapt.util.reflect.events; import art.arcane.adapt.Adapt; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; import art.arcane.adapt.util.reflect.Reflect; import art.arcane.adapt.util.reflect.events.api.Event; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; import art.arcane.adapt.util.reflect.events.api.entity.EntityMountEvent; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; import lombok.NonNull; import org.bukkit.event.EventException; import org.bukkit.event.HandlerList; diff --git a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java index 4ca169ef3..ee70a7d44 100644 --- a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java +++ b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java @@ -24,12 +24,7 @@ import art.arcane.adapt.util.common.function.NastyRunnable; import art.arcane.adapt.util.common.parallel.MultiBurst; import art.arcane.volmlib.util.math.FinalInteger; -import art.arcane.volmlib.util.scheduling.AR; -import art.arcane.volmlib.util.scheduling.FoliaScheduler; -import art.arcane.volmlib.util.scheduling.JSupport; -import art.arcane.volmlib.util.scheduling.SR; -import art.arcane.volmlib.util.scheduling.SchedulerBridge; -import art.arcane.volmlib.util.scheduling.StartupQueueSupport; +import art.arcane.volmlib.util.scheduling.*; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; diff --git a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java index b9df86f4d..8dc446705 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java @@ -1,7 +1,6 @@ package art.arcane.adapt.util.command; import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.plugin.VolmitSender; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java index c24ee2e06..5b3381aa5 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java @@ -18,6 +18,8 @@ package art.arcane.adapt.util.project.command; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.volmlib.util.collection.KList; import org.bukkit.Sound; @@ -26,9 +28,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.SoundPlayer; - /** * Represents a pawn command * diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java index 9cd51813b..b582e4267 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java @@ -18,6 +18,7 @@ package art.arcane.adapt.util.project.command; +import art.arcane.adapt.util.common.plugin.Permission; import org.bukkit.command.CommandSender; import java.lang.reflect.Field; @@ -26,8 +27,6 @@ import java.util.ArrayList; import java.util.List; -import art.arcane.adapt.util.common.plugin.Permission; - public abstract class MortarPermission { private MortarPermission parent; diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java index ca7f10e08..c8b88f9fa 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java @@ -18,6 +18,7 @@ package art.arcane.adapt.util.project.command; +import art.arcane.adapt.util.common.format.C; import lombok.Getter; import lombok.Setter; import org.bukkit.Server; @@ -33,8 +34,6 @@ import java.util.Set; import java.util.UUID; -import art.arcane.adapt.util.common.format.C; - /** * Represents a volume sender. A command sender with extra crap in it * diff --git a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java index 07a12a041..8a5962982 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java @@ -19,11 +19,12 @@ package art.arcane.adapt.util.project.command; import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.reflect.V; -import art.arcane.adapt.util.common.scheduling.J; -import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.command.CommandSender; @@ -31,10 +32,6 @@ import java.util.List; import java.util.Map; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.misc.SoundPlayer; - /** * Represents a virtual command. A chain of iterative processing through * subcommands. diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java index 119fc2203..af0591cb7 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java @@ -1,11 +1,7 @@ package art.arcane.adapt.util.config; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; public final class ConfigDocumentation { private static final Map SUMMARY_BY_KEY = Map.ofEntries( diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java index a15b5e728..d996e4cb2 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java @@ -1,10 +1,10 @@ package art.arcane.adapt.util.config; -import com.google.gson.JsonElement; import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.io.Json; import art.arcane.adapt.util.project.config.ConfigRewriteReporter; import art.arcane.volmlib.util.io.IO; -import art.arcane.adapt.util.common.io.Json; +import com.google.gson.JsonElement; import java.io.File; import java.io.IOException; diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java index c73b2214b..e651b9172 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java @@ -1,18 +1,12 @@ package art.arcane.adapt.util.project.config; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import art.arcane.adapt.Adapt; import art.arcane.adapt.util.config.ConfigFileSupport; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; public class ConfigRewriteReporter { private static final int MAX_KEYS_PER_CATEGORY = 8; diff --git a/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java index 081b93f0d..906b57a33 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java +++ b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java @@ -1,18 +1,14 @@ package art.arcane.adapt.util.config; -import com.google.gson.JsonElement; import art.arcane.adapt.util.common.io.Json; +import com.google.gson.JsonElement; import com.moandjiezana.toml.Toml; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public final class TomlCodec { private TomlCodec() { diff --git a/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java index f4da54172..30d888255 100644 --- a/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java +++ b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java @@ -1,7 +1,5 @@ package art.arcane.adapt.util.project.redis; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.world.PlayerData; @@ -9,6 +7,8 @@ import art.arcane.adapt.util.project.redis.codec.DataMessage; import art.arcane.adapt.util.project.redis.codec.DataRequest; import art.arcane.adapt.util.project.redis.codec.Message; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import io.lettuce.core.RedisClient; import io.lettuce.core.pubsub.api.reactive.ChannelMessage; import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java index 2da16ee6d..eb69d0cc4 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java @@ -33,15 +33,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; public class AdvancementUtils { From a80b07b2cd53751ad391f76d25ca9de94a78af92 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 17 Feb 2026 00:36:48 -0500 Subject: [PATCH 10/25] Decree'nt --- build.gradle.kts | 21 +- settings.gradle.kts | 18 + src/main/java/art/arcane/adapt/Adapt.java | 6 +- .../adapt/api/adaptation/Adaptation.java | 2 +- .../api/adaptation/AdaptationGuiSupport.java | 14 +- .../adapt/api/skill/SkillGuiSupport.java | 17 +- .../arcane/adapt/command/CommandAdapt.java | 130 ++--- .../arcane/adapt/command/CommandClear.java | 48 +- .../arcane/adapt/command/CommandDebug.java | 68 +-- .../arcane/adapt/command/CommandDefault.java | 56 +- .../arcane/adapt/command/CommandReset.java | 38 +- .../adaptation/agility/AgilityArmorUp.java | 2 +- .../agility/AgilityLadderSlide.java | 2 +- .../agility/AgilityParkourMomentum.java | 2 +- .../agility/AgilityRollLanding.java | 2 +- .../adaptation/agility/AgilitySuperJump.java | 2 +- .../adaptation/agility/AgilityWallJump.java | 2 +- .../adaptation/agility/AgilityWindUp.java | 2 +- .../architect/ArchitectElevator.java | 2 +- .../architect/ArchitectFoundation.java | 2 +- .../adaptation/architect/ArchitectGlass.java | 2 +- .../architect/ArchitectPlacement.java | 2 +- .../architect/ArchitectSmartShape.java | 2 +- .../architect/ArchitectWirelessRedstone.java | 2 +- .../adapt/content/adaptation/axe/AxeChop.java | 2 +- .../adaptation/axe/AxeCraftLogSwap.java | 2 +- .../adaptation/axe/AxeDropToInventory.java | 2 +- .../adaptation/axe/AxeGroundSmash.java | 2 +- .../adaptation/axe/AxeLeafVeinminer.java | 2 +- .../content/adaptation/axe/AxeTimberMark.java | 2 +- .../adaptation/axe/AxeWoodVeinminer.java | 2 +- .../blocking/BlockingBastionStance.java | 2 +- .../blocking/BlockingBulwarkBash.java | 2 +- .../blocking/BlockingChainArmorer.java | 2 +- .../blocking/BlockingCounterGuard.java | 2 +- .../blocking/BlockingHorseArmorer.java | 2 +- .../blocking/BlockingMirrorBlock.java | 2 +- .../blocking/BlockingMultiArmor.java | 2 +- .../blocking/BlockingSaddlecrafter.java | 2 +- .../adaptation/brewing/BrewingAbsorption.java | 2 +- .../adaptation/brewing/BrewingBlindness.java | 2 +- .../adaptation/brewing/BrewingDarkness.java | 2 +- .../adaptation/brewing/BrewingDecay.java | 2 +- .../adaptation/brewing/BrewingFatigue.java | 2 +- .../adaptation/brewing/BrewingHaste.java | 2 +- .../brewing/BrewingHealthBoost.java | 2 +- .../adaptation/brewing/BrewingHunger.java | 2 +- .../adaptation/brewing/BrewingLingering.java | 4 +- .../adaptation/brewing/BrewingNausea.java | 2 +- .../adaptation/brewing/BrewingResistance.java | 2 +- .../adaptation/brewing/BrewingSaturation.java | 2 +- .../brewing/BrewingSuperHeated.java | 2 +- .../chronos/ChronosAberrantTouch.java | 2 +- .../chronos/ChronosInstantRecall.java | 2 +- .../chronos/ChronosTemporalEcho.java | 2 +- .../adaptation/chronos/ChronosTimeBomb.java | 2 +- .../chronos/ChronosTimeInABottle.java | 2 +- .../crafting/CraftingBackpacks.java | 2 +- .../crafting/CraftingDeconstruction.java | 2 +- .../adaptation/crafting/CraftingLeather.java | 2 +- .../crafting/CraftingReconstruction.java | 2 +- .../adaptation/crafting/CraftingSkulls.java | 2 +- .../adaptation/crafting/CraftingStations.java | 2 +- .../adaptation/crafting/CraftingXP.java | 2 +- .../discovery/DiscoveryArchaeologist.java | 2 +- .../adaptation/discovery/DiscoveryArmor.java | 2 +- .../discovery/DiscoveryBetterMending.java | 2 +- .../discovery/DiscoveryCartographerPulse.java | 2 +- .../adaptation/discovery/DiscoveryUnity.java | 2 +- .../discovery/DiscoveryVillagerAtt.java | 2 +- .../discovery/DiscoveryXpResist.java | 2 +- .../enchanting/EnchantingAnvilSavant.java | 2 +- .../EnchantingBookshelfAttunement.java | 2 +- .../EnchantingGrindstoneRecovery.java | 2 +- .../enchanting/EnchantingLapisReturn.java | 2 +- .../enchanting/EnchantingOfferReroll.java | 2 +- .../enchanting/EnchantingQuickEnchant.java | 2 +- .../enchanting/EnchantingXPReturn.java | 2 +- .../excavation/ExcavationDropToInventory.java | 2 +- .../excavation/ExcavationHaste.java | 2 +- .../excavation/ExcavationOmniTool.java | 2 +- .../excavation/ExcavationSeismicPing.java | 2 +- .../excavation/ExcavationSpelunker.java | 2 +- .../herbalism/HerbalismBeeShepherd.java | 2 +- .../herbalism/HerbalismCompostCascade.java | 2 +- .../herbalism/HerbalismCraftableCobweb.java | 2 +- .../HerbalismCraftableMushroomBlocks.java | 2 +- .../herbalism/HerbalismDropToInventory.java | 2 +- .../herbalism/HerbalismGrowthAura.java | 2 +- .../herbalism/HerbalismHungryHippo.java | 2 +- .../herbalism/HerbalismHungryShield.java | 2 +- .../adaptation/herbalism/HerbalismLuck.java | 2 +- .../herbalism/HerbalismMyconid.java | 2 +- .../herbalism/HerbalismReplant.java | 2 +- .../herbalism/HerbalismRootedFooting.java | 2 +- .../herbalism/HerbalismSeedSower.java | 2 +- .../herbalism/HerbalismSporeBloom.java | 2 +- .../herbalism/HerbalismTerralid.java | 2 +- .../adaptation/hunter/HunterAdrenaline.java | 2 +- .../hunter/HunterDropToInventory.java | 2 +- .../adaptation/hunter/HunterInvis.java | 2 +- .../adaptation/hunter/HunterJumpBoost.java | 2 +- .../content/adaptation/hunter/HunterLuck.java | 2 +- .../adaptation/hunter/HunterRegen.java | 2 +- .../adaptation/hunter/HunterResistance.java | 2 +- .../adaptation/hunter/HunterSpeed.java | 2 +- .../adaptation/hunter/HunterStrength.java | 2 +- .../hunter/HunterTrophySkinner.java | 2 +- .../adaptation/nether/NetherBlazeLeech.java | 2 +- .../adaptation/nether/NetherFireResist.java | 2 +- .../adaptation/nether/NetherGhastWard.java | 2 +- .../adaptation/nether/NetherLavaWalker.java | 2 +- .../adaptation/nether/NetherPiglinBroker.java | 2 +- .../adaptation/nether/NetherSkullYeet.java | 2 +- .../adaptation/nether/NetherWitherResist.java | 2 +- .../adaptation/pickaxe/PickaxeAutosmelt.java | 2 +- .../adaptation/pickaxe/PickaxeChisel.java | 2 +- .../pickaxe/PickaxeDropToInventory.java | 2 +- .../pickaxe/PickaxeQuarrySense.java | 2 +- .../pickaxe/PickaxeSilkSpawner.java | 2 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 2 +- .../ranged/RangedArrowRecovery.java | 2 +- .../adaptation/ranged/RangedFloaters.java | 2 +- .../adaptation/ranged/RangedForce.java | 2 +- .../adaptation/ranged/RangedLungeShot.java | 2 +- .../adaptation/ranged/RangedPiercing.java | 2 +- .../adaptation/ranged/RangedPinningShot.java | 2 +- .../adaptation/ranged/RangedRicochetBolt.java | 2 +- .../ranged/RangedTrajectorySight.java | 2 +- .../adaptation/ranged/RangedWebBomb.java | 2 +- .../content/adaptation/rift/RiftAccess.java | 2 +- .../content/adaptation/rift/RiftBlink.java | 2 +- .../content/adaptation/rift/RiftDescent.java | 2 +- .../adaptation/rift/RiftEnderTaglock.java | 2 +- .../adaptation/rift/RiftEnderchest.java | 2 +- .../content/adaptation/rift/RiftGate.java | 2 +- .../rift/RiftInflatedPocketDimension.java | 2 +- .../content/adaptation/rift/RiftResist.java | 2 +- .../content/adaptation/rift/RiftVisage.java | 2 +- .../adaptation/rift/RiftVoidMagnet.java | 2 +- .../seaborrne/SeaborneFishersFantasy.java | 2 +- .../adaptation/seaborrne/SeaborneOxygen.java | 2 +- .../seaborrne/SeabornePressureDiver.java | 2 +- .../adaptation/seaborrne/SeaborneSpeed.java | 2 +- .../seaborrne/SeaborneTidecaller.java | 2 +- .../seaborrne/SeaborneTurtlesMiningSpeed.java | 2 +- .../seaborrne/SeaborneTurtlesVision.java | 2 +- .../adaptation/stealth/StealthEnderVeil.java | 2 +- .../adaptation/stealth/StealthGhostArmor.java | 2 +- .../stealth/StealthShadowDecoy.java | 2 +- .../adaptation/stealth/StealthSight.java | 2 +- .../adaptation/stealth/StealthSilentStep.java | 2 +- .../adaptation/stealth/StealthSnatch.java | 2 +- .../adaptation/stealth/StealthSpeed.java | 2 +- .../adaptation/sword/SwordsBloodyBlade.java | 2 +- .../sword/SwordsCrimsonCyclone.java | 2 +- .../adaptation/sword/SwordsDualWield.java | 2 +- .../sword/SwordsExecutionersEdge.java | 2 +- .../adaptation/sword/SwordsMachete.java | 2 +- .../adaptation/sword/SwordsPoisonedBlade.java | 2 +- .../adaptation/sword/SwordsRiposteWindow.java | 2 +- .../adaptation/taming/TamingBeastRecall.java | 2 +- .../adaptation/taming/TamingDamage.java | 2 +- .../adaptation/taming/TamingHealthBoost.java | 2 +- .../taming/TamingHealthRegeneration.java | 2 +- .../taming/TamingMountedTactics.java | 2 +- .../taming/TamingPackLeaderAura.java | 2 +- .../adaptation/taming/TamingSharedPain.java | 2 +- .../adaptation/tragoul/TragoulBloodPact.java | 2 +- .../tragoul/TragoulBoneHarvest.java | 2 +- .../adaptation/tragoul/TragoulGlobe.java | 2 +- .../adaptation/tragoul/TragoulHealing.java | 2 +- .../adaptation/tragoul/TragoulLance.java | 2 +- .../adaptation/tragoul/TragoulThorns.java | 2 +- .../unarmed/UnarmedBatteringCharge.java | 2 +- .../adaptation/unarmed/UnarmedComboChain.java | 2 +- .../unarmed/UnarmedGlassCannon.java | 2 +- .../adaptation/unarmed/UnarmedPower.java | 2 +- .../unarmed/UnarmedSuckerPunch.java | 2 +- .../arcane/adapt/content/gui/ConfigGui.java | 35 +- .../arcane/adapt/content/gui/SkillsGui.java | 17 +- .../content/item/multiItems/MultiItem.java | 2 +- .../art/arcane/adapt/service/CommandSVC.java | 37 +- .../art/arcane/adapt/service/HotloadSVC.java | 10 +- .../util/{common => }/cache/AtomicCache.java | 0 .../adapt/util/{common => }/cache/Cache.java | 0 .../adapt/util/common/collection/GBiset.java | 81 --- .../util/common/collection/GListAdapter.java | 60 -- .../art/arcane/adapt/util/common/data/B.java | 157 ------ .../adapt/util/common/data/TinyColor.java | 146 ----- .../util/common/decree/DecreeContext.java | 20 - .../common/decree/DecreeContextHandler.java | 20 - .../util/common/decree/DecreeExecutor.java | 34 -- .../common/decree/DecreeParameterHandler.java | 5 - .../decree/context/WorldContextHandler.java | 18 - .../decree/handlers/BlockVectorHandler.java | 39 -- .../decree/handlers/BooleanHandler.java | 7 - .../common/decree/handlers/ByteHandler.java | 7 - .../common/decree/handlers/DoubleHandler.java | 7 - .../common/decree/handlers/FloatHandler.java | 7 - .../decree/handlers/IntegerHandler.java | 7 - .../common/decree/handlers/LongHandler.java | 7 - .../common/decree/handlers/ShortHandler.java | 7 - .../common/decree/handlers/StringHandler.java | 7 - .../common/decree/handlers/VectorHandler.java | 39 -- .../common/decree/handlers/WorldHandler.java | 12 - .../decree/specialhandlers/DummyHandler.java | 7 - .../NullablePlayerHandler.java | 22 - .../arcane/adapt/util/common/format/C.java | 47 +- .../adapt/util/common/function/Consumer3.java | 26 - .../adapt/util/common/function/Function3.java | 26 - .../util/common/function/NastyFunction.java | 23 - .../util/common/function/NastyFuture.java | 23 - .../util/common/function/NastyRunnable.java | 23 - .../util/common/inventorygui/Element.java | 80 --- .../common/inventorygui/ElementEvent.java | 34 -- .../util/common/inventorygui/GuiConfirm.java | 7 +- .../util/common/inventorygui/GuiEffects.java | 3 + .../util/common/inventorygui/GuiTheme.java | 6 +- .../util/common/inventorygui/UIElement.java | 286 ---------- .../inventorygui/UIStaticDecorator.java | 35 -- .../common/inventorygui/UIVoidDecorator.java | 27 - .../util/common/inventorygui/UIWindow.java | 529 ------------------ .../util/common/inventorygui/Window.java | 95 ---- .../common/inventorygui/WindowDecorator.java | 23 - .../common/inventorygui/WindowResolution.java | 53 -- .../adapt/util/common/io/ReactiveFolder.java | 2 +- .../adapt/util/common/math/Direction.java | 2 +- .../adapt/util/common/math/VectorMath.java | 2 +- .../util/common/plugin/VolmitSender.java | 12 +- .../adapt/util/common/scheduling/J.java | 354 +----------- .../util/{common => }/data/Metadata.java | 0 .../DirectorSystem.java} | 19 +- .../context/AdaptationListingHandler.java | 2 +- .../handlers/AdaptationListHandler.java | 12 +- .../handlers/AdaptationProviderHandler.java | 12 +- .../handlers/AdaptationSkillListHandler.java | 12 +- .../director/handlers/BlockVectorHandler.java | 39 ++ .../director/handlers/BooleanHandler.java | 7 + .../util/director/handlers/ByteHandler.java | 7 + .../util/director/handlers/DoubleHandler.java | 7 + .../util/director/handlers/FloatHandler.java | 7 + .../director/handlers/IntegerHandler.java | 7 + .../util/director/handlers/LongHandler.java | 7 + .../handlers/OptionalWorldHandler.java | 8 +- .../handlers/ParticleHandler.java | 12 +- .../handlers/PlayerHandler.java | 8 +- .../util/director/handlers/ShortHandler.java | 7 + .../handlers/SkillProviderHandler.java | 12 +- .../handlers/SoundHandler.java | 12 +- .../util/director/handlers/StringHandler.java | 7 + .../util/director/handlers/VectorHandler.java | 39 ++ .../util/director/handlers/WorldHandler.java | 12 + .../specialhandlers/DummyHandler.java | 7 + .../NullablePlayerHandler.java | 22 + .../util/{common => }/reflect/Reflect.java | 0 .../{common => }/reflect/WrappedField.java | 0 .../reflect/WrappedReturningMethod.java | 0 .../reflect/events/ReflectiveEvents.java | 0 .../reflect/events/api/Event.java | 0 .../reflect/events/api/ReflectiveHandler.java | 0 .../api/entity/EndermanAttackPlayerEvent.java | 0 .../api/entity/EntityDismountEvent.java | 0 .../events/api/entity/EntityEvent.java | 0 .../events/api/entity/EntityMountEvent.java | 0 .../reflect/registries/Attributes.java | 0 .../reflect/registries/Enchantments.java | 0 .../reflect/registries/EntityTypes.java | 0 .../reflect/registries/ItemFlags.java | 0 .../reflect/registries/Materials.java | 0 .../reflect/registries/Particles.java | 0 .../reflect/registries/PotionEffectTypes.java | 0 .../reflect/registries/PotionTypes.java | 0 .../reflect/registries/RegistryUtil.java | 0 274 files changed, 727 insertions(+), 2848 deletions(-) rename src/main/java/art/arcane/adapt/util/{common => }/cache/AtomicCache.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/cache/Cache.java (100%) delete mode 100644 src/main/java/art/arcane/adapt/util/common/collection/GBiset.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/data/B.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/data/TinyColor.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/function/Consumer3.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/function/Function3.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java rename src/main/java/art/arcane/adapt/util/{common => }/data/Metadata.java (100%) rename src/main/java/art/arcane/adapt/util/{common/decree/DecreeSystem.java => director/DirectorSystem.java} (56%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/context/AdaptationListingHandler.java (99%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/AdaptationListHandler.java (60%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/AdaptationProviderHandler.java (60%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/AdaptationSkillListHandler.java (60%) create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/BooleanHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/ByteHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/DoubleHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/FloatHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/IntegerHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/LongHandler.java rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/OptionalWorldHandler.java (78%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/ParticleHandler.java (55%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/PlayerHandler.java (84%) create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/ShortHandler.java rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/SkillProviderHandler.java (59%) rename src/main/java/art/arcane/adapt/util/{common/decree => director}/handlers/SoundHandler.java (55%) create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/StringHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/specialhandlers/DummyHandler.java create mode 100644 src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java rename src/main/java/art/arcane/adapt/util/{common => }/reflect/Reflect.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/WrappedField.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/WrappedReturningMethod.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/ReflectiveEvents.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/Event.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/ReflectiveHandler.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/entity/EndermanAttackPlayerEvent.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/entity/EntityDismountEvent.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/entity/EntityEvent.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/events/api/entity/EntityMountEvent.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/Attributes.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/Enchantments.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/EntityTypes.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/ItemFlags.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/Materials.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/Particles.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/PotionEffectTypes.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/PotionTypes.java (100%) rename src/main/java/art/arcane/adapt/util/{common => }/reflect/registries/RegistryUtil.java (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 0dc1a7692..869b84894 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,9 @@ plugins { version = "2.0.0-1.20.2-1.21.11-Dev1" val apiVersion = "1.20" val main = "art.arcane.adapt.Adapt" +val volmLibCoordinate: String = providers.gradleProperty("volmLibCoordinate") + .orElse("com.github.VolmitSoftware:VolmLib:master-SNAPSHOT") + .get() // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -136,18 +139,12 @@ allprojects { } } -sourceSets { - main { - java.srcDir("../VolmLib/shared/src/main/java") - } -} - -kotlin.sourceSets.named("main") { - kotlin.srcDir("../VolmLib/shared/src/main/kotlin") -} - dependencies { implementation(project(":velocity")) + implementation(volmLibCoordinate) { + isChanging = true + isTransitive = false + } implementation(slimjarHelper("spigot")) implementation(slimjarHelper("velocity")) implementation(libs.platformUtils) { @@ -221,8 +218,8 @@ tasks.shadowJar { } configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor(60, "minutes") - resolutionStrategy.cacheDynamicVersionsFor(60, "minutes") + resolutionStrategy.cacheChangingModulesFor(0, "seconds") + resolutionStrategy.cacheDynamicVersionsFor(0, "seconds") } if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 0712309ed..e141bc658 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,23 @@ +import java.io.File + rootProject.name = "Adapt" +val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib") + .orElse("true") + .map { value: String -> value.equals("true", ignoreCase = true) } + .get() +val localVolmLibDirectory: File = file("../VolmLib") + +if (useLocalVolmLib && localVolmLibDirectory.resolve("settings.gradle.kts").exists()) { + includeBuild(localVolmLibDirectory) { + dependencySubstitution { + substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared")) + substitute(module("com.github.VolmitSoftware.VolmLib:shared")).using(project(":shared")) + substitute(module("com.github.VolmitSoftware.VolmLib:volmlib-shared")).using(project(":shared")) + } + } +} + include( ":velocity", ) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index ec98323d5..1a3c6f6d5 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -34,7 +34,7 @@ import art.arcane.adapt.content.protector.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.io.SQLManager; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.plugin.AdaptService; @@ -71,7 +71,7 @@ import java.util.Random; import java.util.function.Supplier; -import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; +import static art.arcane.adapt.util.director.context.AdaptationListingHandler.initializeAdaptationListings; public class Adapt extends VolmitPlugin { public static Adapt instance; @@ -92,7 +92,7 @@ public class Adapt extends VolmitPlugin { @Getter private ProtectorRegistry protectorRegistry; @Getter - private Map guiLeftovers = new HashMap<>(); + private Map guiLeftovers = new HashMap<>(); @Getter private AdvancementManager manager; diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index eb9d492e4..8b1fc74d1 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -27,7 +27,7 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.Ticked; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.volmlib.util.math.M; import org.bukkit.Location; diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index 99bc1577f..1c84c1699 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -24,8 +24,14 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.*; -import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Window; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; @@ -199,7 +205,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { long k = adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).getKnowledge(); - Window w = new UIWindow(player); + UIWindow w = new UIWindow(Adapt.instance, player); GuiTheme.apply(w, "skill/" + adaptation.getSkill().getName() + "/" + adaptation.getName()); w.setViewportHeight(plan.rows()); @@ -220,7 +226,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { boolean pendingPermanentConfirm = isPermanentLearnConfirmationPending(player, adaptation, lvl); Element de = new UIElement("lp-" + lvl + "g") .setMaterial(new MaterialBlock(adaptation.getIcon())) - .setModel(adaptation.getModel(lvl)) + .setBaseItemStack(adaptation.getModel(lvl).toItemStack()) .setName(adaptation.getDisplayName(lvl)) .setEnchanted(mylevel >= lvl) .setProgress(1D) diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index 3b3818388..135357eb1 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -26,16 +26,17 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.inventorygui.GuiEffects; import art.arcane.adapt.util.common.inventorygui.GuiLayout; import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.adapt.util.common.inventorygui.UIElement; -import art.arcane.adapt.util.common.inventorygui.UIWindow; -import art.arcane.adapt.util.common.inventorygui.Window; -import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.Window; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.format.ColorFormatter; import art.arcane.volmlib.util.format.Form; import org.bukkit.Material; import org.bukkit.Sound; @@ -145,7 +146,7 @@ static void openGui(Skill skill, Player player, int page) { int start = currentPage * plan.itemsPerPage(); int end = Math.min(visibleAdaptations.size(), start + plan.itemsPerPage()); - Window window = new UIWindow(player); + UIWindow window = new UIWindow(Adapt.instance, player); GuiTheme.apply(window, "skill/" + skill.getName()); window.setViewportHeight(plan.rows()); @@ -168,7 +169,7 @@ static void openGui(Skill skill, Player player, int page) { int pos = GuiLayout.centeredPosition(i, rowCount); Element element = new UIElement("ada-" + adaptation.getName()) .setMaterial(new MaterialBlock(adaptation.getIcon())) - .setModel(adaptation.getModel()) + .setBaseItemStack(adaptation.getModel().toItemStack()) .setName(adaptation.getDisplayName(level)) .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) @@ -251,7 +252,7 @@ private static String normalizeSortKey(String value) { return ""; } - String normalized = C.stripColor(value).toLowerCase(Locale.ROOT).trim(); + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); } diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index 3311c897a..6ae087249 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -14,12 +14,12 @@ import art.arcane.adapt.content.item.KnowledgeOrb; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.config.ConfigMigrationManager; -import art.arcane.adapt.util.decree.DecreeExecutor; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; +import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; import org.bukkit.entity.Player; import java.util.HashMap; @@ -27,14 +27,14 @@ import java.util.Map; import java.util.concurrent.ThreadLocalRandom; -@Decree(name = "adapt", description = "Basic Command") -public class CommandAdapt implements DecreeExecutor { +@Director(name = "adapt", description = "Basic Command") +public class CommandAdapt { private CommandDebug debug; private CommandClear clear; private CommandReset reset; private CommandDefault defaults; - @Decree(description = "Boost Target player Experience gain.") + @Director(description = "Boost Target player Experience gain.") public void boost( @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") int seconds, @@ -43,45 +43,45 @@ public void boost( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player ) { - if (!sender().hasPermission("adapt.boost")) { - FConst.error("You lack the Permission 'adapt.boost'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.boost")) { + FConst.error("You lack the Permission 'adapt.boost'").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; - if (targetPlayer == null && sender().isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(sender()); + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); return; } else if (targetPlayer == null) { - targetPlayer = player(); + targetPlayer = BukkitDirectorContext.player(); } AdaptServer adaptServer = Adapt.instance.getAdaptServer(); PlayerData playerData = adaptServer.getPlayer(targetPlayer).getData(); playerData.globalXPMultiplier(multiplier, seconds * 1000); - FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(sender()); + FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); } - @Decree(description = "Boost Global Experience gain.", name = "global-boost") + @Director(description = "Boost Global Experience gain.", name = "global-boost") public void globalBoost( @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") int seconds, @Param(aliases = "multiplier", description = "Strength of the boost ", defaultValue = "10") double multiplier ) { - if (!sender().hasPermission("adapt.boost.global")) { - FConst.error("You lack the Permission 'adapt.boost.global'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.boost.global")) { + FConst.error("You lack the Permission 'adapt.boost.global'").send(BukkitDirectorContext.sender()); return; } AdaptServer adaptServer = Adapt.instance.getAdaptServer(); adaptServer.boostXP(multiplier, seconds * 1000); - FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(sender()); + FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); } - @Decree(description = "Open the Adapt GUI") + @Director(description = "Open the Adapt GUI") public void gui( @Param(aliases = "target", defaultValue = "[Main]") AdaptationListingHandler.AdaptationList guiTarget, @@ -90,17 +90,17 @@ public void gui( @Param(aliases = "force", defaultValue = "false") boolean force ) { - if (!sender().hasPermission("adapt.gui")) { - FConst.error("You lack the Permission 'adapt.gui'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.gui")) { + FConst.error("You lack the Permission 'adapt.gui'").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; - if (targetPlayer == null && sender().isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(sender()); + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); return; } else if (targetPlayer == null) { - targetPlayer = player(); + targetPlayer = BukkitDirectorContext.player(); } if (guiTarget.equals("[Main]")) { @@ -112,9 +112,9 @@ public void gui( for (Skill skill : SkillRegistry.skills.sortV()) { if (guiTarget.equals("[Skill]-" + skill.getName())) { if (force || skill.openGui(targetPlayer, true)) { - FConst.success("Opened GUI for " + skill.getName() + " for " + targetPlayer.getName()).send(sender()); + FConst.success("Opened GUI for " + skill.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } else { - FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(sender()); + FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); } return; } @@ -129,9 +129,9 @@ public void gui( } if (guiTarget.equals("[Adaptation]-" + adaptation.getName())) { if (force || adaptation.openGui(targetPlayer, true)) { - FConst.success("Opened GUI for " + adaptation.getName() + " for " + targetPlayer.getName()).send(sender()); + FConst.success("Opened GUI for " + adaptation.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } else { - FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(sender()); + FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); } return; } @@ -140,17 +140,17 @@ public void gui( } } - @Decree(name = "configure", aliases = {"config", "cfg"}, origin = DecreeOrigin.PLAYER, description = "Open the in-game Adapt config editor") + @Director(name = "configure", aliases = {"config", "cfg"}, origin = DirectorOrigin.PLAYER, description = "Open the in-game Adapt config editor") public void configure() { - if (!ConfigGui.canConfigure(player())) { - FConst.error("You need operator status or the permission 'adapt.configurator'").send(sender()); + if (!ConfigGui.canConfigure(BukkitDirectorContext.player())) { + FConst.error("You need operator status or the permission 'adapt.configurator'").send(BukkitDirectorContext.sender()); return; } - ConfigGui.open(player()); + ConfigGui.open(BukkitDirectorContext.player()); } - @Decree(description = "Give yourself an experience orb") + @Director(description = "Give yourself an experience orb") public void experience( @Param(aliases = "skill") AdaptationListingHandler.AdaptationSkillList skillName, @@ -160,18 +160,18 @@ public void experience( Player player ) { - if (!sender().hasPermission("adapt.cheatitem")) { - FConst.error("You lack the Permission 'adapt.cheatitem'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { + FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; if (targetPlayer == null) { - if (sender().isPlayer()) { - targetPlayer = player(); + if (BukkitDirectorContext.isPlayer()) { + targetPlayer = BukkitDirectorContext.player(); } else { - FConst.error("You must be a player to use this command, or Reference a player").send(sender()); + FConst.error("You must be a player to use this command, or Reference a player").send(BukkitDirectorContext.sender()); return; } } @@ -182,30 +182,30 @@ public void experience( experienceMap.put(skill.getName(), (double) amount); } targetPlayer.getInventory().addItem(ExperienceOrb.with(experienceMap)); - FConst.success("Giving all orbs").send(sender()); + FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); return; } if (skillName.equals("[random]")) { List> skills = allSkillSnapshot(); if (skills.isEmpty()) { - FConst.error("No skills are registered.").send(sender()); + FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); return; } targetPlayer.getInventory().addItem(ExperienceOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); - FConst.success("Giving random orb").send(sender()); + FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); return; } Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); if (skill != null) { targetPlayer.getInventory().addItem(ExperienceOrb.with(skill.getName(), amount)); - FConst.success("Giving " + skill.getName() + " orb").send(sender()); + FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); } } - @Decree(description = "Give yourself a knowledge orb") + @Director(description = "Give yourself a knowledge orb") public void knowledge( @Param(aliases = "skill") AdaptationListingHandler.AdaptationSkillList skillName, @@ -214,17 +214,17 @@ public void knowledge( @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player ) { - if (!sender().hasPermission("adapt.cheatitem")) { - FConst.error("You lack the Permission 'adapt.cheatitem'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { + FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; if(targetPlayer == null){ - if (sender().isPlayer()) { - targetPlayer = player(); + if (BukkitDirectorContext.isPlayer()) { + targetPlayer = BukkitDirectorContext.player(); } else { - FConst.error("You must be a player to use this command").send(sender()); + FConst.error("You must be a player to use this command").send(BukkitDirectorContext.sender()); return; } } @@ -235,30 +235,30 @@ public void knowledge( knowledgeMap.put(skill.getName(), amount); } targetPlayer.getInventory().addItem(KnowledgeOrb.with(knowledgeMap)); - FConst.success("Giving all orbs").send(sender()); + FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); return; } if (skillName.equals("[random]")){ List> skills = allSkillSnapshot(); if (skills.isEmpty()) { - FConst.error("No skills are registered.").send(sender()); + FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); return; } targetPlayer.getInventory().addItem(KnowledgeOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); - FConst.success("Giving random orb").send(sender()); + FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); return; } Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); if(skill != null){ targetPlayer.getInventory().addItem(KnowledgeOrb.with(skill.getName(), amount)); - FConst.success("Giving " + skill.getName() + " orb").send(sender()); + FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); } } - @Decree(description = "Assign a skill, or UnAssign a skill as if you are learning / unlearning a skill.") + @Director(description = "Assign a skill, or UnAssign a skill as if you are learning / unlearning a skill.") public void determine( @Param(aliases = "adaptationTarget") AdaptationListingHandler.AdaptationProvider adaptationTarget, @@ -272,16 +272,16 @@ public void determine( Player player ) { - if (!sender().hasPermission("adapt.determine")) { - FConst.error("You lack the Permission 'adapt.determine'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; - if (targetPlayer == null && sender().isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(sender()); + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); } else if (targetPlayer == null) { - targetPlayer = player(); + targetPlayer = BukkitDirectorContext.player(); } //the format is skillname:adaptationname @@ -300,7 +300,7 @@ public void determine( adaptation.unlearn(player, level, force); } } else { - FConst.error("You must specify a player when using this command from console.").send(sender()); + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); } return; } @@ -310,15 +310,15 @@ public void determine( } } - @Decree(name = "migrate-configs", description = "Force migrate and rewrite all skill/adaptation configs to canonical TOML with comments.") + @Director(name = "migrate-configs", description = "Force migrate and rewrite all skill/adaptation configs to canonical TOML with comments.") public void migrateConfigs() { - if (!sender().hasPermission("adapt.debug")) { - FConst.error("You lack the Permission 'adapt.debug'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.debug")) { + FConst.error("You lack the Permission 'adapt.debug'").send(BukkitDirectorContext.sender()); return; } if (Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - FConst.error("Adapt server is not ready yet. Try again in a few seconds.").send(sender()); + FConst.error("Adapt server is not ready yet. Try again in a few seconds.").send(BukkitDirectorContext.sender()); return; } @@ -341,7 +341,7 @@ public void migrateConfigs() { } int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); - FConst.success("Canonicalized TOML configs. skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson).send(sender()); + FConst.success("Canonicalized TOML configs. skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson).send(BukkitDirectorContext.sender()); } private List> allSkillSnapshot() { diff --git a/src/main/java/art/arcane/adapt/command/CommandClear.java b/src/main/java/art/arcane/adapt/command/CommandClear.java index 6f46239e5..f2d2429fa 100644 --- a/src/main/java/art/arcane/adapt/command/CommandClear.java +++ b/src/main/java/art/arcane/adapt/command/CommandClear.java @@ -3,17 +3,17 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.util.command.FConst; -import art.arcane.adapt.util.decree.DecreeExecutor; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; import org.bukkit.entity.Player; -@Decree(name = "clear", origin = DecreeOrigin.BOTH, description = "Clear player progression data") -public class CommandClear implements DecreeExecutor { +@Director(name = "clear", origin = DirectorOrigin.BOTH, description = "Clear player progression data") +public class CommandClear { - @Decree(description = "Clear all player data (XP, knowledge, adaptations, stats, discoveries, advancements, wisdom)") + @Director(description = "Clear all player data (XP, knowledge, adaptations, stats, discoveries, advancements, wisdom)") public void all( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -23,10 +23,10 @@ public void all( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearAll(); - FConst.success("Cleared all data for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared all data for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } - @Decree(description = "Clear XP across all skill lines") + @Director(description = "Clear XP across all skill lines") public void xp( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -36,10 +36,10 @@ public void xp( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearXp(); - FConst.success("Cleared XP for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared XP for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } - @Decree(description = "Clear knowledge across all skill lines") + @Director(description = "Clear knowledge across all skill lines") public void knowledge( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -49,10 +49,10 @@ public void knowledge( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearKnowledge(); - FConst.success("Cleared knowledge for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared knowledge for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } - @Decree(description = "Unlearn all adaptations across all skill lines") + @Director(description = "Unlearn all adaptations across all skill lines") public void adaptations( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -62,10 +62,10 @@ public void adaptations( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearAdaptations(); - FConst.success("Cleared adaptations for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared adaptations for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } - @Decree(description = "Clear the stats map") + @Director(description = "Clear the stats map") public void stats( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -75,10 +75,10 @@ public void stats( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearStats(); - FConst.success("Cleared stats for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared stats for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } - @Decree(description = "Clear all discovery data (biomes, mobs, foods, items, recipes, etc.)") + @Director(description = "Clear all discovery data (biomes, mobs, foods, items, recipes, etc.)") public void discoveries( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player @@ -88,12 +88,12 @@ public void discoveries( PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); data.clearDiscoveries(); - FConst.success("Cleared discoveries for " + targetPlayer.getName()).send(sender()); + FConst.success("Cleared discoveries for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } private Player resolveTarget(Player player) { - if (!sender().hasPermission("adapt.clear")) { - FConst.error("You lack the Permission 'adapt.clear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.clear")) { + FConst.error("You lack the Permission 'adapt.clear'").send(BukkitDirectorContext.sender()); return null; } @@ -101,11 +101,11 @@ private Player resolveTarget(Player player) { return player; } - if (sender().isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(sender()); + if (BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); return null; } - return player(); + return BukkitDirectorContext.player(); } } diff --git a/src/main/java/art/arcane/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java index 2839eaa98..7f748c95d 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -4,34 +4,34 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.adapt.util.decree.DecreeExecutor; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Player; import java.util.List; -@Decree(name = "debug", origin = DecreeOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) -public class CommandDebug implements DecreeExecutor { +@Director(name = "debug", origin = DirectorOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) +public class CommandDebug { - @Decree(description = "Toggle verbose mode") + @Director(description = "Toggle verbose mode") public void verbose() { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } AdaptConfig.get().setVerbose(!AdaptConfig.get().isVerbose()); - FConst.success("Verbose is now " + (AdaptConfig.get().isVerbose() ? "enabled" : "disabled")).send(sender()); + FConst.success("Verbose is now " + (AdaptConfig.get().isVerbose() ? "enabled" : "disabled")).send(BukkitDirectorContext.sender()); } - @Decree(name = "pap", description = "Generate Perms for Adaptations!") + @Director(name = "pap", description = "Generate Perms for Adaptations!") public void pap() { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } @@ -42,13 +42,13 @@ public void pap() { .replaceAll("-", "")) .append("\n"))); Adapt.info("Permissions: \n" + builder); - FConst.success("Permissions have been printed to console.").send(sender()); + FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); } - @Decree(name = "psp", description = "Generate Perms for Skills!") + @Director(name = "psp", description = "Generate Perms for Skills!") public void psp() { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } @@ -59,55 +59,55 @@ public void psp() { .replaceAll("-", "")) .append("\n")); Adapt.info("Permissions: \n" + builder); - FConst.success("Permissions have been printed to console.").send(sender()); + FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); } - @Decree(name = "particle", origin = DecreeOrigin.PLAYER, description = "Summon a particle in front of you for testing!") + @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") public void particle(@Param Particle particle) { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } - Player player = player(); + Player player = BukkitDirectorContext.player(); player.spawnParticle(particle, player.getLocation(), 10, 10); } - @Decree(name = "particle", origin = DecreeOrigin.PLAYER, description = "Summon a particle in front of you for testing!") + @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") public void particle(@Param Sound sound) { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } - SoundPlayer sp = SoundPlayer.of(player()); - sp.play(player().getLocation(), sound, 1, 1); + SoundPlayer sp = SoundPlayer.of(BukkitDirectorContext.player()); + sp.play(BukkitDirectorContext.player().getLocation(), sound, 1, 1); } - @Decree(description = "Show Adapt ticker hotspots") + @Director(description = "Show Adapt ticker hotspots") public void perf( @Param(description = "Top results to print", defaultValue = "12") int top, @Param(description = "Reset metrics after printing", defaultValue = "false") boolean reset ) { - if (!sender().hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(sender()); + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); return; } List lines = Adapt.instance.getTicker().topMetrics(top); long windowMs = Adapt.instance.getTicker().getMetricsWindowMs(); - FConst.success("Ticker window: " + windowMs + "ms").send(sender()); + FConst.success("Ticker window: " + windowMs + "ms").send(BukkitDirectorContext.sender()); if (lines.isEmpty()) { - FConst.success("No tick metrics collected yet.").send(sender()); + FConst.success("No tick metrics collected yet.").send(BukkitDirectorContext.sender()); } else { - lines.forEach(line -> FConst.info(line).send(sender())); + lines.forEach(line -> FConst.info(line).send(BukkitDirectorContext.sender())); } if (reset) { Adapt.instance.getTicker().resetMetrics(); - FConst.success("Ticker metrics reset.").send(sender()); + FConst.success("Ticker metrics reset.").send(BukkitDirectorContext.sender()); } } } diff --git a/src/main/java/art/arcane/adapt/command/CommandDefault.java b/src/main/java/art/arcane/adapt/command/CommandDefault.java index 86d0bc167..e3d63e8a0 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDefault.java +++ b/src/main/java/art/arcane/adapt/command/CommandDefault.java @@ -7,11 +7,11 @@ import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.util.command.FConst; -import art.arcane.adapt.util.decree.DecreeExecutor; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; import java.io.File; import java.io.IOException; @@ -20,59 +20,59 @@ import java.text.SimpleDateFormat; import java.util.Date; -@Decree(name = "default", origin = DecreeOrigin.BOTH, description = "Reset configs to defaults") -public class CommandDefault implements DecreeExecutor { +@Director(name = "default", origin = DirectorOrigin.BOTH, description = "Reset configs to defaults") +public class CommandDefault { - @Decree(description = "Reset a skill config to defaults") + @Director(description = "Reset a skill config to defaults") public void skill( @Param(description = "skill to reset") AdaptationListingHandler.SkillProvider skillTarget ) { - if (!sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(sender()); + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); return; } Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillTarget.name()); if (skill == null) { - FConst.error("Unknown skill: " + skillTarget.name()).send(sender()); + FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); return; } if (!(skill instanceof SimpleSkill simpleSkill)) { - FConst.error("Skill " + skill.getName() + " does not support config reset.").send(sender()); + FConst.error("Skill " + skill.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); return; } File configFile = Adapt.instance.getDataFile("adapt", "skills", skill.getName() + ".toml"); if (configFile.exists() && !configFile.delete()) { - FConst.error("Failed to delete config file for " + skill.getName()).send(sender()); + FConst.error("Failed to delete config file for " + skill.getName()).send(BukkitDirectorContext.sender()); return; } simpleSkill.reloadConfigFromDisk(false); - FConst.success("Reset config for skill " + skill.getName() + " to defaults.").send(sender()); + FConst.success("Reset config for skill " + skill.getName() + " to defaults.").send(BukkitDirectorContext.sender()); } - @Decree(description = "Reset an adaptation config to defaults") + @Director(description = "Reset an adaptation config to defaults") public void adaptation( @Param(description = "adaptation to reset (skill:adaptation)") AdaptationListingHandler.AdaptationProvider adaptationTarget ) { - if (!sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(sender()); + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); return; } String[] split = adaptationTarget.name().split(":"); if (split.length != 2) { - FConst.error("Invalid format. Use skill:adaptation").send(sender()); + FConst.error("Invalid format. Use skill:adaptation").send(BukkitDirectorContext.sender()); return; } Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(split[0]); if (skill == null) { - FConst.error("Unknown skill: " + split[0]).send(sender()); + FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); return; } @@ -85,29 +85,29 @@ public void adaptation( } if (adaptation == null) { - FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(sender()); + FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); return; } if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { - FConst.error("Adaptation " + adaptation.getName() + " does not support config reset.").send(sender()); + FConst.error("Adaptation " + adaptation.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); return; } File configFile = Adapt.instance.getDataFile("adapt", "adaptations", adaptation.getName() + ".toml"); if (configFile.exists() && !configFile.delete()) { - FConst.error("Failed to delete config file for " + adaptation.getName()).send(sender()); + FConst.error("Failed to delete config file for " + adaptation.getName()).send(BukkitDirectorContext.sender()); return; } simpleAdaptation.reloadConfigFromDisk(false); - FConst.success("Reset config for adaptation " + adaptation.getName() + " to defaults.").send(sender()); + FConst.success("Reset config for adaptation " + adaptation.getName() + " to defaults.").send(BukkitDirectorContext.sender()); } - @Decree(description = "Reset ALL configs to defaults and archive the old settings") + @Director(description = "Reset ALL configs to defaults and archive the old settings") public void all() { - if (!sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(sender()); + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); return; } @@ -178,8 +178,8 @@ public void all() { } } - FConst.success("Archived " + archived + " config files to config-archive/" + timestamp + "/").send(sender()); - FConst.success("Reset " + reset + " configs to defaults.").send(sender()); + FConst.success("Archived " + archived + " config files to config-archive/" + timestamp + "/").send(BukkitDirectorContext.sender()); + FConst.success("Reset " + reset + " configs to defaults.").send(BukkitDirectorContext.sender()); } private boolean archiveFile(File source, File destination) { diff --git a/src/main/java/art/arcane/adapt/command/CommandReset.java b/src/main/java/art/arcane/adapt/command/CommandReset.java index 2002a0cc2..61cb51b53 100644 --- a/src/main/java/art/arcane/adapt/command/CommandReset.java +++ b/src/main/java/art/arcane/adapt/command/CommandReset.java @@ -3,41 +3,41 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.command.FConst; -import art.arcane.adapt.util.decree.DecreeExecutor; -import art.arcane.adapt.util.decree.specialhandlers.NullablePlayerHandler; -import art.arcane.volmlib.util.decree.DecreeOrigin; -import art.arcane.volmlib.util.decree.annotations.Decree; -import art.arcane.volmlib.util.decree.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; +import art.arcane.volmlib.util.director.DirectorOrigin; +import art.arcane.volmlib.util.director.annotations.Director; +import art.arcane.volmlib.util.director.annotations.Param; import org.bukkit.entity.Player; import java.util.HashMap; import java.util.Map; import java.util.UUID; -@Decree(name = "reset", origin = DecreeOrigin.BOTH, description = "Permanently delete all Adapt data for a player") -public class CommandReset implements DecreeExecutor { +@Director(name = "reset", origin = DirectorOrigin.BOTH, description = "Permanently delete all Adapt data for a player") +public class CommandReset { private static final Map pendingConfirmations = new HashMap<>(); private static final long CONFIRMATION_TIMEOUT_MS = 30_000; - @Decree(description = "Permanently delete all Adapt data for a player. Requires op. Run twice to confirm.") + @Director(description = "Permanently delete all Adapt data for a player. Requires op. Run twice to confirm.") public void confirm( @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) Player player ) { - if (!sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(sender()); + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); return; } Player targetPlayer = player; - if (targetPlayer == null && sender().isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(sender()); + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); return; } else if (targetPlayer == null) { - targetPlayer = player(); + targetPlayer = BukkitDirectorContext.player(); } - UUID senderUuid = sender().isPlayer() ? player().getUniqueId() : new UUID(0, 0); + UUID senderUuid = BukkitDirectorContext.isPlayer() ? BukkitDirectorContext.player().getUniqueId() : new UUID(0, 0); UUID targetUuid = targetPlayer.getUniqueId(); long now = System.currentTimeMillis(); @@ -47,15 +47,15 @@ public void confirm( AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(targetPlayer); adaptPlayer.delete(targetUuid); - Adapt.info("Operator " + sender().getName() + " reset all Adapt data for " + targetPlayer.getName()); - FConst.success("All Adapt data for " + targetPlayer.getName() + " has been permanently deleted.").send(sender()); + Adapt.info("Operator " + BukkitDirectorContext.name() + " reset all Adapt data for " + targetPlayer.getName()); + FConst.success("All Adapt data for " + targetPlayer.getName() + " has been permanently deleted.").send(BukkitDirectorContext.sender()); return; } pendingConfirmations.put(senderUuid, new PendingReset(targetUuid, now)); - FConst.error("WARNING: This will permanently delete ALL Adapt data for " + targetPlayer.getName() + ".").send(sender()); - FConst.error("This includes XP, skills, adaptations, discoveries, stats, and advancements.").send(sender()); - FConst.error("Run this command again within 30 seconds to confirm.").send(sender()); + FConst.error("WARNING: This will permanently delete ALL Adapt data for " + targetPlayer.getName() + ".").send(BukkitDirectorContext.sender()); + FConst.error("This includes XP, skills, adaptations, discoveries, stats, and advancements.").send(BukkitDirectorContext.sender()); + FConst.error("Run this command again within 30 seconds to confirm.").send(BukkitDirectorContext.sender()); } private static class PendingReset { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java index cceb533f7..8ff769a1f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java index 0ee68c63e..a58422249 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java index 3b35d9bc6..f056af4e9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java index 7770a55cc..c15ef2634 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java index 92326c9b4..b9101de1f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java index 24cddf145..e58051550 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index 8c18154a6..0c48e333c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java index b1e94b495..c8957196d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java @@ -27,7 +27,7 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java index 4c41f7291..edd39b271 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java index 07a960feb..61bf0ef13 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java index b4cfc29b4..fb2676db2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java index 0d74bc92d..88eabd521 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index cb752efe4..3d486224c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.item.BoundRedstoneTorch; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java index a48148676..ba5e59c6e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java index 40f94303d..2ca1a5ade 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java index 93edb57b3..6951eacf3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java index e5c02d4ff..e8c3bb78f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.Impulse; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index 030548cc3..84888f83d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java index f9617cefe..149bb4608 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index 0bb4e5716..758b11a7a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -28,7 +28,7 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java index 97534ea44..441051f2d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java index b25b97ec3..74b34453c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java index c77868f83..8633ece01 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java index 2c2da58f0..5a5beb130 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java index b9848c844..3f1a1e36a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java index 87be1143b..a15ef7158 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java index 4dd7547f4..2fd7fbd12 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.multiItems.MultiArmor; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java index e97c8ca18..072c54ca6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java index 5f723af55..68efa62da 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java index 4cbb0136f..e6100a001 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java index e62ed069f..8a0203dd5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java index 9c447f1c5..e6787c99f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java index fd9cd8df3..8685d7404 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java index 68ca6e635..53493e670 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import art.arcane.adapt.util.reflect.registries.PotionTypes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java index 6704bf49a..68dd53f72 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java index 7fd0dfe54..523485c32 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java index f4b5da082..da827b0d2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java @@ -29,12 +29,12 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.function.Function3; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.ItemFlags; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.function.Function3; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java index 60b91d170..cc8537ceb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java index 21b8bc866..bb83e415b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Color; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java index 2a3e66faf..d3d09c0a4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java index 6cd60da78..f0d65779a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java @@ -29,7 +29,7 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java index dc9f149f4..1acb1db34 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index dcfe34f94..f72b16009 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index a7a321988..a12ed7115 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index 92a73bf69..b251cdf07 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.math.M; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java index e573ddf3d..5ba858a3e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.ChronoTimeBottle; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Keyed; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java index 34c541b6a..03dd3197a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java index d4e02166a..4c2fa3b95 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java @@ -22,7 +22,7 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java index 52031c1df..39d7abcda 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java @@ -23,7 +23,7 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java index 8749fdf1e..156cc69cc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java index c4111eec2..ed79a1b08 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java index 070fff118..2565eb5be 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java @@ -22,7 +22,7 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java index c7a68bf71..dcb761fc5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java @@ -22,7 +22,7 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java index bef482852..7b04f3ebf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java index 5b654b367..0ca29a616 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.Sphere; import art.arcane.adapt.util.common.math.VectorMath; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java index eab5a542c..29d25d7a9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java index aee390f56..e8fe8441b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java index f9ff0c985..f1fb9552e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java @@ -27,7 +27,7 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java index e8c476c53..554465bff 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java index 1f291bec0..59068ae85 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java index 581caa312..de808e690 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java index 0a81cb25b..155305596 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java index 006915497..01d5da6d4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java index 4f56c19df..6b18d0334 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java index c388c167f..ff5886718 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java index 4627dfd33..1e409d72b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KMap; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java index 0fe511a96..96045d663 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java index 87ad3f3cf..00588a20e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java index 5bb098950..50b709df1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index 205b9cf96..0110c6245 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.item.multiItems.OmniTool; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java index 507e90433..9d9b72fa0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index 0da602162..ce3c291eb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java index ca1e26761..1de3fa155 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index 8a7ed7016..5710cec18 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java index 119d2cc84..d81515b36 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java index cce5532cf..e2c8865d8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java index a85181d3a..121899f05 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java index 0fe5525e9..805efaecd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java index 3186e2e0e..16756a090 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java index e05dbd155..e20fb114a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java index e493eefcd..c5928923e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java index 1d4e41079..29edeaa29 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index 59e20b878..e9c45a2ed 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.skill.SkillHerbalism; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java index c6693c31c..adcb4d6d1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java index 3437f94e9..e55d2c295 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index 1eb139880..83d57e045 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java index 392bcf7a2..3a1d4310c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java index 7db81cb90..a044f83a0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java index e825947a7..d9ede317a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java index 404fbc4b9..5f8ed9b2a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java index 94449efbf..949041c4a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java index 123b19c79..9d56ea8d9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java index ffa446efa..40bd61b88 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java index 2cea2edc6..79664e6fd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java index 0bdfe684d..2b2b997a7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java index 7a44e908d..0f9b8ca9a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java index 5cb5b98e7..1546571c5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java index 8ac07f79b..9cab3f5ac 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java index e6399682f..aebc7b399 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java @@ -23,7 +23,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.Data; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java index 3900cf102..212aeb592 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java index ae9a5b6a1..ad937cb71 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java index f6176c336..77e772c7c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java index b1874cc5c..45c003965 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.math.M; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java index e1aa6e143..7fe480ca8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java index 738cd8aff..0df705dc2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Enchantments; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java index 3f5cc1c8f..d4920cad0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java index c82e94391..1860bc9ff 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java @@ -25,7 +25,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index d012dca9a..912a009bc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java index b99914f81..a23133087 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java @@ -6,7 +6,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index ea415a414..17cc821a9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -29,7 +29,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java index 00692b89f..602f71390 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java @@ -7,7 +7,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Enchantments; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java index 251107732..a85a9ac88 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java index 36d7d1e1d..2ba27a324 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java index 91b83c6b3..c25b57c2a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java index 2a0c58224..64a61242d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java index 8e77a6217..05e93e808 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java index 7ce72947b..9e85a072a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index 0137aac21..7b522abfb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -28,7 +28,7 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import fr.skytasul.glowingentities.GlowingEntities; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java index 9a825eed2..fa5f819c7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.item.BoundSnowBall; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java index adf83811a..8f2620ca7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.item.BoundEnderPearl; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.EqualsAndHashCode; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index 5871e55e7..c35f99ceb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java index 3161071e4..bbffda3c0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java index ed4e5f9a8..8afc70707 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.BoundEnderPearl; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java index 875391030..503ddf9ea 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java index c623fc356..aaa0480dd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java @@ -28,7 +28,7 @@ import art.arcane.adapt.content.item.BoundEyeOfEnder; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index f87227ff4..2b1a5a09e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java index 54dc4aea0..4c5791493 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java index ebcf9a4a1..0553d7645 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java @@ -6,7 +6,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index 2adcca51f..a5011bbe6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java index 5365c0ac6..114119701 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java @@ -26,7 +26,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java index f6ea23d73..f0dd5f41f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index 7cce3c5b1..ce3b4565c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java index 3052a7188..9894fda29 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java index 73133b8e1..21de4ba47 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java index 0c27a4d56..797c155b6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java index 40af171c9..9bca9bcfe 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; import org.bukkit.Material; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java index 46816c996..f7041b8ac 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java @@ -6,7 +6,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java index df7b153d6..44e7a2aca 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index e96259a27..9d881e8b6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index 2e3eb9d1c..9e3636563 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java index bb8a92d8e..5cde74410 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import fr.skytasul.glowingentities.GlowingEntities; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index e4c2f8410..ecbc1c8bb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.inventorygui.Inventories; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index 8e9f17d1c..e75809e42 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java index 7da9f94b1..ce0899475 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java index dbad1be17..54f862ff0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java index 948359773..21263b887 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java index e5fe91546..502015813 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index 10cd9e425..1a1e87613 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java index 24fe5a7b9..4d5678671 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java @@ -27,7 +27,7 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import de.slikey.effectlib.effect.BleedEffect; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java index bb98ffe38..17ccb779d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index ea14ac8fb..290e300e9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java index cad1997e9..9d16e8e17 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java index a5bfd8e0b..44404b38f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java index c8453a923..c718d1c66 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java index 5c9e09c75..70d262ea1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index 28161fdfa..5c8451623 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java index af8975051..ba27ea9b5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java index fffac6b7b..4fd58692c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java index 7791eacad..6c73b7c5f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java index 0bb592304..f54b497d3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java index 08326f9dc..cde2cb298 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java index 86e128721..bfaff8c0d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java @@ -23,7 +23,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java index 4e0627dd7..e3b55ce5b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java @@ -26,7 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import de.slikey.effectlib.effect.BleedEffect; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index a48275f3e..61aadee10 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java index 974b4955d..f15edc016 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java @@ -25,7 +25,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java index 44335c5fd..ef00a0582 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java index a188877db..fd6bd58eb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; import lombok.NoArgsConstructor; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java index f7ec9cf9d..7aad04d5a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java @@ -24,7 +24,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index 0ca0e8f4a..15b2ec56a 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -8,8 +8,14 @@ import art.arcane.adapt.service.ConfigInputSVC; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.*; -import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.volmlib.util.data.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Window; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; @@ -17,6 +23,7 @@ import art.arcane.adapt.util.config.TomlCodec; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; +import art.arcane.volmlib.util.format.ColorFormatter; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; @@ -337,7 +344,7 @@ private static ElementDescriptor describe(Field field, Object value) { return new ElementDescriptor(ElementKind.UNSUPPORTED, false); } - private static UIElement createElementForEntry(Player player, String sectionPath, int currentPage, FieldEntry entry) { + private static Element createElementForEntry(Player player, String sectionPath, int currentPage, FieldEntry entry) { Material material = materialFor(entry); String typePrefix = switch (entry.descriptor().kind()) { case BOOLEAN -> C.GREEN + "[Boolean] "; @@ -352,7 +359,7 @@ private static UIElement createElementForEntry(Player player, String sectionPath String name = displayName(entry.field().getName()); String value = summarizeValue(entry.value()); - UIElement element = new UIElement("cfg-" + entry.path()) + Element element = new UIElement("cfg-" + entry.path()) .setMaterial(new MaterialBlock(material)) .setName(typePrefix + C.WHITE + name); element.addLore(C.GRAY + "Value: " + C.AQUA + value); @@ -555,7 +562,7 @@ private static void openFieldEntries(Player player, String safePath, List normalizeSortKey(e.displayName()))); - openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), page, "Configure: " + C.stripColor(skill.getDisplayName()), entries); + openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), page, "Configure: " + ColorFormatter.stripColor(skill.getDisplayName()), entries); } private static void openAdaptationIndex(Player player, int page) { @@ -748,7 +755,7 @@ private static void openAdaptationIndex(Player player, int page) { entries.add(new SectionIndexEntry( ROOT_ADAPTATIONS + "." + adaptation.getName(), - C.stripColor(adaptation.getDisplayName()), + ColorFormatter.stripColor(adaptation.getDisplayName()), adaptation.getIcon(), "Skill: " + adaptation.getSkill().getName() )); @@ -765,7 +772,7 @@ private static void openSectionIndex(Player player, String sectionPath, int page int start = currentPage * plan.itemsPerPage(); int end = Math.min(entries.size(), start + plan.itemsPerPage()); - Window w = new UIWindow(player); + UIWindow w = new UIWindow(Adapt.instance, player); GuiTheme.apply(w, tagForSection(safePath)); w.setViewportHeight(plan.rows()); @@ -1557,7 +1564,7 @@ private static List> getLoadedSkills() { } List> skills = new ArrayList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); - skills.sort(Comparator.comparing(skill -> normalizeSortKey(C.stripColor(skill.getDisplayName())))); + skills.sort(Comparator.comparing(skill -> normalizeSortKey(ColorFormatter.stripColor(skill.getDisplayName())))); return skills; } @@ -1575,7 +1582,7 @@ private static List> getLoadedAdaptations() { } } - adaptations.sort(Comparator.comparing(adaptation -> normalizeSortKey(C.stripColor(adaptation.getDisplayName())))); + adaptations.sort(Comparator.comparing(adaptation -> normalizeSortKey(ColorFormatter.stripColor(adaptation.getDisplayName())))); return adaptations; } @@ -1584,7 +1591,7 @@ private static String normalizeSortKey(String value) { return ""; } - String normalized = C.stripColor(value).toLowerCase(Locale.ROOT).trim(); + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); } diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index fa2de3430..91226af93 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -26,9 +26,16 @@ import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.*; -import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.adapt.util.common.inventorygui.GuiEffects; +import art.arcane.adapt.util.common.inventorygui.GuiLayout; +import art.arcane.adapt.util.common.inventorygui.GuiTheme; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Window; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.format.ColorFormatter; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -92,7 +99,7 @@ public static void open(Player player, int page) { int start = currentPage * plan.itemsPerPage(); int end = Math.min(entries.size(), start + plan.itemsPerPage()); - Window w = new UIWindow(player); + UIWindow w = new UIWindow(Adapt.instance, player); GuiTheme.apply(w, "/"); w.setViewportHeight(plan.rows()); @@ -115,7 +122,7 @@ public static void open(Player player, int page) { int pos = GuiLayout.centeredPosition(i, rowCount); Element element = new UIElement("skill-" + entry.skill().getName()) .setMaterial(new MaterialBlock(entry.skill().getIcon())) - .setModel(entry.skill().getModel()) + .setBaseItemStack(entry.skill().getModel().toItemStack()) .setName(entry.skill().getDisplayName(entry.line().getLevel())) .setProgress(1D) .addLore(C.ITALIC + "" + C.GRAY + entry.skill().getDescription()) @@ -164,7 +171,7 @@ private static String normalizeSortKey(String value) { return ""; } - String normalized = C.stripColor(value).toLowerCase(Locale.ROOT).trim(); + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); } diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java index 5891866a6..8dee0144c 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java @@ -20,7 +20,7 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.nms.NMS; -import art.arcane.adapt.util.common.inventorygui.WindowResolution; +import art.arcane.volmlib.util.inventorygui.WindowResolution; import art.arcane.adapt.util.common.io.BukkitGson; import art.arcane.volmlib.util.collection.KList; import lombok.*; diff --git a/src/main/java/art/arcane/adapt/service/CommandSVC.java b/src/main/java/art/arcane/adapt/service/CommandSVC.java index a0f05b8db..77e1b67fe 100644 --- a/src/main/java/art/arcane/adapt/service/CommandSVC.java +++ b/src/main/java/art/arcane/adapt/service/CommandSVC.java @@ -26,15 +26,15 @@ import art.arcane.adapt.util.common.plugin.AdaptService; import art.arcane.adapt.util.common.plugin.VolmitSender; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.decree.DecreeContext; -import art.arcane.adapt.util.decree.DecreeContextHandler; -import art.arcane.adapt.util.decree.DecreeSystem; -import art.arcane.volmlib.util.director.compat.DirectorDecreeEngineFactory; +import art.arcane.adapt.util.director.DirectorSystem; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.volmlib.util.director.compat.DirectorEngineFactory; import art.arcane.volmlib.util.director.context.DirectorContextRegistry; import art.arcane.volmlib.util.director.runtime.*; import art.arcane.volmlib.util.director.visual.DirectorVisualCommand; import art.arcane.volmlib.util.math.RNG; import org.bukkit.Sound; +import org.bukkit.World; import org.bukkit.command.*; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -42,7 +42,6 @@ import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Optional; public class CommandSVC implements AdaptService, CommandExecutor, TabCompleter, DirectorInvocationHook { @@ -72,35 +71,27 @@ public void onDisable() { } public DirectorRuntimeEngine getDirector() { - return directorCache.aquireNastyPrint(() -> DirectorDecreeEngineFactory.create( + return directorCache.aquireNastyPrint(() -> DirectorEngineFactory.create( new CommandAdapt(), null, buildDirectorContexts(), this::dispatchDirector, this, - DecreeSystem.handlers + DirectorSystem.handlers )); } private DirectorContextRegistry buildDirectorContexts() { DirectorContextRegistry contexts = new DirectorContextRegistry(); - - for (Map.Entry, DecreeContextHandler> entry : DecreeContextHandler.contextHandlers.entrySet()) { - registerContextHandler(contexts, entry.getKey(), entry.getValue()); - } - - return contexts; - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private void registerContextHandler(DirectorContextRegistry contexts, Class type, DecreeContextHandler handler) { - contexts.register((Class) type, (invocation, map) -> { - if (invocation.getSender() instanceof BukkitDirectorSender sender) { - return ((DecreeContextHandler) handler).handle(new VolmitSender(sender.sender())); + contexts.register(World.class, (invocation, map) -> { + if (invocation.getSender() instanceof BukkitDirectorSender sender && sender.sender() instanceof Player player) { + return player.getWorld(); } return null; }); + + return contexts; } private void dispatchDirector(DirectorExecutionMode mode, Runnable runnable) { @@ -114,13 +105,13 @@ private void dispatchDirector(DirectorExecutionMode mode, Runnable runnable) { @Override public void beforeInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { if (invocation.getSender() instanceof BukkitDirectorSender sender) { - DecreeContext.touch(new VolmitSender(sender.sender())); + BukkitDirectorContext.touch(sender.sender()); } } @Override public void afterInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { - DecreeContext.remove(); + BukkitDirectorContext.remove(); } @Nullable @@ -182,7 +173,7 @@ private boolean sendHelpIfRequested(CommandSender sender, String[] args) { } VolmitSender volmitSender = new VolmitSender(sender); - volmitSender.sendDecreeHelp(request.get().command(), request.get().page()); + volmitSender.sendDirectorHelp(request.get().command(), request.get().page()); return true; } diff --git a/src/main/java/art/arcane/adapt/service/HotloadSVC.java b/src/main/java/art/arcane/adapt/service/HotloadSVC.java index b066a3286..81252625b 100644 --- a/src/main/java/art/arcane/adapt/service/HotloadSVC.java +++ b/src/main/java/art/arcane/adapt/service/HotloadSVC.java @@ -11,7 +11,7 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.adapt.util.common.inventorygui.Window; +import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.io.Json; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.plugin.AdaptService; @@ -29,7 +29,7 @@ import java.nio.file.Files; import java.util.*; -import static art.arcane.adapt.util.decree.context.AdaptationListingHandler.initializeAdaptationListings; +import static art.arcane.adapt.util.director.context.AdaptationListingHandler.initializeAdaptationListings; public class HotloadSVC implements AdaptService { private static final long WATCHER_POLL_MS = 500; @@ -423,10 +423,10 @@ private String relativizeToDataFolder(File file) { private void refreshOpenAdaptGuis() { J.s(() -> { - Map open = new HashMap<>(Adapt.instance.getGuiLeftovers()); - for (Map.Entry entry : open.entrySet()) { + Map open = new HashMap<>(Adapt.instance.getGuiLeftovers()); + for (Map.Entry entry : open.entrySet()) { String playerKey = entry.getKey(); - Window window = entry.getValue(); + UIWindow window = entry.getValue(); if (window == null) { continue; } diff --git a/src/main/java/art/arcane/adapt/util/common/cache/AtomicCache.java b/src/main/java/art/arcane/adapt/util/cache/AtomicCache.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/cache/AtomicCache.java rename to src/main/java/art/arcane/adapt/util/cache/AtomicCache.java diff --git a/src/main/java/art/arcane/adapt/util/common/cache/Cache.java b/src/main/java/art/arcane/adapt/util/cache/Cache.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/cache/Cache.java rename to src/main/java/art/arcane/adapt/util/cache/Cache.java diff --git a/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java b/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java deleted file mode 100644 index af9fb0da9..000000000 --- a/src/main/java/art/arcane/adapt/util/common/collection/GBiset.java +++ /dev/null @@ -1,81 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.collection; - - -import java.io.Serializable; - -/** - * A Biset - * - * @param the first object type - * @param the second object type - * @author cyberpwn - */ -@SuppressWarnings("hiding") -public class GBiset implements Serializable { - private static final long serialVersionUID = 1L; - private A a; - private B b; - - /** - * Create a new Biset - * - * @param a the first object - * @param b the second object - */ - public GBiset(A a, B b) { - this.a = a; - this.b = b; - } - - /** - * Get the object of the type A - * - * @return the first object - */ - public A getA() { - return a; - } - - /** - * Set the first object - * - * @param a the first object A - */ - public void setA(A a) { - this.a = a; - } - - /** - * Get the second object - * - * @return the second object - */ - public B getB() { - return b; - } - - /** - * Set the second object - */ - public void setB(B b) { - this.b = b; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java b/src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java deleted file mode 100644 index 0175113c6..000000000 --- a/src/main/java/art/arcane/adapt/util/common/collection/GListAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.collection; - - -import java.util.ArrayList; -import java.util.List; - -/** - * Adapts a list of objects into a list of other objects - * - * @param the from object in lists (the item INSIDE the list) - * @param the to object in lists (the item INSIDE the list) - * @author cyberpwn - */ -public abstract class GListAdapter { - /** - * Adapts a list of FROM to a list of TO - * - * @param from the from list - * @return the to list - */ - public List adapt(List from) { - List adapted = new ArrayList<>(); - - for (FROM i : from) { - TO t = onAdapt(i); - - if (t != null) { - adapted.add(onAdapt(i)); - } - } - - return adapted; - } - - /** - * Adapts a list object FROM to TO for use with the adapt method - * - * @param from the from object - * @return the to object - */ - public abstract TO onAdapt(FROM from); -} diff --git a/src/main/java/art/arcane/adapt/util/common/data/B.java b/src/main/java/art/arcane/adapt/util/common/data/B.java deleted file mode 100644 index 1a992626e..000000000 --- a/src/main/java/art/arcane/adapt/util/common/data/B.java +++ /dev/null @@ -1,157 +0,0 @@ -package art.arcane.adapt.util.data; - -import art.arcane.adapt.core.nms.container.BlockProperty; -import art.arcane.adapt.util.reflect.registries.Materials; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.data.BSupport; -import it.unimi.dsi.fastutil.ints.IntSet; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; - -import java.util.List; - -public class B { - private static final BSupportImpl BASE = new BSupportImpl(); - - private static final class BSupportImpl extends BSupport { - @Override - protected Material shortGrassMaterial() { - return Materials.GRASS; - } - - @Override - protected void appendExtraFoliageMaterials(IntSet foliage) { - foliage.add(Materials.CHERRY_SAPLING.ordinal()); - foliage.add(Materials.PALE_OAK_SAPLING.ordinal()); - } - } - - public static BlockData toDeepSlateOre(BlockData block, BlockData ore) { - return BASE.toDeepSlateOre(block, ore); - } - - public static boolean isDeepSlate(BlockData blockData) { - return BASE.isDeepSlate(blockData); - } - - public static boolean isOre(BlockData blockData) { - return BASE.isOre(blockData); - } - - public static boolean canPlaceOnto(Material mat, Material onto) { - return BASE.canPlaceOnto(mat, onto); - } - - public static boolean isFoliagePlantable(BlockData d) { - return BASE.isFoliagePlantable(d); - } - - public static boolean isFoliagePlantable(Material d) { - return BASE.isFoliagePlantable(d); - } - - public static boolean isWater(BlockData b) { - return BASE.isWater(b); - } - - public static BlockData getAir() { - return BASE.getAir(); - } - - public static Material getMaterialOrNull(String bdx) { - return BASE.getMaterialOrNull(bdx); - } - - public static Material getMaterial(String bdx) { - return BASE.getMaterial(bdx); - } - - public static boolean isSolid(BlockData mat) { - return BASE.isSolid(mat); - } - - public static BlockData getOrNull(String bdxf) { - return BASE.getOrNull(bdxf); - } - - public static BlockData getOrNull(String bdxf, boolean warn) { - return BASE.getOrNull(bdxf, warn); - } - - public static BlockData getNoCompat(String bdxf) { - return BASE.getNoCompat(bdxf); - } - - public static BlockData get(String bdxf) { - return BASE.get(bdxf); - } - - public static boolean isStorage(BlockData mat) { - return BASE.isStorage(mat); - } - - public static boolean isStorageChest(BlockData mat) { - return BASE.isStorageChest(mat); - } - - public static boolean isLit(BlockData mat) { - return BASE.isLit(mat); - } - - public static boolean isUpdatable(BlockData mat) { - return BASE.isUpdatable(mat); - } - - public static boolean isFoliage(Material d) { - return BASE.isFoliage(d); - } - - public static boolean isFoliage(BlockData d) { - return BASE.isFoliage(d); - } - - public static boolean isDecorant(BlockData m) { - return BASE.isDecorant(m); - } - - public static KList get(KList find) { - return BASE.get(find); - } - - public static boolean isFluid(BlockData d) { - return BASE.isFluid(d); - } - - public static boolean isAirOrFluid(BlockData d) { - return BASE.isAirOrFluid(d); - } - - public static boolean isAir(BlockData d) { - return BASE.isAir(d); - } - - public synchronized static String[] getBlockTypes() { - return BASE.getBlockTypes(); - } - - public synchronized static KMap, List> getBlockStates() { - return BASE.getBlockStates(); - } - - public static String[] getItemTypes() { - return BASE.getItemTypes(); - } - - public static boolean isWaterLogged(BlockData b) { - return BASE.isWaterLogged(b); - } - - public static void registerCustomBlockData(String namespace, String key, BlockData blockData) { - BASE.registerCustomBlockData(namespace, key, blockData); - } - - public static boolean isVineBlock(BlockData data) { - return BASE.isVineBlock(data); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/data/TinyColor.java b/src/main/java/art/arcane/adapt/util/common/data/TinyColor.java deleted file mode 100644 index 214339f64..000000000 --- a/src/main/java/art/arcane/adapt/util/common/data/TinyColor.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 art.arcane.adapt.util.data; - -import java.awt.*; - -public class TinyColor { - private int r; - private int g; - private int b; - - public TinyColor(int color) { - this.r = (color >> 16) & 0xFF; - this.g = (color >> 8) & 0xFF; - this.b = color & 0xFF; - } - - public TinyColor(Color color) { - this(color.getRGB()); - } - - public TinyColor(String c) { - this(Color.decode(c)); - } - - public TinyColor(org.bukkit.Color c) { - this(c.asRGB()); - } - - public TinyColor(int r, int g, int b) { - this.r = r; - this.g = g; - this.b = b; - } - - public TinyColor(double h, double s, double b) { - this(Color.getHSBColor((float) h, (float) s, (float) b)); - } - - public TinyColor(float h, float s, float b) { - this(Color.getHSBColor(h, s, b)); - } - - public TinyColor brightness(float brightness) { - return new TinyColor(getHue(), getSaturation(), brightness); - } - - public TinyColor saturation(float saturation) { - return new TinyColor(getHue(), saturation, getBrightness()); - } - - public TinyColor hue(float hue) { - return new TinyColor(hue, getSaturation(), getBrightness()); - } - - public float getHue() { - return Color.RGBtoHSB(r, g, b, null)[0]; - } - - public float getSaturation() { - return Color.RGBtoHSB(r, g, b, null)[1]; - } - - public float getBrightness() { - return Color.RGBtoHSB(r, g, b, null)[2]; - } - - public TinyColor spin(int amount) { - int h = (int) Math.round(getHue() * 360); - h = (h + amount) % 360; - return hue((float) h / 360.0f); - } - - public int toRGB() { - return new Color(r, g, b).getRGB(); - } - - public Color getColor() { - return new Color(r, g, b); - } - - public org.bukkit.Color getBukkitColor() { - return org.bukkit.Color.fromRGB(r, g, b); - } - - public String toHex() { - return String.format("#%02x%02x%02x", r, g, b); - } - - public String toHex(boolean hash) { - if (hash) { - return toHex(); - } - - return String.format("%02x%02x%02x", r, g, b); - } - - public TinyColor copy() { - return new TinyColor(r, g, b); - } - - public TinyColor compliment() { - return spin(180); - } - - private float fclamp(float f, float min, float max) { - return Math.max(min, Math.min(max, f)); - } - - private int iclamp(int i, int min, int max) { - return Math.max(min, Math.min(max, i)); - } - - public TinyColor saturate(int amount) { - return saturation(fclamp(getSaturation() + ((float) amount / 100.0f), 0.0f, 1.0f)); - } - - public TinyColor desaturate(int amount) { - return saturate(-amount); - } - - public TinyColor brighten(int amount) { - return brightness(fclamp(getBrightness() + ((float) amount / 100.0f), 0.0f, 1.0f)); - } - - public TinyColor darken(int amount) { - return brighten(-amount); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java deleted file mode 100644 index 524df5678..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContext.java +++ /dev/null @@ -1,20 +0,0 @@ -package art.arcane.adapt.util.decree; - -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.volmlib.util.decree.context.DecreeContextBase; - -public class DecreeContext { - private static final DecreeContextBase context = new DecreeContextBase<>(); - - public static VolmitSender get() { - return context.get(); - } - - public static void touch(VolmitSender c) { - context.touch(c); - } - - public static void remove() { - context.remove(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java deleted file mode 100644 index f638c22b5..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeContextHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package art.arcane.adapt.util.decree; - -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.volmlib.util.decree.context.DecreeContextHandlerType; -import art.arcane.volmlib.util.decree.context.DecreeContextHandlers; - -import java.util.Map; - -public interface DecreeContextHandler extends DecreeContextHandlerType { - Map, DecreeContextHandler> contextHandlers = buildContextHandlers(); - - static Map, DecreeContextHandler> buildContextHandlers() { - return DecreeContextHandlers.buildOrEmpty( - Adapt.initialize("art.arcane.adapt.util.decree.context"), - DecreeContextHandler.class, - h -> ((DecreeContextHandler) h).getType(), - Throwable::printStackTrace); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java deleted file mode 100644 index e73ef9fa6..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeExecutor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016-2025 Arcane Arts (Volmit Software) - * - * 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 art.arcane.adapt.util.decree; - -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.volmlib.util.decree.DecreeExecutorBase; -import org.bukkit.entity.Player; - -public interface DecreeExecutor extends DecreeExecutorBase { - default VolmitSender sender() { - return DecreeContext.get(); - } - - default Player player() { - return sender().player(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java deleted file mode 100644 index c270bf6fa..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeParameterHandler.java +++ /dev/null @@ -1,5 +0,0 @@ -package art.arcane.adapt.util.decree; - -public interface DecreeParameterHandler - extends DecreeExecutor, art.arcane.volmlib.util.decree.DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java deleted file mode 100644 index 559f62f01..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/context/WorldContextHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -package art.arcane.adapt.util.decree.context; - -import art.arcane.adapt.util.common.plugin.VolmitSender; -import art.arcane.adapt.util.decree.DecreeContextHandler; -import art.arcane.volmlib.util.decree.context.WorldContextHandlerBase; -import org.bukkit.World; - -public class WorldContextHandler extends WorldContextHandlerBase implements DecreeContextHandler { - @Override - protected boolean isPlayer(VolmitSender sender) { - return sender.isPlayer(); - } - - @Override - protected World getWorld(VolmitSender sender) { - return sender.player().getWorld(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java deleted file mode 100644 index 09d8b2e2b..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BlockVectorHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeContext; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.DecreeSystem; -import art.arcane.volmlib.util.decree.handlers.base.BlockVectorHandlerBase; -import art.arcane.volmlib.util.format.Form; -import org.bukkit.FluidCollisionMode; -import org.bukkit.entity.Player; -import org.bukkit.util.BlockVector; - -import java.util.List; - -public class BlockVectorHandler extends BlockVectorHandlerBase implements DecreeParameterHandler { - @Override - protected boolean isSenderPlayer() { - return DecreeContext.get().isPlayer(); - } - - @Override - protected BlockVector getSenderBlockVector() { - return DecreeContext.get().player().getLocation().toVector().toBlockVector(); - } - - @Override - protected BlockVector getLookBlockVector() { - return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); - } - - @Override - protected List playerPossibilities(String query) { - return DecreeSystem.getHandler(Player.class).getPossibilities(query); - } - - @Override - protected String format(double value) { - return Form.f(value, 2); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java deleted file mode 100644 index 42b6e0b41..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/BooleanHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.BooleanHandlerBase; - -public class BooleanHandler extends BooleanHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java deleted file mode 100644 index 3aceab2f8..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ByteHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.ByteHandlerBase; - -public class ByteHandler extends ByteHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java deleted file mode 100644 index 0ad309151..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/DoubleHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.DoubleHandlerBase; - -public class DoubleHandler extends DoubleHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java deleted file mode 100644 index 925d8fb8a..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/FloatHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.FloatHandlerBase; - -public class FloatHandler extends FloatHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java deleted file mode 100644 index 3752a123e..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/IntegerHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.IntegerHandlerBase; - -public class IntegerHandler extends IntegerHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java deleted file mode 100644 index bccc76a1c..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/LongHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.LongHandlerBase; - -public class LongHandler extends LongHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java deleted file mode 100644 index c76c9410d..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ShortHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.ShortHandlerBase; - -public class ShortHandler extends ShortHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java deleted file mode 100644 index 4ef876150..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/StringHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.StringHandlerBase; - -public class StringHandler extends StringHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java deleted file mode 100644 index 173ff1b1e..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/VectorHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeContext; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.DecreeSystem; -import art.arcane.volmlib.util.decree.handlers.base.VectorHandlerBase; -import art.arcane.volmlib.util.format.Form; -import org.bukkit.FluidCollisionMode; -import org.bukkit.entity.Player; -import org.bukkit.util.Vector; - -import java.util.List; - -public class VectorHandler extends VectorHandlerBase implements DecreeParameterHandler { - @Override - protected boolean isSenderPlayer() { - return DecreeContext.get().isPlayer(); - } - - @Override - protected Vector getSenderVector() { - return DecreeContext.get().player().getLocation().toVector(); - } - - @Override - protected Vector getLookVector() { - return DecreeContext.get().player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); - } - - @Override - protected List playerPossibilities(String query) { - return DecreeSystem.getHandler(Player.class).getPossibilities(query); - } - - @Override - protected String format(double value) { - return Form.f(value, 2); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java deleted file mode 100644 index 0aca961ca..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/WorldHandler.java +++ /dev/null @@ -1,12 +0,0 @@ -package art.arcane.adapt.util.decree.handlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.WorldHandlerBase; -import org.bukkit.World; - -public class WorldHandler extends WorldHandlerBase implements DecreeParameterHandler { - @Override - protected String excludedPrefix() { - return "adapt/"; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java deleted file mode 100644 index 81467210d..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/DummyHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package art.arcane.adapt.util.decree.specialhandlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.DummyHandlerBase; - -public class DummyHandler extends DummyHandlerBase implements DecreeParameterHandler { -} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java deleted file mode 100644 index 4458865ba..000000000 --- a/src/main/java/art/arcane/adapt/util/common/decree/specialhandlers/NullablePlayerHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package art.arcane.adapt.util.decree.specialhandlers; - -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.handlers.PlayerHandler; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; -import org.bukkit.entity.Player; - -public class NullablePlayerHandler extends PlayerHandler implements DecreeParameterHandler { - @Override - public Player parse(String in, boolean force) throws DecreeParsingException { - if (in == null) { - return null; - } - - String value = in.trim(); - if (value.isEmpty() || value.equals("---") || value.equalsIgnoreCase("null")) { - return null; - } - - return super.parse(value, force); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/format/C.java b/src/main/java/art/arcane/adapt/util/common/format/C.java index c4c50e578..7178e6691 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/C.java +++ b/src/main/java/art/arcane/adapt/util/common/format/C.java @@ -19,6 +19,7 @@ package art.arcane.adapt.util.common.format; import art.arcane.adapt.util.common.plugin.VolmitSender; +import art.arcane.volmlib.util.format.ColorFormatter; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import org.apache.commons.lang3.Validate; @@ -29,7 +30,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.regex.Pattern; /** * Colors @@ -254,7 +254,6 @@ public net.md_5.bungee.api.ChatColor asBungee() { */ public static final char COLOR_CHAR = '\u00A7'; public final static C[] COLORCYCLE = new C[]{C.GOLD, C.YELLOW, C.GREEN, C.AQUA, C.LIGHT_PURPLE, C.AQUA, C.GREEN, C.YELLOW, C.GOLD, C.RED}; - private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]"); private final static C[] COLORS = new C[]{C.BLACK, C.DARK_BLUE, C.DARK_GREEN, C.DARK_AQUA, C.DARK_RED, C.DARK_PURPLE, C.GOLD, C.GRAY, C.DARK_GRAY, C.BLUE, C.GREEN, C.AQUA, C.RED, C.LIGHT_PURPLE, C.YELLOW, C.WHITE}; @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final static Map BY_ID = new HashMap<>(); @@ -463,11 +462,7 @@ public static C getByChar(String code) { * @return A copy of the input string, without any coloring */ public static String stripColor(final String input) { - if (input == null) { - return null; - } - - return STRIP_COLOR_PATTERN.matcher(input).replaceAll(""); + return ColorFormatter.stripColor(input); } /** @@ -578,18 +573,7 @@ public static String generateColorTable() { * @return Text containing the ChatColor.COLOR_CODE color code character. */ public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { - if (textToTranslate == null) { - return null; - } - - char[] b = textToTranslate.toCharArray(); - for (int i = 0; i < b.length - 1; i++) { - if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) { - b[i] = C.COLOR_CHAR; - b[i + 1] = Character.toLowerCase(b[i + 1]); - } - } - return new String(b); + return ColorFormatter.translateAlternateColorCodes(altColorChar, textToTranslate); } public static C fromItemMeta(byte c) { @@ -613,28 +597,7 @@ public static C randomColor() { * @return Any remaining ChatColors to pass onto the next line. */ public static String getLastColors(String input) { - StringBuilder result = new StringBuilder(); - int length = input.length(); - - // Search backwards from the end as it is faster - for (int index = length - 1; index > -1; index--) { - char section = input.charAt(index); - if (section == COLOR_CHAR && index < length - 1) { - char c = input.charAt(index + 1); - C color = getByChar(c); - - if (color != null) { - result.insert(0, color); - - // Once we find a color or reset we can stop searching - if (color.isColor() || color.equals(RESET)) { - break; - } - } - } - } - - return result.toString(); + return ColorFormatter.getLastColors(input); } public net.md_5.bungee.api.ChatColor asBungee() { @@ -734,4 +697,4 @@ public byte getItemMeta() { default -> (byte) 15; }; } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java deleted file mode 100644 index 908c90210..000000000 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer3.java +++ /dev/null @@ -1,26 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.function; - - -@SuppressWarnings("hiding") -@FunctionalInterface -public interface Consumer3 { - void accept(A a, B b, C c); -} diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function3.java b/src/main/java/art/arcane/adapt/util/common/function/Function3.java deleted file mode 100644 index 53e46f983..000000000 --- a/src/main/java/art/arcane/adapt/util/common/function/Function3.java +++ /dev/null @@ -1,26 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.function; - - -@SuppressWarnings("hiding") -@FunctionalInterface -public interface Function3 { - R apply(A a, B b, C c); -} diff --git a/src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java b/src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java deleted file mode 100644 index 8fb51e304..000000000 --- a/src/main/java/art/arcane/adapt/util/common/function/NastyFunction.java +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.function; - -public interface NastyFunction { - R run(T t) throws Throwable; -} diff --git a/src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java b/src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java deleted file mode 100644 index 61a5e74d6..000000000 --- a/src/main/java/art/arcane/adapt/util/common/function/NastyFuture.java +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.function; - -public interface NastyFuture { - R run() throws Throwable; -} diff --git a/src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java b/src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java deleted file mode 100644 index f89356ee9..000000000 --- a/src/main/java/art/arcane/adapt/util/common/function/NastyRunnable.java +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.function; - -public interface NastyRunnable { - void run() throws Throwable; -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java deleted file mode 100644 index b88ceaa0e..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Element.java +++ /dev/null @@ -1,80 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.volmlib.util.scheduling.Callback; -import org.bukkit.inventory.ItemStack; - -import java.util.List; - -public interface Element { - MaterialBlock getMaterial(); - - Element setMaterial(MaterialBlock b); - - boolean isEnchanted(); - - Element setEnchanted(boolean enchanted); - - String getId(); - - String getName(); - - Element setName(String name); - - CustomModel getModel(); - - Element setModel(CustomModel model); - - double getProgress(); - - Element setProgress(double progress); - - short getEffectiveDurability(); - - int getCount(); - - Element setCount(int c); - - ItemStack computeItemStack(); - - Element setBackground(boolean bg); - - boolean isBackgrond(); - - Element addLore(String loreLine); - - List getLore(); - - Element call(ElementEvent event, Element context); - - Element onLeftClick(Callback clicked); - - Element onRightClick(Callback clicked); - - Element onShiftLeftClick(Callback clicked); - - Element onShiftRightClick(Callback clicked); - - Element onDraggedInto(Callback into); - - Element onOtherDraggedInto(Callback other); -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java deleted file mode 100644 index ad7f937c7..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/ElementEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - - -/** - * Element Event. - * - * @author cyberpwn - */ -public enum ElementEvent { - LEFT, - RIGHT, - SHIFT_LEFT, - SHIFT_RIGHT, - DRAG_INTO, - OTHER_DRAG_INTO -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index 412b63f5f..edac0d026 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -2,8 +2,11 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.math.MaterialBlock; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.data.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.volmlib.util.inventorygui.Window; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -27,7 +30,7 @@ public static void open( return; } - Window w = new UIWindow(player); + UIWindow w = new UIWindow(Adapt.instance, player); GuiTheme.apply(w, "confirm"); w.setViewportHeight(3); diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java index a2f75a334..e869614ed 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java @@ -1,5 +1,8 @@ package art.arcane.adapt.util.common.inventorygui; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.Window; + import java.util.List; public final class GuiEffects { diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java index a9559a165..01336e2f7 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java @@ -1,6 +1,10 @@ package art.arcane.adapt.util.common.inventorygui; -import art.arcane.adapt.util.common.math.MaterialBlock; +import art.arcane.volmlib.util.data.MaterialBlock; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.volmlib.util.inventorygui.Window; +import art.arcane.volmlib.util.inventorygui.WindowResolution; import org.bukkit.Material; public final class GuiTheme { diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java deleted file mode 100644 index 262bacd48..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIElement.java +++ /dev/null @@ -1,286 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import art.arcane.adapt.util.common.format.C; -import art.arcane.adapt.util.common.math.MaterialBlock; -import art.arcane.adapt.util.common.misc.CustomModel; -import art.arcane.adapt.util.reflect.registries.Enchantments; -import art.arcane.adapt.util.reflect.registries.ItemFlags; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.scheduling.Callback; -import org.bukkit.Material; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -public class UIElement implements Element { - private final String id; - private final KList lore; - private MaterialBlock material; - private CustomModel model; - private boolean enchanted; - private String name; - private double progress; - private boolean bg; - private Callback eLeft; - private Callback eRight; - private Callback eShiftLeft; - private Callback eShiftRight; - private Callback eDraggedInto; - private Callback eOtherDraggedInto; - private int count; - - public UIElement(String id) { - this.id = id; - lore = new KList<>(); - enchanted = false; - count = 1; - material = new MaterialBlock(Material.AIR); - } - - @Override - public MaterialBlock getMaterial() { - return material; - } - - @Override - public UIElement setMaterial(MaterialBlock material) { - this.material = material; - return this; - } - - public Double clip(double value, double min, double max) { - return Double.valueOf(Math.min(max, Math.max(min, value))); - } - - @Override - public boolean isEnchanted() { - return enchanted; - } - - @Override - public UIElement setEnchanted(boolean enchanted) { - this.enchanted = enchanted; - return this; - } - - @Override - public String getId() { - return id; - } - - @Override - public String getName() { - return name; - } - - @Override - public UIElement setName(String name) { - this.name = name; - return this; - } - - @Override - public CustomModel getModel() { - return model; - } - - @Override - public UIElement setModel(CustomModel model) { - this.model = model; - return this; - } - - @Override - public KList getLore() { - return lore; - } - - @Override - public UIElement onLeftClick(Callback clicked) { - eLeft = clicked; - return this; - } - - @Override - public UIElement onRightClick(Callback clicked) { - eRight = clicked; - return this; - } - - @Override - public UIElement onShiftLeftClick(Callback clicked) { - eShiftLeft = clicked; - return this; - } - - @Override - public UIElement onShiftRightClick(Callback clicked) { - eShiftRight = clicked; - return this; - } - - @Override - public UIElement onDraggedInto(Callback into) { - eDraggedInto = into; - return this; - } - - @Override - public UIElement onOtherDraggedInto(Callback other) { - eOtherDraggedInto = other; - return this; - } - - @Override - public Element call(ElementEvent event, Element context) { - try { - switch (event) { - case DRAG_INTO: - eDraggedInto.run(context); - return this; - case LEFT: - eLeft.run(context); - return this; - case OTHER_DRAG_INTO: - eOtherDraggedInto.run(context); - return this; - case RIGHT: - eRight.run(context); - return this; - case SHIFT_LEFT: - eShiftLeft.run(context); - return this; - case SHIFT_RIGHT: - eShiftRight.run(context); - return this; - } - } catch (NullPointerException e) { - - } catch (Throwable e) { - e.printStackTrace(); - } - - return this; - } - - @Override - public Element addLore(String loreLine) { - getLore().add(wrapWordsWithFormatting(loreLine.replaceAll("\\Q\n\\E", " "), 52).split("\\Q\n\\E")); - return this; - } - - public String wrapWordsWithFormatting(String f, int l) { - StringBuilder sb = new StringBuilder(); - String last = null; - for (String i : Form.wrapWords(f, l).split("\\Q\n\\E")) { - if (last != null) { - sb.append("\n").append(C.getLastColors(last)).append(i); - } else { - sb.append("\n").append(i); - } - - last = i; - } - - return sb.substring(1); - } - - @Override - public Element setBackground(boolean bg) { - this.bg = bg; - return this; - } - - @Override - public boolean isBackgrond() { - return bg; - } - - @Override - public Element setCount(int c) { - count = clip(c, 1, 64).intValue(); - return this; - } - - @Override - public int getCount() { - return count; - } - - @SuppressWarnings("deprecation") - @Override - public ItemStack computeItemStack() { - try { - ItemStack is = getModel() != null ? getModel().toItemStack() : - new ItemStack(getMaterial().getMaterial()); - is.setAmount(getCount()); - is.setDurability(getEffectiveDurability()); - - ItemMeta im = is.getItemMeta(); - if (im == null) return is; - im.setDisplayName(getName()); - im.setLore(getLore().copy()); - if (isEnchanted()) { - im.addEnchant(Enchantments.DURABILITY, 1, true); - } - // Hide all attributes and enchants and stuff! - im.addItemFlags(ItemFlag.HIDE_ENCHANTS); - im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); - im.addItemFlags(ItemFlags.HIDE_POTION_EFFECTS); - im.addItemFlags(ItemFlag.HIDE_DYE); - im.addItemFlags(ItemFlag.HIDE_DESTROYS); - im.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); - - is.setItemMeta(im); - return is; - } catch (Throwable e) { - e.printStackTrace(); - } - - return null; - } - - @Override - public Element setProgress(double progress) { - this.progress = clip(progress, 0D, 1D); - return this; - } - - @Override - public double getProgress() { - return progress; - } - - @Override - public short getEffectiveDurability() { - if (progress == 1D) { - return 0; - } - - if (getMaterial().getMaterial().getMaxDurability() == 0) { - return 0; - } else { - int prog = (int) ((double) getMaterial().getMaterial().getMaxDurability() * (1D - getProgress())); - return clip(prog, 1, (getMaterial().getMaterial().getMaxDurability() - 1)).shortValue(); - } - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java deleted file mode 100644 index 1188404d5..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIStaticDecorator.java +++ /dev/null @@ -1,35 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import art.arcane.adapt.util.common.math.MaterialBlock; -import org.bukkit.Material; - -public class UIStaticDecorator implements WindowDecorator { - private final Element element; - - public UIStaticDecorator(Element element) { - this.element = element == null ? new UIElement("bg").setMaterial(new MaterialBlock(Material.AIR)) : element; - } - - @Override - public Element onDecorateBackground(Window window, int position, int row) { - return element; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java deleted file mode 100644 index 56d11edd6..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIVoidDecorator.java +++ /dev/null @@ -1,27 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -public class UIVoidDecorator implements WindowDecorator { - @Override - public Element onDecorateBackground(Window window, int position, int row) { - return null; - } -} - diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java deleted file mode 100644 index e4df36793..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/UIWindow.java +++ /dev/null @@ -1,529 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import art.arcane.adapt.Adapt; -import art.arcane.adapt.api.version.Version; -import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.volmlib.util.scheduling.Callback; -import lombok.Getter; -import lombok.Setter; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class UIWindow implements Window, Listener { - private final Player viewer; - private final Map elements; - private WindowDecorator decorator; - private Callback eClose; - private WindowResolution resolution; - private String title; - private boolean visible; - private int viewportPosition; - private int viewportSize; - private int highestRow; - private String tag; - private Inventory inventory; - private int clickcheck; - private boolean doubleclicked; - - public UIWindow(Player viewer) { - clickcheck = 0; - doubleclicked = false; - this.viewer = viewer; - this.elements = new HashMap<>(); - setTitle(""); - setDecorator(new UIVoidDecorator()); - setResolution(WindowResolution.W9_H6); - setViewportHeight(getResolution().getMaxHeight()); - setViewportPosition(0); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryClickEvent e) { - if (!e.getWhoClicked().equals(viewer)) { - return; - } - - if (!isVisible()) { - return; - } - - if (!(e.getInventory().getHolder() instanceof Holder)) { - return; - } - - if (e.getClickedInventory() == null) { - return; - } - - if (!e.getView().getType().equals(getResolution().getType())) { - return; - } - - if (e.getClickedInventory().getType().equals(getResolution().getType())) { - Element element = getElement(getLayoutPosition(e.getSlot()), getLayoutRow(e.getSlot())); - - switch (e.getAction()) { - case CLONE_STACK: - case COLLECT_TO_CURSOR: - case DROP_ALL_CURSOR: - case DROP_ALL_SLOT: - case DROP_ONE_CURSOR: - case DROP_ONE_SLOT: - case HOTBAR_MOVE_AND_READD: - case HOTBAR_SWAP: - case MOVE_TO_OTHER_INVENTORY: - case NOTHING: - case PICKUP_ALL: - case PICKUP_HALF: - case PICKUP_ONE: - case PICKUP_SOME: - case PLACE_ALL: - case PLACE_ONE: - case PLACE_SOME: - case SWAP_WITH_CURSOR: - case UNKNOWN: - break; - } - - switch (e.getClick()) { - case DOUBLE_CLICK -> doubleclicked = true; - case LEFT -> { - clickcheck++; - if (clickcheck == 1) { - J.s(() -> - { - if (clickcheck == 1) { - clickcheck = 0; - - if (element != null) { - element.call(ElementEvent.LEFT, element); - } - } - }); - } else if (clickcheck == 2) { - J.s(() -> - { - if (doubleclicked) { - doubleclicked = false; - } else { - scroll(1); - } - - clickcheck = 0; - }); - } - } - case RIGHT -> { - if (element != null) { - element.call(ElementEvent.RIGHT, element); - } else { - scroll(-1); - } - } - case SHIFT_LEFT -> { - if (element != null) { - element.call(ElementEvent.SHIFT_LEFT, element); - } - } - case SHIFT_RIGHT -> { - if (element != null) { - element.call(ElementEvent.SHIFT_RIGHT, element); - } - } - default -> { - } - } - } - - e.setCancelled(true); - - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(InventoryCloseEvent e) { - - if (!e.getPlayer().equals(viewer)) { - return; - } - - if (!(e.getInventory().getHolder() instanceof Holder)) { - return; - } - - if (isVisible()) { - close(); - callClosed(); - } - } - - @Override - public WindowDecorator getDecorator() { - return decorator; - } - - @Override - public UIWindow setDecorator(WindowDecorator decorator) { - this.decorator = decorator; - return this; - } - - @Override - public UIWindow close() { - setVisible(false); - return this; - } - - @Override - public UIWindow open() { - setVisible(true); - return this; - } - - @Override - public boolean isVisible() { - return visible; - } - - @Override - public UIWindow setVisible(boolean visible) { - if (isVisible() == visible) { - return this; - } - - if (visible) { - Bukkit.getPluginManager().registerEvents(this, Adapt.instance); - - var openInventory = viewer.getOpenInventory().getTopInventory(); - if (openInventory.getHolder() instanceof Holder holder) { - inventory = getCurrentInventory(this, holder); - } else { - inventory = createInventory(this); - } - - this.visible = true; - updateInventory(); - } else { - this.visible = false; - HandlerList.unregisterAll(this); - viewer.closeInventory(); - } - return this; - } - - @Override - public int getViewportPosition() { - return viewportPosition; - } - - @Override - public UIWindow setViewportPosition(int viewportPosition) { - this.viewportPosition = viewportPosition; - scroll(0); - updateInventory(); - - return this; - } - - @Override - public int getMaxViewportPosition() { - return Math.max(0, highestRow - getViewportHeight()); - } - - @Override - public UIWindow scroll(int direction) { - viewportPosition = (int) clip(viewportPosition + direction, 0, getMaxViewportPosition()).doubleValue(); - updateInventory(); - - return this; - } - - @Override - public int getViewportHeight() { - return viewportSize; - } - - @Override - public UIWindow setViewportHeight(int height) { - viewportSize = (int) clip(height, 1, getResolution().getMaxHeight()).doubleValue(); - - if (isVisible()) { - reopen(); - } - - return this; - } - - @Override - public String getTitle() { - return title; - } - - @Override - public UIWindow setTitle(String title) { - this.title = title; - - if (isVisible()) { - reopen(); - } - - return this; - } - - @Override - public String getTag() { - return tag; - } - - @Override - public void setTag(String s) { - tag = s; - } - - @Override - public UIWindow setElement(int position, int row, Element e) { - if (row > highestRow) { - highestRow = row; - } - - elements.put(getRealPosition((int) clip(position, -getResolution().getMaxWidthOffset(), getResolution().getMaxWidthOffset()).doubleValue(), row), e); - updateInventory(); - return this; - } - - @Override - public Element getElement(int position, int row) { - return elements.get(getRealPosition((int) clip(position, -getResolution().getMaxWidthOffset(), getResolution().getMaxWidthOffset()).doubleValue(), row)); - } - - @Override - public Player getViewer() { - return viewer; - } - - @Override - public UIWindow onClosed(Callback window) { - eClose = window; - return this; - } - - @Override - public int getViewportSlots() { - return getViewportHeight() * getResolution().getWidth(); - } - - @Override - public int getLayoutRow(int viewportSlottedPosition) { - return getRow(getRealLayoutPosition(viewportSlottedPosition)); - } - - @Override - public int getLayoutPosition(int viewportSlottedPosition) { - return getPosition(viewportSlottedPosition); - } - - @Override - public int getRealLayoutPosition(int viewportSlottedPosition) { - return getRealPosition(getPosition(viewportSlottedPosition), getRow(viewportSlottedPosition) + getViewportPosition()); - } - - @Override - public int getRealPosition(int position, int row) { - return (int) (((row * getResolution().getWidth()) + getResolution().getMaxWidthOffset()) + clip(position, -getResolution().getMaxWidthOffset(), getResolution().getMaxWidthOffset())); - } - - @Override - public int getRow(int realPosition) { - return realPosition / getResolution().getWidth(); - } - - @Override - public int getPosition(int realPosition) { - return (realPosition % getResolution().getWidth()) - getResolution().getMaxWidthOffset(); - } - - @Override - public Window callClosed() { - if (eClose != null) { - eClose.run(this); - } - - return this; - } - - @Override - public boolean hasElement(int position, int row) { - return getElement(position, row) != null; - } - - @Override - public WindowResolution getResolution() { - return resolution; - } - - public Double clip(double value, double min, double max) { - return Math.min(max, Math.max(min, value)); - } - - @Override - public Window setResolution(WindowResolution resolution) { - close(); - this.resolution = resolution; - setViewportHeight((int) clip(getViewportHeight(), 1, getResolution().getMaxHeight()).doubleValue()); - return this; - } - - @Override - public Window clearElements() { - highestRow = 0; - elements.clear(); - updateInventory(); - return this; - } - - @Override - public Window updateInventory() { - if (!isVisible() || inventory == null) { - return this; - } - - Inventory top = viewer.getOpenInventory().getTopInventory(); - if (!(top.getHolder() instanceof Holder holder) || holder.getWindow() != this) { - return this; - } - - if (inventory != top) { - inventory = top; - } - - if (Version.SET_TITLE) { - try { - viewer.getOpenInventory().setTitle(getTitle()); - } catch (IllegalArgumentException ignored) { - return this; - } - } - - ItemStack[] is = inventory.getContents(); - Set isf = new HashSet<>(); - - for (int i = 0; i < is.length; i++) { - ItemStack isc = is[i]; - ItemStack isx = computeItemStack(i); - int layoutRow = getLayoutRow(i); - int layoutPosition = getLayoutPosition(i); - - if (isx != null && !hasElement(layoutPosition, layoutRow)) { - ItemStack gg = isx.clone(); - gg.setAmount(gg.getAmount() + 1); - isf.add(gg); - } - - if (((isc == null) != (isx == null)) || isx != null && isc != null && !isc.equals(isx)) { - inventory.setItem(i, isx); - } - } - - return this; - } - - @Override - public ItemStack computeItemStack(int viewportSlot) { - int layoutRow = getLayoutRow(viewportSlot); - int layoutPosition = getLayoutPosition(viewportSlot); - Element e = hasElement(layoutPosition, layoutRow) ? getElement(layoutPosition, layoutRow) : getDecorator().onDecorateBackground(this, layoutPosition, layoutRow); - - if (e != null) { - return e.computeItemStack(); - } - - return null; - } - - @Override - public Window reopen() { - if (Version.SET_TITLE) { - visible = false; - HandlerList.unregisterAll(this); - return open(); - } - return this.close().open(); - } - - @Setter - @Getter - private static class Holder implements InventoryHolder { - private Inventory inventory; - private WindowResolution resolution; - private UIWindow window; - - public void unregister() { - HandlerList.unregisterAll(window); - window.visible = false; - } - } - - private static Inventory createInventory(UIWindow window) { - var holder = new Holder(); - Inventory inventory; - if (window.getResolution().getType().equals(InventoryType.CHEST)) { - inventory = Bukkit.createInventory(holder, window.getViewportHeight() * 9, window.getTitle()); - } else { - inventory = Bukkit.createInventory(holder, window.getResolution().getType(), window.getTitle()); - } - holder.setResolution(window.getResolution()); - holder.setInventory(inventory); - holder.setWindow(window); - - window.viewer.openInventory(inventory); - return inventory; - } - - private static Inventory getCurrentInventory(UIWindow window, Holder holder) { - boolean requiresResize = window.getResolution().getType().equals(InventoryType.CHEST) - && holder.inventory.getSize() != window.getViewportHeight() * window.getResolution().getWidth(); - if (!Version.SET_TITLE || holder.getResolution() != window.getResolution() || requiresResize) { - holder.window.close(); - return createInventory(window); - } - - var openInventory = holder.inventory; - holder.unregister(); - holder.setWindow(window); - - openInventory.clear(); - return openInventory; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java deleted file mode 100644 index 5b7735a98..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Window.java +++ /dev/null @@ -1,95 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import art.arcane.volmlib.util.scheduling.Callback; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public interface Window { - WindowDecorator getDecorator(); - - Window setDecorator(WindowDecorator decorator); - - WindowResolution getResolution(); - - Window setResolution(WindowResolution resolution); - - Window clearElements(); - - Window close(); - - Window open(); - - Window callClosed(); - - Window updateInventory(); - - ItemStack computeItemStack(int viewportSlot); - - int getLayoutRow(int viewportSlottedPosition); - - int getLayoutPosition(int viewportSlottedPosition); - - int getRealLayoutPosition(int viewportSlottedPosition); - - int getRealPosition(int position, int row); - - int getRow(int realPosition); - - int getPosition(int realPosition); - - boolean isVisible(); - - Window setVisible(boolean visible); - - int getViewportPosition(); - - Window setViewportPosition(int position); - - int getViewportSlots(); - - int getMaxViewportPosition(); - - Window scroll(int direction); - - int getViewportHeight(); - - Window setViewportHeight(int height); - - String getTitle(); - - Window setTitle(String title); - - String getTag(); - - void setTag(String s); - - boolean hasElement(int position, int row); - - Window setElement(int position, int row, Element e); - - Element getElement(int position, int row); - - Player getViewer(); - - Window reopen(); - - Window onClosed(Callback window); -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java deleted file mode 100644 index 06b72dc4f..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowDecorator.java +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -public interface WindowDecorator { - Element onDecorateBackground(Window window, int position, int row); -} diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java deleted file mode 100644 index 68aef05db..000000000 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/WindowResolution.java +++ /dev/null @@ -1,53 +0,0 @@ -/*------------------------------------------------------------------------------ - - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers - - Copyright (c) 2022 Arcane Arts (Volmit Software) - - - - 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 art.arcane.adapt.util.common.inventorygui; - -import org.bukkit.event.inventory.InventoryType; - -public enum WindowResolution { - W9_H6(9, 6, InventoryType.CHEST), - W5_H1(5, 1, InventoryType.HOPPER), - W3_H3(3, 3, InventoryType.DROPPER); - - private final int width; - private final int maxHeight; - private final InventoryType type; - - WindowResolution(int w, int h, InventoryType type) { - this.width = w; - this.maxHeight = h; - this.type = type; - } - - public int getMaxWidthOffset() { - return (getWidth() - 1) / 2; - } - - public int getWidth() { - return width; - } - - public int getMaxHeight() { - return maxHeight; - } - - public InventoryType getType() { - return type; - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java index b26edcb8e..be4fb2c03 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java @@ -18,8 +18,8 @@ package art.arcane.adapt.util.common.io; -import art.arcane.adapt.util.common.function.Consumer3; import art.arcane.amulet.io.FolderWatcher; +import art.arcane.volmlib.util.function.Consumer3; import java.io.File; import java.util.List; diff --git a/src/main/java/art/arcane/adapt/util/common/math/Direction.java b/src/main/java/art/arcane/adapt/util/common/math/Direction.java index badd9e4f6..6de617093 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Direction.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Direction.java @@ -18,8 +18,8 @@ package art.arcane.adapt.util.common.math; -import art.arcane.adapt.util.common.collection.GBiset; import art.arcane.volmlib.util.data.Cuboid.CuboidDirection; +import art.arcane.volmlib.util.collection.GBiset; import art.arcane.volmlib.util.math.DOP; import org.bukkit.Axis; import org.bukkit.block.BlockFace; diff --git a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java index d2f4ba466..08fa2a136 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java @@ -18,8 +18,8 @@ package art.arcane.adapt.util.common.math; -import art.arcane.adapt.util.common.collection.GListAdapter; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.collection.GListAdapter; import art.arcane.volmlib.util.math.CDou; import org.bukkit.Axis; import org.bukkit.Bukkit; diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index 11e64294b..b492b3ac1 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -483,14 +483,14 @@ public void sendHeader(String name) { sendHeader(name, 40); } - public void sendDecreeHelp(DirectorVisualCommand v) { - sendDecreeHelp(v, 0); + public void sendDirectorHelp(DirectorVisualCommand v) { + sendDirectorHelp(v, 0); } - public void sendDecreeHelp(DirectorVisualCommand v, int page) { + public void sendDirectorHelp(DirectorVisualCommand v, int page) { if (!isPlayer()) { for (DirectorVisualCommand i : v.getNodes()) { - sendDecreeHelpNode(i); + sendDirectorHelpNode(i); } return; @@ -506,7 +506,7 @@ public void sendDecreeHelp(DirectorVisualCommand v, int page) { AtomicBoolean next = new AtomicBoolean(false); for (DirectorVisualCommand i : paginate(v.getNodes(), 17, page, next)) { - sendDecreeHelpNode(i); + sendDirectorHelpNode(i); } String s = ""; @@ -528,7 +528,7 @@ public void sendDecreeHelp(DirectorVisualCommand v, int page) { } } - public void sendDecreeHelpNode(DirectorVisualCommand i) { + public void sendDirectorHelpNode(DirectorVisualCommand i) { if (isPlayer() || s instanceof CommandDummy) { sendMessageRaw(helpCache.computeIfAbsent(i.getPath(), (k) -> { String newline = "\n"; diff --git a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java index ee70a7d44..daa2e51d7 100644 --- a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java +++ b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java @@ -19,35 +19,30 @@ package art.arcane.adapt.util.common.scheduling; import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.function.NastyFunction; -import art.arcane.adapt.util.common.function.NastyFuture; -import art.arcane.adapt.util.common.function.NastyRunnable; import art.arcane.adapt.util.common.parallel.MultiBurst; +import art.arcane.volmlib.util.function.NastyFunction; +import art.arcane.volmlib.util.function.NastyFuture; +import art.arcane.volmlib.util.function.NastyRunnable; import art.arcane.volmlib.util.math.FinalInteger; import art.arcane.volmlib.util.scheduling.*; -import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.plugin.IllegalPluginAccessException; -import org.bukkit.plugin.Plugin; -import java.lang.reflect.Method; -import java.util.Map; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; public class J { - private static final long TICK_MS = 50L; - private static final AtomicInteger TASK_IDS = new AtomicInteger(1); - private static final Map REPEATING_CANCELLERS = new ConcurrentHashMap<>(); - private static final StartupQueueSupport STARTUP_QUEUE = new StartupQueueSupport(); + private static final SchedulerRuntime RUNTIME = new SchedulerRuntime( + () -> Adapt.instance, + J::a, + Adapt::verbose, + Adapt::warn, + Throwable::printStackTrace + ); static { SchedulerBridge.setSyncScheduler(J::s); @@ -109,15 +104,15 @@ public static T attempt(Supplier t, T i) { * Dont call this unless you know what you are doing! */ public static void executeAfterStartupQueue() { - JSupport.executeAfterStartupQueue(STARTUP_QUEUE, J::s, J::a); + RUNTIME.executeAfterStartupQueue(J::s); } public static void ass(Runnable r) { - JSupport.enqueueAfterStartupSync(STARTUP_QUEUE, r, J::s); + RUNTIME.enqueueAfterStartupSync(r, J::s); } public static void asa(Runnable r) { - JSupport.enqueueAfterStartupAsync(STARTUP_QUEUE, r, J::a); + RUNTIME.enqueueAfterStartupAsync(r); } public static boolean isPrimaryThread() { @@ -125,50 +120,19 @@ public static boolean isPrimaryThread() { } public static boolean isFoliaThreading() { - return FoliaScheduler.isFoliaThreading(Bukkit.getServer()); + return RUNTIME.isFoliaThreading(); } public static boolean isOwnedByCurrentRegion(Entity entity) { - return FoliaScheduler.isOwnedByCurrentRegion(entity); + return RUNTIME.isOwnedByCurrentRegion(entity); } public static boolean runEntity(Entity entity, Runnable runnable) { - if (entity == null || runnable == null || !isPluginActive()) { - return false; - } - - if (isFoliaThreading()) { - if (isOwnedByCurrentRegion(entity)) { - runnable.run(); - return true; - } - - return runEntityImmediate(entity, runnable); - } - - if (isPrimaryThread()) { - runnable.run(); - return true; - } - - return runEntityImmediate(entity, runnable); + return RUNTIME.runEntity(entity, runnable); } public static boolean runEntity(Entity entity, Runnable runnable, int delayTicks) { - if (entity == null || runnable == null || !isPluginActive()) { - return false; - } - - if (delayTicks <= 0) { - return runEntity(entity, runnable); - } - - if (isFoliaThreading() && runEntityDelayed(entity, runnable, delayTicks)) { - return true; - } - - s(() -> runEntity(entity, runnable), delayTicks); - return true; + return RUNTIME.runEntity(entity, runnable, delayTicks); } public static boolean teleport(Entity entity, Location location) { @@ -176,190 +140,35 @@ public static boolean teleport(Entity entity, Location location) { } public static boolean teleport(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { - if (entity == null || location == null) { - return false; - } - - if (isFoliaThreading()) { - Object asyncWithCause = null; - if (cause != null) { - asyncWithCause = invokeNoThrow( - entity, - "teleportAsync", - new Class[]{Location.class, PlayerTeleportEvent.TeleportCause.class}, - location, - cause - ); - } - - if (asyncWithCause != null) { - return true; - } - - Object async = invokeNoThrow(entity, "teleportAsync", new Class[]{Location.class}, location); - if (async != null) { - return true; - } - } - - try { - if (cause != null) { - return entity.teleport(location, cause); - } - - return entity.teleport(location); - } catch (UnsupportedOperationException e) { - Adapt.warn("Failed to teleport entity synchronously on this server; teleportAsync was unavailable. Entity=" - + entity.getUniqueId() + " world=" + (location.getWorld() == null ? "null" : location.getWorld().getName())); - return false; - } + return RUNTIME.teleport(entity, location, cause); } public static boolean runAt(Location location, Runnable runnable) { - if (location == null || runnable == null) { - return false; - } - - if (runRegionImmediate(location, runnable)) { - return true; - } - - if (isFoliaThreading()) { - World world = location.getWorld(); - Adapt.verbose("Failed to schedule immediate region task at " - + (world == null ? "null" : world.getName()) - + "@" + (location.getBlockX() >> 4) + "," + (location.getBlockZ() >> 4) + " on Folia."); - return false; - } - - s(runnable); - return true; + return RUNTIME.runAt(location, runnable); } public static boolean runAt(Location location, Runnable runnable, int delayTicks) { - if (location == null || runnable == null) { - return false; - } - - if (delayTicks <= 0) { - return runAt(location, runnable); - } - - if (runRegionDelayed(location, runnable, delayTicks)) { - return true; - } - - if (isFoliaThreading()) { - World world = location.getWorld(); - Adapt.verbose("Failed to schedule delayed region task at " - + (world == null ? "null" : world.getName()) - + "@" + (location.getBlockX() >> 4) + "," + (location.getBlockZ() >> 4) - + " (" + delayTicks + "t) on Folia."); - return false; - } - - s(runnable, delayTicks); - return true; + return RUNTIME.runAt(location, runnable, delayTicks); } public static void cancelPluginTasks() { - Plugin plugin = Adapt.instance; - if (plugin == null) { - return; - } - - for (Runnable cancelAction : REPEATING_CANCELLERS.values()) { - try { - cancelAction.run(); - } catch (Throwable ex) { - Adapt.verbose("Failed to run cancel action: " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - } - REPEATING_CANCELLERS.clear(); - - FoliaScheduler.cancelTasks(plugin); - - try { - Bukkit.getScheduler().cancelTasks(plugin); - } catch (UnsupportedOperationException | IllegalPluginAccessException ex) { - // Folia blocks BukkitScheduler usage. - Adapt.verbose("Skipping BukkitScheduler#cancelTasks for Adapt on this server."); - } + RUNTIME.cancelPluginTasks(); } public static void s(Runnable r) { - if (!isPluginActive()) { - return; - } - - if (!runGlobalImmediate(r)) { - try { - Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r); - } catch (IllegalPluginAccessException e) { - if (!isPluginActive()) { - return; - } - - throw new IllegalStateException("Failed to schedule global sync task while plugin is enabled.", e); - } catch (UnsupportedOperationException e) { - throw new IllegalStateException("Failed to schedule global sync task on this server (Folia scheduler unavailable, BukkitScheduler unsupported).", e); - } - } + RUNTIME.s(r); } public static void s(Runnable r, int delay) { - if (delay <= 0) { - s(r); - return; - } - - if (!isPluginActive()) { - return; - } - - if (!runGlobalDelayed(r, delay)) { - try { - Bukkit.getScheduler().scheduleSyncDelayedTask(Adapt.instance, r, delay); - } catch (IllegalPluginAccessException e) { - if (!isPluginActive()) { - return; - } - - throw new IllegalStateException("Failed to schedule delayed global sync task while plugin is enabled.", e); - } catch (UnsupportedOperationException e) { - throw new IllegalStateException("Failed to schedule delayed global sync task on this server (Folia scheduler unavailable, BukkitScheduler unsupported).", e); - } - } + RUNTIME.s(r, delay); } public static void csr(int id) { - cancelRepeatingTask(id); + RUNTIME.csr(id); } public static int sr(Runnable r, int interval) { - int safeInterval = Math.max(1, interval); - RepeatingState state = new RepeatingState(); - int taskId = trackRepeatingTask(() -> state.cancelled = true); - - Runnable[] loop = new Runnable[1]; - loop[0] = () -> { - if (state.cancelled || !isPluginActive()) { - REPEATING_CANCELLERS.remove(taskId); - return; - } - - r.run(); - if (state.cancelled || !isPluginActive()) { - REPEATING_CANCELLERS.remove(taskId); - return; - } - - s(loop[0], safeInterval); - }; - - s(loop[0]); - return taskId; + return RUNTIME.sr(r, interval); } public static void sr(Runnable r, int interval, int intervals) { @@ -379,53 +188,15 @@ public void run() { } public static void a(Runnable r, int delay) { - if (!isPluginActive()) { - return; - } - - if (delay <= 0) { - if (!runAsyncImmediate(r)) { - a(r); - } - return; - } - - if (!runAsyncDelayed(r, delay)) { - a(() -> { - if (sleep(ticksToMilliseconds(delay))) { - r.run(); - } - }); - } + RUNTIME.a(r, delay); } public static void car(int id) { - cancelRepeatingTask(id); + RUNTIME.car(id); } public static int ar(Runnable r, int interval) { - int safeInterval = Math.max(1, interval); - RepeatingState state = new RepeatingState(); - int taskId = trackRepeatingTask(() -> state.cancelled = true); - - Runnable[] loop = new Runnable[1]; - loop[0] = () -> { - if (state.cancelled || !isPluginActive()) { - REPEATING_CANCELLERS.remove(taskId); - return; - } - - r.run(); - if (state.cancelled || !isPluginActive()) { - REPEATING_CANCELLERS.remove(taskId); - return; - } - - a(loop[0], safeInterval); - }; - - a(loop[0], 0); - return taskId; + return RUNTIME.ar(r, interval); } public static void ar(Runnable r, int interval, int intervals) { @@ -444,73 +215,4 @@ public void run() { }; } - private static int trackRepeatingTask(Runnable cancelAction) { - int id = TASK_IDS.getAndIncrement(); - REPEATING_CANCELLERS.put(id, cancelAction); - return id; - } - - private static void cancelRepeatingTask(int id) { - Runnable cancelAction = REPEATING_CANCELLERS.remove(id); - if (cancelAction != null) { - cancelAction.run(); - } - } - - private static long ticksToMilliseconds(int ticks) { - return Math.max(0L, ticks) * TICK_MS; - } - - private static boolean runGlobalImmediate(Runnable runnable) { - return FoliaScheduler.runGlobal(Adapt.instance, runnable); - } - - private static boolean runGlobalDelayed(Runnable runnable, int delayTicks) { - return FoliaScheduler.runGlobal(Adapt.instance, runnable, Math.max(0, delayTicks)); - } - - private static boolean runRegionImmediate(Location location, Runnable runnable) { - return FoliaScheduler.runRegion(Adapt.instance, location, runnable); - } - - private static boolean runRegionDelayed(Location location, Runnable runnable, int delayTicks) { - return FoliaScheduler.runRegion(Adapt.instance, location, runnable, Math.max(0, delayTicks)); - } - - private static boolean runAsyncImmediate(Runnable runnable) { - return FoliaScheduler.runAsync(Adapt.instance, runnable); - } - - private static boolean runAsyncDelayed(Runnable runnable, int delayTicks) { - return FoliaScheduler.runAsync(Adapt.instance, runnable, Math.max(0, delayTicks)); - } - - private static boolean runEntityImmediate(Entity entity, Runnable runnable) { - return FoliaScheduler.runEntity(Adapt.instance, entity, runnable); - } - - private static boolean runEntityDelayed(Entity entity, Runnable runnable, int delayTicks) { - return FoliaScheduler.runEntity(Adapt.instance, entity, runnable, Math.max(0, delayTicks)); - } - - private static Object invokeNoThrow(Object target, String methodName, Class[] parameterTypes, Object... args) { - try { - Method method = target.getClass().getMethod(methodName, parameterTypes); - return method.invoke(target, args); - } catch (Throwable ex) { - Adapt.verbose("Reflective call failed for method '" + methodName + "' on " + target.getClass().getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } - } - - private static final class RepeatingState { - private volatile boolean cancelled; - } - - private static boolean isPluginActive() { - Adapt adapt = Adapt.instance; - return adapt != null && adapt.isEnabled(); - } } diff --git a/src/main/java/art/arcane/adapt/util/common/data/Metadata.java b/src/main/java/art/arcane/adapt/util/data/Metadata.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/data/Metadata.java rename to src/main/java/art/arcane/adapt/util/data/Metadata.java diff --git a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java b/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java similarity index 56% rename from src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java rename to src/main/java/art/arcane/adapt/util/director/DirectorSystem.java index 5cf9a71f0..1a0fbe1d4 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/DecreeSystem.java +++ b/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java @@ -17,30 +17,31 @@ * */ -package art.arcane.adapt.util.decree; +package art.arcane.adapt.util.director; import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.DecreeSystemSupport; -public final class DecreeSystem { - public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.decree.handlers", null).convert((i) -> (DecreeParameterHandler) i); +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.DirectorSystemSupport; +public final class DirectorSystem { + public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.director.handlers", null).convert((i) -> (DirectorParameterHandler) i); - private DecreeSystem() { + private DirectorSystem() { } /** * Get the handler for the specified type * * @param type The type to handle - * @return The corresponding {@link DecreeParameterHandler}, or null + * @return The corresponding {@link DirectorParameterHandler}, or null */ - public static DecreeParameterHandler getHandler(Class type) { - DecreeParameterHandler handler = DecreeSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); + public static DirectorParameterHandler getHandler(Class type) { + DirectorParameterHandler handler = DirectorSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); if (handler != null) { return handler; } - Adapt.error("Unhandled type in Decree Parameter: " + type.getName() + ". This is bad!"); + Adapt.error("Unhandled type in Director Parameter: " + type.getName() + ". This is bad!"); return null; } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java b/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java similarity index 99% rename from src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java rename to src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java index 02a1730f7..b190ca677 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/context/AdaptationListingHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java @@ -1,4 +1,4 @@ -package art.arcane.adapt.util.decree.context; +package art.arcane.adapt.util.director.context; import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.Adaptation; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java similarity index 60% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java index c810c55a2..cd26fb281 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationListHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; -public class AdaptationListHandler implements DecreeParameterHandler { +public class AdaptationListHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { return AdaptationListingHandler.getAdaptionListings(); @@ -17,7 +17,7 @@ public String toString(AdaptationListingHandler.AdaptationList adaptationList) { } @Override - public AdaptationListingHandler.AdaptationList parse(String in, boolean force) throws DecreeParsingException { + public AdaptationListingHandler.AdaptationList parse(String in, boolean force) throws DirectorParsingException { return new AdaptationListingHandler.AdaptationList(in); } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java similarity index 60% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java index 97d8435d6..ff1a3a021 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; -public class AdaptationProviderHandler implements DecreeParameterHandler { +public class AdaptationProviderHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { @@ -18,7 +18,7 @@ public String toString(AdaptationListingHandler.AdaptationProvider adaptationPro } @Override - public AdaptationListingHandler.AdaptationProvider parse(String in, boolean force) throws DecreeParsingException { + public AdaptationListingHandler.AdaptationProvider parse(String in, boolean force) throws DirectorParsingException { return new AdaptationListingHandler.AdaptationProvider(in); } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java similarity index 60% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java index a8dbf7025..94217cd52 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/AdaptationSkillListHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; -public class AdaptationSkillListHandler implements DecreeParameterHandler { +public class AdaptationSkillListHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { return AdaptationListingHandler.getAdaptionSkillListings(); @@ -17,7 +17,7 @@ public String toString(AdaptationListingHandler.AdaptationSkillList adaptationSk } @Override - public AdaptationListingHandler.AdaptationSkillList parse(String in, boolean force) throws DecreeParsingException { + public AdaptationListingHandler.AdaptationSkillList parse(String in, boolean force) throws DirectorParsingException { return new AdaptationListingHandler.AdaptationSkillList(in); } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java new file mode 100644 index 000000000..1947cc58a --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java @@ -0,0 +1,39 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.DirectorSystem; +import art.arcane.volmlib.util.director.handlers.base.BlockVectorHandlerBase; +import art.arcane.volmlib.util.format.Form; +import org.bukkit.FluidCollisionMode; +import org.bukkit.entity.Player; +import org.bukkit.util.BlockVector; + +import java.util.List; + +public class BlockVectorHandler extends BlockVectorHandlerBase implements DirectorParameterHandler { + @Override + protected boolean isSenderPlayer() { + return BukkitDirectorContext.isPlayer(); + } + + @Override + protected BlockVector getSenderBlockVector() { + return BukkitDirectorContext.player().getLocation().toVector().toBlockVector(); + } + + @Override + protected BlockVector getLookBlockVector() { + return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); + } + + @Override + protected List playerPossibilities(String query) { + return DirectorSystem.getHandler(Player.class).getPossibilities(query); + } + + @Override + protected String format(double value) { + return Form.f(value, 2); + } +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/BooleanHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/BooleanHandler.java new file mode 100644 index 000000000..2890aa598 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/BooleanHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.BooleanHandlerBase; + +public class BooleanHandler extends BooleanHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/ByteHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/ByteHandler.java new file mode 100644 index 000000000..643d0e7d9 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/ByteHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.ByteHandlerBase; + +public class ByteHandler extends ByteHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/DoubleHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/DoubleHandler.java new file mode 100644 index 000000000..f6606c97f --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/DoubleHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.DoubleHandlerBase; + +public class DoubleHandler extends DoubleHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/FloatHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/FloatHandler.java new file mode 100644 index 000000000..5ae9079c4 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/FloatHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.FloatHandlerBase; + +public class FloatHandler extends FloatHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/IntegerHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/IntegerHandler.java new file mode 100644 index 000000000..746cc8881 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/IntegerHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.IntegerHandlerBase; + +public class IntegerHandler extends IntegerHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/LongHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/LongHandler.java new file mode 100644 index 000000000..add7d7017 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/LongHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.LongHandlerBase; + +public class LongHandler extends LongHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java similarity index 78% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java index fdb27d09d..60e83faef 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/OptionalWorldHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java @@ -17,12 +17,12 @@ * */ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.OptionalWorldHandlerBase; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.OptionalWorldHandlerBase; -public class OptionalWorldHandler extends OptionalWorldHandlerBase implements DecreeParameterHandler { +public class OptionalWorldHandler extends OptionalWorldHandlerBase implements DirectorParameterHandler { @Override protected String excludedPrefix() { return "adapt/"; diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java similarity index 55% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java index 5e19dbe0c..8d3cde315 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/ParticleHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.bukkit.Particle; -public class ParticleHandler implements DecreeParameterHandler { +public class ParticleHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { return new KList<>(Particle.values()); @@ -17,11 +17,11 @@ public String toString(Particle particle) { } @Override - public Particle parse(String in, boolean force) throws DecreeParsingException { + public Particle parse(String in, boolean force) throws DirectorParsingException { try { return Particle.valueOf(in); } catch (IllegalArgumentException e) { - throw new DecreeParsingException("Invalid particle: " + in); + throw new DirectorParsingException("Invalid particle: " + in); } } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java similarity index 84% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java index 323994254..83075e8ba 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/PlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java @@ -17,18 +17,18 @@ * */ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.volmlib.util.decree.handlers.base.PlayerHandlerBase; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.PlayerHandlerBase; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; -public class PlayerHandler extends PlayerHandlerBase implements DecreeParameterHandler { +public class PlayerHandler extends PlayerHandlerBase implements DirectorParameterHandler { @Override protected List playerOptions() { if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/ShortHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/ShortHandler.java new file mode 100644 index 000000000..c6bbc54c4 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/ShortHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.ShortHandlerBase; + +public class ShortHandler extends ShortHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java similarity index 59% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java index b0289d0bd..a10717a50 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SkillProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; -import art.arcane.adapt.util.decree.context.AdaptationListingHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; -public class SkillProviderHandler implements DecreeParameterHandler { +public class SkillProviderHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { return AdaptationListingHandler.getSkillProvider(); @@ -17,7 +17,7 @@ public String toString(AdaptationListingHandler.SkillProvider skillProvider) { } @Override - public AdaptationListingHandler.SkillProvider parse(String in, boolean force) throws DecreeParsingException { + public AdaptationListingHandler.SkillProvider parse(String in, boolean force) throws DirectorParsingException { return new AdaptationListingHandler.SkillProvider(in); } diff --git a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java similarity index 55% rename from src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java rename to src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java index 7be15cc8b..b7177cf79 100644 --- a/src/main/java/art/arcane/adapt/util/common/decree/handlers/SoundHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java @@ -1,11 +1,11 @@ -package art.arcane.adapt.util.decree.handlers; +package art.arcane.adapt.util.director.handlers; -import art.arcane.adapt.util.decree.DecreeParameterHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.decree.exceptions.DecreeParsingException; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.bukkit.Sound; -public class SoundHandler implements DecreeParameterHandler { +public class SoundHandler implements DirectorParameterHandler { @Override public KList getPossibilities() { return new KList<>(Sound.values()); @@ -17,11 +17,11 @@ public String toString(Sound sound) { } @Override - public Sound parse(String in, boolean force) throws DecreeParsingException { + public Sound parse(String in, boolean force) throws DirectorParsingException { try { return Sound.valueOf(in); } catch (IllegalArgumentException e) { - throw new DecreeParsingException("Invalid sound: " + in); + throw new DirectorParsingException("Invalid sound: " + in); } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/StringHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/StringHandler.java new file mode 100644 index 000000000..483788555 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/StringHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.StringHandlerBase; + +public class StringHandler extends StringHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java new file mode 100644 index 000000000..43cd72dde --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java @@ -0,0 +1,39 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.DirectorSystem; +import art.arcane.volmlib.util.director.handlers.base.VectorHandlerBase; +import art.arcane.volmlib.util.format.Form; +import org.bukkit.FluidCollisionMode; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +import java.util.List; + +public class VectorHandler extends VectorHandlerBase implements DirectorParameterHandler { + @Override + protected boolean isSenderPlayer() { + return BukkitDirectorContext.isPlayer(); + } + + @Override + protected Vector getSenderVector() { + return BukkitDirectorContext.player().getLocation().toVector(); + } + + @Override + protected Vector getLookVector() { + return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); + } + + @Override + protected List playerPossibilities(String query) { + return DirectorSystem.getHandler(Player.class).getPossibilities(query); + } + + @Override + protected String format(double value) { + return Form.f(value, 2); + } +} diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java new file mode 100644 index 000000000..88f6ddb16 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java @@ -0,0 +1,12 @@ +package art.arcane.adapt.util.director.handlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.WorldHandlerBase; +import org.bukkit.World; + +public class WorldHandler extends WorldHandlerBase implements DirectorParameterHandler { + @Override + protected String excludedPrefix() { + return "adapt/"; + } +} diff --git a/src/main/java/art/arcane/adapt/util/director/specialhandlers/DummyHandler.java b/src/main/java/art/arcane/adapt/util/director/specialhandlers/DummyHandler.java new file mode 100644 index 000000000..a08d16fb1 --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/specialhandlers/DummyHandler.java @@ -0,0 +1,7 @@ +package art.arcane.adapt.util.director.specialhandlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.handlers.base.DummyHandlerBase; + +public class DummyHandler extends DummyHandlerBase implements DirectorParameterHandler { +} diff --git a/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java new file mode 100644 index 000000000..d417dd51c --- /dev/null +++ b/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java @@ -0,0 +1,22 @@ +package art.arcane.adapt.util.director.specialhandlers; + +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.adapt.util.director.handlers.PlayerHandler; +import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; +import org.bukkit.entity.Player; + +public class NullablePlayerHandler extends PlayerHandler implements DirectorParameterHandler { + @Override + public Player parse(String in, boolean force) throws DirectorParsingException { + if (in == null) { + return null; + } + + String value = in.trim(); + if (value.isEmpty() || value.equals("---") || value.equalsIgnoreCase("null")) { + return null; + } + + return super.parse(value, force); + } +} diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/Reflect.java b/src/main/java/art/arcane/adapt/util/reflect/Reflect.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/Reflect.java rename to src/main/java/art/arcane/adapt/util/reflect/Reflect.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java b/src/main/java/art/arcane/adapt/util/reflect/WrappedField.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/WrappedField.java rename to src/main/java/art/arcane/adapt/util/reflect/WrappedField.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java b/src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/WrappedReturningMethod.java rename to src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java b/src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/ReflectiveEvents.java rename to src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/Event.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/Event.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/ReflectiveHandler.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/ReflectiveHandler.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EndermanAttackPlayerEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EndermanAttackPlayerEvent.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityDismountEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityDismountEvent.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityEvent.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityMountEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/events/api/entity/EntityMountEvent.java rename to src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/Attributes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/Attributes.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/Enchantments.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/Enchantments.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/EntityTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/EntityTypes.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/ItemFlags.java b/src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/ItemFlags.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/Materials.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/Materials.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/Particles.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/Particles.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionEffectTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionEffectTypes.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/PotionTypes.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java diff --git a/src/main/java/art/arcane/adapt/util/common/reflect/registries/RegistryUtil.java b/src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java similarity index 100% rename from src/main/java/art/arcane/adapt/util/common/reflect/registries/RegistryUtil.java rename to src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java From 983fa604ff8cf66d9f8b4d28b516a615f636252a Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 17 Feb 2026 04:25:31 -0500 Subject: [PATCH 11/25] Pfft --- .../java/art/arcane/adapt/AdaptConfig.java | 1 + .../java/art/arcane/adapt/PapiExpansion.java | 496 +++++++++++++----- .../adapt/api/adaptation/Adaptation.java | 63 ++- .../api/adaptation/AdaptationGuiSupport.java | 103 +++- .../adaptation/AdaptationRuntimeGuards.java | 5 +- .../art/arcane/adapt/api/data/WorldData.java | 249 ++++++++- .../arcane/adapt/api/skill/SimpleSkill.java | 266 +++++++++- .../adapt/api/skill/SkillGuiSupport.java | 103 +++- .../arcane/adapt/api/skill/SkillRegistry.java | 5 +- .../arcane/adapt/command/CommandAdapt.java | 180 ++++++- .../adaptation/agility/AgilityWindUp.java | 156 ++++-- .../chronos/ChronosInstantRecall.java | 60 +++ .../adaptation/stealth/StealthSpeed.java | 17 +- .../adapt/content/skill/SkillAgility.java | 1 + .../adapt/content/skill/SkillArchitect.java | 1 + .../arcane/adapt/content/skill/SkillAxes.java | 1 + .../adapt/content/skill/SkillBlocking.java | 1 + .../adapt/content/skill/SkillBrewing.java | 1 + .../adapt/content/skill/SkillChronos.java | 1 + .../adapt/content/skill/SkillCrafting.java | 1 + .../adapt/content/skill/SkillDiscovery.java | 1 + .../adapt/content/skill/SkillEnchanting.java | 1 + .../adapt/content/skill/SkillExcavation.java | 1 + .../adapt/content/skill/SkillHerbalism.java | 1 + .../adapt/content/skill/SkillHunter.java | 1 + .../adapt/content/skill/SkillNether.java | 1 + .../adapt/content/skill/SkillPickaxes.java | 1 + .../adapt/content/skill/SkillRanged.java | 1 + .../arcane/adapt/content/skill/SkillRift.java | 1 + .../adapt/content/skill/SkillSeaborne.java | 1 + .../adapt/content/skill/SkillStealth.java | 1 + .../adapt/content/skill/SkillSwords.java | 1 + .../adapt/content/skill/SkillTaming.java | 1 + .../adapt/content/skill/SkillTragOul.java | 1 + .../adapt/content/skill/SkillUnarmed.java | 1 + .../adapt/util/common/format/Localizer.java | 26 +- .../adapt/util/common/mantle/Mantle.java | 112 ---- .../adapt/util/common/mantle/MantleChunk.java | 105 ---- .../util/common/mantle/TectonicPlate.java | 60 --- .../adapt/util/common/mantle/io/IOWorker.java | 96 ---- .../adapt/util/common/math/VelocitySpeed.java | 5 +- src/main/resources/en_US.toml | 5 + 42 files changed, 1530 insertions(+), 605 deletions(-) delete mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java delete mode 100644 src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java diff --git a/src/main/java/art/arcane/adapt/AdaptConfig.java b/src/main/java/art/arcane/adapt/AdaptConfig.java index a7a2e2399..de93f2b03 100644 --- a/src/main/java/art/arcane/adapt/AdaptConfig.java +++ b/src/main/java/art/arcane/adapt/AdaptConfig.java @@ -44,6 +44,7 @@ public class AdaptConfig { public boolean allowAdaptationsInCreative = false; public String adaptActivatorBlock = "BOOKSHELF"; public String adaptActivatorBlockName = "a Bookshelf"; + public boolean adaptActivatorAllowVerticalFaces = false; public List blacklistedWorlds = List.of("some_world_adapt_should_not_run_in", "anotherWorldFolderName"); public int experienceMaxLevel = 1000; boolean preventHunterSkillsWhenHungerApplied = true; diff --git a/src/main/java/art/arcane/adapt/PapiExpansion.java b/src/main/java/art/arcane/adapt/PapiExpansion.java index d0f5e2eb9..fecce1da9 100644 --- a/src/main/java/art/arcane/adapt/PapiExpansion.java +++ b/src/main/java/art/arcane/adapt/PapiExpansion.java @@ -1,93 +1,27 @@ package art.arcane.adapt; +import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.util.common.format.Localizer; -import com.google.common.collect.Maps; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Function; +import java.util.Locale; public class PapiExpansion extends PlaceholderExpansion { - - private final Map> skillMap = Maps.newHashMap(); - private final Map> playerMap = Maps.newHashMap(); - private final Map, String>> adaptationMap = Maps.newHashMap(); - - public PapiExpansion() { - // this should be %adapt_skill_level%, %adapt_skill_knowledge%, %adapt_skill_xp%, %adapt_skill_freshness%, %adapt_skill_multiplier%, %adapt_skill_name% - // where skill is the id of the skill eg: %adapt_herbalism_level% - skillMap.put("level", skill -> String.valueOf(skill.getLevel()).equals("-5000") ? "0" : String.valueOf(skill.getLevel())); - skillMap.put("knowledge", skill -> String.valueOf(skill.getKnowledge()).equals("-5000") ? "0" : String.valueOf(skill.getKnowledge())); - skillMap.put("xp", skill -> String.format("%.2f", skill.getXp()).equals("-5000.00") ? "0" : String.format("%.2f", skill.getXp())); - skillMap.put("freshness", skill -> String.valueOf(skill.getFreshness()).equals("-5000") ? "0" : String.valueOf(skill.getFreshness())); - skillMap.put("multiplier", skill -> String.valueOf(skill.getMultiplier()).equals("-5000") ? "0" : String.valueOf(skill.getMultiplier())); - skillMap.put("name", skill -> Localizer.dLocalize("skill." + skill.getLine() + ".name")); - - // this should be %adapt_player_level%, %adapt_player_multiplier%, %adapt_player_availablepower%, %adapt_player_maxpower%, %adapt_player_usedpower%, %adapt_player_wisdom%, %adapt_player_masterxp%, %adapt_player_seenthings% - // the player is provided by the ingame context - playerMap.put("level", playerData -> String.valueOf(playerData.getMultiplier()).equals("-5000") ? "0" : String.valueOf(playerData.getLevel())); - playerMap.put("multiplier", playerData -> String.valueOf(playerData.getMultiplier()).equals("-5000") ? "0" : String.valueOf(playerData.getMultiplier())); - playerMap.put("availablepower", playerData -> String.valueOf(playerData.getAvailablePower()).equals("-5000") ? "0" : String.valueOf(playerData.getAvailablePower())); - playerMap.put("maxpower", playerData -> String.valueOf(playerData.getMaxPower()).equals("-5000") ? "0" : String.valueOf(playerData.getMaxPower())); - playerMap.put("usedpower", playerData -> String.valueOf(playerData.getUsedPower()).equals("-5000") ? "0" : String.valueOf(playerData.getUsedPower())); - playerMap.put("wisdom", playerData -> String.valueOf(playerData.getWisdom()).equals("-5000") ? "0" : String.valueOf(playerData.getWisdom())); - playerMap.put("masterxp", playerData -> String.valueOf(playerData.getMasterXp()).equals("-5000") ? "0" : String.valueOf(playerData.getMasterXp())); - playerMap.put("seenthings", playerData -> String.valueOf(playerData.getSeenBlocks().getSeen().size() - + playerData.getSeenBiomes().getSeen().size() - + playerData.getSeenEnchants().getSeen().size() - + playerData.getSeenEnvironments().getSeen().size() - + playerData.getSeenFoods().getSeen().size() - + playerData.getSeenItems().getSeen().size() - + playerData.getSeenMobs().getSeen().size() - + playerData.getSeenPeople().getSeen().size() - + playerData.getSeenPotionEffects().getSeen().size() + playerData.getSeenRecipes().getSeen().size() - + playerData.getSeenPotionEffects().getSeen().size() + playerData.getSeenWorlds().getSeen().size())); - - // this should be %adapt_adaptation__level%, %adapt_adaptation__maxlevel% - // where adaptation is the adaptation id (e.g. %adapt_adaptation_stealth-ghost-armor_level%) - adaptationMap.put("maxlevel", (playerData, adaptation) -> String.valueOf(adaptation.getMaxLevel())); - adaptationMap.put("level", (playerData, adaptation) -> String.valueOf(getAdaptionLevel(adaptation, playerData))); - adaptationMap.put("name", (playerData, adaptation) -> String.valueOf(getAdaptionLocalizedName(adaptation))); - } - - private Integer getAdaptionLevel(Adaptation adaptation, PlayerData playerData) { - List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getSkills(); - for (Skill skill : skills) { - List> adaptations = skill.getAdaptations(); - for (Adaptation a : adaptations) { - if (a.equals(adaptation)) { - return playerData.getSkillLine(skill.getName()).getAdaptationLevel(adaptation.getName()); - } - } - } - return 0; - } - - private String getAdaptionLocalizedName(Adaptation adaptation) { - List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getSkills(); - for (Skill skill : skills) { - List> adaptations = skill.getAdaptations(); - for (Adaptation a : adaptations) { - if (a.equals(adaptation)) { - return Localizer.dLocalize(skill.getId() + "." + adaptation.getDisplayName() + ".name"); - } - } - } - return "Unknown"; - } + private static final Locale LOCALE = Locale.ROOT; @Override public @NotNull String getIdentifier() { - return Adapt.instance.getDescription().getName().toLowerCase(); + return Adapt.instance.getDescription().getName().toLowerCase(LOCALE); } @Override @@ -107,56 +41,382 @@ public boolean persist() { @Override public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + if (player == null || player.getUniqueId() == null || params == null || params.isBlank()) { + return ""; + } + if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + return ""; + } + + PlayerData playerData = Adapt.instance.getAdaptServer().peekData(player.getUniqueId()); + if (playerData == null) { + return ""; + } + String[] args = params.split("_"); - PlayerData p = Adapt.instance.getAdaptServer().peekData(player.getUniqueId()); - String key = args[0]; - - // Handle player attributes - if (key.equals("player")) { - String playerAttr = args.length > 1 ? args[1] : ""; - if (playerMap.containsKey(playerAttr)) { - return playerMap.get(playerAttr).apply(p); - } - } - - // Handle skill attributes - if (key.equals("skill")) { - String skillID = args.length > 1 ? args[1] : ""; - PlayerSkillLine line = p.getSkillLine(skillID); - String skillAttr = args.length > 2 ? args[2] : ""; - if (line != null && skillMap.containsKey(skillAttr)) { - return skillMap.get(skillAttr).apply(line); - } - } - - // Handle adaptation attributes - if (key.equals("adaptation")) { - String adaptID = args.length > 1 ? args[1] : ""; - String adaptAttr = args.length > 2 ? args[2] : ""; - Adapt.verbose("Triggered adaptation Lookup: " + adaptID + " " + adaptAttr); - List> skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkills(); - - for (Skill s : skill) { - List> adaptations = s.getAdaptations(); - for (Adaptation a : adaptations) { - String adaptationIdWithoutUUID = a.getId().substring(37); - Adapt.verbose(adaptID + " " + adaptationIdWithoutUUID); - if (adaptationIdWithoutUUID.equals(adaptID)) { - Adapt.verbose("Found adaptation: " + a.getId()); - if ("level".equalsIgnoreCase(adaptAttr)) { - Adapt.verbose("Doing Level Lookup"); - return adaptationMap.get("level").apply(p, a); - } else if ("maxlevel".equalsIgnoreCase(adaptAttr)) { - Adapt.verbose("Doing MaxLevel Lookup"); - return adaptationMap.get("maxlevel").apply(p, a); - } else if ("name".equalsIgnoreCase(adaptAttr)) { - Adapt.verbose("Doing Name Lookup"); - return adaptationMap.get("name").apply(p, a); - } - } + if (args.length == 0) { + return ""; + } + + String root = args[0].toLowerCase(LOCALE); + if ("player".equals(root)) { + String attr = args.length > 1 ? join(args, 1) : ""; + return resolvePlayerAttribute(playerData, attr); + } + + if ("skill".equals(root)) { + if (args.length < 3) { + return "0"; + } + String skillId = args[1]; + String attr = join(args, 2); + Skill skill = resolveSkill(skillId); + if (skill == null) { + return "0"; + } + PlayerSkillLine line = playerData.getSkillLineNullable(skill.getName()); + return resolveSkillAttribute(playerData, skill, line, attr); + } + + if ("adaptation".equals(root)) { + if (args.length < 3) { + return "0"; + } + String adaptationId = args[1]; + String attr = join(args, 2); + Adaptation adaptation = resolveAdaptation(adaptationId); + if (adaptation == null) { + return "0"; + } + return resolveAdaptationAttribute(playerData, adaptation, attr); + } + + if ("skills".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { + return String.valueOf(countSkills()); + } + + if ("adaptations".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { + return String.valueOf(countAdaptations()); + } + + if (args.length >= 2) { + Skill directSkill = resolveSkill(args[0]); + if (directSkill != null) { + String attr = join(args, 1); + PlayerSkillLine line = playerData.getSkillLineNullable(directSkill.getName()); + return resolveSkillAttribute(playerData, directSkill, line, attr); + } + + Adaptation directAdaptation = resolveAdaptation(args[0]); + if (directAdaptation != null) { + String attr = join(args, 1); + return resolveAdaptationAttribute(playerData, directAdaptation, attr); + } + } + + return null; + } + + private String resolvePlayerAttribute(PlayerData playerData, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + return switch (attr) { + case "level" -> String.valueOf(playerData.getLevel()); + case "multiplier" -> format2(playerData.getMultiplier()); + case "availablepower" -> String.valueOf(playerData.getAvailablePower()); + case "maxpower" -> String.valueOf(playerData.getMaxPower()); + case "usedpower" -> String.valueOf(playerData.getUsedPower()); + case "wisdom" -> String.valueOf(playerData.getWisdom()); + case "masterxp" -> format2(playerData.getMasterXp()); + case "seenthings" -> String.valueOf(countSeenThings(playerData)); + case "skills", "skillcount" -> String.valueOf(countSkills()); + case "knownskills" -> String.valueOf(countKnownSkills(playerData)); + case "adaptations", "adaptationcount" -> String.valueOf(countAdaptations()); + case "learnedadaptations" -> String.valueOf(countLearnedAdaptations(playerData)); + default -> "0"; + }; + } + + private String resolveSkillAttribute(PlayerData playerData, Skill skill, PlayerSkillLine line, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + int currentLevel = line == null ? 0 : line.getLevel(); + + if (attr.startsWith("can_claim_") || attr.startsWith("has_level_")) { + Integer targetLevel = parseTrailingInt(attr); + if (targetLevel == null) { + return "false"; + } + return String.valueOf(targetLevel >= 0 && targetLevel <= 100 && currentLevel >= targetLevel); + } + + int learnedAdaptations = 0; + if (line != null) { + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation != null && adaptation.getLevel() > 0) { + learnedAdaptations++; } } } + + return switch (attr) { + case "id" -> skill.getName(); + case "enabled" -> String.valueOf(skill.isEnabled()); + case "name" -> Localizer.dLocalize("skill." + skill.getName() + ".name"); + case "display_name", "displayname" -> skill.getDisplayName(); + case "short_name", "shortname" -> skill.getShortName(); + case "level" -> String.valueOf(currentLevel); + case "knowledge" -> String.valueOf(line == null ? 0 : line.getKnowledge()); + case "xp" -> format2(line == null ? 0 : line.getXp()); + case "freshness" -> format2(line == null ? 0 : line.getFreshness()); + case "multiplier" -> format2(line == null ? 0 : line.getMultiplier()); + case "absolute_level", "absolutelevel" -> format4(line == null ? 0 : line.getAbsoluteLevel()); + case "progress" -> format4(line == null ? 0 : line.getLevelProgress()); + case "progress_percent", "progresspercent" -> format2((line == null ? 0 : line.getLevelProgress()) * 100D); + case "xp_to_next", "xptonext", "xp_needed", "xpneeded" -> format2(line == null ? 0 : XP.getXpUntilLevelUp(line.getXp())); + case "current_level_xp", "currentlevelxp" -> format2(XP.getXpForLevel(currentLevel)); + case "next_level_xp", "nextlevelxp" -> format2(XP.getXpForLevel(currentLevel + 1)); + case "adaptation_count", "adaptationcount" -> String.valueOf(skill.getAdaptations().size()); + case "learned_adaptations", "learnedadaptations" -> String.valueOf(learnedAdaptations); + case "can_use" -> String.valueOf(currentLevel > 0); + case "line" -> skill.getName(); + case "maxlevel", "max_level" -> String.valueOf(AdaptConfig.get().experienceMaxLevel); + default -> "0"; + }; + } + + private String resolveAdaptationAttribute(PlayerData playerData, Adaptation adaptation, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + PlayerSkillLine line = playerData.getSkillLineNullable(adaptation.getSkill().getName()); + int currentLevel = line == null ? 0 : line.getAdaptationLevel(adaptation.getName()); + + if (attr.startsWith("can_claim_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "false"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, targetLevel)); + } + + if (attr.startsWith("cost_to_") || attr.startsWith("knowledge_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(adaptation.getCostFor(targetLevel, currentLevel)); + } + + if (attr.startsWith("power_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); + return String.valueOf(Math.max(0, powerCost)); + } + + if (attr.startsWith("refund_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(adaptation.getRefundCostFor(targetLevel, currentLevel)); + } + + int nextLevel = Math.min(adaptation.getMaxLevel(), currentLevel + 1); + return switch (attr) { + case "id" -> adaptation.getName(); + case "level" -> String.valueOf(currentLevel); + case "maxlevel", "max_level" -> String.valueOf(adaptation.getMaxLevel()); + case "name", "display_name", "displayname" -> adaptation.getDisplayName(); + case "raw_name", "rawname" -> adaptation.getName(); + case "skill", "skill_id", "skillid" -> adaptation.getSkill().getName(); + case "enabled" -> String.valueOf(adaptation.isEnabled()); + case "learned" -> String.valueOf(currentLevel > 0); + case "can_use" -> String.valueOf(currentLevel > 0 && adaptation.isEnabled() && adaptation.getSkill().isEnabled()); + case "cost_next", "costnext", "knowledge_next", "knowledgenext" -> String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : adaptation.getCostFor(nextLevel, currentLevel)); + case "power_next", "powernext" -> String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : Math.max(0, adaptation.getPowerCostFor(nextLevel, currentLevel))); + case "refund_next", "refundnext" -> String.valueOf(currentLevel <= 0 ? 0 : adaptation.getRefundCostFor(Math.max(0, currentLevel - 1), currentLevel)); + case "can_claim_next", "canclaimnext" -> String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, nextLevel)); + default -> "0"; + }; + } + + private boolean canClaimTarget(PlayerData playerData, PlayerSkillLine line, Adaptation adaptation, int currentLevel, int targetLevel) { + if (targetLevel == currentLevel) { + return true; + } + + if (targetLevel > currentLevel) { + int powerCost = Math.max(0, adaptation.getPowerCostFor(targetLevel, currentLevel)); + int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); + if (!playerData.hasPowerAvailable(powerCost)) { + return false; + } + return line != null && line.getKnowledge() >= knowledgeCost; + } + + if (adaptation.isPermanent()) { + return false; + } + + return currentLevel > 0; + } + + private int clampAdaptationTarget(Adaptation adaptation, int requested) { + int clamped = Math.max(0, Math.min(requested, 100)); + return Math.min(clamped, adaptation.getMaxLevel()); + } + + private Skill resolveSkill(String rawSkillId) { + if (rawSkillId == null || rawSkillId.isBlank()) { + return null; + } + + String skillId = rawSkillId.trim(); + Skill direct = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillId); + if (direct != null) { + return direct; + } + + List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : allSkills) { + if (skill.getName().equalsIgnoreCase(skillId)) { + return skill; + } + } + return null; } + + private Adaptation resolveAdaptation(String rawAdaptationId) { + if (rawAdaptationId == null || rawAdaptationId.isBlank()) { + return null; + } + + String adaptationId = rawAdaptationId.trim().toLowerCase(LOCALE); + List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : allSkills) { + for (Adaptation adaptation : skill.getAdaptations()) { + if (matchesAdaptationIdentifier(adaptation, adaptationId)) { + return adaptation; + } + } + } + + return null; + } + + private boolean matchesAdaptationIdentifier(Adaptation adaptation, String normalizedTarget) { + String name = adaptation.getName(); + if (name != null && name.equalsIgnoreCase(normalizedTarget)) { + return true; + } + + String fullId = adaptation.getId(); + if (fullId != null && fullId.equalsIgnoreCase(normalizedTarget)) { + return true; + } + + if (fullId != null && fullId.length() > 37) { + String legacyId = fullId.substring(37); + if (legacyId.equalsIgnoreCase(normalizedTarget)) { + return true; + } + } + + String scoped = adaptation.getSkill().getName() + ":" + adaptation.getName(); + return scoped.equalsIgnoreCase(normalizedTarget); + } + + private int countSeenThings(PlayerData playerData) { + int total = 0; + total += playerData.getSeenBlocks().getSeen().size(); + total += playerData.getSeenBiomes().getSeen().size(); + total += playerData.getSeenEnchants().getSeen().size(); + total += playerData.getSeenEnvironments().getSeen().size(); + total += playerData.getSeenFoods().getSeen().size(); + total += playerData.getSeenItems().getSeen().size(); + total += playerData.getSeenMobs().getSeen().size(); + total += playerData.getSeenPeople().getSeen().size(); + total += playerData.getSeenPotionEffects().getSeen().size(); + total += playerData.getSeenRecipes().getSeen().size(); + total += playerData.getSeenWorlds().getSeen().size(); + return total; + } + + private int countSkills() { + return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills().size(); + } + + private int countKnownSkills(PlayerData playerData) { + int total = 0; + for (PlayerSkillLine line : playerData.getSkillLines().values()) { + if (line != null && line.getLevel() > 0) { + total++; + } + } + return total; + } + + private int countAdaptations() { + int total = 0; + List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : skills) { + total += skill.getAdaptations().size(); + } + return total; + } + + private int countLearnedAdaptations(PlayerData playerData) { + int total = 0; + for (PlayerSkillLine line : playerData.getSkillLines().values()) { + if (line == null) { + continue; + } + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation != null && adaptation.getLevel() > 0) { + total++; + } + } + } + return total; + } + + private Integer parseTrailingInt(String attr) { + int index = attr.lastIndexOf('_'); + if (index == -1 || index >= attr.length() - 1) { + return null; + } + + try { + return Integer.parseInt(attr.substring(index + 1)); + } catch (NumberFormatException ignored) { + return null; + } + } + + private String join(String[] args, int start) { + if (args == null || start >= args.length) { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (int i = start; i < args.length; i++) { + if (builder.length() > 0) { + builder.append('_'); + } + builder.append(args[i]); + } + return builder.toString(); + } + + private String format2(double value) { + return String.format(LOCALE, "%.2f", value); + } + + private String format4(double value) { + return String.format(LOCALE, "%.4f", value); + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 8b1fc74d1..795336f42 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -230,28 +230,62 @@ default String getStorageString(Player p, String key) { * Typed storage convenience helper for integers. */ default Integer getStorageInt(Player p, String key, Integer defaultValue) { - return getStorage(p, key, defaultValue); + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; + } + + if (value instanceof Number number) { + return number.intValue(); + } + + if (value instanceof String stringValue) { + try { + return Integer.parseInt(stringValue.trim()); + } catch (NumberFormatException ignored) { + return defaultValue; + } + } + + return defaultValue; } /** * Typed storage convenience helper for integers. */ default Integer getStorageInt(Player p, String key) { - return getStorage(p, key); + return getStorageInt(p, key, null); } /** * Typed storage convenience helper for doubles. */ default Double getStorageDouble(Player p, String key, Double defaultValue) { - return getStorage(p, key, defaultValue); + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; + } + + if (value instanceof Number number) { + return number.doubleValue(); + } + + if (value instanceof String stringValue) { + try { + return Double.parseDouble(stringValue.trim()); + } catch (NumberFormatException ignored) { + return defaultValue; + } + } + + return defaultValue; } /** * Typed storage convenience helper for doubles. */ default Double getStorageDouble(Player p, String key) { - return getStorage(p, key); + return getStorageDouble(p, key, null); } /** @@ -272,14 +306,31 @@ default Boolean getStorageBoolean(Player p, String key) { * Typed storage convenience helper for longs. */ default Long getStorageLong(Player p, String key, Long defaultValue) { - return getStorage(p, key, defaultValue); + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; + } + + if (value instanceof Number number) { + return number.longValue(); + } + + if (value instanceof String stringValue) { + try { + return Long.parseLong(stringValue.trim()); + } catch (NumberFormatException ignored) { + return defaultValue; + } + } + + return defaultValue; } /** * Typed storage convenience helper for longs. */ default Long getStorageLong(Player p, String key) { - return getStorage(p, key); + return getStorageLong(p, key, null); } /** diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index 1c84c1699..ef9472fc4 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -42,6 +42,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Recipe; import java.lang.reflect.Field; @@ -49,11 +50,15 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; final class AdaptationGuiSupport { private static final Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); private static final long PERMANENT_LEARN_CONFIRM_WINDOW_MS = 6_000L; + private static final long CLOSE_SUPPRESS_MS = 1200L; + private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; + private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); private AdaptationGuiSupport() { } @@ -156,7 +161,7 @@ static CustomModel getModel(Adaptation adaptation) { } static CustomModel getModel(Adaptation adaptation, int level) { - var model = CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "level-" + level); + CustomModel model = CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "level-" + level); if (model.material() == adaptation.getIcon() && model.model() == 0) model = CustomModel.get(Material.PAPER, "snippets", "gui", "level", String.valueOf(level)); if (model.material() == Material.PAPER && model.model() == 0) @@ -245,6 +250,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { adaptation.unlearn(player, lvl, false); spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); + suppressClose(player); w.close(); if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { if (adaptation.isPermanent()) { @@ -254,7 +260,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearned") + " " + adaptation.getDisplayName(mylevel), 1, 10, 11); } } - J.s(() -> openGui(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); + J.s(() -> openAdaptationPage(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); return; } @@ -262,7 +268,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { if (adaptation.isPermanent() && !consumePermanentLearnConfirmation(player, adaptation, lvl)) { spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); - J.s(() -> openGui(adaptation, player, currentPage), 1); + J.s(() -> openAdaptationPage(adaptation, player, currentPage), 1); return; } @@ -276,11 +282,12 @@ static void openGui(Adaptation adaptation, Player player, int page) { spw.play(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.355f); spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); } + suppressClose(player); w.close(); if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + adaptation.getDisplayName(lvl), 1, 5, 11); } - J.s(() -> openGui(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); + J.s(() -> openAdaptationPage(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); } else { spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); } @@ -305,24 +312,24 @@ static void openGui(Adaptation adaptation, Player player, int page) { .setMaterial(new MaterialBlock(Material.ARROW)) .setName(C.WHITE + "Previous") .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openGui(adaptation, player, currentPage - 1)) - .onRightClick((e) -> openGui(adaptation, player, jumpBack))); + .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage - 1)) + .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpBack))); w.setElement(-3, navRow, new UIElement("adapt-first") .setMaterial(new MaterialBlock(Material.LECTERN)) .setName(C.GRAY + "First") - .onLeftClick((e) -> openGui(adaptation, player, 0))); + .onLeftClick((e) -> openAdaptationPage(adaptation, player, 0))); } if (currentPage < plan.pageCount() - 1) { w.setElement(4, navRow, new UIElement("adapt-next") .setMaterial(new MaterialBlock(Material.ARROW)) .setName(C.WHITE + "Next") .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openGui(adaptation, player, currentPage + 1)) - .onRightClick((e) -> openGui(adaptation, player, jumpForward))); + .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage + 1)) + .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpForward))); w.setElement(3, navRow, new UIElement("adapt-last") .setMaterial(new MaterialBlock(Material.LECTERN)) .setName(C.GRAY + "Last") - .onLeftClick((e) -> openGui(adaptation, player, plan.pageCount() - 1))); + .onLeftClick((e) -> openAdaptationPage(adaptation, player, plan.pageCount() - 1))); } int from = adaptation.getMaxLevel() <= 0 ? 0 : (start + 1); @@ -337,7 +344,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { w.setElement(0, navRow, new UIElement("back") .setMaterial(new MaterialBlock(Material.ARROW)) .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> onGuiClose(adaptation, player, true))); + .onLeftClick((e) -> navigateBack(adaptation, player))); } } @@ -345,7 +352,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { AdaptPlayer a = Adapt.instance.getAdaptServer().getPlayer(player); String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; w.setTitle(adaptation.getDisplayName() + " " + C.DARK_GRAY + " " + Form.f(a.getSkillLine(adaptation.getSkill().getName()).getKnowledge()) + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge") + pageSuffix); - w.onClosed((vv) -> J.s(() -> onGuiClose(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); + w.onClosed((vv) -> J.s(() -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); w.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); } @@ -445,16 +452,78 @@ private static Boolean readBooleanField(Object source, String fieldName) { return null; } - private static void onGuiClose(Adaptation adaptation, Player player, boolean openPrevGui) { + private static void openAdaptationPage(Adaptation adaptation, Player player, int page) { + suppressClose(player); + openGui(adaptation, player, page); + } + + private static void navigateBack(Adaptation adaptation, Player player) { + playCloseSound(player); + suppressClose(player); + adaptation.getSkill().openGui(player); + } + + private static void onGuiClosed(Adaptation adaptation, Player player, boolean openPrevGui) { + if (player == null) { + return; + } + + if (consumeCloseSuppression(player)) { + return; + } + + playCloseSound(player); + if (openPrevGui) { + J.s(() -> { + if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { + adaptation.getSkill().openGui(player); + } + }, 1); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static void playCloseSound(Player player) { SoundPlayer spw = SoundPlayer.of(player.getWorld()); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); - if (openPrevGui) { - adaptation.getSkill().openGui(player); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + + private static void suppressClose(Player player) { + if (player == null) { + return; + } + + UUID playerId = player.getUniqueId(); + long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; + CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); + J.s(() -> { + Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); + if (current != null && current.longValue() == suppressUntil) { + CLOSE_SUPPRESS_UNTIL.remove(playerId); + } + }, CLOSE_SUPPRESS_CLEAR_TICKS); + } + + private static boolean consumeCloseSuppression(Player player) { + if (player == null) { + return false; + } + + Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); + if (until == null) { + return false; } + + if (until >= System.currentTimeMillis()) { + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return true; + } + + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return false; } private static String permanentConfirmPrefix(Player player, Adaptation adaptation) { diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index 5dd4bfc7c..d439fbad9 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -503,17 +503,14 @@ static int getActiveLevel(Adaptation adaptation, Player p) { return 0; } - AbilityCheckTelemetry.recordCheckAttempt(); long tick = M.tick; String key = cacheKey(adaptation, p); ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); if (cached != null && cached.tick() == tick) { - if (cached.level() > 0) { - AbilityCheckTelemetry.recordSuccessfulCheck(); - } return cached.level(); } + AbilityCheckTelemetry.recordCheckAttempt(); int level = resolveActiveLevelUncached(adaptation, p); ACTIVE_LEVEL_CACHE.put(key, new ActiveLevelCacheEntry(tick, level)); sweepActiveLevelCache(tick); diff --git a/src/main/java/art/arcane/adapt/api/data/WorldData.java b/src/main/java/art/arcane/adapt/api/data/WorldData.java index 140deab33..48c298798 100644 --- a/src/main/java/art/arcane/adapt/api/data/WorldData.java +++ b/src/main/java/art/arcane/adapt/api/data/WorldData.java @@ -21,17 +21,33 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.data.unit.Earnings; import art.arcane.adapt.api.tick.TickedObject; +import art.arcane.adapt.util.common.parallel.MultiBurst; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.mantle.Mantle; +import art.arcane.spatial.mantle.MantleRegion; import art.arcane.spatial.matter.ClassReader; +import art.arcane.spatial.matter.Matter; +import art.arcane.spatial.matter.MatterSlice; import art.arcane.spatial.matter.SpatialMatter; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.io.CountingDataInputStream; +import art.arcane.volmlib.util.mantle.io.IOWorkerCodecSupport; +import art.arcane.volmlib.util.mantle.runtime.IOWorker; +import art.arcane.volmlib.util.mantle.runtime.Mantle; +import art.arcane.volmlib.util.mantle.runtime.MantleDataAdapter; +import art.arcane.volmlib.util.mantle.runtime.MantleHooks; +import art.arcane.volmlib.util.mantle.runtime.TectonicPlate; +import art.arcane.volmlib.util.parallel.HyperLockSupport; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.event.world.WorldUnloadEvent; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; + public class WorldData extends TickedObject { private static final KMap mantles = new KMap<>(); @@ -42,13 +58,240 @@ public class WorldData extends TickedObject { private final World world; private final int minHeight; - private final Mantle mantle; + private final Mantle mantle; public WorldData(World world) { super("world-data", world.getUID().toString(), 30_000); this.world = world; this.minHeight = world.getMinHeight(); - mantle = new Mantle(Adapt.instance.getDataFolder("data", "mantle", world.getName()), world.getMaxHeight()); + mantle = createMantle(Adapt.instance.getDataFolder("data", "mantle", world.getName()), world.getMaxHeight()); + } + + private static Mantle createMantle(File dataFolder, int worldHeight) { + MantleDataAdapter adapter = createAdapter(); + MantleHooks hooks = createHooks(); + art.arcane.volmlib.util.mantle.Mantle.RegionIO> regionIO = + createRegionIO(dataFolder, worldHeight, adapter, hooks); + return new Mantle<>( + dataFolder, + worldHeight, + new HyperLockSupport(), + MultiBurst.burst, + regionIO, + adapter, + hooks + ); + } + + private static MantleDataAdapter createAdapter() { + return new MantleDataAdapter<>() { + @Override + public Matter createSection() { + return new SpatialMatter(16, 16, 16); + } + + @Override + public Matter readSection(CountingDataInputStream din) throws IOException { + try { + return Matter.readDin(din); + } catch (ClassNotFoundException e) { + throw new IOException("Failed to deserialize mantle section", e); + } + } + + @Override + public void writeSection(Matter section, DataOutputStream dos) throws IOException { + section.writeDos(dos); + } + + @Override + public void trimSection(Matter section) { + section.trimSlices(); + } + + @Override + public boolean isSectionEmpty(Matter section) { + return section.getSliceMap().isEmpty(); + } + + @Override + public Class classifyValue(Object value) { + return value == null ? Object.class : value.getClass(); + } + + @Override + @SuppressWarnings("unchecked") + public void set(Matter section, int x, int y, int z, Class type, T value) { + MatterSlice slice = (MatterSlice) section.slice(section.getClass(value)); + slice.set(x, y, z, value); + } + + @Override + public void remove(Matter section, int x, int y, int z, Class type) { + MatterSlice slice = section.slice(type); + slice.set(x, y, z, null); + } + + @Override + public T get(Matter section, int x, int y, int z, Class type) { + MatterSlice slice = section.slice(type); + return slice.get(x, y, z); + } + + @Override + public void iterate(Matter section, Class type, art.arcane.volmlib.util.function.Consumer4 iterator) { + MatterSlice slice = section.getSlice(type); + if (slice != null) { + slice.iterateSync((x, y, z, value) -> iterator.accept(x, y, z, value)); + } + } + + @Override + public boolean hasSlice(Matter section, Class type) { + return section.hasSlice(type); + } + + @Override + public void deleteSlice(Matter section, Class type) { + section.deleteSlice(type); + } + }; + } + + private static MantleHooks createHooks() { + return new MantleHooks() { + @Override + public void onReadSectionFailure(int index, + long start, + long end, + CountingDataInputStream din, + IOException error) { + Adapt.warn("Failed to read mantle chunk section, skipping it."); + Adapt.error(error.getMessage() == null ? "Unknown mantle section read error" : error.getMessage()); + error.printStackTrace(); + TectonicPlate.addError(); + } + + @Override + public void onReadChunkFailure(int index, + long start, + long end, + CountingDataInputStream din, + Throwable error) { + Adapt.warn("Failed to read mantle chunk, creating a new chunk instead."); + Adapt.error(error.getMessage() == null ? "Unknown mantle chunk read error" : error.getMessage()); + error.printStackTrace(); + } + + @Override + public String formatDuration(double millis) { + return Form.duration(millis, 0); + } + + @Override + public void onDebug(String message) { + Adapt.debug(message); + } + + @Override + public void onWarn(String message) { + Adapt.warn(message); + } + + @Override + public void onError(Throwable throwable) { + Adapt.error(throwable.getMessage() == null ? "Mantle error" : throwable.getMessage()); + throwable.printStackTrace(); + } + }; + } + + private static art.arcane.volmlib.util.mantle.Mantle.RegionIO> createRegionIO(File dataFolder, + int worldHeight, + MantleDataAdapter adapter, + MantleHooks hooks) { + IOWorker> worker = new IOWorker<>( + dataFolder, + IOWorkerCodecSupport.identity(), + 128, + (name, millis) -> Adapt.debug("Acquired mantle channel for " + name + " in " + millis + "ms") + ); + + return new art.arcane.volmlib.util.mantle.Mantle.RegionIO<>() { + @Override + public TectonicPlate read(String name) throws IOException { + try { + return worker.read(name, (regionName, in) -> TectonicPlate.read(worldHeight, in, true, adapter, hooks)); + } catch (IOException e) { + TectonicPlate migrated = readLegacy(dataFolder, worldHeight, name, adapter, hooks, e); + if (migrated != null) { + Adapt.warn("Migrated legacy mantle region " + name + " to shared mantle format."); + return migrated; + } + + throw e; + } + } + + @Override + public void write(String name, TectonicPlate region) throws IOException { + worker.write(name, "adapt", ".bin", region, TectonicPlate::write); + } + + @Override + public void close() throws IOException { + worker.close(); + } + }; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static TectonicPlate readLegacy(File dataFolder, + int worldHeight, + String name, + MantleDataAdapter adapter, + MantleHooks hooks, + IOException original) { + File file = new File(dataFolder, name); + if (!file.exists()) { + return null; + } + + try { + MantleRegion region = MantleRegion.read(worldHeight, file); + TectonicPlate plate = new TectonicPlate<>(worldHeight, region.getX(), region.getZ(), adapter, hooks); + int sectionCount = worldHeight >> 4; + + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + var legacyChunk = region.get(x, z); + if (legacyChunk == null) { + continue; + } + + art.arcane.volmlib.util.mantle.runtime.MantleChunk chunk = plate.getOrCreate(x, z); + for (int section = 0; section < sectionCount; section++) { + Matter legacySection = legacyChunk.get(section); + if (legacySection == null || legacySection.getSliceMap().isEmpty()) { + continue; + } + + Matter target = chunk.getOrCreate(section); + target.clearSlices(); + + Matter copy = legacySection.copy(); + for (var entry : copy.getSliceMap().entrySet()) { + target.putSlice(entry.getKey(), (MatterSlice) entry.getValue()); + } + } + } + } + + return plate; + } catch (Throwable t) { + original.addSuppressed(t); + return null; + } } public static void stop() { diff --git a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index 2f6d7b1e2..899960c0e 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -27,10 +27,12 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.content.item.ItemListings; +import art.arcane.adapt.util.common.format.AdventureCompat; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.format.Form; import lombok.Data; import lombok.EqualsAndHashCode; import org.bukkit.Material; @@ -42,6 +44,7 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Locale; import java.util.UUID; @EqualsAndHashCode(callSuper = false) @@ -50,6 +53,7 @@ public abstract class SimpleSkill extends TickedObject implements Skill { private final String name; private final String emojiName; private C color; + private String colorPrefix; private double minXp; private String description; private String displayName; @@ -143,6 +147,7 @@ private T loadConfig(File file, T fallback, boolean overwriteOnReadFailure) thro } protected void onConfigReload(T previousConfig, T newConfig) { + applyColorField(newConfig); applyDoubleField(newConfig, "minXp", this::setMinXp); Number interval = getNumericField(newConfig, "setInterval"); if (interval != null) { @@ -157,6 +162,24 @@ private void applyDoubleField(T source, String fieldName, java.util.function.Dou } } + private void applyColorField(T source) { + String configuredColor = getStringField(source, "skillColor"); + if (configuredColor == null || configuredColor.isBlank()) { + return; + } + + String prefix = resolveColorPrefix(configuredColor); + if (prefix == null || prefix.isBlank()) { + return; + } + + colorPrefix = prefix; + C resolvedColor = resolveLegacyColor(prefix); + if (resolvedColor != null) { + color = resolvedColor; + } + } + private Number getNumericField(T source, String fieldName) { Field f = getField(source.getClass(), fieldName); if (f == null) { @@ -178,6 +201,27 @@ private Number getNumericField(T source, String fieldName) { return null; } + private String getStringField(T source, String fieldName) { + Field f = getField(source.getClass(), fieldName); + if (f == null) { + return null; + } + + try { + f.setAccessible(true); + Object value = f.get(source); + if (value instanceof String stringValue) { + return stringValue; + } + } catch (Throwable ex) { + Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + + return null; + } + private Field getField(Class type, String name) { Class current = type; while (current != null) { @@ -256,9 +300,42 @@ protected boolean isInCreativeOrSpectator(Player p) { return SkillRuntimeGuards.isInCreativeOrSpectator(p); } + public void setColor(C color) { + C resolved = color == null ? C.WHITE : color; + this.color = resolved; + this.colorPrefix = resolved.toString(); + } + @Override public String getDisplayName() { - return displayName == null ? Skill.super.getDisplayName() : (C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName() + " " + displayName); + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); + } + + String shownName = displayName == null ? Form.capitalize(getName()) : displayName; + return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName() + " " + shownName; + } + + @Override + public String getShortName() { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); + } + + return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName(); + } + + @Override + public String getDisplayName(int level) { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); + } + + if (level > 0) { + return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; + } + + return getDisplayName(); } @Override @@ -324,6 +401,193 @@ public void unregister() { adaptations.forEach(Adaptation::unregister); } + private String colorPrefixValue() { + if (colorPrefix == null || colorPrefix.isBlank()) { + return color == null ? C.WHITE.toString() : color.toString(); + } + return colorPrefix; + } + + private String resolveColorPrefix(String rawInput) { + String raw = rawInput == null ? "" : rawInput.trim(); + if (raw.isBlank()) { + return null; + } + + C named = parseNamedColor(raw); + if (named != null) { + return named.toString(); + } + + String normalizedHex = normalizeHex(raw); + if (normalizedHex != null) { + return C.translateAlternateColorCodes('&', "&#" + normalizedHex); + } + + if (raw.indexOf(C.COLOR_CHAR) >= 0) { + return raw; + } + + String legacy = C.translateAlternateColorCodes('&', raw); + if (legacy != null && legacy.indexOf(C.COLOR_CHAR) >= 0) { + return legacy; + } + + String mini = AdventureCompat.toLegacySection(raw); + if (mini != null && mini.indexOf(C.COLOR_CHAR) >= 0) { + return mini; + } + + return null; + } + + private C parseNamedColor(String raw) { + String normalized = raw.trim().toUpperCase(Locale.ROOT) + .replace('-', '_') + .replace(' ', '_'); + try { + C candidate = C.valueOf(normalized); + if (candidate.isColor()) { + return candidate; + } + } catch (IllegalArgumentException ignored) { + } + + if (raw.length() == 1) { + char code = Character.toLowerCase(raw.charAt(0)); + if (isLegacyColorChar(code)) { + return C.getByChar(code); + } + } + + return null; + } + + private String normalizeHex(String raw) { + String value = raw; + if (value.startsWith("#")) { + value = value.substring(1); + } else if (value.startsWith("0x") || value.startsWith("0X")) { + value = value.substring(2); + } + + if (value.length() != 6) { + return null; + } + + for (int i = 0; i < value.length(); i++) { + if (!isHexChar(value.charAt(i))) { + return null; + } + } + + return value; + } + + private boolean isHexChar(char c) { + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); + } + + private boolean isLegacyColorChar(char code) { + return (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f'); + } + + private C resolveLegacyColor(String prefix) { + if (prefix == null || prefix.isBlank()) { + return color; + } + + for (int i = 0; i < prefix.length() - 1; i++) { + if (prefix.charAt(i) != C.COLOR_CHAR) { + continue; + } + + char code = Character.toLowerCase(prefix.charAt(i + 1)); + if (isLegacyColorChar(code)) { + return C.getByChar(code); + } + + if (code == 'x') { + String hex = parseSectionHex(prefix, i); + if (hex == null) { + continue; + } + + C nearest = nearestLegacyColor(hex); + if (nearest != null) { + return nearest; + } + } + } + + return color; + } + + private String parseSectionHex(String prefix, int start) { + if (start + 13 >= prefix.length()) { + return null; + } + + StringBuilder hex = new StringBuilder(6); + for (int i = 0; i < 6; i++) { + int colorTokenIndex = start + 2 + (i * 2); + int hexIndex = colorTokenIndex + 1; + if (colorTokenIndex >= prefix.length() || hexIndex >= prefix.length()) { + return null; + } + if (prefix.charAt(colorTokenIndex) != C.COLOR_CHAR) { + return null; + } + char hexChar = prefix.charAt(hexIndex); + if (!isHexChar(hexChar)) { + return null; + } + hex.append(hexChar); + } + return hex.toString(); + } + + private C nearestLegacyColor(String hex) { + try { + int rgb = Integer.parseInt(hex, 16); + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + C best = null; + long bestDistance = Long.MAX_VALUE; + for (C candidate : C.values()) { + if (!candidate.isColor()) { + continue; + } + + String candidateHex = candidate.hex(); + if (candidateHex == null || candidateHex.length() != 7) { + continue; + } + + int candidateRgb = Integer.parseInt(candidateHex.substring(1), 16); + int cr = (candidateRgb >> 16) & 0xFF; + int cg = (candidateRgb >> 8) & 0xFF; + int cb = candidateRgb & 0xFF; + + long distance = ((long) r - cr) * ((long) r - cr) + + ((long) g - cg) * ((long) g - cg) + + ((long) b - cb) * ((long) b - cb); + if (distance < bestDistance) { + bestDistance = distance; + best = candidate; + } + } + + return best == null ? color : best; + } catch (Throwable ignored) { + return color; + } + } + @Override public abstract void onTick(); } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index 135357eb1..df6635c89 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -41,14 +41,22 @@ import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; final class SkillGuiSupport { + private static final long CLOSE_SUPPRESS_MS = 1200L; + private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; + private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); + private SkillGuiSupport() { } @@ -174,7 +182,7 @@ static void openGui(Skill skill, Player player, int page) { .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) .setProgress(1D) - .onLeftClick((e) -> adaptation.openGui(player)); + .onLeftClick((e) -> openAdaptation(adaptation, player)); reveal.add(new GuiEffects.Placement(pos, row, element)); } } @@ -191,24 +199,24 @@ static void openGui(Skill skill, Player player, int page) { .setMaterial(new MaterialBlock(Material.ARROW)) .setName(C.WHITE + "Previous") .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openGui(skill, player, currentPage - 1)) - .onRightClick((e) -> openGui(skill, player, jumpBack))); + .onLeftClick((e) -> openSkillPage(skill, player, currentPage - 1)) + .onRightClick((e) -> openSkillPage(skill, player, jumpBack))); window.setElement(-3, navRow, new UIElement("skill-first") .setMaterial(new MaterialBlock(Material.LECTERN)) .setName(C.GRAY + "First") - .onLeftClick((e) -> openGui(skill, player, 0))); + .onLeftClick((e) -> openSkillPage(skill, player, 0))); } if (currentPage < plan.pageCount() - 1) { window.setElement(4, navRow, new UIElement("skill-next") .setMaterial(new MaterialBlock(Material.ARROW)) .setName(C.WHITE + "Next") .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openGui(skill, player, currentPage + 1)) - .onRightClick((e) -> openGui(skill, player, jumpForward))); + .onLeftClick((e) -> openSkillPage(skill, player, currentPage + 1)) + .onRightClick((e) -> openSkillPage(skill, player, jumpForward))); window.setElement(3, navRow, new UIElement("skill-last") .setMaterial(new MaterialBlock(Material.LECTERN)) .setName(C.GRAY + "Last") - .onLeftClick((e) -> openGui(skill, player, plan.pageCount() - 1))); + .onLeftClick((e) -> openSkillPage(skill, player, plan.pageCount() - 1))); } int from = visibleAdaptations.isEmpty() ? 0 : (start + 1); @@ -223,28 +231,95 @@ static void openGui(Skill skill, Player player, int page) { window.setElement(0, navRow, new UIElement("back") .setMaterial(new MaterialBlock(Material.ARROW)) .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> onGuiClose(player, true))); + .onLeftClick((e) -> navigateBack(player))); } } String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")" + pageSuffix); - window.onClosed((vv) -> J.s(() -> onGuiClose(player, !AdaptConfig.get().isEscClosesAllGuis()))); + window.onClosed((vv) -> J.s(() -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); window.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); } - private static void onGuiClose(Player player, boolean openPrevGui) { + private static void openSkillPage(Skill skill, Player player, int page) { + suppressClose(player); + openGui(skill, player, page); + } + + private static void openAdaptation(Adaptation adaptation, Player player) { + suppressClose(player); + adaptation.openGui(player); + } + + private static void navigateBack(Player player) { + playCloseSound(player); + suppressClose(player); + SkillsGui.open(player); + } + + private static void onGuiClosed(Player player, boolean openPrevGui) { + if (player == null) { + return; + } + + if (consumeCloseSuppression(player)) { + return; + } + + playCloseSound(player); + if (openPrevGui) { + J.s(() -> { + if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { + SkillsGui.open(player); + } + }, 1); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static void playCloseSound(Player player) { SoundPlayer spw = SoundPlayer.of(player.getWorld()); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); - if (openPrevGui) { - SkillsGui.open(player); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + + private static void suppressClose(Player player) { + if (player == null) { + return; } + + UUID playerId = player.getUniqueId(); + long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; + CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); + J.s(() -> { + Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); + if (current != null && current.longValue() == suppressUntil) { + CLOSE_SUPPRESS_UNTIL.remove(playerId); + } + }, CLOSE_SUPPRESS_CLEAR_TICKS); + } + + private static boolean consumeCloseSuppression(Player player) { + if (player == null) { + return false; + } + + Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); + if (until == null) { + return false; + } + + if (until >= System.currentTimeMillis()) { + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return true; + } + + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return false; } private static String normalizeSortKey(String value) { diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index 310251ee1..84e46afb0 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -123,7 +123,10 @@ public void on(PlayerInteractEvent e) { boolean commonConditions = p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null; boolean isLectern = commonConditions && e.getClickedBlock().getType().equals(Material.LECTERN); boolean isObserver = commonConditions && e.getClickedBlock().getType().equals(Material.OBSERVER); - boolean isAdaptActivator = !e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN) && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) + boolean allowVerticalFaces = AdaptConfig.get().adaptActivatorAllowVerticalFaces; + boolean validActivatorFace = e.getBlockFace() != null + && (allowVerticalFaces || (!e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN))); + boolean isAdaptActivator = validActivatorFace && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null && canInteract(p, e.getClickedBlock().getLocation()) && e.getClickedBlock().getType().equals(Material.valueOf(AdaptConfig.get().adaptActivatorBlock)) && (p.getInventory().getItemInMainHand().getType().equals(Material.AIR) diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index 6ae087249..c6b66691d 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -1,6 +1,7 @@ package art.arcane.adapt.command; import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.skill.SimpleSkill; @@ -8,6 +9,8 @@ import art.arcane.adapt.api.skill.SkillRegistry; import art.arcane.adapt.api.world.AdaptServer; import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.content.gui.ConfigGui; import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.content.item.ExperienceOrb; @@ -280,12 +283,17 @@ public void determine( Player targetPlayer = player; if (targetPlayer == null && BukkitDirectorContext.isConsole()) { FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return; } else if (targetPlayer == null) { targetPlayer = BukkitDirectorContext.player(); } //the format is skillname:adaptationname - String[] split = adaptationTarget.name().split(":"); + String[] split = adaptationTarget.name().split(":", 2); + if (split.length != 2) { + FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); + return; + } String skillname = split[0]; String adaptationname = split[1]; @@ -295,9 +303,9 @@ public void determine( if (adaptation.getName().equalsIgnoreCase(adaptationname)) { if (targetPlayer != null) { if (assign) { - adaptation.learn(player, level, force); + adaptation.learn(targetPlayer, level, force); } else { - adaptation.unlearn(player, level, force); + adaptation.unlearn(targetPlayer, level, force); } } else { FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); @@ -310,6 +318,160 @@ public void determine( } } + @Director(name = "claim-skill", description = "Set a player's skill line level between 0 and 100 for custom UI integration.") + public void claimSkill( + @Param(aliases = "skill") + AdaptationListingHandler.SkillProvider skillTarget, + @Param(aliases = "level") + int level, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); + return; + } + + Player targetPlayer = resolveTargetPlayer(player); + if (targetPlayer == null) { + return; + } + + if (level < 0 || level > 100) { + FConst.error("Skill claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); + return; + } + + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillTarget.name()); + if (skill == null) { + FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); + return; + } + + PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); + if (skillLine == null) { + FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); + return; + } + + double targetXp = XP.getXpForLevel(level); + skillLine.setXp(targetXp); + if (skillLine.getLastXP() > targetXp) { + skillLine.setLastXP(targetXp); + } + if (skillLine.getLastLevel() > level) { + skillLine.setLastLevel(level); + } + + FConst.success("Set " + targetPlayer.getName() + " " + skill.getName() + " level to " + level + ".").send(BukkitDirectorContext.sender()); + } + + @Director(name = "claim-adaptation", description = "Set an adaptation level between 0 and 100 if the player can afford it.") + public void claimAdaptation( + @Param(aliases = "adaptationTarget") + AdaptationListingHandler.AdaptationProvider adaptationTarget, + @Param(aliases = "level") + int level, + @Param(aliases = "force", defaultValue = "false") + boolean force, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); + return; + } + + Player targetPlayer = resolveTargetPlayer(player); + if (targetPlayer == null) { + return; + } + + if (level < 0 || level > 100) { + FConst.error("Adaptation claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); + return; + } + + String[] split = adaptationTarget.name().split(":", 2); + if (split.length != 2) { + FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); + return; + } + + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(split[0]); + if (skill == null) { + FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); + return; + } + + Adaptation adaptation = null; + for (Adaptation candidate : skill.getAdaptations()) { + if (candidate.getName().equalsIgnoreCase(split[1])) { + adaptation = candidate; + break; + } + } + + if (adaptation == null) { + FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); + return; + } + + PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); + if (skillLine == null) { + FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); + return; + } + + int currentLevel = skillLine.getAdaptationLevel(adaptation.getName()); + int targetLevel = Math.max(0, Math.min(level, adaptation.getMaxLevel())); + if (targetLevel == currentLevel) { + FConst.success("No change: " + adaptation.getName() + " is already at level " + currentLevel + ".").send(BukkitDirectorContext.sender()); + return; + } + + if (targetLevel > currentLevel) { + int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); + int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); + + if (!force) { + if (!playerData.hasPowerAvailable(powerCost)) { + FConst.error("Not enough available power. Need " + powerCost + ", have " + playerData.getAvailablePower() + ".").send(BukkitDirectorContext.sender()); + return; + } + + if (skillLine.getKnowledge() < knowledgeCost) { + FConst.error("Not enough knowledge in " + skill.getName() + ". Need " + knowledgeCost + ", have " + skillLine.getKnowledge() + ".").send(BukkitDirectorContext.sender()); + return; + } + + if (!skillLine.spendKnowledge(knowledgeCost)) { + FConst.error("Failed to spend required knowledge (" + knowledgeCost + ").").send(BukkitDirectorContext.sender()); + return; + } + } + + skillLine.setAdaptation(adaptation, targetLevel); + FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); + return; + } + + if (adaptation.isPermanent() && !force) { + FConst.error(adaptation.getName() + " is permanent and cannot be lowered without force=true.").send(BukkitDirectorContext.sender()); + return; + } + + int refund = AdaptConfig.get().isHardcoreNoRefunds() ? 0 : adaptation.getRefundCostFor(targetLevel, currentLevel); + skillLine.setAdaptation(adaptation, targetLevel); + if (refund > 0) { + skillLine.giveKnowledge(refund); + } + + FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); + } + @Director(name = "migrate-configs", description = "Force migrate and rewrite all skill/adaptation configs to canonical TOML with comments.") public void migrateConfigs() { if (!BukkitDirectorContext.hasPermission("adapt.debug")) { @@ -353,4 +515,16 @@ private List> allSkillSnapshot() { return SkillRegistry.skills.sortV(); } + + private Player resolveTargetPlayer(Player player) { + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return null; + } + if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); + } + return targetPlayer; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index 0c48e333c..91e6a8eb8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -25,7 +25,6 @@ import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.volmlib.util.inventorygui.Element; -import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; @@ -39,6 +38,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.util.Vector; @@ -48,7 +48,7 @@ public class AgilityWindUp extends SimpleAdaptation { private final Map ticksRunning; - private final Map speedBoosting; + private final Map states; public AgilityWindUp() { super("agility-wind-up"); @@ -61,7 +61,7 @@ public AgilityWindUp() { setInitialCost(getConfig().initialCost); setInterval(50); ticksRunning = new ConcurrentHashMap<>(); - speedBoosting = new ConcurrentHashMap<>(); + states = new ConcurrentHashMap<>(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.POWERED_RAIL) .key("challenge_agility_wind_up_10min") @@ -90,9 +90,12 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - ticksRunning.remove(id); - speedBoosting.remove(id); + clearAndRemoveState(e.getPlayer()); + } + + @EventHandler + public void on(PlayerDeathEvent e) { + clearAndRemoveState(e.getEntity()); } @ReflectiveHandler @@ -104,7 +107,10 @@ public void on(EntityMountEvent event) { Player p = (Player) event.getEntity(); UUID id = p.getUniqueId(); ticksRunning.remove(id); - invalidateWindupSpeed(p, id, true); + RuntimeState state = states.get(id); + if (state != null) { + clearBoost(p, state); + } } @ReflectiveHandler @@ -114,7 +120,12 @@ public void on(EntityDismountEvent event) { } Player p = (Player) event.getEntity(); - ticksRunning.remove(p.getUniqueId()); + UUID id = p.getUniqueId(); + ticksRunning.remove(id); + RuntimeState state = states.get(id); + if (state != null) { + clearBoost(p, state); + } } private double getWindupTicks(double factor) { @@ -142,15 +153,17 @@ private void updatePlayer(Player p) { } UUID id = p.getUniqueId(); - if (!hasActiveAdaptation(p) || !isVelocityEligible(p)) { + RuntimeState state = states.computeIfAbsent(id, key -> new RuntimeState()); + if (!hasActiveAdaptation(p) || !isWindupEligible(p) || !p.isSprinting()) { ticksRunning.remove(id); - invalidateWindupSpeed(p, id, false); + clearBoost(p, state); return; } - if (!p.isSprinting()) { + double factor = getLevelPercent(p); + if (factor <= 0) { ticksRunning.remove(id); - invalidateWindupSpeed(p, id, false); + clearBoost(p, state); return; } @@ -160,10 +173,10 @@ private void updatePlayer(Player p) { return; } - double factor = getLevelPercent(p); - double ticksToMax = getWindupTicks(factor); + double ticksToMax = Math.max(1D, getWindupTicks(factor)); double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); double speedIncrease = M.lerp(0, getWindupSpeed(factor), progress); + applyBoost(p, state, speedIncrease); if (areParticlesEnabled()) { if (M.r(0.2 * progress)) { @@ -175,48 +188,71 @@ private void updatePlayer(Player p) { } } - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - speedBoosting.put(id, false); - return; + if (progress >= 1.0 && isMovingHorizontally(p, getConfig().movementVelocityThreshold)) { + getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); } - applyWindupSpeed(p, id, input, speedIncrease); + } - if (progress >= 1.0) { - getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); + private void applyBoost(Player p, RuntimeState state, double speedIncrease) { + if (!state.boosting) { + state.boosting = true; + state.originalWalkSpeed = p.getWalkSpeed(); + } + + float baseWalkSpeed = clampWalkSpeed(state.originalWalkSpeed); + double bonus = Math.max(0D, speedIncrease) * Math.max(0D, getConfig().walkSpeedBonusScalar); + float target = clampWalkSpeed((float) (baseWalkSpeed * (1D + bonus))); + float configuredMaxWalkSpeed = clampWalkSpeed((float) Math.max(0D, getConfig().maxWalkSpeed)); + float maxWalkSpeed = Math.max(baseWalkSpeed, configuredMaxWalkSpeed); + if (target > maxWalkSpeed) { + target = maxWalkSpeed; + } + + float smoothing = (float) Math.max(0D, Math.min(1D, getConfig().walkSpeedLerpPerTick)); + float current = p.getWalkSpeed(); + float next = current + ((target - current) * smoothing); + if (Math.abs(target - next) < 0.0005f) { + next = target; + } + + if (Math.abs(current - next) > 0.0001f) { + p.setWalkSpeed(next); } } - private void applyWindupSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot input, double speedIncrease) { - Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - speedBoosting.put(id, false); + private void clearBoost(Player p, RuntimeState state) { + if (!state.boosting) { return; } - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - double computedTargetSpeed = Math.max(0, getConfig().baseHorizontalSpeed * (1.0 + Math.max(0, speedIncrease))); - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, Math.max(horizontal.length(), computedTargetSpeed)); - Vector targetHorizontal = direction.multiply(targetSpeed); - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - speedBoosting.put(id, true); + state.boosting = false; + float restore = clampWalkSpeed(state.originalWalkSpeed); + float current = p.getWalkSpeed(); + if (Math.abs(current - restore) > 0.0001f) { + p.setWalkSpeed(restore); + } } - private void invalidateWindupSpeed(Player p, UUID id, boolean invalidState) { - if (!speedBoosting.getOrDefault(id, false)) { + private void clearAndRemoveState(Player p) { + if (p == null) { return; } - if (invalidState && getConfig().hardStopOnInvalidState) { - VelocitySpeed.hardStopHorizontal(p); + UUID id = p.getUniqueId(); + ticksRunning.remove(id); + RuntimeState state = states.remove(id); + if (state != null) { + clearBoost(p, state); } + } - speedBoosting.put(id, false); + private boolean isMovingHorizontally(Player p, double velocityThreshold) { + Vector movement = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); + double threshold = Math.max(0D, velocityThreshold); + return movement.lengthSquared() > (threshold * threshold); } - private boolean isVelocityEligible(Player p) { + private boolean isWindupEligible(Player p) { GameMode mode = p.getGameMode(); if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { return false; @@ -230,6 +266,16 @@ private boolean isVelocityEligible(Player p) { && p.getVehicle() == null; } + private float clampWalkSpeed(float speed) { + if (speed < 0f) { + return 0f; + } + if (speed > 1f) { + return 1f; + } + return speed; + } + @Override public boolean isEnabled() { return getConfig().enabled; @@ -264,25 +310,19 @@ protected static class Config { double windupSpeedBase = 0.22; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Level Multiplier for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double windupSpeedLevelMultiplier = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for windup velocity scaling.", impact = "Higher values increase movement speed while windup is active.") - double baseHorizontalSpeed = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") - double maxHorizontalSpeed = 0.31; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the windup target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double accelPerTick = 0.045; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when sprint movement is not applied.", impact = "Higher values stop faster and reduce carry momentum.") - double brakePerTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") - double stopThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, windup velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") - boolean hardStopOnInvalidState = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + @art.arcane.adapt.util.config.ConfigDoc(value = "Scales walk-speed gain from windup speed increase while sprinting.", impact = "Higher values produce stronger land-speed acceleration.") + double walkSpeedBonusScalar = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Smooths walk-speed changes toward windup target each tick.", impact = "Higher values ramp faster; lower values feel softer.") + double walkSpeedLerpPerTick = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed this adaptation can set while windup is active.", impact = "Higher values allow faster grounded sprinting before clamping.") + double maxWalkSpeed = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal movement speed required for max-speed stat credit.", impact = "Higher values require clearer movement before counting max-speed ticks.") + double movementVelocityThreshold = 0.015; + } + + private static class RuntimeState { + private boolean boosting; + private float originalWalkSpeed; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index f72b16009..ec68bb7af 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -381,6 +381,39 @@ private Snapshot snapshotFromPlayer(Player p, long now) { p.getFireTicks()); } + private Snapshot snapshotFromLocation(Player p, Location location, long now) { + World world = location.getWorld(); + if (world == null) { + world = p.getWorld(); + } + + return new Snapshot(now, + world.getName(), + location.getX(), + location.getY(), + location.getZ(), + location.getYaw(), + location.getPitch(), + p.getHealth(), + p.getFoodLevel(), + p.getSaturation(), + p.getExhaustion(), + p.getFireTicks()); + } + + private void resetSnapshotHistory(Player p, Location location) { + if (location == null) { + return; + } + + long now = M.ms(); + UUID id = p.getUniqueId(); + Deque queue = snapshots.computeIfAbsent(id, unused -> new ArrayDeque<>()); + queue.clear(); + queue.addLast(snapshotFromLocation(p, location, now)); + lastSnapshot.put(id, now); + } + private void captureSnapshot(Player p) { long now = M.ms(); UUID id = p.getUniqueId(); @@ -544,6 +577,33 @@ public void on(PlayerQuitEvent e) { clearPlayerState(e.getPlayer().getUniqueId()); } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerTeleportEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isRecallEligible(p) || rewinding.contains(id)) { + return; + } + + Location destination = e.getTo(); + if (destination == null) { + return; + } + + resetSnapshotHistory(p, destination); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerChangedWorldEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isRecallEligible(p) || rewinding.contains(id)) { + return; + } + + resetSnapshotHistory(p, p.getLocation()); + } + @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { Player p = e.getPlayer(); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index e75809e42..27a6aa699 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -75,12 +75,12 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerQuitEvent e) { - states.remove(e.getPlayer().getUniqueId()); + clearAndRemoveState(e.getPlayer()); } @EventHandler public void on(PlayerDeathEvent e) { - states.remove(e.getEntity().getUniqueId()); + clearAndRemoveState(e.getEntity()); } @Override @@ -154,6 +154,19 @@ private void clearBoost(Player p, RuntimeState state) { } } + private void clearAndRemoveState(Player p) { + if (p == null) { + return; + } + + RuntimeState state = states.remove(p.getUniqueId()); + if (state == null) { + return; + } + + clearBoost(p, state); + } + private boolean isEligible(Player p) { if (!hasActiveAdaptation(p)) { return false; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java index dbe9c5b3c..ad4d36232 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java @@ -242,6 +242,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&a"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Move1k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeMove1kReward = 500; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint5k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index 22833169c..39b38d8b8 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -244,6 +244,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&b"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Place1k Reward for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengePlace1kReward = 1750; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Value Multiplier for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java index 543cefeae..39ebe4c62 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java @@ -271,6 +271,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&e"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Axes skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java index 5d31392a8..614954044 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java @@ -253,6 +253,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&8"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Blocked Attack for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnBlockedAttack = 25; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block1k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java index e4c29b555..c6386f44e 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java @@ -325,6 +325,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&d"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeBrew1k = 1000; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew Splash1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index a18fe636a..f84394ac9 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -530,6 +530,7 @@ protected static class Config { long setInterval = 5050; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&b"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Movement For Active Check for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double minimumMovementForActiveCheck = 0.35; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Per Bonus XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java index 344a3d5c1..2cf670cbf 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java @@ -316,6 +316,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&e"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Base XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double furnaceBaseXP = 30; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java index a57e9f0d8..0cbfc0448 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java @@ -384,6 +384,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&b"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery skill.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Biome XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java index b7d476b7a..ebf7d459a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java @@ -231,6 +231,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&d"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Enchant Power XPMultiplier for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double enchantPowerXPMultiplier = 45; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index 0b49e76ec..1ff2f6e39 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -263,6 +263,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&e"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Excavation skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index 0473532fb..74c6658e4 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -317,6 +317,7 @@ public boolean isEnabled() { public static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") public boolean enabled = true; + String skillColor = "&a"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Xp Cooldown for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public double harvestXpCooldown = 3500; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Consume XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java index 643d24653..c65b68040 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java @@ -302,6 +302,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&c"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Hunter skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Turtle Egg Kill XP for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java index b1b6723aa..d25b3d6f2 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java @@ -249,6 +249,7 @@ public boolean isEnabled() { public static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") private boolean enabled = true; + String skillColor = "&8"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Damage Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") private double witherDamageXp = 26.0; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Attack Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index 57e90c611..02349f283 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -300,6 +300,7 @@ protected static class Config { public double debrisBonus = 210; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&6"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Pickaxes skill.", impact = "True enables this behavior and false disables it.") boolean getXpForAttackingWithTools = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java index 48a2e7be0..d353b4d73 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java @@ -265,6 +265,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&2"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shoot XP for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double shootXP = 5; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java index 677631c60..a42e82f63 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java @@ -303,6 +303,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&5"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Destroy End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double destroyEndCrystalXP = 250; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java index 75924b1dc..501f483e8 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java @@ -297,6 +297,7 @@ protected static class Config { double damagedrownxpmultiplier = 3; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&9"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim1nm Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSwim1nmReward = 750; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim5k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java index 5290eab4f..08d5f0d00 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java @@ -243,6 +243,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&8"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak1k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double challengeSneak1kReward = 1750; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java index 4cdebb137..318eadf8e 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java @@ -244,6 +244,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&e"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") long cooldownDelay = 1250; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java index f65fb6ff5..9d6387fd6 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java @@ -254,6 +254,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&6"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Xp Base for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double tameXpBase = 65; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 31cbb47bb..61b067658 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -303,6 +303,7 @@ protected static class Config { boolean takeAwaySkillsOnDeath = false; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&b"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") boolean showParticles = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java index a60d37bc4..207746a5b 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java @@ -249,6 +249,7 @@ public boolean isEnabled() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") boolean enabled = true; + String skillColor = "&e"; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double damageXPMultiplier = 4.5; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java index 48496a651..d9ff488fb 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java +++ b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java @@ -37,6 +37,13 @@ public class Localizer { private static JsonObject cachedPrimaryLanguageRoot; private static String cachedFallbackLanguage; private static JsonObject cachedFallbackLanguageRoot; + private static final String LANGUAGE_COLOR_NOTE = String.join("\n", + "# Color Codes:", + "# - Legacy format: &0..&f, &k..&o, &r (example: \"&7Gray\")", + "# - Hex format: &#RRGGBB or &x&R&R&G&G&B&B (example: \"7FFAAMint\")", + "# - MiniMessage also works (example: \"<#55FFAA>Mint\")", + "" + ); @SneakyThrows public static void updateLanguageFile() { @@ -102,9 +109,9 @@ public static String dLocalize(String key, Object... params) { Adapt.verbose("Loaded Localization: " + resolved + " for key: " + key); } } - var s = applyParameters(Adapt.wordKey.get(cacheKey), params); + String s = applyParameters(Adapt.wordKey.get(cacheKey), params); + s = C.translateAlternateColorCodes('&', s); if (AdaptConfig.get().isAutomaticGradients()) { - s = C.translateAlternateColorCodes('&', s); s = C.aura(s, -20, 7, 8, 0.36); } @@ -141,7 +148,8 @@ private static void syncLanguageResource(File langFolder, String languageCode) t File tomlTarget = new File(langFolder, languageCode + ".toml"); Files.deleteIfExists(tomlTarget.toPath()); - Files.writeString(tomlTarget.toPath(), ConfigFileSupport.serializeJsonElementToToml(parsed)); + String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); + Files.writeString(tomlTarget.toPath(), applyLanguageHeader(toml)); File legacyJsonTarget = new File(langFolder, jsonResourcePath); Files.deleteIfExists(legacyJsonTarget.toPath()); @@ -313,7 +321,8 @@ private static void migrateExistingLanguageFilesToToml() { continue; } - Files.writeString(tomlFile.toPath(), ConfigFileSupport.serializeJsonElementToToml(parsed)); + String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); + Files.writeString(tomlFile.toPath(), applyLanguageHeader(toml)); Adapt.info("Migrated legacy language file [" + jsonFile.getName() + "] -> [" + tomlFile.getName() + "]."); Files.deleteIfExists(jsonFile.toPath()); } @@ -322,4 +331,13 @@ private static void migrateExistingLanguageFilesToToml() { Adapt.warn("Failed to migrate legacy language json files: " + e.getMessage()); } } + + private static String applyLanguageHeader(String body) { + String source = body == null ? "" : body; + String trimmed = source.stripLeading(); + if (trimmed.startsWith("# Color Codes:")) { + return source; + } + return LANGUAGE_COLOR_NOTE + source; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java b/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java deleted file mode 100644 index ddb142f39..000000000 --- a/src/main/java/art/arcane/adapt/util/common/mantle/Mantle.java +++ /dev/null @@ -1,112 +0,0 @@ -package art.arcane.adapt.util.mantle; - -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.common.parallel.MultiBurst; -import art.arcane.adapt.util.mantle.io.IOWorker; -import art.arcane.spatial.matter.Matter; -import art.arcane.spatial.matter.MatterSlice; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.function.Consumer4; -import art.arcane.volmlib.util.io.IO; -import art.arcane.volmlib.util.parallel.HyperLockSupport; - -import java.io.File; -import java.io.IOException; - -public class Mantle extends art.arcane.volmlib.util.mantle.Mantle { - public Mantle(File dataFolder, int worldHeight) { - super(dataFolder, worldHeight, DEFAULT_LOCK_SIZE, new HyperLockSupport(), MultiBurst.burst, new IOWorkerRegionIO(dataFolder, worldHeight)); - } - - @Override - protected TectonicPlate createRegion(int x, int z) { - return new TectonicPlate(getWorldHeight(), x, z); - } - - @Override - protected void setChunkValue(MantleChunk chunk, int x, int y, int z, T value) { - Matter matter = chunk.getOrCreate(y >> 4); - matter.slice(matter.getClass(value)).set(x & 15, y & 15, z & 15, value); - } - - @Override - protected void removeChunkValue(MantleChunk chunk, int x, int y, int z, Class type) { - Matter matter = chunk.getOrCreate(y >> 4); - matter.slice(type).set(x & 15, y & 15, z & 15, null); - } - - @Override - @SuppressWarnings("unchecked") - protected T getChunkValue(MantleChunk chunk, int x, int y, int z, Class type) { - return (T) chunk.getOrCreate(y >> 4) - .slice(type) - .get(x & 15, y & 15, z & 15); - } - - @Override - protected void iterateChunkValues(MantleChunk chunk, Class type, Consumer4 iterator) { - chunk.iterate(type, iterator); - } - - @Override - protected void deleteChunkSlice(MantleChunk chunk, Class type) { - chunk.deleteSlices(type); - } - - @Override - protected String formatDuration(double millis) { - return Form.duration(millis, 0); - } - - @Override - protected void onDebug(String message) { - Adapt.debug(message); - } - - @Override - protected void onWarn(String message) { - Adapt.warn(message); - } - - @Override - protected void onError(Throwable throwable) { - Adapt.error(throwable.getMessage() == null ? "Mantle error" : throwable.getMessage()); - throwable.printStackTrace(); - } - - @Override - protected void deleteTemporaryFiles() { - IO.delete(new File(getDataFolder(), ".tmp")); - } - - public void set(int x, int y, int z, MatterSlice slice) { - if (slice.isEmpty()) { - return; - } - - slice.iterateSync((xx, yy, zz, value) -> set(x + xx, y + yy, z + zz, value)); - } - - private static final class IOWorkerRegionIO implements RegionIO { - private final IOWorker worker; - - private IOWorkerRegionIO(File root, int worldHeight) { - this.worker = new IOWorker(root, worldHeight); - } - - @Override - public TectonicPlate read(String name) throws IOException { - return worker.read(name); - } - - @Override - public void write(String name, TectonicPlate region) throws IOException { - worker.write(name, region); - } - - @Override - public void close() throws IOException { - worker.close(); - } - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java b/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java deleted file mode 100644 index c87b62388..000000000 --- a/src/main/java/art/arcane/adapt/util/common/mantle/MantleChunk.java +++ /dev/null @@ -1,105 +0,0 @@ -package art.arcane.adapt.util.mantle; - -import art.arcane.adapt.Adapt; -import art.arcane.spatial.matter.Matter; -import art.arcane.spatial.matter.MatterSlice; -import art.arcane.spatial.matter.SpatialMatter; -import art.arcane.volmlib.util.function.Consumer4; -import art.arcane.volmlib.util.io.CountingDataInputStream; -import org.jetbrains.annotations.Nullable; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class MantleChunk extends art.arcane.volmlib.util.mantle.MantleChunk { - public MantleChunk(int sectionHeight, int x, int z) { - super(sectionHeight, x, z); - } - - public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { - super(version, sectionHeight, din); - } - - @Override - protected void onReadSectionFailure(int index, long start, long end, CountingDataInputStream din, IOException error) { - Adapt.warn("Failed to read mantle chunk section, skipping it."); - Adapt.error(error.getMessage() == null ? "Unknown mantle section read error" : error.getMessage()); - error.printStackTrace(); - TectonicPlate.addError(); - } - - @Override - protected Matter createSection() { - return new SpatialMatter(16, 16, 16); - } - - @Override - protected Matter readSection(CountingDataInputStream din) throws IOException { - try { - return Matter.readDin(din); - } catch (ClassNotFoundException e) { - throw new IOException("Failed to deserialize mantle section", e); - } - } - - @Override - protected void writeSection(Matter section, DataOutputStream dos) throws IOException { - section.writeDos(dos); - } - - @Override - protected void trimSection(Matter section) { - section.trimSlices(); - } - - @Override - protected boolean isSectionEmpty(Matter section) { - return section.getSliceMap().isEmpty(); - } - - @Override - public MantleChunk use() { - super.use(); - return this; - } - - public void copyFrom(MantleChunk chunk) { - super.copyFrom(chunk); - } - - @Nullable - @SuppressWarnings("unchecked") - public T get(int x, int y, int z, Class type) { - return (T) getOrCreate(y >> 4) - .slice(type) - .get(x & 15, y & 15, z & 15); - } - - public void iterate(Class type, Consumer4 iterator) { - for (int i = 0; i < sectionCount(); i++) { - int baseY = i << 4; - Matter section = get(i); - if (section == null) { - continue; - } - - MatterSlice slice = section.getSlice(type); - if (slice != null) { - slice.iterateSync((x, y, z, value) -> iterator.accept(x, y + baseY, z, value)); - } - } - } - - public void deleteSlices(Class type) { - for (int i = 0; i < sectionCount(); i++) { - Matter section = get(i); - if (section != null && section.hasSlice(type)) { - section.deleteSlice(type); - } - } - } - - public void trimSlices() { - trimSections(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java b/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java deleted file mode 100644 index 608f11e7e..000000000 --- a/src/main/java/art/arcane/adapt/util/common/mantle/TectonicPlate.java +++ /dev/null @@ -1,60 +0,0 @@ -package art.arcane.adapt.util.mantle; - -import art.arcane.adapt.Adapt; -import art.arcane.volmlib.util.io.CountingDataInputStream; - -import java.io.DataOutputStream; -import java.io.IOException; - -public class TectonicPlate extends art.arcane.volmlib.util.mantle.TectonicPlate { - public static final int MISSING = art.arcane.volmlib.util.mantle.TectonicPlate.MISSING; - public static final int CURRENT = art.arcane.volmlib.util.mantle.TectonicPlate.CURRENT; - - public TectonicPlate(int worldHeight, int x, int z) { - super(worldHeight, x, z); - } - - public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException { - super(worldHeight, din, versioned); - } - - @Override - protected void onReadChunkFailure(int index, long start, long end, CountingDataInputStream din, Throwable error) { - Adapt.warn("Failed to read mantle chunk, creating a new chunk instead."); - Adapt.error(error.getMessage() == null ? "Unknown mantle chunk read error" : error.getMessage()); - error.printStackTrace(); - } - - @Override - protected MantleChunk readChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { - return new MantleChunk(version, sectionHeight, din); - } - - @Override - protected MantleChunk createChunk(int sectionHeight, int x, int z) { - return new MantleChunk(sectionHeight, x, z); - } - - @Override - protected boolean isChunkInUse(MantleChunk chunk) { - return chunk.inUse(); - } - - @Override - protected void closeChunk(MantleChunk chunk) { - chunk.close(); - } - - @Override - protected void writeChunk(MantleChunk chunk, DataOutputStream dos) throws IOException { - chunk.write(dos); - } - - public static void addError() { - art.arcane.volmlib.util.mantle.TectonicPlate.addError(); - } - - public static boolean hasError() { - return art.arcane.volmlib.util.mantle.TectonicPlate.hasError(); - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java b/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java deleted file mode 100644 index 28c2c2e78..000000000 --- a/src/main/java/art/arcane/adapt/util/common/mantle/io/IOWorker.java +++ /dev/null @@ -1,96 +0,0 @@ -package art.arcane.adapt.util.mantle.io; - -import art.arcane.adapt.Adapt; -import art.arcane.adapt.util.mantle.MantleChunk; -import art.arcane.adapt.util.mantle.TectonicPlate; -import art.arcane.spatial.mantle.MantleRegion; -import art.arcane.spatial.matter.Matter; -import art.arcane.spatial.matter.MatterSlice; -import art.arcane.volmlib.util.mantle.io.IOWorkerCodecSupport; -import art.arcane.volmlib.util.mantle.io.IOWorkerRuntimeSupport; -import art.arcane.volmlib.util.mantle.io.IOWorkerSupport; - -import java.io.File; -import java.io.IOException; - -public class IOWorker { - private final File root; - private final int worldHeight; - private final IOWorkerSupport support; - private final IOWorkerRuntimeSupport runtime; - - public IOWorker(File root, int worldHeight) { - this.root = root; - this.worldHeight = worldHeight; - this.support = new IOWorkerSupport(root, 128, (name, millis) -> - Adapt.debug("Acquired mantle channel for " + name + " in " + millis + "ms") - ); - this.runtime = new IOWorkerRuntimeSupport(support, IOWorkerCodecSupport.identity()); - } - - public TectonicPlate read(String name) throws IOException { - try { - return runtime.read(name, (regionName, in) -> new TectonicPlate(worldHeight, in, true)); - } catch (IOException e) { - TectonicPlate migrated = readLegacy(name, e); - if (migrated != null) { - Adapt.warn("Migrated legacy mantle region " + name + " to shared mantle format."); - return migrated; - } - - throw e; - } - } - - public void write(String name, TectonicPlate plate) throws IOException { - runtime.write(name, plate, TectonicPlate::write); - } - - public void close() throws IOException { - support.close(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private TectonicPlate readLegacy(String name, IOException original) { - File file = new File(root, name); - if (!file.exists()) { - return null; - } - - try { - MantleRegion region = MantleRegion.read(worldHeight, file); - TectonicPlate plate = new TectonicPlate(worldHeight, region.getX(), region.getZ()); - int sectionCount = worldHeight >> 4; - - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - var legacyChunk = region.get(x, z); - if (legacyChunk == null) { - continue; - } - - MantleChunk chunk = plate.getOrCreate(x, z); - for (int section = 0; section < sectionCount; section++) { - Matter legacySection = legacyChunk.get(section); - if (legacySection == null || legacySection.getSliceMap().isEmpty()) { - continue; - } - - Matter target = chunk.getOrCreate(section); - target.clearSlices(); - - Matter copy = legacySection.copy(); - for (var entry : copy.getSliceMap().entrySet()) { - target.putSlice(entry.getKey(), (MatterSlice) entry.getValue()); - } - } - } - } - - return plate; - } catch (Throwable t) { - original.addSuppressed(t); - return null; - } - } -} diff --git a/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java index a23c7033c..3ec18c2b5 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java @@ -14,7 +14,10 @@ public static InputSnapshot readInput(Player p, double fallbackThresholdSquared) try { Input input = p.getCurrentInput(); if (input != null) { - return new InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); + InputSnapshot snapshot = new InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); + if (snapshot.hasHorizontal()) { + return snapshot; + } } } catch (NoSuchMethodError ignored) { // Fallback path for runtimes without Player#getCurrentInput. diff --git a/src/main/resources/en_US.toml b/src/main/resources/en_US.toml index 16556f1d0..282b44a63 100644 --- a/src/main/resources/en_US.toml +++ b/src/main/resources/en_US.toml @@ -1,3 +1,8 @@ +# Color Codes: +# - Legacy format: &0..&f, &k..&o, &r (example: "&7Gray") +# - Hex format: &#RRGGBB or &x&R&R&G&G&B&B (example: "7FFAAMint") +# - MiniMessage also works (example: "<#55FFAA>Mint") +# # advancement [advancement] From 3f34537e397f2f0a22c09b79f4646111ced51d4e Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 17 Feb 2026 06:21:45 -0500 Subject: [PATCH 12/25] F --- .../api/adaptation/AdaptationGuiSupport.java | 7 +- .../adapt/api/skill/SkillGuiSupport.java | 3 +- .../agility/AgilityParkourMomentum.java | 27 +- .../ranged/RangedTrajectorySight.java | 264 +++++++++++++++++- .../stealth/StealthShadowDecoy.java | 27 +- .../adaptation/stealth/StealthSilentStep.java | 65 ++++- .../taming/TamingPackLeaderAura.java | 45 ++- .../unarmed/UnarmedBatteringCharge.java | 24 +- .../arcane/adapt/content/gui/ConfigGui.java | 6 +- .../arcane/adapt/content/gui/SkillsGui.java | 3 +- 10 files changed, 410 insertions(+), 61 deletions(-) diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index ef9472fc4..2ae364cd0 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -21,7 +21,6 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.recipe.AdaptRecipe; -import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.volmlib.util.inventorygui.UIElement; @@ -251,7 +250,6 @@ static void openGui(Adaptation adaptation, Player player, int page) { spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); suppressClose(player); - w.close(); if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { if (adaptation.isPermanent()) { spw.play(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 0.5f, 1.355f); @@ -283,7 +281,6 @@ static void openGui(Adaptation adaptation, Player player, int page) { spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); } suppressClose(player); - w.close(); if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + adaptation.getDisplayName(lvl), 1, 5, 11); } @@ -349,9 +346,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { } - AdaptPlayer a = Adapt.instance.getAdaptServer().getPlayer(player); - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(adaptation.getDisplayName() + " " + C.DARK_GRAY + " " + Form.f(a.getSkillLine(adaptation.getSkill().getName()).getKnowledge()) + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge") + pageSuffix); + w.setTitle(adaptation.getDisplayName()); w.onClosed((vv) -> J.s(() -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); w.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index df6635c89..a46f82cb4 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -236,8 +236,7 @@ static void openGui(Skill skill, Player player, int page) { } - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")" + pageSuffix); + window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")"); window.onClosed((vv) -> J.s(() -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); window.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java index f056af4e9..2bf28a990 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java @@ -28,6 +28,7 @@ import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.block.Block; @@ -41,7 +42,9 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -89,7 +92,12 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(PlayerMoveEvent e) { - if (e.getTo() == null || e.getFrom().distanceSquared(e.getTo()) < getConfig().minimumMoveSquared) { + if (e.getTo() == null) { + return; + } + + if (e.getFrom().getWorld() == e.getTo().getWorld() + && e.getFrom().distanceSquared(e.getTo()) < getConfig().minimumMoveSquared) { return; } @@ -126,14 +134,17 @@ public void on(PlayerMoveEvent e) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); + Set tracked = trackedPlayerIds(); + for (UUID id : tracked) { + Player p = Bukkit.getPlayer(id); if (p == null || !p.isOnline()) { + momentum.remove(id); + wasOnGround.remove(id); + speedBoosting.remove(id); continue; } withPlayerThread(p, () -> { - UUID id = p.getUniqueId(); int level = getActiveLevel(p); if (level <= 0) { momentum.remove(id); @@ -183,6 +194,14 @@ public void onTick() { } } + private Set trackedPlayerIds() { + Set tracked = new HashSet<>(); + tracked.addAll(momentum.keySet()); + tracked.addAll(wasOnGround.keySet()); + tracked.addAll(speedBoosting.keySet()); + return tracked; + } + private void applyMomentumSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot input, int speedAmp) { Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index 7b522abfb..843bcaaef 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -45,21 +45,31 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class RangedTrajectorySight extends SimpleAdaptation { private static final double EPSILON = 0.0000001D; - private final Map drawStartedMillis = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map previewGlowTargets = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map drawStartedMillis = new ConcurrentHashMap<>(); + private final Map previewGlowTargets = new ConcurrentHashMap<>(); + private final Set previewCandidates = ConcurrentHashMap.newKeySet(); + private final Map previewState = new ConcurrentHashMap<>(); private volatile RangedForce cachedRangedForce; private volatile RangedRicochetBolt cachedRicochetBolt; + private volatile long lastPreviewCandidateRefreshMs; public RangedTrajectorySight() { super("ranged-trajectory-sight"); @@ -71,7 +81,7 @@ public RangedTrajectorySight() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - setInterval(2); + setInterval(20); registerAdvancement(AdaptAdvancement.builder() .icon(Material.SPYGLASS) .key("challenge_ranged_trajectory_100") @@ -92,8 +102,7 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerQuitEvent e) { Player p = e.getPlayer(); - drawStartedMillis.remove(p.getUniqueId()); - clearPreviewGlow(p); + clearPreviewState(p); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -118,16 +127,75 @@ public void on(PlayerInteractEvent e) { } switch (e.getAction()) { - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> drawStartedMillis.put(p.getUniqueId(), System.currentTimeMillis()); + case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> { + drawStartedMillis.put(p.getUniqueId(), System.currentTimeMillis()); + markPreviewCandidate(p); + } default -> { } } } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerItemHeldEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } + + markPreviewCandidate(p); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerSwapHandItemsEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } + + markPreviewCandidate(p); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } + + if (e.isSneaking()) { + markPreviewCandidate(p); + return; + } + + if (resolvePreviewContext(p) == null) { + previewCandidates.remove(p.getUniqueId()); + previewState.remove(p.getUniqueId()); + clearPreviewGlow(p); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + boolean tracked = previewCandidates.contains(id); + if (!tracked && !p.isSneaking() && !p.isHandRaised()) { + return; + } + + if (!hasActiveAdaptation(p)) { + return; + } + + markPreviewCandidate(p); + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void on(EntityShootBowEvent e) { if (e.getEntity() instanceof Player p) { drawStartedMillis.remove(p.getUniqueId()); + markPreviewCandidate(p); } } @@ -144,18 +212,31 @@ public void on(EntityDeathEvent e) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); + long now = System.currentTimeMillis(); + refreshPreviewCandidates(now); + if (previewCandidates.isEmpty()) { + return; + } + + Set candidates = new HashSet<>(previewCandidates); + for (UUID id : candidates) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + clearPreviewState(id); + continue; + } + int level = getActiveLevel(p); if (level <= 0) { - drawStartedMillis.remove(p.getUniqueId()); - clearPreviewGlow(p); + clearPreviewState(p); continue; } PreviewContext context = resolvePreviewContext(p); if (context == null) { - drawStartedMillis.remove(p.getUniqueId()); + previewState.remove(id); + previewCandidates.remove(id); + drawStartedMillis.remove(id); clearPreviewGlow(p); continue; } @@ -170,11 +251,126 @@ public void onTick() { continue; } - UUID predictedHit = renderTrajectory(p, getSegments(level), shot); + if (!shouldRenderPreview(p, level, context, now)) { + continue; + } + + UUID predictedHit = renderTrajectory(p, getRenderSegments(level), shot); updatePreviewGlow(p, predictedHit); + previewState.put(id, PreviewState.capture(now, level, context, p.getEyeLocation())); + } + } + + private void refreshPreviewCandidates(long now) { + long refreshEvery = Math.max(250L, getConfig().previewCandidateRefreshMillis); + if (now - lastPreviewCandidateRefreshMs < refreshEvery) { + return; + } + + lastPreviewCandidateRefreshMs = now; + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } + + if (resolvePreviewContext(p) != null) { + previewCandidates.add(p.getUniqueId()); + } } } + private void markPreviewCandidate(Player p) { + if (p == null) { + return; + } + + previewCandidates.add(p.getUniqueId()); + } + + private boolean shouldRenderPreview(Player p, int level, PreviewContext context, long now) { + PreviewState state = previewState.get(p.getUniqueId()); + if (state == null) { + return true; + } + + long refreshEvery = Math.max(10L, getConfig().previewRenderIntervalMillis); + if (now - state.renderedAtMs() >= refreshEvery) { + return true; + } + + if (state.level() != level) { + return true; + } + + Material material = context.item().getType(); + if (state.material() != material || state.trigger() != context.trigger()) { + return true; + } + + Location eye = p.getEyeLocation(); + if (angleDelta(eye.getYaw(), state.yaw()) >= Math.max(0.01D, getConfig().previewYawDeltaDegrees)) { + return true; + } + + if (angleDelta(eye.getPitch(), state.pitch()) >= Math.max(0.01D, getConfig().previewPitchDeltaDegrees)) { + return true; + } + + double dx = eye.getX() - state.x(); + double dy = eye.getY() - state.y(); + double dz = eye.getZ() - state.z(); + double movedSq = (dx * dx) + (dy * dy) + (dz * dz); + return movedSq >= Math.max(0D, getConfig().previewPositionDeltaSquared); + } + + private double angleDelta(float from, float to) { + double delta = Math.abs((double) from - to) % 360D; + return delta > 180D ? 360D - delta : delta; + } + + private int getRenderSegments(int level) { + int minSegments = Math.max(6, getConfig().minimumRenderedSegments); + int maxSegments = Math.max(minSegments, getConfig().maxRenderedSegments); + int segments = Math.max(minSegments, Math.min(getSegments(level), maxSegments)); + if (Adapt.instance.getTicker() == null) { + return segments; + } + + double load = Adapt.instance.getTicker().getWindowLoadPercent(); + if (load < Math.max(0D, getConfig().previewHighLoadPercent)) { + return segments; + } + + double scale = Math.max(0.2D, Math.min(1D, getConfig().previewHighLoadSegmentScale)); + int scaled = (int) Math.round(segments * scale); + return Math.max(minSegments, Math.min(maxSegments, scaled)); + } + + private void clearPreviewState(Player p) { + if (p == null) { + return; + } + + clearPreviewState(p.getUniqueId()); + clearPreviewGlow(p); + } + + private void clearPreviewState(UUID id) { + if (id == null) { + return; + } + + drawStartedMillis.remove(id); + previewCandidates.remove(id); + previewState.remove(id); + } + private PreviewContext resolvePreviewContext(Player p) { ItemStack main = p.getInventory().getItemInMainHand(); ItemStack off = p.getInventory().getItemInOffHand(); @@ -701,6 +897,32 @@ private static RicochetPreview disabled() { } } + private record PreviewState( + long renderedAtMs, + int level, + Material material, + PreviewTrigger trigger, + double x, + double y, + double z, + float yaw, + float pitch + ) { + private static PreviewState capture(long renderedAtMs, int level, PreviewContext context, Location eye) { + return new PreviewState( + renderedAtMs, + level, + context.item().getType(), + context.trigger(), + eye.getX(), + eye.getY(), + eye.getZ(), + eye.getYaw(), + eye.getPitch() + ); + } + } + private int getSegments(int level) { return Math.max(10, (int) Math.round(getConfig().segmentsBase + (getLevelPercent(level) * getConfig().segmentsFactor))); } @@ -790,5 +1012,23 @@ protected static class Config { double previewStartOffset = 0.55; @art.arcane.adapt.util.config.ConfigDoc(value = "Highlights the predicted hit target entity with per-player glow.", impact = "Enable to glow whichever entity the preview would hit first.") boolean glowPredictedTarget = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between preview renders for a player when aim and context have not changed.", impact = "Lower values make previews smoother but increase CPU and ray-trace load.") + int previewRenderIntervalMillis = 75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between full-candidate refresh scans.", impact = "Lower values discover eligible preview players faster but increase baseline scan cost.") + int previewCandidateRefreshMillis = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Yaw delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small camera turns; higher values reduce recompute frequency.") + double previewYawDeltaDegrees = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Pitch delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small vertical aim changes; higher values reduce recompute frequency.") + double previewPitchDeltaDegrees = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Movement distance squared required to force a preview recompute before the normal render interval.", impact = "Lower values recompute more while strafing; higher values reduce recomputes while moving.") + double previewPositionDeltaSquared = 0.0125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Lowest number of simulation segments used when rendering a trajectory.", impact = "Higher values preserve long previews under load; lower values cut work more aggressively.") + int minimumRenderedSegments = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on simulation segments used for rendering.", impact = "Lower values reduce ray-trace workload; higher values produce longer and more precise previews.") + int maxRenderedSegments = 36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Ticker load percentage at which trajectory segments are scaled down.", impact = "Lower values engage load-shedding sooner; higher values preserve preview fidelity longer.") + double previewHighLoadPercent = 42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Segment scale applied once high-load shedding is active.", impact = "Lower values reduce trajectory compute cost more aggressively during load spikes.") + double previewHighLoadSegmentScale = 0.7; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index 9d881e8b6..22065182f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -59,6 +59,8 @@ public class StealthShadowDecoy extends SimpleAdaptation activeDecoys = new java.util.concurrent.ConcurrentHashMap<>(); private final Map anchorOwners = new java.util.concurrent.ConcurrentHashMap<>(); private final Map ownerEquipmentMaskSync = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerAggroNextAt = new java.util.concurrent.ConcurrentHashMap<>(); public StealthShadowDecoy() { super("stealth-shadow-decoy"); @@ -103,6 +105,8 @@ public void on(PlayerQuitEvent e) { UUID id = e.getPlayer().getUniqueId(); cooldowns.remove(id); ownerEquipmentMaskSync.remove(id); + ownerTrailNextAt.remove(id); + ownerAggroNextAt.remove(id); DecoyState state = activeDecoys.remove(id); if (state != null) { removeDecoy(state, null); @@ -222,7 +226,10 @@ private void spawnDecoy(Player owner, int level) { } long expiresAt = System.currentTimeMillis() + (getDecoyTicks(level) * 50L); - activeDecoys.put(owner.getUniqueId(), new DecoyState(anchor.getUniqueId(), packetDecoy, expiresAt, level)); + UUID ownerId = owner.getUniqueId(); + activeDecoys.put(ownerId, new DecoyState(ownerId, anchor.getUniqueId(), packetDecoy, expiresAt, level)); + ownerTrailNextAt.put(ownerId, 0L); + ownerAggroNextAt.put(ownerId, 0L); redirectAggro(owner, anchor, level); if (areParticlesEnabled()) { @@ -315,8 +322,14 @@ public void onTick() { applyOwnerInvisibility(owner); syncOwnerEquipmentHidden(owner); - spawnOwnerTrail(owner); - redirectAggro(owner, anchor, state.level()); + if (now >= ownerTrailNextAt.getOrDefault(ownerId, 0L)) { + spawnOwnerTrail(owner); + ownerTrailNextAt.put(ownerId, now + Math.max(25L, getConfig().ownerTrailIntervalMillis)); + } + if (now >= ownerAggroNextAt.getOrDefault(ownerId, 0L)) { + redirectAggro(owner, anchor, state.level()); + ownerAggroNextAt.put(ownerId, now + Math.max(25L, getConfig().aggroRedirectIntervalMillis)); + } } } @@ -369,6 +382,8 @@ private void removeDecoy(DecoyState state, Player owner) { Entity entity = Bukkit.getEntity(state.anchorId()); anchorOwners.remove(state.anchorId()); + ownerTrailNextAt.remove(state.ownerId()); + ownerAggroNextAt.remove(state.ownerId()); if (entity instanceof ArmorStand stand && stand.isValid()) { stand.remove(); } @@ -445,8 +460,12 @@ protected static class Config { double ownerTrailYOffset = 0.1; @art.arcane.adapt.util.config.ConfigDoc(value = "Particle speed for owner smoke trail.", impact = "Higher values make trail movement more turbulent.") double ownerTrailSpeed = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between owner trail particle bursts while decoy is active.", impact = "Lower values make the owner trail denser; higher values reduce particle cost.") + long ownerTrailIntervalMillis = 75; @art.arcane.adapt.util.config.ConfigDoc(value = "How often owner equipment-hide packets are resent while invisible, in milliseconds.", impact = "Lower values keep visuals tighter for joining viewers, higher values reduce packet traffic.") long ownerEquipmentHideResendMillis = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between aggro redirect scans while a decoy is active.", impact = "Lower values pull mobs more aggressively; higher values reduce nearby-entity scan cost.") + long aggroRedirectIntervalMillis = 150; @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal knockback applied when the decoy is hit.", impact = "Higher values make the decoy react more dramatically when struck.") double decoyHitKnockback = 0.28; @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical lift applied when the decoy is hit.", impact = "Higher values make impacts pop the decoy upward more.") @@ -459,7 +478,7 @@ protected static class Config { double xpOnDecoy = 18; } - private record DecoyState(UUID anchorId, PacketPlayerDecoy packetDecoy, long expiresAt, int level) { + private record DecoyState(UUID ownerId, UUID anchorId, PacketPlayerDecoy packetDecoy, long expiresAt, int level) { } private static final class PacketPlayerDecoy { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java index 5cde74410..f828a6efa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java @@ -41,6 +41,7 @@ import org.bukkit.event.entity.EntityTargetLivingEntityEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; @@ -51,6 +52,8 @@ public class StealthSilentStep extends SimpleAdaptation dimmed = new java.util.concurrent.ConcurrentHashMap<>(); private final Map> recentBackstabs = new java.util.concurrent.ConcurrentHashMap<>(); private final Map> threatGlows = new java.util.concurrent.ConcurrentHashMap<>(); + private final Set activeSneakers = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map lastTargetDropScan = new java.util.concurrent.ConcurrentHashMap<>(); public StealthSilentStep() { super("stealth-silent-step"); @@ -62,7 +65,7 @@ public StealthSilentStep() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - setInterval(10); + setInterval(50); registerAdvancement(AdaptAdvancement.builder() .icon(Material.IRON_SWORD) .key("challenge_stealth_silent_200") @@ -91,9 +94,28 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerQuitEvent e) { - clearDimming(e.getPlayer()); - clearThreatGlows(e.getPlayer()); - recentBackstabs.remove(e.getPlayer().getUniqueId()); + Player player = e.getPlayer(); + UUID id = player.getUniqueId(); + clearDimming(player); + clearThreatGlows(player); + recentBackstabs.remove(id); + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (e.isSneaking()) { + activeSneakers.add(id); + return; + } + + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + clearDimming(p); + clearThreatGlows(p); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -118,13 +140,31 @@ public void on(EntityTargetLivingEntityEvent e) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerMoveEvent e) { + if (e.getTo() == null) { + return; + } + if (e.getFrom().getWorld() == e.getTo().getWorld() + && e.getFrom().distanceSquared(e.getTo()) < Math.max(0D, getConfig().minimumMoveSquared)) { + return; + } Player p = e.getPlayer(); + UUID id = p.getUniqueId(); int level = getActiveLevel(p, Player::isSneaking); if (level <= 0) { + activeSneakers.remove(id); + lastTargetDropScan.remove(id); return; } + activeSneakers.add(id); + long now = System.currentTimeMillis(); + long lastScan = lastTargetDropScan.getOrDefault(id, 0L); + if (now - lastScan < Math.max(20L, getConfig().targetDropScanIntervalMillis)) { + return; + } + lastTargetDropScan.put(id, now); + double radius = getStealthRadius(level); for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { if (!(entity instanceof Mob mob)) { @@ -175,12 +215,21 @@ public void on(EntityDamageByEntityEvent e) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); + Set tracked = new HashSet<>(activeSneakers); + for (UUID id : tracked) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + continue; + } + int level = getActiveLevel(p, Player::isSneaking); if (level <= 0) { clearDimming(p); clearThreatGlows(p); + activeSneakers.remove(id); + lastTargetDropScan.remove(id); continue; } @@ -478,6 +527,10 @@ protected static class Config { boolean allMobsAffectStealthVisibility = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Entity types that are NOT ignored by stealth targeting suppression.", impact = "Mobs listed here can still detect/target sneaking players with Silent Step.") List targetingBlacklistTypes = new ArrayList<>(List.of("WARDEN", "WITHER", "PHANTOM", "ENDER_DRAGON")); + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum squared movement distance required before running target-drop scans.", impact = "Higher values skip tiny movement jitter and reduce move-event scan pressure.") + double minimumMoveSquared = 0.0025; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between mob target-drop scans while sneaking.", impact = "Lower values react faster but increase nearby-entity scan frequency.") + long targetDropScanIntervalMillis = 120; } private enum ThreatLevel { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index 5c8451623..252be8e26 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -42,6 +42,8 @@ import java.util.List; public class TamingPackLeaderAura extends SimpleAdaptation { + private int ownerCursor = 0; + public TamingPackLeaderAura() { super("tame-pack-leader-aura"); registerConfiguration(Config.class); @@ -72,20 +74,14 @@ public void addStats(int level, Element v) { @Override public void onTick() { - if (J.isFoliaThreading()) { - List owners = new ArrayList<>(); - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - int level = getActiveLevel(owner); - if (level <= 0) { - continue; - } - - double radius = getRadius(level); - owners.add(new OwnerAuraState(adaptPlayer, owner, radius, radius * radius, getAmplifier(level))); - } + List owners = collectOwners(); + if (owners.isEmpty()) { + return; + } - for (OwnerAuraState state : owners) { + List batch = selectBatch(owners); + if (J.isFoliaThreading()) { + for (OwnerAuraState state : batch) { J.runEntity(state.owner(), () -> applyAura(state)); } return; @@ -96,6 +92,12 @@ public void onTick() { return; } + for (OwnerAuraState state : batch) { + applyAura(state); + } + } + + private List collectOwners() { List owners = new ArrayList<>(); for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player owner = adaptPlayer.getPlayer(); @@ -108,9 +110,20 @@ public void onTick() { owners.add(new OwnerAuraState(adaptPlayer, owner, radius, radius * radius, getAmplifier(level))); } - for (OwnerAuraState state : owners) { - applyAura(state); + return owners; + } + + private List selectBatch(List owners) { + int size = owners.size(); + int limit = Math.max(1, Math.min(size, getConfig().maxOwnersPerPass)); + int start = Math.floorMod(ownerCursor, size); + List batch = new ArrayList<>(limit); + for (int i = 0; i < limit; i++) { + int index = (start + i) % size; + batch.add(owners.get(index)); } + ownerCursor = (start + limit) % size; + return batch; } private void applyAura(OwnerAuraState state) { @@ -181,5 +194,7 @@ protected static class Config { double maxAmplifier = 2; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") int effectTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum owners processed per aura pass.", impact = "Lower values reduce burst workload but spread updates across more passes.") + int maxOwnersPerPass = 120; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index 61aadee10..854d361fa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -48,6 +48,7 @@ public class UnarmedBatteringCharge extends SimpleAdaptation { private final Map primedState = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map primedTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); public UnarmedBatteringCharge() { super("unarmed-battering-charge"); @@ -59,7 +60,7 @@ public UnarmedBatteringCharge() { setMaxLevel(getConfig().maxLevel); setInitialCost(getConfig().initialCost); setCostFactor(getConfig().costFactor); - setInterval(8); + setInterval(50); registerAdvancement(AdaptAdvancement.builder() .icon(Material.IRON_INGOT) .key("challenge_unarmed_charge_300") @@ -151,7 +152,9 @@ && isChargeLoadout(p)) { @EventHandler(priority = EventPriority.MONITOR) public void on(PlayerQuitEvent e) { - primedState.remove(e.getPlayer().getUniqueId()); + UUID id = e.getPlayer().getUniqueId(); + primedState.remove(id); + primedTrailNextAt.remove(id); } private boolean isChargeLoadout(Player p) { @@ -204,20 +207,25 @@ private int getCooldownTicks(int level) { @Override public void onTick() { + long now = System.currentTimeMillis(); for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { Player p = adaptPlayer.getPlayer(); + UUID id = p.getUniqueId(); int level = getActiveLevel(p); if (level <= 0) { - primedState.remove(p.getUniqueId()); + primedState.remove(id); + primedTrailNextAt.remove(id); continue; } boolean primed = isChargeReady(p); - boolean wasPrimed = primedState.getOrDefault(p.getUniqueId(), false); + boolean wasPrimed = primedState.getOrDefault(id, false); if (primed) { - if (areParticlesEnabled()) { + long nextTrail = primedTrailNextAt.getOrDefault(id, 0L); + if (areParticlesEnabled() && now >= nextTrail) { p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().add(0, 0.2, 0), 2, 0.2, 0.05, 0.2, 0.02); + primedTrailNextAt.put(id, now + Math.max(25L, getConfig().primedTrailIntervalMillis)); } if (!wasPrimed) { SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASEDRUM, 0.55f, 1.15f); @@ -225,9 +233,11 @@ public void onTick() { p.getWorld().spawnParticle(Particle.CRIT, p.getLocation().add(0, 1.0, 0), 8, 0.2, 0.3, 0.2, 0.1); } } + } else { + primedTrailNextAt.remove(id); } - primedState.put(p.getUniqueId(), primed); + primedState.put(id, primed); } } @@ -272,5 +282,7 @@ protected static class Config { double minimumVelocitySquared = 0.18; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerDamage = 3.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between primed trail particle pulses while charge is ready.", impact = "Lower values increase visual frequency and particle cost; higher values reduce trail spam.") + long primedTrailIntervalMillis = 120; } } diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index 15b2ec56a..f0f957243 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -607,8 +607,7 @@ private static void openFieldEntries(Player player, String safePath, List 24) { titlePath = "..." + titlePath.substring(titlePath.length() - 21); } - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(C.GRAY + "Configure: " + C.WHITE + titlePath + pageSuffix); + w.setTitle(C.GRAY + "Configure: " + C.WHITE + titlePath); w.onClosed((window) -> onGuiClosed(player, safePath)); w.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); @@ -815,8 +814,7 @@ private static void openSectionIndex(Player player, String sectionPath, int page } addIndexOverview(w, navRow, safePath, entries.size(), currentPage, plan.pageCount(), title); - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(C.GRAY + title + pageSuffix); + w.setTitle(C.GRAY + title); w.onClosed((window) -> onGuiClosed(player, safePath)); w.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 91226af93..4b7100f38 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -141,8 +141,7 @@ public static void open(Player player, int page) { } - String pageSuffix = plan.pageCount() > 1 ? " [" + (currentPage + 1) + "/" + plan.pageCount() + "]" : ""; - w.setTitle(Localizer.dLocalize("snippets.gui.level") + " " + (int) XP.getLevelForXp(adaptPlayer.getData().getMasterXp()) + " (" + adaptPlayer.getData().getUsedPower() + "/" + adaptPlayer.getData().getMaxPower() + " " + Localizer.dLocalize("snippets.gui.power_used") + ")" + pageSuffix); + w.setTitle(Localizer.dLocalize("snippets.gui.level") + " " + (int) XP.getLevelForXp(adaptPlayer.getData().getMasterXp()) + " (" + adaptPlayer.getData().getUsedPower() + "/" + adaptPlayer.getData().getMaxPower() + " " + Localizer.dLocalize("snippets.gui.power_used") + ")"); w.open(); w.onClosed((e) -> Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString())); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); From f6725d687859e8630fb79c328e1d3e7b07bedce6 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 17 Feb 2026 22:36:32 -0500 Subject: [PATCH 13/25] f --- build.gradle.kts | 20 +- src/main/java/art/arcane/adapt/Adapt.java | 772 ++- .../java/art/arcane/adapt/AdaptConfig.java | 373 +- .../java/art/arcane/adapt/PapiExpansion.java | 717 +-- .../java/art/arcane/adapt/api/Component.java | 1838 +++---- .../adapt/api/adaptation/Adaptation.java | 1742 +++---- .../adaptation/AdaptationEventRegistrar.java | 140 +- .../api/adaptation/AdaptationGuiSupport.java | 873 ++-- .../adaptation/AdaptationRuntimeGuards.java | 1197 ++--- .../api/adaptation/SimpleAdaptation.java | 440 +- .../api/adaptation/chunk/ChunkLoading.java | 14 +- .../api/advancement/AdaptAdvancement.java | 204 +- .../advancement/AdaptAdvancementFrame.java | 20 +- .../api/advancement/AdvancementManager.java | 522 +- .../api/advancement/AdvancementSpec.java | 122 +- .../advancement/AdvancementVisibility.java | 125 +- .../art/arcane/adapt/api/data/WorldData.java | 580 ++- .../arcane/adapt/api/data/unit/Earnings.java | 44 +- .../art/arcane/adapt/api/item/DataItem.java | 86 +- .../art/arcane/adapt/api/item/PotionItem.java | 42 +- .../notification/ActionBarNotification.java | 50 +- .../notification/AdvancementNotification.java | 64 +- .../adapt/api/notification/Notification.java | 12 +- .../adapt/api/notification/Notifier.java | 212 +- .../api/notification/SoundNotification.java | 82 +- .../api/notification/TitleNotification.java | 48 +- .../adapt/api/potion/BrewingManager.java | 238 +- .../adapt/api/potion/BrewingRecipe.java | 8 +- .../arcane/adapt/api/potion/BrewingTask.java | 166 +- .../adapt/api/potion/PotionBuilder.java | 178 +- .../adapt/api/protection/Protector.java | 50 +- .../api/protection/ProtectorRegistry.java | 86 +- .../WorldPolicyLatencyTelemetry.java | 92 +- .../arcane/adapt/api/recipe/AdaptRecipe.java | 554 +-- .../arcane/adapt/api/recipe/MaterialChar.java | 28 +- .../adapt/api/runtime/AdaptationGate.java | 62 +- .../arcane/adapt/api/skill/SimpleSkill.java | 908 ++-- .../art/arcane/adapt/api/skill/Skill.java | 496 +- .../adapt/api/skill/SkillEventRegistrar.java | 140 +- .../adapt/api/skill/SkillGuiSupport.java | 540 ++- .../arcane/adapt/api/skill/SkillRegistry.java | 850 ++-- .../adapt/api/skill/SkillRuntimeGuards.java | 330 +- .../api/telemetry/AbilityCheckTelemetry.java | 83 +- .../art/arcane/adapt/api/tick/Ticked.java | 48 +- .../arcane/adapt/api/tick/TickedObject.java | 458 +- .../art/arcane/adapt/api/tick/Ticker.java | 264 +- .../arcane/adapt/api/value/MaterialCount.java | 4 +- .../adapt/api/value/MaterialRecipe.java | 4 +- .../arcane/adapt/api/value/MaterialValue.java | 362 +- .../arcane/adapt/api/version/IAttribute.java | 56 +- .../arcane/adapt/api/version/IBindings.java | 97 +- .../adapt/api/version/RuntimeAttribute.java | 463 +- .../adapt/api/version/RuntimeBindings.java | 190 +- .../art/arcane/adapt/api/version/Version.java | 25 +- .../adapt/api/world/AdaptComponent.java | 384 +- .../arcane/adapt/api/world/AdaptPlayer.java | 632 +-- .../arcane/adapt/api/world/AdaptServer.java | 602 +-- .../adapt/api/world/AdaptServerData.java | 2 +- .../adapt/api/world/AdaptStatTracker.java | 8 +- .../adapt/api/world/AdvancementHandler.java | 28 +- .../art/arcane/adapt/api/world/Discovery.java | 10 +- .../adapt/api/world/PlayerAdaptation.java | 6 +- .../arcane/adapt/api/world/PlayerData.java | 612 +-- .../api/world/PlayerDataPersistenceQueue.java | 150 +- .../adapt/api/world/PlayerSkillLine.java | 747 +-- .../java/art/arcane/adapt/api/xp/Curves.java | 326 +- .../art/arcane/adapt/api/xp/NewtonCurve.java | 62 +- .../adapt/api/xp/ResolvedNewtonCurve.java | 8 +- .../art/arcane/adapt/api/xp/SpatialXP.java | 24 +- src/main/java/art/arcane/adapt/api/xp/XP.java | 172 +- .../art/arcane/adapt/api/xp/XPMultiplier.java | 18 +- .../arcane/adapt/command/CommandAdapt.java | 846 ++-- .../arcane/adapt/command/CommandClear.java | 180 +- .../arcane/adapt/command/CommandDebug.java | 160 +- .../arcane/adapt/command/CommandDefault.java | 288 +- .../arcane/adapt/command/CommandReset.java | 86 +- .../adaptation/agility/AgilityArmorUp.java | 276 +- .../agility/AgilityLadderSlide.java | 430 +- .../agility/AgilityParkourMomentum.java | 570 +-- .../agility/AgilityRollLanding.java | 468 +- .../adaptation/agility/AgilitySuperJump.java | 270 +- .../adaptation/agility/AgilityWallJump.java | 538 +-- .../adaptation/agility/AgilityWindUp.java | 480 +- .../architect/ArchitectElevator.java | 700 +-- .../architect/ArchitectFoundation.java | 570 +-- .../adaptation/architect/ArchitectGlass.java | 172 +- .../architect/ArchitectPlacement.java | 926 ++-- .../architect/ArchitectSmartShape.java | 352 +- .../architect/ArchitectWirelessRedstone.java | 443 +- .../adapt/content/adaptation/axe/AxeChop.java | 306 +- .../adaptation/axe/AxeCraftLogSwap.java | 2030 ++++---- .../adaptation/axe/AxeDropToInventory.java | 146 +- .../adaptation/axe/AxeGroundSmash.java | 276 +- .../adaptation/axe/AxeLeafVeinminer.java | 303 +- .../content/adaptation/axe/AxeTimberMark.java | 472 +- .../adaptation/axe/AxeWoodVeinminer.java | 310 +- .../blocking/BlockingBastionStance.java | 318 +- .../blocking/BlockingBulwarkBash.java | 432 +- .../blocking/BlockingChainArmorer.java | 184 +- .../blocking/BlockingCounterGuard.java | 274 +- .../blocking/BlockingHorseArmorer.java | 202 +- .../blocking/BlockingMirrorBlock.java | 386 +- .../blocking/BlockingMultiArmor.java | 372 +- .../blocking/BlockingSaddlecrafter.java | 136 +- .../adaptation/brewing/BrewingAbsorption.java | 170 +- .../adaptation/brewing/BrewingBlindness.java | 166 +- .../adaptation/brewing/BrewingDarkness.java | 144 +- .../adaptation/brewing/BrewingDecay.java | 168 +- .../adaptation/brewing/BrewingFatigue.java | 168 +- .../adaptation/brewing/BrewingHaste.java | 168 +- .../brewing/BrewingHealthBoost.java | 168 +- .../adaptation/brewing/BrewingHunger.java | 168 +- .../adaptation/brewing/BrewingLingering.java | 551 +-- .../adaptation/brewing/BrewingNausea.java | 168 +- .../adaptation/brewing/BrewingResistance.java | 168 +- .../adaptation/brewing/BrewingSaturation.java | 168 +- .../brewing/BrewingSuperHeated.java | 356 +- .../chronos/ChronosAberrantTouch.java | 342 +- .../chronos/ChronosInstantRecall.java | 2056 ++++---- .../chronos/ChronosInstantRecallConfig.java | 111 + .../chronos/ChronosInstantRecallTypes.java | 40 + .../adaptation/chronos/ChronosSoundFX.java | 360 +- .../chronos/ChronosTemporalEcho.java | 296 +- .../adaptation/chronos/ChronosTimeBomb.java | 1323 ++--- .../chronos/ChronosTimeInABottle.java | 1345 +++--- .../crafting/CraftingBackpacks.java | 146 +- .../crafting/CraftingDeconstruction.java | 319 +- .../adaptation/crafting/CraftingLeather.java | 134 +- .../crafting/CraftingReconstruction.java | 628 +-- .../adaptation/crafting/CraftingSkulls.java | 252 +- .../adaptation/crafting/CraftingStations.java | 248 +- .../adaptation/crafting/CraftingXP.java | 186 +- .../discovery/DiscoveryArchaeologist.java | 665 +-- .../adaptation/discovery/DiscoveryArmor.java | 300 +- .../discovery/DiscoveryBetterMending.java | 302 +- .../discovery/DiscoveryCartographerPulse.java | 378 +- .../adaptation/discovery/DiscoveryUnity.java | 178 +- .../discovery/DiscoveryVillagerAtt.java | 294 +- .../discovery/DiscoveryXpResist.java | 302 +- .../enchanting/EnchantingAnvilSavant.java | 226 +- .../EnchantingBookshelfAttunement.java | 170 +- .../EnchantingGrindstoneRecovery.java | 306 +- .../enchanting/EnchantingLapisReturn.java | 190 +- .../enchanting/EnchantingOfferReroll.java | 272 +- .../enchanting/EnchantingQuickEnchant.java | 320 +- .../enchanting/EnchantingXPReturn.java | 156 +- .../excavation/ExcavationDropToInventory.java | 140 +- .../excavation/ExcavationHaste.java | 144 +- .../excavation/ExcavationOmniTool.java | 594 +-- .../excavation/ExcavationSeismicPing.java | 426 +- .../excavation/ExcavationSpelunker.java | 364 +- .../herbalism/HerbalismBeeShepherd.java | 392 +- .../herbalism/HerbalismCompostCascade.java | 836 ++-- .../herbalism/HerbalismCraftableCobweb.java | 140 +- .../HerbalismCraftableMushroomBlocks.java | 174 +- .../herbalism/HerbalismDropToInventory.java | 146 +- .../herbalism/HerbalismGrowthAura.java | 292 +- .../herbalism/HerbalismHungryHippo.java | 134 +- .../herbalism/HerbalismHungryShield.java | 164 +- .../adaptation/herbalism/HerbalismLuck.java | 210 +- .../herbalism/HerbalismMyconid.java | 136 +- .../herbalism/HerbalismReplant.java | 344 +- .../herbalism/HerbalismRootedFooting.java | 234 +- .../herbalism/HerbalismSeedSower.java | 344 +- .../herbalism/HerbalismSporeBloom.java | 990 ++-- .../herbalism/HerbalismTerralid.java | 140 +- .../adaptation/hunter/HunterAdrenaline.java | 170 +- .../hunter/HunterDropToInventory.java | 182 +- .../adaptation/hunter/HunterInvis.java | 216 +- .../adaptation/hunter/HunterJumpBoost.java | 214 +- .../content/adaptation/hunter/HunterLuck.java | 218 +- .../adaptation/hunter/HunterRegen.java | 214 +- .../adaptation/hunter/HunterResistance.java | 214 +- .../adaptation/hunter/HunterSpeed.java | 490 +- .../adaptation/hunter/HunterStrength.java | 214 +- .../hunter/HunterTrophySkinner.java | 356 +- .../adaptation/nether/NetherBlazeLeech.java | 390 +- .../adaptation/nether/NetherFireResist.java | 170 +- .../adaptation/nether/NetherGhastWard.java | 320 +- .../adaptation/nether/NetherLavaWalker.java | 278 +- .../adaptation/nether/NetherPiglinBroker.java | 330 +- .../adaptation/nether/NetherSkullYeet.java | 332 +- .../adaptation/nether/NetherWitherResist.java | 180 +- .../adaptation/pickaxe/PickaxeAutosmelt.java | 378 +- .../adaptation/pickaxe/PickaxeChisel.java | 313 +- .../pickaxe/PickaxeDropToInventory.java | 146 +- .../pickaxe/PickaxeQuarrySense.java | 572 +-- .../pickaxe/PickaxeSilkSpawner.java | 206 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 328 +- .../ranged/RangedArrowRecovery.java | 186 +- .../adaptation/ranged/RangedFloaters.java | 230 +- .../adaptation/ranged/RangedForce.java | 214 +- .../adaptation/ranged/RangedLungeShot.java | 178 +- .../adaptation/ranged/RangedPiercing.java | 182 +- .../adaptation/ranged/RangedPinningShot.java | 292 +- .../adaptation/ranged/RangedRicochetBolt.java | 678 +-- .../ranged/RangedTrajectorySight.java | 1644 ++++--- .../adaptation/ranged/RangedWebBomb.java | 404 +- .../content/adaptation/rift/RiftAccess.java | 509 +- .../content/adaptation/rift/RiftBlink.java | 870 ++-- .../content/adaptation/rift/RiftDescent.java | 198 +- .../adaptation/rift/RiftEnderTaglock.java | 736 +-- .../adaptation/rift/RiftEnderchest.java | 136 +- .../content/adaptation/rift/RiftGate.java | 436 +- .../rift/RiftInflatedPocketDimension.java | 482 +- .../content/adaptation/rift/RiftResist.java | 172 +- .../content/adaptation/rift/RiftVisage.java | 28 +- .../adaptation/rift/RiftVoidMagnet.java | 343 +- .../seaborrne/SeaborneFishersFantasy.java | 164 +- .../adaptation/seaborrne/SeaborneOxygen.java | 150 +- .../seaborrne/SeabornePressureDiver.java | 396 +- .../adaptation/seaborrne/SeaborneSpeed.java | 164 +- .../seaborrne/SeaborneTidecaller.java | 716 +-- .../seaborrne/SeaborneTurtlesMiningSpeed.java | 156 +- .../seaborrne/SeaborneTurtlesVision.java | 138 +- .../adaptation/stealth/StealthEnderVeil.java | 162 +- .../adaptation/stealth/StealthGhostArmor.java | 228 +- .../stealth/StealthShadowDecoy.java | 1575 ++---- .../stealth/StealthShadowDecoyPackets.java | 843 ++++ .../adaptation/stealth/StealthSight.java | 215 +- .../adaptation/stealth/StealthSilentStep.java | 916 ++-- .../adaptation/stealth/StealthSnatch.java | 263 +- .../adaptation/stealth/StealthSpeed.java | 736 +-- .../stealth/util/EntityListing.java | 72 +- .../adaptation/sword/SwordsBloodyBlade.java | 276 +- .../sword/SwordsCrimsonCyclone.java | 468 +- .../adaptation/sword/SwordsDualWield.java | 198 +- .../sword/SwordsExecutionersEdge.java | 272 +- .../adaptation/sword/SwordsMachete.java | 378 +- .../adaptation/sword/SwordsPoisonedBlade.java | 266 +- .../adaptation/sword/SwordsRiposteWindow.java | 300 +- .../sword/effects/DamagingBleedEffect.java | 24 +- .../adaptation/taming/TamingBeastRecall.java | 324 +- .../adaptation/taming/TamingDamage.java | 338 +- .../adaptation/taming/TamingHealthBoost.java | 323 +- .../taming/TamingHealthRegeneration.java | 214 +- .../taming/TamingMountedTactics.java | 450 +- .../taming/TamingPackLeaderAura.java | 288 +- .../adaptation/taming/TamingSharedPain.java | 230 +- .../adaptation/tragoul/TragoulBloodPact.java | 682 +-- .../tragoul/TragoulBoneHarvest.java | 642 +-- .../adaptation/tragoul/TragoulGlobe.java | 256 +- .../adaptation/tragoul/TragoulHealing.java | 248 +- .../adaptation/tragoul/TragoulLance.java | 265 +- .../adaptation/tragoul/TragoulThorns.java | 275 +- .../tragoul/utils/EntityThings.java | 32 +- .../unarmed/UnarmedBatteringCharge.java | 426 +- .../adaptation/unarmed/UnarmedComboChain.java | 348 +- .../unarmed/UnarmedGlassCannon.java | 208 +- .../adaptation/unarmed/UnarmedPower.java | 190 +- .../unarmed/UnarmedSuckerPunch.java | 244 +- .../adapt/content/block/ScaffoldMatter.java | 50 +- .../content/event/AdaptAdaptationEvent.java | 36 +- .../event/AdaptAdaptationTeleportEvent.java | 14 +- .../event/AdaptAdaptationUseEvent.java | 6 +- .../adapt/content/event/AdaptEvent.java | 48 +- .../adapt/content/event/AdaptPlayerEvent.java | 16 +- .../arcane/adapt/content/gui/ConfigGui.java | 2794 +++++------ .../adapt/content/gui/ConfigGuiTypes.java | 43 + .../content/gui/ConfigGuiValueCodec.java | 215 + .../arcane/adapt/content/gui/SkillsGui.java | 329 +- .../adapt/content/item/BoundEnderPearl.java | 112 +- .../adapt/content/item/BoundEyeOfEnder.java | 98 +- .../content/item/BoundRedstoneTorch.java | 128 +- .../adapt/content/item/BoundSnowBall.java | 110 +- .../content/item/ChronoTimeBombItem.java | 98 +- .../adapt/content/item/ChronoTimeBottle.java | 90 +- .../adapt/content/item/ExperienceOrb.java | 126 +- .../adapt/content/item/ItemListings.java | 1450 +++--- .../adapt/content/item/KnowledgeOrb.java | 144 +- .../content/item/multiItems/MultiArmor.java | 52 +- .../content/item/multiItems/MultiItem.java | 240 +- .../content/item/multiItems/OmniTool.java | 142 +- .../content/matter/BrewingStandOwner.java | 2 +- .../matter/BrewingStandOwnerMatter.java | 30 +- .../protector/ChestProtectProtector.java | 38 +- .../protector/FactionsClaimProtector.java | 56 +- .../protector/GriefDefenderProtector.java | 116 +- .../protector/GriefPreventionProtector.java | 80 +- .../protector/LocketteProProtector.java | 24 +- .../content/protector/ResidenceProtector.java | 148 +- .../protector/WorldGuardProtector.java | 173 +- .../adapt/content/skill/SkillAgility.java | 420 +- .../adapt/content/skill/SkillArchitect.java | 388 +- .../arcane/adapt/content/skill/SkillAxes.java | 452 +- .../adapt/content/skill/SkillBlocking.java | 400 +- .../adapt/content/skill/SkillBrewing.java | 539 ++- .../adapt/content/skill/SkillChronos.java | 1047 ++-- .../adapt/content/skill/SkillCrafting.java | 512 +- .../adapt/content/skill/SkillDiscovery.java | 654 +-- .../adapt/content/skill/SkillEnchanting.java | 384 +- .../adapt/content/skill/SkillExcavation.java | 422 +- .../adapt/content/skill/SkillHerbalism.java | 558 +-- .../adapt/content/skill/SkillHunter.java | 496 +- .../adapt/content/skill/SkillNether.java | 432 +- .../adapt/content/skill/SkillPickaxes.java | 528 +- .../adapt/content/skill/SkillRanged.java | 440 +- .../arcane/adapt/content/skill/SkillRift.java | 506 +- .../adapt/content/skill/SkillSeaborne.java | 478 +- .../adapt/content/skill/SkillStealth.java | 424 +- .../adapt/content/skill/SkillSwords.java | 400 +- .../adapt/content/skill/SkillTaming.java | 422 +- .../adapt/content/skill/SkillTragOul.java | 486 +- .../adapt/content/skill/SkillUnarmed.java | 408 +- .../core/nms/container/BlockProperty.java | 284 +- src/main/java/art/arcane/adapt/nms/NMS.java | 30 +- .../service/AdaptIntegrationService.java | 308 +- .../art/arcane/adapt/service/CommandSVC.java | 289 +- .../arcane/adapt/service/ConfigInputSVC.java | 192 +- .../art/arcane/adapt/service/HotloadSVC.java | 756 +-- .../arcane/adapt/util/cache/AtomicCache.java | 137 +- .../art/arcane/adapt/util/cache/Cache.java | 40 +- .../util/common/collection/Dictionary.java | 742 +-- .../adapt/util/common/collection/KeyPair.java | 48 +- .../util/common/format/AdventureCompat.java | 134 +- .../arcane/adapt/util/common/format/C.java | 1215 ++--- .../util/common/format/HiddenStringUtils.java | 170 +- .../arcane/adapt/util/common/format/ING.java | 5 +- .../adapt/util/common/format/Localizer.java | 522 +- .../util/common/function/CallbackCV.java | 2 +- .../adapt/util/common/function/Consumer2.java | 2 +- .../adapt/util/common/function/Consumer4.java | 2 +- .../adapt/util/common/function/Consumer5.java | 2 +- .../adapt/util/common/function/Consumer6.java | 2 +- .../adapt/util/common/function/Consumer7.java | 2 +- .../adapt/util/common/function/Consumer8.java | 2 +- .../adapt/util/common/function/Function2.java | 2 +- .../adapt/util/common/function/Function4.java | 2 +- .../util/common/function/NoiseInjector.java | 2 +- .../util/common/function/NoiseProvider.java | 2 +- .../adapt/util/common/function/Supplier2.java | 2 +- .../adapt/util/common/function/Supplier3.java | 2 +- .../util/common/inventorygui/GuiConfirm.java | 93 +- .../util/common/inventorygui/GuiEffects.java | 28 +- .../util/common/inventorygui/GuiLayout.java | 100 +- .../util/common/inventorygui/GuiTheme.java | 47 +- .../util/common/inventorygui/Inventories.java | 122 +- .../adapt/util/common/inventorygui/Items.java | 578 +-- .../common/inventorygui/PhantomInventory.java | 366 +- .../inventorygui/PhantomInventoryWrapper.java | 24 +- .../adapt/util/common/io/BukkitGson.java | 130 +- .../art/arcane/adapt/util/common/io/Json.java | 56 +- .../arcane/adapt/util/common/io/LZString.java | 960 ++-- .../adapt/util/common/io/PersistentJson.java | 20 +- .../adapt/util/common/io/ReactiveFolder.java | 82 +- .../adapt/util/common/io/SQLManager.java | 220 +- .../util/common/io/ShittyGsonDataClass.java | 18 +- .../adapt/util/common/math/ArrayType.java | 4 +- .../adapt/util/common/math/CarveResult.java | 10 +- .../adapt/util/common/math/CaveResult.java | 18 +- .../adapt/util/common/math/ChunkPosition.java | 72 +- .../adapt/util/common/math/DataPalette.java | 152 +- .../adapt/util/common/math/Dimension.java | 106 +- .../adapt/util/common/math/DimensionFace.java | 24 +- .../adapt/util/common/math/Direction.java | 702 +-- .../util/common/math/DoubleArrayUtils.java | 34 +- .../adapt/util/common/math/HeightMap.java | 22 +- .../adapt/util/common/math/IObjectPlacer.java | 18 +- .../util/common/math/IPostBlockAccess.java | 12 +- .../arcane/adapt/util/common/math/IRare.java | 8 +- .../util/common/math/InterpolationType.java | 10 +- .../adapt/util/common/math/MaterialBlock.java | 174 +- .../adapt/util/common/math/MathHelper.java | 838 ++-- .../arcane/adapt/util/common/math/Sphere.java | 74 +- .../adapt/util/common/math/VectorMath.java | 1098 ++--- .../adapt/util/common/math/VelocitySpeed.java | 191 +- .../adapt/util/common/math/Writable.java | 4 +- .../util/common/misc/AdvancementUtils.java | 109 +- .../arcane/adapt/util/common/misc/Area.java | 424 +- .../adapt/util/common/misc/Chunker.java | 70 +- .../adapt/util/common/misc/CustomModel.java | 311 +- .../adapt/util/common/misc/DependsOn.java | 2 +- .../adapt/util/common/misc/DirtyString.java | 34 +- .../adapt/util/common/misc/Impulse.java | 150 +- .../arcane/adapt/util/common/misc/Info.java | 6 +- .../adapt/util/common/misc/MaxNumber.java | 2 +- .../adapt/util/common/misc/MinNumber.java | 2 +- .../adapt/util/common/misc/NMSVersion.java | 170 +- .../adapt/util/common/misc/Shrinkwrap.java | 26 +- .../adapt/util/common/misc/SoundPlayer.java | 64 +- .../adapt/util/common/nbt/ByteArrayTag.java | 68 +- .../arcane/adapt/util/common/nbt/ByteTag.java | 52 +- .../adapt/util/common/nbt/CompoundTag.java | 64 +- .../adapt/util/common/nbt/DoubleTag.java | 52 +- .../arcane/adapt/util/common/nbt/EndTag.java | 28 +- .../adapt/util/common/nbt/FloatTag.java | 52 +- .../adapt/util/common/nbt/IntArrayTag.java | 58 +- .../arcane/adapt/util/common/nbt/IntTag.java | 52 +- .../arcane/adapt/util/common/nbt/ListTag.java | 92 +- .../arcane/adapt/util/common/nbt/LongTag.java | 52 +- .../adapt/util/common/nbt/NBTConstants.java | 54 +- .../adapt/util/common/nbt/NBTInputStream.java | 263 +- .../util/common/nbt/NBTOutputStream.java | 439 +- .../adapt/util/common/nbt/NBTUtils.java | 224 +- .../adapt/util/common/nbt/NibbleArray.java | 248 +- .../adapt/util/common/nbt/ShortTag.java | 52 +- .../adapt/util/common/nbt/StringTag.java | 52 +- .../art/arcane/adapt/util/common/nbt/Tag.java | 52 +- .../util/common/parallel/BurstExecutor.java | 6 +- .../util/common/parallel/MultiBurst.java | 62 +- .../util/common/plugin/AdaptService.java | 10 +- .../adapt/util/common/plugin/Metrics.java | 1494 +++--- .../util/common/plugin/VolmitPlugin.java | 427 +- .../util/common/plugin/VolmitSender.java | 1110 ++--- .../adapt/util/common/scheduling/J.java | 356 +- .../art/arcane/adapt/util/data/Metadata.java | 62 +- .../adapt/util/director/DirectorSystem.java | 35 +- .../context/AdaptationListingHandler.java | 226 +- .../handlers/AdaptationListHandler.java | 34 +- .../handlers/AdaptationProviderHandler.java | 34 +- .../handlers/AdaptationSkillListHandler.java | 34 +- .../director/handlers/BlockVectorHandler.java | 44 +- .../handlers/OptionalWorldHandler.java | 8 +- .../director/handlers/ParticleHandler.java | 40 +- .../util/director/handlers/PlayerHandler.java | 12 +- .../handlers/SkillProviderHandler.java | 34 +- .../util/director/handlers/SoundHandler.java | 40 +- .../util/director/handlers/VectorHandler.java | 44 +- .../util/director/handlers/WorldHandler.java | 8 +- .../NullablePlayerHandler.java | 24 +- .../adapt/util/project/command/Command.java | 2 +- .../util/project/command/CommandDummy.java | 166 +- .../adapt/util/project/command/FConst.java | 96 +- .../adapt/util/project/command/FService.java | 4 +- .../adapt/util/project/command/Feedback.java | 44 +- .../adapt/util/project/command/ICommand.java | 70 +- .../util/project/command/IController.java | 18 +- .../util/project/command/MortarCommand.java | 318 +- .../project/command/MortarPermission.java | 107 +- .../util/project/command/MortarSender.java | 342 +- .../util/project/command/RouterCommand.java | 62 +- .../util/project/command/SoundFeedback.java | 16 +- .../util/project/command/VirtualCommand.java | 226 +- .../project/config/ConfigDescription.java | 2 +- .../adapt/util/project/config/ConfigDoc.java | 4 +- .../project/config/ConfigDocumentation.java | 532 +- .../project/config/ConfigFileSupport.java | 438 +- .../config/ConfigMigrationManager.java | 342 +- .../project/config/ConfigRewriteReporter.java | 406 +- .../adapt/util/project/config/Desc.java | 2 +- .../adapt/util/project/config/TomlCodec.java | 776 +-- .../adapt/util/project/redis/RedisSync.java | 92 +- .../util/project/secret/SecretSplash.java | 102 +- .../arcane/adapt/util/reflect/Reflect.java | 96 +- .../adapt/util/reflect/WrappedField.java | 54 +- .../util/reflect/WrappedReturningMethod.java | 48 +- .../util/reflect/events/ReflectiveEvents.java | 176 +- .../adapt/util/reflect/events/api/Event.java | 2 +- .../reflect/events/api/ReflectiveHandler.java | 4 +- .../api/entity/EndermanAttackPlayerEvent.java | 9 +- .../api/entity/EntityDismountEvent.java | 4 +- .../events/api/entity/EntityEvent.java | 5 +- .../events/api/entity/EntityMountEvent.java | 4 +- .../util/reflect/registries/Attributes.java | 8 +- .../util/reflect/registries/Enchantments.java | 6 +- .../util/reflect/registries/EntityTypes.java | 2 +- .../util/reflect/registries/ItemFlags.java | 2 +- .../util/reflect/registries/Materials.java | 158 +- .../util/reflect/registries/Particles.java | 14 +- .../reflect/registries/PotionEffectTypes.java | 12 +- .../util/reflect/registries/PotionTypes.java | 10 +- .../util/reflect/registries/RegistryUtil.java | 418 +- .../util/AdvancementUtils.java | 585 ++- src/main/resources/de_DE.toml | 2560 +++++----- src/main/resources/en_US.toml | 4272 ++++++++--------- src/main/resources/es_ES.toml | 2560 +++++----- src/main/resources/fi_FI.toml | 2560 +++++----- src/main/resources/fr_FR.toml | 2560 +++++----- src/main/resources/he_IL.toml | 2560 +++++----- src/main/resources/it_IT.toml | 2560 +++++----- src/main/resources/ja-JP.toml | 2560 +++++----- src/main/resources/ko_KR.toml | 2560 +++++----- src/main/resources/lt_LT.toml | 2560 +++++----- src/main/resources/nl_NL.toml | 2560 +++++----- src/main/resources/pl_PL.toml | 2560 +++++----- src/main/resources/plugin.yml | 2 +- src/main/resources/pt_PT.toml | 2560 +++++----- src/main/resources/ru_RU.toml | 2560 +++++----- src/main/resources/tr_TR.toml | 2560 +++++----- src/main/resources/vi_VI.toml | 2560 +++++----- src/main/resources/zh_CN.toml | 2560 +++++----- src/main/resources/zh_TW.toml | 2560 +++++----- 482 files changed, 87903 insertions(+), 87400 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallTypes.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java create mode 100644 src/main/java/art/arcane/adapt/content/gui/ConfigGuiTypes.java create mode 100644 src/main/java/art/arcane/adapt/content/gui/ConfigGuiValueCodec.java diff --git a/build.gradle.kts b/build.gradle.kts index 869b84894..3b3f11847 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,6 @@ import io.github.slimjar.resolver.data.Mirror import org.gradle.api.plugins.JavaPluginExtension import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import xyz.jpenilla.runpaper.task.RunServer import kotlin.system.exitProcess @@ -33,7 +32,6 @@ plugins { alias(libs.plugins.runVelocity) alias(libs.plugins.idea) alias(libs.plugins.slimjar) - alias(libs.plugins.kotlin.jvm) } version = "2.0.0-1.20.2-1.21.11-Dev1" @@ -54,7 +52,7 @@ registerCustomOutputTask("CrazyDev22", "C://Users/Julian/Desktop/server/plugins" registerCustomOutputTask("Pixel", "D://Iris Dimension Engine//1.20.4 - Development//plugins") // ========================== UNIX ============================== registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins") -registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/plugin-jars") +registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins") registerCustomOutputTaskUnix("the456gamer", "/home/the456gamer/projects/minecraft/adapt-testserver/plugins/update/", false) // ============================================================== @@ -135,6 +133,7 @@ allprojects { tasks.compileJava { options.compilerArgs.add("-parameters") options.encoding = "UTF-8" + options.debugOptions.debugLevel = "none" options.release.set(21) } } @@ -147,7 +146,7 @@ dependencies { } implementation(slimjarHelper("spigot")) implementation(slimjarHelper("velocity")) - implementation(libs.platformUtils) { + slimApi(libs.platformUtils) { isTransitive = false } @@ -160,7 +159,7 @@ dependencies { slimApi(libs.amulet) slimApi(libs.chrono) slimApi(libs.spatial) - implementation(libs.kotlin.coroutines) + slimApi(libs.kotlin.coroutines) // Dynamically Loaded slimApi(libs.adventure.minimessage) @@ -170,7 +169,7 @@ dependencies { slimApi(libs.lettuce) slimApi(libs.particle) // Keep UAA in the main shaded jar so Folia patches in source overrides take precedence. - implementation(libs.ultimateAdvancementApi) + slimApi(libs.ultimateAdvancementApi) slimApi(libs.customBlockData) slimApi(libs.lur) slimApi(libs.lang3) @@ -215,6 +214,8 @@ slimJar { tasks.shadowJar { // minimize() duplicatesStrategy = DuplicatesStrategy.EXCLUDE + exclude("net/kyori/**") + exclude("com/google/gson/**") } configurations.configureEach { @@ -246,13 +247,6 @@ java { } } -kotlin { - jvmToolchain(21) - compilerOptions { - jvmTarget.set(JvmTarget.JVM_21) - } -} - tasks { build { dependsOn(shadowJar) } diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 1a3c6f6d5..d4444e134 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -34,7 +34,6 @@ import art.arcane.adapt.content.protector.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.io.SQLManager; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.plugin.AdaptService; @@ -48,6 +47,7 @@ import art.arcane.adapt.util.secret.SecretSplash; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.volmlib.util.io.JarScanner; import com.jeff_media.customblockdata.CustomBlockData; import de.crazydev22.platformutils.AudienceProvider; @@ -74,438 +74,432 @@ import static art.arcane.adapt.util.director.context.AdaptationListingHandler.initializeAdaptationListings; public class Adapt extends VolmitPlugin { - public static Adapt instance; - public static HashMap wordKey = new HashMap<>(); - public final EffectManager adaptEffectManager; - public static Platform platform; - public static AudienceProvider audiences; - private KMap, AdaptService> services; - - @Getter - private GlowingEntities glowingEntities; - @Getter - private Ticker ticker; - @Getter - private AdaptServer adaptServer; - @Getter - private SQLManager sqlManager; - @Getter - private ProtectorRegistry protectorRegistry; - @Getter - private Map guiLeftovers = new HashMap<>(); - - @Getter - private AdvancementManager manager; - @Getter - private RedisSync redisSync; - @Getter - private PlayerDataPersistenceQueue playerDataPersistenceQueue; - - - private final KList postShutdown = new KList<>(); - private static VolmitSender sender; - private static final long STARTUP_SLOW_PHASE_MS = 1500L; - private static final boolean SLIMJAR_DEBUG = Boolean.getBoolean("adapt.debug-slimjar"); - private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("adapt.disable-remapper"); - - - public Adapt() { - instance = this; - long libraryLoadStart = System.currentTimeMillis(); - getLogger().info("Loading Libraries..."); - new SpigotApplicationBuilder(this) - .debug(SLIMJAR_DEBUG) - .remap(!DISABLE_REMAPPER) - .build(); - long libraryLoadElapsed = System.currentTimeMillis() - libraryLoadStart; - if (DISABLE_REMAPPER) { - getLogger().warning("SlimJar remapper disabled via -Dadapt.disable-remapper=true."); - } - getLogger().info("Libraries Loaded! (" + libraryLoadElapsed + "ms)"); - adaptEffectManager = new EffectManager(this); + private static final long STARTUP_SLOW_PHASE_MS = 1500L; + private static final boolean SLIMJAR_DEBUG = Boolean.getBoolean("adapt.debug-slimjar"); + private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("adapt.disable-remapper"); + public static Adapt instance; + public static HashMap wordKey = new HashMap<>(); + public static Platform platform; + public static AudienceProvider audiences; + private static VolmitSender sender; + public final EffectManager adaptEffectManager; + private final KList postShutdown = new KList<>(); + private KMap, AdaptService> services; + @Getter + private GlowingEntities glowingEntities; + @Getter + private Ticker ticker; + @Getter + private AdaptServer adaptServer; + @Getter + private SQLManager sqlManager; + @Getter + private ProtectorRegistry protectorRegistry; + @Getter + private Map guiLeftovers = new HashMap<>(); + @Getter + private AdvancementManager manager; + @Getter + private RedisSync redisSync; + @Getter + private PlayerDataPersistenceQueue playerDataPersistenceQueue; + + + public Adapt() { + instance = this; + long libraryLoadStart = System.currentTimeMillis(); + getLogger().info("Loading Libraries..."); + new SpigotApplicationBuilder(this) + .debug(SLIMJAR_DEBUG) + .remap(!DISABLE_REMAPPER) + .build(); + long libraryLoadElapsed = System.currentTimeMillis() - libraryLoadStart; + if (DISABLE_REMAPPER) { + getLogger().warning("SlimJar remapper disabled via -Dadapt.disable-remapper=true."); } - - @SuppressWarnings("unchecked") - public static T service(Class c) { - return (T) instance.services.get(c); + getLogger().info("Libraries Loaded! (" + libraryLoadElapsed + "ms)"); + adaptEffectManager = new EffectManager(this); + } + + @SuppressWarnings("unchecked") + public static T service(Class c) { + return (T) instance.services.get(c); + } + + private static void runStartupPhaseVoid(String phase, Runnable action) { + runStartupPhase(phase, () -> { + action.run(); + return null; + }); + } + + private static T runStartupPhase(String phase, Supplier action) { + if (phase == null || phase.isBlank()) { + return action.get(); } - @Override - public void onLoad() { - manager = new AdvancementManager(); - if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { - WorldGuardProtector.registerFlag(); - } - } - - @Override - public void start() { - runStartupPhaseVoid("backup-legacy-configs", ConfigMigrationManager::backupLegacyJsonConfigsOnce); - platform = PlatformUtils.createPlatform(this); - audiences = platform.getAudienceProvider(); - services = new KMap<>(); - runStartupPhaseVoid("discover-services", () -> initialize("art.arcane.adapt.service") - .forEach((i) -> services.put((Class) i.getClass(), (AdaptService) i))); - - runStartupPhaseVoid("language-update", Localizer::updateLanguageFile); - if (!runStartupPhase("models-load", CustomModel::reloadFromDisk)) { - Adapt.warn("Failed to load models config during startup migration."); - } - if (!AdaptConfig.get().isCustomModels()) { - CustomModel.clear(); - } - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - new PapiExpansion().register(); - } - printInformation(); - sqlManager = new SQLManager(); - if (AdaptConfig.get().isUseSql()) { - runStartupPhase("sql-connect", () -> { - sqlManager.establishConnection(); - return null; - }); - } - redisSync = new RedisSync(); - playerDataPersistenceQueue = new PlayerDataPersistenceQueue(); - runStartupPhase("start-sim", () -> { - startSim(); - return null; - }); - runStartupPhase("config-canonicalization", () -> { - migrateAllSkillAndAdaptationConfigs(); - return null; - }); - CustomBlockData.registerListener(this); - registerListener(new BrewingManager()); - registerListener(Version.get()); - setupMetrics(); - startupPrint(); // Splash screen - if (AdaptConfig.get().isAutoUpdateCheck()) { - autoUpdateCheck(); - } - protectorRegistry = new ProtectorRegistry(); - if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { - protectorRegistry.registerProtector(new WorldGuardProtector()); - } - if (getServer().getPluginManager().getPlugin("Factions") != null) { - protectorRegistry.registerProtector(new FactionsClaimProtector()); - } - if (getServer().getPluginManager().getPlugin("ChestProtect") != null) { - protectorRegistry.registerProtector(new ChestProtectProtector()); - } - if (getServer().getPluginManager().getPlugin("Residence") != null) { - protectorRegistry.registerProtector(new ResidenceProtector()); - } - if (getServer().getPluginManager().getPlugin("GriefDefender") != null) { - protectorRegistry.registerProtector(new GriefDefenderProtector()); - } - if (getServer().getPluginManager().getPlugin("GriefPrevention") != null) { - protectorRegistry.registerProtector(new GriefPreventionProtector()); - } - if (getServer().getPluginManager().getPlugin("LockettePro") != null) { - protectorRegistry.registerProtector(new LocketteProProtector()); - } - glowingEntities = new GlowingEntities(this); - initializeAdaptationListings(); - services.values().forEach(AdaptService::onEnable); - services.values().forEach(this::registerListener); - ConfigFileSupport.flushCreatedConfigSummary(); + info("Startup phase: " + phase); + long start = System.currentTimeMillis(); + try { + return action.get(); + } finally { + long elapsed = System.currentTimeMillis() - start; + if (elapsed >= STARTUP_SLOW_PHASE_MS) { + warn("Startup phase '" + phase + "' took " + elapsed + "ms."); + } else { + verbose("Startup phase '" + phase + "' took " + elapsed + "ms."); + } } + } - private static void runStartupPhaseVoid(String phase, Runnable action) { - runStartupPhase(phase, () -> { - action.run(); - return null; - }); + public static VolmitSender getSender() { + if (sender == null) { + sender = new VolmitSender(Bukkit.getConsoleSender()); + sender.setTag(instance.getTag()); } - - private static T runStartupPhase(String phase, Supplier action) { - if (phase == null || phase.isBlank()) { - return action.get(); - } - - info("Startup phase: " + phase); - long start = System.currentTimeMillis(); + return sender; + } + + public static List initialize(String s) { + return initialize(s, null); + } + + public static KList initialize(String s, Class slicedClass) { + JarScanner js = new JarScanner(instance.getFile(), s); + KList v = new KList<>(); + J.attempt(js::scan); + for (Class i : js.getClasses()) { + if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { try { - return action.get(); - } finally { - long elapsed = System.currentTimeMillis() - start; - if (elapsed >= STARTUP_SLOW_PHASE_MS) { - warn("Startup phase '" + phase + "' took " + elapsed + "ms."); - } else { - verbose("Startup phase '" + phase + "' took " + elapsed + "ms."); - } - } + Adapt.verbose("Found class: " + i.getName()); + v.add(i.getDeclaredConstructor().newInstance()); + } catch (Throwable e) { + Adapt.verbose("Failed to load class: " + i.getName()); + StringWriter writer = new StringWriter(); + e.printStackTrace(new PrintWriter(writer)); + for (String line : writer.toString().split("\n")) { + verbose(line); + } + } + } } - private void migrateAllSkillAndAdaptationConfigs() { - if (adaptServer == null || adaptServer.getSkillRegistry() == null) { - return; - } - - if (!ConfigMigrationManager.hasLegacySkillOrAdaptationJsonFiles()) { - int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); - Adapt.info("Skipped skill/adaptation canonicalization (legacy json not found). deletedLegacyJson=" + deletedLegacyJson + "."); + return v; + } + + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + public static void printInformation() { + debug("XP Curve: " + AdaptConfig.get().getXpCurve()); + debug("XP/Level base: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); + debug("XP/Level multiplier: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()); + info("Language: " + AdaptConfig.get().getLanguage() + " - Language Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); + } + + @SneakyThrows + public static void autoUpdateCheck() { + try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts").openStream()))) { + info("Checking for updates..."); + String inputLine; + while ((inputLine = in.readLine()) != null) { + if (inputLine.contains("version '")) { + String version = inputLine.replace("version '", "").replace("'", "").replace("// Needs to be version specific", "").replace(" ", ""); + if (instance.getDescription().getVersion().contains("development")) { + info("Development build detected. Skipping update check."); return; - } + } else if (!version.equals(instance.getDescription().getVersion())) { + info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", instance.getDescription().getVersion(), version)); + } else { + info("You are running the latest version of Adapt!"); + } + break; + } + } + } catch (Throwable e) { + error("Failed to check for updates."); + } + } - int migratedSkills = 0; - int migratedAdaptations = 0; - for (Skill skill : adaptServer.getSkillRegistry().getSkills()) { - if (skill instanceof SimpleSkill simpleSkill) { - if (simpleSkill.reloadConfigFromDisk(false)) { - migratedSkills++; - } - } - - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation instanceof SimpleAdaptation simpleAdaptation) { - if (simpleAdaptation.reloadConfigFromDisk(false)) { - migratedAdaptations++; - } - } - } - } + public static void actionbar(Player p, String msg) { + new VolmitSender(p).sendAction(msg); + } - int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); - Adapt.info("Canonicalized skill/adaptation configs to TOML (skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson + ")."); + public static void debug(String string) { + if (AdaptConfig.get().isDebug()) { + msg(C.DARK_PURPLE + string); } + } + public static void warn(String string) { + msg(C.YELLOW + string); + } - public void startSim() { - long startTicker = System.currentTimeMillis(); - ticker = new Ticker(); - verbose("start-sim detail: ticker init in " + (System.currentTimeMillis() - startTicker) + "ms"); + public static void error(String string) { + msg(C.RED + string); + } - long startServer = System.currentTimeMillis(); - adaptServer = new AdaptServer(); - long serverMs = System.currentTimeMillis() - startServer; - if (serverMs >= STARTUP_SLOW_PHASE_MS) { - warn("start-sim detail: AdaptServer init took " + serverMs + "ms."); - } else { - verbose("start-sim detail: AdaptServer init in " + serverMs + "ms"); - } - - long startAdv = System.currentTimeMillis(); - manager.enable(); - verbose("start-sim detail: advancement manager enable in " + (System.currentTimeMillis() - startAdv) + "ms"); + public static void verbose(String string) { + if (AdaptConfig.get().isVerbose()) { + msg(C.LIGHT_PURPLE + string); } - - public void postShutdown(Runnable r) { - postShutdown.add(r); + } + + public static void success(String string) { + msg(C.GREEN + string); + } + + public static void info(String string) { + msg(C.WHITE + string); + } + + public static void messagePlayer(Player p, String string) { + String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; + p.sendMessage(msg); + } + + public static void msg(String string) { + try { + if (instance == null) { + System.out.println("[Adapt]: " + string); + return; + } + + String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; + Bukkit.getConsoleSender().sendMessage(msg); + } catch (Throwable e) { + System.out.println("[Adapt]: " + string); } + } - public void stopSim() { - if (ticker != null) { - ticker.clear(); - } - postShutdown.forEach(Runnable::run); - if (adaptServer != null) { - adaptServer.unregister(); - } - if (manager != null) { - manager.disable(); - } - MaterialValue.save(); - WorldData.stop(); - CustomModel.clear(); + @Override + public void onLoad() { + manager = new AdvancementManager(); + if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { + WorldGuardProtector.registerFlag(); } - - - @Override - public void stop() { - if (services != null) { - services.values().forEach(AdaptService::onDisable); - } - stopSim(); - if (playerDataPersistenceQueue != null) { - playerDataPersistenceQueue.flushAndShutdown(30_000L); - playerDataPersistenceQueue = null; - } - if (redisSync != null) { - try { - redisSync.close(); - } catch (Exception e) { - Adapt.verbose("Failed to close redis sync: " + e.getMessage()); - } finally { - redisSync = null; - } - } - if (sqlManager != null) { - sqlManager.closeConnection(); - } - if (glowingEntities != null) { - glowingEntities.disable(); - } - if (protectorRegistry != null) { - protectorRegistry.unregisterAll(); - } - if (services != null) { - services.clear(); - } + } + + @Override + public void start() { + runStartupPhaseVoid("backup-legacy-configs", ConfigMigrationManager::backupLegacyJsonConfigsOnce); + platform = PlatformUtils.createPlatform(this); + audiences = platform.getAudienceProvider(); + services = new KMap<>(); + runStartupPhaseVoid("discover-services", () -> initialize("art.arcane.adapt.service") + .forEach((i) -> services.put((Class) i.getClass(), (AdaptService) i))); + + runStartupPhaseVoid("language-update", Localizer::updateLanguageFile); + if (!runStartupPhase("models-load", CustomModel::reloadFromDisk)) { + Adapt.warn("Failed to load models config during startup migration."); } - - private void startupPrint() { - if (!AdaptConfig.get().isSplashScreen()) { - return; - } - Random r = new Random(); - int game = r.nextInt(100); - if (game < 90) { - Adapt.info("\n" + C.DARK_GRAY + " █████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "█████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "████████" + C.DARK_RED + "╗\n" + - C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + - C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + - C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║ \n" + - C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); - } else { - info(SecretSplash.getSecretSplash().getRandom()); - } + if (!AdaptConfig.get().isCustomModels()) { + CustomModel.clear(); } - - public File getJarFile() { - return getFile(); + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + new PapiExpansion().register(); } - - @Override - public String getTag(String subTag) { - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.DARK_RED + "Adapt" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + printInformation(); + sqlManager = new SQLManager(); + if (AdaptConfig.get().isUseSql()) { + runStartupPhase("sql-connect", () -> { + sqlManager.establishConnection(); + return null; + }); } - - private void setupMetrics() { - if (AdaptConfig.get().isMetrics()) { - new Metrics(this, 24221); - } + redisSync = new RedisSync(); + playerDataPersistenceQueue = new PlayerDataPersistenceQueue(); + runStartupPhase("start-sim", () -> { + startSim(); + return null; + }); + runStartupPhase("config-canonicalization", () -> { + migrateAllSkillAndAdaptationConfigs(); + return null; + }); + CustomBlockData.registerListener(this); + registerListener(new BrewingManager()); + registerListener(Version.get()); + setupMetrics(); + startupPrint(); // Splash screen + if (AdaptConfig.get().isAutoUpdateCheck()) { + autoUpdateCheck(); + } + protectorRegistry = new ProtectorRegistry(); + if (getServer().getPluginManager().getPlugin("WorldGuard") != null) { + protectorRegistry.registerProtector(new WorldGuardProtector()); + } + if (getServer().getPluginManager().getPlugin("Factions") != null) { + protectorRegistry.registerProtector(new FactionsClaimProtector()); + } + if (getServer().getPluginManager().getPlugin("ChestProtect") != null) { + protectorRegistry.registerProtector(new ChestProtectProtector()); + } + if (getServer().getPluginManager().getPlugin("Residence") != null) { + protectorRegistry.registerProtector(new ResidenceProtector()); + } + if (getServer().getPluginManager().getPlugin("GriefDefender") != null) { + protectorRegistry.registerProtector(new GriefDefenderProtector()); + } + if (getServer().getPluginManager().getPlugin("GriefPrevention") != null) { + protectorRegistry.registerProtector(new GriefPreventionProtector()); + } + if (getServer().getPluginManager().getPlugin("LockettePro") != null) { + protectorRegistry.registerProtector(new LocketteProProtector()); + } + glowingEntities = new GlowingEntities(this); + initializeAdaptationListings(); + services.values().forEach(AdaptService::onEnable); + services.values().forEach(this::registerListener); + ConfigFileSupport.flushCreatedConfigSummary(); + } + + private void migrateAllSkillAndAdaptationConfigs() { + if (adaptServer == null || adaptServer.getSkillRegistry() == null) { + return; } - public static VolmitSender getSender() { - if (sender == null) { - sender = new VolmitSender(Bukkit.getConsoleSender()); - sender.setTag(instance.getTag()); - } - return sender; - } - - public static List initialize(String s) { - return initialize(s, null); - } - - public static KList initialize(String s, Class slicedClass) { - JarScanner js = new JarScanner(instance.getFile(), s); - KList v = new KList<>(); - J.attempt(js::scan); - for (Class i : js.getClasses()) { - if (slicedClass == null || i.isAnnotationPresent(slicedClass)) { - try { - Adapt.verbose("Found class: " + i.getName()); - v.add(i.getDeclaredConstructor().newInstance()); - } catch (Throwable e) { - Adapt.verbose("Failed to load class: " + i.getName()); - StringWriter writer = new StringWriter(); - e.printStackTrace(new PrintWriter(writer)); - for (String line : writer.toString().split("\n")) { - verbose(line); - } - } - } - } - - return v; + if (!ConfigMigrationManager.hasLegacySkillOrAdaptationJsonFiles()) { + int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); + Adapt.info("Skipped skill/adaptation canonicalization (legacy json not found). deletedLegacyJson=" + deletedLegacyJson + "."); + return; } - public static int getJavaVersion() { - String version = System.getProperty("java.version"); - if (version.startsWith("1.")) { - version = version.substring(2, 3); - } else { - int dot = version.indexOf("."); - if (dot != -1) { - version = version.substring(0, dot); - } + int migratedSkills = 0; + int migratedAdaptations = 0; + for (Skill skill : adaptServer.getSkillRegistry().getSkills()) { + if (skill instanceof SimpleSkill simpleSkill) { + if (simpleSkill.reloadConfigFromDisk(false)) { + migratedSkills++; } - return Integer.parseInt(version); - } - - public static void printInformation() { - debug("XP Curve: " + AdaptConfig.get().getXpCurve()); - debug("XP/Level base: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); - debug("XP/Level multiplier: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()); - info("Language: " + AdaptConfig.get().getLanguage() + " - Language Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); - } - - @SneakyThrows - public static void autoUpdateCheck() { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts").openStream()))) { - info("Checking for updates..."); - String inputLine; - while ((inputLine = in.readLine()) != null) { - if (inputLine.contains("version '")) { - String version = inputLine.replace("version '", "").replace("'", "").replace("// Needs to be version specific", "").replace(" ", ""); - if (instance.getDescription().getVersion().contains("development")) { - info("Development build detected. Skipping update check."); - return; - } else if (!version.equals(instance.getDescription().getVersion())) { - info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", instance.getDescription().getVersion(), version)); - } else { - info("You are running the latest version of Adapt!"); - } - break; - } - } - } catch (Throwable e) { - error("Failed to check for updates."); + } + + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation instanceof SimpleAdaptation simpleAdaptation) { + if (simpleAdaptation.reloadConfigFromDisk(false)) { + migratedAdaptations++; + } } + } } - public static void actionbar(Player p, String msg) { - new VolmitSender(p).sendAction(msg); + int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); + Adapt.info("Canonicalized skill/adaptation configs to TOML (skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson + ")."); + } + + public void startSim() { + long startTicker = System.currentTimeMillis(); + ticker = new Ticker(); + verbose("start-sim detail: ticker init in " + (System.currentTimeMillis() - startTicker) + "ms"); + + long startServer = System.currentTimeMillis(); + adaptServer = new AdaptServer(); + long serverMs = System.currentTimeMillis() - startServer; + if (serverMs >= STARTUP_SLOW_PHASE_MS) { + warn("start-sim detail: AdaptServer init took " + serverMs + "ms."); + } else { + verbose("start-sim detail: AdaptServer init in " + serverMs + "ms"); } - public static void debug(String string) { - if (AdaptConfig.get().isDebug()) { - msg(C.DARK_PURPLE + string); - } - } + long startAdv = System.currentTimeMillis(); + manager.enable(); + verbose("start-sim detail: advancement manager enable in " + (System.currentTimeMillis() - startAdv) + "ms"); + } - public static void warn(String string) { - msg(C.YELLOW + string); - } + public void postShutdown(Runnable r) { + postShutdown.add(r); + } - public static void error(String string) { - msg(C.RED + string); + public void stopSim() { + if (ticker != null) { + ticker.clear(); } - - public static void verbose(String string) { - if (AdaptConfig.get().isVerbose()) { - msg(C.LIGHT_PURPLE + string); - } + postShutdown.forEach(Runnable::run); + if (adaptServer != null) { + adaptServer.unregister(); } - - public static void success(String string) { - msg(C.GREEN + string); + if (manager != null) { + manager.disable(); } - - public static void info(String string) { - msg(C.WHITE + string); + MaterialValue.save(); + WorldData.stop(); + CustomModel.clear(); + } + + @Override + public void stop() { + if (services != null) { + services.values().forEach(AdaptService::onDisable); + } + stopSim(); + if (playerDataPersistenceQueue != null) { + playerDataPersistenceQueue.flushAndShutdown(30_000L); + playerDataPersistenceQueue = null; } + if (redisSync != null) { + try { + redisSync.close(); + } catch (Exception e) { + Adapt.verbose("Failed to close redis sync: " + e.getMessage()); + } finally { + redisSync = null; + } + } + if (sqlManager != null) { + sqlManager.closeConnection(); + } + if (glowingEntities != null) { + glowingEntities.disable(); + } + if (protectorRegistry != null) { + protectorRegistry.unregisterAll(); + } + if (services != null) { + services.clear(); + } + } - public static void messagePlayer(Player p, String string) { - String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; - p.sendMessage(msg); + private void startupPrint() { + if (!AdaptConfig.get().isSplashScreen()) { + return; } + Random r = new Random(); + int game = r.nextInt(100); + if (game < 90) { + Adapt.info("\n" + C.DARK_GRAY + " █████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "█████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "████████" + C.DARK_RED + "╗\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║ \n" + + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); + } else { + info(SecretSplash.getSecretSplash().getRandom()); + } + } - public static void msg(String string) { - try { - if (instance == null) { - System.out.println("[Adapt]: " + string); - return; - } + public File getJarFile() { + return getFile(); + } - String msg = C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + string; - Bukkit.getConsoleSender().sendMessage(msg); - } catch (Throwable e) { - System.out.println("[Adapt]: " + string); - } + @Override + public String getTag(String subTag) { + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.DARK_RED + "Adapt" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + } + + private void setupMetrics() { + if (AdaptConfig.get().isMetrics()) { + new Metrics(this, 24221); } + } } diff --git a/src/main/java/art/arcane/adapt/AdaptConfig.java b/src/main/java/art/arcane/adapt/AdaptConfig.java index de93f2b03..8facc6e61 100644 --- a/src/main/java/art/arcane/adapt/AdaptConfig.java +++ b/src/main/java/art/arcane/adapt/AdaptConfig.java @@ -34,197 +34,196 @@ @SuppressWarnings("ALL") @Getter public class AdaptConfig { - private static AdaptConfig config = null; - private static final Object CONFIG_LOCK = new Object(); - public boolean debug = false; - public boolean autoUpdateCheck = true; - public boolean autoUpdateLanguage = true; - public boolean splashScreen = true; - public boolean xpInCreative = false; - public boolean allowAdaptationsInCreative = false; - public String adaptActivatorBlock = "BOOKSHELF"; - public String adaptActivatorBlockName = "a Bookshelf"; - public boolean adaptActivatorAllowVerticalFaces = false; - public List blacklistedWorlds = List.of("some_world_adapt_should_not_run_in", "anotherWorldFolderName"); - public int experienceMaxLevel = 1000; - boolean preventHunterSkillsWhenHungerApplied = true; - private ValueConfig value = new ValueConfig(); - private boolean metrics = true; - private String language = "en_US"; - private String fallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing = "en_US"; - private Curves xpCurve = Curves.ADAPT_BALANCED; - private double playerXpPerSkillLevelUpBase = 489; - private double playerXpPerSkillLevelUpLevelMultiplier = 44; - private double powerPerLevel = 0.65; - private boolean hardcoreResetOnPlayerDeath = false; - private boolean hardcoreNoRefunds = false; - private boolean loginBonus = true; - private boolean welcomeMessage = true; - private boolean advancements = true; - private boolean useSql = false; - private boolean useRedis = false; - private int sqlSecondsCheckverify = 30; - private boolean useEnchantmentTableParticleForActiveEffects = true; - private boolean escClosesAllGuis = false; - private boolean guiBackButton = false; - private boolean customModels = true; - private boolean automaticGradients = false; - private int learnUnlearnButtonDelayTicks = 14; - private int maxRecipeListPrecaution = 25; - private boolean actionbarNotifyXp = true; - private boolean actionbarNotifyLevel = true; - private boolean unlearnAllButton = false; - private Effects effects = new Effects(); - private FarmPrevention farmPrevention = new FarmPrevention(); - private AdaptationXp adaptationXp = new AdaptationXp(); - private RedisConfig redis = new RedisConfig(); - private SqlSettings sql = new SqlSettings(); - private Protector protectorSupport = new Protector(); - private Map> adaptationUsageConflicts = defaultAdaptationUsageConflicts(); - private Map> protectionOverrides = Map.of( - "adaptation-name", Map.of( - "WorldGuard", true - ) - ); - - @Setter - private boolean verbose = false; - - public static AdaptConfig get() { - synchronized (CONFIG_LOCK) { - try { - if (config == null) { - config = loadConfig(new AdaptConfig(), true); - } - } catch (Throwable e) { - e.printStackTrace(); - config = new AdaptConfig(); - } - - return config; - } - } - - public static boolean reload() { - synchronized (CONFIG_LOCK) { - try { - config = loadConfig(config == null ? new AdaptConfig() : config, false); - return true; - } catch (Throwable e) { - return false; - } + private static final Object CONFIG_LOCK = new Object(); + private static AdaptConfig config = null; + public boolean debug = false; + public boolean autoUpdateCheck = true; + public boolean autoUpdateLanguage = true; + public boolean splashScreen = true; + public boolean xpInCreative = false; + public boolean allowAdaptationsInCreative = false; + public String adaptActivatorBlock = "BOOKSHELF"; + public String adaptActivatorBlockName = "a Bookshelf"; + public boolean adaptActivatorAllowVerticalFaces = false; + public List blacklistedWorlds = List.of("some_world_adapt_should_not_run_in", "anotherWorldFolderName"); + public int experienceMaxLevel = 1000; + boolean preventHunterSkillsWhenHungerApplied = true; + private ValueConfig value = new ValueConfig(); + private boolean metrics = true; + private String language = "en_US"; + private String fallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing = "en_US"; + private Curves xpCurve = Curves.ADAPT_BALANCED; + private double playerXpPerSkillLevelUpBase = 489; + private double playerXpPerSkillLevelUpLevelMultiplier = 44; + private double powerPerLevel = 0.65; + private boolean hardcoreResetOnPlayerDeath = false; + private boolean hardcoreNoRefunds = false; + private boolean loginBonus = true; + private boolean welcomeMessage = true; + private boolean advancements = true; + private boolean useSql = false; + private boolean useRedis = false; + private int sqlSecondsCheckverify = 30; + private boolean useEnchantmentTableParticleForActiveEffects = true; + private boolean escClosesAllGuis = false; + private boolean guiBackButton = false; + private boolean customModels = true; + private boolean automaticGradients = false; + private int learnUnlearnButtonDelayTicks = 14; + private int maxRecipeListPrecaution = 25; + private boolean actionbarNotifyXp = true; + private boolean actionbarNotifyLevel = true; + private boolean unlearnAllButton = false; + private Effects effects = new Effects(); + private FarmPrevention farmPrevention = new FarmPrevention(); + private AdaptationXp adaptationXp = new AdaptationXp(); + private RedisConfig redis = new RedisConfig(); + private SqlSettings sql = new SqlSettings(); + private Protector protectorSupport = new Protector(); + private Map> adaptationUsageConflicts = defaultAdaptationUsageConflicts(); + private Map> protectionOverrides = Map.of( + "adaptation-name", Map.of( + "WorldGuard", true + ) + ); + + @Setter + private boolean verbose = false; + + public static AdaptConfig get() { + synchronized (CONFIG_LOCK) { + try { + if (config == null) { + config = loadConfig(new AdaptConfig(), true); } - } + } catch (Throwable e) { + e.printStackTrace(); + config = new AdaptConfig(); + } - private static AdaptConfig loadConfig(AdaptConfig fallback, boolean overwriteOnFailure) throws IOException { - File canonicalFile = Adapt.instance.getDataFile("adapt", "adapt.toml"); - File legacyFile = Adapt.instance.getDataFile("adapt", "adapt.json"); - return ConfigFileSupport.load( - canonicalFile, - legacyFile, - AdaptConfig.class, - fallback, - overwriteOnFailure, - "core-config", - "Created missing config [adapt/adapt.toml] from defaults." - ); + return config; } - - @Getter - public static class Protector { - private boolean worldguard = true; - private boolean griefdefender = true; - private boolean factionsClaim = false; - private boolean residence = true; - private boolean chestProtect = true; - private boolean griefprevention = true; - private boolean lockettePro = true; - } - - - @Getter - public static class SqlSettings { - private String host = "localhost"; - private int port = 1337; - private String database = "adapt"; - private String username = "user"; - private String password = "password"; - private int poolSize = 10; - private long connectionTimeout = 5000; + } + + public static boolean reload() { + synchronized (CONFIG_LOCK) { + try { + config = loadConfig(config == null ? new AdaptConfig() : config, false); + return true; + } catch (Throwable e) { + return false; + } } - - @Getter - public static class ValueConfig { - private double baseValue = 1; - private Map valueMutlipliers = defaultValueMultipliersOverrides(); - - private Map defaultValueMultipliersOverrides() { - Map f = new HashMap<>(); - f.put(Material.BLAZE_ROD.name(), 50D); - f.put(Material.ENDER_PEARL.name(), 75D); - f.put(Material.GHAST_TEAR.name(), 100D); - f.put(Material.LEATHER.name(), 1.5D); - f.put(Material.BEEF.name(), 1.125D); - f.put(Material.PORKCHOP.name(), 1.125D); - f.put(Material.EGG.name(), 1.335D); - f.put(Material.CHICKEN.name(), 1.13D); - f.put(Material.MUTTON.name(), 1.125D); - f.put(Material.WHEAT.name(), 1.25D); - f.put(Material.BEETROOT.name(), 1.25D); - f.put(Material.CARROT.name(), 1.25D); - f.put(Material.FLINT.name(), 1.35D); - f.put(Material.IRON_ORE.name(), 1.75D); - f.put(Material.DIAMOND_ORE.name(), 5D); - f.put(Material.GOLD_ORE.name(), 4D); - f.put(Material.LAPIS_ORE.name(), 3.5D); - f.put(Material.COAL_ORE.name(), 1.35D); - f.put(Material.REDSTONE_ORE.name(), 4.5D); - f.put(Material.NETHER_GOLD_ORE.name(), 4.5D); - f.put(Material.NETHER_QUARTZ_ORE.name(), 1.11D); - return f; - } - } - - @Getter - public static class FarmPrevention { - private boolean enabled = true; - private boolean perActivityTracking = true; - private long skillRecoveryMillis = 180000; - private long activityRecoveryMillis = 300000; - private long activityStateTtlMillis = 1800000; - private double skillBasePressure = 1.0; - private double skillXpPressure = 0.02; - private double skillDecayCurve = 14.0; - private double skillFloorMultiplier = 0.08; - private double activityBasePressure = 1.0; - private double activityXpPressure = 0.03; - private double activityDecayCurve = 9.0; - private double activityFloorMultiplier = 0.12; - private double crossSkillRecoveryFactor = 0.9; - } - - @Getter - public static class Effects { - private boolean particlesEnabled = true; - private boolean soundsEnabled = true; - private Map adaptationParticleOverrides = Map.of( - "adaptation-name", true - ); - private Map skillParticleOverrides = Map.of( - "skill-name", true - ); - } - - @Getter - public static class AdaptationXp { - private boolean usageBaselineEnabled = true; - private double usageBaselineXp = 0.8; - private double usageBaselineXpPerLevel = 0.18; - private long usageBaselineCooldownMillis = 12000; - } - - private static Map> defaultAdaptationUsageConflicts() { - return new HashMap<>(); + } + + private static AdaptConfig loadConfig(AdaptConfig fallback, boolean overwriteOnFailure) throws IOException { + File canonicalFile = Adapt.instance.getDataFile("adapt", "adapt.toml"); + File legacyFile = Adapt.instance.getDataFile("adapt", "adapt.json"); + return ConfigFileSupport.load( + canonicalFile, + legacyFile, + AdaptConfig.class, + fallback, + overwriteOnFailure, + "core-config", + "Created missing config [adapt/adapt.toml] from defaults." + ); + } + + private static Map> defaultAdaptationUsageConflicts() { + return new HashMap<>(); + } + + @Getter + public static class Protector { + private boolean worldguard = true; + private boolean griefdefender = true; + private boolean factionsClaim = false; + private boolean residence = true; + private boolean chestProtect = true; + private boolean griefprevention = true; + private boolean lockettePro = true; + } + + @Getter + public static class SqlSettings { + private String host = "localhost"; + private int port = 1337; + private String database = "adapt"; + private String username = "user"; + private String password = "password"; + private int poolSize = 10; + private long connectionTimeout = 5000; + } + + @Getter + public static class ValueConfig { + private double baseValue = 1; + private Map valueMutlipliers = defaultValueMultipliersOverrides(); + + private Map defaultValueMultipliersOverrides() { + Map f = new HashMap<>(); + f.put(Material.BLAZE_ROD.name(), 50D); + f.put(Material.ENDER_PEARL.name(), 75D); + f.put(Material.GHAST_TEAR.name(), 100D); + f.put(Material.LEATHER.name(), 1.5D); + f.put(Material.BEEF.name(), 1.125D); + f.put(Material.PORKCHOP.name(), 1.125D); + f.put(Material.EGG.name(), 1.335D); + f.put(Material.CHICKEN.name(), 1.13D); + f.put(Material.MUTTON.name(), 1.125D); + f.put(Material.WHEAT.name(), 1.25D); + f.put(Material.BEETROOT.name(), 1.25D); + f.put(Material.CARROT.name(), 1.25D); + f.put(Material.FLINT.name(), 1.35D); + f.put(Material.IRON_ORE.name(), 1.75D); + f.put(Material.DIAMOND_ORE.name(), 5D); + f.put(Material.GOLD_ORE.name(), 4D); + f.put(Material.LAPIS_ORE.name(), 3.5D); + f.put(Material.COAL_ORE.name(), 1.35D); + f.put(Material.REDSTONE_ORE.name(), 4.5D); + f.put(Material.NETHER_GOLD_ORE.name(), 4.5D); + f.put(Material.NETHER_QUARTZ_ORE.name(), 1.11D); + return f; } + } + + @Getter + public static class FarmPrevention { + private boolean enabled = true; + private boolean perActivityTracking = true; + private long skillRecoveryMillis = 180000; + private long activityRecoveryMillis = 300000; + private long activityStateTtlMillis = 1800000; + private double skillBasePressure = 1.0; + private double skillXpPressure = 0.02; + private double skillDecayCurve = 14.0; + private double skillFloorMultiplier = 0.08; + private double activityBasePressure = 1.0; + private double activityXpPressure = 0.03; + private double activityDecayCurve = 9.0; + private double activityFloorMultiplier = 0.12; + private double crossSkillRecoveryFactor = 0.9; + } + + @Getter + public static class Effects { + private boolean particlesEnabled = true; + private boolean soundsEnabled = true; + private Map adaptationParticleOverrides = Map.of( + "adaptation-name", true + ); + private Map skillParticleOverrides = Map.of( + "skill-name", true + ); + } + + @Getter + public static class AdaptationXp { + private boolean usageBaselineEnabled = true; + private double usageBaselineXp = 0.8; + private double usageBaselineXpPerLevel = 0.18; + private long usageBaselineCooldownMillis = 12000; + } } diff --git a/src/main/java/art/arcane/adapt/PapiExpansion.java b/src/main/java/art/arcane/adapt/PapiExpansion.java index fecce1da9..100bbc10d 100644 --- a/src/main/java/art/arcane/adapt/PapiExpansion.java +++ b/src/main/java/art/arcane/adapt/PapiExpansion.java @@ -1,6 +1,5 @@ package art.arcane.adapt; -import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.adaptation.Adaptation; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.world.PlayerAdaptation; @@ -17,406 +16,422 @@ import java.util.Locale; public class PapiExpansion extends PlaceholderExpansion { - private static final Locale LOCALE = Locale.ROOT; - - @Override - public @NotNull String getIdentifier() { - return Adapt.instance.getDescription().getName().toLowerCase(LOCALE); + private static final Locale LOCALE = Locale.ROOT; + + @Override + public @NotNull String getIdentifier() { + return Adapt.instance.getDescription().getName().toLowerCase(LOCALE); + } + + @Override + public @NotNull String getAuthor() { + return String.join(", ", Adapt.instance.getDescription().getAuthors()); + } + + @Override + public @NotNull String getVersion() { + return Adapt.instance.getDescription().getVersion(); + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + if (player == null || player.getUniqueId() == null || params == null || params.isBlank()) { + return ""; } - - @Override - public @NotNull String getAuthor() { - return String.join(", ", Adapt.instance.getDescription().getAuthors()); + if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + return ""; } - @Override - public @NotNull String getVersion() { - return Adapt.instance.getDescription().getVersion(); + PlayerData playerData = Adapt.instance.getAdaptServer().peekData(player.getUniqueId()); + if (playerData == null) { + return ""; } - @Override - public boolean persist() { - return true; + String[] args = params.split("_"); + if (args.length == 0) { + return ""; } - @Override - public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { - if (player == null || player.getUniqueId() == null || params == null || params.isBlank()) { - return ""; - } - if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - return ""; - } - - PlayerData playerData = Adapt.instance.getAdaptServer().peekData(player.getUniqueId()); - if (playerData == null) { - return ""; - } - - String[] args = params.split("_"); - if (args.length == 0) { - return ""; - } - - String root = args[0].toLowerCase(LOCALE); - if ("player".equals(root)) { - String attr = args.length > 1 ? join(args, 1) : ""; - return resolvePlayerAttribute(playerData, attr); - } - - if ("skill".equals(root)) { - if (args.length < 3) { - return "0"; - } - String skillId = args[1]; - String attr = join(args, 2); - Skill skill = resolveSkill(skillId); - if (skill == null) { - return "0"; - } - PlayerSkillLine line = playerData.getSkillLineNullable(skill.getName()); - return resolveSkillAttribute(playerData, skill, line, attr); - } - - if ("adaptation".equals(root)) { - if (args.length < 3) { - return "0"; - } - String adaptationId = args[1]; - String attr = join(args, 2); - Adaptation adaptation = resolveAdaptation(adaptationId); - if (adaptation == null) { - return "0"; - } - return resolveAdaptationAttribute(playerData, adaptation, attr); - } + String root = args[0].toLowerCase(LOCALE); + if ("player".equals(root)) { + String attr = args.length > 1 ? join(args, 1) : ""; + return resolvePlayerAttribute(playerData, attr); + } - if ("skills".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { - return String.valueOf(countSkills()); - } + if ("skill".equals(root)) { + if (args.length < 3) { + return "0"; + } + String skillId = args[1]; + String attr = join(args, 2); + Skill skill = resolveSkill(skillId); + if (skill == null) { + return "0"; + } + PlayerSkillLine line = playerData.getSkillLineNullable(skill.getName()); + return resolveSkillAttribute(playerData, skill, line, attr); + } - if ("adaptations".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { - return String.valueOf(countAdaptations()); - } + if ("adaptation".equals(root)) { + if (args.length < 3) { + return "0"; + } + String adaptationId = args[1]; + String attr = join(args, 2); + Adaptation adaptation = resolveAdaptation(adaptationId); + if (adaptation == null) { + return "0"; + } + return resolveAdaptationAttribute(playerData, adaptation, attr); + } - if (args.length >= 2) { - Skill directSkill = resolveSkill(args[0]); - if (directSkill != null) { - String attr = join(args, 1); - PlayerSkillLine line = playerData.getSkillLineNullable(directSkill.getName()); - return resolveSkillAttribute(playerData, directSkill, line, attr); - } - - Adaptation directAdaptation = resolveAdaptation(args[0]); - if (directAdaptation != null) { - String attr = join(args, 1); - return resolveAdaptationAttribute(playerData, directAdaptation, attr); - } - } + if ("skills".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { + return String.valueOf(countSkills()); + } - return null; - } - - private String resolvePlayerAttribute(PlayerData playerData, String rawAttr) { - String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); - return switch (attr) { - case "level" -> String.valueOf(playerData.getLevel()); - case "multiplier" -> format2(playerData.getMultiplier()); - case "availablepower" -> String.valueOf(playerData.getAvailablePower()); - case "maxpower" -> String.valueOf(playerData.getMaxPower()); - case "usedpower" -> String.valueOf(playerData.getUsedPower()); - case "wisdom" -> String.valueOf(playerData.getWisdom()); - case "masterxp" -> format2(playerData.getMasterXp()); - case "seenthings" -> String.valueOf(countSeenThings(playerData)); - case "skills", "skillcount" -> String.valueOf(countSkills()); - case "knownskills" -> String.valueOf(countKnownSkills(playerData)); - case "adaptations", "adaptationcount" -> String.valueOf(countAdaptations()); - case "learnedadaptations" -> String.valueOf(countLearnedAdaptations(playerData)); - default -> "0"; - }; - } - - private String resolveSkillAttribute(PlayerData playerData, Skill skill, PlayerSkillLine line, String rawAttr) { - String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); - int currentLevel = line == null ? 0 : line.getLevel(); - - if (attr.startsWith("can_claim_") || attr.startsWith("has_level_")) { - Integer targetLevel = parseTrailingInt(attr); - if (targetLevel == null) { - return "false"; - } - return String.valueOf(targetLevel >= 0 && targetLevel <= 100 && currentLevel >= targetLevel); - } + if ("adaptations".equals(root) && args.length > 1 && "count".equalsIgnoreCase(args[1])) { + return String.valueOf(countAdaptations()); + } - int learnedAdaptations = 0; - if (line != null) { - for (PlayerAdaptation adaptation : line.getAdaptations().values()) { - if (adaptation != null && adaptation.getLevel() > 0) { - learnedAdaptations++; - } - } - } + if (args.length >= 2) { + Skill directSkill = resolveSkill(args[0]); + if (directSkill != null) { + String attr = join(args, 1); + PlayerSkillLine line = playerData.getSkillLineNullable(directSkill.getName()); + return resolveSkillAttribute(playerData, directSkill, line, attr); + } + + Adaptation directAdaptation = resolveAdaptation(args[0]); + if (directAdaptation != null) { + String attr = join(args, 1); + return resolveAdaptationAttribute(playerData, directAdaptation, attr); + } + } - return switch (attr) { - case "id" -> skill.getName(); - case "enabled" -> String.valueOf(skill.isEnabled()); - case "name" -> Localizer.dLocalize("skill." + skill.getName() + ".name"); - case "display_name", "displayname" -> skill.getDisplayName(); - case "short_name", "shortname" -> skill.getShortName(); - case "level" -> String.valueOf(currentLevel); - case "knowledge" -> String.valueOf(line == null ? 0 : line.getKnowledge()); - case "xp" -> format2(line == null ? 0 : line.getXp()); - case "freshness" -> format2(line == null ? 0 : line.getFreshness()); - case "multiplier" -> format2(line == null ? 0 : line.getMultiplier()); - case "absolute_level", "absolutelevel" -> format4(line == null ? 0 : line.getAbsoluteLevel()); - case "progress" -> format4(line == null ? 0 : line.getLevelProgress()); - case "progress_percent", "progresspercent" -> format2((line == null ? 0 : line.getLevelProgress()) * 100D); - case "xp_to_next", "xptonext", "xp_needed", "xpneeded" -> format2(line == null ? 0 : XP.getXpUntilLevelUp(line.getXp())); - case "current_level_xp", "currentlevelxp" -> format2(XP.getXpForLevel(currentLevel)); - case "next_level_xp", "nextlevelxp" -> format2(XP.getXpForLevel(currentLevel + 1)); - case "adaptation_count", "adaptationcount" -> String.valueOf(skill.getAdaptations().size()); - case "learned_adaptations", "learnedadaptations" -> String.valueOf(learnedAdaptations); - case "can_use" -> String.valueOf(currentLevel > 0); - case "line" -> skill.getName(); - case "maxlevel", "max_level" -> String.valueOf(AdaptConfig.get().experienceMaxLevel); - default -> "0"; - }; - } - - private String resolveAdaptationAttribute(PlayerData playerData, Adaptation adaptation, String rawAttr) { - String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); - PlayerSkillLine line = playerData.getSkillLineNullable(adaptation.getSkill().getName()); - int currentLevel = line == null ? 0 : line.getAdaptationLevel(adaptation.getName()); - - if (attr.startsWith("can_claim_")) { - Integer target = parseTrailingInt(attr); - if (target == null) { - return "false"; - } - int targetLevel = clampAdaptationTarget(adaptation, target); - return String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, targetLevel)); - } + return null; + } + + private String resolvePlayerAttribute(PlayerData playerData, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + return switch (attr) { + case "level" -> String.valueOf(playerData.getLevel()); + case "multiplier" -> format2(playerData.getMultiplier()); + case "availablepower" -> String.valueOf(playerData.getAvailablePower()); + case "maxpower" -> String.valueOf(playerData.getMaxPower()); + case "usedpower" -> String.valueOf(playerData.getUsedPower()); + case "wisdom" -> String.valueOf(playerData.getWisdom()); + case "masterxp" -> format2(playerData.getMasterXp()); + case "seenthings" -> String.valueOf(countSeenThings(playerData)); + case "skills", "skillcount" -> String.valueOf(countSkills()); + case "knownskills" -> String.valueOf(countKnownSkills(playerData)); + case "adaptations", "adaptationcount" -> + String.valueOf(countAdaptations()); + case "learnedadaptations" -> + String.valueOf(countLearnedAdaptations(playerData)); + default -> "0"; + }; + } + + private String resolveSkillAttribute(PlayerData playerData, Skill skill, PlayerSkillLine line, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + int currentLevel = line == null ? 0 : line.getLevel(); + + if (attr.startsWith("can_claim_") || attr.startsWith("has_level_")) { + Integer targetLevel = parseTrailingInt(attr); + if (targetLevel == null) { + return "false"; + } + return String.valueOf(targetLevel >= 0 && targetLevel <= 100 && currentLevel >= targetLevel); + } - if (attr.startsWith("cost_to_") || attr.startsWith("knowledge_to_")) { - Integer target = parseTrailingInt(attr); - if (target == null) { - return "0"; - } - int targetLevel = clampAdaptationTarget(adaptation, target); - return String.valueOf(adaptation.getCostFor(targetLevel, currentLevel)); + int learnedAdaptations = 0; + if (line != null) { + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation != null && adaptation.getLevel() > 0) { + learnedAdaptations++; } + } + } - if (attr.startsWith("power_to_")) { - Integer target = parseTrailingInt(attr); - if (target == null) { - return "0"; - } - int targetLevel = clampAdaptationTarget(adaptation, target); - int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); - return String.valueOf(Math.max(0, powerCost)); - } + return switch (attr) { + case "id" -> skill.getName(); + case "enabled" -> String.valueOf(skill.isEnabled()); + case "name" -> Localizer.dLocalize("skill." + skill.getName() + ".name"); + case "display_name", "displayname" -> skill.getDisplayName(); + case "short_name", "shortname" -> skill.getShortName(); + case "level" -> String.valueOf(currentLevel); + case "knowledge" -> + String.valueOf(line == null ? 0 : line.getKnowledge()); + case "xp" -> format2(line == null ? 0 : line.getXp()); + case "freshness" -> format2(line == null ? 0 : line.getFreshness()); + case "multiplier" -> format2(line == null ? 0 : line.getMultiplier()); + case "absolute_level", "absolutelevel" -> + format4(line == null ? 0 : line.getAbsoluteLevel()); + case "progress" -> format4(line == null ? 0 : line.getLevelProgress()); + case "progress_percent", "progresspercent" -> + format2((line == null ? 0 : line.getLevelProgress()) * 100D); + case "xp_to_next", "xptonext", "xp_needed", "xpneeded" -> + format2(line == null ? 0 : XP.getXpUntilLevelUp(line.getXp())); + case "current_level_xp", "currentlevelxp" -> + format2(XP.getXpForLevel(currentLevel)); + case "next_level_xp", "nextlevelxp" -> + format2(XP.getXpForLevel(currentLevel + 1)); + case "adaptation_count", "adaptationcount" -> + String.valueOf(skill.getAdaptations().size()); + case "learned_adaptations", "learnedadaptations" -> + String.valueOf(learnedAdaptations); + case "can_use" -> String.valueOf(currentLevel > 0); + case "line" -> skill.getName(); + case "maxlevel", "max_level" -> + String.valueOf(AdaptConfig.get().experienceMaxLevel); + default -> "0"; + }; + } + + private String resolveAdaptationAttribute(PlayerData playerData, Adaptation adaptation, String rawAttr) { + String attr = rawAttr == null ? "" : rawAttr.toLowerCase(LOCALE); + PlayerSkillLine line = playerData.getSkillLineNullable(adaptation.getSkill().getName()); + int currentLevel = line == null ? 0 : line.getAdaptationLevel(adaptation.getName()); + + if (attr.startsWith("can_claim_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "false"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, targetLevel)); + } - if (attr.startsWith("refund_to_")) { - Integer target = parseTrailingInt(attr); - if (target == null) { - return "0"; - } - int targetLevel = clampAdaptationTarget(adaptation, target); - return String.valueOf(adaptation.getRefundCostFor(targetLevel, currentLevel)); - } + if (attr.startsWith("cost_to_") || attr.startsWith("knowledge_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(adaptation.getCostFor(targetLevel, currentLevel)); + } - int nextLevel = Math.min(adaptation.getMaxLevel(), currentLevel + 1); - return switch (attr) { - case "id" -> adaptation.getName(); - case "level" -> String.valueOf(currentLevel); - case "maxlevel", "max_level" -> String.valueOf(adaptation.getMaxLevel()); - case "name", "display_name", "displayname" -> adaptation.getDisplayName(); - case "raw_name", "rawname" -> adaptation.getName(); - case "skill", "skill_id", "skillid" -> adaptation.getSkill().getName(); - case "enabled" -> String.valueOf(adaptation.isEnabled()); - case "learned" -> String.valueOf(currentLevel > 0); - case "can_use" -> String.valueOf(currentLevel > 0 && adaptation.isEnabled() && adaptation.getSkill().isEnabled()); - case "cost_next", "costnext", "knowledge_next", "knowledgenext" -> String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : adaptation.getCostFor(nextLevel, currentLevel)); - case "power_next", "powernext" -> String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : Math.max(0, adaptation.getPowerCostFor(nextLevel, currentLevel))); - case "refund_next", "refundnext" -> String.valueOf(currentLevel <= 0 ? 0 : adaptation.getRefundCostFor(Math.max(0, currentLevel - 1), currentLevel)); - case "can_claim_next", "canclaimnext" -> String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, nextLevel)); - default -> "0"; - }; - } - - private boolean canClaimTarget(PlayerData playerData, PlayerSkillLine line, Adaptation adaptation, int currentLevel, int targetLevel) { - if (targetLevel == currentLevel) { - return true; - } + if (attr.startsWith("power_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); + return String.valueOf(Math.max(0, powerCost)); + } - if (targetLevel > currentLevel) { - int powerCost = Math.max(0, adaptation.getPowerCostFor(targetLevel, currentLevel)); - int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); - if (!playerData.hasPowerAvailable(powerCost)) { - return false; - } - return line != null && line.getKnowledge() >= knowledgeCost; - } + if (attr.startsWith("refund_to_")) { + Integer target = parseTrailingInt(attr); + if (target == null) { + return "0"; + } + int targetLevel = clampAdaptationTarget(adaptation, target); + return String.valueOf(adaptation.getRefundCostFor(targetLevel, currentLevel)); + } - if (adaptation.isPermanent()) { - return false; - } + int nextLevel = Math.min(adaptation.getMaxLevel(), currentLevel + 1); + return switch (attr) { + case "id" -> adaptation.getName(); + case "level" -> String.valueOf(currentLevel); + case "maxlevel", "max_level" -> String.valueOf(adaptation.getMaxLevel()); + case "name", "display_name", "displayname" -> adaptation.getDisplayName(); + case "raw_name", "rawname" -> adaptation.getName(); + case "skill", "skill_id", "skillid" -> adaptation.getSkill().getName(); + case "enabled" -> String.valueOf(adaptation.isEnabled()); + case "learned" -> String.valueOf(currentLevel > 0); + case "can_use" -> + String.valueOf(currentLevel > 0 && adaptation.isEnabled() && adaptation.getSkill().isEnabled()); + case "cost_next", "costnext", "knowledge_next", "knowledgenext" -> + String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : adaptation.getCostFor(nextLevel, currentLevel)); + case "power_next", "powernext" -> + String.valueOf(currentLevel >= adaptation.getMaxLevel() ? 0 : Math.max(0, adaptation.getPowerCostFor(nextLevel, currentLevel))); + case "refund_next", "refundnext" -> + String.valueOf(currentLevel <= 0 ? 0 : adaptation.getRefundCostFor(Math.max(0, currentLevel - 1), currentLevel)); + case "can_claim_next", "canclaimnext" -> + String.valueOf(canClaimTarget(playerData, line, adaptation, currentLevel, nextLevel)); + default -> "0"; + }; + } + + private boolean canClaimTarget(PlayerData playerData, PlayerSkillLine line, Adaptation adaptation, int currentLevel, int targetLevel) { + if (targetLevel == currentLevel) { + return true; + } - return currentLevel > 0; + if (targetLevel > currentLevel) { + int powerCost = Math.max(0, adaptation.getPowerCostFor(targetLevel, currentLevel)); + int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); + if (!playerData.hasPowerAvailable(powerCost)) { + return false; + } + return line != null && line.getKnowledge() >= knowledgeCost; } - private int clampAdaptationTarget(Adaptation adaptation, int requested) { - int clamped = Math.max(0, Math.min(requested, 100)); - return Math.min(clamped, adaptation.getMaxLevel()); + if (adaptation.isPermanent()) { + return false; } - private Skill resolveSkill(String rawSkillId) { - if (rawSkillId == null || rawSkillId.isBlank()) { - return null; - } + return currentLevel > 0; + } - String skillId = rawSkillId.trim(); - Skill direct = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillId); - if (direct != null) { - return direct; - } + private int clampAdaptationTarget(Adaptation adaptation, int requested) { + int clamped = Math.max(0, Math.min(requested, 100)); + return Math.min(clamped, adaptation.getMaxLevel()); + } - List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : allSkills) { - if (skill.getName().equalsIgnoreCase(skillId)) { - return skill; - } - } + private Skill resolveSkill(String rawSkillId) { + if (rawSkillId == null || rawSkillId.isBlank()) { + return null; + } - return null; + String skillId = rawSkillId.trim(); + Skill direct = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillId); + if (direct != null) { + return direct; } - private Adaptation resolveAdaptation(String rawAdaptationId) { - if (rawAdaptationId == null || rawAdaptationId.isBlank()) { - return null; - } + List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : allSkills) { + if (skill.getName().equalsIgnoreCase(skillId)) { + return skill; + } + } - String adaptationId = rawAdaptationId.trim().toLowerCase(LOCALE); - List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : allSkills) { - for (Adaptation adaptation : skill.getAdaptations()) { - if (matchesAdaptationIdentifier(adaptation, adaptationId)) { - return adaptation; - } - } - } + return null; + } - return null; + private Adaptation resolveAdaptation(String rawAdaptationId) { + if (rawAdaptationId == null || rawAdaptationId.isBlank()) { + return null; } - private boolean matchesAdaptationIdentifier(Adaptation adaptation, String normalizedTarget) { - String name = adaptation.getName(); - if (name != null && name.equalsIgnoreCase(normalizedTarget)) { - return true; - } - - String fullId = adaptation.getId(); - if (fullId != null && fullId.equalsIgnoreCase(normalizedTarget)) { - return true; + String adaptationId = rawAdaptationId.trim().toLowerCase(LOCALE); + List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : allSkills) { + for (Adaptation adaptation : skill.getAdaptations()) { + if (matchesAdaptationIdentifier(adaptation, adaptationId)) { + return adaptation; } + } + } - if (fullId != null && fullId.length() > 37) { - String legacyId = fullId.substring(37); - if (legacyId.equalsIgnoreCase(normalizedTarget)) { - return true; - } - } + return null; + } - String scoped = adaptation.getSkill().getName() + ":" + adaptation.getName(); - return scoped.equalsIgnoreCase(normalizedTarget); + private boolean matchesAdaptationIdentifier(Adaptation adaptation, String normalizedTarget) { + String name = adaptation.getName(); + if (name != null && name.equalsIgnoreCase(normalizedTarget)) { + return true; } - private int countSeenThings(PlayerData playerData) { - int total = 0; - total += playerData.getSeenBlocks().getSeen().size(); - total += playerData.getSeenBiomes().getSeen().size(); - total += playerData.getSeenEnchants().getSeen().size(); - total += playerData.getSeenEnvironments().getSeen().size(); - total += playerData.getSeenFoods().getSeen().size(); - total += playerData.getSeenItems().getSeen().size(); - total += playerData.getSeenMobs().getSeen().size(); - total += playerData.getSeenPeople().getSeen().size(); - total += playerData.getSeenPotionEffects().getSeen().size(); - total += playerData.getSeenRecipes().getSeen().size(); - total += playerData.getSeenWorlds().getSeen().size(); - return total; + String fullId = adaptation.getId(); + if (fullId != null && fullId.equalsIgnoreCase(normalizedTarget)) { + return true; } - private int countSkills() { - return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills().size(); + if (fullId != null && fullId.length() > 37) { + String legacyId = fullId.substring(37); + if (legacyId.equalsIgnoreCase(normalizedTarget)) { + return true; + } } - private int countKnownSkills(PlayerData playerData) { - int total = 0; - for (PlayerSkillLine line : playerData.getSkillLines().values()) { - if (line != null && line.getLevel() > 0) { - total++; - } - } - return total; + String scoped = adaptation.getSkill().getName() + ":" + adaptation.getName(); + return scoped.equalsIgnoreCase(normalizedTarget); + } + + private int countSeenThings(PlayerData playerData) { + int total = 0; + total += playerData.getSeenBlocks().getSeen().size(); + total += playerData.getSeenBiomes().getSeen().size(); + total += playerData.getSeenEnchants().getSeen().size(); + total += playerData.getSeenEnvironments().getSeen().size(); + total += playerData.getSeenFoods().getSeen().size(); + total += playerData.getSeenItems().getSeen().size(); + total += playerData.getSeenMobs().getSeen().size(); + total += playerData.getSeenPeople().getSeen().size(); + total += playerData.getSeenPotionEffects().getSeen().size(); + total += playerData.getSeenRecipes().getSeen().size(); + total += playerData.getSeenWorlds().getSeen().size(); + return total; + } + + private int countSkills() { + return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills().size(); + } + + private int countKnownSkills(PlayerData playerData) { + int total = 0; + for (PlayerSkillLine line : playerData.getSkillLines().values()) { + if (line != null && line.getLevel() > 0) { + total++; + } } - - private int countAdaptations() { - int total = 0; - List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : skills) { - total += skill.getAdaptations().size(); - } - return total; - } - - private int countLearnedAdaptations(PlayerData playerData) { - int total = 0; - for (PlayerSkillLine line : playerData.getSkillLines().values()) { - if (line == null) { - continue; - } - for (PlayerAdaptation adaptation : line.getAdaptations().values()) { - if (adaptation != null && adaptation.getLevel() > 0) { - total++; - } - } - } - return total; + return total; + } + + private int countAdaptations() { + int total = 0; + List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); + for (Skill skill : skills) { + total += skill.getAdaptations().size(); } - - private Integer parseTrailingInt(String attr) { - int index = attr.lastIndexOf('_'); - if (index == -1 || index >= attr.length() - 1) { - return null; - } - - try { - return Integer.parseInt(attr.substring(index + 1)); - } catch (NumberFormatException ignored) { - return null; - } + return total; + } + + private int countLearnedAdaptations(PlayerData playerData) { + int total = 0; + for (PlayerSkillLine line : playerData.getSkillLines().values()) { + if (line == null) { + continue; + } + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation != null && adaptation.getLevel() > 0) { + total++; + } + } } + return total; + } - private String join(String[] args, int start) { - if (args == null || start >= args.length) { - return ""; - } - StringBuilder builder = new StringBuilder(); - for (int i = start; i < args.length; i++) { - if (builder.length() > 0) { - builder.append('_'); - } - builder.append(args[i]); - } - return builder.toString(); + private Integer parseTrailingInt(String attr) { + int index = attr.lastIndexOf('_'); + if (index == -1 || index >= attr.length() - 1) { + return null; } - private String format2(double value) { - return String.format(LOCALE, "%.2f", value); + try { + return Integer.parseInt(attr.substring(index + 1)); + } catch (NumberFormatException ignored) { + return null; } + } - private String format4(double value) { - return String.format(LOCALE, "%.4f", value); + private String join(String[] args, int start) { + if (args == null || start >= args.length) { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (int i = start; i < args.length; i++) { + if (builder.length() > 0) { + builder.append('_'); + } + builder.append(args[i]); } + return builder.toString(); + } + + private String format2(double value) { + return String.format(LOCALE, "%.2f", value); + } + + private String format4(double value) { + return String.format(LOCALE, "%.4f", value); + } } diff --git a/src/main/java/art/arcane/adapt/api/Component.java b/src/main/java/art/arcane/adapt/api/Component.java index 7a07873f4..8f3d3ca40 100644 --- a/src/main/java/art/arcane/adapt/api/Component.java +++ b/src/main/java/art/arcane/adapt/api/Component.java @@ -59,1058 +59,1084 @@ import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public interface Component { - Set NON_ADAPTABLE_DAMAGE_CAUSES = Set.of( - EntityDamageEvent.DamageCause.VOID, - EntityDamageEvent.DamageCause.LAVA, - EntityDamageEvent.DamageCause.HOT_FLOOR, - EntityDamageEvent.DamageCause.CRAMMING, - EntityDamageEvent.DamageCause.MELTING, - EntityDamageEvent.DamageCause.SUFFOCATION, - EntityDamageEvent.DamageCause.SUICIDE, - EntityDamageEvent.DamageCause.WITHER, - EntityDamageEvent.DamageCause.FLY_INTO_WALL, - EntityDamageEvent.DamageCause.FALL, - EntityDamageEvent.DamageCause.SONIC_BOOM, - EntityDamageEvent.DamageCause.THORNS - ); - - default boolean areParticlesEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isParticlesEnabled(); - } - - default boolean areSoundsEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isSoundsEnabled(); - } - - default void wisdom(Player p, long w) { - XP.wisdom(p, w); - } - - /** - * Attempts to "damage" an item. - * 1. If the item is null, null is returned - * 2. If the item doesnt have durability, (damage) amount will be consumed from the stack, null will be returned if - * more consumed than amount - * 3. If the item has durability, the damage will be consuemd and return the item affected, OR null if it broke - * - * @param item the item (tool) - * @param damage the damage to cause - * @return the damaged item or null if destroyed - */ - default ItemStack damage(ItemStack item, int damage) { - if (item == null) { - return null; - } + Set NON_ADAPTABLE_DAMAGE_CAUSES = Set.of( + EntityDamageEvent.DamageCause.VOID, + EntityDamageEvent.DamageCause.LAVA, + EntityDamageEvent.DamageCause.HOT_FLOOR, + EntityDamageEvent.DamageCause.CRAMMING, + EntityDamageEvent.DamageCause.MELTING, + EntityDamageEvent.DamageCause.SUFFOCATION, + EntityDamageEvent.DamageCause.SUICIDE, + EntityDamageEvent.DamageCause.WITHER, + EntityDamageEvent.DamageCause.FLY_INTO_WALL, + EntityDamageEvent.DamageCause.FALL, + EntityDamageEvent.DamageCause.SONIC_BOOM, + EntityDamageEvent.DamageCause.THORNS + ); + + default boolean areParticlesEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isParticlesEnabled(); + } + + default boolean areSoundsEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isSoundsEnabled(); + } + + default void wisdom(Player p, long w) { + XP.wisdom(p, w); + } + + /** + * Attempts to "damage" an item. 1. If the item is null, null is returned 2. + * If the item doesnt have durability, (damage) amount will be consumed from + * the stack, null will be returned if more consumed than amount 3. If the + * item has durability, the damage will be consuemd and return the item + * affected, OR null if it broke + * + * @param item the item (tool) + * @param damage the damage to cause + * @return the damaged item or null if destroyed + */ + default ItemStack damage(ItemStack item, int damage) { + if (item == null) { + return null; + } - if (item.getItemMeta() == null) { - if (item.getAmount() == 1) { - return null; - } + if (item.getItemMeta() == null) { + if (item.getAmount() == 1) { + return null; + } - item = item.clone(); - item.setAmount(item.getAmount() - 1); - return item; - } + item = item.clone(); + item.setAmount(item.getAmount() - 1); + return item; + } - if (item.getItemMeta() instanceof Damageable d) { - if (d.getDamage() + 1 > item.getType().getMaxDurability()) { - return null; - } + if (item.getItemMeta() instanceof Damageable d) { + if (d.getDamage() + 1 > item.getType().getMaxDurability()) { + return null; + } - d.setDamage(d.getDamage() + 1); - item = item.clone(); - item.setItemMeta(d); - return item; - } else { - if (item.getAmount() == 1) { - return null; - } + d.setDamage(d.getDamage() + 1); + item = item.clone(); + item.setItemMeta(d); + return item; + } else { + if (item.getAmount() == 1) { + return null; + } - item = item.clone(); - item.setAmount(item.getAmount() - 1); + item = item.clone(); + item.setAmount(item.getAmount() - 1); - return item; - } + return item; } + } - default void decrementItemstack(ItemStack hand, Player p) { - if (hand.getAmount() > 1) { - hand.setAmount(hand.getAmount() - 1); - } else { - p.getInventory().setItemInMainHand(null); - } + default void decrementItemstack(ItemStack hand, Player p) { + if (hand.getAmount() > 1) { + hand.setAmount(hand.getAmount() - 1); + } else { + p.getInventory().setItemInMainHand(null); } + } + + default double getArmorValue(Player player) { + org.bukkit.inventory.PlayerInventory inv = player.getInventory(); + ItemStack boots = inv.getBoots(); + ItemStack helmet = inv.getHelmet(); + ItemStack chest = inv.getChestplate(); + ItemStack pants = inv.getLeggings(); + double armorValue = 0.0; + if (helmet == null) armorValue = armorValue + 0.0; + else if (Bukkit.getServer().getPluginManager().getPlugin("MagicCosmetics") != null && MagicAPI.hasEquipCosmetic(player, CosmeticType.HAT)) { + armorValue = armorValue + 0; + } else if (helmet.getType() == Material.LEATHER_HELMET) + armorValue = armorValue + 0.04; + else if (helmet.getType() == Material.GOLDEN_HELMET) + armorValue = armorValue + 0.08; + else if (helmet.getType() == Material.TURTLE_HELMET) + armorValue = armorValue + 0.08; + else if (helmet.getType() == Material.CHAINMAIL_HELMET) + armorValue = armorValue + 0.08; + else if (helmet.getType() == Material.IRON_HELMET) + armorValue = armorValue + 0.08; + else if (helmet.getType() == Material.DIAMOND_HELMET) + armorValue = armorValue + 0.12; + else if (helmet.getType() == Material.NETHERITE_HELMET) + armorValue = armorValue + 0.12; + // + if (boots == null) armorValue = armorValue + 0.0; + else if (boots.getType() == Material.LEATHER_BOOTS) + armorValue = armorValue + 0.04; + else if (boots.getType() == Material.GOLDEN_BOOTS) + armorValue = armorValue + 0.04; + else if (boots.getType() == Material.CHAINMAIL_BOOTS) + armorValue = armorValue + 0.04; + else if (boots.getType() == Material.IRON_BOOTS) + armorValue = armorValue + 0.08; + else if (boots.getType() == Material.DIAMOND_BOOTS) + armorValue = armorValue + 0.12; + else if (boots.getType() == Material.NETHERITE_BOOTS) + armorValue = armorValue + 0.12; + // + if (pants == null) armorValue = armorValue + 0.0; + else if (pants.getType() == Material.LEATHER_LEGGINGS) + armorValue = armorValue + 0.08; + else if (pants.getType() == Material.GOLDEN_LEGGINGS) + armorValue = armorValue + 0.12; + else if (pants.getType() == Material.CHAINMAIL_LEGGINGS) + armorValue = armorValue + 0.16; + else if (pants.getType() == Material.IRON_LEGGINGS) + armorValue = armorValue + 0.20; + else if (pants.getType() == Material.DIAMOND_LEGGINGS) + armorValue = armorValue + 0.24; + else if (pants.getType() == Material.NETHERITE_LEGGINGS) + armorValue = armorValue + 0.24; + // + if (chest == null) armorValue = armorValue + 0.0; + else if (Bukkit.getServer().getPluginManager().getPlugin("MagicCosmetics") != null && MagicAPI.hasEquipCosmetic(player, CosmeticType.BAG)) { + armorValue = armorValue + 0; + } else if (chest.getType() == Material.LEATHER_CHESTPLATE) + armorValue = armorValue + 0.12; + else if (chest.getType() == Material.GOLDEN_CHESTPLATE) + armorValue = armorValue + 0.20; + else if (chest.getType() == Material.CHAINMAIL_CHESTPLATE) + armorValue = armorValue + 0.20; + else if (chest.getType() == Material.IRON_CHESTPLATE) + armorValue = armorValue + 0.24; + else if (chest.getType() == Material.DIAMOND_CHESTPLATE) + armorValue = armorValue + 0.32; + else if (chest.getType() == Material.NETHERITE_CHESTPLATE) + armorValue = armorValue + 0.32; + return armorValue; + } + + default boolean isAdaptableDamageCause(EntityDamageEvent event) { + return !NON_ADAPTABLE_DAMAGE_CAUSES.contains(event.getCause()); + } + + default void addPotionStacks(Player p, PotionEffectType potionEffect, int amplifier, int duration, boolean overlap) { + List activeEffects = new ArrayList<>(p.getActivePotionEffects()); + SoundPlayer sp = SoundPlayer.of(p); + for (PotionEffect activeEffect : activeEffects) { + if (activeEffect.getType() == potionEffect) { + if (!overlap) { + return; // don't modify the effect if overlap is false + } + // modify the effect if overlap is true + int newDuration = activeEffect.getDuration() + duration; + int newAmplifier = Math.max(activeEffect.getAmplifier(), amplifier); + p.removePotionEffect(potionEffect); + p.addPotionEffect(new PotionEffect(potionEffect, newDuration, newAmplifier)); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.25f); + return; + } + } + // if we didn't find an existing effect, add a new one + J.s(() -> { + p.addPotionEffect(new PotionEffect(potionEffect, duration, amplifier)); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.25f); + }, 1); - default double getArmorValue(Player player) { - org.bukkit.inventory.PlayerInventory inv = player.getInventory(); - ItemStack boots = inv.getBoots(); - ItemStack helmet = inv.getHelmet(); - ItemStack chest = inv.getChestplate(); - ItemStack pants = inv.getLeggings(); - double armorValue = 0.0; - if (helmet == null) armorValue = armorValue + 0.0; - else if (Bukkit.getServer().getPluginManager().getPlugin("MagicCosmetics") != null && MagicAPI.hasEquipCosmetic(player, CosmeticType.HAT)) { - armorValue = armorValue + 0; - } else if (helmet.getType() == Material.LEATHER_HELMET) armorValue = armorValue + 0.04; - else if (helmet.getType() == Material.GOLDEN_HELMET) armorValue = armorValue + 0.08; - else if (helmet.getType() == Material.TURTLE_HELMET) armorValue = armorValue + 0.08; - else if (helmet.getType() == Material.CHAINMAIL_HELMET) armorValue = armorValue + 0.08; - else if (helmet.getType() == Material.IRON_HELMET) armorValue = armorValue + 0.08; - else if (helmet.getType() == Material.DIAMOND_HELMET) armorValue = armorValue + 0.12; - else if (helmet.getType() == Material.NETHERITE_HELMET) armorValue = armorValue + 0.12; - // - if (boots == null) armorValue = armorValue + 0.0; - else if (boots.getType() == Material.LEATHER_BOOTS) armorValue = armorValue + 0.04; - else if (boots.getType() == Material.GOLDEN_BOOTS) armorValue = armorValue + 0.04; - else if (boots.getType() == Material.CHAINMAIL_BOOTS) armorValue = armorValue + 0.04; - else if (boots.getType() == Material.IRON_BOOTS) armorValue = armorValue + 0.08; - else if (boots.getType() == Material.DIAMOND_BOOTS) armorValue = armorValue + 0.12; - else if (boots.getType() == Material.NETHERITE_BOOTS) armorValue = armorValue + 0.12; - // - if (pants == null) armorValue = armorValue + 0.0; - else if (pants.getType() == Material.LEATHER_LEGGINGS) armorValue = armorValue + 0.08; - else if (pants.getType() == Material.GOLDEN_LEGGINGS) armorValue = armorValue + 0.12; - else if (pants.getType() == Material.CHAINMAIL_LEGGINGS) armorValue = armorValue + 0.16; - else if (pants.getType() == Material.IRON_LEGGINGS) armorValue = armorValue + 0.20; - else if (pants.getType() == Material.DIAMOND_LEGGINGS) armorValue = armorValue + 0.24; - else if (pants.getType() == Material.NETHERITE_LEGGINGS) armorValue = armorValue + 0.24; - // - if (chest == null) armorValue = armorValue + 0.0; - else if (Bukkit.getServer().getPluginManager().getPlugin("MagicCosmetics") != null && MagicAPI.hasEquipCosmetic(player, CosmeticType.BAG)) { - armorValue = armorValue + 0; - } else if (chest.getType() == Material.LEATHER_CHESTPLATE) armorValue = armorValue + 0.12; - else if (chest.getType() == Material.GOLDEN_CHESTPLATE) armorValue = armorValue + 0.20; - else if (chest.getType() == Material.CHAINMAIL_CHESTPLATE) armorValue = armorValue + 0.20; - else if (chest.getType() == Material.IRON_CHESTPLATE) armorValue = armorValue + 0.24; - else if (chest.getType() == Material.DIAMOND_CHESTPLATE) armorValue = armorValue + 0.32; - else if (chest.getType() == Material.NETHERITE_CHESTPLATE) armorValue = armorValue + 0.32; - return armorValue; - } - - default boolean isAdaptableDamageCause(EntityDamageEvent event) { - return !NON_ADAPTABLE_DAMAGE_CAUSES.contains(event.getCause()); - } - - default void addPotionStacks(Player p, PotionEffectType potionEffect, int amplifier, int duration, boolean overlap) { - List activeEffects = new ArrayList<>(p.getActivePotionEffects()); - SoundPlayer sp = SoundPlayer.of(p); - for (PotionEffect activeEffect : activeEffects) { - if (activeEffect.getType() == potionEffect) { - if (!overlap) { - return; // don't modify the effect if overlap is false - } - // modify the effect if overlap is true - int newDuration = activeEffect.getDuration() + duration; - int newAmplifier = Math.max(activeEffect.getAmplifier(), amplifier); - p.removePotionEffect(potionEffect); - p.addPotionEffect(new PotionEffect(potionEffect, newDuration, newAmplifier)); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.25f); - return; - } - } - // if we didn't find an existing effect, add a new one - J.s(() -> { - p.addPotionEffect(new PotionEffect(potionEffect, duration, amplifier)); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.25f); - }, 1); + } - } + default void potion(Player p, PotionEffectType type, int power, int duration) { + p.addPotionEffect(new PotionEffect(type, power, duration, true, false, false)); + } - default void potion(Player p, PotionEffectType type, int power, int duration) { - p.addPotionEffect(new PotionEffect(type, power, duration, true, false, false)); + default double blockXP(Block block, double xp) { + try { + return Math.round(xp * getBlockMultiplier(block)); + } catch (Exception e) { + Adapt.verbose("Error in blockXP: " + e.getMessage()); } + return xp; + } - default double blockXP(Block block, double xp) { - try { - return Math.round(xp * getBlockMultiplier(block)); - } catch (Exception e) { - Adapt.verbose("Error in blockXP: " + e.getMessage()); - } - return xp; - } + default double getBlockMultiplier(Block block) { + return WorldData.of(block.getWorld()).reportEarnings(block); + } - default double getBlockMultiplier(Block block) { - return WorldData.of(block.getWorld()).reportEarnings(block); - } + default double getValue(Material material) { + return MaterialValue.getValue(material); + } - default double getValue(Material material) { - return MaterialValue.getValue(material); - } + default double getValue(BlockData block) { + return MaterialValue.getValue(block.getMaterial()); + } - default double getValue(BlockData block) { - return MaterialValue.getValue(block.getMaterial()); - } + default double getValue(ItemStack f) { + return MaterialValue.getValue(f.getType()); + } - default double getValue(ItemStack f) { - return MaterialValue.getValue(f.getType()); - } + default double getValue(Block block) { + return MaterialValue.getValue(block.getType()); + } - default double getValue(Block block) { - return MaterialValue.getValue(block.getType()); + default void runVisualLoop(int durationTicks, IntConsumer onTick) { + if (durationTicks <= 0) { + return; } - default void runVisualLoop(int durationTicks, IntConsumer onTick) { - if (durationTicks <= 0) { - return; - } - - int[] tick = {0}; - Runnable[] loop = new Runnable[1]; - loop[0] = () -> { - if (tick[0] >= durationTicks) { - return; - } - - onTick.accept(tick[0]); - tick[0]++; - if (tick[0] < durationTicks) { - J.s(loop[0], 1); - } - }; - J.s(loop[0]); - } - - default void vfxMovingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double density) { - if (!areParticlesEnabled()) { - return; - } + int[] tick = {0}; + Runnable[] loop = new Runnable[1]; + loop[0] = () -> { + if (tick[0] >= durationTicks) { + return; + } + + onTick.accept(tick[0]); + tick[0]++; + if (tick[0] < durationTicks) { + J.s(loop[0], 1); + } + }; + J.s(loop[0]); + } + + default void vfxMovingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double density) { + if (!areParticlesEnabled()) { + return; + } - if (ticks <= 0) { - return; - } + if (ticks <= 0) { + return; + } - int durationTicks = ticks; - World world = startLocation.getWorld(); - double startX = startLocation.getX(); - double startY = startLocation.getY(); - double startZ = startLocation.getZ(); - double endX = endLocation.getX(); - double endY = endLocation.getY(); - double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / durationTicks; - double deltaY = (endY - startY) / durationTicks; - double deltaZ = (endZ - startZ) / durationTicks; - Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); - - runVisualLoop(durationTicks, tick -> { - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - Location particleLocation = new Location(world, x, y, z); - - for (double i = 0; i < Math.PI; i += Math.PI / density) { - double radius = Math.sin(i) * size; - double yCoord = Math.cos(i) * size; - for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { - double xCoord = Math.sin(j) * radius; - double zCoord = Math.cos(j) * radius; - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } - } - }); - } - - default void vfxMovingSwirlingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double swirlRadius, double density) { - if (!areParticlesEnabled()) { - return; - } + int durationTicks = ticks; + World world = startLocation.getWorld(); + double startX = startLocation.getX(); + double startY = startLocation.getY(); + double startZ = startLocation.getZ(); + double endX = endLocation.getX(); + double endY = endLocation.getY(); + double endZ = endLocation.getZ(); + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; + Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); + + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; + Location particleLocation = new Location(world, x, y, z); + + for (double i = 0; i < Math.PI; i += Math.PI / density) { + double radius = Math.sin(i) * size; + double yCoord = Math.cos(i) * size; + for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { + double xCoord = Math.sin(j) * radius; + double zCoord = Math.cos(j) * radius; + + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); + } + } + }); + } + + default void vfxMovingSwirlingSphere(Location startLocation, Location endLocation, int ticks, Color color, double size, double swirlRadius, double density) { + if (!areParticlesEnabled()) { + return; + } - if (ticks <= 0) { - return; - } + if (ticks <= 0) { + return; + } - int durationTicks = ticks; - World world = startLocation.getWorld(); - double startX = startLocation.getX(); - double startY = startLocation.getY(); - double startZ = startLocation.getZ(); - double endX = endLocation.getX(); - double endY = endLocation.getY(); - double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / durationTicks; - double deltaY = (endY - startY) / durationTicks; - double deltaZ = (endZ - startZ) / durationTicks; - Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); - - runVisualLoop(durationTicks, tick -> { - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - - // Add swirling effect - double swirlAngle = 2 * Math.PI * tick / durationTicks; - x += swirlRadius * Math.cos(swirlAngle); - z += swirlRadius * Math.sin(swirlAngle); - - Location particleLocation = new Location(world, x, y, z); - - for (double i = 0; i < Math.PI; i += Math.PI / density) { - double radius = Math.sin(i) * size; - double yCoord = Math.cos(i) * size; - for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { - double xCoord = Math.sin(j) * radius; - double zCoord = Math.cos(j) * radius; - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } - } - }); - } - - default void vfxPlayerBoundingBoxOutline(Player player, Color color, int ticks, int particleCount) { - if (!areParticlesEnabled()) { - return; - } + int durationTicks = ticks; + World world = startLocation.getWorld(); + double startX = startLocation.getX(); + double startY = startLocation.getY(); + double startZ = startLocation.getZ(); + double endX = endLocation.getX(); + double endY = endLocation.getY(); + double endZ = endLocation.getZ(); + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; + Particle.DustOptions dustOptions = new Particle.DustOptions(color, (float) size); + + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; + + // Add swirling effect + double swirlAngle = 2 * Math.PI * tick / durationTicks; + x += swirlRadius * Math.cos(swirlAngle); + z += swirlRadius * Math.sin(swirlAngle); + + Location particleLocation = new Location(world, x, y, z); + + for (double i = 0; i < Math.PI; i += Math.PI / density) { + double radius = Math.sin(i) * size; + double yCoord = Math.cos(i) * size; + for (double j = 0; j < Math.PI * 2; j += Math.PI / density) { + double xCoord = Math.sin(j) * radius; + double zCoord = Math.cos(j) * radius; + + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); + } + } + }); + } + + default void vfxPlayerBoundingBoxOutline(Player player, Color color, int ticks, int particleCount) { + if (!areParticlesEnabled()) { + return; + } - World world = player.getWorld(); - Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); - - runVisualLoop(ticks, tick -> { - BoundingBox boundingBox = player.getBoundingBox(); - double minX = boundingBox.getMinX(); - double minY = boundingBox.getMinY(); - double minZ = boundingBox.getMinZ(); - double maxX = boundingBox.getMaxX(); - double maxY = boundingBox.getMaxY(); - double maxZ = boundingBox.getMaxZ(); - - for (int i = 0; i < particleCount; i++) { - double t = (double) i / (particleCount - 1); - - // Edges along X-axis - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, maxZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, maxZ, 0, 0, 0, 0, dustOptions); - - // Edges along Y-axis - world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); - - // Edges along Z-axis - world.spawnParticle(REDSTONE, minX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, minX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - world.spawnParticle(REDSTONE, maxX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); - } - }); - } - - default void vfxVortexSphere(Location startLocation, Location endLocation, int ticks, Color color, double radius) { - if (!areParticlesEnabled()) { - return; - } + World world = player.getWorld(); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); + + runVisualLoop(ticks, tick -> { + BoundingBox boundingBox = player.getBoundingBox(); + double minX = boundingBox.getMinX(); + double minY = boundingBox.getMinY(); + double minZ = boundingBox.getMinZ(); + double maxX = boundingBox.getMaxX(); + double maxY = boundingBox.getMaxY(); + double maxZ = boundingBox.getMaxZ(); + + for (int i = 0; i < particleCount; i++) { + double t = (double) i / (particleCount - 1); + + // Edges along X-axis + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), minY, maxZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX + t * (maxX - minX), maxY, maxZ, 0, 0, 0, 0, dustOptions); + + // Edges along Y-axis + world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), minZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY + t * (maxY - minY), maxZ, 0, 0, 0, 0, dustOptions); + + // Edges along Z-axis + world.spawnParticle(REDSTONE, minX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, minY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, minX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + world.spawnParticle(REDSTONE, maxX, maxY, minZ + t * (maxZ - minZ), 0, 0, 0, 0, dustOptions); + } + }); + } + + default void vfxVortexSphere(Location startLocation, Location endLocation, int ticks, Color color, double radius) { + if (!areParticlesEnabled()) { + return; + } - if (ticks <= 0) { - return; - } + if (ticks <= 0) { + return; + } - int durationTicks = ticks; - World world = startLocation.getWorld(); - Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); - - double startX = startLocation.getX(); - double startY = startLocation.getY(); - double startZ = startLocation.getZ(); - double endX = endLocation.getX(); - double endY = endLocation.getY(); - double endZ = endLocation.getZ(); - double deltaX = (endX - startX) / durationTicks; - double deltaY = (endY - startY) / durationTicks; - double deltaZ = (endZ - startZ) / durationTicks; - - runVisualLoop(durationTicks, tick -> { - double x = startX + deltaX * tick; - double y = startY + deltaY * tick; - double z = startZ + deltaZ * tick; - Location particleLocation = new Location(world, x, y, z); - - double currentRadius = radius * (1 - (double) tick / durationTicks); - - for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 10) { - for (double phi = 0; phi < Math.PI; phi += Math.PI / 10) { - double xCoord = currentRadius * Math.sin(phi) * Math.cos(theta); - double yCoord = currentRadius * Math.sin(phi) * Math.sin(theta); - double zCoord = currentRadius * Math.cos(phi); - - Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); - world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); - } - } - }); - } - - - default void vfxDome(Location center, double range, Color color, int particleCount) { - if (!areParticlesEnabled()) { - return; - } + int durationTicks = ticks; + World world = startLocation.getWorld(); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); + + double startX = startLocation.getX(); + double startY = startLocation.getY(); + double startZ = startLocation.getZ(); + double endX = endLocation.getX(); + double endY = endLocation.getY(); + double endZ = endLocation.getZ(); + double deltaX = (endX - startX) / durationTicks; + double deltaY = (endY - startY) / durationTicks; + double deltaZ = (endZ - startZ) / durationTicks; + + runVisualLoop(durationTicks, tick -> { + double x = startX + deltaX * tick; + double y = startY + deltaY * tick; + double z = startZ + deltaZ * tick; + Location particleLocation = new Location(world, x, y, z); + + double currentRadius = radius * (1 - (double) tick / durationTicks); + + for (double theta = 0; theta < 2 * Math.PI; theta += Math.PI / 10) { + for (double phi = 0; phi < Math.PI; phi += Math.PI / 10) { + double xCoord = currentRadius * Math.sin(phi) * Math.cos(theta); + double yCoord = currentRadius * Math.sin(phi) * Math.sin(theta); + double zCoord = currentRadius * Math.cos(phi); + + Location loc = particleLocation.clone().add(xCoord, yCoord, zCoord); + world.spawnParticle(REDSTONE, loc, 0, 0, 0, 0, dustOptions); + } + } + }); + } + + + default void vfxDome(Location center, double range, Color color, int particleCount) { + if (!areParticlesEnabled()) { + return; + } - Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1); - World world = center.getWorld(); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1); + World world = center.getWorld(); - for (int i = 0; i < particleCount; i++) { - double theta = 2 * Math.PI * RANDOM.nextDouble(); - double phi = Math.PI / 2 * RANDOM.nextDouble(); // Adjusted range of phi to create a dome - double x = range * Math.sin(phi) * Math.cos(theta); - double y = range * Math.sin(phi) * Math.sin(theta); - double z = range * Math.cos(phi); + for (int i = 0; i < particleCount; i++) { + double theta = 2 * Math.PI * RANDOM.nextDouble(); + double phi = Math.PI / 2 * RANDOM.nextDouble(); // Adjusted range of phi to create a dome + double x = range * Math.sin(phi) * Math.cos(theta); + double y = range * Math.sin(phi) * Math.sin(theta); + double z = range * Math.cos(phi); - Location particleLocation = center.clone().add(x, y, z); - world.spawnParticle(REDSTONE, particleLocation, 0, 0, 0, 0, dustOptions); - } + Location particleLocation = center.clone().add(x, y, z); + world.spawnParticle(REDSTONE, particleLocation, 0, 0, 0, 0, dustOptions); } + } - default void vfxSphereV1(Player p, Location l, double radius, Particle particle, int verticalDensity, int radialDensity) { - if (!areParticlesEnabled()) { - return; - } + default void vfxSphereV1(Player p, Location l, double radius, Particle particle, int verticalDensity, int radialDensity) { + if (!areParticlesEnabled()) { + return; + } - for (double phi = 0; phi <= Math.PI; phi += Math.PI / verticalDensity) { - for (double theta = 0; theta <= 2 * Math.PI; theta += Math.PI / radialDensity) { - double x = radius * Math.cos(theta) * Math.sin(phi); - double y = radius * Math.cos(phi) + 1.5; - double z = radius * Math.sin(theta) * Math.sin(phi); + for (double phi = 0; phi <= Math.PI; phi += Math.PI / verticalDensity) { + for (double theta = 0; theta <= 2 * Math.PI; theta += Math.PI / radialDensity) { + double x = radius * Math.cos(theta) * Math.sin(phi); + double y = radius * Math.cos(phi) + 1.5; + double z = radius * Math.sin(theta) * Math.sin(phi); - l.add(x, y, z); - p.getWorld().spawnParticle(particle, l, 1, 0F, 0F, 0F, 0.001); - l.subtract(x, y, z); - } - } + l.add(x, y, z); + p.getWorld().spawnParticle(particle, l, 1, 0F, 0F, 0F, 0.001); + l.subtract(x, y, z); + } } + } - default void vfxZuck(Location from, Location to) { - if (!areParticlesEnabled()) { - return; - } - - Vector v = from.clone().subtract(to).toVector(); - double l = v.length(); - v.normalize(); - if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { - from.getWorld().spawnParticle(ENCHANTMENT_TABLE, to, 1, 6, 6, 6, 0.6); - } + default void vfxZuck(Location from, Location to) { + if (!areParticlesEnabled()) { + return; } - default void vfxZuck(Location from, Location to, Particle particle) { - if (!areParticlesEnabled()) { - return; - } + Vector v = from.clone().subtract(to).toVector(); + double l = v.length(); + v.normalize(); + if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { + from.getWorld().spawnParticle(ENCHANTMENT_TABLE, to, 1, 6, 6, 6, 0.6); + } + } - Vector v = from.clone().subtract(to).toVector(); - double l = v.length(); - v.normalize(); - if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { - from.getWorld().spawnParticle(particle, to, 1, 6, 6, 6, 0.6); - } + default void vfxZuck(Location from, Location to, Particle particle) { + if (!areParticlesEnabled()) { + return; } - default void safeGiveItem(Player player, Entity itemEntity, ItemStack is) { - EntityPickupItemEvent e = new EntityPickupItemEvent(player, (Item) itemEntity, 0); - Bukkit.getPluginManager().callEvent(e); - if (!e.isCancelled()) { - itemEntity.remove(); - if (!player.getInventory().addItem(is).isEmpty()) { - player.getWorld().dropItem(player.getLocation(), is); - } - } + Vector v = from.clone().subtract(to).toVector(); + double l = v.length(); + v.normalize(); + if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { + from.getWorld().spawnParticle(particle, to, 1, 6, 6, 6, 0.6); } + } + + default void safeGiveItem(Player player, Entity itemEntity, ItemStack is) { + EntityPickupItemEvent e = new EntityPickupItemEvent(player, (Item) itemEntity, 0); + Bukkit.getPluginManager().callEvent(e); + if (!e.isCancelled()) { + itemEntity.remove(); + if (!player.getInventory().addItem(is).isEmpty()) { + player.getWorld().dropItem(player.getLocation(), is); + } + } + } - default void safeGiveItem(Player player, ItemStack item) { - if (!player.getInventory().addItem(item).isEmpty()) { - player.getWorld().dropItem(player.getLocation(), item); - } + default void safeGiveItem(Player player, ItemStack item) { + if (!player.getInventory().addItem(item).isEmpty()) { + player.getWorld().dropItem(player.getLocation(), item); } + } - default void vfxParticleLine(Location start, Location end, Particle particle, int pointsPerLine, int particleCount, double offsetX, double offsetY, double offsetZ, double extra, @Nullable Double data, boolean forceDisplay, - @Nullable Predicate operationPerPoint) { - if (!areParticlesEnabled()) { - return; - } - - double d = start.distance(end) / pointsPerLine; - for (int i = 0; i < pointsPerLine; i++) { - Location l = start.clone(); - Vector direction = end.toVector().subtract(start.toVector()).normalize(); - Vector v = direction.multiply(i * d); - l.add(v.getX(), v.getY(), v.getZ()); - if (operationPerPoint == null) { - start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); - continue; - } - boolean allowed; - try { - allowed = operationPerPoint.test(l); - } catch (IllegalStateException ex) { - // Folia region ownership checks can reject block probes off-thread. - // Skip this point instead of failing the entire visual effect. - Adapt.verbose("Skipping particle line point due to region ownership rejection: " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - allowed = false; - } - - if (allowed) { - start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); - } - } + default void vfxParticleLine(Location start, Location end, Particle particle, int pointsPerLine, int particleCount, double offsetX, double offsetY, double offsetZ, double extra, @Nullable Double data, boolean forceDisplay, + @Nullable Predicate operationPerPoint) { + if (!areParticlesEnabled()) { + return; } - default void vfxParticleLine(Location start, Location end, int particleCount, Particle particle) { - if (!areParticlesEnabled()) { - return; - } - - World world = start.getWorld(); - double distance = start.distance(end); - Vector direction = end.toVector().subtract(start.toVector()).normalize(); - double step = distance / (particleCount - 1); + double d = start.distance(end) / pointsPerLine; + for (int i = 0; i < pointsPerLine; i++) { + Location l = start.clone(); + Vector direction = end.toVector().subtract(start.toVector()).normalize(); + Vector v = direction.multiply(i * d); + l.add(v.getX(), v.getY(), v.getZ()); + if (operationPerPoint == null) { + start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); + continue; + } + boolean allowed; + try { + allowed = operationPerPoint.test(l); + } catch (IllegalStateException ex) { + // Folia region ownership checks can reject block probes off-thread. + // Skip this point instead of failing the entire visual effect. + Adapt.verbose("Skipping particle line point due to region ownership rejection: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + allowed = false; + } + + if (allowed) { + start.getWorld().spawnParticle(particle, l, particleCount, offsetX, offsetY, offsetZ, extra, data, forceDisplay); + } + } + } - for (int i = 0; i < particleCount; i++) { - Location particleLocation = start.clone().add(direction.clone().multiply(i * step)); - world.spawnParticle(particle, particleLocation, 1); - } + default void vfxParticleLine(Location start, Location end, int particleCount, Particle particle) { + if (!areParticlesEnabled()) { + return; } + World world = start.getWorld(); + double distance = start.distance(end); + Vector direction = end.toVector().subtract(start.toVector()).normalize(); + double step = distance / (particleCount - 1); - private List getHollowCuboid(Location loc, double particleDistance) { - List result = Lists.newArrayList(); - World world = loc.getWorld(); - double minX = loc.getBlockX(); - double minY = loc.getBlockY(); - double minZ = loc.getBlockZ(); - double maxX = loc.getBlockX() + 1; - double maxY = loc.getBlockY() + 1; - double maxZ = loc.getBlockZ() + 1; - - for (double x = minX; x <= maxX; x += particleDistance) { - for (double y = minY; y <= maxY; y += particleDistance) { - for (double z = minZ; z <= maxZ; z += particleDistance) { - int components = 0; - if (x == minX || x == maxX) components++; - if (y == minY || y == maxY) components++; - if (z == minZ || z == maxZ) components++; - if (components >= 2) { - result.add(new Location(world, x, y, z)); - } - } - } - } - return result; - } - - private List getHollowCuboid(Location loc, Location loc2, double particleDistance) { - List result = Lists.newArrayList(); - World world = loc.getWorld(); - - double minX = loc.getBlockX(); - double minY = loc.getBlockY(); - double minZ = loc.getBlockZ(); - double maxX = loc2.getBlockX() + 1; - double maxY = loc2.getBlockY() + 1; - double maxZ = loc2.getBlockZ() + 1; - - for (double x = minX; x <= maxX; x += particleDistance) { - for (double y = minY; y <= maxY; y += particleDistance) { - for (double z = minZ; z <= maxZ; z += particleDistance) { - int components = 0; - if (x == minX || x == maxX) components++; - if (y == minY || y == maxY) components++; - if (z == minZ || z == maxZ) components++; - if (components >= 2) { - result.add(new Location(world, x, y, z)); - } - } - } - } - return result; + for (int i = 0; i < particleCount; i++) { + Location particleLocation = start.clone().add(direction.clone().multiply(i * step)); + world.spawnParticle(particle, particleLocation, 1); } + } + + + private List getHollowCuboid(Location loc, double particleDistance) { + List result = Lists.newArrayList(); + World world = loc.getWorld(); + double minX = loc.getBlockX(); + double minY = loc.getBlockY(); + double minZ = loc.getBlockZ(); + double maxX = loc.getBlockX() + 1; + double maxY = loc.getBlockY() + 1; + double maxZ = loc.getBlockZ() + 1; + + for (double x = minX; x <= maxX; x += particleDistance) { + for (double y = minY; y <= maxY; y += particleDistance) { + for (double z = minZ; z <= maxZ; z += particleDistance) { + int components = 0; + if (x == minX || x == maxX) components++; + if (y == minY || y == maxY) components++; + if (z == minZ || z == maxZ) components++; + if (components >= 2) { + result.add(new Location(world, x, y, z)); + } + } + } + } + return result; + } + + private List getHollowCuboid(Location loc, Location loc2, double particleDistance) { + List result = Lists.newArrayList(); + World world = loc.getWorld(); + + double minX = loc.getBlockX(); + double minY = loc.getBlockY(); + double minZ = loc.getBlockZ(); + double maxX = loc2.getBlockX() + 1; + double maxY = loc2.getBlockY() + 1; + double maxZ = loc2.getBlockZ() + 1; + + for (double x = minX; x <= maxX; x += particleDistance) { + for (double y = minY; y <= maxY; y += particleDistance) { + for (double z = minZ; z <= maxZ; z += particleDistance) { + int components = 0; + if (x == minX || x == maxX) components++; + if (y == minY || y == maxY) components++; + if (z == minZ || z == maxZ) components++; + if (components >= 2) { + result.add(new Location(world, x, y, z)); + } + } + } + } + return result; + } - default void vfxCuboidOutline(Block block, Particle particle) { - if (!areParticlesEnabled()) { - return; - } + default void vfxCuboidOutline(Block block, Particle particle) { + if (!areParticlesEnabled()) { + return; + } - List hollowCube = getHollowCuboid(block.getLocation(), 0.25); - for (Location l : hollowCube) { - block.getWorld().spawnParticle(particle, l, 1, 0F, 0F, 0F, 0.000); - } + List hollowCube = getHollowCuboid(block.getLocation(), 0.25); + for (Location l : hollowCube) { + block.getWorld().spawnParticle(particle, l, 1, 0F, 0F, 0F, 0.000); } + } - default void vfxCuboidOutline(Block blockStart, Block blockEnd, Particle particle) { - if (!areParticlesEnabled()) { - return; - } + default void vfxCuboidOutline(Block blockStart, Block blockEnd, Particle particle) { + if (!areParticlesEnabled()) { + return; + } - List hollowCube = getHollowCuboid(blockStart.getLocation(), blockEnd.getLocation(), 0.25); - for (Location l : hollowCube) { - blockStart.getWorld().spawnParticle(particle, l, 2, 0F, 0F, 0F, 0.000); - } + List hollowCube = getHollowCuboid(blockStart.getLocation(), blockEnd.getLocation(), 0.25); + for (Location l : hollowCube) { + blockStart.getWorld().spawnParticle(particle, l, 2, 0F, 0F, 0F, 0.000); } + } - default void vfxCuboidOutline(Block blockStart, Block blockEnd, Color color, int size) { - if (!areParticlesEnabled()) { - return; - } + default void vfxCuboidOutline(Block blockStart, Block blockEnd, Color color, int size) { + if (!areParticlesEnabled()) { + return; + } - List hollowCube = getHollowCuboid(blockStart.getLocation(), blockEnd.getLocation(), 0.25); - Particle.DustOptions dustOptions = new Particle.DustOptions(color, size); - for (Location l : hollowCube) { - blockStart.getWorld().spawnParticle(REDSTONE, l, 2, 0F, 0F, 0F, 0.000, dustOptions); - } + List hollowCube = getHollowCuboid(blockStart.getLocation(), blockEnd.getLocation(), 0.25); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, size); + for (Location l : hollowCube) { + blockStart.getWorld().spawnParticle(REDSTONE, l, 2, 0F, 0F, 0F, 0.000, dustOptions); } + } - default void vfxPrismOutline(Location placer, double outset, Particle particle, int particleCount) { - if (!areParticlesEnabled()) { - return; - } + default void vfxPrismOutline(Location placer, double outset, Particle particle, int particleCount) { + if (!areParticlesEnabled()) { + return; + } - Location top = new Location(placer.getWorld(), placer.getX(), placer.getY() + outset, placer.getZ()); - Location baseCorner1 = new Location(placer.getWorld(), placer.getX() - outset, placer.getY(), placer.getZ() - outset); - Location baseCorner2 = new Location(placer.getWorld(), placer.getX() + outset, placer.getY(), placer.getZ() - outset); - Location baseCorner3 = new Location(placer.getWorld(), placer.getX() + outset, placer.getY(), placer.getZ() + outset); - Location baseCorner4 = new Location(placer.getWorld(), placer.getX() - outset, placer.getY(), placer.getZ() + outset); + Location top = new Location(placer.getWorld(), placer.getX(), placer.getY() + outset, placer.getZ()); + Location baseCorner1 = new Location(placer.getWorld(), placer.getX() - outset, placer.getY(), placer.getZ() - outset); + Location baseCorner2 = new Location(placer.getWorld(), placer.getX() + outset, placer.getY(), placer.getZ() - outset); + Location baseCorner3 = new Location(placer.getWorld(), placer.getX() + outset, placer.getY(), placer.getZ() + outset); + Location baseCorner4 = new Location(placer.getWorld(), placer.getX() - outset, placer.getY(), placer.getZ() + outset); - vfxParticleLine(baseCorner1, baseCorner2, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); - vfxParticleLine(baseCorner2, baseCorner3, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); - vfxParticleLine(baseCorner3, baseCorner4, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); - vfxParticleLine(baseCorner4, baseCorner1, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); + vfxParticleLine(baseCorner1, baseCorner2, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); + vfxParticleLine(baseCorner2, baseCorner3, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); + vfxParticleLine(baseCorner3, baseCorner4, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); + vfxParticleLine(baseCorner4, baseCorner1, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); - for (Location location : Arrays.asList(baseCorner1, baseCorner2, baseCorner3, baseCorner4)) { - vfxParticleLine(location, top, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); - } + for (Location location : Arrays.asList(baseCorner1, baseCorner2, baseCorner3, baseCorner4)) { + vfxParticleLine(location, top, particle, particleCount, 1, 0.0D, 0D, 0.0D, 0D, null, true, l -> l.getBlock().isPassable()); } + } - default void vfxFastSphere(Location center, double range, Color color, int particleCount) { - if (!areParticlesEnabled()) { - return; - } + default void vfxFastSphere(Location center, double range, Color color, int particleCount) { + if (!areParticlesEnabled()) { + return; + } - Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1); - World world = center.getWorld(); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1); + World world = center.getWorld(); - for (int i = 0; i < particleCount; i++) { - double x, y, z; - do { - x = RANDOM.nextDouble() * 2 - 1; - y = RANDOM.nextDouble() * 2 - 1; - z = RANDOM.nextDouble() * 2 - 1; - } while (x * x + y * y + z * z > 1); + for (int i = 0; i < particleCount; i++) { + double x, y, z; + do { + x = RANDOM.nextDouble() * 2 - 1; + y = RANDOM.nextDouble() * 2 - 1; + z = RANDOM.nextDouble() * 2 - 1; + } while (x * x + y * y + z * z > 1); - double magnitude = Math.sqrt(x * x + y * y + z * z); - x = x / magnitude * range; - y = y / magnitude * range; - z = z / magnitude * range; + double magnitude = Math.sqrt(x * x + y * y + z * z); + x = x / magnitude * range; + y = y / magnitude * range; + z = z / magnitude * range; - Location particleLocation = center.clone().add(x, y, z); - world.spawnParticle(REDSTONE, particleLocation, 0, 0, 0, 0, dustOptions); - } + Location particleLocation = center.clone().add(x, y, z); + world.spawnParticle(REDSTONE, particleLocation, 0, 0, 0, 0, dustOptions); } + } - default void vfxLoadingRing(Location center, double radius, Color color, int durationTicks, int particleCount) { - if (!areParticlesEnabled()) { - return; - } - - World world = center.getWorld(); - Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); - - runVisualLoop(durationTicks, tick -> { - double angle = 2 * Math.PI * tick / durationTicks; - double x = radius * Math.cos(angle); - double z = radius * Math.sin(angle); - Location particleLocation = center.clone().add(x, 0, z); - world.spawnParticle(REDSTONE, particleLocation, particleCount, 0, 0, 0, dustOptions); - }); + default void vfxLoadingRing(Location center, double radius, Color color, int durationTicks, int particleCount) { + if (!areParticlesEnabled()) { + return; } - default void vfxLoadingRing(Location center, double radius, Particle particle, int durationTicks, int particleCount) { - if (!areParticlesEnabled()) { - return; - } - - World world = center.getWorld(); - - runVisualLoop(durationTicks, tick -> { - double angle = 2 * Math.PI * tick / durationTicks; - double x = radius * Math.cos(angle); - double z = radius * Math.sin(angle); - Location particleLocation = center.clone().add(x, 0, z); - world.spawnParticle(particle, particleLocation, particleCount, 0, 0, 0); - }); + World world = center.getWorld(); + Particle.DustOptions dustOptions = new Particle.DustOptions(color, 1.0f); + + runVisualLoop(durationTicks, tick -> { + double angle = 2 * Math.PI * tick / durationTicks; + double x = radius * Math.cos(angle); + double z = radius * Math.sin(angle); + Location particleLocation = center.clone().add(x, 0, z); + world.spawnParticle(REDSTONE, particleLocation, particleCount, 0, 0, 0, dustOptions); + }); + } + + default void vfxLoadingRing(Location center, double radius, Particle particle, int durationTicks, int particleCount) { + if (!areParticlesEnabled()) { + return; } + World world = center.getWorld(); - default void vfxLevelUp(Player p) { - if (!areParticlesEnabled()) { - return; - } - - p.spawnParticle(Particle.REVERSE_PORTAL, p.getLocation().clone().add(0, 1.7, 0), 100, 0.1, 0.1, 0.1, 4.1); - } + runVisualLoop(durationTicks, tick -> { + double angle = 2 * Math.PI * tick / durationTicks; + double x = radius * Math.cos(angle); + double z = radius * Math.sin(angle); + Location particleLocation = center.clone().add(x, 0, z); + world.spawnParticle(particle, particleLocation, particleCount, 0, 0, 0); + }); + } - default void vfxFastRing(Location location, double radius, Color color) { - if (!areParticlesEnabled()) { - return; - } - for (int d = 0; d <= 90; d += 1) { - Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); - particleLoc.setX(location.getX() + Math.cos(d) * radius); - particleLoc.setZ(location.getZ() + Math.sin(d) * radius); - location.getWorld().spawnParticle(REDSTONE, particleLoc, 1, new Particle.DustOptions(color, 1)); - } + default void vfxLevelUp(Player p) { + if (!areParticlesEnabled()) { + return; } - default void vfxFastRing(Location location, double radius, Particle particle) { - if (!areParticlesEnabled()) { - return; - } + p.spawnParticle(Particle.REVERSE_PORTAL, p.getLocation().clone().add(0, 1.7, 0), 100, 0.1, 0.1, 0.1, 4.1); + } - for (int d = 0; d <= 90; d += 1) { - Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); - particleLoc.setX(location.getX() + Math.cos(d) * radius); - particleLoc.setZ(location.getZ() + Math.sin(d) * radius); - location.getWorld().spawnParticle(particle, particleLoc, 1); - } + default void vfxFastRing(Location location, double radius, Color color) { + if (!areParticlesEnabled()) { + return; } - default void vfxFastRing(Location location, double radius, Particle particle, int angle) { - if (!areParticlesEnabled()) { - return; - } + for (int d = 0; d <= 90; d += 1) { + Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); + particleLoc.setX(location.getX() + Math.cos(d) * radius); + particleLoc.setZ(location.getZ() + Math.sin(d) * radius); + location.getWorld().spawnParticle(REDSTONE, particleLoc, 1, new Particle.DustOptions(color, 1)); + } + } - for (int d = 0; d <= 90; d += angle) { - Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); - particleLoc.setX(location.getX() + Math.cos(d) * radius); - particleLoc.setZ(location.getZ() + Math.sin(d) * radius); - location.getWorld().spawnParticle(particle, particleLoc, 1); - } + default void vfxFastRing(Location location, double radius, Particle particle) { + if (!areParticlesEnabled()) { + return; } - default void vfxShootParticle(Player player, Particle particle, double velocity, int count) { - if (!areParticlesEnabled()) { - return; - } + for (int d = 0; d <= 90; d += 1) { + Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); + particleLoc.setX(location.getX() + Math.cos(d) * radius); + particleLoc.setZ(location.getZ() + Math.sin(d) * radius); + location.getWorld().spawnParticle(particle, particleLoc, 1); + } + } - Location location = player.getEyeLocation(); - Vector direction = location.getDirection(); - for (int i = 0; i < count; i++) { - player.getWorld().spawnParticle(particle, location.getX(), location.getY(), location.getZ(), 0, (float) direction.getX(), (float) direction.getY(), (float) direction.getZ(), velocity, null); - } + default void vfxFastRing(Location location, double radius, Particle particle, int angle) { + if (!areParticlesEnabled()) { + return; } - default void vfxParticleSpiral(Location center, int radius, int height, Particle type) { - if (!areParticlesEnabled()) { - return; - } + for (int d = 0; d <= 90; d += angle) { + Location particleLoc = new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()); + particleLoc.setX(location.getX() + Math.cos(d) * radius); + particleLoc.setZ(location.getZ() + Math.sin(d) * radius); + location.getWorld().spawnParticle(particle, particleLoc, 1); + } + } - double angle = 0; - for (int i = 0; i <= height; i++) { - double x = center.getX() + (radius * Math.cos(angle)); - double z = center.getZ() + (radius * Math.sin(angle)); - center.getWorld().spawnParticle(type, x, +center.getY(), z, 1, 0, 0, 0, 0); - angle += 0.1; - } + default void vfxShootParticle(Player player, Particle particle, double velocity, int count) { + if (!areParticlesEnabled()) { + return; } + Location location = player.getEyeLocation(); + Vector direction = location.getDirection(); + for (int i = 0; i < count; i++) { + player.getWorld().spawnParticle(particle, location.getX(), location.getY(), location.getZ(), 0, (float) direction.getX(), (float) direction.getY(), (float) direction.getZ(), velocity, null); + } + } - default void vfxXP(Player p, Location l, int amt) { - if (!areParticlesEnabled()) { - return; - } + default void vfxParticleSpiral(Location center, int radius, int height, Particle type) { + if (!areParticlesEnabled()) { + return; + } - if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { - p.spawnParticle(ENCHANTMENT_TABLE, l, Math.min(amt / 10, 20), 0.5, 0.5, 0.5, 1); - } + double angle = 0; + for (int i = 0; i <= height; i++) { + double x = center.getX() + (radius * Math.cos(angle)); + double z = center.getZ() + (radius * Math.sin(angle)); + center.getWorld().spawnParticle(type, x, +center.getY(), z, 1, 0, 0, 0, 0); + angle += 0.1; } + } - default void vfxXP(Location l) { - if (!areParticlesEnabled()) { - return; - } - if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { - l.getWorld().spawnParticle(ENCHANTMENT_TABLE, l.add(0, 1.7, 0), 3, 0.1, 0.1, 0.1, 3); - } + default void vfxXP(Player p, Location l, int amt) { + if (!areParticlesEnabled()) { + return; } - default void damageHand(Player p, int damage) { - ItemStack is = p.getInventory().getItemInMainHand(); - ItemMeta im = is.getItemMeta(); - - if (im == null) { - return; - } + if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { + p.spawnParticle(ENCHANTMENT_TABLE, l, Math.min(amt / 10, 20), 0.5, 0.5, 0.5, 1); + } + } - if (im.isUnbreakable()) { - return; - } + default void vfxXP(Location l) { + if (!areParticlesEnabled()) { + return; + } - Damageable dm = (Damageable) im; - dm.setDamage(dm.getDamage() + damage); + if (AdaptConfig.get().isUseEnchantmentTableParticleForActiveEffects()) { + l.getWorld().spawnParticle(ENCHANTMENT_TABLE, l.add(0, 1.7, 0), 3, 0.1, 0.1, 0.1, 3); + } + } - if (dm.getDamage() > is.getType().getMaxDurability()) { - p.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f); - return; - } + default void damageHand(Player p, int damage) { + ItemStack is = p.getInventory().getItemInMainHand(); + ItemMeta im = is.getItemMeta(); - is.setItemMeta(im); - p.getInventory().setItemInMainHand(is); + if (im == null) { + return; } - default void damageOffHand(Player p, int damage) { - ItemStack is = p.getInventory().getItemInOffHand(); - ItemMeta im = is.getItemMeta(); + if (im.isUnbreakable()) { + return; + } - if (im == null) { - return; - } + Damageable dm = (Damageable) im; + dm.setDamage(dm.getDamage() + damage); - if (im.isUnbreakable()) { - return; - } + if (dm.getDamage() > is.getType().getMaxDurability()) { + p.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f); + return; + } - Damageable dm = (Damageable) im; - dm.setDamage(dm.getDamage() + damage); + is.setItemMeta(im); + p.getInventory().setItemInMainHand(is); + } - if (dm.getDamage() > is.getType().getMaxDurability()) { - p.getInventory().setItemInOffHand(new ItemStack(Material.AIR)); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f); - return; - } + default void damageOffHand(Player p, int damage) { + ItemStack is = p.getInventory().getItemInOffHand(); + ItemMeta im = is.getItemMeta(); - is.setItemMeta(im); - p.getInventory().setItemInOffHand(is); + if (im == null) { + return; } - default Block getRightBlock(Player p, Block b) { - Location l = p.getLocation(); - float yaw = l.getYaw(); - // Make sure yaw is in the range 0 to 360 - while (yaw < 0) { - yaw += 360; - } - yaw = yaw % 360; - // The player's yaw is their rotation in the world, - // so, we can use that to get the right face of a block! - BlockFace rightFace; - // if the player is facing SE to SW - if (yaw < 45 || yaw >= 315) { - rightFace = BlockFace.EAST; - return b.getRelative(rightFace); - } - // if the player is facing SW to NW - else if (yaw < 135) { - rightFace = BlockFace.SOUTH; - return b.getRelative(rightFace); - } - // if the player is facing NW to NE - else if (yaw < 225) { - rightFace = BlockFace.WEST; - return b.getRelative(rightFace); - } - // if the player is facing NE to SE - else if (yaw < 315) { - rightFace = BlockFace.NORTH; - return b.getRelative(rightFace); - } else { - return null; - } + if (im.isUnbreakable()) { + return; } - default Block getLeftBlock(Player p, Block b) { - Location l = p.getLocation(); - float yaw = l.getYaw(); + Damageable dm = (Damageable) im; + dm.setDamage(dm.getDamage() + damage); - // Make sure yaw is in the range 0 to 360 - while (yaw < 0) { - yaw += 360; - } - yaw = yaw % 360; - // The player's yaw is their rotation in the world, - // so, we can use that to get the right face of a block! - BlockFace leftFace; - // if the player is facing SE to SW - if (yaw < 45 || yaw >= 315) { - leftFace = BlockFace.WEST; - return b.getRelative(leftFace); - } - // if the player is facing SW to NW - else if (yaw < 135) { - leftFace = BlockFace.NORTH; - return b.getRelative(leftFace); - } - // if the player is facing NW to NE - else if (yaw < 225) { - leftFace = BlockFace.EAST; - return b.getRelative(leftFace); - } - // if the player is facing NE to SE - else if (yaw < 315) { - leftFace = BlockFace.SOUTH; - return b.getRelative(leftFace); - } else { - return null; - } + if (dm.getDamage() > is.getType().getMaxDurability()) { + p.getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f); + return; } + is.setItemMeta(im); + p.getInventory().setItemInOffHand(is); + } - default void setExp(Player p, int exp) { - p.setExp(0); - p.setLevel(0); - p.setTotalExperience(0); + default Block getRightBlock(Player p, Block b) { + Location l = p.getLocation(); + float yaw = l.getYaw(); + // Make sure yaw is in the range 0 to 360 + while (yaw < 0) { + yaw += 360; + } + yaw = yaw % 360; + // The player's yaw is their rotation in the world, + // so, we can use that to get the right face of a block! + BlockFace rightFace; + // if the player is facing SE to SW + if (yaw < 45 || yaw >= 315) { + rightFace = BlockFace.EAST; + return b.getRelative(rightFace); + } + // if the player is facing SW to NW + else if (yaw < 135) { + rightFace = BlockFace.SOUTH; + return b.getRelative(rightFace); + } + // if the player is facing NW to NE + else if (yaw < 225) { + rightFace = BlockFace.WEST; + return b.getRelative(rightFace); + } + // if the player is facing NE to SE + else if (yaw < 315) { + rightFace = BlockFace.NORTH; + return b.getRelative(rightFace); + } else { + return null; + } + } - if (exp <= 0) { - return; - } + default Block getLeftBlock(Player p, Block b) { + Location l = p.getLocation(); + float yaw = l.getYaw(); - giveExp(p, exp); + // Make sure yaw is in the range 0 to 360 + while (yaw < 0) { + yaw += 360; } - - default void giveExp(Player p, int exp) { - while (exp > 0) { - int xp = getExpToLevel(p) - getExp(p); - if (xp > exp) { - xp = exp; - } - p.giveExp(xp); - exp -= xp; - } + yaw = yaw % 360; + // The player's yaw is their rotation in the world, + // so, we can use that to get the right face of a block! + BlockFace leftFace; + // if the player is facing SE to SW + if (yaw < 45 || yaw >= 315) { + leftFace = BlockFace.WEST; + return b.getRelative(leftFace); } - - default void takeExp(Player p, int exp) { - takeExp(p, exp, true); + // if the player is facing SW to NW + else if (yaw < 135) { + leftFace = BlockFace.NORTH; + return b.getRelative(leftFace); + } + // if the player is facing NW to NE + else if (yaw < 225) { + leftFace = BlockFace.EAST; + return b.getRelative(leftFace); } + // if the player is facing NE to SE + else if (yaw < 315) { + leftFace = BlockFace.SOUTH; + return b.getRelative(leftFace); + } else { + return null; + } + } - default void takeExp(Player p, int exp, boolean fromTotal) { - int xp = getTotalExp(p); - if (fromTotal) { - xp -= exp; - } else { - int m = getExp(p) - exp; - if (m < 0) { - m = 0; - } - xp -= getExp(p) + m; - } + default void setExp(Player p, int exp) { + p.setExp(0); + p.setLevel(0); + p.setTotalExperience(0); - setExp(p, xp); + if (exp <= 0) { + return; } - default int getExp(Player p) { - return (int) (getExpToLevel(p) * p.getExp()); + giveExp(p, exp); + } + + default void giveExp(Player p, int exp) { + while (exp > 0) { + int xp = getExpToLevel(p) - getExp(p); + if (xp > exp) { + xp = exp; + } + p.giveExp(xp); + exp -= xp; } - - default int getTotalExp(Player p) { - return getTotalExp(p, false); + } + + default void takeExp(Player p, int exp) { + takeExp(p, exp, true); + } + + default void takeExp(Player p, int exp, boolean fromTotal) { + int xp = getTotalExp(p); + + if (fromTotal) { + xp -= exp; + } else { + int m = getExp(p) - exp; + if (m < 0) { + m = 0; + } + xp -= getExp(p) + m; } - default int getTotalExp(Player p, boolean recalc) { - if (recalc) { - recalcTotalExp(p); - } - return p.getTotalExperience(); - } + setExp(p, xp); + } - default int getLevel(Player p) { - return p.getLevel(); - } + default int getExp(Player p) { + return (int) (getExpToLevel(p) * p.getExp()); + } - default int getExpToLevel(Player p) { - return p.getExpToLevel(); - } + default int getTotalExp(Player p) { + return getTotalExp(p, false); + } - default int getExpToLevel(int level) { - return level >= 30 ? 62 + (level - 30) * 7 : (level >= 15 ? 17 + (level - 15) * 3 : 17); + default int getTotalExp(Player p, boolean recalc) { + if (recalc) { + recalcTotalExp(p); } + return p.getTotalExperience(); + } - default void recalcTotalExp(Player p) { - int total = getExp(p); - for (int i = 0; i < p.getLevel(); i++) { - total += getExpToLevel(i); - } - p.setTotalExperience(total); - } - - /** - * Takes a custom amount of the item stack exact type (Ignores the item amount) - * - * @param inv the inv - * @param is the item ignore the amount - * @param amount the amount to use - * @return true if taken, false if not (missing) - */ - default boolean takeAll(Inventory inv, ItemStack is, int amount) { - ItemStack isf = is.clone(); - isf.setAmount(amount); - return takeAll(inv, is); - } - - /** - * Take one of an exact type ignoring the item stack amount - * - * @param inv the inv - * @param is the item ignoring the amount - * @return true if taken, false if diddnt - */ - default boolean takeOne(Inventory inv, ItemStack is, int amount) { - return takeAll(inv, is, 1); - } - - /** - * Take a specific amount of an EXACT META TYPE from an inventory - * - * @param inv the inv - * @param is uses the amount - * @return returns false if it couldnt get enough (and none was taken) - */ - default boolean takeAll(Inventory inv, ItemStack is) { - ItemStack[] items = inv.getStorageContents(); - - int take = is.getAmount(); - - for (int ii = 0; ii < items.length; ii++) { - ItemStack i = items[ii]; - - if (i == null) { - continue; - } - - if (i.isSimilar(is)) { - if (take > i.getAmount()) { - i.setAmount(i.getAmount() - take); - items[ii] = i; - take = 0; - break; - } else { - items[ii] = null; - take -= i.getAmount(); - } - } - } + default int getLevel(Player p) { + return p.getLevel(); + } + + default int getExpToLevel(Player p) { + return p.getExpToLevel(); + } + + default int getExpToLevel(int level) { + return level >= 30 ? 62 + (level - 30) * 7 : (level >= 15 ? 17 + (level - 15) * 3 : 17); + } - if (take > 0) { - return false; + default void recalcTotalExp(Player p) { + int total = getExp(p); + for (int i = 0; i < p.getLevel(); i++) { + total += getExpToLevel(i); + } + p.setTotalExperience(total); + } + + /** + * Takes a custom amount of the item stack exact type (Ignores the item + * amount) + * + * @param inv the inv + * @param is the item ignore the amount + * @param amount the amount to use + * @return true if taken, false if not (missing) + */ + default boolean takeAll(Inventory inv, ItemStack is, int amount) { + ItemStack isf = is.clone(); + isf.setAmount(amount); + return takeAll(inv, is); + } + + /** + * Take one of an exact type ignoring the item stack amount + * + * @param inv the inv + * @param is the item ignoring the amount + * @return true if taken, false if diddnt + */ + default boolean takeOne(Inventory inv, ItemStack is, int amount) { + return takeAll(inv, is, 1); + } + + /** + * Take a specific amount of an EXACT META TYPE from an inventory + * + * @param inv the inv + * @param is uses the amount + * @return returns false if it couldnt get enough (and none was taken) + */ + default boolean takeAll(Inventory inv, ItemStack is) { + ItemStack[] items = inv.getStorageContents(); + + int take = is.getAmount(); + + for (int ii = 0; ii < items.length; ii++) { + ItemStack i = items[ii]; + + if (i == null) { + continue; + } + + if (i.isSimilar(is)) { + if (take > i.getAmount()) { + i.setAmount(i.getAmount() - take); + items[ii] = i; + take = 0; + break; + } else { + items[ii] = null; + take -= i.getAmount(); } + } + } - inv.setStorageContents(items); - return true; + if (take > 0) { + return false; } + + inv.setStorageContents(items); + return true; + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 795336f42..93b407460 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -27,8 +27,8 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.tick.Ticked; import art.arcane.adapt.api.world.AdaptPlayer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.CustomModel; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import org.bukkit.Location; import org.bukkit.Material; @@ -42,7 +42,8 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; -import java.util.*; +import java.util.List; +import java.util.Set; import java.util.function.IntConsumer; import java.util.function.Predicate; @@ -51,870 +52,897 @@ */ public interface Adaptation extends Ticked, Component { - /** - * @return maximum unlockable level for this adaptation. - */ - int getMaxLevel(); - - /** - * Grants adaptation-attributed xp to a player. - */ - default void xp(Player p, double amount) { - xp(p, amount, null); - } - - /** - * Grants adaptation-attributed xp to a player with an optional reward key suffix. - */ - default void xp(Player p, double amount, String rewardKey) { - getSkill().xp(p, amount, adaptationRewardKey(rewardKey)); - } - - /** - * Grants adaptation-attributed xp and renders it at a world location. - */ - default void xp(Player p, Location l, double amount) { - xp(p, l, amount, null); - } - - /** - * Grants adaptation-attributed xp at a location with an optional reward key suffix. - */ - default void xp(Player p, Location l, double amount, String rewardKey) { - getSkill().xp(p, l, amount, adaptationRewardKey(rewardKey)); - } - - /** - * Grants silent adaptation-attributed xp with an optional reward key suffix. - */ - default void xpSilent(Player p, double amount, String rewardKey) { - getSkill().xpSilent(p, amount, adaptationRewardKey(rewardKey)); - } - - /** - * Grants silent adaptation-attributed xp. - */ - default void xpSilent(Player p, double amount) { - xpSilent(p, amount, null); - } - - /** - * Ensures this logic runs on the player's owned thread/region (Folia-safe). - */ - default void withPlayerThread(Player p, Runnable runnable) { - AdaptationRuntimeGuards.withPlayerThread(this, p, runnable); - } - - /** - * Thread/region guard with optional cancellable event gating. - */ - default void withPlayerThread(Player p, Cancellable cancellable, Runnable runnable) { - AdaptationRuntimeGuards.withPlayerThread(this, p, cancellable, runnable); - } - - /** - * Runs only when player-thread-safe and the player owns this adaptation. - */ - default void withAdaptedPlayer(Player p, Runnable runnable) { - AdaptationRuntimeGuards.withAdaptedPlayer(this, p, runnable); - } - - /** - * Runs only when event is not cancelled, player is thread-safe, and adaptation is owned. - */ - default void withAdaptedPlayer(Player p, Cancellable cancellable, Runnable runnable) { - AdaptationRuntimeGuards.withAdaptedPlayer(this, p, cancellable, runnable); - } - - /** - * Resolves active runtime level and invokes consumer only when level > 0. - */ - default void withActiveLevel(Player p, IntConsumer consumer) { - AdaptationRuntimeGuards.withActiveLevel(this, p, consumer); - } - - /** - * Active-level helper with cancellable event gating. - */ - default void withActiveLevel(Player p, Cancellable cancellable, IntConsumer consumer) { - AdaptationRuntimeGuards.withActiveLevel(this, p, cancellable, consumer); - } - - /** - * Builds an adaptation-scoped reward key (`adaptation::`). - */ - default String adaptationRewardKey(String rewardKey) { - return AdaptationRuntimeGuards.adaptationRewardKey(this, rewardKey); - } - - /** - * Adaptation-aware particle toggle check (global + skill + adaptation config aware). - */ - @Override - default boolean areParticlesEnabled() { - return AdaptationGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); - } - - /** - * Adaptation-aware sound toggle check (global + skill + adaptation config aware). - */ - @Override - default boolean areSoundsEnabled() { - return AdaptationGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); - } - - /** - * Grants small anti-abuse baseline xp for successful adaptation use. - */ - default void awardUsageBaselineXp(Player p, int level) { - AdaptationRuntimeGuards.awardUsageBaselineXp(this, p, level); - } - - /** - * Reads adaptation-scoped per-player storage, falling back to default value. - */ - default F getStorage(Player p, String key, F defaultValue) { - return AdaptationRuntimeGuards.getStorage(this, p, key, defaultValue); - } - - /** - * Reads adaptation-scoped per-player storage. - */ - default F getStorage(Player p, String key) { - return getStorage(p, key, null); - } - - /** - * Writes adaptation-scoped per-player storage. - */ - default boolean setStorage(Player p, String key, Object value) { - return AdaptationRuntimeGuards.setStorage(this, p, key, value); - } - - /** - * Fires use checks/events for this adaptation against an AdaptPlayer context. - */ - default boolean canUse(AdaptPlayer player) { - return AdaptationRuntimeGuards.canUse(this, player); - } - - /** - * Fires use checks/events for this adaptation against a Bukkit player. - */ - default boolean canUse(Player player) { - return AdaptationRuntimeGuards.canUse(this, player); - } - - /** - * Returns true when this player is blacklisted from this adaptation via permission. - */ - default boolean hasBlacklistPermission(Player p, Adaptation a) { - return AdaptationRuntimeGuards.hasBlacklistPermission(this, p, a); - } - - /** - * Typed storage convenience helper for strings. - */ - default String getStorageString(Player p, String key, String defaultValue) { - return getStorage(p, key, defaultValue); - } - - /** - * Typed storage convenience helper for strings. - */ - default String getStorageString(Player p, String key) { - return getStorage(p, key); - } - - /** - * Typed storage convenience helper for integers. - */ - default Integer getStorageInt(Player p, String key, Integer defaultValue) { - Object value = getStorage(p, key, (Object) defaultValue); - if (value == null) { - return defaultValue; - } - - if (value instanceof Number number) { - return number.intValue(); - } - - if (value instanceof String stringValue) { - try { - return Integer.parseInt(stringValue.trim()); - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - + /** + * @return maximum unlockable level for this adaptation. + */ + int getMaxLevel(); + + /** + * Grants adaptation-attributed xp to a player. + */ + default void xp(Player p, double amount) { + xp(p, amount, null); + } + + /** + * Grants adaptation-attributed xp to a player with an optional reward key + * suffix. + */ + default void xp(Player p, double amount, String rewardKey) { + getSkill().xp(p, amount, adaptationRewardKey(rewardKey)); + } + + /** + * Grants adaptation-attributed xp and renders it at a world location. + */ + default void xp(Player p, Location l, double amount) { + xp(p, l, amount, null); + } + + /** + * Grants adaptation-attributed xp at a location with an optional reward key + * suffix. + */ + default void xp(Player p, Location l, double amount, String rewardKey) { + getSkill().xp(p, l, amount, adaptationRewardKey(rewardKey)); + } + + /** + * Grants silent adaptation-attributed xp with an optional reward key suffix. + */ + default void xpSilent(Player p, double amount, String rewardKey) { + getSkill().xpSilent(p, amount, adaptationRewardKey(rewardKey)); + } + + /** + * Grants silent adaptation-attributed xp. + */ + default void xpSilent(Player p, double amount) { + xpSilent(p, amount, null); + } + + /** + * Ensures this logic runs on the player's owned thread/region (Folia-safe). + */ + default void withPlayerThread(Player p, Runnable runnable) { + AdaptationRuntimeGuards.withPlayerThread(this, p, runnable); + } + + /** + * Thread/region guard with optional cancellable event gating. + */ + default void withPlayerThread(Player p, Cancellable cancellable, Runnable runnable) { + AdaptationRuntimeGuards.withPlayerThread(this, p, cancellable, runnable); + } + + /** + * Runs only when player-thread-safe and the player owns this adaptation. + */ + default void withAdaptedPlayer(Player p, Runnable runnable) { + AdaptationRuntimeGuards.withAdaptedPlayer(this, p, runnable); + } + + /** + * Runs only when event is not cancelled, player is thread-safe, and + * adaptation is owned. + */ + default void withAdaptedPlayer(Player p, Cancellable cancellable, Runnable runnable) { + AdaptationRuntimeGuards.withAdaptedPlayer(this, p, cancellable, runnable); + } + + /** + * Resolves active runtime level and invokes consumer only when level > 0. + */ + default void withActiveLevel(Player p, IntConsumer consumer) { + AdaptationRuntimeGuards.withActiveLevel(this, p, consumer); + } + + /** + * Active-level helper with cancellable event gating. + */ + default void withActiveLevel(Player p, Cancellable cancellable, IntConsumer consumer) { + AdaptationRuntimeGuards.withActiveLevel(this, p, cancellable, consumer); + } + + /** + * Builds an adaptation-scoped reward key (`adaptation::`). + */ + default String adaptationRewardKey(String rewardKey) { + return AdaptationRuntimeGuards.adaptationRewardKey(this, rewardKey); + } + + /** + * Adaptation-aware particle toggle check (global + skill + adaptation config + * aware). + */ + @Override + default boolean areParticlesEnabled() { + return AdaptationGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); + } + + /** + * Adaptation-aware sound toggle check (global + skill + adaptation config + * aware). + */ + @Override + default boolean areSoundsEnabled() { + return AdaptationGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); + } + + /** + * Grants small anti-abuse baseline xp for successful adaptation use. + */ + default void awardUsageBaselineXp(Player p, int level) { + AdaptationRuntimeGuards.awardUsageBaselineXp(this, p, level); + } + + /** + * Reads adaptation-scoped per-player storage, falling back to default value. + */ + default F getStorage(Player p, String key, F defaultValue) { + return AdaptationRuntimeGuards.getStorage(this, p, key, defaultValue); + } + + /** + * Reads adaptation-scoped per-player storage. + */ + default F getStorage(Player p, String key) { + return getStorage(p, key, null); + } + + /** + * Writes adaptation-scoped per-player storage. + */ + default boolean setStorage(Player p, String key, Object value) { + return AdaptationRuntimeGuards.setStorage(this, p, key, value); + } + + /** + * Fires use checks/events for this adaptation against an AdaptPlayer + * context. + */ + default boolean canUse(AdaptPlayer player) { + return AdaptationRuntimeGuards.canUse(this, player); + } + + /** + * Fires use checks/events for this adaptation against a Bukkit player. + */ + default boolean canUse(Player player) { + return AdaptationRuntimeGuards.canUse(this, player); + } + + /** + * Returns true when this player is blacklisted from this adaptation via + * permission. + */ + default boolean hasBlacklistPermission(Player p, Adaptation a) { + return AdaptationRuntimeGuards.hasBlacklistPermission(this, p, a); + } + + /** + * Typed storage convenience helper for strings. + */ + default String getStorageString(Player p, String key, String defaultValue) { + return getStorage(p, key, defaultValue); + } + + /** + * Typed storage convenience helper for strings. + */ + default String getStorageString(Player p, String key) { + return getStorage(p, key); + } + + /** + * Typed storage convenience helper for integers. + */ + default Integer getStorageInt(Player p, String key, Integer defaultValue) { + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; + } + + if (value instanceof Number number) { + return number.intValue(); + } + + if (value instanceof String stringValue) { + try { + return Integer.parseInt(stringValue.trim()); + } catch (NumberFormatException ignored) { return defaultValue; + } } - /** - * Typed storage convenience helper for integers. - */ - default Integer getStorageInt(Player p, String key) { - return getStorageInt(p, key, null); - } - - /** - * Typed storage convenience helper for doubles. - */ - default Double getStorageDouble(Player p, String key, Double defaultValue) { - Object value = getStorage(p, key, (Object) defaultValue); - if (value == null) { - return defaultValue; - } - - if (value instanceof Number number) { - return number.doubleValue(); - } - - if (value instanceof String stringValue) { - try { - return Double.parseDouble(stringValue.trim()); - } catch (NumberFormatException ignored) { - return defaultValue; - } - } + return defaultValue; + } - return defaultValue; - } + /** + * Typed storage convenience helper for integers. + */ + default Integer getStorageInt(Player p, String key) { + return getStorageInt(p, key, null); + } - /** - * Typed storage convenience helper for doubles. - */ - default Double getStorageDouble(Player p, String key) { - return getStorageDouble(p, key, null); + /** + * Typed storage convenience helper for doubles. + */ + default Double getStorageDouble(Player p, String key, Double defaultValue) { + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; } - /** - * Typed storage convenience helper for booleans. - */ - default Boolean getStorageBoolean(Player p, String key, Boolean defaultValue) { - return getStorage(p, key, defaultValue); + if (value instanceof Number number) { + return number.doubleValue(); } - /** - * Typed storage convenience helper for booleans. - */ - default Boolean getStorageBoolean(Player p, String key) { - return getStorage(p, key); - } - - /** - * Typed storage convenience helper for longs. - */ - default Long getStorageLong(Player p, String key, Long defaultValue) { - Object value = getStorage(p, key, (Object) defaultValue); - if (value == null) { - return defaultValue; - } - - if (value instanceof Number number) { - return number.longValue(); - } - - if (value instanceof String stringValue) { - try { - return Long.parseLong(stringValue.trim()); - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - + if (value instanceof String stringValue) { + try { + return Double.parseDouble(stringValue.trim()); + } catch (NumberFormatException ignored) { return defaultValue; - } - - /** - * Typed storage convenience helper for longs. - */ - default Long getStorageLong(Player p, String key) { - return getStorageLong(p, key, null); - } - - /** - * Returns the concrete config class used by this adaptation. - */ - Class getConfigurationClass(); - - /** - * Registers the config class used for load/reload of this adaptation. - */ - void registerConfiguration(Class type); - - /** - * @return true when this adaptation is runtime-enabled. - */ - boolean isEnabled(); - - /** - * @return true when this adaptation cannot be unlearned. - */ - boolean isPermanent(); - - /** - * Returns the live config instance for this adaptation. - */ - T getConfig(); - - /** - * Builds the advancement tree node for this adaptation. - */ - AdaptAdvancement buildAdvancements(); - - /** - * Adds per-level stat lines to GUI elements. - */ - void addStats(int level, Element v); - - /** - * @return base upgrade cost. - */ - int getBaseCost(); - - /** - * @return localized adaptation description. - */ - String getDescription(); - - /** - * @return base icon for this adaptation. - */ - Material getIcon(); - - /** - * @return owning skill of this adaptation. - */ - Skill getSkill(); - - /** - * Assigns the owning skill for this adaptation. - */ - void setSkill(Skill skill); - - /** - * @return internal adaptation key. - */ - String getName(); - - /** - * @return initial unlock/upgrade cost component. - */ - int getInitialCost(); - - /** - * @return per-level cost multiplier. - */ - double getCostFactor(); - - /** - * @return recipes registered by this adaptation. - */ - List getRecipes(); - - /** - * @return brewing recipes registered by this adaptation. - */ - List getBrewingRecipes(); - - /** - * Called while building advancement trees to append custom nodes. - */ - void onRegisterAdvancements(List advancements); - - /** - * Returns the active protector set after applying this adaptation's override config. - */ - default Set getProtectors() { - return AdaptationRuntimeGuards.getProtectors(this); - } - - /** - * World-policy check for breaking a block. - */ - default boolean canBlockBreak(Player player, Location blockLocation) { - return evaluateWorldPolicy(protector -> protector.canBlockBreak(player, blockLocation, this)); - } - - /** - * World-policy check for placing a block. - */ - default boolean canBlockPlace(Player player, Location blockLocation) { - return evaluateWorldPolicy(protector -> protector.canBlockPlace(player, blockLocation, this)); - } - - /** - * World-policy check for PVP damage. - */ - default boolean canPVP(Player player, Location victimLocation) { - return evaluateWorldPolicy(protector -> protector.canPVP(player, victimLocation, this)); - } - - /** - * World-policy check for PVE damage. - */ - default boolean canPVE(Player player, Location victimLocation) { - return evaluateWorldPolicy(protector -> protector.canPVE(player, victimLocation, this)); - } - - /** - * World-policy check for generic interaction. - */ - default boolean canInteract(Player player, Location targetLocation) { - return evaluateWorldPolicy(protector -> protector.canInteract(player, targetLocation, this)); - } - - /** - * Returns true when attacker can damage target under current world policies. - */ - default boolean canDamageTarget(Player attacker, Entity target) { - return AdaptationRuntimeGuards.canDamageTarget(this, attacker, target); - } - - /** - * Returns active level only when player is in survival. - */ - default int getActiveSurvivalLevel(Player player) { - return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player); - } - - /** - * Returns active level only when the player meets the supplied requirement. - */ - default int getActiveLevel(Player player, Predicate requirement) { - return AdaptationRuntimeGuards.getActiveLevel(this, player, requirement); - } - - /** - * Returns active-survival level only when the player meets the supplied requirement. - */ - default int getActiveSurvivalLevel(Player player, Predicate requirement) { - return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player, requirement); - } - - /** - * Returns active level gated by interaction permission at a location. - */ - default int getActiveInteractLevel(Player player, Location location) { - return AdaptationRuntimeGuards.getActiveInteractLevel(this, player, location); - } - - /** - * Returns active level gated by block-break permission at a location. - */ - default int getActiveBlockBreakLevel(Player player, Location location) { - return AdaptationRuntimeGuards.getActiveBlockBreakLevel(this, player, location); - } - - /** - * Returns active level gated by block-place permission at a location. - */ - default int getActiveBlockPlaceLevel(Player player, Location location) { - return AdaptationRuntimeGuards.getActiveBlockPlaceLevel(this, player, location); - } - - /** - * Returns active level gated by PVP/PVE policy for a specific target. - */ - default int getActiveDamageLevel(Player attacker, Entity target) { - return AdaptationRuntimeGuards.getActiveDamageLevel(this, attacker, target); - } - - /** - * Resolves a context only when adaptation is active and interact is allowed. - */ - default BlockActionContext resolveInteractContext(Player player, Location location) { - return AdaptationRuntimeGuards.resolveInteractContext(this, player, location); - } - - /** - * Resolves interact context and applies an additional player requirement. - */ - default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement) { - return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement); - } - - /** - * Resolves interact context with optional requirement and survival-only gating. - */ - default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { - return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement, survivalOnly); - } - - /** - * Resolves a context only when adaptation is active and block break is allowed. - */ - default BlockActionContext resolveBlockBreakContext(Player player, Location location) { - return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location); - } - - /** - * Resolves block-break context and applies an additional player requirement. - */ - default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement) { - return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement); - } - - /** - * Resolves block-break context with optional requirement and survival-only gating. - */ - default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { - return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement, survivalOnly); - } - - /** - * Resolves a context only when adaptation is active and block place is allowed. - */ - default BlockActionContext resolveBlockPlaceContext(Player player, Location location) { - return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location); - } - - /** - * Resolves block-place context and applies an additional player requirement. - */ - default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement) { - return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement); - } - - /** - * Resolves block-place context with optional requirement and survival-only gating. - */ - default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { - return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement, survivalOnly); - } - - /** - * Resolves a context requiring both interact and block-break permission. - */ - default BlockActionContext resolveInteractBreakContext(Player player, Location location) { - return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location); - } - - /** - * Resolves interact+break context and applies an additional player requirement. - */ - default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement) { - return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement); - } - - /** - * Resolves interact+break context with optional requirement and survival-only gating. - */ - default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { - return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement, survivalOnly); - } - - /** - * Returns a validated main-hand item when adaptation is active and requirement passes. - */ - default ItemStack readyMainHand(Player player, Predicate requirement) { - return AdaptationRuntimeGuards.readyMainHand(this, player, requirement); - } - - /** - * Returns a validated active main-hand item without extra requirement checks. - */ - default ItemStack readyMainHand(Player player) { - return readyMainHand(player, null); - } - - /** - * Resolves melee combat context from an entity-damage event. - */ - default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { - return AdaptationRuntimeGuards.resolveMeleeContext(this, event, mainHandRequirement); - } - - /** - * Resolves melee combat context from an entity-damage event. - */ - default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event) { - return resolveMeleeContext(event, null); - } - - /** - * Resolves generic player attack context from an entity-damage event. - */ - default AttackContext resolveAttackContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { - return AdaptationRuntimeGuards.resolveAttackContext(this, event, mainHandRequirement); - } - - /** - * Resolves generic player attack context from an entity-damage event. - */ - default AttackContext resolveAttackContext(EntityDamageByEntityEvent event) { - return resolveAttackContext(event, null); - } - - /** - * Resolves projectile combat context from an entity-damage event. - */ - default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event, Predicate projectileRequirement) { - return AdaptationRuntimeGuards.resolveProjectileContext(this, event, projectileRequirement); - } - - /** - * Resolves projectile combat context from an entity-damage event. - */ - default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event) { - return resolveProjectileContext(event, null); - } - - /** - * World-policy check for chest access. - */ - default boolean canAccessChest(Player player, Location chestLocation) { - return evaluateWorldPolicy(protector -> protector.canAccessChest(player, chestLocation, this)); - } - - /** - * World-policy check for generic region access at the player's current location. - */ - default boolean checkRegion(Player player) { - return evaluateWorldPolicy(protector -> protector.checkRegion(player, player.getLocation(), this)); - } - - private boolean evaluateWorldPolicy(Predicate evaluator) { - long start = System.nanoTime(); - try { - for (Protector protector : getProtectors()) { - if (!evaluator.test(protector)) { - return false; - } - } - return true; - } finally { - WorldPolicyLatencyTelemetry.recordNanos(System.nanoTime() - start); - } - } - - /** - * Returns true when this adaptation currently conflicts with another active adaptation. - */ - default boolean hasUsageConflict(Player p) { - return AdaptationRuntimeGuards.hasUsageConflict(this, p); - } - - /** - * Runtime-ready level check (ownership + enabled + world/protection/conflict checks). - */ - default int getActiveLevel(Player p) { - return AdaptationRuntimeGuards.getActiveLevel(this, p); - } - - /** - * Ownership check only (learned level > 0), without runtime gating. - */ - default boolean hasAdaptation(Player p) { - return getLevel(p) > 0; - } - - /** - * Runtime-usable state (ownership + active world/permission/conflict checks). - */ - default boolean hasActiveAdaptation(Player p) { - return getActiveLevel(p) > 0; - } - - /** - * Generic attack context payload. - */ - record AttackContext(Player attacker, Entity target, ItemStack mainHand, int level) { - } - - /** - * Block-action context payload. - */ - record BlockActionContext(Player player, Location location, int level) { - } - - /** - * Melee-specific context payload. - */ - record MeleeContext(Player attacker, LivingEntity target, ItemStack mainHand, int level) { - } - - /** - * Projectile-specific context payload. - */ - record ProjectileContext(Player attacker, LivingEntity target, Projectile projectile, int level) { - } - - /** - * Raw learned level (ignores runtime gating such as world/protection/conflict). - */ - default int getLevel(Player p) { - return AdaptationRuntimeGuards.getLevel(this, p); - } - - /** - * Learned level normalized to 0..1. - */ - default double getLevelPercent(Player p) { - if (!this.isEnabled()) { - return 0; - } - if (!this.getSkill().isEnabled()) { - return 0; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - return 0.0; - } - return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), getLevel(p))), 1); - } - - /** - * Level normalized to 0..1 using an explicit level value. - */ - default double getLevelPercent(int p) { - return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), p)), 1); - } - - /** - * Cost for purchasing exactly this level step. - */ - default int getCostFor(int level) { - return (int) (Math.max(1, getBaseCost() + (getBaseCost() * (level * getCostFactor())))) + (level == 1 ? getInitialCost() : 0); - } - - /** - * Power cost delta for level transitions. - */ - default int getPowerCostFor(int level, int myLevel) { - return level - myLevel; - } - - /** - * Cumulative cost to move from current level to target level. - */ - default int getCostFor(int level, int myLevel) { - if (myLevel >= level) { - return 0; - } - - - int c = 0; - - for (int i = myLevel + 1; i <= level; i++) { - c += getCostFor(i); - } - - return c; - } - - /** - * Cumulative refund amount when reducing from current level to target level. - */ - default int getRefundCostFor(int level, int myLevel) { - if (myLevel <= level) { - return 0; - } - - int c = 0; - - for (int i = level + 1; i <= myLevel; i++) { - c += getCostFor(i); + } + } + + return defaultValue; + } + + /** + * Typed storage convenience helper for doubles. + */ + default Double getStorageDouble(Player p, String key) { + return getStorageDouble(p, key, null); + } + + /** + * Typed storage convenience helper for booleans. + */ + default Boolean getStorageBoolean(Player p, String key, Boolean defaultValue) { + return getStorage(p, key, defaultValue); + } + + /** + * Typed storage convenience helper for booleans. + */ + default Boolean getStorageBoolean(Player p, String key) { + return getStorage(p, key); + } + + /** + * Typed storage convenience helper for longs. + */ + default Long getStorageLong(Player p, String key, Long defaultValue) { + Object value = getStorage(p, key, (Object) defaultValue); + if (value == null) { + return defaultValue; + } + + if (value instanceof Number number) { + return number.longValue(); + } + + if (value instanceof String stringValue) { + try { + return Long.parseLong(stringValue.trim()); + } catch (NumberFormatException ignored) { + return defaultValue; + } + } + + return defaultValue; + } + + /** + * Typed storage convenience helper for longs. + */ + default Long getStorageLong(Player p, String key) { + return getStorageLong(p, key, null); + } + + /** + * Returns the concrete config class used by this adaptation. + */ + Class getConfigurationClass(); + + /** + * Registers the config class used for load/reload of this adaptation. + */ + void registerConfiguration(Class type); + + /** + * @return true when this adaptation is runtime-enabled. + */ + boolean isEnabled(); + + /** + * @return true when this adaptation cannot be unlearned. + */ + boolean isPermanent(); + + /** + * Returns the live config instance for this adaptation. + */ + T getConfig(); + + /** + * Builds the advancement tree node for this adaptation. + */ + AdaptAdvancement buildAdvancements(); + + /** + * Adds per-level stat lines to GUI elements. + */ + void addStats(int level, Element v); + + /** + * @return base upgrade cost. + */ + int getBaseCost(); + + /** + * @return localized adaptation description. + */ + String getDescription(); + + /** + * @return base icon for this adaptation. + */ + Material getIcon(); + + /** + * @return owning skill of this adaptation. + */ + Skill getSkill(); + + /** + * Assigns the owning skill for this adaptation. + */ + void setSkill(Skill skill); + + /** + * @return internal adaptation key. + */ + String getName(); + + /** + * @return initial unlock/upgrade cost component. + */ + int getInitialCost(); + + /** + * @return per-level cost multiplier. + */ + double getCostFactor(); + + /** + * @return recipes registered by this adaptation. + */ + List getRecipes(); + + /** + * @return brewing recipes registered by this adaptation. + */ + List getBrewingRecipes(); + + /** + * Called while building advancement trees to append custom nodes. + */ + void onRegisterAdvancements(List advancements); + + /** + * Returns the active protector set after applying this adaptation's override + * config. + */ + default Set getProtectors() { + return AdaptationRuntimeGuards.getProtectors(this); + } + + /** + * World-policy check for breaking a block. + */ + default boolean canBlockBreak(Player player, Location blockLocation) { + return evaluateWorldPolicy(protector -> protector.canBlockBreak(player, blockLocation, this)); + } + + /** + * World-policy check for placing a block. + */ + default boolean canBlockPlace(Player player, Location blockLocation) { + return evaluateWorldPolicy(protector -> protector.canBlockPlace(player, blockLocation, this)); + } + + /** + * World-policy check for PVP damage. + */ + default boolean canPVP(Player player, Location victimLocation) { + return evaluateWorldPolicy(protector -> protector.canPVP(player, victimLocation, this)); + } + + /** + * World-policy check for PVE damage. + */ + default boolean canPVE(Player player, Location victimLocation) { + return evaluateWorldPolicy(protector -> protector.canPVE(player, victimLocation, this)); + } + + /** + * World-policy check for generic interaction. + */ + default boolean canInteract(Player player, Location targetLocation) { + return evaluateWorldPolicy(protector -> protector.canInteract(player, targetLocation, this)); + } + + /** + * Returns true when attacker can damage target under current world policies. + */ + default boolean canDamageTarget(Player attacker, Entity target) { + return AdaptationRuntimeGuards.canDamageTarget(this, attacker, target); + } + + /** + * Returns active level only when player is in survival. + */ + default int getActiveSurvivalLevel(Player player) { + return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player); + } + + /** + * Returns active level only when the player meets the supplied requirement. + */ + default int getActiveLevel(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.getActiveLevel(this, player, requirement); + } + + /** + * Returns active-survival level only when the player meets the supplied + * requirement. + */ + default int getActiveSurvivalLevel(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.getActiveSurvivalLevel(this, player, requirement); + } + + /** + * Returns active level gated by interaction permission at a location. + */ + default int getActiveInteractLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveInteractLevel(this, player, location); + } + + /** + * Returns active level gated by block-break permission at a location. + */ + default int getActiveBlockBreakLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveBlockBreakLevel(this, player, location); + } + + /** + * Returns active level gated by block-place permission at a location. + */ + default int getActiveBlockPlaceLevel(Player player, Location location) { + return AdaptationRuntimeGuards.getActiveBlockPlaceLevel(this, player, location); + } + + /** + * Returns active level gated by PVP/PVE policy for a specific target. + */ + default int getActiveDamageLevel(Player attacker, Entity target) { + return AdaptationRuntimeGuards.getActiveDamageLevel(this, attacker, target); + } + + /** + * Resolves a context only when adaptation is active and interact is allowed. + */ + default BlockActionContext resolveInteractContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location); + } + + /** + * Resolves interact context and applies an additional player requirement. + */ + default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement); + } + + /** + * Resolves interact context with optional requirement and survival-only + * gating. + */ + default BlockActionContext resolveInteractContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveInteractContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context only when adaptation is active and block break is + * allowed. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location); + } + + /** + * Resolves block-break context and applies an additional player requirement. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement); + } + + /** + * Resolves block-break context with optional requirement and survival-only + * gating. + */ + default BlockActionContext resolveBlockBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveBlockBreakContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context only when adaptation is active and block place is + * allowed. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location); + } + + /** + * Resolves block-place context and applies an additional player requirement. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement); + } + + /** + * Resolves block-place context with optional requirement and survival-only + * gating. + */ + default BlockActionContext resolveBlockPlaceContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveBlockPlaceContext(this, player, location, requirement, survivalOnly); + } + + /** + * Resolves a context requiring both interact and block-break permission. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location); + } + + /** + * Resolves interact+break context and applies an additional player + * requirement. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement); + } + + /** + * Resolves interact+break context with optional requirement and survival-only + * gating. + */ + default BlockActionContext resolveInteractBreakContext(Player player, Location location, Predicate requirement, boolean survivalOnly) { + return AdaptationRuntimeGuards.resolveInteractBreakContext(this, player, location, requirement, survivalOnly); + } + + /** + * Returns a validated main-hand item when adaptation is active and + * requirement passes. + */ + default ItemStack readyMainHand(Player player, Predicate requirement) { + return AdaptationRuntimeGuards.readyMainHand(this, player, requirement); + } + + /** + * Returns a validated active main-hand item without extra requirement + * checks. + */ + default ItemStack readyMainHand(Player player) { + return readyMainHand(player, null); + } + + /** + * Resolves melee combat context from an entity-damage event. + */ + default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + return AdaptationRuntimeGuards.resolveMeleeContext(this, event, mainHandRequirement); + } + + /** + * Resolves melee combat context from an entity-damage event. + */ + default MeleeContext resolveMeleeContext(EntityDamageByEntityEvent event) { + return resolveMeleeContext(event, null); + } + + /** + * Resolves generic player attack context from an entity-damage event. + */ + default AttackContext resolveAttackContext(EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + return AdaptationRuntimeGuards.resolveAttackContext(this, event, mainHandRequirement); + } + + /** + * Resolves generic player attack context from an entity-damage event. + */ + default AttackContext resolveAttackContext(EntityDamageByEntityEvent event) { + return resolveAttackContext(event, null); + } + + /** + * Resolves projectile combat context from an entity-damage event. + */ + default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event, Predicate projectileRequirement) { + return AdaptationRuntimeGuards.resolveProjectileContext(this, event, projectileRequirement); + } + + /** + * Resolves projectile combat context from an entity-damage event. + */ + default ProjectileContext resolveProjectileContext(EntityDamageByEntityEvent event) { + return resolveProjectileContext(event, null); + } + + /** + * World-policy check for chest access. + */ + default boolean canAccessChest(Player player, Location chestLocation) { + return evaluateWorldPolicy(protector -> protector.canAccessChest(player, chestLocation, this)); + } + + /** + * World-policy check for generic region access at the player's current + * location. + */ + default boolean checkRegion(Player player) { + return evaluateWorldPolicy(protector -> protector.checkRegion(player, player.getLocation(), this)); + } + + private boolean evaluateWorldPolicy(Predicate evaluator) { + long start = System.nanoTime(); + try { + for (Protector protector : getProtectors()) { + if (!evaluator.test(protector)) { + return false; } - - return c; - } - - /** - * UI display name for this adaptation. - */ - default String getDisplayName() { - return AdaptationGuiSupport.getDisplayName(this); - } - - /** - * UI display name with level suffix. - */ - default String getDisplayName(int level) { - return AdaptationGuiSupport.getDisplayName(this, level); - } - - /** - * UI display name with numeric level but no roman formatting. - */ - default String getDisplayNameNoRoman(int level) { - return AdaptationGuiSupport.getDisplayNameNoRoman(this, level); - } - - /** - * Returns targeted block face from player look direction. - */ - default BlockFace getBlockFace(Player player, int maxrange) { - return AdaptationGuiSupport.getBlockFace(player, maxrange); - } - - /** - * Returns generated custom model binding for this adaptation icon. - */ - default CustomModel getModel() { - return AdaptationGuiSupport.getModel(this); - } - - /** - * Returns generated custom model binding for a specific adaptation level. - */ - default CustomModel getModel(int level) { - return AdaptationGuiSupport.getModel(this, level); - } - - /** - * Opens adaptation GUI and optionally applies blacklist permission checks. - */ - default boolean openGui(Player player, boolean checkPermissions) { - return AdaptationGuiSupport.openGui(this, player, checkPermissions); - } - - /** - * Opens page 0 of this adaptation GUI. - */ - default void openGui(Player player) { - AdaptationGuiSupport.openGui(this, player); - } - - /** - * Opens a specific page of this adaptation GUI. - */ - default void openGui(Player player, int page) { - AdaptationGuiSupport.openGui(this, player, page); - } - - /** - * Checks whether permanent learn confirmation is pending for this player/level. - */ - default boolean isPermanentLearnConfirmationPending(Player player, int level) { - return AdaptationGuiSupport.isPermanentLearnConfirmationPending(player, this, level); - } - - /** - * Consumes pending permanent learn confirmation for this player/level. - */ - default boolean consumePermanentLearnConfirmation(Player player, int level) { - return AdaptationGuiSupport.consumePermanentLearnConfirmation(player, this, level); - } - - /** - * Unlearns levels from this adaptation. - */ - default void unlearn(Player player, int lvl, boolean force) { - AdaptationGuiSupport.unlearn(this, player, lvl, force); - } - - /** - * Learns levels into this adaptation. - */ - default void learn(Player player, int lvl, boolean force) { - AdaptationGuiSupport.learn(this, player, lvl, force); - } - - /** - * Returns true when the provided recipe belongs to this adaptation. - */ - default boolean isAdaptationRecipe(Recipe recipe) { - return AdaptationGuiSupport.isAdaptationRecipe(this, recipe); - } + } + return true; + } finally { + WorldPolicyLatencyTelemetry.recordNanos(System.nanoTime() - start); + } + } + + /** + * Returns true when this adaptation currently conflicts with another active + * adaptation. + */ + default boolean hasUsageConflict(Player p) { + return AdaptationRuntimeGuards.hasUsageConflict(this, p); + } + + /** + * Runtime-ready level check (ownership + enabled + world/protection/conflict + * checks). + */ + default int getActiveLevel(Player p) { + return AdaptationRuntimeGuards.getActiveLevel(this, p); + } + + /** + * Ownership check only (learned level > 0), without runtime gating. + */ + default boolean hasAdaptation(Player p) { + return getLevel(p) > 0; + } + + /** + * Runtime-usable state (ownership + active world/permission/conflict + * checks). + */ + default boolean hasActiveAdaptation(Player p) { + return getActiveLevel(p) > 0; + } + + /** + * Raw learned level (ignores runtime gating such as + * world/protection/conflict). + */ + default int getLevel(Player p) { + return AdaptationRuntimeGuards.getLevel(this, p); + } + + /** + * Learned level normalized to 0..1. + */ + default double getLevelPercent(Player p) { + if (!this.isEnabled()) { + return 0; + } + if (!this.getSkill().isEnabled()) { + return 0; + } + if (!p.getClass().getSimpleName().equals("CraftPlayer")) { + return 0.0; + } + return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), getLevel(p))), 1); + } + + /** + * Level normalized to 0..1 using an explicit level value. + */ + default double getLevelPercent(int p) { + return Math.min(Math.max(0, M.lerpInverse(0, getMaxLevel(), p)), 1); + } + + /** + * Cost for purchasing exactly this level step. + */ + default int getCostFor(int level) { + return (int) (Math.max(1, getBaseCost() + (getBaseCost() * (level * getCostFactor())))) + (level == 1 ? getInitialCost() : 0); + } + + /** + * Power cost delta for level transitions. + */ + default int getPowerCostFor(int level, int myLevel) { + return level - myLevel; + } + + /** + * Cumulative cost to move from current level to target level. + */ + default int getCostFor(int level, int myLevel) { + if (myLevel >= level) { + return 0; + } + + + int c = 0; + + for (int i = myLevel + 1; i <= level; i++) { + c += getCostFor(i); + } + + return c; + } + + /** + * Cumulative refund amount when reducing from current level to target level. + */ + default int getRefundCostFor(int level, int myLevel) { + if (myLevel <= level) { + return 0; + } + + int c = 0; + + for (int i = level + 1; i <= myLevel; i++) { + c += getCostFor(i); + } + + return c; + } + + /** + * UI display name for this adaptation. + */ + default String getDisplayName() { + return AdaptationGuiSupport.getDisplayName(this); + } + + /** + * UI display name with level suffix. + */ + default String getDisplayName(int level) { + return AdaptationGuiSupport.getDisplayName(this, level); + } + + /** + * UI display name with numeric level but no roman formatting. + */ + default String getDisplayNameNoRoman(int level) { + return AdaptationGuiSupport.getDisplayNameNoRoman(this, level); + } + + /** + * Returns targeted block face from player look direction. + */ + default BlockFace getBlockFace(Player player, int maxrange) { + return AdaptationGuiSupport.getBlockFace(player, maxrange); + } + + /** + * Returns generated custom model binding for this adaptation icon. + */ + default CustomModel getModel() { + return AdaptationGuiSupport.getModel(this); + } + + /** + * Returns generated custom model binding for a specific adaptation level. + */ + default CustomModel getModel(int level) { + return AdaptationGuiSupport.getModel(this, level); + } + + /** + * Opens adaptation GUI and optionally applies blacklist permission checks. + */ + default boolean openGui(Player player, boolean checkPermissions) { + return AdaptationGuiSupport.openGui(this, player, checkPermissions); + } + + /** + * Opens page 0 of this adaptation GUI. + */ + default void openGui(Player player) { + AdaptationGuiSupport.openGui(this, player); + } + + /** + * Opens a specific page of this adaptation GUI. + */ + default void openGui(Player player, int page) { + AdaptationGuiSupport.openGui(this, player, page); + } + + /** + * Checks whether permanent learn confirmation is pending for this + * player/level. + */ + default boolean isPermanentLearnConfirmationPending(Player player, int level) { + return AdaptationGuiSupport.isPermanentLearnConfirmationPending(player, this, level); + } + + /** + * Consumes pending permanent learn confirmation for this player/level. + */ + default boolean consumePermanentLearnConfirmation(Player player, int level) { + return AdaptationGuiSupport.consumePermanentLearnConfirmation(player, this, level); + } + + /** + * Unlearns levels from this adaptation. + */ + default void unlearn(Player player, int lvl, boolean force) { + AdaptationGuiSupport.unlearn(this, player, lvl, force); + } + + /** + * Learns levels into this adaptation. + */ + default void learn(Player player, int lvl, boolean force) { + AdaptationGuiSupport.learn(this, player, lvl, force); + } + + /** + * Returns true when the provided recipe belongs to this adaptation. + */ + default boolean isAdaptationRecipe(Recipe recipe) { + return AdaptationGuiSupport.isAdaptationRecipe(this, recipe); + } + + /** + * Generic attack context payload. + */ + record AttackContext(Player attacker, Entity target, ItemStack mainHand, + int level) { + } + + /** + * Block-action context payload. + */ + record BlockActionContext(Player player, Location location, int level) { + } + + /** + * Melee-specific context payload. + */ + record MeleeContext(Player attacker, LivingEntity target, ItemStack mainHand, + int level) { + } + + /** + * Projectile-specific context payload. + */ + record ProjectileContext(Player attacker, LivingEntity target, + Projectile projectile, int level) { + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java index 5d2748117..766529ad9 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java @@ -32,88 +32,88 @@ import java.util.Map; public final class AdaptationEventRegistrar { - private AdaptationEventRegistrar() { + private AdaptationEventRegistrar() { + } + + public static boolean register(Plugin plugin, Listener listener) { + if (!(listener instanceof Adaptation)) { + return false; } - public static boolean register(Plugin plugin, Listener listener) { - if (!(listener instanceof Adaptation)) { - return false; + boolean registeredAny = false; + for (Method method : collectHandlerMethods(listener.getClass()).values()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { + continue; + } + + Class parameterType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(parameterType)) { + continue; + } + + @SuppressWarnings("unchecked") + Class eventType = (Class) parameterType; + try { + method.setAccessible(true); + } catch (Throwable ex) { + Adapt.warn("Failed enabling access to adaptation handler " + + listener.getClass().getName() + "#" + method.getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + continue; + } + + EventExecutor executor = (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; } - boolean registeredAny = false; - for (Method method : collectHandlerMethods(listener.getClass()).values()) { - EventHandler annotation = method.getAnnotation(EventHandler.class); - if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { - continue; - } - - Class parameterType = method.getParameterTypes()[0]; - if (!Event.class.isAssignableFrom(parameterType)) { - continue; - } - - @SuppressWarnings("unchecked") - Class eventType = (Class) parameterType; - try { - method.setAccessible(true); - } catch (Throwable ex) { - Adapt.warn("Failed enabling access to adaptation handler " - + listener.getClass().getName() + "#" + method.getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - continue; - } - - EventExecutor executor = (target, event) -> { - if (!eventType.isAssignableFrom(event.getClass())) { - return; - } - - try { - method.invoke(target, event); - } catch (InvocationTargetException ex) { - throw new EventException(ex.getCause()); - } catch (Throwable ex) { - throw new EventException(ex); - } - }; - - boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); - Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); - registeredAny = true; + try { + method.invoke(target, event); + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable ex) { + throw new EventException(ex); } + }; - return registeredAny; + boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); + Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); + registeredAny = true; } - private static Map collectHandlerMethods(Class type) { - Map methods = new LinkedHashMap<>(); - Class current = type; - while (current != null && current != Object.class) { - for (Method method : current.getDeclaredMethods()) { - if (!method.isAnnotationPresent(EventHandler.class)) { - continue; - } - methods.putIfAbsent(signature(method), method); - } - current = current.getSuperclass(); - } - return methods; - } + return registeredAny; + } - private static String signature(Method method) { - return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + private static Map collectHandlerMethods(Class type) { + Map methods = new LinkedHashMap<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Method method : current.getDeclaredMethods()) { + if (!method.isAnnotationPresent(EventHandler.class)) { + continue; + } + methods.putIfAbsent(signature(method), method); + } + current = current.getSuperclass(); } + return methods; + } - private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { - if (!Cancellable.class.isAssignableFrom(eventType)) { - return annotation.ignoreCancelled(); - } + private static String signature(Method method) { + return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + } - if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { - return false; - } + private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { + if (!Cancellable.class.isAssignableFrom(eventType)) { + return annotation.ignoreCancelled(); + } - return true; + if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { + return false; } + + return true; + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index 2ae364cd0..a218078fd 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -21,20 +21,21 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.recipe.AdaptRecipe; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.UIElement; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.inventorygui.GuiEffects; import art.arcane.adapt.util.common.inventorygui.GuiLayout; import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.volmlib.util.inventorygui.Element; -import art.arcane.volmlib.util.inventorygui.Window; -import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.volmlib.util.math.M; import org.bukkit.Material; import org.bukkit.Sound; @@ -45,487 +46,527 @@ import org.bukkit.inventory.Recipe; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; final class AdaptationGuiSupport { - private static final Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); - private static final long PERMANENT_LEARN_CONFIRM_WINDOW_MS = 6_000L; - private static final long CLOSE_SUPPRESS_MS = 1200L; - private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; - private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); + private static final Map PERMANENT_LEARN_CONFIRMATIONS = new ConcurrentHashMap<>(); + private static final long PERMANENT_LEARN_CONFIRM_WINDOW_MS = 6_000L; + private static final long CLOSE_SUPPRESS_MS = 1200L; + private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; + private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); + + private AdaptationGuiSupport() { + } + + static boolean areParticlesEnabled(Adaptation adaptation, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } - private AdaptationGuiSupport() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + if (effects != null && effects.getAdaptationParticleOverrides() != null && !effects.getAdaptationParticleOverrides().isEmpty()) { + String key = adaptation.getName(); + Boolean override = effects.getAdaptationParticleOverrides().get(key); + if (override == null && key != null) { + override = effects.getAdaptationParticleOverrides().get(key.toLowerCase(Locale.ROOT)); + } + if (override != null && !override) { + return false; + } } - static boolean areParticlesEnabled(Adaptation adaptation, boolean componentEnabled) { - if (!componentEnabled) { - return false; - } + Object config = adaptation.getConfig(); + if (config == null) { + return true; + } - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - if (effects != null && effects.getAdaptationParticleOverrides() != null && !effects.getAdaptationParticleOverrides().isEmpty()) { - String key = adaptation.getName(); - Boolean override = effects.getAdaptationParticleOverrides().get(key); - if (override == null && key != null) { - override = effects.getAdaptationParticleOverrides().get(key.toLowerCase(Locale.ROOT)); - } - if (override != null && !override) { - return false; - } - } + Boolean directToggle = readBooleanField(config, "showParticles"); + if (directToggle != null) { + return directToggle; + } - Object config = adaptation.getConfig(); - if (config == null) { - return true; - } + Boolean genericToggle = readBooleanField(config, "showParticleEffects"); + if (genericToggle != null) { + return genericToggle; + } - Boolean directToggle = readBooleanField(config, "showParticles"); - if (directToggle != null) { - return directToggle; - } + return true; + } - Boolean genericToggle = readBooleanField(config, "showParticleEffects"); - if (genericToggle != null) { - return genericToggle; - } + static boolean areSoundsEnabled(Adaptation adaptation, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } - return true; + Object config = adaptation.getConfig(); + if (config == null) { + return true; } - static boolean areSoundsEnabled(Adaptation adaptation, boolean componentEnabled) { - if (!componentEnabled) { - return false; - } + Boolean directToggle = readBooleanField(config, "showSounds"); + if (directToggle != null) { + return directToggle; + } - Object config = adaptation.getConfig(); - if (config == null) { - return true; - } + return true; + } - Boolean directToggle = readBooleanField(config, "showSounds"); - if (directToggle != null) { - return directToggle; - } - - return true; + static String getDisplayName(Adaptation adaptation) { + if (!adaptation.isEnabled()) { + return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); } - - static String getDisplayName(Adaptation adaptation) { - if (!adaptation.isEnabled()) { - return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); - } - if (!adaptation.getSkill().isEnabled()) { - return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); - } - return C.RESET + "" + C.BOLD + adaptation.getSkill().getColor().toString() + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + if (!adaptation.getSkill().isEnabled()) { + return C.DARK_GRAY + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); } + return C.RESET + "" + C.BOLD + adaptation.getSkill().getColor().toString() + Form.capitalizeWords(adaptation.getName().replaceAll("\\Q" + adaptation.getSkill().getName() + "-\\E", "").replaceAll("\\Q-\\E", " ")); + } - static String getDisplayName(Adaptation adaptation, int level) { - if (!adaptation.isEnabled()) { - return adaptation.getDisplayName(); - } - if (!adaptation.getSkill().isEnabled()) { - return adaptation.getDisplayName(); - } - if (level >= 1) { - return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + Form.toRoman(level) + C.RESET; - } - - return adaptation.getDisplayName(); + static String getDisplayName(Adaptation adaptation, int level) { + if (!adaptation.isEnabled()) { + return adaptation.getDisplayName(); + } + if (!adaptation.getSkill().isEnabled()) { + return adaptation.getDisplayName(); + } + if (level >= 1) { + return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + Form.toRoman(level) + C.RESET; } - static String getDisplayNameNoRoman(Adaptation adaptation, int level) { - if (level >= 1) { - return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; - } + return adaptation.getDisplayName(); + } - return adaptation.getDisplayName(); + static String getDisplayNameNoRoman(Adaptation adaptation, int level) { + if (level >= 1) { + return adaptation.getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; } - static BlockFace getBlockFace(Player player, int maxrange) { - List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, maxrange); - if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) return null; - Block targetBlock = lastTwoTargetBlocks.get(1); - Block adjacentBlock = lastTwoTargetBlocks.get(0); - return targetBlock.getFace(adjacentBlock); + return adaptation.getDisplayName(); + } + + static BlockFace getBlockFace(Player player, int maxrange) { + List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, maxrange); + if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) + return null; + Block targetBlock = lastTwoTargetBlocks.get(1); + Block adjacentBlock = lastTwoTargetBlocks.get(0); + return targetBlock.getFace(adjacentBlock); + } + + static CustomModel getModel(Adaptation adaptation) { + return CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "icon"); + } + + static CustomModel getModel(Adaptation adaptation, int level) { + CustomModel model = CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "level-" + level); + if (model.material() == adaptation.getIcon() && model.model() == 0) + model = CustomModel.get(Material.PAPER, "snippets", "gui", "level", String.valueOf(level)); + if (model.material() == Material.PAPER && model.model() == 0) + model = adaptation.getModel(); + return model; + } + + static boolean openGui(Adaptation adaptation, Player player, boolean checkPermissions) { + if (checkPermissions && adaptation.hasBlacklistPermission(player, adaptation)) { + return false; + } else { + openGui(adaptation, player); + return true; } + } - static CustomModel getModel(Adaptation adaptation) { - return CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "icon"); - } + static void openGui(Adaptation adaptation, Player player) { + openGui(adaptation, player, 0); + } - static CustomModel getModel(Adaptation adaptation, int level) { - CustomModel model = CustomModel.get(adaptation.getIcon(), "adaptation", adaptation.getName(), "level-" + level); - if (model.material() == adaptation.getIcon() && model.model() == 0) - model = CustomModel.get(Material.PAPER, "snippets", "gui", "level", String.valueOf(level)); - if (model.material() == Material.PAPER && model.model() == 0) - model = adaptation.getModel(); - return model; + static void openGui(Adaptation adaptation, Player player, int page) { + if (!adaptation.isEnabled()) { + return; } - - static boolean openGui(Adaptation adaptation, Player player, boolean checkPermissions) { - if (checkPermissions && adaptation.hasBlacklistPermission(player, adaptation)) { - return false; - } else { - openGui(adaptation, player); - return true; - } + if (!adaptation.getSkill().isEnabled()) { + return; } - - static void openGui(Adaptation adaptation, Player player) { - openGui(adaptation, player, 0); + if (!J.isPrimaryThread()) { + int targetPage = page; + J.s(() -> openGui(adaptation, player, targetPage)); + return; } - static void openGui(Adaptation adaptation, Player player, int page) { - if (!adaptation.isEnabled()) { - return; - } - if (!adaptation.getSkill().isEnabled()) { - return; - } - if (!J.isPrimaryThread()) { - int targetPage = page; - J.s(() -> openGui(adaptation, player, targetPage)); - return; - } + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); + + boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); + GuiLayout.PagePlan plan = GuiLayout.plan(adaptation.getMaxLevel(), reserveNavigation); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(adaptation.getMaxLevel(), start + plan.itemsPerPage()); + + int mylevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + + long k = adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).getKnowledge(); + + UIWindow w = new UIWindow(Adapt.instance, player); + GuiTheme.apply(w, "skill/" + adaptation.getSkill().getName() + "/" + adaptation.getName()); + w.setViewportHeight(plan.rows()); + + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; + } + + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + int lvl = rowStart + i + 1; + int pos = GuiLayout.centeredPosition(i, rowCount); + int c = adaptation.getCostFor(lvl, mylevel); + int rc = adaptation.getRefundCostFor(lvl - 1, mylevel); + int pc = adaptation.getPowerCostFor(lvl, mylevel); + boolean pendingPermanentConfirm = isPermanentLearnConfirmationPending(player, adaptation, lvl); + Element de = new UIElement("lp-" + lvl + "g") + .setMaterial(new MaterialBlock(adaptation.getIcon())) + .setBaseItemStack(adaptation.getModel(lvl).toItemStack()) + .setName(adaptation.getDisplayName(lvl)) + .setEnchanted(mylevel >= lvl) + .setProgress(1D) + .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 40)) + .addLore(mylevel >= lvl ? ("") : ("" + C.WHITE + c + C.GRAY + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost") + " " + (AdaptConfig.get().isHardcoreNoRefunds() ? C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds") : ""))) + .addLore(mylevel >= lvl ? AdaptConfig.get().isHardcoreNoRefunds() ? (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds")) : (adaptation.isPermanent() ? "" : (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearn_refund") + " " + C.GREEN + rc + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost"))) : (k >= c ? (C.BLUE + Localizer.dLocalize("snippets.adapt_menu.click_learn") + " " + adaptation.getDisplayName(lvl)) : (k == 0 ? (C.RED + Localizer.dLocalize("snippets.adapt_menu.no_knowledge")) : (C.RED + "(" + Localizer.dLocalize("snippets.adapt_menu.you_only_have") + " " + C.WHITE + k + C.RED + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_available") + ")")))) + .addLore(mylevel < lvl && adaptation.getPlayer(player).getData().hasPowerAvailable(pc) ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : mylevel >= lvl ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : C.RED + Localizer.dLocalize("snippets.adapt_menu.not_enough_power") + "\n" + C.RED + Localizer.dLocalize("snippets.adapt_menu.how_to_level_up")) + .addLore((adaptation.isPermanent() ? C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") : "")) + .addLore(adaptation.isPermanent() && mylevel < lvl + ? (pendingPermanentConfirm + ? C.GOLD + "" + C.BOLD + "Click again now to confirm permanent learn." + : C.YELLOW + "Double-click required to confirm permanent learn.") + : "") + .onLeftClick((e) -> { + AdaptPlayer adaptPlayer = adaptation.getPlayer(player); + PlayerSkillLine skillLine = adaptPlayer.getSkillLine(adaptation.getSkill().getName()); + if (skillLine == null) { + spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); + return; + } + + int delayTicks = AdaptConfig.get().getLearnUnlearnButtonDelayTicks(); + int currentLevel = skillLine.getAdaptationLevel(adaptation.getName()); + if (currentLevel >= lvl) { + adaptation.unlearn(player, lvl, false); + int updatedLevel = skillLine.getAdaptationLevel(adaptation.getName()); + if (updatedLevel < currentLevel) { + spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); + if (delayTicks != 0) { + player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearned") + " " + adaptation.getDisplayName(currentLevel), 1, 10, 11); + } + closeAndReopenAfterLevelChange(adaptation, player, currentPage, delayTicks); + return; + } - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); - - boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); - GuiLayout.PagePlan plan = GuiLayout.plan(adaptation.getMaxLevel(), reserveNavigation); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(adaptation.getMaxLevel(), start + plan.itemsPerPage()); - - int mylevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); - - long k = adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).getKnowledge(); - - UIWindow w = new UIWindow(Adapt.instance, player); - GuiTheme.apply(w, "skill/" + adaptation.getSkill().getName() + "/" + adaptation.getName()); - w.setViewportHeight(plan.rows()); - - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - int lvl = rowStart + i + 1; - int pos = GuiLayout.centeredPosition(i, rowCount); - int c = adaptation.getCostFor(lvl, mylevel); - int rc = adaptation.getRefundCostFor(lvl - 1, mylevel); - int pc = adaptation.getPowerCostFor(lvl, mylevel); - boolean pendingPermanentConfirm = isPermanentLearnConfirmationPending(player, adaptation, lvl); - Element de = new UIElement("lp-" + lvl + "g") - .setMaterial(new MaterialBlock(adaptation.getIcon())) - .setBaseItemStack(adaptation.getModel(lvl).toItemStack()) - .setName(adaptation.getDisplayName(lvl)) - .setEnchanted(mylevel >= lvl) - .setProgress(1D) - .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 40)) - .addLore(mylevel >= lvl ? ("") : ("" + C.WHITE + c + C.GRAY + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost") + " " + (AdaptConfig.get().isHardcoreNoRefunds() ? C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds") : ""))) - .addLore(mylevel >= lvl ? AdaptConfig.get().isHardcoreNoRefunds() ? (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds")) : (adaptation.isPermanent() ? "" : (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearn_refund") + " " + C.GREEN + rc + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost"))) : (k >= c ? (C.BLUE + Localizer.dLocalize("snippets.adapt_menu.click_learn") + " " + adaptation.getDisplayName(lvl)) : (k == 0 ? (C.RED + Localizer.dLocalize("snippets.adapt_menu.no_knowledge")) : (C.RED + "(" + Localizer.dLocalize("snippets.adapt_menu.you_only_have") + " " + C.WHITE + k + C.RED + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_available") + ")")))) - .addLore(mylevel < lvl && adaptation.getPlayer(player).getData().hasPowerAvailable(pc) ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : mylevel >= lvl ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : C.RED + Localizer.dLocalize("snippets.adapt_menu.not_enough_power") + "\n" + C.RED + Localizer.dLocalize("snippets.adapt_menu.how_to_level_up")) - .addLore((adaptation.isPermanent() ? C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") : "")) - .addLore(adaptation.isPermanent() && mylevel < lvl - ? (pendingPermanentConfirm - ? C.GOLD + "" + C.BOLD + "Click again now to confirm permanent learn." - : C.YELLOW + "Double-click required to confirm permanent learn.") - : "") - .onLeftClick((e) -> { - if (mylevel >= lvl) { - adaptation.unlearn(player, lvl, false); - spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.7f, 1.355f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.4f, 0.755f); - suppressClose(player); - if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { - if (adaptation.isPermanent()) { - spw.play(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 0.5f, 1.355f); - player.sendTitle(" ", C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") + " " + adaptation.getDisplayName(mylevel), 1, 10, 11); - } else { - player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearned") + " " + adaptation.getDisplayName(mylevel), 1, 10, 11); - } - } - J.s(() -> openAdaptationPage(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); - return; - } - - if (k >= c && adaptation.getPlayer(player).getData().hasPowerAvailable(pc)) { - if (adaptation.isPermanent() && !consumePermanentLearnConfirmation(player, adaptation, lvl)) { - spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); - player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); - J.s(() -> openAdaptationPage(adaptation, player, currentPage), 1); - return; - } - - if (adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).spendKnowledge(c)) { - adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl); - spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.9f, 1.355f); - spw.play(player.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.7f, 0.355f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_POWER_SELECT, 0.4f, 0.155f); - spw.play(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 0.2f, 1.455f); - if (adaptation.isPermanent()) { - spw.play(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.355f); - spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); - } - suppressClose(player); - if (AdaptConfig.get().getLearnUnlearnButtonDelayTicks() != 0) { - player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + adaptation.getDisplayName(lvl), 1, 5, 11); - } - J.s(() -> openAdaptationPage(adaptation, player, currentPage), AdaptConfig.get().getLearnUnlearnButtonDelayTicks()); - } else { - spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); - } - } else { - spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); - } - }); - de.addLore(" "); - adaptation.addStats(lvl, de); - reveal.add(new GuiEffects.Placement(pos, row, de)); - } - } - GuiEffects.applyReveal(w, reveal); - - if (plan.hasNavigationRow()) { - int navRow = plan.rows() - 1; - int jumpPages = 5; - int jumpBack = Math.max(0, currentPage - jumpPages); - int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); - if (currentPage > 0) { - w.setElement(-4, navRow, new UIElement("adapt-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage - 1)) - .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpBack))); - w.setElement(-3, navRow, new UIElement("adapt-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> openAdaptationPage(adaptation, player, 0))); - } - if (currentPage < plan.pageCount() - 1) { - w.setElement(4, navRow, new UIElement("adapt-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage + 1)) - .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpForward))); - w.setElement(3, navRow, new UIElement("adapt-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> openAdaptationPage(adaptation, player, plan.pageCount() - 1))); - } - - int from = adaptation.getMaxLevel() <= 0 ? 0 : (start + 1); - int to = adaptation.getMaxLevel() <= 0 ? 0 : end; - w.setElement(-1, navRow, new UIElement("adapt-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + adaptation.getMaxLevel()) - .setProgress(1D)); - - if (AdaptConfig.get().isGuiBackButton()) { - w.setElement(0, navRow, new UIElement("back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> navigateBack(adaptation, player))); - } + spw.play(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 0.5f, 1.355f); + if (delayTicks != 0) { + player.sendTitle(" ", C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") + " " + adaptation.getDisplayName(currentLevel), 1, 10, 11); + } + J.s(() -> openAdaptationPage(adaptation, player, currentPage), delayTicks); + return; + } + + long currentKnowledge = skillLine.getKnowledge(); + if (currentKnowledge >= c && adaptPlayer.getData().hasPowerAvailable(pc)) { + if (adaptation.isPermanent() && !consumePermanentLearnConfirmation(player, adaptation, lvl)) { + spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); + player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); + J.s(() -> openAdaptationPage(adaptation, player, currentPage), 1); + return; + } - } + if (skillLine.spendKnowledge(c)) { + skillLine.setAdaptation(adaptation, lvl); + spw.play(player.getLocation(), Sound.BLOCK_NETHER_GOLD_ORE_PLACE, 0.9f, 1.355f); + spw.play(player.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1.7f, 0.355f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_POWER_SELECT, 0.4f, 0.155f); + spw.play(player.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 0.2f, 1.455f); + if (adaptation.isPermanent()) { + spw.play(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.355f); + spw.play(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_1, 0.7f, 1.355f); + } + if (delayTicks != 0) { + player.sendTitle(" ", C.GRAY + Localizer.dLocalize("snippets.adapt_menu.learned") + " " + adaptation.getDisplayName(lvl), 1, 5, 11); + } + closeAndReopenAfterLevelChange(adaptation, player, currentPage, delayTicks); + } else { + spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); + } + } else { + spw.play(player.getLocation(), Sound.BLOCK_BAMBOO_HIT, 0.7f, 1.855f); + } + }); + de.addLore(" "); + adaptation.addStats(lvl, de); + reveal.add(new GuiEffects.Placement(pos, row, de)); + } + } + GuiEffects.applyReveal(w, reveal); + + if (plan.hasNavigationRow()) { + int navRow = plan.rows() - 1; + int jumpPages = 5; + int jumpBack = Math.max(0, currentPage - jumpPages); + int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); + if (currentPage > 0) { + w.setElement(-4, navRow, new UIElement("adapt-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") + .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage - 1)) + .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpBack))); + w.setElement(-3, navRow, new UIElement("adapt-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> openAdaptationPage(adaptation, player, 0))); + } + if (currentPage < plan.pageCount() - 1) { + w.setElement(4, navRow, new UIElement("adapt-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") + .onLeftClick((e) -> openAdaptationPage(adaptation, player, currentPage + 1)) + .onRightClick((e) -> openAdaptationPage(adaptation, player, jumpForward))); + w.setElement(3, navRow, new UIElement("adapt-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> openAdaptationPage(adaptation, player, plan.pageCount() - 1))); + } + + int from = adaptation.getMaxLevel() <= 0 ? 0 : (start + 1); + int to = adaptation.getMaxLevel() <= 0 ? 0 : end; + w.setElement(-1, navRow, new UIElement("adapt-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + adaptation.getMaxLevel()) + .setProgress(1D)); + + if (AdaptConfig.get().isGuiBackButton()) { + w.setElement(0, navRow, new UIElement("back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) + .onLeftClick((e) -> navigateBack(adaptation, player))); + } - w.setTitle(adaptation.getDisplayName()); - w.onClosed((vv) -> J.s(() -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); } - static boolean isPermanentLearnConfirmationPending(Player player, Adaptation adaptation, int level) { - if (player == null || adaptation == null) { - return false; - } + w.setTitle(adaptation.getDisplayName()); + w.onClosed((vv) -> J.s(() -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); + w.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } - Long until = PERMANENT_LEARN_CONFIRMATIONS.get(permanentConfirmKey(player, adaptation, level)); - return until != null && until >= M.ms(); + static boolean isPermanentLearnConfirmationPending(Player player, Adaptation adaptation, int level) { + if (player == null || adaptation == null) { + return false; } - static boolean consumePermanentLearnConfirmation(Player player, Adaptation adaptation, int level) { - if (player == null) { - return false; - } + Long until = PERMANENT_LEARN_CONFIRMATIONS.get(permanentConfirmKey(player, adaptation, level)); + return until != null && until >= M.ms(); + } - long now = M.ms(); - PERMANENT_LEARN_CONFIRMATIONS.entrySet().removeIf(e -> e.getValue() < now); + static boolean consumePermanentLearnConfirmation(Player player, Adaptation adaptation, int level) { + if (player == null) { + return false; + } - String key = permanentConfirmKey(player, adaptation, level); - Long until = PERMANENT_LEARN_CONFIRMATIONS.get(key); - if (until != null && until >= now) { - PERMANENT_LEARN_CONFIRMATIONS.remove(key); - return true; - } + long now = M.ms(); + PERMANENT_LEARN_CONFIRMATIONS.entrySet().removeIf(e -> e.getValue() < now); - String prefix = permanentConfirmPrefix(player, adaptation); - PERMANENT_LEARN_CONFIRMATIONS.keySet().removeIf(existing -> existing.startsWith(prefix)); - PERMANENT_LEARN_CONFIRMATIONS.put(key, now + PERMANENT_LEARN_CONFIRM_WINDOW_MS); - return false; + String key = permanentConfirmKey(player, adaptation, level); + Long until = PERMANENT_LEARN_CONFIRMATIONS.get(key); + if (until != null && until >= now) { + PERMANENT_LEARN_CONFIRMATIONS.remove(key); + return true; } - static void unlearn(Adaptation adaptation, Player player, int lvl, boolean force) { - if (adaptation.isPermanent() && !force) { - return; - } - int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); - int rc = adaptation.getRefundCostFor(lvl - 1, myLevel); - if (!AdaptConfig.get().isHardcoreNoRefunds()) { - adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).giveKnowledge(rc); - } - adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl - 1); - } + String prefix = permanentConfirmPrefix(player, adaptation); + PERMANENT_LEARN_CONFIRMATIONS.keySet().removeIf(existing -> existing.startsWith(prefix)); + PERMANENT_LEARN_CONFIRMATIONS.put(key, now + PERMANENT_LEARN_CONFIRM_WINDOW_MS); + return false; + } - static void learn(Adaptation adaptation, Player player, int lvl, boolean force) { - int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); - int c = adaptation.getCostFor(lvl, myLevel); - if (adaptation.getPlayer(player).getData().hasPowerAvailable(c) || force) { - if (adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).spendKnowledge(c) || force) { - adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl); - } - } + static void unlearn(Adaptation adaptation, Player player, int lvl, boolean force) { + if (adaptation.isPermanent() && !force) { + return; } + int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + int rc = adaptation.getRefundCostFor(lvl - 1, myLevel); + if (!AdaptConfig.get().isHardcoreNoRefunds()) { + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).giveKnowledge(rc); + } + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl - 1); + } + + static void learn(Adaptation adaptation, Player player, int lvl, boolean force) { + int myLevel = adaptation.getPlayer(player).getSkillLine(adaptation.getSkill().getName()).getAdaptationLevel(adaptation.getName()); + int c = adaptation.getCostFor(lvl, myLevel); + if (adaptation.getPlayer(player).getData().hasPowerAvailable(c) || force) { + if (adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).spendKnowledge(c) || force) { + adaptation.getPlayer(player).getData().getSkillLine(adaptation.getSkill().getName()).setAdaptation(adaptation, lvl); + } + } + } - static boolean isAdaptationRecipe(Adaptation adaptation, Recipe recipe) { - if (!adaptation.isEnabled()) { - return false; - } - if (!adaptation.getSkill().isEnabled()) { - return false; - } - for (AdaptRecipe i : adaptation.getRecipes()) { - if (i.is(recipe)) { - return true; - } - } - return false; + static boolean isAdaptationRecipe(Adaptation adaptation, Recipe recipe) { + if (!adaptation.isEnabled()) { + return false; } + if (!adaptation.getSkill().isEnabled()) { + return false; + } + for (AdaptRecipe i : adaptation.getRecipes()) { + if (i.is(recipe)) { + return true; + } + } + return false; + } - private static Boolean readBooleanField(Object source, String fieldName) { - if (source == null || fieldName == null || fieldName.isBlank()) { - return null; - } + private static Boolean readBooleanField(Object source, String fieldName) { + if (source == null || fieldName == null || fieldName.isBlank()) { + return null; + } - Class current = source.getClass(); - while (current != null) { - try { - Field field = current.getDeclaredField(fieldName); - field.setAccessible(true); - Object value = field.get(source); - if (value instanceof Boolean bool) { - return bool; - } - return null; - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } catch (Throwable ex) { - Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } + Class current = source.getClass(); + while (current != null) { + try { + Field field = current.getDeclaredField(fieldName); + field.setAccessible(true); + Object value = field.get(source); + if (value instanceof Boolean bool) { + return bool; } - return null; + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; + } } - private static void openAdaptationPage(Adaptation adaptation, Player player, int page) { - suppressClose(player); - openGui(adaptation, player, page); - } - - private static void navigateBack(Adaptation adaptation, Player player) { - playCloseSound(player); - suppressClose(player); - adaptation.getSkill().openGui(player); - } + return null; + } - private static void onGuiClosed(Adaptation adaptation, Player player, boolean openPrevGui) { - if (player == null) { - return; - } + private static void openAdaptationPage(Adaptation adaptation, Player player, int page) { + suppressClose(player); + openGui(adaptation, player, page); + } - if (consumeCloseSuppression(player)) { - return; - } + private static void closeAndReopenAfterLevelChange(Adaptation adaptation, Player player, int page, int delayTicks) { + closeCurrentAdaptationGui(player); + int reopenDelay = Math.max(0, delayTicks); + J.s(() -> reopenAdaptationPageIfReady(adaptation, player, page), reopenDelay); + } - playCloseSound(player); - if (openPrevGui) { - J.s(() -> { - if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { - adaptation.getSkill().openGui(player); - } - }, 1); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); - } + private static void closeCurrentAdaptationGui(Player player) { + if (player == null || !player.isOnline()) { + return; } - private static void playCloseSound(Player player) { - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); + suppressClose(player); + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + if (player.getOpenInventory() != null && player.getOpenInventory().getTopInventory().getType() != InventoryType.CRAFTING) { + player.closeInventory(); } + } - private static void suppressClose(Player player) { - if (player == null) { - return; - } + private static void reopenAdaptationPageIfReady(Adaptation adaptation, Player player, int page) { + if (player == null || !player.isOnline()) { + return; + } - UUID playerId = player.getUniqueId(); - long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; - CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); - J.s(() -> { - Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); - if (current != null && current.longValue() == suppressUntil) { - CLOSE_SUPPRESS_UNTIL.remove(playerId); - } - }, CLOSE_SUPPRESS_CLEAR_TICKS); + if (player.getOpenInventory() == null || player.getOpenInventory().getTopInventory().getType() != InventoryType.CRAFTING) { + return; } - private static boolean consumeCloseSuppression(Player player) { - if (player == null) { - return false; - } + openAdaptationPage(adaptation, player, page); + } - Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); - if (until == null) { - return false; - } + private static void navigateBack(Adaptation adaptation, Player player) { + playCloseSound(player); + suppressClose(player); + adaptation.getSkill().openGui(player); + } + + private static void onGuiClosed(Adaptation adaptation, Player player, boolean openPrevGui) { + if (player == null) { + return; + } - if (until >= System.currentTimeMillis()) { - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return true; + if (consumeCloseSuppression(player)) { + return; + } + + playCloseSound(player); + if (openPrevGui) { + J.s(() -> { + if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { + adaptation.getSkill().openGui(player); } + }, 1); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static void playCloseSound(Player player) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 0.655f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 0.855f); + } + + private static void suppressClose(Player player) { + if (player == null) { + return; + } - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return false; + UUID playerId = player.getUniqueId(); + long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; + CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); + J.s(() -> { + Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); + if (current != null && current.longValue() == suppressUntil) { + CLOSE_SUPPRESS_UNTIL.remove(playerId); + } + }, CLOSE_SUPPRESS_CLEAR_TICKS); + } + + private static boolean consumeCloseSuppression(Player player) { + if (player == null) { + return false; } - private static String permanentConfirmPrefix(Player player, Adaptation adaptation) { - return player.getUniqueId() + "|" + adaptation.getName() + "|"; + Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); + if (until == null) { + return false; } - private static String permanentConfirmKey(Player player, Adaptation adaptation, int level) { - return permanentConfirmPrefix(player, adaptation) + level; + if (until >= System.currentTimeMillis()) { + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return true; } + + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return false; + } + + private static String permanentConfirmPrefix(Player player, Adaptation adaptation) { + return player.getUniqueId() + "|" + adaptation.getName() + "|"; + } + + private static String permanentConfirmKey(Player player, Adaptation adaptation, int level) { + return permanentConfirmPrefix(player, adaptation) + level; + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index d439fbad9..00852d97e 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -46,703 +46,708 @@ import java.util.function.Predicate; final class AdaptationRuntimeGuards { - private static final Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); - private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); - private static final Map PROTECTOR_CACHE = new ConcurrentHashMap<>(); - private static final Map USAGE_CONFLICT_CACHE = new ConcurrentHashMap<>(); - private static final int ACTIVE_LEVEL_CACHE_SOFT_LIMIT = 16_384; - private static final int ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS = 64; - private static final int ACTIVE_LEVEL_CACHE_RETENTION_TICKS = 2; - private static volatile long lastActiveCacheSweepTick = Long.MIN_VALUE; - - private AdaptationRuntimeGuards() { - } - - static void withPlayerThread(Adaptation adaptation, Player p, Runnable runnable) { - try { - if (p == null || runnable == null) { - return; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - J.runEntity(p, () -> withPlayerThread(adaptation, p, runnable)); - return; - } - - runnable.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded player runnable for adaptation " + adaptation.getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - } - - static void withPlayerThread(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { - try { - if (p == null || cancellable == null || runnable == null) { - return; - } - - if (cancellable.isCancelled()) { - return; - } + private static final Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); + private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); + private static final Map PROTECTOR_CACHE = new ConcurrentHashMap<>(); + private static final Map USAGE_CONFLICT_CACHE = new ConcurrentHashMap<>(); + private static final int ACTIVE_LEVEL_CACHE_SOFT_LIMIT = 16_384; + private static final int ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS = 64; + private static final int ACTIVE_LEVEL_CACHE_RETENTION_TICKS = 2; + private static volatile long lastActiveCacheSweepTick = Long.MIN_VALUE; + + private AdaptationRuntimeGuards() { + } + + static void withPlayerThread(Adaptation adaptation, Player p, Runnable runnable) { + try { + if (p == null || runnable == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, () -> withPlayerThread(adaptation, p, runnable)); + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded player runnable for adaptation " + adaptation.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withPlayerThread(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { + try { + if (p == null || cancellable == null || runnable == null) { + return; + } + + if (cancellable.isCancelled()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, () -> withPlayerThread(adaptation, p, cancellable, runnable)); + return; + } + + if (cancellable.isCancelled()) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded cancellable player runnable for adaptation " + adaptation.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withAdaptedPlayer(Adaptation adaptation, Player p, Runnable runnable) { + withPlayerThread(adaptation, p, () -> { + if (!adaptation.hasActiveAdaptation(p)) { + return; + } + runnable.run(); + }); + } + + static void withAdaptedPlayer(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { + withPlayerThread(adaptation, p, cancellable, () -> { + if (!adaptation.hasActiveAdaptation(p)) { + return; + } + runnable.run(); + }); + } + + static void withActiveLevel(Adaptation adaptation, Player p, IntConsumer consumer) { + withPlayerThread(adaptation, p, () -> { + int level = getActiveLevel(adaptation, p); + if (level <= 0) { + return; + } + consumer.accept(level); + }); + } + + static void withActiveLevel(Adaptation adaptation, Player p, Cancellable cancellable, IntConsumer consumer) { + withPlayerThread(adaptation, p, cancellable, () -> { + int level = getActiveLevel(adaptation, p); + if (level <= 0) { + return; + } + consumer.accept(level); + }); + } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - J.runEntity(p, () -> withPlayerThread(adaptation, p, cancellable, runnable)); - return; - } - - if (cancellable.isCancelled()) { - return; - } - - runnable.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded cancellable player runnable for adaptation " + adaptation.getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + static String adaptationRewardKey(Adaptation adaptation, String rewardKey) { + String suffix = rewardKey == null ? "" : rewardKey.trim(); + if (suffix.isEmpty()) { + suffix = "use"; } + return "adaptation:" + adaptation.getName() + ":" + suffix; + } - static void withAdaptedPlayer(Adaptation adaptation, Player p, Runnable runnable) { - withPlayerThread(adaptation, p, () -> { - if (!adaptation.hasActiveAdaptation(p)) { - return; - } - runnable.run(); - }); - } - - static void withAdaptedPlayer(Adaptation adaptation, Player p, Cancellable cancellable, Runnable runnable) { - withPlayerThread(adaptation, p, cancellable, () -> { - if (!adaptation.hasActiveAdaptation(p)) { - return; - } - runnable.run(); - }); - } - - static void withActiveLevel(Adaptation adaptation, Player p, IntConsumer consumer) { - withPlayerThread(adaptation, p, () -> { - int level = getActiveLevel(adaptation, p); - if (level <= 0) { - return; - } - consumer.accept(level); - }); - } - - static void withActiveLevel(Adaptation adaptation, Player p, Cancellable cancellable, IntConsumer consumer) { - withPlayerThread(adaptation, p, cancellable, () -> { - int level = getActiveLevel(adaptation, p); - if (level <= 0) { - return; - } - consumer.accept(level); - }); - } - - static String adaptationRewardKey(Adaptation adaptation, String rewardKey) { - String suffix = rewardKey == null ? "" : rewardKey.trim(); - if (suffix.isEmpty()) { - suffix = "use"; - } - return "adaptation:" + adaptation.getName() + ":" + suffix; + static void awardUsageBaselineXp(Adaptation adaptation, Player p, int level) { + if (p == null || level <= 0 || !p.getClass().getSimpleName().equals("CraftPlayer")) { + return; } - static void awardUsageBaselineXp(Adaptation adaptation, Player p, int level) { - if (p == null || level <= 0 || !p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - - AdaptConfig.AdaptationXp cfg = AdaptConfig.get().getAdaptationXp(); - if (cfg == null || !cfg.isUsageBaselineEnabled()) { - return; - } - - long now = M.ms(); - long cooldown = Math.max(250L, cfg.getUsageBaselineCooldownMillis()); - String key = p.getUniqueId() + "|" + adaptation.getName(); - Long next = USAGE_BASELINE_XP_COOLDOWNS.get(key); - if (next != null && next > now) { - return; - } - - if (USAGE_BASELINE_XP_COOLDOWNS.size() > 6000) { - USAGE_BASELINE_XP_COOLDOWNS.entrySet().removeIf(i -> i.getValue() <= now); - } - - double reward = cfg.getUsageBaselineXp() + ((Math.max(1, level) - 1) * cfg.getUsageBaselineXpPerLevel()); - if (reward <= 0) { - return; - } - - USAGE_BASELINE_XP_COOLDOWNS.put(key, now + cooldown); - adaptation.xpSilent(p, reward, "baseline-use"); + AdaptConfig.AdaptationXp cfg = AdaptConfig.get().getAdaptationXp(); + if (cfg == null || !cfg.isUsageBaselineEnabled()) { + return; } - static boolean canUse(Adaptation adaptation, AdaptPlayer player) { - Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + adaptation.getName() + "..."); - AdaptAdaptationUseEvent e = new AdaptAdaptationUseEvent(!Bukkit.isPrimaryThread(), player, adaptation); - Bukkit.getServer().getPluginManager().callEvent(e); - return (!e.isCancelled()); + long now = M.ms(); + long cooldown = Math.max(250L, cfg.getUsageBaselineCooldownMillis()); + String key = p.getUniqueId() + "|" + adaptation.getName(); + Long next = USAGE_BASELINE_XP_COOLDOWNS.get(key); + if (next != null && next > now) { + return; } - static boolean canUse(Adaptation adaptation, Player player) { - return canUse(adaptation, adaptation.getPlayer(player)); + if (USAGE_BASELINE_XP_COOLDOWNS.size() > 6000) { + USAGE_BASELINE_XP_COOLDOWNS.entrySet().removeIf(i -> i.getValue() <= now); } - static boolean hasBlacklistPermission(Adaptation adaptation, Player p, Adaptation targetAdaptation) { - if (p.isOp()) { - return false; - } - Adaptation target = targetAdaptation == null ? adaptation : targetAdaptation; - String blacklistPermission = "adapt.blacklist." + target.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); - - return p.hasPermission(blacklistPermission); + double reward = cfg.getUsageBaselineXp() + ((Math.max(1, level) - 1) * cfg.getUsageBaselineXpPerLevel()); + if (reward <= 0) { + return; } - static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity target) { - if (attacker == null || target == null) { - return false; - } + USAGE_BASELINE_XP_COOLDOWNS.put(key, now + cooldown); + adaptation.xpSilent(p, reward, "baseline-use"); + } - if (target instanceof Player victim) { - return adaptation.canPVP(attacker, victim.getLocation()); - } + static boolean canUse(Adaptation adaptation, AdaptPlayer player) { + Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + adaptation.getName() + "..."); + AdaptAdaptationUseEvent e = new AdaptAdaptationUseEvent(!Bukkit.isPrimaryThread(), player, adaptation); + Bukkit.getServer().getPluginManager().callEvent(e); + return (!e.isCancelled()); + } - return adaptation.canPVE(attacker, target.getLocation()); - } + static boolean canUse(Adaptation adaptation, Player player) { + return canUse(adaptation, adaptation.getPlayer(player)); + } - static int getActiveSurvivalLevel(Adaptation adaptation, Player player) { - if (player == null || player.getGameMode() != GameMode.SURVIVAL) { - return 0; - } - - return getActiveLevel(adaptation, player); + static boolean hasBlacklistPermission(Adaptation adaptation, Player p, Adaptation targetAdaptation) { + if (p.isOp()) { + return false; } + Adaptation target = targetAdaptation == null ? adaptation : targetAdaptation; + String blacklistPermission = "adapt.blacklist." + target.getName().replaceAll("-", ""); + Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); - static int getActiveLevel(Adaptation adaptation, Player player, Predicate requirement) { - int level = getActiveLevel(adaptation, player); - if (level <= 0) { - return 0; - } + return p.hasPermission(blacklistPermission); + } - if (requirement != null && !requirement.test(player)) { - return 0; - } - - return level; + static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity target) { + if (attacker == null || target == null) { + return false; } - static int getActiveSurvivalLevel(Adaptation adaptation, Player player, Predicate requirement) { - int level = getActiveSurvivalLevel(adaptation, player); - if (level <= 0) { - return 0; - } - - if (requirement != null && !requirement.test(player)) { - return 0; - } - - return level; + if (target instanceof Player victim) { + return adaptation.canPVP(attacker, victim.getLocation()); } - static int getActiveInteractLevel(Adaptation adaptation, Player player, Location location) { - int level = getActiveLevel(adaptation, player); - if (level <= 0 || location == null) { - return 0; - } + return adaptation.canPVE(attacker, target.getLocation()); + } - return adaptation.canInteract(player, location) ? level : 0; + static int getActiveSurvivalLevel(Adaptation adaptation, Player player) { + if (player == null || player.getGameMode() != GameMode.SURVIVAL) { + return 0; } - static int getActiveBlockBreakLevel(Adaptation adaptation, Player player, Location location) { - int level = getActiveLevel(adaptation, player); - if (level <= 0 || location == null) { - return 0; - } + return getActiveLevel(adaptation, player); + } - return adaptation.canBlockBreak(player, location) ? level : 0; + static int getActiveLevel(Adaptation adaptation, Player player, Predicate requirement) { + int level = getActiveLevel(adaptation, player); + if (level <= 0) { + return 0; } - static int getActiveBlockPlaceLevel(Adaptation adaptation, Player player, Location location) { - int level = getActiveLevel(adaptation, player); - if (level <= 0 || location == null) { - return 0; - } - - return adaptation.canBlockPlace(player, location) ? level : 0; + if (requirement != null && !requirement.test(player)) { + return 0; } - static int getActiveDamageLevel(Adaptation adaptation, Player attacker, Entity target) { - int level = getActiveLevel(adaptation, attacker); - if (level <= 0 || target == null) { - return 0; - } + return level; + } - return canDamageTarget(adaptation, attacker, target) ? level : 0; + static int getActiveSurvivalLevel(Adaptation adaptation, Player player, Predicate requirement) { + int level = getActiveSurvivalLevel(adaptation, player); + if (level <= 0) { + return 0; } - static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location) { - return resolveInteractContext(adaptation, player, location, null, false); + if (requirement != null && !requirement.test(player)) { + return 0; } - static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { - return resolveInteractContext(adaptation, player, location, requirement, false); - } + return level; + } - static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { - int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); - if (level <= 0 || location == null || !adaptation.canInteract(player, location)) { - return null; - } - - return new Adaptation.BlockActionContext(player, location, level); + static int getActiveInteractLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; } - static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location) { - return resolveBlockBreakContext(adaptation, player, location, null, false); - } + return adaptation.canInteract(player, location) ? level : 0; + } - static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { - return resolveBlockBreakContext(adaptation, player, location, requirement, false); + static int getActiveBlockBreakLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; } - static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { - int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); - if (level <= 0 || location == null || !adaptation.canBlockBreak(player, location)) { - return null; - } + return adaptation.canBlockBreak(player, location) ? level : 0; + } - return new Adaptation.BlockActionContext(player, location, level); + static int getActiveBlockPlaceLevel(Adaptation adaptation, Player player, Location location) { + int level = getActiveLevel(adaptation, player); + if (level <= 0 || location == null) { + return 0; } - static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location) { - return resolveBlockPlaceContext(adaptation, player, location, null, false); - } + return adaptation.canBlockPlace(player, location) ? level : 0; + } - static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { - return resolveBlockPlaceContext(adaptation, player, location, requirement, false); + static int getActiveDamageLevel(Adaptation adaptation, Player attacker, Entity target) { + int level = getActiveLevel(adaptation, attacker); + if (level <= 0 || target == null) { + return 0; } - static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { - int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); - if (level <= 0 || location == null || !adaptation.canBlockPlace(player, location)) { - return null; - } + return canDamageTarget(adaptation, attacker, target) ? level : 0; + } - return new Adaptation.BlockActionContext(player, location, level); - } + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location) { + return resolveInteractContext(adaptation, player, location, null, false); + } - static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location) { - return resolveInteractBreakContext(adaptation, player, location, null, false); - } + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveInteractContext(adaptation, player, location, requirement, false); + } - static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { - return resolveInteractBreakContext(adaptation, player, location, requirement, false); + static Adaptation.BlockActionContext resolveInteractContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canInteract(player, location)) { + return null; } - static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { - Adaptation.BlockActionContext context = resolveInteractContext(adaptation, player, location, requirement, survivalOnly); - if (context == null || !adaptation.canBlockBreak(context.player(), context.location())) { - return null; - } + return new Adaptation.BlockActionContext(player, location, level); + } - return context; - } + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location) { + return resolveBlockBreakContext(adaptation, player, location, null, false); + } - static ItemStack readyMainHand(Adaptation adaptation, Player player, Predicate requirement) { - if (player == null) { - return null; - } + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveBlockBreakContext(adaptation, player, location, requirement, false); + } - ItemStack hand = player.getInventory().getItemInMainHand(); - if (!adaptation.isItem(hand)) { - return null; - } + static Adaptation.BlockActionContext resolveBlockBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canBlockBreak(player, location)) { + return null; + } - if (requirement != null && (!requirement.test(hand) || player.hasCooldown(hand.getType()))) { - return null; - } + return new Adaptation.BlockActionContext(player, location, level); + } - return hand; - } + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location) { + return resolveBlockPlaceContext(adaptation, player, location, null, false); + } - static Adaptation.MeleeContext resolveMeleeContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { - Adaptation.AttackContext attack = resolveAttackContext(adaptation, event, mainHandRequirement); - if (attack == null || !(attack.target() instanceof LivingEntity target)) { - return null; - } + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveBlockPlaceContext(adaptation, player, location, requirement, false); + } - return new Adaptation.MeleeContext(attack.attacker(), target, attack.mainHand(), attack.level()); + static Adaptation.BlockActionContext resolveBlockPlaceContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + int level = resolveActionLevel(adaptation, player, requirement, survivalOnly); + if (level <= 0 || location == null || !adaptation.canBlockPlace(player, location)) { + return null; } - static Adaptation.AttackContext resolveAttackContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { - if (event == null || !(event.getDamager() instanceof Player attacker)) { - return null; - } + return new Adaptation.BlockActionContext(player, location, level); + } - int level = getActiveLevel(adaptation, attacker); - if (level <= 0) { - return null; - } + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location) { + return resolveInteractBreakContext(adaptation, player, location, null, false); + } - ItemStack hand = readyMainHand(adaptation, attacker, mainHandRequirement); - if (mainHandRequirement != null && hand == null) { - return null; - } + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement) { + return resolveInteractBreakContext(adaptation, player, location, requirement, false); + } - Entity target = event.getEntity(); - if (!canDamageTarget(adaptation, attacker, target)) { - return null; - } - - return new Adaptation.AttackContext(attacker, target, hand, level); + static Adaptation.BlockActionContext resolveInteractBreakContext(Adaptation adaptation, Player player, Location location, Predicate requirement, boolean survivalOnly) { + Adaptation.BlockActionContext context = resolveInteractContext(adaptation, player, location, requirement, survivalOnly); + if (context == null || !adaptation.canBlockBreak(context.player(), context.location())) { + return null; } - static Adaptation.ProjectileContext resolveProjectileContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate projectileRequirement) { - if (event == null - || !(event.getDamager() instanceof Projectile projectile) - || !(projectile.getShooter() instanceof Player attacker) - || !(event.getEntity() instanceof LivingEntity target)) { - return null; - } + return context; + } - if (projectileRequirement != null && !projectileRequirement.test(projectile)) { - return null; - } - - int level = getActiveLevel(adaptation, attacker); - if (level <= 0) { - return null; - } - - if (!canDamageTarget(adaptation, attacker, target)) { - return null; - } - - return new Adaptation.ProjectileContext(attacker, target, projectile, level); + static ItemStack readyMainHand(Adaptation adaptation, Player player, Predicate requirement) { + if (player == null) { + return null; } - static boolean hasUsageConflict(Adaptation adaptation, Player p) { - if (adaptation == null || p == null) { - return false; - } + ItemStack hand = player.getInventory().getItemInMainHand(); + if (!adaptation.isItem(hand)) { + return null; + } - Set denied = resolveUsageConflicts(adaptation); - if (denied.isEmpty()) { - return false; - } + if (requirement != null && (!requirement.test(hand) || player.hasCooldown(hand.getType()))) { + return null; + } - AdaptPlayer adaptPlayer = adaptation.getPlayer(p); - for (String conflict : denied) { - if (adaptPlayer.hasAdaptation(conflict)) { - Adapt.verbose("Player " + p.getName() + " has conflicting adaptation " + conflict + " and cannot use " + adaptation.getName()); - return true; - } - } + return hand; + } - return false; + static Adaptation.MeleeContext resolveMeleeContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + Adaptation.AttackContext attack = resolveAttackContext(adaptation, event, mainHandRequirement); + if (attack == null || !(attack.target() instanceof LivingEntity target)) { + return null; } - static Set getProtectors(Adaptation adaptation) { - if (adaptation == null) { - return Collections.emptySet(); - } + return new Adaptation.MeleeContext(attack.attacker(), target, attack.mainHand(), attack.level()); + } - List defaults = Adapt.instance.getProtectorRegistry().getDefaultProtectors(); - List allProtectors = Adapt.instance.getProtectorRegistry().getAllProtectors(); - Map overrides = AdaptConfig.get().getProtectionOverrides().getOrDefault(adaptation.getName(), Collections.emptyMap()); - int signature = buildProtectorSignature(defaults, allProtectors, overrides); - String cacheKey = adaptation.getName(); - ProtectorCacheEntry cached = PROTECTOR_CACHE.get(cacheKey); - if (cached != null && cached.signature() == signature) { - return cached.protectors(); - } + static Adaptation.AttackContext resolveAttackContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate mainHandRequirement) { + if (event == null || !(event.getDamager() instanceof Player attacker)) { + return null; + } - Map byName = new HashMap<>(); - for (Protector protector : allProtectors) { - byName.put(protector.getName(), protector); - } + int level = getActiveLevel(adaptation, attacker); + if (level <= 0) { + return null; + } - Set resolved = new HashSet<>(defaults); - for (Map.Entry entry : overrides.entrySet()) { - String protectorName = entry.getKey(); - Boolean enabled = entry.getValue(); - if (protectorName == null || enabled == null) { - continue; - } - - if (enabled) { - Protector protector = byName.get(protectorName); - if (protector == null) { - Adapt.error("Could not find protector " + protectorName + " for adaptation " + adaptation.getName() + ". Skipping..."); - continue; - } - resolved.add(protector); - continue; - } - - resolved.removeIf(existing -> existing.getName().equals(protectorName)); - } + ItemStack hand = readyMainHand(adaptation, attacker, mainHandRequirement); + if (mainHandRequirement != null && hand == null) { + return null; + } - Set immutable = Collections.unmodifiableSet(new HashSet<>(resolved)); - PROTECTOR_CACHE.put(cacheKey, new ProtectorCacheEntry(signature, immutable)); - return immutable; - } - - static int getActiveLevel(Adaptation adaptation, Player p) { - try { - if (p == null || p.isDead()) { - return 0; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return 0; - } - - long tick = M.tick; - String key = cacheKey(adaptation, p); - ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); - if (cached != null && cached.tick() == tick) { - return cached.level(); - } - - AbilityCheckTelemetry.recordCheckAttempt(); - int level = resolveActiveLevelUncached(adaptation, p); - ACTIVE_LEVEL_CACHE.put(key, new ActiveLevelCacheEntry(tick, level)); - sweepActiveLevelCache(tick); - - if (level > 0) { - AbilityCheckTelemetry.recordSuccessfulCheck(); - } - return level; - } catch (Exception e) { - if (e instanceof IndexOutOfBoundsException) { - Adapt.verbose("Citizens/PacketSpoofing is Messing stuff up again. I hate it."); - Adapt.verbose(e.getMessage()); - } else { - e.printStackTrace(); - } - return 0; - } + Entity target = event.getEntity(); + if (!canDamageTarget(adaptation, attacker, target)) { + return null; } - static int getLevel(Adaptation adaptation, Player p) { - if (p == null) { - return 0; - } - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - return 0; - } - if (!p.getClass().getSimpleName().equals("CraftPlayer")) { - Adapt.verbose("Simple name: " + p.getClass().getSimpleName()); - return 0; - } - if (!adaptation.isEnabled()) { - return 0; - } - if (!adaptation.getSkill().isEnabled()) { - return 0; - } - AdaptPlayer adaptPlayer = adaptation.getPlayer(p); - PlayerSkillLine line = adaptPlayer.getData().getSkillLine(adaptation.getSkill().getName()); - if (line == null) { - return 0; - } - return line.getAdaptationLevel(adaptation.getName()); - } - - static F getStorage(Adaptation adaptation, Player p, String key, F defaultValue) { - PlayerData data = adaptation.getPlayer(p).getData(); - PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); - if (line == null) return defaultValue; - PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); - if (playerAdaptation == null) return defaultValue; - Object o = playerAdaptation.getStorage().get(key); - return o == null ? defaultValue : (F) o; - } - - static boolean setStorage(Adaptation adaptation, Player p, String key, Object value) { - PlayerData data = adaptation.getPlayer(p).getData(); - PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); - if (line == null) return false; - PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); - if (playerAdaptation == null) return false; - if (value == null) { - playerAdaptation.getStorage().remove(key); - return true; - } + return new Adaptation.AttackContext(attacker, target, hand, level); + } - playerAdaptation.getStorage().put(key, value); - return true; + static Adaptation.ProjectileContext resolveProjectileContext(Adaptation adaptation, EntityDamageByEntityEvent event, Predicate projectileRequirement) { + if (event == null + || !(event.getDamager() instanceof Projectile projectile) + || !(projectile.getShooter() instanceof Player attacker) + || !(event.getEntity() instanceof LivingEntity target)) { + return null; } - private static int resolveActionLevel(Adaptation adaptation, Player player, Predicate requirement, boolean survivalOnly) { - if (survivalOnly) { - return getActiveSurvivalLevel(adaptation, player, requirement); - } - - return getActiveLevel(adaptation, player, requirement); + if (projectileRequirement != null && !projectileRequirement.test(projectile)) { + return null; } - private static int resolveActiveLevelUncached(Adaptation adaptation, Player p) { - int level = adaptation.getLevel(p); - if (level <= 0) { - return 0; - } + int level = getActiveLevel(adaptation, attacker); + if (level <= 0) { + return null; + } - if (AdaptConfig.get().blacklistedWorlds.contains(p.getWorld().getName())) { - Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + adaptation.getName()); - return 0; - } - if (p.getGameMode().equals(GameMode.CREATIVE) || p.getGameMode().equals(GameMode.SPECTATOR)) { - Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + adaptation.getName()); - return 0; - } - if (!adaptation.checkRegion(p)) { - Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + adaptation.getName() + " permission."); - return 0; - } + if (!canDamageTarget(adaptation, attacker, target)) { + return null; + } - if (adaptation.hasBlacklistPermission(p, adaptation)) { - Adapt.verbose("Player " + p.getName() + " has blacklist permission for adaptation " + adaptation.getName()); - return 0; - } - if (hasUsageConflict(adaptation, p)) { - return 0; - } - if (!adaptation.canUse(p)) { - Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + adaptation.getName()); - return 0; - } + return new Adaptation.ProjectileContext(attacker, target, projectile, level); + } - awardUsageBaselineXp(adaptation, p, level); - Adapt.verbose("Player " + p.getName() + " used adaptation " + adaptation.getName()); - return level; + static boolean hasUsageConflict(Adaptation adaptation, Player p) { + if (adaptation == null || p == null) { + return false; } - private static String cacheKey(Adaptation adaptation, Player player) { - return player.getUniqueId() + "|" + adaptation.getName(); + Set denied = resolveUsageConflicts(adaptation); + if (denied.isEmpty()) { + return false; } - private static void sweepActiveLevelCache(long tick) { - if (ACTIVE_LEVEL_CACHE.size() <= ACTIVE_LEVEL_CACHE_SOFT_LIMIT) { - return; - } - - long previousSweep = lastActiveCacheSweepTick; - if (previousSweep != Long.MIN_VALUE && tick - previousSweep < ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS) { - return; - } - lastActiveCacheSweepTick = tick; + AdaptPlayer adaptPlayer = adaptation.getPlayer(p); + for (String conflict : denied) { + if (adaptPlayer.hasAdaptation(conflict)) { + Adapt.verbose("Player " + p.getName() + " has conflicting adaptation " + conflict + " and cannot use " + adaptation.getName()); + return true; + } + } + + return false; + } + + static Set getProtectors(Adaptation adaptation) { + if (adaptation == null) { + return Collections.emptySet(); + } + + List defaults = Adapt.instance.getProtectorRegistry().getDefaultProtectors(); + List allProtectors = Adapt.instance.getProtectorRegistry().getAllProtectors(); + Map overrides = AdaptConfig.get().getProtectionOverrides().getOrDefault(adaptation.getName(), Collections.emptyMap()); + int signature = buildProtectorSignature(defaults, allProtectors, overrides); + String cacheKey = adaptation.getName(); + ProtectorCacheEntry cached = PROTECTOR_CACHE.get(cacheKey); + if (cached != null && cached.signature() == signature) { + return cached.protectors(); + } + + Map byName = new HashMap<>(); + for (Protector protector : allProtectors) { + byName.put(protector.getName(), protector); + } + + Set resolved = new HashSet<>(defaults); + for (Map.Entry entry : overrides.entrySet()) { + String protectorName = entry.getKey(); + Boolean enabled = entry.getValue(); + if (protectorName == null || enabled == null) { + continue; + } + + if (enabled) { + Protector protector = byName.get(protectorName); + if (protector == null) { + Adapt.error("Could not find protector " + protectorName + " for adaptation " + adaptation.getName() + ". Skipping..."); + continue; + } + resolved.add(protector); + continue; + } + + resolved.removeIf(existing -> existing.getName().equals(protectorName)); + } + + Set immutable = Collections.unmodifiableSet(new HashSet<>(resolved)); + PROTECTOR_CACHE.put(cacheKey, new ProtectorCacheEntry(signature, immutable)); + return immutable; + } + + static int getActiveLevel(Adaptation adaptation, Player p) { + try { + if (p == null || p.isDead()) { + return 0; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } + + long tick = runtimeCacheTick(); + int learnedLevel = getLevel(adaptation, p); + String key = cacheKey(adaptation, p); + ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); + if (cached != null && cached.tick() == tick && cached.learnedLevel() == learnedLevel) { + return cached.level(); + } + + AbilityCheckTelemetry.recordCheckAttempt(); + int level = resolveActiveLevelUncached(adaptation, p, learnedLevel); + ACTIVE_LEVEL_CACHE.put(key, new ActiveLevelCacheEntry(tick, learnedLevel, level)); + sweepActiveLevelCache(tick); - long minTick = tick - ACTIVE_LEVEL_CACHE_RETENTION_TICKS; - ACTIVE_LEVEL_CACHE.entrySet().removeIf(entry -> entry.getValue().tick() < minTick); + if (level > 0) { + AbilityCheckTelemetry.recordSuccessfulCheck(); + } + return level; + } catch (Exception e) { + if (e instanceof IndexOutOfBoundsException) { + Adapt.verbose("Citizens/PacketSpoofing is Messing stuff up again. I hate it."); + Adapt.verbose(e.getMessage()); + } else { + e.printStackTrace(); + } + return 0; } + } - private static Set resolveUsageConflicts(Adaptation adaptation) { - Map> conflicts = AdaptConfig.get().getAdaptationUsageConflicts(); - if (conflicts == null || conflicts.isEmpty()) { - return Collections.emptySet(); - } + static int getLevel(Adaptation adaptation, Player p) { + if (p == null) { + return 0; + } + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + return 0; + } + if (!p.getClass().getSimpleName().equals("CraftPlayer")) { + Adapt.verbose("Simple name: " + p.getClass().getSimpleName()); + return 0; + } + if (!adaptation.isEnabled()) { + return 0; + } + if (!adaptation.getSkill().isEnabled()) { + return 0; + } + AdaptPlayer adaptPlayer = adaptation.getPlayer(p); + PlayerSkillLine line = adaptPlayer.getData().getSkillLine(adaptation.getSkill().getName()); + if (line == null) { + return 0; + } + return line.getAdaptationLevel(adaptation.getName()); + } - String me = adaptation.getName().toLowerCase(Locale.ROOT); - int signature = buildUsageConflictSignature(conflicts); - UsageConflictCacheEntry cached = USAGE_CONFLICT_CACHE.get(me); - if (cached != null && cached.signature() == signature) { - return cached.denied(); - } + static F getStorage(Adaptation adaptation, Player p, String key, F defaultValue) { + PlayerData data = adaptation.getPlayer(p).getData(); + PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); + if (line == null) return defaultValue; + PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); + if (playerAdaptation == null) return defaultValue; + Object o = playerAdaptation.getStorage().get(key); + return o == null ? defaultValue : (F) o; + } - Set denied = new HashSet<>(); - for (Map.Entry> entry : conflicts.entrySet()) { - String key = entry.getKey(); - List values = entry.getValue(); - if (key == null || values == null) { - continue; - } - - if (key.equalsIgnoreCase(me)) { - for (String value : values) { - if (value != null) { - denied.add(value.toLowerCase(Locale.ROOT)); - } - } - continue; - } - - boolean containsThisAdaptation = false; - for (String value : values) { - if (value != null && me.equals(value.toLowerCase(Locale.ROOT))) { - containsThisAdaptation = true; - break; - } - } - if (containsThisAdaptation) { - denied.add(key.toLowerCase(Locale.ROOT)); - } - } + static boolean setStorage(Adaptation adaptation, Player p, String key, Object value) { + PlayerData data = adaptation.getPlayer(p).getData(); + PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); + if (line == null) return false; + PlayerAdaptation playerAdaptation = line.getAdaptation(adaptation.getName()); + if (playerAdaptation == null) return false; + if (value == null) { + playerAdaptation.getStorage().remove(key); + return true; + } + + playerAdaptation.getStorage().put(key, value); + return true; + } - denied.remove(me); - Set immutable = Collections.unmodifiableSet(new HashSet<>(denied)); - USAGE_CONFLICT_CACHE.put(me, new UsageConflictCacheEntry(signature, immutable)); - return immutable; - } - - private static int buildUsageConflictSignature(Map> conflicts) { - int hash = 1; - for (Map.Entry> entry : conflicts.entrySet()) { - String key = entry.getKey(); - List values = entry.getValue(); - int local = 0; - if (key != null) { - local += key.toLowerCase(Locale.ROOT).hashCode(); - } - if (values != null) { - local += values.size() * 17; - for (String value : values) { - if (value != null) { - local += value.toLowerCase(Locale.ROOT).hashCode(); - } - } - } - hash += local; - } - return hash; + private static int resolveActionLevel(Adaptation adaptation, Player player, Predicate requirement, boolean survivalOnly) { + if (survivalOnly) { + return getActiveSurvivalLevel(adaptation, player, requirement); } - private static int buildProtectorSignature(List defaults, List allProtectors, Map overrides) { - int hash = 1; - hash += defaults.size() * 31; - for (Protector protector : defaults) { - hash += protector.getName().hashCode(); - } - hash += allProtectors.size() * 17; - for (Protector protector : allProtectors) { - hash += protector.getName().hashCode(); + return getActiveLevel(adaptation, player, requirement); + } + + private static int resolveActiveLevelUncached(Adaptation adaptation, Player p, int learnedLevel) { + int level = learnedLevel; + if (level <= 0) { + return 0; + } + + if (AdaptConfig.get().blacklistedWorlds.contains(p.getWorld().getName())) { + Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + adaptation.getName()); + return 0; + } + if (p.getGameMode().equals(GameMode.CREATIVE) || p.getGameMode().equals(GameMode.SPECTATOR)) { + Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + adaptation.getName()); + return 0; + } + if (!adaptation.checkRegion(p)) { + Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + adaptation.getName() + " permission."); + return 0; + } + + if (adaptation.hasBlacklistPermission(p, adaptation)) { + Adapt.verbose("Player " + p.getName() + " has blacklist permission for adaptation " + adaptation.getName()); + return 0; + } + if (hasUsageConflict(adaptation, p)) { + return 0; + } + if (!adaptation.canUse(p)) { + Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + adaptation.getName()); + return 0; + } + + awardUsageBaselineXp(adaptation, p, level); + Adapt.verbose("Player " + p.getName() + " used adaptation " + adaptation.getName()); + return level; + } + + private static String cacheKey(Adaptation adaptation, Player player) { + return player.getUniqueId() + "|" + adaptation.getName(); + } + + private static long runtimeCacheTick() { + return M.ms() / 50L; + } + + private static void sweepActiveLevelCache(long tick) { + if (ACTIVE_LEVEL_CACHE.size() <= ACTIVE_LEVEL_CACHE_SOFT_LIMIT) { + return; + } + + long previousSweep = lastActiveCacheSweepTick; + if (previousSweep != Long.MIN_VALUE && tick - previousSweep < ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS) { + return; + } + lastActiveCacheSweepTick = tick; + + long minTick = tick - ACTIVE_LEVEL_CACHE_RETENTION_TICKS; + ACTIVE_LEVEL_CACHE.entrySet().removeIf(entry -> entry.getValue().tick() < minTick); + } + + private static Set resolveUsageConflicts(Adaptation adaptation) { + Map> conflicts = AdaptConfig.get().getAdaptationUsageConflicts(); + if (conflicts == null || conflicts.isEmpty()) { + return Collections.emptySet(); + } + + String me = adaptation.getName().toLowerCase(Locale.ROOT); + int signature = buildUsageConflictSignature(conflicts); + UsageConflictCacheEntry cached = USAGE_CONFLICT_CACHE.get(me); + if (cached != null && cached.signature() == signature) { + return cached.denied(); + } + + Set denied = new HashSet<>(); + for (Map.Entry> entry : conflicts.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + if (key == null || values == null) { + continue; + } + + if (key.equalsIgnoreCase(me)) { + for (String value : values) { + if (value != null) { + denied.add(value.toLowerCase(Locale.ROOT)); + } } - hash += overrides.size() * 13; - for (Map.Entry entry : overrides.entrySet()) { - String name = entry.getKey(); - Boolean enabled = entry.getValue(); - if (name != null) { - hash += name.hashCode(); - } - if (enabled != null) { - hash += enabled ? 1 : 2; - } + continue; + } + + boolean containsThisAdaptation = false; + for (String value : values) { + if (value != null && me.equals(value.toLowerCase(Locale.ROOT))) { + containsThisAdaptation = true; + break; } - return hash; - } - - private record ActiveLevelCacheEntry(long tick, int level) { - } - - private record ProtectorCacheEntry(int signature, Set protectors) { - } - - private record UsageConflictCacheEntry(int signature, Set denied) { - } + } + if (containsThisAdaptation) { + denied.add(key.toLowerCase(Locale.ROOT)); + } + } + + denied.remove(me); + Set immutable = Collections.unmodifiableSet(new HashSet<>(denied)); + USAGE_CONFLICT_CACHE.put(me, new UsageConflictCacheEntry(signature, immutable)); + return immutable; + } + + private static int buildUsageConflictSignature(Map> conflicts) { + int hash = 1; + for (Map.Entry> entry : conflicts.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + int local = 0; + if (key != null) { + local += key.toLowerCase(Locale.ROOT).hashCode(); + } + if (values != null) { + local += values.size() * 17; + for (String value : values) { + if (value != null) { + local += value.toLowerCase(Locale.ROOT).hashCode(); + } + } + } + hash += local; + } + return hash; + } + + private static int buildProtectorSignature(List defaults, List allProtectors, Map overrides) { + int hash = 1; + hash += defaults.size() * 31; + for (Protector protector : defaults) { + hash += protector.getName().hashCode(); + } + hash += allProtectors.size() * 17; + for (Protector protector : allProtectors) { + hash += protector.getName().hashCode(); + } + hash += overrides.size() * 13; + for (Map.Entry entry : overrides.entrySet()) { + String name = entry.getKey(); + Boolean enabled = entry.getValue(); + if (name != null) { + hash += name.hashCode(); + } + if (enabled != null) { + hash += enabled ? 1 : 2; + } + } + return hash; + } + + private record ActiveLevelCacheEntry(long tick, int learnedLevel, int level) { + } + + private record ProtectorCacheEntry(int signature, Set protectors) { + } + + private record UsageConflictCacheEntry(int signature, Set denied) { + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java index 50aa38bd6..3742f3a13 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/SimpleAdaptation.java @@ -46,260 +46,260 @@ @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleAdaptation extends TickedObject implements Adaptation { - private int maxLevel; - private int initialCost; - private int baseCost; - private double costFactor; - private String displayName; - private Skill skill; - private String description; - private Material icon; - private String name; - private List cachedAdvancements; - private List recipes; - private List brewingRecipes; - private KList statTrackers; - private Class configType; - private volatile T config; - - public SimpleAdaptation(String name) { - super("adaptations", UUID.randomUUID() + "-" + name, 1000); - cachedAdvancements = new ArrayList<>(); - recipes = new ArrayList<>(); - brewingRecipes = new ArrayList<>(); - statTrackers = new KList<>(); - setMaxLevel(5); - setCostFactor(0.45); - setBaseCost(4); - setIcon(Material.PAPER); - setInitialCost(2); - setDescription("No Description Provided"); - this.name = name; + private int maxLevel; + private int initialCost; + private int baseCost; + private double costFactor; + private String displayName; + private Skill skill; + private String description; + private Material icon; + private String name; + private List cachedAdvancements; + private List recipes; + private List brewingRecipes; + private KList statTrackers; + private Class configType; + private volatile T config; + + public SimpleAdaptation(String name) { + super("adaptations", UUID.randomUUID() + "-" + name, 1000); + cachedAdvancements = new ArrayList<>(); + recipes = new ArrayList<>(); + brewingRecipes = new ArrayList<>(); + statTrackers = new KList<>(); + setMaxLevel(5); + setCostFactor(0.45); + setBaseCost(4); + setIcon(Material.PAPER); + setInitialCost(2); + setDescription("No Description Provided"); + this.name = name; + } + + @Override + public Class getConfigurationClass() { + return configType; + } + + @Override + public void registerConfiguration(Class type) { + this.configType = type; + } + + protected File getConfigFile() { + return Adapt.instance.getDataFile("adapt", "adaptations", getName() + ".toml"); + } + + protected File getLegacyConfigFile() { + return Adapt.instance.getDataFile("adapt", "adaptations", getName() + ".json"); + } + + protected T createDefaultConfig() { + try { + return getConfigurationClass().getConstructor().newInstance(); + } catch (Throwable e) { + throw new IllegalStateException("Failed to create default config for adaptation " + getName(), e); } + } - @Override - public Class getConfigurationClass() { - return configType; + public synchronized boolean reloadConfigFromDisk(boolean announce) { + if (getConfigurationClass() == null) { + return false; } - @Override - public void registerConfiguration(Class type) { - this.configType = type; + T previous = config; + File file = getConfigFile(); + try { + T loaded = loadConfig(file, previous == null ? createDefaultConfig() : previous, previous == null); + config = loaded; + applySharedConfigValues(loaded); + onConfigReload(previous, loaded); + if (announce) { + Adapt.info("Hotloaded " + file.getPath()); + } + return true; + } catch (Throwable e) { + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); + return false; } - - protected File getConfigFile() { - return Adapt.instance.getDataFile("adapt", "adaptations", getName() + ".toml"); - } - - protected File getLegacyConfigFile() { - return Adapt.instance.getDataFile("adapt", "adaptations", getName() + ".json"); + } + + private T loadConfig(File file, T fallback, boolean overwriteOnReadFailure) throws IOException { + return ConfigFileSupport.load( + file, + getLegacyConfigFile(), + getConfigurationClass(), + fallback, + overwriteOnReadFailure, + "adaptation:" + getName(), + "Created missing adaptation config [adapt/adaptations/" + getName() + ".toml] from defaults." + ); + } + + private void applySharedConfigValues(T currentConfig) { + applyIntField(currentConfig, "baseCost", this::setBaseCost); + applyIntField(currentConfig, "initialCost", this::setInitialCost); + applyIntField(currentConfig, "maxLevel", this::setMaxLevel); + applyLongField(currentConfig, "setInterval", this::setInterval); + } + + protected void onConfigReload(T previousConfig, T newConfig) { + applyDoubleField(newConfig, "costFactor", this::setCostFactor); + } + + private void applyIntField(T source, String fieldName, java.util.function.IntConsumer consumer) { + Number number = getNumericField(source, fieldName); + if (number != null) { + consumer.accept(number.intValue()); } + } - protected T createDefaultConfig() { - try { - return getConfigurationClass().getConstructor().newInstance(); - } catch (Throwable e) { - throw new IllegalStateException("Failed to create default config for adaptation " + getName(), e); - } + private void applyLongField(T source, String fieldName, java.util.function.LongConsumer consumer) { + Number number = getNumericField(source, fieldName); + if (number != null) { + consumer.accept(number.longValue()); } + } - public synchronized boolean reloadConfigFromDisk(boolean announce) { - if (getConfigurationClass() == null) { - return false; - } - - T previous = config; - File file = getConfigFile(); - try { - T loaded = loadConfig(file, previous == null ? createDefaultConfig() : previous, previous == null); - config = loaded; - applySharedConfigValues(loaded); - onConfigReload(previous, loaded); - if (announce) { - Adapt.info("Hotloaded " + file.getPath()); - } - return true; - } catch (Throwable e) { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); - return false; - } + private void applyDoubleField(T source, String fieldName, java.util.function.DoubleConsumer consumer) { + Number number = getNumericField(source, fieldName); + if (number != null) { + consumer.accept(number.doubleValue()); } + } - private T loadConfig(File file, T fallback, boolean overwriteOnReadFailure) throws IOException { - return ConfigFileSupport.load( - file, - getLegacyConfigFile(), - getConfigurationClass(), - fallback, - overwriteOnReadFailure, - "adaptation:" + getName(), - "Created missing adaptation config [adapt/adaptations/" + getName() + ".toml] from defaults." - ); + private Number getNumericField(T source, String fieldName) { + Field f = getField(source.getClass(), fieldName); + if (f == null) { + return null; } - private void applySharedConfigValues(T currentConfig) { - applyIntField(currentConfig, "baseCost", this::setBaseCost); - applyIntField(currentConfig, "initialCost", this::setInitialCost); - applyIntField(currentConfig, "maxLevel", this::setMaxLevel); - applyLongField(currentConfig, "setInterval", this::setInterval); + try { + f.setAccessible(true); + Object value = f.get(source); + if (value instanceof Number number) { + return number; + } + } catch (Throwable ignored) { + Adapt.verbose("Failed reading config field '" + fieldName + "' for adaptation " + getName()); } - protected void onConfigReload(T previousConfig, T newConfig) { - applyDoubleField(newConfig, "costFactor", this::setCostFactor); + return null; + } + + private Field getField(Class type, String name) { + Class current = type; + while (current != null) { + try { + return current.getDeclaredField(name); + } catch (NoSuchFieldException ignored) { + current = current.getSuperclass(); + } } - private void applyIntField(T source, String fieldName, java.util.function.IntConsumer consumer) { - Number number = getNumericField(source, fieldName); - if (number != null) { - consumer.accept(number.intValue()); - } - } - - private void applyLongField(T source, String fieldName, java.util.function.LongConsumer consumer) { - Number number = getNumericField(source, fieldName); - if (number != null) { - consumer.accept(number.longValue()); - } - } + return null; + } - private void applyDoubleField(T source, String fieldName, java.util.function.DoubleConsumer consumer) { - Number number = getNumericField(source, fieldName); - if (number != null) { - consumer.accept(number.doubleValue()); - } + @Override + public T getConfig() { + T local = config; + if (local != null) { + return local; } - private Number getNumericField(T source, String fieldName) { - Field f = getField(source.getClass(), fieldName); - if (f == null) { - return null; - } - - try { - f.setAccessible(true); - Object value = f.get(source); - if (value instanceof Number number) { - return number; - } - } catch (Throwable ignored) { - Adapt.verbose("Failed reading config field '" + fieldName + "' for adaptation " + getName()); - } - - return null; - } - - private Field getField(Class type, String name) { - Class current = type; - while (current != null) { - try { - return current.getDeclaredField(name); - } catch (NoSuchFieldException ignored) { - current = current.getSuperclass(); - } - } - - return null; - } - - @Override - public T getConfig() { - T local = config; - if (local != null) { - return local; - } - - synchronized (this) { - local = config; - if (local != null) { - return local; - } - - boolean loaded = reloadConfigFromDisk(false); - local = config; - if (!loaded || local == null) { - local = createDefaultConfig(); - applySharedConfigValues(local); - onConfigReload(null, local); - config = local; - Adapt.warn("Falling back to in-memory defaults for adaptation config " + getName() + "."); - } - } - + synchronized (this) { + local = config; + if (local != null) { return local; + } + + boolean loaded = reloadConfigFromDisk(false); + local = config; + if (!loaded || local == null) { + local = createDefaultConfig(); + applySharedConfigValues(local); + onConfigReload(null, local); + config = local; + Adapt.warn("Falling back to in-memory defaults for adaptation config " + getName() + "."); + } } - public void registerRecipe(AdaptRecipe r) { - recipes.add(r); - } - - public void registerBrewingRecipe(BrewingRecipe r) { - brewingRecipes.add(r); - } - - @Override - public String getDisplayName() { - try { - return displayName == null ? Adaptation.super.getDisplayName() : (C.RESET + "" + C.BOLD + getSkill().getColor().toString() + displayName); - } catch (Exception ignored) { - Adapt.verbose("Failed to get display name for " + getName()); - return null; - } - } + return local; + } - public void registerStatTracker(AdaptStatTracker tracker) { - statTrackers.add(tracker); - } + public void registerRecipe(AdaptRecipe r) { + recipes.add(r); + } - public KList getStatTrackers() { - return statTrackers; - } + public void registerBrewingRecipe(BrewingRecipe r) { + brewingRecipes.add(r); + } - public void registerAdvancement(AdaptAdvancement a) { - cachedAdvancements.add(a); + @Override + public String getDisplayName() { + try { + return displayName == null ? Adaptation.super.getDisplayName() : (C.RESET + "" + C.BOLD + getSkill().getColor().toString() + displayName); + } catch (Exception ignored) { + Adapt.verbose("Failed to get display name for " + getName()); + return null; } + } - protected void registerAdvancementSpec(AdvancementSpec spec) { - if (spec == null) { - return; - } + public void registerStatTracker(AdaptStatTracker tracker) { + statTrackers.add(tracker); + } - registerAdvancement(spec.toAdvancement()); - } + public KList getStatTrackers() { + return statTrackers; + } - protected void registerMilestone(AdvancementSpec spec, String stat, double goal, double reward) { - if (spec == null) { - return; - } + public void registerAdvancement(AdaptAdvancement a) { + cachedAdvancements.add(a); + } - registerAdvancementSpec(spec); - registerStatTracker(spec.statTracker(stat, goal, reward)); + protected void registerAdvancementSpec(AdvancementSpec spec) { + if (spec == null) { + return; } - protected void registerMilestone(String advancementKey, String stat, double goal, double reward) { - registerStatTracker(AdaptStatTracker.builder() - .advancement(advancementKey) - .stat(stat) - .goal(goal) - .reward(reward) - .build()); - } + registerAdvancement(spec.toAdvancement()); + } - @Override - public void onRegisterAdvancements(List advancements) { - advancements.addAll(cachedAdvancements); + protected void registerMilestone(AdvancementSpec spec, String stat, double goal, double reward) { + if (spec == null) { + return; } - public AdaptAdvancement buildAdvancements() { - List a = new ArrayList<>(); - onRegisterAdvancements(a); - - return AdaptAdvancement.builder() - .key("adaptation_" + getName()) - .title(C.WHITE + "[ " + getDisplayName() + C.WHITE + " ]") - .description(getDescription() + ". " + Localizer.dLocalize("snippets.gui.unlock_this_by_clicking") + " " + AdaptConfig.get().adaptActivatorBlockName) - .icon(getIcon()) - .children(a) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build(); - } + registerAdvancementSpec(spec); + registerStatTracker(spec.statTracker(stat, goal, reward)); + } + + protected void registerMilestone(String advancementKey, String stat, double goal, double reward) { + registerStatTracker(AdaptStatTracker.builder() + .advancement(advancementKey) + .stat(stat) + .goal(goal) + .reward(reward) + .build()); + } + + @Override + public void onRegisterAdvancements(List advancements) { + advancements.addAll(cachedAdvancements); + } + + public AdaptAdvancement buildAdvancements() { + List a = new ArrayList<>(); + onRegisterAdvancements(a); + + return AdaptAdvancement.builder() + .key("adaptation_" + getName()) + .title(C.WHITE + "[ " + getDisplayName() + C.WHITE + " ]") + .description(getDescription() + ". " + Localizer.dLocalize("snippets.gui.unlock_this_by_clicking") + " " + AdaptConfig.get().adaptActivatorBlockName) + .icon(getIcon()) + .children(a) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build(); + } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java index 6ca1b1cd3..4a7396ac8 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java @@ -26,12 +26,12 @@ import java.util.function.Consumer; public class ChunkLoading { - public static void loadChunkAsync(Location l, Consumer chunk) { - if (l.getWorld().isChunkLoaded(l.getBlockX() >> 4, l.getBlockZ() >> 4)) { - chunk.accept(l.getChunk()); - return; - } - Adapt.verbose("Loading chunk async for " + l); - Adapt.platform.getChunkAtAsync(l).thenAccept(c -> J.s(() -> chunk.accept(c))); + public static void loadChunkAsync(Location l, Consumer chunk) { + if (l.getWorld().isChunkLoaded(l.getBlockX() >> 4, l.getBlockZ() >> 4)) { + chunk.accept(l.getChunk()); + return; } + Adapt.verbose("Loading chunk async for " + l); + Adapt.platform.getChunkAtAsync(l).thenAccept(c -> J.s(() -> chunk.accept(c))); + } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java index 24df2343a..448712e45 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancement.java @@ -42,117 +42,117 @@ @Builder @Data public class AdaptAdvancement { - private String background; - @Builder.Default - private Material icon = Material.EMERALD; - @Builder.Default - private CustomModel model = null; - @Builder.Default - private String title = "MISSING TITLE"; - @Builder.Default - private String description = "MISSING DESCRIPTION"; - @Builder.Default - private AdaptAdvancementFrame frame = AdaptAdvancementFrame.TASK; - @Builder.Default - private boolean toast = false; - @Builder.Default - private boolean announce = false; - @Builder.Default - private AdvancementVisibility visibility = AdvancementVisibility.PARENT_GRANTED; - @Builder.Default - private String key = "root"; - @Singular - private List children; - - private Advancement toAdvancement(Advancement parent, int index, int depth) { - if (children == null) { - children = new ArrayList<>(); - } - - var icon = getModel() != null ? - getModel().toItemStack() : - new ItemStack(getIcon()); - AdvancementDisplay d = new AdvancementDisplay.Builder(icon, getTitle()) - .description(getDescription()) - .frame(getFrame().toUaaFrame()) - .showToast(toast) - .x(1f + depth) - .y(1f + index) - .build(); - - if (parent == null) { - if (background == null) - throw new IllegalArgumentException("Background cannot be null"); - - return new MainAdvancement(Adapt.instance.getManager().createAdvancementTab(getKey()), getKey(), d, background); - } - - return new SubAdvancement(getKey(), d, parent, getVisibility()); + private String background; + @Builder.Default + private Material icon = Material.EMERALD; + @Builder.Default + private CustomModel model = null; + @Builder.Default + private String title = "MISSING TITLE"; + @Builder.Default + private String description = "MISSING DESCRIPTION"; + @Builder.Default + private AdaptAdvancementFrame frame = AdaptAdvancementFrame.TASK; + @Builder.Default + private boolean toast = false; + @Builder.Default + private boolean announce = false; + @Builder.Default + private AdvancementVisibility visibility = AdvancementVisibility.PARENT_GRANTED; + @Builder.Default + private String key = "root"; + @Singular + private List children; + + private Advancement toAdvancement(Advancement parent, int index, int depth) { + if (children == null) { + children = new ArrayList<>(); } - public KList toAdvancements() { - return toAdvancements(null, 0, 0); + ItemStack icon = getModel() != null ? + getModel().toItemStack() : + new ItemStack(getIcon()); + AdvancementDisplay d = new AdvancementDisplay.Builder(icon, getTitle()) + .description(getDescription()) + .frame(getFrame().toUaaFrame()) + .showToast(toast) + .x(1f + depth) + .y(1f + index) + .build(); + + if (parent == null) { + if (background == null) + throw new IllegalArgumentException("Background cannot be null"); + + return new MainAdvancement(Adapt.instance.getManager().createAdvancementTab(getKey()), getKey(), d, background); } - private KList toAdvancements(Advancement p, int index, int depth) { - KList aa = new KList<>(); - Advancement a = toAdvancement(p, index, depth); - if (children != null && !children.isEmpty()) { - for (AdaptAdvancement i : children) { - aa.addAll(i.toAdvancements(a, aa.size(), depth + 1)); - } - } + return new SubAdvancement(getKey(), d, parent, getVisibility()); + } - aa.add(a); + public KList toAdvancements() { + return toAdvancements(null, 0, 0); + } - return aa; + private KList toAdvancements(Advancement p, int index, int depth) { + KList aa = new KList<>(); + Advancement a = toAdvancement(p, index, depth); + if (children != null && !children.isEmpty()) { + for (AdaptAdvancement i : children) { + aa.addAll(i.toAdvancements(a, aa.size(), depth + 1)); + } } - private static class MainAdvancement extends RootAdvancement { - - public MainAdvancement(@NotNull AdvancementTab advancementTab, @NotNull String key, @NotNull AdvancementDisplay display, @NotNull String backgroundTexture) { - super(advancementTab, key, display, backgroundTexture); - } - - @Override - public void grant(@NotNull Player player, boolean giveRewards) { - super.grant(player, giveRewards); - try { - getAdvancementTab().showTab(player); - } catch (Throwable t) { - Adapt.verbose("Failed to show advancement tab '" + getKey() + "' for " + player.getName() + ": " - + t.getClass().getSimpleName() - + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); - } - } - - @Override - public void revoke(@NotNull Player player) { - super.revoke(player); - try { - getAdvancementTab().hideTab(player); - } catch (Throwable t) { - Adapt.verbose("Failed to hide advancement tab '" + getKey() + "' for " + player.getName() + ": " - + t.getClass().getSimpleName() - + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); - } - } + aa.add(a); + + return aa; + } + + private static class MainAdvancement extends RootAdvancement { + + public MainAdvancement(@NotNull AdvancementTab advancementTab, @NotNull String key, @NotNull AdvancementDisplay display, @NotNull String backgroundTexture) { + super(advancementTab, key, display, backgroundTexture); + } + + @Override + public void grant(@NotNull Player player, boolean giveRewards) { + super.grant(player, giveRewards); + try { + getAdvancementTab().showTab(player); + } catch (Throwable t) { + Adapt.verbose("Failed to show advancement tab '" + getKey() + "' for " + player.getName() + ": " + + t.getClass().getSimpleName() + + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); + } + } + + @Override + public void revoke(@NotNull Player player) { + super.revoke(player); + try { + getAdvancementTab().hideTab(player); + } catch (Throwable t) { + Adapt.verbose("Failed to hide advancement tab '" + getKey() + "' for " + player.getName() + ": " + + t.getClass().getSimpleName() + + (t.getMessage() == null ? "" : " (" + t.getMessage() + ")")); + } + } + } + + private static class SubAdvancement extends BaseAdvancement { + private final AdvancementVisibility visibility; + + public SubAdvancement(@NotNull String key, + @NotNull AdvancementDisplay display, + @NotNull Advancement parent, + @NotNull AdvancementVisibility visibility) { + super(key, display, parent); + this.visibility = visibility; } - private static class SubAdvancement extends BaseAdvancement { - private final AdvancementVisibility visibility; - - public SubAdvancement(@NotNull String key, - @NotNull AdvancementDisplay display, - @NotNull Advancement parent, - @NotNull AdvancementVisibility visibility) { - super(key, display, parent); - this.visibility = visibility; - } - - @Override - public boolean isVisible(@NotNull TeamProgression progression) { - return visibility.isVisible(this, progression); - } + @Override + public boolean isVisible(@NotNull TeamProgression progression) { + return visibility.isVisible(this, progression); } + } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java index 7ee4d173b..40d795092 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdaptAdvancementFrame.java @@ -3,15 +3,15 @@ import com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementFrameType; public enum AdaptAdvancementFrame { - TASK, - GOAL, - CHALLENGE; + TASK, + GOAL, + CHALLENGE; - public AdvancementFrameType toUaaFrame() { - return switch (this) { - case GOAL -> AdvancementFrameType.GOAL; - case CHALLENGE -> AdvancementFrameType.CHALLENGE; - case TASK -> AdvancementFrameType.TASK; - }; - } + public AdvancementFrameType toUaaFrame() { + return switch (this) { + case GOAL -> AdvancementFrameType.GOAL; + case CHALLENGE -> AdvancementFrameType.CHALLENGE; + case TASK -> AdvancementFrameType.TASK; + }; + } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index eaf2b5d21..4be01bd56 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -15,329 +15,333 @@ import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; -import java.util.*; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import static art.arcane.adapt.Adapt.instance; public class AdvancementManager { - private final AdvancementMain main; - private final Map advancements; - private final AtomicBoolean loaded = new AtomicBoolean(false); - private final AtomicBoolean enabled = new AtomicBoolean(false); - private final AtomicBoolean runtimeSchedulerUnsupported = new AtomicBoolean(false); - - public AdvancementManager() { - AdvancementMain loadedMain = null; - try { - loadedMain = new AdvancementMain(instance); - loadedMain.load(); - loaded.set(true); - } catch (Throwable e) { - loadedMain = null; - Adapt.warn("UltimateAdvancementAPI is unavailable: " + e.getMessage() + ". Advancements will be disabled."); - } - - main = loadedMain; - advancements = new ConcurrentHashMap<>(); + private final AdvancementMain main; + private final Map advancements; + private final AtomicBoolean loaded = new AtomicBoolean(false); + private final AtomicBoolean enabled = new AtomicBoolean(false); + private final AtomicBoolean runtimeSchedulerUnsupported = new AtomicBoolean(false); + + public AdvancementManager() { + AdvancementMain loadedMain = null; + try { + loadedMain = new AdvancementMain(instance); + loadedMain.load(); + loaded.set(true); + } catch (Throwable e) { + loadedMain = null; + Adapt.warn("UltimateAdvancementAPI is unavailable: " + e.getMessage() + ". Advancements will be disabled."); } - AdvancementTab createAdvancementTab(String namespace) { - if (main == null) { - throw new IllegalStateException("UltimateAdvancementAPI is unavailable"); - } + main = loadedMain; + advancements = new ConcurrentHashMap<>(); + } - return main.createAdvancementTab(instance, "adapt_" + namespace); + AdvancementTab createAdvancementTab(String namespace) { + if (main == null) { + throw new IllegalStateException("UltimateAdvancementAPI is unavailable"); } - public void grant(AdaptPlayer player, String key, boolean toast) { - player.getData().ensureGranted(key); - Player p = player.getPlayer(); - if (!AdaptConfig.get().isAdvancements() || !enabled.get() || runtimeSchedulerUnsupported.get() || p == null || !p.isOnline()) return; - Advancement advancement = advancements.get(key); - if (advancement == null) { - Adapt.verbose("Advancement key '" + key + "' is not registered; skipping grant."); - return; - } + return main.createAdvancementTab(instance, "adapt_" + namespace); + } + + public void grant(AdaptPlayer player, String key, boolean toast) { + player.getData().ensureGranted(key); + Player p = player.getPlayer(); + if (!AdaptConfig.get().isAdvancements() || !enabled.get() || runtimeSchedulerUnsupported.get() || p == null || !p.isOnline()) + return; + Advancement advancement = advancements.get(key); + if (advancement == null) { + Adapt.verbose("Advancement key '" + key + "' is not registered; skipping grant."); + return; + } + + J.runEntity(p, () -> { + if (!p.isOnline()) { + return; + } - J.runEntity(p, () -> { - if (!p.isOnline()) { - return; - } + attemptGrant(p, advancement, key, toast, true); + }, 5); + } - attemptGrant(p, advancement, key, toast, true); - }, 5); + private void attemptGrant(Player player, Advancement advancement, String key, boolean toast, boolean allowRetryOnGlobal) { + if (player == null || !player.isOnline()) { + return; } - private void attemptGrant(Player player, Advancement advancement, String key, boolean toast, boolean allowRetryOnGlobal) { - if (player == null || !player.isOnline()) { - return; + try { + advancement.grant(player, true); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; + } + + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; } - try { - advancement.grant(player, true); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + player.getName() + "."); - return; - } - - if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); - return; - } - - if (allowRetryOnGlobal) { - J.s(() -> attemptGrant(player, advancement, key, toast, false), 1); - return; - } - } - - Adapt.warn("Failed to grant advancement '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); - return; + if (allowRetryOnGlobal) { + J.s(() -> attemptGrant(player, advancement, key, toast, false), 1); + return; } + } - if (!toast) { - return; - } + Adapt.warn("Failed to grant advancement '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + return; + } - try { - advancement.displayToastToPlayer(player); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); - return; - } - - if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); - return; - } - - if (allowRetryOnGlobal) { - J.s(() -> attemptToast(player, advancement, key, false), 1); - return; - } - } - - Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); - } + if (!toast) { + return; } - private void attemptToast(Player player, Advancement advancement, String key, boolean allowRetryOnGlobal) { - if (player == null || !player.isOnline()) { - return; + try { + advancement.displayToastToPlayer(player); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; + } + + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; } - try { - advancement.displayToastToPlayer(player); - } catch (Throwable t) { - if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); - return; - } - - if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); - return; - } - - if (allowRetryOnGlobal) { - J.s(() -> attemptToast(player, advancement, key, false), 1); - return; - } - } - - Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false), 1); + return; } - } + } - private void markRuntimeSchedulerUnsupported(Throwable throwable) { - if (!runtimeSchedulerUnsupported.compareAndSet(false, true)) { - return; - } + Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); + } + } - Adapt.info("UltimateAdvancementAPI live packet grants/toasts are unavailable on this Folia runtime; stored advancement grants will continue without live packets/toasts."); - if (throwable != null) { - Adapt.verbose("UltimateAdvancementAPI fallback cause: " + summarizeThrowable(throwable)); - } + private void attemptToast(Player player, Advancement advancement, String key, boolean allowRetryOnGlobal) { + if (player == null || !player.isOnline()) { + return; } - private boolean isUserNotLoadedError(Throwable throwable) { - Throwable current = throwable; - while (current != null) { - if ("UserNotLoadedException".equals(current.getClass().getSimpleName())) { - return true; - } + try { + advancement.displayToastToPlayer(player); + } catch (Throwable t) { + if (isUserNotLoadedError(t)) { + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + return; + } + + if (isSchedulerContextMismatch(t)) { + if (J.isFoliaThreading()) { + markRuntimeSchedulerUnsupported(t); + return; + } - current = current.getCause(); + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false), 1); + return; } + } - return false; + Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); } + } - private boolean isSchedulerContextMismatch(Throwable throwable) { - Throwable current = throwable; - while (current != null) { - if (current instanceof UnsupportedOperationException) { - return true; - } - - String message = current.getMessage(); - if (message != null) { - String lower = message.toLowerCase(Locale.ROOT); - if (lower.contains("thread") - || lower.contains("scheduler") - || lower.contains("region") - || lower.contains("primary thread") - || lower.contains("asynchronously")) { - return true; - } - } - - current = current.getCause(); - } + private void markRuntimeSchedulerUnsupported(Throwable throwable) { + if (!runtimeSchedulerUnsupported.compareAndSet(false, true)) { + return; + } - return false; + Adapt.info("UltimateAdvancementAPI live packet grants/toasts are unavailable on this Folia runtime; stored advancement grants will continue without live packets/toasts."); + if (throwable != null) { + Adapt.verbose("UltimateAdvancementAPI fallback cause: " + summarizeThrowable(throwable)); } + } - private String summarizeThrowable(Throwable throwable) { - if (throwable == null) { - return "unknown"; - } + private boolean isUserNotLoadedError(Throwable throwable) { + Throwable current = throwable; + while (current != null) { + if ("UserNotLoadedException".equals(current.getClass().getSimpleName())) { + return true; + } + + current = current.getCause(); + } - Throwable root = throwable; - while (root.getCause() != null && root.getCause() != root) { - root = root.getCause(); + return false; + } + + private boolean isSchedulerContextMismatch(Throwable throwable) { + Throwable current = throwable; + while (current != null) { + if (current instanceof UnsupportedOperationException) { + return true; + } + + String message = current.getMessage(); + if (message != null) { + String lower = message.toLowerCase(Locale.ROOT); + if (lower.contains("thread") + || lower.contains("scheduler") + || lower.contains("region") + || lower.contains("primary thread") + || lower.contains("asynchronously")) { + return true; } + } - StringBuilder summary = new StringBuilder(throwable.getClass().getSimpleName()); - appendMessage(summary, throwable.getMessage()); + current = current.getCause(); + } - if (root != throwable) { - summary.append(" | cause=").append(root.getClass().getSimpleName()); - appendMessage(summary, root.getMessage()); - } + return false; + } - return summary.toString(); + private String summarizeThrowable(Throwable throwable) { + if (throwable == null) { + return "unknown"; } - private void appendMessage(StringBuilder builder, String message) { - if (message != null && !message.isBlank()) { - builder.append(": ").append(message); - } + Throwable root = throwable; + while (root.getCause() != null && root.getCause() != root) { + root = root.getCause(); } - public void unlockExisting(AdaptPlayer player, AdvancementHandler handler) { - if (!AdaptConfig.get().isAdvancements() || !enabled.get()) return; - if (player == null || handler == null) { - return; - } + StringBuilder summary = new StringBuilder(throwable.getClass().getSimpleName()); + appendMessage(summary, throwable.getMessage()); - Player target = player.getPlayer(); - if (target == null || !target.isOnline()) { - return; - } + if (root != throwable) { + summary.append(" | cause=").append(root.getClass().getSimpleName()); + appendMessage(summary, root.getMessage()); + } - if (runtimeSchedulerUnsupported.get()) { - handler.setReady(true); - return; - } + return summary.toString(); + } - J.runEntity(target, () -> { - instance.getAdaptServer() - .getSkillRegistry() - .getSkills() - .stream() - .map(Skill::buildAdvancements) - .forEach(aa -> unlockExisting(player, aa)); + private void appendMessage(StringBuilder builder, String message) { + if (message != null && !message.isBlank()) { + builder.append(": ").append(message); + } + } - handler.setReady(true); - }, 20); + public void unlockExisting(AdaptPlayer player, AdvancementHandler handler) { + if (!AdaptConfig.get().isAdvancements() || !enabled.get()) return; + if (player == null || handler == null) { + return; } - private void unlockExisting(AdaptPlayer player, AdaptAdvancement aa) { - if (aa.getChildren() != null) { - for (AdaptAdvancement i : aa.getChildren()) { - unlockExisting(player, i); - } - } + Player target = player.getPlayer(); + if (target == null || !target.isOnline()) { + return; + } - if (player.getData().isGranted(aa.getKey())) { - grant(player, aa.getKey(), false); - } + if (runtimeSchedulerUnsupported.get()) { + handler.setReady(true); + return; } - public void enable() { - if (main == null) { - return; - } + J.runEntity(target, () -> { + instance.getAdaptServer() + .getSkillRegistry() + .getSkills() + .stream() + .map(Skill::buildAdvancements) + .forEach(aa -> unlockExisting(player, aa)); + + handler.setReady(true); + }, 20); + } + + private void unlockExisting(AdaptPlayer player, AdaptAdvancement aa) { + if (aa.getChildren() != null) { + for (AdaptAdvancement i : aa.getChildren()) { + unlockExisting(player, i); + } + } - runtimeSchedulerUnsupported.set(false); + if (player.getData().isGranted(aa.getKey())) { + grant(player, aa.getKey(), false); + } + } - if (loaded.compareAndSet(false, true)) - main.load(); + public void enable() { + if (main == null) { + return; + } - if (!AdaptConfig.get().isAdvancements() || !enabled.compareAndSet(false, true)) - return; - if (AdaptConfig.get().isUseSql()) { - AdaptConfig.SqlSettings sql = AdaptConfig.get().getSql(); - main.enableMySQL(sql.getUsername(), sql.getPassword(), sql.getDatabase(), sql.getHost(), sql.getPort(), sql.getPoolSize(), sql.getConnectionTimeout()); - } else { - main.enableSQLite(instance.getDataFile("data", "advancements.db")); - } + runtimeSchedulerUnsupported.set(false); - if (J.isFoliaThreading() && isLegacyAsyncSchedulerUnsupported()) { - markRuntimeSchedulerUnsupported(null); - } + if (loaded.compareAndSet(false, true)) + main.load(); - for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { - AdaptAdvancement aa = i.buildAdvancements(); - Set set = new HashSet<>(); - RootAdvancement root = null; - - for (var a : aa.toAdvancements().reverse()) { - advancements.put(a.getKey().getKey(), a); - if (a instanceof RootAdvancement r && root == null) root = r; - else if (a instanceof BaseAdvancement b) set.add(b); - } - - if (root == null) { - Adapt.error("Root advancement not found for " + i.getId()); - continue; - } - root.getAdvancementTab().registerAdvancements(root, set); - } + if (!AdaptConfig.get().isAdvancements() || !enabled.compareAndSet(false, true)) + return; + if (AdaptConfig.get().isUseSql()) { + AdaptConfig.SqlSettings sql = AdaptConfig.get().getSql(); + main.enableMySQL(sql.getUsername(), sql.getPassword(), sql.getDatabase(), sql.getHost(), sql.getPort(), sql.getPoolSize(), sql.getConnectionTimeout()); + } else { + main.enableSQLite(instance.getDataFile("data", "advancements.db")); } - public void disable() { - if (main == null) { - enabled.set(false); - loaded.set(false); - runtimeSchedulerUnsupported.set(false); - return; - } + if (J.isFoliaThreading() && isLegacyAsyncSchedulerUnsupported()) { + markRuntimeSchedulerUnsupported(null); + } - main.disable(); - enabled.set(false); - loaded.set(false); - runtimeSchedulerUnsupported.set(false); + for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { + AdaptAdvancement aa = i.buildAdvancements(); + Set set = new HashSet<>(); + RootAdvancement root = null; + + for (com.fren_gor.ultimateAdvancementAPI.advancement.Advancement a : aa.toAdvancements().reverse()) { + advancements.put(a.getKey().getKey(), a); + if (a instanceof RootAdvancement r && root == null) root = r; + else if (a instanceof BaseAdvancement b) set.add(b); + } + + if (root == null) { + Adapt.error("Root advancement not found for " + i.getId()); + continue; + } + root.getAdvancementTab().registerAdvancements(root, set); + } + } + + public void disable() { + if (main == null) { + enabled.set(false); + loaded.set(false); + runtimeSchedulerUnsupported.set(false); + return; } - private boolean isLegacyAsyncSchedulerUnsupported() { - try { - BukkitTask probe = Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { - }, 1L, 1L); - probe.cancel(); - return false; - } catch (UnsupportedOperationException ignored) { - return true; - } catch (Throwable ignored) { - return false; - } + main.disable(); + enabled.set(false); + loaded.set(false); + runtimeSchedulerUnsupported.set(false); + } + + private boolean isLegacyAsyncSchedulerUnsupported() { + try { + BukkitTask probe = Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { + }, 1L, 1L); + probe.cancel(); + return false; + } catch (UnsupportedOperationException ignored) { + return true; + } catch (Throwable ignored) { + return false; } + } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java index 18f1c8165..0484db034 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementSpec.java @@ -30,73 +30,73 @@ @Builder(toBuilder = true) @Data public class AdvancementSpec { - private String key; - private String title; - private String description; - @Builder.Default - private Material icon = Material.EMERALD; - @Builder.Default - private CustomModel model = null; - @Builder.Default - private AdaptAdvancementFrame frame = AdaptAdvancementFrame.TASK; - @Builder.Default - private AdvancementVisibility visibility = AdvancementVisibility.PARENT_GRANTED; - @Builder.Default - private boolean toast = false; - @Builder.Default - private boolean announce = false; - @Singular - private List children; + private String key; + private String title; + private String description; + @Builder.Default + private Material icon = Material.EMERALD; + @Builder.Default + private CustomModel model = null; + @Builder.Default + private AdaptAdvancementFrame frame = AdaptAdvancementFrame.TASK; + @Builder.Default + private AdvancementVisibility visibility = AdvancementVisibility.PARENT_GRANTED; + @Builder.Default + private boolean toast = false; + @Builder.Default + private boolean announce = false; + @Singular + private List children; - public static AdvancementSpec challenge(String key, Material icon, String title, String description) { - return AdvancementSpec.builder() - .key(key) - .icon(icon) - .title(title) - .description(description) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build(); - } + public static AdvancementSpec challenge(String key, Material icon, String title, String description) { + return AdvancementSpec.builder() + .key(key) + .icon(icon) + .title(title) + .description(description) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build(); + } - public AdvancementSpec withChild(AdvancementSpec child) { - if (child == null) { - return this; - } - - return toBuilder().child(child).build(); + public AdvancementSpec withChild(AdvancementSpec child) { + if (child == null) { + return this; } - public AdaptAdvancement toAdvancement() { - AdaptAdvancement.AdaptAdvancementBuilder builder = AdaptAdvancement.builder() - .key(key) - .title(title) - .description(description) - .icon(icon) - .model(model) - .frame(frame) - .toast(toast) - .announce(announce) - .visibility(visibility); + return toBuilder().child(child).build(); + } - if (children != null) { - for (AdvancementSpec child : children) { - if (child == null) { - continue; - } - builder.child(child.toAdvancement()); - } - } + public AdaptAdvancement toAdvancement() { + AdaptAdvancement.AdaptAdvancementBuilder builder = AdaptAdvancement.builder() + .key(key) + .title(title) + .description(description) + .icon(icon) + .model(model) + .frame(frame) + .toast(toast) + .announce(announce) + .visibility(visibility); - return builder.build(); + if (children != null) { + for (AdvancementSpec child : children) { + if (child == null) { + continue; + } + builder.child(child.toAdvancement()); + } } - public AdaptStatTracker statTracker(String stat, double goal, double reward) { - return AdaptStatTracker.builder() - .stat(stat) - .goal(goal) - .reward(reward) - .advancement(key) - .build(); - } + return builder.build(); + } + + public AdaptStatTracker statTracker(String stat, double goal, double reward) { + return AdaptStatTracker.builder() + .stat(stat) + .goal(goal) + .reward(reward) + .advancement(key) + .build(); + } } diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java index 4f1acde5b..91c020d6d 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementVisibility.java @@ -10,71 +10,76 @@ public interface AdvancementVisibility { - /** - * Advancements with this Visibility will always be visible - */ - AdvancementVisibility ALWAYS = (advancement, progression) -> true; + /** + * Advancements with this Visibility will always be visible + */ + AdvancementVisibility ALWAYS = (advancement, progression) -> true; - /** - * Advancements with this Visibility will be visible once their parent or any of their children is granted - */ - AdvancementVisibility PARENT_GRANTED = (advancement, progression) -> { - Preconditions.checkNotNull(advancement, "Advancement is null."); - Preconditions.checkNotNull(progression, "TeamProgression is null."); - if (advancement.getProgression(progression) > 0) - return true; + /** + * Advancements with this Visibility will be visible once their parent or any + * of their children is granted + */ + AdvancementVisibility PARENT_GRANTED = (advancement, progression) -> { + Preconditions.checkNotNull(advancement, "Advancement is null."); + Preconditions.checkNotNull(progression, "TeamProgression is null."); + if (advancement.getProgression(progression) > 0) + return true; - if (advancement instanceof AbstractMultiParentsAdvancement multiParent) { - return multiParent.isAnyParentGranted(progression); - } - if (advancement instanceof BaseAdvancement base) { - return base.getParent().isGranted(progression); - } - return false; - }; + if (advancement instanceof AbstractMultiParentsAdvancement multiParent) { + return multiParent.isAnyParentGranted(progression); + } + if (advancement instanceof BaseAdvancement base) { + return base.getParent().isGranted(progression); + } + return false; + }; - /** - * Advancements with this Visibility will be visible once they are granted or any of their children is granted (Similar to Vanilla "hidden") - */ - AdvancementVisibility HIDDEN = (advancement, progression) -> { - Preconditions.checkNotNull(advancement, "Advancement is null."); - Preconditions.checkNotNull(progression, "TeamProgression is null."); - return advancement.getProgression(progression) > 0; - }; + /** + * Advancements with this Visibility will be visible once they are granted or + * any of their children is granted (Similar to Vanilla "hidden") + */ + AdvancementVisibility HIDDEN = (advancement, progression) -> { + Preconditions.checkNotNull(advancement, "Advancement is null."); + Preconditions.checkNotNull(progression, "TeamProgression is null."); + return advancement.getProgression(progression) > 0; + }; - /** - * Advancements with this Visibility will be visible once their parent or grandparent or any of their children is granted (Similar to Vanilla behavior) - */ - AdvancementVisibility VANILLA = (advancement, progression) -> { - Preconditions.checkNotNull(advancement, "Advancement is null."); - Preconditions.checkNotNull(progression, "TeamProgression is null."); - if (advancement.getProgression(progression) > 0) - return true; + /** + * Advancements with this Visibility will be visible once their parent or + * grandparent or any of their children is granted (Similar to Vanilla + * behavior) + */ + AdvancementVisibility VANILLA = (advancement, progression) -> { + Preconditions.checkNotNull(advancement, "Advancement is null."); + Preconditions.checkNotNull(progression, "TeamProgression is null."); + if (advancement.getProgression(progression) > 0) + return true; - if (advancement instanceof AbstractMultiParentsAdvancement multiParent) { - return multiParent.isAnyGrandparentGranted(progression); - } else if (advancement instanceof BaseAdvancement base) { - Advancement parent = base.getParent(); + if (advancement instanceof AbstractMultiParentsAdvancement multiParent) { + return multiParent.isAnyGrandparentGranted(progression); + } else if (advancement instanceof BaseAdvancement base) { + Advancement parent = base.getParent(); - if (parent.isGranted(progression)) { - return true; - } - if (parent instanceof AbstractMultiParentsAdvancement multiParent) { - return multiParent.isAnyParentGranted(progression); - } else if (parent instanceof BaseAdvancement baseA) { - return baseA.getParent().isGranted(progression); - } - return false; - } - return false; - }; + if (parent.isGranted(progression)) { + return true; + } + if (parent instanceof AbstractMultiParentsAdvancement multiParent) { + return multiParent.isAnyParentGranted(progression); + } else if (parent instanceof BaseAdvancement baseA) { + return baseA.getParent().isGranted(progression); + } + return false; + } + return false; + }; - /** - * Do not call this method directly, use {@link AdvancementVisibility} to get accurate visibility data - * - * @param advancement Advancement to check - * @param progression Progression to check - * @return true if advancement should be visible - */ - boolean isVisible(@NotNull Advancement advancement, @NotNull TeamProgression progression); + /** + * Do not call this method directly, use {@link AdvancementVisibility} to get + * accurate visibility data + * + * @param advancement Advancement to check + * @param progression Progression to check + * @return true if advancement should be visible + */ + boolean isVisible(@NotNull Advancement advancement, @NotNull TeamProgression progression); } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/api/data/WorldData.java b/src/main/java/art/arcane/adapt/api/data/WorldData.java index 48c298798..bdfeb3343 100644 --- a/src/main/java/art/arcane/adapt/api/data/WorldData.java +++ b/src/main/java/art/arcane/adapt/api/data/WorldData.java @@ -32,11 +32,7 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.io.CountingDataInputStream; import art.arcane.volmlib.util.mantle.io.IOWorkerCodecSupport; -import art.arcane.volmlib.util.mantle.runtime.IOWorker; -import art.arcane.volmlib.util.mantle.runtime.Mantle; -import art.arcane.volmlib.util.mantle.runtime.MantleDataAdapter; -import art.arcane.volmlib.util.mantle.runtime.MantleHooks; -import art.arcane.volmlib.util.mantle.runtime.TectonicPlate; +import art.arcane.volmlib.util.mantle.runtime.*; import art.arcane.volmlib.util.parallel.HyperLockSupport; import org.bukkit.World; import org.bukkit.block.Block; @@ -49,313 +45,313 @@ import java.io.IOException; public class WorldData extends TickedObject { - private static final KMap mantles = new KMap<>(); - - static { - SpatialMatter.registerSliceType(new Earnings.EarningsMatter()); - ClassReader.add(WorldData.class.getClassLoader()); - } - - private final World world; - private final int minHeight; - private final Mantle mantle; - - public WorldData(World world) { - super("world-data", world.getUID().toString(), 30_000); - this.world = world; - this.minHeight = world.getMinHeight(); - mantle = createMantle(Adapt.instance.getDataFolder("data", "mantle", world.getName()), world.getMaxHeight()); - } - - private static Mantle createMantle(File dataFolder, int worldHeight) { - MantleDataAdapter adapter = createAdapter(); - MantleHooks hooks = createHooks(); - art.arcane.volmlib.util.mantle.Mantle.RegionIO> regionIO = - createRegionIO(dataFolder, worldHeight, adapter, hooks); - return new Mantle<>( - dataFolder, - worldHeight, - new HyperLockSupport(), - MultiBurst.burst, - regionIO, - adapter, - hooks - ); - } - - private static MantleDataAdapter createAdapter() { - return new MantleDataAdapter<>() { - @Override - public Matter createSection() { - return new SpatialMatter(16, 16, 16); - } - - @Override - public Matter readSection(CountingDataInputStream din) throws IOException { - try { - return Matter.readDin(din); - } catch (ClassNotFoundException e) { - throw new IOException("Failed to deserialize mantle section", e); - } - } - - @Override - public void writeSection(Matter section, DataOutputStream dos) throws IOException { - section.writeDos(dos); - } - - @Override - public void trimSection(Matter section) { - section.trimSlices(); - } - - @Override - public boolean isSectionEmpty(Matter section) { - return section.getSliceMap().isEmpty(); - } - - @Override - public Class classifyValue(Object value) { - return value == null ? Object.class : value.getClass(); - } - - @Override - @SuppressWarnings("unchecked") - public void set(Matter section, int x, int y, int z, Class type, T value) { - MatterSlice slice = (MatterSlice) section.slice(section.getClass(value)); - slice.set(x, y, z, value); - } - - @Override - public void remove(Matter section, int x, int y, int z, Class type) { - MatterSlice slice = section.slice(type); - slice.set(x, y, z, null); - } - - @Override - public T get(Matter section, int x, int y, int z, Class type) { - MatterSlice slice = section.slice(type); - return slice.get(x, y, z); - } - - @Override - public void iterate(Matter section, Class type, art.arcane.volmlib.util.function.Consumer4 iterator) { - MatterSlice slice = section.getSlice(type); - if (slice != null) { - slice.iterateSync((x, y, z, value) -> iterator.accept(x, y, z, value)); - } - } - - @Override - public boolean hasSlice(Matter section, Class type) { - return section.hasSlice(type); - } - - @Override - public void deleteSlice(Matter section, Class type) { - section.deleteSlice(type); - } - }; - } - - private static MantleHooks createHooks() { - return new MantleHooks() { - @Override - public void onReadSectionFailure(int index, - long start, - long end, - CountingDataInputStream din, - IOException error) { - Adapt.warn("Failed to read mantle chunk section, skipping it."); - Adapt.error(error.getMessage() == null ? "Unknown mantle section read error" : error.getMessage()); - error.printStackTrace(); - TectonicPlate.addError(); - } - - @Override - public void onReadChunkFailure(int index, - long start, - long end, - CountingDataInputStream din, - Throwable error) { - Adapt.warn("Failed to read mantle chunk, creating a new chunk instead."); - Adapt.error(error.getMessage() == null ? "Unknown mantle chunk read error" : error.getMessage()); - error.printStackTrace(); - } - - @Override - public String formatDuration(double millis) { - return Form.duration(millis, 0); - } - - @Override - public void onDebug(String message) { - Adapt.debug(message); - } - - @Override - public void onWarn(String message) { - Adapt.warn(message); - } - - @Override - public void onError(Throwable throwable) { - Adapt.error(throwable.getMessage() == null ? "Mantle error" : throwable.getMessage()); - throwable.printStackTrace(); - } - }; - } - - private static art.arcane.volmlib.util.mantle.Mantle.RegionIO> createRegionIO(File dataFolder, - int worldHeight, - MantleDataAdapter adapter, - MantleHooks hooks) { - IOWorker> worker = new IOWorker<>( - dataFolder, - IOWorkerCodecSupport.identity(), - 128, - (name, millis) -> Adapt.debug("Acquired mantle channel for " + name + " in " + millis + "ms") - ); - - return new art.arcane.volmlib.util.mantle.Mantle.RegionIO<>() { - @Override - public TectonicPlate read(String name) throws IOException { - try { - return worker.read(name, (regionName, in) -> TectonicPlate.read(worldHeight, in, true, adapter, hooks)); - } catch (IOException e) { - TectonicPlate migrated = readLegacy(dataFolder, worldHeight, name, adapter, hooks, e); - if (migrated != null) { - Adapt.warn("Migrated legacy mantle region " + name + " to shared mantle format."); - return migrated; - } - - throw e; - } - } - - @Override - public void write(String name, TectonicPlate region) throws IOException { - worker.write(name, "adapt", ".bin", region, TectonicPlate::write); - } - - @Override - public void close() throws IOException { - worker.close(); - } - }; - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static TectonicPlate readLegacy(File dataFolder, - int worldHeight, - String name, - MantleDataAdapter adapter, - MantleHooks hooks, - IOException original) { - File file = new File(dataFolder, name); - if (!file.exists()) { - return null; + private static final KMap mantles = new KMap<>(); + + static { + SpatialMatter.registerSliceType(new Earnings.EarningsMatter()); + ClassReader.add(WorldData.class.getClassLoader()); + } + + private final World world; + private final int minHeight; + private final Mantle mantle; + + public WorldData(World world) { + super("world-data", world.getUID().toString(), 30_000); + this.world = world; + this.minHeight = world.getMinHeight(); + mantle = createMantle(Adapt.instance.getDataFolder("data", "mantle", world.getName()), world.getMaxHeight()); + } + + private static Mantle createMantle(File dataFolder, int worldHeight) { + MantleDataAdapter adapter = createAdapter(); + MantleHooks hooks = createHooks(); + art.arcane.volmlib.util.mantle.Mantle.RegionIO> regionIO = + createRegionIO(dataFolder, worldHeight, adapter, hooks); + return new Mantle<>( + dataFolder, + worldHeight, + new HyperLockSupport(), + MultiBurst.burst, + regionIO, + adapter, + hooks + ); + } + + private static MantleDataAdapter createAdapter() { + return new MantleDataAdapter<>() { + @Override + public Matter createSection() { + return new SpatialMatter(16, 16, 16); + } + + @Override + public Matter readSection(CountingDataInputStream din) throws IOException { + try { + return Matter.readDin(din); + } catch (ClassNotFoundException e) { + throw new IOException("Failed to deserialize mantle section", e); } - + } + + @Override + public void writeSection(Matter section, DataOutputStream dos) throws IOException { + section.writeDos(dos); + } + + @Override + public void trimSection(Matter section) { + section.trimSlices(); + } + + @Override + public boolean isSectionEmpty(Matter section) { + return section.getSliceMap().isEmpty(); + } + + @Override + public Class classifyValue(Object value) { + return value == null ? Object.class : value.getClass(); + } + + @Override + @SuppressWarnings("unchecked") + public void set(Matter section, int x, int y, int z, Class type, T value) { + MatterSlice slice = (MatterSlice) section.slice(section.getClass(value)); + slice.set(x, y, z, value); + } + + @Override + public void remove(Matter section, int x, int y, int z, Class type) { + MatterSlice slice = section.slice(type); + slice.set(x, y, z, null); + } + + @Override + public T get(Matter section, int x, int y, int z, Class type) { + MatterSlice slice = section.slice(type); + return slice.get(x, y, z); + } + + @Override + public void iterate(Matter section, Class type, art.arcane.volmlib.util.function.Consumer4 iterator) { + MatterSlice slice = section.getSlice(type); + if (slice != null) { + slice.iterateSync((x, y, z, value) -> iterator.accept(x, y, z, value)); + } + } + + @Override + public boolean hasSlice(Matter section, Class type) { + return section.hasSlice(type); + } + + @Override + public void deleteSlice(Matter section, Class type) { + section.deleteSlice(type); + } + }; + } + + private static MantleHooks createHooks() { + return new MantleHooks() { + @Override + public void onReadSectionFailure(int index, + long start, + long end, + CountingDataInputStream din, + IOException error) { + Adapt.warn("Failed to read mantle chunk section, skipping it."); + Adapt.error(error.getMessage() == null ? "Unknown mantle section read error" : error.getMessage()); + error.printStackTrace(); + TectonicPlate.addError(); + } + + @Override + public void onReadChunkFailure(int index, + long start, + long end, + CountingDataInputStream din, + Throwable error) { + Adapt.warn("Failed to read mantle chunk, creating a new chunk instead."); + Adapt.error(error.getMessage() == null ? "Unknown mantle chunk read error" : error.getMessage()); + error.printStackTrace(); + } + + @Override + public String formatDuration(double millis) { + return Form.duration(millis, 0); + } + + @Override + public void onDebug(String message) { + Adapt.debug(message); + } + + @Override + public void onWarn(String message) { + Adapt.warn(message); + } + + @Override + public void onError(Throwable throwable) { + Adapt.error(throwable.getMessage() == null ? "Mantle error" : throwable.getMessage()); + throwable.printStackTrace(); + } + }; + } + + private static art.arcane.volmlib.util.mantle.Mantle.RegionIO> createRegionIO(File dataFolder, + int worldHeight, + MantleDataAdapter adapter, + MantleHooks hooks) { + IOWorker> worker = new IOWorker<>( + dataFolder, + IOWorkerCodecSupport.identity(), + 128, + (name, millis) -> Adapt.debug("Acquired mantle channel for " + name + " in " + millis + "ms") + ); + + return new art.arcane.volmlib.util.mantle.Mantle.RegionIO<>() { + @Override + public TectonicPlate read(String name) throws IOException { try { - MantleRegion region = MantleRegion.read(worldHeight, file); - TectonicPlate plate = new TectonicPlate<>(worldHeight, region.getX(), region.getZ(), adapter, hooks); - int sectionCount = worldHeight >> 4; - - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - var legacyChunk = region.get(x, z); - if (legacyChunk == null) { - continue; - } - - art.arcane.volmlib.util.mantle.runtime.MantleChunk chunk = plate.getOrCreate(x, z); - for (int section = 0; section < sectionCount; section++) { - Matter legacySection = legacyChunk.get(section); - if (legacySection == null || legacySection.getSliceMap().isEmpty()) { - continue; - } - - Matter target = chunk.getOrCreate(section); - target.clearSlices(); - - Matter copy = legacySection.copy(); - for (var entry : copy.getSliceMap().entrySet()) { - target.putSlice(entry.getKey(), (MatterSlice) entry.getValue()); - } - } - } - } - - return plate; - } catch (Throwable t) { - original.addSuppressed(t); - return null; + return worker.read(name, (regionName, in) -> TectonicPlate.read(worldHeight, in, true, adapter, hooks)); + } catch (IOException e) { + TectonicPlate migrated = readLegacy(dataFolder, worldHeight, name, adapter, hooks, e); + if (migrated != null) { + Adapt.warn("Migrated legacy mantle region " + name + " to shared mantle format."); + return migrated; + } + + throw e; } + } + + @Override + public void write(String name, TectonicPlate region) throws IOException { + worker.write(name, "adapt", ".bin", region, TectonicPlate::write); + } + + @Override + public void close() throws IOException { + worker.close(); + } + }; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static TectonicPlate readLegacy(File dataFolder, + int worldHeight, + String name, + MantleDataAdapter adapter, + MantleHooks hooks, + IOException original) { + File file = new File(dataFolder, name); + if (!file.exists()) { + return null; } - public static void stop() { - mantles.v().forEach(WorldData::unregister); - } - - public static WorldData of(World world) { - return mantles.computeIfAbsent(world, WorldData::new); - } + try { + MantleRegion region = MantleRegion.read(worldHeight, file); + TectonicPlate plate = new TectonicPlate<>(worldHeight, region.getX(), region.getZ(), adapter, hooks); + int sectionCount = worldHeight >> 4; + + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + art.arcane.spatial.mantle.MantleChunk legacyChunk = region.get(x, z); + if (legacyChunk == null) { + continue; + } + + art.arcane.volmlib.util.mantle.runtime.MantleChunk chunk = plate.getOrCreate(x, z); + for (int section = 0; section < sectionCount; section++) { + Matter legacySection = legacyChunk.get(section); + if (legacySection == null || legacySection.getSliceMap().isEmpty()) { + continue; + } - public double getEarningsMultiplier(Block block) { - Earnings e = get(block, Earnings.class); + Matter target = chunk.getOrCreate(section); + target.clearSlices(); - if (e == null) { - return 1; + Matter copy = legacySection.copy(); + for (java.util.Map.Entry, art.arcane.spatial.matter.MatterSlice> entry : copy.getSliceMap().entrySet()) { + target.putSlice(entry.getKey(), (MatterSlice) entry.getValue()); + } + } } + } - return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); + return plate; + } catch (Throwable t) { + original.addSuppressed(t); + return null; } + } - public T get(Block block, Class type) { - return mantle.get(block.getX(), block.getY() - minHeight, block.getZ(), type); - } + public static void stop() { + mantles.v().forEach(WorldData::unregister); + } - public void set(Block block, T value) { - mantle.set(block.getX(), block.getY() - minHeight, block.getZ(), value); - } + public static WorldData of(World world) { + return mantles.computeIfAbsent(world, WorldData::new); + } - public void remove(Block block, Class type) { - mantle.remove(block.getX(), block.getY() - minHeight, block.getZ(), type); + public double getEarningsMultiplier(Block block) { + Earnings e = get(block, Earnings.class); + + if (e == null) { + return 1; } - public double reportEarnings(Block block) { - Earnings e = get(block, Earnings.class); - e = e == null ? new Earnings(0) : e; + return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); + } - if (e.getEarnings() >= 127) { - return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); - } + public T get(Block block, Class type) { + return mantle.get(block.getX(), block.getY() - minHeight, block.getZ(), type); + } - set(block, e.increment()); - return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); - } + public void set(Block block, T value) { + mantle.set(block.getX(), block.getY() - minHeight, block.getZ(), value); + } - public void unregister() { - super.unregister(); - mantle.close(); - mantles.remove(world); - } + public void remove(Block block, Class type) { + mantle.remove(block.getX(), block.getY() - minHeight, block.getZ(), type); + } - @EventHandler - public void on(WorldSaveEvent e) { - if (e.getWorld() != world) return; - J.a(mantle::saveAll); - } + public double reportEarnings(Block block) { + Earnings e = get(block, Earnings.class); + e = e == null ? new Earnings(0) : e; - @EventHandler - public void on(WorldUnloadEvent e) { - if (e.getWorld() != world) return; - unregister(); + if (e.getEarnings() >= 127) { + return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); } - @Override - public void onTick() { - mantle.trim(60_000); - } + set(block, e.increment()); + return 1 / (double) (e.getEarnings() == 0 ? 1 : e.getEarnings()); + } + + public void unregister() { + super.unregister(); + mantle.close(); + mantles.remove(world); + } + + @EventHandler + public void on(WorldSaveEvent e) { + if (e.getWorld() != world) return; + J.a(mantle::saveAll); + } + + @EventHandler + public void on(WorldUnloadEvent e) { + if (e.getWorld() != world) return; + unregister(); + } + + @Override + public void onTick() { + mantle.trim(60_000); + } } diff --git a/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java b/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java index c5972c596..1dcc9e392 100644 --- a/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java +++ b/src/main/java/art/arcane/adapt/api/data/unit/Earnings.java @@ -29,33 +29,33 @@ @Data @AllArgsConstructor public class Earnings { - private final int earnings; + private final int earnings; - public Earnings increment() { - if (earnings >= 127) { - return this; - } - - return new Earnings(getEarnings() + 1); + public Earnings increment() { + if (earnings >= 127) { + return this; } - public static class EarningsMatter extends RawMatter { - public EarningsMatter() { - this(1, 1, 1); - } + return new Earnings(getEarnings() + 1); + } - public EarningsMatter(int width, int height, int depth) { - super(width, height, depth, Earnings.class); - } + public static class EarningsMatter extends RawMatter { + public EarningsMatter() { + this(1, 1, 1); + } - @Override - public void writeNode(Earnings earnings, DataOutputStream dataOutputStream) throws IOException { - dataOutputStream.writeByte(earnings.getEarnings()); - } + public EarningsMatter(int width, int height, int depth) { + super(width, height, depth, Earnings.class); + } + + @Override + public void writeNode(Earnings earnings, DataOutputStream dataOutputStream) throws IOException { + dataOutputStream.writeByte(earnings.getEarnings()); + } - @Override - public Earnings readNode(DataInputStream dataInputStream) throws IOException { - return new Earnings(dataInputStream.readByte()); - } + @Override + public Earnings readNode(DataInputStream dataInputStream) throws IOException { + return new Earnings(dataInputStream.readByte()); } + } } diff --git a/src/main/java/art/arcane/adapt/api/item/DataItem.java b/src/main/java/art/arcane/adapt/api/item/DataItem.java index 99ddbea30..448de8705 100644 --- a/src/main/java/art/arcane/adapt/api/item/DataItem.java +++ b/src/main/java/art/arcane/adapt/api/item/DataItem.java @@ -30,59 +30,59 @@ import java.util.List; public interface DataItem { - Material getMaterial(); + Material getMaterial(); - Class getType(); + Class getType(); - void applyLore(T data, List lore); + void applyLore(T data, List lore); - void applyMeta(T data, ItemMeta meta); + void applyMeta(T data, ItemMeta meta); - default ItemStack blank() { - return new ItemStack(getMaterial()); - } + default ItemStack blank() { + return new ItemStack(getMaterial()); + } - default T getData(ItemStack stack) { - if (stack != null - && stack.getType().equals(getMaterial()) - && stack.getItemMeta() != null) { - String r = stack.getItemMeta().getPersistentDataContainer().get(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING); - if (r != null) { - return BukkitGson.gson.fromJson(r, getType()); - } - } - - return null; + default T getData(ItemStack stack) { + if (stack != null + && stack.getType().equals(getMaterial()) + && stack.getItemMeta() != null) { + String r = stack.getItemMeta().getPersistentDataContainer().get(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING); + if (r != null) { + return BukkitGson.gson.fromJson(r, getType()); + } } - default boolean hasData(ItemStack stack) { - if (stack != null - && stack.getType().equals(getMaterial()) - && stack.getItemMeta() != null) { - return stack.getItemMeta().getPersistentDataContainer().has(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING); - } - return false; + return null; + } + + default boolean hasData(ItemStack stack) { + if (stack != null + && stack.getType().equals(getMaterial()) + && stack.getItemMeta() != null) { + return stack.getItemMeta().getPersistentDataContainer().has(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING); } + return false; + } - default void setData(ItemStack item, T t) { - item.setItemMeta(withData(t).getItemMeta()); - } + default void setData(ItemStack item, T t) { + item.setItemMeta(withData(t).getItemMeta()); + } - default ItemStack withData(T t) { - ItemStack item = blank(); - ItemMeta meta = item.getItemMeta(); - - if (meta == null) { - return null; - } - - applyMeta(t, meta); - List lore = new ArrayList<>(); - applyLore(t, lore); - meta.setLore(lore); - meta.getPersistentDataContainer().set(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING, BukkitGson.gson.toJson(t)); - item.setItemMeta(meta); - return item; + default ItemStack withData(T t) { + ItemStack item = blank(); + ItemMeta meta = item.getItemMeta(); + + if (meta == null) { + return null; } + + applyMeta(t, meta); + List lore = new ArrayList<>(); + applyLore(t, lore); + meta.setLore(lore); + meta.getPersistentDataContainer().set(new NamespacedKey(Adapt.instance, getType().getCanonicalName().hashCode() + ""), PersistentDataType.STRING, BukkitGson.gson.toJson(t)); + item.setItemMeta(meta); + return item; + } } diff --git a/src/main/java/art/arcane/adapt/api/item/PotionItem.java b/src/main/java/art/arcane/adapt/api/item/PotionItem.java index ac0c8bc7d..7a4b69e9f 100644 --- a/src/main/java/art/arcane/adapt/api/item/PotionItem.java +++ b/src/main/java/art/arcane/adapt/api/item/PotionItem.java @@ -27,25 +27,25 @@ import java.util.List; public abstract class PotionItem implements DataItem { - @Override - public Class getType() { - return Data.class; - } - - @Override - public void applyLore(Data data, List lore) { - lore.add(C.GREEN + "Grants " + data.getType().getName() + " " + Form.toRoman(data.getPower() + 1)); - } - - @Override - public void applyMeta(Data data, ItemMeta meta) { - - } - - @lombok.Data - @NoArgsConstructor - public static class Data { - private PotionEffectType type; - private int power; - } + @Override + public Class getType() { + return Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + lore.add(C.GREEN + "Grants " + data.getType().getName() + " " + Form.toRoman(data.getPower() + 1)); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + + } + + @lombok.Data + @NoArgsConstructor + public static class Data { + private PotionEffectType type; + private int power; + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java b/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java index 534e08926..2d85012a9 100644 --- a/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/ActionBarNotification.java @@ -27,34 +27,34 @@ @Data @Builder public class ActionBarNotification implements Notification { - @Builder.Default - private final long duration = 750; - @Builder.Default - private final String title = " "; - @Builder.Default - private final String group = "default"; - @Builder.Default - private final long maxTTL = Long.MAX_VALUE; + @Builder.Default + private final long duration = 750; + @Builder.Default + private final String title = " "; + @Builder.Default + private final String group = "default"; + @Builder.Default + private final long maxTTL = Long.MAX_VALUE; - @Override - public long getTotalDuration() { - if (M.ms() > maxTTL) { - return 0; - } - return duration; + @Override + public long getTotalDuration() { + if (M.ms() > maxTTL) { + return 0; } + return duration; + } - @Override - public String getGroup() { - return group; - } - - @Override - public void play(AdaptPlayer p) { - if (M.ms() > maxTTL) { - return; - } + @Override + public String getGroup() { + return group; + } - Adapt.actionbar(p.getPlayer(), title); + @Override + public void play(AdaptPlayer p) { + if (M.ms() > maxTTL) { + return; } + + Adapt.actionbar(p.getPlayer(), title); + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java index 6ca2794ad..236ae3e52 100644 --- a/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/AdvancementNotification.java @@ -30,42 +30,42 @@ @Data @Builder public class AdvancementNotification implements Notification { - @Builder.Default - private final Material icon = Material.DIAMOND; - @Builder.Default - private final CustomModel model = null; - @Builder.Default - private final String title = " "; - @Builder.Default - private final String description = " "; - @Builder.Default - private final AdaptAdvancementFrame frameType = AdaptAdvancementFrame.TASK; - @Builder.Default - private final String group = "default"; + @Builder.Default + private final Material icon = Material.DIAMOND; + @Builder.Default + private final CustomModel model = null; + @Builder.Default + private final String title = " "; + @Builder.Default + private final String description = " "; + @Builder.Default + private final AdaptAdvancementFrame frameType = AdaptAdvancementFrame.TASK; + @Builder.Default + private final String group = "default"; - @Override - public long getTotalDuration() { - return 100; // TODO: Actually calculate - } + @Override + public long getTotalDuration() { + return 100; // TODO: Actually calculate + } - @Override - public String getGroup() { - return group; - } + @Override + public String getGroup() { + return group; + } - @Override - public void play(AdaptPlayer p) { - if (p.getPlayer() != null) { - var icon = getModel() != null ? getModel().toItemStack() : new ItemStack(getIcon()); - AdvancementUtils.displayToast(p.getPlayer(), icon, title, description, frameType); - } + @Override + public void play(AdaptPlayer p) { + if (p.getPlayer() != null) { + ItemStack icon = getModel() != null ? getModel().toItemStack() : new ItemStack(getIcon()); + AdvancementUtils.displayToast(p.getPlayer(), icon, title, description, frameType); } + } - public String buildTitle() { - if (description.trim().isEmpty()) { - return title; - } - - return title + "\n" + description; + public String buildTitle() { + if (description.trim().isEmpty()) { + return title; } + + return title + "\n" + description; + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/Notification.java b/src/main/java/art/arcane/adapt/api/notification/Notification.java index 69062cead..1240aeba7 100644 --- a/src/main/java/art/arcane/adapt/api/notification/Notification.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notification.java @@ -21,13 +21,13 @@ import art.arcane.adapt.api.world.AdaptPlayer; public interface Notification { - String DEFAULT_GROUP = "default"; + String DEFAULT_GROUP = "default"; - long getTotalDuration(); + long getTotalDuration(); - void play(AdaptPlayer p); + void play(AdaptPlayer p); - default String getGroup() { - return DEFAULT_GROUP; - } + default String getGroup() { + return DEFAULT_GROUP; + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/Notifier.java b/src/main/java/art/arcane/adapt/api/notification/Notifier.java index 0c1d0bb86..0be774ba4 100644 --- a/src/main/java/art/arcane/adapt/api/notification/Notifier.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notifier.java @@ -36,128 +36,128 @@ @EqualsAndHashCode(callSuper = true) @Data public class Notifier extends TickedObject { - private final Queue queue; - private final AdaptPlayer target; - private final KMap lastSkills; - private final KMap lastSkillValues; - private int busyTicks; - private int delayTicks; - private long lastInstance; - - public Notifier(AdaptPlayer target) { - super("notifications", target.getPlayer().getUniqueId() + "-notify", 97); - queue = new ConcurrentLinkedQueue<>(); - lastSkills = new KMap<>(); - lastSkillValues = new KMap<>(); - busyTicks = 0; - delayTicks = 0; - this.target = target; - lastInstance = 0; + private final Queue queue; + private final AdaptPlayer target; + private final KMap lastSkills; + private final KMap lastSkillValues; + private int busyTicks; + private int delayTicks; + private long lastInstance; + + public Notifier(AdaptPlayer target) { + super("notifications", target.getPlayer().getUniqueId() + "-notify", 97); + queue = new ConcurrentLinkedQueue<>(); + lastSkills = new KMap<>(); + lastSkillValues = new KMap<>(); + busyTicks = 0; + delayTicks = 0; + this.target = target; + lastInstance = 0; + } + + public void notifyXP(String line, double value) { + try { + if (!lastSkills.containsKey(line)) { + lastSkillValues.put(line, 0d); + } + + lastSkills.put(line, M.ms()); + lastSkillValues.put(line, lastSkillValues.get(line) + value); + lastInstance = M.ms(); + + + StringBuilder sb = new StringBuilder(); + + for (String i : lastSkills.sortKNumber().reverse()) { + Skill sk = getServer().getSkillRegistry().getSkill(i); + sb.append(i.equals(line) ? sk.getDisplayName() : sk.getShortName()) + .append(C.RESET).append(C.GRAY) + .append(" +").append(C.WHITE) + .append(line.equals(i) ? C.UNDERLINE : "") + .append(Form.f(lastSkillValues.get(i).intValue())) + .append(C.RESET).append(C.GRAY) + .append("XP "); + } + + while (lastSkills.size() > 5) { + String s = lastSkills.sortKNumber().reverse().get(0); + lastSkills.remove(s); + lastSkillValues.remove(s); + } + + target.getActionBarNotifier().queue(ActionBarNotification.builder() + .duration(0) + .maxTTL(M.ms() + 100) + .title(sb.toString()) + .group("xp") + .build()); + } catch (Throwable e) { + Adapt.verbose("Failed to notify xp: " + e.getMessage()); } + } - public void notifyXP(String line, double value) { - try { - if (!lastSkills.containsKey(line)) { - lastSkillValues.put(line, 0d); - } - - lastSkills.put(line, M.ms()); - lastSkillValues.put(line, lastSkillValues.get(line) + value); - lastInstance = M.ms(); - - - StringBuilder sb = new StringBuilder(); - - for (String i : lastSkills.sortKNumber().reverse()) { - Skill sk = getServer().getSkillRegistry().getSkill(i); - sb.append(i.equals(line) ? sk.getDisplayName() : sk.getShortName()) - .append(C.RESET).append(C.GRAY) - .append(" +").append(C.WHITE) - .append(line.equals(i) ? C.UNDERLINE : "") - .append(Form.f(lastSkillValues.get(i).intValue())) - .append(C.RESET).append(C.GRAY) - .append("XP "); - } - - while (lastSkills.size() > 5) { - String s = lastSkills.sortKNumber().reverse().get(0); - lastSkills.remove(s); - lastSkillValues.remove(s); - } - - target.getActionBarNotifier().queue(ActionBarNotification.builder() - .duration(0) - .maxTTL(M.ms() + 100) - .title(sb.toString()) - .group("xp") - .build()); - } catch (Throwable e) { - Adapt.verbose("Failed to notify xp: " + e.getMessage()); - } - } - - public void queue(Notification... f) { - queue.addAll(Arrays.asList(f)); - } + public void queue(Notification... f) { + queue.addAll(Arrays.asList(f)); + } - public boolean isBusy() { - return busyTicks > 1 || !queue.isEmpty(); - } + public boolean isBusy() { + return busyTicks > 1 || !queue.isEmpty(); + } - @Override - public void onTick() { - cleanupSkills(); + @Override + public void onTick() { + cleanupSkills(); - if (busyTicks > 6) { - busyTicks = 6; - } - - if (busyTicks-- > 0) { - return; - } + if (busyTicks > 6) { + busyTicks = 6; + } - if (busyTicks < 0) { - busyTicks = 0; - } + if (busyTicks-- > 0) { + return; + } - delayTicks--; - if (delayTicks > 0) { - return; - } + if (busyTicks < 0) { + busyTicks = 0; + } - if (delayTicks < 0) { - delayTicks = 0; - } + delayTicks--; + if (delayTicks > 0) { + return; + } + if (delayTicks < 0) { + delayTicks = 0; + } - if (!isBusy()) { - cleanupStackedNotifications(); - } - Notification n = queue.poll(); + if (!isBusy()) { + cleanupStackedNotifications(); + } - if (n == null) { - return; - } + Notification n = queue.poll(); - delayTicks += (n.getTotalDuration() / 50D) + 1; - Adapt.verbose("Playing Notification " + n + " --> " + System.identityHashCode(this)); - n.play(target); + if (n == null) { + return; } - private void cleanupStackedNotifications() { + delayTicks += (n.getTotalDuration() / 50D) + 1; + Adapt.verbose("Playing Notification " + n + " --> " + System.identityHashCode(this)); + n.play(target); + } - } + private void cleanupStackedNotifications() { + + } - private void cleanupSkills() { - for (String i : lastSkills.k()) { - if (lastSkills.get(i) == null) { // Shouldn't happen, but just in case I guess. - return; - } - if (M.ms() - lastSkills.get(i) > 10000 || (M.ms() - lastInstance > 3100 && M.ms() - lastSkills.get(i) > 3100)) { - lastSkills.remove(i); - lastSkillValues.remove(i); - } - } + private void cleanupSkills() { + for (String i : lastSkills.k()) { + if (lastSkills.get(i) == null) { // Shouldn't happen, but just in case I guess. + return; + } + if (M.ms() - lastSkills.get(i) > 10000 || (M.ms() - lastInstance > 3100 && M.ms() - lastSkills.get(i) > 3100)) { + lastSkills.remove(i); + lastSkillValues.remove(i); + } } + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java index 5a3ac3d10..8075db642 100644 --- a/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/SoundNotification.java @@ -27,50 +27,50 @@ @Data @Builder public class SoundNotification implements Notification { - @Builder.Default - private final long isolation = 0; - @Builder.Default - private final long predelay = 0; - @Builder.Default - private final Sound sound = Sound.BLOCK_LEVER_CLICK; - @Builder.Default - private final float volume = 1F; - @Builder.Default - private final float pitch = 1F; - @Builder.Default - private final String group = "default"; + @Builder.Default + private final long isolation = 0; + @Builder.Default + private final long predelay = 0; + @Builder.Default + private final Sound sound = Sound.BLOCK_LEVER_CLICK; + @Builder.Default + private final float volume = 1F; + @Builder.Default + private final float pitch = 1F; + @Builder.Default + private final String group = "default"; - public SoundNotification withXP(double xp) { - double sig = xp / 1000D; - float pitch = this.pitch; - float volume = this.volume; - pitch -= sig / 6.6; - pitch = pitch < 0.1 ? (float) 0.1 : pitch; - double vp = sig / 5; - vp = Math.min(vp, 0.8); - volume += vp; - pitch = pitch < 0.1 ? (float) 0.1 : pitch; + public SoundNotification withXP(double xp) { + double sig = xp / 1000D; + float pitch = this.pitch; + float volume = this.volume; + pitch -= sig / 6.6; + pitch = pitch < 0.1 ? (float) 0.1 : pitch; + double vp = sig / 5; + vp = Math.min(vp, 0.8); + volume += vp; + pitch = pitch < 0.1 ? (float) 0.1 : pitch; - return SoundNotification.builder() - .sound(sound) - .isolation(isolation) - .predelay(predelay) - .volume(volume) - .pitch(pitch) - .build(); - } + return SoundNotification.builder() + .sound(sound) + .isolation(isolation) + .predelay(predelay) + .volume(volume) + .pitch(pitch) + .build(); + } - @Override - public long getTotalDuration() { - return isolation; - } + @Override + public long getTotalDuration() { + return isolation; + } - @Override - public String getGroup() { - return group; - } + @Override + public String getGroup() { + return group; + } - public void play(AdaptPlayer p) { - SoundPlayer.of(p.getPlayer()).play(p.getPlayer().getLocation(), sound, volume, pitch); - } + public void play(AdaptPlayer p) { + SoundPlayer.of(p.getPlayer()).play(p.getPlayer().getLocation(), sound, volume, pitch); + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java b/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java index 0770bc846..404925253 100644 --- a/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java +++ b/src/main/java/art/arcane/adapt/api/notification/TitleNotification.java @@ -25,31 +25,31 @@ @Data @Builder public class TitleNotification implements Notification { - @Builder.Default - private final long in = 250; - @Builder.Default - private final long stay = 1450; - @Builder.Default - private final long out = 750; - @Builder.Default - private final String title = " "; - @Builder.Default - private final String subtitle = " "; - @Builder.Default - private final String group = "default"; + @Builder.Default + private final long in = 250; + @Builder.Default + private final long stay = 1450; + @Builder.Default + private final long out = 750; + @Builder.Default + private final String title = " "; + @Builder.Default + private final String subtitle = " "; + @Builder.Default + private final String group = "default"; - @Override - public long getTotalDuration() { - return in + out + stay; - } + @Override + public long getTotalDuration() { + return in + out + stay; + } - @Override - public String getGroup() { - return group; - } + @Override + public String getGroup() { + return group; + } - @Override - public void play(AdaptPlayer p) { - p.getPlayer().sendTitle(title.isEmpty() ? " " : title, subtitle, (int) (in / 50D), (int) (stay / 50D), (int) (out / 50D)); - } + @Override + public void play(AdaptPlayer p) { + p.getPlayer().sendTitle(title.isEmpty() ? " " : title, subtitle, (int) (in / 50D), (int) (stay / 50D), (int) (out / 50D)); + } } diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java index 86b659aa7..59411f674 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingManager.java @@ -25,145 +25,145 @@ public class BrewingManager implements Listener { - private static final Map> recipes = new ConcurrentHashMap<>(); - private static final Map activeTasks = new ConcurrentHashMap<>(); + private static final Map> recipes = new ConcurrentHashMap<>(); + private static final Map activeTasks = new ConcurrentHashMap<>(); - public static void registerRecipe(String adaptation, BrewingRecipe recipe) { - if (adaptation == null || adaptation.isBlank() || recipe == null) { - return; - } - - recipes.computeIfAbsent(recipe, unused -> ConcurrentHashMap.newKeySet()).add(adaptation); + public static void registerRecipe(String adaptation, BrewingRecipe recipe) { + if (adaptation == null || adaptation.isBlank() || recipe == null) { + return; } - @EventHandler - public void onInventoryClick(InventoryClickEvent e) { - if (e.getView().getTopInventory().getType() != InventoryType.BREWING || e.getView().getTopInventory().getHolder() == null) { - return; + recipes.computeIfAbsent(recipe, unused -> ConcurrentHashMap.newKeySet()).add(adaptation); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + if (e.getView().getTopInventory().getType() != InventoryType.BREWING || e.getView().getTopInventory().getHolder() == null) { + return; + } + Adapt.verbose("Brewing click: " + e.getRawSlot()); + BrewerInventory inv = (BrewerInventory) e.getInventory(); + boolean doTheThing = inv.getIngredient() == null + && e.getCursor() != null + && e.getRawSlot() == 3 + && e.getClickedInventory() != null + && e.getClickedInventory().getType().equals(InventoryType.BREWING) + && (e.getClick() == ClickType.LEFT); + if (doTheThing) { + Adapt.verbose("Brewing Stand Ingredient Clicked"); + e.setCancelled(true); + } + Player clicker = (Player) e.getWhoClicked(); + J.runEntity(clicker, () -> { + if (doTheThing) { + inv.setIngredient(e.getCursor()); + e.setCursor(null); + } + + BrewingStand stand = inv.getHolder(); + if (stand == null) { + return; + } + + Location standLocation = stand.getLocation(); + AdaptPlayer p = Adapt.instance.getAdaptServer().getPlayer(clicker); + BrewingRecipe recipe = findMatchingRecipe(standLocation); + if (recipe == null) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); } - Adapt.verbose("Brewing click: " + e.getRawSlot()); - BrewerInventory inv = (BrewerInventory) e.getInventory(); - boolean doTheThing = inv.getIngredient() == null - && e.getCursor() != null - && e.getRawSlot() == 3 - && e.getClickedInventory() != null - && e.getClickedInventory().getType().equals(InventoryType.BREWING) - && (e.getClick() == ClickType.LEFT); - if (doTheThing) { - Adapt.verbose("Brewing Stand Ingredient Clicked"); - e.setCancelled(true); + return; + } + + Set requiredAdaptations = recipes.get(recipe); + BrewingTask active = activeTasks.get(standLocation); + if (!playerHasRequiredAdaptation(p, requiredAdaptations)) { + if (active != null && !active.getRecipe().getId().equals(recipe.getId())) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); + } } - Player clicker = (Player) e.getWhoClicked(); - J.runEntity(clicker, () -> { - if (doTheThing) { - inv.setIngredient(e.getCursor()); - e.setCursor(null); - } - - BrewingStand stand = inv.getHolder(); - if (stand == null) { - return; - } - - Location standLocation = stand.getLocation(); - AdaptPlayer p = Adapt.instance.getAdaptServer().getPlayer(clicker); - BrewingRecipe recipe = findMatchingRecipe(standLocation); - if (recipe == null) { - BrewingTask removed = activeTasks.remove(standLocation); - if (removed != null) { - removed.cancel(); - } - return; - } - - Set requiredAdaptations = recipes.get(recipe); - BrewingTask active = activeTasks.get(standLocation); - if (!playerHasRequiredAdaptation(p, requiredAdaptations)) { - if (active != null && !active.getRecipe().getId().equals(recipe.getId())) { - BrewingTask removed = activeTasks.remove(standLocation); - if (removed != null) { - removed.cancel(); - } - } - return; - } - - if (active != null && active.getRecipe().getId().equals(recipe.getId())) { - return; - } - - if (active != null) { - BrewingTask removed = activeTasks.remove(standLocation); - if (removed != null) { - removed.cancel(); - } - } - - activeTasks.put(standLocation, new BrewingTask(recipe, standLocation)); - }, 1); - } + return; + } - @EventHandler - public void onBrew(BrewEvent e) { - ItemStack ingredient = e.getContents().getIngredient(); - if (ingredient == null) { - return; - } + if (active != null && active.getRecipe().getId().equals(recipe.getId())) { + return; + } - Material m = ingredient.getType(); - if (m != Material.GUNPOWDER && m != Material.DRAGON_BREATH) { - return; + if (active != null) { + BrewingTask removed = activeTasks.remove(standLocation); + if (removed != null) { + removed.cancel(); } - for (int i = 0; i < 3; i++) { - ItemStack s = e.getContents().getItem(i); - if (s == null) continue; - PotionMeta meta = (PotionMeta) s.getItemMeta(); - var opt = Reflect.getEnum(PotionType.class, "UNCRAFTABLE"); - if (opt.isEmpty() && meta.getBasePotionData() != null) - continue; - if (opt.isPresent() && meta.getBasePotionData().getType() == opt.get()) - continue; - ItemStack newStack = s.clone(); - if (m == Material.GUNPOWDER) { - newStack.setType(Material.SPLASH_POTION); - } else { - newStack.setType(Material.LINGERING_POTION); + } + + activeTasks.put(standLocation, new BrewingTask(recipe, standLocation)); + }, 1); + } + + @EventHandler + public void onBrew(BrewEvent e) { + ItemStack ingredient = e.getContents().getIngredient(); + if (ingredient == null) { + return; + } + + Material m = ingredient.getType(); + if (m != Material.GUNPOWDER && m != Material.DRAGON_BREATH) { + return; + } + for (int i = 0; i < 3; i++) { + ItemStack s = e.getContents().getItem(i); + if (s == null) continue; + PotionMeta meta = (PotionMeta) s.getItemMeta(); + java.util.Optional opt = Reflect.getEnum(PotionType.class, "UNCRAFTABLE"); + if (opt.isEmpty() && meta.getBasePotionData() != null) + continue; + if (opt.isPresent() && meta.getBasePotionData().getType() == opt.get()) + continue; + ItemStack newStack = s.clone(); + if (m == Material.GUNPOWDER) { + newStack.setType(Material.SPLASH_POTION); + } else { + newStack.setType(Material.LINGERING_POTION); /*PotionMeta meta = (PotionMeta)newStack.getItemMeta(); List newEffects = Lists.newArrayList(); meta.getCustomEffects().forEach(effect -> newEffects.add(new PotionEffect(effect.getType(), effect.getDuration() / 4, effect.getAmplifier()))); meta.clearCustomEffects(); newEffects.forEach(effect -> meta.addCustomEffect(effect, true)); newStack.setItemMeta(meta);*/ - } - e.getResults().set(i, newStack); - } + } + e.getResults().set(i, newStack); } + } - private BrewingRecipe findMatchingRecipe(Location standLocation) { - if (standLocation == null) { - return null; - } - - for (BrewingRecipe recipe : recipes.keySet()) { - if (BrewingTask.isValid(recipe, standLocation)) { - return recipe; - } - } + private BrewingRecipe findMatchingRecipe(Location standLocation) { + if (standLocation == null) { + return null; + } - return null; + for (BrewingRecipe recipe : recipes.keySet()) { + if (BrewingTask.isValid(recipe, standLocation)) { + return recipe; + } } - private boolean playerHasRequiredAdaptation(AdaptPlayer player, Set requiredAdaptations) { - if (player == null || requiredAdaptations == null || requiredAdaptations.isEmpty()) { - return false; - } + return null; + } - for (String adaptation : requiredAdaptations) { - if (player.hasAdaptation(adaptation)) { - return true; - } - } + private boolean playerHasRequiredAdaptation(AdaptPlayer player, Set requiredAdaptations) { + if (player == null || requiredAdaptations == null || requiredAdaptations.isEmpty()) { + return false; + } - return false; + for (String adaptation : requiredAdaptations) { + if (player.hasAdaptation(adaptation)) { + return true; + } } + + return false; + } } diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java b/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java index 2c320c913..7ecbb0d86 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingRecipe.java @@ -8,8 +8,8 @@ @Data @Builder public class BrewingRecipe { - private final String id; - private final Material ingredient; - private final ItemStack basePotion, result; - private final int brewingTime, fuelCost; + private final String id; + private final Material ingredient; + private final ItemStack basePotion, result; + private final int brewingTime, fuelCost; } diff --git a/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java index 884c335b1..242114738 100644 --- a/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java +++ b/src/main/java/art/arcane/adapt/api/potion/BrewingTask.java @@ -13,102 +13,102 @@ public class BrewingTask implements Runnable { - private static final int DEFAULT_BREW_TIME = 400; - - @Getter - private final BrewingRecipe recipe; - - private final Location location; - private int brewTime; - private volatile boolean cancelled; - - public BrewingTask(BrewingRecipe recipe, Location loc) { - this.recipe = recipe; - this.location = loc; - this.brewTime = recipe.getBrewingTime(); - - BrewingStand block = (BrewingStand) loc.getBlock().getState(); - if (block.getFuelLevel() > recipe.getFuelCost()) { - block.setFuelLevel(block.getFuelLevel() - recipe.getFuelCost()); - } else { - int rest = recipe.getFuelCost() - block.getFuelLevel(); - block.getInventory().setIngredient(decrease(block.getInventory().getFuel(), 1 + rest / 20)); - block.setFuelLevel(20 - rest % 20); - } + private static final int DEFAULT_BREW_TIME = 400; + + @Getter + private final BrewingRecipe recipe; + + private final Location location; + private int brewTime; + private volatile boolean cancelled; + + public BrewingTask(BrewingRecipe recipe, Location loc) { + this.recipe = recipe; + this.location = loc; + this.brewTime = recipe.getBrewingTime(); + + BrewingStand block = (BrewingStand) loc.getBlock().getState(); + if (block.getFuelLevel() > recipe.getFuelCost()) { + block.setFuelLevel(block.getFuelLevel() - recipe.getFuelCost()); + } else { + int rest = recipe.getFuelCost() - block.getFuelLevel(); + block.getInventory().setIngredient(decrease(block.getInventory().getFuel(), 1 + rest / 20)); + block.setFuelLevel(20 - rest % 20); + } - block.setBrewingTime(DEFAULT_BREW_TIME); - block.update(true); + block.setBrewingTime(DEFAULT_BREW_TIME); + block.update(true); - J.runAt(location, this::run); + J.runAt(location, this::run); + } + + public static ItemStack decrease(ItemStack source, int amount) { + if (source.getAmount() > amount) { + source.setAmount(source.getAmount() - amount); + return source; + } else { + return new ItemStack(Material.AIR); } + } - public static ItemStack decrease(ItemStack source, int amount) { - if (source.getAmount() > amount) { - source.setAmount(source.getAmount() - amount); - return source; - } else { - return new ItemStack(Material.AIR); - } + public static boolean isValid(BrewingRecipe recipe, Location loc) { + BrewingStand block = (BrewingStand) loc.getBlock().getState(); + BrewerInventory inv = block.getInventory(); + if (inv.getIngredient() == null || recipe.getIngredient() != inv.getIngredient().getType()) { + return false; } - public static boolean isValid(BrewingRecipe recipe, Location loc) { - BrewingStand block = (BrewingStand) loc.getBlock().getState(); - BrewerInventory inv = block.getInventory(); - if (inv.getIngredient() == null || recipe.getIngredient() != inv.getIngredient().getType()) { - return false; - } + int totalFuel = (inv.getFuel() != null && inv.getFuel().getType() != Material.AIR ? inv.getFuel().getAmount() * 20 : 0) + block.getFuelLevel(); + if (totalFuel < recipe.getFuelCost()) { + return false; + } - int totalFuel = (inv.getFuel() != null && inv.getFuel().getType() != Material.AIR ? inv.getFuel().getAmount() * 20 : 0) + block.getFuelLevel(); - if (totalFuel < recipe.getFuelCost()) { - return false; - } + for (int i = 0; i < 3; i++) { + if (recipe.getBasePotion().isSimilar(inv.getItem(i))) { + return true; + } + } - for (int i = 0; i < 3; i++) { - if (recipe.getBasePotion().isSimilar(inv.getItem(i))) { - return true; - } - } + return false; + } - return false; + @Override + public void run() { + if (cancelled) { + return; } - @Override - public void run() { - if (cancelled) { - return; - } + BrewingStand block = (BrewingStand) this.location.getBlock().getState(); + BrewerInventory inventory = block.getInventory(); + if (brewTime <= 0) { + inventory.setIngredient(decrease(inventory.getIngredient(), 1)); - BrewingStand block = (BrewingStand) this.location.getBlock().getState(); - BrewerInventory inventory = block.getInventory(); - if (brewTime <= 0) { - inventory.setIngredient(decrease(inventory.getIngredient(), 1)); - - for (int i = 0; i < 3; i++) { - if (recipe.getBasePotion().equals(inventory.getItem(i))) { - inventory.setItem(i, recipe.getResult()); - } - } - - inventory.getViewers().forEach(e -> { - if (e instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(block.getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1, 1); - } - }); - cancel(); - return; + for (int i = 0; i < 3; i++) { + if (recipe.getBasePotion().equals(inventory.getItem(i))) { + inventory.setItem(i, recipe.getResult()); } - brewTime--; - block.setBrewingTime(getRemainingTime()); - block.update(true); - J.runAt(location, this::run, 1); - } + } - public void cancel() { - cancelled = true; - } - - private int getRemainingTime() { - return (int) (DEFAULT_BREW_TIME * (brewTime / (float) recipe.getBrewingTime())); + inventory.getViewers().forEach(e -> { + if (e instanceof Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(block.getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1, 1); + } + }); + cancel(); + return; } + brewTime--; + block.setBrewingTime(getRemainingTime()); + block.update(true); + J.runAt(location, this::run, 1); + } + + public void cancel() { + cancelled = true; + } + + private int getRemainingTime() { + return (int) (DEFAULT_BREW_TIME * (brewTime / (float) recipe.getBrewingTime())); + } } diff --git a/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java index 1fa2083ab..f002903e1 100644 --- a/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java +++ b/src/main/java/art/arcane/adapt/api/potion/PotionBuilder.java @@ -21,95 +21,95 @@ @Getter public class PotionBuilder { - private final List effects = Lists.newArrayList(); - private final Type type; - - private Component name; - private List lore; - private Color color; - private PotionType baseType = PotionTypes.UNCRAFTABLE; - private ItemStack baseItem; - - private PotionBuilder(Type type) { - this.type = type; - } - - public static ItemStack vanilla(@NotNull Type type, @NotNull PotionType potion) { - return of(type) - .setBaseType(potion) - .build(); - } - - public static PotionBuilder of(@NotNull Type type) { - return new PotionBuilder(type); - } - - public static PotionBuilder of(@NotNull ItemStack item) { - return Version.get().editPotion(item); - } - - public PotionBuilder setColor(@Nullable Color color) { - this.color = color; - return this; - } - - public PotionBuilder addEffect(@NotNull PotionEffect effect) { - effects.add(effect); - return this; - } - - public PotionBuilder addEffect(PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon) { - effects.add(new PotionEffect(type, duration, amplifier, ambient, particles, icon)); - return this; - } - - public PotionBuilder setName(@Nullable String name) { - this.name = name != null ? Component.text(name) - .decoration(TextDecoration.ITALIC, false) : null; - return this; + private final List effects = Lists.newArrayList(); + private final Type type; + + private Component name; + private List lore; + private Color color; + private PotionType baseType = PotionTypes.UNCRAFTABLE; + private ItemStack baseItem; + + private PotionBuilder(Type type) { + this.type = type; + } + + public static ItemStack vanilla(@NotNull Type type, @NotNull PotionType potion) { + return of(type) + .setBaseType(potion) + .build(); + } + + public static PotionBuilder of(@NotNull Type type) { + return new PotionBuilder(type); + } + + public static PotionBuilder of(@NotNull ItemStack item) { + return Version.get().editPotion(item); + } + + public PotionBuilder setColor(@Nullable Color color) { + this.color = color; + return this; + } + + public PotionBuilder addEffect(@NotNull PotionEffect effect) { + effects.add(effect); + return this; + } + + public PotionBuilder addEffect(PotionEffectType type, int duration, int amplifier, boolean ambient, boolean particles, boolean icon) { + effects.add(new PotionEffect(type, duration, amplifier, ambient, particles, icon)); + return this; + } + + public PotionBuilder setName(@Nullable String name) { + this.name = name != null ? Component.text(name) + .decoration(TextDecoration.ITALIC, false) : null; + return this; + } + + public PotionBuilder setName(@Nullable Component name) { + this.name = name; + return this; + } + + public PotionBuilder addLore(@NotNull Component lore) { + if (this.lore == null) { + this.lore = Lists.newArrayList(); } - public PotionBuilder setName(@Nullable Component name) { - this.name = name; - return this; - } - - public PotionBuilder addLore(@NotNull Component lore) { - if(this.lore == null) { - this.lore = Lists.newArrayList(); - } - - this.lore.add(lore); - return this; - } - - public PotionBuilder setLore(@Nullable List<@NotNull Component> lore) { - this.lore = lore; - return this; - } - - public PotionBuilder setBaseItem(@Nullable ItemStack item) { - this.baseItem = item; - return this; - } - - public PotionBuilder setBaseType(@Nullable PotionType data) { - this.baseType = data; - return this; - } - - @SuppressWarnings("ConstantConditions") - public ItemStack build() { - return Version.get().buildPotion(this); - } - - @Getter - @AllArgsConstructor - public enum Type { - REGULAR(Material.POTION), - SPLASH(Material.SPLASH_POTION), - LINGERING(Material.LINGERING_POTION); - - private final Material material; - } + this.lore.add(lore); + return this; + } + + public PotionBuilder setLore(@Nullable List<@NotNull Component> lore) { + this.lore = lore; + return this; + } + + public PotionBuilder setBaseItem(@Nullable ItemStack item) { + this.baseItem = item; + return this; + } + + public PotionBuilder setBaseType(@Nullable PotionType data) { + this.baseType = data; + return this; + } + + @SuppressWarnings("ConstantConditions") + public ItemStack build() { + return Version.get().buildPotion(this); + } + + @Getter + @AllArgsConstructor + public enum Type { + REGULAR(Material.POTION), + SPLASH(Material.SPLASH_POTION), + LINGERING(Material.LINGERING_POTION); + + private final Material material; + } } diff --git a/src/main/java/art/arcane/adapt/api/protection/Protector.java b/src/main/java/art/arcane/adapt/api/protection/Protector.java index f415bb5cc..53a272de9 100644 --- a/src/main/java/art/arcane/adapt/api/protection/Protector.java +++ b/src/main/java/art/arcane/adapt/api/protection/Protector.java @@ -24,39 +24,39 @@ public interface Protector { - default boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { - return true; - } + default boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { + return true; + } - default boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { - return true; - } + default boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { + return true; + } - default boolean canPVP(Player player, Location victimLocation, Adaptation adaptation) { - return true; - } + default boolean canPVP(Player player, Location victimLocation, Adaptation adaptation) { + return true; + } - default boolean canPVE(Player player, Location victimLocation, Adaptation adaptation) { - return true; - } + default boolean canPVE(Player player, Location victimLocation, Adaptation adaptation) { + return true; + } - default boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { - return true; - } + default boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { + return true; + } - default boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { - return true; - } + default boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { + return true; + } - default boolean checkRegion(Player player, Location location, Adaptation adaptation) { - return true; - } + default boolean checkRegion(Player player, Location location, Adaptation adaptation) { + return true; + } - String getName(); + String getName(); - boolean isEnabledByDefault(); + boolean isEnabledByDefault(); - default void unregister() { - } + default void unregister() { + } } diff --git a/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java index b303e5b02..0ffe64835 100644 --- a/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java +++ b/src/main/java/art/arcane/adapt/api/protection/ProtectorRegistry.java @@ -25,60 +25,60 @@ import java.util.List; public class ProtectorRegistry { - private final List protectors = new ArrayList<>(); - private volatile List allProtectorsSnapshot = List.of(); - private volatile List defaultProtectorsSnapshot = List.of(); + private final List protectors = new ArrayList<>(); + private volatile List allProtectorsSnapshot = List.of(); + private volatile List defaultProtectorsSnapshot = List.of(); - public synchronized void registerProtector(Protector protector) { - if (protector == null || protectors.contains(protector)) { - return; - } - - Adapt.verbose("Protector: \"" + protector.getName() + "\" registered."); - protectors.add(protector); - rebuildSnapshots(); + public synchronized void registerProtector(Protector protector) { + if (protector == null || protectors.contains(protector)) { + return; } - public synchronized void unregisterProtector(Protector protector) { - if (protector == null) { - return; - } + Adapt.verbose("Protector: \"" + protector.getName() + "\" registered."); + protectors.add(protector); + rebuildSnapshots(); + } - protector.unregister(); - if (protectors.remove(protector)) { - rebuildSnapshots(); - } + public synchronized void unregisterProtector(Protector protector) { + if (protector == null) { + return; } - public List getDefaultProtectors() { - return defaultProtectorsSnapshot; + protector.unregister(); + if (protectors.remove(protector)) { + rebuildSnapshots(); } + } - public List getAllProtectors() { - return allProtectorsSnapshot; - } + public List getDefaultProtectors() { + return defaultProtectorsSnapshot; + } - public synchronized void unregisterAll() { - protectors.forEach(Protector::unregister); - protectors.clear(); - rebuildSnapshots(); - } + public List getAllProtectors() { + return allProtectorsSnapshot; + } - private void rebuildSnapshots() { - List all = new ArrayList<>(protectors.size()); - List defaults = new ArrayList<>(Math.max(1, protectors.size() / 2)); + public synchronized void unregisterAll() { + protectors.forEach(Protector::unregister); + protectors.clear(); + rebuildSnapshots(); + } - for (Protector protector : protectors) { - if (protector == null) { - continue; - } - all.add(protector); - if (protector.isEnabledByDefault()) { - defaults.add(protector); - } - } + private void rebuildSnapshots() { + List all = new ArrayList<>(protectors.size()); + List defaults = new ArrayList<>(Math.max(1, protectors.size() / 2)); - allProtectorsSnapshot = Collections.unmodifiableList(all); - defaultProtectorsSnapshot = Collections.unmodifiableList(defaults); + for (Protector protector : protectors) { + if (protector == null) { + continue; + } + all.add(protector); + if (protector.isEnabledByDefault()) { + defaults.add(protector); + } } + + allProtectorsSnapshot = Collections.unmodifiableList(all); + defaultProtectorsSnapshot = Collections.unmodifiableList(defaults); + } } diff --git a/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java b/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java index 9a762aefe..7afb0a1c0 100644 --- a/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java +++ b/src/main/java/art/arcane/adapt/api/protection/WorldPolicyLatencyTelemetry.java @@ -3,66 +3,66 @@ import java.util.ArrayDeque; public final class WorldPolicyLatencyTelemetry { - private static final long WINDOW_MS = 60_000L; - private static final int MAX_SAMPLES = 200_000; + private static final long WINDOW_MS = 60_000L; + private static final int MAX_SAMPLES = 200_000; - private static final ArrayDeque SAMPLES = new ArrayDeque<>(); - private static long totalNanos = 0L; + private static final ArrayDeque SAMPLES = new ArrayDeque<>(); + private static long totalNanos = 0L; - private WorldPolicyLatencyTelemetry() { - } + private WorldPolicyLatencyTelemetry() { + } - public static void recordNanos(long durationNanos) { - if (durationNanos < 0L) { - return; - } + public static void recordNanos(long durationNanos) { + if (durationNanos < 0L) { + return; + } - long now = System.currentTimeMillis(); - synchronized (SAMPLES) { - trim(now); - SAMPLES.addLast(new Sample(now, durationNanos)); - totalNanos += durationNanos; + long now = System.currentTimeMillis(); + synchronized (SAMPLES) { + trim(now); + SAMPLES.addLast(new Sample(now, durationNanos)); + totalNanos += durationNanos; - while (SAMPLES.size() > MAX_SAMPLES) { - Sample oldest = SAMPLES.removeFirst(); - totalNanos -= oldest.durationNanos; - } + while (SAMPLES.size() > MAX_SAMPLES) { + Sample oldest = SAMPLES.removeFirst(); + totalNanos -= oldest.durationNanos; + } - if (totalNanos < 0L) { - totalNanos = 0L; - } - } + if (totalNanos < 0L) { + totalNanos = 0L; + } } + } - public static double averageMillis(long now) { - synchronized (SAMPLES) { - trim(now); - if (SAMPLES.isEmpty()) { - return 0D; - } + public static double averageMillis(long now) { + synchronized (SAMPLES) { + trim(now); + if (SAMPLES.isEmpty()) { + return 0D; + } - return (totalNanos / 1_000_000D) / (double) SAMPLES.size(); - } + return (totalNanos / 1_000_000D) / (double) SAMPLES.size(); } + } - public static void clear() { - synchronized (SAMPLES) { - SAMPLES.clear(); - totalNanos = 0L; - } + public static void clear() { + synchronized (SAMPLES) { + SAMPLES.clear(); + totalNanos = 0L; } + } - private static void trim(long now) { - while (!SAMPLES.isEmpty() && (now - SAMPLES.peekFirst().timestampMs) > WINDOW_MS) { - Sample oldest = SAMPLES.removeFirst(); - totalNanos -= oldest.durationNanos; - } - - if (totalNanos < 0L) { - totalNanos = 0L; - } + private static void trim(long now) { + while (!SAMPLES.isEmpty() && (now - SAMPLES.peekFirst().timestampMs) > WINDOW_MS) { + Sample oldest = SAMPLES.removeFirst(); + totalNanos -= oldest.durationNanos; } - private record Sample(long timestampMs, long durationNanos) { + if (totalNanos < 0L) { + totalNanos = 0L; } + } + + private record Sample(long timestampMs, long durationNanos) { + } } diff --git a/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java b/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java index ae8eec0ba..96089c6d5 100644 --- a/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java +++ b/src/main/java/art/arcane/adapt/api/recipe/AdaptRecipe.java @@ -30,312 +30,312 @@ import java.util.List; public interface AdaptRecipe { - static Shapeless.ShapelessBuilder shapeless() { - return Shapeless.builder(); + static Shapeless.ShapelessBuilder shapeless() { + return Shapeless.builder(); + } + + static Shaped.ShapedBuilder shaped() { + return Shaped.builder(); + } + + static Smithing.SmithingBuilder smithing() { + return Smithing.builder(); + } + + static Stonecutter.StonecutterBuilder stonecutter() { + return Stonecutter.builder(); + } + + static Smoker.SmokerBuilder smoker() { + return Smoker.builder(); + } + + static Blast.BlastBuilder blast() { + return Blast.builder(); + } + + static Furnace.FurnaceBuilder furnace() { + return Furnace.builder(); + } + + static Campfire.CampfireBuilder campfire() { + return Campfire.builder(); + } + + ItemStack getResult(); + + String getKey(); + + default NamespacedKey getNSKey() { + return new NamespacedKey(Adapt.instance, getKey()); + } + + void register(); + + boolean is(Recipe recipe); + + void unregister(); + + @Builder + @Data + class Smoker implements AdaptRecipe { + private String key; + private ItemStack result; + private Material ingredient; + private float experience; + private int cookTime; + + @Override + public ItemStack getResult() { + return null; + } + + public void register() { + SmokingRecipe s = new SmokingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Smoker Recipe " + s.getKey()); + } + + @Override + public boolean is(Recipe recipe) { + return recipe instanceof SmokingRecipe s && s.getKey().equals(getNSKey()); + } + + + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Smoker Recipe " + getKey()); + } + } + + @Builder + @Data + class Furnace implements AdaptRecipe { + private String key; + private ItemStack result; + private Material ingredient; + // private float experience = 1; +// private int cookTime = 20; + private float experience; + private int cookTime; + + @Override + public ItemStack getResult() { + return null; + } + + public void register() { + FurnaceRecipe s = new FurnaceRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Furnace Recipe " + s.getKey()); + } + + @Override + public boolean is(Recipe recipe) { + return recipe instanceof FurnaceRecipe s && s.getKey().equals(getNSKey()); + } + + + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Furnace Recipe " + getKey()); + } + } + + @Builder + @Data + class Campfire implements AdaptRecipe { + private String key; + private ItemStack result; + private Material ingredient; + private float experience; + private int cookTime; + + @Override + public ItemStack getResult() { + return null; } - static Shaped.ShapedBuilder shaped() { - return Shaped.builder(); + public void register() { + CampfireRecipe s = new CampfireRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Campfire Recipe " + s.getKey()); } - static Smithing.SmithingBuilder smithing() { - return Smithing.builder(); + @Override + public boolean is(Recipe recipe) { + return recipe instanceof CampfireRecipe s && s.getKey().equals(getNSKey()); } - static Stonecutter.StonecutterBuilder stonecutter() { - return Stonecutter.builder(); + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Campfire Recipe " + getKey()); + } + } + + @Builder + @Data + class Blast implements AdaptRecipe { + private String key; + private ItemStack result; + private Material ingredient; + private float experience; + private int cookTime; + + @Override + public ItemStack getResult() { + return null; } - static Smoker.SmokerBuilder smoker() { - return Smoker.builder(); + public void register() { + BlastingRecipe s = new BlastingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Blast Furnace Recipe " + s.getKey()); } - static Blast.BlastBuilder blast() { - return Blast.builder(); + @Override + public boolean is(Recipe recipe) { + return recipe instanceof BlastingRecipe s && s.getKey().equals(getNSKey()); } - static Furnace.FurnaceBuilder furnace() { - return Furnace.builder(); + + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Blast Furnace Recipe " + getKey()); + } + } + + @Builder + @Data + class Shapeless implements AdaptRecipe { + private String key; + private ItemStack result; + @Singular + private List ingredients; + + @Override + public ItemStack getResult() { + return null; } - static Campfire.CampfireBuilder campfire() { - return Campfire.builder(); + public void register() { + ShapelessRecipe s = new ShapelessRecipe(new NamespacedKey(Adapt.instance, getKey()), result); + ingredients.forEach(s::addIngredient); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Shapeless Crafting Recipe " + s.getKey()); } - ItemStack getResult(); + @Override + public boolean is(Recipe recipe) { + return recipe instanceof ShapelessRecipe s && s.getKey().equals(getNSKey()); + } - String getKey(); - default NamespacedKey getNSKey() { - return new NamespacedKey(Adapt.instance, getKey()); + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Shapeless Crafting Recipe " + getKey()); + } + } + + @Builder + @Data + class Stonecutter implements AdaptRecipe { + private String key; + private ItemStack result; + private Material ingredient; + + @Override + public ItemStack getResult() { + return null; } - void register(); + public void register() { + StonecuttingRecipe s = new StonecuttingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Stone Cutter Recipe " + s.getKey()); + } - boolean is(Recipe recipe); + @Override + public boolean is(Recipe recipe) { + return recipe instanceof StonecuttingRecipe s && s.getKey().equals(getNSKey()); + } - void unregister(); - @Builder - @Data - class Smoker implements AdaptRecipe { - private String key; - private ItemStack result; - private Material ingredient; - private float experience; - private int cookTime; + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Stone Cutter Recipe " + getKey()); + } + } + + @Builder + @Data + class Shaped implements AdaptRecipe { + private String key; + private ItemStack result; + @Singular + private List ingredients; + @Singular + private List shapes; + + @Override + public ItemStack getResult() { + return null; + } - @Override - public ItemStack getResult() { - return null; - } + public void register() { + ShapedRecipe s = new ShapedRecipe(new NamespacedKey(Adapt.instance, getKey()), result); + s.shape(shapes.toArray(new String[0])); + ingredients.forEach(i -> s.setIngredient(i.getCharacter(), i.getChoice())); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Shaped Crafting Recipe " + s.getKey()); + } - public void register() { - SmokingRecipe s = new SmokingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Smoker Recipe " + s.getKey()); - } + @Override + public boolean is(Recipe recipe) { + return recipe instanceof ShapedRecipe s && s.getKey().equals(getNSKey()); + } - @Override - public boolean is(Recipe recipe) { - return recipe instanceof SmokingRecipe s && s.getKey().equals(getNSKey()); - } + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Shaped Crafting Recipe " + getKey()); + } + } + + @Builder + @Data + class Smithing implements AdaptRecipe { + private String key; + private ItemStack result; + private Material base; + private Material addition; + + @Override + public ItemStack getResult() { + return null; + } + public void register() { + SmithingRecipe s = new SmithingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, new RecipeChoice.ExactChoice(new ItemStack(base)), new RecipeChoice.ExactChoice(new ItemStack(addition))); + Bukkit.getServer().addRecipe(s); + Adapt.verbose("Registered Smithing Table Recipe " + s.getKey()); + } - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Smoker Recipe " + getKey()); - } + @Override + public boolean is(Recipe recipe) { + return recipe instanceof SmithingRecipe s && s.getKey().equals(getNSKey()); } - @Builder - @Data - class Furnace implements AdaptRecipe { - private String key; - private ItemStack result; - private Material ingredient; - // private float experience = 1; -// private int cookTime = 20; - private float experience; - private int cookTime; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - FurnaceRecipe s = new FurnaceRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Furnace Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof FurnaceRecipe s && s.getKey().equals(getNSKey()); - } - - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Furnace Recipe " + getKey()); - } - } - - @Builder - @Data - class Campfire implements AdaptRecipe { - private String key; - private ItemStack result; - private Material ingredient; - private float experience; - private int cookTime; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - CampfireRecipe s = new CampfireRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Campfire Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof CampfireRecipe s && s.getKey().equals(getNSKey()); - } - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Campfire Recipe " + getKey()); - } - } - - @Builder - @Data - class Blast implements AdaptRecipe { - private String key; - private ItemStack result; - private Material ingredient; - private float experience; - private int cookTime; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - BlastingRecipe s = new BlastingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient, experience, cookTime); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Blast Furnace Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof BlastingRecipe s && s.getKey().equals(getNSKey()); - } - - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Blast Furnace Recipe " + getKey()); - } - } - - @Builder - @Data - class Shapeless implements AdaptRecipe { - private String key; - private ItemStack result; - @Singular - private List ingredients; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - ShapelessRecipe s = new ShapelessRecipe(new NamespacedKey(Adapt.instance, getKey()), result); - ingredients.forEach(s::addIngredient); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Shapeless Crafting Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof ShapelessRecipe s && s.getKey().equals(getNSKey()); - } - - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Shapeless Crafting Recipe " + getKey()); - } - } - - @Builder - @Data - class Stonecutter implements AdaptRecipe { - private String key; - private ItemStack result; - private Material ingredient; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - StonecuttingRecipe s = new StonecuttingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, ingredient); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Stone Cutter Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof StonecuttingRecipe s && s.getKey().equals(getNSKey()); - } - - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Stone Cutter Recipe " + getKey()); - } - } - - @Builder - @Data - class Shaped implements AdaptRecipe { - private String key; - private ItemStack result; - @Singular - private List ingredients; - @Singular - private List shapes; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - ShapedRecipe s = new ShapedRecipe(new NamespacedKey(Adapt.instance, getKey()), result); - s.shape(shapes.toArray(new String[0])); - ingredients.forEach(i -> s.setIngredient(i.getCharacter(), i.getChoice())); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Shaped Crafting Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof ShapedRecipe s && s.getKey().equals(getNSKey()); - } - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Shaped Crafting Recipe " + getKey()); - } - } - - @Builder - @Data - class Smithing implements AdaptRecipe { - private String key; - private ItemStack result; - private Material base; - private Material addition; - - @Override - public ItemStack getResult() { - return null; - } - - public void register() { - SmithingRecipe s = new SmithingRecipe(new NamespacedKey(Adapt.instance, getKey()), result, new RecipeChoice.ExactChoice(new ItemStack(base)), new RecipeChoice.ExactChoice(new ItemStack(addition))); - Bukkit.getServer().addRecipe(s); - Adapt.verbose("Registered Smithing Table Recipe " + s.getKey()); - } - - @Override - public boolean is(Recipe recipe) { - return recipe instanceof SmithingRecipe s && s.getKey().equals(getNSKey()); - } - - @Override - public void unregister() { - Bukkit.getServer().removeRecipe(getNSKey()); - Adapt.verbose("Unregistered Smithing Table Recipe " + getKey()); - } + @Override + public void unregister() { + Bukkit.getServer().removeRecipe(getNSKey()); + Adapt.verbose("Unregistered Smithing Table Recipe " + getKey()); } + } } diff --git a/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java b/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java index 4d3b14d73..d79584918 100644 --- a/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java +++ b/src/main/java/art/arcane/adapt/api/recipe/MaterialChar.java @@ -28,21 +28,21 @@ @AllArgsConstructor @Data public class MaterialChar { - private char character; - private RecipeChoice choice; + private char character; + private RecipeChoice choice; - public MaterialChar(char character, Tag tag) { - this.character = character; - this.choice = new RecipeChoice.MaterialChoice(tag); - } + public MaterialChar(char character, Tag tag) { + this.character = character; + this.choice = new RecipeChoice.MaterialChoice(tag); + } - public MaterialChar(char character, Material... material) { - this.character = character; - this.choice = new RecipeChoice.MaterialChoice(material); - } + public MaterialChar(char character, Material... material) { + this.character = character; + this.choice = new RecipeChoice.MaterialChoice(material); + } - public MaterialChar(char character, ItemStack... itemStack) { - this.character = character; - this.choice = new RecipeChoice.ExactChoice(itemStack); - } + public MaterialChar(char character, ItemStack... itemStack) { + this.character = character; + this.choice = new RecipeChoice.ExactChoice(itemStack); + } } diff --git a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java index 06d232786..7db146943 100644 --- a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java +++ b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java @@ -25,47 +25,47 @@ import org.bukkit.entity.Player; public final class AdaptationGate { - private AdaptationGate() { - } - - public static boolean shouldSkipPlayer(Player player, Skill skill, boolean hasAdaptPlayer) { - if (player == null || skill == null) { - return true; - } + private AdaptationGate() { + } - if (!player.getClass().getSimpleName().equals("CraftPlayer")) { - return true; - } + public static boolean shouldSkipPlayer(Player player, Skill skill, boolean hasAdaptPlayer) { + if (player == null || skill == null) { + return true; + } - return !skill.isEnabled() - || skill.hasBlacklistPermission(player, skill) - || isWorldBlacklisted(player) - || isInCreativeOrSpectator(player) - || !hasAdaptPlayer; + if (!player.getClass().getSimpleName().equals("CraftPlayer")) { + return true; } - public static boolean shouldSkipWorld(World world, Skill skill) { - if (world == null || skill == null) { - return true; - } + return !skill.isEnabled() + || skill.hasBlacklistPermission(player, skill) + || isWorldBlacklisted(player) + || isInCreativeOrSpectator(player) + || !hasAdaptPlayer; + } - return !skill.isEnabled() || AdaptConfig.get().blacklistedWorlds.contains(world.getName()); + public static boolean shouldSkipWorld(World world, Skill skill) { + if (world == null || skill == null) { + return true; } - public static boolean isWorldBlacklisted(Player player) { - if (player == null) { - return true; - } + return !skill.isEnabled() || AdaptConfig.get().blacklistedWorlds.contains(world.getName()); + } - return AdaptConfig.get().blacklistedWorlds.contains(player.getWorld().getName()); + public static boolean isWorldBlacklisted(Player player) { + if (player == null) { + return true; } - public static boolean isInCreativeOrSpectator(Player player) { - if (player == null) { - return true; - } + return AdaptConfig.get().blacklistedWorlds.contains(player.getWorld().getName()); + } - return !AdaptConfig.get().isXpInCreative() - && (player.getGameMode().equals(GameMode.CREATIVE) || player.getGameMode().equals(GameMode.SPECTATOR)); + public static boolean isInCreativeOrSpectator(Player player) { + if (player == null) { + return true; } + + return !AdaptConfig.get().isXpInCreative() + && (player.getGameMode().equals(GameMode.CREATIVE) || player.getGameMode().equals(GameMode.SPECTATOR)); + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index 899960c0e..9ed00cbad 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -50,544 +50,544 @@ @EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleSkill extends TickedObject implements Skill { - private final String name; - private final String emojiName; - private C color; - private String colorPrefix; - private double minXp; - private String description; - private String displayName; - private Material icon; - @EqualsAndHashCode.Exclude - private KList> adaptations; - private KList statTrackers; - private KList cachedAdvancements; - private String advancementBackground; - private KList recipes; - private Class configType; - private volatile T config; - - public SimpleSkill(String name, String emojiName) { - super("skill", UUID.randomUUID() + "-skill-" + name, 50); - statTrackers = new KList<>(); - recipes = new KList<>(); - cachedAdvancements = new KList<>(); - this.emojiName = emojiName; - adaptations = new KList<>(); - setColor(C.WHITE); - this.name = name; - setIcon(Material.BOOK); - setDescription("No Description Provided"); - setMinXp(100); - setAdvancementBackground("minecraft:textures/block/deepslate_tiles.png"); - - J.a(() -> { - J.attempt(this::getConfig); - getAdaptations().forEach(i -> J.attempt(i::getConfig)); - }, 1); - } - - @Override - public Class getConfigurationClass() { - return configType; - } - - @Override - public void registerConfiguration(Class type) { - this.configType = type; - } - - protected File getConfigFile() { - return Adapt.instance.getDataFile("adapt", "skills", getName() + ".toml"); - } - - protected File getLegacyConfigFile() { - return Adapt.instance.getDataFile("adapt", "skills", getName() + ".json"); - } - - protected T createDefaultConfig() { - try { - return getConfigurationClass().getConstructor().newInstance(); - } catch (Throwable e) { - throw new IllegalStateException("Failed to create default config for skill " + getName(), e); - } + private final String name; + private final String emojiName; + private C color; + private String colorPrefix; + private double minXp; + private String description; + private String displayName; + private Material icon; + @EqualsAndHashCode.Exclude + private KList> adaptations; + private KList statTrackers; + private KList cachedAdvancements; + private String advancementBackground; + private KList recipes; + private Class configType; + private volatile T config; + + public SimpleSkill(String name, String emojiName) { + super("skill", UUID.randomUUID() + "-skill-" + name, 50); + statTrackers = new KList<>(); + recipes = new KList<>(); + cachedAdvancements = new KList<>(); + this.emojiName = emojiName; + adaptations = new KList<>(); + setColor(C.WHITE); + this.name = name; + setIcon(Material.BOOK); + setDescription("No Description Provided"); + setMinXp(100); + setAdvancementBackground("minecraft:textures/block/deepslate_tiles.png"); + + J.a(() -> { + J.attempt(this::getConfig); + getAdaptations().forEach(i -> J.attempt(i::getConfig)); + }, 1); + } + + @Override + public Class getConfigurationClass() { + return configType; + } + + @Override + public void registerConfiguration(Class type) { + this.configType = type; + } + + protected File getConfigFile() { + return Adapt.instance.getDataFile("adapt", "skills", getName() + ".toml"); + } + + protected File getLegacyConfigFile() { + return Adapt.instance.getDataFile("adapt", "skills", getName() + ".json"); + } + + protected T createDefaultConfig() { + try { + return getConfigurationClass().getConstructor().newInstance(); + } catch (Throwable e) { + throw new IllegalStateException("Failed to create default config for skill " + getName(), e); + } + } + + public synchronized boolean reloadConfigFromDisk(boolean announce) { + if (getConfigurationClass() == null) { + return false; + } + + T previous = config; + File file = getConfigFile(); + try { + T loaded = loadConfig(file, previous == null ? createDefaultConfig() : previous, previous == null); + config = loaded; + onConfigReload(previous, loaded); + if (announce) { + Adapt.info("Hotloaded " + file.getPath()); + } + return true; + } catch (Throwable e) { + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); + return false; + } + } + + private T loadConfig(File file, T fallback, boolean overwriteOnReadFailure) throws IOException { + return ConfigFileSupport.load( + file, + getLegacyConfigFile(), + getConfigurationClass(), + fallback, + overwriteOnReadFailure, + "skill:" + getName(), + "Created missing skill config [adapt/skills/" + getName() + ".toml] from defaults." + ); + } + + protected void onConfigReload(T previousConfig, T newConfig) { + applyColorField(newConfig); + applyDoubleField(newConfig, "minXp", this::setMinXp); + Number interval = getNumericField(newConfig, "setInterval"); + if (interval != null) { + setInterval(interval.longValue()); + } + } + + private void applyDoubleField(T source, String fieldName, java.util.function.DoubleConsumer consumer) { + Number number = getNumericField(source, fieldName); + if (number != null) { + consumer.accept(number.doubleValue()); + } + } + + private void applyColorField(T source) { + String configuredColor = getStringField(source, "skillColor"); + if (configuredColor == null || configuredColor.isBlank()) { + return; + } + + String prefix = resolveColorPrefix(configuredColor); + if (prefix == null || prefix.isBlank()) { + return; + } + + colorPrefix = prefix; + C resolvedColor = resolveLegacyColor(prefix); + if (resolvedColor != null) { + color = resolvedColor; + } + } + + private Number getNumericField(T source, String fieldName) { + Field f = getField(source.getClass(), fieldName); + if (f == null) { + return null; + } + + try { + f.setAccessible(true); + Object value = f.get(source); + if (value instanceof Number number) { + return number; + } + } catch (Throwable ex) { + Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + + return null; + } + + private String getStringField(T source, String fieldName) { + Field f = getField(source.getClass(), fieldName); + if (f == null) { + return null; + } + + try { + f.setAccessible(true); + Object value = f.get(source); + if (value instanceof String stringValue) { + return stringValue; + } + } catch (Throwable ex) { + Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + + return null; + } + + private Field getField(Class type, String name) { + Class current = type; + while (current != null) { + try { + return current.getDeclaredField(name); + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } } - public synchronized boolean reloadConfigFromDisk(boolean announce) { - if (getConfigurationClass() == null) { - return false; - } - - T previous = config; - File file = getConfigFile(); - try { - T loaded = loadConfig(file, previous == null ? createDefaultConfig() : previous, previous == null); - config = loaded; - onConfigReload(previous, loaded); - if (announce) { - Adapt.info("Hotloaded " + file.getPath()); - } - return true; - } catch (Throwable e) { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); - return false; - } + return null; + } + + @Override + public T getConfig() { + T local = config; + if (local != null) { + return local; } - private T loadConfig(File file, T fallback, boolean overwriteOnReadFailure) throws IOException { - return ConfigFileSupport.load( - file, - getLegacyConfigFile(), - getConfigurationClass(), - fallback, - overwriteOnReadFailure, - "skill:" + getName(), - "Created missing skill config [adapt/skills/" + getName() + ".toml] from defaults." - ); - } - - protected void onConfigReload(T previousConfig, T newConfig) { - applyColorField(newConfig); - applyDoubleField(newConfig, "minXp", this::setMinXp); - Number interval = getNumericField(newConfig, "setInterval"); - if (interval != null) { - setInterval(interval.longValue()); - } - } + synchronized (this) { + local = config; + if (local != null) { + return local; + } - private void applyDoubleField(T source, String fieldName, java.util.function.DoubleConsumer consumer) { - Number number = getNumericField(source, fieldName); - if (number != null) { - consumer.accept(number.doubleValue()); - } + boolean loaded = reloadConfigFromDisk(false); + local = config; + if (!loaded || local == null) { + local = createDefaultConfig(); + onConfigReload(null, local); + config = local; + Adapt.warn("Falling back to in-memory defaults for skill config " + getName() + "."); + } } - private void applyColorField(T source) { - String configuredColor = getStringField(source, "skillColor"); - if (configuredColor == null || configuredColor.isBlank()) { - return; - } + return local; + } - String prefix = resolveColorPrefix(configuredColor); - if (prefix == null || prefix.isBlank()) { - return; - } + public void registerRecipe(AdaptRecipe r) { + recipes.add(r); + } - colorPrefix = prefix; - C resolvedColor = resolveLegacyColor(prefix); - if (resolvedColor != null) { - color = resolvedColor; - } - } + public void registerAdvancement(AdaptAdvancement a) { + cachedAdvancements.add(a); + } - private Number getNumericField(T source, String fieldName) { - Field f = getField(source.getClass(), fieldName); - if (f == null) { - return null; - } - - try { - f.setAccessible(true); - Object value = f.get(source); - if (value instanceof Number number) { - return number; - } - } catch (Throwable ex) { - Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - - return null; + public boolean checkValidEntity(EntityType e) { + if (!e.isAlive() || e.equals(EntityType.PARROT)) { + return false; } + return !ItemListings.getInvalidDamageableEntities().contains(e); + } - private String getStringField(T source, String fieldName) { - Field f = getField(source.getClass(), fieldName); - if (f == null) { - return null; - } - - try { - f.setAccessible(true); - Object value = f.get(source); - if (value instanceof String stringValue) { - return stringValue; - } - } catch (Throwable ex) { - Adapt.verbose("Failed reading config field '" + fieldName + "' for skill " + getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + protected boolean shouldReturnForPlayer(Player p) { + return SkillRuntimeGuards.shouldSkipPlayer(this, p); + } - return null; - } + protected void shouldReturnForPlayer(Player p, Runnable r) { + SkillRuntimeGuards.withPlayer(this, p, r); + } - private Field getField(Class type, String name) { - Class current = type; - while (current != null) { - try { - return current.getDeclaredField(name); - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } - } + protected void shouldReturnForPlayer(Player p, Cancellable c, Runnable r) { + SkillRuntimeGuards.withPlayer(this, p, c, r); + } - return null; - } + protected boolean shouldReturnForWorld(World world, Skill skill) { + return SkillRuntimeGuards.shouldSkipWorld(skill, world); + } - @Override - public T getConfig() { - T local = config; - if (local != null) { - return local; - } + protected boolean isWorldBlacklisted(Player p) { + return SkillRuntimeGuards.isWorldBlacklisted(p); + } - synchronized (this) { - local = config; - if (local != null) { - return local; - } - - boolean loaded = reloadConfigFromDisk(false); - local = config; - if (!loaded || local == null) { - local = createDefaultConfig(); - onConfigReload(null, local); - config = local; - Adapt.warn("Falling back to in-memory defaults for skill config " + getName() + "."); - } - } + protected boolean isInCreativeOrSpectator(Player p) { + return SkillRuntimeGuards.isInCreativeOrSpectator(p); + } + + public void setColor(C color) { + C resolved = color == null ? C.WHITE : color; + this.color = resolved; + this.colorPrefix = resolved.toString(); + } + + @Override + public String getDisplayName() { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); + } + + String shownName = displayName == null ? Form.capitalize(getName()) : displayName; + return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName() + " " + shownName; + } - return local; + @Override + public String getShortName() { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); } - public void registerRecipe(AdaptRecipe r) { - recipes.add(r); - } + return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName(); + } - public void registerAdvancement(AdaptAdvancement a) { - cachedAdvancements.add(a); + @Override + public String getDisplayName(int level) { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); } - public boolean checkValidEntity(EntityType e) { - if (!e.isAlive() || e.equals(EntityType.PARROT)) { - return false; - } - return !ItemListings.getInvalidDamageableEntities().contains(e); + if (level > 0) { + return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; } - protected boolean shouldReturnForPlayer(Player p) { - return SkillRuntimeGuards.shouldSkipPlayer(this, p); - } + return getDisplayName(); + } - protected void shouldReturnForPlayer(Player p, Runnable r) { - SkillRuntimeGuards.withPlayer(this, p, r); - } + @Override + public void onRegisterAdvancements(KList advancements) { + advancements.addAll(cachedAdvancements); + } - protected void shouldReturnForPlayer(Player p, Cancellable c, Runnable r) { - SkillRuntimeGuards.withPlayer(this, p, c, r); - } + public AdaptAdvancement buildAdvancements() { + KList a = new KList<>(); + onRegisterAdvancements(a); - protected boolean shouldReturnForWorld(World world, Skill skill) { - return SkillRuntimeGuards.shouldSkipWorld(skill, world); + for (Adaptation i : getAdaptations()) { + a.add(i.buildAdvancements()); } - protected boolean isWorldBlacklisted(Player p) { - return SkillRuntimeGuards.isWorldBlacklisted(p); - } + return AdaptAdvancement.builder() + .background(getAdvancementBackground()) + .key("skill_" + getName()) + .title(displayName) + .description(getDescription()) + .icon(getIcon()) + .model(getModel()) + .children(a) + .visibility(AdvancementVisibility.HIDDEN) + .build(); + } - protected boolean isInCreativeOrSpectator(Player p) { - return SkillRuntimeGuards.isInCreativeOrSpectator(p); - } - - public void setColor(C color) { - C resolved = color == null ? C.WHITE : color; - this.color = resolved; - this.colorPrefix = resolved.toString(); - } + @Override + public void registerStatTracker(AdaptStatTracker tracker) { + getStatTrackers().add(tracker); + } - @Override - public String getDisplayName() { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } + protected void registerMilestone(String advancementKey, String stat, double goal, double reward) { + registerStatTracker(AdaptStatTracker.builder() + .advancement(advancementKey) + .stat(stat) + .goal(goal) + .reward(reward) + .build()); + } - String shownName = displayName == null ? Form.capitalize(getName()) : displayName; - return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName() + " " + shownName; + protected void checkStatTrackersForOnlinePlayers() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player player = adaptPlayer.getPlayer(); + shouldReturnForPlayer(player, () -> checkStatTrackers(adaptPlayer)); } + } - @Override - public String getShortName() { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } + @Override + public KList getStatTrackers() { + return statTrackers; + } - return C.RESET + "" + C.BOLD + colorPrefixValue() + getEmojiName(); - } + @Override + public void registerAdaptation(Adaptation a) { + a.setSkill(this); + adaptations.add(a); + } - @Override - public String getDisplayName(int level) { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } - - if (level > 0) { - return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; - } + @Override + public void unregister() { + super.unregister(); + adaptations.forEach(Adaptation::unregister); + } - return getDisplayName(); + private String colorPrefixValue() { + if (colorPrefix == null || colorPrefix.isBlank()) { + return color == null ? C.WHITE.toString() : color.toString(); } + return colorPrefix; + } - @Override - public void onRegisterAdvancements(KList advancements) { - advancements.addAll(cachedAdvancements); + private String resolveColorPrefix(String rawInput) { + String raw = rawInput == null ? "" : rawInput.trim(); + if (raw.isBlank()) { + return null; } - public AdaptAdvancement buildAdvancements() { - KList a = new KList<>(); - onRegisterAdvancements(a); - - for (Adaptation i : getAdaptations()) { - a.add(i.buildAdvancements()); - } - - return AdaptAdvancement.builder() - .background(getAdvancementBackground()) - .key("skill_" + getName()) - .title(displayName) - .description(getDescription()) - .icon(getIcon()) - .model(getModel()) - .children(a) - .visibility(AdvancementVisibility.HIDDEN) - .build(); + C named = parseNamedColor(raw); + if (named != null) { + return named.toString(); } - @Override - public void registerStatTracker(AdaptStatTracker tracker) { - getStatTrackers().add(tracker); + String normalizedHex = normalizeHex(raw); + if (normalizedHex != null) { + return C.translateAlternateColorCodes('&', "&#" + normalizedHex); } - protected void registerMilestone(String advancementKey, String stat, double goal, double reward) { - registerStatTracker(AdaptStatTracker.builder() - .advancement(advancementKey) - .stat(stat) - .goal(goal) - .reward(reward) - .build()); + if (raw.indexOf(C.COLOR_CHAR) >= 0) { + return raw; } - protected void checkStatTrackersForOnlinePlayers() { - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player player = adaptPlayer.getPlayer(); - shouldReturnForPlayer(player, () -> checkStatTrackers(adaptPlayer)); - } + String legacy = C.translateAlternateColorCodes('&', raw); + if (legacy != null && legacy.indexOf(C.COLOR_CHAR) >= 0) { + return legacy; } - @Override - public KList getStatTrackers() { - return statTrackers; + String mini = AdventureCompat.toLegacySection(raw); + if (mini != null && mini.indexOf(C.COLOR_CHAR) >= 0) { + return mini; } - @Override - public void registerAdaptation(Adaptation a) { - a.setSkill(this); - adaptations.add(a); - } + return null; + } - @Override - public void unregister() { - super.unregister(); - adaptations.forEach(Adaptation::unregister); + private C parseNamedColor(String raw) { + String normalized = raw.trim().toUpperCase(Locale.ROOT) + .replace('-', '_') + .replace(' ', '_'); + try { + C candidate = C.valueOf(normalized); + if (candidate.isColor()) { + return candidate; + } + } catch (IllegalArgumentException ignored) { } - private String colorPrefixValue() { - if (colorPrefix == null || colorPrefix.isBlank()) { - return color == null ? C.WHITE.toString() : color.toString(); - } - return colorPrefix; + if (raw.length() == 1) { + char code = Character.toLowerCase(raw.charAt(0)); + if (isLegacyColorChar(code)) { + return C.getByChar(code); + } } - private String resolveColorPrefix(String rawInput) { - String raw = rawInput == null ? "" : rawInput.trim(); - if (raw.isBlank()) { - return null; - } + return null; + } - C named = parseNamedColor(raw); - if (named != null) { - return named.toString(); - } - - String normalizedHex = normalizeHex(raw); - if (normalizedHex != null) { - return C.translateAlternateColorCodes('&', "&#" + normalizedHex); - } - - if (raw.indexOf(C.COLOR_CHAR) >= 0) { - return raw; - } - - String legacy = C.translateAlternateColorCodes('&', raw); - if (legacy != null && legacy.indexOf(C.COLOR_CHAR) >= 0) { - return legacy; - } + private String normalizeHex(String raw) { + String value = raw; + if (value.startsWith("#")) { + value = value.substring(1); + } else if (value.startsWith("0x") || value.startsWith("0X")) { + value = value.substring(2); + } - String mini = AdventureCompat.toLegacySection(raw); - if (mini != null && mini.indexOf(C.COLOR_CHAR) >= 0) { - return mini; - } + if (value.length() != 6) { + return null; + } + for (int i = 0; i < value.length(); i++) { + if (!isHexChar(value.charAt(i))) { return null; + } } - private C parseNamedColor(String raw) { - String normalized = raw.trim().toUpperCase(Locale.ROOT) - .replace('-', '_') - .replace(' ', '_'); - try { - C candidate = C.valueOf(normalized); - if (candidate.isColor()) { - return candidate; - } - } catch (IllegalArgumentException ignored) { - } + return value; + } - if (raw.length() == 1) { - char code = Character.toLowerCase(raw.charAt(0)); - if (isLegacyColorChar(code)) { - return C.getByChar(code); - } - } + private boolean isHexChar(char c) { + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); + } - return null; + private boolean isLegacyColorChar(char code) { + return (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f'); + } + + private C resolveLegacyColor(String prefix) { + if (prefix == null || prefix.isBlank()) { + return color; } - private String normalizeHex(String raw) { - String value = raw; - if (value.startsWith("#")) { - value = value.substring(1); - } else if (value.startsWith("0x") || value.startsWith("0X")) { - value = value.substring(2); - } + for (int i = 0; i < prefix.length() - 1; i++) { + if (prefix.charAt(i) != C.COLOR_CHAR) { + continue; + } - if (value.length() != 6) { - return null; - } + char code = Character.toLowerCase(prefix.charAt(i + 1)); + if (isLegacyColorChar(code)) { + return C.getByChar(code); + } - for (int i = 0; i < value.length(); i++) { - if (!isHexChar(value.charAt(i))) { - return null; - } + if (code == 'x') { + String hex = parseSectionHex(prefix, i); + if (hex == null) { + continue; } - return value; + C nearest = nearestLegacyColor(hex); + if (nearest != null) { + return nearest; + } + } } - private boolean isHexChar(char c) { - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F'); + return color; + } + + private String parseSectionHex(String prefix, int start) { + if (start + 13 >= prefix.length()) { + return null; } - private boolean isLegacyColorChar(char code) { - return (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f'); + StringBuilder hex = new StringBuilder(6); + for (int i = 0; i < 6; i++) { + int colorTokenIndex = start + 2 + (i * 2); + int hexIndex = colorTokenIndex + 1; + if (colorTokenIndex >= prefix.length() || hexIndex >= prefix.length()) { + return null; + } + if (prefix.charAt(colorTokenIndex) != C.COLOR_CHAR) { + return null; + } + char hexChar = prefix.charAt(hexIndex); + if (!isHexChar(hexChar)) { + return null; + } + hex.append(hexChar); } + return hex.toString(); + } - private C resolveLegacyColor(String prefix) { - if (prefix == null || prefix.isBlank()) { - return color; + private C nearestLegacyColor(String hex) { + try { + int rgb = Integer.parseInt(hex, 16); + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + C best = null; + long bestDistance = Long.MAX_VALUE; + for (C candidate : C.values()) { + if (!candidate.isColor()) { + continue; } - for (int i = 0; i < prefix.length() - 1; i++) { - if (prefix.charAt(i) != C.COLOR_CHAR) { - continue; - } - - char code = Character.toLowerCase(prefix.charAt(i + 1)); - if (isLegacyColorChar(code)) { - return C.getByChar(code); - } - - if (code == 'x') { - String hex = parseSectionHex(prefix, i); - if (hex == null) { - continue; - } - - C nearest = nearestLegacyColor(hex); - if (nearest != null) { - return nearest; - } - } + String candidateHex = candidate.hex(); + if (candidateHex == null || candidateHex.length() != 7) { + continue; } - return color; - } + int candidateRgb = Integer.parseInt(candidateHex.substring(1), 16); + int cr = (candidateRgb >> 16) & 0xFF; + int cg = (candidateRgb >> 8) & 0xFF; + int cb = candidateRgb & 0xFF; - private String parseSectionHex(String prefix, int start) { - if (start + 13 >= prefix.length()) { - return null; + long distance = ((long) r - cr) * ((long) r - cr) + + ((long) g - cg) * ((long) g - cg) + + ((long) b - cb) * ((long) b - cb); + if (distance < bestDistance) { + bestDistance = distance; + best = candidate; } + } - StringBuilder hex = new StringBuilder(6); - for (int i = 0; i < 6; i++) { - int colorTokenIndex = start + 2 + (i * 2); - int hexIndex = colorTokenIndex + 1; - if (colorTokenIndex >= prefix.length() || hexIndex >= prefix.length()) { - return null; - } - if (prefix.charAt(colorTokenIndex) != C.COLOR_CHAR) { - return null; - } - char hexChar = prefix.charAt(hexIndex); - if (!isHexChar(hexChar)) { - return null; - } - hex.append(hexChar); - } - return hex.toString(); - } - - private C nearestLegacyColor(String hex) { - try { - int rgb = Integer.parseInt(hex, 16); - int r = (rgb >> 16) & 0xFF; - int g = (rgb >> 8) & 0xFF; - int b = rgb & 0xFF; - - C best = null; - long bestDistance = Long.MAX_VALUE; - for (C candidate : C.values()) { - if (!candidate.isColor()) { - continue; - } - - String candidateHex = candidate.hex(); - if (candidateHex == null || candidateHex.length() != 7) { - continue; - } - - int candidateRgb = Integer.parseInt(candidateHex.substring(1), 16); - int cr = (candidateRgb >> 16) & 0xFF; - int cg = (candidateRgb >> 8) & 0xFF; - int cb = candidateRgb & 0xFF; - - long distance = ((long) r - cr) * ((long) r - cr) - + ((long) g - cg) * ((long) g - cg) - + ((long) b - cb) * ((long) b - cb); - if (distance < bestDistance) { - bestDistance = distance; - best = candidate; - } - } - - return best == null ? color : best; - } catch (Throwable ignored) { - return color; - } + return best == null ? color : best; + } catch (Throwable ignored) { + return color; } + } - @Override - public abstract void onTick(); + @Override + public abstract void onTick(); } diff --git a/src/main/java/art/arcane/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java index f78f23c7f..5f63910f8 100644 --- a/src/main/java/art/arcane/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -38,256 +38,258 @@ * Public API for a skill line and its shared behavior. */ public interface Skill extends Ticked, Component { - /** - * Builds the root advancement tree for this skill (including child adaptations). - */ - AdaptAdvancement buildAdvancements(); - - /** - * Returns the concrete config class used by this skill. - */ - Class getConfigurationClass(); - - /** - * Registers the config class used for load/reload of this skill. - */ - void registerConfiguration(Class type); - - /** - * @return true when this skill is runtime-enabled. - */ - boolean isEnabled(); - - /** - * Returns the live config instance for this skill. - */ - T getConfig(); - - /** - * @return internal skill key (e.g. "swords"). - */ - String getName(); - - /** - * @return icon glyph/emoji prefix used in UI. - */ - String getEmojiName(); - - /** - * @return base icon for this skill. - */ - Material getIcon(); - - /** - * @return localized skill description. - */ - String getDescription(); - - /** - * @return recipes registered by this skill. - */ - KList getRecipes(); - - /** - * Registers an adaptation under this skill. - */ - void registerAdaptation(Adaptation a); - - /** - * Registers a stat tracker/milestone for this skill. - */ - void registerStatTracker(AdaptStatTracker tracker); - - /** - * @return all stat trackers for this skill. - */ - KList getStatTrackers(); - - /** - * Skill-level particle toggle check (global + per-skill config aware). - */ - @Override - default boolean areParticlesEnabled() { - return SkillGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); + /** + * Builds the root advancement tree for this skill (including child + * adaptations). + */ + AdaptAdvancement buildAdvancements(); + + /** + * Returns the concrete config class used by this skill. + */ + Class getConfigurationClass(); + + /** + * Registers the config class used for load/reload of this skill. + */ + void registerConfiguration(Class type); + + /** + * @return true when this skill is runtime-enabled. + */ + boolean isEnabled(); + + /** + * Returns the live config instance for this skill. + */ + T getConfig(); + + /** + * @return internal skill key (e.g. "swords"). + */ + String getName(); + + /** + * @return icon glyph/emoji prefix used in UI. + */ + String getEmojiName(); + + /** + * @return base icon for this skill. + */ + Material getIcon(); + + /** + * @return localized skill description. + */ + String getDescription(); + + /** + * @return recipes registered by this skill. + */ + KList getRecipes(); + + /** + * Registers an adaptation under this skill. + */ + void registerAdaptation(Adaptation a); + + /** + * Registers a stat tracker/milestone for this skill. + */ + void registerStatTracker(AdaptStatTracker tracker); + + /** + * @return all stat trackers for this skill. + */ + KList getStatTrackers(); + + /** + * Skill-level particle toggle check (global + per-skill config aware). + */ + @Override + default boolean areParticlesEnabled() { + return SkillGuiSupport.areParticlesEnabled(this, Component.super.areParticlesEnabled()); + } + + /** + * Skill-level sound toggle check (global + per-skill config aware). + */ + @Override + default boolean areSoundsEnabled() { + return SkillGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); + } + + /** + * Evaluates and grants eligible stat-tracker advancements for one player. + */ + default void checkStatTrackers(AdaptPlayer player) { + SkillRuntimeGuards.checkStatTrackers(this, player); + } + + /** + * @return all adaptations currently attached to this skill. + */ + KList> getAdaptations(); + + /** + * @return primary chat/UI color for this skill. + */ + C getColor(); + + /** + * @return minimum xp tuning value used by this skill. + */ + double getMinXp(); + + /** + * Called while building advancement trees to append custom nodes. + */ + void onRegisterAdvancements(KList advancements); + + /** + * Returns true when this player is blacklisted from this skill via + * permission. + */ + default boolean hasBlacklistPermission(Player p, Skill s) { + return SkillRuntimeGuards.hasBlacklistPermission(p, s); + } + + /** + * Formatted display name with color + emoji. + */ + default String getDisplayName() { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); } - - /** - * Skill-level sound toggle check (global + per-skill config aware). - */ - @Override - default boolean areSoundsEnabled() { - return SkillGuiSupport.areSoundsEnabled(this, Component.super.areSoundsEnabled()); - } - - /** - * Evaluates and grants eligible stat-tracker advancements for one player. - */ - default void checkStatTrackers(AdaptPlayer player) { - SkillRuntimeGuards.checkStatTrackers(this, player); - } - - /** - * @return all adaptations currently attached to this skill. - */ - KList> getAdaptations(); - - /** - * @return primary chat/UI color for this skill. - */ - C getColor(); - - /** - * @return minimum xp tuning value used by this skill. - */ - double getMinXp(); - - /** - * Called while building advancement trees to append custom nodes. - */ - void onRegisterAdvancements(KList advancements); - - /** - * Returns true when this player is blacklisted from this skill via permission. - */ - default boolean hasBlacklistPermission(Player p, Skill s) { - return SkillRuntimeGuards.hasBlacklistPermission(p, s); + return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName() + " " + Form.capitalize(getName()); + } + + /** + * Compact formatted display name with color + emoji. + */ + default String getShortName() { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); } - - /** - * Formatted display name with color + emoji. - */ - default String getDisplayName() { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } - return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName() + " " + Form.capitalize(getName()); + return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName(); + } + + /** + * Display name with optional level suffix. + */ + default String getDisplayName(int level) { + if (!this.isEnabled()) { + return C.DARK_GRAY + Form.capitalize(getName()); } - - /** - * Compact formatted display name with color + emoji. - */ - default String getShortName() { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } - return C.RESET + "" + C.BOLD + getColor().toString() + getEmojiName(); + if (level > 0) { + return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; } - - /** - * Display name with optional level suffix. - */ - default String getDisplayName(int level) { - if (!this.isEnabled()) { - return C.DARK_GRAY + Form.capitalize(getName()); - } - if (level > 0) { - return getDisplayName() + C.RESET + " " + C.UNDERLINE + C.WHITE + level + C.RESET; - } - return getDisplayName(); - } - - /** - * Returns the generated custom model binding for this skill icon. - */ - default CustomModel getModel() { - return CustomModel.get(getIcon(), "skill", getName()); - } - - /** - * Grants visible xp at the player's location. - */ - default void xp(Player p, double xp) { - xp(p, xp, null); - } - - /** - * Grants visible xp at the player's location using an optional reward key. - */ - default void xp(Player p, double xp, String rewardKey) { - xp(p, p == null ? null : p.getLocation(), xp, rewardKey); - - } - - /** - * Grants visible xp at a specific location. - */ - default void xp(Player p, Location at, double xp) { - xp(p, at, xp, null); - } - - /** - * Grants visible xp at a specific location using an optional reward key. - */ - default void xp(Player p, Location at, double xp, String rewardKey) { - SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, false, true); - } - - /** - * Grants silent xp at a specific location (with optional burst visuals). - */ - default void xpS(Player p, Location at, double xp) { - xpS(p, at, xp, null); - } - - /** - * Grants silent xp at a specific location using an optional reward key. - */ - default void xpS(Player p, Location at, double xp, String rewardKey) { - SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, true, true); - } - - /** - * Grants silent xp without visuals. - */ - default void xpSilent(Player p, double xp) { - xpSilent(p, xp, null); - } - - /** - * Grants silent xp without visuals using an optional reward key. - */ - default void xpSilent(Player p, double xp, String rewardKey) { - SkillRuntimeGuards.grantXpSilent(this, p, xp, rewardKey); - } - - /** - * Emits spatial xp pulses around a world location. - */ - default void xp(Location at, double xp, int rad, long duration) { - XP.spatialXP(at, this, xp, rad, duration); - vfxXP(at); - } - - /** - * Grants knowledge points for this skill. - */ - default void knowledge(Player p, long k) { - SkillRuntimeGuards.grantKnowledge(this, p, k); - } - - /** - * Opens the skill GUI and optionally enforces blacklist permission checks. - */ - default boolean openGui(Player player, boolean checkPermissions) { - if (checkPermissions && hasBlacklistPermission(player, this)) { - return false; - } else { - openGui(player); - return true; - } - } - - /** - * Opens page 0 of this skill GUI. - */ - default void openGui(Player player) { - openGui(player, 0); - } - - /** - * Opens a specific page of this skill GUI. - */ - default void openGui(Player player, int page) { - SkillGuiSupport.openGui(this, player, page); + return getDisplayName(); + } + + /** + * Returns the generated custom model binding for this skill icon. + */ + default CustomModel getModel() { + return CustomModel.get(getIcon(), "skill", getName()); + } + + /** + * Grants visible xp at the player's location. + */ + default void xp(Player p, double xp) { + xp(p, xp, null); + } + + /** + * Grants visible xp at the player's location using an optional reward key. + */ + default void xp(Player p, double xp, String rewardKey) { + xp(p, p == null ? null : p.getLocation(), xp, rewardKey); + + } + + /** + * Grants visible xp at a specific location. + */ + default void xp(Player p, Location at, double xp) { + xp(p, at, xp, null); + } + + /** + * Grants visible xp at a specific location using an optional reward key. + */ + default void xp(Player p, Location at, double xp, String rewardKey) { + SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, false, true); + } + + /** + * Grants silent xp at a specific location (with optional burst visuals). + */ + default void xpS(Player p, Location at, double xp) { + xpS(p, at, xp, null); + } + + /** + * Grants silent xp at a specific location using an optional reward key. + */ + default void xpS(Player p, Location at, double xp, String rewardKey) { + SkillRuntimeGuards.grantXp(this, p, at, xp, rewardKey, true, true); + } + + /** + * Grants silent xp without visuals. + */ + default void xpSilent(Player p, double xp) { + xpSilent(p, xp, null); + } + + /** + * Grants silent xp without visuals using an optional reward key. + */ + default void xpSilent(Player p, double xp, String rewardKey) { + SkillRuntimeGuards.grantXpSilent(this, p, xp, rewardKey); + } + + /** + * Emits spatial xp pulses around a world location. + */ + default void xp(Location at, double xp, int rad, long duration) { + XP.spatialXP(at, this, xp, rad, duration); + vfxXP(at); + } + + /** + * Grants knowledge points for this skill. + */ + default void knowledge(Player p, long k) { + SkillRuntimeGuards.grantKnowledge(this, p, k); + } + + /** + * Opens the skill GUI and optionally enforces blacklist permission checks. + */ + default boolean openGui(Player player, boolean checkPermissions) { + if (checkPermissions && hasBlacklistPermission(player, this)) { + return false; + } else { + openGui(player); + return true; } + } + + /** + * Opens page 0 of this skill GUI. + */ + default void openGui(Player player) { + openGui(player, 0); + } + + /** + * Opens a specific page of this skill GUI. + */ + default void openGui(Player player, int page) { + SkillGuiSupport.openGui(this, player, page); + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java index 82f9d06b4..c3eb1d7c4 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java @@ -33,88 +33,88 @@ import java.util.Map; public final class SkillEventRegistrar { - private SkillEventRegistrar() { + private SkillEventRegistrar() { + } + + public static boolean register(Plugin plugin, Listener listener) { + if (!(listener instanceof Skill)) { + return false; } - public static boolean register(Plugin plugin, Listener listener) { - if (!(listener instanceof Skill)) { - return false; + boolean registeredAny = false; + for (Method method : collectHandlerMethods(listener.getClass()).values()) { + EventHandler annotation = method.getAnnotation(EventHandler.class); + if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { + continue; + } + + Class parameterType = method.getParameterTypes()[0]; + if (!Event.class.isAssignableFrom(parameterType)) { + continue; + } + + @SuppressWarnings("unchecked") + Class eventType = (Class) parameterType; + try { + method.setAccessible(true); + } catch (Throwable ex) { + Adapt.warn("Failed enabling access to skill handler " + + listener.getClass().getName() + "#" + method.getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + continue; + } + + EventExecutor executor = (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; } - boolean registeredAny = false; - for (Method method : collectHandlerMethods(listener.getClass()).values()) { - EventHandler annotation = method.getAnnotation(EventHandler.class); - if (annotation == null || method.getParameterCount() != 1 || Modifier.isStatic(method.getModifiers())) { - continue; - } - - Class parameterType = method.getParameterTypes()[0]; - if (!Event.class.isAssignableFrom(parameterType)) { - continue; - } - - @SuppressWarnings("unchecked") - Class eventType = (Class) parameterType; - try { - method.setAccessible(true); - } catch (Throwable ex) { - Adapt.warn("Failed enabling access to skill handler " - + listener.getClass().getName() + "#" + method.getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - continue; - } - - EventExecutor executor = (target, event) -> { - if (!eventType.isAssignableFrom(event.getClass())) { - return; - } - - try { - method.invoke(target, event); - } catch (InvocationTargetException ex) { - throw new EventException(ex.getCause()); - } catch (Throwable ex) { - throw new EventException(ex); - } - }; - - boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); - Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); - registeredAny = true; + try { + method.invoke(target, event); + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable ex) { + throw new EventException(ex); } + }; - return registeredAny; + boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); + Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); + registeredAny = true; } - private static Map collectHandlerMethods(Class type) { - Map methods = new LinkedHashMap<>(); - Class current = type; - while (current != null && current != Object.class) { - for (Method method : current.getDeclaredMethods()) { - if (!method.isAnnotationPresent(EventHandler.class)) { - continue; - } - methods.putIfAbsent(signature(method), method); - } - current = current.getSuperclass(); - } - return methods; - } + return registeredAny; + } - private static String signature(Method method) { - return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + private static Map collectHandlerMethods(Class type) { + Map methods = new LinkedHashMap<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Method method : current.getDeclaredMethods()) { + if (!method.isAnnotationPresent(EventHandler.class)) { + continue; + } + methods.putIfAbsent(signature(method), method); + } + current = current.getSuperclass(); } + return methods; + } - private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { - if (!Cancellable.class.isAssignableFrom(eventType)) { - return annotation.ignoreCancelled(); - } + private static String signature(Method method) { + return method.getName() + "|" + Arrays.toString(method.getParameterTypes()); + } - if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { - return false; - } + private static boolean shouldIgnoreCancelled(Method method, EventHandler annotation, Class eventType) { + if (!Cancellable.class.isAssignableFrom(eventType)) { + return annotation.ignoreCancelled(); + } - return true; + if (method.isAnnotationPresent(ReceiveCancelledEvents.class)) { + return false; } + + return true; + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index a46f82cb4..d0b825550 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -26,335 +26,329 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.inventorygui.GuiEffects; import art.arcane.adapt.util.common.inventorygui.GuiLayout; import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.volmlib.util.inventorygui.UIWindow; -import art.arcane.volmlib.util.inventorygui.UIElement; -import art.arcane.volmlib.util.inventorygui.Window; -import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.volmlib.util.format.ColorFormatter; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; final class SkillGuiSupport { - private static final long CLOSE_SUPPRESS_MS = 1200L; - private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; - private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); - - private SkillGuiSupport() { - } - - static boolean areParticlesEnabled(Skill skill, boolean componentEnabled) { - if (!componentEnabled) { - return false; - } - - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - if (effects != null && effects.getSkillParticleOverrides() != null && !effects.getSkillParticleOverrides().isEmpty()) { - String key = skill.getName(); - Boolean override = effects.getSkillParticleOverrides().get(key); - if (override == null && key != null) { - override = effects.getSkillParticleOverrides().get(key.toLowerCase(Locale.ROOT)); - } - if (override != null && !override) { - return false; - } - } + private static final long CLOSE_SUPPRESS_MS = 1200L; + private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; + private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); - Object config = skill.getConfig(); - if (config != null) { - Boolean directToggle = readBooleanField(config, "showParticles"); - if (directToggle != null && !directToggle) { - return false; - } - - Boolean genericToggle = readBooleanField(config, "showParticleEffects"); - if (genericToggle != null && !genericToggle) { - return false; - } - } + private SkillGuiSupport() { + } - return true; + static boolean areParticlesEnabled(Skill skill, boolean componentEnabled) { + if (!componentEnabled) { + return false; } - static boolean areSoundsEnabled(Skill skill, boolean componentEnabled) { - if (!componentEnabled) { - return false; - } - - Object config = skill.getConfig(); - if (config != null) { - Boolean directToggle = readBooleanField(config, "showSounds"); - if (directToggle != null && !directToggle) { - return false; - } - } - - return true; + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + if (effects != null && effects.getSkillParticleOverrides() != null && !effects.getSkillParticleOverrides().isEmpty()) { + String key = skill.getName(); + Boolean override = effects.getSkillParticleOverrides().get(key); + if (override == null && key != null) { + override = effects.getSkillParticleOverrides().get(key.toLowerCase(Locale.ROOT)); + } + if (override != null && !override) { + return false; + } } - static void openGui(Skill skill, Player player, int page) { - if (skill == null || !skill.isEnabled() || !SkillRuntimeGuards.isRuntimePlayer(player)) { - return; - } + Object config = skill.getConfig(); + if (config != null) { + Boolean directToggle = readBooleanField(config, "showParticles"); + if (directToggle != null && !directToggle) { + return false; + } - if (!J.isPrimaryThread()) { - int targetPage = page; - J.s(() -> openGui(skill, player, targetPage)); - return; - } + Boolean genericToggle = readBooleanField(config, "showParticleEffects"); + if (genericToggle != null && !genericToggle) { + return false; + } + } - AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(player); - if (adaptPlayer == null) { - return; - } + return true; + } - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); - - List> visibleAdaptations = new ArrayList<>(); - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.isEnabled()) { - continue; - } - if (!adaptation.getSkill().isEnabled()) { - continue; - } - if (adaptation.hasBlacklistPermission(player, adaptation)) { - continue; - } - visibleAdaptations.add(adaptation); - } - visibleAdaptations.sort( - Comparator.comparing((Adaptation adaptation) -> normalizeSortKey(adaptation.getDisplayName())) - .thenComparing(Adaptation::getName, String.CASE_INSENSITIVE_ORDER) - ); - - boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); - GuiLayout.PagePlan plan = GuiLayout.plan(visibleAdaptations.size(), reserveNavigation); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(visibleAdaptations.size(), start + plan.itemsPerPage()); - - UIWindow window = new UIWindow(Adapt.instance, player); - GuiTheme.apply(window, "skill/" + skill.getName()); - window.setViewportHeight(plan.rows()); - - if (visibleAdaptations.isEmpty()) { - window.setElement(0, 0, new UIElement("ada-empty") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.GRAY + "No adaptations available")); - } else { - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - Adaptation adaptation = visibleAdaptations.get(rowStart + i); - int level = adaptPlayer.getData().getSkillLine(skill.getName()).getAdaptationLevel(adaptation.getName()); - int pos = GuiLayout.centeredPosition(i, rowCount); - Element element = new UIElement("ada-" + adaptation.getName()) - .setMaterial(new MaterialBlock(adaptation.getIcon())) - .setBaseItemStack(adaptation.getModel().toItemStack()) - .setName(adaptation.getDisplayName(level)) - .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) - .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) - .setProgress(1D) - .onLeftClick((e) -> openAdaptation(adaptation, player)); - reveal.add(new GuiEffects.Placement(pos, row, element)); - } - } - GuiEffects.applyReveal(window, reveal); - } + static boolean areSoundsEnabled(Skill skill, boolean componentEnabled) { + if (!componentEnabled) { + return false; + } - if (plan.hasNavigationRow()) { - int navRow = plan.rows() - 1; - int jumpPages = 5; - int jumpBack = Math.max(0, currentPage - jumpPages); - int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); - if (currentPage > 0) { - window.setElement(-4, navRow, new UIElement("skill-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") - .onLeftClick((e) -> openSkillPage(skill, player, currentPage - 1)) - .onRightClick((e) -> openSkillPage(skill, player, jumpBack))); - window.setElement(-3, navRow, new UIElement("skill-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> openSkillPage(skill, player, 0))); - } - if (currentPage < plan.pageCount() - 1) { - window.setElement(4, navRow, new UIElement("skill-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") - .onLeftClick((e) -> openSkillPage(skill, player, currentPage + 1)) - .onRightClick((e) -> openSkillPage(skill, player, jumpForward))); - window.setElement(3, navRow, new UIElement("skill-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> openSkillPage(skill, player, plan.pageCount() - 1))); - } - - int from = visibleAdaptations.isEmpty() ? 0 : (start + 1); - int to = visibleAdaptations.isEmpty() ? 0 : end; - window.setElement(-1, navRow, new UIElement("skill-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + visibleAdaptations.size()) - .setProgress(1D)); - - if (AdaptConfig.get().isGuiBackButton()) { - window.setElement(0, navRow, new UIElement("back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) - .onLeftClick((e) -> navigateBack(player))); - } + Object config = skill.getConfig(); + if (config != null) { + Boolean directToggle = readBooleanField(config, "showSounds"); + if (directToggle != null && !directToggle) { + return false; + } + } - } + return true; + } - window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")"); - window.onClosed((vv) -> J.s(() -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); - window.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); + static void openGui(Skill skill, Player player, int page) { + if (skill == null || !skill.isEnabled() || !SkillRuntimeGuards.isRuntimePlayer(player)) { + return; } - private static void openSkillPage(Skill skill, Player player, int page) { - suppressClose(player); - openGui(skill, player, page); + if (!J.isPrimaryThread()) { + int targetPage = page; + J.s(() -> openGui(skill, player, targetPage)); + return; } - private static void openAdaptation(Adaptation adaptation, Player player) { - suppressClose(player); - adaptation.openGui(player); + AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(player); + if (adaptPlayer == null) { + return; } - private static void navigateBack(Player player) { - playCloseSound(player); - suppressClose(player); - SkillsGui.open(player); + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + + List> visibleAdaptations = new ArrayList<>(); + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.isEnabled()) { + continue; + } + if (!adaptation.getSkill().isEnabled()) { + continue; + } + if (adaptation.hasBlacklistPermission(player, adaptation)) { + continue; + } + visibleAdaptations.add(adaptation); } - - private static void onGuiClosed(Player player, boolean openPrevGui) { - if (player == null) { - return; + visibleAdaptations.sort( + Comparator.comparing((Adaptation adaptation) -> normalizeSortKey(adaptation.getDisplayName())) + .thenComparing(Adaptation::getName, String.CASE_INSENSITIVE_ORDER) + ); + + boolean reserveNavigation = AdaptConfig.get().isGuiBackButton(); + GuiLayout.PagePlan plan = GuiLayout.plan(visibleAdaptations.size(), reserveNavigation); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(visibleAdaptations.size(), start + plan.itemsPerPage()); + + UIWindow window = new UIWindow(Adapt.instance, player); + GuiTheme.apply(window, "skill/" + skill.getName()); + window.setViewportHeight(plan.rows()); + + if (visibleAdaptations.isEmpty()) { + window.setElement(0, 0, new UIElement("ada-empty") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.GRAY + "No adaptations available")); + } else { + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; } - if (consumeCloseSuppression(player)) { - return; - } - - playCloseSound(player); - if (openPrevGui) { - J.s(() -> { - if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { - SkillsGui.open(player); - } - }, 1); - } else { - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + Adaptation adaptation = visibleAdaptations.get(rowStart + i); + int level = adaptPlayer.getData().getSkillLine(skill.getName()).getAdaptationLevel(adaptation.getName()); + int pos = GuiLayout.centeredPosition(i, rowCount); + Element element = new UIElement("ada-" + adaptation.getName()) + .setMaterial(new MaterialBlock(adaptation.getIcon())) + .setBaseItemStack(adaptation.getModel().toItemStack()) + .setName(adaptation.getDisplayName(level)) + .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) + .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) + .setProgress(1D) + .onLeftClick((e) -> openAdaptation(adaptation, player)); + reveal.add(new GuiEffects.Placement(pos, row, element)); } + } + GuiEffects.applyReveal(window, reveal); } - private static void playCloseSound(Player player) { - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); - spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + if (plan.hasNavigationRow()) { + int navRow = plan.rows() - 1; + int jumpPages = 5; + int jumpBack = Math.max(0, currentPage - jumpPages); + int jumpForward = Math.min(plan.pageCount() - 1, currentPage + jumpPages); + if (currentPage > 0) { + window.setElement(-4, navRow, new UIElement("skill-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Right click: jump -" + jumpPages + " pages") + .onLeftClick((e) -> openSkillPage(skill, player, currentPage - 1)) + .onRightClick((e) -> openSkillPage(skill, player, jumpBack))); + window.setElement(-3, navRow, new UIElement("skill-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> openSkillPage(skill, player, 0))); + } + if (currentPage < plan.pageCount() - 1) { + window.setElement(4, navRow, new UIElement("skill-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Right click: jump +" + jumpPages + " pages") + .onLeftClick((e) -> openSkillPage(skill, player, currentPage + 1)) + .onRightClick((e) -> openSkillPage(skill, player, jumpForward))); + window.setElement(3, navRow, new UIElement("skill-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> openSkillPage(skill, player, plan.pageCount() - 1))); + } + + int from = visibleAdaptations.isEmpty() ? 0 : (start + 1); + int to = visibleAdaptations.isEmpty() ? 0 : end; + window.setElement(-1, navRow, new UIElement("skill-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + plan.pageCount()) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + visibleAdaptations.size()) + .setProgress(1D)); + + if (AdaptConfig.get().isGuiBackButton()) { + window.setElement(0, navRow, new UIElement("back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName("" + C.RESET + C.GRAY + Localizer.dLocalize("snippets.gui.back")) + .onLeftClick((e) -> navigateBack(player))); + } + } - private static void suppressClose(Player player) { - if (player == null) { - return; - } + window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")"); + window.onClosed((vv) -> J.s(() -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); + window.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); + } + + private static void openSkillPage(Skill skill, Player player, int page) { + suppressClose(player); + openGui(skill, player, page); + } + + private static void openAdaptation(Adaptation adaptation, Player player) { + suppressClose(player); + adaptation.openGui(player); + } + + private static void navigateBack(Player player) { + playCloseSound(player); + suppressClose(player); + SkillsGui.open(player); + } + + private static void onGuiClosed(Player player, boolean openPrevGui) { + if (player == null) { + return; + } - UUID playerId = player.getUniqueId(); - long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; - CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); - J.s(() -> { - Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); - if (current != null && current.longValue() == suppressUntil) { - CLOSE_SUPPRESS_UNTIL.remove(playerId); - } - }, CLOSE_SUPPRESS_CLEAR_TICKS); + if (consumeCloseSuppression(player)) { + return; } - private static boolean consumeCloseSuppression(Player player) { - if (player == null) { - return false; + playCloseSound(player); + if (openPrevGui) { + J.s(() -> { + if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { + SkillsGui.open(player); } + }, 1); + } else { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + } + } + + private static void playCloseSound(Player player) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); + spw.play(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + } + + private static void suppressClose(Player player) { + if (player == null) { + return; + } - Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); - if (until == null) { - return false; - } + UUID playerId = player.getUniqueId(); + long suppressUntil = System.currentTimeMillis() + CLOSE_SUPPRESS_MS; + CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); + J.s(() -> { + Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); + if (current != null && current.longValue() == suppressUntil) { + CLOSE_SUPPRESS_UNTIL.remove(playerId); + } + }, CLOSE_SUPPRESS_CLEAR_TICKS); + } + + private static boolean consumeCloseSuppression(Player player) { + if (player == null) { + return false; + } - if (until >= System.currentTimeMillis()) { - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return true; - } + Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); + if (until == null) { + return false; + } - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return false; + if (until >= System.currentTimeMillis()) { + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return true; } - private static String normalizeSortKey(String value) { - if (value == null) { - return ""; - } + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return false; + } - String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); - return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + private static String normalizeSortKey(String value) { + if (value == null) { + return ""; } - private static Boolean readBooleanField(Object source, String fieldName) { - if (source == null || fieldName == null || fieldName.isBlank()) { - return null; - } + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); + return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + } - Class current = source.getClass(); - while (current != null) { - try { - Field field = current.getDeclaredField(fieldName); - field.setAccessible(true); - Object value = field.get(source); - if (value instanceof Boolean bool) { - return bool; - } - return null; - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } catch (Throwable ex) { - Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } - } + private static Boolean readBooleanField(Object source, String fieldName) { + if (source == null || fieldName == null || fieldName.isBlank()) { + return null; + } + Class current = source.getClass(); + while (current != null) { + try { + Field field = current.getDeclaredField(fieldName); + field.setAccessible(true); + Object value = field.get(source); + if (value instanceof Boolean bool) { + return bool; + } + return null; + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } catch (Throwable ex) { + Adapt.verbose("Failed reading boolean field '" + fieldName + "' from " + source.getClass().getName() + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); return null; + } } + + return null; + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index 84e46afb0..d500c3332 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -54,516 +54,516 @@ import java.util.concurrent.ConcurrentHashMap; public class SkillRegistry extends TickedObject { - private static final long SLOW_SKILL_REG_MS = 300L; - private static final int DEFERRED_SKILLS_PER_TICK = 2; - - public static final KMap> skills = new KMap<>(); - private final KMap> knownSkills = new KMap<>(); - private final KMap>> skillTypes = new KMap<>(); - private final Map> adaptationRecipeIndex = new ConcurrentHashMap<>(); - private final Deque> deferredBootstrapRecipeRegistration = new ArrayDeque<>(); - private volatile boolean deferredBootstrapRecipeTaskScheduled; - private volatile boolean bootstrapLoading = true; - private volatile boolean foliaRecipeRegistrationWaitWarned; - - public SkillRegistry() { - super("registry", UUID.randomUUID() + "-sk", 1250); - registerSkill(SkillAgility.class); - registerSkill(SkillArchitect.class); - registerSkill(SkillAxes.class); - registerSkill(SkillBlocking.class); - registerSkill(SkillChronos.class); - registerSkill(SkillCrafting.class); - registerSkill(SkillDiscovery.class); - registerSkill(SkillEnchanting.class); - registerSkill(SkillHerbalism.class); - registerSkill(SkillHunter.class); - registerSkill(SkillPickaxes.class); - registerSkill(SkillRanged.class); - registerSkill(SkillRift.class); - registerSkill(SkillSeaborne.class); - registerSkill(SkillStealth.class); - registerSkill(SkillSwords.class); - registerSkill(SkillTaming.class); - registerSkill(SkillTragOul.class); - registerSkill(SkillUnarmed.class); - registerSkill(SkillExcavation.class); - registerSkill(SkillBrewing.class); - registerSkill(SkillNether.class); - bootstrapLoading = false; - scheduleDeferredBootstrapRecipeRegistration(); - } - - @EventHandler - public void on(PlayerExpChangeEvent e) { - Player p = e.getPlayer(); - if (e.getAmount() > 0) { - getPlayer(p).boostXPToRecents(0.03, 10000); - } + public static final KMap> skills = new KMap<>(); + private static final long SLOW_SKILL_REG_MS = 300L; + private static final int DEFERRED_SKILLS_PER_TICK = 2; + private final KMap> knownSkills = new KMap<>(); + private final KMap>> skillTypes = new KMap<>(); + private final Map> adaptationRecipeIndex = new ConcurrentHashMap<>(); + private final Deque> deferredBootstrapRecipeRegistration = new ArrayDeque<>(); + private volatile boolean deferredBootstrapRecipeTaskScheduled; + private volatile boolean bootstrapLoading = true; + private volatile boolean foliaRecipeRegistrationWaitWarned; + + public SkillRegistry() { + super("registry", UUID.randomUUID() + "-sk", 1250); + registerSkill(SkillAgility.class); + registerSkill(SkillArchitect.class); + registerSkill(SkillAxes.class); + registerSkill(SkillBlocking.class); + registerSkill(SkillChronos.class); + registerSkill(SkillCrafting.class); + registerSkill(SkillDiscovery.class); + registerSkill(SkillEnchanting.class); + registerSkill(SkillHerbalism.class); + registerSkill(SkillHunter.class); + registerSkill(SkillPickaxes.class); + registerSkill(SkillRanged.class); + registerSkill(SkillRift.class); + registerSkill(SkillSeaborne.class); + registerSkill(SkillStealth.class); + registerSkill(SkillSwords.class); + registerSkill(SkillTaming.class); + registerSkill(SkillTragOul.class); + registerSkill(SkillUnarmed.class); + registerSkill(SkillExcavation.class); + registerSkill(SkillBrewing.class); + registerSkill(SkillNether.class); + bootstrapLoading = false; + scheduleDeferredBootstrapRecipeRegistration(); + } + + @EventHandler + public void on(PlayerExpChangeEvent e) { + Player p = e.getPlayer(); + if (e.getAmount() > 0) { + getPlayer(p).boostXPToRecents(0.03, 10000); } + } - private boolean canInteract(Player player, Location targetLocation) { - if (player == null || targetLocation == null) { - return false; - } - - for (Protector protector : Adapt.instance.getProtectorRegistry().getAllProtectors()) { - if (!protector.canInteract(player, targetLocation, null)) { - return false; - } - } - - return true; + private boolean canInteract(Player player, Location targetLocation) { + if (player == null || targetLocation == null) { + return false; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - - boolean commonConditions = p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null; - boolean isLectern = commonConditions && e.getClickedBlock().getType().equals(Material.LECTERN); - boolean isObserver = commonConditions && e.getClickedBlock().getType().equals(Material.OBSERVER); - boolean allowVerticalFaces = AdaptConfig.get().adaptActivatorAllowVerticalFaces; - boolean validActivatorFace = e.getBlockFace() != null - && (allowVerticalFaces || (!e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN))); - boolean isAdaptActivator = validActivatorFace && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) - && e.getClickedBlock() != null - && canInteract(p, e.getClickedBlock().getLocation()) - && e.getClickedBlock().getType().equals(Material.valueOf(AdaptConfig.get().adaptActivatorBlock)) && (p.getInventory().getItemInMainHand().getType().equals(Material.AIR) - || !p.getInventory().getItemInMainHand().getType().isBlock()) && - (p.getInventory().getItemInOffHand().getType().equals(Material.AIR) || !p.getInventory().getItemInOffHand().getType().isBlock()); - - if (isAdaptActivator) { - SoundPlayer spw = SoundPlayer.of(e.getClickedBlock().getWorld()); - spw.play(e.getClickedBlock().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.5f, 0.72f); - spw.play(e.getClickedBlock().getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 0.35f, 0.755f); - SkillsGui.open(p); - e.setCancelled(true); - p.getWorld().spawnParticle(Particles.CRIT_MAGIC, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 25, 0, 0, 0, 1.1); - p.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 12, 0, 0, 0, 1.1); - } - - if (isLectern) { - ItemStack it = p.getInventory().getItemInMainHand(); - if (it.getItemMeta() != null && !it.getItemMeta().getPersistentDataContainer().getKeys().isEmpty()) { - e.setCancelled(true); - playDebug(p); - it.getItemMeta().getPersistentDataContainer().getKeys().forEach(k -> Bukkit.getServer().getConsoleSender().sendMessage(k + " = " + it.getItemMeta().getPersistentDataContainer().getOrDefault(k, PersistentDataType.STRING, "Not a String"))); - } - } + for (Protector protector : Adapt.instance.getProtectorRegistry().getAllProtectors()) { + if (!protector.canInteract(player, targetLocation, null)) { + return false; + } + } - if (isObserver) { - ItemStack it = p.getInventory().getItemInMainHand(); - if (it.getType().equals(Material.EXPERIENCE_BOTTLE)) { - e.setCancelled(true); - Bukkit.getServer().getConsoleSender().sendMessage(" "); - p.setCooldown(Material.ENCHANTED_BOOK, 3); - AdaptPlayer a = getPlayer(p); - playDebug(p); - - String xv = a.getData().getMultiplier() - 1d > 0 ? "+" + Form.pc(a.getData().getMultiplier() - 1D) : Form.pc(a.getData().getMultiplier() - 1D); - Bukkit.getServer().getConsoleSender().sendMessage("Global" + C.GRAY + ": " + C.GREEN + xv); - - for (XPMultiplier i : a.getData().getMultipliers()) { - String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); - } - for (XPMultiplier i : Adapt.instance.getAdaptServer().getData().getMultipliers()) { - String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); - } - - for (PlayerSkillLine i : a.getData().getSkillLines().v()) { - Skill s = i.getRawSkill(a); - if (s == null) { - continue; - } - String v = i.getMultiplier() - a.getData().getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier() - a.getData().getMultiplier()) : Form.pc(i.getMultiplier() - a.getData().getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(" " + s.getDisplayName() + C.GRAY + ": " + s.getColor() + v); - for (XPMultiplier j : i.getMultipliers()) { - String vv = j.getMultiplier() > 0 ? "+" + Form.pc(j.getMultiplier()) : Form.pc(j.getMultiplier()); - Bukkit.getServer().getConsoleSender().sendMessage(" " + s.getShortName() + C.GRAY + " " + vv + " for " + Form.duration(j.getGoodFor() - M.ms(), 0)); - } - } - } - } + return true; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + + boolean commonConditions = p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getClickedBlock() != null; + boolean isLectern = commonConditions && e.getClickedBlock().getType().equals(Material.LECTERN); + boolean isObserver = commonConditions && e.getClickedBlock().getType().equals(Material.OBSERVER); + boolean allowVerticalFaces = AdaptConfig.get().adaptActivatorAllowVerticalFaces; + boolean validActivatorFace = e.getBlockFace() != null + && (allowVerticalFaces || (!e.getBlockFace().equals(BlockFace.UP) && !e.getBlockFace().equals(BlockFace.DOWN))); + boolean isAdaptActivator = validActivatorFace && !p.isSneaking() && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) + && e.getClickedBlock() != null + && canInteract(p, e.getClickedBlock().getLocation()) + && e.getClickedBlock().getType().equals(Material.valueOf(AdaptConfig.get().adaptActivatorBlock)) && (p.getInventory().getItemInMainHand().getType().equals(Material.AIR) + || !p.getInventory().getItemInMainHand().getType().isBlock()) && + (p.getInventory().getItemInOffHand().getType().equals(Material.AIR) || !p.getInventory().getItemInOffHand().getType().isBlock()); + + if (isAdaptActivator) { + SoundPlayer spw = SoundPlayer.of(e.getClickedBlock().getWorld()); + spw.play(e.getClickedBlock().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.5f, 0.72f); + spw.play(e.getClickedBlock().getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 0.35f, 0.755f); + SkillsGui.open(p); + e.setCancelled(true); + p.getWorld().spawnParticle(Particles.CRIT_MAGIC, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 25, 0, 0, 0, 1.1); + p.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, e.getClickedBlock().getLocation().clone().add(0.5, 1, 0.5), 12, 0, 0, 0, 1.1); } - private void playDebug(Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_BELL_RESONATE, 1f, 0.6f); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 0.1f); - sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.6f); - sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.2f); + if (isLectern) { + ItemStack it = p.getInventory().getItemInMainHand(); + if (it.getItemMeta() != null && !it.getItemMeta().getPersistentDataContainer().getKeys().isEmpty()) { + e.setCancelled(true); + playDebug(p); + it.getItemMeta().getPersistentDataContainer().getKeys().forEach(k -> Bukkit.getServer().getConsoleSender().sendMessage(k + " = " + it.getItemMeta().getPersistentDataContainer().getOrDefault(k, PersistentDataType.STRING, "Not a String"))); + } + } + if (isObserver) { + ItemStack it = p.getInventory().getItemInMainHand(); + if (it.getType().equals(Material.EXPERIENCE_BOTTLE)) { + e.setCancelled(true); + Bukkit.getServer().getConsoleSender().sendMessage(" "); + p.setCooldown(Material.ENCHANTED_BOOK, 3); + AdaptPlayer a = getPlayer(p); + playDebug(p); + + String xv = a.getData().getMultiplier() - 1d > 0 ? "+" + Form.pc(a.getData().getMultiplier() - 1D) : Form.pc(a.getData().getMultiplier() - 1D); + Bukkit.getServer().getConsoleSender().sendMessage("Global" + C.GRAY + ": " + C.GREEN + xv); + + for (XPMultiplier i : a.getData().getMultipliers()) { + String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); + } + for (XPMultiplier i : Adapt.instance.getAdaptServer().getData().getMultipliers()) { + String vv = i.getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier()) : Form.pc(i.getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(C.GREEN + "* " + vv + C.GRAY + " for " + Form.duration(i.getGoodFor() - M.ms(), 0)); + } + + for (PlayerSkillLine i : a.getData().getSkillLines().v()) { + Skill s = i.getRawSkill(a); + if (s == null) { + continue; + } + String v = i.getMultiplier() - a.getData().getMultiplier() > 0 ? "+" + Form.pc(i.getMultiplier() - a.getData().getMultiplier()) : Form.pc(i.getMultiplier() - a.getData().getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(" " + s.getDisplayName() + C.GRAY + ": " + s.getColor() + v); + for (XPMultiplier j : i.getMultipliers()) { + String vv = j.getMultiplier() > 0 ? "+" + Form.pc(j.getMultiplier()) : Form.pc(j.getMultiplier()); + Bukkit.getServer().getConsoleSender().sendMessage(" " + s.getShortName() + C.GRAY + " " + vv + " for " + Form.duration(j.getGoodFor() - M.ms(), 0)); + } + } + } } + } - public Skill getSkill(String i) { - if (i == null) { - return null; - } + private void playDebug(Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_BELL_RESONATE, 1f, 0.6f); + sp.play(p.getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1f, 0.1f); + sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.6f); + sp.play(p.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 1f, 1.2f); - Skill direct = skills.get(i); - if (direct != null) { - return direct; - } + } - return skills.get(normalizeSkillName(i)); + public Skill getSkill(String i) { + if (i == null) { + return null; } - public Skill getAnySkill(String i) { - if (i == null) { - return null; - } - - Skill direct = knownSkills.get(i); - if (direct != null) { - return direct; - } - - return knownSkills.get(normalizeSkillName(i)); + Skill direct = skills.get(i); + if (direct != null) { + return direct; } - public List> getSkills() { - return skills.v(); + return skills.get(normalizeSkillName(i)); + } + + public Skill getAnySkill(String i) { + if (i == null) { + return null; } - public List> getAllSkills() { - return new ArrayList<>(knownSkills.v()); + Skill direct = knownSkills.get(i); + if (direct != null) { + return direct; } - public synchronized void registerSkill(Class> skillType) { - long started = System.currentTimeMillis(); - long instantiateStarted = started; - Skill skill = instantiateSkill(skillType); - long instantiateMs = System.currentTimeMillis() - instantiateStarted; - if (skill == null) { - return; - } + return knownSkills.get(normalizeSkillName(i)); + } - String skillName = normalizeSkillName(skill.getName()); - skillTypes.put(skillName, skillType); - Skill previous = knownSkills.put(skillName, skill); - if (previous != null && previous != skill) { - unregisterRecipes(previous); - previous.unregister(); - } + public List> getSkills() { + return skills.v(); + } - if (!skill.isEnabled()) { - skill.unregister(); - skills.remove(skillName); - return; - } + public List> getAllSkills() { + return new ArrayList<>(knownSkills.v()); + } - skills.put(skillName, skill); - if (bootstrapLoading) { - deferredBootstrapRecipeRegistration.addLast(skill); - } else { - registerRecipes(skill); - } + public synchronized void registerSkill(Class> skillType) { + long started = System.currentTimeMillis(); + long instantiateStarted = started; + Skill skill = instantiateSkill(skillType); + long instantiateMs = System.currentTimeMillis() - instantiateStarted; + if (skill == null) { + return; + } - long totalMs = System.currentTimeMillis() - started; - if (totalMs >= SLOW_SKILL_REG_MS || instantiateMs >= SLOW_SKILL_REG_MS) { - Adapt.warn("Skill registration slow-path [" + skillName + "] total=" + totalMs + "ms instantiate=" + instantiateMs + "ms bootstrap=" + bootstrapLoading + "."); - } + String skillName = normalizeSkillName(skill.getName()); + skillTypes.put(skillName, skillType); + Skill previous = knownSkills.put(skillName, skill); + if (previous != null && previous != skill) { + unregisterRecipes(previous); + previous.unregister(); } - public synchronized boolean hotReloadSkillConfig(String skillName) { - String normalized = normalizeSkillName(skillName); - Skill loaded = knownSkills.get(normalized); - if (loaded instanceof SimpleSkill simpleSkill) { - boolean wasEnabled = loaded.isEnabled(); - boolean ok = simpleSkill.reloadConfigFromDisk(false); - if (!ok) { - return false; - } - - if (!loaded.isEnabled()) { - unregisterRecipes(loaded); - if (wasEnabled) { - loaded.unregister(); - } - skills.remove(normalized); - return true; - } - - if (!wasEnabled) { - return replaceSkillInstance(normalized, inferSkillType(normalized, loaded), loaded); - } - - skills.put(normalized, loaded); - unregisterRecipes(loaded); - registerRecipes(loaded); - return true; - } + if (!skill.isEnabled()) { + skill.unregister(); + skills.remove(skillName); + return; + } - Class> skillType = inferSkillType(normalized, loaded); - if (skillType == null) { - Adapt.verbose("No known skill type for config hotload: " + skillName); - return false; - } + skills.put(skillName, skill); + if (bootstrapLoading) { + deferredBootstrapRecipeRegistration.addLast(skill); + } else { + registerRecipes(skill); + } - return replaceSkillInstance(normalized, skillType, loaded); + long totalMs = System.currentTimeMillis() - started; + if (totalMs >= SLOW_SKILL_REG_MS || instantiateMs >= SLOW_SKILL_REG_MS) { + Adapt.warn("Skill registration slow-path [" + skillName + "] total=" + totalMs + "ms instantiate=" + instantiateMs + "ms bootstrap=" + bootstrapLoading + "."); } + } + + public synchronized boolean hotReloadSkillConfig(String skillName) { + String normalized = normalizeSkillName(skillName); + Skill loaded = knownSkills.get(normalized); + if (loaded instanceof SimpleSkill simpleSkill) { + boolean wasEnabled = loaded.isEnabled(); + boolean ok = simpleSkill.reloadConfigFromDisk(false); + if (!ok) { + return false; + } + + if (!loaded.isEnabled()) { + unregisterRecipes(loaded); + if (wasEnabled) { + loaded.unregister(); + } + skills.remove(normalized); + return true; + } - @SuppressWarnings("unchecked") - private Class> inferSkillType(String normalizedSkillName, Skill loaded) { - Class> skillType = skillTypes.get(normalizedSkillName); - if (skillType != null) { - return skillType; - } + if (!wasEnabled) { + return replaceSkillInstance(normalized, inferSkillType(normalized, loaded), loaded); + } - if (loaded != null && Skill.class.isAssignableFrom(loaded.getClass())) { - return (Class>) loaded.getClass(); - } + skills.put(normalized, loaded); + unregisterRecipes(loaded); + registerRecipes(loaded); + return true; + } - return null; + Class> skillType = inferSkillType(normalized, loaded); + if (skillType == null) { + Adapt.verbose("No known skill type for config hotload: " + skillName); + return false; } - private boolean replaceSkillInstance(String normalizedName, Class> skillType, Skill previousLoaded) { - Skill replacement = instantiateSkill(skillType); - if (replacement == null) { - return false; - } + return replaceSkillInstance(normalized, skillType, loaded); + } - Skill previousKnown = knownSkills.put(normalizedName, replacement); - if (previousKnown != null && previousKnown != replacement) { - unregisterRecipes(previousKnown); - previousKnown.unregister(); - } else if (previousLoaded != null && previousLoaded != replacement) { - unregisterRecipes(previousLoaded); - previousLoaded.unregister(); - } + @SuppressWarnings("unchecked") + private Class> inferSkillType(String normalizedSkillName, Skill loaded) { + Class> skillType = skillTypes.get(normalizedSkillName); + if (skillType != null) { + return skillType; + } - if (!replacement.isEnabled()) { - replacement.unregister(); - skills.remove(normalizedName); - return true; - } + if (loaded != null && Skill.class.isAssignableFrom(loaded.getClass())) { + return (Class>) loaded.getClass(); + } - Skill previous = skills.put(normalizedName, replacement); - if (previous != null && previous != replacement) { - unregisterRecipes(previous); - previous.unregister(); - } + return null; + } - registerRecipes(replacement); - return true; + private boolean replaceSkillInstance(String normalizedName, Class> skillType, Skill previousLoaded) { + Skill replacement = instantiateSkill(skillType); + if (replacement == null) { + return false; } - public synchronized void refreshRecipes(Skill skill) { - if (skill == null) { - return; - } - - unregisterRecipes(skill); - if (skill.isEnabled()) { - registerRecipes(skill); - } + Skill previousKnown = knownSkills.put(normalizedName, replacement); + if (previousKnown != null && previousKnown != replacement) { + unregisterRecipes(previousKnown); + previousKnown.unregister(); + } else if (previousLoaded != null && previousLoaded != replacement) { + unregisterRecipes(previousLoaded); + previousLoaded.unregister(); } - public boolean isKnownSkill(String skillName) { - if (skillName == null) { - return false; - } + if (!replacement.isEnabled()) { + replacement.unregister(); + skills.remove(normalizedName); + return true; + } - return skillTypes.containsKey(normalizeSkillName(skillName)); + Skill previous = skills.put(normalizedName, replacement); + if (previous != null && previous != replacement) { + unregisterRecipes(previous); + previous.unregister(); } - public Adaptation getRequiredAdaptation(Recipe recipe) { - if (!(recipe instanceof Keyed keyed)) { - return null; - } + registerRecipes(replacement); + return true; + } - return adaptationRecipeIndex.get(keyed.getKey()); + public synchronized void refreshRecipes(Skill skill) { + if (skill == null) { + return; } - private Skill instantiateSkill(Class> skillType) { - try { - return skillType.getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - e.printStackTrace(); - return null; - } + unregisterRecipes(skill); + if (skill.isEnabled()) { + registerRecipes(skill); } + } - private void unregisterRecipes(Skill s) { - s.getRecipes().forEach(AdaptRecipe::unregister); - s.getAdaptations().forEach(adaptation -> { - adaptation.getRecipes().forEach(recipe -> { - removeAdaptationRecipeIndex(recipe, adaptation); - recipe.unregister(); - }); - }); + public boolean isKnownSkill(String skillName) { + if (skillName == null) { + return false; } - private void registerRecipes(Skill s) { - if (s == null) { - return; - } + return skillTypes.containsKey(normalizeSkillName(skillName)); + } - if (shouldDelayRecipeRegistrationForFolia()) { - enqueueDeferredRecipeRegistration(s); - return; - } + public Adaptation getRequiredAdaptation(Recipe recipe) { + if (!(recipe instanceof Keyed keyed)) { + return null; + } + + return adaptationRecipeIndex.get(keyed.getKey()); + } + + private Skill instantiateSkill(Class> skillType) { + try { + return skillType.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | + InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return null; + } + } + + private void unregisterRecipes(Skill s) { + s.getRecipes().forEach(AdaptRecipe::unregister); + s.getAdaptations().forEach(adaptation -> { + adaptation.getRecipes().forEach(recipe -> { + removeAdaptationRecipeIndex(recipe, adaptation); + recipe.unregister(); + }); + }); + } + + private void registerRecipes(Skill s) { + if (s == null) { + return; + } - registerRecipesNow(s); + if (shouldDelayRecipeRegistrationForFolia()) { + enqueueDeferredRecipeRegistration(s); + return; } - private void registerRecipesNow(Skill s) { - if (!s.isEnabled()) { - return; - } - s.getRecipes().forEach(AdaptRecipe::register); - s.getAdaptations().forEach(adaptation -> { - if (!adaptation.isEnabled()) { - return; - } - adaptation.getRecipes().forEach(recipe -> { - recipe.register(); - indexAdaptationRecipe(recipe, adaptation); - }); - adaptation.getBrewingRecipes().forEach(r -> BrewingManager.registerRecipe(adaptation.getName(), r)); - }); - } - - private boolean shouldDelayRecipeRegistrationForFolia() { - if (!J.isFoliaThreading()) { - return false; - } + registerRecipesNow(s); + } - return !Bukkit.getOnlinePlayers().isEmpty(); + private void registerRecipesNow(Skill s) { + if (!s.isEnabled()) { + return; + } + s.getRecipes().forEach(AdaptRecipe::register); + s.getAdaptations().forEach(adaptation -> { + if (!adaptation.isEnabled()) { + return; + } + adaptation.getRecipes().forEach(recipe -> { + recipe.register(); + indexAdaptationRecipe(recipe, adaptation); + }); + adaptation.getBrewingRecipes().forEach(r -> BrewingManager.registerRecipe(adaptation.getName(), r)); + }); + } + + private boolean shouldDelayRecipeRegistrationForFolia() { + if (!J.isFoliaThreading()) { + return false; } - private synchronized void enqueueDeferredRecipeRegistration(Skill skill) { - if (skill == null) { - return; - } + return !Bukkit.getOnlinePlayers().isEmpty(); + } - deferredBootstrapRecipeRegistration.remove(skill); - deferredBootstrapRecipeRegistration.addLast(skill); - scheduleDeferredBootstrapRecipeRegistration(); + private synchronized void enqueueDeferredRecipeRegistration(Skill skill) { + if (skill == null) { + return; } - @Override - public void unregister() { - deferredBootstrapRecipeTaskScheduled = false; - deferredBootstrapRecipeRegistration.clear(); - for (Skill i : knownSkills.v()) { - i.unregister(); - unregisterRecipes(i); - } - skills.clear(); - knownSkills.clear(); - skillTypes.clear(); - adaptationRecipeIndex.clear(); + deferredBootstrapRecipeRegistration.remove(skill); + deferredBootstrapRecipeRegistration.addLast(skill); + scheduleDeferredBootstrapRecipeRegistration(); + } + + @Override + public void unregister() { + deferredBootstrapRecipeTaskScheduled = false; + deferredBootstrapRecipeRegistration.clear(); + for (Skill i : knownSkills.v()) { + i.unregister(); + unregisterRecipes(i); } + skills.clear(); + knownSkills.clear(); + skillTypes.clear(); + adaptationRecipeIndex.clear(); + } - @Override - public void onTick() { + @Override + public void onTick() { + } + + private String normalizeSkillName(String raw) { + String normalized = raw.trim().toLowerCase(Locale.ROOT); + if (normalized.startsWith("[skill]-")) { + normalized = normalized.substring("[skill]-".length()); } - private String normalizeSkillName(String raw) { - String normalized = raw.trim().toLowerCase(Locale.ROOT); - if (normalized.startsWith("[skill]-")) { - normalized = normalized.substring("[skill]-".length()); - } + if (normalized.equals("chrono")) { + normalized = "chronos"; + } - if (normalized.equals("chrono")) { - normalized = "chronos"; - } + return normalized; + } - return normalized; + private void indexAdaptationRecipe(AdaptRecipe recipe, Adaptation adaptation) { + if (recipe == null || adaptation == null) { + return; } - private void indexAdaptationRecipe(AdaptRecipe recipe, Adaptation adaptation) { - if (recipe == null || adaptation == null) { - return; - } + NamespacedKey key = recipe.getNSKey(); + Adaptation previous = adaptationRecipeIndex.put(key, adaptation); + if (previous != null && previous != adaptation) { + Adapt.warn("Recipe key conflict for " + key + ": " + previous.getName() + " replaced by " + adaptation.getName()); + } + } - NamespacedKey key = recipe.getNSKey(); - Adaptation previous = adaptationRecipeIndex.put(key, adaptation); - if (previous != null && previous != adaptation) { - Adapt.warn("Recipe key conflict for " + key + ": " + previous.getName() + " replaced by " + adaptation.getName()); - } + private void removeAdaptationRecipeIndex(AdaptRecipe recipe, Adaptation adaptation) { + if (recipe == null) { + return; } - private void removeAdaptationRecipeIndex(AdaptRecipe recipe, Adaptation adaptation) { - if (recipe == null) { - return; - } + NamespacedKey key = recipe.getNSKey(); + if (adaptation == null) { + adaptationRecipeIndex.remove(key); + return; + } - NamespacedKey key = recipe.getNSKey(); - if (adaptation == null) { - adaptationRecipeIndex.remove(key); - return; - } + adaptationRecipeIndex.computeIfPresent(key, (k, current) -> current == adaptation ? null : current); + } - adaptationRecipeIndex.computeIfPresent(key, (k, current) -> current == adaptation ? null : current); + private synchronized void scheduleDeferredBootstrapRecipeRegistration() { + if (deferredBootstrapRecipeRegistration.isEmpty() || deferredBootstrapRecipeTaskScheduled) { + return; } - private synchronized void scheduleDeferredBootstrapRecipeRegistration() { - if (deferredBootstrapRecipeRegistration.isEmpty() || deferredBootstrapRecipeTaskScheduled) { - return; - } - - deferredBootstrapRecipeTaskScheduled = true; - Adapt.info("Deferring recipe registration for " + deferredBootstrapRecipeRegistration.size() + " skills."); - J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); + deferredBootstrapRecipeTaskScheduled = true; + Adapt.info("Deferring recipe registration for " + deferredBootstrapRecipeRegistration.size() + " skills."); + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); + } + + private void runDeferredBootstrapRecipeRegistrationTick() { + if (shouldDelayRecipeRegistrationForFolia()) { + if (!foliaRecipeRegistrationWaitWarned) { + foliaRecipeRegistrationWaitWarned = true; + Adapt.warn("Delaying Adapt recipe registration on Folia while players are online to avoid unsafe recipe reload races. Recipes will register automatically when the server has no online players."); + } + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); + return; } - private void runDeferredBootstrapRecipeRegistrationTick() { + foliaRecipeRegistrationWaitWarned = false; + boolean complete; + + synchronized (this) { + if (!deferredBootstrapRecipeTaskScheduled) { + return; + } + + int processed = 0; + long started = System.currentTimeMillis(); + while (processed < DEFERRED_SKILLS_PER_TICK) { if (shouldDelayRecipeRegistrationForFolia()) { - if (!foliaRecipeRegistrationWaitWarned) { - foliaRecipeRegistrationWaitWarned = true; - Adapt.warn("Delaying Adapt recipe registration on Folia while players are online to avoid unsafe recipe reload races. Recipes will register automatically when the server has no online players."); - } - J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); - return; + break; } - foliaRecipeRegistrationWaitWarned = false; - boolean complete; - - synchronized (this) { - if (!deferredBootstrapRecipeTaskScheduled) { - return; - } - - int processed = 0; - long started = System.currentTimeMillis(); - while (processed < DEFERRED_SKILLS_PER_TICK) { - if (shouldDelayRecipeRegistrationForFolia()) { - break; - } - - Skill skill = deferredBootstrapRecipeRegistration.pollFirst(); - if (skill == null) { - break; - } - registerRecipesNow(skill); - processed++; - if (System.currentTimeMillis() - started > 8L) { - break; - } - } - - complete = deferredBootstrapRecipeRegistration.isEmpty(); - if (complete) { - deferredBootstrapRecipeTaskScheduled = false; - } + Skill skill = deferredBootstrapRecipeRegistration.pollFirst(); + if (skill == null) { + break; } - - if (complete) { - Adapt.info("Deferred recipe registration completed."); - return; + registerRecipesNow(skill); + processed++; + if (System.currentTimeMillis() - started > 8L) { + break; } + } - if (shouldDelayRecipeRegistrationForFolia()) { - J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); - } else { - J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); - } + complete = deferredBootstrapRecipeRegistration.isEmpty(); + if (complete) { + deferredBootstrapRecipeTaskScheduled = false; + } + } + + if (complete) { + Adapt.info("Deferred recipe registration completed."); + return; + } + + if (shouldDelayRecipeRegistrationForFolia()) { + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 20); + } else { + J.s(this::runDeferredBootstrapRecipeRegistrationTick, 1); } + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java index 2931e059b..6caa7f507 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java @@ -34,189 +34,189 @@ import org.bukkit.event.Cancellable; final class SkillRuntimeGuards { - private SkillRuntimeGuards() { - } - - static void checkStatTrackers(Skill skill, AdaptPlayer player) { - if (skill == null || player == null || !skill.isEnabled()) { - return; - } - if (!AdaptConfig.get().isAdvancements()) { - return; - } - if (!isRuntimePlayer(player.getPlayer())) { - return; - } + private SkillRuntimeGuards() { + } - PlayerData data = player.getData(); - - for (AdaptStatTracker tracker : skill.getStatTrackers()) { - if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { - player.getAdvancementHandler().grant(tracker.getAdvancement()); - skill.xp(player.getPlayer(), tracker.getReward()); - } - } - - for (Adaptation adaptation : skill.getAdaptations()) { - if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { - continue; - } - if (!adaptation.isEnabled()) { - continue; - } - for (AdaptStatTracker tracker : simpleAdaptation.getStatTrackers()) { - if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { - player.getAdvancementHandler().grant(tracker.getAdvancement()); - skill.xp(player.getPlayer(), tracker.getReward()); - } - } - } + static void checkStatTrackers(Skill skill, AdaptPlayer player) { + if (skill == null || player == null || !skill.isEnabled()) { + return; } - - static boolean hasBlacklistPermission(Player player, Skill skill) { - if (player == null || skill == null) { - return true; - } - if (player.isOp()) { - return false; - } - String blacklistPermission = "adapt.blacklist." + skill.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + player.getName() + " has blacklist permission " + blacklistPermission); - return player.hasPermission(blacklistPermission); - } - - static boolean shouldSkipPlayer(Skill skill, Player player) { - try { - if (skill == null || player == null) { - return true; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - return true; - } - - return AdaptationGate.shouldSkipPlayer(player, skill, skill.getPlayer(player) != null); - } catch (Exception ex) { - Adapt.verbose("Failed shouldSkipPlayer check for " + (player == null ? "null" : player.getName()) - + " in skill " + (skill == null ? "null" : skill.getName()) + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return true; - } + if (!AdaptConfig.get().isAdvancements()) { + return; } - - static void withPlayer(Skill skill, Player player, Runnable runnable) { - try { - if (skill == null || player == null || runnable == null) { - return; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - J.runEntity(player, () -> withPlayer(skill, player, runnable)); - return; - } - - if (shouldSkipPlayer(skill, player)) { - return; - } - - runnable.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + if (!isRuntimePlayer(player.getPlayer())) { + return; } - static void withPlayer(Skill skill, Player player, Cancellable cancellable, Runnable runnable) { - try { - if (skill == null || player == null || cancellable == null || runnable == null) { - return; - } - - if (cancellable.isCancelled()) { - return; - } - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { - J.runEntity(player, () -> withPlayer(skill, player, cancellable, runnable)); - return; - } + PlayerData data = player.getData(); - if (cancellable.isCancelled() || shouldSkipPlayer(skill, player)) { - return; - } - - runnable.run(); - } catch (Exception ex) { - Adapt.verbose("Failed guarded cancellable player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + for (AdaptStatTracker tracker : skill.getStatTrackers()) { + if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { + player.getAdvancementHandler().grant(tracker.getAdvancement()); + skill.xp(player.getPlayer(), tracker.getReward()); + } } - static boolean shouldSkipWorld(Skill skill, World world) { - try { - return AdaptationGate.shouldSkipWorld(world, skill); - } catch (Exception ex) { - Adapt.verbose("Failed shouldSkipWorld check for skill " + (skill == null ? "null" : skill.getName()) - + ": " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return true; - } + for (Adaptation adaptation : skill.getAdaptations()) { + if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { + continue; + } + if (!adaptation.isEnabled()) { + continue; + } + for (AdaptStatTracker tracker : simpleAdaptation.getStatTrackers()) { + if (!data.isGranted(tracker.getAdvancement()) && data.getStat(tracker.getStat()) >= tracker.getGoal()) { + player.getAdvancementHandler().grant(tracker.getAdvancement()); + skill.xp(player.getPlayer(), tracker.getReward()); + } + } } + } - static boolean isWorldBlacklisted(Player player) { - return AdaptationGate.isWorldBlacklisted(player); + static boolean hasBlacklistPermission(Player player, Skill skill) { + if (player == null || skill == null) { + return true; } - - static boolean isInCreativeOrSpectator(Player player) { - return AdaptationGate.isInCreativeOrSpectator(player); + if (player.isOp()) { + return false; } - - static boolean canGrantXp(Skill skill, Player player) { - return skill != null && skill.isEnabled() && isRuntimePlayer(player); + String blacklistPermission = "adapt.blacklist." + skill.getName().replaceAll("-", ""); + Adapt.verbose("Checking if player " + player.getName() + " has blacklist permission " + blacklistPermission); + return player.hasPermission(blacklistPermission); + } + + static boolean shouldSkipPlayer(Skill skill, Player player) { + try { + if (skill == null || player == null) { + return true; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + return true; + } + + return AdaptationGate.shouldSkipPlayer(player, skill, skill.getPlayer(player) != null); + } catch (Exception ex) { + Adapt.verbose("Failed shouldSkipPlayer check for " + (player == null ? "null" : player.getName()) + + " in skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return true; } - - static void grantXp(Skill skill, Player player, Location location, double xp, String rewardKey, boolean silent, boolean visualBurst) { - if (!canGrantXp(skill, player)) { - return; - } - try { - if (silent) { - XP.xpSilent(player, skill, xp, rewardKey); - } else { - XP.xp(player, skill, xp, rewardKey); - } - - if (visualBurst && location != null && xp > 50) { - skill.vfxXP(player, location, (int) xp); - } - Adapt.verbose("Gave " + player.getName() + " " + xp + " xp in " + skill.getName() + " " + skill.getClass()); - } catch (Exception ex) { - Adapt.verbose("Failed to give xp to " + player.getName() + " for " + skill.getName() + " (" + xp + ")"); - } + } + + static void withPlayer(Skill skill, Player player, Runnable runnable) { + try { + if (skill == null || player == null || runnable == null) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + J.runEntity(player, () -> withPlayer(skill, player, runnable)); + return; + } + + if (shouldSkipPlayer(skill, player)) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static void withPlayer(Skill skill, Player player, Cancellable cancellable, Runnable runnable) { + try { + if (skill == null || player == null || cancellable == null || runnable == null) { + return; + } + + if (cancellable.isCancelled()) { + return; + } + + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(player)) { + J.runEntity(player, () -> withPlayer(skill, player, cancellable, runnable)); + return; + } + + if (cancellable.isCancelled() || shouldSkipPlayer(skill, player)) { + return; + } + + runnable.run(); + } catch (Exception ex) { + Adapt.verbose("Failed guarded cancellable player runnable for skill " + (skill == null ? "null" : skill.getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + + static boolean shouldSkipWorld(Skill skill, World world) { + try { + return AdaptationGate.shouldSkipWorld(world, skill); + } catch (Exception ex) { + Adapt.verbose("Failed shouldSkipWorld check for skill " + (skill == null ? "null" : skill.getName()) + + ": " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return true; } + } - static void grantXpSilent(Skill skill, Player player, double xp, String rewardKey) { - if (!canGrantXp(skill, player)) { - return; - } - try { - XP.xpSilent(player, skill, xp, rewardKey); - } catch (Exception ignored) { - Adapt.verbose("Player was Given XP (Likely Teleportation) before i can see it because some plugin has higher priority than me and moves a player. so im not going to throw an error, as i know why it's happening."); - } + static boolean isWorldBlacklisted(Player player) { + return AdaptationGate.isWorldBlacklisted(player); + } + + static boolean isInCreativeOrSpectator(Player player) { + return AdaptationGate.isInCreativeOrSpectator(player); + } + + static boolean canGrantXp(Skill skill, Player player) { + return skill != null && skill.isEnabled() && isRuntimePlayer(player); + } + + static void grantXp(Skill skill, Player player, Location location, double xp, String rewardKey, boolean silent, boolean visualBurst) { + if (!canGrantXp(skill, player)) { + return; } + try { + if (silent) { + XP.xpSilent(player, skill, xp, rewardKey); + } else { + XP.xp(player, skill, xp, rewardKey); + } + + if (visualBurst && location != null && xp > 50) { + skill.vfxXP(player, location, (int) xp); + } + Adapt.verbose("Gave " + player.getName() + " " + xp + " xp in " + skill.getName() + " " + skill.getClass()); + } catch (Exception ex) { + Adapt.verbose("Failed to give xp to " + player.getName() + " for " + skill.getName() + " (" + xp + ")"); + } + } - static void grantKnowledge(Skill skill, Player player, long knowledge) { - if (skill == null || !skill.isEnabled() || player == null) { - return; - } - XP.knowledge(player, skill, knowledge); + static void grantXpSilent(Skill skill, Player player, double xp, String rewardKey) { + if (!canGrantXp(skill, player)) { + return; } + try { + XP.xpSilent(player, skill, xp, rewardKey); + } catch (Exception ignored) { + Adapt.verbose("Player was Given XP (Likely Teleportation) before i can see it because some plugin has higher priority than me and moves a player. so im not going to throw an error, as i know why it's happening."); + } + } - static boolean isRuntimePlayer(Player player) { - return player != null && player.getClass().getSimpleName().equals("CraftPlayer"); + static void grantKnowledge(Skill skill, Player player, long knowledge) { + if (skill == null || !skill.isEnabled() || player == null) { + return; } + XP.knowledge(player, skill, knowledge); + } + + static boolean isRuntimePlayer(Player player) { + return player != null && player.getClass().getSimpleName().equals("CraftPlayer"); + } } diff --git a/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java index ae217f961..a25bbbfb9 100644 --- a/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java +++ b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java @@ -21,56 +21,63 @@ import java.util.ArrayDeque; public final class AbilityCheckTelemetry { - private static final long WINDOW_MS = 60_000L; - private static final Object LOCK = new Object(); - private static final ArrayDeque checkOps = new ArrayDeque<>(); - private static final ArrayDeque successfulOps = new ArrayDeque<>(); + private static final long WINDOW_MS = 60_000L; + private static final Object LOCK = new Object(); + private static final ArrayDeque checkOps = new ArrayDeque<>(); + private static final ArrayDeque successfulOps = new ArrayDeque<>(); - private AbilityCheckTelemetry() { + private AbilityCheckTelemetry() { + } + + public static void recordCheckAttempt() { + long now = System.currentTimeMillis(); + synchronized (LOCK) { + checkOps.addLast(now); + trim(checkOps, now); + trim(successfulOps, now); } + } - public static void recordCheckAttempt() { - long now = System.currentTimeMillis(); - synchronized (LOCK) { - checkOps.addLast(now); - trim(checkOps, now); - trim(successfulOps, now); - } + public static void recordSuccessfulCheck() { + long now = System.currentTimeMillis(); + synchronized (LOCK) { + successfulOps.addLast(now); + trim(checkOps, now); + trim(successfulOps, now); } + } - public static void recordSuccessfulCheck() { - long now = System.currentTimeMillis(); - synchronized (LOCK) { - successfulOps.addLast(now); - trim(checkOps, now); - trim(successfulOps, now); - } + public static long checksPerMinute(long now) { + synchronized (LOCK) { + trim(checkOps, now); + return checkOps.size(); } + } - public static long checksPerMinute(long now) { - synchronized (LOCK) { - trim(checkOps, now); - return checkOps.size(); - } + public static long successfulChecksPerMinute(long now) { + synchronized (LOCK) { + trim(successfulOps, now); + return successfulOps.size(); } + } - public static long successfulChecksPerMinute(long now) { - synchronized (LOCK) { - trim(successfulOps, now); - return successfulOps.size(); - } + public static double checksPerTick(long now) { + synchronized (LOCK) { + trim(checkOps, now); + return checkOps.size() / 1200D; } + } - public static void clear() { - synchronized (LOCK) { - checkOps.clear(); - successfulOps.clear(); - } + public static void clear() { + synchronized (LOCK) { + checkOps.clear(); + successfulOps.clear(); } + } - private static void trim(ArrayDeque samples, long now) { - while (!samples.isEmpty() && (now - samples.peekFirst()) > WINDOW_MS) { - samples.removeFirst(); - } + private static void trim(ArrayDeque samples, long now) { + while (!samples.isEmpty() && (now - samples.peekFirst()) > WINDOW_MS) { + samples.removeFirst(); } + } } diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticked.java b/src/main/java/art/arcane/adapt/api/tick/Ticked.java index 518f4a23e..c281f99d7 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticked.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticked.java @@ -22,45 +22,45 @@ import art.arcane.volmlib.util.math.M; public interface Ticked extends AdaptComponent { - default void retick() { - burst(1); - } + default void retick() { + burst(1); + } - default void skip() { - skip(1); - } + default void skip() { + skip(1); + } - void unregister(); + void unregister(); - boolean isBursting(); + boolean isBursting(); - boolean isSkipping(); + boolean isSkipping(); - void stopBursting(); + void stopBursting(); - void stopSkipping(); + void stopSkipping(); - long getTickCount(); + long getTickCount(); - long getAge(); + long getAge(); - void burst(int ticks); + void burst(int ticks); - void skip(int ticks); + void skip(int ticks); - long getLastTick(); + long getLastTick(); - long getInterval(); + long getInterval(); - void setInterval(long ms); + void setInterval(long ms); - void tick(); + void tick(); - String getGroup(); + String getGroup(); - String getId(); + String getId(); - default boolean shouldTick() { - return M.ms() - getLastTick() > getInterval(); - } + default boolean shouldTick() { + return M.ms() - getLastTick() > getInterval(); + } } diff --git a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java index 35837b921..d292a4368 100644 --- a/src/main/java/art/arcane/adapt/api/tick/TickedObject.java +++ b/src/main/java/art/arcane/adapt/api/tick/TickedObject.java @@ -34,270 +34,270 @@ import java.util.concurrent.atomic.AtomicLong; public abstract class TickedObject implements Ticked, Listener { - private static final Set LISTENER_INTROSPECTION_WARNED = ConcurrentHashMap.newKeySet(); - private static final Set FOLIA_TICK_VIOLATION_WARNED = ConcurrentHashMap.newKeySet(); - - private final AtomicLong lastTick; - private final AtomicLong interval; - private final AtomicInteger skip; - private final AtomicInteger burst; - private final AtomicLong ticks; - private final AtomicInteger dieIn; - private final AtomicBoolean die; - private final AtomicBoolean pendingSyncTick; - private final long start; - private final String group; - private final String id; - private final boolean listenerRegistered; - - public TickedObject() { - this("null"); + private static final Set LISTENER_INTROSPECTION_WARNED = ConcurrentHashMap.newKeySet(); + private static final Set FOLIA_TICK_VIOLATION_WARNED = ConcurrentHashMap.newKeySet(); + + private final AtomicLong lastTick; + private final AtomicLong interval; + private final AtomicInteger skip; + private final AtomicInteger burst; + private final AtomicLong ticks; + private final AtomicInteger dieIn; + private final AtomicBoolean die; + private final AtomicBoolean pendingSyncTick; + private final long start; + private final String group; + private final String id; + private final boolean listenerRegistered; + + public TickedObject() { + this("null"); + } + + public TickedObject(String group, String id) { + this(group, id, 1000); + } + + public TickedObject(String group) { + this(group, UUID.randomUUID().toString(), 1000); + } + + public TickedObject(String group, long interval) { + this(group, UUID.randomUUID().toString(), interval); + } + + public TickedObject(String group, String id, long interval) { + this.group = group; + this.id = id; + this.die = new AtomicBoolean(false); + this.dieIn = new AtomicInteger(0); + this.interval = new AtomicLong(interval); + this.lastTick = new AtomicLong(M.ms()); + this.burst = new AtomicInteger(0); + this.skip = new AtomicInteger(0); + this.ticks = new AtomicLong(0); + this.pendingSyncTick = new AtomicBoolean(false); + this.start = M.ms(); + this.listenerRegistered = shouldRegisterAsListener(); + Adapt.instance.getTicker().register(this); + if (listenerRegistered) { + Adapt.instance.registerListener(this); } + } + + private static boolean hasEventHandlerMethods(Class type) { + Class current = type; + while (current != null && current != Object.class) { + Method[] methods; + try { + methods = current.getDeclaredMethods(); + } catch (Throwable e) { + warnListenerIntrospectionFailure(current, e); + return false; + } - public TickedObject(String group, String id) { - this(group, id, 1000); - } - - public TickedObject(String group) { - this(group, UUID.randomUUID().toString(), 1000); - } - - public TickedObject(String group, long interval) { - this(group, UUID.randomUUID().toString(), interval); - } - - public TickedObject(String group, String id, long interval) { - this.group = group; - this.id = id; - this.die = new AtomicBoolean(false); - this.dieIn = new AtomicInteger(0); - this.interval = new AtomicLong(interval); - this.lastTick = new AtomicLong(M.ms()); - this.burst = new AtomicInteger(0); - this.skip = new AtomicInteger(0); - this.ticks = new AtomicLong(0); - this.pendingSyncTick = new AtomicBoolean(false); - this.start = M.ms(); - this.listenerRegistered = shouldRegisterAsListener(); - Adapt.instance.getTicker().register(this); - if (listenerRegistered) { - Adapt.instance.registerListener(this); - } - } - - public void dieAfter(int ticks) { - dieIn.set(ticks); - die.set(true); - } - - @Override - public void unregister() { - Adapt.instance.getTicker().unregister(this); - if (listenerRegistered) { - Adapt.instance.unregisterListener(this); - } - } - - @Override - public long getLastTick() { - return lastTick.get(); - } - - @Override - public long getInterval() { - if (burst.get() > 0) { - return 0; - } - - return interval.get(); - } - - @Override - public void setInterval(long ms) { - interval.set(ms); - } - - @Override - public void tick() { - if (!J.isPrimaryThread()) { - if (pendingSyncTick.compareAndSet(false, true)) { - J.s(() -> { - try { - tick(); - } finally { - pendingSyncTick.set(false); - } - }); - } - return; - } - - if (skip.getAndDecrement() > 0) { - return; - } - - if (die.get() && dieIn.decrementAndGet() <= 0) { - unregister(); - return; - } - - lastTick.set(M.ms()); - burst.decrementAndGet(); - try { - onTick(); - } catch (IllegalStateException ex) { - if (J.isFoliaThreading() && isFoliaThreadOwnershipViolation(ex)) { - warnFoliaTickViolation(ex); - return; - } - throw ex; - } catch (NullPointerException ex) { - if (J.isFoliaThreading() && isFoliaTransientWorldStateNpe(ex)) { - warnFoliaTickViolation(ex); - return; - } - throw ex; - } - } - - public abstract void onTick(); - - protected boolean shouldRegisterAsListener() { + for (Method method : methods) { try { - return hasEventHandlerMethods(getClass()); + if (method.isAnnotationPresent(EventHandler.class)) { + return true; + } } catch (Throwable e) { - warnListenerIntrospectionFailure(getClass(), e); - return false; + warnListenerIntrospectionFailure(current, e); + return false; } + } + current = current.getSuperclass(); } + return false; + } - @Override - public String getGroup() { - return group; + private static void warnListenerIntrospectionFailure(Class type, Throwable error) { + if (type == null) { + return; } - @Override - public String getId() { - return id; + String key = type.getName() + ":" + error.getClass().getName() + ":" + (error.getMessage() == null ? "" : error.getMessage()); + if (LISTENER_INTROSPECTION_WARNED.add(key)) { + Adapt.warn("Skipping listener registration for " + type.getName() + " due to missing/incompatible event class: " + error.getClass().getSimpleName() + (error.getMessage() == null ? "" : " (" + error.getMessage() + ")")); } - - @Override - public long getTickCount() { - return ticks.get(); + } + + public void dieAfter(int ticks) { + dieIn.set(ticks); + die.set(true); + } + + @Override + public void unregister() { + Adapt.instance.getTicker().unregister(this); + if (listenerRegistered) { + Adapt.instance.unregisterListener(this); } + } - @Override - public long getAge() { - return M.ms() - start; - } + @Override + public long getLastTick() { + return lastTick.get(); + } - @Override - public boolean isBursting() { - return burst.get() > 0; + @Override + public long getInterval() { + if (burst.get() > 0) { + return 0; } - @Override - public void burst(int ticks) { - if (burst.get() < 0) { - burst.set(ticks); - return; - } - - burst.addAndGet(ticks); + return interval.get(); + } + + @Override + public void setInterval(long ms) { + interval.set(ms); + } + + @Override + public void tick() { + if (!J.isPrimaryThread()) { + if (pendingSyncTick.compareAndSet(false, true)) { + J.s(() -> { + try { + tick(); + } finally { + pendingSyncTick.set(false); + } + }); + } + return; } - @Override - public boolean isSkipping() { - return skip.get() > 0; + if (skip.getAndDecrement() > 0) { + return; } - @Override - public void stopBursting() { - burst.set(0); + if (die.get() && dieIn.decrementAndGet() <= 0) { + unregister(); + return; } - @Override - public void stopSkipping() { - skip.set(0); + lastTick.set(M.ms()); + burst.decrementAndGet(); + try { + onTick(); + } catch (IllegalStateException ex) { + if (J.isFoliaThreading() && isFoliaThreadOwnershipViolation(ex)) { + warnFoliaTickViolation(ex); + return; + } + throw ex; + } catch (NullPointerException ex) { + if (J.isFoliaThreading() && isFoliaTransientWorldStateNpe(ex)) { + warnFoliaTickViolation(ex); + return; + } + throw ex; } + } - @Override - public void skip(int ticks) { - if (skip.get() < 0) { - skip.set(ticks); - return; - } + public abstract void onTick(); - skip.addAndGet(ticks); + protected boolean shouldRegisterAsListener() { + try { + return hasEventHandlerMethods(getClass()); + } catch (Throwable e) { + warnListenerIntrospectionFailure(getClass(), e); + return false; } - - private static boolean hasEventHandlerMethods(Class type) { - Class current = type; - while (current != null && current != Object.class) { - Method[] methods; - try { - methods = current.getDeclaredMethods(); - } catch (Throwable e) { - warnListenerIntrospectionFailure(current, e); - return false; - } - - for (Method method : methods) { - try { - if (method.isAnnotationPresent(EventHandler.class)) { - return true; - } - } catch (Throwable e) { - warnListenerIntrospectionFailure(current, e); - return false; - } - } - current = current.getSuperclass(); - } - return false; + } + + @Override + public String getGroup() { + return group; + } + + @Override + public String getId() { + return id; + } + + @Override + public long getTickCount() { + return ticks.get(); + } + + @Override + public long getAge() { + return M.ms() - start; + } + + @Override + public boolean isBursting() { + return burst.get() > 0; + } + + @Override + public void burst(int ticks) { + if (burst.get() < 0) { + burst.set(ticks); + return; } - private static void warnListenerIntrospectionFailure(Class type, Throwable error) { - if (type == null) { - return; - } - - String key = type.getName() + ":" + error.getClass().getName() + ":" + (error.getMessage() == null ? "" : error.getMessage()); - if (LISTENER_INTROSPECTION_WARNED.add(key)) { - Adapt.warn("Skipping listener registration for " + type.getName() + " due to missing/incompatible event class: " + error.getClass().getSimpleName() + (error.getMessage() == null ? "" : " (" + error.getMessage() + ")")); - } + burst.addAndGet(ticks); + } + + @Override + public boolean isSkipping() { + return skip.get() > 0; + } + + @Override + public void stopBursting() { + burst.set(0); + } + + @Override + public void stopSkipping() { + skip.set(0); + } + + @Override + public void skip(int ticks) { + if (skip.get() < 0) { + skip.set(ticks); + return; } - private boolean isFoliaThreadOwnershipViolation(Throwable throwable) { - if (throwable == null) { - return false; - } + skip.addAndGet(ticks); + } - String message = throwable.getMessage(); - if (message == null) { - return false; - } + private boolean isFoliaThreadOwnershipViolation(Throwable throwable) { + if (throwable == null) { + return false; + } - String lower = message.toLowerCase(Locale.ROOT); - return lower.contains("thread failed main thread check") - || lower.contains("cannot read world asynchronously") - || lower.contains("accessing entity state off owning region"); + String message = throwable.getMessage(); + if (message == null) { + return false; } - private boolean isFoliaTransientWorldStateNpe(NullPointerException throwable) { - if (throwable == null || throwable.getMessage() == null) { - return false; - } + String lower = message.toLowerCase(Locale.ROOT); + return lower.contains("thread failed main thread check") + || lower.contains("cannot read world asynchronously") + || lower.contains("accessing entity state off owning region"); + } - String lower = throwable.getMessage().toLowerCase(Locale.ROOT); - return lower.contains("getcurrentworlddata"); + private boolean isFoliaTransientWorldStateNpe(NullPointerException throwable) { + if (throwable == null || throwable.getMessage() == null) { + return false; } - private void warnFoliaTickViolation(Throwable throwable) { - String message = throwable == null || throwable.getMessage() == null ? throwable.getClass().getSimpleName() : throwable.getMessage(); - String key = getClass().getName() + ":" + throwable.getClass().getName() + ":" + message; - if (FOLIA_TICK_VIOLATION_WARNED.add(key)) { - Adapt.warn("Suppressed unsafe Folia tick execution in " + getClass().getName() + ": " + message); - } + String lower = throwable.getMessage().toLowerCase(Locale.ROOT); + return lower.contains("getcurrentworlddata"); + } + + private void warnFoliaTickViolation(Throwable throwable) { + String message = throwable == null || throwable.getMessage() == null ? throwable.getClass().getSimpleName() : throwable.getMessage(); + String key = getClass().getName() + ":" + throwable.getClass().getName() + ":" + message; + if (FOLIA_TICK_VIOLATION_WARNED.add(key)) { + Adapt.warn("Suppressed unsafe Folia tick execution in " + getClass().getName() + ": " + message); } + } } diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java index 1da60778f..9423b54ca 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -21,168 +21,164 @@ import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.collection.KList; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; public class Ticker { - private final KList ticklist; - private final KList newTicks; - private final KList removeTicks; - private final Map metrics; - private final AtomicLong windowStartMs; - private volatile boolean ticking; - - public Ticker() { - this.ticklist = new KList<>(4096); - this.newTicks = new KList<>(128); - this.removeTicks = new KList<>(128); - this.metrics = new ConcurrentHashMap<>(); - this.windowStartMs = new AtomicLong(System.currentTimeMillis()); - ticking = false; - J.sr(() -> { - if (!ticking) { - tick(); - } - }, 1); + private final KList ticklist; + private final KList newTicks; + private final KList removeTicks; + private final Map metrics; + private final AtomicLong windowStartMs; + private volatile boolean ticking; + + public Ticker() { + this.ticklist = new KList<>(4096); + this.newTicks = new KList<>(128); + this.removeTicks = new KList<>(128); + this.metrics = new ConcurrentHashMap<>(); + this.windowStartMs = new AtomicLong(System.currentTimeMillis()); + ticking = false; + J.sr(() -> { + if (!ticking) { + tick(); + } + }, 1); + } + + public void register(Ticked ticked) { + synchronized (newTicks) { + newTicks.add(ticked); } + } - public void register(Ticked ticked) { - synchronized (newTicks) { - newTicks.add(ticked); - } + public void unregister(Ticked ticked) { + synchronized (removeTicks) { + removeTicks.add(ticked.getId()); } + } - public void unregister(Ticked ticked) { - synchronized (removeTicks) { - removeTicks.add(ticked.getId()); - } + public void clear() { + synchronized (ticklist) { + ticklist.clear(); } - - public void clear() { - synchronized (ticklist) { - ticklist.clear(); - } - synchronized (removeTicks) { - removeTicks.clear(); - } - synchronized (newTicks) { - newTicks.clear(); - } - metrics.clear(); - windowStartMs.set(System.currentTimeMillis()); - + synchronized (removeTicks) { + removeTicks.clear(); } - - public void resetMetrics() { - metrics.clear(); - windowStartMs.set(System.currentTimeMillis()); + synchronized (newTicks) { + newTicks.clear(); } + metrics.clear(); + windowStartMs.set(System.currentTimeMillis()); - public long getMetricsWindowMs() { - return Math.max(0, System.currentTimeMillis() - windowStartMs.get()); - } + } - public double getWindowLoadPercent() { - long windowMs = getMetricsWindowMs(); - if (windowMs <= 0L) { - return 0D; - } + public void resetMetrics() { + metrics.clear(); + windowStartMs.set(System.currentTimeMillis()); + } - double totalMs = 0D; - for (TickMetric metric : metrics.values()) { - totalMs += metric.totalNanos.get() / 1_000_000D; - } - double percent = (totalMs / (double) windowMs) * 100D; - if (!Double.isFinite(percent)) { - return 0D; - } + public long getMetricsWindowMs() { + return Math.max(0, System.currentTimeMillis() - windowStartMs.get()); + } - return Math.max(0D, percent); + public double getWindowLoadPercent() { + long windowMs = getMetricsWindowMs(); + if (windowMs <= 0L) { + return 0D; } - public List topMetrics(int limit) { - int safeLimit = Math.max(1, limit); - ArrayList> entries = new ArrayList<>(metrics.entrySet()); - entries.sort(Comparator.comparingLong((Map.Entry e) -> e.getValue().totalNanos.get()).reversed()); - - int outputSize = Math.min(safeLimit, entries.size()); - ArrayList top = new ArrayList<>(outputSize); - for (int i = 0; i < outputSize; i++) { - Map.Entry entry = entries.get(i); - top.add(formatMetric(entry.getKey(), entry.getValue())); - } - return top; + double totalMs = 0D; + for (TickMetric metric : metrics.values()) { + totalMs += metric.totalNanos.get() / 1_000_000D; + } + double percent = (totalMs / (double) windowMs) * 100D; + if (!Double.isFinite(percent)) { + return 0D; } - private void tick() { - ticking = true; - for (int i = 0; i < ticklist.size(); i++) { - Ticked t = ticklist.get(i); - if (t != null && t.shouldTick()) { - long start = System.nanoTime(); - try { - t.tick(); - } catch (Throwable exxx) { - exxx.printStackTrace(); - } finally { - recordMetric(t, System.nanoTime() - start); - } - } - } + return Math.max(0D, percent); + } - synchronized (newTicks) { - while (newTicks.isNotEmpty()) { - ticklist.add(newTicks.popRandom()); - } - } + public List topMetrics(int limit) { + int safeLimit = Math.max(1, limit); + ArrayList> entries = new ArrayList<>(metrics.entrySet()); + entries.sort(Comparator.comparingLong((Map.Entry e) -> e.getValue().totalNanos.get()).reversed()); - synchronized (removeTicks) { - while (removeTicks.isNotEmpty()) { - String id = removeTicks.popRandom(); - - for (int i = 0; i < ticklist.size(); i++) { - if (ticklist.get(i).getId().equals(id)) { - ticklist.remove(i); - break; - } - } - } + int outputSize = Math.min(safeLimit, entries.size()); + ArrayList top = new ArrayList<>(outputSize); + for (int i = 0; i < outputSize; i++) { + Map.Entry entry = entries.get(i); + top.add(formatMetric(entry.getKey(), entry.getValue())); + } + return top; + } + + private void tick() { + ticking = true; + for (int i = 0; i < ticklist.size(); i++) { + Ticked t = ticklist.get(i); + if (t != null && t.shouldTick()) { + long start = System.nanoTime(); + try { + t.tick(); + } catch (Throwable exxx) { + exxx.printStackTrace(); + } finally { + recordMetric(t, System.nanoTime() - start); } + } + } - ticking = false; + synchronized (newTicks) { + while (newTicks.isNotEmpty()) { + ticklist.add(newTicks.popRandom()); + } } - private void recordMetric(Ticked ticked, long durationNs) { - if (ticked == null || durationNs < 0) { - return; - } + synchronized (removeTicks) { + while (removeTicks.isNotEmpty()) { + String id = removeTicks.popRandom(); - String key = ticked.getGroup() + ":" + ticked.getId(); - TickMetric metric = metrics.computeIfAbsent(key, unused -> new TickMetric()); - metric.calls.incrementAndGet(); - metric.totalNanos.addAndGet(durationNs); - metric.maxNanos.updateAndGet(old -> Math.max(old, durationNs)); + for (int i = 0; i < ticklist.size(); i++) { + if (ticklist.get(i).getId().equals(id)) { + ticklist.remove(i); + break; + } + } + } } - private String formatMetric(String key, TickMetric metric) { - long calls = Math.max(1, metric.calls.get()); - double totalMs = metric.totalNanos.get() / 1_000_000D; - double avgMs = totalMs / (double) calls; - double maxMs = metric.maxNanos.get() / 1_000_000D; - return key + " total=" + String.format(Locale.US, "%.3fms", totalMs) - + " avg=" + String.format(Locale.US, "%.3fms", avgMs) - + " max=" + String.format(Locale.US, "%.3fms", maxMs) - + " calls=" + calls; - } + ticking = false; + } - private static class TickMetric { - private final AtomicLong calls = new AtomicLong(); - private final AtomicLong totalNanos = new AtomicLong(); - private final AtomicLong maxNanos = new AtomicLong(); + private void recordMetric(Ticked ticked, long durationNs) { + if (ticked == null || durationNs < 0) { + return; } + + String key = ticked.getGroup() + ":" + ticked.getId(); + TickMetric metric = metrics.computeIfAbsent(key, unused -> new TickMetric()); + metric.calls.incrementAndGet(); + metric.totalNanos.addAndGet(durationNs); + metric.maxNanos.updateAndGet(old -> Math.max(old, durationNs)); + } + + private String formatMetric(String key, TickMetric metric) { + long calls = Math.max(1, metric.calls.get()); + double totalMs = metric.totalNanos.get() / 1_000_000D; + double avgMs = totalMs / (double) calls; + double maxMs = metric.maxNanos.get() / 1_000_000D; + return key + " total=" + String.format(Locale.US, "%.3fms", totalMs) + + " avg=" + String.format(Locale.US, "%.3fms", avgMs) + + " max=" + String.format(Locale.US, "%.3fms", maxMs) + + " calls=" + calls; + } + + private static class TickMetric { + private final AtomicLong calls = new AtomicLong(); + private final AtomicLong totalNanos = new AtomicLong(); + private final AtomicLong maxNanos = new AtomicLong(); + } } diff --git a/src/main/java/art/arcane/adapt/api/value/MaterialCount.java b/src/main/java/art/arcane/adapt/api/value/MaterialCount.java index aac3d4216..da2bc414e 100644 --- a/src/main/java/art/arcane/adapt/api/value/MaterialCount.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialCount.java @@ -25,6 +25,6 @@ @AllArgsConstructor @Data public class MaterialCount { - private Material material; - private int amount; + private Material material; + private int amount; } diff --git a/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java b/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java index 5673a04f0..da30c1627 100644 --- a/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialRecipe.java @@ -28,6 +28,6 @@ @Builder @Data public class MaterialRecipe { - private List input; - private MaterialCount output; + private List input; + private MaterialCount output; } diff --git a/src/main/java/art/arcane/adapt/api/value/MaterialValue.java b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java index cfd6fa24c..82711893e 100644 --- a/src/main/java/art/arcane/adapt/api/value/MaterialValue.java +++ b/src/main/java/art/arcane/adapt/api/value/MaterialValue.java @@ -36,221 +36,221 @@ @Getter public class MaterialValue { - private static final Map valueMultipliers = new HashMap<>(); - private static MaterialValue valueCache = null; + private static final Map valueMultipliers = new HashMap<>(); + private static MaterialValue valueCache = null; - static { - AdaptConfig.get().getValue().getValueMutlipliers().forEach((k, v) -> { - try { - Material m = Material.valueOf(k.toUpperCase()); - valueMultipliers.put(m, v); - } catch (Exception e) { - Adapt.verbose("Invalid material value multiplier: " + k); - } - }); + static { + AdaptConfig.get().getValue().getValueMutlipliers().forEach((k, v) -> { + try { + Material m = Material.valueOf(k.toUpperCase()); + valueMultipliers.put(m, v); + } catch (Exception e) { + Adapt.verbose("Invalid material value multiplier: " + k); + } + }); + } + + private final Map value = new HashMap<>(); + + public static void save() { + if (valueCache == null) { + return; } - private final Map value = new HashMap<>(); + File l = Adapt.instance.getDataFile("data", "value-cache.json"); + try { + IO.writeAll(l, Json.toJson(valueCache, true)); + } catch (IOException e) { + Adapt.verbose("Failed to save value cache"); + } + } - public static void save() { - if (valueCache == null) { - return; - } + public static MaterialValue get() { + if (valueCache == null) { + MaterialValue dummy = new MaterialValue(); + File l = Adapt.instance.getDataFile("data", "value-cache.json"); - File l = Adapt.instance.getDataFile("data", "value-cache.json"); + if (!l.exists()) { try { - IO.writeAll(l, Json.toJson(valueCache, true)); + IO.writeAll(l, Json.toJson(dummy, true)); } catch (IOException e) { - Adapt.verbose("Failed to save value cache"); + e.printStackTrace(); + valueCache = dummy; + return dummy; } + } + + try { + valueCache = Json.fromJson(IO.readAll(l), MaterialValue.class); + } catch (IOException e) { + e.printStackTrace(); + valueCache = new MaterialValue(); + } } - public static MaterialValue get() { - if (valueCache == null) { - MaterialValue dummy = new MaterialValue(); - File l = Adapt.instance.getDataFile("data", "value-cache.json"); + return valueCache; + } - if (!l.exists()) { - try { - IO.writeAll(l, Json.toJson(dummy, true)); - } catch (IOException e) { - e.printStackTrace(); - valueCache = dummy; - return dummy; - } - } + public static void debugValue(Material m) { + debugValue(m, 0, 1, new HashSet<>()); + } - try { - valueCache = Json.fromJson(IO.readAll(l), MaterialValue.class); - } catch (IOException e) { - e.printStackTrace(); - valueCache = new MaterialValue(); - } - } + private static void debugValue(Material m, int ind, int x, Set ignore) { + PrecisionStopwatch p = PrecisionStopwatch.start(); + Adapt.verbose(Form.repeat(" ", ind) + m.name() + ": " + getValue(m) + (x == 1 ? "" : " (x" + x + ")")); - return valueCache; - } + int r = 0; + for (MaterialRecipe i : getRecipes(m)) { + if (ignore.contains(i)) { + continue; + } - public static void debugValue(Material m) { - debugValue(m, 0, 1, new HashSet<>()); - } - - private static void debugValue(Material m, int ind, int x, Set ignore) { - PrecisionStopwatch p = PrecisionStopwatch.start(); - Adapt.verbose(Form.repeat(" ", ind) + m.name() + ": " + getValue(m) + (x == 1 ? "" : " (x" + x + ")")); + ignore.add(i); + if (ignore.size() > AdaptConfig.get().getMaxRecipeListPrecaution()) { + Adapt.verbose("Avoiding infinite loop"); + return; + } - int r = 0; - for (MaterialRecipe i : getRecipes(m)) { - if (ignore.contains(i)) { - continue; - } + int o = i.getOutput().getAmount(); + Adapt.verbose(Form.repeat(" ", ind) + "# Recipe [" + ind + "x" + r + (o == 1 ? "]" : "] (x" + o + ") ")); - ignore.add(i); - if (ignore.size() > AdaptConfig.get().getMaxRecipeListPrecaution()) { - Adapt.verbose("Avoiding infinite loop"); - return; - } + for (MaterialCount j : i.getInput()) { + debugValue(j.getMaterial(), ind + 1, j.getAmount(), ignore); + } - int o = i.getOutput().getAmount(); - Adapt.verbose(Form.repeat(" ", ind) + "# Recipe [" + ind + "x" + r + (o == 1 ? "]" : "] (x" + o + ") ")); + r++; + } + Adapt.verbose(Form.repeat(" ", ind) + " took " + Form.duration(p.getMilliseconds(), 0)); + } - for (MaterialCount j : i.getInput()) { - debugValue(j.getMaterial(), ind + 1, j.getAmount(), ignore); - } + private static double getMultiplier(Material m) { + Double d = AdaptConfig.get().getValue().getValueMutlipliers().get(m); + return d == null ? 1 : d; + } - r++; - } - Adapt.verbose(Form.repeat(" ", ind) + " took " + Form.duration(p.getMilliseconds(), 0)); + public static double getValue(Material m) { + try { + return getValue(m, new HashSet<>()); + } catch (Exception ignored) { + return 1; } + } - private static double getMultiplier(Material m) { - Double d = AdaptConfig.get().getValue().getValueMutlipliers().get(m); - return d == null ? 1 : d; + private static double getValue(Material m, Set ignore) { + if (get().value.containsKey(m)) { + if (m.isBlock() && m.getHardness() == 0) { + return 0; + } + return get().value.get(m); } - - public static double getValue(Material m) { - try { - return getValue(m, new HashSet<>()); - } catch (Exception ignored) { - return 1; + double v = AdaptConfig.get().getValue().getBaseValue(); + List recipes = getRecipes(m); + if (recipes.isEmpty()) { + get().value.put(m, v * getMultiplier(m)); // No recipes, just use base value, if no base value then 1 + } else { + List d = new ArrayList<>(); + for (MaterialRecipe i : recipes) { + if (ignore.contains(i)) { + continue; } - } - - private static double getValue(Material m, Set ignore) { - if (get().value.containsKey(m)) { - if (m.isBlock() && m.getHardness() == 0) { - return 0; - } - return get().value.get(m); + ignore.add(i); + double vx = v; + for (MaterialCount j : i.getInput()) { + vx += getValue(j.getMaterial(), ignore); } - double v = AdaptConfig.get().getValue().getBaseValue(); - List recipes = getRecipes(m); - if (recipes.isEmpty()) { - get().value.put(m, v * getMultiplier(m)); // No recipes, just use base value, if no base value then 1 - } else { - List d = new ArrayList<>(); - for (MaterialRecipe i : recipes) { - if (ignore.contains(i)) { - continue; - } - ignore.add(i); - double vx = v; - for (MaterialCount j : i.getInput()) { - vx += getValue(j.getMaterial(), ignore); - } - d.add(vx / i.getOutput().getAmount()); - } - if (d.size() > 0) { - v += d.stream().mapToDouble(i -> i).average().getAsDouble(); - } - if (v > AdaptConfig.get().getMaxRecipeListPrecaution()) { - get().value.put(m, (v / 10 + 1) * getMultiplier(m)); - } else { - get().value.put(m, v); - } + d.add(vx / i.getOutput().getAmount()); + } + if (d.size() > 0) { + v += d.stream().mapToDouble(i -> i).average().getAsDouble(); + } + if (v > AdaptConfig.get().getMaxRecipeListPrecaution()) { + get().value.put(m, (v / 10 + 1) * getMultiplier(m)); + } else { + get().value.put(m, v); + } - } - if (m.isBlock() && m.getHardness() == 0) { - return 0; - } - return get().value.get(m); } + if (m.isBlock() && m.getHardness() == 0) { + return 0; + } + return get().value.get(m); + } - private static List getRecipes(Material mat) { - List r = new ArrayList<>(); - try { - ItemStack is = new ItemStack(mat); - try { - is.setDurability((short) -1); - } catch (Throwable e) { - Adapt.verbose("Failed to set durability of " + mat.name()); - } - Bukkit.getRecipesFor(is).forEach(i -> { - if (i instanceof AdaptRecipe) { - Adapt.verbose("Skipping Adapt Recipe to prevent duplicates, " + mat.name() + " -> " + ((AdaptRecipe) i).getKey() + ""); - return; - } - MaterialRecipe rx = toMaterial(i); - if (rx != null) { - r.add(rx); - } - }); - } catch (Throwable e) { - Adapt.verbose("Failed to get recipes for " + mat.name()); + private static List getRecipes(Material mat) { + List r = new ArrayList<>(); + try { + ItemStack is = new ItemStack(mat); + try { + is.setDurability((short) -1); + } catch (Throwable e) { + Adapt.verbose("Failed to set durability of " + mat.name()); + } + Bukkit.getRecipesFor(is).forEach(i -> { + if (i instanceof AdaptRecipe) { + Adapt.verbose("Skipping Adapt Recipe to prevent duplicates, " + mat.name() + " -> " + ((AdaptRecipe) i).getKey() + ""); + return; } - return r; + MaterialRecipe rx = toMaterial(i); + if (rx != null) { + r.add(rx); + } + }); + } catch (Throwable e) { + Adapt.verbose("Failed to get recipes for " + mat.name()); } + return r; + } - private static MaterialRecipe toMaterial(Recipe r) { - try { - if (r instanceof ShapelessRecipe recipe) { - return MaterialRecipe.builder() - .input(new ArrayList<>(recipe.getIngredientList().stream().map(i -> new MaterialCount(i.getType(), 1)).toList())) - .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) - .build(); - } else if (r instanceof ShapedRecipe recipe) { - MaterialRecipe re = MaterialRecipe.builder() - .input(new ArrayList<>()) - .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) - .build(); - Map f = new HashMap<>(); - for (ItemStack i : recipe.getIngredientMap().values()) { - if (i == null || i.getType().isAir()) { - continue; - } - - f.compute(i.getType(), (k, v) -> v == null ? 1 : v + 1); - } + private static MaterialRecipe toMaterial(Recipe r) { + try { + if (r instanceof ShapelessRecipe recipe) { + return MaterialRecipe.builder() + .input(new ArrayList<>(recipe.getIngredientList().stream().map(i -> new MaterialCount(i.getType(), 1)).toList())) + .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) + .build(); + } else if (r instanceof ShapedRecipe recipe) { + MaterialRecipe re = MaterialRecipe.builder() + .input(new ArrayList<>()) + .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) + .build(); + Map f = new HashMap<>(); + for (ItemStack i : recipe.getIngredientMap().values()) { + if (i == null || i.getType().isAir()) { + continue; + } - f.forEach((k, v) -> re.getInput().add(new MaterialCount(k, v))); + f.compute(i.getType(), (k, v) -> v == null ? 1 : v + 1); + } - return re; - } else if (r instanceof CookingRecipe recipe) { - List a = new ArrayList<>(); - a.add(new MaterialCount(recipe.getInput().getType(), 1)); + f.forEach((k, v) -> re.getInput().add(new MaterialCount(k, v))); - return MaterialRecipe.builder() - .input(a) - .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) - .build(); - } else if (r instanceof MerchantRecipe recipe) { - return MaterialRecipe.builder() - .input(new ArrayList<>(recipe.getIngredients().stream().map(i -> new MaterialCount(i.getType(), 1)).toList())) - .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) - .build(); - } else if (r instanceof StonecuttingRecipe recipe) { - List a = new ArrayList<>(); - a.add(new MaterialCount(recipe.getInput().getType(), 1)); + return re; + } else if (r instanceof CookingRecipe recipe) { + List a = new ArrayList<>(); + a.add(new MaterialCount(recipe.getInput().getType(), 1)); - return MaterialRecipe.builder() - .input(a) - .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) - .build(); - } - } catch (Throwable e) { - e.printStackTrace(); - } + return MaterialRecipe.builder() + .input(a) + .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) + .build(); + } else if (r instanceof MerchantRecipe recipe) { + return MaterialRecipe.builder() + .input(new ArrayList<>(recipe.getIngredients().stream().map(i -> new MaterialCount(i.getType(), 1)).toList())) + .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) + .build(); + } else if (r instanceof StonecuttingRecipe recipe) { + List a = new ArrayList<>(); + a.add(new MaterialCount(recipe.getInput().getType(), 1)); - return null; + return MaterialRecipe.builder() + .input(a) + .output(new MaterialCount(recipe.getResult().getType(), recipe.getResult().getAmount())) + .build(); + } + } catch (Throwable e) { + e.printStackTrace(); } + + return null; + } } diff --git a/src/main/java/art/arcane/adapt/api/version/IAttribute.java b/src/main/java/art/arcane/adapt/api/version/IAttribute.java index 819f45e15..0adcfd3fd 100644 --- a/src/main/java/art/arcane/adapt/api/version/IAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/IAttribute.java @@ -13,44 +13,44 @@ public interface IAttribute { - double getValue(); + double getValue(); - double getDefaultValue(); + double getDefaultValue(); - double getBaseValue(); + double getBaseValue(); - void setBaseValue(double baseValue); + void setBaseValue(double baseValue); - default void setModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { - removeModifier(uuid, key); - addModifier(uuid, key, amount, operation); - } + default void setModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { + removeModifier(uuid, key); + addModifier(uuid, key, amount, operation); + } - void addModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation); + void addModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation); - boolean hasModifier(UUID uuid, NamespacedKey key); + boolean hasModifier(UUID uuid, NamespacedKey key); - void removeModifier(UUID uuid, NamespacedKey key); + void removeModifier(UUID uuid, NamespacedKey key); - KList getModifier(UUID uuid, NamespacedKey key); + KList getModifier(UUID uuid, NamespacedKey key); - @ToString - @EqualsAndHashCode - @AllArgsConstructor - class Modifier { - private final UUID uuid; - private final NamespacedKey key; - @Getter - private final double amount; - @Getter - private final AttributeModifier.Operation operation; + @ToString + @EqualsAndHashCode + @AllArgsConstructor + class Modifier { + private final UUID uuid; + private final NamespacedKey key; + @Getter + private final double amount; + @Getter + private final AttributeModifier.Operation operation; - public Optional getUUID() { - return Optional.ofNullable(uuid); - } + public Optional getUUID() { + return Optional.ofNullable(uuid); + } - public Optional getKey() { - return Optional.ofNullable(key); - } + public Optional getKey() { + return Optional.ofNullable(key); } + } } diff --git a/src/main/java/art/arcane/adapt/api/version/IBindings.java b/src/main/java/art/arcane/adapt/api/version/IBindings.java index a0b6a2f58..30961ee12 100644 --- a/src/main/java/art/arcane/adapt/api/version/IBindings.java +++ b/src/main/java/art/arcane/adapt/api/version/IBindings.java @@ -17,60 +17,61 @@ public interface IBindings extends Listener { - default void applyModel(CustomModel model, ItemMeta meta) { - meta.setCustomModelData(model.model()); - } - - IAttribute getAttribute(Attributable attributable, Attribute modifier); + default void applyModel(CustomModel model, ItemMeta meta) { + meta.setCustomModelData(model.model()); + } - default ItemStack buildPotion(PotionBuilder builder) { - ItemStack stack = builder.getBaseItem(); - if (stack == null) stack = new ItemStack(builder.getType().getMaterial()); - else if (stack.getType() != builder.getType().getMaterial()) stack.setType(builder.getType().getMaterial()); - PotionMeta meta = (PotionMeta) stack.getItemMeta(); - assert meta != null; - meta.clearCustomEffects(); - builder.getEffects().forEach(e -> meta.addCustomEffect(e, true)); - if (builder.getColor() != null) - meta.setColor(builder.getColor()); - stack.setItemMeta(meta); + IAttribute getAttribute(Attributable attributable, Attribute modifier); - Adapt.platform.editItem(stack) - .lore(builder.getLore()) - .customName(builder.getName()) - .build(); - return stack; - } + default ItemStack buildPotion(PotionBuilder builder) { + ItemStack stack = builder.getBaseItem(); + if (stack == null) stack = new ItemStack(builder.getType().getMaterial()); + else if (stack.getType() != builder.getType().getMaterial()) + stack.setType(builder.getType().getMaterial()); + PotionMeta meta = (PotionMeta) stack.getItemMeta(); + assert meta != null; + meta.clearCustomEffects(); + builder.getEffects().forEach(e -> meta.addCustomEffect(e, true)); + if (builder.getColor() != null) + meta.setColor(builder.getColor()); + stack.setItemMeta(meta); - default PotionBuilder editPotion(ItemStack stack) { - Type type = null; - for (final var val : Type.values()) { - if (val.getMaterial() == stack.getType()) { - type = val; - break; - } - } + Adapt.platform.editItem(stack) + .lore(builder.getLore()) + .customName(builder.getName()) + .build(); + return stack; + } - if (type == null) { - throw new IllegalArgumentException("Invalid potion type!"); - } - final var editor = Adapt.platform.editItem(stack); - final var builder = PotionBuilder.of(type) - .setBaseItem(stack); + default PotionBuilder editPotion(ItemStack stack) { + Type type = null; + for (final art.arcane.adapt.api.potion.PotionBuilder.Type val : Type.values()) { + if (val.getMaterial() == stack.getType()) { + type = val; + break; + } + } - PotionMeta meta = (PotionMeta) stack.getItemMeta(); - assert meta != null; - builder.setBaseType(meta.getBasePotionType()) - .setLore(editor.lore()) - .setColor(meta.getColor()) - .setName(editor.customName()); - for (var effect : meta.getCustomEffects()) { - builder.addEffect(effect); - } + if (type == null) { + throw new IllegalArgumentException("Invalid potion type!"); + } + final de.crazydev22.platformutils.ItemEditor editor = Adapt.platform.editItem(stack); + final art.arcane.adapt.api.potion.PotionBuilder builder = PotionBuilder.of(type) + .setBaseItem(stack); - return builder; + PotionMeta meta = (PotionMeta) stack.getItemMeta(); + assert meta != null; + builder.setBaseType(meta.getBasePotionType()) + .setLore(editor.lore()) + .setColor(meta.getColor()) + .setName(editor.customName()); + for (org.bukkit.potion.PotionEffect effect : meta.getCustomEffects()) { + builder.addEffect(effect); } - @Unmodifiable - List getInvalidDamageableEntities(); + return builder; + } + + @Unmodifiable + List getInvalidDamageableEntities(); } diff --git a/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java index baf8f272f..718cc510e 100644 --- a/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java +++ b/src/main/java/art/arcane/adapt/api/version/RuntimeAttribute.java @@ -11,239 +11,240 @@ import java.util.List; import java.util.UUID; -public record RuntimeAttribute(AttributeInstance instance) implements IAttribute { - private static final Method GET_KEY_METHOD = findMethod("getKey"); - private static final Method GET_UUID_METHOD = findMethod("getUniqueId"); - private static final Method GET_NAME_METHOD = findMethod("getName"); +public record RuntimeAttribute( + AttributeInstance instance) implements IAttribute { + private static final Method GET_KEY_METHOD = findMethod("getKey"); + private static final Method GET_UUID_METHOD = findMethod("getUniqueId"); + private static final Method GET_NAME_METHOD = findMethod("getName"); - @Override - public double getValue() { - return instance.getValue(); - } - - @Override - public double getDefaultValue() { - return instance.getDefaultValue(); - } - - @Override - public double getBaseValue() { - return instance.getBaseValue(); + private static AttributeModifier createModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { + for (Constructor constructor : AttributeModifier.class.getConstructors()) { + AttributeModifier modifier = tryCreateKeyed(constructor, key, amount, operation); + if (modifier != null) { + return modifier; + } } - @Override - public void setBaseValue(double baseValue) { - instance.setBaseValue(baseValue); - } - - @Override - public void addModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { - instance.addModifier(createModifier(uuid, key, amount, operation)); - } - - @Override - public boolean hasModifier(UUID uuid, NamespacedKey key) { - for (AttributeModifier modifier : instance.getModifiers()) { - if (matches(modifier, uuid, key)) { - return true; - } - } - - return false; - } - - @Override - public void removeModifier(UUID uuid, NamespacedKey key) { - List toRemove = null; - for (AttributeModifier modifier : instance.getModifiers()) { - if (!matches(modifier, uuid, key)) { - continue; - } - - if (toRemove == null) { - toRemove = new ArrayList<>(); - } - toRemove.add(modifier); - } - - if (toRemove == null) { - return; - } - - for (AttributeModifier modifier : toRemove) { - instance.removeModifier(modifier); - } - } - - @Override - public KList getModifier(UUID uuid, NamespacedKey key) { - KList modifiers = new KList<>(); - for (AttributeModifier modifier : instance.getModifiers()) { - if (matches(modifier, uuid, key)) { - modifiers.add(wrap(modifier)); - } - } - return modifiers; - } - - private static AttributeModifier createModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { - for (Constructor constructor : AttributeModifier.class.getConstructors()) { - AttributeModifier modifier = tryCreateKeyed(constructor, key, amount, operation); - if (modifier != null) { - return modifier; - } - } - - String legacyName = key.getNamespace() + "-" + key.getKey(); - for (Constructor constructor : AttributeModifier.class.getConstructors()) { - AttributeModifier modifier = tryCreateLegacy(constructor, uuid, legacyName, amount, operation); - if (modifier != null) { - return modifier; - } - } - - throw new IllegalStateException("No compatible AttributeModifier constructor found"); - } - - private static AttributeModifier tryCreateKeyed(Constructor constructor, NamespacedKey key, double amount, AttributeModifier.Operation operation) { - Class[] params = constructor.getParameterTypes(); - if (params.length < 3 || params.length > 4 || params[0] != NamespacedKey.class || params[1] != double.class || params[2] != AttributeModifier.Operation.class) { - return null; - } - - Object[] args = new Object[params.length]; - args[0] = key; - args[1] = amount; - args[2] = operation; - if (params.length == 4) { - Object slot = resolveEnum(params[3]); - if (slot == null) { - return null; - } - args[3] = slot; - } - - try { - return (AttributeModifier) constructor.newInstance(args); - } catch (ReflectiveOperationException ignored) { - return null; - } - } - - private static AttributeModifier tryCreateLegacy(Constructor constructor, UUID uuid, String name, double amount, AttributeModifier.Operation operation) { - Class[] params = constructor.getParameterTypes(); - if (params.length < 4 || params.length > 5 || params[0] != UUID.class || params[1] != String.class || params[2] != double.class || params[3] != AttributeModifier.Operation.class) { - return null; - } - - Object[] args = new Object[params.length]; - args[0] = uuid; - args[1] = name; - args[2] = amount; - args[3] = operation; - if (params.length == 5) { - Object slot = resolveEnum(params[4]); - if (slot == null) { - return null; - } - args[4] = slot; - } - - try { - return (AttributeModifier) constructor.newInstance(args); - } catch (ReflectiveOperationException ignored) { - return null; - } - } - - private static boolean matches(AttributeModifier modifier, UUID uuid, NamespacedKey key) { - NamespacedKey modifierKey = readKey(modifier); - if (modifierKey != null && modifierKey.equals(key)) { - return true; - } - - UUID modifierUuid = readUuid(modifier); - if (modifierUuid != null && modifierUuid.equals(uuid)) { - return true; - } - - String modifierName = readName(modifier); - return modifierName != null && modifierName.equals(key.getNamespace() + "-" + key.getKey()); - } - - private static Modifier wrap(AttributeModifier modifier) { - return new Modifier(readUuid(modifier), readKey(modifier), modifier.getAmount(), modifier.getOperation()); - } - - private static NamespacedKey readKey(AttributeModifier modifier) { - if (GET_KEY_METHOD == null) { - return null; - } - - try { - return (NamespacedKey) GET_KEY_METHOD.invoke(modifier); - } catch (ReflectiveOperationException ignored) { - return null; - } - } - - private static UUID readUuid(AttributeModifier modifier) { - if (GET_UUID_METHOD == null) { - return null; - } - - try { - return (UUID) GET_UUID_METHOD.invoke(modifier); - } catch (ReflectiveOperationException ignored) { - return null; - } - } - - private static String readName(AttributeModifier modifier) { - if (GET_NAME_METHOD == null) { - return null; - } - - try { - return (String) GET_NAME_METHOD.invoke(modifier); - } catch (ReflectiveOperationException ignored) { - return null; - } - } - - private static Method findMethod(String methodName) { - try { - return AttributeModifier.class.getMethod(methodName); - } catch (NoSuchMethodException ignored) { - return null; - } - } - - private static Object resolveEnum(Class type) { - if (!type.isEnum()) { - return null; - } - - Object any = enumConstant(type, "ANY"); - if (any != null) { - return any; - } - - Object hand = enumConstant(type, "HAND"); - if (hand != null) { - return hand; - } - - Object[] constants = type.getEnumConstants(); - return constants == null || constants.length == 0 ? null : constants[0]; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static Object enumConstant(Class type, String name) { - try { - return Enum.valueOf((Class) type, name); - } catch (IllegalArgumentException ignored) { - return null; - } + String legacyName = key.getNamespace() + "-" + key.getKey(); + for (Constructor constructor : AttributeModifier.class.getConstructors()) { + AttributeModifier modifier = tryCreateLegacy(constructor, uuid, legacyName, amount, operation); + if (modifier != null) { + return modifier; + } } + + throw new IllegalStateException("No compatible AttributeModifier constructor found"); + } + + private static AttributeModifier tryCreateKeyed(Constructor constructor, NamespacedKey key, double amount, AttributeModifier.Operation operation) { + Class[] params = constructor.getParameterTypes(); + if (params.length < 3 || params.length > 4 || params[0] != NamespacedKey.class || params[1] != double.class || params[2] != AttributeModifier.Operation.class) { + return null; + } + + Object[] args = new Object[params.length]; + args[0] = key; + args[1] = amount; + args[2] = operation; + if (params.length == 4) { + Object slot = resolveEnum(params[3]); + if (slot == null) { + return null; + } + args[3] = slot; + } + + try { + return (AttributeModifier) constructor.newInstance(args); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + private static AttributeModifier tryCreateLegacy(Constructor constructor, UUID uuid, String name, double amount, AttributeModifier.Operation operation) { + Class[] params = constructor.getParameterTypes(); + if (params.length < 4 || params.length > 5 || params[0] != UUID.class || params[1] != String.class || params[2] != double.class || params[3] != AttributeModifier.Operation.class) { + return null; + } + + Object[] args = new Object[params.length]; + args[0] = uuid; + args[1] = name; + args[2] = amount; + args[3] = operation; + if (params.length == 5) { + Object slot = resolveEnum(params[4]); + if (slot == null) { + return null; + } + args[4] = slot; + } + + try { + return (AttributeModifier) constructor.newInstance(args); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + private static boolean matches(AttributeModifier modifier, UUID uuid, NamespacedKey key) { + NamespacedKey modifierKey = readKey(modifier); + if (modifierKey != null && modifierKey.equals(key)) { + return true; + } + + UUID modifierUuid = readUuid(modifier); + if (modifierUuid != null && modifierUuid.equals(uuid)) { + return true; + } + + String modifierName = readName(modifier); + return modifierName != null && modifierName.equals(key.getNamespace() + "-" + key.getKey()); + } + + private static Modifier wrap(AttributeModifier modifier) { + return new Modifier(readUuid(modifier), readKey(modifier), modifier.getAmount(), modifier.getOperation()); + } + + private static NamespacedKey readKey(AttributeModifier modifier) { + if (GET_KEY_METHOD == null) { + return null; + } + + try { + return (NamespacedKey) GET_KEY_METHOD.invoke(modifier); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + private static UUID readUuid(AttributeModifier modifier) { + if (GET_UUID_METHOD == null) { + return null; + } + + try { + return (UUID) GET_UUID_METHOD.invoke(modifier); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + private static String readName(AttributeModifier modifier) { + if (GET_NAME_METHOD == null) { + return null; + } + + try { + return (String) GET_NAME_METHOD.invoke(modifier); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + private static Method findMethod(String methodName) { + try { + return AttributeModifier.class.getMethod(methodName); + } catch (NoSuchMethodException ignored) { + return null; + } + } + + private static Object resolveEnum(Class type) { + if (!type.isEnum()) { + return null; + } + + Object any = enumConstant(type, "ANY"); + if (any != null) { + return any; + } + + Object hand = enumConstant(type, "HAND"); + if (hand != null) { + return hand; + } + + Object[] constants = type.getEnumConstants(); + return constants == null || constants.length == 0 ? null : constants[0]; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private static Object enumConstant(Class type, String name) { + try { + return Enum.valueOf((Class) type, name); + } catch (IllegalArgumentException ignored) { + return null; + } + } + + @Override + public double getValue() { + return instance.getValue(); + } + + @Override + public double getDefaultValue() { + return instance.getDefaultValue(); + } + + @Override + public double getBaseValue() { + return instance.getBaseValue(); + } + + @Override + public void setBaseValue(double baseValue) { + instance.setBaseValue(baseValue); + } + + @Override + public void addModifier(UUID uuid, NamespacedKey key, double amount, AttributeModifier.Operation operation) { + instance.addModifier(createModifier(uuid, key, amount, operation)); + } + + @Override + public boolean hasModifier(UUID uuid, NamespacedKey key) { + for (AttributeModifier modifier : instance.getModifiers()) { + if (matches(modifier, uuid, key)) { + return true; + } + } + + return false; + } + + @Override + public void removeModifier(UUID uuid, NamespacedKey key) { + List toRemove = null; + for (AttributeModifier modifier : instance.getModifiers()) { + if (!matches(modifier, uuid, key)) { + continue; + } + + if (toRemove == null) { + toRemove = new ArrayList<>(); + } + toRemove.add(modifier); + } + + if (toRemove == null) { + return; + } + + for (AttributeModifier modifier : toRemove) { + instance.removeModifier(modifier); + } + } + + @Override + public KList getModifier(UUID uuid, NamespacedKey key) { + KList modifiers = new KList<>(); + for (AttributeModifier modifier : instance.getModifiers()) { + if (matches(modifier, uuid, key)) { + modifiers.add(wrap(modifier)); + } + } + return modifiers; + } } diff --git a/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java b/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java index da10c0ae8..3325d0a98 100644 --- a/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java +++ b/src/main/java/art/arcane/adapt/api/version/RuntimeBindings.java @@ -19,113 +19,113 @@ import java.util.Set; public class RuntimeBindings implements IBindings { - private static final Method SET_ITEM_MODEL_METHOD = findMethod(ItemMeta.class, "setItemModel", NamespacedKey.class); - private static final Method SET_BASE_POTION_TYPE_METHOD = findMethod(PotionMeta.class, "setBasePotionType", PotionType.class); - private static final List INVALID_DAMAGEABLE_ENTITIES = detectInvalidDamageableEntities(); - - @Override - public void applyModel(CustomModel model, ItemMeta meta) { - NamespacedKey modelKey = model.modelKey(); - if (modelKey != null && !CustomModel.EMPTY_KEY.equals(modelKey) && SET_ITEM_MODEL_METHOD != null) { - try { - SET_ITEM_MODEL_METHOD.invoke(meta, modelKey); - return; - } catch (ReflectiveOperationException ignored) { - // Fallback is custom model data for older API variants. - } - } - - meta.setCustomModelData(model.model()); + private static final Method SET_ITEM_MODEL_METHOD = findMethod(ItemMeta.class, "setItemModel", NamespacedKey.class); + private static final Method SET_BASE_POTION_TYPE_METHOD = findMethod(PotionMeta.class, "setBasePotionType", PotionType.class); + private static final List INVALID_DAMAGEABLE_ENTITIES = detectInvalidDamageableEntities(); + + private static List detectInvalidDamageableEntities() { + Set entities = new LinkedHashSet<>(); + + addIfPresent(entities, + "ARMOR_STAND", + "ITEM_FRAME", + "GLOW_ITEM_FRAME", + "PAINTING", + "LEASH_HITCH", + "LEASH_KNOT", + "EVOKER_FANGS", + "MARKER", + "BOAT", + "CHEST_BOAT", + "MINECART" + ); + addByPrefix(entities, "MINECART_"); + addBySuffix(entities, "_MINECART"); + addBySuffix(entities, "_BOAT"); + addBySuffix(entities, "_CHEST_BOAT"); + addBySuffix(entities, "_RAFT"); + addBySuffix(entities, "_CHEST_RAFT"); + + return List.copyOf(entities); + } + + private static void addIfPresent(Set entities, String... names) { + for (String name : names) { + try { + entities.add(EntityType.valueOf(name)); + } catch (IllegalArgumentException ignored) { + // Entity was renamed/removed in this API version. + } } + } - @Override - public IAttribute getAttribute(Attributable attributable, Attribute modifier) { - return Optional.ofNullable(attributable.getAttribute(modifier)) - .map(RuntimeAttribute::new) - .orElse(null); + private static void addByPrefix(Set entities, String prefix) { + for (EntityType entity : EntityType.values()) { + if (entity.name().startsWith(prefix)) { + entities.add(entity); + } } + } - @Override - public ItemStack buildPotion(PotionBuilder builder) { - ItemStack stack = IBindings.super.buildPotion(builder); - PotionMeta meta = (PotionMeta) stack.getItemMeta(); - if (meta == null || builder.getBaseType() == null || SET_BASE_POTION_TYPE_METHOD == null) { - return stack; - } - - try { - SET_BASE_POTION_TYPE_METHOD.invoke(meta, builder.getBaseType()); - stack.setItemMeta(meta); - } catch (ReflectiveOperationException ignored) { - // Older APIs may not expose base potion type mutators. - } - - return stack; + private static void addBySuffix(Set entities, String suffix) { + for (EntityType entity : EntityType.values()) { + if (entity.name().endsWith(suffix)) { + entities.add(entity); + } } + } - @Override - @Unmodifiable - public List getInvalidDamageableEntities() { - return INVALID_DAMAGEABLE_ENTITIES; + private static Method findMethod(Class type, String name, Class... parameters) { + try { + return type.getMethod(name, parameters); + } catch (NoSuchMethodException ignored) { + return null; } - - private static List detectInvalidDamageableEntities() { - Set entities = new LinkedHashSet<>(); - - addIfPresent(entities, - "ARMOR_STAND", - "ITEM_FRAME", - "GLOW_ITEM_FRAME", - "PAINTING", - "LEASH_HITCH", - "LEASH_KNOT", - "EVOKER_FANGS", - "MARKER", - "BOAT", - "CHEST_BOAT", - "MINECART" - ); - addByPrefix(entities, "MINECART_"); - addBySuffix(entities, "_MINECART"); - addBySuffix(entities, "_BOAT"); - addBySuffix(entities, "_CHEST_BOAT"); - addBySuffix(entities, "_RAFT"); - addBySuffix(entities, "_CHEST_RAFT"); - - return List.copyOf(entities); + } + + @Override + public void applyModel(CustomModel model, ItemMeta meta) { + NamespacedKey modelKey = model.modelKey(); + if (modelKey != null && !CustomModel.EMPTY_KEY.equals(modelKey) && SET_ITEM_MODEL_METHOD != null) { + try { + SET_ITEM_MODEL_METHOD.invoke(meta, modelKey); + return; + } catch (ReflectiveOperationException ignored) { + // Fallback is custom model data for older API variants. + } } - private static void addIfPresent(Set entities, String... names) { - for (String name : names) { - try { - entities.add(EntityType.valueOf(name)); - } catch (IllegalArgumentException ignored) { - // Entity was renamed/removed in this API version. - } - } + meta.setCustomModelData(model.model()); + } + + @Override + public IAttribute getAttribute(Attributable attributable, Attribute modifier) { + return Optional.ofNullable(attributable.getAttribute(modifier)) + .map(RuntimeAttribute::new) + .orElse(null); + } + + @Override + public ItemStack buildPotion(PotionBuilder builder) { + ItemStack stack = IBindings.super.buildPotion(builder); + PotionMeta meta = (PotionMeta) stack.getItemMeta(); + if (meta == null || builder.getBaseType() == null || SET_BASE_POTION_TYPE_METHOD == null) { + return stack; } - private static void addByPrefix(Set entities, String prefix) { - for (EntityType entity : EntityType.values()) { - if (entity.name().startsWith(prefix)) { - entities.add(entity); - } - } + try { + SET_BASE_POTION_TYPE_METHOD.invoke(meta, builder.getBaseType()); + stack.setItemMeta(meta); + } catch (ReflectiveOperationException ignored) { + // Older APIs may not expose base potion type mutators. } - private static void addBySuffix(Set entities, String suffix) { - for (EntityType entity : EntityType.values()) { - if (entity.name().endsWith(suffix)) { - entities.add(entity); - } - } - } + return stack; + } - private static Method findMethod(Class type, String name, Class... parameters) { - try { - return type.getMethod(name, parameters); - } catch (NoSuchMethodException ignored) { - return null; - } - } + @Override + @Unmodifiable + public List getInvalidDamageableEntities() { + return INVALID_DAMAGEABLE_ENTITIES; + } } diff --git a/src/main/java/art/arcane/adapt/api/version/Version.java b/src/main/java/art/arcane/adapt/api/version/Version.java index 2c616eb88..818958cb1 100644 --- a/src/main/java/art/arcane/adapt/api/version/Version.java +++ b/src/main/java/art/arcane/adapt/api/version/Version.java @@ -3,19 +3,20 @@ import org.bukkit.inventory.InventoryView; public class Version { - private static final IBindings bindings = new RuntimeBindings(); - public static final boolean SET_TITLE; + public static final boolean SET_TITLE; + private static final IBindings bindings = new RuntimeBindings(); - public static IBindings get() { - return bindings; + static { + boolean titleMethod = false; + try { + InventoryView.class.getDeclaredMethod("setTitle", String.class); + titleMethod = true; + } catch (Throwable ignored) { } + SET_TITLE = titleMethod; + } - static { - boolean titleMethod = false; - try { - InventoryView.class.getDeclaredMethod("setTitle", String.class); - titleMethod = true; - } catch (Throwable ignored) {} - SET_TITLE = titleMethod; - } + public static IBindings get() { + return bindings; + } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java b/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java index 78a088109..a8a608ad0 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptComponent.java @@ -30,229 +30,239 @@ import static org.bukkit.Material.*; public interface AdaptComponent { - default AdaptServer getServer() { - return Adapt.instance.getAdaptServer(); + default AdaptServer getServer() { + return Adapt.instance.getAdaptServer(); + } + + default AdaptPlayer getPlayer(Player p) { + return getServer().getPlayer(p); + } + + default boolean isItem(ItemStack is) { + return is != null && !is.getType().equals(Material.AIR); + } + + default boolean isTool(ItemStack is) { + return isAxe(is) || isPickaxe(is) || isHoe(is) || isShovel(is) || isSword(is) || isTrident(is) || isMace(is); + } + + default boolean isMelee(ItemStack is) { + return isTool(is); + } + + default boolean isMace(ItemStack is) { + return is.getType() == Materials.MACE; + } + + default boolean isShield(ItemStack is) { + return is.getType().equals(Material.SHIELD); + } + + default boolean isXpBlock(Material material) { + return material.equals(Material.EXPERIENCE_BOTTLE); + } + + default boolean isRanged(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case BOW, CROSSBOW -> true; + default -> false; + }; } - default AdaptPlayer getPlayer(Player p) { - return getServer().getPlayer(p); - } - - default boolean isItem(ItemStack is) { - return is != null && !is.getType().equals(Material.AIR); - } - - default boolean isTool(ItemStack is) { - return isAxe(is) || isPickaxe(is) || isHoe(is) || isShovel(is) || isSword(is) || isTrident(is) || isMace(is); - } - - default boolean isMelee(ItemStack is) { - return isTool(is); - } - - default boolean isMace(ItemStack is) { - return is.getType() == Materials.MACE; - } - - default boolean isShield(ItemStack is) { - return is.getType().equals(Material.SHIELD); - } + return false; + } - default boolean isXpBlock(Material material) { - return material.equals(Material.EXPERIENCE_BOTTLE); + default boolean isSword(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_SWORD, GOLDEN_SWORD, IRON_SWORD, NETHERITE_SWORD, + STONE_SWORD, WOODEN_SWORD -> true; + default -> false; + }; } - default boolean isRanged(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case BOW, CROSSBOW -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isTrident(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case TRIDENT, SEA_PICKLE -> true; + default -> false; + }; } - default boolean isSword(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_SWORD, GOLDEN_SWORD, IRON_SWORD, NETHERITE_SWORD, STONE_SWORD, WOODEN_SWORD -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isAxe(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_AXE, GOLDEN_AXE, IRON_AXE, NETHERITE_AXE, STONE_AXE, + WOODEN_AXE -> true; + default -> false; + }; } - default boolean isTrident(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case TRIDENT, SEA_PICKLE -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isPickaxe(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_PICKAXE, GOLDEN_PICKAXE, IRON_PICKAXE, NETHERITE_PICKAXE, + STONE_PICKAXE, WOODEN_PICKAXE -> true; + default -> false; + }; } - default boolean isAxe(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_AXE, GOLDEN_AXE, IRON_AXE, NETHERITE_AXE, STONE_AXE, WOODEN_AXE -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isShovel(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_SHOVEL, GOLDEN_SHOVEL, IRON_SHOVEL, NETHERITE_SHOVEL, + STONE_SHOVEL, WOODEN_SHOVEL -> true; + default -> false; + }; } - - default boolean isPickaxe(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_PICKAXE, GOLDEN_PICKAXE, IRON_PICKAXE, NETHERITE_PICKAXE, STONE_PICKAXE, WOODEN_PICKAXE -> - true; - default -> false; - }; - } - - return false; - } - - default boolean isShovel(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_SHOVEL, GOLDEN_SHOVEL, IRON_SHOVEL, NETHERITE_SHOVEL, STONE_SHOVEL, WOODEN_SHOVEL -> true; - default -> false; - }; - } - return false; + return false; + } + + default boolean isLog(ItemStack it) { + if (isItem(it)) { + return List.of(MUSHROOM_STEM, BROWN_MUSHROOM_BLOCK, RED_MUSHROOM_BLOCK, MANGROVE_ROOTS, MUDDY_MANGROVE_ROOTS).contains(it.getType()) + || it.getType().name().endsWith("_LOG") + || it.getType().name().endsWith("_WOOD"); } - default boolean isLog(ItemStack it) { - if (isItem(it)) { - return List.of(MUSHROOM_STEM, BROWN_MUSHROOM_BLOCK, RED_MUSHROOM_BLOCK, MANGROVE_ROOTS, MUDDY_MANGROVE_ROOTS).contains(it.getType()) - || it.getType().name().endsWith("_LOG") - || it.getType().name().endsWith("_WOOD"); - } + return false; + } - return false; + default boolean isLeaves(ItemStack it) { + if (isItem(it)) { + return List.of(Material.MANGROVE_ROOTS, Material.MUDDY_MANGROVE_ROOTS).contains(it.getType()) + || it.getType().name().endsWith("_LEAVES"); } - default boolean isLeaves(ItemStack it) { - if (isItem(it)) { - return List.of(Material.MANGROVE_ROOTS, Material.MUDDY_MANGROVE_ROOTS).contains(it.getType()) - || it.getType().name().endsWith("_LEAVES"); - } + return false; + } - return false; + default boolean isBoots(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_BOOTS, GOLDEN_BOOTS, IRON_BOOTS, NETHERITE_BOOTS, + CHAINMAIL_BOOTS, LEATHER_BOOTS -> true; + default -> false; + }; } - default boolean isBoots(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_BOOTS, GOLDEN_BOOTS, IRON_BOOTS, NETHERITE_BOOTS, CHAINMAIL_BOOTS, LEATHER_BOOTS -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isHelmet(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case CHAINMAIL_HELMET, DIAMOND_HELMET, GOLDEN_HELMET, IRON_HELMET, + LEATHER_HELMET, NETHERITE_HELMET, TURTLE_HELMET -> true; + default -> false; + }; } - default boolean isHelmet(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case CHAINMAIL_HELMET, DIAMOND_HELMET, GOLDEN_HELMET, IRON_HELMET, LEATHER_HELMET, NETHERITE_HELMET, TURTLE_HELMET -> - true; - default -> false; - }; - } + return false; + } - return false; + default boolean isLeggings(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_LEGGINGS, GOLDEN_LEGGINGS, IRON_LEGGINGS, + NETHERITE_LEGGINGS, CHAINMAIL_LEGGINGS, LEATHER_LEGGINGS -> true; + default -> false; + }; } - default boolean isLeggings(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_LEGGINGS, GOLDEN_LEGGINGS, IRON_LEGGINGS, NETHERITE_LEGGINGS, CHAINMAIL_LEGGINGS, LEATHER_LEGGINGS -> - true; - default -> false; - }; - } - - return false; + return false; + } + + default boolean isChestplate(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_CHESTPLATE, GOLDEN_CHESTPLATE, IRON_CHESTPLATE, + NETHERITE_CHESTPLATE, CHAINMAIL_CHESTPLATE, LEATHER_CHESTPLATE -> + true; + default -> false; + }; } - default boolean isChestplate(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_CHESTPLATE, GOLDEN_CHESTPLATE, IRON_CHESTPLATE, NETHERITE_CHESTPLATE, CHAINMAIL_CHESTPLATE, LEATHER_CHESTPLATE -> - true; - default -> false; - }; - } + return false; + } - return false; + default boolean isElytra(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case ELYTRA, LEGACY_ELYTRA -> true; + default -> false; + }; } - default boolean isElytra(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case ELYTRA, LEGACY_ELYTRA -> true; - default -> false; - }; - } + return false; + } - return false; + default boolean isHoe(ItemStack it) { + if (isItem(it)) { + return switch (it.getType()) { + case DIAMOND_HOE, GOLDEN_HOE, IRON_HOE, NETHERITE_HOE, STONE_HOE, + WOODEN_HOE -> true; + default -> false; + }; } - default boolean isHoe(ItemStack it) { - if (isItem(it)) { - return switch (it.getType()) { - case DIAMOND_HOE, GOLDEN_HOE, IRON_HOE, NETHERITE_HOE, STONE_HOE, WOODEN_HOE -> true; - default -> false; - }; - } - - return false; - } - - default boolean isOre(BlockData b) { - return switch (b.getMaterial()) { - case COPPER_ORE, DEEPSLATE_COPPER_ORE, COAL_ORE, GOLD_ORE, IRON_ORE, DIAMOND_ORE, LAPIS_ORE, EMERALD_ORE, NETHER_QUARTZ_ORE, NETHER_GOLD_ORE, REDSTONE_ORE, DEEPSLATE_COAL_ORE, DEEPSLATE_IRON_ORE, DEEPSLATE_GOLD_ORE, DEEPSLATE_LAPIS_ORE, DEEPSLATE_DIAMOND_ORE, DEEPSLATE_EMERALD_ORE, DEEPSLATE_REDSTONE_ORE -> - true; - default -> false; - }; - } - - default boolean isStorage(BlockData b) { - return switch (b.getMaterial()) { - case CHEST, - SMOKER, - TRAPPED_CHEST, - SHULKER_BOX, - WHITE_SHULKER_BOX, - ORANGE_SHULKER_BOX, - MAGENTA_SHULKER_BOX, - LIGHT_BLUE_SHULKER_BOX, - YELLOW_SHULKER_BOX, - LIME_SHULKER_BOX, - PINK_SHULKER_BOX, - GRAY_SHULKER_BOX, - LIGHT_GRAY_SHULKER_BOX, - CYAN_SHULKER_BOX, - PURPLE_SHULKER_BOX, - BLUE_SHULKER_BOX, - BROWN_SHULKER_BOX, - GREEN_SHULKER_BOX, - RED_SHULKER_BOX, - BLACK_SHULKER_BOX, - BARREL, - DISPENSER, - DROPPER, - FURNACE, - BLAST_FURNACE, - HOPPER -> true; - default -> false; - }; - } + return false; + } + + default boolean isOre(BlockData b) { + return switch (b.getMaterial()) { + case COPPER_ORE, DEEPSLATE_COPPER_ORE, COAL_ORE, GOLD_ORE, IRON_ORE, + DIAMOND_ORE, LAPIS_ORE, EMERALD_ORE, NETHER_QUARTZ_ORE, + NETHER_GOLD_ORE, REDSTONE_ORE, DEEPSLATE_COAL_ORE, + DEEPSLATE_IRON_ORE, DEEPSLATE_GOLD_ORE, DEEPSLATE_LAPIS_ORE, + DEEPSLATE_DIAMOND_ORE, DEEPSLATE_EMERALD_ORE, + DEEPSLATE_REDSTONE_ORE -> true; + default -> false; + }; + } + + default boolean isStorage(BlockData b) { + return switch (b.getMaterial()) { + case CHEST, + SMOKER, + TRAPPED_CHEST, + SHULKER_BOX, + WHITE_SHULKER_BOX, + ORANGE_SHULKER_BOX, + MAGENTA_SHULKER_BOX, + LIGHT_BLUE_SHULKER_BOX, + YELLOW_SHULKER_BOX, + LIME_SHULKER_BOX, + PINK_SHULKER_BOX, + GRAY_SHULKER_BOX, + LIGHT_GRAY_SHULKER_BOX, + CYAN_SHULKER_BOX, + PURPLE_SHULKER_BOX, + BLUE_SHULKER_BOX, + BROWN_SHULKER_BOX, + GREEN_SHULKER_BOX, + RED_SHULKER_BOX, + BLACK_SHULKER_BOX, + BARREL, + DISPENSER, + DROPPER, + FURNACE, + BLAST_FURNACE, + HOPPER -> true; + default -> false; + }; + } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index 9c33fa2bd..e8589d13a 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -49,380 +49,380 @@ @EqualsAndHashCode(callSuper = false) @Data public class AdaptPlayer extends TickedObject { - private static final Set LOAD_FAILURE_GUARD = ConcurrentHashMap.newKeySet(); - - private final Player player; - private final PlayerData data; - private ChronoLatch savelatch; - private ChronoLatch updatelatch; - private Notifier not; - private Notifier actionBarNotifier; - private AdvancementHandler advancementHandler; - private RollingSequence speed; - private long lastloc; - private Vector velocity; - private Location lastpos; - private long lastSeen; - private volatile boolean pendingDataDeletion; - private volatile boolean runtimeReady; - - public AdaptPlayer(Player p) { - this(p, null); - } - - public AdaptPlayer(Player p, PlayerData prefetchedData) { - super("players", p.getUniqueId().toString(), 50); - this.player = p; - data = prefetchedData == null ? loadPlayerData(p.getUniqueId()) : prefetchedData; - updatelatch = new ChronoLatch(1000); - savelatch = new ChronoLatch(60000); - not = new Notifier(this); - actionBarNotifier = new Notifier(this); - advancementHandler = new AdvancementHandler(this); - speed = new RollingSequence(7); - lastloc = M.ms(); - lastSeen = M.ms(); - velocity = new Vector(); - runtimeReady = true; + private static final Set LOAD_FAILURE_GUARD = ConcurrentHashMap.newKeySet(); + + private final Player player; + private final PlayerData data; + private ChronoLatch savelatch; + private ChronoLatch updatelatch; + private Notifier not; + private Notifier actionBarNotifier; + private AdvancementHandler advancementHandler; + private RollingSequence speed; + private long lastloc; + private Vector velocity; + private Location lastpos; + private long lastSeen; + private volatile boolean pendingDataDeletion; + private volatile boolean runtimeReady; + + public AdaptPlayer(Player p) { + this(p, null); + } + + public AdaptPlayer(Player p, PlayerData prefetchedData) { + super("players", p.getUniqueId().toString(), 50); + this.player = p; + data = prefetchedData == null ? loadPlayerData(p.getUniqueId()) : prefetchedData; + updatelatch = new ChronoLatch(1000); + savelatch = new ChronoLatch(60000); + not = new Notifier(this); + actionBarNotifier = new Notifier(this); + advancementHandler = new AdvancementHandler(this); + speed = new RollingSequence(7); + lastloc = M.ms(); + lastSeen = M.ms(); + velocity = new Vector(); + runtimeReady = true; + } + + public static PlayerData loadPlayerData(UUID uuid) { + boolean upload = false; + if (AdaptConfig.get().isUseSql()) { + if (Adapt.instance.getRedisSync() != null) { + java.util.Optional opt = Adapt.instance.getRedisSync().cachedData(uuid); + if (opt.isPresent()) { + Adapt.verbose("Using cached data for player: " + uuid); + LOAD_FAILURE_GUARD.remove(uuid); + return opt.get(); + } + } + + if (Adapt.instance.getSqlManager() != null) { + String sqlData = Adapt.instance.getSqlManager().fetchData(uuid); + if (sqlData != null) { + try { + PlayerData parsed = PlayerData.fromJson(sqlData); + LOAD_FAILURE_GUARD.remove(uuid); + return parsed; + } catch (Throwable e) { + LOAD_FAILURE_GUARD.add(uuid); + Adapt.warn("Failed to parse SQL player data for " + uuid + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); + } + } + upload = true; + } } - public boolean canConsumeFood(double cost, int minFood) { - return (player.getFoodLevel() + player.getSaturation()) - minFood > cost; + File f = getPlayerDataFile(uuid); + if (f.exists()) { + try { + String text = IO.readAll(f); + if (upload) { + PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); + if (queue != null) { + queue.queueSave(uuid, text, f); + } else if (Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().updateData(uuid, text); + } + } + PlayerData parsed = PlayerData.fromJson(text); + LOAD_FAILURE_GUARD.remove(uuid); + return parsed; + } catch (Throwable e) { + LOAD_FAILURE_GUARD.add(uuid); + Adapt.warn("Failed to load player data for " + uuid + " from " + f.getAbsolutePath() + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); + } } - public boolean consumeFood(double cost, int minFood) { - if (canConsumeFood(cost, minFood)) { - int food = player.getFoodLevel(); - double sat = player.getSaturation(); - - if (sat >= cost) { - sat = (player.getSaturation() - cost); - cost = 0; - } else if (player.getSaturation() > 0) { - cost -= sat; - sat = 0; - } - - if (cost >= 1) { - food -= (int) Math.floor(cost); - cost = Math.floor(cost); - } - - if (cost > 0) { - if (sat >= cost) { - sat -= cost; - cost = 0; - } else { - sat++; - food--; - } - } - - if (sat >= cost && cost > 0) { - sat -= cost; - cost = 0; - } - - player.setFoodLevel(food); - player.setSaturation((float) sat); - - return true; + LOAD_FAILURE_GUARD.remove(uuid); + return new PlayerData(); + } + + private static File getPlayerDataFile(UUID uuid) { + return new File(Adapt.instance.getDataFolder("data", "players"), uuid.toString() + ".json"); + } + + public boolean canConsumeFood(double cost, int minFood) { + return (player.getFoodLevel() + player.getSaturation()) - minFood > cost; + } + + public boolean consumeFood(double cost, int minFood) { + if (canConsumeFood(cost, minFood)) { + int food = player.getFoodLevel(); + double sat = player.getSaturation(); + + if (sat >= cost) { + sat = (player.getSaturation() - cost); + cost = 0; + } else if (player.getSaturation() > 0) { + cost -= sat; + sat = 0; + } + + if (cost >= 1) { + food -= (int) Math.floor(cost); + cost = Math.floor(cost); + } + + if (cost > 0) { + if (sat >= cost) { + sat -= cost; + cost = 0; + } else { + sat++; + food--; } + } - return false; - } + if (sat >= cost && cost > 0) { + sat -= cost; + cost = 0; + } - public boolean isBusy() { - return not.isBusy(); - } + player.setFoodLevel(food); + player.setSaturation((float) sat); - public PlayerSkillLine getSkillLine(String l) { - return getData().getSkillLine(l); + return true; } - private void save() { - save(false); - } + return false; + } - private void save(boolean synchronous) { - UUID uuid = player.getUniqueId(); - File playerDataFile = getPlayerDataFile(uuid); + public boolean isBusy() { + return not.isBusy(); + } - if (pendingDataDeletion) { - queueDelete(uuid, playerDataFile); - return; - } + public PlayerSkillLine getSkillLine(String l) { + return getData().getSkillLine(l); + } - if (LOAD_FAILURE_GUARD.contains(uuid)) { - Adapt.warn("Skipping save for " + uuid + " because player data failed to load earlier. Existing file is preserved."); - return; - } + private void save() { + save(false); + } - String json = this.data.toJson(AdaptConfig.get().isUseSql()); - if (synchronous) { - if (AdaptConfig.get().isUseSql()) { - if (Adapt.instance.getRedisSync() != null) { - Adapt.instance.getRedisSync().publish(uuid, json); - } - if (Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().updateData(uuid, json); - } - } else { - J.attempt(() -> IO.writeAll(playerDataFile, json)); - } - return; - } + private void save(boolean synchronous) { + UUID uuid = player.getUniqueId(); + File playerDataFile = getPlayerDataFile(uuid); - PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); - if (queue != null) { - queue.queueSave(uuid, json, playerDataFile); - return; - } + if (pendingDataDeletion) { + queueDelete(uuid, playerDataFile); + return; + } - if (AdaptConfig.get().isUseSql()) { - if (Adapt.instance.getRedisSync() != null) { - Adapt.instance.getRedisSync().publish(uuid, json); - } - if (Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().updateData(uuid, json); - } - } else { - J.attempt(() -> IO.writeAll(playerDataFile, json)); + if (LOAD_FAILURE_GUARD.contains(uuid)) { + Adapt.warn("Skipping save for " + uuid + " because player data failed to load earlier. Existing file is preserved."); + return; + } + + String json = this.data.toJson(AdaptConfig.get().isUseSql()); + if (synchronous) { + if (AdaptConfig.get().isUseSql()) { + if (Adapt.instance.getRedisSync() != null) { + Adapt.instance.getRedisSync().publish(uuid, json); } + if (Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().updateData(uuid, json); + } + } else { + J.attempt(() -> IO.writeAll(playerDataFile, json)); + } + return; } - @Override - public void unregister() { - super.unregister(); - save(true); + PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); + if (queue != null) { + queue.queueSave(uuid, json, playerDataFile); + return; } - public void delete(UUID uuid) { - pendingDataDeletion = true; - File local = getPlayerDataFile(uuid); - Adapt.warn("Deleting Player Data: " + local.getAbsolutePath()); - queueDelete(uuid, local); + if (AdaptConfig.get().isUseSql()) { + if (Adapt.instance.getRedisSync() != null) { + Adapt.instance.getRedisSync().publish(uuid, json); + } + if (Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().updateData(uuid, json); + } + } else { + J.attempt(() -> IO.writeAll(playerDataFile, json)); + } + } + + @Override + public void unregister() { + super.unregister(); + save(true); + } + + public void delete(UUID uuid) { + pendingDataDeletion = true; + File local = getPlayerDataFile(uuid); + Adapt.warn("Deleting Player Data: " + local.getAbsolutePath()); + queueDelete(uuid, local); + + Player p = player; + if (!p.isOnline()) { + return; + } - Player p = player; - if (!p.isOnline()) { - return; - } + J.s(() -> p.kickPlayer("Your data has been deleted."), 20); + } - J.s(() -> p.kickPlayer("Your data has been deleted."), 20); + public boolean shouldUnload() { + if (player.isOnline()) { + lastSeen = M.ms(); + return false; } - public boolean shouldUnload() { - if (player.isOnline()) { - lastSeen = M.ms(); - return false; - } + return lastSeen + 60_000 < System.currentTimeMillis(); + } - return lastSeen + 60_000 < System.currentTimeMillis(); + @Override + public void onTick() { + if (!runtimeReady) { + return; } - public static PlayerData loadPlayerData(UUID uuid) { - boolean upload = false; - if (AdaptConfig.get().isUseSql()) { - if (Adapt.instance.getRedisSync() != null) { - var opt = Adapt.instance.getRedisSync().cachedData(uuid); - if (opt.isPresent()) { - Adapt.verbose("Using cached data for player: " + uuid); - LOAD_FAILURE_GUARD.remove(uuid); - return opt.get(); - } - } - - if (Adapt.instance.getSqlManager() != null) { - String sqlData = Adapt.instance.getSqlManager().fetchData(uuid); - if (sqlData != null) { - try { - PlayerData parsed = PlayerData.fromJson(sqlData); - LOAD_FAILURE_GUARD.remove(uuid); - return parsed; - } catch (Throwable e) { - LOAD_FAILURE_GUARD.add(uuid); - Adapt.warn("Failed to parse SQL player data for " + uuid + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); - } - } - upload = true; - } - } - - File f = getPlayerDataFile(uuid); - if (f.exists()) { - try { - String text = IO.readAll(f); - if (upload) { - PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); - if (queue != null) { - queue.queueSave(uuid, text, f); - } else if (Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().updateData(uuid, text); - } - } - PlayerData parsed = PlayerData.fromJson(text); - LOAD_FAILURE_GUARD.remove(uuid); - return parsed; - } catch (Throwable e) { - LOAD_FAILURE_GUARD.add(uuid); - Adapt.warn("Failed to load player data for " + uuid + " from " + f.getAbsolutePath() + ": " + e.getClass().getSimpleName() + (e.getMessage() == null ? "" : " (" + e.getMessage() + ")")); - } - } + if (updatelatch == null) { + updatelatch = new ChronoLatch(1000); + } + if (savelatch == null) { + savelatch = new ChronoLatch(60000); + } + if (speed == null) { + speed = new RollingSequence(7); + } + if (velocity == null) { + velocity = new Vector(); + } - LOAD_FAILURE_GUARD.remove(uuid); - return new PlayerData(); + if (updatelatch.flip()) { + getData().update(this); } - @Override - public void onTick() { - if (!runtimeReady) { - return; - } + if (savelatch.flip()) { + save(); + } - if (updatelatch == null) { - updatelatch = new ChronoLatch(1000); - } - if (savelatch == null) { - savelatch = new ChronoLatch(60000); - } - if (speed == null) { - speed = new RollingSequence(7); - } - if (velocity == null) { - velocity = new Vector(); - } + getServer().takeSpatial(this); - if (updatelatch.flip()) { - getData().update(this); - } + Location at = player.getLocation(); - if (savelatch.flip()) { - save(); + if (lastpos != null) { + if (lastpos.getWorld().equals(at.getWorld())) { + if (lastpos.distanceSquared(at) <= 7 * 7) { + speed.put(lastpos.distance(at) / ((double) (M.ms() - lastloc) / 50D)); + velocity = velocity.clone().add(at.clone().subtract(lastpos).toVector()).multiply(0.5); + velocity.setX(Math.abs(velocity.getX()) < 0.01 ? 0 : velocity.getX()); + velocity.setY(Math.abs(velocity.getY()) < 0.01 ? 0 : velocity.getY()); + velocity.setZ(Math.abs(velocity.getZ()) < 0.01 ? 0 : velocity.getZ()); } + } + } - getServer().takeSpatial(this); - - Location at = player.getLocation(); - - if (lastpos != null) { - if (lastpos.getWorld().equals(at.getWorld())) { - if (lastpos.distanceSquared(at) <= 7 * 7) { - speed.put(lastpos.distance(at) / ((double) (M.ms() - lastloc) / 50D)); - velocity = velocity.clone().add(at.clone().subtract(lastpos).toVector()).multiply(0.5); - velocity.setX(Math.abs(velocity.getX()) < 0.01 ? 0 : velocity.getX()); - velocity.setY(Math.abs(velocity.getY()) < 0.01 ? 0 : velocity.getY()); - velocity.setZ(Math.abs(velocity.getZ()) < 0.01 ? 0 : velocity.getZ()); - } - } - } + lastpos = at.clone(); + lastloc = M.ms(); + } - lastpos = at.clone(); - lastloc = M.ms(); + public double getSpeed() { + if (!runtimeReady || speed == null) { + return 0D; } - public double getSpeed() { - if (!runtimeReady || speed == null) { - return 0D; - } + return speed.getAverage(); + } - return speed.getAverage(); + public boolean hasAdaptation(String id) { + if (id == null || id.isBlank()) { + return false; } - public boolean hasAdaptation(String id) { - if (id == null || id.isBlank()) { - return false; - } + int separator = id.indexOf('-'); + if (separator <= 0) { + return false; + } - int separator = id.indexOf('-'); - if (separator <= 0) { - return false; - } + String skillLine = id.substring(0, separator); + if (skillLine.isBlank()) { + return false; + } - String skillLine = id.substring(0, separator); - if (skillLine.isBlank()) { - return false; - } + PlayerSkillLine line = getData().getSkillLineNullable(skillLine); + if (line == null) { + return false; + } - PlayerSkillLine line = getData().getSkillLineNullable(skillLine); - if (line == null) { - return false; - } + PlayerAdaptation adaptation = line.getAdaptation(id); + return adaptation != null && adaptation.getLevel() > 0; + } - PlayerAdaptation adaptation = line.getAdaptation(id); - return adaptation != null && adaptation.getLevel() > 0; + public void giveXPToRecents(AdaptPlayer p, double xpGained, int ms) { + for (PlayerSkillLine i : p.getData().getSkillLines().v()) { + if (M.ms() - i.getLast() < ms) { + i.giveXP(not, xpGained); + } } + } - public void giveXPToRecents(AdaptPlayer p, double xpGained, int ms) { - for (PlayerSkillLine i : p.getData().getSkillLines().v()) { - if (M.ms() - i.getLast() < ms) { - i.giveXP(not, xpGained); - } - } - } + public void giveXPToRandom(AdaptPlayer p, double xpGained) { + p.getData().getSkillLines().v().getRandom().giveXP(p.getNot(), xpGained); + } - public void giveXPToRandom(AdaptPlayer p, double xpGained) { - p.getData().getSkillLines().v().getRandom().giveXP(p.getNot(), xpGained); - } + public void boostXPToRandom(AdaptPlayer p, double boost, int ms) { + p.getData().getSkillLines().v().getRandom().boost(boost, ms); + } - public void boostXPToRandom(AdaptPlayer p, double boost, int ms) { - p.getData().getSkillLines().v().getRandom().boost(boost, ms); + public void boostXPToRecents(double boost, int ms) { + for (PlayerSkillLine i : this.getData().getSkillLines().v()) { + if (M.ms() - i.getLast() < ms) { + i.boost(boost, ms); + } } - - public void boostXPToRecents(double boost, int ms) { - for (PlayerSkillLine i : this.getData().getSkillLines().v()) { - if (M.ms() - i.getLast() < ms) { - i.boost(boost, ms); - } - } + } + + public void loggedIn() { + lastSeen = M.ms(); + if (AdaptConfig.get().isLoginBonus()) { + long timeGone = M.ms() - getData().getLastLogin(); + boolean first = getData().getLastLogin() == 0; + getData().setLastLogin(M.ms()); + long boostTime = (long) Math.min(timeGone / 12D, TimeUnit.HOURS.toMillis(1)); + if (boostTime < TimeUnit.MINUTES.toMillis(5)) { + return; + } + double boostAmount = M.lerp(0.1, 0.25, (double) boostTime / (double) TimeUnit.HOURS.toMillis(1)); + getData().globalXPMultiplier(boostAmount, (int) boostTime); + if (!AdaptConfig.get().isWelcomeMessage()) + return; + getNot().queue(AdvancementNotification.builder() + .title(first ? Localizer.dLocalize("snippets.gui.welcome") : Localizer.dLocalize("snippets.gui.welcome_back")) + .description("+" + C.GREEN + Form.pc(boostAmount, 0) + C.GRAY + " " + Localizer.dLocalize("snippets.gui.xp_bonus_for_time") + " " + C.AQUA + Form.duration(boostTime, 0)) + .model(CustomModel.get(Material.DIAMOND, "snippets", "gui", first ? "welcome" : "welcomeback")) + .build()); } + } - public void loggedIn() { - lastSeen = M.ms(); - if (AdaptConfig.get().isLoginBonus()) { - long timeGone = M.ms() - getData().getLastLogin(); - boolean first = getData().getLastLogin() == 0; - getData().setLastLogin(M.ms()); - long boostTime = (long) Math.min(timeGone / 12D, TimeUnit.HOURS.toMillis(1)); - if (boostTime < TimeUnit.MINUTES.toMillis(5)) { - return; - } - double boostAmount = M.lerp(0.1, 0.25, (double) boostTime / (double) TimeUnit.HOURS.toMillis(1)); - getData().globalXPMultiplier(boostAmount, (int) boostTime); - if (!AdaptConfig.get().isWelcomeMessage()) - return; - getNot().queue(AdvancementNotification.builder() - .title(first ? Localizer.dLocalize("snippets.gui.welcome") : Localizer.dLocalize("snippets.gui.welcome_back")) - .description("+" + C.GREEN + Form.pc(boostAmount, 0) + C.GRAY + " " + Localizer.dLocalize("snippets.gui.xp_bonus_for_time") + " " + C.AQUA + Form.duration(boostTime, 0)) - .model(CustomModel.get(Material.DIAMOND, "snippets", "gui", first ? "welcome" : "welcomeback")) - .build()); - } + public boolean hasSkill(Skill s) { + if (s == null) { + return false; } - public boolean hasSkill(Skill s) { - if (s == null) { - return false; - } + PlayerSkillLine line = getData().getSkillLine(s.getName()); + return line != null && line.getXp() > 1; + } - PlayerSkillLine line = getData().getSkillLine(s.getName()); - return line != null && line.getXp() > 1; + private void queueDelete(UUID uuid, File localFile) { + PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); + if (queue != null) { + queue.queueDelete(uuid, localFile); + return; } - private static File getPlayerDataFile(UUID uuid) { - return new File(Adapt.instance.getDataFolder("data", "players"), uuid.toString() + ".json"); + if (localFile.exists() && !localFile.delete()) { + Adapt.verbose("Failed to delete local player data file " + localFile.getAbsolutePath()); } - - private void queueDelete(UUID uuid, File localFile) { - PlayerDataPersistenceQueue queue = Adapt.instance.getPlayerDataPersistenceQueue(); - if (queue != null) { - queue.queueDelete(uuid, localFile); - return; - } - - if (localFile.exists() && !localFile.delete()) { - Adapt.verbose("Failed to delete local player data file " + localFile.getAbsolutePath()); - } - if (AdaptConfig.get().isUseSql() && Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().delete(uuid); - } + if (AdaptConfig.get().isUseSql() && Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().delete(uuid); } + } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java index 328904187..e49d415d3 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java @@ -63,356 +63,356 @@ import java.util.concurrent.locks.ReentrantLock; public class AdaptServer extends TickedObject { - private final ReentrantLock clearLock = new ReentrantLock(); - private final Map players = new ConcurrentHashMap<>(); - private final Cache prefetchedPlayerData = Caffeine.newBuilder() - .expireAfterWrite(2, TimeUnit.MINUTES) - .maximumSize(2048) - .build(); - @Getter - private volatile List onlinePlayerSnapshot = List.of(); - @Getter - private volatile List onlineAdaptPlayerSnapshot = List.of(); - @Getter - private final List spatialTickets = new ArrayList<>(); - @Getter - private final SkillRegistry skillRegistry = new SkillRegistry(); - @Getter - private AdaptServerData data = new AdaptServerData(); - - public AdaptServer() { - super("core", UUID.randomUUID().toString(), 1000); - load(); - - Bukkit.getOnlinePlayers().forEach(this::join); - refreshOnlinePlayerSnapshots(); + private final ReentrantLock clearLock = new ReentrantLock(); + private final Map players = new ConcurrentHashMap<>(); + private final Cache prefetchedPlayerData = Caffeine.newBuilder() + .expireAfterWrite(2, TimeUnit.MINUTES) + .maximumSize(2048) + .build(); + @Getter + private final List spatialTickets = new ArrayList<>(); + @Getter + private final SkillRegistry skillRegistry = new SkillRegistry(); + @Getter + private volatile List onlinePlayerSnapshot = List.of(); + @Getter + private volatile List onlineAdaptPlayerSnapshot = List.of(); + @Getter + private AdaptServerData data = new AdaptServerData(); + + public AdaptServer() { + super("core", UUID.randomUUID().toString(), 1000); + load(); + + Bukkit.getOnlinePlayers().forEach(this::join); + refreshOnlinePlayerSnapshots(); + } + + public void offer(SpatialXP xp) { + if (xp == null || xp.getSkill() == null || xp.getLocation() == null) { + return; } - - public void offer(SpatialXP xp) { - if (xp == null || xp.getSkill() == null || xp.getLocation() == null) { - return; - } - if (xp.getRadius() <= 0 || xp.getXp() <= 0 || xp.getMs() <= M.ms()) { - return; + if (xp.getRadius() <= 0 || xp.getXp() <= 0 || xp.getMs() <= M.ms()) { + return; + } + synchronized (spatialTickets) { + spatialTickets.add(xp); + } + } + + public void takeSpatial(AdaptPlayer p) { + try { + SpatialXP x; + synchronized (spatialTickets) { + int size = spatialTickets.size(); + if (size == 0) { + return; } + x = spatialTickets.get(size - 1); + } + + if (M.ms() > x.getMs()) { synchronized (spatialTickets) { - spatialTickets.add(xp); + spatialTickets.remove(x); } - } + return; + } - public void takeSpatial(AdaptPlayer p) { - try { - SpatialXP x; - synchronized (spatialTickets) { - int size = spatialTickets.size(); - if (size == 0) { - return; - } - x = spatialTickets.get(size - 1); - } - - if (M.ms() > x.getMs()) { - synchronized (spatialTickets) { - spatialTickets.remove(x); - } - return; - } - - if (!p.getPlayer().getClass().getSimpleName().equals("CraftPlayer")) { - synchronized (spatialTickets) { - spatialTickets.remove(x); - } - return; - } - - if (p.getPlayer().getWorld().equals(x.getLocation().getWorld())) { - double c = p.getPlayer().getLocation().distanceSquared(x.getLocation()); - if (c < x.getRadius() * x.getRadius()) { - double distl = M.lerpInverse(0, x.getRadius() * x.getRadius(), c); - double xp = x.getXp() / (1.5D * ((distl * 9) + 1)); - synchronized (spatialTickets) { - x.setXp(x.getXp() - xp); - - if (x.getXp() < 10) { - xp += x.getXp(); - spatialTickets.remove(x); - } - } - - XP.xp(p, x.getSkill(), xp); - } - } - } catch (Throwable ignored) { + if (!p.getPlayer().getClass().getSimpleName().equals("CraftPlayer")) { + synchronized (spatialTickets) { + spatialTickets.remove(x); } - } - - public void join(Player p) { - AdaptPlayer existing = players.get(p.getUniqueId()); - if (existing != null) { - if (existing.getPlayer() == p) { - existing.loggedIn(); - refreshOnlinePlayerSnapshots(); - return; + return; + } + + if (p.getPlayer().getWorld().equals(x.getLocation().getWorld())) { + double c = p.getPlayer().getLocation().distanceSquared(x.getLocation()); + if (c < x.getRadius() * x.getRadius()) { + double distl = M.lerpInverse(0, x.getRadius() * x.getRadius(), c); + double xp = x.getXp() / (1.5D * ((distl * 9) + 1)); + synchronized (spatialTickets) { + x.setXp(x.getXp() - xp); + + if (x.getXp() < 10) { + xp += x.getXp(); + spatialTickets.remove(x); } + } - players.remove(p.getUniqueId()); + XP.xp(p, x.getSkill(), xp); } - - PlayerData prefetched = takePrefetchedData(p.getUniqueId()); - AdaptPlayer a = new AdaptPlayer(p, prefetched); - players.put(p.getUniqueId(), a); - refreshOnlinePlayerSnapshots(); - a.loggedIn(); + } + } catch (Throwable ignored) { } + } - public void quit(UUID p) { - AdaptPlayer a = players.get(p); - if (a == null) return; - a.unregister(); - // Keep the entry briefly after quit so late quit listeners/tasks do not - // re-create a new AdaptPlayer for an offline player. - prefetchedPlayerData.invalidate(p); + public void join(Player p) { + AdaptPlayer existing = players.get(p.getUniqueId()); + if (existing != null) { + if (existing.getPlayer() == p) { + existing.loggedIn(); refreshOnlinePlayerSnapshots(); - } + return; + } - @Override - public void unregister() { - new HashSet<>(players.keySet()).forEach(this::quit); - prefetchedPlayerData.invalidateAll(); - onlinePlayerSnapshot = List.of(); - onlineAdaptPlayerSnapshot = List.of(); - skillRegistry.unregister(); - save(); - super.unregister(); + players.remove(p.getUniqueId()); } - @EventHandler(priority = EventPriority.LOWEST) - public void on(ProjectileLaunchEvent e) { - if (e.getEntity() instanceof Snowball s && e.getEntity().getShooter() instanceof Player p) { - KnowledgeOrb.Data data = KnowledgeOrb.get(s.getItem()); - if (data != null) { - Skill skill = getSkillRegistry().getSkill(data.getSkill()); - data.apply(p); - SoundNotification.builder() - .sound(Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM) - .volume(0.35f).pitch(1.455f) - .build().play(getPlayer(p)); - SoundNotification.builder() - .sound(Sound.ENTITY_SHULKER_OPEN) - .volume(1f).pitch(1.655f) - .build().play(getPlayer(p)); - getPlayer(p).getNot().queue(AdvancementNotification.builder() - .icon(Material.BOOK) - .model(CustomModel.get(Material.BOOK, "snippets", "gui", "knowledge")) - .title(C.GRAY + "+ " + C.WHITE + data.getKnowledge() + " " + skill.getDisplayName() + " Knowledge") - .build()); - } else { - ExperienceOrb.Data datax = ExperienceOrb.get(s.getItem()); - if (datax != null) { - datax.apply(p); - SoundNotification.builder() - .sound(Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM) - .volume(0.35f).pitch(1.455f) - .build().play(getPlayer(p)); - SoundNotification.builder() - .sound(Sound.ENTITY_SHULKER_OPEN) - .volume(1f).pitch(1.655f) - .build().play(getPlayer(p)); - } - } + PlayerData prefetched = takePrefetchedData(p.getUniqueId()); + AdaptPlayer a = new AdaptPlayer(p, prefetched); + players.put(p.getUniqueId(), a); + refreshOnlinePlayerSnapshots(); + a.loggedIn(); + } + + public void quit(UUID p) { + AdaptPlayer a = players.get(p); + if (a == null) return; + a.unregister(); + // Keep the entry briefly after quit so late quit listeners/tasks do not + // re-create a new AdaptPlayer for an offline player. + prefetchedPlayerData.invalidate(p); + refreshOnlinePlayerSnapshots(); + } + + @Override + public void unregister() { + new HashSet<>(players.keySet()).forEach(this::quit); + prefetchedPlayerData.invalidateAll(); + onlinePlayerSnapshot = List.of(); + onlineAdaptPlayerSnapshot = List.of(); + skillRegistry.unregister(); + save(); + super.unregister(); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void on(ProjectileLaunchEvent e) { + if (e.getEntity() instanceof Snowball s && e.getEntity().getShooter() instanceof Player p) { + KnowledgeOrb.Data data = KnowledgeOrb.get(s.getItem()); + if (data != null) { + Skill skill = getSkillRegistry().getSkill(data.getSkill()); + data.apply(p); + SoundNotification.builder() + .sound(Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM) + .volume(0.35f).pitch(1.455f) + .build().play(getPlayer(p)); + SoundNotification.builder() + .sound(Sound.ENTITY_SHULKER_OPEN) + .volume(1f).pitch(1.655f) + .build().play(getPlayer(p)); + getPlayer(p).getNot().queue(AdvancementNotification.builder() + .icon(Material.BOOK) + .model(CustomModel.get(Material.BOOK, "snippets", "gui", "knowledge")) + .title(C.GRAY + "+ " + C.WHITE + data.getKnowledge() + " " + skill.getDisplayName() + " Knowledge") + .build()); + } else { + ExperienceOrb.Data datax = ExperienceOrb.get(s.getItem()); + if (datax != null) { + datax.apply(p); + SoundNotification.builder() + .sound(Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM) + .volume(0.35f).pitch(1.455f) + .build().play(getPlayer(p)); + SoundNotification.builder() + .sound(Sound.ENTITY_SHULKER_OPEN) + .volume(1f).pitch(1.655f) + .build().play(getPlayer(p)); } - + } } - @EventHandler(priority = EventPriority.LOWEST) - public void on(PlayerJoinEvent e) { - Player p = e.getPlayer(); - join(p); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(AsyncPlayerPreLoginEvent e) { - if (e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { - return; - } + } - UUID uuid = e.getUniqueId(); - if (players.containsKey(uuid) || prefetchedPlayerData.getIfPresent(uuid) != null) { - return; - } + @EventHandler(priority = EventPriority.LOWEST) + public void on(PlayerJoinEvent e) { + Player p = e.getPlayer(); + join(p); + } - try { - prefetchedPlayerData.put(uuid, AdaptPlayer.loadPlayerData(uuid)); - } catch (Throwable ignored) { - Adapt.verbose("Failed to prefetch player data for " + uuid); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(AsyncPlayerPreLoginEvent e) { + if (e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - quit(p.getUniqueId()); + UUID uuid = e.getUniqueId(); + if (players.containsKey(uuid) || prefetchedPlayerData.getIfPresent(uuid) != null) { + return; } - @EventHandler - public void on(CraftItemEvent e) { - if (e.getWhoClicked() instanceof Player p) { - Adaptation required = getSkillRegistry().getRequiredAdaptation(e.getRecipe()); - if (required == null || required.hasAdaptation(p)) { - return; - } - - Skill requiredSkill = required.getSkill(); - String skillName = requiredSkill == null ? "Unknown Skill" : requiredSkill.getDisplayName(); - SoundPlayer sp = SoundPlayer.of(p); - Adapt.actionbar(p, C.RED + "Requires " + required.getDisplayName() + C.RED + " from " + skillName); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.5f, 1.8f); - e.setCancelled(true); - } + try { + prefetchedPlayerData.put(uuid, AdaptPlayer.loadPlayerData(uuid)); + } catch (Throwable ignored) { + Adapt.verbose("Failed to prefetch player data for " + uuid); } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + quit(p.getUniqueId()); + } + + @EventHandler + public void on(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player p) { + Adaptation required = getSkillRegistry().getRequiredAdaptation(e.getRecipe()); + if (required == null || required.hasAdaptation(p)) { + return; + } + + Skill requiredSkill = required.getSkill(); + String skillName = requiredSkill == null ? "Unknown Skill" : requiredSkill.getDisplayName(); + SoundPlayer sp = SoundPlayer.of(p); + Adapt.actionbar(p, C.RED + "Requires " + required.getDisplayName() + C.RED + " from " + skillName); + sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.5f, 1.8f); + e.setCancelled(true); + } + } - @Override - public void onTick() { - data.getMultipliers().removeIf(multiplier -> multiplier == null || multiplier.isExpired()); - - synchronized (spatialTickets) { - spatialTickets.removeIf(ticket -> M.ms() > ticket.getMs()); - } - - if (!clearLock.tryLock()) - return; - - try { - int sizeBefore = players.size(); - Iterator> iterator = players.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - AdaptPlayer player = entry.getValue(); - if (player == null) { - iterator.remove(); - prefetchedPlayerData.invalidate(entry.getKey()); - continue; - } - - if (!player.shouldUnload()) { - continue; - } - - player.unregister(); - iterator.remove(); - prefetchedPlayerData.invalidate(entry.getKey()); - } + @Override + public void onTick() { + data.getMultipliers().removeIf(multiplier -> multiplier == null || multiplier.isExpired()); - if (players.size() != sizeBefore) { - refreshOnlinePlayerSnapshots(); - } - } finally { - clearLock.unlock(); - } + synchronized (spatialTickets) { + spatialTickets.removeIf(ticket -> M.ms() > ticket.getMs()); } - public PlayerData peekData(UUID player) { - if (Bukkit.getPlayer(player) != null) { - return getPlayer(Bukkit.getPlayer(player)).getData(); + if (!clearLock.tryLock()) + return; + + try { + int sizeBefore = players.size(); + Iterator> iterator = players.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + AdaptPlayer player = entry.getValue(); + if (player == null) { + iterator.remove(); + prefetchedPlayerData.invalidate(entry.getKey()); + continue; } - if (AdaptConfig.get().isUseSql()) { - String sqlData = Adapt.instance.getSqlManager().fetchData(player); - if (sqlData != null) { - return Json.fromJson(sqlData, PlayerData.class); - } + if (!player.shouldUnload()) { + continue; } - File f = new File(Adapt.instance.getDataFolder("data", "players"), player + ".json"); - if (f.exists()) { - try { - return Json.fromJson(IO.readAll(f), PlayerData.class); - } catch (Throwable ignored) { - Adapt.verbose("Failed to load player data for " + player); - } - } + player.unregister(); + iterator.remove(); + prefetchedPlayerData.invalidate(entry.getKey()); + } - return new PlayerData(); + if (players.size() != sizeBefore) { + refreshOnlinePlayerSnapshots(); + } + } finally { + clearLock.unlock(); } + } - @NonNull - public Optional getPlayerData(@NonNull UUID uuid) { - return Optional.ofNullable(players.get(uuid)) - .map(AdaptPlayer::getData); + public PlayerData peekData(UUID player) { + if (Bukkit.getPlayer(player) != null) { + return getPlayer(Bukkit.getPlayer(player)).getData(); } - public AdaptPlayer getPlayer(Player p) { - AdaptPlayer existing = players.get(p.getUniqueId()); - if (existing != null) { - return existing; - } - - AdaptPlayer created = players.computeIfAbsent(p.getUniqueId(), player -> { - Adapt.warn("Failed to find AdaptPlayer for " + p.getName() + " (" + p.getUniqueId() + ")"); - Adapt.warn("Loading new AdaptPlayer..."); - return new AdaptPlayer(p, takePrefetchedData(player)); - }); - refreshOnlinePlayerSnapshots(); - return created; + if (AdaptConfig.get().isUseSql()) { + String sqlData = Adapt.instance.getSqlManager().fetchData(player); + if (sqlData != null) { + return Json.fromJson(sqlData, PlayerData.class); + } } - private PlayerData takePrefetchedData(UUID uuid) { - PlayerData prefetched = prefetchedPlayerData.getIfPresent(uuid); - if (prefetched != null) { - prefetchedPlayerData.invalidate(uuid); - } - return prefetched; + File f = new File(Adapt.instance.getDataFolder("data", "players"), player + ".json"); + if (f.exists()) { + try { + return Json.fromJson(IO.readAll(f), PlayerData.class); + } catch (Throwable ignored) { + Adapt.verbose("Failed to load player data for " + player); + } } - private void refreshOnlinePlayerSnapshots() { - ArrayList adaptPlayers = new ArrayList<>(players.size()); - ArrayList playerSnapshot = new ArrayList<>(players.size()); - - for (AdaptPlayer adaptPlayer : players.values()) { - if (adaptPlayer == null) { - continue; - } - Player online = adaptPlayer.getPlayer(); - if (online == null || !online.isOnline()) { - continue; - } - adaptPlayers.add(adaptPlayer); - playerSnapshot.add(online); - } - - onlineAdaptPlayerSnapshot = Collections.unmodifiableList(adaptPlayers); - onlinePlayerSnapshot = Collections.unmodifiableList(playerSnapshot); - } + return new PlayerData(); + } - public void openSkillGUI(Skill skill, Player p) { - skill.openGui(p); - } + @NonNull + public Optional getPlayerData(@NonNull UUID uuid) { + return Optional.ofNullable(players.get(uuid)) + .map(AdaptPlayer::getData); + } - public void openAdaptGui(Player p) { - SkillsGui.open(p); + public AdaptPlayer getPlayer(Player p) { + AdaptPlayer existing = players.get(p.getUniqueId()); + if (existing != null) { + return existing; } - public void openAdaptationGUI(Adaptation adaptation, Player p) { - adaptation.openGui(p); + AdaptPlayer created = players.computeIfAbsent(p.getUniqueId(), player -> { + Adapt.warn("Failed to find AdaptPlayer for " + p.getName() + " (" + p.getUniqueId() + ")"); + Adapt.warn("Loading new AdaptPlayer..."); + return new AdaptPlayer(p, takePrefetchedData(player)); + }); + refreshOnlinePlayerSnapshots(); + return created; + } + + private PlayerData takePrefetchedData(UUID uuid) { + PlayerData prefetched = prefetchedPlayerData.getIfPresent(uuid); + if (prefetched != null) { + prefetchedPlayerData.invalidate(uuid); } - - public void boostXP(double boost, int ms) { - data.getMultipliers().add(new XPMultiplier(boost, ms)); + return prefetched; + } + + private void refreshOnlinePlayerSnapshots() { + ArrayList adaptPlayers = new ArrayList<>(players.size()); + ArrayList playerSnapshot = new ArrayList<>(players.size()); + + for (AdaptPlayer adaptPlayer : players.values()) { + if (adaptPlayer == null) { + continue; + } + Player online = adaptPlayer.getPlayer(); + if (online == null || !online.isOnline()) { + continue; + } + adaptPlayers.add(adaptPlayer); + playerSnapshot.add(online); } - public void load() { - File f = new File(Adapt.instance.getDataFolder("data"), "server-data.json"); - if (f.exists()) { - try { - data = Json.fromJson(IO.readAll(f), AdaptServerData.class); - } catch (Throwable ignored) { - Adapt.verbose("Failed to load global boosts data"); - } - } + onlineAdaptPlayerSnapshot = Collections.unmodifiableList(adaptPlayers); + onlinePlayerSnapshot = Collections.unmodifiableList(playerSnapshot); + } + + public void openSkillGUI(Skill skill, Player p) { + skill.openGui(p); + } + + public void openAdaptGui(Player p) { + SkillsGui.open(p); + } + + public void openAdaptationGUI(Adaptation adaptation, Player p) { + adaptation.openGui(p); + } + + public void boostXP(double boost, int ms) { + data.getMultipliers().add(new XPMultiplier(boost, ms)); + } + + public void load() { + File f = new File(Adapt.instance.getDataFolder("data"), "server-data.json"); + if (f.exists()) { + try { + data = Json.fromJson(IO.readAll(f), AdaptServerData.class); + } catch (Throwable ignored) { + Adapt.verbose("Failed to load global boosts data"); + } } + } - @SneakyThrows - public void save() { - IO.writeAll(new File(Adapt.instance.getDataFolder("data"), "server-data.json"), Json.toJson(data, true)); - } + @SneakyThrows + public void save() { + IO.writeAll(new File(Adapt.instance.getDataFolder("data"), "server-data.json"), Json.toJson(data, true)); + } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java b/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java index 4ea6da3c1..c57115819 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServerData.java @@ -26,5 +26,5 @@ @Data @NoArgsConstructor public class AdaptServerData { - private KList multipliers = new KList<>(); + private KList multipliers = new KList<>(); } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java b/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java index 704875a27..2e3dcdc6d 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptStatTracker.java @@ -24,8 +24,8 @@ @Data @Builder public class AdaptStatTracker { - private String stat; - private double goal; - private double reward; - private String advancement; + private String stat; + private double goal; + private double reward; + private String advancement; } diff --git a/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java index a357ced67..0403c3df3 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java +++ b/src/main/java/art/arcane/adapt/api/world/AdvancementHandler.java @@ -25,21 +25,21 @@ @Data public class AdvancementHandler { - private AdaptPlayer player; - private boolean ready; + private AdaptPlayer player; + private boolean ready; - public AdvancementHandler(AdaptPlayer player) { - this.player = player; - ready = false; - instance.getManager().unlockExisting(player, this); - } + public AdvancementHandler(AdaptPlayer player) { + this.player = player; + ready = false; + instance.getManager().unlockExisting(player, this); + } - public void grant(String key, boolean toast) { - if (!AdaptConfig.get().isAdvancements()) return; - instance.getManager().grant(getPlayer(), key, toast); - } + public void grant(String key, boolean toast) { + if (!AdaptConfig.get().isAdvancements()) return; + instance.getManager().grant(getPlayer(), key, toast); + } - public void grant(String key) { - grant(key, true); - } + public void grant(String key) { + grant(key, true); + } } diff --git a/src/main/java/art/arcane/adapt/api/world/Discovery.java b/src/main/java/art/arcane/adapt/api/world/Discovery.java index c6e4cebf9..fa8bca5e3 100644 --- a/src/main/java/art/arcane/adapt/api/world/Discovery.java +++ b/src/main/java/art/arcane/adapt/api/world/Discovery.java @@ -22,10 +22,10 @@ import lombok.Getter; public class Discovery { - @Getter - private final KList seen = new KList<>(); + @Getter + private final KList seen = new KList<>(); - public boolean isNewDiscovery(T t) { - return seen.addIfMissing(t); - } + public boolean isNewDiscovery(T t) { + return seen.addIfMissing(t); + } } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java index 4ebd9c292..b63a8b23b 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerAdaptation.java @@ -23,7 +23,7 @@ @Data public class PlayerAdaptation { - private String id; - private int level; - private final KMap storage = new KMap<>(); + private final KMap storage = new KMap<>(); + private String id; + private int level; } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java index 358414b53..cf040d512 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -43,344 +43,344 @@ @Data @NoArgsConstructor public class PlayerData { - private final KMap skillLines = new KMap<>(); - private KMap stats = new KMap<>(); - private String last = "none"; - private KSet advancements = new KSet<>(); - private Discovery seenBiomes = new Discovery<>(); - private Discovery seenMobs = new Discovery<>(); - private Discovery seenFoods = new Discovery<>(); - private Discovery seenItems = new Discovery<>(); - private Discovery seenRecipes = new Discovery<>(); - private Discovery seenEnchants = new Discovery<>(); - private Discovery seenWorlds = new Discovery<>(); - private Discovery seenPeople = new Discovery<>(); - private Discovery seenEnvironments = new Discovery<>(); - private Discovery seenPotionEffects = new Discovery<>(); - private Discovery seenBlocks = new Discovery<>(); - private KList multipliers = new KList<>(); - private long wisdom = 0; - private double multiplier = 0; - private long lastLogin = 0; - private double masterXp = 1; - private double lastMasterXp = 0; - - public void giveMasterXp(double xp) { - masterXp += xp; + private final KMap skillLines = new KMap<>(); + private KMap stats = new KMap<>(); + private String last = "none"; + private KSet advancements = new KSet<>(); + private Discovery seenBiomes = new Discovery<>(); + private Discovery seenMobs = new Discovery<>(); + private Discovery seenFoods = new Discovery<>(); + private Discovery seenItems = new Discovery<>(); + private Discovery seenRecipes = new Discovery<>(); + private Discovery seenEnchants = new Discovery<>(); + private Discovery seenWorlds = new Discovery<>(); + private Discovery seenPeople = new Discovery<>(); + private Discovery seenEnvironments = new Discovery<>(); + private Discovery seenPotionEffects = new Discovery<>(); + private Discovery seenBlocks = new Discovery<>(); + private KList multipliers = new KList<>(); + private long wisdom = 0; + private double multiplier = 0; + private long lastLogin = 0; + private double masterXp = 1; + private double lastMasterXp = 0; + + public static PlayerData fromJson(String json) { + return Json.fromJson(json, PlayerData.class); + } + + public void giveMasterXp(double xp) { + masterXp += xp; + } + + public void globalXPMultiplier(double v, int duration) { + multipliers.add(new XPMultiplier(v, duration)); + } + + public boolean isGranted(String advancement) { + return advancements.contains(advancement); + } + + public void ensureGranted(String advancement) { + advancements.add(advancement); + } + + public double getStat(String key) { + Double d = stats.get(key); + return d == null ? 0 : d; + } + + public void addStat(String key, double amt) { + if (!stats.containsKey(key)) { + stats.put(key, amt); + } else { + stats.put(key, stats.get(key) + amt); } + } - public void globalXPMultiplier(double v, int duration) { - multipliers.add(new XPMultiplier(v, duration)); - } - - public boolean isGranted(String advancement) { - return advancements.contains(advancement); - } + public void update(AdaptPlayer p) { + double m = 1D; + m += collectActivePlayerMultiplierBonus(); + m += collectGlobalMultiplierBonus(); - public void ensureGranted(String advancement) { - advancements.add(advancement); + if (m <= 0) { + m = 0.01; } - public double getStat(String key) { - Double d = stats.get(key); - return d == null ? 0 : d; - } - - public void addStat(String key, double amt) { - if (!stats.containsKey(key)) { - stats.put(key, amt); - } else { - stats.put(key, stats.get(key) + amt); - } - } - - public void update(AdaptPlayer p) { - double m = 1D; - m += collectActivePlayerMultiplierBonus(); - m += collectGlobalMultiplierBonus(); - - if (m <= 0) { - m = 0.01; - } - - if (m > 1000) { - m = 1000; - } - - multiplier = m; - - for (var entry : skillLines.entrySet()) { - String lineId = entry.getKey(); - Skill loadedSkill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(lineId); - if (loadedSkill == null) { - // Never prune unknown lines automatically; missing skills can be transient - // during startup/reload or due temporary config disables. - continue; - } - - PlayerSkillLine lineData = entry.getValue(); - if (lineData == null) { - skillLines.remove(lineId); - continue; - } - - if (lineData.getXp() == 0 && lineData.getKnowledge() == 0) { - skillLines.remove(lineId, lineData); - continue; - } - - lineData.update(p, lineId, this); - } - - int oldLevel = (int) XP.getLevelForXp(getLastMasterXp()); - int level = (int) XP.getLevelForXp(getMasterXp()); - - if (oldLevel != level) { - setLastMasterXp(getMasterXp()); - p.getNot().queue(SoundNotification.builder() - .sound(Sound.BLOCK_ENCHANTMENT_TABLE_USE) - .volume(1f) - .pitch(0.54f) - .group("lvl") - .build(), - SoundNotification.builder() - .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) - .volume(1f) - .pitch(0.44f) - .group("lvl") - .build(), - SoundNotification.builder() - .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) - .volume(1f) - .pitch(0.74f) - .group("lvl") - .build(), - SoundNotification.builder() - .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) - .volume(1f) - .pitch(1.34f) - .group("lvl") - .build(), - TitleNotification.builder() - .in(250) - .stay(1450) - .out(2250) - .group("lvl") - .title("") - .subtitle(C.GOLD + Localizer.dLocalize("snippets.gui.level") +" " + level)// I'm sorry I missed this! - .build()); - p.getActionBarNotifier().queue( - ActionBarNotification.builder() - .duration(450) - .group("power") - .title(C.GOLD + "" + Form.f(level * AdaptConfig.get().getPowerPerLevel(), 0) + C.GRAY + " " + Localizer.dLocalize("snippets.gui.max_ability_power")) // I'm sorry I missed this! - .build()); - - } + if (m > 1000) { + m = 1000; } - private double collectActivePlayerMultiplierBonus() { - double bonus = 0D; - for (int i = multipliers.size() - 1; i >= 0; i--) { - XPMultiplier active = multipliers.get(i); - if (active == null || active.isExpired()) { - multipliers.remove(i); - continue; - } - bonus += active.getMultiplier(); - } - return bonus; + multiplier = m; + + for (java.util.Map.Entry entry : skillLines.entrySet()) { + String lineId = entry.getKey(); + Skill loadedSkill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(lineId); + if (loadedSkill == null) { + // Never prune unknown lines automatically; missing skills can be transient + // during startup/reload or due temporary config disables. + continue; + } + + PlayerSkillLine lineData = entry.getValue(); + if (lineData == null) { + skillLines.remove(lineId); + continue; + } + + if (lineData.getXp() == 0 && lineData.getKnowledge() == 0) { + skillLines.remove(lineId, lineData); + continue; + } + + lineData.update(p, lineId, this); } - private double collectGlobalMultiplierBonus() { - double bonus = 0D; - KList globalMultipliers = Adapt.instance.getAdaptServer().getData().getMultipliers(); - for (int i = 0; i < globalMultipliers.size(); i++) { - XPMultiplier active = globalMultipliers.get(i); - if (active == null || active.isExpired()) { - continue; - } - bonus += active.getMultiplier(); - } - return bonus; - } + int oldLevel = (int) XP.getLevelForXp(getLastMasterXp()); + int level = (int) XP.getLevelForXp(getMasterXp()); + + if (oldLevel != level) { + setLastMasterXp(getMasterXp()); + p.getNot().queue(SoundNotification.builder() + .sound(Sound.BLOCK_ENCHANTMENT_TABLE_USE) + .volume(1f) + .pitch(0.54f) + .group("lvl") + .build(), + SoundNotification.builder() + .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) + .volume(1f) + .pitch(0.44f) + .group("lvl") + .build(), + SoundNotification.builder() + .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) + .volume(1f) + .pitch(0.74f) + .group("lvl") + .build(), + SoundNotification.builder() + .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) + .volume(1f) + .pitch(1.34f) + .group("lvl") + .build(), + TitleNotification.builder() + .in(250) + .stay(1450) + .out(2250) + .group("lvl") + .title("") + .subtitle(C.GOLD + Localizer.dLocalize("snippets.gui.level") + " " + level)// I'm sorry I missed this! + .build()); + p.getActionBarNotifier().queue( + ActionBarNotification.builder() + .duration(450) + .group("power") + .title(C.GOLD + "" + Form.f(level * AdaptConfig.get().getPowerPerLevel(), 0) + C.GRAY + " " + Localizer.dLocalize("snippets.gui.max_ability_power")) // I'm sorry I missed this! + .build()); - public int getAvailablePower() { - return getMaxPower() - getUsedPower(); } - - public boolean hasPowerAvailable() { - return hasPowerAvailable(1); + } + + private double collectActivePlayerMultiplierBonus() { + double bonus = 0D; + for (int i = multipliers.size() - 1; i >= 0; i--) { + XPMultiplier active = multipliers.get(i); + if (active == null || active.isExpired()) { + multipliers.remove(i); + continue; + } + bonus += active.getMultiplier(); } - - public boolean hasPowerAvailable(int amount) { - return getAvailablePower() >= amount; + return bonus; + } + + private double collectGlobalMultiplierBonus() { + double bonus = 0D; + KList globalMultipliers = Adapt.instance.getAdaptServer().getData().getMultipliers(); + for (int i = 0; i < globalMultipliers.size(); i++) { + XPMultiplier active = globalMultipliers.get(i); + if (active == null || active.isExpired()) { + continue; + } + bonus += active.getMultiplier(); } - - public int getUsedPower() { - int usedPower = 0; - for (PlayerSkillLine line : skillLines.values()) { - if (line == null) { - continue; - } - - for (PlayerAdaptation adaptation : line.getAdaptations().values()) { - if (adaptation == null) { - continue; - } - usedPower += adaptation.getLevel(); - } + return bonus; + } + + public int getAvailablePower() { + return getMaxPower() - getUsedPower(); + } + + public boolean hasPowerAvailable() { + return hasPowerAvailable(1); + } + + public boolean hasPowerAvailable(int amount) { + return getAvailablePower() >= amount; + } + + public int getUsedPower() { + int usedPower = 0; + for (PlayerSkillLine line : skillLines.values()) { + if (line == null) { + continue; + } + + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation == null) { + continue; } - return usedPower; - } - - public int getLevel() { - return (int) XP.getLevelForXp(getMasterXp()); + usedPower += adaptation.getLevel(); + } } + return usedPower; + } - public int getMaxPower() { - return (int) (XP.getLevelForXp(getMasterXp()) * AdaptConfig.get().getPowerPerLevel()); - } + public int getLevel() { + return (int) XP.getLevelForXp(getMasterXp()); + } - public PlayerSkillLine getSkillLine(String skillLine) { - if (Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillLine) == null) { - return null; - } + public int getMaxPower() { + return (int) (XP.getLevelForXp(getMasterXp()) * AdaptConfig.get().getPowerPerLevel()); + } - synchronized (skillLines) { - try { - PlayerSkillLine s = skillLines.get(skillLine); - - if (s != null) { - return s; - } - } catch (Throwable e) { - e.printStackTrace(); - Adapt.error("Failed to get skill line " + skillLine); - } - - PlayerSkillLine s = new PlayerSkillLine(); - s.setLine(skillLine); - skillLines.put(skillLine, s); - return s; - } + public PlayerSkillLine getSkillLine(String skillLine) { + if (Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillLine) == null) { + return null; } - public PlayerSkillLine getSkillLineNullable(String skillLine) { - return skillLines.get(skillLine); - } + synchronized (skillLines) { + try { + PlayerSkillLine s = skillLines.get(skillLine); - public void resetMonotonyForOtherSkills(String currentSkill) { - for (PlayerSkillLine line : skillLines.values()) { - if (!line.getLine().equals(currentSkill)) { - line.relaxStalenessForActivitySwitch(); - } + if (s != null) { + return s; } + } catch (Throwable e) { + e.printStackTrace(); + Adapt.error("Failed to get skill line " + skillLine); + } + + PlayerSkillLine s = new PlayerSkillLine(); + s.setLine(skillLine); + skillLines.put(skillLine, s); + return s; } + } - public void addWisdom() { - wisdom++; - } + public PlayerSkillLine getSkillLineNullable(String skillLine) { + return skillLines.get(skillLine); + } - public void clearXp() { - for (PlayerSkillLine line : skillLines.values()) { - line.setXp(0); - line.setLastXP(0); - line.setLastLevel(0); - line.setMonotonyCounter(0); - line.setMonotonyMultiplier(1.0); - line.setLastXpTimestamp(0); - line.setSkillStaleness(new PlayerSkillLine.RewardStalenessState()); - line.getActivityStaleness().clear(); - line.getAdaptations().clear(); - } - masterXp = 1; - lastMasterXp = 0; + public void resetMonotonyForOtherSkills(String currentSkill) { + for (PlayerSkillLine line : skillLines.values()) { + if (!line.getLine().equals(currentSkill)) { + line.relaxStalenessForActivitySwitch(); + } } - - public void clearKnowledge() { - for (PlayerSkillLine line : skillLines.values()) { - line.setKnowledge(0); - } + } + + public void addWisdom() { + wisdom++; + } + + public void clearXp() { + for (PlayerSkillLine line : skillLines.values()) { + line.setXp(0); + line.setLastXP(0); + line.setLastLevel(0); + line.setMonotonyCounter(0); + line.setMonotonyMultiplier(1.0); + line.setLastXpTimestamp(0); + line.setSkillStaleness(new PlayerSkillLine.RewardStalenessState()); + line.getActivityStaleness().clear(); + line.getAdaptations().clear(); } + masterXp = 1; + lastMasterXp = 0; + } - public void clearAdaptations() { - for (PlayerSkillLine line : skillLines.values()) { - line.getAdaptations().clear(); - } - } - - public void clearStats() { - stats.clear(); - } - - public void clearDiscoveries() { - seenBiomes = new Discovery<>(); - seenMobs = new Discovery<>(); - seenFoods = new Discovery<>(); - seenItems = new Discovery<>(); - seenRecipes = new Discovery<>(); - seenEnchants = new Discovery<>(); - seenWorlds = new Discovery<>(); - seenPeople = new Discovery<>(); - seenEnvironments = new Discovery<>(); - seenPotionEffects = new Discovery<>(); - seenBlocks = new Discovery<>(); + public void clearKnowledge() { + for (PlayerSkillLine line : skillLines.values()) { + line.setKnowledge(0); } + } - public void pruneAdaptationsForPowerBudget() { - int usedPower = getUsedPower(); - int maxPower = getMaxPower(); - - while (usedPower > maxPower) { - String worstSkill = null; - String worstAdaptation = null; - int worstLevel = Integer.MAX_VALUE; - - for (var skillEntry : skillLines.entrySet()) { - for (var adaptEntry : skillEntry.getValue().getAdaptations().entrySet()) { - int level = adaptEntry.getValue().getLevel(); - if (level > 0 && level < worstLevel) { - worstLevel = level; - worstSkill = skillEntry.getKey(); - worstAdaptation = adaptEntry.getKey(); - } - } - } - - if (worstSkill == null) { - break; - } - - PlayerAdaptation adapt = skillLines.get(worstSkill).getAdaptations().get(worstAdaptation); - if (adapt.getLevel() <= 1) { - skillLines.get(worstSkill).getAdaptations().remove(worstAdaptation); - usedPower -= 1; - } else { - adapt.setLevel(adapt.getLevel() - 1); - usedPower -= 1; - } - } - } - - public void clearAll() { - clearXp(); - clearKnowledge(); - clearAdaptations(); - clearStats(); - clearDiscoveries(); - advancements.clear(); - multipliers.clear(); - wisdom = 0; + public void clearAdaptations() { + for (PlayerSkillLine line : skillLines.values()) { + line.getAdaptations().clear(); } - - public String toJson(boolean raw) { - synchronized (skillLines) { - return Json.toJson(this, !raw); + } + + public void clearStats() { + stats.clear(); + } + + public void clearDiscoveries() { + seenBiomes = new Discovery<>(); + seenMobs = new Discovery<>(); + seenFoods = new Discovery<>(); + seenItems = new Discovery<>(); + seenRecipes = new Discovery<>(); + seenEnchants = new Discovery<>(); + seenWorlds = new Discovery<>(); + seenPeople = new Discovery<>(); + seenEnvironments = new Discovery<>(); + seenPotionEffects = new Discovery<>(); + seenBlocks = new Discovery<>(); + } + + public void pruneAdaptationsForPowerBudget() { + int usedPower = getUsedPower(); + int maxPower = getMaxPower(); + + while (usedPower > maxPower) { + String worstSkill = null; + String worstAdaptation = null; + int worstLevel = Integer.MAX_VALUE; + + for (java.util.Map.Entry skillEntry : skillLines.entrySet()) { + for (java.util.Map.Entry adaptEntry : skillEntry.getValue().getAdaptations().entrySet()) { + int level = adaptEntry.getValue().getLevel(); + if (level > 0 && level < worstLevel) { + worstLevel = level; + worstSkill = skillEntry.getKey(); + worstAdaptation = adaptEntry.getKey(); + } } + } + + if (worstSkill == null) { + break; + } + + PlayerAdaptation adapt = skillLines.get(worstSkill).getAdaptations().get(worstAdaptation); + if (adapt.getLevel() <= 1) { + skillLines.get(worstSkill).getAdaptations().remove(worstAdaptation); + usedPower -= 1; + } else { + adapt.setLevel(adapt.getLevel() - 1); + usedPower -= 1; + } } - - public static PlayerData fromJson(String json) { - return Json.fromJson(json, PlayerData.class); + } + + public void clearAll() { + clearXp(); + clearKnowledge(); + clearAdaptations(); + clearStats(); + clearDiscoveries(); + advancements.clear(); + multipliers.clear(); + wisdom = 0; + } + + public String toJson(boolean raw) { + synchronized (skillLines) { + return Json.toJson(this, !raw); } + } } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java index 631917b8f..bb866bdbf 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerDataPersistenceQueue.java @@ -10,95 +10,95 @@ import java.util.concurrent.atomic.AtomicBoolean; public class PlayerDataPersistenceQueue implements AutoCloseable { - private static final long DEFAULT_SHUTDOWN_TIMEOUT_MS = 30_000L; + private static final long DEFAULT_SHUTDOWN_TIMEOUT_MS = 30_000L; - private final ExecutorService ioExecutor; - private final AtomicBoolean acceptingTasks = new AtomicBoolean(true); + private final ExecutorService ioExecutor; + private final AtomicBoolean acceptingTasks = new AtomicBoolean(true); - public PlayerDataPersistenceQueue() { - ioExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { - private int tid = 0; + public PlayerDataPersistenceQueue() { + ioExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() { + private int tid = 0; - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread(runnable, "Adapt PlayerData IO " + (++tid)); - thread.setDaemon(true); - thread.setUncaughtExceptionHandler((t, e) -> - Adapt.warn("Uncaught async persistence exception in " + t.getName() + ": " + e.getMessage())); - return thread; - } - }); - } + @Override + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Adapt PlayerData IO " + (++tid)); + thread.setDaemon(true); + thread.setUncaughtExceptionHandler((t, e) -> + Adapt.warn("Uncaught async persistence exception in " + t.getName() + ": " + e.getMessage())); + return thread; + } + }); + } - public void queueSave(UUID uuid, String json, File localFile) { - submit("save", uuid, () -> { - if (AdaptConfig.get().isUseSql()) { - if (Adapt.instance.getRedisSync() != null) { - Adapt.instance.getRedisSync().publish(uuid, json); - } - if (Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().updateData(uuid, json); - } - return; - } + public void queueSave(UUID uuid, String json, File localFile) { + submit("save", uuid, () -> { + if (AdaptConfig.get().isUseSql()) { + if (Adapt.instance.getRedisSync() != null) { + Adapt.instance.getRedisSync().publish(uuid, json); + } + if (Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().updateData(uuid, json); + } + return; + } - IO.writeAll(localFile, json); - }); - } + IO.writeAll(localFile, json); + }); + } - public void queueDelete(UUID uuid, File localFile) { - submit("delete", uuid, () -> { - if (localFile.exists() && !localFile.delete()) { - Adapt.verbose("Failed to delete local player data file " + localFile.getAbsolutePath()); - } + public void queueDelete(UUID uuid, File localFile) { + submit("delete", uuid, () -> { + if (localFile.exists() && !localFile.delete()) { + Adapt.verbose("Failed to delete local player data file " + localFile.getAbsolutePath()); + } - if (AdaptConfig.get().isUseSql() && Adapt.instance.getSqlManager() != null) { - Adapt.instance.getSqlManager().delete(uuid); - } - }); - } + if (AdaptConfig.get().isUseSql() && Adapt.instance.getSqlManager() != null) { + Adapt.instance.getSqlManager().delete(uuid); + } + }); + } - public void flushAndShutdown(long timeoutMs) { - acceptingTasks.set(false); - ioExecutor.shutdown(); - try { - if (!ioExecutor.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS)) { - Adapt.warn("Timed out waiting for player data persistence queue to drain. Forcing shutdown."); - ioExecutor.shutdownNow(); - ioExecutor.awaitTermination(Math.max(1000, timeoutMs / 2), TimeUnit.MILLISECONDS); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Adapt.warn("Interrupted while shutting down player data persistence queue."); - ioExecutor.shutdownNow(); - } + public void flushAndShutdown(long timeoutMs) { + acceptingTasks.set(false); + ioExecutor.shutdown(); + try { + if (!ioExecutor.awaitTermination(timeoutMs, TimeUnit.MILLISECONDS)) { + Adapt.warn("Timed out waiting for player data persistence queue to drain. Forcing shutdown."); + ioExecutor.shutdownNow(); + ioExecutor.awaitTermination(Math.max(1000, timeoutMs / 2), TimeUnit.MILLISECONDS); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + Adapt.warn("Interrupted while shutting down player data persistence queue."); + ioExecutor.shutdownNow(); } + } - @Override - public void close() { - flushAndShutdown(DEFAULT_SHUTDOWN_TIMEOUT_MS); - } + @Override + public void close() { + flushAndShutdown(DEFAULT_SHUTDOWN_TIMEOUT_MS); + } - private void submit(String operation, UUID uuid, ThrowingRunnable runnable) { - if (!acceptingTasks.get()) { - return; - } + private void submit(String operation, UUID uuid, ThrowingRunnable runnable) { + if (!acceptingTasks.get()) { + return; + } + try { + ioExecutor.execute(() -> { try { - ioExecutor.execute(() -> { - try { - runnable.run(); - } catch (Throwable e) { - Adapt.warn("Failed to " + operation + " player data for " + uuid + ": " + e.getMessage()); - } - }); - } catch (RejectedExecutionException ignored) { - Adapt.verbose("Rejected player data " + operation + " task for " + uuid + " because the queue is shutting down."); + runnable.run(); + } catch (Throwable e) { + Adapt.warn("Failed to " + operation + " player data for " + uuid + ": " + e.getMessage()); } + }); + } catch (RejectedExecutionException ignored) { + Adapt.verbose("Rejected player data " + operation + " task for " + uuid + " because the queue is shutting down."); } + } - @FunctionalInterface - private interface ThrowingRunnable { - void run() throws Exception; - } + @FunctionalInterface + private interface ThrowingRunnable { + void run() throws Exception; + } } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index 169e4eb65..2def63aa0 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -38,420 +38,423 @@ @Data @NoArgsConstructor public class PlayerSkillLine { - private String line = ""; - private double xp = 0; - private double lastXP = 0; - private long knowledge = 0; - private double multiplier = 1D; - private double freshness = 1D; - private double rfreshness = 1D; - private int lastLevel = 0; - private long last = M.ms(); - private int monotonyCounter = 0; - private long lastXpTimestamp = 0; - private double monotonyMultiplier = 1.0; - private RewardStalenessState skillStaleness = new RewardStalenessState(); - private long lastStalenessCleanup = 0; - private final KMap storage = new KMap<>(); - private final KMap adaptations = new KMap<>(); - private final KList multipliers = new KList<>(); - private final KMap activityStaleness = new KMap<>(); - - private static double diff(long a, long b) { - return Math.abs(a - b / (double) (a == 0 ? 1 : a)); + private final KMap storage = new KMap<>(); + private final KMap adaptations = new KMap<>(); + private final KList multipliers = new KList<>(); + private final KMap activityStaleness = new KMap<>(); + private String line = ""; + private double xp = 0; + private double lastXP = 0; + private long knowledge = 0; + private double multiplier = 1D; + private double freshness = 1D; + private double rfreshness = 1D; + private int lastLevel = 0; + private long last = M.ms(); + private int monotonyCounter = 0; + private long lastXpTimestamp = 0; + private double monotonyMultiplier = 1.0; + private RewardStalenessState skillStaleness = new RewardStalenessState(); + private long lastStalenessCleanup = 0; + + private static double diff(long a, long b) { + return Math.abs(a - b / (double) (a == 0 ? 1 : a)); + } + + public void update(AdaptPlayer p, String line, PlayerData data) { + grantSkillsAndAdaptations(p, line); + checkMaxLevel(p, line); + updateFreshness(); + updateMultiplier(data); + updateEarnedXP(p, line); + updateLevel(p, line, data); + } + + private void grantSkillsAndAdaptations(AdaptPlayer p, String line) { + if (!p.getData().isGranted("skill_" + line) && AdaptConfig.get().isAdvancements()) { + p.getAdvancementHandler().grant("skill_" + line); + } + + for (String i : getAdaptations().keySet()) { + if (!p.getData().isGranted("adaptation_" + i) && AdaptConfig.get().isAdvancements()) { + p.getAdvancementHandler().grant("adaptation_" + i); + } + } + } + + private void checkMaxLevel(AdaptPlayer p, String line) { + if (!p.isBusy() && getXp() > XP.getXpForLevel(AdaptConfig.get().experienceMaxLevel)) { + p.getData().addWisdom(); + Adapt.warn("A Player has reached the maximum level of " + AdaptConfig.get().experienceMaxLevel + " and has been granted 1 wisdom, Dropping Level to " + lastLevel); + setXp(XP.getXpForLevel(AdaptConfig.get().experienceMaxLevel - 1)); + } + } + + private void updateFreshness() { + double max = 1D + (getLevel() * 0.004); + + freshness += (0.08 * freshness) + 0.003; + if (freshness > max) freshness = max; + if (freshness < 0.01) freshness = 0.01; + if (freshness < rfreshness) + rfreshness -= ((rfreshness - freshness) * 0.003); + if (freshness > rfreshness) rfreshness += (freshness - rfreshness) * 0.265; + } + + private void updateMultiplier(PlayerData data) { + double m = rfreshness; + for (int i = multipliers.size() - 1; i >= 0; i--) { + XPMultiplier active = multipliers.get(i); + if (active == null || active.isExpired()) { + multipliers.remove(i); + continue; + } + m += active.getMultiplier(); + } + + m = Math.max(0.01, Math.min(m, 1000)); + multiplier = m * data.getMultiplier(); + } + + private void updateEarnedXP(AdaptPlayer p, String line) { + double earned = xp - lastXP; + if (earned > p.getServer().getSkillRegistry().getSkill(line).getMinXp()) + lastXP = xp; + } + + private void updateLevel(AdaptPlayer p, String line, PlayerData data) { + if (lastLevel < getLevel()) { + long kb = getKnowledge(); + for (int i = lastLevel; i < getLevel(); i++) { + giveKnowledge((i / 13) + 1); + p.getData().giveMasterXp((i * AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()) + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); + } + + if (AdaptConfig.get().isActionbarNotifyLevel()) + notifyLevel(p, getLevel(), getKnowledge()); + lastLevel = getLevel(); + } + } + + public void giveXP(Notifier p, double xp) { + giveXP(p, xp, null); + } + + public void giveXP(Notifier p, double xp, String rewardKey) { + freshness -= 0.012 + (xp * 0.00025); + + long now = System.currentTimeMillis(); + lastXpTimestamp = now; + monotonyCounter++; + monotonyMultiplier = computeStalenessMultiplier(xp, rewardKey, now); + + xp = multiplier * monotonyMultiplier * xp; + this.xp += xp; + + if (p != null) { + last = M.ms(); + if (AdaptConfig.get().isActionbarNotifyXp()) { + p.notifyXP(line, xp); + } + } + } + + public void relaxStalenessForActivitySwitch() { + AdaptConfig.FarmPrevention prevention = AdaptConfig.get().getFarmPrevention(); + if (prevention == null || !prevention.isEnabled()) { + monotonyCounter = 0; + monotonyMultiplier = 1.0; + return; + } + + double factor = clamp(prevention.getCrossSkillRecoveryFactor(), 0.0, 1.0); + applyRecoveryFactor(skillStaleness, factor); + for (RewardStalenessState state : activityStaleness.values()) { + applyRecoveryFactor(state, factor); + } + monotonyCounter = 0; + } + + private double computeStalenessMultiplier(double awardXp, String rewardKey, long now) { + if (awardXp <= 0) { + return 1.0; + } + + AdaptConfig.FarmPrevention prevention = AdaptConfig.get().getFarmPrevention(); + if (prevention == null || !prevention.isEnabled()) { + return 1.0; + } + + double skillGain = prevention.getSkillBasePressure() + (awardXp * prevention.getSkillXpPressure()); + double skillMultiplier = applyStaleness( + ensureSkillStaleness(), + now, + skillGain, + prevention.getSkillRecoveryMillis(), + prevention.getSkillDecayCurve(), + prevention.getSkillFloorMultiplier() + ); + + double activityMultiplier = 1.0; + if (prevention.isPerActivityTracking()) { + String normalizedRewardKey = normalizeRewardKey(rewardKey); + if (normalizedRewardKey != null) { + cleanupActivityStaleness(now, prevention.getActivityStateTtlMillis()); + RewardStalenessState activityState = activityStaleness.computeIfAbsent(normalizedRewardKey, k -> new RewardStalenessState()); + double activityGain = prevention.getActivityBasePressure() + (awardXp * prevention.getActivityXpPressure()); + activityMultiplier = applyStaleness( + activityState, + now, + activityGain, + prevention.getActivityRecoveryMillis(), + prevention.getActivityDecayCurve(), + prevention.getActivityFloorMultiplier() + ); + } } - public void update(AdaptPlayer p, String line, PlayerData data) { - grantSkillsAndAdaptations(p, line); - checkMaxLevel(p, line); - updateFreshness(); - updateMultiplier(data); - updateEarnedXP(p, line); - updateLevel(p, line, data); + double floor = clamp(prevention.getSkillFloorMultiplier(), 0.0, 1.0); + if (prevention.isPerActivityTracking()) { + floor = clamp(floor * prevention.getActivityFloorMultiplier(), 0.0, 1.0); } + return clamp(skillMultiplier * activityMultiplier, floor, 1.0); + } - private void grantSkillsAndAdaptations(AdaptPlayer p, String line) { - if (!p.getData().isGranted("skill_" + line) && AdaptConfig.get().isAdvancements()) { - p.getAdvancementHandler().grant("skill_" + line); - } - - for (String i : getAdaptations().keySet()) { - if (!p.getData().isGranted("adaptation_" + i) && AdaptConfig.get().isAdvancements()) { - p.getAdvancementHandler().grant("adaptation_" + i); - } - } + private RewardStalenessState ensureSkillStaleness() { + if (skillStaleness == null) { + skillStaleness = new RewardStalenessState(); } + return skillStaleness; + } - private void checkMaxLevel(AdaptPlayer p, String line) { - if (!p.isBusy() && getXp() > XP.getXpForLevel(AdaptConfig.get().experienceMaxLevel)) { - p.getData().addWisdom(); - Adapt.warn("A Player has reached the maximum level of " + AdaptConfig.get().experienceMaxLevel + " and has been granted 1 wisdom, Dropping Level to " + lastLevel); - setXp(XP.getXpForLevel(AdaptConfig.get().experienceMaxLevel - 1)); - } + private double applyStaleness(RewardStalenessState state, long now, double gain, long recoveryMillis, double curve, double floor) { + if (state == null) { + return 1.0; } - private void updateFreshness() { - double max = 1D + (getLevel() * 0.004); + decayState(state, now, recoveryMillis); + state.setPressure(clamp(state.getPressure() + Math.max(0.0, gain), 0.0, 100000.0)); + state.setLastAwardAt(now); - freshness += (0.08 * freshness) + 0.003; - if (freshness > max) freshness = max; - if (freshness < 0.01) freshness = 0.01; - if (freshness < rfreshness) rfreshness -= ((rfreshness - freshness) * 0.003); - if (freshness > rfreshness) rfreshness += (freshness - rfreshness) * 0.265; + double clampedFloor = clamp(floor, 0.0, 1.0); + if (curve <= 0) { + return 1.0; } - private void updateMultiplier(PlayerData data) { - double m = rfreshness; - for (int i = multipliers.size() - 1; i >= 0; i--) { - XPMultiplier active = multipliers.get(i); - if (active == null || active.isExpired()) { - multipliers.remove(i); - continue; - } - m += active.getMultiplier(); - } - - m = Math.max(0.01, Math.min(m, 1000)); - multiplier = m * data.getMultiplier(); - } + double scaled = Math.exp(-state.getPressure() / curve); + return clamp(clampedFloor + ((1.0 - clampedFloor) * scaled), clampedFloor, 1.0); + } - private void updateEarnedXP(AdaptPlayer p, String line) { - double earned = xp - lastXP; - if (earned > p.getServer().getSkillRegistry().getSkill(line).getMinXp()) lastXP = xp; + private void decayState(RewardStalenessState state, long now, long recoveryMillis) { + if (state == null || recoveryMillis <= 0) { + return; } - private void updateLevel(AdaptPlayer p, String line, PlayerData data) { - if (lastLevel < getLevel()) { - long kb = getKnowledge(); - for (int i = lastLevel; i < getLevel(); i++) { - giveKnowledge((i / 13) + 1); - p.getData().giveMasterXp((i * AdaptConfig.get().getPlayerXpPerSkillLevelUpLevelMultiplier()) + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); - } - - if (AdaptConfig.get().isActionbarNotifyLevel()) notifyLevel(p, getLevel(), getKnowledge()); - lastLevel = getLevel(); - } + long lastAward = state.getLastAwardAt(); + if (lastAward <= 0) { + state.setLastAwardAt(now); + return; } - public void giveXP(Notifier p, double xp) { - giveXP(p, xp, null); + long elapsed = Math.max(0, now - lastAward); + if (elapsed == 0) { + return; } - public void giveXP(Notifier p, double xp, String rewardKey) { - freshness -= 0.012 + (xp * 0.00025); - - long now = System.currentTimeMillis(); - lastXpTimestamp = now; - monotonyCounter++; - monotonyMultiplier = computeStalenessMultiplier(xp, rewardKey, now); + double decay = Math.exp(-(double) elapsed / (double) recoveryMillis); + state.setPressure(Math.max(0.0, state.getPressure() * decay)); + } - xp = multiplier * monotonyMultiplier * xp; - this.xp += xp; - - if (p != null) { - last = M.ms(); - if (AdaptConfig.get().isActionbarNotifyXp()) { - p.notifyXP(line, xp); - } - } + private void cleanupActivityStaleness(long now, long ttl) { + if (ttl <= 0) { + return; } - - public void relaxStalenessForActivitySwitch() { - AdaptConfig.FarmPrevention prevention = AdaptConfig.get().getFarmPrevention(); - if (prevention == null || !prevention.isEnabled()) { - monotonyCounter = 0; - monotonyMultiplier = 1.0; - return; - } - - double factor = clamp(prevention.getCrossSkillRecoveryFactor(), 0.0, 1.0); - applyRecoveryFactor(skillStaleness, factor); - for (RewardStalenessState state : activityStaleness.values()) { - applyRecoveryFactor(state, factor); - } - monotonyCounter = 0; + if (now - lastStalenessCleanup < 15000) { + return; } - private double computeStalenessMultiplier(double awardXp, String rewardKey, long now) { - if (awardXp <= 0) { - return 1.0; - } - - AdaptConfig.FarmPrevention prevention = AdaptConfig.get().getFarmPrevention(); - if (prevention == null || !prevention.isEnabled()) { - return 1.0; - } - - double skillGain = prevention.getSkillBasePressure() + (awardXp * prevention.getSkillXpPressure()); - double skillMultiplier = applyStaleness( - ensureSkillStaleness(), - now, - skillGain, - prevention.getSkillRecoveryMillis(), - prevention.getSkillDecayCurve(), - prevention.getSkillFloorMultiplier() - ); + activityStaleness.entrySet().removeIf(entry -> { + RewardStalenessState state = entry.getValue(); + return state == null || (state.getLastAwardAt() > 0 && now - state.getLastAwardAt() > ttl); + }); + lastStalenessCleanup = now; + } - double activityMultiplier = 1.0; - if (prevention.isPerActivityTracking()) { - String normalizedRewardKey = normalizeRewardKey(rewardKey); - if (normalizedRewardKey != null) { - cleanupActivityStaleness(now, prevention.getActivityStateTtlMillis()); - RewardStalenessState activityState = activityStaleness.computeIfAbsent(normalizedRewardKey, k -> new RewardStalenessState()); - double activityGain = prevention.getActivityBasePressure() + (awardXp * prevention.getActivityXpPressure()); - activityMultiplier = applyStaleness( - activityState, - now, - activityGain, - prevention.getActivityRecoveryMillis(), - prevention.getActivityDecayCurve(), - prevention.getActivityFloorMultiplier() - ); - } - } - - double floor = clamp(prevention.getSkillFloorMultiplier(), 0.0, 1.0); - if (prevention.isPerActivityTracking()) { - floor = clamp(floor * prevention.getActivityFloorMultiplier(), 0.0, 1.0); - } - return clamp(skillMultiplier * activityMultiplier, floor, 1.0); + private void applyRecoveryFactor(RewardStalenessState state, double factor) { + if (state == null) { + return; } + state.setPressure(Math.max(0.0, state.getPressure() * factor)); + } - private RewardStalenessState ensureSkillStaleness() { - if (skillStaleness == null) { - skillStaleness = new RewardStalenessState(); - } - return skillStaleness; + private String normalizeRewardKey(String rewardKey) { + if (rewardKey == null) { + return null; } + String normalized = rewardKey.trim(); + return normalized.isEmpty() ? null : normalized; + } - private double applyStaleness(RewardStalenessState state, long now, double gain, long recoveryMillis, double curve, double floor) { - if (state == null) { - return 1.0; - } + private double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } - decayState(state, now, recoveryMillis); - state.setPressure(clamp(state.getPressure() + Math.max(0.0, gain), 0.0, 100000.0)); - state.setLastAwardAt(now); + public void giveXPFresh(Notifier p, double xp) { + xp = multiplier * xp; + this.xp += xp; - double clampedFloor = clamp(floor, 0.0, 1.0); - if (curve <= 0) { - return 1.0; - } - - double scaled = Math.exp(-state.getPressure() / curve); - return clamp(clampedFloor + ((1.0 - clampedFloor) * scaled), clampedFloor, 1.0); + if (p != null) { + last = M.ms(); + if (AdaptConfig.get().isActionbarNotifyXp()) { + p.notifyXP(line, xp); + } } + } - private void decayState(RewardStalenessState state, long now, long recoveryMillis) { - if (state == null || recoveryMillis <= 0) { - return; - } - - long lastAward = state.getLastAwardAt(); - if (lastAward <= 0) { - state.setLastAwardAt(now); - return; - } + public boolean hasEarnedWithin(long ms) { + return M.ms() - last < ms; + } - long elapsed = Math.max(0, now - lastAward); - if (elapsed == 0) { - return; - } + public PlayerAdaptation getAdaptation(String id) { + return adaptations.get(id); + } - double decay = Math.exp(-(double) elapsed / (double) recoveryMillis); - state.setPressure(Math.max(0.0, state.getPressure() * decay)); - } + public int getAdaptationLevel(String id) { + PlayerAdaptation a = getAdaptation(id); - private void cleanupActivityStaleness(long now, long ttl) { - if (ttl <= 0) { - return; - } - if (now - lastStalenessCleanup < 15000) { - return; - } - - activityStaleness.entrySet().removeIf(entry -> { - RewardStalenessState state = entry.getValue(); - return state == null || (state.getLastAwardAt() > 0 && now - state.getLastAwardAt() > ttl); - }); - lastStalenessCleanup = now; + if (a == null) { + return 0; } - private void applyRecoveryFactor(RewardStalenessState state, double factor) { - if (state == null) { - return; - } - state.setPressure(Math.max(0.0, state.getPressure() * factor)); - } + return a.getLevel(); + } - private String normalizeRewardKey(String rewardKey) { - if (rewardKey == null) { - return null; - } - String normalized = rewardKey.trim(); - return normalized.isEmpty() ? null : normalized; + public void setAdaptation(Adaptation a, int level) { + if (a == null) { + return; } - private double clamp(double value, double min, double max) { - return Math.max(min, Math.min(max, value)); + int clamped = Math.max(0, Math.min(level, a.getMaxLevel())); + if (clamped <= 0) { + adaptations.remove(a.getName()); + return; } - public void giveXPFresh(Notifier p, double xp) { - xp = multiplier * xp; - this.xp += xp; + PlayerAdaptation v = new PlayerAdaptation(); + v.setId(a.getName()); + v.setLevel(clamped); + adaptations.put(a.getName(), v); + } - if (p != null) { - last = M.ms(); - if (AdaptConfig.get().isActionbarNotifyXp()) { - p.notifyXP(line, xp); - } - } - } + public Skill getRawSkill(AdaptPlayer p) { + return p.getServer().getSkillRegistry().getSkill(line); + } - public boolean hasEarnedWithin(long ms) { - return M.ms() - last < ms; - } - - public PlayerAdaptation getAdaptation(String id) { - return adaptations.get(id); - } - - public int getAdaptationLevel(String id) { - PlayerAdaptation a = getAdaptation(id); - - if (a == null) { - return 0; - } - - return a.getLevel(); - } - - public void setAdaptation(Adaptation a, int level) { - if (a == null) { - return; - } - - int clamped = Math.max(0, Math.min(level, a.getMaxLevel())); - if (clamped <= 0) { - adaptations.remove(a.getName()); - return; - } - - PlayerAdaptation v = new PlayerAdaptation(); - v.setId(a.getName()); - v.setLevel(clamped); - adaptations.put(a.getName(), v); - } - - public Skill getRawSkill(AdaptPlayer p) { - return p.getServer().getSkillRegistry().getSkill(line); - } - - private void notifyLevel(AdaptPlayer p, double lvl, long kn) { + private void notifyLevel(AdaptPlayer p, double lvl, long kn) { // Skill s = p.getServer().getSkillRegistry().getSkill(getLine()); - if (lvl % 10 == 0) { - p.getNot().queue(SoundNotification.builder() - .sound(Sound.UI_TOAST_CHALLENGE_COMPLETE) - .volume(1f) - .pitch(1.35f) - .group("lvl" + getLine()) - .build(), SoundNotification.builder() - .sound(Sound.UI_TOAST_CHALLENGE_COMPLETE) - .volume(1f) - .pitch(0.75f) - .group("lvl" + getLine()) - .build(), TitleNotification.builder() - .in(250) - .stay(1450) - .out(2250) - .group("lvl" + getLine()) - .title("") - .subtitle(p.getServer().getSkillRegistry().getSkill(getLine()).getDisplayName(getLevel())) - .build()); - p.getActionBarNotifier().queue( - ActionBarNotification.builder() - .duration(450) - .group("know" + getLine()) - .title(kn + " " + p.getServer().getSkillRegistry().getSkill(getLine()).getShortName() + " Knowledge") - .build()); - - } else { - p.getActionBarNotifier().queue( - SoundNotification.builder() - .sound(Sound.BLOCK_AMETHYST_BLOCK_BREAK) - .volume(1f) - .pitch(1.74f) - .group("lvl" + getLine()) - .build(), - SoundNotification.builder() - .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) - .volume(1f) - .pitch(0.74f) - .group("lvl" + getLine()) - .build(), - ActionBarNotification.builder() - .duration(450) - .group("lvl" + getLine()) - .title(p.getServer().getSkillRegistry().getSkill(getLine()).getDisplayName(getLevel())) - .build()); - } - - lastLevel = (int) Math.floor(XP.getLevelForXp(getXp())); - } - - public void giveKnowledge(long points) { - this.knowledge += points; - } - - public double getMinimumXPForLevel() { - return XP.getXpForLevel(getLevel()); - } - - public double getXPForLevelUpAbsolute() { - return getMaximumXPForLevel() - getXp(); - } - - public double getXPForLevelUp() { - return getMaximumXPForLevel() - getMinimumXPForLevel(); - } - - public double getMaximumXPForLevel() { - return XP.getXpForLevel(getLevel()); - } - - public double getAbsoluteLevel() { - return XP.getLevelForXp(xp); - } - - public double getLevelProgress() { - return getAbsoluteLevel() - getLevel(); - } - - public double getLevelProgressRemaining() { - return 1D - getLevelProgress(); - } - - public int getLevel() { - return (int) Math.floor(getAbsoluteLevel()); - } - - public void boost(double v, int i) { - multipliers.add(new XPMultiplier(v, i)); - } - - public boolean spendKnowledge(int c) { - if (getKnowledge() >= c) { - setKnowledge(getKnowledge() - c); - return true; - } - - return false; - } - - @Data - @NoArgsConstructor - public static class RewardStalenessState { - private double pressure = 0; - private long lastAwardAt = 0; - } + if (lvl % 10 == 0) { + p.getNot().queue(SoundNotification.builder() + .sound(Sound.UI_TOAST_CHALLENGE_COMPLETE) + .volume(1f) + .pitch(1.35f) + .group("lvl" + getLine()) + .build(), SoundNotification.builder() + .sound(Sound.UI_TOAST_CHALLENGE_COMPLETE) + .volume(1f) + .pitch(0.75f) + .group("lvl" + getLine()) + .build(), TitleNotification.builder() + .in(250) + .stay(1450) + .out(2250) + .group("lvl" + getLine()) + .title("") + .subtitle(p.getServer().getSkillRegistry().getSkill(getLine()).getDisplayName(getLevel())) + .build()); + p.getActionBarNotifier().queue( + ActionBarNotification.builder() + .duration(450) + .group("know" + getLine()) + .title(kn + " " + p.getServer().getSkillRegistry().getSkill(getLine()).getShortName() + " Knowledge") + .build()); + + } else { + p.getActionBarNotifier().queue( + SoundNotification.builder() + .sound(Sound.BLOCK_AMETHYST_BLOCK_BREAK) + .volume(1f) + .pitch(1.74f) + .group("lvl" + getLine()) + .build(), + SoundNotification.builder() + .sound(Sound.BLOCK_AMETHYST_BLOCK_CHIME) + .volume(1f) + .pitch(0.74f) + .group("lvl" + getLine()) + .build(), + ActionBarNotification.builder() + .duration(450) + .group("lvl" + getLine()) + .title(p.getServer().getSkillRegistry().getSkill(getLine()).getDisplayName(getLevel())) + .build()); + } + + lastLevel = (int) Math.floor(XP.getLevelForXp(getXp())); + } + + public void giveKnowledge(long points) { + this.knowledge += points; + } + + public double getMinimumXPForLevel() { + return XP.getXpForLevel(getLevel()); + } + + public double getXPForLevelUpAbsolute() { + return getMaximumXPForLevel() - getXp(); + } + + public double getXPForLevelUp() { + return getMaximumXPForLevel() - getMinimumXPForLevel(); + } + + public double getMaximumXPForLevel() { + return XP.getXpForLevel(getLevel()); + } + + public double getAbsoluteLevel() { + return XP.getLevelForXp(xp); + } + + public double getLevelProgress() { + return getAbsoluteLevel() - getLevel(); + } + + public double getLevelProgressRemaining() { + return 1D - getLevelProgress(); + } + + public int getLevel() { + return (int) Math.floor(getAbsoluteLevel()); + } + + public void boost(double v, int i) { + multipliers.add(new XPMultiplier(v, i)); + } + + public boolean spendKnowledge(int c) { + if (getKnowledge() >= c) { + setKnowledge(getKnowledge() - c); + return true; + } + + return false; + } + + @Data + @NoArgsConstructor + public static class RewardStalenessState { + private double pressure = 0; + private long lastAwardAt = 0; + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/Curves.java b/src/main/java/art/arcane/adapt/api/xp/Curves.java index 4ec039036..2ccd1469a 100644 --- a/src/main/java/art/arcane/adapt/api/xp/Curves.java +++ b/src/main/java/art/arcane/adapt/api/xp/Curves.java @@ -23,182 +23,182 @@ import java.util.function.Function; public enum Curves { - // Strange ones - QLOG(resolved(level -> Math.pow(level, 2) * Math.log(level), xp -> Math.sqrt(xp / Math.log(xp)), 0.001)), - ELIN(resolved(level -> 1000 * Math.exp(0.001 * level), xp -> Math.log(xp / 1000) / 0.001, 0.001)), - CUBRT(resolved(level -> Math.pow(level, 1 / 3.0), xp -> Math.pow(xp, 3), 0.001)), - HYPER(resolved(level -> 1000 / (2 - level), xp -> 2 - (1000 / xp), 0.001)), - SIGM(resolved(level -> 1000 / (1 + Math.exp(-0.01 * (level - 50))), xp -> 50 + Math.log(xp / (1000 - xp)) / -0.01, 0.001)), - - // Normal ones - X1D2(resolved(level -> Math.pow(level, 1.2), xp -> Math.pow(xp, 1D / 1.2D), 0.001)), - X1D5(resolved(level -> Math.pow(level, 1.5), xp -> Math.pow(xp, 1D / 1.5D), 0.001)), - X2(resolved(level -> Math.pow(level, 2), xp -> Math.pow(xp, 1D / 2D), 0.001)), - X3(resolved(level -> Math.pow(level, 3), xp -> Math.pow(xp, 1D / 3D), 0.001)), - X4(resolved(level -> Math.pow(level, 4), xp -> Math.pow(xp, 1D / 4D), 0.001)), - X5(resolved(level -> Math.pow(level, 5), xp -> Math.pow(xp, 1D / 5D), 0.001)), - X6(resolved(level -> Math.pow(level, 6), xp -> Math.pow(xp, 1D / 6D), 0.001)), - X7(resolved(level -> Math.pow(level, 7), xp -> Math.pow(xp, 1D / 7D), 0.001)), - L1K(resolved(level -> level * 1000D, xp -> xp / 1000D, 0.001)), - L4K(resolved(level -> level * 4000D, xp -> xp / 4000D, 0.001)), - L8K(resolved(level -> level * 8000D, xp -> xp / 8000D, 0.001)), - L16K(resolved(level -> level * 16000D, xp -> xp / 16000D, 0.001)), - - // Game ones - SKYRIM(SkyrimNewtonCurve.create()), - WOW(WOWNewtonCurve.create()), - - // Adapt ones - XL05L7(level -> ((537 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL1L7(level -> ((1337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL15L7(level -> ((1837 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL2L7(level -> ((2337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL3L7(level -> ((3337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL4L7(level -> ((4337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL5L7(level -> ((5337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL6L7(level -> ((6337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL7L7(level -> ((7337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL8L7(level -> ((8337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL9L7(level -> ((9337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL20L7(level -> ((20337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL40L7(level -> ((40337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL80L7(level -> ((80337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL160L7(level -> ((160337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - XL100L7(level -> ((1000337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), - ADAPT_BALANCED(resolved( - level -> 1200 * level + 100 * Math.pow(level, 2), - xp -> (-1200 + Math.sqrt(1440000 + 400 * xp)) / 200, - 0.001 - )), - - LINEAR_EXPONENTIAL_1(resolved(level -> 1000 * level + 100 * Math.pow(level, 2), xp -> { - double a = 1000; - double b = 100; - return (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); - }, 0.001)), - - LINEAR_EXPONENTIAL_2(resolved(level -> 2000 * level + 50 * Math.pow(level, 2.5), xp -> { - double a = 2000; - double b = 50; - double lvl = (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); - return Math.pow((xp - a * lvl) / b, 1 / 2.5); - }, 0.001)), - - LINEAR_EXPONENTIAL_3(resolved(level -> 500 * level + 200 * Math.pow(level, 1.5), xp -> { - double a = 500; - double b = 200; - double lvl = (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); - return Math.pow((xp - a * lvl) / b, 1 / 1.5); - }, 0.001)); - - - @Getter - private final NewtonCurve curve; - - Curves(NewtonCurve curve) { - this.curve = curve; - } + // Strange ones + QLOG(resolved(level -> Math.pow(level, 2) * Math.log(level), xp -> Math.sqrt(xp / Math.log(xp)), 0.001)), + ELIN(resolved(level -> 1000 * Math.exp(0.001 * level), xp -> Math.log(xp / 1000) / 0.001, 0.001)), + CUBRT(resolved(level -> Math.pow(level, 1 / 3.0), xp -> Math.pow(xp, 3), 0.001)), + HYPER(resolved(level -> 1000 / (2 - level), xp -> 2 - (1000 / xp), 0.001)), + SIGM(resolved(level -> 1000 / (1 + Math.exp(-0.01 * (level - 50))), xp -> 50 + Math.log(xp / (1000 - xp)) / -0.01, 0.001)), + + // Normal ones + X1D2(resolved(level -> Math.pow(level, 1.2), xp -> Math.pow(xp, 1D / 1.2D), 0.001)), + X1D5(resolved(level -> Math.pow(level, 1.5), xp -> Math.pow(xp, 1D / 1.5D), 0.001)), + X2(resolved(level -> Math.pow(level, 2), xp -> Math.pow(xp, 1D / 2D), 0.001)), + X3(resolved(level -> Math.pow(level, 3), xp -> Math.pow(xp, 1D / 3D), 0.001)), + X4(resolved(level -> Math.pow(level, 4), xp -> Math.pow(xp, 1D / 4D), 0.001)), + X5(resolved(level -> Math.pow(level, 5), xp -> Math.pow(xp, 1D / 5D), 0.001)), + X6(resolved(level -> Math.pow(level, 6), xp -> Math.pow(xp, 1D / 6D), 0.001)), + X7(resolved(level -> Math.pow(level, 7), xp -> Math.pow(xp, 1D / 7D), 0.001)), + L1K(resolved(level -> level * 1000D, xp -> xp / 1000D, 0.001)), + L4K(resolved(level -> level * 4000D, xp -> xp / 4000D, 0.001)), + L8K(resolved(level -> level * 8000D, xp -> xp / 8000D, 0.001)), + L16K(resolved(level -> level * 16000D, xp -> xp / 16000D, 0.001)), + + // Game ones + SKYRIM(SkyrimNewtonCurve.create()), + WOW(WOWNewtonCurve.create()), + + // Adapt ones + XL05L7(level -> ((537 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL1L7(level -> ((1337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL15L7(level -> ((1837 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL2L7(level -> ((2337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL3L7(level -> ((3337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL4L7(level -> ((4337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL5L7(level -> ((5337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL6L7(level -> ((6337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL7L7(level -> ((7337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL8L7(level -> ((8337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL9L7(level -> ((9337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL20L7(level -> ((20337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL40L7(level -> ((40337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL80L7(level -> ((80337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL160L7(level -> ((160337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + XL100L7(level -> ((1000337 * level) + Math.pow(level * 0.95, Math.PI)) / 1.137), + ADAPT_BALANCED(resolved( + level -> 1200 * level + 100 * Math.pow(level, 2), + xp -> (-1200 + Math.sqrt(1440000 + 400 * xp)) / 200, + 0.001 + )), + + LINEAR_EXPONENTIAL_1(resolved(level -> 1000 * level + 100 * Math.pow(level, 2), xp -> { + double a = 1000; + double b = 100; + return (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); + }, 0.001)), + + LINEAR_EXPONENTIAL_2(resolved(level -> 2000 * level + 50 * Math.pow(level, 2.5), xp -> { + double a = 2000; + double b = 50; + double lvl = (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); + return Math.pow((xp - a * lvl) / b, 1 / 2.5); + }, 0.001)), + + LINEAR_EXPONENTIAL_3(resolved(level -> 500 * level + 200 * Math.pow(level, 1.5), xp -> { + double a = 500; + double b = 200; + double lvl = (-a + Math.sqrt(a * a + 4 * b * xp)) / (2 * b); + return Math.pow((xp - a * lvl) / b, 1 / 1.5); + }, 0.001)); + + + @Getter + private final NewtonCurve curve; + + Curves(NewtonCurve curve) { + this.curve = curve; + } + + private static NewtonCurve resolved(Function xpForLevel, Function levelForXP, double maxError) { + return new NewtonCurve() { + @Override + public double getXPForLevel(double level) { + return xpForLevel.apply(level); + } + + @Override + public double computeLevelForXP(double xp, double maxError) { + return levelForXP.apply(xp); + } + }; + } + + public static class SkyrimNewtonCurve { + public static NewtonCurve create() { + Function xpForLevel = level -> { + double f = 0; + for (int i = 1; i < level; i++) { + f += getNextLevelCost(i); + } + return f; + }; + + Function levelForXP = xp -> { + double currentLevel = 1; + while (xp >= getNextLevelCost(currentLevel)) { + xp -= getNextLevelCost(currentLevel); + currentLevel++; + } + return currentLevel; + }; - private static NewtonCurve resolved(Function xpForLevel, Function levelForXP, double maxError) { - return new NewtonCurve() { - @Override - public double getXPForLevel(double level) { - return xpForLevel.apply(level); - } - - @Override - public double computeLevelForXP(double xp, double maxError) { - return levelForXP.apply(xp); - } - }; + return Curves.resolved(xpForLevel, levelForXP, 0.001); } - public static class SkyrimNewtonCurve { - public static NewtonCurve create() { - Function xpForLevel = level -> { - double f = 0; - for (int i = 1; i < level; i++) { - f += getNextLevelCost(i); - } - return f; - }; - - Function levelForXP = xp -> { - double currentLevel = 1; - while (xp >= getNextLevelCost(currentLevel)) { - xp -= getNextLevelCost(currentLevel); - currentLevel++; - } - return currentLevel; - }; - - return Curves.resolved(xpForLevel, levelForXP, 0.001); - } - - private static double getNextLevelCost(double currentLevel) { - return Math.pow(currentLevel - 1, 1.95) + 300; - } + private static double getNextLevelCost(double currentLevel) { + return Math.pow(currentLevel - 1, 1.95) + 300; } + } - public class WOWNewtonCurve { - public static NewtonCurve create() { - Function xpForLevel = level -> { - double f = 0; - for (int i = 1; i < level; i++) { - f += getNextLevelCost(i); - } - return f; - }; - - Function levelForXP = xp -> { - double currentLevel = 1; - while (xp >= getNextLevelCost(currentLevel)) { - xp -= getNextLevelCost(currentLevel); - currentLevel++; - } - return currentLevel; - }; - - return Curves.resolved(xpForLevel, levelForXP, 0.001); + public class WOWNewtonCurve { + public static NewtonCurve create() { + Function xpForLevel = level -> { + double f = 0; + for (int i = 1; i < level; i++) { + f += getNextLevelCost(i); } - - private static double getNextLevelCost(double currentLevel) { - return ((8 * currentLevel) + getDiff(currentLevel)) * getMXP(currentLevel) * getDRF(currentLevel); + return f; + }; + + Function levelForXP = xp -> { + double currentLevel = 1; + while (xp >= getNextLevelCost(currentLevel)) { + xp -= getNextLevelCost(currentLevel); + currentLevel++; } + return currentLevel; + }; - private static double getMXP(double currentLevel) { - return 235 + (5 * currentLevel); - } + return Curves.resolved(xpForLevel, levelForXP, 0.001); + } - private static double getDRF(double currentLevel) { - if (currentLevel >= 11 && currentLevel <= 27) { - return (1 - (currentLevel - 10) / 100); - } + private static double getNextLevelCost(double currentLevel) { + return ((8 * currentLevel) + getDiff(currentLevel)) * getMXP(currentLevel) * getDRF(currentLevel); + } - if (currentLevel >= 28 && currentLevel <= 59) { - return 0.82; - } + private static double getMXP(double currentLevel) { + return 235 + (5 * currentLevel); + } - return 1; - } + private static double getDRF(double currentLevel) { + if (currentLevel >= 11 && currentLevel <= 27) { + return (1 - (currentLevel - 10) / 100); + } - private static double getDiff(double currentLevel) { - if (currentLevel <= 28) { - return 0; - } - if (currentLevel == 29) { - return 1; - } - if (currentLevel == 30) { - return 3; - } - if (currentLevel == 31) { - return 6; - } - - return 5 * (Math.min(currentLevel, 59) - 30); - } + if (currentLevel >= 28 && currentLevel <= 59) { + return 0.82; + } + + return 1; + } + + private static double getDiff(double currentLevel) { + if (currentLevel <= 28) { + return 0; + } + if (currentLevel == 29) { + return 1; + } + if (currentLevel == 30) { + return 3; + } + if (currentLevel == 31) { + return 6; + } + + return 5 * (Math.min(currentLevel, 59) - 30); } + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java b/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java index ef18481bf..f9a986425 100644 --- a/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java +++ b/src/main/java/art/arcane/adapt/api/xp/NewtonCurve.java @@ -22,39 +22,39 @@ @FunctionalInterface public interface NewtonCurve { - double getXPForLevel(double level); + double getXPForLevel(double level); - default double computeLevelForXP(double xp, double maxError) { - double div = 2; - int iterations = 0; - double jumpSize = 100; - double cursor = 0; - double test; - boolean last = false; + default double computeLevelForXP(double xp, double maxError) { + double div = 2; + int iterations = 0; + double jumpSize = 100; + double cursor = 0; + double test; + boolean last = false; - while (jumpSize > maxError && iterations < 100) { - iterations++; - test = getXPForLevel(cursor); - if (test < xp) { - if (last) { - jumpSize /= div; - } - last = false; - cursor += jumpSize; - } else { - if (!last) { - jumpSize /= div; - } - - last = true; - cursor -= jumpSize; - } - // Check if the level has exceeded the maximum allowed (1000) - if (cursor > AdaptConfig.get().experienceMaxLevel) { - cursor = AdaptConfig.get().experienceMaxLevel; - break; - } + while (jumpSize > maxError && iterations < 100) { + iterations++; + test = getXPForLevel(cursor); + if (test < xp) { + if (last) { + jumpSize /= div; + } + last = false; + cursor += jumpSize; + } else { + if (!last) { + jumpSize /= div; } - return cursor; + + last = true; + cursor -= jumpSize; + } + // Check if the level has exceeded the maximum allowed (1000) + if (cursor > AdaptConfig.get().experienceMaxLevel) { + cursor = AdaptConfig.get().experienceMaxLevel; + break; + } } + return cursor; + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java b/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java index 642a60728..a5a3e4684 100644 --- a/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java +++ b/src/main/java/art/arcane/adapt/api/xp/ResolvedNewtonCurve.java @@ -19,9 +19,9 @@ package art.arcane.adapt.api.xp; public interface ResolvedNewtonCurve extends NewtonCurve { - double getLevelForXP(double xp); + double getLevelForXP(double xp); - default double computeLevelForXP(double xp, double maxError) { - return getLevelForXP(xp); - } + default double computeLevelForXP(double xp, double maxError) { + return getLevelForXP(xp); + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java b/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java index f958a7354..54fde9b94 100644 --- a/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java +++ b/src/main/java/art/arcane/adapt/api/xp/SpatialXP.java @@ -25,17 +25,17 @@ @Data public class SpatialXP { - private Location location; - private double radius; - private Skill skill; - private double xp; - private long ms; + private Location location; + private double radius; + private Skill skill; + private double xp; + private long ms; - public SpatialXP(Location l, Skill s, double xp, double radius, long duration) { - this.location = l; - this.skill = s; - this.xp = xp; - this.ms = M.ms() + duration; - this.radius = radius; - } + public SpatialXP(Location l, Skill s, double xp, double radius, long duration) { + this.location = l; + this.skill = s; + this.xp = xp; + this.ms = M.ms() + duration; + this.radius = radius; + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/XP.java b/src/main/java/art/arcane/adapt/api/xp/XP.java index cce0fc537..22dba40f3 100644 --- a/src/main/java/art/arcane/adapt/api/xp/XP.java +++ b/src/main/java/art/arcane/adapt/api/xp/XP.java @@ -28,90 +28,90 @@ import org.bukkit.entity.Player; public class XP { - public static void xp(Player p, Skill skill, double xp) { - xp(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, null); - } - - public static void xp(Player p, Skill skill, double xp, String rewardKey) { - xp(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, rewardKey); - } - - public static void xp(AdaptPlayer p, Skill skill, double xp) { - xp(p, skill, xp, null); - } - - public static void xp(AdaptPlayer p, Skill skill, double xp, String rewardKey) { - PlayerSkillLine skillLine = p.getSkillLine(skill.getName()); - if (skillLine != null) { - p.getData().resetMonotonyForOtherSkills(skill.getName()); - skillLine.giveXP(p.getNot(), xp, rewardKey); - } - } - - public static void xpSilent(Player p, Skill skill, double xp) { - xpSilent(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, null); - } - - public static void xpSilent(Player p, Skill skill, double xp, String rewardKey) { - xpSilent(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, rewardKey); - } - - public static void xpSilent(AdaptPlayer p, Skill skill, double xp) { - xpSilent(p, skill, xp, null); - } - - public static void xpSilent(AdaptPlayer p, Skill skill, double xp, String rewardKey) { - if (p.getSkillLine(skill.getName()) != null) { - p.getData().resetMonotonyForOtherSkills(skill.getName()); - p.getSkillLine(skill.getName()).giveXP(null, xp, rewardKey); - } - } - - public static void spatialXP(Location l, Skill skill, double xp, int rad, long duration) { - Adapt.instance.getAdaptServer().offer(new SpatialXP(l, skill, xp, rad, duration)); - } - - public static void wisdom(Player p, long k) { - wisdom(Adapt.instance.getAdaptServer().getPlayer(p), k); - } - - public static void wisdom(AdaptPlayer p, long k) { - p.getData().setWisdom(p.getData().getWisdom() + k); - } - - public static void knowledge(Player p, Skill skill, long k) { - knowledge(Adapt.instance.getAdaptServer().getPlayer(p), skill, k); - } - - public static void knowledge(AdaptPlayer p, Skill skill, long k) { - p.getSkillLine(skill.getName()).giveKnowledge(k); - } - - public static void boostXP(Player p, Skill skill, double percentChange, int durationMS) { - boostXP(Adapt.instance.getAdaptServer().getPlayer(p), skill, percentChange, durationMS); - } - - public static void boostXP(AdaptPlayer p, Skill skill, double percentChange, int durationMS) { - p.getSkillLine(skill.getName()).boost(percentChange, durationMS); - } - - public static double getXpUntilLevelUp(double xp) { - double level = getLevelForXp(xp); - double xa = getXpForLevel((int) level); - double xb = getXpForLevel((int) level + 1); - return M.lerp(xb - xa, 0, level - (int) level); - } - - public static double getLevelProgress(double xp) { - double level = getLevelForXp(xp); - return level - (int) level; - } - - public static double getXpForLevel(double level) { - return AdaptConfig.get().getXpCurve().getCurve().getXPForLevel(level); - } - - public static double getLevelForXp(double xp) { - return AdaptConfig.get().getXpCurve().getCurve().computeLevelForXP(xp, 0.000001); - } + public static void xp(Player p, Skill skill, double xp) { + xp(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, null); + } + + public static void xp(Player p, Skill skill, double xp, String rewardKey) { + xp(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, rewardKey); + } + + public static void xp(AdaptPlayer p, Skill skill, double xp) { + xp(p, skill, xp, null); + } + + public static void xp(AdaptPlayer p, Skill skill, double xp, String rewardKey) { + PlayerSkillLine skillLine = p.getSkillLine(skill.getName()); + if (skillLine != null) { + p.getData().resetMonotonyForOtherSkills(skill.getName()); + skillLine.giveXP(p.getNot(), xp, rewardKey); + } + } + + public static void xpSilent(Player p, Skill skill, double xp) { + xpSilent(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, null); + } + + public static void xpSilent(Player p, Skill skill, double xp, String rewardKey) { + xpSilent(Adapt.instance.getAdaptServer().getPlayer(p), skill, xp, rewardKey); + } + + public static void xpSilent(AdaptPlayer p, Skill skill, double xp) { + xpSilent(p, skill, xp, null); + } + + public static void xpSilent(AdaptPlayer p, Skill skill, double xp, String rewardKey) { + if (p.getSkillLine(skill.getName()) != null) { + p.getData().resetMonotonyForOtherSkills(skill.getName()); + p.getSkillLine(skill.getName()).giveXP(null, xp, rewardKey); + } + } + + public static void spatialXP(Location l, Skill skill, double xp, int rad, long duration) { + Adapt.instance.getAdaptServer().offer(new SpatialXP(l, skill, xp, rad, duration)); + } + + public static void wisdom(Player p, long k) { + wisdom(Adapt.instance.getAdaptServer().getPlayer(p), k); + } + + public static void wisdom(AdaptPlayer p, long k) { + p.getData().setWisdom(p.getData().getWisdom() + k); + } + + public static void knowledge(Player p, Skill skill, long k) { + knowledge(Adapt.instance.getAdaptServer().getPlayer(p), skill, k); + } + + public static void knowledge(AdaptPlayer p, Skill skill, long k) { + p.getSkillLine(skill.getName()).giveKnowledge(k); + } + + public static void boostXP(Player p, Skill skill, double percentChange, int durationMS) { + boostXP(Adapt.instance.getAdaptServer().getPlayer(p), skill, percentChange, durationMS); + } + + public static void boostXP(AdaptPlayer p, Skill skill, double percentChange, int durationMS) { + p.getSkillLine(skill.getName()).boost(percentChange, durationMS); + } + + public static double getXpUntilLevelUp(double xp) { + double level = getLevelForXp(xp); + double xa = getXpForLevel((int) level); + double xb = getXpForLevel((int) level + 1); + return M.lerp(xb - xa, 0, level - (int) level); + } + + public static double getLevelProgress(double xp) { + double level = getLevelForXp(xp); + return level - (int) level; + } + + public static double getXpForLevel(double level) { + return AdaptConfig.get().getXpCurve().getCurve().getXPForLevel(level); + } + + public static double getLevelForXp(double xp) { + return AdaptConfig.get().getXpCurve().getCurve().computeLevelForXP(xp, 0.000001); + } } diff --git a/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java b/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java index 8b28f4fd6..72026e468 100644 --- a/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java +++ b/src/main/java/art/arcane/adapt/api/xp/XPMultiplier.java @@ -25,15 +25,15 @@ @NoArgsConstructor @Data public class XPMultiplier { - private double multiplier = 0D; - private long goodFor = M.ms() + 10000; + private double multiplier = 0D; + private long goodFor = M.ms() + 10000; - public XPMultiplier(double percentChange, long duration) { - this.multiplier = percentChange; - this.goodFor = M.ms() + duration; - } + public XPMultiplier(double percentChange, long duration) { + this.multiplier = percentChange; + this.goodFor = M.ms() + duration; + } - public boolean isExpired() { - return M.ms() > goodFor; - } + public boolean isExpired() { + return M.ms() > goodFor; + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index c6b66691d..80b0f5751 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -17,12 +17,12 @@ import art.arcane.adapt.content.item.KnowledgeOrb; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.config.ConfigMigrationManager; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.director.DirectorOrigin; import art.arcane.volmlib.util.director.annotations.Director; import art.arcane.volmlib.util.director.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import org.bukkit.entity.Player; import java.util.HashMap; @@ -32,499 +32,499 @@ @Director(name = "adapt", description = "Basic Command") public class CommandAdapt { - private CommandDebug debug; - private CommandClear clear; - private CommandReset reset; - private CommandDefault defaults; - - @Director(description = "Boost Target player Experience gain.") - public void boost( - @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") - int seconds, - @Param(aliases = "multiplier", description = "Strength of the boost ", defaultValue = "10") - double multiplier, - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (!BukkitDirectorContext.hasPermission("adapt.boost")) { - FConst.error("You lack the Permission 'adapt.boost'").send(BukkitDirectorContext.sender()); - return; - } - - Player targetPlayer = player; - if (targetPlayer == null && BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return; - } else if (targetPlayer == null) { - targetPlayer = BukkitDirectorContext.player(); - } - - AdaptServer adaptServer = Adapt.instance.getAdaptServer(); - PlayerData playerData = adaptServer.getPlayer(targetPlayer).getData(); - playerData.globalXPMultiplier(multiplier, seconds * 1000); + private CommandDebug debug; + private CommandClear clear; + private CommandReset reset; + private CommandDefault defaults; + + @Director(description = "Boost Target player Experience gain.") + public void boost( + @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") + int seconds, + @Param(aliases = "multiplier", description = "Strength of the boost ", defaultValue = "10") + double multiplier, + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.boost")) { + FConst.error("You lack the Permission 'adapt.boost'").send(BukkitDirectorContext.sender()); + return; + } - FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return; + } else if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); } - @Director(description = "Boost Global Experience gain.", name = "global-boost") - public void globalBoost( - @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") - int seconds, - @Param(aliases = "multiplier", description = "Strength of the boost ", defaultValue = "10") - double multiplier - ) { - if (!BukkitDirectorContext.hasPermission("adapt.boost.global")) { - FConst.error("You lack the Permission 'adapt.boost.global'").send(BukkitDirectorContext.sender()); - return; - } + AdaptServer adaptServer = Adapt.instance.getAdaptServer(); + PlayerData playerData = adaptServer.getPlayer(targetPlayer).getData(); + playerData.globalXPMultiplier(multiplier, seconds * 1000); + + FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); + } + + @Director(description = "Boost Global Experience gain.", name = "global-boost") + public void globalBoost( + @Param(aliases = "seconds", description = "Amount of seconds", defaultValue = "10") + int seconds, + @Param(aliases = "multiplier", description = "Strength of the boost ", defaultValue = "10") + double multiplier + ) { + if (!BukkitDirectorContext.hasPermission("adapt.boost.global")) { + FConst.error("You lack the Permission 'adapt.boost.global'").send(BukkitDirectorContext.sender()); + return; + } - AdaptServer adaptServer = Adapt.instance.getAdaptServer(); - adaptServer.boostXP(multiplier, seconds * 1000); + AdaptServer adaptServer = Adapt.instance.getAdaptServer(); + adaptServer.boostXP(multiplier, seconds * 1000); + + FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); + } + + @Director(description = "Open the Adapt GUI") + public void gui( + @Param(aliases = "target", defaultValue = "[Main]") + AdaptationListingHandler.AdaptationList guiTarget, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player, + @Param(aliases = "force", defaultValue = "false") + boolean force + ) { + if (!BukkitDirectorContext.hasPermission("adapt.gui")) { + FConst.error("You lack the Permission 'adapt.gui'").send(BukkitDirectorContext.sender()); + return; + } - FConst.success("Boosted XP by " + multiplier + " for " + seconds + " seconds").send(BukkitDirectorContext.sender()); + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return; + } else if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); } - @Director(description = "Open the Adapt GUI") - public void gui( - @Param(aliases = "target", defaultValue = "[Main]") - AdaptationListingHandler.AdaptationList guiTarget, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player, - @Param(aliases = "force", defaultValue = "false") - boolean force - ) { - if (!BukkitDirectorContext.hasPermission("adapt.gui")) { - FConst.error("You lack the Permission 'adapt.gui'").send(BukkitDirectorContext.sender()); - return; - } + if (guiTarget.equals("[Main]")) { + SkillsGui.open(targetPlayer); + return; + } - Player targetPlayer = player; - if (targetPlayer == null && BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return; - } else if (targetPlayer == null) { - targetPlayer = BukkitDirectorContext.player(); - } + if (guiTarget.startsWith("[Skill]-")) { + for (Skill skill : SkillRegistry.skills.sortV()) { + if (guiTarget.equals("[Skill]-" + skill.getName())) { + if (force || skill.openGui(targetPlayer, true)) { + FConst.success("Opened GUI for " + skill.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } else { + FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); + } + return; + } + } + } - if (guiTarget.equals("[Main]")) { - SkillsGui.open(targetPlayer); + if (guiTarget.startsWith("[Adaptation]-")) { + for (Skill skill : SkillRegistry.skills.sortV()) { + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.isEnabled()) { + continue; + } + if (guiTarget.equals("[Adaptation]-" + adaptation.getName())) { + if (force || adaptation.openGui(targetPlayer, true)) { + FConst.success("Opened GUI for " + adaptation.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } else { + FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); + } return; + } } + } + } + } - if (guiTarget.startsWith("[Skill]-")) { - for (Skill skill : SkillRegistry.skills.sortV()) { - if (guiTarget.equals("[Skill]-" + skill.getName())) { - if (force || skill.openGui(targetPlayer, true)) { - FConst.success("Opened GUI for " + skill.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); - } else { - FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); - } - return; - } - } - } + @Director(name = "configure", aliases = {"config", "cfg"}, origin = DirectorOrigin.PLAYER, description = "Open the in-game Adapt config editor") + public void configure() { + if (!ConfigGui.canConfigure(BukkitDirectorContext.player())) { + FConst.error("You need operator status or the permission 'adapt.configurator'").send(BukkitDirectorContext.sender()); + return; + } - if (guiTarget.startsWith("[Adaptation]-")) { - for (Skill skill : SkillRegistry.skills.sortV()) { - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.isEnabled()) { - continue; - } - if (guiTarget.equals("[Adaptation]-" + adaptation.getName())) { - if (force || adaptation.openGui(targetPlayer, true)) { - FConst.success("Opened GUI for " + adaptation.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); - } else { - FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); - } - return; - } - } - } - } + ConfigGui.open(BukkitDirectorContext.player()); + } + + @Director(description = "Give yourself an experience orb") + public void experience( + @Param(aliases = "skill") + AdaptationListingHandler.AdaptationSkillList skillName, + @Param(aliases = "amount", defaultValue = "10") + int amount, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + + ) { + if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { + FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "configure", aliases = {"config", "cfg"}, origin = DirectorOrigin.PLAYER, description = "Open the in-game Adapt config editor") - public void configure() { - if (!ConfigGui.canConfigure(BukkitDirectorContext.player())) { - FConst.error("You need operator status or the permission 'adapt.configurator'").send(BukkitDirectorContext.sender()); - return; - } + Player targetPlayer = player; - ConfigGui.open(BukkitDirectorContext.player()); + if (targetPlayer == null) { + if (BukkitDirectorContext.isPlayer()) { + targetPlayer = BukkitDirectorContext.player(); + } else { + FConst.error("You must be a player to use this command, or Reference a player").send(BukkitDirectorContext.sender()); + return; + } } - @Director(description = "Give yourself an experience orb") - public void experience( - @Param(aliases = "skill") - AdaptationListingHandler.AdaptationSkillList skillName, - @Param(aliases = "amount", defaultValue = "10") - int amount, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player + if (skillName.equals("[all]")) { + Map experienceMap = new HashMap<>(); + for (Skill skill : allSkillSnapshot()) { + experienceMap.put(skill.getName(), (double) amount); + } + targetPlayer.getInventory().addItem(ExperienceOrb.with(experienceMap)); + FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); + return; + } - ) { - if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { - FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); - return; - } + if (skillName.equals("[random]")) { + List> skills = allSkillSnapshot(); + if (skills.isEmpty()) { + FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); + return; + } - Player targetPlayer = player; + targetPlayer.getInventory().addItem(ExperienceOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); + FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); + return; + } - if (targetPlayer == null) { - if (BukkitDirectorContext.isPlayer()) { - targetPlayer = BukkitDirectorContext.player(); - } else { - FConst.error("You must be a player to use this command, or Reference a player").send(BukkitDirectorContext.sender()); - return; - } - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); + if (skill != null) { + targetPlayer.getInventory().addItem(ExperienceOrb.with(skill.getName(), amount)); + FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); + } + } + + @Director(description = "Give yourself a knowledge orb") + public void knowledge( + @Param(aliases = "skill") + AdaptationListingHandler.AdaptationSkillList skillName, + @Param(aliases = "amount", defaultValue = "10") + int amount, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { + FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); + return; + } + Player targetPlayer = player; + + if (targetPlayer == null) { + if (BukkitDirectorContext.isPlayer()) { + targetPlayer = BukkitDirectorContext.player(); + } else { + FConst.error("You must be a player to use this command").send(BukkitDirectorContext.sender()); + return; + } + } - if (skillName.equals("[all]")) { - Map experienceMap = new HashMap<>(); - for (Skill skill : allSkillSnapshot()) { - experienceMap.put(skill.getName(), (double) amount); - } - targetPlayer.getInventory().addItem(ExperienceOrb.with(experienceMap)); - FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); - return; - } + if (skillName.equals("[all]")) { + Map knowledgeMap = new HashMap<>(); + for (Skill skill : allSkillSnapshot()) { + knowledgeMap.put(skill.getName(), amount); + } + targetPlayer.getInventory().addItem(KnowledgeOrb.with(knowledgeMap)); + FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); + return; + } - if (skillName.equals("[random]")) { - List> skills = allSkillSnapshot(); - if (skills.isEmpty()) { - FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); - return; - } + if (skillName.equals("[random]")) { + List> skills = allSkillSnapshot(); + if (skills.isEmpty()) { + FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); + return; + } - targetPlayer.getInventory().addItem(ExperienceOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); - FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); - return; - } + targetPlayer.getInventory().addItem(KnowledgeOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); + FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); - if (skill != null) { - targetPlayer.getInventory().addItem(ExperienceOrb.with(skill.getName(), amount)); - FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); + if (skill != null) { + targetPlayer.getInventory().addItem(KnowledgeOrb.with(skill.getName(), amount)); + FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); + } + } + + @Director(description = "Assign a skill, or UnAssign a skill as if you are learning / unlearning a skill.") + public void determine( + @Param(aliases = "adaptationTarget") + AdaptationListingHandler.AdaptationProvider adaptationTarget, + @Param(aliases = "assign") + boolean assign, + @Param(aliases = "force") + boolean force, + @Param(aliases = "level") + int level, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + + ) { + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); + return; } - @Director(description = "Give yourself a knowledge orb") - public void knowledge( - @Param(aliases = "skill") - AdaptationListingHandler.AdaptationSkillList skillName, - @Param(aliases = "amount", defaultValue = "10") - int amount, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (!BukkitDirectorContext.hasPermission("adapt.cheatitem")) { - FConst.error("You lack the Permission 'adapt.cheatitem'").send(BukkitDirectorContext.sender()); - return; - } - Player targetPlayer = player; + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return; + } else if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); + } - if(targetPlayer == null){ - if (BukkitDirectorContext.isPlayer()) { - targetPlayer = BukkitDirectorContext.player(); + //the format is skillname:adaptationname + String[] split = adaptationTarget.name().split(":", 2); + if (split.length != 2) { + FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); + return; + } + String skillname = split[0]; + String adaptationname = split[1]; + + for (Skill skill : SkillRegistry.skills.sortV()) { + if (skill.getName().equalsIgnoreCase(skillname)) { + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation.getName().equalsIgnoreCase(adaptationname)) { + if (targetPlayer != null) { + if (assign) { + adaptation.learn(targetPlayer, level, force); + } else { + adaptation.unlearn(targetPlayer, level, force); + } } else { - FConst.error("You must be a player to use this command").send(BukkitDirectorContext.sender()); - return; - } - } - - if (skillName.equals("[all]")) { - Map knowledgeMap = new HashMap<>(); - for (Skill skill : allSkillSnapshot()) { - knowledgeMap.put(skill.getName(), amount); - } - targetPlayer.getInventory().addItem(KnowledgeOrb.with(knowledgeMap)); - FConst.success("Giving all orbs").send(BukkitDirectorContext.sender()); - return; - } - - if (skillName.equals("[random]")){ - List> skills = allSkillSnapshot(); - if (skills.isEmpty()) { - FConst.error("No skills are registered.").send(BukkitDirectorContext.sender()); - return; + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); } - - targetPlayer.getInventory().addItem(KnowledgeOrb.with(skills.get(ThreadLocalRandom.current().nextInt(skills.size())).getName(), amount)); - FConst.success("Giving random orb").send(BukkitDirectorContext.sender()); return; + } } + return; + } + } + } + + @Director(name = "claim-skill", description = "Set a player's skill line level between 0 and 100 for custom UI integration.") + public void claimSkill( + @Param(aliases = "skill") + AdaptationListingHandler.SkillProvider skillTarget, + @Param(aliases = "level") + int level, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName.name()); - if(skill != null){ - targetPlayer.getInventory().addItem(KnowledgeOrb.with(skill.getName(), amount)); - FConst.success("Giving " + skill.getName() + " orb").send(BukkitDirectorContext.sender()); - } + Player targetPlayer = resolveTargetPlayer(player); + if (targetPlayer == null) { + return; } - @Director(description = "Assign a skill, or UnAssign a skill as if you are learning / unlearning a skill.") - public void determine( - @Param(aliases = "adaptationTarget") - AdaptationListingHandler.AdaptationProvider adaptationTarget, - @Param(aliases = "assign") - boolean assign, - @Param(aliases = "force") - boolean force, - @Param(aliases = "level") - int level, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - - ) { - if (!BukkitDirectorContext.hasPermission("adapt.determine")) { - FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); - return; - } + if (level < 0 || level > 100) { + FConst.error("Skill claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); + return; + } - Player targetPlayer = player; - if (targetPlayer == null && BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return; - } else if (targetPlayer == null) { - targetPlayer = BukkitDirectorContext.player(); - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillTarget.name()); + if (skill == null) { + FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); + return; + } - //the format is skillname:adaptationname - String[] split = adaptationTarget.name().split(":", 2); - if (split.length != 2) { - FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); - return; - } - String skillname = split[0]; - String adaptationname = split[1]; - - for (Skill skill : SkillRegistry.skills.sortV()) { - if (skill.getName().equalsIgnoreCase(skillname)) { - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation.getName().equalsIgnoreCase(adaptationname)) { - if (targetPlayer != null) { - if (assign) { - adaptation.learn(targetPlayer, level, force); - } else { - adaptation.unlearn(targetPlayer, level, force); - } - } else { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - } - return; - } - } - return; - } - } + PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); + if (skillLine == null) { + FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "claim-skill", description = "Set a player's skill line level between 0 and 100 for custom UI integration.") - public void claimSkill( - @Param(aliases = "skill") - AdaptationListingHandler.SkillProvider skillTarget, - @Param(aliases = "level") - int level, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (!BukkitDirectorContext.hasPermission("adapt.determine")) { - FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); - return; - } + double targetXp = XP.getXpForLevel(level); + skillLine.setXp(targetXp); + if (skillLine.getLastXP() > targetXp) { + skillLine.setLastXP(targetXp); + } + if (skillLine.getLastLevel() > level) { + skillLine.setLastLevel(level); + } - Player targetPlayer = resolveTargetPlayer(player); - if (targetPlayer == null) { - return; - } + FConst.success("Set " + targetPlayer.getName() + " " + skill.getName() + " level to " + level + ".").send(BukkitDirectorContext.sender()); + } + + @Director(name = "claim-adaptation", description = "Set an adaptation level between 0 and 100 if the player can afford it.") + public void claimAdaptation( + @Param(aliases = "adaptationTarget") + AdaptationListingHandler.AdaptationProvider adaptationTarget, + @Param(aliases = "level") + int level, + @Param(aliases = "force", defaultValue = "false") + boolean force, + @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.hasPermission("adapt.determine")) { + FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); + return; + } - if (level < 0 || level > 100) { - FConst.error("Skill claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); - return; - } + Player targetPlayer = resolveTargetPlayer(player); + if (targetPlayer == null) { + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillTarget.name()); - if (skill == null) { - FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); - return; - } + if (level < 0 || level > 100) { + FConst.error("Adaptation claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); + return; + } - PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); - if (skillLine == null) { - FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); - return; - } + String[] split = adaptationTarget.name().split(":", 2); + if (split.length != 2) { + FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); + return; + } - double targetXp = XP.getXpForLevel(level); - skillLine.setXp(targetXp); - if (skillLine.getLastXP() > targetXp) { - skillLine.setLastXP(targetXp); - } - if (skillLine.getLastLevel() > level) { - skillLine.setLastLevel(level); - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(split[0]); + if (skill == null) { + FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); + return; + } - FConst.success("Set " + targetPlayer.getName() + " " + skill.getName() + " level to " + level + ".").send(BukkitDirectorContext.sender()); - } - - @Director(name = "claim-adaptation", description = "Set an adaptation level between 0 and 100 if the player can afford it.") - public void claimAdaptation( - @Param(aliases = "adaptationTarget") - AdaptationListingHandler.AdaptationProvider adaptationTarget, - @Param(aliases = "level") - int level, - @Param(aliases = "force", defaultValue = "false") - boolean force, - @Param(aliases = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (!BukkitDirectorContext.hasPermission("adapt.determine")) { - FConst.error("You lack the Permission 'adapt.determine'").send(BukkitDirectorContext.sender()); - return; - } + Adaptation adaptation = null; + for (Adaptation candidate : skill.getAdaptations()) { + if (candidate.getName().equalsIgnoreCase(split[1])) { + adaptation = candidate; + break; + } + } - Player targetPlayer = resolveTargetPlayer(player); - if (targetPlayer == null) { - return; - } + if (adaptation == null) { + FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); + return; + } - if (level < 0 || level > 100) { - FConst.error("Adaptation claim level must be between 0 and 100.").send(BukkitDirectorContext.sender()); - return; - } + PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); + if (skillLine == null) { + FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); + return; + } - String[] split = adaptationTarget.name().split(":", 2); - if (split.length != 2) { - FConst.error("Invalid adaptation target format. Use skill:adaptation").send(BukkitDirectorContext.sender()); - return; - } + int currentLevel = skillLine.getAdaptationLevel(adaptation.getName()); + int targetLevel = Math.max(0, Math.min(level, adaptation.getMaxLevel())); + if (targetLevel == currentLevel) { + FConst.success("No change: " + adaptation.getName() + " is already at level " + currentLevel + ".").send(BukkitDirectorContext.sender()); + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(split[0]); - if (skill == null) { - FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); - return; - } + if (targetLevel > currentLevel) { + int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); + int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); - Adaptation adaptation = null; - for (Adaptation candidate : skill.getAdaptations()) { - if (candidate.getName().equalsIgnoreCase(split[1])) { - adaptation = candidate; - break; - } + if (!force) { + if (!playerData.hasPowerAvailable(powerCost)) { + FConst.error("Not enough available power. Need " + powerCost + ", have " + playerData.getAvailablePower() + ".").send(BukkitDirectorContext.sender()); + return; } - if (adaptation == null) { - FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); - return; + if (skillLine.getKnowledge() < knowledgeCost) { + FConst.error("Not enough knowledge in " + skill.getName() + ". Need " + knowledgeCost + ", have " + skillLine.getKnowledge() + ".").send(BukkitDirectorContext.sender()); + return; } - PlayerData playerData = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - PlayerSkillLine skillLine = playerData.getSkillLine(skill.getName()); - if (skillLine == null) { - FConst.error("Failed to resolve skill line for " + skill.getName() + ".").send(BukkitDirectorContext.sender()); - return; + if (!skillLine.spendKnowledge(knowledgeCost)) { + FConst.error("Failed to spend required knowledge (" + knowledgeCost + ").").send(BukkitDirectorContext.sender()); + return; } + } - int currentLevel = skillLine.getAdaptationLevel(adaptation.getName()); - int targetLevel = Math.max(0, Math.min(level, adaptation.getMaxLevel())); - if (targetLevel == currentLevel) { - FConst.success("No change: " + adaptation.getName() + " is already at level " + currentLevel + ".").send(BukkitDirectorContext.sender()); - return; - } + skillLine.setAdaptation(adaptation, targetLevel); + FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); + return; + } - if (targetLevel > currentLevel) { - int knowledgeCost = adaptation.getCostFor(targetLevel, currentLevel); - int powerCost = adaptation.getPowerCostFor(targetLevel, currentLevel); - - if (!force) { - if (!playerData.hasPowerAvailable(powerCost)) { - FConst.error("Not enough available power. Need " + powerCost + ", have " + playerData.getAvailablePower() + ".").send(BukkitDirectorContext.sender()); - return; - } - - if (skillLine.getKnowledge() < knowledgeCost) { - FConst.error("Not enough knowledge in " + skill.getName() + ". Need " + knowledgeCost + ", have " + skillLine.getKnowledge() + ".").send(BukkitDirectorContext.sender()); - return; - } - - if (!skillLine.spendKnowledge(knowledgeCost)) { - FConst.error("Failed to spend required knowledge (" + knowledgeCost + ").").send(BukkitDirectorContext.sender()); - return; - } - } + if (adaptation.isPermanent() && !force) { + FConst.error(adaptation.getName() + " is permanent and cannot be lowered without force=true.").send(BukkitDirectorContext.sender()); + return; + } - skillLine.setAdaptation(adaptation, targetLevel); - FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); - return; - } + int refund = AdaptConfig.get().isHardcoreNoRefunds() ? 0 : adaptation.getRefundCostFor(targetLevel, currentLevel); + skillLine.setAdaptation(adaptation, targetLevel); + if (refund > 0) { + skillLine.giveKnowledge(refund); + } - if (adaptation.isPermanent() && !force) { - FConst.error(adaptation.getName() + " is permanent and cannot be lowered without force=true.").send(BukkitDirectorContext.sender()); - return; - } + FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); + } - int refund = AdaptConfig.get().isHardcoreNoRefunds() ? 0 : adaptation.getRefundCostFor(targetLevel, currentLevel); - skillLine.setAdaptation(adaptation, targetLevel); - if (refund > 0) { - skillLine.giveKnowledge(refund); - } + @Director(name = "migrate-configs", description = "Force migrate and rewrite all skill/adaptation configs to canonical TOML with comments.") + public void migrateConfigs() { + if (!BukkitDirectorContext.hasPermission("adapt.debug")) { + FConst.error("You lack the Permission 'adapt.debug'").send(BukkitDirectorContext.sender()); + return; + } - FConst.success("Set " + targetPlayer.getName() + " " + adaptation.getName() + " to level " + targetLevel + ".").send(BukkitDirectorContext.sender()); + if (Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + FConst.error("Adapt server is not ready yet. Try again in a few seconds.").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "migrate-configs", description = "Force migrate and rewrite all skill/adaptation configs to canonical TOML with comments.") - public void migrateConfigs() { - if (!BukkitDirectorContext.hasPermission("adapt.debug")) { - FConst.error("You lack the Permission 'adapt.debug'").send(BukkitDirectorContext.sender()); - return; + int migratedSkills = 0; + int migratedAdaptations = 0; + for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getSkills()) { + if (skill instanceof SimpleSkill simpleSkill) { + if (simpleSkill.reloadConfigFromDisk(false)) { + migratedSkills++; } + } - if (Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - FConst.error("Adapt server is not ready yet. Try again in a few seconds.").send(BukkitDirectorContext.sender()); - return; + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation instanceof SimpleAdaptation simpleAdaptation) { + if (simpleAdaptation.reloadConfigFromDisk(false)) { + migratedAdaptations++; + } } + } + } - int migratedSkills = 0; - int migratedAdaptations = 0; - for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getSkills()) { - if (skill instanceof SimpleSkill simpleSkill) { - if (simpleSkill.reloadConfigFromDisk(false)) { - migratedSkills++; - } - } - - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation instanceof SimpleAdaptation simpleAdaptation) { - if (simpleAdaptation.reloadConfigFromDisk(false)) { - migratedAdaptations++; - } - } - } - } + int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); + FConst.success("Canonicalized TOML configs. skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson).send(BukkitDirectorContext.sender()); + } - int deletedLegacyJson = ConfigMigrationManager.deleteMigratedLegacyJsonFiles(); - FConst.success("Canonicalized TOML configs. skills=" + migratedSkills + ", adaptations=" + migratedAdaptations + ", deletedLegacyJson=" + deletedLegacyJson).send(BukkitDirectorContext.sender()); + private List> allSkillSnapshot() { + if (Adapt.instance != null + && Adapt.instance.getAdaptServer() != null + && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { + return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); } - private List> allSkillSnapshot() { - if (Adapt.instance != null - && Adapt.instance.getAdaptServer() != null - && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { - return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - } + return SkillRegistry.skills.sortV(); + } - return SkillRegistry.skills.sortV(); + private Player resolveTargetPlayer(Player player) { + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return null; } - - private Player resolveTargetPlayer(Player player) { - Player targetPlayer = player; - if (targetPlayer == null && BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return null; - } - if (targetPlayer == null) { - targetPlayer = BukkitDirectorContext.player(); - } - return targetPlayer; + if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); } + return targetPlayer; + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandClear.java b/src/main/java/art/arcane/adapt/command/CommandClear.java index f2d2429fa..93d8ff010 100644 --- a/src/main/java/art/arcane/adapt/command/CommandClear.java +++ b/src/main/java/art/arcane/adapt/command/CommandClear.java @@ -3,109 +3,109 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.util.command.FConst; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.director.DirectorOrigin; import art.arcane.volmlib.util.director.annotations.Director; import art.arcane.volmlib.util.director.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import org.bukkit.entity.Player; @Director(name = "clear", origin = DirectorOrigin.BOTH, description = "Clear player progression data") public class CommandClear { - @Director(description = "Clear all player data (XP, knowledge, adaptations, stats, discoveries, advancements, wisdom)") - public void all( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearAll(); - FConst.success("Cleared all data for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); - } - - @Director(description = "Clear XP across all skill lines") - public void xp( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearXp(); - FConst.success("Cleared XP for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); - } - - @Director(description = "Clear knowledge across all skill lines") - public void knowledge( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearKnowledge(); - FConst.success("Cleared knowledge for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + @Director(description = "Clear all player data (XP, knowledge, adaptations, stats, discoveries, advancements, wisdom)") + public void all( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearAll(); + FConst.success("Cleared all data for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + @Director(description = "Clear XP across all skill lines") + public void xp( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearXp(); + FConst.success("Cleared XP for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + @Director(description = "Clear knowledge across all skill lines") + public void knowledge( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearKnowledge(); + FConst.success("Cleared knowledge for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + @Director(description = "Unlearn all adaptations across all skill lines") + public void adaptations( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearAdaptations(); + FConst.success("Cleared adaptations for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + @Director(description = "Clear the stats map") + public void stats( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearStats(); + FConst.success("Cleared stats for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + @Director(description = "Clear all discovery data (biomes, mobs, foods, items, recipes, etc.)") + public void discoveries( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + Player targetPlayer = resolveTarget(player); + if (targetPlayer == null) return; + + PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); + data.clearDiscoveries(); + FConst.success("Cleared discoveries for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + } + + private Player resolveTarget(Player player) { + if (!BukkitDirectorContext.hasPermission("adapt.clear")) { + FConst.error("You lack the Permission 'adapt.clear'").send(BukkitDirectorContext.sender()); + return null; } - @Director(description = "Unlearn all adaptations across all skill lines") - public void adaptations( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearAdaptations(); - FConst.success("Cleared adaptations for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + if (player != null) { + return player; } - @Director(description = "Clear the stats map") - public void stats( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearStats(); - FConst.success("Cleared stats for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); + if (BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return null; } - @Director(description = "Clear all discovery data (biomes, mobs, foods, items, recipes, etc.)") - public void discoveries( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - Player targetPlayer = resolveTarget(player); - if (targetPlayer == null) return; - - PlayerData data = Adapt.instance.getAdaptServer().getPlayer(targetPlayer).getData(); - data.clearDiscoveries(); - FConst.success("Cleared discoveries for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); - } - - private Player resolveTarget(Player player) { - if (!BukkitDirectorContext.hasPermission("adapt.clear")) { - FConst.error("You lack the Permission 'adapt.clear'").send(BukkitDirectorContext.sender()); - return null; - } - - if (player != null) { - return player; - } - - if (BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return null; - } - - return BukkitDirectorContext.player(); - } + return BukkitDirectorContext.player(); + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java index 7f748c95d..10d9081c2 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -4,10 +4,10 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.common.misc.SoundPlayer; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.volmlib.util.director.DirectorOrigin; import art.arcane.volmlib.util.director.annotations.Director; import art.arcane.volmlib.util.director.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -17,97 +17,97 @@ @Director(name = "debug", origin = DirectorOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) public class CommandDebug { - @Director(description = "Toggle verbose mode") - public void verbose() { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } + @Director(description = "Toggle verbose mode") + public void verbose() { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; + } - AdaptConfig.get().setVerbose(!AdaptConfig.get().isVerbose()); - FConst.success("Verbose is now " + (AdaptConfig.get().isVerbose() ? "enabled" : "disabled")).send(BukkitDirectorContext.sender()); + AdaptConfig.get().setVerbose(!AdaptConfig.get().isVerbose()); + FConst.success("Verbose is now " + (AdaptConfig.get().isVerbose() ? "enabled" : "disabled")).send(BukkitDirectorContext.sender()); + } + + @Director(name = "pap", description = "Generate Perms for Adaptations!") + public void pap() { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "pap", description = "Generate Perms for Adaptations!") - public void pap() { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } - - StringBuilder builder = new StringBuilder(); - Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> skill.getAdaptations().forEach(adaptation -> builder - .append("adapt.blacklist.") - .append(adaptation.getName() - .replaceAll("-", "")) - .append("\n"))); - Adapt.info("Permissions: \n" + builder); - FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); + StringBuilder builder = new StringBuilder(); + Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> skill.getAdaptations().forEach(adaptation -> builder + .append("adapt.blacklist.") + .append(adaptation.getName() + .replaceAll("-", "")) + .append("\n"))); + Adapt.info("Permissions: \n" + builder); + FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); + } + + @Director(name = "psp", description = "Generate Perms for Skills!") + public void psp() { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "psp", description = "Generate Perms for Skills!") - public void psp() { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } - - StringBuilder builder = new StringBuilder(); - Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> builder - .append("adapt.blacklist.") - .append(skill.getName() - .replaceAll("-", "")) - .append("\n")); - Adapt.info("Permissions: \n" + builder); - FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); + StringBuilder builder = new StringBuilder(); + Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> builder + .append("adapt.blacklist.") + .append(skill.getName() + .replaceAll("-", "")) + .append("\n")); + Adapt.info("Permissions: \n" + builder); + FConst.success("Permissions have been printed to console.").send(BukkitDirectorContext.sender()); + } + + @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") + public void particle(@Param Particle particle) { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") - public void particle(@Param Particle particle) { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } + Player player = BukkitDirectorContext.player(); + player.spawnParticle(particle, player.getLocation(), 10, 10); + } - Player player = BukkitDirectorContext.player(); - player.spawnParticle(particle, player.getLocation(), 10, 10); + @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") + public void particle(@Param Sound sound) { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; } - @Director(name = "particle", origin = DirectorOrigin.PLAYER, description = "Summon a particle in front of you for testing!") - public void particle(@Param Sound sound) { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } + SoundPlayer sp = SoundPlayer.of(BukkitDirectorContext.player()); + sp.play(BukkitDirectorContext.player().getLocation(), sound, 1, 1); + } + + @Director(description = "Show Adapt ticker hotspots") + public void perf( + @Param(description = "Top results to print", defaultValue = "12") + int top, + @Param(description = "Reset metrics after printing", defaultValue = "false") + boolean reset + ) { + if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { + FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); + return; + } - SoundPlayer sp = SoundPlayer.of(BukkitDirectorContext.player()); - sp.play(BukkitDirectorContext.player().getLocation(), sound, 1, 1); + List lines = Adapt.instance.getTicker().topMetrics(top); + long windowMs = Adapt.instance.getTicker().getMetricsWindowMs(); + FConst.success("Ticker window: " + windowMs + "ms").send(BukkitDirectorContext.sender()); + if (lines.isEmpty()) { + FConst.success("No tick metrics collected yet.").send(BukkitDirectorContext.sender()); + } else { + lines.forEach(line -> FConst.info(line).send(BukkitDirectorContext.sender())); } - @Director(description = "Show Adapt ticker hotspots") - public void perf( - @Param(description = "Top results to print", defaultValue = "12") - int top, - @Param(description = "Reset metrics after printing", defaultValue = "false") - boolean reset - ) { - if (!BukkitDirectorContext.hasPermission("adapt.idontknowwhatimdoingiswear")) { - FConst.error("You lack the Permission 'adapt.idontknowwhatimdoingiswear'").send(BukkitDirectorContext.sender()); - return; - } - - List lines = Adapt.instance.getTicker().topMetrics(top); - long windowMs = Adapt.instance.getTicker().getMetricsWindowMs(); - FConst.success("Ticker window: " + windowMs + "ms").send(BukkitDirectorContext.sender()); - if (lines.isEmpty()) { - FConst.success("No tick metrics collected yet.").send(BukkitDirectorContext.sender()); - } else { - lines.forEach(line -> FConst.info(line).send(BukkitDirectorContext.sender())); - } - - if (reset) { - Adapt.instance.getTicker().resetMetrics(); - FConst.success("Ticker metrics reset.").send(BukkitDirectorContext.sender()); - } + if (reset) { + Adapt.instance.getTicker().resetMetrics(); + FConst.success("Ticker metrics reset.").send(BukkitDirectorContext.sender()); } + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandDefault.java b/src/main/java/art/arcane/adapt/command/CommandDefault.java index e3d63e8a0..b9dab0baa 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDefault.java +++ b/src/main/java/art/arcane/adapt/command/CommandDefault.java @@ -7,11 +7,11 @@ import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.util.command.FConst; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.director.DirectorOrigin; import art.arcane.volmlib.util.director.annotations.Director; import art.arcane.volmlib.util.director.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import java.io.File; import java.io.IOException; @@ -23,173 +23,173 @@ @Director(name = "default", origin = DirectorOrigin.BOTH, description = "Reset configs to defaults") public class CommandDefault { - @Director(description = "Reset a skill config to defaults") - public void skill( - @Param(description = "skill to reset") - AdaptationListingHandler.SkillProvider skillTarget - ) { - if (!BukkitDirectorContext.sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); - return; - } + @Director(description = "Reset a skill config to defaults") + public void skill( + @Param(description = "skill to reset") + AdaptationListingHandler.SkillProvider skillTarget + ) { + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillTarget.name()); - if (skill == null) { - FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); - return; - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skillTarget.name()); + if (skill == null) { + FConst.error("Unknown skill: " + skillTarget.name()).send(BukkitDirectorContext.sender()); + return; + } - if (!(skill instanceof SimpleSkill simpleSkill)) { - FConst.error("Skill " + skill.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); - return; - } + if (!(skill instanceof SimpleSkill simpleSkill)) { + FConst.error("Skill " + skill.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); + return; + } - File configFile = Adapt.instance.getDataFile("adapt", "skills", skill.getName() + ".toml"); - if (configFile.exists() && !configFile.delete()) { - FConst.error("Failed to delete config file for " + skill.getName()).send(BukkitDirectorContext.sender()); - return; - } + File configFile = Adapt.instance.getDataFile("adapt", "skills", skill.getName() + ".toml"); + if (configFile.exists() && !configFile.delete()) { + FConst.error("Failed to delete config file for " + skill.getName()).send(BukkitDirectorContext.sender()); + return; + } - simpleSkill.reloadConfigFromDisk(false); - FConst.success("Reset config for skill " + skill.getName() + " to defaults.").send(BukkitDirectorContext.sender()); + simpleSkill.reloadConfigFromDisk(false); + FConst.success("Reset config for skill " + skill.getName() + " to defaults.").send(BukkitDirectorContext.sender()); + } + + @Director(description = "Reset an adaptation config to defaults") + public void adaptation( + @Param(description = "adaptation to reset (skill:adaptation)") + AdaptationListingHandler.AdaptationProvider adaptationTarget + ) { + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); + return; } - @Director(description = "Reset an adaptation config to defaults") - public void adaptation( - @Param(description = "adaptation to reset (skill:adaptation)") - AdaptationListingHandler.AdaptationProvider adaptationTarget - ) { - if (!BukkitDirectorContext.sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); - return; - } + String[] split = adaptationTarget.name().split(":"); + if (split.length != 2) { + FConst.error("Invalid format. Use skill:adaptation").send(BukkitDirectorContext.sender()); + return; + } - String[] split = adaptationTarget.name().split(":"); - if (split.length != 2) { - FConst.error("Invalid format. Use skill:adaptation").send(BukkitDirectorContext.sender()); - return; - } + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(split[0]); + if (skill == null) { + FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); + return; + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(split[0]); - if (skill == null) { - FConst.error("Unknown skill: " + split[0]).send(BukkitDirectorContext.sender()); - return; - } + Adaptation adaptation = null; + for (Adaptation a : skill.getAdaptations()) { + if (a.getName().equalsIgnoreCase(split[1])) { + adaptation = a; + break; + } + } - Adaptation adaptation = null; - for (Adaptation a : skill.getAdaptations()) { - if (a.getName().equalsIgnoreCase(split[1])) { - adaptation = a; - break; - } - } + if (adaptation == null) { + FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); + return; + } - if (adaptation == null) { - FConst.error("Unknown adaptation: " + split[1] + " in skill " + skill.getName()).send(BukkitDirectorContext.sender()); - return; - } + if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { + FConst.error("Adaptation " + adaptation.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); + return; + } - if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { - FConst.error("Adaptation " + adaptation.getName() + " does not support config reset.").send(BukkitDirectorContext.sender()); - return; - } + File configFile = Adapt.instance.getDataFile("adapt", "adaptations", adaptation.getName() + ".toml"); + if (configFile.exists() && !configFile.delete()) { + FConst.error("Failed to delete config file for " + adaptation.getName()).send(BukkitDirectorContext.sender()); + return; + } - File configFile = Adapt.instance.getDataFile("adapt", "adaptations", adaptation.getName() + ".toml"); - if (configFile.exists() && !configFile.delete()) { - FConst.error("Failed to delete config file for " + adaptation.getName()).send(BukkitDirectorContext.sender()); - return; - } + simpleAdaptation.reloadConfigFromDisk(false); + FConst.success("Reset config for adaptation " + adaptation.getName() + " to defaults.").send(BukkitDirectorContext.sender()); + } - simpleAdaptation.reloadConfigFromDisk(false); - FConst.success("Reset config for adaptation " + adaptation.getName() + " to defaults.").send(BukkitDirectorContext.sender()); + @Director(description = "Reset ALL configs to defaults and archive the old settings") + public void all() { + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); + return; } - @Director(description = "Reset ALL configs to defaults and archive the old settings") - public void all() { - if (!BukkitDirectorContext.sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); - return; - } + String timestamp = new SimpleDateFormat("yyyy-MM-dd_HHmmss").format(new Date()); + File archiveDir = Adapt.instance.getDataFolder("config-archive", timestamp); - String timestamp = new SimpleDateFormat("yyyy-MM-dd_HHmmss").format(new Date()); - File archiveDir = Adapt.instance.getDataFolder("config-archive", timestamp); + int archived = 0; + int reset = 0; - int archived = 0; - int reset = 0; + // Archive and reset main config + File mainConfig = Adapt.instance.getDataFile("adapt", "adapt.toml"); + if (mainConfig.exists()) { + if (archiveFile(mainConfig, new File(archiveDir, "adapt.toml"))) { + archived++; + } + mainConfig.delete(); + } - // Archive and reset main config - File mainConfig = Adapt.instance.getDataFile("adapt", "adapt.toml"); - if (mainConfig.exists()) { - if (archiveFile(mainConfig, new File(archiveDir, "adapt.toml"))) { - archived++; - } - mainConfig.delete(); - } + // Archive and reset skill configs + File skillsDir = Adapt.instance.getDataFolder("adapt", "skills"); + if (skillsDir.exists()) { + File archiveSkillsDir = new File(archiveDir, "skills"); + archiveSkillsDir.mkdirs(); + File[] skillFiles = skillsDir.listFiles((dir, name) -> name.endsWith(".toml")); + if (skillFiles != null) { + for (File f : skillFiles) { + if (archiveFile(f, new File(archiveSkillsDir, f.getName()))) { + archived++; + } + f.delete(); + } + } + } - // Archive and reset skill configs - File skillsDir = Adapt.instance.getDataFolder("adapt", "skills"); - if (skillsDir.exists()) { - File archiveSkillsDir = new File(archiveDir, "skills"); - archiveSkillsDir.mkdirs(); - File[] skillFiles = skillsDir.listFiles((dir, name) -> name.endsWith(".toml")); - if (skillFiles != null) { - for (File f : skillFiles) { - if (archiveFile(f, new File(archiveSkillsDir, f.getName()))) { - archived++; - } - f.delete(); - } - } - } + // Archive and reset adaptation configs + File adaptationsDir = Adapt.instance.getDataFolder("adapt", "adaptations"); + if (adaptationsDir.exists()) { + File archiveAdaptationsDir = new File(archiveDir, "adaptations"); + archiveAdaptationsDir.mkdirs(); + File[] adaptationFiles = adaptationsDir.listFiles((dir, name) -> name.endsWith(".toml")); + if (adaptationFiles != null) { + for (File f : adaptationFiles) { + if (archiveFile(f, new File(archiveAdaptationsDir, f.getName()))) { + archived++; + } + f.delete(); + } + } + } - // Archive and reset adaptation configs - File adaptationsDir = Adapt.instance.getDataFolder("adapt", "adaptations"); - if (adaptationsDir.exists()) { - File archiveAdaptationsDir = new File(archiveDir, "adaptations"); - archiveAdaptationsDir.mkdirs(); - File[] adaptationFiles = adaptationsDir.listFiles((dir, name) -> name.endsWith(".toml")); - if (adaptationFiles != null) { - for (File f : adaptationFiles) { - if (archiveFile(f, new File(archiveAdaptationsDir, f.getName()))) { - archived++; - } - f.delete(); - } - } - } + // Reload main config from defaults + AdaptConfig.reload(); - // Reload main config from defaults - AdaptConfig.reload(); - - // Reload all skill and adaptation configs from defaults - for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getSkills()) { - if (skill instanceof SimpleSkill simpleSkill) { - if (simpleSkill.reloadConfigFromDisk(false)) { - reset++; - } - } - - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation instanceof SimpleAdaptation simpleAdaptation) { - if (simpleAdaptation.reloadConfigFromDisk(false)) { - reset++; - } - } - } + // Reload all skill and adaptation configs from defaults + for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getSkills()) { + if (skill instanceof SimpleSkill simpleSkill) { + if (simpleSkill.reloadConfigFromDisk(false)) { + reset++; } + } - FConst.success("Archived " + archived + " config files to config-archive/" + timestamp + "/").send(BukkitDirectorContext.sender()); - FConst.success("Reset " + reset + " configs to defaults.").send(BukkitDirectorContext.sender()); + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation instanceof SimpleAdaptation simpleAdaptation) { + if (simpleAdaptation.reloadConfigFromDisk(false)) { + reset++; + } + } + } } - private boolean archiveFile(File source, File destination) { - try { - destination.getParentFile().mkdirs(); - Files.copy(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); - return true; - } catch (IOException e) { - Adapt.warn("Failed to archive " + source.getPath() + ": " + e.getMessage()); - return false; - } + FConst.success("Archived " + archived + " config files to config-archive/" + timestamp + "/").send(BukkitDirectorContext.sender()); + FConst.success("Reset " + reset + " configs to defaults.").send(BukkitDirectorContext.sender()); + } + + private boolean archiveFile(File source, File destination) { + try { + destination.getParentFile().mkdirs(); + Files.copy(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); + return true; + } catch (IOException e) { + Adapt.warn("Failed to archive " + source.getPath() + ": " + e.getMessage()); + return false; } + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandReset.java b/src/main/java/art/arcane/adapt/command/CommandReset.java index 61cb51b53..e57636d3c 100644 --- a/src/main/java/art/arcane/adapt/command/CommandReset.java +++ b/src/main/java/art/arcane/adapt/command/CommandReset.java @@ -3,11 +3,11 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.command.FConst; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.adapt.util.director.specialhandlers.NullablePlayerHandler; import art.arcane.volmlib.util.director.DirectorOrigin; import art.arcane.volmlib.util.director.annotations.Director; import art.arcane.volmlib.util.director.annotations.Param; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import org.bukkit.entity.Player; import java.util.HashMap; @@ -16,55 +16,55 @@ @Director(name = "reset", origin = DirectorOrigin.BOTH, description = "Permanently delete all Adapt data for a player") public class CommandReset { - private static final Map pendingConfirmations = new HashMap<>(); - private static final long CONFIRMATION_TIMEOUT_MS = 30_000; - - @Director(description = "Permanently delete all Adapt data for a player. Requires op. Run twice to confirm.") - public void confirm( - @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) - Player player - ) { - if (!BukkitDirectorContext.sender().isOp()) { - FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); - return; - } + private static final Map pendingConfirmations = new HashMap<>(); + private static final long CONFIRMATION_TIMEOUT_MS = 30_000; - Player targetPlayer = player; - if (targetPlayer == null && BukkitDirectorContext.isConsole()) { - FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); - return; - } else if (targetPlayer == null) { - targetPlayer = BukkitDirectorContext.player(); - } + @Director(description = "Permanently delete all Adapt data for a player. Requires op. Run twice to confirm.") + public void confirm( + @Param(description = "player", defaultValue = "---", customHandler = NullablePlayerHandler.class) + Player player + ) { + if (!BukkitDirectorContext.sender().isOp()) { + FConst.error("This command can only be run by server operators.").send(BukkitDirectorContext.sender()); + return; + } - UUID senderUuid = BukkitDirectorContext.isPlayer() ? BukkitDirectorContext.player().getUniqueId() : new UUID(0, 0); - UUID targetUuid = targetPlayer.getUniqueId(); - long now = System.currentTimeMillis(); + Player targetPlayer = player; + if (targetPlayer == null && BukkitDirectorContext.isConsole()) { + FConst.error("You must specify a player when using this command from console.").send(BukkitDirectorContext.sender()); + return; + } else if (targetPlayer == null) { + targetPlayer = BukkitDirectorContext.player(); + } - PendingReset pending = pendingConfirmations.get(senderUuid); - if (pending != null && pending.targetUuid.equals(targetUuid) && now - pending.timestamp < CONFIRMATION_TIMEOUT_MS) { - pendingConfirmations.remove(senderUuid); + UUID senderUuid = BukkitDirectorContext.isPlayer() ? BukkitDirectorContext.player().getUniqueId() : new UUID(0, 0); + UUID targetUuid = targetPlayer.getUniqueId(); + long now = System.currentTimeMillis(); - AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(targetPlayer); - adaptPlayer.delete(targetUuid); - Adapt.info("Operator " + BukkitDirectorContext.name() + " reset all Adapt data for " + targetPlayer.getName()); - FConst.success("All Adapt data for " + targetPlayer.getName() + " has been permanently deleted.").send(BukkitDirectorContext.sender()); - return; - } + PendingReset pending = pendingConfirmations.get(senderUuid); + if (pending != null && pending.targetUuid.equals(targetUuid) && now - pending.timestamp < CONFIRMATION_TIMEOUT_MS) { + pendingConfirmations.remove(senderUuid); - pendingConfirmations.put(senderUuid, new PendingReset(targetUuid, now)); - FConst.error("WARNING: This will permanently delete ALL Adapt data for " + targetPlayer.getName() + ".").send(BukkitDirectorContext.sender()); - FConst.error("This includes XP, skills, adaptations, discoveries, stats, and advancements.").send(BukkitDirectorContext.sender()); - FConst.error("Run this command again within 30 seconds to confirm.").send(BukkitDirectorContext.sender()); + AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(targetPlayer); + adaptPlayer.delete(targetUuid); + Adapt.info("Operator " + BukkitDirectorContext.name() + " reset all Adapt data for " + targetPlayer.getName()); + FConst.success("All Adapt data for " + targetPlayer.getName() + " has been permanently deleted.").send(BukkitDirectorContext.sender()); + return; } - private static class PendingReset { - final UUID targetUuid; - final long timestamp; + pendingConfirmations.put(senderUuid, new PendingReset(targetUuid, now)); + FConst.error("WARNING: This will permanently delete ALL Adapt data for " + targetPlayer.getName() + ".").send(BukkitDirectorContext.sender()); + FConst.error("This includes XP, skills, adaptations, discoveries, stats, and advancements.").send(BukkitDirectorContext.sender()); + FConst.error("Run this command again within 30 seconds to confirm.").send(BukkitDirectorContext.sender()); + } + + private static class PendingReset { + final UUID targetUuid; + final long timestamp; - PendingReset(UUID targetUuid, long timestamp) { - this.targetUuid = targetUuid; - this.timestamp = timestamp; - } + PendingReset(UUID targetUuid, long timestamp) { + this.targetUuid = targetUuid; + this.timestamp = timestamp; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java index 8ff769a1f..374d3e420 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java @@ -26,10 +26,10 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -45,159 +45,159 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityArmorUp extends SimpleAdaptation { - private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-armor-up".getBytes()); - private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:armor-up"); - private final Map ticksRunning; - - - public AgilityArmorUp() { - super("agility-armor-up"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.armor_up.description")); - setIcon(Material.IRON_CHESTPLATE); - setDisplayName(Localizer.dLocalize("agility.armor_up.name")); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setInitialCost(getConfig().initialCost); - setInterval(50); - ticksRunning = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_agility_armor_up_30min") - .title(Localizer.dLocalize("advancement.challenge_agility_armor_up_30min.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_armor_up_30min.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_CHESTPLATE) - .key("challenge_agility_armor_up_5hr") - .title(Localizer.dLocalize("advancement.challenge_agility_armor_up_5hr.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_armor_up_5hr.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_armor_up_30min", "agility.armor-up.ticks-armored", 36000, 500); - registerMilestone("challenge_agility_armor_up_5hr", "agility.armor-up.ticks-armored", 360000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getWindupArmor(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("agility.armor_up.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getWindupTicks(getLevelPercent(level)) * 50D, 1) + " " + C.GRAY + Localizer.dLocalize("agility.armor_up.lore2")); + private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-armor-up".getBytes()); + private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString("adapt:armor-up"); + private final Map ticksRunning; + + + public AgilityArmorUp() { + super("agility-armor-up"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.armor_up.description")); + setIcon(Material.IRON_CHESTPLATE); + setDisplayName(Localizer.dLocalize("agility.armor_up.name")); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setInitialCost(getConfig().initialCost); + setInterval(50); + ticksRunning = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_agility_armor_up_30min") + .title(Localizer.dLocalize("advancement.challenge_agility_armor_up_30min.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_armor_up_30min.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_CHESTPLATE) + .key("challenge_agility_armor_up_5hr") + .title(Localizer.dLocalize("advancement.challenge_agility_armor_up_5hr.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_armor_up_5hr.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_armor_up_30min", "agility.armor-up.ticks-armored", 36000, 500); + registerMilestone("challenge_agility_armor_up_5hr", "agility.armor-up.ticks-armored", 360000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getWindupArmor(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("agility.armor_up.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getWindupTicks(getLevelPercent(level)) * 50D, 1) + " " + C.GRAY + Localizer.dLocalize("agility.armor_up.lore2")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + ticksRunning.remove(e.getPlayer().getUniqueId()); + } + + private double getWindupTicks(double factor) { + return M.lerp(getConfig().windupTicksSlowest, getConfig().windupTicksFastest, factor); + } + + private double getWindupArmor(double factor) { + return getConfig().windupArmorBase + (factor * getConfig().windupArmorLevelMultiplier); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + withPlayerThread(p, () -> updatePlayer(p)); } + } - @EventHandler - public void on(PlayerQuitEvent e) { - ticksRunning.remove(e.getPlayer().getUniqueId()); + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; } - private double getWindupTicks(double factor) { - return M.lerp(getConfig().windupTicksSlowest, getConfig().windupTicksFastest, factor); + UUID id = p.getUniqueId(); + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + if (attribute == null) { + ticksRunning.remove(id); + return; } - private double getWindupArmor(double factor) { - return getConfig().windupArmorBase + (factor * getConfig().windupArmorLevelMultiplier); + try { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } catch (Exception e) { + Adapt.verbose("Failed to remove windup modifier: " + e.getMessage()); } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - withPlayerThread(p, () -> updatePlayer(p)); - } + if (!hasActiveAdaptation(p) || p.isSwimming() || p.isFlying() || p.isGliding() || p.isSneaking()) { + ticksRunning.remove(id); + return; } - private void updatePlayer(Player p) { - if (p == null || !p.isOnline()) { - return; - } - - UUID id = p.getUniqueId(); - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); - if (attribute == null) { - ticksRunning.remove(id); - return; - } - - try { - attribute.removeModifier(MODIFIER, MODIFIER_KEY); - } catch (Exception e) { - Adapt.verbose("Failed to remove windup modifier: " + e.getMessage()); - } - - if (!hasActiveAdaptation(p) || p.isSwimming() || p.isFlying() || p.isGliding() || p.isSneaking()) { - ticksRunning.remove(id); - return; - } - - if (!p.isSprinting()) { - ticksRunning.remove(id); - return; - } - - ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); - int tr = ticksRunning.getOrDefault(id, 0); - if (tr <= 0) { - return; - } - - double factor = getLevelPercent(p); - double ticksToMax = getWindupTicks(factor); - double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); - double armorInc = M.lerp(0, getWindupArmor(factor), progress); - - if (areParticlesEnabled()) { - if (M.r(0.2 * progress)) { - p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation(), 1); - } - - if (M.r(0.25 * progress)) { - p.getWorld().spawnParticle(Particle.WAX_ON, p.getLocation(), 1, 0, 0, 0, 0); - } - } - - attribute.setModifier(MODIFIER, MODIFIER_KEY, armorInc * 10, AttributeModifier.Operation.MULTIPLY_SCALAR_1); - getPlayer(p).getData().addStat("agility.armor-up.ticks-armored", 1); + if (!p.isSprinting()) { + ticksRunning.remove(id); + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); + int tr = ticksRunning.getOrDefault(id, 0); + if (tr <= 0) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + double factor = getLevelPercent(p); + double ticksToMax = getWindupTicks(factor); + double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); + double armorInc = M.lerp(0, getWindupArmor(factor), progress); + if (areParticlesEnabled()) { + if (M.r(0.2 * progress)) { + p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation(), 1); + } - @NoArgsConstructor - @ConfigDescription("Gain more armor the longer you sprint.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Armor Up adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupTicksSlowest = 180; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupTicksFastest = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Base for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupArmorBase = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Level Multiplier for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupArmorLevelMultiplier = 0.525; + if (M.r(0.25 * progress)) { + p.getWorld().spawnParticle(Particle.WAX_ON, p.getLocation(), 1, 0, 0, 0, 0); + } } + attribute.setModifier(MODIFIER, MODIFIER_KEY, armorInc * 10, AttributeModifier.Operation.MULTIPLY_SCALAR_1); + getPlayer(p).getData().addStat("agility.armor-up.ticks-armored", 1); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + + @NoArgsConstructor + @ConfigDescription("Gain more armor the longer you sprint.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Armor Up adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupTicksSlowest = 180; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupTicksFastest = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Base for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupArmorBase = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Armor Level Multiplier for the Agility Armor Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupArmorLevelMultiplier = 0.525; + } + } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java index a58422249..ab37b1eaa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityLadderSlide.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,235 +44,235 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityLadderSlide extends SimpleAdaptation { - private final Map upwardStates; - - public AgilityLadderSlide() { - super("agility-ladder-slide"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.ladder_slide.description")); - setDisplayName(Localizer.dLocalize("agility.ladder_slide.name")); - setIcon(Material.LADDER); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(50); - upwardStates = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LADDER) - .key("challenge_agility_ladder_500") - .title(Localizer.dLocalize("advancement.challenge_agility_ladder_500.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_ladder_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_CHAIN) - .key("challenge_agility_ladder_10k") - .title(Localizer.dLocalize("advancement.challenge_agility_ladder_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_ladder_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_ladder_500", "agility.ladder-slide.blocks-climbed", 500, 300); - registerMilestone("challenge_agility_ladder_10k", "agility.ladder-slide.blocks-climbed", 10000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getConfig().speedMultiplier, 1) + "x " + C.GRAY + Localizer.dLocalize("agility.ladder_slide.lore1")); - v.addLore(C.YELLOW + "Downward speed boost coming in a future update."); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - if (e.getTo() == null) { - return; - } - - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - var context = resolveInteractContext(p, p.getLocation(), player -> !player.isFlying() && !player.isGliding() && !player.isSwimming()); - if (context == null) { - clearUpwardState(p); - return; - } - - Location location = context.location(); - - Block activeLadder = getActiveLadderBlock(location); - if (activeLadder == null) { - clearUpwardState(p); - return; - } - - double dy = e.getTo().getY() - e.getFrom().getY(); - boolean lookingUp = p.getLocation().getPitch() <= -Math.abs(getConfig().lookUpPitchThreshold); - if (!lookingUp) { - clearUpwardState(p); - return; - } - - double epsilon = Math.abs(getConfig().movementDirectionEpsilonUpward); - Vector velocity = p.getVelocity(); - if (p.isSneaking()) { - clearUpwardState(p); - applyVerticalVelocity(p, velocity, 0); - return; - } - - boolean movingUp = dy > epsilon; - if (!movingUp) { - clearUpwardState(p); - return; - } - - double baseUp = Math.max(0, getConfig().normalUpwardLadderSpeed); - double targetUp = isNearLadderEnd(activeLadder, true) ? baseUp : getUpwardSpeed(); - applySmoothUpwardVelocity(p, velocity, baseUp, targetUp); - p.setFallDistance(0); - getPlayer(p).getData().addStat("agility.ladder-slide.blocks-climbed", 1); - }); - } - - @EventHandler - public void on(PlayerQuitEvent e) { - clearUpwardState(e.getPlayer()); - } - - private void applySmoothUpwardVelocity(Player p, Vector velocity, double baseUpwardSpeed, double targetUpwardSpeed) { - double minUp = Math.max(0, baseUpwardSpeed); - double target = Math.max(minUp, targetUpwardSpeed); - long now = System.currentTimeMillis(); - UpwardState state = upwardStates.get(p.getUniqueId()); - if (state == null || now - state.lastSeenAt > Math.max(0, getConfig().upwardStateResetMs)) { - state = new UpwardState(Math.max(minUp, Math.max(0, velocity.getY())), now); - upwardStates.put(p.getUniqueId(), state); - } - - double smoothing = clamp(getConfig().upwardAccelerationSmoothing, 0.01, 1.0); - double current = Math.max(minUp, state.currentSpeed); - double next = current + ((target - current) * smoothing); - state.currentSpeed = Math.min(target, Math.max(minUp, next)); - state.lastSeenAt = now; - applyVerticalVelocity(p, velocity, state.currentSpeed); + private final Map upwardStates; + + public AgilityLadderSlide() { + super("agility-ladder-slide"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.ladder_slide.description")); + setDisplayName(Localizer.dLocalize("agility.ladder_slide.name")); + setIcon(Material.LADDER); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(50); + upwardStates = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LADDER) + .key("challenge_agility_ladder_500") + .title(Localizer.dLocalize("advancement.challenge_agility_ladder_500.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_ladder_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_CHAIN) + .key("challenge_agility_ladder_10k") + .title(Localizer.dLocalize("advancement.challenge_agility_ladder_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_ladder_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_ladder_500", "agility.ladder-slide.blocks-climbed", 500, 300); + registerMilestone("challenge_agility_ladder_10k", "agility.ladder-slide.blocks-climbed", 10000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getConfig().speedMultiplier, 1) + "x " + C.GRAY + Localizer.dLocalize("agility.ladder_slide.lore1")); + v.addLore(C.YELLOW + "Downward speed boost coming in a future update."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + if (e.getTo() == null) { + return; } - private void clearUpwardState(Player p) { - if (p == null) { - return; - } - upwardStates.remove(p.getUniqueId()); + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation(), player -> !player.isFlying() && !player.isGliding() && !player.isSwimming()); + if (context == null) { + clearUpwardState(p); + return; + } + + Location location = context.location(); + + Block activeLadder = getActiveLadderBlock(location); + if (activeLadder == null) { + clearUpwardState(p); + return; + } + + double dy = e.getTo().getY() - e.getFrom().getY(); + boolean lookingUp = p.getLocation().getPitch() <= -Math.abs(getConfig().lookUpPitchThreshold); + if (!lookingUp) { + clearUpwardState(p); + return; + } + + double epsilon = Math.abs(getConfig().movementDirectionEpsilonUpward); + Vector velocity = p.getVelocity(); + if (p.isSneaking()) { + clearUpwardState(p); + applyVerticalVelocity(p, velocity, 0); + return; + } + + boolean movingUp = dy > epsilon; + if (!movingUp) { + clearUpwardState(p); + return; + } + + double baseUp = Math.max(0, getConfig().normalUpwardLadderSpeed); + double targetUp = isNearLadderEnd(activeLadder, true) ? baseUp : getUpwardSpeed(); + applySmoothUpwardVelocity(p, velocity, baseUp, targetUp); + p.setFallDistance(0); + getPlayer(p).getData().addStat("agility.ladder-slide.blocks-climbed", 1); + }); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + clearUpwardState(e.getPlayer()); + } + + private void applySmoothUpwardVelocity(Player p, Vector velocity, double baseUpwardSpeed, double targetUpwardSpeed) { + double minUp = Math.max(0, baseUpwardSpeed); + double target = Math.max(minUp, targetUpwardSpeed); + long now = System.currentTimeMillis(); + UpwardState state = upwardStates.get(p.getUniqueId()); + if (state == null || now - state.lastSeenAt > Math.max(0, getConfig().upwardStateResetMs)) { + state = new UpwardState(Math.max(minUp, Math.max(0, velocity.getY())), now); + upwardStates.put(p.getUniqueId(), state); } - private double clamp(double v, double min, double max) { - return Math.max(min, Math.min(max, v)); + double smoothing = clamp(getConfig().upwardAccelerationSmoothing, 0.01, 1.0); + double current = Math.max(minUp, state.currentSpeed); + double next = current + ((target - current) * smoothing); + state.currentSpeed = Math.min(target, Math.max(minUp, next)); + state.lastSeenAt = now; + applyVerticalVelocity(p, velocity, state.currentSpeed); + } + + private void clearUpwardState(Player p) { + if (p == null) { + return; } + upwardStates.remove(p.getUniqueId()); + } - private void applyVerticalVelocity(Player p, Vector velocity, double targetY) { - if (Math.abs(velocity.getY() - targetY) <= getConfig().velocityEpsilon) { - return; - } + private double clamp(double v, double min, double max) { + return Math.max(min, Math.min(max, v)); + } - p.setVelocity(velocity.setY(targetY)); + private void applyVerticalVelocity(Player p, Vector velocity, double targetY) { + if (Math.abs(velocity.getY() - targetY) <= getConfig().velocityEpsilon) { + return; } - private Block getActiveLadderBlock(Location location) { - Block feet = location.getBlock(); - if (feet.getType() == Material.LADDER) { - return feet; - } + p.setVelocity(velocity.setY(targetY)); + } - Block head = location.clone().add(0, 1, 0).getBlock(); - if (head.getType() == Material.LADDER) { - return head; - } - - return null; - } - - private boolean isNearLadderEnd(Block ladder, boolean upward) { - int laddersAhead = 0; - Block cursor = ladder; - BlockFace direction = upward ? BlockFace.UP : BlockFace.DOWN; - int limit = Math.max(1, getConfig().maxLadderScanDistance); - for (int i = 0; i < limit; i++) { - cursor = cursor.getRelative(direction); - if (cursor.getType() != Material.LADDER) { - break; - } - - laddersAhead++; - if (laddersAhead > getConfig().revertDistanceBlocks) { - return false; - } - } - - return laddersAhead <= getConfig().revertDistanceBlocks; - } - - @Override - public void onTick() { + private Block getActiveLadderBlock(Location location) { + Block feet = location.getBlock(); + if (feet.getType() == Material.LADDER) { + return feet; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + Block head = location.clone().add(0, 1, 0).getBlock(); + if (head.getType() == Material.LADDER) { + return head; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + return null; + } + + private boolean isNearLadderEnd(Block ladder, boolean upward) { + int laddersAhead = 0; + Block cursor = ladder; + BlockFace direction = upward ? BlockFace.UP : BlockFace.DOWN; + int limit = Math.max(1, getConfig().maxLadderScanDistance); + for (int i = 0; i < limit; i++) { + cursor = cursor.getRelative(direction); + if (cursor.getType() != Material.LADDER) { + break; + } + + laddersAhead++; + if (laddersAhead > getConfig().revertDistanceBlocks) { + return false; + } } - @NoArgsConstructor - @ConfigDescription("Climb and slide ladders much faster in both directions.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to baseUpwardLadderSpeed to compute the target climb speed.", impact = "Higher values increase final ladder climb speed after the ramp-up phase.") - double speedMultiplier = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Velocity difference threshold used to skip tiny Y-velocity adjustments.", impact = "Lower values apply more frequent micro-updates; higher values reduce minor velocity writes.") - double velocityEpsilon = 0.003; - @art.arcane.adapt.util.config.ConfigDoc(value = "Baseline climb speed used before the speed multiplier is applied.", impact = "Higher values raise the base climb profile and increase total ladder ascent speed.") - double baseUpwardLadderSpeed = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vanilla-like upward speed used near ladder endpoints to avoid overshooting.", impact = "Higher values make endpoint climbing snappier; lower values keep transitions conservative.") - double normalUpwardLadderSpeed = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum positive Y movement treated as intentional upward ladder motion.", impact = "Lower values are more sensitive to slight upward input; higher values require clearer upward movement.") - double movementDirectionEpsilonUpward = 0.0004; - @art.arcane.adapt.util.config.ConfigDoc(value = "Smoothing factor for blending current upward velocity toward target ladder speed.", impact = "Values near 1.0 ramp quickly; lower values create a slower curve-like acceleration profile.") - double upwardAccelerationSmoothing = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "How long to retain previous upward speed state between ladder movement samples.", impact = "Lower values reset ramp-up sooner; higher values preserve momentum between short interruptions.") - long upwardStateResetMs = 200; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward look angle required to activate upward ladder acceleration.", impact = "Larger values require players to look farther upward before acceleration engages.") - double lookUpPitchThreshold = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Distance from ladder top where motion reverts toward normal upward speed.", impact = "Higher values begin fallback earlier near ladder ends; lower values keep boosted speed longer.") - int revertDistanceBlocks = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum blocks scanned to detect ladder continuity when checking ladder endpoints.", impact = "Higher values support taller ladders at slightly higher per-check cost.") - int maxLadderScanDistance = 64; - } - - private double getUpwardSpeed() { - return getConfig().baseUpwardLadderSpeed * getConfig().speedMultiplier; - } - - private static class UpwardState { - private double currentSpeed; - private long lastSeenAt; - - private UpwardState(double currentSpeed, long lastSeenAt) { - this.currentSpeed = currentSpeed; - this.lastSeenAt = lastSeenAt; - } + return laddersAhead <= getConfig().revertDistanceBlocks; + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private double getUpwardSpeed() { + return getConfig().baseUpwardLadderSpeed * getConfig().speedMultiplier; + } + + @NoArgsConstructor + @ConfigDescription("Climb and slide ladders much faster in both directions.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to baseUpwardLadderSpeed to compute the target climb speed.", impact = "Higher values increase final ladder climb speed after the ramp-up phase.") + double speedMultiplier = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Velocity difference threshold used to skip tiny Y-velocity adjustments.", impact = "Lower values apply more frequent micro-updates; higher values reduce minor velocity writes.") + double velocityEpsilon = 0.003; + @art.arcane.adapt.util.config.ConfigDoc(value = "Baseline climb speed used before the speed multiplier is applied.", impact = "Higher values raise the base climb profile and increase total ladder ascent speed.") + double baseUpwardLadderSpeed = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vanilla-like upward speed used near ladder endpoints to avoid overshooting.", impact = "Higher values make endpoint climbing snappier; lower values keep transitions conservative.") + double normalUpwardLadderSpeed = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum positive Y movement treated as intentional upward ladder motion.", impact = "Lower values are more sensitive to slight upward input; higher values require clearer upward movement.") + double movementDirectionEpsilonUpward = 0.0004; + @art.arcane.adapt.util.config.ConfigDoc(value = "Smoothing factor for blending current upward velocity toward target ladder speed.", impact = "Values near 1.0 ramp quickly; lower values create a slower curve-like acceleration profile.") + double upwardAccelerationSmoothing = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "How long to retain previous upward speed state between ladder movement samples.", impact = "Lower values reset ramp-up sooner; higher values preserve momentum between short interruptions.") + long upwardStateResetMs = 200; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward look angle required to activate upward ladder acceleration.", impact = "Larger values require players to look farther upward before acceleration engages.") + double lookUpPitchThreshold = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Distance from ladder top where motion reverts toward normal upward speed.", impact = "Higher values begin fallback earlier near ladder ends; lower values keep boosted speed longer.") + int revertDistanceBlocks = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum blocks scanned to detect ladder continuity when checking ladder endpoints.", impact = "Higher values support taller ladders at slightly higher per-check cost.") + int maxLadderScanDistance = 64; + } + + private static class UpwardState { + private double currentSpeed; + private long lastSeenAt; + + private UpwardState(double currentSpeed, long lastSeenAt) { + this.currentSpeed = currentSpeed; + this.lastSeenAt = lastSeenAt; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java index 2bf28a990..fcf3de8f0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityParkourMomentum.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -49,330 +49,330 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityParkourMomentum extends SimpleAdaptation { - private final Map momentum = new ConcurrentHashMap<>(); - private final Map wasOnGround = new ConcurrentHashMap<>(); - private final Map speedBoosting = new ConcurrentHashMap<>(); - - public AgilityParkourMomentum() { - super("agility-parkour-momentum"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.parkour_momentum.description")); - setDisplayName(Localizer.dLocalize("agility.parkour_momentum.name")); - setIcon(Material.RABBIT_FOOT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(10); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RABBIT_FOOT) - .key("challenge_agility_parkour_500") - .title(Localizer.dLocalize("advancement.challenge_agility_parkour_500.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_parkour_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_agility_parkour_500", "agility.parkour-momentum.ledge-landings", 500, 400); + private final Map momentum = new ConcurrentHashMap<>(); + private final Map wasOnGround = new ConcurrentHashMap<>(); + private final Map speedBoosting = new ConcurrentHashMap<>(); + + public AgilityParkourMomentum() { + super("agility-parkour-momentum"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.parkour_momentum.description")); + setDisplayName(Localizer.dLocalize("agility.parkour_momentum.name")); + setIcon(Material.RABBIT_FOOT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(10); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RABBIT_FOOT) + .key("challenge_agility_parkour_500") + .title(Localizer.dLocalize("advancement.challenge_agility_parkour_500.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_parkour_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_agility_parkour_500", "agility.parkour-momentum.ledge-landings", 500, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxMomentum(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore1")); + v.addLore(C.GREEN + "+ " + getMaxSpeedAmplifier(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore2")); + v.addLore(C.GREEN + "+ " + getMaxJumpAmplifier(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + momentum.remove(id); + wasOnGround.remove(id); + speedBoosting.remove(id); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + if (e.getTo() == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxMomentum(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore1")); - v.addLore(C.GREEN + "+ " + getMaxSpeedAmplifier(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore2")); - v.addLore(C.GREEN + "+ " + getMaxJumpAmplifier(level) + C.GRAY + " " + Localizer.dLocalize("agility.parkour_momentum.lore3")); + if (e.getFrom().getWorld() == e.getTo().getWorld() + && e.getFrom().distanceSquared(e.getTo()) < getConfig().minimumMoveSquared) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + momentum.remove(id); + wasOnGround.remove(id); + return; + } + + boolean onGroundNow = p.isOnGround(); + boolean onGroundBefore = wasOnGround.getOrDefault(id, onGroundNow); + int current = momentum.getOrDefault(id, 0); + + if (!onGroundBefore && onGroundNow) { + if (isMomentumLanding(p) && isOnLedge(p)) { + current += getConfig().landingGain; + getPlayer(p).getData().addStat("agility.parkour-momentum.ledge-landings", 1); + } else { + current -= getConfig().failedLandingPenalty; + } + } else if (onGroundNow && !p.isSprinting()) { + current -= getConfig().groundDecayOnMove; + } + + current = clampMomentum(current, getMaxMomentum(level)); + momentum.put(id, current); + wasOnGround.put(id, onGroundNow); + }); + } + + @Override + public void onTick() { + Set tracked = trackedPlayerIds(); + for (UUID id : tracked) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { momentum.remove(id); wasOnGround.remove(id); speedBoosting.remove(id); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - if (e.getTo() == null) { - return; + continue; + } + + withPlayerThread(p, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + momentum.remove(id); + wasOnGround.remove(id); + invalidateMomentumSpeed(p, id, true); + return; } - if (e.getFrom().getWorld() == e.getTo().getWorld() - && e.getFrom().distanceSquared(e.getTo()) < getConfig().minimumMoveSquared) { - return; + int maxMomentum = getMaxMomentum(level); + int current = momentum.getOrDefault(id, 0); + if (current <= 0) { + invalidateMomentumSpeed(p, id, false); + return; } - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - int level = getActiveLevel(p); - if (level <= 0) { - momentum.remove(id); - wasOnGround.remove(id); - return; - } - - boolean onGroundNow = p.isOnGround(); - boolean onGroundBefore = wasOnGround.getOrDefault(id, onGroundNow); - int current = momentum.getOrDefault(id, 0); - - if (!onGroundBefore && onGroundNow) { - if (isMomentumLanding(p) && isOnLedge(p)) { - current += getConfig().landingGain; - getPlayer(p).getData().addStat("agility.parkour-momentum.ledge-landings", 1); - } else { - current -= getConfig().failedLandingPenalty; - } - } else if (onGroundNow && !p.isSprinting()) { - current -= getConfig().groundDecayOnMove; - } - - current = clampMomentum(current, getMaxMomentum(level)); - momentum.put(id, current); - wasOnGround.put(id, onGroundNow); - }); - } - - @Override - public void onTick() { - Set tracked = trackedPlayerIds(); - for (UUID id : tracked) { - Player p = Bukkit.getPlayer(id); - if (p == null || !p.isOnline()) { - momentum.remove(id); - wasOnGround.remove(id); - speedBoosting.remove(id); - continue; - } - - withPlayerThread(p, () -> { - int level = getActiveLevel(p); - if (level <= 0) { - momentum.remove(id); - wasOnGround.remove(id); - invalidateMomentumSpeed(p, id, true); - return; - } - - int maxMomentum = getMaxMomentum(level); - int current = momentum.getOrDefault(id, 0); - if (current <= 0) { - invalidateMomentumSpeed(p, id, false); - return; - } - - if (p.isOnGround() && !isOnLedge(p)) { - current -= getConfig().offLedgeDecayPerTick; - momentum.put(id, clampMomentum(current, maxMomentum)); - brakeMomentumSpeed(p, id); - return; - } - - int speedAmp = Math.max(0, Math.min(getMaxSpeedAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxSpeedAmplifier(level) + 1)) - 1)); - int jumpAmp = Math.max(0, Math.min(getMaxJumpAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxJumpAmplifier(level) + 1)) - 1)); - - p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, 25, jumpAmp, false, false)); - - if (speedAmp <= 0) { - brakeMomentumSpeed(p, id); - } else if (!isVelocityEligible(p)) { - invalidateMomentumSpeed(p, id, true); - } else { - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeMomentumSpeed(p, id); - } else { - applyMomentumSpeed(p, id, input, speedAmp); - } - } - - if (p.isOnGround() && !p.isSprinting()) { - current -= getConfig().passiveGroundDecayPerTick; - } - - momentum.put(id, clampMomentum(current, maxMomentum)); - }); - } - } - - private Set trackedPlayerIds() { - Set tracked = new HashSet<>(); - tracked.addAll(momentum.keySet()); - tracked.addAll(wasOnGround.keySet()); - tracked.addAll(speedBoosting.keySet()); - return tracked; - } - - private void applyMomentumSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot input, int speedAmp) { - Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - brakeMomentumSpeed(p, id); - return; + if (p.isOnGround() && !isOnLedge(p)) { + current -= getConfig().offLedgeDecayPerTick; + momentum.put(id, clampMomentum(current, maxMomentum)); + brakeMomentumSpeed(p, id); + return; } - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, - Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(speedAmp))); - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - Vector targetHorizontal = direction.multiply(targetSpeed); - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - speedBoosting.put(id, true); - } + int speedAmp = Math.max(0, Math.min(getMaxSpeedAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxSpeedAmplifier(level) + 1)) - 1)); + int jumpAmp = Math.max(0, Math.min(getMaxJumpAmplifier(level), (int) Math.floor((current / (double) maxMomentum) * (getMaxJumpAmplifier(level) + 1)) - 1)); - private void brakeMomentumSpeed(Player p, UUID id) { - if (!speedBoosting.getOrDefault(id, false)) { - return; - } + p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, 25, jumpAmp, false, false)); - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - double stopThreshold = Math.max(0, getConfig().stopThreshold); - if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - speedBoosting.put(id, false); - return; + if (speedAmp <= 0) { + brakeMomentumSpeed(p, id); + } else if (!isVelocityEligible(p)) { + invalidateMomentumSpeed(p, id, true); + } else { + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeMomentumSpeed(p, id); + } else { + applyMomentumSpeed(p, id, input, speedAmp); + } } - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); - if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - speedBoosting.put(id, false); - return; + if (p.isOnGround() && !p.isSprinting()) { + current -= getConfig().passiveGroundDecayPerTick; } - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + momentum.put(id, clampMomentum(current, maxMomentum)); + }); } - - private void invalidateMomentumSpeed(Player p, UUID id, boolean invalidState) { - if (!speedBoosting.getOrDefault(id, false)) { - return; - } - - if (invalidState && getConfig().hardStopOnInvalidState) { - VelocitySpeed.hardStopHorizontal(p); - } - - speedBoosting.put(id, false); + } + + private Set trackedPlayerIds() { + Set tracked = new HashSet<>(); + tracked.addAll(momentum.keySet()); + tracked.addAll(wasOnGround.keySet()); + tracked.addAll(speedBoosting.keySet()); + return tracked; + } + + private void applyMomentumSpeed(Player p, UUID id, VelocitySpeed.InputSnapshot input, int speedAmp) { + Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); + if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { + brakeMomentumSpeed(p, id); + return; } - private boolean isVelocityEligible(Player p) { - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } - - return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, + Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(speedAmp))); + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + Vector targetHorizontal = direction.multiply(targetSpeed); + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); + nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + speedBoosting.put(id, true); + } + + private void brakeMomentumSpeed(Player p, UUID id) { + if (!speedBoosting.getOrDefault(id, false)) { + return; } - private boolean isMomentumLanding(Player p) { - return p.isSprinting() && !p.isSwimming() && !p.isGliding() && !p.isFlying(); + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + double stopThreshold = Math.max(0, getConfig().stopThreshold); + if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + speedBoosting.put(id, false); + return; } - private boolean isOnLedge(Player p) { - if (!p.isOnGround()) { - return false; - } - - Block feet = p.getLocation().getBlock(); - Block below = feet.getRelative(BlockFace.DOWN); - if (!below.getType().isSolid()) { - return false; - } + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); + if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + speedBoosting.put(id, false); + return; + } - BlockFace[] sides = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; - for (BlockFace side : sides) { - Block sideAtFeet = feet.getRelative(side); - Block sideBelow = below.getRelative(side); - if (!sideAtFeet.getType().isSolid() && !sideBelow.getType().isSolid()) { - return true; - } - } + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + } - return false; + private void invalidateMomentumSpeed(Player p, UUID id, boolean invalidState) { + if (!speedBoosting.getOrDefault(id, false)) { + return; } - private int clampMomentum(int value, int max) { - return Math.max(0, Math.min(max, value)); + if (invalidState && getConfig().hardStopOnInvalidState) { + VelocitySpeed.hardStopHorizontal(p); } - private int getMaxMomentum(int level) { - return Math.max(3, (int) Math.round(getConfig().momentumBase + (getLevelPercent(level) * getConfig().momentumFactor))); - } + speedBoosting.put(id, false); + } - private int getMaxSpeedAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().speedAmplifierBase + (getLevelPercent(level) * getConfig().speedAmplifierFactor))); + private boolean isVelocityEligible(Player p) { + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; } - private int getMaxJumpAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().jumpAmplifierBase + (getLevelPercent(level) * getConfig().jumpAmplifierFactor))); + return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + } + + private boolean isMomentumLanding(Player p) { + return p.isSprinting() && !p.isSwimming() && !p.isGliding() && !p.isFlying(); + } + + private boolean isOnLedge(Player p) { + if (!p.isOnGround()) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + Block feet = p.getLocation().getBlock(); + Block below = feet.getRelative(BlockFace.DOWN); + if (!below.getType().isSolid()) { + return false; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + BlockFace[] sides = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; + for (BlockFace side : sides) { + Block sideAtFeet = feet.getRelative(side); + Block sideBelow = below.getRelative(side); + if (!sideAtFeet.getType().isSolid() && !sideBelow.getType().isSolid()) { + return true; + } } - @NoArgsConstructor - @ConfigDescription("Build momentum by chaining sprint-jumps and landings to gain speed and jump boosts.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double momentumBase = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double momentumFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedAmplifierFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpAmplifierFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Landing Gain for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int landingGain = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Failed Landing Penalty for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int failedLandingPenalty = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ground Decay On Move for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int groundDecayOnMove = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Ground Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int passiveGroundDecayPerTick = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Off Ledge Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int offLedgeDecayPerTick = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Move Squared for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumMoveSquared = 0.0025; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for momentum velocity scaling.", impact = "Higher values increase movement speed when momentum speed is active.") - double baseHorizontalSpeed = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent excessive momentum carry.") - double maxHorizontalSpeed = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the momentum target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double accelPerTick = 0.04; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values stop faster and reduce carry momentum.") - double brakePerTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") - double stopThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, speed velocity is force-cleared when entering invalid states.", impact = "Prevents retained boosts if state transitions skip expected checks.") - boolean hardStopOnInvalidState = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + return false; + } + + private int clampMomentum(int value, int max) { + return Math.max(0, Math.min(max, value)); + } + + private int getMaxMomentum(int level) { + return Math.max(3, (int) Math.round(getConfig().momentumBase + (getLevelPercent(level) * getConfig().momentumFactor))); + } + + private int getMaxSpeedAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().speedAmplifierBase + (getLevelPercent(level) * getConfig().speedAmplifierFactor))); + } + + private int getMaxJumpAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().jumpAmplifierBase + (getLevelPercent(level) * getConfig().jumpAmplifierFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Build momentum by chaining sprint-jumps and landings to gain speed and jump boosts.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double momentumBase = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Momentum Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double momentumFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedAmplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Base for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Amplifier Factor for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpAmplifierFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Landing Gain for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int landingGain = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Failed Landing Penalty for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int failedLandingPenalty = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ground Decay On Move for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int groundDecayOnMove = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Ground Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int passiveGroundDecayPerTick = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Off Ledge Decay Per Tick for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int offLedgeDecayPerTick = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Move Squared for the Agility Parkour Momentum adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumMoveSquared = 0.0025; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for momentum velocity scaling.", impact = "Higher values increase movement speed when momentum speed is active.") + double baseHorizontalSpeed = 0.13; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent excessive momentum carry.") + double maxHorizontalSpeed = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the momentum target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + double accelPerTick = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values stop faster and reduce carry momentum.") + double brakePerTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + double stopThreshold = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, speed velocity is force-cleared when entering invalid states.", impact = "Prevents retained boosts if state transitions skip expected checks.") + boolean hardStopOnInvalidState = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + double fallbackInputVelocityThreshold = 0.0008; + + double fallbackInputVelocityThresholdSquared() { + double threshold = Math.max(0, fallbackInputVelocityThreshold); + return threshold * threshold; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java index c15ef2634..47aa53f92 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityRollLanding.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -46,247 +46,247 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityRollLanding extends SimpleAdaptation { - private final Map rollInputs = new ConcurrentHashMap<>(); - private final Map proneUntilMillis = new ConcurrentHashMap<>(); - - public AgilityRollLanding() { - super("agility-roll-landing"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.roll_landing.description")); - setDisplayName(Localizer.dLocalize("agility.roll_landing.name")); - setIcon(Material.HAY_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.HAY_BLOCK) - .key("challenge_agility_roll_100") - .title(Localizer.dLocalize("advancement.challenge_agility_roll_100.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_roll_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SLIME_BLOCK) - .key("challenge_agility_roll_1000") - .title(Localizer.dLocalize("advancement.challenge_agility_roll_1000.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_roll_1000.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ELYTRA) - .key("challenge_agility_fearless") - .title(Localizer.dLocalize("advancement.challenge_agility_fearless.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_fearless.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.HIDDEN) - .build()); - registerMilestone("challenge_agility_roll_100", "agility.roll-landing.damage-prevented", 100, 300); - registerMilestone("challenge_agility_roll_1000", "agility.roll-landing.damage-prevented", 1000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getFallReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getInputWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore3")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - rollInputs.remove(e.getPlayer().getUniqueId()); - proneUntilMillis.remove(e.getPlayer().getUniqueId()); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (e.isSneaking()) { - recordRollInput(p, null, null); - } - }); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> recordRollInput(p, e.getFrom().getY(), e.getTo() == null ? null : e.getTo().getY())); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { - return; - } - - withAdaptedPlayer(p, e, () -> { - if (p.hasCooldown(Material.HAY_BLOCK)) { - return; - } - - long now = System.currentTimeMillis(); - long input = rollInputs.getOrDefault(p.getUniqueId(), 0L); - int level = getActiveLevel(p); - if (now - input > getInputWindowMillis(level)) { - return; - } - - double absorbCap = e.getDamage() * getFallReduction(level); - int hungerNeeded = (int) Math.ceil(absorbCap * getHungerPerDamage(level)); - if (hungerNeeded <= 0 || p.getFoodLevel() <= 0) { - return; - } - - int usableFood = Math.min(p.getFoodLevel(), hungerNeeded); - double absorbed = usableFood / getHungerPerDamage(level); - if (absorbed <= 0) { - return; - } - - p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); - e.setDamage(Math.max(0, e.getDamage() - absorbed)); - if (e.getDamage() <= 0.01) { - e.setCancelled(true); - } - - p.setCooldown(Material.HAY_BLOCK, getCooldownTicks(level)); - triggerRollPose(p, level); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_SMALL_FALL, 0.8f, 0.7f); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.0f, 0.89f); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_WOOL_BREAK, 0.55f, 0.9f); - getPlayer(p).getData().addStat("agility.roll-landing.damage-prevented", absorbed); - if (p.getFallDistance() >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_fearless")) { - getPlayer(p).getAdvancementHandler().grant("challenge_agility_fearless"); - } - xp(p, absorbed * getConfig().xpPerDamagePrevented); - }); - } - - private void recordRollInput(Player p, Double fromY, Double toY) { - if (!p.isSneaking()) { - return; - } - - if (p.isOnGround() || p.isFlying() || p.isGliding()) { - return; - } - - boolean descending = p.getVelocity().getY() <= getConfig().maxVerticalVelocityForRollInput; - if (!descending && fromY != null && toY != null) { - descending = toY < fromY; - } - - if (!descending) { - return; - } - - rollInputs.put(p.getUniqueId(), System.currentTimeMillis()); - } - - private void triggerRollPose(Player p, int level) { - int proneTicks = getProneTicks(level); - long until = System.currentTimeMillis() + (proneTicks * 50L); - UUID id = p.getUniqueId(); - proneUntilMillis.put(id, until); - p.setSwimming(true); - - J.runEntity(p, () -> { - if (!p.isOnline() || p.isDead()) { - proneUntilMillis.remove(id); - return; - } - - long expectedUntil = proneUntilMillis.getOrDefault(id, 0L); - if (expectedUntil > System.currentTimeMillis()) { - return; - } - - proneUntilMillis.remove(id); - if (!p.isInWater()) { - p.setSwimming(false); - } - }, proneTicks); - } - - private double getFallReduction(int level) { - return Math.min(getConfig().maxReduction, getConfig().reductionBase + (getLevelPercent(level) * getConfig().reductionFactor)); + private final Map rollInputs = new ConcurrentHashMap<>(); + private final Map proneUntilMillis = new ConcurrentHashMap<>(); + + public AgilityRollLanding() { + super("agility-roll-landing"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.roll_landing.description")); + setDisplayName(Localizer.dLocalize("agility.roll_landing.name")); + setIcon(Material.HAY_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.HAY_BLOCK) + .key("challenge_agility_roll_100") + .title(Localizer.dLocalize("advancement.challenge_agility_roll_100.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_roll_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SLIME_BLOCK) + .key("challenge_agility_roll_1000") + .title(Localizer.dLocalize("advancement.challenge_agility_roll_1000.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_roll_1000.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ELYTRA) + .key("challenge_agility_fearless") + .title(Localizer.dLocalize("advancement.challenge_agility_fearless.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_fearless.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.HIDDEN) + .build()); + registerMilestone("challenge_agility_roll_100", "agility.roll-landing.damage-prevented", 100, 300); + registerMilestone("challenge_agility_roll_1000", "agility.roll-landing.damage-prevented", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getFallReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getInputWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("agility.roll_landing.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + rollInputs.remove(e.getPlayer().getUniqueId()); + proneUntilMillis.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (e.isSneaking()) { + recordRollInput(p, null, null); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> recordRollInput(p, e.getFrom().getY(), e.getTo() == null ? null : e.getTo().getY())); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { + return; } - private long getInputWindowMillis(int level) { - return Math.max(60L, Math.round(getConfig().inputWindowMillisBase + (getLevelPercent(level) * getConfig().inputWindowMillisFactor))); + withAdaptedPlayer(p, e, () -> { + if (p.hasCooldown(Material.HAY_BLOCK)) { + return; + } + + long now = System.currentTimeMillis(); + long input = rollInputs.getOrDefault(p.getUniqueId(), 0L); + int level = getActiveLevel(p); + if (now - input > getInputWindowMillis(level)) { + return; + } + + double absorbCap = e.getDamage() * getFallReduction(level); + int hungerNeeded = (int) Math.ceil(absorbCap * getHungerPerDamage(level)); + if (hungerNeeded <= 0 || p.getFoodLevel() <= 0) { + return; + } + + int usableFood = Math.min(p.getFoodLevel(), hungerNeeded); + double absorbed = usableFood / getHungerPerDamage(level); + if (absorbed <= 0) { + return; + } + + p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); + e.setDamage(Math.max(0, e.getDamage() - absorbed)); + if (e.getDamage() <= 0.01) { + e.setCancelled(true); + } + + p.setCooldown(Material.HAY_BLOCK, getCooldownTicks(level)); + triggerRollPose(p, level); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_SMALL_FALL, 0.8f, 0.7f); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.0f, 0.89f); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_WOOL_BREAK, 0.55f, 0.9f); + getPlayer(p).getData().addStat("agility.roll-landing.damage-prevented", absorbed); + if (p.getFallDistance() >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_fearless")) { + getPlayer(p).getAdvancementHandler().grant("challenge_agility_fearless"); + } + xp(p, absorbed * getConfig().xpPerDamagePrevented); + }); + } + + private void recordRollInput(Player p, Double fromY, Double toY) { + if (!p.isSneaking()) { + return; } - private double getHungerPerDamage(int level) { - return Math.max(0.1, getConfig().hungerPerDamageBase - (getLevelPercent(level) * getConfig().hungerPerDamageReduction)); + if (p.isOnGround() || p.isFlying() || p.isGliding()) { + return; } - private int getCooldownTicks(int level) { - return Math.max(4, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + boolean descending = p.getVelocity().getY() <= getConfig().maxVerticalVelocityForRollInput; + if (!descending && fromY != null && toY != null) { + descending = toY < fromY; } - private int getProneTicks(int level) { - return Math.max(2, (int) Math.round(getConfig().proneTicksBase + (getLevelPercent(level) * getConfig().proneTicksFactor))); + if (!descending) { + return; } - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Timed sneak before landing converts part of fall damage into hunger cost.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.62; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reductionBase = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reductionFactor = 0.43; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxReduction = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double inputWindowMillisBase = 190; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double inputWindowMillisFactor = 260; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerPerDamageBase = 1.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerPerDamageReduction = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Velocity For Roll Input for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxVerticalVelocityForRollInput = -0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double proneTicksBase = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double proneTicksFactor = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage Prevented for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerDamagePrevented = 4.2; - } + rollInputs.put(p.getUniqueId(), System.currentTimeMillis()); + } + + private void triggerRollPose(Player p, int level) { + int proneTicks = getProneTicks(level); + long until = System.currentTimeMillis() + (proneTicks * 50L); + UUID id = p.getUniqueId(); + proneUntilMillis.put(id, until); + p.setSwimming(true); + + J.runEntity(p, () -> { + if (!p.isOnline() || p.isDead()) { + proneUntilMillis.remove(id); + return; + } + + long expectedUntil = proneUntilMillis.getOrDefault(id, 0L); + if (expectedUntil > System.currentTimeMillis()) { + return; + } + + proneUntilMillis.remove(id); + if (!p.isInWater()) { + p.setSwimming(false); + } + }, proneTicks); + } + + private double getFallReduction(int level) { + return Math.min(getConfig().maxReduction, getConfig().reductionBase + (getLevelPercent(level) * getConfig().reductionFactor)); + } + + private long getInputWindowMillis(int level) { + return Math.max(60L, Math.round(getConfig().inputWindowMillisBase + (getLevelPercent(level) * getConfig().inputWindowMillisFactor))); + } + + private double getHungerPerDamage(int level) { + return Math.max(0.1, getConfig().hungerPerDamageBase - (getLevelPercent(level) * getConfig().hungerPerDamageReduction)); + } + + private int getCooldownTicks(int level) { + return Math.max(4, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + private int getProneTicks(int level) { + return Math.max(2, (int) Math.round(getConfig().proneTicksBase + (getLevelPercent(level) * getConfig().proneTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Timed sneak before landing converts part of fall damage into hunger cost.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.62; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionBase = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionFactor = 0.43; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReduction = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double inputWindowMillisBase = 190; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Millis Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double inputWindowMillisFactor = 260; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerPerDamageBase = 1.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Per Damage Reduction for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerPerDamageReduction = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Velocity For Roll Input for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxVerticalVelocityForRollInput = -0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Base for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double proneTicksBase = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Prone Ticks Factor for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double proneTicksFactor = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage Prevented for the Agility Roll Landing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDamagePrevented = 4.2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java index b9101de1f..9ab608a5c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilitySuperJump.java @@ -24,12 +24,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -48,145 +48,145 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilitySuperJump extends SimpleAdaptation { - private final Map lastJump; - - public AgilitySuperJump() { - super("agility-super-jump"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.super_jump.description")); - setDisplayName(Localizer.dLocalize("agility.super_jump.name")); - setIcon(Material.LEATHER_BOOTS); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(9999); - lastJump = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_BOOTS) - .key("challenge_agility_super_jump_100") - .title(Localizer.dLocalize("advancement.challenge_agility_super_jump_100.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_super_jump_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_BOOTS) - .key("challenge_agility_super_jump_5k") - .title(Localizer.dLocalize("advancement.challenge_agility_super_jump_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_super_jump_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_super_jump_100", "agility.super-jump.jumps", 100, 300); - registerMilestone("challenge_agility_super_jump_5k", "agility.super-jump.jumps", 5000, 1500); + private final Map lastJump; + + public AgilitySuperJump() { + super("agility-super-jump"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.super_jump.description")); + setDisplayName(Localizer.dLocalize("agility.super_jump.name")); + setIcon(Material.LEATHER_BOOTS); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(9999); + lastJump = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_BOOTS) + .key("challenge_agility_super_jump_100") + .title(Localizer.dLocalize("advancement.challenge_agility_super_jump_100.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_super_jump_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_BOOTS) + .key("challenge_agility_super_jump_5k") + .title(Localizer.dLocalize("advancement.challenge_agility_super_jump_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_super_jump_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_super_jump_100", "agility.super-jump.jumps", 100, 300); + registerMilestone("challenge_agility_super_jump_5k", "agility.super-jump.jumps", 5000, 1500); + } + + private double getJumpHeight(int level) { + return getConfig().baseJumpMultiplier + (getConfig().jumpLevelMultiplier * level); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getJumpHeight(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.super_jump.lore1")); + v.addLore(C.LIGHT_PURPLE + " " + Localizer.dLocalize("agility.super_jump.lore2")); + + } + + @EventHandler + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (e.isSneaking() && p.isOnGround()) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.3f, 0.35f); + } + }); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + lastJump.remove(p.getUniqueId()); + } + + @EventHandler + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + if (p.isSwimming() || p.isFlying() || p.isGliding() || p.isSprinting()) { + return; } - private double getJumpHeight(int level) { - return getConfig().baseJumpMultiplier + (getConfig().jumpLevelMultiplier * level); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getJumpHeight(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.super_jump.lore1")); - v.addLore(C.LIGHT_PURPLE + " " + Localizer.dLocalize("agility.super_jump.lore2")); - - } + withAdaptedPlayer(p, e, () -> { + if (!p.isSneaking() || !canUse(getPlayer(p))) { + return; + } - @EventHandler - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (e.isSneaking() && p.isOnGround()) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.3f, 0.35f); - } - }); - } + Vector velocity = p.getVelocity(); - @EventHandler - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - lastJump.remove(p.getUniqueId()); - } + if (velocity.getY() > 0) { + double jumpVelocity = 0.4; + PotionEffect jumpPotion = p.getPotionEffect(PotionEffectTypes.JUMP); - @EventHandler - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - if (p.isSwimming() || p.isFlying() || p.isGliding() || p.isSprinting()) { - return; + if (jumpPotion != null) { + jumpVelocity += (double) ((float) jumpPotion.getAmplifier() + 1) * 0.1F; } - withAdaptedPlayer(p, e, () -> { - if (!p.isSneaking() || !canUse(getPlayer(p))) { - return; - } - - Vector velocity = p.getVelocity(); - - if (velocity.getY() > 0) { - double jumpVelocity = 0.4; - PotionEffect jumpPotion = p.getPotionEffect(PotionEffectTypes.JUMP); - - if (jumpPotion != null) { - jumpVelocity += (double) ((float) jumpPotion.getAmplifier() + 1) * 0.1F; - } - - if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) < 1000) { - return; - } else if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) > 1500) { - lastJump.remove(p.getUniqueId()); - } - if (p.getLocation().getBlock().getType() != Material.LADDER && velocity.getY() > jumpVelocity && p.isOnline()) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.25f, 0.7f); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.25f, 1.7f); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData()); - } - p.setVelocity(p.getVelocity().setY(getJumpHeight(getLevel(p)))); - lastJump.put(p.getUniqueId(), M.ms()); - getPlayer(p).getData().addStat("agility.super-jump.jumps", 1); - } - } - }); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sneak and jump for exceptional height advantage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Super Jump adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Jump Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseJumpMultiplier = 0.23; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Level Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpLevelMultiplier = 0.23; - } + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) < 1000) { + return; + } else if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) > 1500) { + lastJump.remove(p.getUniqueId()); + } + if (p.getLocation().getBlock().getType() != Material.LADDER && velocity.getY() > jumpVelocity && p.isOnline()) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.25f, 0.7f); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1.25f, 1.7f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData()); + } + p.setVelocity(p.getVelocity().setY(getJumpHeight(getLevel(p)))); + lastJump.put(p.getUniqueId(), M.ms()); + getPlayer(p).getData().addStat("agility.super-jump.jumps", 1); + } + } + }); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak and jump for exceptional height advantage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Super Jump adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Jump Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseJumpMultiplier = 0.23; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Level Multiplier for the Agility Super Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpLevelMultiplier = 0.23; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java index e58051550..8d6ee99b8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -48,304 +48,304 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityWallJump extends SimpleAdaptation { - private final Map airjumps; - private final Map horizontalIntent; - private final Map horizontalIntentTime; - private final Map sneakState; - - public AgilityWallJump() { - super("agility-wall-jump"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.wall_jump.description")); - setDisplayName(Localizer.dLocalize("agility.wall_jump.name")); - setIcon(Material.VINE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(50); - airjumps = new ConcurrentHashMap<>(); - horizontalIntent = new ConcurrentHashMap<>(); - horizontalIntentTime = new ConcurrentHashMap<>(); - sneakState = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LADDER) - .key("challenge_agility_wall_jump_500") - .title(Localizer.dLocalize("advancement.challenge_agility_wall_jump_500.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_wall_jump_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FEATHER) - .key("challenge_agility_parkour_master") - .title(Localizer.dLocalize("advancement.challenge_agility_parkour_master.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_parkour_master.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.HIDDEN) - .build()); - registerMilestone("challenge_agility_wall_jump_500", "agility.wall-jump.air-jumps", 500, 500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxJumps(level) + C.GRAY + " " + Localizer.dLocalize("agility.wall_jump.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getJumpHeight(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.wall_jump.lore2")); + private final Map airjumps; + private final Map horizontalIntent; + private final Map horizontalIntentTime; + private final Map sneakState; + + public AgilityWallJump() { + super("agility-wall-jump"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.wall_jump.description")); + setDisplayName(Localizer.dLocalize("agility.wall_jump.name")); + setIcon(Material.VINE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(50); + airjumps = new ConcurrentHashMap<>(); + horizontalIntent = new ConcurrentHashMap<>(); + horizontalIntentTime = new ConcurrentHashMap<>(); + sneakState = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LADDER) + .key("challenge_agility_wall_jump_500") + .title(Localizer.dLocalize("advancement.challenge_agility_wall_jump_500.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_wall_jump_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FEATHER) + .key("challenge_agility_parkour_master") + .title(Localizer.dLocalize("advancement.challenge_agility_parkour_master.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_parkour_master.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.HIDDEN) + .build()); + registerMilestone("challenge_agility_wall_jump_500", "agility.wall-jump.air-jumps", 500, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxJumps(level) + C.GRAY + " " + Localizer.dLocalize("agility.wall_jump.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getJumpHeight(level), 0) + C.GRAY + " " + Localizer.dLocalize("agility.wall_jump.lore2")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + airjumps.remove(id); + horizontalIntent.remove(id); + horizontalIntentTime.remove(id); + sneakState.remove(id); + } + + @EventHandler + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> sneakState.put(p.getUniqueId(), e.isSneaking())); + } + + private int getMaxJumps(int level) { + return (int) (level + (level / getConfig().maxJumpsLevelBonusDivisor)); + } + + private double getJumpHeight(int level) { + return getConfig().jumpHeightBase + (getLevelPercent(level) * getConfig().jumpHeightBonusLevelMultiplier); + } + + @EventHandler + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + sneakState.put(id, p.isSneaking()); + if (resolveInteractContext(p, p.getLocation()) == null) { + return; } - - @EventHandler - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); + if (airjumps.containsKey(id)) { + if (p.isOnGround() && !p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData().getMaterial().isAir()) { airjumps.remove(id); - horizontalIntent.remove(id); - horizontalIntentTime.remove(id); - sneakState.remove(id); + } } - @EventHandler - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> sneakState.put(p.getUniqueId(), e.isSneaking())); + if (e.getTo() == null || e.getFrom().getWorld() == null || e.getTo().getWorld() == null || !e.getFrom().getWorld().equals(e.getTo().getWorld())) { + return; } - private int getMaxJumps(int level) { - return (int) (level + (level / getConfig().maxJumpsLevelBonusDivisor)); + Vector delta = e.getTo().toVector().subtract(e.getFrom().toVector()); + delta.setY(0); + double movementThresholdSq = getConfig().inputMovementThreshold * getConfig().inputMovementThreshold; + if (delta.lengthSquared() >= movementThresholdSq) { + horizontalIntent.put(id, delta.normalize()); + horizontalIntentTime.put(id, M.ms()); } - - private double getJumpHeight(int level) { - return getConfig().jumpHeightBase + (getLevelPercent(level) * getConfig().jumpHeightBonusLevelMultiplier); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + withPlayerThread(p, () -> updatePlayer(p)); } + } - @EventHandler - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - sneakState.put(id, p.isSneaking()); - if (resolveInteractContext(p, p.getLocation()) == null) { - return; - } - if (airjumps.containsKey(id)) { - if (p.isOnGround() && !p.getLocation().getBlock().getRelative(BlockFace.DOWN).getBlockData().getMaterial().isAir()) { - airjumps.remove(id); - } - } - - if (e.getTo() == null || e.getFrom().getWorld() == null || e.getTo().getWorld() == null || !e.getFrom().getWorld().equals(e.getTo().getWorld())) { - return; - } - - Vector delta = e.getTo().toVector().subtract(e.getFrom().toVector()); - delta.setY(0); - double movementThresholdSq = getConfig().inputMovementThreshold * getConfig().inputMovementThreshold; - if (delta.lengthSquared() >= movementThresholdSq) { - horizontalIntent.put(id, delta.normalize()); - horizontalIntentTime.put(id, M.ms()); - } + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - withPlayerThread(p, () -> updatePlayer(p)); - } + UUID id = p.getUniqueId(); + int level = getActiveInteractLevel(p, p.getLocation()); + if (level <= 0) { + return; } - private void updatePlayer(Player p) { - if (p == null || !p.isOnline()) { - return; - } - - UUID id = p.getUniqueId(); - int level = getActiveInteractLevel(p, p.getLocation()); - if (level <= 0) { - return; - } - - Double j = airjumps.get(id); - - if (j != null && j - 0.25 >= getMaxJumps(level)) { - p.setGravity(true); - return; - } - - if (p.isOnGround()) { - airjumps.remove(id); - if (!p.hasGravity()) { - p.setGravity(true); - } - return; - } + Double j = airjumps.get(id); - Block stickBlock = stickToWall(p); - if (p.isFlying() || !isSneaking(id, p)) { - boolean jumped = false; - - if (!p.hasGravity() && p.getFallDistance() > 0.45 && stickBlock != null) { - j = j == null ? 0 : j; - j++; - - if (j - 0.25 <= getMaxJumps(level)) { - jumped = true; - Vector launch = p.getVelocity().clone().setY(getJumpHeight(level)); - if (isBackwardLaunch(p)) { - Vector direction = p.getLocation().getDirection().clone().setY(0); - if (direction.lengthSquared() > 0.000001) { - direction.normalize().multiply(-getConfig().backwardPushSpeed); - launch.setX(direction.getX()); - launch.setZ(direction.getZ()); - } - } - p.setVelocity(launch); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, stickBlock.getBlockData()); - } - getPlayer(p).getData().addStat("agility.wall-jump.air-jumps", 1); - if (j >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_parkour_master")) { - getPlayer(p).getAdvancementHandler().grant("challenge_agility_parkour_master"); - } - } - airjumps.put(id, j); - } + if (j != null && j - 0.25 >= getMaxJumps(level)) { + p.setGravity(true); + return; + } - if (!jumped && !p.hasGravity()) { - p.setGravity(true); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.439f); - } - return; - } + if (p.isOnGround()) { + airjumps.remove(id); + if (!p.hasGravity()) { + p.setGravity(true); + } + return; + } - if (stickBlock != null) { - if (p.hasGravity()) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.89f); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_CHAIN, 1f, 1.39f); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.2, 0.1, 0.1, stickBlock.getBlockData()); - } + Block stickBlock = stickToWall(p); + if (p.isFlying() || !isSneaking(id, p)) { + boolean jumped = false; + + if (!p.hasGravity() && p.getFallDistance() > 0.45 && stickBlock != null) { + j = j == null ? 0 : j; + j++; + + if (j - 0.25 <= getMaxJumps(level)) { + jumped = true; + Vector launch = p.getVelocity().clone().setY(getJumpHeight(level)); + if (isBackwardLaunch(p)) { + Vector direction = p.getLocation().getDirection().clone().setY(0); + if (direction.lengthSquared() > 0.000001) { + direction.normalize().multiply(-getConfig().backwardPushSpeed); + launch.setX(direction.getX()); + launch.setZ(direction.getZ()); } - - applyWallStickForce(p, stickBlock); - p.setGravity(false); - Vector c = p.getVelocity(); - p.setVelocity(p.getVelocity().setY((c.getY() * 0.35) - 0.0025)); - Double vv = airjumps.get(id); - vv = vv == null ? 0 : vv; - vv += 0.0127; - airjumps.put(id, vv); - } - - if (stickBlock == null && !p.hasGravity()) { - p.setGravity(true); + } + p.setVelocity(launch); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.8, 0.1, 0.1, stickBlock.getBlockData()); + } + getPlayer(p).getData().addStat("agility.wall-jump.air-jumps", 1); + if (j >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_agility_parkour_master")) { + getPlayer(p).getAdvancementHandler().grant("challenge_agility_parkour_master"); + } } + airjumps.put(id, j); + } + + if (!jumped && !p.hasGravity()) { + p.setGravity(true); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.439f); + } + return; } - private boolean isBackwardLaunch(Player p) { - UUID id = p.getUniqueId(); - Long at = horizontalIntentTime.get(id); - Vector intent = horizontalIntent.get(id); - if (at == null || intent == null || M.ms() - at > getConfig().inputWindowMs) { - return false; + if (stickBlock != null) { + if (p.hasGravity()) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.89f); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_CHAIN, 1f, 1.39f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particles.BLOCK_CRACK, p.getLocation().clone().add(0, 0.3, 0), 15, 0.1, 0.2, 0.1, 0.1, stickBlock.getBlockData()); } - - Vector facing = p.getLocation().getDirection().clone().setY(0); - if (facing.lengthSquared() <= 0.000001) { - return false; - } - - facing.normalize(); - return intent.dot(facing) <= -Math.abs(getConfig().backwardIntentDotThreshold); + } + + applyWallStickForce(p, stickBlock); + p.setGravity(false); + Vector c = p.getVelocity(); + p.setVelocity(p.getVelocity().setY((c.getY() * 0.35) - 0.0025)); + Double vv = airjumps.get(id); + vv = vv == null ? 0 : vv; + vv += 0.0127; + airjumps.put(id, vv); } - private boolean isSneaking(UUID id, Player p) { - Boolean cached = sneakState.get(id); - return cached != null ? cached : p.isSneaking(); + if (stickBlock == null && !p.hasGravity()) { + p.setGravity(true); } - - private Block stickToWall(Player p) { - for (Block wall : getBlocks(p)) { - if (wall.getBlockData().getMaterial().isSolid()) { - return wall; - } - } - - return null; + } + + private boolean isBackwardLaunch(Player p) { + UUID id = p.getUniqueId(); + Long at = horizontalIntentTime.get(id); + Vector intent = horizontalIntent.get(id); + if (at == null || intent == null || M.ms() - at > getConfig().inputWindowMs) { + return false; } - private void applyWallStickForce(Player p, Block wall) { - Vector velocity = p.getVelocity(); - Vector shift = p.getLocation().toVector().subtract(wall.getLocation().clone().add(0.5, 0.5, 0.5).toVector()); - velocity.setX(velocity.getX() - (shift.getX() / 16)); - velocity.setZ(velocity.getZ() - (shift.getZ() / 16)); - p.setVelocity(velocity); + Vector facing = p.getLocation().getDirection().clone().setY(0); + if (facing.lengthSquared() <= 0.000001) { + return false; } - private Block[] getBlocks(Player p) { - Block base = p.getLocation().getBlock(); - return new Block[]{ - base.getRelative(BlockFace.NORTH), - base.getRelative(BlockFace.SOUTH), - base.getRelative(BlockFace.EAST), - base.getRelative(BlockFace.WEST), - base.getRelative(BlockFace.NORTH_EAST), - base.getRelative(BlockFace.SOUTH_EAST), - base.getRelative(BlockFace.NORTH_WEST), - base.getRelative(BlockFace.SOUTH_WEST), - base.getRelative(BlockFace.NORTH_EAST).getRelative(BlockFace.UP), - base.getRelative(BlockFace.SOUTH_EAST).getRelative(BlockFace.UP), - base.getRelative(BlockFace.NORTH_WEST).getRelative(BlockFace.UP), - base.getRelative(BlockFace.SOUTH_WEST).getRelative(BlockFace.UP), - base.getRelative(BlockFace.NORTH).getRelative(BlockFace.UP), - base.getRelative(BlockFace.SOUTH).getRelative(BlockFace.UP), - base.getRelative(BlockFace.EAST).getRelative(BlockFace.UP), - base.getRelative(BlockFace.WEST).getRelative(BlockFace.UP), - }; - } + facing.normalize(); + return intent.dot(facing) <= -Math.abs(getConfig().backwardIntentDotThreshold); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private boolean isSneaking(UUID id, Player p) { + Boolean cached = sneakState.get(id); + return cached != null ? cached : p.isSneaking(); + } - @Override - public boolean isPermanent() { - return getConfig().permanent; + private Block stickToWall(Player p) { + for (Block wall : getBlocks(p)) { + if (wall.getBlockData().getMaterial().isSolid()) { + return wall; + } } - @NoArgsConstructor - @ConfigDescription("Hold shift while mid-air against a wall to latch and jump.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wall Jump adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Jumps Level Bonus Divisor for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxJumpsLevelBonusDivisor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Base for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpHeightBase = 0.625; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Bonus Level Multiplier for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpHeightBonusLevelMultiplier = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Push Speed for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double backwardPushSpeed = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Intent Dot Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double backwardIntentDotThreshold = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Movement Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double inputMovementThreshold = 0.0025; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Ms for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long inputWindowMs = 450; - } + return null; + } + + private void applyWallStickForce(Player p, Block wall) { + Vector velocity = p.getVelocity(); + Vector shift = p.getLocation().toVector().subtract(wall.getLocation().clone().add(0.5, 0.5, 0.5).toVector()); + velocity.setX(velocity.getX() - (shift.getX() / 16)); + velocity.setZ(velocity.getZ() - (shift.getZ() / 16)); + p.setVelocity(velocity); + } + + private Block[] getBlocks(Player p) { + Block base = p.getLocation().getBlock(); + return new Block[]{ + base.getRelative(BlockFace.NORTH), + base.getRelative(BlockFace.SOUTH), + base.getRelative(BlockFace.EAST), + base.getRelative(BlockFace.WEST), + base.getRelative(BlockFace.NORTH_EAST), + base.getRelative(BlockFace.SOUTH_EAST), + base.getRelative(BlockFace.NORTH_WEST), + base.getRelative(BlockFace.SOUTH_WEST), + base.getRelative(BlockFace.NORTH_EAST).getRelative(BlockFace.UP), + base.getRelative(BlockFace.SOUTH_EAST).getRelative(BlockFace.UP), + base.getRelative(BlockFace.NORTH_WEST).getRelative(BlockFace.UP), + base.getRelative(BlockFace.SOUTH_WEST).getRelative(BlockFace.UP), + base.getRelative(BlockFace.NORTH).getRelative(BlockFace.UP), + base.getRelative(BlockFace.SOUTH).getRelative(BlockFace.UP), + base.getRelative(BlockFace.EAST).getRelative(BlockFace.UP), + base.getRelative(BlockFace.WEST).getRelative(BlockFace.UP), + }; + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Hold shift while mid-air against a wall to latch and jump.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wall Jump adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Jumps Level Bonus Divisor for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxJumpsLevelBonusDivisor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Base for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpHeightBase = 0.625; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Height Bonus Level Multiplier for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpHeightBonusLevelMultiplier = 0.225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Push Speed for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double backwardPushSpeed = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Backward Intent Dot Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double backwardIntentDotThreshold = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Movement Threshold for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double inputMovementThreshold = 0.0025; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Input Window Ms for the Agility Wall Jump adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long inputWindowMs = 450; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index 91e6a8eb8..f75ed3c08 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -24,12 +24,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EntityDismountEvent; import art.arcane.adapt.util.reflect.events.api.entity.EntityMountEvent; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.GameMode; @@ -47,282 +47,282 @@ import java.util.concurrent.ConcurrentHashMap; public class AgilityWindUp extends SimpleAdaptation { - private final Map ticksRunning; - private final Map states; - - public AgilityWindUp() { - super("agility-wind-up"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("agility.wind_up.description")); - setDisplayName(Localizer.dLocalize("agility.wind_up.name")); - setIcon(Material.POWERED_RAIL); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setInitialCost(getConfig().initialCost); - setInterval(50); - ticksRunning = new ConcurrentHashMap<>(); - states = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.POWERED_RAIL) - .key("challenge_agility_wind_up_10min") - .title(Localizer.dLocalize("advancement.challenge_agility_wind_up_10min.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_wind_up_10min.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ACTIVATOR_RAIL) - .key("challenge_agility_wind_up_2hr") - .title(Localizer.dLocalize("advancement.challenge_agility_wind_up_2hr.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_wind_up_2hr.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_wind_up_10min", "agility.wind-up.max-speed-ticks", 12000, 400); - registerMilestone("challenge_agility_wind_up_2hr", "agility.wind-up.max-speed-ticks", 144000, 1500); + private final Map ticksRunning; + private final Map states; + + public AgilityWindUp() { + super("agility-wind-up"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("agility.wind_up.description")); + setDisplayName(Localizer.dLocalize("agility.wind_up.name")); + setIcon(Material.POWERED_RAIL); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setInitialCost(getConfig().initialCost); + setInterval(50); + ticksRunning = new ConcurrentHashMap<>(); + states = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.POWERED_RAIL) + .key("challenge_agility_wind_up_10min") + .title(Localizer.dLocalize("advancement.challenge_agility_wind_up_10min.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_wind_up_10min.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ACTIVATOR_RAIL) + .key("challenge_agility_wind_up_2hr") + .title(Localizer.dLocalize("advancement.challenge_agility_wind_up_2hr.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_wind_up_2hr.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_wind_up_10min", "agility.wind-up.max-speed-ticks", 12000, 400); + registerMilestone("challenge_agility_wind_up_2hr", "agility.wind-up.max-speed-ticks", 144000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getWindupSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("agility.wind_up.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getWindupTicks(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("agility.wind_up.lore2")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + clearAndRemoveState(e.getPlayer()); + } + + @EventHandler + public void on(PlayerDeathEvent e) { + clearAndRemoveState(e.getEntity()); + } + + @ReflectiveHandler + public void on(EntityMountEvent event) { + if (event.getEntity().getType() != EntityType.PLAYER) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getWindupSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("agility.wind_up.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getWindupTicks(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("agility.wind_up.lore2")); + Player p = (Player) event.getEntity(); + UUID id = p.getUniqueId(); + ticksRunning.remove(id); + RuntimeState state = states.get(id); + if (state != null) { + clearBoost(p, state); } + } - @EventHandler - public void on(PlayerQuitEvent e) { - clearAndRemoveState(e.getPlayer()); + @ReflectiveHandler + public void on(EntityDismountEvent event) { + if (event.getEntity().getType() != EntityType.PLAYER) { + return; } - @EventHandler - public void on(PlayerDeathEvent e) { - clearAndRemoveState(e.getEntity()); + Player p = (Player) event.getEntity(); + UUID id = p.getUniqueId(); + ticksRunning.remove(id); + RuntimeState state = states.get(id); + if (state != null) { + clearBoost(p, state); } - - @ReflectiveHandler - public void on(EntityMountEvent event) { - if (event.getEntity().getType() != EntityType.PLAYER) { - return; - } - - Player p = (Player) event.getEntity(); - UUID id = p.getUniqueId(); - ticksRunning.remove(id); - RuntimeState state = states.get(id); - if (state != null) { - clearBoost(p, state); - } + } + + private double getWindupTicks(double factor) { + return M.lerp(getConfig().windupTicksSlowest, getConfig().windupTicksFastest, factor); + } + + private double getWindupSpeed(double factor) { + return getConfig().windupSpeedBase + (factor * getConfig().windupSpeedLevelMultiplier); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + withPlayerThread(p, () -> updatePlayer(p)); } + } - @ReflectiveHandler - public void on(EntityDismountEvent event) { - if (event.getEntity().getType() != EntityType.PLAYER) { - return; - } - - Player p = (Player) event.getEntity(); - UUID id = p.getUniqueId(); - ticksRunning.remove(id); - RuntimeState state = states.get(id); - if (state != null) { - clearBoost(p, state); - } + private void updatePlayer(Player p) { + if (p == null || !p.isOnline()) { + return; } - private double getWindupTicks(double factor) { - return M.lerp(getConfig().windupTicksSlowest, getConfig().windupTicksFastest, factor); + UUID id = p.getUniqueId(); + RuntimeState state = states.computeIfAbsent(id, key -> new RuntimeState()); + if (!hasActiveAdaptation(p) || !isWindupEligible(p) || !p.isSprinting()) { + ticksRunning.remove(id); + clearBoost(p, state); + return; } - private double getWindupSpeed(double factor) { - return getConfig().windupSpeedBase + (factor * getConfig().windupSpeedLevelMultiplier); + double factor = getLevelPercent(p); + if (factor <= 0) { + ticksRunning.remove(id); + clearBoost(p, state); + return; } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - withPlayerThread(p, () -> updatePlayer(p)); - } + ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); + int tr = ticksRunning.getOrDefault(id, 0); + if (tr <= 0) { + return; } - private void updatePlayer(Player p) { - if (p == null || !p.isOnline()) { - return; - } - - UUID id = p.getUniqueId(); - RuntimeState state = states.computeIfAbsent(id, key -> new RuntimeState()); - if (!hasActiveAdaptation(p) || !isWindupEligible(p) || !p.isSprinting()) { - ticksRunning.remove(id); - clearBoost(p, state); - return; - } - - double factor = getLevelPercent(p); - if (factor <= 0) { - ticksRunning.remove(id); - clearBoost(p, state); - return; - } - - ticksRunning.compute(id, (k, v) -> v == null ? 1 : v + 1); - int tr = ticksRunning.getOrDefault(id, 0); - if (tr <= 0) { - return; - } - - double ticksToMax = Math.max(1D, getWindupTicks(factor)); - double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); - double speedIncrease = M.lerp(0, getWindupSpeed(factor), progress); - applyBoost(p, state, speedIncrease); - - if (areParticlesEnabled()) { - if (M.r(0.2 * progress)) { - p.getWorld().spawnParticle(Particle.LAVA, p.getLocation(), 1); - } - - if (M.r(0.25 * progress)) { - p.getWorld().spawnParticle(Particle.FLAME, p.getLocation(), 1, 0, 0, 0, 0); - } - } - - if (progress >= 1.0 && isMovingHorizontally(p, getConfig().movementVelocityThreshold)) { - getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); - } - } + double ticksToMax = Math.max(1D, getWindupTicks(factor)); + double progress = Math.min(M.lerpInverse(0, ticksToMax, tr), 1); + double speedIncrease = M.lerp(0, getWindupSpeed(factor), progress); + applyBoost(p, state, speedIncrease); - private void applyBoost(Player p, RuntimeState state, double speedIncrease) { - if (!state.boosting) { - state.boosting = true; - state.originalWalkSpeed = p.getWalkSpeed(); - } - - float baseWalkSpeed = clampWalkSpeed(state.originalWalkSpeed); - double bonus = Math.max(0D, speedIncrease) * Math.max(0D, getConfig().walkSpeedBonusScalar); - float target = clampWalkSpeed((float) (baseWalkSpeed * (1D + bonus))); - float configuredMaxWalkSpeed = clampWalkSpeed((float) Math.max(0D, getConfig().maxWalkSpeed)); - float maxWalkSpeed = Math.max(baseWalkSpeed, configuredMaxWalkSpeed); - if (target > maxWalkSpeed) { - target = maxWalkSpeed; - } - - float smoothing = (float) Math.max(0D, Math.min(1D, getConfig().walkSpeedLerpPerTick)); - float current = p.getWalkSpeed(); - float next = current + ((target - current) * smoothing); - if (Math.abs(target - next) < 0.0005f) { - next = target; - } - - if (Math.abs(current - next) > 0.0001f) { - p.setWalkSpeed(next); - } + if (areParticlesEnabled()) { + if (M.r(0.2 * progress)) { + p.getWorld().spawnParticle(Particle.LAVA, p.getLocation(), 1); + } + + if (M.r(0.25 * progress)) { + p.getWorld().spawnParticle(Particle.FLAME, p.getLocation(), 1, 0, 0, 0, 0); + } } - private void clearBoost(Player p, RuntimeState state) { - if (!state.boosting) { - return; - } - - state.boosting = false; - float restore = clampWalkSpeed(state.originalWalkSpeed); - float current = p.getWalkSpeed(); - if (Math.abs(current - restore) > 0.0001f) { - p.setWalkSpeed(restore); - } + if (progress >= 1.0 && isMovingHorizontally(p, getConfig().movementVelocityThreshold)) { + getPlayer(p).getData().addStat("agility.wind-up.max-speed-ticks", 1); } + } - private void clearAndRemoveState(Player p) { - if (p == null) { - return; - } - - UUID id = p.getUniqueId(); - ticksRunning.remove(id); - RuntimeState state = states.remove(id); - if (state != null) { - clearBoost(p, state); - } + private void applyBoost(Player p, RuntimeState state, double speedIncrease) { + if (!state.boosting) { + state.boosting = true; + state.originalWalkSpeed = p.getWalkSpeed(); } - private boolean isMovingHorizontally(Player p, double velocityThreshold) { - Vector movement = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); - double threshold = Math.max(0D, velocityThreshold); - return movement.lengthSquared() > (threshold * threshold); + float baseWalkSpeed = clampWalkSpeed(state.originalWalkSpeed); + double bonus = Math.max(0D, speedIncrease) * Math.max(0D, getConfig().walkSpeedBonusScalar); + float target = clampWalkSpeed((float) (baseWalkSpeed * (1D + bonus))); + float configuredMaxWalkSpeed = clampWalkSpeed((float) Math.max(0D, getConfig().maxWalkSpeed)); + float maxWalkSpeed = Math.max(baseWalkSpeed, configuredMaxWalkSpeed); + if (target > maxWalkSpeed) { + target = maxWalkSpeed; } - private boolean isWindupEligible(Player p) { - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } - - return !p.isDead() - && !p.isSwimming() - && !p.isFlying() - && !p.isGliding() - && !p.isSneaking() - && p.getVehicle() == null; + float smoothing = (float) Math.max(0D, Math.min(1D, getConfig().walkSpeedLerpPerTick)); + float current = p.getWalkSpeed(); + float next = current + ((target - current) * smoothing); + if (Math.abs(target - next) < 0.0005f) { + next = target; } - private float clampWalkSpeed(float speed) { - if (speed < 0f) { - return 0f; - } - if (speed > 1f) { - return 1f; - } - return speed; + if (Math.abs(current - next) > 0.0001f) { + p.setWalkSpeed(next); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private void clearBoost(Player p, RuntimeState state) { + if (!state.boosting) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + state.boosting = false; + float restore = clampWalkSpeed(state.originalWalkSpeed); + float current = p.getWalkSpeed(); + if (Math.abs(current - restore) > 0.0001f) { + p.setWalkSpeed(restore); } + } + private void clearAndRemoveState(Player p) { + if (p == null) { + return; + } - @NoArgsConstructor - @ConfigDescription("Get faster the longer you sprint.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wind Up adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupTicksSlowest = 180; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupTicksFastest = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Base for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupSpeedBase = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Level Multiplier for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windupSpeedLevelMultiplier = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scales walk-speed gain from windup speed increase while sprinting.", impact = "Higher values produce stronger land-speed acceleration.") - double walkSpeedBonusScalar = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Smooths walk-speed changes toward windup target each tick.", impact = "Higher values ramp faster; lower values feel softer.") - double walkSpeedLerpPerTick = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed this adaptation can set while windup is active.", impact = "Higher values allow faster grounded sprinting before clamping.") - double maxWalkSpeed = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal movement speed required for max-speed stat credit.", impact = "Higher values require clearer movement before counting max-speed ticks.") - double movementVelocityThreshold = 0.015; + UUID id = p.getUniqueId(); + ticksRunning.remove(id); + RuntimeState state = states.remove(id); + if (state != null) { + clearBoost(p, state); + } + } + + private boolean isMovingHorizontally(Player p, double velocityThreshold) { + Vector movement = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); + double threshold = Math.max(0D, velocityThreshold); + return movement.lengthSquared() > (threshold * threshold); + } + + private boolean isWindupEligible(Player p) { + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; } - private static class RuntimeState { - private boolean boosting; - private float originalWalkSpeed; + return !p.isDead() + && !p.isSwimming() + && !p.isFlying() + && !p.isGliding() + && !p.isSneaking() + && p.getVehicle() == null; + } + + private float clampWalkSpeed(float speed) { + if (speed < 0f) { + return 0f; + } + if (speed > 1f) { + return 1f; } + return speed; + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + + @NoArgsConstructor + @ConfigDescription("Get faster the longer you sprint.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Agility Wind Up adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Slowest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupTicksSlowest = 180; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Ticks Fastest for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupTicksFastest = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Base for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupSpeedBase = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Windup Speed Level Multiplier for the Agility Wind Up adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windupSpeedLevelMultiplier = 0.225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scales walk-speed gain from windup speed increase while sprinting.", impact = "Higher values produce stronger land-speed acceleration.") + double walkSpeedBonusScalar = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Smooths walk-speed changes toward windup target each tick.", impact = "Higher values ramp faster; lower values feel softer.") + double walkSpeedLerpPerTick = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed this adaptation can set while windup is active.", impact = "Higher values allow faster grounded sprinting before clamping.") + double maxWalkSpeed = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal movement speed required for max-speed stat credit.", impact = "Higher values require clearer movement before counting max-speed ticks.") + double movementVelocityThreshold = 0.015; + } + + private static class RuntimeState { + private boolean boosting; + private float originalWalkSpeed; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java index c8957196d..5fc1b2c12 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectElevator.java @@ -27,11 +27,11 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import com.jeff_media.customblockdata.CustomBlockData; import com.jeff_media.customblockdata.events.CustomBlockDataMoveEvent; import com.jeff_media.customblockdata.events.CustomBlockDataRemoveEvent; @@ -62,371 +62,371 @@ import java.util.UUID; public class ArchitectElevator extends SimpleAdaptation { - private static final NamespacedKey ELEVATOR_KEY = new NamespacedKey(Adapt.instance, "elevator"); - private static final NamespacedKey TARGET_DOWN = new NamespacedKey(Adapt.instance, "target_down"); - private static final NamespacedKey TARGET_UP = new NamespacedKey(Adapt.instance, "target_up"); - - private static final int PARTICLE_COUNT = 20; - private static final float SOUND_VOLUME = 1f; - private static final float SOUND_PITCH = 1f; - - private final Set players = java.util.concurrent.ConcurrentHashMap.newKeySet(); - - public ArchitectElevator() { - super("architect-elevator"); - registerConfiguration(ArchitectElevator.Config.class); - setDescription(Localizer.dLocalize("architect.elevator.description")); - setDisplayName(Localizer.dLocalize("architect.elevator.name")); - setIcon(Material.HEAVY_WEIGHTED_PRESSURE_PLATE); - setInterval(988); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - - registerRecipe(AdaptRecipe.shaped() - .key("elevator") - .shape("XXX") - .shape("XYX") - .shape("XXX") - .ingredient(new MaterialChar('X', Tag.WOOL)) - .ingredient(new MaterialChar('Y', Material.ENDER_PEARL)) - .result(getElevatorItem()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHITE_WOOL) - .key("challenge_architect_elevator_100") - .title(Localizer.dLocalize("advancement.challenge_architect_elevator_100.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_elevator_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WHITE_WOOL) - .key("challenge_architect_elevator_penthouse") - .title(Localizer.dLocalize("advancement.challenge_architect_elevator_penthouse.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_elevator_penthouse.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_elevator_100", "architect.elevator.trips", 100, 300); - } - - @Override - public void addStats(int level, Element v) { - - } - - public ItemStack getElevatorItem() { - ItemStack elevatorItem = CustomModel.get(Material.NOTE_BLOCK, "architect", "elevator", "item") - .toItemStack(); - ItemMeta meta = elevatorItem.getItemMeta(); - if (meta != null) { - meta.getPersistentDataContainer().set(ELEVATOR_KEY, PersistentDataType.BYTE, (byte) 0); - meta.setDisplayName(Localizer.dLocalize("items.elevator_block.name")); - meta.setLore(List.of(Localizer.dLocalize("items.elevator_block.usage1"), - Localizer.dLocalize("items.elevator_block.usage2"), - Localizer.dLocalize("items.elevator_block.usage3"))); - elevatorItem.setItemMeta(meta); - } - return elevatorItem; - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - if (e.getTo() == null) return; - Player player = e.getPlayer(); - - if (!players.add(player.getUniqueId())) { - if (e.getFrom().getY() < e.getTo().getY() || player.isFlying()) - players.remove(player.getUniqueId()); - return; - } - - if (player.isFlying() || player.getVelocity().getY() <= 0 || e.getFrom().getY() >= e.getTo().getY()) - return; - - Block block = findElevator(player); - if (block == null) return; - handleElevatorMovement(block, player, false); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent event) { - if (!event.isSneaking() || event.getPlayer().isInsideVehicle()) return; - Player player = event.getPlayer(); - Block block = findElevator(player); - if (block == null) return; - handleElevatorMovement(block, player, true); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(BlockPlaceEvent event) { - ItemMeta meta = event.getItemInHand().getItemMeta(); - if (meta == null || !meta.getPersistentDataContainer().has(ELEVATOR_KEY, PersistentDataType.BYTE)) - return; - int maxDistance = getMaxDistance(event.getPlayer()); - if (maxDistance <= 0) { - event.setCancelled(true); - return; - } - - Block block = event.getBlock(); - World world = block.getWorld(); - CustomBlockData data = new CustomBlockData(block, Adapt.instance); - data.set(ELEVATOR_KEY, PersistentDataType.INTEGER, maxDistance); - - int lowerDist = Math.min(block.getY() - world.getMinHeight(), maxDistance); - for (int d = 1; d <= lowerDist; d++) { - var lower = block.getRelative(BlockFace.DOWN, d); - if (checkElevator(lower, TARGET_UP, d)) { - data.set(TARGET_DOWN, PersistentDataType.INTEGER, d); - break; - } - } - - int upperDist = Math.min(world.getMaxHeight() - block.getY(), maxDistance); - for (int d = 1; d <= upperDist; d++) { - var upper = block.getRelative(BlockFace.UP, d); - if (checkElevator(upper, TARGET_DOWN, d)) { - data.set(TARGET_UP, PersistentDataType.INTEGER, d); - } - } - } - - public int getMaxDistance(Player player) { - int level = getActiveLevel(player); - if (level == 0) return 0; - Config config = getConfig(); - return config.baseDistance * (level * config.multiplier); - } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void on(CustomBlockDataMoveEvent event) { - if (!event.getCustomBlockData().has(ELEVATOR_KEY)) return; - event.setCancelled(true); - - Event bukkit = event.getBukkitEvent(); - if (bukkit instanceof Cancellable cancellable) { - cancellable.setCancelled(true); + private static final NamespacedKey ELEVATOR_KEY = new NamespacedKey(Adapt.instance, "elevator"); + private static final NamespacedKey TARGET_DOWN = new NamespacedKey(Adapt.instance, "target_down"); + private static final NamespacedKey TARGET_UP = new NamespacedKey(Adapt.instance, "target_up"); + + private static final int PARTICLE_COUNT = 20; + private static final float SOUND_VOLUME = 1f; + private static final float SOUND_PITCH = 1f; + + private final Set players = java.util.concurrent.ConcurrentHashMap.newKeySet(); + + public ArchitectElevator() { + super("architect-elevator"); + registerConfiguration(ArchitectElevator.Config.class); + setDescription(Localizer.dLocalize("architect.elevator.description")); + setDisplayName(Localizer.dLocalize("architect.elevator.name")); + setIcon(Material.HEAVY_WEIGHTED_PRESSURE_PLATE); + setInterval(988); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + + registerRecipe(AdaptRecipe.shaped() + .key("elevator") + .shape("XXX") + .shape("XYX") + .shape("XXX") + .ingredient(new MaterialChar('X', Tag.WOOL)) + .ingredient(new MaterialChar('Y', Material.ENDER_PEARL)) + .result(getElevatorItem()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHITE_WOOL) + .key("challenge_architect_elevator_100") + .title(Localizer.dLocalize("advancement.challenge_architect_elevator_100.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_elevator_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WHITE_WOOL) + .key("challenge_architect_elevator_penthouse") + .title(Localizer.dLocalize("advancement.challenge_architect_elevator_penthouse.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_elevator_penthouse.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_elevator_100", "architect.elevator.trips", 100, 300); + } + + private static boolean isElevator(Block b) { + return b.getType() == Material.NOTE_BLOCK + && CustomBlockData.hasCustomBlockData(b, Adapt.instance) + && new CustomBlockData(b, Adapt.instance) + .has(ELEVATOR_KEY, PersistentDataType.INTEGER); + } + + private static boolean hasEnoughSpace(Player player, int targetY) { + BoundingBox box = player.getBoundingBox() + .shift(0, -player.getLocation().getY(), 0) + .shift(0, targetY, 0); + + double maxX = Math.ceil(box.getMaxX()); + double maxY = Math.ceil(box.getMaxY()); + double maxZ = Math.ceil(box.getMaxZ()); + World world = player.getWorld(); + for (int x = (int) box.getMinX(); x <= maxX; x++) { + for (int z = (int) box.getMinZ(); z <= maxZ; z++) { + for (int y = (int) box.getMinY(); y <= maxY; y++) { + Block block = world.getBlockAt(x, y, z); + if (block.isPassable() || block.isLiquid()) + continue; + VoxelShape shape = block.getCollisionShape(); + box.shift(-x, -y, -z); + if (shape.overlaps(box)) + return false; + box.shift(x, y, z); } + } } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void on(BlockExplodeEvent event) { - event.blockList().removeIf(ArchitectElevator::isElevator); + return true; + } + + @Override + public void addStats(int level, Element v) { + + } + + public ItemStack getElevatorItem() { + ItemStack elevatorItem = CustomModel.get(Material.NOTE_BLOCK, "architect", "elevator", "item") + .toItemStack(); + ItemMeta meta = elevatorItem.getItemMeta(); + if (meta != null) { + meta.getPersistentDataContainer().set(ELEVATOR_KEY, PersistentDataType.BYTE, (byte) 0); + meta.setDisplayName(Localizer.dLocalize("items.elevator_block.name")); + meta.setLore(List.of(Localizer.dLocalize("items.elevator_block.usage1"), + Localizer.dLocalize("items.elevator_block.usage2"), + Localizer.dLocalize("items.elevator_block.usage3"))); + elevatorItem.setItemMeta(meta); } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void on(EntityExplodeEvent event) { - event.blockList().removeIf(ArchitectElevator::isElevator); + return elevatorItem; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + if (e.getTo() == null) return; + Player player = e.getPlayer(); + + if (!players.add(player.getUniqueId())) { + if (e.getFrom().getY() < e.getTo().getY() || player.isFlying()) + players.remove(player.getUniqueId()); + return; } - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void on(CustomBlockDataRemoveEvent event) { - CustomBlockData data = event.getCustomBlockData(); - if (!data.has(ELEVATOR_KEY)) return; - Event bukkit = event.getBukkitEvent(); - if (!(bukkit instanceof BlockBreakEvent breakEvent)) { - if (bukkit instanceof Cancellable cancellable) - cancellable.setCancelled(true); - event.setCancelled(true); - return; - } - - breakEvent.setDropItems(false); - Block block = event.getBlock(); - World world = block.getWorld(); - Location location = block.getLocation(); - world.dropItemNaturally(location, getElevatorItem()); - - data.remove(ELEVATOR_KEY); - int y = block.getY(); - int lowerY = data.getOrDefault(TARGET_DOWN, PersistentDataType.INTEGER, 0); - int upperY = data.getOrDefault(TARGET_UP, PersistentDataType.INTEGER, 0); - data.remove(TARGET_DOWN); - data.remove(TARGET_UP); - - if (y - lowerY < world.getMinHeight()) - lowerY = 0; - - if (y + upperY > world.getMaxHeight()) - upperY = 0; - - if (lowerY != 0 && upperY != 0) { - Block lower = block.getRelative(BlockFace.DOWN, lowerY); - Block upper = block.getRelative(BlockFace.UP, upperY); - - boolean lowerElevator = isElevator(lower); - boolean upperElevator = isElevator(upper); - - if (lowerElevator && upperElevator) { - CustomBlockData lowerData = new CustomBlockData(lower, Adapt.instance); - CustomBlockData upperData = new CustomBlockData(upper, Adapt.instance); - - int dist = upperY + lowerY; - int lowerDist = lowerData.getOrDefault(ELEVATOR_KEY, PersistentDataType.INTEGER, 0); - int upperDist = upperData.getOrDefault(ELEVATOR_KEY, PersistentDataType.INTEGER, 0); - int maxDistance = Math.max(upperDist, lowerDist); - - if (dist <= maxDistance) { - lowerData.set(TARGET_UP, PersistentDataType.INTEGER, dist); - upperData.set(TARGET_DOWN, PersistentDataType.INTEGER, dist); - } else { - lowerData.remove(TARGET_UP); - upperData.remove(TARGET_DOWN); - } - } else if (lowerElevator) { - new CustomBlockData(lower, Adapt.instance) - .remove(TARGET_UP); - } else if (upperElevator) { - new CustomBlockData(upper, Adapt.instance) - .remove(TARGET_DOWN); - } - } else if (lowerY != 0) { - Block lower = block.getRelative(BlockFace.DOWN, lowerY); - - if (isElevator(lower)) { - new CustomBlockData(lower, Adapt.instance) - .remove(TARGET_UP); - } - } else if (upperY != 0) { - Block upper = block.getRelative(BlockFace.UP, upperY); - - if (isElevator(upper)) { - new CustomBlockData(upper, Adapt.instance) - .remove(TARGET_DOWN); - } - } + if (player.isFlying() || player.getVelocity().getY() <= 0 || e.getFrom().getY() >= e.getTo().getY()) + return; + + Block block = findElevator(player); + if (block == null) return; + handleElevatorMovement(block, player, false); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent event) { + if (!event.isSneaking() || event.getPlayer().isInsideVehicle()) return; + Player player = event.getPlayer(); + Block block = findElevator(player); + if (block == null) return; + handleElevatorMovement(block, player, true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockPlaceEvent event) { + ItemMeta meta = event.getItemInHand().getItemMeta(); + if (meta == null || !meta.getPersistentDataContainer().has(ELEVATOR_KEY, PersistentDataType.BYTE)) + return; + int maxDistance = getMaxDistance(event.getPlayer()); + if (maxDistance <= 0) { + event.setCancelled(true); + return; } - @Nullable - private Block findElevator(Player player) { - Block base = player.getLocation().getBlock(); - for (int d = 1; d <= 2; d++) { - Block rel = base.getRelative(BlockFace.DOWN, d); - if (isElevator(rel)) - return rel; - } - return null; + Block block = event.getBlock(); + World world = block.getWorld(); + CustomBlockData data = new CustomBlockData(block, Adapt.instance); + data.set(ELEVATOR_KEY, PersistentDataType.INTEGER, maxDistance); + + int lowerDist = Math.min(block.getY() - world.getMinHeight(), maxDistance); + for (int d = 1; d <= lowerDist; d++) { + org.bukkit.block.Block lower = block.getRelative(BlockFace.DOWN, d); + if (checkElevator(lower, TARGET_UP, d)) { + data.set(TARGET_DOWN, PersistentDataType.INTEGER, d); + break; + } } - private boolean checkElevator(Block block, NamespacedKey key, int source) { - if (!isElevator(block)) - return false; - - new CustomBlockData(block, Adapt.instance) - .set(key, PersistentDataType.INTEGER, source); - return true; + int upperDist = Math.min(world.getMaxHeight() - block.getY(), maxDistance); + for (int d = 1; d <= upperDist; d++) { + org.bukkit.block.Block upper = block.getRelative(BlockFace.UP, d); + if (checkElevator(upper, TARGET_DOWN, d)) { + data.set(TARGET_UP, PersistentDataType.INTEGER, d); + } } - - private void handleElevatorMovement(Block block, Player player, boolean down) { - if (!isElevator(block) || player.isInsideVehicle()) - return; - - CustomBlockData data = new CustomBlockData(block, Adapt.instance); - int distance = data.getOrDefault(down ? TARGET_DOWN : TARGET_UP, PersistentDataType.INTEGER, 0); - if (distance == 0) - return; - int targetY = block.getY() + (down ? -distance : distance); - if (targetY < block.getWorld().getMinHeight() || targetY > block.getWorld().getMaxHeight()) - return; - - Block target = block.getRelative(down ? BlockFace.DOWN : BlockFace.UP, distance); - if (!isElevator(target)) - return; - - var loc = player.getLocation(); - loc.setY(target.getY() + 1); - - if (!hasEnoughSpace(player, loc.getBlockY())) - return; - - teleportPlayer(player, loc); - getPlayer(player).getData().addStat("architect.elevator.trips", 1); - if (distance >= 50 && AdaptConfig.get().isAdvancements() && !getPlayer(player).getData().isGranted("challenge_architect_elevator_penthouse")) { - getPlayer(player).getAdvancementHandler().grant("challenge_architect_elevator_penthouse"); - } + } + + public int getMaxDistance(Player player) { + int level = getActiveLevel(player); + if (level == 0) return 0; + Config config = getConfig(); + return config.baseDistance * (level * config.multiplier); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(CustomBlockDataMoveEvent event) { + if (!event.getCustomBlockData().has(ELEVATOR_KEY)) return; + event.setCancelled(true); + + Event bukkit = event.getBukkitEvent(); + if (bukkit instanceof Cancellable cancellable) { + cancellable.setCancelled(true); } - - private static boolean isElevator(Block b) { - return b.getType() == Material.NOTE_BLOCK - && CustomBlockData.hasCustomBlockData(b, Adapt.instance) - && new CustomBlockData(b, Adapt.instance) - .has(ELEVATOR_KEY, PersistentDataType.INTEGER); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(BlockExplodeEvent event) { + event.blockList().removeIf(ArchitectElevator::isElevator); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(EntityExplodeEvent event) { + event.blockList().removeIf(ArchitectElevator::isElevator); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(CustomBlockDataRemoveEvent event) { + CustomBlockData data = event.getCustomBlockData(); + if (!data.has(ELEVATOR_KEY)) return; + Event bukkit = event.getBukkitEvent(); + if (!(bukkit instanceof BlockBreakEvent breakEvent)) { + if (bukkit instanceof Cancellable cancellable) + cancellable.setCancelled(true); + event.setCancelled(true); + return; } - private static boolean hasEnoughSpace(Player player, int targetY) { - BoundingBox box = player.getBoundingBox() - .shift(0, -player.getLocation().getY(), 0) - .shift(0, targetY, 0); - - double maxX = Math.ceil(box.getMaxX()); - double maxY = Math.ceil(box.getMaxY()); - double maxZ = Math.ceil(box.getMaxZ()); - World world = player.getWorld(); - for (int x = (int) box.getMinX(); x <= maxX; x++) { - for (int z = (int) box.getMinZ(); z <= maxZ; z++) { - for (int y = (int) box.getMinY(); y <= maxY; y++) { - Block block = world.getBlockAt(x, y, z); - if (block.isPassable() || block.isLiquid()) - continue; - VoxelShape shape = block.getCollisionShape(); - box.shift(-x, -y, -z); - if (shape.overlaps(box)) - return false; - box.shift(x, y, z); - } - } + breakEvent.setDropItems(false); + Block block = event.getBlock(); + World world = block.getWorld(); + Location location = block.getLocation(); + world.dropItemNaturally(location, getElevatorItem()); + + data.remove(ELEVATOR_KEY); + int y = block.getY(); + int lowerY = data.getOrDefault(TARGET_DOWN, PersistentDataType.INTEGER, 0); + int upperY = data.getOrDefault(TARGET_UP, PersistentDataType.INTEGER, 0); + data.remove(TARGET_DOWN); + data.remove(TARGET_UP); + + if (y - lowerY < world.getMinHeight()) + lowerY = 0; + + if (y + upperY > world.getMaxHeight()) + upperY = 0; + + if (lowerY != 0 && upperY != 0) { + Block lower = block.getRelative(BlockFace.DOWN, lowerY); + Block upper = block.getRelative(BlockFace.UP, upperY); + + boolean lowerElevator = isElevator(lower); + boolean upperElevator = isElevator(upper); + + if (lowerElevator && upperElevator) { + CustomBlockData lowerData = new CustomBlockData(lower, Adapt.instance); + CustomBlockData upperData = new CustomBlockData(upper, Adapt.instance); + + int dist = upperY + lowerY; + int lowerDist = lowerData.getOrDefault(ELEVATOR_KEY, PersistentDataType.INTEGER, 0); + int upperDist = upperData.getOrDefault(ELEVATOR_KEY, PersistentDataType.INTEGER, 0); + int maxDistance = Math.max(upperDist, lowerDist); + + if (dist <= maxDistance) { + lowerData.set(TARGET_UP, PersistentDataType.INTEGER, dist); + upperData.set(TARGET_DOWN, PersistentDataType.INTEGER, dist); + } else { + lowerData.remove(TARGET_UP); + upperData.remove(TARGET_DOWN); } - return true; + } else if (lowerElevator) { + new CustomBlockData(lower, Adapt.instance) + .remove(TARGET_UP); + } else if (upperElevator) { + new CustomBlockData(upper, Adapt.instance) + .remove(TARGET_DOWN); + } + } else if (lowerY != 0) { + Block lower = block.getRelative(BlockFace.DOWN, lowerY); + + if (isElevator(lower)) { + new CustomBlockData(lower, Adapt.instance) + .remove(TARGET_UP); + } + } else if (upperY != 0) { + Block upper = block.getRelative(BlockFace.UP, upperY); + + if (isElevator(upper)) { + new CustomBlockData(upper, Adapt.instance) + .remove(TARGET_DOWN); + } } - - private void teleportPlayer(Player p, Location l) { - playTeleportEffects(p); - J.teleport(p, l); - SoundPlayer.of(p.getWorld()).play(p, Sound.ENTITY_ENDERMAN_TELEPORT, SOUND_VOLUME, SOUND_PITCH); - playTeleportEffects(p); + } + + @Nullable + private Block findElevator(Player player) { + Block base = player.getLocation().getBlock(); + for (int d = 1; d <= 2; d++) { + Block rel = base.getRelative(BlockFace.DOWN, d); + if (isElevator(rel)) + return rel; } - - private void playTeleportEffects(Player p) { - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.PORTAL, p.getLocation(), PARTICLE_COUNT); - } + return null; + } + + private boolean checkElevator(Block block, NamespacedKey key, int source) { + if (!isElevator(block)) + return false; + + new CustomBlockData(block, Adapt.instance) + .set(key, PersistentDataType.INTEGER, source); + return true; + } + + private void handleElevatorMovement(Block block, Player player, boolean down) { + if (!isElevator(block) || player.isInsideVehicle()) + return; + + CustomBlockData data = new CustomBlockData(block, Adapt.instance); + int distance = data.getOrDefault(down ? TARGET_DOWN : TARGET_UP, PersistentDataType.INTEGER, 0); + if (distance == 0) + return; + int targetY = block.getY() + (down ? -distance : distance); + if (targetY < block.getWorld().getMinHeight() || targetY > block.getWorld().getMaxHeight()) + return; + + Block target = block.getRelative(down ? BlockFace.DOWN : BlockFace.UP, distance); + if (!isElevator(target)) + return; + + org.bukkit.Location loc = player.getLocation(); + loc.setY(target.getY() + 1); + + if (!hasEnoughSpace(player, loc.getBlockY())) + return; + + teleportPlayer(player, loc); + getPlayer(player).getData().addStat("architect.elevator.trips", 1); + if (distance >= 50 && AdaptConfig.get().isAdvancements() && !getPlayer(player).getData().isGranted("challenge_architect_elevator_penthouse")) { + getPlayer(player).getAdvancementHandler().grant("challenge_architect_elevator_penthouse"); } - - @Override - public void onTick() { - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Build wool elevators to teleport vertically.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseDistance = 32; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int multiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.40; + } + + private void teleportPlayer(Player p, Location l) { + playTeleportEffects(p); + J.teleport(p, l); + SoundPlayer.of(p.getWorld()).play(p, Sound.ENTITY_ENDERMAN_TELEPORT, SOUND_VOLUME, SOUND_PITCH); + playTeleportEffects(p); + } + + private void playTeleportEffects(Player p) { + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.PORTAL, p.getLocation(), PARTICLE_COUNT); } + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Build wool elevators to teleport vertically.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseDistance = 32; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier for the Architect Elevator adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int multiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.40; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java index edd39b271..72ffea8c2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectFoundation.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -56,309 +56,309 @@ import java.util.concurrent.ConcurrentHashMap; public class ArchitectFoundation extends SimpleAdaptation { - private static final BlockData AIR = Material.AIR.createBlockData(); - private static final BlockData BLOCK = Material.TINTED_GLASS.createBlockData(); - private final Map blockPower; - private final Map cooldowns; - private final Set active; - private final Set activeBlocks; - - public ArchitectFoundation() { - super("architect-foundation"); - registerConfiguration(ArchitectFoundation.Config.class); - setDescription(Localizer.dLocalize("architect.foundation.description")); - setDisplayName(Localizer.dLocalize("architect.foundation.name")); - setIcon(Material.TINTED_GLASS); - setInterval(988); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - blockPower = new ConcurrentHashMap<>(); - cooldowns = new ConcurrentHashMap<>(); - active = ConcurrentHashMap.newKeySet(); - activeBlocks = ConcurrentHashMap.newKeySet(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SCAFFOLDING) - .key("challenge_architect_foundation_1k") - .title(Localizer.dLocalize("advancement.challenge_architect_foundation_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_foundation_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SCAFFOLDING) - .key("challenge_architect_foundation_10k") - .title(Localizer.dLocalize("advancement.challenge_architect_foundation_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_foundation_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_foundation_1k", "architect.foundation.blocks-placed", 1000, 300); - registerMilestone("challenge_architect_foundation_10k", "architect.foundation.blocks-placed", 10000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("architect.foundation.lore1") - + (getBlockPower(getLevelPercent(level))) + C.GRAY + " " - + Localizer.dLocalize("architect.foundation.lore2")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - if (!p.isSneaking()) { - return; - } - if (getActiveBlockPlaceLevel(p, p.getLocation()) <= 0) { - return; - } - if (e.getTo() == null || !e.getFrom().getBlock().equals(e.getTo().getBlock())) { - return; - } - if (!this.active.contains(id)) { - return; - } - int power = blockPower.getOrDefault(id, 0); - - if (power <= 0) { - return; - } - - Location l = e.getTo(); - World world = l.getWorld(); - Set locs = new HashSet<>(); - locs.add(world.getBlockAt(l.clone().add(0.3, -1, -0.3))); - locs.add(world.getBlockAt(l.clone().add(-0.3, -1, -0.3))); - locs.add(world.getBlockAt(l.clone().add(0.3, -1, 0.3))); - locs.add(world.getBlockAt(l.clone().add(-0.3, -1, +0.3))); - - for (Block b : locs) { - if (addFoundation(b)) { - power--; - getPlayer(p).getData().addStat("architect.foundation.blocks-placed", 1); - } - - if (power <= 0) { - break; - } - } - - blockPower.put(id, power); - }); - } - - // prevent piston from moving blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockPistonExtendEvent e) { - e.getBlocks().forEach(b -> { - if (activeBlocks.contains(b)) { - Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); - e.setCancelled(true); - } - }); - } - - // prevent piston from pulling blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockPistonRetractEvent e) { - e.getBlocks().forEach(b -> { - if (activeBlocks.contains(b)) { - Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); - e.setCancelled(true); - } - }); - } - - // prevent TNT from destroying blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockExplodeEvent e) { - if (activeBlocks.contains(e.getBlock())) { - Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); - e.setCancelled(true); + private static final BlockData AIR = Material.AIR.createBlockData(); + private static final BlockData BLOCK = Material.TINTED_GLASS.createBlockData(); + private final Map blockPower; + private final Map cooldowns; + private final Set active; + private final Set activeBlocks; + + public ArchitectFoundation() { + super("architect-foundation"); + registerConfiguration(ArchitectFoundation.Config.class); + setDescription(Localizer.dLocalize("architect.foundation.description")); + setDisplayName(Localizer.dLocalize("architect.foundation.name")); + setIcon(Material.TINTED_GLASS); + setInterval(988); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + blockPower = new ConcurrentHashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + active = ConcurrentHashMap.newKeySet(); + activeBlocks = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SCAFFOLDING) + .key("challenge_architect_foundation_1k") + .title(Localizer.dLocalize("advancement.challenge_architect_foundation_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_foundation_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SCAFFOLDING) + .key("challenge_architect_foundation_10k") + .title(Localizer.dLocalize("advancement.challenge_architect_foundation_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_foundation_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_foundation_1k", "architect.foundation.blocks-placed", 1000, 300); + registerMilestone("challenge_architect_foundation_10k", "architect.foundation.blocks-placed", 10000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.foundation.lore1") + + (getBlockPower(getLevelPercent(level))) + C.GRAY + " " + + Localizer.dLocalize("architect.foundation.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + if (!p.isSneaking()) { + return; + } + if (getActiveBlockPlaceLevel(p, p.getLocation()) <= 0) { + return; + } + if (e.getTo() == null || !e.getFrom().getBlock().equals(e.getTo().getBlock())) { + return; + } + if (!this.active.contains(id)) { + return; + } + int power = blockPower.getOrDefault(id, 0); + + if (power <= 0) { + return; + } + + Location l = e.getTo(); + World world = l.getWorld(); + Set locs = new HashSet<>(); + locs.add(world.getBlockAt(l.clone().add(0.3, -1, -0.3))); + locs.add(world.getBlockAt(l.clone().add(-0.3, -1, -0.3))); + locs.add(world.getBlockAt(l.clone().add(0.3, -1, 0.3))); + locs.add(world.getBlockAt(l.clone().add(-0.3, -1, +0.3))); + + for (Block b : locs) { + if (addFoundation(b)) { + power--; + getPlayer(p).getData().addStat("architect.foundation.blocks-placed", 1); } - } - // prevent block from being destroyed // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { - if (activeBlocks.contains(e.getBlock())) { - e.setCancelled(true); + if (power <= 0) { + break; } + } + + blockPower.put(id, power); + }); + } + + // prevent piston from moving blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockPistonExtendEvent e) { + e.getBlocks().forEach(b -> { + if (activeBlocks.contains(b)) { + Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); + e.setCancelled(true); + } + }); + } + + // prevent piston from pulling blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockPistonRetractEvent e) { + e.getBlocks().forEach(b -> { + if (activeBlocks.contains(b)) { + Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); + e.setCancelled(true); + } + }); + } + + // prevent TNT from destroying blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockExplodeEvent e) { + if (activeBlocks.contains(e.getBlock())) { + Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); + e.setCancelled(true); } + } - // prevent Entities from destroying blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityExplodeEvent e) { - e.blockList().removeIf(activeBlocks::contains); + // prevent block from being destroyed // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + if (activeBlocks.contains(e.getBlock())) { + e.setCancelled(true); } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (p.getGameMode().equals(GameMode.CREATIVE) - || p.getGameMode().equals(GameMode.SPECTATOR)) { - return; - } - UUID id = p.getUniqueId(); - - boolean ready = !hasCooldown(id); - boolean active = this.active.contains(id); - - if (e.isSneaking() && ready && !active) { - this.active.add(id); - cooldowns.put(id, Long.MAX_VALUE); - // effect start placing - } else if (!e.isSneaking() && active) { - this.active.remove(id); - cooldowns.put(id, M.ms() + getConfig().cooldown); - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1.0f, 10.0f); - sp.play(p.getLocation(), Sound.BLOCK_SCULK_CATALYST_BREAK, 1.0f, 0.81f); - } - }); - } - - public boolean addFoundation(Block block) { - if (!block.getType().isAir()) { - return false; - } - - if(!block.getWorld() - .getNearbyEntities(block.getLocation() - .add(.5, .5, .5), .5, .5, .5, entity -> - entity instanceof ItemFrame || entity instanceof Painting).isEmpty()) - return false; - - - J.runAt(block.getLocation(), () -> { - block.setBlockData(BLOCK); - activeBlocks.add(block); - }); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.0f, 1.0f); - if (areParticlesEnabled()) { - - vfxCuboidOutline(block, Particle.REVERSE_PORTAL); - vfxCuboidOutline(block, Particle.ASH); - } - J.runAt(block.getLocation(), () -> removeFoundation(block), 3 * 20); - return true; + } + + // prevent Entities from destroying blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityExplodeEvent e) { + e.blockList().removeIf(activeBlocks::contains); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (p.getGameMode().equals(GameMode.CREATIVE) + || p.getGameMode().equals(GameMode.SPECTATOR)) { + return; + } + UUID id = p.getUniqueId(); + + boolean ready = !hasCooldown(id); + boolean active = this.active.contains(id); + + if (e.isSneaking() && ready && !active) { + this.active.add(id); + cooldowns.put(id, Long.MAX_VALUE); + // effect start placing + } else if (!e.isSneaking() && active) { + this.active.remove(id); + cooldowns.put(id, M.ms() + getConfig().cooldown); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1.0f, 10.0f); + sp.play(p.getLocation(), Sound.BLOCK_SCULK_CATALYST_BREAK, 1.0f, 0.81f); + } + }); + } + + public boolean addFoundation(Block block) { + if (!block.getType().isAir()) { + return false; } - public void removeFoundation(Block block) { - if (!block.getBlockData().equals(BLOCK)) { - return; - } + if (!block.getWorld() + .getNearbyEntities(block.getLocation() + .add(.5, .5, .5), .5, .5, .5, entity -> + entity instanceof ItemFrame || entity instanceof Painting).isEmpty()) + return false; - J.runAt(block.getLocation(), () -> { - block.setBlockData(AIR); - activeBlocks.remove(block); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_BREAK, 1.0f, 1.0f); - }); - if (areParticlesEnabled()) { - vfxCuboidOutline(block, Particles.ENCHANTMENT_TABLE); - } - } - public int getBlockPower(double factor) { - return (int) Math.floor(M.lerp(getConfig().minBlocks, getConfig().maxBlocks, factor)); - } + J.runAt(block.getLocation(), () -> { + block.setBlockData(BLOCK); + activeBlocks.add(block); + }); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.0f, 1.0f); + if (areParticlesEnabled()) { - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - if (i == null || !i.isOnline()) { - continue; - } - withPlayerThread(i, () -> refreshPlayerPower(i)); - } + vfxCuboidOutline(block, Particle.REVERSE_PORTAL); + vfxCuboidOutline(block, Particle.ASH); } + J.runAt(block.getLocation(), () -> removeFoundation(block), 3 * 20); + return true; + } - private void refreshPlayerPower(Player i) { - UUID id = i.getUniqueId(); - if (!hasActiveAdaptation(i)) { - active.remove(id); - blockPower.remove(id); - cooldowns.remove(id); - return; - } - - boolean ready = !hasCooldown(id); - int availablePower = getBlockPower(getLevelPercent(i)); - blockPower.compute(id, (k, v) -> { - if (v == null || (ready && v != availablePower)) { - final var world = i.getWorld(); - final var location = i.getLocation(); - - SoundPlayer spw = SoundPlayer.of(world); - spw.play(location, Sound.BLOCK_BEACON_ACTIVATE, 1.0f, 10.0f); - spw.play(location, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1.0f, 0.81f); - - return availablePower; - } - return v; - }); + public void removeFoundation(Block block) { + if (!block.getBlockData().equals(BLOCK)) { + return; } - private boolean hasCooldown(UUID id) { - Long cooldown = cooldowns.get(id); - if (cooldown != null && M.ms() >= cooldown) { - cooldowns.remove(id); - cooldown = null; - } - - return cooldown != null; + J.runAt(block.getLocation(), () -> { + block.setBlockData(AIR); + activeBlocks.remove(block); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_BREAK, 1.0f, 1.0f); + }); + if (areParticlesEnabled()) { + vfxCuboidOutline(block, Particles.ENCHANTMENT_TABLE); } - - @EventHandler - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - blockPower.remove(id); - cooldowns.remove(id); - active.remove(id); + } + + public int getBlockPower(double factor) { + return (int) Math.floor(M.lerp(getConfig().minBlocks, getConfig().maxBlocks, factor)); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + if (i == null || !i.isOnline()) { + continue; + } + withPlayerThread(i, () -> refreshPlayerPower(i)); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + } + + private void refreshPlayerPower(Player i) { + UUID id = i.getUniqueId(); + if (!hasActiveAdaptation(i)) { + active.remove(id); + blockPower.remove(id); + cooldowns.remove(id); + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + boolean ready = !hasCooldown(id); + int availablePower = getBlockPower(getLevelPercent(i)); + blockPower.compute(id, (k, v) -> { + if (v == null || (ready && v != availablePower)) { + final org.bukkit.World world = i.getWorld(); + final org.bukkit.Location location = i.getLocation(); + + SoundPlayer spw = SoundPlayer.of(world); + spw.play(location, Sound.BLOCK_BEACON_ACTIVATE, 1.0f, 10.0f); + spw.play(location, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1.0f, 0.81f); + + return availablePower; + } + return v; + }); + } + + private boolean hasCooldown(UUID id) { + Long cooldown = cooldowns.get(id); + if (cooldown != null && M.ms() >= cooldown) { + cooldowns.remove(id); + cooldown = null; } - @NoArgsConstructor - @ConfigDescription("Sneak to place a temporary foundation beneath you.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long duration = 3000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int minBlocks = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int maxBlocks = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int cooldown = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Foundation adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.40; - } + return cooldown != null; + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + blockPower.remove(id); + cooldowns.remove(id); + active.remove(id); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak to place a temporary foundation beneath you.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long duration = 3000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int minBlocks = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int maxBlocks = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Foundation adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int cooldown = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Foundation adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.40; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java index 61bf0ef13..87ee7652b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectGlass.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -38,98 +38,98 @@ import org.bukkit.inventory.ItemStack; public class ArchitectGlass extends SimpleAdaptation { - public ArchitectGlass() { - super("architect-glass"); - registerConfiguration(ArchitectGlass.Config.class); - setDescription(Localizer.dLocalize("architect.glass.description")); - setDisplayName(Localizer.dLocalize("architect.glass.name")); - setIcon(Material.GLASS); - setInterval(25000); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_architect_glass_200") - .title(Localizer.dLocalize("advancement.challenge_architect_glass_200.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_glass_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_architect_glass_5k") - .title(Localizer.dLocalize("advancement.challenge_architect_glass_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_glass_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_glass_200", "architect.glass.blocks-recovered", 200, 300); - registerMilestone("challenge_architect_glass_5k", "architect.glass.blocks-recovered", 5000, 1000); - } + public ArchitectGlass() { + super("architect-glass"); + registerConfiguration(ArchitectGlass.Config.class); + setDescription(Localizer.dLocalize("architect.glass.description")); + setDisplayName(Localizer.dLocalize("architect.glass.name")); + setIcon(Material.GLASS); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_architect_glass_200") + .title(Localizer.dLocalize("advancement.challenge_architect_glass_200.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_glass_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_architect_glass_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_glass_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_glass_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_glass_200", "architect.glass.blocks-recovered", 200, 300); + registerMilestone("challenge_architect_glass_5k", "architect.glass.blocks-recovered", 5000, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("architect.glass.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.glass.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (p.getInventory().getItemInMainHand().getType() == Material.AIR || !isTool(p.getInventory().getItemInMainHand())) { - if (!canBlockBreak(p, e.getBlock().getLocation())) { - return; - } - if (e.getBlock().getType().toString().contains("GLASS") && !e.getBlock().getType().toString().contains("TINTED_GLASS")) { - e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), new ItemStack(e.getBlock().getType(), 1)); - SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); - spw.play(e.getBlock().getLocation(), Sound.BLOCK_LARGE_AMETHYST_BUD_BREAK, 1.0f, 1.0f); - if (areParticlesEnabled()) { + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (p.getInventory().getItemInMainHand().getType() == Material.AIR || !isTool(p.getInventory().getItemInMainHand())) { + if (!canBlockBreak(p, e.getBlock().getLocation())) { + return; + } + if (e.getBlock().getType().toString().contains("GLASS") && !e.getBlock().getType().toString().contains("TINTED_GLASS")) { + e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), new ItemStack(e.getBlock().getType(), 1)); + SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); + spw.play(e.getBlock().getLocation(), Sound.BLOCK_LARGE_AMETHYST_BUD_BREAK, 1.0f, 1.0f); + if (areParticlesEnabled()) { - e.getBlock().getWorld().spawnParticle(Particle.SCRAPE, e.getBlock().getLocation(), 1); - vfxCuboidOutline(e.getBlock(), Particle.REVERSE_PORTAL); - } - e.getBlock().breakNaturally(); - getPlayer(p).getData().addStat("architect.glass.blocks-recovered", 1); - } - } - }); - } + e.getBlock().getWorld().spawnParticle(Particle.SCRAPE, e.getBlock().getLocation(), 1); + vfxCuboidOutline(e.getBlock(), Particle.REVERSE_PORTAL); + } + e.getBlock().breakNaturally(); + getPlayer(p).getData().addStat("architect.glass.blocks-recovered", 1); + } + } + }); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Silk-touch glass blocks when breaking them with an empty hand.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Glass adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 5; - } + @NoArgsConstructor + @ConfigDescription("Silk-touch glass blocks when breaking them with an empty hand.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Glass adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java index fb2676db2..5c503fc2a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectPlacement.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -53,535 +53,533 @@ import java.util.concurrent.ConcurrentHashMap; public class ArchitectPlacement extends SimpleAdaptation { - private final Map> totalMap = new ConcurrentHashMap<>(); - private final Map> previewDisplays = new ConcurrentHashMap<>(); - - public ArchitectPlacement() { - super("architect-placement"); - registerConfiguration(ArchitectPlacement.Config.class); - setDescription(Localizer.dLocalize("architect.placement.description")); - setDisplayName(Localizer.dLocalize("architect.placement.name")); - setIcon(Material.SCAFFOLDING); - setInterval(360); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BRICKS) - .key("challenge_architect_placement_1k") - .title(Localizer.dLocalize("advancement.challenge_architect_placement_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_placement_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BRICKS) - .key("challenge_architect_placement_25k") - .title(Localizer.dLocalize("advancement.challenge_architect_placement_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_placement_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_placement_1k", "architect.placement.blocks-placed", 1000, 300); - registerMilestone("challenge_architect_placement_25k", "architect.placement.blocks-placed", 25000, 1500); - } + private final Map> totalMap = new ConcurrentHashMap<>(); + private final Map> previewDisplays = new ConcurrentHashMap<>(); + + public ArchitectPlacement() { + super("architect-placement"); + registerConfiguration(ArchitectPlacement.Config.class); + setDescription(Localizer.dLocalize("architect.placement.description")); + setDisplayName(Localizer.dLocalize("architect.placement.name")); + setIcon(Material.SCAFFOLDING); + setInterval(360); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BRICKS) + .key("challenge_architect_placement_1k") + .title(Localizer.dLocalize("advancement.challenge_architect_placement_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_placement_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BRICKS) + .key("challenge_architect_placement_25k") + .title(Localizer.dLocalize("advancement.challenge_architect_placement_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_placement_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_placement_1k", "architect.placement.blocks-placed", 1000, 300); + registerMilestone("challenge_architect_placement_25k", "architect.placement.blocks-placed", 25000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.placement.lore3")); + } + + private BlockFace getBlockFace(Player player) { + List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, 5); + if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) + return null; + Block targetBlock = lastTwoTargetBlocks.get(1); + Block adjacentBlock = lastTwoTargetBlocks.get(0); + return targetBlock.getFace(adjacentBlock); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + totalMap.remove(id); + clearPreviewDisplays(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + SoundPlayer sp = SoundPlayer.of(p); + if (getActiveLevel(p, Player::isSneaking) <= 0) { + return; + } + + Map blocks = totalMap.get(id); + if (blocks == null || blocks.isEmpty()) { + return; + } + + ItemStack hand = e.getItemInHand(); + Block first = null; + for (Block candidate : blocks.keySet()) { + first = candidate; + break; + } + if (!hand.getType().isBlock() || first == null || first.getType() != hand.getType()) { + return; + } + + double v = getValue(e.getBlock()); + Block ignored = null; + for (Map.Entry entry : blocks.entrySet()) { + Block source = entry.getKey(); + BlockFace face = entry.getValue(); + if (source == null || face == null) { + continue; + } + if (source.getRelative(face).equals(e.getBlock())) { + ignored = source; + break; + } + } + + if (hand.getAmount() < blocks.size()) { + Adapt.messagePlayer(p, C.RED + Localizer.dLocalize("architect.placement.lore1") + " " + C.GREEN + blocks.size() + C.RED + " " + Localizer.dLocalize("architect.placement.lore2")); + return; + } + + if (ignored != null) { + blocks.remove(ignored); + } + for (Map.Entry entry : blocks.entrySet()) { + Block b = entry.getKey(); + BlockFace face = entry.getValue(); + if (b == null || face == null) { + continue; + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("architect.placement.lore3")); - } + Block relative = b.getRelative(face); + if (!relative.getType().isAir()) { + continue; + } - private BlockFace getBlockFace(Player player) { - List lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, 5); - if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) return null; - Block targetBlock = lastTwoTargetBlocks.get(1); - Block adjacentBlock = lastTwoTargetBlocks.get(0); - return targetBlock.getFace(adjacentBlock); - } + if (!canBlockPlace(p, relative.getLocation())) { + Adapt.verbose("Player " + p.getName() + " doesn't have permission."); + continue; + } - @EventHandler - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); + relative.setBlockData(b.getBlockData()); + getPlayer(p).getData().addStat("blocks.placed", 1); + getPlayer(p).getData().addStat("blocks.placed.value", v); + getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); + sp.play(b.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); + xp(p, 2); + + hand.setAmount(hand.getAmount() - 1); + } + + if (ignored != null) { + e.getBlock().setBlockData(ignored.getBlockData()); + getPlayer(p).getData().addStat("blocks.placed", 1); + getPlayer(p).getData().addStat("blocks.placed.value", v); + getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); + sp.play(ignored.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); + xp(p, 2); + + hand.setAmount(hand.getAmount() - 1); + } else { + e.setCancelled(true); + } + + totalMap.remove(id); + clearPreviewDisplays(id); + if (hand.getAmount() > 0) { + runPlayerViewport(getBlockFace(p), p.getTargetBlock(null, 5), p.getInventory().getItemInMainHand().getType(), p); + } + }); + } + + + @EventHandler + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { totalMap.remove(id); clearPreviewDisplays(id); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(BlockPlaceEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - SoundPlayer sp = SoundPlayer.of(p); - if (getActiveLevel(p, Player::isSneaking) <= 0) { - return; - } - - var blocks = totalMap.get(id); - if (blocks == null || blocks.isEmpty()) { - return; - } - - ItemStack hand = e.getItemInHand(); - Block first = blocks.keySet().stream().findFirst().orElse(null); - if (!hand.getType().isBlock() || first == null || first.getType() != hand.getType()) { - return; - } - - double v = getValue(e.getBlock()); - Block ignored = blocks.keySet() - .stream() - .filter(b -> b.getRelative(blocks.get(b)).equals(e.getBlock())) - .findFirst() - .orElse(null); - - if (hand.getAmount() < blocks.size()) { - Adapt.messagePlayer(p, C.RED + Localizer.dLocalize("architect.placement.lore1") + " " + C.GREEN + blocks.size() + C.RED + " " + Localizer.dLocalize("architect.placement.lore2")); - return; - } - - if (ignored != null) { - blocks.remove(ignored); - } - for (Map.Entry entry : new ArrayList<>(blocks.entrySet())) { // Block Placer - Block b = entry.getKey(); - BlockFace face = entry.getValue(); - if (b == null || face == null) { - continue; - } - - Block relative = b.getRelative(face); - if (!relative.getType().isAir()) { - continue; - } - - if (!canBlockPlace(p, relative.getLocation())) { - Adapt.verbose("Player " + p.getName() + " doesn't have permission."); - continue; - } - - relative.setBlockData(b.getBlockData()); - getPlayer(p).getData().addStat("blocks.placed", 1); - getPlayer(p).getData().addStat("blocks.placed.value", v); - getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); - sp.play(b.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); - xp(p, 2); - - hand.setAmount(hand.getAmount() - 1); - } - - if (ignored != null) { - e.getBlock().setBlockData(ignored.getBlockData()); - getPlayer(p).getData().addStat("blocks.placed", 1); - getPlayer(p).getData().addStat("blocks.placed.value", v); - getPlayer(p).getData().addStat("architect.placement.blocks-placed", 1); - sp.play(ignored.getLocation(), Sound.BLOCK_AZALEA_BREAK, 0.4f, 0.25f); - xp(p, 2); - - hand.setAmount(hand.getAmount() - 1); - } else { - e.setCancelled(true); - } - - totalMap.remove(id); - clearPreviewDisplays(id); - if (hand.getAmount() > 0) { - runPlayerViewport(getBlockFace(p), p.getTargetBlock(null, 5), p.getInventory().getItemInMainHand().getType(), p); - } - }); - } + return; + } + if (e.isSneaking()) { + totalMap.remove(id); + clearPreviewDisplays(id); + } - @EventHandler - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - int level = getActiveLevel(p); - if (level <= 0) { - totalMap.remove(id); - clearPreviewDisplays(id); - return; - } + if (!e.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { + Block block = p.getTargetBlock(null, 5); // 5 is the range of player + if (block instanceof Container) { // return if block is a container + return; + } + Material handMaterial = p.getInventory().getItemInMainHand().getType(); + if (handMaterial.isAir()) { + return; + } + BlockFace viewPortBlock = getBlockFace(p); + runPlayerViewport(viewPortBlock, block, handMaterial, p); + } + }); + } + + + @EventHandler + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; + } - if (e.isSneaking()) { - totalMap.remove(id); - clearPreviewDisplays(id); - } + if (!p.isSneaking()) { + totalMap.remove(id); + clearPreviewDisplays(id); + } - if (!e.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { - Block block = p.getTargetBlock(null, 5); // 5 is the range of player - if (block instanceof Container) { // return if block is a container - return; - } - Material handMaterial = p.getInventory().getItemInMainHand().getType(); - if (handMaterial.isAir()) { - return; - } - BlockFace viewPortBlock = getBlockFace(p); - runPlayerViewport(viewPortBlock, block, handMaterial, p); - } - }); + if (p.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { + Block block = p.getTargetBlock(null, 5); // 5 is the range of player + if (block instanceof Container) { // return if block is a container + return; + } + Material handMaterial = p.getInventory().getItemInMainHand().getType(); + if (handMaterial.isAir()) { + return; + } + BlockFace viewPortBlock = getBlockFace(p); + runPlayerViewport(viewPortBlock, block, handMaterial, p); + } + }); + } + + public void runPlayerViewport(BlockFace viewPortBlock, Block block, Material handMaterial, Player p) { + UUID id = p.getUniqueId(); + if (viewPortBlock == null || block == null || handMaterial == null || handMaterial.isAir()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; } + Map map = new HashMap<>(); - @EventHandler - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - int level = getActiveLevel(p); - if (level <= 0) { - totalMap.remove(id); - clearPreviewDisplays(id); - return; - } - - if (!p.isSneaking()) { - totalMap.remove(id); - clearPreviewDisplays(id); - } - - if (p.isSneaking() && p.getInventory().getItemInMainHand().getType().isBlock()) { - Block block = p.getTargetBlock(null, 5); // 5 is the range of player - if (block instanceof Container) { // return if block is a container - return; - } - Material handMaterial = p.getInventory().getItemInMainHand().getType(); - if (handMaterial.isAir()) { - return; - } - BlockFace viewPortBlock = getBlockFace(p); - runPlayerViewport(viewPortBlock, block, handMaterial, p); - } - }); - } - - public void runPlayerViewport(BlockFace viewPortBlock, Block block, Material handMaterial, Player p) { - UUID id = p.getUniqueId(); - if (viewPortBlock == null || block == null || handMaterial == null || handMaterial.isAir()) { - totalMap.remove(id); - clearPreviewDisplays(id); - return; + if (viewPortBlock.getDirection().equals(BlockFace.NORTH.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.SOUTH.getDirection())) { // North & South = X + for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { // 1 is the radius of the blocks + for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { + addViewportEntry(map, block.getWorld().getBlockAt(x, y, block.getZ()), viewPortBlock, handMaterial); } - - Map map = new ConcurrentHashMap<>(); - - if (viewPortBlock.getDirection().equals(BlockFace.NORTH.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.SOUTH.getDirection())) { // North & South = X - for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { // 1 is the radius of the blocks - for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { - addViewportEntry(map, block.getWorld().getBlockAt(x, y, block.getZ()), viewPortBlock, handMaterial); - } - } - } else if (viewPortBlock.getDirection().equals(BlockFace.EAST.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.WEST.getDirection())) { // East & West = Z - for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks - for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { - addViewportEntry(map, block.getWorld().getBlockAt(block.getX(), y, z), viewPortBlock, handMaterial); - } - } - } else if (viewPortBlock.getDirection().equals(BlockFace.UP.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.DOWN.getDirection())) { // Up & Down = Y - for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks - for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { - addViewportEntry(map, block.getWorld().getBlockAt(x, block.getY(), z), viewPortBlock, handMaterial); - } - } + } + } else if (viewPortBlock.getDirection().equals(BlockFace.EAST.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.WEST.getDirection())) { // East & West = Z + for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks + for (int y = block.getY() - 1; y <= block.getY() + 1; y++) { + addViewportEntry(map, block.getWorld().getBlockAt(block.getX(), y, z), viewPortBlock, handMaterial); } - - if (map.isEmpty()) { - totalMap.remove(id); - clearPreviewDisplays(id); - return; + } + } else if (viewPortBlock.getDirection().equals(BlockFace.UP.getDirection()) || viewPortBlock.getDirection().equals(BlockFace.DOWN.getDirection())) { // Up & Down = Y + for (int z = block.getZ() - 1; z <= block.getZ() + 1; z++) { // 1 is the radius of the blocks + for (int x = block.getX() - 1; x <= block.getX() + 1; x++) { + addViewportEntry(map, block.getWorld().getBlockAt(x, block.getY(), z), viewPortBlock, handMaterial); } + } + } - totalMap.put(id, map); + if (map.isEmpty()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; } - private void addViewportEntry(Map map, Block target, BlockFace viewPortBlock, Material handMaterial) { - if (target == null || viewPortBlock == null || handMaterial == null) { - return; - } + totalMap.put(id, map); + } - int maxBlocks = Math.max(1, getConfig().maxBlocks); - if (map.size() >= maxBlocks) { - return; - } - - if (target.getType() == handMaterial) { - map.put(target, viewPortBlock); - } + private void addViewportEntry(Map map, Block target, BlockFace viewPortBlock, Material handMaterial) { + if (target == null || viewPortBlock == null || handMaterial == null) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + int maxBlocks = Math.max(1, getConfig().maxBlocks); + if (map.size() >= maxBlocks) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (target.getType() == handMaterial) { + map.put(target, viewPortBlock); } + } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void onTick() { - if (previewDisplays.isEmpty() && totalMap.isEmpty()) { - return; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - for (UUID playerId : new HashSet<>(previewDisplays.keySet())) { - if (!totalMap.containsKey(playerId)) { - clearPreviewDisplays(playerId); - } - } - - if (totalMap.isEmpty()) { - return; - } - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline() || !totalMap.containsKey(p.getUniqueId())) { - continue; - } - withPlayerThread(p, () -> renderPreview(p)); - } + @Override + public void onTick() { + if (previewDisplays.isEmpty() && totalMap.isEmpty()) { + return; } - private void renderPreview(Player p) { - UUID id = p.getUniqueId(); - Map blockRender = totalMap.get(id); - if (getActiveLevel(p, Player::isSneaking) <= 0 || blockRender == null || blockRender.isEmpty()) { - totalMap.remove(id); - clearPreviewDisplays(id); - return; - } - - Set activePreviews = new HashSet<>(); - boolean displayPreview = getConfig().useDisplayEntities; - - for (Map.Entry entry : new ArrayList<>(blockRender.entrySet())) { - Block b = entry.getKey(); - BlockFace bf = entry.getValue(); - if (b == null || bf == null || b instanceof Container) { - continue; - } + for (Map.Entry> entry : previewDisplays.entrySet()) { + UUID playerId = entry.getKey(); + Map displays = entry.getValue(); + if (!totalMap.containsKey(playerId) && previewDisplays.remove(playerId, displays)) { + clearPreviewDisplays(displays); + } + } - Block transposedBlock = b.getRelative(bf); - if (displayPreview) { - if (!transposedBlock.getType().isAir()) { - continue; - } - - PreviewKey key = PreviewKey.of(transposedBlock); - activePreviews.add(key); - ensurePreviewDisplay(id, key, b.getBlockData()); - } else if (areParticlesEnabled()) { - vfxCuboidOutline(transposedBlock, Particle.REVERSE_PORTAL); - } - } + if (totalMap.isEmpty()) { + return; + } - if (displayPreview) { - clearStalePreviewDisplays(id, activePreviews); - } else { - clearPreviewDisplays(id); - } + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline() || !totalMap.containsKey(p.getUniqueId())) { + continue; + } + withPlayerThread(p, () -> renderPreview(p)); + } + } + + private void renderPreview(Player p) { + UUID id = p.getUniqueId(); + Map blockRender = totalMap.get(id); + if (getActiveLevel(p, Player::isSneaking) <= 0 || blockRender == null || blockRender.isEmpty()) { + totalMap.remove(id); + clearPreviewDisplays(id); + return; } - private void ensurePreviewDisplay(UUID playerId, PreviewKey key, org.bukkit.block.data.BlockData sourceData) { - if (key == null || sourceData == null) { - return; - } + Set activePreviews = new HashSet<>(); + boolean displayPreview = getConfig().useDisplayEntities; - Map displays = previewDisplays.computeIfAbsent(playerId, unused -> new ConcurrentHashMap<>()); - BlockDisplay existing = displays.get(key); - if (existing != null) { - if (existing.isValid()) { - if (J.isFoliaThreading()) { - J.runEntity(existing, () -> { - if (!existing.isValid()) { - displays.remove(key, existing); - return; - } - existing.setBlock(sourceData); - showPreviewToOwner(playerId, existing); - }); - } else { - existing.setBlock(sourceData); - showPreviewToOwner(playerId, existing); - } - return; - } - displays.remove(key); - } + for (Map.Entry entry : blockRender.entrySet()) { + Block b = entry.getKey(); + BlockFace bf = entry.getValue(); + if (b == null || bf == null || b instanceof Container) { + continue; + } - Runnable spawnTask = () -> { - if (!totalMap.containsKey(playerId)) { - return; - } + Block transposedBlock = b.getRelative(bf); + if (displayPreview) { + if (!transposedBlock.getType().isAir()) { + continue; + } - org.bukkit.World world = Bukkit.getWorld(key.worldId()); - if (world == null) { - return; - } + PreviewKey key = PreviewKey.of(transposedBlock); + activePreviews.add(key); + ensurePreviewDisplay(id, key, b.getBlockData()); + } else if (areParticlesEnabled()) { + vfxCuboidOutline(transposedBlock, Particle.REVERSE_PORTAL); + } + } - Block targetBlock = world.getBlockAt(key.x(), key.y(), key.z()); - BlockDisplay live = displays.get(key); - if (live != null && live.isValid()) { - live.setBlock(sourceData); - showPreviewToOwner(playerId, live); - return; - } + if (displayPreview) { + clearStalePreviewDisplays(id, activePreviews); + } else { + clearPreviewDisplays(id); + } + } - BlockDisplay spawned = world.spawn(targetBlock.getLocation(), BlockDisplay.class, display -> { - display.setPersistent(false); - display.setInvulnerable(true); - display.setGravity(false); - display.setSilent(true); - display.setVisibleByDefault(false); - display.setInterpolationDuration(2); - display.setTeleportDuration(1); - display.setViewRange((float) Math.max(0.25, getConfig().displayEntityViewRange)); - display.setShadowRadius(0f); - display.setShadowStrength(0f); - display.setBrightness(new Display.Brightness(15, 15)); - display.setBlock(sourceData); - }); - displays.put(key, spawned); - showPreviewToOwner(playerId, spawned); - hidePreviewFromOthers(playerId, spawned); - }; + private void ensurePreviewDisplay(UUID playerId, PreviewKey key, org.bukkit.block.data.BlockData sourceData) { + if (key == null || sourceData == null) { + return; + } + Map displays = previewDisplays.computeIfAbsent(playerId, unused -> new ConcurrentHashMap<>()); + BlockDisplay existing = displays.get(key); + if (existing != null) { + if (existing.isValid()) { if (J.isFoliaThreading()) { - org.bukkit.World world = Bukkit.getWorld(key.worldId()); - if (world == null) { - return; + J.runEntity(existing, () -> { + if (!existing.isValid()) { + displays.remove(key, existing); + return; } - J.runAt(new org.bukkit.Location(world, key.x() + 0.5, key.y(), key.z() + 0.5), spawnTask); - return; + existing.setBlock(sourceData); + showPreviewToOwner(playerId, existing); + }); + } else { + existing.setBlock(sourceData); + showPreviewToOwner(playerId, existing); } - - spawnTask.run(); + return; + } + displays.remove(key); } - private void clearStalePreviewDisplays(UUID playerId, Set activePreviews) { - Map displays = previewDisplays.get(playerId); - if (displays == null || displays.isEmpty()) { - return; - } - - for (PreviewKey key : new HashSet<>(displays.keySet())) { - if (activePreviews.contains(key)) { - continue; - } + Runnable spawnTask = () -> { + if (!totalMap.containsKey(playerId)) { + return; + } + + org.bukkit.World world = Bukkit.getWorld(key.worldId()); + if (world == null) { + return; + } + + Block targetBlock = world.getBlockAt(key.x(), key.y(), key.z()); + BlockDisplay live = displays.get(key); + if (live != null && live.isValid()) { + live.setBlock(sourceData); + showPreviewToOwner(playerId, live); + return; + } + + BlockDisplay spawned = world.spawn(targetBlock.getLocation(), BlockDisplay.class, display -> { + display.setPersistent(false); + display.setInvulnerable(true); + display.setGravity(false); + display.setSilent(true); + display.setVisibleByDefault(false); + display.setInterpolationDuration(2); + display.setTeleportDuration(1); + display.setViewRange((float) Math.max(0.25, getConfig().displayEntityViewRange)); + display.setShadowRadius(0f); + display.setShadowStrength(0f); + display.setBrightness(new Display.Brightness(15, 15)); + display.setBlock(sourceData); + }); + displays.put(key, spawned); + showPreviewToOwner(playerId, spawned); + }; + + if (J.isFoliaThreading()) { + org.bukkit.World world = Bukkit.getWorld(key.worldId()); + if (world == null) { + return; + } + J.runAt(new org.bukkit.Location(world, key.x() + 0.5, key.y(), key.z() + 0.5), spawnTask); + return; + } - BlockDisplay removed = displays.remove(key); - removeDisplayEntity(removed); - } + spawnTask.run(); + } - if (displays.isEmpty()) { - previewDisplays.remove(playerId); - } + private void clearStalePreviewDisplays(UUID playerId, Set activePreviews) { + Map displays = previewDisplays.get(playerId); + if (displays == null || displays.isEmpty()) { + return; } - private void clearPreviewDisplays(UUID playerId) { - Map displays = previewDisplays.remove(playerId); - if (displays == null || displays.isEmpty()) { - return; - } + for (Map.Entry entry : displays.entrySet()) { + PreviewKey key = entry.getKey(); + if (key == null || activePreviews.contains(key)) { + continue; + } - for (BlockDisplay display : displays.values()) { - removeDisplayEntity(display); - } + BlockDisplay removed = entry.getValue(); + if (removed != null && displays.remove(key, removed)) { + removeDisplayEntity(removed); + } } - private void removeDisplayEntity(Entity entity) { - if (entity == null) { - return; - } - - if (J.isFoliaThreading()) { - J.runEntity(entity, () -> { - if (entity.isValid()) { - entity.remove(); - } - }); - } else if (entity.isValid()) { - entity.remove(); - } + if (displays.isEmpty()) { + previewDisplays.remove(playerId); } + } - private void showPreviewToOwner(UUID playerId, Entity entity) { - if (entity == null || !entity.isValid()) { - return; - } + private void clearPreviewDisplays(UUID playerId) { + Map displays = previewDisplays.remove(playerId); + clearPreviewDisplays(displays); + } - Player owner = Bukkit.getPlayer(playerId); - if (owner == null || !owner.isOnline()) { - return; - } + private void clearPreviewDisplays(Map displays) { + if (displays == null || displays.isEmpty()) { + return; + } - if (J.isFoliaThreading()) { - J.runEntity(owner, () -> { - if (entity.isValid()) { - owner.showEntity(Adapt.instance, entity); - } - }); - return; - } + for (BlockDisplay display : displays.values()) { + removeDisplayEntity(display); + } + } - owner.showEntity(Adapt.instance, entity); + private void removeDisplayEntity(Entity entity) { + if (entity == null) { + return; } - private void hidePreviewFromOthers(UUID ownerId, Entity entity) { - if (entity == null || !entity.isValid()) { - return; + if (J.isFoliaThreading()) { + J.runEntity(entity, () -> { + if (entity.isValid()) { + entity.remove(); } + }); + } else if (entity.isValid()) { + entity.remove(); + } + } - for (Player viewer : Bukkit.getOnlinePlayers()) { - if (viewer == null || !viewer.isOnline() || viewer.getUniqueId().equals(ownerId)) { - continue; - } + private void showPreviewToOwner(UUID playerId, Entity entity) { + if (entity == null || !entity.isValid()) { + return; + } - if (J.isFoliaThreading()) { - J.runEntity(viewer, () -> { - if (entity.isValid()) { - viewer.hideEntity(Adapt.instance, entity); - } - }); - } else { - viewer.hideEntity(Adapt.instance, entity); - } - } + Player owner = Bukkit.getPlayer(playerId); + if (owner == null || !owner.isOnline()) { + return; } - private record PreviewKey(UUID worldId, int x, int y, int z) { - private static PreviewKey of(Block block) { - return new PreviewKey(block.getWorld().getUID(), block.getX(), block.getY(), block.getZ()); + if (J.isFoliaThreading()) { + J.runEntity(owner, () -> { + if (entity.isValid()) { + owner.showEntity(Adapt.instance, entity); } + }); + return; } - @NoArgsConstructor - @ConfigDescription("Place multiple blocks at once while sneaking with a matching block.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Placement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int maxBlocks = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Placement adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Use owner-only block display previews instead of particles for the wand guide.", impact = "True shows ghost blocks only to the wand user; false keeps particle outlines.") - boolean useDisplayEntities = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "View range used for wand preview display entities.", impact = "Lower values hide previews sooner; higher values keep them visible from farther away.") - double displayEntityViewRange = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 2; + owner.showEntity(Adapt.instance, entity); + } + + private record PreviewKey(UUID worldId, int x, int y, int z) { + private static PreviewKey of(Block block) { + return new PreviewKey(block.getWorld().getUID(), block.getX(), block.getY(), block.getZ()); } + } + + @NoArgsConstructor + @ConfigDescription("Place multiple blocks at once while sneaking with a matching block.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Architect Placement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int maxBlocks = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Placement adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Use owner-only block display previews instead of particles for the wand guide.", impact = "True shows ghost blocks only to the wand user; false keeps particle outlines.") + boolean useDisplayEntities = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "View range used for wand preview display entities.", impact = "Lower values hide previews sooner; higher values keep them visible from farther away.") + double displayEntityViewRange = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java index 88eabd521..4da03afa4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Axis; import org.bukkit.Material; @@ -48,210 +48,210 @@ import java.util.*; public class ArchitectSmartShape extends SimpleAdaptation { - private static final List ROTATION_ORDER = Arrays.asList( - BlockFace.NORTH, - BlockFace.NORTH_NORTH_EAST, - BlockFace.NORTH_EAST, - BlockFace.EAST_NORTH_EAST, - BlockFace.EAST, - BlockFace.EAST_SOUTH_EAST, - BlockFace.SOUTH_EAST, - BlockFace.SOUTH_SOUTH_EAST, - BlockFace.SOUTH, - BlockFace.SOUTH_SOUTH_WEST, - BlockFace.SOUTH_WEST, - BlockFace.WEST_SOUTH_WEST, - BlockFace.WEST, - BlockFace.WEST_NORTH_WEST, - BlockFace.NORTH_WEST, - BlockFace.NORTH_NORTH_WEST - ); + private static final List ROTATION_ORDER = Arrays.asList( + BlockFace.NORTH, + BlockFace.NORTH_NORTH_EAST, + BlockFace.NORTH_EAST, + BlockFace.EAST_NORTH_EAST, + BlockFace.EAST, + BlockFace.EAST_SOUTH_EAST, + BlockFace.SOUTH_EAST, + BlockFace.SOUTH_SOUTH_EAST, + BlockFace.SOUTH, + BlockFace.SOUTH_SOUTH_WEST, + BlockFace.SOUTH_WEST, + BlockFace.WEST_SOUTH_WEST, + BlockFace.WEST, + BlockFace.WEST_NORTH_WEST, + BlockFace.NORTH_WEST, + BlockFace.NORTH_NORTH_WEST + ); - public ArchitectSmartShape() { - super("architect-smart-shape"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("architect.smart_shape.description")); - setDisplayName(Localizer.dLocalize("architect.smart_shape.name")); - setIcon(Material.BRICKS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(800); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.QUARTZ_STAIRS) - .key("challenge_architect_smart_shape_200") - .title(Localizer.dLocalize("advancement.challenge_architect_smart_shape_200.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_smart_shape_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.QUARTZ_STAIRS) - .key("challenge_architect_smart_shape_5k") - .title(Localizer.dLocalize("advancement.challenge_architect_smart_shape_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_smart_shape_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_smart_shape_200", "architect.smart-shape.rotations", 200, 300); - registerMilestone("challenge_architect_smart_shape_5k", "architect.smart-shape.rotations", 5000, 1000); - } + public ArchitectSmartShape() { + super("architect-smart-shape"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("architect.smart_shape.description")); + setDisplayName(Localizer.dLocalize("architect.smart_shape.name")); + setIcon(Material.BRICKS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(800); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.QUARTZ_STAIRS) + .key("challenge_architect_smart_shape_200") + .title(Localizer.dLocalize("advancement.challenge_architect_smart_shape_200.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_smart_shape_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.QUARTZ_STAIRS) + .key("challenge_architect_smart_shape_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_smart_shape_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_smart_shape_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_smart_shape_200", "architect.smart-shape.rotations", 200, 300); + registerMilestone("challenge_architect_smart_shape_5k", "architect.smart-shape.rotations", 5000, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("architect.smart_shape.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("architect.smart_shape.lore2")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("architect.smart_shape.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("architect.smart_shape.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + if (e.getAction() != Action.LEFT_CLICK_BLOCK || e.getClickedBlock() == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.LEFT_CLICK_BLOCK || e.getClickedBlock() == null) { - return; - } + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } - if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { - return; - } + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (!p.isSneaking()) { + return; + } - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (!p.isSneaking()) { - return; - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (isItem(hand) && hand.getType() != Material.AIR) { + return; + } - ItemStack hand = p.getInventory().getItemInMainHand(); - if (isItem(hand) && hand.getType() != Material.AIR) { - return; - } + Block target = e.getClickedBlock(); + if (!canBlockPlace(p, target.getLocation())) { + return; + } - Block target = e.getClickedBlock(); - if (!canBlockPlace(p, target.getLocation())) { - return; - } + BlockData data = target.getBlockData().clone(); + int options = rotateData(data); + if (options <= 0) { + return; + } - BlockData data = target.getBlockData().clone(); - int options = rotateData(data); - if (options <= 0) { - return; - } + target.setBlockData(data, true); + e.setCancelled(true); + SoundPlayer.of(p.getWorld()).play(target.getLocation(), Sound.ITEM_AXE_STRIP, 0.45f, 1.8f); + xp(p, Math.max(getConfig().minXpPerRotate, options * getConfig().xpPerOrientationOption)); + getPlayer(p).getData().addStat("architect.smart-shape.rotations", 1); + }); + } - target.setBlockData(data, true); - e.setCancelled(true); - SoundPlayer.of(p.getWorld()).play(target.getLocation(), Sound.ITEM_AXE_STRIP, 0.45f, 1.8f); - xp(p, Math.max(getConfig().minXpPerRotate, options * getConfig().xpPerOrientationOption)); - getPlayer(p).getData().addStat("architect.smart-shape.rotations", 1); - }); + private int rotateData(BlockData data) { + if (data instanceof Directional directional) { + BlockFace next = getNextFace(directional.getFacing(), directional.getFaces()); + if (next != null && next != directional.getFacing()) { + directional.setFacing(next); + return directional.getFaces().size(); + } } - private int rotateData(BlockData data) { - if (data instanceof Directional directional) { - BlockFace next = getNextFace(directional.getFacing(), directional.getFaces()); - if (next != null && next != directional.getFacing()) { - directional.setFacing(next); - return directional.getFaces().size(); - } - } - - if (data instanceof Rotatable rotatable) { - BlockFace next = getNextFace(rotatable.getRotation(), Set.copyOf(ROTATION_ORDER), ROTATION_ORDER); - if (next != null && next != rotatable.getRotation()) { - rotatable.setRotation(next); - return ROTATION_ORDER.size(); - } - } + if (data instanceof Rotatable rotatable) { + BlockFace next = getNextFace(rotatable.getRotation(), Set.copyOf(ROTATION_ORDER), ROTATION_ORDER); + if (next != null && next != rotatable.getRotation()) { + rotatable.setRotation(next); + return ROTATION_ORDER.size(); + } + } - if (data instanceof Orientable orientable) { - Axis current = orientable.getAxis(); - Axis next = switch (current) { - case X -> Axis.Y; - case Y -> Axis.Z; - case Z -> Axis.X; - }; + if (data instanceof Orientable orientable) { + Axis current = orientable.getAxis(); + Axis next = switch (current) { + case X -> Axis.Y; + case Y -> Axis.Z; + case Z -> Axis.X; + }; - if (orientable.getAxes().contains(next)) { - orientable.setAxis(next); - return orientable.getAxes().size(); - } + if (orientable.getAxes().contains(next)) { + orientable.setAxis(next); + return orientable.getAxes().size(); + } - if (orientable.getAxes().contains(Axis.X)) { - orientable.setAxis(Axis.X); - return orientable.getAxes().size(); - } - } - - return 0; + if (orientable.getAxes().contains(Axis.X)) { + orientable.setAxis(Axis.X); + return orientable.getAxes().size(); + } } - private BlockFace getNextFace(BlockFace current, Set supported) { - if (supported == null || supported.isEmpty()) { - return null; - } + return 0; + } - List ordered = new ArrayList<>(supported); - ordered.sort(Comparator.comparingInt(Enum::ordinal)); - return getNextFace(current, supported, ordered); + private BlockFace getNextFace(BlockFace current, Set supported) { + if (supported == null || supported.isEmpty()) { + return null; } - private BlockFace getNextFace(BlockFace current, Set supported, List order) { - if (supported == null || supported.isEmpty()) { - return null; - } + List ordered = new ArrayList<>(supported); + ordered.sort(Comparator.comparingInt(Enum::ordinal)); + return getNextFace(current, supported, ordered); + } - int idx = order.indexOf(current); - if (idx < 0) { - for (BlockFace face : order) { - if (supported.contains(face)) { - return face; - } - } + private BlockFace getNextFace(BlockFace current, Set supported, List order) { + if (supported == null || supported.isEmpty()) { + return null; + } - return null; + int idx = order.indexOf(current); + if (idx < 0) { + for (BlockFace face : order) { + if (supported.contains(face)) { + return face; } + } - for (int i = 1; i <= order.size(); i++) { - BlockFace candidate = order.get((idx + i) % order.size()); - if (supported.contains(candidate)) { - return candidate; - } - } + return null; + } - return current; + for (int i = 1; i <= order.size(); i++) { + BlockFace candidate = order.get((idx + i) % order.size()); + if (supported.contains(candidate)) { + return candidate; + } } - @Override - public void onTick() { + return current; + } - } + @Override + public void onTick() { - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Sneak-left-click a block with an empty hand to rotate its orientation.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Xp Per Rotate for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minXpPerRotate = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Orientation Option for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerOrientationOption = 0.16; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-left-click a block with an empty hand to rotate its orientation.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Xp Per Rotate for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minXpPerRotate = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Orientation Option for the Architect Smart Shape adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerOrientationOption = 0.16; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index 3d486224c..e3700eaea 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -28,10 +28,10 @@ import art.arcane.adapt.content.item.BoundRedstoneTorch; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -53,259 +53,260 @@ import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class ArchitectWirelessRedstone extends SimpleAdaptation { - private final Map cooldowns; - - public ArchitectWirelessRedstone() { - super("architect-wireless-redstone"); - registerConfiguration(ArchitectWirelessRedstone.Config.class); - setDescription(Localizer.dLocalize("architect.wireless_redstone.description")); - setDisplayName(Localizer.dLocalize("architect.wireless_redstone.name")); - setIcon(Material.REDSTONE_TORCH); - setInterval(100); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shapeless() - .key("remote-redstone-torch") - .ingredient(Material.REDSTONE_TORCH) - .ingredient(Material.TARGET) - .ingredient(Material.ENDER_PEARL) - .result(BoundRedstoneTorch.io.withData(new BoundRedstoneTorch.Data(null))) - .build()); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.REDSTONE) - .key("challenge_architect_wireless_100") - .title(Localizer.dLocalize("advancement.challenge_architect_wireless_100.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_wireless_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.REDSTONE) - .key("challenge_architect_wireless_5k") - .title(Localizer.dLocalize("advancement.challenge_architect_wireless_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_architect_wireless_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_architect_wireless_100", "architect.wireless-redstone.pulses", 100, 300); - registerMilestone("challenge_architect_wireless_5k", "architect.wireless-redstone.pulses", 5000, 1000); + private final Map cooldowns; + + public ArchitectWirelessRedstone() { + super("architect-wireless-redstone"); + registerConfiguration(ArchitectWirelessRedstone.Config.class); + setDescription(Localizer.dLocalize("architect.wireless_redstone.description")); + setDisplayName(Localizer.dLocalize("architect.wireless_redstone.name")); + setIcon(Material.REDSTONE_TORCH); + setInterval(100); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shapeless() + .key("remote-redstone-torch") + .ingredient(Material.REDSTONE_TORCH) + .ingredient(Material.TARGET) + .ingredient(Material.ENDER_PEARL) + .result(BoundRedstoneTorch.io.withData(new BoundRedstoneTorch.Data(null))) + .build()); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.REDSTONE) + .key("challenge_architect_wireless_100") + .title(Localizer.dLocalize("advancement.challenge_architect_wireless_100.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_wireless_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.REDSTONE) + .key("challenge_architect_wireless_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_wireless_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_wireless_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_wireless_100", "architect.wireless-redstone.pulses", 100, 300); + registerMilestone("challenge_architect_wireless_5k", "architect.wireless-redstone.pulses", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.wireless_redstone.lore1")); + } + + + @EventHandler + public void onPlaceBlock(BlockPlaceEvent event) { + ItemStack item = event.getItemInHand(); + if (BoundRedstoneTorch.hasItemData(item) && isRedstoneTorch(item)) { + event.setBuild(false); + event.setCancelled(true); } + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("architect.wireless_redstone.lore1")); + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND && event.getHand() != EquipmentSlot.OFF_HAND) { + return; } + ItemStack itemInHand = event.getItem(); - @EventHandler - public void onPlaceBlock(BlockPlaceEvent event) { - ItemStack item = event.getItemInHand(); - if (BoundRedstoneTorch.hasItemData(item) && isRedstoneTorch(item)) { - event.setBuild(false); - event.setCancelled(true); - } + if (itemInHand == null) { + return; } - @EventHandler - public void onPlayerInteract(PlayerInteractEvent event) { - if (event.getHand() != EquipmentSlot.HAND && event.getHand() != EquipmentSlot.OFF_HAND) { - return; - } - - ItemStack itemInHand = event.getItem(); - - if (itemInHand == null) { - return; - } - - boolean specialItem = - isRedstoneTorch(itemInHand) && BoundRedstoneTorch.hasItemData(itemInHand); - if (!specialItem) { - return; - } - - Player player = event.getPlayer(); - withPlayerThread(player, event, () -> { - if (resolveInteractContext(player, player.getLocation()) == null) { - return; - } - - boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; - boolean inCreative = player.getGameMode() == GameMode.CREATIVE; - if (inCreative && !canUseInCreative) { - return; - } - - switch (event.getAction()) { - case LEFT_CLICK_BLOCK -> handleLeftClickBlock(event, player); - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> handleRightClick(event, player); - } - }); + boolean specialItem = + isRedstoneTorch(itemInHand) && BoundRedstoneTorch.hasItemData(itemInHand); + if (!specialItem) { + return; } + Player player = event.getPlayer(); + withPlayerThread(player, event, () -> { + if (resolveInteractContext(player, player.getLocation()) == null) { + return; + } - private boolean isRedstoneTorch(ItemStack item) { - return item.getType().equals(Material.REDSTONE_TORCH); - } + boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; + boolean inCreative = player.getGameMode() == GameMode.CREATIVE; + if (inCreative && !canUseInCreative) { + return; + } + switch (event.getAction()) { + case LEFT_CLICK_BLOCK -> handleLeftClickBlock(event, player); + case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> + handleRightClick(event, player); + } + }); + } - private void handleLeftClickBlock(PlayerInteractEvent event, Player player) { - Adapt.verbose("Player " + player.getName() + " is left clicking a block"); - if (!player.isSneaking()) { - return; - } - // main hand only - if (event.getHand() != EquipmentSlot.HAND) { - return; - } - - if (event.getClickedBlock() == null) { - SoundPlayer sp = SoundPlayer.of(player); - sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); - return; - } + private boolean isRedstoneTorch(ItemStack item) { + return item.getType().equals(Material.REDSTONE_TORCH); + } - // prevent breaking block - event.setUseItemInHand(Result.DENY); - Location location = new Location(event.getClickedBlock().getWorld(), event.getClickedBlock().getX(), event.getClickedBlock().getY(), event.getClickedBlock().getZ()); - linkTorch(player, location); + private void handleLeftClickBlock(PlayerInteractEvent event, Player player) { + Adapt.verbose("Player " + player.getName() + " is left clicking a block"); + if (!player.isSneaking()) { + return; } - private void handleRightClick(PlayerInteractEvent event, Player player) { - Adapt.verbose("Player " + player.getName() + " is right clicking"); - - if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { - event.setUseItemInHand(Result.DENY); - event.setUseInteractedBlock(Result.DENY); - } - - if (hasCooldown(player)) { - SoundPlayer sp = SoundPlayer.of(player); - sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); - } else { - cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + getConfig().cooldown); - updatePlayerCooldown(player, false); - triggerPulse(player, event.getItem()); - } + // main hand only + if (event.getHand() != EquipmentSlot.HAND) { + return; } - public void updatePlayerCooldown(Player player, boolean reset) { - player.setCooldown(Material.REDSTONE_TORCH, reset ? 0 : 5000); + if (event.getClickedBlock() == null) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); + return; } + // prevent breaking block + event.setUseItemInHand(Result.DENY); - private boolean hasCooldown(Player i) { - if (cooldowns.containsKey(i.getUniqueId())) { - if (M.ms() >= cooldowns.get(i.getUniqueId())) { - cooldowns.remove(i.getUniqueId()); - } - } - return cooldowns.containsKey(i.getUniqueId()); - } + Location location = new Location(event.getClickedBlock().getWorld(), event.getClickedBlock().getX(), event.getClickedBlock().getY(), event.getClickedBlock().getZ()); + linkTorch(player, location); + } + private void handleRightClick(PlayerInteractEvent event, Player player) { + Adapt.verbose("Player " + player.getName() + " is right clicking"); - private void linkTorch(Player p, Location l) { - if (!l.getBlock().getType().equals(Material.TARGET)) { - return; - } - if (areParticlesEnabled()) { - vfxCuboidOutline(l.getBlock(), l.getBlock(), Color.RED, 1); - } - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(l, Sound.BLOCK_CHEST_OPEN, 0.1f, 9f); - spw.play(l, Sound.ENTITY_ENDER_EYE_DEATH, 0.2f, 0.48f); - ItemStack hand = p.getInventory().getItemInMainHand(); - if (hand.getAmount() == 1) { - BoundRedstoneTorch.setData(hand, l); - } else { - hand.setAmount(hand.getAmount() - 1); - ItemStack torch = BoundRedstoneTorch.withData(l); - p.getInventory().addItem(torch).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); - } + if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { + event.setUseItemInHand(Result.DENY); + event.setUseInteractedBlock(Result.DENY); } - - private void triggerPulse(Player p, ItemStack item) { - Location l = BoundRedstoneTorch.getLocation(item); - if (isBound(item) && l != null) { - loadChunkAsync(l, chunk -> J.runAt(l, () -> { - Block b = l.getBlock(); - BlockData data = b.getBlockData(); - if (data instanceof AnaloguePowerable redBlock && b.getType().equals(Material.TARGET)) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(l, Sound.BLOCK_CHEST_OPEN, 0.1f, 9f); - redBlock.setPower(15); - vfxCuboidOutline(l.getBlock(), l.getBlock(), Color.RED, 1); - b.setBlockData(redBlock); - getPlayer(p).getData().addStat("architect.wireless-redstone.pulses", 1); - J.runAt(l, () -> { - redBlock.setPower(0); - b.setBlockData(redBlock); - }, 2); - } else { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); - } - })); - } + if (hasCooldown(player)) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); + } else { + cooldowns.put(player.getUniqueId(), System.currentTimeMillis() + getConfig().cooldown); + updatePlayerCooldown(player, false); + triggerPulse(player, event.getItem()); } + } + + public void updatePlayerCooldown(Player player, boolean reset) { + player.setCooldown(Material.REDSTONE_TORCH, reset ? 0 : 5000); + } - private boolean isBound(ItemStack stack) { - return (stack.getType().equals(Material.REDSTONE_TORCH) && BoundRedstoneTorch.getLocation(stack) != null); - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private boolean hasCooldown(Player i) { + if (cooldowns.containsKey(i.getUniqueId())) { + if (M.ms() >= cooldowns.get(i.getUniqueId())) { + cooldowns.remove(i.getUniqueId()); + } } + return cooldowns.containsKey(i.getUniqueId()); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - ItemStack hand = p.getInventory().getItemInMainHand(); - ItemStack offhand = p.getInventory().getItemInOffHand(); - if ((isRedstoneTorch(hand) && BoundRedstoneTorch.hasItemData(hand)) || ( - isRedstoneTorch(offhand) && BoundRedstoneTorch.hasItemData(offhand))) { - withPlayerThread(p, () -> updatePlayerCooldown(p, false)); - } else { - withPlayerThread(p, () -> updatePlayerCooldown(p, true)); - } - } + private void linkTorch(Player p, Location l) { + if (!l.getBlock().getType().equals(Material.TARGET)) { + return; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (areParticlesEnabled()) { + vfxCuboidOutline(l.getBlock(), l.getBlock(), Color.RED, 1); } - - @NoArgsConstructor - @ConfigDescription("Use a crafted redstone remote to toggle redstone at a distance.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Wireless Redstone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int cooldown = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Wireless Redstone adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(l, Sound.BLOCK_CHEST_OPEN, 0.1f, 9f); + spw.play(l, Sound.ENTITY_ENDER_EYE_DEATH, 0.2f, 0.48f); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand.getAmount() == 1) { + BoundRedstoneTorch.setData(hand, l); + } else { + hand.setAmount(hand.getAmount() - 1); + ItemStack torch = BoundRedstoneTorch.withData(l); + p.getInventory().addItem(torch).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); + } + } + + + private void triggerPulse(Player p, ItemStack item) { + Location l = BoundRedstoneTorch.getLocation(item); + if (isBound(item) && l != null) { + loadChunkAsync(l, chunk -> J.runAt(l, () -> { + Block b = l.getBlock(); + BlockData data = b.getBlockData(); + if (data instanceof AnaloguePowerable redBlock && b.getType().equals(Material.TARGET)) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(l, Sound.BLOCK_CHEST_OPEN, 0.1f, 9f); + redBlock.setPower(15); + vfxCuboidOutline(l.getBlock(), l.getBlock(), Color.RED, 1); + b.setBlockData(redBlock); + getPlayer(p).getData().addStat("architect.wireless-redstone.pulses", 1); + J.runAt(l, () -> { + redBlock.setPower(0); + b.setBlockData(redBlock); + }, 2); + } else { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); + } + })); + } + } + + private boolean isBound(ItemStack stack) { + return (stack.getType().equals(Material.REDSTONE_TORCH) && BoundRedstoneTorch.getLocation(stack) != null); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + ItemStack hand = p.getInventory().getItemInMainHand(); + ItemStack offhand = p.getInventory().getItemInOffHand(); + if ((isRedstoneTorch(hand) && BoundRedstoneTorch.hasItemData(hand)) || ( + isRedstoneTorch(offhand) && BoundRedstoneTorch.hasItemData(offhand))) { + withPlayerThread(p, () -> updatePlayerCooldown(p, false)); + } else { + withPlayerThread(p, () -> updatePlayerCooldown(p, true)); + } } + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Use a crafted redstone remote to toggle redstone at a distance.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Architect Wireless Redstone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int cooldown = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Wireless Redstone adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java index ba5e59c6e..83f1f66b4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java @@ -26,10 +26,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,167 +44,167 @@ public class AxeChop extends SimpleAdaptation { - public AxeChop() { - super("axe-chop"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("axe.chop.description")); - setDisplayName(Localizer.dLocalize("axe.chop.name")); - setIcon(Material.IRON_AXE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(6911); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_AXE) - .key("challenge_axe_chop_100") - .title(Localizer.dLocalize("advancement.challenge_axe_chop_100.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_chop_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_AXE) - .key("challenge_axe_chop_2500") - .title(Localizer.dLocalize("advancement.challenge_axe_chop_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_chop_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHERITE_AXE) - .key("challenge_axe_chop_one_swing") - .title(Localizer.dLocalize("advancement.challenge_axe_chop_one_swing.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_chop_one_swing.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_chop_100", "axe.chop.trees-felled", 100, 400); - registerMilestone("challenge_axe_chop_2500", "axe.chop.trees-felled", 2500, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + level + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore2")); - v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore3")); + public AxeChop() { + super("axe-chop"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("axe.chop.description")); + setDisplayName(Localizer.dLocalize("axe.chop.name")); + setIcon(Material.IRON_AXE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(6911); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_AXE) + .key("challenge_axe_chop_100") + .title(Localizer.dLocalize("advancement.challenge_axe_chop_100.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_chop_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_AXE) + .key("challenge_axe_chop_2500") + .title(Localizer.dLocalize("advancement.challenge_axe_chop_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_chop_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_AXE) + .key("challenge_axe_chop_one_swing") + .title(Localizer.dLocalize("advancement.challenge_axe_chop_one_swing.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_chop_one_swing.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_chop_100", "axe.chop.trees-felled", 100, 400); + registerMilestone("challenge_axe_chop_2500", "axe.chop.trees-felled", 2500, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + level + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore2")); + v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("axe.chop.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { - return; + if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isAxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { + if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { + return; + } + BlockData b = e.getClickedBlock().getBlockData(); + if (isLog(new ItemStack(b.getMaterial()))) { + e.setCancelled(true); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_AXE_STRIP, 1.25f, 0.6f); + int logsChopped = 0; + for (int i = 0; i < getLevel(p); i++) { + if (breakStuff(e.getClickedBlock(), getRange(getLevel(p)), p)) { + logsChopped++; + p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); + damageHand(p, getDamagePerBlock(getLevelPercent(p))); + } } - - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isAxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { - if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { - return; - } - BlockData b = e.getClickedBlock().getBlockData(); - if (isLog(new ItemStack(b.getMaterial()))) { - e.setCancelled(true); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_AXE_STRIP, 1.25f, 0.6f); - int logsChopped = 0; - for (int i = 0; i < getLevel(p); i++) { - if (breakStuff(e.getClickedBlock(), getRange(getLevel(p)), p)) { - logsChopped++; - p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); - damageHand(p, getDamagePerBlock(getLevelPercent(p))); - } - } - if (logsChopped > 0) { - getPlayer(p).getData().addStat("axe.chop.trees-felled", 1); - if (logsChopped >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_chop_one_swing")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_chop_one_swing"); - } - } - } + if (logsChopped > 0) { + getPlayer(p).getData().addStat("axe.chop.trees-felled", 1); + if (logsChopped >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_chop_one_swing")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_chop_one_swing"); + } } + } } - - private int getRange(int level) { - return level * getConfig().rangeLevelMultiplier; + } + + private int getRange(int level) { + return level * getConfig().rangeLevelMultiplier; + } + + private int getCooldownTime(double levelPercent) { + return (int) (getConfig().cooldownTicksBase + (getConfig().cooldownTicksInverseLevelMultiplier * ((1D - levelPercent)))); + } + + private int getDamagePerBlock(double levelPercent) { + return (int) (getConfig().damagePerBlockBase + (getConfig().damagePerBlockInverseLevelMultiplier * ((1D - levelPercent)))); + } + + private boolean breakStuff(Block b, int power, Player player) { + Block last = b; + for (int i = b.getY(); i < power + b.getY(); i++) { + Block bb = b.getWorld().getBlockAt(b.getX(), i, b.getZ()); + if (isLog(new ItemStack(bb.getType()))) { + last = bb; + } else { + break; + } } - private int getCooldownTime(double levelPercent) { - return (int) (getConfig().cooldownTicksBase + (getConfig().cooldownTicksInverseLevelMultiplier * ((1D - levelPercent)))); + if (!canBlockBreak(player, last.getLocation())) { + Adapt.verbose("Player " + player.getName() + " doesn't have permission."); + return false; } - private int getDamagePerBlock(double levelPercent) { - return (int) (getConfig().damagePerBlockBase + (getConfig().damagePerBlockInverseLevelMultiplier * ((1D - levelPercent)))); + if (!isLog(new ItemStack(last.getType()))) { + return false; } - private boolean breakStuff(Block b, int power, Player player) { - Block last = b; - for (int i = b.getY(); i < power + b.getY(); i++) { - Block bb = b.getWorld().getBlockAt(b.getX(), i, b.getZ()); - if (isLog(new ItemStack(bb.getType()))) { - last = bb; - } else { - break; - } - } - - if (!canBlockBreak(player, last.getLocation())) { - Adapt.verbose("Player " + player.getName() + " doesn't have permission."); - return false; - } - - if (!isLog(new ItemStack(last.getType()))) { - return false; - } - - Block ll = last; - - SoundPlayer spw = SoundPlayer.of(b.getWorld()); - spw.play(ll.getLocation(), Sound.ITEM_AXE_STRIP, 0.75f, 1.3f); - - player.breakBlock(ll); - return true; - } - - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Chop down trees by right-clicking the base log.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rangeLevelMultiplier = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksInverseLevelMultiplier = 16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerBlockBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerBlockInverseLevelMultiplier = 4; - } + Block ll = last; + + SoundPlayer spw = SoundPlayer.of(b.getWorld()); + spw.play(ll.getLocation(), Sound.ITEM_AXE_STRIP, 0.75f, 1.3f); + + player.breakBlock(ll); + return true; + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Chop down trees by right-clicking the base log.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rangeLevelMultiplier = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksInverseLevelMultiplier = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerBlockBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Inverse Level Multiplier for the Axe Chop adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerBlockInverseLevelMultiplier = 4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java index 2ca1a5ade..4661069af 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeCraftLogSwap.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -38,1035 +38,1035 @@ public class AxeCraftLogSwap extends SimpleAdaptation { - public AxeCraftLogSwap() { - super("axe-logswap"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("axe.log_swap.description")); - setDisplayName(Localizer.dLocalize("axe.log_swap.name")); - setIcon(Material.MUDDY_MANGROVE_ROOTS); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(17773); - - //Birch -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchoak") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_PLANKS, 1)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchacacia") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchdarkoak") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchjungle") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchspruce") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchmangrove") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchcherry") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapbirchpaleoak") - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Material.BIRCH_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } - - //Oak -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakbirch") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakacacia") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakdarkoak") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakjungle") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakspruce") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakmangrove") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakcherry") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapoakpaleoak") - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Material.OAK_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } - - //Acacia -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciabirch") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciaoak") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciadarkoak") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciajungle") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciaspruce") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciamangrove") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciacherry") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapacaciapaleoak") - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Material.ACACIA_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } + public AxeCraftLogSwap() { + super("axe-logswap"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("axe.log_swap.description")); + setDisplayName(Localizer.dLocalize("axe.log_swap.name")); + setIcon(Material.MUDDY_MANGROVE_ROOTS); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(17773); - //Dark Oak -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakbirch") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakoak") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakacacia") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakjungle") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakspruce") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakmangrove") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakcherry") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapdarkoakpaleoak") - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Material.DARK_OAK_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } - - //Jungle -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjunglebirch") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjungleoak") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjungleacacia") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjungledarkoak") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjunglespruce") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjunglemangrove") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjunglecherry") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapjunglepaleoak") - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Material.JUNGLE_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } - - //Spruce -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucebirch") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapspruceoak") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapspruceacacia") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucedarkoak") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucejungle") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucemangrove") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.MANGROVE_PROPAGULE) - .result(new ItemStack(Material.MANGROVE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucecherry") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapsprucepaleoak") - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Material.SPRUCE_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } + //Birch -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchoak") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_PLANKS, 1)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchacacia") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchdarkoak") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchjungle") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchspruce") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchmangrove") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchcherry") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapbirchpaleoak") + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Material.BIRCH_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); + } - //Mangrove -> Types - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovebirch") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangroveoak") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangroveacacia") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovedarkoak") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovejungle") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovespruce") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovecherry") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapmangrovepaleoak") - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Material.MANGROVE_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } + //Oak -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakbirch") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakacacia") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakdarkoak") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakjungle") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakspruce") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakmangrove") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakcherry") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapoakpaleoak") + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Material.OAK_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); + } - //Cherry -> Types - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherrybirch") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherryoak") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherryacacia") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherrydarkoak") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherryjungle") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherryspruce") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swapcherrypaleoak") - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.CHERRY_LOG) - .ingredient(Materials.PALE_OAK_SAPLING) - .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) - .build()); - } - } + //Acacia -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciabirch") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciaoak") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciadarkoak") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciajungle") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciaspruce") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciamangrove") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciacherry") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapacaciapaleoak") + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Material.ACACIA_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); + } - //Pale Oak -> Types - if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakbirch") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.BIRCH_SAPLING) - .result(new ItemStack(Material.BIRCH_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakoak") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.OAK_SAPLING) - .result(new ItemStack(Material.OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakacacia") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.ACACIA_SAPLING) - .result(new ItemStack(Material.ACACIA_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakdarkoak") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.DARK_OAK_SAPLING) - .result(new ItemStack(Material.DARK_OAK_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakjungle") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.JUNGLE_SAPLING) - .result(new ItemStack(Material.JUNGLE_LOG, 8)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakspruce") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Material.SPRUCE_SAPLING) - .result(new ItemStack(Material.SPRUCE_LOG, 8)) - .build()); - if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { - registerRecipe(AdaptRecipe.shapeless() - .key("axe-swappaleoakcherry") - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.PALE_OAK_LOG) - .ingredient(Materials.CHERRY_SAPLING) - .result(new ItemStack(Materials.CHERRY_LOG, 8)) - .build()); - } - } - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_SAPLING) - .key("challenge_axe_log_swap_500") - .title(Localizer.dLocalize("advancement.challenge_axe_log_swap_500.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_log_swap_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_log_swap_500", "axe.log-swap.conversions", 500, 400); + //Dark Oak -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakbirch") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakoak") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakacacia") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakjungle") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakspruce") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakmangrove") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakcherry") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapdarkoakpaleoak") + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Material.DARK_OAK_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("axe.log_swap.lore1")); + //Jungle -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjunglebirch") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjungleoak") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjungleacacia") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjungledarkoak") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjunglespruce") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjunglemangrove") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjunglecherry") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapjunglepaleoak") + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Material.JUNGLE_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { - return; - } - if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().startsWith("axe-swap")) { - getPlayer(p).getData().addStat("axe.log-swap.conversions", 1); - } + //Spruce -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucebirch") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapspruceoak") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapspruceacacia") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucedarkoak") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucejungle") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucemangrove") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.MANGROVE_PROPAGULE) + .result(new ItemStack(Material.MANGROVE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucecherry") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapsprucepaleoak") + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Material.SPRUCE_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); } - @Override - public void onTick() { + //Mangrove -> Types + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovebirch") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangroveoak") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangroveacacia") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovedarkoak") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovejungle") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovespruce") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovecherry") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapmangrovepaleoak") + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Material.MANGROVE_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); } - @Override - public boolean isEnabled() { - return getConfig().enabled; + //Cherry -> Types + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherrybirch") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherryoak") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherryacacia") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherrydarkoak") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherryjungle") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherryspruce") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swapcherrypaleoak") + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.CHERRY_LOG) + .ingredient(Materials.PALE_OAK_SAPLING) + .result(new ItemStack(Materials.PALE_OAK_LOG, 8)) + .build()); + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; + //Pale Oak -> Types + if (Materials.PALE_OAK_LOG != null && Materials.PALE_OAK_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakbirch") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.BIRCH_SAPLING) + .result(new ItemStack(Material.BIRCH_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakoak") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.OAK_SAPLING) + .result(new ItemStack(Material.OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakacacia") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.ACACIA_SAPLING) + .result(new ItemStack(Material.ACACIA_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakdarkoak") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.DARK_OAK_SAPLING) + .result(new ItemStack(Material.DARK_OAK_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakjungle") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.JUNGLE_SAPLING) + .result(new ItemStack(Material.JUNGLE_LOG, 8)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakspruce") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Material.SPRUCE_SAPLING) + .result(new ItemStack(Material.SPRUCE_LOG, 8)) + .build()); + if (Materials.CHERRY_LOG != null && Materials.CHERRY_SAPLING != null) { + registerRecipe(AdaptRecipe.shapeless() + .key("axe-swappaleoakcherry") + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.PALE_OAK_LOG) + .ingredient(Materials.CHERRY_SAPLING) + .result(new ItemStack(Materials.CHERRY_LOG, 8)) + .build()); + } } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_SAPLING) + .key("challenge_axe_log_swap_500") + .title(Localizer.dLocalize("advancement.challenge_axe_log_swap_500.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_log_swap_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_log_swap_500", "axe.log-swap.conversions", 500, 400); + } - @NoArgsConstructor - @ConfigDescription("Convert log types using a sapling in a crafting table.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("axe.log_swap.lore1")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { + return; + } + if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().startsWith("axe-swap")) { + getPlayer(p).getData().addStat("axe.log-swap.conversions", 1); } + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Convert log types using a sapling in a crafting table.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java index 6951eacf3..e30bc51cf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeDropToInventory.java @@ -25,10 +25,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -41,87 +41,87 @@ import java.util.List; public class AxeDropToInventory extends SimpleAdaptation { - public AxeDropToInventory() { - super("axe-drop-to-inventory"); - registerConfiguration(AxeDropToInventory.Config.class); - setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); - setDisplayName(Localizer.dLocalize("axe.drop_to_inventory.name")); - setIcon(Material.BARREL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(8800); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_axe_dti_5k") - .title(Localizer.dLocalize("advancement.challenge_axe_dti_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_dti_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_dti_5k", "axe.drop-to-inv.items-caught", 5000, 500); - } + public AxeDropToInventory() { + super("axe-drop-to-inventory"); + registerConfiguration(AxeDropToInventory.Config.class); + setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); + setDisplayName(Localizer.dLocalize("axe.drop_to_inventory.name")); + setIcon(Material.BARREL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(8800); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_axe_dti_5k") + .title(Localizer.dLocalize("advancement.challenge_axe_dti_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_dti_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_dti_5k", "axe.drop-to-inv.items-caught", 5000, 500); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); - } + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDropItemEvent e) { + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { - return; - } + Player p = e.getPlayer(); + if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { + return; + } - SoundPlayer sp = SoundPlayer.of(p); - if (ItemListings.toolAxes.contains(p.getInventory().getItemInMainHand().getType())) { - List items = new KList<>(e.getItems()); - e.getItems().clear(); - int caught = 0; - for (Item i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i.getItemStack()); - } - caught++; - } - if (caught > 0) { - getPlayer(p).getData().addStat("axe.drop-to-inv.items-caught", caught); - } + SoundPlayer sp = SoundPlayer.of(p); + if (ItemListings.toolAxes.contains(p.getInventory().getItemInMainHand().getType())) { + List items = new KList<>(e.getItems()); + e.getItems().clear(); + int caught = 0; + for (Item i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i.getItemStack()); } + caught++; + } + if (caught > 0) { + getPlayer(p).getData().addStat("axe.drop-to-inv.items-caught", caught); + } } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @NoArgsConstructor - @ConfigDescription("Chopped wood drops directly into your inventory.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Chopped wood drops directly into your inventory.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java index e8c3bb78f..330daea7a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.Impulse; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,147 +42,147 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; public class AxeGroundSmash extends SimpleAdaptation { - public AxeGroundSmash() { - super("axe-ground-smash"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("axe.ground_smash.description")); - setDisplayName(Localizer.dLocalize("axe.ground_smash.name")); - setIcon(Material.NETHERITE_AXE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(4333); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_AXE) - .key("challenge_axe_ground_smash_500") - .title(Localizer.dLocalize("advancement.challenge_axe_ground_smash_500.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_ground_smash_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHERITE_AXE) - .key("challenge_axe_ground_smash_5") - .title(Localizer.dLocalize("advancement.challenge_axe_ground_smash_5.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_ground_smash_5.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_ground_smash_500", "axe.ground-smash.mobs-hit", 500, 500); - } - - @Override - public void addStats(int level, Element v) { - double f = getLevelPercent(level); - v.addLore(C.RED + "+ " + Form.f(getFalloffDamage(f), 1) + " - " + Form.f(getDamage(f), 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore1")); - v.addLore(C.RED + "+ " + Form.f(getRadius(f), 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore2")); - v.addLore(C.RED + "+ " + Form.pc(getForce(f), 0) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore3")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore4")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e, this::isAxe); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - if (!p.isSneaking()) { - return; - } - - double f = getLevelPercent(combat.level()); - - p.setCooldown(combat.mainHand().getType(), getCooldownTime(f)); - double radius = getRadius(f); - new Impulse(radius) - .damage(getDamage(f), getFalloffDamage(f)) - .force(getForce(f)) - .punch(e.getEntity().getLocation()); - int mobsHit = 0; - for (Entity nearby : e.getEntity().getWorld().getNearbyEntities(e.getEntity().getLocation(), radius, radius, radius)) { - if (nearby instanceof LivingEntity && nearby != p) { - mobsHit++; - } - } - if (mobsHit > 0) { - getPlayer(p).getData().addStat("axe.ground-smash.mobs-hit", mobsHit); - if (mobsHit >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_ground_smash_5")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_ground_smash_5"); - } - } - SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.6f, 0.4f); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.5f, 0.1f); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_TURTLE_EGG_CRACK, SoundCategory.HOSTILE, 1f, 0.4f); - } - - - public int getCooldownTime(double factor) { - return (int) (((1D - factor) * getConfig().cooldownTicksInverseLevelMultiplier) + getConfig().cooldownTicksBase); - } - - public double getRadius(double factor) { - return getConfig().radiusLevelFactorMultiplier * factor; + public AxeGroundSmash() { + super("axe-ground-smash"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("axe.ground_smash.description")); + setDisplayName(Localizer.dLocalize("axe.ground_smash.name")); + setIcon(Material.NETHERITE_AXE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(4333); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_AXE) + .key("challenge_axe_ground_smash_500") + .title(Localizer.dLocalize("advancement.challenge_axe_ground_smash_500.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_ground_smash_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_AXE) + .key("challenge_axe_ground_smash_5") + .title(Localizer.dLocalize("advancement.challenge_axe_ground_smash_5.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_ground_smash_5.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_ground_smash_500", "axe.ground-smash.mobs-hit", 500, 500); + } + + @Override + public void addStats(int level, Element v) { + double f = getLevelPercent(level); + v.addLore(C.RED + "+ " + Form.f(getFalloffDamage(f), 1) + " - " + Form.f(getDamage(f), 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore1")); + v.addLore(C.RED + "+ " + Form.f(getRadius(f), 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore2")); + v.addLore(C.RED + "+ " + Form.pc(getForce(f), 0) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore3")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("axe.ground_smash.lore4")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e, this::isAxe); + if (combat == null) { + return; } - public double getDamage(double factor) { - return getConfig().damageLevelFactorMultiplier * factor; + Player p = combat.attacker(); + if (!p.isSneaking()) { + return; } - public double getForce(double factor) { - return (getConfig().forceFactorMultiplier * factor) + getConfig().forceBase; + double f = getLevelPercent(combat.level()); + + p.setCooldown(combat.mainHand().getType(), getCooldownTime(f)); + double radius = getRadius(f); + new Impulse(radius) + .damage(getDamage(f), getFalloffDamage(f)) + .force(getForce(f)) + .punch(e.getEntity().getLocation()); + int mobsHit = 0; + for (Entity nearby : e.getEntity().getWorld().getNearbyEntities(e.getEntity().getLocation(), radius, radius, radius)) { + if (nearby instanceof LivingEntity && nearby != p) { + mobsHit++; + } } - - public double getFalloffDamage(double factor) { - return getConfig().falloffFactor * factor; - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Jump then crouch to smash all nearby enemies with your axe.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Falloff Factor for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double falloffFactor = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusLevelFactorMultiplier = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageLevelFactorMultiplier = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double forceFactorMultiplier = 1.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double forceBase = 0.27; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksInverseLevelMultiplier = 225; + if (mobsHit > 0) { + getPlayer(p).getData().addStat("axe.ground-smash.mobs-hit", mobsHit); + if (mobsHit >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_ground_smash_5")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_ground_smash_5"); + } } + SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.6f, 0.4f); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, SoundCategory.HOSTILE, 0.5f, 0.1f); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_TURTLE_EGG_CRACK, SoundCategory.HOSTILE, 1f, 0.4f); + } + + + public int getCooldownTime(double factor) { + return (int) (((1D - factor) * getConfig().cooldownTicksInverseLevelMultiplier) + getConfig().cooldownTicksBase); + } + + public double getRadius(double factor) { + return getConfig().radiusLevelFactorMultiplier * factor; + } + + public double getDamage(double factor) { + return getConfig().damageLevelFactorMultiplier * factor; + } + + public double getForce(double factor) { + return (getConfig().forceFactorMultiplier * factor) + getConfig().forceBase; + } + + public double getFalloffDamage(double factor) { + return getConfig().falloffFactor * factor; + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Jump then crouch to smash all nearby enemies with your axe.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Falloff Factor for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double falloffFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusLevelFactorMultiplier = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Level Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageLevelFactorMultiplier = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Factor Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double forceFactorMultiplier = 1.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double forceBase = 0.27; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Inverse Level Multiplier for the Axe Ground Smash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksInverseLevelMultiplier = 225; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index 84888f83d..c6f96425a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -26,11 +26,11 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -48,164 +48,175 @@ import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class AxeLeafVeinminer extends SimpleAdaptation { - public AxeLeafVeinminer() { - super("axe-leaf-veinminer"); - registerConfiguration(AxeLeafVeinminer.Config.class); - setDescription(Localizer.dLocalize("axe.leaf_miner.description")); - setDisplayName(Localizer.dLocalize("axe.leaf_miner.name")); - setIcon(Material.BIRCH_LEAVES); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(5849); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_LEAVES) - .key("challenge_axe_leaf_5k") - .title(Localizer.dLocalize("advancement.challenge_axe_leaf_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_leaf_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_leaf_5k", "axe.leaf-veinminer.leaves-broken", 5000, 400); + public AxeLeafVeinminer() { + super("axe-leaf-veinminer"); + registerConfiguration(AxeLeafVeinminer.Config.class); + setDescription(Localizer.dLocalize("axe.leaf_miner.description")); + setDisplayName(Localizer.dLocalize("axe.leaf_miner.name")); + setIcon(Material.BIRCH_LEAVES); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5849); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_LEAVES) + .key("challenge_axe_leaf_5k") + .title(Localizer.dLocalize("advancement.challenge_axe_leaf_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_leaf_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_leaf_5k", "axe.leaf-veinminer.leaves-broken", 5000, 400); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("axe.leaf_miner.lore1")); + v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("axe.leaf_miner.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("axe.leaf_miner.lore3")); + } + + private int getRadius(int lvl) { + return lvl + getConfig().baseRange; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + + if (VEIN_MINED.get(e.getBlock())) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + return; } - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("axe.leaf_miner.lore1")); - v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("axe.leaf_miner.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("axe.leaf_miner.lore3")); + if (!p.isSneaking()) { + return; } - private int getRadius(int lvl) { - return lvl + getConfig().baseRange; + if (!isAxe(p.getInventory().getItemInMainHand())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { - - if (VEIN_MINED.get(e.getBlock())) { - return; - } - - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasActiveAdaptation(p)) { - return; - } - - if (!p.isSneaking()) { - return; - } - - if (!isAxe(p.getInventory().getItemInMainHand())) { - return; - } - - Material blockType = e.getBlock().getType(); - if (!blockType.isItem() || !isLeaves(new ItemStack(blockType))) { - return; - } + Material blockType = e.getBlock().getType(); + if (!blockType.isItem() || !isLeaves(new ItemStack(blockType))) { + return; + } - VEIN_MINED.add(e.getBlock()); - - Block block = e.getBlock(); - Map blockMap = new java.util.concurrent.ConcurrentHashMap<>(); - Deque stack = new LinkedList<>(); - stack.push(block); - int radius = getRadius(getLevel(p)); - int radiusSquared = radius * radius; - while (!stack.isEmpty() && blockMap.size() < radius) { - Block currentBlock = stack.pop(); - if (blockMap.containsKey(currentBlock.getLocation())) continue; - blockMap.put(currentBlock.getLocation(), currentBlock); - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - if (x == 0 && y == 0 && z == 0) continue; - Block b = currentBlock.getRelative(x, y, z); - if (b.getType() != block.getType() - || blockMap.containsKey(b.getLocation()) - || stack.contains(b)) - continue; - if (currentBlock.getLocation().distanceSquared(b.getLocation()) <= radiusSquared && canBlockBreak(p, b.getLocation())) { - stack.push(b); - } - } - } + VEIN_MINED.add(e.getBlock()); + + Block block = e.getBlock(); + Map blockMap = new HashMap<>(); + Deque stack = new ArrayDeque<>(); + Set queued = new HashSet<>(); + stack.push(block); + queued.add(block.getLocation()); + int radius = getRadius(getLevel(p)); + int radiusSquared = radius * radius; + while (!stack.isEmpty() && blockMap.size() < radius) { + Block currentBlock = stack.pop(); + Location currentLocation = currentBlock.getLocation(); + if (blockMap.containsKey(currentLocation)) { + continue; + } + blockMap.put(currentLocation, currentBlock); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && y == 0 && z == 0) { + continue; } - } - - int leavesBroken = blockMap.size(); - J.runEntity(p, () -> { - for (Location l : blockMap.keySet()) { - Block b = block.getWorld().getBlockAt(l); - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("axe-drop-to-inventory") : null; - - VEIN_MINED.add(b); - if (adaptation != null && adaptation.getLevel() > 0) { - Collection items = block.getDrops(); - for (ItemStack i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.01f, 0.01f); - HashMap extra = p.getInventory().addItem(i); - if (!extra.isEmpty()) { - p.getWorld().dropItem(p.getLocation(), extra.get(0)); - } - } - p.breakBlock(b); - } else { - b.breakNaturally(p.getItemInUse()); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(b.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.01f, 0.25f); - if (areParticlesEnabled()) { - b.getWorld().spawnParticle(Particle.ASH, b.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); - } - } - if (areParticlesEnabled()) { - this.vfxCuboidOutline(b, Particles.ENCHANTMENT_TABLE); - } - VEIN_MINED.remove(b); + Block b = currentBlock.getRelative(x, y, z); + Location nextLocation = b.getLocation(); + if (b.getType() != block.getType() + || blockMap.containsKey(nextLocation) + || !queued.add(nextLocation)) { + continue; } - VEIN_MINED.remove(block); - }); - if (leavesBroken > 0) { - getPlayer(p).getData().addStat("axe.leaf-veinminer.leaves-broken", leavesBroken); + if (currentBlock.getLocation().distanceSquared(b.getLocation()) <= radiusSquared && canBlockBreak(p, b.getLocation())) { + stack.push(b); + } else { + queued.remove(nextLocation); + } + } } + } } - - @Override - public void onTick() { - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Break bulk leaves at once while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Leaf Veinminer adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.325; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Leaf Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseRange = 5; + int leavesBroken = blockMap.size(); + J.runEntity(p, () -> { + for (Location l : blockMap.keySet()) { + Block b = block.getWorld().getBlockAt(l); + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("axe-drop-to-inventory") : null; + + VEIN_MINED.add(b); + if (adaptation != null && adaptation.getLevel() > 0) { + Collection items = block.getDrops(); + for (ItemStack i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.01f, 0.01f); + HashMap extra = p.getInventory().addItem(i); + if (!extra.isEmpty()) { + p.getWorld().dropItem(p.getLocation(), extra.get(0)); + } + } + p.breakBlock(b); + } else { + b.breakNaturally(p.getItemInUse()); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(b.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.01f, 0.25f); + if (areParticlesEnabled()) { + b.getWorld().spawnParticle(Particle.ASH, b.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); + } + } + if (areParticlesEnabled()) { + this.vfxCuboidOutline(b, Particles.ENCHANTMENT_TABLE); + } + VEIN_MINED.remove(b); + } + VEIN_MINED.remove(block); + }); + if (leavesBroken > 0) { + getPlayer(p).getData().addStat("axe.leaf-veinminer.leaves-broken", leavesBroken); } + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Break bulk leaves at once while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Leaf Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.325; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Leaf Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseRange = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java index 149bb4608..409ca6ac6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -48,269 +48,269 @@ import java.util.UUID; public class AxeTimberMark extends SimpleAdaptation { - public AxeTimberMark() { - super("axe-timber-mark"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("axe.timber_mark.description")); - setDisplayName(Localizer.dLocalize("axe.timber_mark.name")); - setIcon(Material.OAK_LOG); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_LOG) - .key("challenge_axe_timber_200") - .title(Localizer.dLocalize("advancement.challenge_axe_timber_200.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_timber_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_AXE) - .key("challenge_axe_timber_40") - .title(Localizer.dLocalize("advancement.challenge_axe_timber_40.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_timber_40.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_timber_200", "axe.timber-mark.marks-felled", 200, 400); + public AxeTimberMark() { + super("axe-timber-mark"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("axe.timber_mark.description")); + setDisplayName(Localizer.dLocalize("axe.timber_mark.name")); + setIcon(Material.OAK_LOG); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_LOG) + .key("challenge_axe_timber_200") + .title(Localizer.dLocalize("advancement.challenge_axe_timber_200.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_timber_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_AXE) + .key("challenge_axe_timber_40") + .title(Localizer.dLocalize("advancement.challenge_axe_timber_40.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_timber_40.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_timber_200", "axe.timber-mark.marks-felled", 200, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxBlocks(level) + C.GRAY + " " + Localizer.dLocalize("axe.timber_mark.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getMarkDurationMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("axe.timber_mark.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + setStorage(e.getPlayer(), "timberMarkBlock", null); + setStorage(e.getPlayer(), "timberMarkUntil", 0L); + setStorage(e.getPlayer(), "timberMarkOwner", id.toString()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxBlocks(level) + C.GRAY + " " + Localizer.dLocalize("axe.timber_mark.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getMarkDurationMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("axe.timber_mark.lore2")); + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - setStorage(e.getPlayer(), "timberMarkBlock", null); - setStorage(e.getPlayer(), "timberMarkUntil", 0L); - setStorage(e.getPlayer(), "timberMarkOwner", id.toString()); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { - return; - } - - if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { - return; - } - - Block clicked = e.getClickedBlock(); - if (!isLog(new org.bukkit.inventory.ItemStack(clicked.getType()))) { - return; - } + Block clicked = e.getClickedBlock(); + if (!isLog(new org.bukkit.inventory.ItemStack(clicked.getType()))) { + return; + } - setStorage(p, "timberMarkBlock", clicked.getLocation().toString()); - setStorage(p, "timberMarkUntil", System.currentTimeMillis() + getMarkDurationMillis(level)); - e.setUseInteractedBlock(Event.Result.DENY); - e.setUseItemInHand(Event.Result.DENY); - e.setCancelled(true); - SoundPlayer.of(p.getWorld()).play(clicked.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 0.6f, 1.8f); + setStorage(p, "timberMarkBlock", clicked.getLocation().toString()); + setStorage(p, "timberMarkUntil", System.currentTimeMillis() + getMarkDurationMillis(level)); + e.setUseInteractedBlock(Event.Result.DENY); + e.setUseItemInHand(Event.Result.DENY); + e.setCancelled(true); + SoundPlayer.of(p.getWorld()).play(clicked.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 0.6f, 1.8f); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + + Player p = e.getPlayer(); + int level = getActiveLevel(p); + if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { + Long until = getStorageLong(p, "timberMarkUntil", 0L); + String marked = getStorageString(p, "timberMarkBlock", ""); + if (until == null || until < System.currentTimeMillis() || marked == null || marked.isEmpty()) { + return; + } - Player p = e.getPlayer(); - int level = getActiveLevel(p); - if (level <= 0 || !isAxe(p.getInventory().getItemInMainHand())) { - return; - } + if (!e.getBlock().getLocation().toString().equals(marked)) { + return; + } - Long until = getStorageLong(p, "timberMarkUntil", 0L); - String marked = getStorageString(p, "timberMarkBlock", ""); - if (until == null || until < System.currentTimeMillis() || marked == null || marked.isEmpty()) { - return; - } + Material type = e.getBlock().getType(); + int maxBlocks = getMaxBlocks(level); + Set connected = floodLogs(e.getBlock(), type, maxBlocks); + int logsFelled = 0; + for (Block b : connected) { + if (b.equals(e.getBlock())) { + continue; + } + + if (!canBlockBreak(p, b.getLocation())) { + continue; + } + + b.breakNaturally(p.getInventory().getItemInMainHand()); + xp(p, getConfig().xpPerExtraLog); + logsFelled++; + } - if (!e.getBlock().getLocation().toString().equals(marked)) { - return; - } + Set leaves = floodLeaves(connected, getMaxLeaves(level)); + for (Block leaf : leaves) { + if (!canBlockBreak(p, leaf.getLocation())) { + continue; + } - Material type = e.getBlock().getType(); - int maxBlocks = getMaxBlocks(level); - Set connected = floodLogs(e.getBlock(), type, maxBlocks); - int logsFelled = 0; - for (Block b : connected) { - if (b.equals(e.getBlock())) { - continue; - } + leaf.breakNaturally(p.getInventory().getItemInMainHand()); + xp(p, getConfig().xpPerLeafCleared); + } - if (!canBlockBreak(p, b.getLocation())) { - continue; + SoundPlayer.of(p.getWorld()).play(e.getBlock().getLocation(), Sound.BLOCK_WOOD_BREAK, 0.6f, 0.8f); + setStorage(p, "timberMarkUntil", 0L); + setStorage(p, "timberMarkBlock", ""); + getPlayer(p).getData().addStat("axe.timber-mark.marks-felled", 1); + if (logsFelled >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_timber_40")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_timber_40"); + } + } + + private Set floodLogs(Block start, Material type, int maxBlocks) { + Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); + ArrayDeque queue = new ArrayDeque<>(); + queue.add(start); + visited.add(start); + while (!queue.isEmpty() && visited.size() < maxBlocks) { + Block b = queue.poll(); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + Block n = b.getRelative(x, y, z); + if (n.getType() != type || visited.contains(n)) { + continue; } - b.breakNaturally(p.getInventory().getItemInMainHand()); - xp(p, getConfig().xpPerExtraLog); - logsFelled++; - } - - Set leaves = floodLeaves(connected, getMaxLeaves(level)); - for (Block leaf : leaves) { - if (!canBlockBreak(p, leaf.getLocation())) { - continue; + visited.add(n); + queue.add(n); + if (visited.size() >= maxBlocks) { + return visited; } - - leaf.breakNaturally(p.getInventory().getItemInMainHand()); - xp(p, getConfig().xpPerLeafCleared); - } - - SoundPlayer.of(p.getWorld()).play(e.getBlock().getLocation(), Sound.BLOCK_WOOD_BREAK, 0.6f, 0.8f); - setStorage(p, "timberMarkUntil", 0L); - setStorage(p, "timberMarkBlock", ""); - getPlayer(p).getData().addStat("axe.timber-mark.marks-felled", 1); - if (logsFelled >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_timber_40")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_timber_40"); + } } + } } + return visited; + } + + private Set floodLeaves(Set logs, int maxLeaves) { + Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); + ArrayDeque queue = new ArrayDeque<>(); + + for (Block log : logs) { + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + Block n = log.getRelative(x, y, z); + if (!isLeafMaterial(n.getType()) || visited.contains(n)) { + continue; + } - private Set floodLogs(Block start, Material type, int maxBlocks) { - Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); - ArrayDeque queue = new ArrayDeque<>(); - queue.add(start); - visited.add(start); - while (!queue.isEmpty() && visited.size() < maxBlocks) { - Block b = queue.poll(); - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - Block n = b.getRelative(x, y, z); - if (n.getType() != type || visited.contains(n)) { - continue; - } - - visited.add(n); - queue.add(n); - if (visited.size() >= maxBlocks) { - return visited; - } - } - } + visited.add(n); + queue.add(n); + if (visited.size() >= maxLeaves) { + return visited; } + } } - return visited; + } } - private Set floodLeaves(Set logs, int maxLeaves) { - Set visited = java.util.concurrent.ConcurrentHashMap.newKeySet(); - ArrayDeque queue = new ArrayDeque<>(); - - for (Block log : logs) { - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - Block n = log.getRelative(x, y, z); - if (!isLeafMaterial(n.getType()) || visited.contains(n)) { - continue; - } - - visited.add(n); - queue.add(n); - if (visited.size() >= maxLeaves) { - return visited; - } - } - } + while (!queue.isEmpty() && visited.size() < maxLeaves) { + Block b = queue.poll(); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + Block n = b.getRelative(x, y, z); + if (!isLeafMaterial(n.getType()) || visited.contains(n)) { + continue; } - } - while (!queue.isEmpty() && visited.size() < maxLeaves) { - Block b = queue.poll(); - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - for (int z = -1; z <= 1; z++) { - Block n = b.getRelative(x, y, z); - if (!isLeafMaterial(n.getType()) || visited.contains(n)) { - continue; - } - - visited.add(n); - queue.add(n); - if (visited.size() >= maxLeaves) { - return visited; - } - } - } + visited.add(n); + queue.add(n); + if (visited.size() >= maxLeaves) { + return visited; } + } } - - return visited; - } - - private boolean isLeafMaterial(Material type) { - return type.name().endsWith("_LEAVES"); - } - - private int getMaxBlocks(int level) { - return Math.max(8, (int) Math.round(getConfig().maxBlocksBase + (getLevelPercent(level) * getConfig().maxBlocksFactor))); - } - - private long getMarkDurationMillis(int level) { - return (long) Math.max(1000, Math.round(getConfig().markDurationMillisBase + (getLevelPercent(level) * getConfig().markDurationMillisFactor))); + } } - private int getMaxLeaves(int level) { - return Math.max(0, (int) Math.round(getConfig().maxLeavesBase + (getLevelPercent(level) * getConfig().maxLeavesFactor))); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Mark a trunk, then break the marked log to fell connected wood.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBlocksBase = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBlocksFactor = 56; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double markDurationMillisBase = 6000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double markDurationMillisFactor = 9000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Extra Log for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerExtraLog = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxLeavesBase = 24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxLeavesFactor = 180; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Leaf Cleared for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerLeafCleared = 0.4; - } + return visited; + } + + private boolean isLeafMaterial(Material type) { + return type.name().endsWith("_LEAVES"); + } + + private int getMaxBlocks(int level) { + return Math.max(8, (int) Math.round(getConfig().maxBlocksBase + (getLevelPercent(level) * getConfig().maxBlocksFactor))); + } + + private long getMarkDurationMillis(int level) { + return (long) Math.max(1000, Math.round(getConfig().markDurationMillisBase + (getLevelPercent(level) * getConfig().markDurationMillisFactor))); + } + + private int getMaxLeaves(int level) { + return Math.max(0, (int) Math.round(getConfig().maxLeavesBase + (getLevelPercent(level) * getConfig().maxLeavesFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Mark a trunk, then break the marked log to fell connected wood.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBlocksBase = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBlocksFactor = 56; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double markDurationMillisBase = 6000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mark Duration Millis Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double markDurationMillisFactor = 9000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Extra Log for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerExtraLog = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Base for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxLeavesBase = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Leaves Factor for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxLeavesFactor = 180; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Leaf Cleared for the Axe Timber Mark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerLeafCleared = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index 758b11a7a..3256cdcfb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -28,11 +28,11 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -51,172 +51,172 @@ import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class AxeWoodVeinminer extends SimpleAdaptation { - public AxeWoodVeinminer() { - super("axe-wood-veinminer"); - registerConfiguration(AxeWoodVeinminer.Config.class); - setDescription(Localizer.dLocalize("axe.wood_miner.description")); - setDisplayName(Localizer.dLocalize("axe.wood_miner.name")); - setIcon(Material.DIAMOND_AXE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(5849); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_LOG) - .key("challenge_axe_wood_vein_2500") - .title(Localizer.dLocalize("advancement.challenge_axe_wood_vein_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_wood_vein_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_AXE) - .key("challenge_axe_wood_vein_cascade") - .title(Localizer.dLocalize("advancement.challenge_axe_wood_vein_cascade.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_wood_vein_cascade.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_axe_wood_vein_2500", "axe.wood-veinminer.logs-veinmined", 2500, 500); + public AxeWoodVeinminer() { + super("axe-wood-veinminer"); + registerConfiguration(AxeWoodVeinminer.Config.class); + setDescription(Localizer.dLocalize("axe.wood_miner.description")); + setDisplayName(Localizer.dLocalize("axe.wood_miner.name")); + setIcon(Material.DIAMOND_AXE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5849); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_LOG) + .key("challenge_axe_wood_vein_2500") + .title(Localizer.dLocalize("advancement.challenge_axe_wood_vein_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_wood_vein_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_AXE) + .key("challenge_axe_wood_vein_cascade") + .title(Localizer.dLocalize("advancement.challenge_axe_wood_vein_cascade.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_wood_vein_cascade.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_axe_wood_vein_2500", "axe.wood-veinminer.logs-veinmined", 2500, 500); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("axe.wood_miner.lore1")); + v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("axe.wood_miner.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("axe.wood_miner.lore3")); + } + + private int getRadius(int lvl) { + return lvl + getConfig().baseRange; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + if (VEIN_MINED.get(e.getBlock())) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("axe.wood_miner.lore1")); - v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("axe.wood_miner.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("axe.wood_miner.lore3")); + if (!p.isSneaking()) { + return; } - private int getRadius(int lvl) { - return lvl + getConfig().baseRange; + if (!isAxe(p.getInventory().getItemInMainHand())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { - if (VEIN_MINED.get(e.getBlock())) { - return; - } - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - - if (!p.isSneaking()) { - return; - } - - if (!isAxe(p.getInventory().getItemInMainHand())) { - return; - } - - if (!isLog(new ItemStack(e.getBlock().getType()))) { - return; - } - - VEIN_MINED.add(e.getBlock()); - Block block = e.getBlock(); - Set blockMap = new HashSet<>(); - int blockCount = 0; - int radius = getRadius(getLevel(p)); - int radiusSquared = radius * radius; - for (int i = 0; i < radius; i++) { - for (int x = -i; x <= i; x++) { - for (int y = -i; y <= i; y++) { - for (int z = -i; z <= i; z++) { - Block b = block.getRelative(x, y, z); - if (b.getType() == block.getType()) { - blockCount++; - if (blockCount > getConfig().maxBlocks) { - Adapt.verbose("Block: " + blockCount + " > " + getConfig().maxBlocks); - continue; - } - if (block.getLocation().distanceSquared(b.getLocation()) > radiusSquared) { - Adapt.verbose("Block: " + b.getLocation() + " is too far away from " + block.getLocation() + " (" + radius + ")"); - continue; - } - if (!canBlockBreak(p, b.getLocation())) { - Adapt.verbose("Player " + p.getName() + " doesn't have permission."); - continue; - } - blockMap.add(b); - } - } - } - } - } + if (!isLog(new ItemStack(e.getBlock().getType()))) { + return; + } - int logsVeinmined = blockMap.size(); - J.runEntity(p, () -> { - for (Block blocks : blockMap) { - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("axe-drop-to-inventory") : null; - VEIN_MINED.add(blocks); - if (adaptation != null && adaptation.getLevel() > 0) { - Collection items = blocks.getDrops(); - for (ItemStack item : items) { - safeGiveItem(p, item); - Adapt.verbose("Giving item: " + item); - } - blocks.setType(Material.AIR); - } else { - blocks.breakNaturally(p.getItemInUse()); - SoundPlayer spw = SoundPlayer.of(blocks.getWorld()); - spw.play(e.getBlock().getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.01f, 0.25f); - if (areParticlesEnabled()) { - blocks.getWorld().spawnParticle(Particle.ASH, blocks.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); - } - } - if (areParticlesEnabled()) { - this.vfxCuboidOutline(blocks, Particles.ENCHANTMENT_TABLE); - } - VEIN_MINED.remove(blocks); - } - VEIN_MINED.remove(block); - }); - if (logsVeinmined > 0) { - getPlayer(p).getData().addStat("axe.wood-veinminer.logs-veinmined", logsVeinmined); - if (logsVeinmined >= 15 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_wood_vein_cascade")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_wood_vein_cascade"); + VEIN_MINED.add(e.getBlock()); + Block block = e.getBlock(); + Set blockMap = new HashSet<>(); + int blockCount = 0; + int radius = getRadius(getLevel(p)); + int radiusSquared = radius * radius; + for (int i = 0; i < radius; i++) { + for (int x = -i; x <= i; x++) { + for (int y = -i; y <= i; y++) { + for (int z = -i; z <= i; z++) { + Block b = block.getRelative(x, y, z); + if (b.getType() == block.getType()) { + blockCount++; + if (blockCount > getConfig().maxBlocks) { + Adapt.verbose("Block: " + blockCount + " > " + getConfig().maxBlocks); + continue; + } + if (block.getLocation().distanceSquared(b.getLocation()) > radiusSquared) { + Adapt.verbose("Block: " + b.getLocation() + " is too far away from " + block.getLocation() + " (" + radius + ")"); + continue; + } + if (!canBlockBreak(p, b.getLocation())) { + Adapt.verbose("Player " + p.getName() + " doesn't have permission."); + continue; + } + blockMap.add(b); } + } } + } } - - @Override - public void onTick() { - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Break bulk wood at once while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Wood Veinminer adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxBlocks = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseRange = 3; + int logsVeinmined = blockMap.size(); + J.runEntity(p, () -> { + for (Block blocks : blockMap) { + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("axes"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("axe-drop-to-inventory") : null; + VEIN_MINED.add(blocks); + if (adaptation != null && adaptation.getLevel() > 0) { + Collection items = blocks.getDrops(); + for (ItemStack item : items) { + safeGiveItem(p, item); + Adapt.verbose("Giving item: " + item); + } + blocks.setType(Material.AIR); + } else { + blocks.breakNaturally(p.getItemInUse()); + SoundPlayer spw = SoundPlayer.of(blocks.getWorld()); + spw.play(e.getBlock().getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.01f, 0.25f); + if (areParticlesEnabled()) { + blocks.getWorld().spawnParticle(Particle.ASH, blocks.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); + } + } + if (areParticlesEnabled()) { + this.vfxCuboidOutline(blocks, Particles.ENCHANTMENT_TABLE); + } + VEIN_MINED.remove(blocks); + } + VEIN_MINED.remove(block); + }); + if (logsVeinmined > 0) { + getPlayer(p).getData().addStat("axe.wood-veinminer.logs-veinmined", logsVeinmined); + if (logsVeinmined >= 15 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_wood_vein_cascade")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_wood_vein_cascade"); + } } + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Break bulk wood at once while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Axe Wood Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blocks for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxBlocks = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Axe Wood Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseRange = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java index 441051f2d..274d472f4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBastionStance.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,175 +44,175 @@ import java.util.concurrent.ThreadLocalRandom; public class BlockingBastionStance extends SimpleAdaptation { - public BlockingBastionStance() { - super("blocking-bastion-stance"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.bastion_stance.description")); - setDisplayName(Localizer.dLocalize("blocking.bastion_stance.name")); - setIcon(Material.SHIELD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_bastion_500") - .title(Localizer.dLocalize("advancement.challenge_blocking_bastion_500.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_bastion_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_bastion_500", "blocking.bastion-stance.projectiles-softened", 500, 500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_bastion_10") - .title(Localizer.dLocalize("advancement.challenge_blocking_bastion_10.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_bastion_10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getKnockbackReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getProjectileReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore2")); - v.addLore(C.GREEN + "+ " + Form.pc(getProjectileNegateChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore3")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getEntity() instanceof Player defender) || !isBastionStance(defender)) { - return; - } - - if (!(e.getDamager() instanceof Projectile)) { - return; - } - - int level = getActiveLevel(defender); - if (level <= 0) { - return; - } - - // Track session counter for special achievement - int sessionCount = getStorageInt(defender, "bastionSessionCount", 0) + 1; - setStorage(defender, "bastionSessionCount", sessionCount); - if (sessionCount >= 10 && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_bastion_10")) { - getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_bastion_10"); - } - - if (ThreadLocalRandom.current().nextDouble() <= getProjectileNegateChance(level)) { - e.setCancelled(true); - SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 0.9f); - xp(defender, getConfig().xpOnNegate); - getPlayer(defender).getData().addStat("blocking.bastion-stance.projectiles-softened", 1); - return; - } - - e.setDamage(Math.max(0, e.getDamage() * (1D - getProjectileReduction(level)))); - SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.75f, 0.75f); - xp(defender, e.getDamage() * getConfig().xpPerMitigatedDamage); - getPlayer(defender).getData().addStat("blocking.bastion-stance.projectiles-softened", 1); - } - - @EventHandler(priority = EventPriority.HIGH) - public void on(PlayerVelocityEvent e) { - Player p = e.getPlayer(); - int level = getActiveLevel(p); - if (!isBastionStance(p, level)) { - return; - } - - double factor = 1D - getKnockbackReduction(level); - Vector v = e.getVelocity(); - e.setVelocity(new Vector(v.getX() * factor, v.getY(), v.getZ() * factor)); - } - - private boolean isBastionStance(Player p) { - return isBastionStance(p, getActiveLevel(p)); - } - - private boolean isBastionStance(Player p, int level) { - return level > 0 && p.isBlocking() && p.isSneaking() && hasShield(p); - } - - private boolean hasShield(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); - } - - private double getKnockbackReduction(int level) { - return Math.min(getConfig().maxKnockbackReduction, getConfig().knockbackReductionBase + (getLevelPercent(level) * getConfig().knockbackReductionFactor)); + public BlockingBastionStance() { + super("blocking-bastion-stance"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.bastion_stance.description")); + setDisplayName(Localizer.dLocalize("blocking.bastion_stance.name")); + setIcon(Material.SHIELD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_bastion_500") + .title(Localizer.dLocalize("advancement.challenge_blocking_bastion_500.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_bastion_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_bastion_500", "blocking.bastion-stance.projectiles-softened", 500, 500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_bastion_10") + .title(Localizer.dLocalize("advancement.challenge_blocking_bastion_10.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_bastion_10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getKnockbackReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getProjectileReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getProjectileNegateChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bastion_stance.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player defender) || !isBastionStance(defender)) { + return; } - private double getProjectileReduction(int level) { - return Math.min(getConfig().maxProjectileReduction, getConfig().projectileReductionBase + (getLevelPercent(level) * getConfig().projectileReductionFactor)); + if (!(e.getDamager() instanceof Projectile)) { + return; } - private double getProjectileNegateChance(int level) { - return Math.min(getConfig().maxProjectileNegateChance, getConfig().projectileNegateChanceBase + (getLevelPercent(level) * getConfig().projectileNegateChanceFactor)); + int level = getActiveLevel(defender); + if (level <= 0) { + return; } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - int level = getActiveLevel(p); - if (level > 0 && !isBastionStance(p, level)) { - setStorage(p, "bastionSessionCount", 0); - } - } + // Track session counter for special achievement + int sessionCount = getStorageInt(defender, "bastionSessionCount", 0) + 1; + setStorage(defender, "bastionSessionCount", sessionCount); + if (sessionCount >= 10 && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_bastion_10")) { + getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_bastion_10"); } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (ThreadLocalRandom.current().nextDouble() <= getProjectileNegateChance(level)) { + e.setCancelled(true); + SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 0.9f); + xp(defender, getConfig().xpOnNegate); + getPlayer(defender).getData().addStat("blocking.bastion-stance.projectiles-softened", 1); + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + e.setDamage(Math.max(0, e.getDamage() * (1D - getProjectileReduction(level)))); + SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.75f, 0.75f); + xp(defender, e.getDamage() * getConfig().xpPerMitigatedDamage); + getPlayer(defender).getData().addStat("blocking.bastion-stance.projectiles-softened", 1); + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(PlayerVelocityEvent e) { + Player p = e.getPlayer(); + int level = getActiveLevel(p); + if (!isBastionStance(p, level)) { + return; } - @NoArgsConstructor - @ConfigDescription("Sneak-block with a shield to brace against knockback and soften projectiles.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.68; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackReductionBase = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackReductionFactor = 0.52; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Knockback Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxKnockbackReduction = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double projectileReductionBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double projectileReductionFactor = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxProjectileReduction = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double projectileNegateChanceBase = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double projectileNegateChanceFactor = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Negate Chance for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxProjectileNegateChance = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerMitigatedDamage = 2.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Negate for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnNegate = 8.0; + double factor = 1D - getKnockbackReduction(level); + Vector v = e.getVelocity(); + e.setVelocity(new Vector(v.getX() * factor, v.getY(), v.getZ() * factor)); + } + + private boolean isBastionStance(Player p) { + return isBastionStance(p, getActiveLevel(p)); + } + + private boolean isBastionStance(Player p, int level) { + return level > 0 && p.isBlocking() && p.isSneaking() && hasShield(p); + } + + private boolean hasShield(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); + } + + private double getKnockbackReduction(int level) { + return Math.min(getConfig().maxKnockbackReduction, getConfig().knockbackReductionBase + (getLevelPercent(level) * getConfig().knockbackReductionFactor)); + } + + private double getProjectileReduction(int level) { + return Math.min(getConfig().maxProjectileReduction, getConfig().projectileReductionBase + (getLevelPercent(level) * getConfig().projectileReductionFactor)); + } + + private double getProjectileNegateChance(int level) { + return Math.min(getConfig().maxProjectileNegateChance, getConfig().projectileNegateChanceBase + (getLevelPercent(level) * getConfig().projectileNegateChanceFactor)); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + int level = getActiveLevel(p); + if (level > 0 && !isBastionStance(p, level)) { + setStorage(p, "bastionSessionCount", 0); + } } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-block with a shield to brace against knockback and soften projectiles.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.68; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackReductionBase = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackReductionFactor = 0.52; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Knockback Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxKnockbackReduction = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double projectileReductionBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Reduction Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double projectileReductionFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Reduction for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxProjectileReduction = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Base for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double projectileNegateChanceBase = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Projectile Negate Chance Factor for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double projectileNegateChanceFactor = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Projectile Negate Chance for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxProjectileNegateChance = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMitigatedDamage = 2.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Negate for the Blocking Bastion Stance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnNegate = 8.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java index 74b34453c..d1d60a8df 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingBulwarkBash.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -48,239 +48,239 @@ import java.util.UUID; public class BlockingBulwarkBash extends SimpleAdaptation { - private final Map lastSprintMillis = new java.util.concurrent.ConcurrentHashMap<>(); - - public BlockingBulwarkBash() { - super("blocking-bulwark-bash"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.bulwark_bash.description")); - setDisplayName(Localizer.dLocalize("blocking.bulwark_bash.name")); - setIcon(Material.BELL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_bulwark_500") - .title(Localizer.dLocalize("advancement.challenge_blocking_bulwark_500.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_bulwark_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_bulwark_500", "blocking.bulwark-bash.mobs-bashed", 500, 500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_bulwark_4") - .title(Localizer.dLocalize("advancement.challenge_blocking_bulwark_4.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_bulwark_4.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + private final Map lastSprintMillis = new java.util.concurrent.ConcurrentHashMap<>(); + + public BlockingBulwarkBash() { + super("blocking-bulwark-bash"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.bulwark_bash.description")); + setDisplayName(Localizer.dLocalize("blocking.bulwark_bash.name")); + setIcon(Material.BELL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_bulwark_500") + .title(Localizer.dLocalize("advancement.challenge_blocking_bulwark_500.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_bulwark_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_bulwark_500", "blocking.bulwark-bash.mobs-bashed", 500, 500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_bulwark_4") + .title(Localizer.dLocalize("advancement.challenge_blocking_bulwark_4.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_bulwark_4.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRange(level)) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerToggleSprintEvent e) { + if (e.isSprinting()) { + lastSprintMillis.put(e.getPlayer().getUniqueId(), System.currentTimeMillis()); } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRange(level)) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("blocking.bulwark_bash.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + lastSprintMillis.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e); + if (combat == null) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerToggleSprintEvent e) { - if (e.isSprinting()) { - lastSprintMillis.put(e.getPlayer().getUniqueId(), System.currentTimeMillis()); - } + Player p = combat.attacker(); + LivingEntity target = combat.target(); + if (p.getInventory().getItemInOffHand().getType() != Material.SHIELD || p.hasCooldown(Material.SHIELD)) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - lastSprintMillis.remove(e.getPlayer().getUniqueId()); + if (!isJumpCrit(p) || !wasRecentlySprinting(p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - LivingEntity target = combat.target(); - if (p.getInventory().getItemInOffHand().getType() != Material.SHIELD || p.hasCooldown(Material.SHIELD)) { - return; - } - - if (!isJumpCrit(p) || !wasRecentlySprinting(p)) { - return; - } - - int level = combat.level(); - int affected = 0; - double radius = getRange(level); - for (org.bukkit.entity.Entity nearby : target.getWorld().getNearbyEntities(target.getLocation(), radius, radius, radius)) { - if (!(nearby instanceof LivingEntity hit) || hit == p) { - continue; - } - - if (!canDamageTarget(p, hit)) { - continue; - } - - applyImpact(p, hit, level); - affected++; - } - - if (affected <= 0) { - return; - } - - e.setDamage(e.getDamage() + getBaseDamage(level)); - p.setCooldown(Material.SHIELD, getCooldownTicks(level)); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.SWEEP_ATTACK, p.getLocation().add(0, 1, 0), 1, 0, 0, 0, 0); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().add(0, 0.3, 0), 18, 0.35, 0.1, 0.35, 0.06); - } - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 0.85f); - sp.play(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.9f, 0.7f); - xp(p, getConfig().xpPerTargetHit * affected); - getPlayer(p).getData().addStat("blocking.bulwark-bash.mobs-bashed", affected); - - // Special achievement: hit 4+ enemies in single bash - if (affected >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_blocking_bulwark_4")) { - getPlayer(p).getAdvancementHandler().grant("challenge_blocking_bulwark_4"); - } - } + int level = combat.level(); + int affected = 0; + double radius = getRange(level); + for (org.bukkit.entity.Entity nearby : target.getWorld().getNearbyEntities(target.getLocation(), radius, radius, radius)) { + if (!(nearby instanceof LivingEntity hit) || hit == p) { + continue; + } - private void applyImpact(Player p, LivingEntity target, int level) { - Vector kb = target.getLocation().toVector().subtract(p.getLocation().toVector()).setY(0); - if (kb.lengthSquared() <= 0.0001) { - kb = p.getLocation().getDirection().setY(0); - } - if (kb.lengthSquared() <= 0.0001) { - return; - } - - kb.normalize(); - target.setVelocity(target.getVelocity().multiply(0.25).add(kb.multiply(getKnockback(level)).setY(getUpwardKnockback(level)))); - target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getStunTicks(level), getStunAmplifier(level), false, false, true), true); - } + if (!canDamageTarget(p, hit)) { + continue; + } - private boolean wasRecentlySprinting(Player p) { - long last = lastSprintMillis.getOrDefault(p.getUniqueId(), 0L); - return p.isSprinting() || (System.currentTimeMillis() - last) <= getConfig().recentSprintWindowMillis; + applyImpact(p, hit, level); + affected++; } - private boolean isJumpCrit(Player p) { - return p.getFallDistance() > getConfig().minFallDistanceForCrit - && !p.isOnGround() - && !p.isInWater() - && !p.isInsideVehicle() - && !p.isClimbing(); + if (affected <= 0) { + return; } - private double getRange(int level) { - return getConfig().rangeBase + (getLevelPercent(level) * getConfig().rangeFactor); + e.setDamage(e.getDamage() + getBaseDamage(level)); + p.setCooldown(Material.SHIELD, getCooldownTicks(level)); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SWEEP_ATTACK, p.getLocation().add(0, 1, 0), 1, 0, 0, 0, 0); } - - private double getDamageBonus(int level) { - return getConfig().damageBonusBase + (getLevelPercent(level) * getConfig().damageBonusFactor); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().add(0, 0.3, 0), 18, 0.35, 0.1, 0.35, 0.06); } - - private double getBaseDamage(int level) { - return getConfig().baseDamage + getDamageBonus(level); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 0.85f); + sp.play(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 0.9f, 0.7f); + xp(p, getConfig().xpPerTargetHit * affected); + getPlayer(p).getData().addStat("blocking.bulwark-bash.mobs-bashed", affected); + + // Special achievement: hit 4+ enemies in single bash + if (affected >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_blocking_bulwark_4")) { + getPlayer(p).getAdvancementHandler().grant("challenge_blocking_bulwark_4"); } + } - private double getKnockback(int level) { - return getConfig().knockbackBase + (getLevelPercent(level) * getConfig().knockbackFactor); + private void applyImpact(Player p, LivingEntity target, int level) { + Vector kb = target.getLocation().toVector().subtract(p.getLocation().toVector()).setY(0); + if (kb.lengthSquared() <= 0.0001) { + kb = p.getLocation().getDirection().setY(0); } - - private double getUpwardKnockback(int level) { - return getConfig().upwardKnockbackBase + (getLevelPercent(level) * getConfig().upwardKnockbackFactor); + if (kb.lengthSquared() <= 0.0001) { + return; } - private int getStunTicks(int level) { - return Math.max(10, (int) Math.round(getConfig().stunTicksBase + (getLevelPercent(level) * getConfig().stunTicksFactor))); - } - - private int getStunAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().stunAmplifierBase + (getLevelPercent(level) * getConfig().stunAmplifierFactor))); - } - - private int getCooldownTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); - } - - @Override - public void onTick() { - long cutoff = System.currentTimeMillis() - Math.max(1000L, getConfig().recentSprintWindowMillis * 3L); - lastSprintMillis.entrySet().removeIf(i -> i.getValue() < cutoff); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sprint, jump, and land a shielded crit to unleash a bash shockwave.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDamage = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusBase = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusFactor = 2.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rangeBase = 2.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rangeFactor = 1.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackBase = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double upwardKnockbackBase = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double upwardKnockbackFactor = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stunTicksBase = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stunTicksFactor = 24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stunAmplifierBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stunAmplifierFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 220; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 120; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - float minFallDistanceForCrit = 0.08f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recent Sprint Window Millis for the Blocking Bulwark Bash adaptation.", impact = "Allows crit bash to trigger shortly after sprint momentum, even if sprint toggles off mid-air.") - long recentSprintWindowMillis = 900L; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerTargetHit = 8; - } + kb.normalize(); + target.setVelocity(target.getVelocity().multiply(0.25).add(kb.multiply(getKnockback(level)).setY(getUpwardKnockback(level)))); + target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getStunTicks(level), getStunAmplifier(level), false, false, true), true); + } + + private boolean wasRecentlySprinting(Player p) { + long last = lastSprintMillis.getOrDefault(p.getUniqueId(), 0L); + return p.isSprinting() || (System.currentTimeMillis() - last) <= getConfig().recentSprintWindowMillis; + } + + private boolean isJumpCrit(Player p) { + return p.getFallDistance() > getConfig().minFallDistanceForCrit + && !p.isOnGround() + && !p.isInWater() + && !p.isInsideVehicle() + && !p.isClimbing(); + } + + private double getRange(int level) { + return getConfig().rangeBase + (getLevelPercent(level) * getConfig().rangeFactor); + } + + private double getDamageBonus(int level) { + return getConfig().damageBonusBase + (getLevelPercent(level) * getConfig().damageBonusFactor); + } + + private double getBaseDamage(int level) { + return getConfig().baseDamage + getDamageBonus(level); + } + + private double getKnockback(int level) { + return getConfig().knockbackBase + (getLevelPercent(level) * getConfig().knockbackFactor); + } + + private double getUpwardKnockback(int level) { + return getConfig().upwardKnockbackBase + (getLevelPercent(level) * getConfig().upwardKnockbackFactor); + } + + private int getStunTicks(int level) { + return Math.max(10, (int) Math.round(getConfig().stunTicksBase + (getLevelPercent(level) * getConfig().stunTicksFactor))); + } + + private int getStunAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().stunAmplifierBase + (getLevelPercent(level) * getConfig().stunAmplifierFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + long cutoff = System.currentTimeMillis() - Math.max(1000L, getConfig().recentSprintWindowMillis * 3L); + lastSprintMillis.entrySet().removeIf(i -> i.getValue() < cutoff); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sprint, jump, and land a shielded crit to unleash a bash shockwave.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDamage = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusBase = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusFactor = 2.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rangeBase = 2.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rangeFactor = 1.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackBase = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double upwardKnockbackBase = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Upward Knockback Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double upwardKnockbackFactor = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stunTicksBase = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stunTicksFactor = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stunAmplifierBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stun Amplifier Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stunAmplifierFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 220; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + float minFallDistanceForCrit = 0.08f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recent Sprint Window Millis for the Blocking Bulwark Bash adaptation.", impact = "Allows crit bash to trigger shortly after sprint momentum, even if sprint toggles off mid-air.") + long recentSprintWindowMillis = 900L; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Blocking Bulwark Bash adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTargetHit = 8; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java index 8633ece01..2b70aa66b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingChainArmorer.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -39,103 +39,103 @@ public class BlockingChainArmorer extends SimpleAdaptation { - public BlockingChainArmorer() { - super("blocking-chainarmorer"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.chain_armorer.description")); - setDisplayName(Localizer.dLocalize("blocking.chain_armorer.name")); - setIcon(Material.CHAINMAIL_CHESTPLATE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17774); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-chainarmorer-boots") - .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) - .shapes(List.of( - "I I", - "I I")) - .result(new ItemStack(Material.CHAINMAIL_BOOTS, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-chainarmorer-leggings") - .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) - .shapes(List.of( - "III", - "I I", - "I I")) - .result(new ItemStack(Material.CHAINMAIL_LEGGINGS, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-chainarmorer-chestplate") - .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) - .shapes(List.of( - "I I", - "III", - "III")) - .result(new ItemStack(Material.CHAINMAIL_CHESTPLATE, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-chainarmorer-helmet") - .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) - .shapes(List.of( - "III", - "I I")) - .result(new ItemStack(Material.CHAINMAIL_HELMET, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHAINMAIL_CHESTPLATE) - .key("challenge_blocking_chain_25") - .title(Localizer.dLocalize("advancement.challenge_blocking_chain_25.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_chain_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_chain_25", "blocking.chain-armorer.pieces-crafted", 25, 400); - } + public BlockingChainArmorer() { + super("blocking-chainarmorer"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.chain_armorer.description")); + setDisplayName(Localizer.dLocalize("blocking.chain_armorer.name")); + setIcon(Material.CHAINMAIL_CHESTPLATE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17774); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-chainarmorer-boots") + .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) + .shapes(List.of( + "I I", + "I I")) + .result(new ItemStack(Material.CHAINMAIL_BOOTS, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-chainarmorer-leggings") + .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) + .shapes(List.of( + "III", + "I I", + "I I")) + .result(new ItemStack(Material.CHAINMAIL_LEGGINGS, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-chainarmorer-chestplate") + .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) + .shapes(List.of( + "I I", + "III", + "III")) + .result(new ItemStack(Material.CHAINMAIL_CHESTPLATE, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-chainarmorer-helmet") + .ingredient(new MaterialChar('I', Material.IRON_NUGGET)) + .shapes(List.of( + "III", + "I I")) + .result(new ItemStack(Material.CHAINMAIL_HELMET, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHAINMAIL_CHESTPLATE) + .key("challenge_blocking_chain_25") + .title(Localizer.dLocalize("advancement.challenge_blocking_chain_25.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_chain_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_chain_25", "blocking.chain-armorer.pieces-crafted", 25, 400); + } - @EventHandler - public void on(CraftItemEvent e) { - if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { - getPlayer(p).getData().addStat("blocking.chain-armorer.pieces-crafted", 1); - } + @EventHandler + public void on(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + getPlayer(p).getData().addStat("blocking.chain-armorer.pieces-crafted", 1); } + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.chain_armorer.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.chain_armorer.lore1")); + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft Chainmail Armor using iron nuggets.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0; - } + @NoArgsConstructor + @ConfigDescription("Craft Chainmail Armor using iron nuggets.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java index 5a5beb130..3aad296a5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingCounterGuard.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -41,157 +41,157 @@ import org.bukkit.inventory.ItemStack; public class BlockingCounterGuard extends SimpleAdaptation { - public BlockingCounterGuard() { - super("blocking-counter-guard"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.counter_guard.description")); - setDisplayName(Localizer.dLocalize("blocking.counter_guard.name")); - setIcon(Material.IRON_BARS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_counter_500") - .title(Localizer.dLocalize("advancement.challenge_blocking_counter_500.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_counter_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_counter_500", "blocking.counter-guard.damage-reflected", 500, 500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_counter_max") - .title(Localizer.dLocalize("advancement.challenge_blocking_counter_max.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_counter_max.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + public BlockingCounterGuard() { + super("blocking-counter-guard"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.counter_guard.description")); + setDisplayName(Localizer.dLocalize("blocking.counter_guard.name")); + setIcon(Material.IRON_BARS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_counter_500") + .title(Localizer.dLocalize("advancement.challenge_blocking_counter_500.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_counter_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_counter_500", "blocking.counter-guard.damage-reflected", 500, 500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_counter_max") + .title(Localizer.dLocalize("advancement.challenge_blocking_counter_max.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_counter_max.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxStacks(level) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getReflectChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore2")); + v.addLore(C.GREEN + "+ " + Form.f(getReflectDamage(level)) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player defender)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxStacks(level) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getReflectChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore2")); - v.addLore(C.GREEN + "+ " + Form.f(getReflectDamage(level)) + C.GRAY + " " + Localizer.dLocalize("blocking.counter_guard.lore3")); + if (!hasActiveAdaptation(defender) || !hasShield(defender)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getEntity() instanceof Player defender)) { - return; - } - - if (!hasActiveAdaptation(defender) || !hasShield(defender)) { - return; - } - - int level = getActiveLevel(defender); - int stacks = getStorageInt(defender, "counterStacks", 0); - - if (defender.isBlocking()) { - stacks = Math.min(getMaxStacks(level), stacks + 1); - setStorage(defender, "counterStacks", stacks); - } - - if (stacks <= 0 || !M.r(getReflectChance(level))) { - return; - } - - Entity source = e.getDamager(); - if (source instanceof Projectile projectile && projectile.getShooter() instanceof Entity shooter) { - source = shooter; - } - - if (!(source instanceof LivingEntity attacker)) { - return; - } - - if (!canDamageTarget(defender, attacker)) { - return; - } - - // Special achievement: reach max stacks and release - if (stacks >= getMaxStacks(level) && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_counter_max")) { - getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_counter_max"); - } - - double reflected = getReflectDamage(level) + (stacks * getConfig().damagePerStack); - attacker.damage(reflected, defender); - setStorage(defender, "counterStacks", Math.max(0, stacks - getConfig().stackCostOnReflect)); - xp(defender, reflected * getConfig().xpPerReflectedDamage); - getPlayer(defender).getData().addStat("blocking.counter-guard.damage-reflected", reflected); - } + int level = getActiveLevel(defender); + int stacks = getStorageInt(defender, "counterStacks", 0); - private boolean hasShield(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); + if (defender.isBlocking()) { + stacks = Math.min(getMaxStacks(level), stacks + 1); + setStorage(defender, "counterStacks", stacks); } - private double getReflectChance(int level) { - return Math.min(getConfig().maxReflectChance, getConfig().reflectChanceBase + (getLevelPercent(level) * getConfig().reflectChanceFactor)); + if (stacks <= 0 || !M.r(getReflectChance(level))) { + return; } - private int getMaxStacks(int level) { - return Math.max(1, (int) Math.round(getConfig().baseStacks + (getLevelPercent(level) * getConfig().stackFactor))); + Entity source = e.getDamager(); + if (source instanceof Projectile projectile && projectile.getShooter() instanceof Entity shooter) { + source = shooter; } - private double getReflectDamage(int level) { - return getConfig().baseReflectDamage + (getLevelPercent(level) * getConfig().reflectDamageFactor); + if (!(source instanceof LivingEntity attacker)) { + return; } - @Override - public void onTick() { - + if (!canDamageTarget(defender, attacker)) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + // Special achievement: reach max stacks and release + if (stacks >= getMaxStacks(level) && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_counter_max")) { + getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_counter_max"); } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Blocking builds retaliation stacks that reflect damage back to attackers.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Stacks for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseStacks = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stackFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectChanceBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectChanceFactor = 0.27; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxReflectChance = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Reflect Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseReflectDamage = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Damage Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectDamageFactor = 3.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerStack = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Cost On Reflect for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int stackCostOnReflect = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reflected Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerReflectedDamage = 5.0; - } + double reflected = getReflectDamage(level) + (stacks * getConfig().damagePerStack); + attacker.damage(reflected, defender); + setStorage(defender, "counterStacks", Math.max(0, stacks - getConfig().stackCostOnReflect)); + xp(defender, reflected * getConfig().xpPerReflectedDamage); + getPlayer(defender).getData().addStat("blocking.counter-guard.damage-reflected", reflected); + } + + private boolean hasShield(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); + } + + private double getReflectChance(int level) { + return Math.min(getConfig().maxReflectChance, getConfig().reflectChanceBase + (getLevelPercent(level) * getConfig().reflectChanceFactor)); + } + + private int getMaxStacks(int level) { + return Math.max(1, (int) Math.round(getConfig().baseStacks + (getLevelPercent(level) * getConfig().stackFactor))); + } + + private double getReflectDamage(int level) { + return getConfig().baseReflectDamage + (getLevelPercent(level) * getConfig().reflectDamageFactor); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Blocking builds retaliation stacks that reflect damage back to attackers.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Stacks for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseStacks = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stackFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectChanceBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectChanceFactor = 0.27; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReflectChance = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Reflect Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseReflectDamage = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Damage Factor for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectDamageFactor = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerStack = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Cost On Reflect for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int stackCostOnReflect = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reflected Damage for the Blocking Counter Guard adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerReflectedDamage = 5.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java index 3f1a1e36a..4cf6f64f1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingHorseArmorer.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -39,113 +39,113 @@ public class BlockingHorseArmorer extends SimpleAdaptation { - public BlockingHorseArmorer() { - super("blocking-horsearmorer"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.horse_armorer.description")); - setDisplayName(Localizer.dLocalize("blocking.horse_armorer.name")); - setIcon(Material.GOLDEN_HORSE_ARMOR); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17774); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-horsearmorerleather") - .ingredient(new MaterialChar('I', Material.LEATHER)) - .ingredient(new MaterialChar('U', Material.SADDLE)) - .shapes(List.of( - "III", - "IUI", - "III")) - .result(new ItemStack(Material.LEATHER_HORSE_ARMOR, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-horsearmoreriron") - .ingredient(new MaterialChar('I', Material.IRON_INGOT)) - .ingredient(new MaterialChar('U', Material.SADDLE)) - .shapes(List.of( - "III", - "IUI", - "III")) - .result(new ItemStack(Material.IRON_HORSE_ARMOR, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-horsearmorergold") - .ingredient(new MaterialChar('I', Material.GOLD_INGOT)) - .ingredient(new MaterialChar('U', Material.SADDLE)) - .shapes(List.of( - "III", - "IUI", - "III")) - .result(new ItemStack(Material.GOLDEN_HORSE_ARMOR, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-horsearmorerdiamond") - .ingredient(new MaterialChar('I', Material.DIAMOND)) - .ingredient(new MaterialChar('U', Material.SADDLE)) - .shapes(List.of( - "III", - "IUI", - "III")) - .result(new ItemStack(Material.DIAMOND_HORSE_ARMOR, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_HORSE_ARMOR) - .key("challenge_blocking_horse_armor_10") - .title(Localizer.dLocalize("advancement.challenge_blocking_horse_armor_10.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_horse_armor_10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_horse_armor_10", "blocking.horse-armorer.armor-crafted", 10, 400); - } + public BlockingHorseArmorer() { + super("blocking-horsearmorer"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.horse_armorer.description")); + setDisplayName(Localizer.dLocalize("blocking.horse_armorer.name")); + setIcon(Material.GOLDEN_HORSE_ARMOR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17774); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-horsearmorerleather") + .ingredient(new MaterialChar('I', Material.LEATHER)) + .ingredient(new MaterialChar('U', Material.SADDLE)) + .shapes(List.of( + "III", + "IUI", + "III")) + .result(new ItemStack(Material.LEATHER_HORSE_ARMOR, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-horsearmoreriron") + .ingredient(new MaterialChar('I', Material.IRON_INGOT)) + .ingredient(new MaterialChar('U', Material.SADDLE)) + .shapes(List.of( + "III", + "IUI", + "III")) + .result(new ItemStack(Material.IRON_HORSE_ARMOR, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-horsearmorergold") + .ingredient(new MaterialChar('I', Material.GOLD_INGOT)) + .ingredient(new MaterialChar('U', Material.SADDLE)) + .shapes(List.of( + "III", + "IUI", + "III")) + .result(new ItemStack(Material.GOLDEN_HORSE_ARMOR, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-horsearmorerdiamond") + .ingredient(new MaterialChar('I', Material.DIAMOND)) + .ingredient(new MaterialChar('U', Material.SADDLE)) + .shapes(List.of( + "III", + "IUI", + "III")) + .result(new ItemStack(Material.DIAMOND_HORSE_ARMOR, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_HORSE_ARMOR) + .key("challenge_blocking_horse_armor_10") + .title(Localizer.dLocalize("advancement.challenge_blocking_horse_armor_10.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_horse_armor_10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_horse_armor_10", "blocking.horse-armorer.armor-crafted", 10, 400); + } - @EventHandler - public void on(CraftItemEvent e) { - if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { - getPlayer(p).getData().addStat("blocking.horse-armorer.armor-crafted", 1); - } + @EventHandler + public void on(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + getPlayer(p).getData().addStat("blocking.horse-armorer.armor-crafted", 1); } + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.horse_armorer.lore1")); - v.addLore("XXX"); - v.addLore("XSX"); - v.addLore("XXX"); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.horse_armorer.lore1")); + v.addLore("XXX"); + v.addLore("XSX"); + v.addLore("XXX"); - } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft Horse Armor by surrounding a saddle with material.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0; - } + @NoArgsConstructor + @ConfigDescription("Craft Horse Armor by surrounding a saddle with material.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java index a15ef7158..9de5de2ef 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMirrorBlock.java @@ -26,11 +26,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -48,219 +48,219 @@ import java.util.concurrent.ThreadLocalRandom; public class BlockingMirrorBlock extends SimpleAdaptation { - private static final String REFLECTED_META = "adapt-mirror-reflected"; - private static final String DAMAGE_FACTOR_META = "adapt-mirror-damage-factor"; - - public BlockingMirrorBlock() { - super("blocking-mirror-block"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.mirror_block.description")); - setDisplayName(Localizer.dLocalize("blocking.mirror_block.name")); - setIcon(Material.LIGHT_WEIGHTED_PRESSURE_PLATE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_mirror_100") - .title(Localizer.dLocalize("advancement.challenge_blocking_mirror_100.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_mirror_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_mirror_100", "blocking.mirror-block.projectiles-reflected", 100, 500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_blocking_mirror_3in5") - .title(Localizer.dLocalize("advancement.challenge_blocking_mirror_3in5.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_mirror_3in5.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getReflectChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getReflectedDamageFactor(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getReflectCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore3")); + private static final String REFLECTED_META = "adapt-mirror-reflected"; + private static final String DAMAGE_FACTOR_META = "adapt-mirror-damage-factor"; + + public BlockingMirrorBlock() { + super("blocking-mirror-block"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.mirror_block.description")); + setDisplayName(Localizer.dLocalize("blocking.mirror_block.name")); + setIcon(Material.LIGHT_WEIGHTED_PRESSURE_PLATE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_mirror_100") + .title(Localizer.dLocalize("advancement.challenge_blocking_mirror_100.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_mirror_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_mirror_100", "blocking.mirror-block.projectiles-reflected", 100, 500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_blocking_mirror_3in5") + .title(Localizer.dLocalize("advancement.challenge_blocking_mirror_3in5.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_mirror_3in5.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getReflectChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getReflectedDamageFactor(level), 0) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getReflectCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("blocking.mirror_block.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Projectile projectile)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Projectile projectile)) { - return; - } - - applyReflectedDamageModifier(e, projectile); - - if (!(e.getEntity() instanceof Player defender) || !isMirrorReady(defender) || projectile.hasMetadata(REFLECTED_META)) { - return; - } - - int level = getActiveLevel(defender); - long now = System.currentTimeMillis(); - long next = getStorageLong(defender, "mirrorBlockNext", 0L); - if (next > now) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getReflectChance(level)) { - return; - } - - e.setCancelled(true); - reflectProjectile(defender, projectile, level); - setStorage(defender, "mirrorBlockNext", now + getReflectCooldownMillis(level)); - - SoundPlayer sp = SoundPlayer.of(defender.getWorld()); - sp.play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 1.35f); - sp.play(defender.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_HIT, 0.8f, 0.8f); - if (areParticlesEnabled()) { - defender.spawnParticle(Particle.CRIT, defender.getLocation().add(0, 1, 0), 20, 0.35, 0.3, 0.35, 0.08); - } - xp(defender, getConfig().xpOnReflect); - getPlayer(defender).getData().addStat("blocking.mirror-block.projectiles-reflected", 1); - - // Special achievement: reflect 3 projectiles within 5 seconds - long windowStart = getStorageLong(defender, "mirrorWindowStart", 0L); - int windowCount = getStorageInt(defender, "mirrorWindowCount", 0); - if (now - windowStart > 5000L) { - windowStart = now; - windowCount = 1; - } else { - windowCount++; - } - setStorage(defender, "mirrorWindowStart", windowStart); - setStorage(defender, "mirrorWindowCount", windowCount); - if (windowCount >= 3 && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_mirror_3in5")) { - getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_mirror_3in5"); - } - } - - private void applyReflectedDamageModifier(EntityDamageByEntityEvent e, Projectile projectile) { - if (!(projectile.getShooter() instanceof Player shooter) || !projectile.hasMetadata(DAMAGE_FACTOR_META)) { - return; - } - - if (!canDamageTarget(shooter, e.getEntity())) { - return; - } + applyReflectedDamageModifier(e, projectile); - double factor = getMetadataDouble(projectile, DAMAGE_FACTOR_META, 1D); - e.setDamage(e.getDamage() * factor); + if (!(e.getEntity() instanceof Player defender) || !isMirrorReady(defender) || projectile.hasMetadata(REFLECTED_META)) { + return; } - private void reflectProjectile(Player defender, Projectile projectile, int level) { - Vector incoming = projectile.getVelocity().clone(); - Vector reflected = incoming.multiply(-Math.max(0.01, getReflectVelocityFactor(level))); - if (reflected.lengthSquared() < getConfig().minReflectedVelocitySquared) { - reflected = defender.getEyeLocation().getDirection().normalize().multiply(getConfig().fallbackReflectedSpeed); - } - - J.teleport(projectile, defender.getEyeLocation().add(defender.getEyeLocation().getDirection().multiply(0.55))); - projectile.setShooter(defender); - projectile.setVelocity(reflected); - projectile.setMetadata(REFLECTED_META, new FixedMetadataValue(Adapt.instance, true)); - projectile.setMetadata(DAMAGE_FACTOR_META, new FixedMetadataValue(Adapt.instance, getReflectedDamageFactor(level))); + int level = getActiveLevel(defender); + long now = System.currentTimeMillis(); + long next = getStorageLong(defender, "mirrorBlockNext", 0L); + if (next > now) { + return; } - private boolean isMirrorReady(Player p) { - return hasActiveAdaptation(p) && p.isBlocking() && hasShield(p); + if (ThreadLocalRandom.current().nextDouble() > getReflectChance(level)) { + return; } - private boolean hasShield(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); - } - - private double getMetadataDouble(Projectile projectile, String key, double fallback) { - for (MetadataValue value : projectile.getMetadata(key)) { - if (value.getOwningPlugin() == Adapt.instance) { - return value.asDouble(); - } - } + e.setCancelled(true); + reflectProjectile(defender, projectile, level); + setStorage(defender, "mirrorBlockNext", now + getReflectCooldownMillis(level)); - return fallback; + SoundPlayer sp = SoundPlayer.of(defender.getWorld()); + sp.play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1f, 1.35f); + sp.play(defender.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_HIT, 0.8f, 0.8f); + if (areParticlesEnabled()) { + defender.spawnParticle(Particle.CRIT, defender.getLocation().add(0, 1, 0), 20, 0.35, 0.3, 0.35, 0.08); } - - private double getReflectChance(int level) { - return Math.min(getConfig().maxReflectChance, getConfig().reflectChanceBase + (getLevelPercent(level) * getConfig().reflectChanceFactor)); + xp(defender, getConfig().xpOnReflect); + getPlayer(defender).getData().addStat("blocking.mirror-block.projectiles-reflected", 1); + + // Special achievement: reflect 3 projectiles within 5 seconds + long windowStart = getStorageLong(defender, "mirrorWindowStart", 0L); + int windowCount = getStorageInt(defender, "mirrorWindowCount", 0); + if (now - windowStart > 5000L) { + windowStart = now; + windowCount = 1; + } else { + windowCount++; } - - private double getReflectedDamageFactor(int level) { - return Math.min(getConfig().maxReflectedDamageFactor, - getConfig().reflectedDamageFactorBase + (getLevelPercent(level) * getConfig().reflectedDamageFactorIncrease)); + setStorage(defender, "mirrorWindowStart", windowStart); + setStorage(defender, "mirrorWindowCount", windowCount); + if (windowCount >= 3 && AdaptConfig.get().isAdvancements() && !getPlayer(defender).getData().isGranted("challenge_blocking_mirror_3in5")) { + getPlayer(defender).getAdvancementHandler().grant("challenge_blocking_mirror_3in5"); } + } - private double getReflectVelocityFactor(int level) { - return Math.min(getConfig().maxReflectVelocityFactor, getConfig().reflectVelocityFactorBase + (getLevelPercent(level) * getConfig().reflectVelocityFactor)); + private void applyReflectedDamageModifier(EntityDamageByEntityEvent e, Projectile projectile) { + if (!(projectile.getShooter() instanceof Player shooter) || !projectile.hasMetadata(DAMAGE_FACTOR_META)) { + return; } - private long getReflectCooldownMillis(int level) { - return Math.max(100L, Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + if (!canDamageTarget(shooter, e.getEntity())) { + return; } - @Override - public void onTick() { + double factor = getMetadataDouble(projectile, DAMAGE_FACTOR_META, 1D); + e.setDamage(e.getDamage() * factor); + } + private void reflectProjectile(Player defender, Projectile projectile, int level) { + Vector incoming = projectile.getVelocity().clone(); + Vector reflected = incoming.multiply(-Math.max(0.01, getReflectVelocityFactor(level))); + if (reflected.lengthSquared() < getConfig().minReflectedVelocitySquared) { + reflected = defender.getEyeLocation().getDirection().normalize().multiply(getConfig().fallbackReflectedSpeed); } - @Override - public boolean isEnabled() { - return getConfig().enabled; + J.teleport(projectile, defender.getEyeLocation().add(defender.getEyeLocation().getDirection().multiply(0.55))); + projectile.setShooter(defender); + projectile.setVelocity(reflected); + projectile.setMetadata(REFLECTED_META, new FixedMetadataValue(Adapt.instance, true)); + projectile.setMetadata(DAMAGE_FACTOR_META, new FixedMetadataValue(Adapt.instance, getReflectedDamageFactor(level))); + } + + private boolean isMirrorReady(Player p) { + return hasActiveAdaptation(p) && p.isBlocking() && hasShield(p); + } + + private boolean hasShield(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + return (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); + } + + private double getMetadataDouble(Projectile projectile, String key, double fallback) { + for (MetadataValue value : projectile.getMetadata(key)) { + if (value.getOwningPlugin() == Adapt.instance) { + return value.asDouble(); + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Blocking with a shield can reflect incoming projectiles at reduced force and damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectChanceBase = 0.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectChanceFactor = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxReflectChance = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectedDamageFactorBase = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Increase for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectedDamageFactorIncrease = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflected Damage Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxReflectedDamageFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectVelocityFactorBase = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reflectVelocityFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxReflectVelocityFactor = 1.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 1200; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Reflected Velocity Squared for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minReflectedVelocitySquared = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Reflected Speed for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fallbackReflectedSpeed = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Reflect for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnReflect = 8; - } + return fallback; + } + + private double getReflectChance(int level) { + return Math.min(getConfig().maxReflectChance, getConfig().reflectChanceBase + (getLevelPercent(level) * getConfig().reflectChanceFactor)); + } + + private double getReflectedDamageFactor(int level) { + return Math.min(getConfig().maxReflectedDamageFactor, + getConfig().reflectedDamageFactorBase + (getLevelPercent(level) * getConfig().reflectedDamageFactorIncrease)); + } + + private double getReflectVelocityFactor(int level) { + return Math.min(getConfig().maxReflectVelocityFactor, getConfig().reflectVelocityFactorBase + (getLevelPercent(level) * getConfig().reflectVelocityFactor)); + } + + private long getReflectCooldownMillis(int level) { + return Math.max(100L, Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Blocking with a shield can reflect incoming projectiles at reduced force and damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectChanceBase = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Chance Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectChanceFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Chance for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReflectChance = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectedDamageFactorBase = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflected Damage Factor Increase for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectedDamageFactorIncrease = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflected Damage Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReflectedDamageFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectVelocityFactorBase = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reflectVelocityFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reflect Velocity Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReflectVelocityFactor = 1.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 1200; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Reflected Velocity Squared for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minReflectedVelocitySquared = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Reflected Speed for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fallbackReflectedSpeed = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Reflect for the Blocking Mirror Block adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnReflect = 8; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java index 2fd7fbd12..2410df8f3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingMultiArmor.java @@ -26,10 +26,10 @@ import art.arcane.adapt.content.item.multiItems.MultiArmor; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -51,220 +51,220 @@ import java.util.UUID; public class BlockingMultiArmor extends SimpleAdaptation { - private static final MultiArmor multiarmor = new MultiArmor(); - private final Map cooldowns; + private static final MultiArmor multiarmor = new MultiArmor(); + private final Map cooldowns; - public BlockingMultiArmor() { - super("blocking-multiarmor"); - registerConfiguration(BlockingMultiArmor.Config.class); - setDisplayName(Localizer.dLocalize("blocking.multi_armor.name")); - setDescription(Localizer.dLocalize("blocking.multi_armor.description")); - setIcon(Material.ELYTRA); - setInterval(20202); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ELYTRA) - .key("challenge_blocking_multi_200") - .title(Localizer.dLocalize("advancement.challenge_blocking_multi_200.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_multi_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_CHESTPLATE) - .key("challenge_blocking_multi_5k") - .title(Localizer.dLocalize("advancement.challenge_blocking_multi_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_multi_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_blocking_multi_200", "blocking.multi-armor.swaps", 200, 400); - registerMilestone("challenge_blocking_multi_5k", "blocking.multi-armor.swaps", 5000, 1500); - } + public BlockingMultiArmor() { + super("blocking-multiarmor"); + registerConfiguration(BlockingMultiArmor.Config.class); + setDisplayName(Localizer.dLocalize("blocking.multi_armor.name")); + setDescription(Localizer.dLocalize("blocking.multi_armor.description")); + setIcon(Material.ELYTRA); + setInterval(20202); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ELYTRA) + .key("challenge_blocking_multi_200") + .title(Localizer.dLocalize("advancement.challenge_blocking_multi_200.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_multi_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_CHESTPLATE) + .key("challenge_blocking_multi_5k") + .title(Localizer.dLocalize("advancement.challenge_blocking_multi_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_multi_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_blocking_multi_200", "blocking.multi-armor.swaps", 200, 400); + registerMilestone("challenge_blocking_multi_5k", "blocking.multi-armor.swaps", 5000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore1")); - v.addLore(C.GRAY + "" + C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore2")); - v.addLore(C.GREEN + Localizer.dLocalize("blocking.multi_armor.lore3")); - v.addLore(C.RED + Localizer.dLocalize("blocking.multi_armor.lore4")); - v.addLore(C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore5")); - v.addLore(C.UNDERLINE + Localizer.dLocalize("blocking.multi_armor.lore6")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore1")); + v.addLore(C.GRAY + "" + C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore2")); + v.addLore(C.GREEN + Localizer.dLocalize("blocking.multi_armor.lore3")); + v.addLore(C.RED + Localizer.dLocalize("blocking.multi_armor.lore4")); + v.addLore(C.GRAY + Localizer.dLocalize("blocking.multi_armor.lore5")); + v.addLore(C.UNDERLINE + Localizer.dLocalize("blocking.multi_armor.lore6")); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @EventHandler(priority = EventPriority.HIGH) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - ItemStack chest = p.getInventory().getChestplate(); - if (chest != null && hasActiveAdaptation(p) && validateArmor(chest)) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null) { - if (cooldown + 3000 > System.currentTimeMillis()) - return; - else cooldowns.remove(p.getUniqueId()); - } + @EventHandler(priority = EventPriority.HIGH) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + ItemStack chest = p.getInventory().getChestplate(); + if (chest != null && hasActiveAdaptation(p) && validateArmor(chest)) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null) { + if (cooldown + 3000 > System.currentTimeMillis()) + return; + else cooldowns.remove(p.getUniqueId()); + } - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (p.isOnGround() && !p.isFlying()) { - if (isChestplate(chest)) { - return; - } - J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextChestplate(chest))); - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - spw.play(p.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 0.5f, 0.77f); - getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + if (p.isOnGround() && !p.isFlying()) { + if (isChestplate(chest)) { + return; + } + J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextChestplate(chest))); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + spw.play(p.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 0.5f, 0.77f); + getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); - } else if (p.getFallDistance() > 4) { - if (isElytra(chest)) { - return; - } - J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextElytra(chest))); - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - spw.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.5f, 0.77f); - getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); - } + } else if (p.getFallDistance() > 4) { + if (isElytra(chest)) { + return; } + J.runEntity(p, () -> p.getInventory().setChestplate(multiarmor.nextElytra(chest))); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + spw.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.5f, 0.77f); + getPlayer(p).getData().addStat("blocking.multi-armor.swaps", 1); + } } + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerDropItemEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasActiveAdaptation(p)) { - return; - } - if (p.isSneaking()) { - if (validateArmor(e.getItemDrop().getItemStack())) { - List drops = multiarmor.explode(e.getItemDrop().getItemStack()); - for (ItemStack i : drops) { - Damageable iDmgable = (Damageable) i.getItemMeta(); - if (i.hasItemMeta()) { - ItemMeta im = i.getItemMeta().clone(); - ItemMeta im2 = im; - if (im.hasDisplayName()) { - im2.setDisplayName(im.getDisplayName()); - } - if (im.hasEnchants()) { - Map enchants = im.getEnchants(); - for (Enchantment enchant : enchants.keySet()) { - im2.addEnchant(enchant, enchants.get(enchant), true); - } - } - if (iDmgable != null && iDmgable.hasDamage()) { - ((Damageable) im2).setDamage(iDmgable.getDamage()); - } - im2.setLore(null); - i.setItemMeta(im2); - } - drops.set(drops.indexOf(i), i); - } - - J.runEntity(p, () -> { - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); - for (ItemStack i : drops) { - p.getWorld().dropItem(p.getLocation(), i); - } - }); - e.getItemDrop().setItemStack(new ItemStack(Material.AIR)); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + return; + } + if (p.isSneaking()) { + if (validateArmor(e.getItemDrop().getItemStack())) { + List drops = multiarmor.explode(e.getItemDrop().getItemStack()); + for (ItemStack i : drops) { + Damageable iDmgable = (Damageable) i.getItemMeta(); + if (i.hasItemMeta()) { + ItemMeta im = i.getItemMeta().clone(); + ItemMeta im2 = im; + if (im.hasDisplayName()) { + im2.setDisplayName(im.getDisplayName()); } + if (im.hasEnchants()) { + Map enchants = im.getEnchants(); + for (Enchantment enchant : enchants.keySet()) { + im2.addEnchant(enchant, enchants.get(enchant), true); + } + } + if (iDmgable != null && iDmgable.hasDamage()) { + ((Damageable) im2).setDamage(iDmgable.getDamage()); + } + im2.setLore(null); + i.setItemMeta(im2); + } + drops.set(drops.indexOf(i), i); } + + J.runEntity(p, () -> { + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); + for (ItemStack i : drops) { + p.getWorld().dropItem(p.getLocation(), i); + } + }); + e.getItemDrop().setItemStack(new ItemStack(Material.AIR)); + } } + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryClickEvent e) { - Player player = (Player) e.getWhoClicked(); - int level = getActiveLevel(player); - if (level <= 0) { - return; - } - if (e.getClickedInventory() != null - && e.getClick().equals(ClickType.SHIFT_LEFT) - && e.getClickedInventory().getItem(e.getSlot()) != null - && e.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { - ItemStack cursor = e.getWhoClicked().getItemOnCursor().clone(); - ItemStack clicked = e.getClickedInventory().getItem(e.getSlot()).clone(); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(InventoryClickEvent e) { + Player player = (Player) e.getWhoClicked(); + int level = getActiveLevel(player); + if (level <= 0) { + return; + } + if (e.getClickedInventory() != null + && e.getClick().equals(ClickType.SHIFT_LEFT) + && e.getClickedInventory().getItem(e.getSlot()) != null + && e.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { + ItemStack cursor = e.getWhoClicked().getItemOnCursor().clone(); + ItemStack clicked = e.getClickedInventory().getItem(e.getSlot()).clone(); - if (cursor.getType().equals(Material.ELYTRA) || clicked.getType().equals(Material.ELYTRA)) { // One must be an ELYTRA + if (cursor.getType().equals(Material.ELYTRA) || clicked.getType().equals(Material.ELYTRA)) { // One must be an ELYTRA - if (multiarmor.explode(cursor).size() > 1 || multiarmor.explode(clicked).size() > 1) { + if (multiarmor.explode(cursor).size() > 1 || multiarmor.explode(clicked).size() > 1) { - if (multiarmor.explode(cursor).size() >= getSlots(level) || multiarmor.explode(clicked).size() >= getSlots(level)) { - e.setCancelled(true); - SoundPlayer sp = SoundPlayer.of(player); - sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); - return; - } - } - if (ItemListings.getMultiArmorable().contains(cursor.getType()) && ItemListings.getMultiArmorable().contains(clicked.getType())) { // Chest/Elytra Only + if (multiarmor.explode(cursor).size() >= getSlots(level) || multiarmor.explode(clicked).size() >= getSlots(level)) { + e.setCancelled(true); + SoundPlayer sp = SoundPlayer.of(player); + sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); + return; + } + } + if (ItemListings.getMultiArmorable().contains(cursor.getType()) && ItemListings.getMultiArmorable().contains(clicked.getType())) { // Chest/Elytra Only - if (!cursor.getType().isAir() && !clicked.getType().isAir() && multiarmor.supportsItem(cursor) && multiarmor.supportsItem(clicked)) { - e.setCancelled(true); - e.getWhoClicked().setItemOnCursor(new ItemStack(Material.AIR)); - e.getClickedInventory().setItem(e.getSlot(), multiarmor.build(cursor, clicked)); - SoundPlayer spw = SoundPlayer.of(e.getWhoClicked().getWorld()); - spw.play(e.getWhoClicked().getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - } - } - } + if (!cursor.getType().isAir() && !clicked.getType().isAir() && multiarmor.supportsItem(cursor) && multiarmor.supportsItem(clicked)) { + e.setCancelled(true); + e.getWhoClicked().setItemOnCursor(new ItemStack(Material.AIR)); + e.getClickedInventory().setItem(e.getSlot(), multiarmor.build(cursor, clicked)); + SoundPlayer spw = SoundPlayer.of(e.getWhoClicked().getWorld()); + spw.play(e.getWhoClicked().getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + } } + } } + } - private boolean validateArmor(ItemStack item) { - if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) { - for (String lore : item.getItemMeta().getLore()) { - if (lore != null && lore.contains("MultiArmor")) { - return true; - } - } + private boolean validateArmor(ItemStack item) { + if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) { + for (String lore : item.getItemMeta().getLore()) { + if (lore != null && lore.contains("MultiArmor")) { + return true; } - return false; + } } + return false; + } - private double getSlots(double level) { - return getConfig().startingSlots + level; - } + private double getSlots(double level) { + return getConfig().startingSlots + level; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Bind Elytras to armor for dynamic merge and swap.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Blocking Multi Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int startingSlots = 1; - } + @NoArgsConstructor + @ConfigDescription("Bind Elytras to armor for dynamic merge and swap.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Blocking Multi Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int startingSlots = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java index 072c54ca6..a4dfefda8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/blocking/BlockingSaddlecrafter.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -39,79 +39,79 @@ public class BlockingSaddlecrafter extends SimpleAdaptation { - public BlockingSaddlecrafter() { - super("blocking-saddlecrafter"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("blocking.saddle_crafter.description")); - setDisplayName(Localizer.dLocalize("blocking.saddle_crafter.name")); - setIcon(Material.LEATHER_HORSE_ARMOR); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17774); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("blocking-saddlecrafter") - .ingredient(new MaterialChar('I', Material.LEATHER)) - .shapes(List.of( - "I I", - "III")) - .result(new ItemStack(Material.SADDLE, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SADDLE) - .key("challenge_blocking_saddle_25") - .title(Localizer.dLocalize("advancement.challenge_blocking_saddle_25.title")) - .description(Localizer.dLocalize("advancement.challenge_blocking_saddle_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_blocking_saddle_25", "blocking.saddlecrafter.saddles-crafted", 25, 400); - } + public BlockingSaddlecrafter() { + super("blocking-saddlecrafter"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("blocking.saddle_crafter.description")); + setDisplayName(Localizer.dLocalize("blocking.saddle_crafter.name")); + setIcon(Material.LEATHER_HORSE_ARMOR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17774); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("blocking-saddlecrafter") + .ingredient(new MaterialChar('I', Material.LEATHER)) + .shapes(List.of( + "I I", + "III")) + .result(new ItemStack(Material.SADDLE, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SADDLE) + .key("challenge_blocking_saddle_25") + .title(Localizer.dLocalize("advancement.challenge_blocking_saddle_25.title")) + .description(Localizer.dLocalize("advancement.challenge_blocking_saddle_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_blocking_saddle_25", "blocking.saddlecrafter.saddles-crafted", 25, 400); + } - @EventHandler - public void on(CraftItemEvent e) { - if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { - getPlayer(p).getData().addStat("blocking.saddlecrafter.saddles-crafted", 1); - } + @EventHandler + public void on(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player p && hasActiveAdaptation(p) && isAdaptationRecipe(e.getRecipe())) { + getPlayer(p).getData().addStat("blocking.saddlecrafter.saddles-crafted", 1); } + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.saddle_crafter.lore1")); - v.addLore("X-X"); - v.addLore("XXX"); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("blocking.saddle_crafter.lore1")); + v.addLore("X-X"); + v.addLore("XXX"); + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft a Saddle using leather.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0; - } + @NoArgsConstructor + @ConfigDescription("Craft a Saddle using leather.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java index 68efa62da..eaab453d3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingAbsorption.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,96 +41,96 @@ public class BrewingAbsorption extends SimpleAdaptation { - public BrewingAbsorption() { - super("brewing-absorption"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.absorption.description")); - setDisplayName(Localizer.dLocalize("brewing.absorption.name")); - setIcon(Material.QUARTZ); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1333); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-absorption-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.QUARTZ) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Absorption") - .setColor(Color.GRAY) - .addEffect(PotionEffectType.ABSORPTION, 1200, 1, true, true, true) - .build()) - .build() - ); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-absorption-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.QUARTZ_BLOCK) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Haste 2") - .setColor(Color.GRAY) - .addEffect(PotionEffectType.ABSORPTION, 600, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_brewing_absorption_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_absorption_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_absorption_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_absorption_25", "brewing.absorption.potions-brewed", 25, 300); - } + public BrewingAbsorption() { + super("brewing-absorption"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.absorption.description")); + setDisplayName(Localizer.dLocalize("brewing.absorption.name")); + setIcon(Material.QUARTZ); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1333); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-absorption-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.QUARTZ) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Absorption") + .setColor(Color.GRAY) + .addEffect(PotionEffectType.ABSORPTION, 1200, 1, true, true, true) + .build()) + .build() + ); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-absorption-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.QUARTZ_BLOCK) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Haste 2") + .setColor(Color.GRAY) + .addEffect(PotionEffectType.ABSORPTION, 600, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_brewing_absorption_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_absorption_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_absorption_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_absorption_25", "brewing.absorption.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.absorption.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.absorption.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.absorption.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.absorption.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.absorption.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.absorption.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Absorption from Instant Heal and Quartz.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Absorption from Instant Heal and Quartz.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java index e6100a001..3dc987832 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingBlindness.java @@ -28,8 +28,8 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingBlindness extends SimpleAdaptation { - public BrewingBlindness() { - super("brewing-blindness"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.blindness.description")); - setDisplayName(Localizer.dLocalize("brewing.blindness.name")); - setIcon(Material.INK_SAC); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1333); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-blindness-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.INK_SAC) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Blindness") - .setColor(Color.OLIVE) - .addEffect(PotionEffectType.BLINDNESS, 600, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-blindness-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.GLOW_INK_SAC) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Blindness 2") - .setColor(Color.OLIVE) - .addEffect(PotionEffectType.BLINDNESS, 300, 3, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.INK_SAC) - .key("challenge_brewing_blindness_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_blindness_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_blindness_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_blindness_25", "brewing.blindness.potions-brewed", 25, 300); - } + public BrewingBlindness() { + super("brewing-blindness"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.blindness.description")); + setDisplayName(Localizer.dLocalize("brewing.blindness.name")); + setIcon(Material.INK_SAC); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1333); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-blindness-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.INK_SAC) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Blindness") + .setColor(Color.OLIVE) + .addEffect(PotionEffectType.BLINDNESS, 600, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-blindness-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.GLOW_INK_SAC) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Blindness 2") + .setColor(Color.OLIVE) + .addEffect(PotionEffectType.BLINDNESS, 300, 3, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.INK_SAC) + .key("challenge_brewing_blindness_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_blindness_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_blindness_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_blindness_25", "brewing.blindness.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.blindness.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.blindness.lore1")); // v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.blindness.lore2")); - } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.blindness.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.blindness.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Blindness from Awkward Potion and Ink Sack.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Blindness from Awkward Potion and Ink Sack.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java index 8a0203dd5..69b23b856 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDarkness.java @@ -28,8 +28,8 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,83 +41,83 @@ public class BrewingDarkness extends SimpleAdaptation { - public BrewingDarkness() { - super("brewing-darkness"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.darkness.description")); - setDisplayName(Localizer.dLocalize("brewing.darkness.name")); - setIcon(Material.BLACK_CONCRETE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1335); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-darkness") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.BLACK_CONCRETE) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.NIGHT_VISION)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Darkness") - .setColor(Color.BLACK) - .addEffect(PotionEffectType.DARKNESS, 600, 100, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BREWING_STAND) - .key("challenge_brewing_darkness_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_darkness_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_darkness_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_darkness_25", "brewing.darkness.potions-brewed", 25, 300); - } + public BrewingDarkness() { + super("brewing-darkness"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.darkness.description")); + setDisplayName(Localizer.dLocalize("brewing.darkness.name")); + setIcon(Material.BLACK_CONCRETE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1335); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-darkness") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.BLACK_CONCRETE) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.NIGHT_VISION)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Darkness") + .setColor(Color.BLACK) + .addEffect(PotionEffectType.DARKNESS, 600, 100, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BREWING_STAND) + .key("challenge_brewing_darkness_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_darkness_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_darkness_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_darkness_25", "brewing.darkness.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.darkness.lore1")); - v.addLore(C.GRAY + "- " + Localizer.dLocalize("brewing.darkness.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.darkness.lore1")); + v.addLore(C.GRAY + "- " + Localizer.dLocalize("brewing.darkness.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.darkness.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.darkness.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Darkness from NightVision Potion and Black Concrete.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Darkness from NightVision Potion and Black Concrete.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java index e6787c99f..a4c7de080 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingDecay.java @@ -28,8 +28,8 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingDecay extends SimpleAdaptation { - public BrewingDecay() { - super("brewing-decay"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.decay.description")); - setDisplayName(Localizer.dLocalize("brewing.decay.name")); - setIcon(Material.WITHER_ROSE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1334); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-decay-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.POISONOUS_POTATO) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Decay") - .setColor(Color.MAROON) - .addEffect(PotionEffectType.WITHER, 300, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-decay-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.CRIMSON_ROOTS) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Decay 2") - .setColor(Color.MAROON) - .addEffect(PotionEffectType.WITHER, 150, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_ROSE) - .key("challenge_brewing_decay_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_decay_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_decay_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_decay_25", "brewing.decay.potions-brewed", 25, 300); - } + public BrewingDecay() { + super("brewing-decay"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.decay.description")); + setDisplayName(Localizer.dLocalize("brewing.decay.name")); + setIcon(Material.WITHER_ROSE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1334); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-decay-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.POISONOUS_POTATO) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Decay") + .setColor(Color.MAROON) + .addEffect(PotionEffectType.WITHER, 300, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-decay-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.CRIMSON_ROOTS) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Decay 2") + .setColor(Color.MAROON) + .addEffect(PotionEffectType.WITHER, 150, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_brewing_decay_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_decay_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_decay_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_decay_25", "brewing.decay.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.decay.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.decay.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.decay.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.decay.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.decay.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.decay.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Wither from Weakness Potion and Poisonous Potato.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Wither from Weakness Potion and Poisonous Potato.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java index 8685d7404..7870714c3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingFatigue.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingFatigue extends SimpleAdaptation { - public BrewingFatigue() { - super("brewing-fatigue"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.fatigue.description")); - setDisplayName(Localizer.dLocalize("brewing.fatigue.name")); - setIcon(Material.SLIME_BALL); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1332); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-fatigue-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.SLIME_BALL) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Fatigue") - .setColor(Color.fromRGB(0, 66, 0)) - .addEffect(PotionEffectTypes.SLOW_DIGGING, 1200, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-fatigue-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.SLIME_BLOCK) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Fatigue 2") - .setColor(Color.fromRGB(0, 66, 0)) - .addEffect(PotionEffectTypes.SLOW_DIGGING, 600, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BREWING_STAND) - .key("challenge_brewing_fatigue_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_fatigue_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_fatigue_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_fatigue_25", "brewing.fatigue.potions-brewed", 25, 300); - } + public BrewingFatigue() { + super("brewing-fatigue"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.fatigue.description")); + setDisplayName(Localizer.dLocalize("brewing.fatigue.name")); + setIcon(Material.SLIME_BALL); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1332); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-fatigue-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.SLIME_BALL) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Fatigue") + .setColor(Color.fromRGB(0, 66, 0)) + .addEffect(PotionEffectTypes.SLOW_DIGGING, 1200, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-fatigue-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.SLIME_BLOCK) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Fatigue 2") + .setColor(Color.fromRGB(0, 66, 0)) + .addEffect(PotionEffectTypes.SLOW_DIGGING, 600, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BREWING_STAND) + .key("challenge_brewing_fatigue_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_fatigue_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_fatigue_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_fatigue_25", "brewing.fatigue.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.fatigue.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.fatigue.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.fatigue.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.fatigue.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.fatigue.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.fatigue.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Fatigue from Weakness Potion and Slime.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Fatigue from Weakness Potion and Slime.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java index 53493e670..580edadcb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHaste.java @@ -28,10 +28,10 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingHaste extends SimpleAdaptation { - public BrewingHaste() { - super("brewing-haste"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.haste.description")); - setDisplayName(Localizer.dLocalize("brewing.haste.name")); - setIcon(Material.AMETHYST_SHARD); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1334); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-haste-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.AMETHYST_SHARD) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.SPEED)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Haste") - .setColor(Color.YELLOW) - .addEffect(PotionEffectTypes.FAST_DIGGING, 1200, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-haste-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.AMETHYST_BLOCK) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.SPEED)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Haste 2") - .setColor(Color.YELLOW) - .addEffect(PotionEffectTypes.FAST_DIGGING, 600, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BREWING_STAND) - .key("challenge_brewing_haste_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_haste_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_haste_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_haste_25", "brewing.haste.potions-brewed", 25, 300); - } + public BrewingHaste() { + super("brewing-haste"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.haste.description")); + setDisplayName(Localizer.dLocalize("brewing.haste.name")); + setIcon(Material.AMETHYST_SHARD); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1334); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-haste-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.AMETHYST_SHARD) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.SPEED)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Haste") + .setColor(Color.YELLOW) + .addEffect(PotionEffectTypes.FAST_DIGGING, 1200, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-haste-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.AMETHYST_BLOCK) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.SPEED)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Haste 2") + .setColor(Color.YELLOW) + .addEffect(PotionEffectTypes.FAST_DIGGING, 600, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BREWING_STAND) + .key("challenge_brewing_haste_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_haste_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_haste_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_haste_25", "brewing.haste.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.haste.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.haste.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.haste.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.haste.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.haste.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.haste.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Haste from Speed Potion and Amethyst.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Haste from Speed Potion and Amethyst.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java index 68dd53f72..43c079a6c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHealthBoost.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingHealthBoost extends SimpleAdaptation { - public BrewingHealthBoost() { - super("brewing-healthboost"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.health_boost.description")); - setDisplayName(Localizer.dLocalize("brewing.health_boost.name")); - setIcon(Material.ENCHANTED_GOLDEN_APPLE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1330); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-healthboost") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.GOLDEN_APPLE) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Life") - .setColor(Color.RED) - .addEffect(PotionEffectType.HEALTH_BOOST, 1200, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-healthboost") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.ENCHANTED_GOLDEN_APPLE) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Life") - .setColor(Color.RED) - .addEffect(PotionEffectType.HEALTH_BOOST, 1200, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLISTERING_MELON_SLICE) - .key("challenge_brewing_health_boost_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_health_boost_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_health_boost_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_health_boost_25", "brewing.health-boost.potions-brewed", 25, 300); - } + public BrewingHealthBoost() { + super("brewing-healthboost"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.health_boost.description")); + setDisplayName(Localizer.dLocalize("brewing.health_boost.name")); + setIcon(Material.ENCHANTED_GOLDEN_APPLE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1330); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-healthboost") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.GOLDEN_APPLE) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Life") + .setColor(Color.RED) + .addEffect(PotionEffectType.HEALTH_BOOST, 1200, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-healthboost") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.ENCHANTED_GOLDEN_APPLE) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.INSTANT_HEAL)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Life") + .setColor(Color.RED) + .addEffect(PotionEffectType.HEALTH_BOOST, 1200, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLISTERING_MELON_SLICE) + .key("challenge_brewing_health_boost_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_health_boost_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_health_boost_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_health_boost_25", "brewing.health-boost.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.health_boost.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.health_boost.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.health_boost.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.health_boost.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.health-boost.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.health-boost.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Health Boost from Instant Heal and Golden Apple.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Health Boost from Instant Heal and Golden Apple.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java index 523485c32..83ecdb3d1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingHunger.java @@ -28,8 +28,8 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingHunger extends SimpleAdaptation { - public BrewingHunger() { - super("brewing-hunger"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.hunger.description")); - setDisplayName(Localizer.dLocalize("brewing.hunger.name")); - setIcon(Material.ROTTEN_FLESH); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1331); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-hunger-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.ROTTEN_FLESH) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Hunger") - .setColor(Color.GREEN) - .addEffect(PotionEffectType.HUNGER, 1200, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-hunger-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.ROTTEN_FLESH) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Hunger 2") - .setColor(Color.GREEN) - .addEffect(PotionEffectType.HUNGER, 600, 3, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ROTTEN_FLESH) - .key("challenge_brewing_hunger_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_hunger_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_hunger_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_hunger_25", "brewing.hunger.potions-brewed", 25, 300); - } + public BrewingHunger() { + super("brewing-hunger"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.hunger.description")); + setDisplayName(Localizer.dLocalize("brewing.hunger.name")); + setIcon(Material.ROTTEN_FLESH); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1331); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-hunger-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.ROTTEN_FLESH) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Hunger") + .setColor(Color.GREEN) + .addEffect(PotionEffectType.HUNGER, 1200, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-hunger-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.ROTTEN_FLESH) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.WEAKNESS)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Hunger 2") + .setColor(Color.GREEN) + .addEffect(PotionEffectType.HUNGER, 600, 3, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ROTTEN_FLESH) + .key("challenge_brewing_hunger_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_hunger_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_hunger_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_hunger_25", "brewing.hunger.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.hunger.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.hunger.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.hunger.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.hunger.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.hunger.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.hunger.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Hunger from Awkward Potion and Rotten Flesh.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Hunger from Awkward Potion and Rotten Flesh.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java index da827b0d2..b1e200939 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingLingering.java @@ -29,13 +29,13 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.ItemFlags; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.function.Function3; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.function.Function3; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -63,309 +63,310 @@ import java.util.function.Function; public class BrewingLingering extends SimpleAdaptation { - private static final Function getColor; - private static final Function> getEffectAttributes; - private static final Function3 getAttributeModifierAmount; - private static final DecimalFormat ATTRIBUTE_MODIFIER_FORMAT = new DecimalFormat("#.##"); - - public BrewingLingering() { - super("brewing-lingering"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.lingering.description")); - setDisplayName(Localizer.dLocalize("brewing.lingering.name")); - setIcon(Material.DRAGON_BREATH); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(4788); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LINGERING_POTION) - .key("challenge_brewing_lingering_200") - .title(Localizer.dLocalize("advancement.challenge_brewing_lingering_200.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_lingering_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DRAGON_BREATH) - .key("challenge_brewing_lingering_5k") - .title(Localizer.dLocalize("advancement.challenge_brewing_lingering_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_lingering_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_brewing_lingering_200", "brewing.lingering.potions-extended", 200, 300); - registerMilestone("challenge_brewing_lingering_5k", "brewing.lingering.potions-extended", 5000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration((long) getDurationBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.lingering.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getPercentBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.lingering.lore2")); - } - - public double getDurationBoost(double factor) { - return (getConfig().durationBoostFactorTicks * factor) + getConfig().baseDurationBoostTicks; + private static final Function getColor; + private static final Function> getEffectAttributes; + private static final Function3 getAttributeModifierAmount; + private static final DecimalFormat ATTRIBUTE_MODIFIER_FORMAT = new DecimalFormat("#.##"); + + static { + java.lang.invoke.MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle getCategory; + try { + java.lang.reflect.Method method = PotionEffectType.class.getDeclaredMethod("getCategory"); + getCategory = lookup.unreflect(method); + } catch (Throwable ignored) { + getCategory = null; } - public double getPercentBoost(double factor) { - return 1 + ((factor * factor * getConfig().durationMultiplierFactor) + getConfig().baseDurationMultiplier); + MethodHandle modifiersHandle; + MethodHandle amountHandle; + try { + modifiersHandle = lookup.findVirtual(PotionEffectType.class, "getEffectAttributes", MethodType.methodType(Map.class)); + amountHandle = lookup.findVirtual(PotionEffectType.class, "getAttributeModifierAmount", MethodType.methodType(double.class, Attribute.class, int.class)); + } catch (Throwable ignored) { + Adapt.verbose("Failed to find attributes for potion effect type"); + modifiersHandle = null; + amountHandle = null; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BrewEvent e) { - if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { - return; - } - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - - if (owner == null) { - Adapt.verbose("No Owner"); - return; + if (getCategory != null) { + MethodHandle handle = getCategory; + getColor = type -> { + try { + return ((Enum) handle.invoke(type)).ordinal() == 1 ? NamedTextColor.RED : NamedTextColor.BLUE; + } catch (Throwable err) { + throw new RuntimeException(err); } + }; + } else getColor = $ -> NamedTextColor.BLUE; - PlayerData data = null; - var results = e.getResults(); - boolean ef = false; - for (int i = 0; i < results.size(); i++) { - ItemStack is = results.get(i); - - if (is == null || is.getItemMeta() == null || !(is.getItemMeta() instanceof PotionMeta p)) - continue; - - data = data == null ? getServer().peekData(owner.getOwner()) : data; - - if (data.getSkillLines().containsKey(getSkill().getName()) && data.getSkillLine(getSkill().getName()).getAdaptations().containsKey(getName())) { - PlayerAdaptation a = data.getSkillLine(getSkill().getName()).getAdaptations().get(getName()); - - if (a.getLevel() > 0) { - double factor = getLevelPercent(a.getLevel()); - boolean enhanced = enhance(factor, is, p); - if (enhanced) { - data.addStat("brewing.lingering.potions-extended", 1); - } - ef = enhanced || ef; - results.set(i, is); - } - } + if (modifiersHandle != null) { + MethodHandle handle = modifiersHandle; + getEffectAttributes = type -> { + try { + return (Map) handle.invoke(type); + } catch (Throwable err) { + throw new RuntimeException(err); } + }; + } else getEffectAttributes = $ -> Map.of(); - if (ef) { - SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); - spw.play(e.getBlock().getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1f, 0.75f); - spw.play(e.getBlock().getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1f, 1.75f); + if (amountHandle != null) { + MethodHandle handle = amountHandle; + getAttributeModifierAmount = (type, attribute, level) -> { + try { + return (double) handle.invoke(type, attribute, level); + } catch (Throwable err) { + throw new RuntimeException(err); } + }; + } else getAttributeModifierAmount = ($, $$, $$$) -> 0d; + + ATTRIBUTE_MODIFIER_FORMAT.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); + } + + public BrewingLingering() { + super("brewing-lingering"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.lingering.description")); + setDisplayName(Localizer.dLocalize("brewing.lingering.name")); + setIcon(Material.DRAGON_BREATH); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(4788); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LINGERING_POTION) + .key("challenge_brewing_lingering_200") + .title(Localizer.dLocalize("advancement.challenge_brewing_lingering_200.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_lingering_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DRAGON_BREATH) + .key("challenge_brewing_lingering_5k") + .title(Localizer.dLocalize("advancement.challenge_brewing_lingering_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_lingering_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_brewing_lingering_200", "brewing.lingering.potions-extended", 200, 300); + registerMilestone("challenge_brewing_lingering_5k", "brewing.lingering.potions-extended", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration((long) getDurationBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.lingering.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getPercentBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.lingering.lore2")); + } + + public double getDurationBoost(double factor) { + return (getConfig().durationBoostFactorTicks * factor) + getConfig().baseDurationBoostTicks; + } + + public double getPercentBoost(double factor) { + return 1 + ((factor * factor * getConfig().durationMultiplierFactor) + getConfig().baseDurationMultiplier); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BrewEvent e) { + if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { + return; } + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - private boolean enhance(double factor, ItemStack is, PotionMeta p) { - var effects = p.getBasePotionType().getPotionEffects(); - if (effects.stream() - .map(PotionEffect::getType) - .allMatch(PotionEffectType::isInstant)) - return false; - - p.clearCustomEffects(); - for (final PotionEffect effect : effects) { - if (effect.getType().isInstant()) { - p.addCustomEffect(effect, true); - continue; - } - - p.addCustomEffect(new PotionEffect( - effect.getType(), - (int) (getDurationBoost(factor) + (effect.getDuration() * getPercentBoost(factor))), - effect.getAmplifier() - ), true); - } - - p.addItemFlags(ItemFlags.HIDE_POTION_EFFECTS); - is.setItemMeta(p); - - if (getConfig().useCustomLore) { - KList lore = new KList<>(); - KList modifiers = new KList<>(); - for (var effect : p.getCustomEffects()) { - var type = effect.getType(); - var key = type.getKey(); - var name = Component.translatable("effect." + key.getNamespace() + "." + key.getKey()); - if (effect.getAmplifier() > 0) { - name = Component.translatable("potion.withAmplifier", name, - Component.translatable("potion.potency." + effect.getAmplifier())); - } - - if (effect.getDuration() > 20) { - name = Component.translatable("potion.withDuration", name, formatDuration(effect)); - } - - lore.add(name.color(getColor.apply(type))); - getEffectAttributes.apply(type) - .entrySet() - .stream() - .map(Modifier::new) - .map(m -> m.adjust(type, effect.getAmplifier())) - .filter(m -> m.amount != 0) - .forEach(modifiers::add); - } - - if (!modifiers.isEmpty()) { - lore.add(Component.empty()); - lore.add(Component.translatable("potion.whenDrank").color(NamedTextColor.DARK_PURPLE)); - - for (Modifier modifier : modifiers) { - double amount = modifier.amount; - var formatted = Component.text(ATTRIBUTE_MODIFIER_FORMAT.format(modifier.operation == AttributeModifier.Operation.ADD_NUMBER ? amount : amount * 100d)); - var name = Component.translatable("attribute.name." + modifier.attribute.getKey().getKey()); - - if (amount > 0) { - lore.add(Component.translatable("attribute.modifier.plus." + modifier.operation.ordinal(), formatted, name) - .color(NamedTextColor.BLUE)); - } else { - lore.add(Component.translatable("attribute.modifier.take." + modifier.operation.ordinal(), formatted, name) - .color(NamedTextColor.RED)); - } - } - } - lore.replaceAll(c -> c.decoration(TextDecoration.ITALIC, false)); - - Adapt.platform.editItem(is) - .lore(lore) - .build(); - } - - return true; + if (owner == null) { + Adapt.verbose("No Owner"); + return; } - private Component formatDuration(PotionEffect effect) { - if (effect.isInfinite()) { - return Component.translatable("effect.duration.infinite"); - } else { - int seconds = effect.getDuration() / 20; - int minutes = seconds / 60; - seconds %= 60; - int hours = minutes / 60; - minutes %= 60; - return Component.text(hours > 0 ? - "%02d:%02d:%02d".formatted(hours, minutes, seconds) : - "%02d:%02d".formatted(minutes, seconds)); - } - } + PlayerData data = null; + java.util.List results = e.getResults(); + boolean ef = false; + for (int i = 0; i < results.size(); i++) { + ItemStack is = results.get(i); + if (is == null || is.getItemMeta() == null || !(is.getItemMeta() instanceof PotionMeta p)) + continue; - @Override - public void onTick() { + data = data == null ? getServer().peekData(owner.getOwner()) : data; - } + if (data.getSkillLines().containsKey(getSkill().getName()) && data.getSkillLine(getSkill().getName()).getAdaptations().containsKey(getName())) { + PlayerAdaptation a = data.getSkillLine(getSkill().getName()).getAdaptations().get(getName()); - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (a.getLevel() > 0) { + double factor = getLevelPercent(a.getLevel()); + boolean enhanced = enhance(factor, is, p); + if (enhanced) { + data.addStat("brewing.lingering.potions-extended", 1); + } + ef = enhanced || ef; + results.set(i, is); + } + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (ef) { + SoundPlayer spw = SoundPlayer.of(e.getBlock().getWorld()); + spw.play(e.getBlock().getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1f, 0.75f); + spw.play(e.getBlock().getLocation(), Sound.BLOCK_BREWING_STAND_BREW, 1f, 1.75f); } - - @NoArgsConstructor - @ConfigDescription("Brewed potions last longer.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Boost Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDurationBoostTicks = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Boost Factor Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationBoostFactorTicks = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Multiplier Factor for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationMultiplierFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Multiplier for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDurationMultiplier = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Custom Lore for the Brewing Lingering adaptation.", impact = "True enables this behavior and false disables it.") - boolean useCustomLore = true; + } + + private boolean enhance(double factor, ItemStack is, PotionMeta p) { + java.util.List effects = p.getBasePotionType().getPotionEffects(); + if (effects.stream() + .map(PotionEffect::getType) + .allMatch(PotionEffectType::isInstant)) + return false; + + p.clearCustomEffects(); + for (final PotionEffect effect : effects) { + if (effect.getType().isInstant()) { + p.addCustomEffect(effect, true); + continue; + } + + p.addCustomEffect(new PotionEffect( + effect.getType(), + (int) (getDurationBoost(factor) + (effect.getDuration() * getPercentBoost(factor))), + effect.getAmplifier() + ), true); } - private record Modifier(Attribute attribute, AttributeModifier.Operation operation, double amount) { - private Modifier(Map.Entry entry) { - this(entry.getKey(), entry.getValue()); + p.addItemFlags(ItemFlags.HIDE_POTION_EFFECTS); + is.setItemMeta(p); + + if (getConfig().useCustomLore) { + KList lore = new KList<>(); + KList modifiers = new KList<>(); + for (org.bukkit.potion.PotionEffect effect : p.getCustomEffects()) { + org.bukkit.potion.PotionEffectType type = effect.getType(); + org.bukkit.NamespacedKey key = type.getKey(); + net.kyori.adventure.text.TranslatableComponent name = Component.translatable("effect." + key.getNamespace() + "." + key.getKey()); + if (effect.getAmplifier() > 0) { + name = Component.translatable("potion.withAmplifier", name, + Component.translatable("potion.potency." + effect.getAmplifier())); } - private Modifier(Attribute attribute, AttributeModifier modifier) { - this(attribute, modifier.getOperation(), modifier.getAmount()); + if (effect.getDuration() > 20) { + name = Component.translatable("potion.withDuration", name, formatDuration(effect)); } - private Modifier adjust(PotionEffectType type, int amplifier) { - return new Modifier( - attribute, - operation, - getAttributeModifierAmount.apply(type, attribute, amplifier) - ); + lore.add(name.color(getColor.apply(type))); + getEffectAttributes.apply(type) + .entrySet() + .stream() + .map(Modifier::new) + .map(m -> m.adjust(type, effect.getAmplifier())) + .filter(m -> m.amount != 0) + .forEach(modifiers::add); + } + + if (!modifiers.isEmpty()) { + lore.add(Component.empty()); + lore.add(Component.translatable("potion.whenDrank").color(NamedTextColor.DARK_PURPLE)); + + for (Modifier modifier : modifiers) { + double amount = modifier.amount; + net.kyori.adventure.text.TextComponent formatted = Component.text(ATTRIBUTE_MODIFIER_FORMAT.format(modifier.operation == AttributeModifier.Operation.ADD_NUMBER ? amount : amount * 100d)); + net.kyori.adventure.text.TranslatableComponent name = Component.translatable("attribute.name." + modifier.attribute.getKey().getKey()); + + if (amount > 0) { + lore.add(Component.translatable("attribute.modifier.plus." + modifier.operation.ordinal(), formatted, name) + .color(NamedTextColor.BLUE)); + } else { + lore.add(Component.translatable("attribute.modifier.take." + modifier.operation.ordinal(), formatted, name) + .color(NamedTextColor.RED)); + } } + } + lore.replaceAll(c -> c.decoration(TextDecoration.ITALIC, false)); + + Adapt.platform.editItem(is) + .lore(lore) + .build(); } - static { - var lookup = MethodHandles.lookup(); - MethodHandle getCategory; - try { - var method = PotionEffectType.class.getDeclaredMethod("getCategory"); - getCategory = lookup.unreflect(method); - } catch (Throwable ignored) { - getCategory = null; - } + return true; + } + + private Component formatDuration(PotionEffect effect) { + if (effect.isInfinite()) { + return Component.translatable("effect.duration.infinite"); + } else { + int seconds = effect.getDuration() / 20; + int minutes = seconds / 60; + seconds %= 60; + int hours = minutes / 60; + minutes %= 60; + return Component.text(hours > 0 ? + "%02d:%02d:%02d".formatted(hours, minutes, seconds) : + "%02d:%02d".formatted(minutes, seconds)); + } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Brewed potions last longer.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Boost Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDurationBoostTicks = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Boost Factor Ticks for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationBoostFactorTicks = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Multiplier Factor for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationMultiplierFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Multiplier for the Brewing Lingering adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDurationMultiplier = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Custom Lore for the Brewing Lingering adaptation.", impact = "True enables this behavior and false disables it.") + boolean useCustomLore = true; + } + + private record Modifier(Attribute attribute, + AttributeModifier.Operation operation, + double amount) { + private Modifier(Map.Entry entry) { + this(entry.getKey(), entry.getValue()); + } - MethodHandle modifiersHandle; - MethodHandle amountHandle; - try { - modifiersHandle = lookup.findVirtual(PotionEffectType.class, "getEffectAttributes", MethodType.methodType(Map.class)); - amountHandle = lookup.findVirtual(PotionEffectType.class, "getAttributeModifierAmount", MethodType.methodType(double.class, Attribute.class, int.class)); - } catch (Throwable ignored) { - Adapt.verbose("Failed to find attributes for potion effect type"); - modifiersHandle = null; - amountHandle = null; - } + private Modifier(Attribute attribute, AttributeModifier modifier) { + this(attribute, modifier.getOperation(), modifier.getAmount()); + } - if (getCategory != null) { - MethodHandle handle = getCategory; - getColor = type -> { - try { - return ((Enum) handle.invoke(type)).ordinal() == 1 ? NamedTextColor.RED : NamedTextColor.BLUE; - } catch (Throwable err) { - throw new RuntimeException(err); - } - }; - } else getColor = $ -> NamedTextColor.BLUE; - - if (modifiersHandle != null) { - MethodHandle handle = modifiersHandle; - getEffectAttributes = type -> { - try { - return (Map) handle.invoke(type); - } catch (Throwable err) { - throw new RuntimeException(err); - } - }; - } else getEffectAttributes = $ -> Map.of(); - - if (amountHandle != null) { - MethodHandle handle = amountHandle; - getAttributeModifierAmount = (type, attribute, level) -> { - try { - return (double) handle.invoke(type, attribute, level); - } catch (Throwable err) { - throw new RuntimeException(err); - } - }; - } else getAttributeModifierAmount = ($, $$, $$$) -> 0d; - - ATTRIBUTE_MODIFIER_FORMAT.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); + private Modifier adjust(PotionEffectType type, int amplifier) { + return new Modifier( + attribute, + operation, + getAttributeModifierAmount.apply(type, attribute, amplifier) + ); } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java index cc8537ceb..b3bc449f8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingNausea.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingNausea extends SimpleAdaptation { - public BrewingNausea() { - super("brewing-nausea"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.nausea.description")); - setDisplayName(Localizer.dLocalize("brewing.nausea.name")); - setIcon(Material.CRIMSON_FUNGUS); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1333); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-nausea-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.BROWN_MUSHROOM) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Nausea") - .setColor(Color.LIME) - .addEffect(PotionEffectTypes.CONFUSION, 600, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-nausea-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.CRIMSON_FUNGUS) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Nausea 2") - .setColor(Color.LIME) - .addEffect(PotionEffectTypes.CONFUSION, 300, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.POISONOUS_POTATO) - .key("challenge_brewing_nausea_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_nausea_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_nausea_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_nausea_25", "brewing.nausea.potions-brewed", 25, 300); - } + public BrewingNausea() { + super("brewing-nausea"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.nausea.description")); + setDisplayName(Localizer.dLocalize("brewing.nausea.name")); + setIcon(Material.CRIMSON_FUNGUS); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1333); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-nausea-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.BROWN_MUSHROOM) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Nausea") + .setColor(Color.LIME) + .addEffect(PotionEffectTypes.CONFUSION, 600, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-nausea-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.CRIMSON_FUNGUS) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Nausea 2") + .setColor(Color.LIME) + .addEffect(PotionEffectTypes.CONFUSION, 300, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.POISONOUS_POTATO) + .key("challenge_brewing_nausea_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_nausea_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_nausea_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_nausea_25", "brewing.nausea.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.nausea.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.nausea.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.nausea.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.nausea.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.nausea.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.nausea.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Nausea from Awkward Potion and Mushroom.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Nausea from Awkward Potion and Mushroom.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java index bb83e415b..96dab1c8d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingResistance.java @@ -28,8 +28,8 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingResistance extends SimpleAdaptation { - public BrewingResistance() { - super("brewing-resistance"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.resistance.description")); - setDisplayName(Localizer.dLocalize("brewing.resistance.name")); - setIcon(Material.IRON_BLOCK); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1333); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-resistance-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.IRON_INGOT) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Resistance") - .setColor(Color.WHITE) - .addEffect(PotionEffectType.RESISTANCE, 1200, 1, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-resistance-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.IRON_BLOCK) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Resistance 2") - .setColor(Color.WHITE) - .addEffect(PotionEffectType.RESISTANCE, 600, 2, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_brewing_resistance_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_resistance_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_resistance_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_resistance_25", "brewing.resistance.potions-brewed", 25, 300); - } + public BrewingResistance() { + super("brewing-resistance"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.resistance.description")); + setDisplayName(Localizer.dLocalize("brewing.resistance.name")); + setIcon(Material.IRON_BLOCK); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1333); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-resistance-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.IRON_INGOT) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Resistance") + .setColor(Color.WHITE) + .addEffect(PotionEffectType.RESISTANCE, 1200, 1, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-resistance-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.IRON_BLOCK) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionType.AWKWARD)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Resistance 2") + .setColor(Color.WHITE) + .addEffect(PotionEffectType.RESISTANCE, 600, 2, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_brewing_resistance_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_resistance_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_resistance_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_resistance_25", "brewing.resistance.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.resistance.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.resistance.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.resistance.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.resistance.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.resistance.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.resistance.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Resistance from Awkward Potion and Iron.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Resistance from Awkward Potion and Iron.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java index d3d09c0a4..ad1fefaff 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSaturation.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -41,95 +41,95 @@ public class BrewingSaturation extends SimpleAdaptation { - public BrewingSaturation() { - super("brewing-saturation"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.saturation.description")); - setDisplayName(Localizer.dLocalize("brewing.saturation.name")); - setIcon(Material.BAKED_POTATO); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1334); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-saturation-1") - .brewingTime(320) - .fuelCost(16) - .ingredient(Material.BAKED_POTATO) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.REGEN)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Saturation") - .setColor(Color.ORANGE) - .addEffect(PotionEffectType.SATURATION, 1, 4, true, true, true) - .build()) - .build()); - registerBrewingRecipe(BrewingRecipe.builder() - .id("brewing-saturation-2") - .brewingTime(320) - .fuelCost(32) - .ingredient(Material.HAY_BLOCK) - .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.REGEN)) - .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) - .setName("Bottled Saturation 2") - .setColor(Color.ORANGE) - .addEffect(PotionEffectType.SATURATION, 1, 8, true, true, true) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_CARROT) - .key("challenge_brewing_saturation_25") - .title(Localizer.dLocalize("advancement.challenge_brewing_saturation_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_saturation_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_brewing_saturation_25", "brewing.saturation.potions-brewed", 25, 300); - } + public BrewingSaturation() { + super("brewing-saturation"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.saturation.description")); + setDisplayName(Localizer.dLocalize("brewing.saturation.name")); + setIcon(Material.BAKED_POTATO); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1334); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-saturation-1") + .brewingTime(320) + .fuelCost(16) + .ingredient(Material.BAKED_POTATO) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.REGEN)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Saturation") + .setColor(Color.ORANGE) + .addEffect(PotionEffectType.SATURATION, 1, 4, true, true, true) + .build()) + .build()); + registerBrewingRecipe(BrewingRecipe.builder() + .id("brewing-saturation-2") + .brewingTime(320) + .fuelCost(32) + .ingredient(Material.HAY_BLOCK) + .basePotion(PotionBuilder.vanilla(PotionBuilder.Type.REGULAR, PotionTypes.REGEN)) + .result(PotionBuilder.of(PotionBuilder.Type.REGULAR) + .setName("Bottled Saturation 2") + .setColor(Color.ORANGE) + .addEffect(PotionEffectType.SATURATION, 1, 8, true, true, true) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_CARROT) + .key("challenge_brewing_saturation_25") + .title(Localizer.dLocalize("advancement.challenge_brewing_saturation_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_saturation_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_brewing_saturation_25", "brewing.saturation.potions-brewed", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.saturation.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.saturation.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.saturation.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("brewing.saturation.lore2")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BrewEvent e) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.saturation.potions-brewed", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BrewEvent e) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.saturation.potions-brewed", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Brew a Potion of Saturation from Regen Potion and Baked Potato.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - } + @NoArgsConstructor + @ConfigDescription("Brew a Potion of Saturation from Regen Potion and Baked Potato.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java index f0d65779a..3fa602be9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/brewing/BrewingSuperHeated.java @@ -29,11 +29,11 @@ import art.arcane.adapt.content.matter.BrewingStandOwner; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; @@ -55,214 +55,214 @@ public class BrewingSuperHeated extends SimpleAdaptation { - private static final int MAX_CHECKS_BEFORE_REMOVE = 20; - private final Map activeStands = new ConcurrentHashMap<>(); + private static final int MAX_CHECKS_BEFORE_REMOVE = 20; + private final Map activeStands = new ConcurrentHashMap<>(); - public BrewingSuperHeated() { - super("brewing-super-heated"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("brewing.super_heated.description")); - setDisplayName(Localizer.dLocalize("brewing.super_heated.name")); - setIcon(Material.LAVA_BUCKET); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(253); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_brewing_super_heated_100") - .title(Localizer.dLocalize("advancement.challenge_brewing_super_heated_100.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_super_heated_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.MAGMA_CREAM) - .key("challenge_brewing_super_heated_2500") - .title(Localizer.dLocalize("advancement.challenge_brewing_super_heated_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_brewing_super_heated_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_brewing_super_heated_100", "brewing.super-heated.brews-accelerated", 100, 300); - registerMilestone("challenge_brewing_super_heated_2500", "brewing.super-heated.brews-accelerated", 2500, 1000); - } + public BrewingSuperHeated() { + super("brewing-super-heated"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("brewing.super_heated.description")); + setDisplayName(Localizer.dLocalize("brewing.super_heated.name")); + setIcon(Material.LAVA_BUCKET); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(253); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_brewing_super_heated_100") + .title(Localizer.dLocalize("advancement.challenge_brewing_super_heated_100.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_super_heated_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.MAGMA_CREAM) + .key("challenge_brewing_super_heated_2500") + .title(Localizer.dLocalize("advancement.challenge_brewing_super_heated_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_brewing_super_heated_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_brewing_super_heated_100", "brewing.super-heated.brews-accelerated", 100, 300); + registerMilestone("challenge_brewing_super_heated_2500", "brewing.super-heated.brews-accelerated", 2500, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getFireBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.super_heated.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getLavaBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.super_heated.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getFireBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.super_heated.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getLavaBoost(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("brewing.super_heated.lore2")); + } - public double getLavaBoost(double factor) { - return (getConfig().lavaMultiplier) * (getConfig().multiplierFactor * factor); - } + public double getLavaBoost(double factor) { + return (getConfig().lavaMultiplier) * (getConfig().multiplierFactor * factor); + } - public double getFireBoost(double factor) { - return (getConfig().fireMultiplier) * (getConfig().multiplierFactor * factor); + public double getFireBoost(double factor) { + return (getConfig().fireMultiplier) * (getConfig().multiplierFactor * factor); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(InventoryMoveItemEvent e) { + if (!e.getDestination().getType().equals(InventoryType.BREWING) || e.getDestination().getLocation() == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryMoveItemEvent e) { - if (!e.getDestination().getType().equals(InventoryType.BREWING) || e.getDestination().getLocation() == null) { - return; - } + activeStands.put(e.getDestination().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); + } - activeStands.put(e.getDestination().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BrewEvent e) { + if (activeStands.containsKey(e.getBlock())) { + BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); + if (owner != null) { + getServer().peekData(owner.getOwner()).addStat("brewing.super-heated.brews-accelerated", 1); + } } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BrewEvent e) { - if (activeStands.containsKey(e.getBlock())) { - BrewingStandOwner owner = WorldData.of(e.getBlock().getWorld()).get(e.getBlock(), BrewingStandOwner.class); - if (owner != null) { - getServer().peekData(owner.getOwner()).addStat("brewing.super-heated.brews-accelerated", 1); - } - } - if (((BrewingStand) e.getBlock().getState()).getBrewingTime() > 0) { - activeStands.put(e.getBlock(), MAX_CHECKS_BEFORE_REMOVE); - } + if (((BrewingStand) e.getBlock().getState()).getBrewingTime() > 0) { + activeStands.put(e.getBlock(), MAX_CHECKS_BEFORE_REMOVE); } + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryClickEvent e) { - if (e.getClickedInventory() == null) { - return; - } - if (e.getView().getTopInventory().getType().equals(InventoryType.BREWING)) { - activeStands.put(e.getView().getTopInventory().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(InventoryClickEvent e) { + if (e.getClickedInventory() == null) { + return; } + if (e.getView().getTopInventory().getType().equals(InventoryType.BREWING)) { + activeStands.put(e.getView().getTopInventory().getLocation().getBlock(), MAX_CHECKS_BEFORE_REMOVE); + } + } - @Override - public void onTick() { - if (activeStands.isEmpty()) { - return; - } - - for (Block block : activeStands.keySet()) { - if (block == null) { - continue; - } - - J.runAt(block.getLocation(), () -> tickStand(block)); - } + @Override + public void onTick() { + if (activeStands.isEmpty()) { + return; } - private void tickStand(Block block) { - BlockState state = block.getState(); - if (!(state instanceof BrewingStand brewingStand)) { - activeStands.remove(block); - return; - } + for (Block block : activeStands.keySet()) { + if (block == null) { + continue; + } - if (brewingStand.getBrewingTime() <= 0) { - BrewingStand current = (BrewingStand) block.getState(); - if (current.getBrewingTime() <= 0) { - Integer remainingChecks = activeStands.get(block); - if (remainingChecks == null) { - return; - } + J.runAt(block.getLocation(), () -> tickStand(block)); + } + } - if (remainingChecks <= 0) { - activeStands.remove(block); - } else { - activeStands.put(block, remainingChecks - 1); - } - } - return; - } + private void tickStand(Block block) { + BlockState state = block.getState(); + if (!(state instanceof BrewingStand brewingStand)) { + activeStands.remove(block); + return; + } - BrewingStandOwner owner = WorldData.of(brewingStand.getWorld()).get(block, BrewingStandOwner.class); - if (owner == null) { - activeStands.remove(block); - return; + if (brewingStand.getBrewingTime() <= 0) { + BrewingStand current = (BrewingStand) block.getState(); + if (current.getBrewingTime() <= 0) { + Integer remainingChecks = activeStands.get(block); + if (remainingChecks == null) { + return; } - PlayerData playerData = getServer().peekData(owner.getOwner()); - if (playerData == null) { - activeStands.remove(block); - return; + if (remainingChecks <= 0) { + activeStands.remove(block); + } else { + activeStands.put(block, remainingChecks - 1); } + } + return; + } - PlayerSkillLine line = playerData.getSkillLineNullable(getSkill().getName()); - PlayerAdaptation adaptation = line != null ? line.getAdaptation(getName()) : null; - if (adaptation == null || adaptation.getLevel() <= 0) { - activeStands.remove(block); - return; - } + BrewingStandOwner owner = WorldData.of(brewingStand.getWorld()).get(block, BrewingStandOwner.class); + if (owner == null) { + activeStands.remove(block); + return; + } - updateHeat(brewingStand, getLevelPercent(adaptation.getLevel())); + PlayerData playerData = getServer().peekData(owner.getOwner()); + if (playerData == null) { + activeStands.remove(block); + return; } - private void updateHeat(BrewingStand b, double factor) { - double l = 0; - double f = 0; + PlayerSkillLine line = playerData.getSkillLineNullable(getSkill().getName()); + PlayerAdaptation adaptation = line != null ? line.getAdaptation(getName()) : null; + if (adaptation == null || adaptation.getLevel() <= 0) { + activeStands.remove(block); + return; + } - switch (b.getBlock().getRelative(BlockFace.DOWN).getType()) { - case LAVA -> l = l + 1; - case FIRE -> f = f + 1; - } - switch (b.getBlock().getRelative(BlockFace.NORTH).getType()) { - case LAVA -> l = l + 1; - case FIRE -> f = f + 1; - } - switch (b.getBlock().getRelative(BlockFace.SOUTH).getType()) { - case LAVA -> l = l + 1; - case FIRE -> f = f + 1; - } - switch (b.getBlock().getRelative(BlockFace.EAST).getType()) { - case LAVA -> l = l + 1; - case FIRE -> f = f + 1; - } - switch (b.getBlock().getRelative(BlockFace.WEST).getType()) { - case LAVA -> l = l + 1; - case FIRE -> f = f + 1; - } + updateHeat(brewingStand, getLevelPercent(adaptation.getLevel())); + } - double pct = (getFireBoost(factor) * f) + (getLavaBoost(factor) * l) + 1; - int warp = (int) ((getInterval() / 50D) * pct); - b.setBrewingTime(Math.max(1, b.getBrewingTime() - warp)); - b.update(); + private void updateHeat(BrewingStand b, double factor) { + double l = 0; + double f = 0; - if (M.r(1D / (333D / getInterval()))) { - SoundPlayer spw = SoundPlayer.of(b.getBlock().getWorld()); - spw.play(b.getBlock().getLocation(), Sound.BLOCK_FIRE_AMBIENT, 1f, 1f + RNG.r.f(0.3f, 0.6f)); - } + switch (b.getBlock().getRelative(BlockFace.DOWN).getType()) { + case LAVA -> l = l + 1; + case FIRE -> f = f + 1; } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + switch (b.getBlock().getRelative(BlockFace.NORTH).getType()) { + case LAVA -> l = l + 1; + case FIRE -> f = f + 1; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + switch (b.getBlock().getRelative(BlockFace.SOUTH).getType()) { + case LAVA -> l = l + 1; + case FIRE -> f = f + 1; + } + switch (b.getBlock().getRelative(BlockFace.EAST).getType()) { + case LAVA -> l = l + 1; + case FIRE -> f = f + 1; + } + switch (b.getBlock().getRelative(BlockFace.WEST).getType()) { + case LAVA -> l = l + 1; + case FIRE -> f = f + 1; } - @NoArgsConstructor - @ConfigDescription("Brewing stands work faster when surrounded by fire or lava.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier Factor for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double multiplierFactor = 1.33; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fireMultiplier = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lava Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lavaMultiplier = 0.69; + double pct = (getFireBoost(factor) * f) + (getLavaBoost(factor) * l) + 1; + int warp = (int) ((getInterval() / 50D) * pct); + b.setBrewingTime(Math.max(1, b.getBrewingTime() - warp)); + b.update(); + + if (M.r(1D / (333D / getInterval()))) { + SoundPlayer spw = SoundPlayer.of(b.getBlock().getWorld()); + spw.play(b.getBlock().getLocation(), Sound.BLOCK_FIRE_AMBIENT, 1f, 1f + RNG.r.f(0.3f, 0.6f)); } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Brewing stands work faster when surrounded by fire or lava.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Multiplier Factor for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double multiplierFactor = 1.33; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fireMultiplier = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lava Multiplier for the Brewing Super Heated adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lavaMultiplier = 0.69; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java index 1acb1db34..f8d561c83 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAberrantTouch.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -42,185 +42,185 @@ import java.util.UUID; public class ChronosAberrantTouch extends SimpleAdaptation { - private final Map cooldowns; - private final Map targetStacks; - - public ChronosAberrantTouch() { - super("chronos-aberrant-touch"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("chronos.aberrant_touch.description")); - setDisplayName(Localizer.dLocalize("chronos.aberrant_touch.name")); - setIcon(Material.SPIDER_EYE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1000); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - targetStacks = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLOCK) - .key("challenge_chronos_aberrant_500") - .title(Localizer.dLocalize("advancement.challenge_chronos_aberrant_500.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_aberrant_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLOCK) - .key("challenge_chronos_aberrant_frozen") - .title(Localizer.dLocalize("advancement.challenge_chronos_aberrant_frozen.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_aberrant_frozen.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_chronos_aberrant_500", "chronos.aberrant-touch.slowness-stacks-applied", 500, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("chronos.aberrant_touch.lore1")); - v.addLore(C.YELLOW + "+ " + getPvEDurationCapTicks(level) / 20D + "s " + Localizer.dLocalize("chronos.aberrant_touch.lore2")); - v.addLore(C.RED + "* " + getConfig().playerAmplifierCap + " " + Localizer.dLocalize("chronos.aberrant_touch.lore3")); - v.addLore(C.AQUA + "* " + getConfig().rootAtStacks + " stacks roots for " + (getConfig().rootDurationTicks / 20D) + "s"); - } - - private int getPvEAmplifierCap(int level) { - return Math.max(0, Math.min(getConfig().entityAmplifierCap, level)); - } - - private int getPvEDurationCapTicks(int level) { - return getConfig().entityDurationCapTicks + (level * getConfig().entityDurationCapPerLevelTicks); - } - - private int getDurationAddedTicks(int level) { - return getConfig().durationAddTicks + (level * getConfig().durationPerLevelTicks); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player attacker)) { - return; - } - - long now = System.currentTimeMillis(); - long cooldownUntil = cooldowns.getOrDefault(attacker.getUniqueId(), 0L); - if (cooldownUntil > now) { - return; - } - - var combat = resolveMeleeContext(e); - if (combat == null) { - return; - } - - attacker = combat.attacker(); - LivingEntity target = combat.target(); - if (!getPlayer(attacker).consumeFood(getConfig().hungerCost, getConfig().minimumFoodLevel)) { - return; - } - - int level = combat.level(); - int amplifierCap = target instanceof Player ? getConfig().playerAmplifierCap : getPvEAmplifierCap(level); - int durationCap = target instanceof Player ? getConfig().playerDurationCapTicks : getPvEDurationCapTicks(level); - - PotionEffect existing = target.getPotionEffect(PotionEffectType.SLOWNESS); - int currentAmplifier = existing == null ? -1 : existing.getAmplifier(); - int currentDuration = existing == null ? 0 : existing.getDuration(); - - int newAmplifier = Math.min(amplifierCap, currentAmplifier + 1); - int newDuration = Math.min(durationCap, currentDuration + getDurationAddedTicks(level)); - - target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, Math.max(20, newDuration), Math.max(0, newAmplifier), true, true, true), true); - getPlayer(attacker).getData().addStat("chronos.aberrant-touch.slowness-stacks-applied", 1); - - StackState state = targetStacks.getOrDefault(target.getUniqueId(), new StackState(0, 0L)); - int stacks = (now - state.lastHitMillis() > getConfig().stackResetMillis) ? 1 : state.stacks() + 1; - if (stacks >= getConfig().rootAtStacks) { - target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().rootDurationTicks, getConfig().rootAmplifier, true, true, true), true); - target.setVelocity(new Vector()); - stacks = 0; - if (AdaptConfig.get().isAdvancements() && !getPlayer(attacker).getData().isGranted("challenge_chronos_aberrant_frozen")) { - getPlayer(attacker).getAdvancementHandler().grant("challenge_chronos_aberrant_frozen"); - } - } - targetStacks.put(target.getUniqueId(), new StackState(stacks, now)); - - if (getConfig().playClockSounds) { - ChronosSoundFX.playTouchProc(attacker, target.getLocation()); - } - xp(attacker, attacker.getLocation(), getConfig().xpPerProc + (getConfig().xpPerLevel * level)); - cooldowns.put(attacker.getUniqueId(), now + getConfig().cooldownMillis); + private final Map cooldowns; + private final Map targetStacks; + + public ChronosAberrantTouch() { + super("chronos-aberrant-touch"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.aberrant_touch.description")); + setDisplayName(Localizer.dLocalize("chronos.aberrant_touch.name")); + setIcon(Material.SPIDER_EYE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + targetStacks = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_aberrant_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_aberrant_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_aberrant_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_aberrant_frozen") + .title(Localizer.dLocalize("advancement.challenge_chronos_aberrant_frozen.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_aberrant_frozen.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_aberrant_500", "chronos.aberrant-touch.slowness-stacks-applied", 500, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("chronos.aberrant_touch.lore1")); + v.addLore(C.YELLOW + "+ " + getPvEDurationCapTicks(level) / 20D + "s " + Localizer.dLocalize("chronos.aberrant_touch.lore2")); + v.addLore(C.RED + "* " + getConfig().playerAmplifierCap + " " + Localizer.dLocalize("chronos.aberrant_touch.lore3")); + v.addLore(C.AQUA + "* " + getConfig().rootAtStacks + " stacks roots for " + (getConfig().rootDurationTicks / 20D) + "s"); + } + + private int getPvEAmplifierCap(int level) { + return Math.max(0, Math.min(getConfig().entityAmplifierCap, level)); + } + + private int getPvEDurationCapTicks(int level) { + return getConfig().entityDurationCapTicks + (level * getConfig().entityDurationCapPerLevelTicks); + } + + private int getDurationAddedTicks(int level) { + return getConfig().durationAddTicks + (level * getConfig().durationPerLevelTicks); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player attacker)) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); - targetStacks.entrySet().removeIf(entry -> now - entry.getValue().lastHitMillis() > getConfig().stackResetMillis); + long now = System.currentTimeMillis(); + long cooldownUntil = cooldowns.getOrDefault(attacker.getUniqueId(), 0L); + if (cooldownUntil > now) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e); + if (combat == null) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + attacker = combat.attacker(); + LivingEntity target = combat.target(); + if (!getPlayer(attacker).consumeFood(getConfig().hungerCost, getConfig().minimumFoodLevel)) { + return; } - @NoArgsConstructor - @ConfigDescription("Melee attacks apply stacking slowness at the cost of hunger, with PvP caps.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Aberrant Touch adaptation.", impact = "True enables this behavior and false disables it.") - boolean playClockSounds = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.38; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Add Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int durationAddTicks = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int durationPerLevelTicks = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int playerDurationCapTicks = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int playerAmplifierCap = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int entityDurationCapTicks = 120; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int entityDurationCapPerLevelTicks = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int entityAmplifierCap = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerCost = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Food Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int minimumFoodLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root At Stacks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rootAtStacks = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Duration Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rootDurationTicks = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Amplifier for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rootAmplifier = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Reset Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long stackResetMillis = 2500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownMillis = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Proc for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerProc = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerLevel = 1.25; + int level = combat.level(); + int amplifierCap = target instanceof Player ? getConfig().playerAmplifierCap : getPvEAmplifierCap(level); + int durationCap = target instanceof Player ? getConfig().playerDurationCapTicks : getPvEDurationCapTicks(level); + + PotionEffect existing = target.getPotionEffect(PotionEffectType.SLOWNESS); + int currentAmplifier = existing == null ? -1 : existing.getAmplifier(); + int currentDuration = existing == null ? 0 : existing.getDuration(); + + int newAmplifier = Math.min(amplifierCap, currentAmplifier + 1); + int newDuration = Math.min(durationCap, currentDuration + getDurationAddedTicks(level)); + + target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, Math.max(20, newDuration), Math.max(0, newAmplifier), true, true, true), true); + getPlayer(attacker).getData().addStat("chronos.aberrant-touch.slowness-stacks-applied", 1); + + StackState state = targetStacks.getOrDefault(target.getUniqueId(), new StackState(0, 0L)); + int stacks = (now - state.lastHitMillis() > getConfig().stackResetMillis) ? 1 : state.stacks() + 1; + if (stacks >= getConfig().rootAtStacks) { + target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().rootDurationTicks, getConfig().rootAmplifier, true, true, true), true); + target.setVelocity(new Vector()); + stacks = 0; + if (AdaptConfig.get().isAdvancements() && !getPlayer(attacker).getData().isGranted("challenge_chronos_aberrant_frozen")) { + getPlayer(attacker).getAdvancementHandler().grant("challenge_chronos_aberrant_frozen"); + } } + targetStacks.put(target.getUniqueId(), new StackState(stacks, now)); - private record StackState(int stacks, long lastHitMillis) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playTouchProc(attacker, target.getLocation()); } + xp(attacker, attacker.getLocation(), getConfig().xpPerProc + (getConfig().xpPerLevel * level)); + cooldowns.put(attacker.getUniqueId(), now + getConfig().cooldownMillis); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + targetStacks.entrySet().removeIf(entry -> now - entry.getValue().lastHitMillis() > getConfig().stackResetMillis); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Melee attacks apply stacking slowness at the cost of hunger, with PvP caps.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Aberrant Touch adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.38; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Add Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int durationAddTicks = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int durationPerLevelTicks = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int playerDurationCapTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int playerAmplifierCap = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int entityDurationCapTicks = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Duration Cap Per Level Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int entityDurationCapPerLevelTicks = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Amplifier Cap for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int entityAmplifierCap = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerCost = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Food Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int minimumFoodLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root At Stacks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rootAtStacks = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Duration Ticks for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rootDurationTicks = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Root Amplifier for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rootAmplifier = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Reset Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long stackResetMillis = 2500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownMillis = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Proc for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerProc = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Aberrant Touch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerLevel = 1.25; + } + + private record StackState(int stacks, long lastHitMillis) { + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index ec68bb7af..b666cd135 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -26,12 +26,10 @@ import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; -import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; -import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.entity.ArmorStand; @@ -53,1265 +51,1109 @@ import java.util.function.Consumer; import java.util.function.Predicate; -public class ChronosInstantRecall extends SimpleAdaptation { - private static final EnumSet RECALL_ACTIONS = EnumSet.of( - Action.RIGHT_CLICK_AIR, - Action.RIGHT_CLICK_BLOCK, - Action.LEFT_CLICK_AIR, - Action.LEFT_CLICK_BLOCK - ); - private static final Map TELEPORT_XP_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); - - private final Map> snapshots; - private final Map lastSnapshot; - private final Map cooldowns; - private final Set cooldownReadyNotify; - private final Map rewindProtection; - private final Set rewinding; - private final Map recallXpStamps; - private final Map jumpArmUntil; - private final Map lastOnGround; - - public ChronosInstantRecall() { - super("chronos-instant-recall"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("chronos.instant_recall.description")); - setDisplayName(Localizer.dLocalize("chronos.instant_recall.name")); - setIcon(Material.RECOVERY_COMPASS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(50); - snapshots = new ConcurrentHashMap<>(); - lastSnapshot = new ConcurrentHashMap<>(); - cooldowns = new ConcurrentHashMap<>(); - cooldownReadyNotify = ConcurrentHashMap.newKeySet(); - rewindProtection = new ConcurrentHashMap<>(); - rewinding = ConcurrentHashMap.newKeySet(); - recallXpStamps = new ConcurrentHashMap<>(); - jumpArmUntil = new ConcurrentHashMap<>(); - lastOnGround = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLOCK) - .key("challenge_chronos_recall_50") - .title(Localizer.dLocalize("advancement.challenge_chronos_recall_50.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_recall_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RECOVERY_COMPASS) - .key("challenge_chronos_recall_1k") - .title(Localizer.dLocalize("advancement.challenge_chronos_recall_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_recall_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RECOVERY_COMPASS) - .key("challenge_chronos_recall_cheat_death") - .title(Localizer.dLocalize("advancement.challenge_chronos_recall_cheat_death.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_recall_cheat_death.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_chronos_recall_50", "chronos.instant-recall.recalls", 50, 300); - registerMilestone("challenge_chronos_recall_1k", "chronos.instant-recall.recalls", 1000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration(getRewindDurationMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore1")); - v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore2")); - v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.instant_recall.lore3")); - List combos = getTriggerCombos(); - if (combos.isEmpty()) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); - return; - } - - for (String combo : combos) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); - } - } - - @Override - public String getDescription() { - return "Rewind to a recent snapshot with health and hunger restored. " + summarizeTriggerDescription(); - } - - private String summarizeTriggerDescription() { - List combos = getTriggerCombos(); - if (combos.isEmpty()) { - return "No active triggers are currently enabled."; - } - - if (combos.size() == 1) { - return "Trigger: " + combos.get(0) + "."; - } +public class ChronosInstantRecall extends SimpleAdaptation { + private static final EnumSet RECALL_ACTIONS = EnumSet.of( + Action.RIGHT_CLICK_AIR, + Action.RIGHT_CLICK_BLOCK, + Action.LEFT_CLICK_AIR, + Action.LEFT_CLICK_BLOCK + ); + private static final Map TELEPORT_XP_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); + + private final Map> snapshots = new ConcurrentHashMap<>(); + private final Map lastSnapshot = new ConcurrentHashMap<>(); + private final Map cooldowns = new ConcurrentHashMap<>(); + private final Set cooldownReadyNotify = ConcurrentHashMap.newKeySet(); + private final Map rewindProtection = new ConcurrentHashMap<>(); + private final Set rewinding = ConcurrentHashMap.newKeySet(); + private final Map recallXpStamps = new ConcurrentHashMap<>(); + private final Map jumpArmUntil = new ConcurrentHashMap<>(); + private final Map lastOnGround = new ConcurrentHashMap<>(); + + public ChronosInstantRecall() { + super("chronos-instant-recall"); + registerConfiguration(ChronosInstantRecallConfig.class); + setDescription(Localizer.dLocalize("chronos.instant_recall.description")); + setDisplayName(Localizer.dLocalize("chronos.instant_recall.name")); + setIcon(Material.RECOVERY_COMPASS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(50); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_recall_50") + .title(Localizer.dLocalize("advancement.challenge_chronos_recall_50.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_recall_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_chronos_recall_1k") + .title(Localizer.dLocalize("advancement.challenge_chronos_recall_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_recall_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_chronos_recall_cheat_death") + .title(Localizer.dLocalize("advancement.challenge_chronos_recall_cheat_death.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_recall_cheat_death.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_recall_50", "chronos.instant-recall.recalls", 50, 300); + registerMilestone("challenge_chronos_recall_1k", "chronos.instant-recall.recalls", 1000, 1500); + } + + private static void markRecallTeleportSuppressed(UUID id, long suppressUntilMillis) { + long current = TELEPORT_XP_SUPPRESS_UNTIL.getOrDefault(id, 0L); + if (suppressUntilMillis > current) { + TELEPORT_XP_SUPPRESS_UNTIL.put(id, suppressUntilMillis); + } + } + + public static boolean isRecallTeleportSuppressed(Player p) { + if (p == null) { + return false; + } + + UUID id = p.getUniqueId(); + long until = TELEPORT_XP_SUPPRESS_UNTIL.getOrDefault(id, 0L); + if (until <= M.ms()) { + TELEPORT_XP_SUPPRESS_UNTIL.remove(id); + return false; + } + + return true; + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getRewindDurationMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore1")); + v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.instant_recall.lore3")); + List combos = getTriggerCombos(); + if (combos.isEmpty()) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); + return; + } + + for (String combo : combos) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); + } + } + + @Override + public String getDescription() { + return "Rewind to a recent snapshot with health and hunger restored. " + summarizeTriggerDescription(); + } + + private String summarizeTriggerDescription() { + List combos = getTriggerCombos(); + if (combos.isEmpty()) { + return "No active triggers are currently enabled."; + } + + if (combos.size() == 1) { + return "Trigger: " + combos.get(0) + "."; + } + + if (combos.size() == 2) { + return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; + } + + return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; + } + + private long getRewindDurationMillis(int level) { + return (long) (getRewindDurationSeconds(level) * 1000D); + } + + private double getRewindDurationSeconds(int level) { + double raw = getConfig().baseRewindSeconds + (level * getConfig().rewindSecondsPerLevel); + return Math.max(0.25D, Math.min(getConfig().maxRewindSeconds, raw)); + } - if (combos.size() == 2) { - return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; - } + private long getCooldownMillis(int level) { + return getRewindDurationMillis(level) + (getConfig().cooldownPaddingSeconds * 1000L); + } - return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; - } + private long getMaximumHistoryMillis() { + return (long) ((getRewindDurationSeconds(getConfig().maxLevel) + getConfig().historyPaddingSeconds) * 1000D); + } - private long getRewindDurationMillis(int level) { - return (long) (getRewindDurationSeconds(level) * 1000D); + private int getRewindAnimationTicks() { + int durationMillis = Math.max(100, getConfig().rewindAnimationDurationMillis); + int ticks = (int) Math.ceil(durationMillis / 50D); + if (ticks <= 1) { + ticks = Math.max(2, getConfig().rewindAnimationTicks); } - private double getRewindDurationSeconds(int level) { - double raw = getConfig().baseRewindSeconds + (level * getConfig().rewindSecondsPerLevel); - return Math.max(0.25D, Math.min(getConfig().maxRewindSeconds, raw)); - } + return Math.max(2, ticks); + } - private long getCooldownMillis(int level) { - return getRewindDurationMillis(level) + (getConfig().cooldownPaddingSeconds * 1000L); + private boolean isSingleWorldPath(List path) { + if (path.isEmpty()) { + return false; } - private long getMaximumHistoryMillis() { - return (long) ((getRewindDurationSeconds(getConfig().maxLevel) + getConfig().historyPaddingSeconds) * 1000D); + String world = path.get(0).worldName(); + for (Snapshot snapshot : path) { + if (!Objects.equals(world, snapshot.worldName())) { + return false; + } } - private int getRewindAnimationTicks() { - int durationMillis = Math.max(100, getConfig().rewindAnimationDurationMillis); - int ticks = (int) Math.ceil(durationMillis / 50D); - if (ticks <= 1) { - ticks = Math.max(2, getConfig().rewindAnimationTicks); - } + return true; + } - return Math.max(2, ticks); + private ArmorStand spawnRecallCameraAnchor(Player p) { + Location location = p.getLocation().clone(); + if (location.getWorld() == null) { + return null; } - private boolean isSingleWorldPath(List path) { - if (path.isEmpty()) { - return false; - } + return location.getWorld().spawn(location, ArmorStand.class, stand -> { + stand.setInvisible(true); + stand.setMarker(false); + stand.setGravity(false); + stand.setSilent(true); + stand.setInvulnerable(true); + stand.setCollidable(false); + stand.setBasePlate(false); + stand.setSmall(true); + stand.setPersistent(false); + }); + } - String world = path.get(0).worldName(); - for (Snapshot snapshot : path) { - if (!Objects.equals(world, snapshot.worldName())) { - return false; - } - } - - return true; + private List getTriggerCombos() { + List triggers = new ArrayList<>(); + String clickSurface = getClickSurfaceLabel(); + if (getConfig().enableClockClickTrigger) { + appendClickCombos(triggers, "Clock", getConfig().clockClickLeftClick, getConfig().clockClickRightClick, clickSurface); } - private ArmorStand spawnRecallCameraAnchor(Player p) { - Location location = p.getLocation().clone(); - if (location.getWorld() == null) { - return null; - } - - return location.getWorld().spawn(location, ArmorStand.class, stand -> { - stand.setInvisible(true); - stand.setMarker(false); - stand.setGravity(false); - stand.setSilent(true); - stand.setInvulnerable(true); - stand.setCollidable(false); - stand.setBasePlate(false); - stand.setSmall(true); - stand.setPersistent(false); - }); - } - - private List getTriggerCombos() { - List triggers = new ArrayList<>(); - String clickSurface = getClickSurfaceLabel(); - if (getConfig().enableClockClickTrigger) { - appendClickCombos(triggers, "Clock", getConfig().clockClickLeftClick, getConfig().clockClickRightClick, clickSurface); - } - - if (getConfig().enableSprintClickTrigger) { - appendClickCombos(triggers, "Sprint + Clock", getConfig().sprintClickLeftClick, getConfig().sprintClickRightClick, clickSurface); - } - - if (getConfig().enableSingleSneakTrigger) { - String combo = getConfig().singleSneakRequiresSprint ? "Sprint + Sneak" : "Sneak"; - if (getConfig().singleSneakRequiresClockInHand) { - combo += " + Clock"; - } - triggers.add(combo); - } - - if (getConfig().enableDoubleJumpTrigger) { - String combo = "Double Jump"; - if (getConfig().doubleJumpRequiresSprint) { - combo += " + Sprint"; - } - if (getConfig().doubleJumpRequiresClockInHand) { - combo += " + Clock"; - } - triggers.add(combo); - } - - return triggers; + if (getConfig().enableSprintClickTrigger) { + appendClickCombos(triggers, "Sprint + Clock", getConfig().sprintClickLeftClick, getConfig().sprintClickRightClick, clickSurface); } - private void appendClickCombos(List triggers, String prefix, boolean allowLeft, boolean allowRight, String clickSurface) { - if (clickSurface.isBlank()) { - return; - } - - if (allowLeft) { - triggers.add(prefix + " + Left Click" + clickSurface); - } - - if (allowRight) { - triggers.add(prefix + " + Right Click" + clickSurface); - } + if (getConfig().enableSingleSneakTrigger) { + String combo = getConfig().singleSneakRequiresSprint ? "Sprint + Sneak" : "Sneak"; + if (getConfig().singleSneakRequiresClockInHand) { + combo += " + Clock"; + } + triggers.add(combo); } - private String getClickSurfaceLabel() { - if (getConfig().allowAirClicks && getConfig().allowBlockClicks) { - return " (air/block)"; - } + if (getConfig().enableDoubleJumpTrigger) { + String combo = "Double Jump"; + if (getConfig().doubleJumpRequiresSprint) { + combo += " + Sprint"; + } + if (getConfig().doubleJumpRequiresClockInHand) { + combo += " + Clock"; + } + triggers.add(combo); + } - if (getConfig().allowAirClicks) { - return " (air)"; - } + return triggers; + } - if (getConfig().allowBlockClicks) { - return " (block)"; - } + private void appendClickCombos(List triggers, String prefix, boolean allowLeft, boolean allowRight, String clickSurface) { + if (clickSurface.isBlank()) { + return; + } - return ""; + if (allowLeft) { + triggers.add(prefix + " + Left Click" + clickSurface); } - private RecallXPContext buildRecallXPContext(Snapshot from, Snapshot to) { - double distance; - if (Objects.equals(from.worldName(), to.worldName())) { - double dx = from.x() - to.x(); - double dy = from.y() - to.y(); - double dz = from.z() - to.z(); - distance = Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); - } else { - distance = getConfig().xpCrossWorldDistanceCredit; - } + if (allowRight) { + triggers.add(prefix + " + Right Click" + clickSurface); + } + } - double healthRecovered = Math.max(0D, to.health() - from.health()); - double hungerRecovered = Math.max(0D, to.foodLevel() - from.foodLevel()); - double saturationRecovered = Math.max(0D, to.saturation() - from.saturation()); - - return new RecallXPContext( - from.worldName(), - from.x(), - from.y(), - from.z(), - to.worldName(), - to.x(), - to.y(), - to.z(), - distance, - healthRecovered, - hungerRecovered, - saturationRecovered); - } - - private boolean pointsAreSimilar(String worldA, double ax, double ay, double az, String worldB, double bx, double by, double bz, double radius) { - if (!Objects.equals(worldA, worldB)) { - return false; - } + private String getClickSurfaceLabel() { + if (getConfig().allowAirClicks && getConfig().allowBlockClicks) { + return " (air/block)"; + } - double dx = ax - bx; - double dy = ay - by; - double dz = az - bz; - return (dx * dx) + (dy * dy) + (dz * dz) <= (radius * radius); + if (getConfig().allowAirClicks) { + return " (air)"; } - private boolean isRepeatRecall(RecallXPFarmStamp stamp, RecallXPContext context) { - return pointsAreSimilar(stamp.fromWorld(), stamp.fromX(), stamp.fromY(), stamp.fromZ(), - context.fromWorld(), context.fromX(), context.fromY(), context.fromZ(), - getConfig().xpRepeatSourceRadius) - && pointsAreSimilar(stamp.toWorld(), stamp.toX(), stamp.toY(), stamp.toZ(), - context.toWorld(), context.toX(), context.toY(), context.toZ(), - getConfig().xpRepeatTargetRadius); + if (getConfig().allowBlockClicks) { + return " (block)"; } - private double computeRecallXPGain(UUID playerId, int level, RecallXPContext context, long now) { - double raw = (context.distance() * getConfig().xpPerDistanceBlock) - + (context.healthRecovered() * getConfig().xpPerHealthPoint) - + (context.hungerRecovered() * getConfig().xpPerHungerPoint) - + (context.saturationRecovered() * getConfig().xpPerSaturationPoint); + return ""; + } - if (raw < getConfig().xpMinRawReward) { - return 0D; - } + private RecallXPContext buildRecallXPContext(Snapshot from, Snapshot to) { + double distance; + if (Objects.equals(from.worldName(), to.worldName())) { + double dx = from.x() - to.x(); + double dy = from.y() - to.y(); + double dz = from.z() - to.z(); + distance = Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); + } else { + distance = getConfig().xpCrossWorldDistanceCredit; + } + + double healthRecovered = Math.max(0D, to.health() - from.health()); + double hungerRecovered = Math.max(0D, to.foodLevel() - from.foodLevel()); + double saturationRecovered = Math.max(0D, to.saturation() - from.saturation()); + + return new RecallXPContext( + from.worldName(), + from.x(), + from.y(), + from.z(), + to.worldName(), + to.x(), + to.y(), + to.z(), + distance, + healthRecovered, + hungerRecovered, + saturationRecovered); + } + + private boolean pointsAreSimilar(String worldA, double ax, double ay, double az, String worldB, double bx, double by, double bz, double radius) { + if (!Objects.equals(worldA, worldB)) { + return false; + } + + double dx = ax - bx; + double dy = ay - by; + double dz = az - bz; + return (dx * dx) + (dy * dy) + (dz * dz) <= (radius * radius); + } + + private boolean isRepeatRecall(RecallXPFarmStamp stamp, RecallXPContext context) { + return pointsAreSimilar(stamp.fromWorld(), stamp.fromX(), stamp.fromY(), stamp.fromZ(), + context.fromWorld(), context.fromX(), context.fromY(), context.fromZ(), + getConfig().xpRepeatSourceRadius) + && pointsAreSimilar(stamp.toWorld(), stamp.toX(), stamp.toY(), stamp.toZ(), + context.toWorld(), context.toX(), context.toY(), context.toZ(), + getConfig().xpRepeatTargetRadius); + } + + private double computeRecallXPGain(UUID playerId, int level, RecallXPContext context, long now) { + double raw = (context.distance() * getConfig().xpPerDistanceBlock) + + (context.healthRecovered() * getConfig().xpPerHealthPoint) + + (context.hungerRecovered() * getConfig().xpPerHungerPoint) + + (context.saturationRecovered() * getConfig().xpPerSaturationPoint); + + if (raw < getConfig().xpMinRawReward) { + return 0D; + } + + double leveled = raw * (1D + ((Math.max(1, level) - 1) * getConfig().xpLevelMultiplierPerLevel)); + double multiplier = 1D; + + RecallXPFarmStamp previous = recallXpStamps.get(playerId); + if (previous != null) { + long elapsed = now - previous.awardedAt(); + if (elapsed < getConfig().xpDiminishWindowMillis) { + double t = Math.max(0D, Math.min(1D, elapsed / (double) Math.max(1L, getConfig().xpDiminishWindowMillis))); + multiplier *= getConfig().xpDiminishMinMultiplier + ((1D - getConfig().xpDiminishMinMultiplier) * t); + } + + if (elapsed < getConfig().xpRepeatWindowMillis && isRepeatRecall(previous, context)) { + multiplier *= getConfig().xpRepeatPenaltyMultiplier; + } + } + + double reward = Math.min(getConfig().xpMaxAward, leveled * multiplier); + if (reward < getConfig().xpMinAward) { + return 0D; + } + + return reward; + } + + private Snapshot snapshotFromPlayer(Player p, long now) { + return new Snapshot(now, + p.getWorld().getName(), + p.getLocation().getX(), + p.getLocation().getY(), + p.getLocation().getZ(), + p.getLocation().getYaw(), + p.getLocation().getPitch(), + p.getHealth(), + p.getFoodLevel(), + p.getSaturation(), + p.getExhaustion(), + p.getFireTicks()); + } + + private Snapshot snapshotFromLocation(Player p, Location location, long now) { + World world = location.getWorld(); + if (world == null) { + world = p.getWorld(); + } + + return new Snapshot(now, + world.getName(), + location.getX(), + location.getY(), + location.getZ(), + location.getYaw(), + location.getPitch(), + p.getHealth(), + p.getFoodLevel(), + p.getSaturation(), + p.getExhaustion(), + p.getFireTicks()); + } + + private void resetSnapshotHistory(Player p, Location location) { + if (location == null) { + return; + } + + long now = M.ms(); + UUID id = p.getUniqueId(); + Deque queue = snapshots.computeIfAbsent(id, unused -> new ArrayDeque<>()); + queue.clear(); + queue.addLast(snapshotFromLocation(p, location, now)); + lastSnapshot.put(id, now); + } + + private void captureSnapshot(Player p) { + long now = M.ms(); + UUID id = p.getUniqueId(); + long last = lastSnapshot.getOrDefault(id, 0L); + if (now - last < getConfig().snapshotIntervalMillis) { + return; + } - double leveled = raw * (1D + ((Math.max(1, level) - 1) * getConfig().xpLevelMultiplierPerLevel)); - double multiplier = 1D; + lastSnapshot.put(id, now); + Deque queue = snapshots.computeIfAbsent(id, k -> new ArrayDeque<>()); + queue.addLast(snapshotFromPlayer(p, now)); + + long maxAge = getMaximumHistoryMillis(); + while (!queue.isEmpty() && now - queue.getFirst().timestamp() > maxAge) { + queue.removeFirst(); + } + } + + private Snapshot findSnapshot(Player p, long rewindMillis) { + Deque queue = snapshots.get(p.getUniqueId()); + if (queue == null || queue.isEmpty()) { + return null; + } - RecallXPFarmStamp previous = recallXpStamps.get(playerId); - if (previous != null) { - long elapsed = now - previous.awardedAt(); - if (elapsed < getConfig().xpDiminishWindowMillis) { - double t = Math.max(0D, Math.min(1D, elapsed / (double) Math.max(1L, getConfig().xpDiminishWindowMillis))); - multiplier *= getConfig().xpDiminishMinMultiplier + ((1D - getConfig().xpDiminishMinMultiplier) * t); - } + long target = M.ms() - rewindMillis; + Snapshot fallback = queue.getFirst(); - if (elapsed < getConfig().xpRepeatWindowMillis && isRepeatRecall(previous, context)) { - multiplier *= getConfig().xpRepeatPenaltyMultiplier; - } - } + for (Snapshot s : queue) { + if (s.timestamp() <= target) { + fallback = s; + } else { + break; + } + } + + return fallback; + } - double reward = Math.min(getConfig().xpMaxAward, leveled * multiplier); - if (reward < getConfig().xpMinAward) { - return 0D; - } + private List buildRewindPath(Player p, long rewindMillis, Snapshot anchor) { + List path = new ArrayList<>(); + long now = M.ms(); + path.add(snapshotFromPlayer(p, now)); - return reward; + Deque queue = snapshots.get(p.getUniqueId()); + if (queue == null || queue.isEmpty()) { + path.add(anchor); + return path; } - private Snapshot snapshotFromPlayer(Player p, long now) { - return new Snapshot(now, - p.getWorld().getName(), - p.getLocation().getX(), - p.getLocation().getY(), - p.getLocation().getZ(), - p.getLocation().getYaw(), - p.getLocation().getPitch(), - p.getHealth(), - p.getFoodLevel(), - p.getSaturation(), - p.getExhaustion(), - p.getFireTicks()); + Iterator reverse = queue.descendingIterator(); + while (reverse.hasNext()) { + Snapshot snap = reverse.next(); + if (snap.timestamp() < anchor.timestamp()) { + break; + } + if (snap.timestamp() <= now) { + path.add(snap); + } + } + + Snapshot last = path.get(path.size() - 1); + if (last.timestamp() != anchor.timestamp()) { + path.add(anchor); + } + + if (path.size() < 2) { + path.add(anchor); + } + + return path; + } + + private List buildAnimationPath(List rawPath, int animationTicks) { + List animationPath = new ArrayList<>(); + if (rawPath.isEmpty()) { + return animationPath; } - private Snapshot snapshotFromLocation(Player p, Location location, long now) { - World world = location.getWorld(); - if (world == null) { - world = p.getWorld(); - } - - return new Snapshot(now, - world.getName(), - location.getX(), - location.getY(), - location.getZ(), - location.getYaw(), - location.getPitch(), - p.getHealth(), - p.getFoodLevel(), - p.getSaturation(), - p.getExhaustion(), - p.getFireTicks()); - } - - private void resetSnapshotHistory(Player p, Location location) { - if (location == null) { - return; - } - - long now = M.ms(); - UUID id = p.getUniqueId(); - Deque queue = snapshots.computeIfAbsent(id, unused -> new ArrayDeque<>()); - queue.clear(); - queue.addLast(snapshotFromLocation(p, location, now)); - lastSnapshot.put(id, now); + if (animationTicks <= 1 || rawPath.size() == 1) { + animationPath.add(rawPath.get(rawPath.size() - 1)); + return animationPath; } - private void captureSnapshot(Player p) { - long now = M.ms(); - UUID id = p.getUniqueId(); - long last = lastSnapshot.getOrDefault(id, 0L); - if (now - last < getConfig().snapshotIntervalMillis) { - return; - } + for (int step = 0; step < animationTicks; step++) { + double progress = step / (double) (animationTicks - 1); + double scaled = progress * (rawPath.size() - 1); + int lower = (int) Math.floor(scaled); + int upper = Math.min(rawPath.size() - 1, lower + 1); + double alpha = scaled - lower; + Snapshot a = rawPath.get(lower); + Snapshot b = rawPath.get(upper); + animationPath.add(interpolateSnapshot(a, b, alpha)); + } - lastSnapshot.put(id, now); - Deque queue = snapshots.computeIfAbsent(id, k -> new ArrayDeque<>()); - queue.addLast(snapshotFromPlayer(p, now)); + Snapshot anchor = rawPath.get(rawPath.size() - 1); + animationPath.set(animationPath.size() - 1, anchor); + return animationPath; + } - long maxAge = getMaximumHistoryMillis(); - while (!queue.isEmpty() && now - queue.getFirst().timestamp() > maxAge) { - queue.removeFirst(); - } + private Snapshot interpolateSnapshot(Snapshot a, Snapshot b, double alpha) { + if (alpha <= 0D) { + return a; + } + if (alpha >= 1D) { + return b; } - private Snapshot findSnapshot(Player p, long rewindMillis) { - Deque queue = snapshots.get(p.getUniqueId()); - if (queue == null || queue.isEmpty()) { - return null; - } + long timestamp = (long) Math.round(lerp(a.timestamp(), b.timestamp(), alpha)); + String worldName = alpha < 0.5D ? a.worldName() : b.worldName(); + double x = lerp(a.x(), b.x(), alpha); + double y = lerp(a.y(), b.y(), alpha); + double z = lerp(a.z(), b.z(), alpha); + float yaw = lerpAngle(a.yaw(), b.yaw(), alpha); + float pitch = (float) lerp(a.pitch(), b.pitch(), alpha); + double health = lerp(a.health(), b.health(), alpha); + int foodLevel = (int) Math.round(lerp(a.foodLevel(), b.foodLevel(), alpha)); + float saturation = (float) lerp(a.saturation(), b.saturation(), alpha); + float exhaustion = (float) lerp(a.exhaustion(), b.exhaustion(), alpha); + int fireTicks = (int) Math.round(lerp(a.fireTicks(), b.fireTicks(), alpha)); - long target = M.ms() - rewindMillis; - Snapshot fallback = queue.getFirst(); + return new Snapshot(timestamp, worldName, x, y, z, yaw, pitch, health, foodLevel, saturation, exhaustion, fireTicks); + } - for (Snapshot s : queue) { - if (s.timestamp() <= target) { - fallback = s; - } else { - break; - } - } + private double lerp(double a, double b, double alpha) { + return a + ((b - a) * alpha); + } - return fallback; + private float lerpAngle(float from, float to, double alpha) { + float delta = to - from; + while (delta > 180F) { + delta -= 360F; + } + while (delta < -180F) { + delta += 360F; } - private List buildRewindPath(Player p, long rewindMillis, Snapshot anchor) { - List path = new ArrayList<>(); - long now = M.ms(); - path.add(snapshotFromPlayer(p, now)); - - Deque queue = snapshots.get(p.getUniqueId()); - if (queue == null || queue.isEmpty()) { - path.add(anchor); - return path; - } + return from + (float) (delta * alpha); + } - Iterator reverse = queue.descendingIterator(); - while (reverse.hasNext()) { - Snapshot snap = reverse.next(); - if (snap.timestamp() < anchor.timestamp()) { - break; - } - if (snap.timestamp() <= now) { - path.add(snap); - } - } + private Location toLocation(Snapshot snapshot, World fallback) { + World world = Bukkit.getWorld(snapshot.worldName()); + if (world == null) { + world = fallback; + } + return new Location(world, snapshot.x(), snapshot.y(), snapshot.z(), snapshot.yaw(), snapshot.pitch()); + } - Snapshot last = path.get(path.size() - 1); - if (last.timestamp() != anchor.timestamp()) { - path.add(anchor); - } + private void applySnapshotState(Player p, Snapshot snapshot) { + double maxHealth = p.getAttribute(Attribute.MAX_HEALTH) == null ? 20D : p.getAttribute(Attribute.MAX_HEALTH).getValue(); + p.setHealth(Math.max(1, Math.min(maxHealth, snapshot.health()))); + p.setFoodLevel(Math.max(0, Math.min(20, snapshot.foodLevel()))); + p.setSaturation(Math.max(0, snapshot.saturation())); + p.setExhaustion(Math.max(0, snapshot.exhaustion())); + p.setFireTicks(Math.max(0, snapshot.fireTicks())); + p.setFallDistance(0); + p.setVelocity(new Vector()); + } - if (path.size() < 2) { - path.add(anchor); - } + @EventHandler + public void on(PlayerQuitEvent e) { + clearPlayerState(e.getPlayer().getUniqueId()); + } - return path; + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerTeleportEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isRecallEligible(p) || rewinding.contains(id)) { + return; } - private List buildAnimationPath(List rawPath, int animationTicks) { - List animationPath = new ArrayList<>(); - if (rawPath.isEmpty()) { - return animationPath; - } + Location destination = e.getTo(); + if (destination == null) { + return; + } - if (animationTicks <= 1 || rawPath.size() == 1) { - animationPath.add(rawPath.get(rawPath.size() - 1)); - return animationPath; - } + resetSnapshotHistory(p, destination); + } - for (int step = 0; step < animationTicks; step++) { - double progress = step / (double) (animationTicks - 1); - double scaled = progress * (rawPath.size() - 1); - int lower = (int) Math.floor(scaled); - int upper = Math.min(rawPath.size() - 1, lower + 1); - double alpha = scaled - lower; - Snapshot a = rawPath.get(lower); - Snapshot b = rawPath.get(upper); - animationPath.add(interpolateSnapshot(a, b, alpha)); - } - - Snapshot anchor = rawPath.get(rawPath.size() - 1); - animationPath.set(animationPath.size() - 1, anchor); - return animationPath; + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerChangedWorldEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isRecallEligible(p) || rewinding.contains(id)) { + return; } - private Snapshot interpolateSnapshot(Snapshot a, Snapshot b, double alpha) { - if (alpha <= 0D) { - return a; - } - if (alpha >= 1D) { - return b; - } + resetSnapshotHistory(p, p.getLocation()); + } - long timestamp = (long) Math.round(lerp(a.timestamp(), b.timestamp(), alpha)); - String worldName = alpha < 0.5D ? a.worldName() : b.worldName(); - double x = lerp(a.x(), b.x(), alpha); - double y = lerp(a.y(), b.y(), alpha); - double z = lerp(a.z(), b.z(), alpha); - float yaw = lerpAngle(a.yaw(), b.yaw(), alpha); - float pitch = (float) lerp(a.pitch(), b.pitch(), alpha); - double health = lerp(a.health(), b.health(), alpha); - int foodLevel = (int) Math.round(lerp(a.foodLevel(), b.foodLevel(), alpha)); - float saturation = (float) lerp(a.saturation(), b.saturation(), alpha); - float exhaustion = (float) lerp(a.exhaustion(), b.exhaustion(), alpha); - int fireTicks = (int) Math.round(lerp(a.fireTicks(), b.fireTicks(), alpha)); - - return new Snapshot(timestamp, worldName, x, y, z, yaw, pitch, health, foodLevel, saturation, exhaustion, fireTicks); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (!isRecallEligible(p)) { + return; } - private double lerp(double a, double b, double alpha) { - return a + ((b - a) * alpha); + if (shouldTriggerSprintClockClick(e)) { + e.setCancelled(true); + attemptRecall(p); + return; } - private float lerpAngle(float from, float to, double alpha) { - float delta = to - from; - while (delta > 180F) { - delta -= 360F; - } - while (delta < -180F) { - delta += 360F; - } - - return from + (float) (delta * alpha); + if (shouldTriggerClockClick(e)) { + e.setCancelled(true); + attemptRecall(p); } + } - private Location toLocation(Snapshot snapshot, World fallback) { - World world = Bukkit.getWorld(snapshot.worldName()); - if (world == null) { - world = fallback; - } - return new Location(world, snapshot.x(), snapshot.y(), snapshot.z(), snapshot.yaw(), snapshot.pitch()); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerToggleFlightEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isRecallEligible(p) || !getConfig().enableDoubleJumpTrigger) { + return; } - private void applySnapshotState(Player p, Snapshot snapshot) { - double maxHealth = p.getAttribute(Attribute.MAX_HEALTH) == null ? 20D : p.getAttribute(Attribute.MAX_HEALTH).getValue(); - p.setHealth(Math.max(1, Math.min(maxHealth, snapshot.health()))); - p.setFoodLevel(Math.max(0, Math.min(20, snapshot.foodLevel()))); - p.setSaturation(Math.max(0, snapshot.saturation())); - p.setExhaustion(Math.max(0, snapshot.exhaustion())); - p.setFireTicks(Math.max(0, snapshot.fireTicks())); - p.setFallDistance(0); - p.setVelocity(new Vector()); + Long armUntil = jumpArmUntil.get(id); + if (armUntil == null) { + return; } - @EventHandler - public void on(PlayerQuitEvent e) { - clearPlayerState(e.getPlayer().getUniqueId()); + e.setCancelled(true); + p.setFlying(false); + clearDoubleJumpArm(p, id); + if (armUntil > M.ms()) { + attemptRecall(p); } + } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerTeleportEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (!isRecallEligible(p) || rewinding.contains(id)) { - return; - } - - Location destination = e.getTo(); - if (destination == null) { - return; - } - - resetSnapshotHistory(p, destination); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!e.isSneaking() || !isRecallEligible(p) || !getConfig().enableSingleSneakTrigger) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerChangedWorldEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (!isRecallEligible(p) || rewinding.contains(id)) { - return; - } - - resetSnapshotHistory(p, p.getLocation()); + if (getConfig().singleSneakRequiresSprint && !p.isSprinting()) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (!isRecallEligible(p)) { - return; - } - - if (shouldTriggerSprintClockClick(e)) { - e.setCancelled(true); - attemptRecall(p); - return; - } - - if (shouldTriggerClockClick(e)) { - e.setCancelled(true); - attemptRecall(p); - } + if (getConfig().singleSneakRequiresClockInHand && !hasRecallClockInEitherHand(p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerToggleFlightEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (!isRecallEligible(p) || !getConfig().enableDoubleJumpTrigger) { - return; - } + attemptRecall(p); + } - Long armUntil = jumpArmUntil.get(id); - if (armUntil == null) { - return; - } + private EquipmentSlot resolveRecallHand(Player p, EquipmentSlot eventHand) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); - e.setCancelled(true); - p.setFlying(false); - clearDoubleJumpArm(p, id); - if (armUntil > M.ms()) { - attemptRecall(p); - } - } + if (eventHand == null) { + if (isRecallClock(main)) { + return EquipmentSlot.HAND; + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (!e.isSneaking() || !isRecallEligible(p) || !getConfig().enableSingleSneakTrigger) { - return; - } + if (isRecallClock(off)) { + return EquipmentSlot.OFF_HAND; + } - if (getConfig().singleSneakRequiresSprint && !p.isSprinting()) { - return; - } - - if (getConfig().singleSneakRequiresClockInHand && !hasRecallClockInEitherHand(p)) { - return; - } + return null; + } - attemptRecall(p); + if (eventHand == EquipmentSlot.HAND) { + return isRecallClock(main) ? EquipmentSlot.HAND : null; } - private EquipmentSlot resolveRecallHand(Player p, EquipmentSlot eventHand) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); + if (eventHand == EquipmentSlot.OFF_HAND) { + if (isRecallClock(main)) { + return null; + } - if (eventHand == null) { - if (isRecallClock(main)) { - return EquipmentSlot.HAND; - } + return isRecallClock(off) ? EquipmentSlot.OFF_HAND : null; + } - if (isRecallClock(off)) { - return EquipmentSlot.OFF_HAND; - } + return null; + } - return null; - } + private boolean isRecallEligible(Player p) { + return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + } - if (eventHand == EquipmentSlot.HAND) { - return isRecallClock(main) ? EquipmentSlot.HAND : null; - } + private boolean isLeftClick(Action action) { + return action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK; + } - if (eventHand == EquipmentSlot.OFF_HAND) { - if (isRecallClock(main)) { - return null; - } + private boolean isRightClick(Action action) { + return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; + } - return isRecallClock(off) ? EquipmentSlot.OFF_HAND : null; - } + private boolean isBlockClick(Action action) { + return action == Action.LEFT_CLICK_BLOCK || action == Action.RIGHT_CLICK_BLOCK; + } - return null; + private boolean isActionAllowed(Action action) { + if (!RECALL_ACTIONS.contains(action)) { + return false; } - private boolean isRecallEligible(Player p) { - return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + if (!getConfig().allowAirClicks && !isBlockClick(action)) { + return false; } - private boolean isLeftClick(Action action) { - return action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK; + if (!getConfig().allowBlockClicks && isBlockClick(action)) { + return false; } - private boolean isRightClick(Action action) { - return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; - } + return true; + } - private boolean isBlockClick(Action action) { - return action == Action.LEFT_CLICK_BLOCK || action == Action.RIGHT_CLICK_BLOCK; + private boolean shouldTriggerClockClick(PlayerInteractEvent e) { + if (!getConfig().enableClockClickTrigger) { + return false; } - private boolean isActionAllowed(Action action) { - if (!RECALL_ACTIONS.contains(action)) { - return false; - } - - if (!getConfig().allowAirClicks && !isBlockClick(action)) { - return false; - } - - if (!getConfig().allowBlockClicks && isBlockClick(action)) { - return false; - } - - return true; + Action action = e.getAction(); + if (!isActionAllowed(action)) { + return false; } - private boolean shouldTriggerClockClick(PlayerInteractEvent e) { - if (!getConfig().enableClockClickTrigger) { - return false; - } - - Action action = e.getAction(); - if (!isActionAllowed(action)) { - return false; - } - - if (isLeftClick(action) && !getConfig().clockClickLeftClick) { - return false; - } - - if (isRightClick(action) && !getConfig().clockClickRightClick) { - return false; - } - - return resolveRecallHand(e.getPlayer(), e.getHand()) != null; + if (isLeftClick(action) && !getConfig().clockClickLeftClick) { + return false; } - private boolean shouldTriggerSprintClockClick(PlayerInteractEvent e) { - if (!getConfig().enableSprintClickTrigger || !e.getPlayer().isSprinting()) { - return false; - } - - Action action = e.getAction(); - if (!isActionAllowed(action)) { - return false; - } - - if (isLeftClick(action) && !getConfig().sprintClickLeftClick) { - return false; - } + if (isRightClick(action) && !getConfig().clockClickRightClick) { + return false; + } - if (isRightClick(action) && !getConfig().sprintClickRightClick) { - return false; - } + return resolveRecallHand(e.getPlayer(), e.getHand()) != null; + } - return resolveRecallHand(e.getPlayer(), e.getHand()) != null; + private boolean shouldTriggerSprintClockClick(PlayerInteractEvent e) { + if (!getConfig().enableSprintClickTrigger || !e.getPlayer().isSprinting()) { + return false; } - private boolean hasRecallClockInEitherHand(Player p) { - return isRecallClock(p.getInventory().getItemInMainHand()) - || isRecallClock(p.getInventory().getItemInOffHand()); + Action action = e.getAction(); + if (!isActionAllowed(action)) { + return false; } - private boolean canArmDoubleJump(Player p) { - if (rewinding.contains(p.getUniqueId())) { - return false; - } - - if (getConfig().doubleJumpRequiresSprint && !p.isSprinting()) { - return false; - } - - return !getConfig().doubleJumpRequiresClockInHand || hasRecallClockInEitherHand(p); + if (isLeftClick(action) && !getConfig().sprintClickLeftClick) { + return false; } - private void clearDoubleJumpArm(Player p, UUID id) { - if (jumpArmUntil.remove(id) == null) { - return; - } - - if (p.getGameMode() == GameMode.SURVIVAL) { - p.setAllowFlight(false); - p.setFlying(false); - } + if (isRightClick(action) && !getConfig().sprintClickRightClick) { + return false; } - private void armDoubleJump(Player p, UUID id) { - int triggerWindowMillis = Math.max(150, getConfig().doubleJumpWindowMillis); - jumpArmUntil.put(id, M.ms() + triggerWindowMillis); - p.setAllowFlight(true); - J.runEntity(p, () -> { - if (!p.isOnline()) { - return; - } + return resolveRecallHand(e.getPlayer(), e.getHand()) != null; + } - Long armUntil = jumpArmUntil.get(id); - if (armUntil != null && armUntil <= M.ms()) { - clearDoubleJumpArm(p, id); - } - }, Math.max(1, (int) Math.ceil(triggerWindowMillis / 50D))); - } + private boolean hasRecallClockInEitherHand(Player p) { + return isRecallClock(p.getInventory().getItemInMainHand()) + || isRecallClock(p.getInventory().getItemInOffHand()); + } - private boolean isDoubleJumpStart(boolean wasOnGround, boolean onGround, Player p) { - return wasOnGround - && !onGround - && p.getVelocity().getY() >= getConfig().doubleJumpMinVerticalVelocity; + private boolean canArmDoubleJump(Player p) { + if (rewinding.contains(p.getUniqueId())) { + return false; } - private void clearPlayerState(UUID id) { - snapshots.remove(id); - lastSnapshot.remove(id); - cooldowns.remove(id); - cooldownReadyNotify.remove(id); - rewindProtection.remove(id); - rewinding.remove(id); - recallXpStamps.remove(id); - jumpArmUntil.remove(id); - lastOnGround.remove(id); - TELEPORT_XP_SUPPRESS_UNTIL.remove(id); + if (getConfig().doubleJumpRequiresSprint && !p.isSprinting()) { + return false; } - private static void markRecallTeleportSuppressed(UUID id, long suppressUntilMillis) { - long current = TELEPORT_XP_SUPPRESS_UNTIL.getOrDefault(id, 0L); - if (suppressUntilMillis > current) { - TELEPORT_XP_SUPPRESS_UNTIL.put(id, suppressUntilMillis); - } - } + return !getConfig().doubleJumpRequiresClockInHand || hasRecallClockInEitherHand(p); + } - public static boolean isRecallTeleportSuppressed(Player p) { - if (p == null) { - return false; - } - - UUID id = p.getUniqueId(); - long until = TELEPORT_XP_SUPPRESS_UNTIL.getOrDefault(id, 0L); - if (until <= M.ms()) { - TELEPORT_XP_SUPPRESS_UNTIL.remove(id); - return false; - } - - return true; + private void clearDoubleJumpArm(Player p, UUID id) { + if (jumpArmUntil.remove(id) == null) { + return; } - private boolean isRecallClock(ItemStack stack) { - return stack != null - && stack.getType() == Material.CLOCK - && !ChronoTimeBombItem.isBindableItem(stack); + if (p.getGameMode() == GameMode.SURVIVAL) { + p.setAllowFlight(false); + p.setFlying(false); } + } - private void attemptRecall(Player p) { - UUID id = p.getUniqueId(); - if (!isRecallEligible(p)) { - return; - } + private void armDoubleJump(Player p, UUID id) { + int triggerWindowMillis = Math.max(150, getConfig().doubleJumpWindowMillis); + jumpArmUntil.put(id, M.ms() + triggerWindowMillis); + p.setAllowFlight(true); + J.runEntity(p, () -> { + if (!p.isOnline()) { + return; + } + Long armUntil = jumpArmUntil.get(id); + if (armUntil != null && armUntil <= M.ms()) { clearDoubleJumpArm(p, id); - if (rewinding.contains(id)) { - return; + } + }, Math.max(1, (int) Math.ceil(triggerWindowMillis / 50D))); + } + + private boolean isDoubleJumpStart(boolean wasOnGround, boolean onGround, Player p) { + return wasOnGround + && !onGround + && p.getVelocity().getY() >= getConfig().doubleJumpMinVerticalVelocity; + } + + private void clearPlayerState(UUID id) { + snapshots.remove(id); + lastSnapshot.remove(id); + cooldowns.remove(id); + cooldownReadyNotify.remove(id); + rewindProtection.remove(id); + rewinding.remove(id); + recallXpStamps.remove(id); + jumpArmUntil.remove(id); + lastOnGround.remove(id); + TELEPORT_XP_SUPPRESS_UNTIL.remove(id); + } + + private boolean isRecallClock(ItemStack stack) { + return stack != null + && stack.getType() == Material.CLOCK + && !ChronoTimeBombItem.isBindableItem(stack); + } + + private void attemptRecall(Player p) { + UUID id = p.getUniqueId(); + if (!isRecallEligible(p)) { + return; + } + + clearDoubleJumpArm(p, id); + if (rewinding.contains(id)) { + return; + } + + long now = M.ms(); + long cooldown = cooldowns.getOrDefault(id, 0L); + if (cooldown > now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + int level = getActiveLevel(p); + long rewindMillis = getRewindDurationMillis(level); + Snapshot anchor = findSnapshot(p, rewindMillis); + if (anchor == null) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + int animationTicks = getRewindAnimationTicks(); + List path = buildRewindPath(p, rewindMillis, anchor); + List animationPath = buildAnimationPath(path, animationTicks); + if (animationPath.isEmpty()) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + Snapshot finalSnapshot = animationPath.get(animationPath.size() - 1); + RecallXPContext xpContext = buildRecallXPContext(animationPath.get(0), finalSnapshot); + double healthBeforeRecall = p.getHealth(); + double healthAfterRecall = finalSnapshot.health(); + long castAt = M.ms(); + GameMode originalGameMode = p.getGameMode(); + boolean temporarySpectator = getConfig().rewindUseTemporarySpectator && originalGameMode == GameMode.SURVIVAL; + boolean allowClientCamera = temporarySpectator + && getConfig().rewindUseClientCamera + && isSingleWorldPath(animationPath); + ArmorStand cameraAnchor = null; + if (temporarySpectator) { + p.setGameMode(GameMode.SPECTATOR); + p.setFlying(true); + if (allowClientCamera) { + cameraAnchor = spawnRecallCameraAnchor(p); + if (cameraAnchor != null && cameraAnchor.isValid()) { + p.setSpectatorTarget(cameraAnchor); + } else { + allowClientCamera = false; } + } + } - long now = M.ms(); - long cooldown = cooldowns.getOrDefault(id, 0L); - if (cooldown > now) { - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - return; - } + cooldowns.put(id, castAt + getCooldownMillis(level)); + cooldownReadyNotify.add(id); + rewinding.add(id); + long protectionUntil = castAt + ((long) (animationTicks + getConfig().rewindProtectionTicks) * 50L); + rewindProtection.put(id, protectionUntil); + markRecallTeleportSuppressed(id, protectionUntil + ((long) Math.max(0, getConfig().rewindTeleportXpSuppressExtraTicks) * 50L)); + p.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, animationTicks + getConfig().rewindProtectionTicks, 0, true, false, false), true); - int level = getActiveLevel(p); - long rewindMillis = getRewindDurationMillis(level); - Snapshot anchor = findSnapshot(p, rewindMillis); - if (anchor == null) { - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - return; - } + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindStart(p); + ChronosSoundFX.playRewindFinish(p); + } - int animationTicks = getRewindAnimationTicks(); - List path = buildRewindPath(p, rewindMillis, anchor); - List animationPath = buildAnimationPath(path, animationTicks); - if (animationPath.isEmpty()) { - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - return; - } + final boolean initialClientCamera = allowClientCamera; + final ArmorStand initialCameraAnchor = cameraAnchor; + int[] step = {0}; + Location[] lastLoc = {p.getLocation().clone()}; + boolean[] clientCameraActive = {initialClientCamera}; + ArmorStand[] cameraRef = {initialCameraAnchor}; - Snapshot finalSnapshot = animationPath.get(animationPath.size() - 1); - RecallXPContext xpContext = buildRecallXPContext(animationPath.get(0), finalSnapshot); - double healthBeforeRecall = p.getHealth(); - double healthAfterRecall = finalSnapshot.health(); - long castAt = M.ms(); - GameMode originalGameMode = p.getGameMode(); - boolean temporarySpectator = getConfig().rewindUseTemporarySpectator && originalGameMode == GameMode.SURVIVAL; - boolean allowClientCamera = temporarySpectator - && getConfig().rewindUseClientCamera - && isSingleWorldPath(animationPath); - ArmorStand cameraAnchor = null; - if (temporarySpectator) { - p.setGameMode(GameMode.SPECTATOR); - p.setFlying(true); - if (allowClientCamera) { - cameraAnchor = spawnRecallCameraAnchor(p); - if (cameraAnchor != null && cameraAnchor.isValid()) { - p.setSpectatorTarget(cameraAnchor); - } else { - allowClientCamera = false; - } - } + Consumer cleanupVisualState = restoreGameMode -> { + if (cameraRef[0] != null) { + Entity anchorEntity = cameraRef[0]; + cameraRef[0] = null; + if (anchorEntity.isValid()) { + anchorEntity.remove(); } + } - cooldowns.put(id, castAt + getCooldownMillis(level)); - cooldownReadyNotify.add(id); - rewinding.add(id); - long protectionUntil = castAt + ((long) (animationTicks + getConfig().rewindProtectionTicks) * 50L); - rewindProtection.put(id, protectionUntil); - markRecallTeleportSuppressed(id, protectionUntil + ((long) Math.max(0, getConfig().rewindTeleportXpSuppressExtraTicks) * 50L)); - p.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, animationTicks + getConfig().rewindProtectionTicks, 0, true, false, false), true); - - if (getConfig().playClockSounds) { - ChronosSoundFX.playRewindStart(p); - ChronosSoundFX.playRewindFinish(p); + if (temporarySpectator) { + p.setSpectatorTarget(null); + if (restoreGameMode && p.getGameMode() == GameMode.SPECTATOR) { + p.setGameMode(originalGameMode); + p.setFlying(false); } + } + }; - final boolean initialClientCamera = allowClientCamera; - final ArmorStand initialCameraAnchor = cameraAnchor; - int[] step = {0}; - Location[] lastLoc = {p.getLocation().clone()}; - boolean[] clientCameraActive = {initialClientCamera}; - ArmorStand[] cameraRef = {initialCameraAnchor}; - - Consumer cleanupVisualState = restoreGameMode -> { - if (cameraRef[0] != null) { - Entity anchorEntity = cameraRef[0]; - cameraRef[0] = null; - if (anchorEntity.isValid()) { - anchorEntity.remove(); - } - } - - if (temporarySpectator) { - p.setSpectatorTarget(null); - if (restoreGameMode && p.getGameMode() == GameMode.SPECTATOR) { - p.setGameMode(originalGameMode); - p.setFlying(false); - } - } - }; - - Runnable[] rewindTask = new Runnable[1]; - rewindTask[0] = () -> { - if (!p.isOnline() || p.isDead()) { - rewinding.remove(id); - cleanupVisualState.accept(true); - return; - } - - float progress = animationTicks <= 1 ? 1f : (float) step[0] / (float) (animationTicks - 1); - int index = Math.min(animationPath.size() - 1, step[0]); - Snapshot snapshot = animationPath.get(index); - Location destination = toLocation(snapshot, p.getWorld()); - - if (getConfig().showRewindTraceParticles && lastLoc[0].getWorld() != null && lastLoc[0].getWorld().equals(destination.getWorld())) { - Predicate traceFilter = J.isFoliaThreading() ? null : l -> l.getBlock().isPassable(); - vfxParticleLine(lastLoc[0].clone().add(0, 1, 0), destination.clone().add(0, 1, 0), Particle.REVERSE_PORTAL, - Math.max(4, getConfig().rewindTracePoints), 1, 0.08D, 0.08D, 0.08D, 0D, null, true, - traceFilter); - } - - boolean movedClient = false; - if (clientCameraActive[0] && cameraRef[0] != null && cameraRef[0].isValid()) { - Entity target = p.getSpectatorTarget(); - if (target == null || !target.getUniqueId().equals(cameraRef[0].getUniqueId())) { - p.setSpectatorTarget(cameraRef[0]); - target = p.getSpectatorTarget(); - } - - if (target != null - && target.getUniqueId().equals(cameraRef[0].getUniqueId()) - && destination.getWorld() != null - && destination.getWorld().equals(cameraRef[0].getWorld())) { - J.teleport(cameraRef[0], destination, PlayerTeleportEvent.TeleportCause.PLUGIN); - movedClient = true; - } else { - clientCameraActive[0] = false; - } - } - - if (!movedClient) { - J.teleport(p, destination, PlayerTeleportEvent.TeleportCause.PLUGIN); - } - - if (!temporarySpectator || step[0] >= animationPath.size() - 1) { - applySnapshotState(p, snapshot); - } - - if (getConfig().playClockSounds) { - ChronosSoundFX.playRewindStep(p, progress); - } - - lastLoc[0] = destination; - step[0]++; - - if (step[0] >= animationTicks) { - cleanupVisualState.accept(true); - Location finalDestination = toLocation(finalSnapshot, p.getWorld()); - if (finalDestination.getWorld() != null) { - J.teleport(p, finalDestination, PlayerTeleportEvent.TeleportCause.PLUGIN); - } - applySnapshotState(p, finalSnapshot); - - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 26, 0.25, 0.35, 0.25, 0.01); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.ITEM, p.getLocation().add(0, 1, 0), 18, 0.30, 0.30, 0.30, 0.01, new ItemStack(Material.CLOCK)); - } - if (getConfig().playClockSounds) { - ChronosSoundFX.playRewindFinish(p); - } - rewinding.remove(id); - getPlayer(p).getData().addStat("chronos.instant-recall.recalls", 1); - if (healthBeforeRecall <= 4 && healthAfterRecall >= 16 - && AdaptConfig.get().isAdvancements() - && !getPlayer(p).getData().isGranted("challenge_chronos_recall_cheat_death")) { - getPlayer(p).getAdvancementHandler().grant("challenge_chronos_recall_cheat_death"); - } - long awardAt = M.ms(); - double xpGain = computeRecallXPGain(id, level, xpContext, awardAt); - if (xpGain > 0D) { - xp(p, p.getLocation(), xpGain); - recallXpStamps.put(id, new RecallXPFarmStamp( - awardAt, - xpContext.fromWorld(), - xpContext.fromX(), - xpContext.fromY(), - xpContext.fromZ(), - xpContext.toWorld(), - xpContext.toX(), - xpContext.toY(), - xpContext.toZ())); - } - return; - } - - if (J.isFoliaThreading()) { - J.runEntity(p, rewindTask[0], 1); - } else { - J.s(rewindTask[0], 1); - } - }; - if (J.isFoliaThreading()) { - J.runEntity(p, rewindTask[0]); + Runnable[] rewindTask = new Runnable[1]; + rewindTask[0] = () -> { + if (!p.isOnline() || p.isDead()) { + rewinding.remove(id); + cleanupVisualState.accept(true); + return; + } + + float progress = animationTicks <= 1 ? 1f : (float) step[0] / (float) (animationTicks - 1); + int index = Math.min(animationPath.size() - 1, step[0]); + Snapshot snapshot = animationPath.get(index); + Location destination = toLocation(snapshot, p.getWorld()); + + if (getConfig().showRewindTraceParticles && lastLoc[0].getWorld() != null && lastLoc[0].getWorld().equals(destination.getWorld())) { + Predicate traceFilter = J.isFoliaThreading() ? null : l -> l.getBlock().isPassable(); + vfxParticleLine(lastLoc[0].clone().add(0, 1, 0), destination.clone().add(0, 1, 0), Particle.REVERSE_PORTAL, + Math.max(4, getConfig().rewindTracePoints), 1, 0.08D, 0.08D, 0.08D, 0D, null, true, + traceFilter); + } + + boolean movedClient = false; + if (clientCameraActive[0] && cameraRef[0] != null && cameraRef[0].isValid()) { + Entity target = p.getSpectatorTarget(); + if (target == null || !target.getUniqueId().equals(cameraRef[0].getUniqueId())) { + p.setSpectatorTarget(cameraRef[0]); + target = p.getSpectatorTarget(); + } + + if (target != null + && target.getUniqueId().equals(cameraRef[0].getUniqueId()) + && destination.getWorld() != null + && destination.getWorld().equals(cameraRef[0].getWorld())) { + J.teleport(cameraRef[0], destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + movedClient = true; } else { - J.s(rewindTask[0]); + clientCameraActive[0] = false; } - } + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } + if (!movedClient) { + J.teleport(p, destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + } - UUID id = p.getUniqueId(); - long protectedUntil = rewindProtection.getOrDefault(id, 0L); - if (rewinding.contains(id) || protectedUntil > M.ms()) { - e.setCancelled(true); - p.setNoDamageTicks(Math.max(p.getNoDamageTicks(), getConfig().rewindProtectionTicks)); - } - } + if (!temporarySpectator || step[0] >= animationPath.size() - 1) { + applySnapshotState(p, snapshot); + } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - if (!isRecallEligible(p)) { - return; - } - - captureSnapshot(p); - } + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindStep(p, progress); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void onDoubleJumpMove(PlayerMoveEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - boolean wasOnGround = lastOnGround.getOrDefault(id, true); - boolean onGround = p.isOnGround(); - lastOnGround.put(id, onGround); + lastLoc[0] = destination; + step[0]++; - if (!isRecallEligible(p) || !getConfig().enableDoubleJumpTrigger) { - clearDoubleJumpArm(p, id); - return; + if (step[0] >= animationTicks) { + cleanupVisualState.accept(true); + Location finalDestination = toLocation(finalSnapshot, p.getWorld()); + if (finalDestination.getWorld() != null) { + J.teleport(p, finalDestination, PlayerTeleportEvent.TeleportCause.PLUGIN); } + applySnapshotState(p, finalSnapshot); - if (!wasOnGround && onGround) { - clearDoubleJumpArm(p, id); - return; + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 26, 0.25, 0.35, 0.25, 0.01); } - - if (!canArmDoubleJump(p)) { - clearDoubleJumpArm(p, id); - return; - } - - if (cooldowns.getOrDefault(id, 0L) > M.ms()) { - clearDoubleJumpArm(p, id); - return; + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.ITEM, p.getLocation().add(0, 1, 0), 18, 0.30, 0.30, 0.30, 0.01, new ItemStack(Material.CLOCK)); } - - if (isDoubleJumpStart(wasOnGround, onGround, p)) { - armDoubleJump(p, id); - return; + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindFinish(p); } + rewinding.remove(id); + getPlayer(p).getData().addStat("chronos.instant-recall.recalls", 1); + if (healthBeforeRecall <= 4 && healthAfterRecall >= 16 + && AdaptConfig.get().isAdvancements() + && !getPlayer(p).getData().isGranted("challenge_chronos_recall_cheat_death")) { + getPlayer(p).getAdvancementHandler().grant("challenge_chronos_recall_cheat_death"); + } + long awardAt = M.ms(); + double xpGain = computeRecallXPGain(id, level, xpContext, awardAt); + if (xpGain > 0D) { + xp(p, p.getLocation(), xpGain); + recallXpStamps.put(id, new RecallXPFarmStamp( + awardAt, + xpContext.fromWorld(), + xpContext.fromX(), + xpContext.fromY(), + xpContext.fromZ(), + xpContext.toWorld(), + xpContext.toX(), + xpContext.toY(), + xpContext.toZ())); + } + return; + } + + if (J.isFoliaThreading()) { + J.runEntity(p, rewindTask[0], 1); + } else { + J.s(rewindTask[0], 1); + } + }; + if (J.isFoliaThreading()) { + J.runEntity(p, rewindTask[0]); + } else { + J.s(rewindTask[0]); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + UUID id = p.getUniqueId(); + long protectedUntil = rewindProtection.getOrDefault(id, 0L); + if (rewinding.contains(id) || protectedUntil > M.ms()) { + e.setCancelled(true); + p.setNoDamageTicks(Math.max(p.getNoDamageTicks(), getConfig().rewindProtectionTicks)); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + if (!isRecallEligible(p)) { + return; + } + + captureSnapshot(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onDoubleJumpMove(PlayerMoveEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + boolean wasOnGround = lastOnGround.getOrDefault(id, true); + boolean onGround = p.isOnGround(); + lastOnGround.put(id, onGround); + + if (!isRecallEligible(p) || !getConfig().enableDoubleJumpTrigger) { + clearDoubleJumpArm(p, id); + return; + } + + if (!wasOnGround && onGround) { + clearDoubleJumpArm(p, id); + return; + } + + if (!canArmDoubleJump(p)) { + clearDoubleJumpArm(p, id); + return; + } + + if (cooldowns.getOrDefault(id, 0L) > M.ms()) { + clearDoubleJumpArm(p, id); + return; + } + + if (isDoubleJumpStart(wasOnGround, onGround, p)) { + armDoubleJump(p, id); + return; + } + + Long armUntil = jumpArmUntil.get(id); + if (armUntil != null && armUntil <= M.ms()) { + clearDoubleJumpArm(p, id); + } + } + + @Override + public void onTick() { + long now = M.ms(); + Set cooldownReadySet = cooldownReadyNotify; + for (Iterator iterator = cooldownReadySet.iterator(); iterator.hasNext(); ) { + UUID id = iterator.next(); + Player p = Bukkit.getPlayer(id); + if (p == null) { + iterator.remove(); + continue; + } - Long armUntil = jumpArmUntil.get(id); - if (armUntil != null && armUntil <= M.ms()) { - clearDoubleJumpArm(p, id); + long cooldown = cooldowns.getOrDefault(id, 0L); + if (cooldown <= now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playCooldownReady(p); } + iterator.remove(); + } } - @Override - public void onTick() { - long now = M.ms(); - - for (UUID id : new HashSet<>(cooldownReadyNotify)) { - Player p = Bukkit.getPlayer(id); - if (p == null) { - cooldownReadyNotify.remove(id); - continue; - } - - long cooldown = cooldowns.getOrDefault(id, 0L); - if (cooldown <= now) { - if (getConfig().playClockSounds) { - ChronosSoundFX.playCooldownReady(p); - } - cooldownReadyNotify.remove(id); - } - } + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + rewindProtection.entrySet().removeIf(entry -> entry.getValue() <= now); + TELEPORT_XP_SUPPRESS_UNTIL.entrySet().removeIf(entry -> entry.getValue() <= now); + } - cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); - rewindProtection.entrySet().removeIf(entry -> entry.getValue() <= now); - TELEPORT_XP_SUPPRESS_UNTIL.entrySet().removeIf(entry -> entry.getValue() <= now); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Click with a clock to rewind to a recent snapshot with health and hunger restored.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") - boolean playClockSounds = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Rewind Trace Particles for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") - boolean showRewindTraceParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Trace Points for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rewindTracePoints = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Target rewind animation duration in milliseconds.", impact = "Higher values make recall rewind visuals slower/smoother; lower values make them faster.") - int rewindAnimationDurationMillis = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Legacy fallback rewind animation ticks used when duration is invalid.", impact = "Retained for backward compatibility with older configs.") - int rewindAnimationTicks = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Temporarily switches to spectator during rewind animation for smoother camera movement through obstacles.", impact = "True improves rewind smoothness; false keeps player in survival during rewind.") - boolean rewindUseTemporarySpectator = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Uses a client-side spectator camera anchor during rewind so server position only updates at the end.", impact = "True reduces jitter and rubber-banding by avoiding per-tick player teleports.") - boolean rewindUseClientCamera = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Extra ticks to suppress teleport XP/stat tracking after recall rewinds.", impact = "Higher values make it safer against teleport-XP side effects from other skills.") - int rewindTeleportXpSuppressExtraTicks = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables direct click-with-clock activation for instant recall.", impact = "True allows recall by clicking with a valid recall clock; false disables direct clock-click activation.") - boolean enableClockClickTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click to activate recall when clock-click trigger is enabled.", impact = "True allows left-click activation; false blocks left-click activation.") - boolean clockClickLeftClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click to activate recall when clock-click trigger is enabled.", impact = "True allows right-click activation; false blocks right-click activation.") - boolean clockClickRightClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for instant recall with a valid recall clock.", impact = "True allows sprint-click activation; false disables sprint-click activation.") - boolean enableSprintClickTrigger = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click for sprint-click recall trigger.", impact = "True enables left-click sprint activation; false disables it.") - boolean sprintClickLeftClick = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click for sprint-click recall trigger.", impact = "True enables right-click sprint activation; false disables it.") - boolean sprintClickRightClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-in-air interactions for recall click triggers.", impact = "True lets air-clicks activate enabled click modes; false blocks air-click activations.") - boolean allowAirClicks = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-on-block interactions for recall click triggers.", impact = "True lets block-clicks activate enabled click modes; false blocks block-click activations.") - boolean allowBlockClicks = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for instant recall.", impact = "True allows pressing sneak once to trigger recall.") - boolean enableSingleSneakTrigger = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak instant recall trigger.", impact = "True requires sprint state when using single-sneak activation.") - boolean singleSneakRequiresSprint = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for single-sneak trigger.", impact = "True keeps single-sneak recall tied to clock usage.") - boolean singleSneakRequiresClockInHand = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump activation for instant recall.", impact = "True allows jump-based recall trigger; false disables jump trigger.") - boolean enableDoubleJumpTrigger = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting while double-jumping to trigger recall.", impact = "True requires sprint state for double-jump trigger; false allows it without sprint.") - boolean doubleJumpRequiresSprint = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for double-jump trigger.", impact = "True requires clock-in-hand for jump trigger; false allows jump trigger without holding a clock.") - boolean doubleJumpRequiresClockInHand = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum milliseconds allowed between jump taps for double-jump recall.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") - int doubleJumpWindowMillis = 450; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump recall.", impact = "Higher values reduce accidental arm events; lower values increase sensitivity.") - double doubleJumpMinVerticalVelocity = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Rewind Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseRewindSeconds = 3.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Seconds Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rewindSecondsPerLevel = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap for recall rewind duration in seconds.", impact = "Prevents high levels/config values from exceeding this rewind window.") - double maxRewindSeconds = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int cooldownPaddingSeconds = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snapshot Interval Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int snapshotIntervalMillis = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls History Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int historyPaddingSeconds = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Protection Ticks for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rewindProtectionTicks = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Distance Block for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerDistanceBlock = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Health Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerHealthPoint = 0.85; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Hunger Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerHungerPoint = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Saturation Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerSaturationPoint = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Multiplier Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpLevelMultiplierPerLevel = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Raw Reward for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpMinRawReward = 1.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpMinAward = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Max Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpMaxAward = 36; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Cross World Distance Credit for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpCrossWorldDistanceCredit = 16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long xpDiminishWindowMillis = 45000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Min Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpDiminishMinMultiplier = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long xpRepeatWindowMillis = 180000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Source Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpRepeatSourceRadius = 3.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Target Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpRepeatTargetRadius = 3.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Penalty Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpRepeatPenaltyMultiplier = 0.2; - } - - private record Snapshot(long timestamp, - String worldName, - double x, - double y, - double z, - float yaw, - float pitch, - double health, - int foodLevel, - float saturation, - float exhaustion, - int fireTicks) { - } - - private record RecallXPContext(String fromWorld, - double fromX, - double fromY, - double fromZ, - String toWorld, - double toX, - double toY, - double toZ, - double distance, - double healthRecovered, - double hungerRecovered, - double saturationRecovered) { - } - - private record RecallXPFarmStamp(long awardedAt, - String fromWorld, - double fromX, - double fromY, - double fromZ, - String toWorld, - double toX, - double toY, - double toZ) { - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java new file mode 100644 index 000000000..7ce3e1e7c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java @@ -0,0 +1,111 @@ +package art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.util.config.ConfigDescription; + +@ConfigDescription("Click with a clock to rewind to a recent snapshot with health and hunger restored.") +public class ChronosInstantRecallConfig { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Rewind Trace Particles for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") + boolean showRewindTraceParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Trace Points for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rewindTracePoints = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Target rewind animation duration in milliseconds.", impact = "Higher values make recall rewind visuals slower/smoother; lower values make them faster.") + int rewindAnimationDurationMillis = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Legacy fallback rewind animation ticks used when duration is invalid.", impact = "Retained for backward compatibility with older configs.") + int rewindAnimationTicks = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Temporarily switches to spectator during rewind animation for smoother camera movement through obstacles.", impact = "True improves rewind smoothness; false keeps player in survival during rewind.") + boolean rewindUseTemporarySpectator = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses a client-side spectator camera anchor during rewind so server position only updates at the end.", impact = "True reduces jitter and rubber-banding by avoiding per-tick player teleports.") + boolean rewindUseClientCamera = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra ticks to suppress teleport XP/stat tracking after recall rewinds.", impact = "Higher values make it safer against teleport-XP side effects from other skills.") + int rewindTeleportXpSuppressExtraTicks = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables direct click-with-clock activation for instant recall.", impact = "True allows recall by clicking with a valid recall clock; false disables direct clock-click activation.") + boolean enableClockClickTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click to activate recall when clock-click trigger is enabled.", impact = "True allows left-click activation; false blocks left-click activation.") + boolean clockClickLeftClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click to activate recall when clock-click trigger is enabled.", impact = "True allows right-click activation; false blocks right-click activation.") + boolean clockClickRightClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for instant recall with a valid recall clock.", impact = "True allows sprint-click activation; false disables sprint-click activation.") + boolean enableSprintClickTrigger = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click for sprint-click recall trigger.", impact = "True enables left-click sprint activation; false disables it.") + boolean sprintClickLeftClick = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click for sprint-click recall trigger.", impact = "True enables right-click sprint activation; false disables it.") + boolean sprintClickRightClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-in-air interactions for recall click triggers.", impact = "True lets air-clicks activate enabled click modes; false blocks air-click activations.") + boolean allowAirClicks = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows click-on-block interactions for recall click triggers.", impact = "True lets block-clicks activate enabled click modes; false blocks block-click activations.") + boolean allowBlockClicks = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for instant recall.", impact = "True allows pressing sneak once to trigger recall.") + boolean enableSingleSneakTrigger = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak instant recall trigger.", impact = "True requires sprint state when using single-sneak activation.") + boolean singleSneakRequiresSprint = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for single-sneak trigger.", impact = "True keeps single-sneak recall tied to clock usage.") + boolean singleSneakRequiresClockInHand = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump activation for instant recall.", impact = "True allows jump-based recall trigger; false disables jump trigger.") + boolean enableDoubleJumpTrigger = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting while double-jumping to trigger recall.", impact = "True requires sprint state for double-jump trigger; false allows it without sprint.") + boolean doubleJumpRequiresSprint = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require holding a valid recall clock in either hand for double-jump trigger.", impact = "True requires clock-in-hand for jump trigger; false allows jump trigger without holding a clock.") + boolean doubleJumpRequiresClockInHand = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum milliseconds allowed between jump taps for double-jump recall.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") + int doubleJumpWindowMillis = 450; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump recall.", impact = "Higher values reduce accidental arm events; lower values increase sensitivity.") + double doubleJumpMinVerticalVelocity = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Rewind Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseRewindSeconds = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Seconds Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rewindSecondsPerLevel = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap for recall rewind duration in seconds.", impact = "Prevents high levels/config values from exceeding this rewind window.") + double maxRewindSeconds = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int cooldownPaddingSeconds = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snapshot Interval Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int snapshotIntervalMillis = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls History Padding Seconds for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int historyPaddingSeconds = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Protection Ticks for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rewindProtectionTicks = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Distance Block for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDistanceBlock = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Health Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerHealthPoint = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Hunger Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerHungerPoint = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Saturation Point for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerSaturationPoint = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Multiplier Per Level for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpLevelMultiplierPerLevel = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Raw Reward for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpMinRawReward = 1.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Min Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpMinAward = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Max Award for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpMaxAward = 36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Cross World Distance Credit for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpCrossWorldDistanceCredit = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long xpDiminishWindowMillis = 45000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Diminish Min Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpDiminishMinMultiplier = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Window Millis for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long xpRepeatWindowMillis = 180000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Source Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpRepeatSourceRadius = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Target Radius for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpRepeatTargetRadius = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Repeat Penalty Multiplier for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpRepeatPenaltyMultiplier = 0.2; +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallTypes.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallTypes.java new file mode 100644 index 000000000..90f4ed010 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallTypes.java @@ -0,0 +1,40 @@ +package art.arcane.adapt.content.adaptation.chronos; + +record Snapshot(long timestamp, + String worldName, + double x, + double y, + double z, + float yaw, + float pitch, + double health, + int foodLevel, + float saturation, + float exhaustion, + int fireTicks) { +} + +record RecallXPContext(String fromWorld, + double fromX, + double fromY, + double fromZ, + String toWorld, + double toX, + double toY, + double toZ, + double distance, + double healthRecovered, + double hungerRecovered, + double saturationRecovered) { +} + +record RecallXPFarmStamp(long awardedAt, + String fromWorld, + double fromX, + double fromY, + double fromZ, + String toWorld, + double toX, + double toY, + double toZ) { +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java index d9e2dfd11..0ef659d5c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosSoundFX.java @@ -26,189 +26,189 @@ import org.bukkit.entity.Player; public final class ChronosSoundFX { - private ChronosSoundFX() { - } - - private static void play(Location location, Sound sound, float volume, float pitch) { - if (location == null || location.getWorld() == null) { - return; - } - if (!areSoundsEnabled()) { - return; - } - - Location at = location.clone(); - Runnable playTask = () -> { - World world = at.getWorld(); - if (world != null) { - world.playSound(at, sound, volume, pitch); - } - }; - - if (J.isPrimaryThread()) { - playTask.run(); - } else { - J.runAt(at, playTask); - } - } - - private static void playLater(Location location, Sound sound, float volume, float pitch, int delayTicks) { - if (location == null || location.getWorld() == null) { - return; - } - if (!areSoundsEnabled()) { - return; - } - - Location at = location.clone(); - Runnable playTask = () -> { - World world = at.getWorld(); - if (world != null) { - world.playSound(at, sound, volume, pitch); - } - }; - - if (delayTicks <= 0 && J.isPrimaryThread()) { - playTask.run(); - return; - } - - J.runAt(at, playTask, Math.max(0, delayTicks)); - } - - private static void playOnPlayer(Player player, Sound sound, float volume, float pitch) { - if (player == null || !player.isOnline()) { - return; - } - if (!areSoundsEnabled()) { - return; - } - - Runnable playTask = () -> { - if (!player.isOnline()) { - return; - } - Location at = player.getLocation(); - World world = at.getWorld(); - if (world != null) { - world.playSound(at, sound, volume, pitch); - } - }; - - if (J.isPrimaryThread()) { - playTask.run(); - } else { - J.runEntity(player, playTask); - } - } + private ChronosSoundFX() { + } - private static void playOnPlayerLater(Player player, Sound sound, float volume, float pitch, int delayTicks) { - if (player == null || !player.isOnline()) { - return; - } - if (!areSoundsEnabled()) { - return; - } - - Runnable playTask = () -> { - if (!player.isOnline()) { - return; - } - Location at = player.getLocation(); - World world = at.getWorld(); - if (world != null) { - world.playSound(at, sound, volume, pitch); - } - }; - - if (delayTicks <= 0 && J.isPrimaryThread()) { - playTask.run(); - return; - } - - J.runEntity(player, playTask, Math.max(0, delayTicks)); + private static void play(Location location, Sound sound, float volume, float pitch) { + if (location == null || location.getWorld() == null) { + return; } - - public static void playClockReject(Player p) { - Location l = p.getLocation(); - play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.42f, 0.58f); - play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.32f, 0.62f); + if (!areSoundsEnabled()) { + return; } - public static void playCooldownReady(Player p) { - Location l = p.getLocation(); - play(l, Sound.BLOCK_LEVER_CLICK, 0.35f, 1.75f); - playLater(l, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.28f, 1.93f, 1); - } - - public static void playBottleUse(Player p, Location at, int advanceTicks) { - float pitch = Math.min(1.95f, 0.8f + (advanceTicks / 175f)); - play(at, Sound.BLOCK_LEVER_CLICK, 0.55f, pitch); - play(at, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.35f, Math.min(2f, pitch + 0.24f)); - playLater(at, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.62f, Math.min(2f, pitch + 0.12f), 2); - } - - public static void playRewindStart(Player p) { - Location l = p.getLocation(); - play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.45f, 0.82f); - play(l, Sound.BLOCK_LEVER_CLICK, 0.5f, 0.75f); - } - - public static void playRewindStep(Player p, float progress) { - Location l = p.getLocation(); - float clamped = Math.max(0f, Math.min(1f, progress)); - float pitch = 0.74f + (clamped * 0.95f); - play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.2f, Math.min(2f, pitch + 0.1f)); - play(l, Sound.BLOCK_LEVER_CLICK, 0.24f, pitch); - } + Location at = location.clone(); + Runnable playTask = () -> { + World world = at.getWorld(); + if (world != null) { + world.playSound(at, sound, volume, pitch); + } + }; - public static void playRewindFinish(Player p) { - // Reverse wind-up + 3 tines with varied tone/volume, all following the caster. - playOnPlayer(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.68f, 0.9f); - playOnPlayerLater(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.76f, 1.16f, 1); - playOnPlayerLater(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.84f, 1.42f, 2); - - // Low tine - playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.70f, 0.78f, 3); - playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.88f, 1.28f, 4); - - // Mid tine - playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.44f, 0.98f, 7); - playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.80f, 1.5f, 8); - - // High tine + trailing echo - playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.16f, 1.2f, 11); - playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.72f, 1.74f, 12); - playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 0.56f, 1.34f, 15); - } - - public static void playTouchProc(Player p, Location target) { - Location l = target == null ? p.getLocation() : target; - play(l, Sound.BLOCK_LEVER_CLICK, 0.34f, 1.7f); - play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.26f, 1.18f); - } - - public static void playTimeBombArm(Player p) { - Location l = p.getLocation(); - play(l, Sound.BLOCK_LEVER_CLICK, 0.46f, 1.35f); - play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.35f, 1.1f); - } - - public static void playTimeBombDetonate(Location center) { - play(center, Sound.BLOCK_NOTE_BLOCK_BASS, 0.8f, 0.6f); - play(center, Sound.BLOCK_LEVER_CLICK, 0.8f, 0.68f); - playLater(center, Sound.BLOCK_BELL_RESONATE, 0.55f, 1.05f, 2); - } - - public static void playTimeFieldTick(Location center, float pitch) { - float clamped = Math.max(0.35f, Math.min(2f, pitch)); - play(center, Sound.BLOCK_NOTE_BLOCK_BASS, 0.14f, Math.max(0.3f, Math.min(2f, clamped * 0.78f))); - play(center, Sound.BLOCK_LEVER_CLICK, 0.22f, clamped); - play(center, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.15f, Math.min(2f, clamped + 0.18f)); - } - - private static boolean areSoundsEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isSoundsEnabled(); - } + if (J.isPrimaryThread()) { + playTask.run(); + } else { + J.runAt(at, playTask); + } + } + + private static void playLater(Location location, Sound sound, float volume, float pitch, int delayTicks) { + if (location == null || location.getWorld() == null) { + return; + } + if (!areSoundsEnabled()) { + return; + } + + Location at = location.clone(); + Runnable playTask = () -> { + World world = at.getWorld(); + if (world != null) { + world.playSound(at, sound, volume, pitch); + } + }; + + if (delayTicks <= 0 && J.isPrimaryThread()) { + playTask.run(); + return; + } + + J.runAt(at, playTask, Math.max(0, delayTicks)); + } + + private static void playOnPlayer(Player player, Sound sound, float volume, float pitch) { + if (player == null || !player.isOnline()) { + return; + } + if (!areSoundsEnabled()) { + return; + } + + Runnable playTask = () -> { + if (!player.isOnline()) { + return; + } + Location at = player.getLocation(); + World world = at.getWorld(); + if (world != null) { + world.playSound(at, sound, volume, pitch); + } + }; + + if (J.isPrimaryThread()) { + playTask.run(); + } else { + J.runEntity(player, playTask); + } + } + + private static void playOnPlayerLater(Player player, Sound sound, float volume, float pitch, int delayTicks) { + if (player == null || !player.isOnline()) { + return; + } + if (!areSoundsEnabled()) { + return; + } + + Runnable playTask = () -> { + if (!player.isOnline()) { + return; + } + Location at = player.getLocation(); + World world = at.getWorld(); + if (world != null) { + world.playSound(at, sound, volume, pitch); + } + }; + + if (delayTicks <= 0 && J.isPrimaryThread()) { + playTask.run(); + return; + } + + J.runEntity(player, playTask, Math.max(0, delayTicks)); + } + + public static void playClockReject(Player p) { + Location l = p.getLocation(); + play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.42f, 0.58f); + play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.32f, 0.62f); + } + + public static void playCooldownReady(Player p) { + Location l = p.getLocation(); + play(l, Sound.BLOCK_LEVER_CLICK, 0.35f, 1.75f); + playLater(l, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.28f, 1.93f, 1); + } + + public static void playBottleUse(Player p, Location at, int advanceTicks) { + float pitch = Math.min(1.95f, 0.8f + (advanceTicks / 175f)); + play(at, Sound.BLOCK_LEVER_CLICK, 0.55f, pitch); + play(at, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.35f, Math.min(2f, pitch + 0.24f)); + playLater(at, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.62f, Math.min(2f, pitch + 0.12f), 2); + } + + public static void playRewindStart(Player p) { + Location l = p.getLocation(); + play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.45f, 0.82f); + play(l, Sound.BLOCK_LEVER_CLICK, 0.5f, 0.75f); + } + + public static void playRewindStep(Player p, float progress) { + Location l = p.getLocation(); + float clamped = Math.max(0f, Math.min(1f, progress)); + float pitch = 0.74f + (clamped * 0.95f); + play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.2f, Math.min(2f, pitch + 0.1f)); + play(l, Sound.BLOCK_LEVER_CLICK, 0.24f, pitch); + } + + public static void playRewindFinish(Player p) { + // Reverse wind-up + 3 tines with varied tone/volume, all following the caster. + playOnPlayer(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.68f, 0.9f); + playOnPlayerLater(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.76f, 1.16f, 1); + playOnPlayerLater(p, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.84f, 1.42f, 2); + + // Low tine + playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.70f, 0.78f, 3); + playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.88f, 1.28f, 4); + + // Mid tine + playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.44f, 0.98f, 7); + playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.80f, 1.5f, 8); + + // High tine + trailing echo + playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 1.16f, 1.2f, 11); + playOnPlayerLater(p, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.72f, 1.74f, 12); + playOnPlayerLater(p, Sound.BLOCK_BELL_RESONATE, 0.56f, 1.34f, 15); + } + + public static void playTouchProc(Player p, Location target) { + Location l = target == null ? p.getLocation() : target; + play(l, Sound.BLOCK_LEVER_CLICK, 0.34f, 1.7f); + play(l, Sound.BLOCK_NOTE_BLOCK_BASS, 0.26f, 1.18f); + } + + public static void playTimeBombArm(Player p) { + Location l = p.getLocation(); + play(l, Sound.BLOCK_LEVER_CLICK, 0.46f, 1.35f); + play(l, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.35f, 1.1f); + } + + public static void playTimeBombDetonate(Location center) { + play(center, Sound.BLOCK_NOTE_BLOCK_BASS, 0.8f, 0.6f); + play(center, Sound.BLOCK_LEVER_CLICK, 0.8f, 0.68f); + playLater(center, Sound.BLOCK_BELL_RESONATE, 0.55f, 1.05f, 2); + } + + public static void playTimeFieldTick(Location center, float pitch) { + float clamped = Math.max(0.35f, Math.min(2f, pitch)); + play(center, Sound.BLOCK_NOTE_BLOCK_BASS, 0.14f, Math.max(0.3f, Math.min(2f, clamped * 0.78f))); + play(center, Sound.BLOCK_LEVER_CLICK, 0.22f, clamped); + play(center, Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.15f, Math.min(2f, clamped + 0.18f)); + } + + private static boolean areSoundsEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isSoundsEnabled(); + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index a12ed7115..621b93721 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,166 +44,170 @@ import java.util.UUID; public class ChronosTemporalEcho extends SimpleAdaptation { - private static final String ECHO_META = "adapt-chronos-temporal-echo"; - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - - public ChronosTemporalEcho() { - super("chronos-temporal-echo"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("chronos.temporal_echo.description")); - setDisplayName(Localizer.dLocalize("chronos.temporal_echo.name")); - setIcon(Material.AMETHYST_CLUSTER); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1600); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_chronos_echo_200") - .title(Localizer.dLocalize("advancement.challenge_chronos_echo_200.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_echo_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_chronos_echo_200", "chronos.temporal-echo.echo-hits", 200, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration(getEchoDelayTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getEchoVelocityFactor(level), 0) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore3")); + private static final String ECHO_META = "adapt-chronos-temporal-echo"; + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + + public ChronosTemporalEcho() { + super("chronos-temporal-echo"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.temporal_echo.description")); + setDisplayName(Localizer.dLocalize("chronos.temporal_echo.name")); + setIcon(Material.AMETHYST_CLUSTER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1600); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_chronos_echo_200") + .title(Localizer.dLocalize("advancement.challenge_chronos_echo_200.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_echo_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_echo_200", "chronos.temporal-echo.echo-hits", 200, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getEchoDelayTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getEchoVelocityFactor(level), 0) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("chronos.temporal_echo.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity().getShooter() instanceof Player p) || !hasActiveAdaptation(p) || e.getEntity().hasMetadata(ECHO_META)) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity().getShooter() instanceof Player p) || !hasActiveAdaptation(p) || e.getEntity().hasMetadata(ECHO_META)) { - return; - } - - EchoType echoType = getEchoType(e.getEntity()); - if (echoType == null) { - return; - } - - int level = getActiveLevel(p); - long now = System.currentTimeMillis(); - if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { - return; - } - - cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); - Projectile original = e.getEntity(); - Vector originalVelocity = original.getVelocity().clone(); - int delay = getEchoDelayTicks(level); - J.runEntity(p, () -> spawnEcho(p, echoType, originalVelocity, level), delay); + EchoType echoType = getEchoType(e.getEntity()); + if (echoType == null) { + return; } - private void spawnEcho(Player p, EchoType type, Vector velocity, int level) { - if (!p.isOnline() || p.isDead()) { - return; - } - - Projectile echo = switch (type) { - case ARROW -> p.getWorld().spawnArrow(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), - p.getLocation().getDirection(), 0.6f, 0f); - case SNOWBALL -> p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), Snowball.class); - case EGG -> p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), Egg.class); - case ENDER_PEARL -> p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), EnderPearl.class); - }; - echo.setShooter(p); - echo.setVelocity(velocity.multiply(getEchoVelocityFactor(level))); - echo.setMetadata(ECHO_META, new FixedMetadataValue(Adapt.instance, true)); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.75f, 1.35f); - getPlayer(p).getData().addStat("chronos.temporal-echo.echo-hits", 1); - xp(p, getConfig().xpPerEcho); + int level = getActiveLevel(p); + long now = System.currentTimeMillis(); + if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { + return; } - private EchoType getEchoType(Projectile projectile) { - if (projectile instanceof Arrow) { - return EchoType.ARROW; - } + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + Projectile original = e.getEntity(); + Vector originalVelocity = original.getVelocity().clone(); + int delay = getEchoDelayTicks(level); + J.runEntity(p, () -> spawnEcho(p, echoType, originalVelocity, level), delay); + } - if (projectile instanceof Snowball) { - return EchoType.SNOWBALL; - } - - if (projectile instanceof Egg) { - return EchoType.EGG; - } - - if (projectile instanceof EnderPearl) { - return EchoType.ENDER_PEARL; - } - - return null; + private void spawnEcho(Player p, EchoType type, Vector velocity, int level) { + if (!p.isOnline() || p.isDead()) { + return; } - private int getEchoDelayTicks(int level) { - return Math.max(1, (int) Math.round(getConfig().echoDelayTicksBase - (getLevelPercent(level) * getConfig().echoDelayTicksFactor))); + Projectile echo = switch (type) { + case ARROW -> + p.getWorld().spawnArrow(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), + p.getLocation().getDirection(), 0.6f, 0f); + case SNOWBALL -> + p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), Snowball.class); + case EGG -> + p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), Egg.class); + case ENDER_PEARL -> + p.getWorld().spawn(p.getEyeLocation().add(p.getLocation().getDirection().normalize().multiply(0.35)), EnderPearl.class); + }; + echo.setShooter(p); + echo.setVelocity(velocity.multiply(getEchoVelocityFactor(level))); + echo.setMetadata(ECHO_META, new FixedMetadataValue(Adapt.instance, true)); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.75f, 1.35f); + getPlayer(p).getData().addStat("chronos.temporal-echo.echo-hits", 1); + xp(p, getConfig().xpPerEcho); + } + + private EchoType getEchoType(Projectile projectile) { + if (projectile instanceof Arrow) { + return EchoType.ARROW; } - private double getEchoVelocityFactor(int level) { - return Math.min(getConfig().maxEchoVelocityFactor, getConfig().echoVelocityFactorBase + (getLevelPercent(level) * getConfig().echoVelocityFactorFactor)); + if (projectile instanceof Snowball) { + return EchoType.SNOWBALL; } - private long getCooldownMillis(int level) { - return Math.max(500L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + if (projectile instanceof Egg) { + return EchoType.EGG; } - @Override - public void onTick() { - + if (projectile instanceof EnderPearl) { + return EchoType.ENDER_PEARL; } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Replays your last projectile action shortly after firing at reduced power.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double echoDelayTicksBase = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double echoDelayTicksFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double echoVelocityFactorBase = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double echoVelocityFactorFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Echo Velocity Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxEchoVelocityFactor = 0.92; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 2600; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Echo for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerEcho = 12; - } - - private enum EchoType { - ARROW, - SNOWBALL, - EGG, - ENDER_PEARL - } + return null; + } + + private int getEchoDelayTicks(int level) { + return Math.max(1, (int) Math.round(getConfig().echoDelayTicksBase - (getLevelPercent(level) * getConfig().echoDelayTicksFactor))); + } + + private double getEchoVelocityFactor(int level) { + return Math.min(getConfig().maxEchoVelocityFactor, getConfig().echoVelocityFactorBase + (getLevelPercent(level) * getConfig().echoVelocityFactorFactor)); + } + + private long getCooldownMillis(int level) { + return Math.max(500L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private enum EchoType { + ARROW, + SNOWBALL, + EGG, + ENDER_PEARL + } + + @NoArgsConstructor + @ConfigDescription("Replays your last projectile action shortly after firing at reduced power.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double echoDelayTicksBase = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Delay Ticks Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double echoDelayTicksFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double echoVelocityFactorBase = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Echo Velocity Factor Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double echoVelocityFactorFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Echo Velocity Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxEchoVelocityFactor = 0.92; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 2600; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Echo for the Chronos Temporal Echo adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerEcho = 12; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index b251cdf07..b039b959a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -27,9 +27,9 @@ import art.arcane.adapt.content.item.ChronoTimeBombItem; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -53,755 +53,756 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ChronosTimeBomb extends SimpleAdaptation { - private static final EnumSet SUPPORTED_ACTIONS = EnumSet.of( - Action.RIGHT_CLICK_AIR, - Action.RIGHT_CLICK_BLOCK - ); - private static final long PROJECTILE_TRACK_TTL_MS = 15000L; - - private final Map cooldowns; - private final Set cooldownReadyNotify; - private final List fields; - private final Map activeBombProjectiles; - private final Map frozenEntities; - private final Map frozenPlayers; - private final AtomicBoolean syncTickQueued; - - public ChronosTimeBomb() { - super("chronos-time-bomb"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("chronos.time_bomb.description")); - setDisplayName(Localizer.dLocalize("chronos.time_bomb.name")); - setIcon(Material.TNT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(50); - - registerRecipe(AdaptRecipe.shapeless() - .key("chronos-time-bomb") - .ingredient(Material.SNOWBALL) - .ingredient(Material.CLOCK) - .ingredient(Material.DIAMOND) - .ingredient(Material.SAND) - .result(ChronoTimeBombItem.withData()) - .build()); - - cooldowns = new ConcurrentHashMap<>(); - cooldownReadyNotify = ConcurrentHashMap.newKeySet(); - fields = new CopyOnWriteArrayList<>(); - activeBombProjectiles = new ConcurrentHashMap<>(); - frozenEntities = new ConcurrentHashMap<>(); - frozenPlayers = new ConcurrentHashMap<>(); - syncTickQueued = new AtomicBoolean(false); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ICE) - .key("challenge_chronos_bomb_freeze_50") - .title(Localizer.dLocalize("advancement.challenge_chronos_bomb_freeze_50.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_bomb_freeze_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ICE) - .key("challenge_chronos_bomb_crowd_8") - .title(Localizer.dLocalize("advancement.challenge_chronos_bomb_crowd_8.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_bomb_crowd_8.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_chronos_bomb_freeze_50", "chronos.time-bomb.projectiles-frozen", 50, 500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getRadius(level) + " " + Localizer.dLocalize("chronos.time_bomb.lore1")); - v.addLore(C.YELLOW + "+ " + (getDurationTicks(level) / 20D) + "s " + Localizer.dLocalize("chronos.time_bomb.lore2")); - v.addLore(C.RED + "* " + (getCooldownMillis() / 1000D) + "s " + Localizer.dLocalize("chronos.time_bomb.lore3")); - v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.time_bomb.lore4")); - } - - private double getRadius(int level) { - return getConfig().baseRadius + ((Math.max(1, level) - 1) * getConfig().radiusPerLevel); - } - - private int getDurationTicks(int level) { - return getConfig().baseDurationTicks + ((Math.max(1, level) - 1) * getConfig().durationPerLevelTicks); - } - - private long getCooldownMillis() { - return Math.max(0L, getConfig().cooldownMillis); - } - - @EventHandler - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - cooldowns.remove(id); - cooldownReadyNotify.remove(id); - frozenPlayers.remove(id); - activeBombProjectiles.entrySet().removeIf(entry -> entry.getValue().owner().equals(id)); + private static final EnumSet SUPPORTED_ACTIONS = EnumSet.of( + Action.RIGHT_CLICK_AIR, + Action.RIGHT_CLICK_BLOCK + ); + private static final long PROJECTILE_TRACK_TTL_MS = 15000L; + + private final Map cooldowns; + private final Set cooldownReadyNotify; + private final List fields; + private final Map activeBombProjectiles; + private final Map frozenEntities; + private final Map frozenPlayers; + private final AtomicBoolean syncTickQueued; + + public ChronosTimeBomb() { + super("chronos-time-bomb"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.time_bomb.description")); + setDisplayName(Localizer.dLocalize("chronos.time_bomb.name")); + setIcon(Material.TNT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(50); + + registerRecipe(AdaptRecipe.shapeless() + .key("chronos-time-bomb") + .ingredient(Material.SNOWBALL) + .ingredient(Material.CLOCK) + .ingredient(Material.DIAMOND) + .ingredient(Material.SAND) + .result(ChronoTimeBombItem.withData()) + .build()); + + cooldowns = new ConcurrentHashMap<>(); + cooldownReadyNotify = ConcurrentHashMap.newKeySet(); + fields = new CopyOnWriteArrayList<>(); + activeBombProjectiles = new ConcurrentHashMap<>(); + frozenEntities = new ConcurrentHashMap<>(); + frozenPlayers = new ConcurrentHashMap<>(); + syncTickQueued = new AtomicBoolean(false); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ICE) + .key("challenge_chronos_bomb_freeze_50") + .title(Localizer.dLocalize("advancement.challenge_chronos_bomb_freeze_50.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_bomb_freeze_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ICE) + .key("challenge_chronos_bomb_crowd_8") + .title(Localizer.dLocalize("advancement.challenge_chronos_bomb_crowd_8.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_bomb_crowd_8.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_bomb_freeze_50", "chronos.time-bomb.projectiles-frozen", 50, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getRadius(level) + " " + Localizer.dLocalize("chronos.time_bomb.lore1")); + v.addLore(C.YELLOW + "+ " + (getDurationTicks(level) / 20D) + "s " + Localizer.dLocalize("chronos.time_bomb.lore2")); + v.addLore(C.RED + "* " + (getCooldownMillis() / 1000D) + "s " + Localizer.dLocalize("chronos.time_bomb.lore3")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.time_bomb.lore4")); + } + + private double getRadius(int level) { + return getConfig().baseRadius + ((Math.max(1, level) - 1) * getConfig().radiusPerLevel); + } + + private int getDurationTicks(int level) { + return getConfig().baseDurationTicks + ((Math.max(1, level) - 1) * getConfig().durationPerLevelTicks); + } + + private long getCooldownMillis() { + return Math.max(0L, getConfig().cooldownMillis); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + cooldowns.remove(id); + cooldownReadyNotify.remove(id); + frozenPlayers.remove(id); + activeBombProjectiles.entrySet().removeIf(entry -> entry.getValue().owner().equals(id)); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (!SUPPORTED_ACTIONS.contains(action)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - Action action = e.getAction(); - if (!SUPPORTED_ACTIONS.contains(action)) { - return; - } - - EquipmentSlot handSlot = e.getHand(); - if (handSlot == null) { - return; - } - - Player p = e.getPlayer(); - if (handSlot == EquipmentSlot.OFF_HAND && ChronoTimeBombItem.isBindableItem(p.getInventory().getItemInMainHand())) { - return; - } - - ItemStack hand = getItemInHand(p, handSlot); - if (!ChronoTimeBombItem.isBindableItem(hand)) { - return; - } - - if (!hasActiveAdaptation(p)) { - e.setCancelled(true); - return; - } - - long now = M.ms(); - long cooldown = cooldowns.getOrDefault(p.getUniqueId(), 0L); - if (cooldown > now) { - e.setCancelled(true); - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - } + EquipmentSlot handSlot = e.getHand(); + if (handSlot == null) { + return; } - private ItemStack getItemInHand(Player p, EquipmentSlot handSlot) { - return handSlot == EquipmentSlot.OFF_HAND - ? p.getInventory().getItemInOffHand() - : p.getInventory().getItemInMainHand(); + Player p = e.getPlayer(); + if (handSlot == EquipmentSlot.OFF_HAND && ChronoTimeBombItem.isBindableItem(p.getInventory().getItemInMainHand())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity() instanceof ThrownPotion potion)) { - return; - } - - if (!(potion.getShooter() instanceof Player p)) { - return; - } - - ItemStack item = potion.getItem(); - if (!ChronoTimeBombItem.isBindableItem(item)) { - return; - } + ItemStack hand = getItemInHand(p, handSlot); + if (!ChronoTimeBombItem.isBindableItem(hand)) { + return; + } - int level = getActiveLevel(p); - if (level <= 0) { - e.setCancelled(true); - return; - } + if (!hasActiveAdaptation(p)) { + e.setCancelled(true); + return; + } - long now = M.ms(); - long cooldown = cooldowns.getOrDefault(p.getUniqueId(), 0L); - if (cooldown > now) { - e.setCancelled(true); - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - return; - } + long now = M.ms(); + long cooldown = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (cooldown > now) { + e.setCancelled(true); + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + } + } + + private ItemStack getItemInHand(Player p, EquipmentSlot handSlot) { + return handSlot == EquipmentSlot.OFF_HAND + ? p.getInventory().getItemInOffHand() + : p.getInventory().getItemInMainHand(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity() instanceof ThrownPotion potion)) { + return; + } - cooldowns.put(p.getUniqueId(), now + getCooldownMillis()); - cooldownReadyNotify.add(p.getUniqueId()); - activeBombProjectiles.put(potion.getUniqueId(), new ArmedBombProjectile(p.getUniqueId(), level, now)); + if (!(potion.getShooter() instanceof Player p)) { + return; + } - if (getConfig().playClockSounds) { - ChronosSoundFX.playTimeBombArm(p); - } + ItemStack item = potion.getItem(); + if (!ChronoTimeBombItem.isBindableItem(item)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(LingeringPotionSplashEvent e) { - ThrownPotion potion = e.getEntity(); - UUID projectileId = potion.getUniqueId(); - ArmedBombProjectile armed = activeBombProjectiles.remove(projectileId); - boolean bindable = ChronoTimeBombItem.isBindableItem(potion.getItem()); + int level = getActiveLevel(p); + if (level <= 0) { + e.setCancelled(true); + return; + } - if (!bindable && armed == null) { - return; - } + long now = M.ms(); + long cooldown = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (cooldown > now) { + e.setCancelled(true); + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } - if (e.getAreaEffectCloud() != null) { - e.getAreaEffectCloud().remove(); - } + cooldowns.put(p.getUniqueId(), now + getCooldownMillis()); + cooldownReadyNotify.add(p.getUniqueId()); + activeBombProjectiles.put(potion.getUniqueId(), new ArmedBombProjectile(p.getUniqueId(), level, now)); - if (armed == null) { - return; - } + if (getConfig().playClockSounds) { + ChronosSoundFX.playTimeBombArm(p); + } + } - Location deployCenter = potion.getLocation().clone().add(0, getConfig().fieldCenterYOffset, 0); - Player owner = Bukkit.getPlayer(armed.owner()); - if (owner != null && !canInteract(owner, deployCenter)) { - return; - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(LingeringPotionSplashEvent e) { + ThrownPotion potion = e.getEntity(); + UUID projectileId = potion.getUniqueId(); + ArmedBombProjectile armed = activeBombProjectiles.remove(projectileId); + boolean bindable = ChronoTimeBombItem.isBindableItem(potion.getItem()); - deployField(armed.owner(), armed.level(), deployCenter); + if (!bindable && armed == null) { + return; } - private void deployField(UUID ownerId, int level, Location center) { - TemporalField field = new TemporalField( - ownerId, - center, - getRadius(level), - M.ms() + (getDurationTicks(level) * 50L), - level, - M.ms(), - M.ms()); - fields.add(field); + if (e.getAreaEffectCloud() != null) { + e.getAreaEffectCloud().remove(); + } - if (getConfig().playClockSounds) { - ChronosSoundFX.playTimeBombDetonate(center); - } + if (armed == null) { + return; + } - if (areParticlesEnabled() && center.getWorld() != null) { - center.getWorld().spawnParticle(Particle.ENCHANT, center, 45, field.radius() * 0.4, 0.35, field.radius() * 0.4, 0.15); - center.getWorld().spawnParticle(Particle.END_ROD, center, 20, field.radius() * 0.2, 0.2, field.radius() * 0.2, 0.02); - } + Location deployCenter = potion.getLocation().clone().add(0, getConfig().fieldCenterYOffset, 0); + Player owner = Bukkit.getPlayer(armed.owner()); + if (owner != null && !canInteract(owner, deployCenter)) { + return; + } - Player owner = Bukkit.getPlayer(ownerId); - if (owner != null) { - xp(owner, center, getConfig().xpOnCast + (level * getConfig().xpPerLevel)); - } + deployField(armed.owner(), armed.level(), deployCenter); + } + + private void deployField(UUID ownerId, int level, Location center) { + TemporalField field = new TemporalField( + ownerId, + center, + getRadius(level), + M.ms() + (getDurationTicks(level) * 50L), + level, + M.ms(), + M.ms()); + fields.add(field); + + if (getConfig().playClockSounds) { + ChronosSoundFX.playTimeBombDetonate(center); } - private void freezeEntity(Entity entity) { - if (entity instanceof Player) { - return; - } + if (areParticlesEnabled() && center.getWorld() != null) { + center.getWorld().spawnParticle(Particle.ENCHANT, center, 45, field.radius() * 0.4, 0.35, field.radius() * 0.4, 0.15); + center.getWorld().spawnParticle(Particle.END_ROD, center, 20, field.radius() * 0.2, 0.2, field.radius() * 0.2, 0.02); + } - UUID id = entity.getUniqueId(); - FrozenEntityState state = frozenEntities.get(id); - if (state == null) { - Boolean hadAi = null; - if (entity instanceof LivingEntity living) { - hadAi = living.hasAI(); - } - - state = new FrozenEntityState(entity.getVelocity().clone(), entity.hasGravity(), hadAi); - frozenEntities.put(id, state); - } else if (getConfig().accumulateFrozenImpulse) { - state.captureImpulse(entity.getVelocity(), getConfig().frozenImpulseMinMagnitude, getConfig().frozenImpulseSampleCap); - } + Player owner = Bukkit.getPlayer(ownerId); + if (owner != null) { + xp(owner, center, getConfig().xpOnCast + (level * getConfig().xpPerLevel)); + } + } - entity.setGravity(false); - entity.setVelocity(new Vector()); - entity.setFallDistance(0); + private void freezeEntity(Entity entity) { + if (entity instanceof Player) { + return; + } - if (entity instanceof LivingEntity living) { - living.setAI(false); - } + UUID id = entity.getUniqueId(); + FrozenEntityState state = frozenEntities.get(id); + if (state == null) { + Boolean hadAi = null; + if (entity instanceof LivingEntity living) { + hadAi = living.hasAI(); + } + + state = new FrozenEntityState(entity.getVelocity().clone(), entity.hasGravity(), hadAi); + frozenEntities.put(id, state); + } else if (getConfig().accumulateFrozenImpulse) { + state.captureImpulse(entity.getVelocity(), getConfig().frozenImpulseMinMagnitude, getConfig().frozenImpulseSampleCap); } - private void freezePlayer(Player player) { - UUID id = player.getUniqueId(); - if (!frozenPlayers.containsKey(id)) { - frozenPlayers.put(id, new FrozenPlayerState(player.getAllowFlight(), player.isFlying(), player.hasGravity())); - } + entity.setGravity(false); + entity.setVelocity(new Vector()); + entity.setFallDistance(0); - if (!getConfig().freezePlayersInAir || player.isOnGround()) { - return; - } + if (entity instanceof LivingEntity living) { + living.setAI(false); + } + } - player.setFallDistance(0); - player.setAllowFlight(true); - player.setFlying(true); - player.setGravity(false); - player.setVelocity(new Vector()); + private void freezePlayer(Player player) { + UUID id = player.getUniqueId(); + if (!frozenPlayers.containsKey(id)) { + frozenPlayers.put(id, new FrozenPlayerState(player.getAllowFlight(), player.isFlying(), player.hasGravity())); } - private void unfreezePlayer(UUID playerId, FrozenPlayerState state) { - Player player = Bukkit.getPlayer(playerId); - if (player == null || !player.isOnline()) { - return; - } + if (!getConfig().freezePlayersInAir || player.isOnGround()) { + return; + } - player.setGravity(state.gravity()); - if (state.allowFlight()) { - player.setAllowFlight(true); - player.setFlying(state.flying()); - } else { - player.setFlying(false); - player.setAllowFlight(false); - } + player.setFallDistance(0); + player.setAllowFlight(true); + player.setFlying(true); + player.setGravity(false); + player.setVelocity(new Vector()); + } + + private void unfreezePlayer(UUID playerId, FrozenPlayerState state) { + Player player = Bukkit.getPlayer(playerId); + if (player == null || !player.isOnline()) { + return; } - private boolean shouldFreezePlayer(Player player) { - if (!getConfig().freezePlayersInAir) { - return false; - } + player.setGravity(state.gravity()); + if (state.allowFlight()) { + player.setAllowFlight(true); + player.setFlying(state.flying()); + } else { + player.setFlying(false); + player.setAllowFlight(false); + } + } - UUID playerId = player.getUniqueId(); - for (TemporalField field : fields) { - if (field.center().getWorld() == null || !field.center().getWorld().equals(player.getWorld())) { - continue; - } + private boolean shouldFreezePlayer(Player player) { + if (!getConfig().freezePlayersInAir) { + return false; + } - if (field.center().distanceSquared(player.getLocation()) > field.radius() * field.radius()) { - continue; - } + UUID playerId = player.getUniqueId(); + for (TemporalField field : fields) { + if (field.center().getWorld() == null || !field.center().getWorld().equals(player.getWorld())) { + continue; + } - if (playerId.equals(field.owner())) { - continue; - } + if (field.center().distanceSquared(player.getLocation()) > field.radius() * field.radius()) { + continue; + } - Player owner = Bukkit.getPlayer(field.owner()); - if (owner != null && !canDamageTarget(owner, player)) { - continue; - } + if (playerId.equals(field.owner())) { + continue; + } - return true; - } + Player owner = Bukkit.getPlayer(field.owner()); + if (owner != null && !canDamageTarget(owner, player)) { + continue; + } - return false; + return true; } - private void unfreezeEntity(UUID entityId, FrozenEntityState state) { - Entity entity = Bukkit.getEntity(entityId); - if (entity == null || entity.isDead() || !entity.isValid()) { - return; - } - - entity.setGravity(state.gravity()); - entity.setVelocity(state.buildReleaseVelocity(getConfig().frozenImpulseReleaseCap)); + return false; + } - if (entity instanceof LivingEntity living && state.hadAi() != null) { - living.setAI(state.hadAi()); - } + private void unfreezeEntity(UUID entityId, FrozenEntityState state) { + Entity entity = Bukkit.getEntity(entityId); + if (entity == null || entity.isDead() || !entity.isValid()) { + return; } - private boolean isInsideAnyField(Location location) { - if (location.getWorld() == null) { - return false; - } - - for (TemporalField field : fields) { - if (field.center().getWorld() == null || !field.center().getWorld().equals(location.getWorld())) { - continue; - } - - if (field.center().distanceSquared(location) <= field.radius() * field.radius()) { - return true; - } - } + entity.setGravity(state.gravity()); + entity.setVelocity(state.buildReleaseVelocity(getConfig().frozenImpulseReleaseCap)); - return false; + if (entity instanceof LivingEntity living && state.hadAi() != null) { + living.setAI(state.hadAi()); } + } - private void applyField(TemporalField field, long now) { - if (field.center().getWorld() == null) { - return; - } + private boolean isInsideAnyField(Location location) { + if (location.getWorld() == null) { + return false; + } - Player owner = Bukkit.getPlayer(field.owner()); - - Collection nearby = field.center().getWorld().getNearbyEntities(field.center(), field.radius(), field.radius(), field.radius()); - int entitiesSlowed = 0; - for (Entity entity : nearby) { - if (entity.getLocation().distanceSquared(field.center()) > field.radius() * field.radius()) { - continue; - } - - if (!(entity instanceof Player)) { - boolean wasNew = !frozenEntities.containsKey(entity.getUniqueId()); - freezeEntity(entity); - if (wasNew && owner != null) { - entitiesSlowed++; - if (entity instanceof Projectile) { - getPlayer(owner).getData().addStat("chronos.time-bomb.projectiles-frozen", 1); - } - getPlayer(owner).getData().addStat("chronos.time-bomb.entities-slowed", 1); - } - continue; - } - - LivingEntity living = (LivingEntity) entity; - boolean caster = entity.getUniqueId().equals(field.owner()); - if (caster) { - living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().casterSlownessAmplifier, true, false, false), true); - living.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, getConfig().effectRefreshTicks, getConfig().fatigueAmplifier, true, false, false), true); - continue; - } - - if (owner != null) { - if (!canDamageTarget(owner, living)) { - continue; - } - } - - living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().slownessAmplifier, true, false, false), true); - living.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, getConfig().effectRefreshTicks, getConfig().fatigueAmplifier, true, false, false), true); - freezePlayer((Player) living); - living.setVelocity(new Vector()); - entitiesSlowed++; - } + for (TemporalField field : fields) { + if (field.center().getWorld() == null || !field.center().getWorld().equals(location.getWorld())) { + continue; + } - if (entitiesSlowed >= 8 && owner != null - && AdaptConfig.get().isAdvancements() - && !getPlayer(owner).getData().isGranted("challenge_chronos_bomb_crowd_8")) { - getPlayer(owner).getAdvancementHandler().grant("challenge_chronos_bomb_crowd_8"); - } + if (field.center().distanceSquared(location) <= field.radius() * field.radius()) { + return true; + } + } - if (areParticlesEnabled()) { - spawnFieldSphere(field, now); - } + return false; + } - if (getConfig().playClockSounds && now >= field.nextTickSoundAt()) { - long totalDurationMillis = Math.max(50L, getDurationTicks(field.level()) * 50L); - long remainingMillis = Math.max(0L, field.expiresAt() - now); - double progress = 1D - (remainingMillis / (double) totalDurationMillis); - progress = Math.max(0D, Math.min(1D, progress)); - - double pitchCurve = Math.pow(progress, Math.max(0.1D, getConfig().fieldTickPitchCurveExponent)); - float pitch = (float) (getConfig().fieldTickPitchStart - + ((getConfig().fieldTickPitchEnd - getConfig().fieldTickPitchStart) * pitchCurve)); - ChronosSoundFX.playTimeFieldTick(field.center(), pitch); - - double acceleration = Math.max(0D, Math.min(0.95D, getConfig().fieldTickAccelerationFactor)); - long interval = (long) Math.max(getConfig().fieldTickMinIntervalMillis, - getConfig().fieldTickSoundIntervalMillis * (1D - (progress * acceleration))); - field.setNextTickSoundAt(now + interval); - } + private void applyField(TemporalField field, long now) { + if (field.center().getWorld() == null) { + return; } - private void spawnFieldSphere(TemporalField field, long now) { - if (!getConfig().showFieldSphere || now < field.nextVisualAt() || field.center().getWorld() == null) { - return; - } - - int particles = Math.max(24, getConfig().fieldSphereParticleCount); - double radius = Math.max(0.1, field.radius()); - vfxFastSphere(field.center(), radius, Color.BLACK, particles); - vfxFastSphere(field.center(), Math.max(0.2, radius * 0.75), Color.fromRGB(210, 210, 210), Math.max(12, particles / 2)); + Player owner = Bukkit.getPlayer(field.owner()); + + Collection nearby = field.center().getWorld().getNearbyEntities(field.center(), field.radius(), field.radius(), field.radius()); + int entitiesSlowed = 0; + for (Entity entity : nearby) { + if (entity.getLocation().distanceSquared(field.center()) > field.radius() * field.radius()) { + continue; + } + + if (!(entity instanceof Player)) { + boolean wasNew = !frozenEntities.containsKey(entity.getUniqueId()); + freezeEntity(entity); + if (wasNew && owner != null) { + entitiesSlowed++; + if (entity instanceof Projectile) { + getPlayer(owner).getData().addStat("chronos.time-bomb.projectiles-frozen", 1); + } + getPlayer(owner).getData().addStat("chronos.time-bomb.entities-slowed", 1); + } + continue; + } + + LivingEntity living = (LivingEntity) entity; + boolean caster = entity.getUniqueId().equals(field.owner()); + if (caster) { + living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().casterSlownessAmplifier, true, false, false), true); + living.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, getConfig().effectRefreshTicks, getConfig().fatigueAmplifier, true, false, false), true); + continue; + } + + if (owner != null) { + if (!canDamageTarget(owner, living)) { + continue; + } + } + + living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().slownessAmplifier, true, false, false), true); + living.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, getConfig().effectRefreshTicks, getConfig().fatigueAmplifier, true, false, false), true); + freezePlayer((Player) living); + living.setVelocity(new Vector()); + entitiesSlowed++; + } - field.setNextVisualAt(now + Math.max(1, getConfig().fieldSphereRefreshMillis)); + if (entitiesSlowed >= 8 && owner != null + && AdaptConfig.get().isAdvancements() + && !getPlayer(owner).getData().isGranted("challenge_chronos_bomb_crowd_8")) { + getPlayer(owner).getAdvancementHandler().grant("challenge_chronos_bomb_crowd_8"); } - @Override - public void onTick() { - if (J.isPrimaryThread()) { - onTickSync(); - return; - } + if (areParticlesEnabled()) { + spawnFieldSphere(field, now); + } - if (!syncTickQueued.compareAndSet(false, true)) { - return; - } + if (getConfig().playClockSounds && now >= field.nextTickSoundAt()) { + long totalDurationMillis = Math.max(50L, getDurationTicks(field.level()) * 50L); + long remainingMillis = Math.max(0L, field.expiresAt() - now); + double progress = 1D - (remainingMillis / (double) totalDurationMillis); + progress = Math.max(0D, Math.min(1D, progress)); + + double pitchCurve = Math.pow(progress, Math.max(0.1D, getConfig().fieldTickPitchCurveExponent)); + float pitch = (float) (getConfig().fieldTickPitchStart + + ((getConfig().fieldTickPitchEnd - getConfig().fieldTickPitchStart) * pitchCurve)); + ChronosSoundFX.playTimeFieldTick(field.center(), pitch); + + double acceleration = Math.max(0D, Math.min(0.95D, getConfig().fieldTickAccelerationFactor)); + long interval = (long) Math.max(getConfig().fieldTickMinIntervalMillis, + getConfig().fieldTickSoundIntervalMillis * (1D - (progress * acceleration))); + field.setNextTickSoundAt(now + interval); + } + } - J.s(() -> { - try { - onTickSync(); - } finally { - syncTickQueued.set(false); - } - }); - } - - private void onTickSync() { - long now = M.ms(); - cleanupBombProjectiles(now); - - for (UUID id : new HashSet<>(cooldownReadyNotify)) { - Player p = Bukkit.getPlayer(id); - if (p == null) { - cooldownReadyNotify.remove(id); - continue; - } - - long cooldown = cooldowns.getOrDefault(id, 0L); - if (cooldown <= now) { - if (getConfig().playClockSounds) { - ChronosSoundFX.playCooldownReady(p); - } - cooldownReadyNotify.remove(id); - } - } + private void spawnFieldSphere(TemporalField field, long now) { + if (!getConfig().showFieldSphere || now < field.nextVisualAt() || field.center().getWorld() == null) { + return; + } - fields.removeIf(field -> field.expiresAt() <= now); - cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + int particles = Math.max(24, getConfig().fieldSphereParticleCount); + double radius = Math.max(0.1, field.radius()); + vfxFastSphere(field.center(), radius, Color.BLACK, particles); + vfxFastSphere(field.center(), Math.max(0.2, radius * 0.75), Color.fromRGB(210, 210, 210), Math.max(12, particles / 2)); - for (TemporalField field : fields) { - applyField(field, now); - } + field.setNextVisualAt(now + Math.max(1, getConfig().fieldSphereRefreshMillis)); + } - for (Map.Entry entry : new ArrayList<>(frozenPlayers.entrySet())) { - UUID playerId = entry.getKey(); - Player player = Bukkit.getPlayer(playerId); - if (player == null || !player.isOnline() || player.isDead()) { - frozenPlayers.remove(playerId); - continue; - } - - if (shouldFreezePlayer(player)) { - freezePlayer(player); - continue; - } - - unfreezePlayer(playerId, entry.getValue()); - frozenPlayers.remove(playerId); - } + @Override + public void onTick() { + if (J.isPrimaryThread()) { + onTickSync(); + return; + } - for (Map.Entry entry : new ArrayList<>(frozenEntities.entrySet())) { - UUID entityId = entry.getKey(); - Entity entity = Bukkit.getEntity(entityId); - if (entity == null || entity.isDead() || !entity.isValid()) { - frozenEntities.remove(entityId); - continue; - } - - if (isInsideAnyField(entity.getLocation())) { - freezeEntity(entity); - continue; - } - - unfreezeEntity(entityId, entry.getValue()); - frozenEntities.remove(entityId); - } + if (!syncTickQueued.compareAndSet(false, true)) { + return; } - private void cleanupBombProjectiles(long now) { - for (Map.Entry entry : new ArrayList<>(activeBombProjectiles.entrySet())) { - UUID projectileId = entry.getKey(); - Entity entity = Bukkit.getEntity(projectileId); - if (entity == null || !entity.isValid() || entity.isDead()) { - activeBombProjectiles.remove(projectileId); - continue; - } + J.s(() -> { + try { + onTickSync(); + } finally { + syncTickQueued.set(false); + } + }); + } + + private void onTickSync() { + long now = M.ms(); + cleanupBombProjectiles(now); + + for (UUID id : new HashSet<>(cooldownReadyNotify)) { + Player p = Bukkit.getPlayer(id); + if (p == null) { + cooldownReadyNotify.remove(id); + continue; + } - if (now - entry.getValue().launchedAt() > PROJECTILE_TRACK_TTL_MS) { - activeBombProjectiles.remove(projectileId); - } + long cooldown = cooldowns.getOrDefault(id, 0L); + if (cooldown <= now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playCooldownReady(p); } + cooldownReadyNotify.remove(id); + } } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Throw a crafted chrono bomb that creates a temporal field, slowing entities and freezing projectiles.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean playClockSounds = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseRadius = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusPerLevel = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseDurationTicks = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int durationPerLevelTicks = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownMillis = 15000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Deploy Range for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double targetDeployRange = 64; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Center YOffset for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fieldCenterYOffset = 1.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int slownessAmplifier = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Caster Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int casterSlownessAmplifier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fatigueAmplifier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Freeze Players In Air for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean freezePlayersInAir = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Accumulate Frozen Impulse for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean accumulateFrozenImpulse = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Min Magnitude for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double frozenImpulseMinMagnitude = 0.03; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Sample Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double frozenImpulseSampleCap = 2.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Release Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double frozenImpulseReleaseCap = 7.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Refresh Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int effectRefreshTicks = 24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Field Sphere for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean showFieldSphere = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Particle Count for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fieldSphereParticleCount = 280; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Refresh Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long fieldSphereRefreshMillis = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Sound Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fieldTickSoundIntervalMillis = 325; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Min Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fieldTickMinIntervalMillis = 70; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Start for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fieldTickPitchStart = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch End for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fieldTickPitchEnd = 1.96; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Curve Exponent for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fieldTickPitchCurveExponent = 3.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Acceleration Factor for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fieldTickAccelerationFactor = 0.82; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Cast for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnCast = 28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerLevel = 3; - } - - private static final class TemporalField { - private final UUID owner; - private final Location center; - private final double radius; - private final long expiresAt; - private final int level; - private long nextTickSoundAt; - private long nextVisualAt; - - private TemporalField(UUID owner, Location center, double radius, long expiresAt, int level, long nextTickSoundAt, long nextVisualAt) { - this.owner = owner; - this.center = center; - this.radius = radius; - this.expiresAt = expiresAt; - this.level = level; - this.nextTickSoundAt = nextTickSoundAt; - this.nextVisualAt = nextVisualAt; - } + fields.removeIf(field -> field.expiresAt() <= now); + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); - private UUID owner() { - return owner; - } + for (TemporalField field : fields) { + applyField(field, now); + } - private Location center() { - return center; - } + for (Map.Entry entry : new ArrayList<>(frozenPlayers.entrySet())) { + UUID playerId = entry.getKey(); + Player player = Bukkit.getPlayer(playerId); + if (player == null || !player.isOnline() || player.isDead()) { + frozenPlayers.remove(playerId); + continue; + } + + if (shouldFreezePlayer(player)) { + freezePlayer(player); + continue; + } + + unfreezePlayer(playerId, entry.getValue()); + frozenPlayers.remove(playerId); + } - private double radius() { - return radius; - } + for (Map.Entry entry : new ArrayList<>(frozenEntities.entrySet())) { + UUID entityId = entry.getKey(); + Entity entity = Bukkit.getEntity(entityId); + if (entity == null || entity.isDead() || !entity.isValid()) { + frozenEntities.remove(entityId); + continue; + } + + if (isInsideAnyField(entity.getLocation())) { + freezeEntity(entity); + continue; + } + + unfreezeEntity(entityId, entry.getValue()); + frozenEntities.remove(entityId); + } + } + + private void cleanupBombProjectiles(long now) { + for (Map.Entry entry : new ArrayList<>(activeBombProjectiles.entrySet())) { + UUID projectileId = entry.getKey(); + Entity entity = Bukkit.getEntity(projectileId); + if (entity == null || !entity.isValid() || entity.isDead()) { + activeBombProjectiles.remove(projectileId); + continue; + } + + if (now - entry.getValue().launchedAt() > PROJECTILE_TRACK_TTL_MS) { + activeBombProjectiles.remove(projectileId); + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Throw a crafted chrono bomb that creates a temporal field, slowing entities and freezing projectiles.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseRadius = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusPerLevel = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Duration Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseDurationTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Per Level Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int durationPerLevelTicks = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownMillis = 15000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Deploy Range for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double targetDeployRange = 64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Center YOffset for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fieldCenterYOffset = 1.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int slownessAmplifier = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Caster Slowness Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int casterSlownessAmplifier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Amplifier for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fatigueAmplifier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Freeze Players In Air for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean freezePlayersInAir = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Accumulate Frozen Impulse for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean accumulateFrozenImpulse = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Min Magnitude for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double frozenImpulseMinMagnitude = 0.03; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Sample Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double frozenImpulseSampleCap = 2.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Frozen Impulse Release Cap for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double frozenImpulseReleaseCap = 7.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Refresh Ticks for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int effectRefreshTicks = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Field Sphere for the Chronos Time Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean showFieldSphere = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Particle Count for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fieldSphereParticleCount = 280; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Sphere Refresh Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long fieldSphereRefreshMillis = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Sound Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fieldTickSoundIntervalMillis = 325; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Min Interval Millis for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fieldTickMinIntervalMillis = 70; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Start for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fieldTickPitchStart = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch End for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fieldTickPitchEnd = 1.96; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Pitch Curve Exponent for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fieldTickPitchCurveExponent = 3.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Field Tick Acceleration Factor for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fieldTickAccelerationFactor = 0.82; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Cast for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnCast = 28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level for the Chronos Time Bomb adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerLevel = 3; + } + + private static final class TemporalField { + private final UUID owner; + private final Location center; + private final double radius; + private final long expiresAt; + private final int level; + private long nextTickSoundAt; + private long nextVisualAt; + + private TemporalField(UUID owner, Location center, double radius, long expiresAt, int level, long nextTickSoundAt, long nextVisualAt) { + this.owner = owner; + this.center = center; + this.radius = radius; + this.expiresAt = expiresAt; + this.level = level; + this.nextTickSoundAt = nextTickSoundAt; + this.nextVisualAt = nextVisualAt; + } - private long expiresAt() { - return expiresAt; - } + private UUID owner() { + return owner; + } - private int level() { - return level; - } + private Location center() { + return center; + } - private long nextTickSoundAt() { - return nextTickSoundAt; - } + private double radius() { + return radius; + } - private void setNextTickSoundAt(long nextTickSoundAt) { - this.nextTickSoundAt = nextTickSoundAt; - } + private long expiresAt() { + return expiresAt; + } - private long nextVisualAt() { - return nextVisualAt; - } + private int level() { + return level; + } - private void setNextVisualAt(long nextVisualAt) { - this.nextVisualAt = nextVisualAt; - } + private long nextTickSoundAt() { + return nextTickSoundAt; } - private static final class FrozenEntityState { - private final Vector originalVelocity; - private final boolean gravity; - private final Boolean hadAi; - private final Vector impulseDirectionAccumulator; - private double impulseMagnitudeAccumulator; - private int impulseSamples; - - private FrozenEntityState(Vector originalVelocity, boolean gravity, Boolean hadAi) { - this.originalVelocity = originalVelocity.clone(); - this.gravity = gravity; - this.hadAi = hadAi; - this.impulseDirectionAccumulator = new Vector(); - this.impulseMagnitudeAccumulator = 0D; - this.impulseSamples = 0; - } + private void setNextTickSoundAt(long nextTickSoundAt) { + this.nextTickSoundAt = nextTickSoundAt; + } - private boolean gravity() { - return gravity; - } + private long nextVisualAt() { + return nextVisualAt; + } - private Boolean hadAi() { - return hadAi; - } + private void setNextVisualAt(long nextVisualAt) { + this.nextVisualAt = nextVisualAt; + } + } + + private static final class FrozenEntityState { + private final Vector originalVelocity; + private final boolean gravity; + private final Boolean hadAi; + private final Vector impulseDirectionAccumulator; + private double impulseMagnitudeAccumulator; + private int impulseSamples; + + private FrozenEntityState(Vector originalVelocity, boolean gravity, Boolean hadAi) { + this.originalVelocity = originalVelocity.clone(); + this.gravity = gravity; + this.hadAi = hadAi; + this.impulseDirectionAccumulator = new Vector(); + this.impulseMagnitudeAccumulator = 0D; + this.impulseSamples = 0; + } - private void captureImpulse(Vector currentVelocity, double minMagnitude, double sampleCap) { - if (currentVelocity == null) { - return; - } - - Vector sample = currentVelocity.clone(); - double magnitude = sample.length(); - if (magnitude < Math.max(0D, minMagnitude)) { - return; - } - - if (sampleCap > 0D && magnitude > sampleCap) { - sample = sample.normalize().multiply(sampleCap); - magnitude = sampleCap; - } - - if (magnitude <= 0D) { - return; - } - - impulseDirectionAccumulator.add(sample); - impulseMagnitudeAccumulator += magnitude; - impulseSamples++; - } + private boolean gravity() { + return gravity; + } - private Vector buildReleaseVelocity(double releaseCap) { - Vector release = originalVelocity.clone(); - if (impulseSamples <= 0 - || impulseMagnitudeAccumulator <= 0D - || impulseDirectionAccumulator.lengthSquared() <= 1.0E-6D) { - return release; - } - - double magnitude = impulseMagnitudeAccumulator; - if (releaseCap > 0D) { - magnitude = Math.min(magnitude, releaseCap); - } - - Vector direction = impulseDirectionAccumulator.clone().normalize(); - release.add(direction.multiply(magnitude)); - return release; - } + private Boolean hadAi() { + return hadAi; } - private record FrozenPlayerState(boolean allowFlight, boolean flying, boolean gravity) { + private void captureImpulse(Vector currentVelocity, double minMagnitude, double sampleCap) { + if (currentVelocity == null) { + return; + } + + Vector sample = currentVelocity.clone(); + double magnitude = sample.length(); + if (magnitude < Math.max(0D, minMagnitude)) { + return; + } + + if (sampleCap > 0D && magnitude > sampleCap) { + sample = sample.normalize().multiply(sampleCap); + magnitude = sampleCap; + } + + if (magnitude <= 0D) { + return; + } + + impulseDirectionAccumulator.add(sample); + impulseMagnitudeAccumulator += magnitude; + impulseSamples++; } - private record ArmedBombProjectile(UUID owner, int level, long launchedAt) { + private Vector buildReleaseVelocity(double releaseCap) { + Vector release = originalVelocity.clone(); + if (impulseSamples <= 0 + || impulseMagnitudeAccumulator <= 0D + || impulseDirectionAccumulator.lengthSquared() <= 1.0E-6D) { + return release; + } + + double magnitude = impulseMagnitudeAccumulator; + if (releaseCap > 0D) { + magnitude = Math.min(magnitude, releaseCap); + } + + Vector direction = impulseDirectionAccumulator.clone().normalize(); + release.add(direction.multiply(magnitude)); + return release; } + } + + private record FrozenPlayerState(boolean allowFlight, boolean flying, + boolean gravity) { + } + + private record ArmedBombProjectile(UUID owner, int level, long launchedAt) { + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java index 5ba858a3e..4dee1a529 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java @@ -26,8 +26,8 @@ import art.arcane.adapt.content.item.ChronoTimeBottle; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Keyed; import org.bukkit.Material; @@ -57,761 +57,766 @@ import java.util.concurrent.ThreadLocalRandom; public class ChronosTimeInABottle extends SimpleAdaptation { - private static final String RECIPE_KEY = "chronos-time-in-a-bottle"; - - public ChronosTimeInABottle() { - super("chronos-time-bottle"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("chronos.time_in_a_bottle.description")); - setDisplayName(Localizer.dLocalize("chronos.time_in_a_bottle.name")); - setIcon(Material.CLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1000); - - registerRecipe(AdaptRecipe.shapeless() - .key(RECIPE_KEY) - .ingredient(Material.CLOCK) - .ingredient(Material.POTION) - .ingredient(Material.GLASS_BOTTLE) - .result(ChronoTimeBottle.withStoredSeconds(0)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLOCK) - .key("challenge_chronos_bottle_1k") - .title(Localizer.dLocalize("advancement.challenge_chronos_bottle_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_bottle_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RECOVERY_COMPASS) - .key("challenge_chronos_bottle_25k") - .title(Localizer.dLocalize("advancement.challenge_chronos_bottle_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_bottle_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_chronos_bottle_1k", "chronos.time-bottle.charges-spent", 1000, 500); - registerMilestone("challenge_chronos_bottle_25k", "chronos.time-bottle.charges-spent", 25000, 2000); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(CraftItemEvent e) { - if (!(e.getRecipe() instanceof Keyed keyed) || !keyed.getKey().getKey().equals(RECIPE_KEY)) { - return; - } + private static final String RECIPE_KEY = "chronos-time-in-a-bottle"; + + public ChronosTimeInABottle() { + super("chronos-time-bottle"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.time_in_a_bottle.description")); + setDisplayName(Localizer.dLocalize("chronos.time_in_a_bottle.name")); + setIcon(Material.CLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + + registerRecipe(AdaptRecipe.shapeless() + .key(RECIPE_KEY) + .ingredient(Material.CLOCK) + .ingredient(Material.POTION) + .ingredient(Material.GLASS_BOTTLE) + .result(ChronoTimeBottle.withStoredSeconds(0)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_bottle_1k") + .title(Localizer.dLocalize("advancement.challenge_chronos_bottle_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_bottle_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_chronos_bottle_25k") + .title(Localizer.dLocalize("advancement.challenge_chronos_bottle_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_bottle_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_chronos_bottle_1k", "chronos.time-bottle.charges-spent", 1000, 500); + registerMilestone("challenge_chronos_bottle_25k", "chronos.time-bottle.charges-spent", 25000, 2000); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(CraftItemEvent e) { + if (!(e.getRecipe() instanceof Keyed keyed) || !keyed.getKey().getKey().equals(RECIPE_KEY)) { + return; + } + + boolean hasSwiftnessPotion = false; + for (ItemStack item : e.getInventory().getMatrix()) { + if (item == null || item.getType() != Material.POTION) { + continue; + } + if (item.getItemMeta() instanceof PotionMeta meta && meta.getBasePotionType() == PotionType.SWIFTNESS) { + hasSwiftnessPotion = true; + break; + } + } + + if (!hasSwiftnessPotion) { + e.setCancelled(true); + if (e.getWhoClicked() instanceof Player p && getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + } + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getConfig().chargePerSecond + (level * getConfig().chargePerSecondPerLevel)) + " " + Localizer.dLocalize("chronos.time_in_a_bottle.lore1")); + v.addLore(C.YELLOW + "+ " + Math.round(getCookTicksPerStoredSecond(level)) + " " + Localizer.dLocalize("chronos.time_in_a_bottle.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.time_in_a_bottle.lore3")); + } + + private double getCookTicksPerStoredSecond(int level) { + return getConfig().baseCookTicksPerStoredSecond + (level * getConfig().cookTicksPerSecondPerLevel); + } + + private int getMaxCookTicksPerUse(int level) { + return getConfig().maxCookTicksPerUse + (level * getConfig().maxCookTicksPerUsePerLevel); + } + + private double getBrewingTicksPerStoredSecond(int level) { + return getConfig().baseBrewingTicksPerStoredSecond + (level * getConfig().brewingTicksPerSecondPerLevel); + } + + private int getMaxBrewingTicksPerUse(int level) { + return getConfig().maxBrewingTicksPerUse + (level * getConfig().maxBrewingTicksPerUsePerLevel); + } + + private double getCampfireTicksPerStoredSecond(int level) { + return getConfig().baseCampfireTicksPerStoredSecond + (level * getConfig().campfireTicksPerSecondPerLevel); + } + + private int getMaxCampfireTicksPerUse(int level) { + return getConfig().maxCampfireTicksPerUse + (level * getConfig().maxCampfireTicksPerUsePerLevel); + } + + private double getFurnaceSpendMultiplier() { + return Math.max(0.01, getConfig().furnaceSpendMultiplier); + } + + private double getBrewingSpendMultiplier() { + return Math.max(0.01, getConfig().brewingSpendMultiplier); + } + + private double getCampfireSpendMultiplier() { + return Math.max(0.01, getConfig().campfireSpendMultiplier); + } + + private double getEntityAgeTicksPerStoredSecond(int level) { + return getConfig().baseEntityAgeTicksPerStoredSecond + (level * getConfig().entityAgeTicksPerSecondPerLevel); + } + + private int getMaxEntityAgeTicksPerUse(int level) { + return getConfig().maxEntityAgeTicksPerUse + (level * getConfig().maxEntityAgeTicksPerUsePerLevel); + } + + private double getEntitySpendMultiplier() { + return Math.max(0.01, getConfig().entitySpendMultiplier); + } + + private int getMaxGrowthStepsPerUse(int level) { + return getConfig().maxGrowthStepsPerUse + (level * getConfig().maxGrowthStepsPerUsePerLevel); + } + + private double getGrowthLevelScale(int level) { + return Math.max(getConfig().minGrowthCostLevelScale, 1D - (level * getConfig().growthCostReductionPerLevel)); + } + + private double getSaplingGrowChance(int level) { + return Math.max(0, Math.min(1, getConfig().saplingGrowChanceBase + (level * getConfig().saplingGrowChancePerLevel))); + } + + private TreeType getTreeType(Material type) { + return switch (type) { + case OAK_SAPLING -> + ThreadLocalRandom.current().nextBoolean() ? TreeType.TREE : TreeType.BIG_TREE; + case SPRUCE_SAPLING -> + ThreadLocalRandom.current().nextBoolean() ? TreeType.REDWOOD : TreeType.TALL_REDWOOD; + case BIRCH_SAPLING -> TreeType.BIRCH; + case JUNGLE_SAPLING -> + ThreadLocalRandom.current().nextBoolean() ? TreeType.SMALL_JUNGLE : TreeType.JUNGLE; + case ACACIA_SAPLING -> TreeType.ACACIA; + case DARK_OAK_SAPLING -> TreeType.DARK_OAK; + case CHERRY_SAPLING -> TreeType.CHERRY; + case MANGROVE_PROPAGULE -> + ThreadLocalRandom.current().nextBoolean() ? TreeType.MANGROVE : TreeType.TALL_MANGROVE; + case AZALEA, FLOWERING_AZALEA -> TreeType.AZALEA; + default -> null; + }; + } + + private double getGrowthStepCostSeconds(Block target, int level) { + GrowthProfile profile = detectGrowthProfile(target.getType()); + double naturalSeconds = getNaturalGrowthSeconds(profile); + int steps = getEstimatedGrowthSteps(target, profile); + double baseStepSeconds = naturalSeconds / Math.max(1, steps); + double profileMultiplier = getGrowthProfileCostMultiplier(profile); + double scaled = baseStepSeconds * profileMultiplier * getConfig().growthCostMultiplier * getGrowthLevelScale(level); + return Math.max(getConfig().minGrowthStepSeconds, scaled); + } + + private int getEstimatedGrowthSteps(Block target, GrowthProfile profile) { + BlockData data = target.getBlockData(); + if (data instanceof Ageable ageable) { + return Math.max(1, ageable.getMaximumAge()); + } + + if (data instanceof Sapling) { + return Math.max(1, getConfig().saplingGrowthSteps); + } + + return switch (profile) { + case SAPLING -> Math.max(1, getConfig().saplingGrowthSteps); + case STEM -> Math.max(1, getConfig().stemGrowthSteps); + case BERRY_BUSH -> Math.max(1, getConfig().berryGrowthSteps); + case VINE -> Math.max(1, getConfig().vineGrowthSteps); + case CAVE_VINE -> Math.max(1, getConfig().caveVineGrowthSteps); + case KELP -> Math.max(1, getConfig().kelpGrowthSteps); + default -> Math.max(1, getConfig().defaultGrowthSteps); + }; + } + + private double getNaturalGrowthSeconds(GrowthProfile profile) { + return switch (profile) { + case CROP -> getConfig().cropNaturalSeconds; + case NETHER_WART -> getConfig().netherWartNaturalSeconds; + case SAPLING -> getConfig().saplingNaturalSeconds; + case STEM -> getConfig().stemNaturalSeconds; + case BERRY_BUSH -> getConfig().berryBushNaturalSeconds; + case VINE -> getConfig().vineNaturalSeconds; + case CAVE_VINE -> getConfig().caveVineNaturalSeconds; + case KELP -> getConfig().kelpNaturalSeconds; + default -> getConfig().defaultGrowableNaturalSeconds; + }; + } + + private double getGrowthProfileCostMultiplier(GrowthProfile profile) { + return switch (profile) { + case CROP -> getConfig().cropCostMultiplier; + case NETHER_WART -> getConfig().netherWartCostMultiplier; + case SAPLING -> getConfig().saplingCostMultiplier; + case STEM -> getConfig().stemCostMultiplier; + case BERRY_BUSH -> getConfig().berryBushCostMultiplier; + case VINE -> getConfig().vineCostMultiplier; + case CAVE_VINE -> getConfig().caveVineCostMultiplier; + case KELP -> getConfig().kelpCostMultiplier; + default -> getConfig().defaultGrowableCostMultiplier; + }; + } - boolean hasSwiftnessPotion = false; - for (ItemStack item : e.getInventory().getMatrix()) { - if (item == null || item.getType() != Material.POTION) { - continue; - } - if (item.getItemMeta() instanceof PotionMeta meta && meta.getBasePotionType() == PotionType.SWIFTNESS) { - hasSwiftnessPotion = true; - break; - } - } - - if (!hasSwiftnessPotion) { - e.setCancelled(true); - if (e.getWhoClicked() instanceof Player p && getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(p); - } - } - } + private GrowthProfile detectGrowthProfile(Material type) { + String name = type.name(); - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + (getConfig().chargePerSecond + (level * getConfig().chargePerSecondPerLevel)) + " " + Localizer.dLocalize("chronos.time_in_a_bottle.lore1")); - v.addLore(C.YELLOW + "+ " + Math.round(getCookTicksPerStoredSecond(level)) + " " + Localizer.dLocalize("chronos.time_in_a_bottle.lore2")); - v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.time_in_a_bottle.lore3")); + if (type == Material.NETHER_WART) { + return GrowthProfile.NETHER_WART; } - private double getCookTicksPerStoredSecond(int level) { - return getConfig().baseCookTicksPerStoredSecond + (level * getConfig().cookTicksPerSecondPerLevel); + if (type == Material.SWEET_BERRY_BUSH) { + return GrowthProfile.BERRY_BUSH; } - private int getMaxCookTicksPerUse(int level) { - return getConfig().maxCookTicksPerUse + (level * getConfig().maxCookTicksPerUsePerLevel); + if (type == Material.KELP || type == Material.KELP_PLANT) { + return GrowthProfile.KELP; } - private double getBrewingTicksPerStoredSecond(int level) { - return getConfig().baseBrewingTicksPerStoredSecond + (level * getConfig().brewingTicksPerSecondPerLevel); + if (type == Material.CAVE_VINES || type == Material.CAVE_VINES_PLANT) { + return GrowthProfile.CAVE_VINE; } - private int getMaxBrewingTicksPerUse(int level) { - return getConfig().maxBrewingTicksPerUse + (level * getConfig().maxBrewingTicksPerUsePerLevel); + if (type == Material.VINE || type == Material.WEEPING_VINES || type == Material.WEEPING_VINES_PLANT + || type == Material.TWISTING_VINES || type == Material.TWISTING_VINES_PLANT) { + return GrowthProfile.VINE; } - private double getCampfireTicksPerStoredSecond(int level) { - return getConfig().baseCampfireTicksPerStoredSecond + (level * getConfig().campfireTicksPerSecondPerLevel); + if (name.endsWith("_SAPLING") || type == Material.MANGROVE_PROPAGULE || type == Material.AZALEA + || type == Material.FLOWERING_AZALEA) { + return GrowthProfile.SAPLING; } - private int getMaxCampfireTicksPerUse(int level) { - return getConfig().maxCampfireTicksPerUse + (level * getConfig().maxCampfireTicksPerUsePerLevel); + if (type == Material.PUMPKIN_STEM || type == Material.MELON_STEM || type == Material.ATTACHED_MELON_STEM + || type == Material.ATTACHED_PUMPKIN_STEM) { + return GrowthProfile.STEM; } - private double getFurnaceSpendMultiplier() { - return Math.max(0.01, getConfig().furnaceSpendMultiplier); + if (type == Material.WHEAT || type == Material.CARROTS || type == Material.POTATOES || type == Material.BEETROOTS + || type == Material.COCOA || type == Material.TORCHFLOWER_CROP || type == Material.PITCHER_CROP) { + return GrowthProfile.CROP; } - private double getBrewingSpendMultiplier() { - return Math.max(0.01, getConfig().brewingSpendMultiplier); - } + return GrowthProfile.DEFAULT; + } - private double getCampfireSpendMultiplier() { - return Math.max(0.01, getConfig().campfireSpendMultiplier); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerItemConsumeEvent e) { + if (ChronoTimeBottle.isBindableItem(e.getItem())) { + e.setCancelled(true); + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(e.getPlayer()); + } } + } - private double getEntityAgeTicksPerStoredSecond(int level) { - return getConfig().baseEntityAgeTicksPerStoredSecond + (level * getConfig().entityAgeTicksPerSecondPerLevel); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; } - private int getMaxEntityAgeTicksPerUse(int level) { - return getConfig().maxEntityAgeTicksPerUse + (level * getConfig().maxEntityAgeTicksPerUsePerLevel); + Player p = e.getPlayer(); + EquipmentSlot handSlot = e.getHand(); + if (handSlot == null) { + return; } - private double getEntitySpendMultiplier() { - return Math.max(0.01, getConfig().entitySpendMultiplier); + ItemStack hand = handSlot == EquipmentSlot.OFF_HAND + ? p.getInventory().getItemInOffHand() + : p.getInventory().getItemInMainHand(); + if (!ChronoTimeBottle.isBindableItem(hand)) { + return; } - private int getMaxGrowthStepsPerUse(int level) { - return getConfig().maxGrowthStepsPerUse + (level * getConfig().maxGrowthStepsPerUsePerLevel); - } + // Chrono bottles are never drinkable; always deny vanilla potion use. + e.setUseItemInHand(Event.Result.DENY); - private double getGrowthLevelScale(int level) { - return Math.max(getConfig().minGrowthCostLevelScale, 1D - (level * getConfig().growthCostReductionPerLevel)); + if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + return; } - private double getSaplingGrowChance(int level) { - return Math.max(0, Math.min(1, getConfig().saplingGrowChanceBase + (level * getConfig().saplingGrowChancePerLevel))); + Block clicked = e.getClickedBlock(); + int level = getActiveInteractLevel(p, clicked.getLocation()); + if (level <= 0) { + return; } - - private TreeType getTreeType(Material type) { - return switch (type) { - case OAK_SAPLING -> ThreadLocalRandom.current().nextBoolean() ? TreeType.TREE : TreeType.BIG_TREE; - case SPRUCE_SAPLING -> ThreadLocalRandom.current().nextBoolean() ? TreeType.REDWOOD : TreeType.TALL_REDWOOD; - case BIRCH_SAPLING -> TreeType.BIRCH; - case JUNGLE_SAPLING -> ThreadLocalRandom.current().nextBoolean() ? TreeType.SMALL_JUNGLE : TreeType.JUNGLE; - case ACACIA_SAPLING -> TreeType.ACACIA; - case DARK_OAK_SAPLING -> TreeType.DARK_OAK; - case CHERRY_SAPLING -> TreeType.CHERRY; - case MANGROVE_PROPAGULE -> ThreadLocalRandom.current().nextBoolean() ? TreeType.MANGROVE : TreeType.TALL_MANGROVE; - case AZALEA, FLOWERING_AZALEA -> TreeType.AZALEA; - default -> null; - }; + double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); + if (storedSeconds <= 0) { + return; } - private double getGrowthStepCostSeconds(Block target, int level) { - GrowthProfile profile = detectGrowthProfile(target.getType()); - double naturalSeconds = getNaturalGrowthSeconds(profile); - int steps = getEstimatedGrowthSteps(target, profile); - double baseStepSeconds = naturalSeconds / Math.max(1, steps); - double profileMultiplier = getGrowthProfileCostMultiplier(profile); - double scaled = baseStepSeconds * profileMultiplier * getConfig().growthCostMultiplier * getGrowthLevelScale(level); - return Math.max(getConfig().minGrowthStepSeconds, scaled); + TimeSpendResult result = accelerateTarget(clicked, storedSeconds, level); + if (!result.applied()) { + return; } - private int getEstimatedGrowthSteps(Block target, GrowthProfile profile) { - BlockData data = target.getBlockData(); - if (data instanceof Ageable ageable) { - return Math.max(1, ageable.getMaximumAge()); - } - - if (data instanceof Sapling) { - return Math.max(1, getConfig().saplingGrowthSteps); - } - - return switch (profile) { - case SAPLING -> Math.max(1, getConfig().saplingGrowthSteps); - case STEM -> Math.max(1, getConfig().stemGrowthSteps); - case BERRY_BUSH -> Math.max(1, getConfig().berryGrowthSteps); - case VINE -> Math.max(1, getConfig().vineGrowthSteps); - case CAVE_VINE -> Math.max(1, getConfig().caveVineGrowthSteps); - case KELP -> Math.max(1, getConfig().kelpGrowthSteps); - default -> Math.max(1, getConfig().defaultGrowthSteps); - }; - } - - private double getNaturalGrowthSeconds(GrowthProfile profile) { - return switch (profile) { - case CROP -> getConfig().cropNaturalSeconds; - case NETHER_WART -> getConfig().netherWartNaturalSeconds; - case SAPLING -> getConfig().saplingNaturalSeconds; - case STEM -> getConfig().stemNaturalSeconds; - case BERRY_BUSH -> getConfig().berryBushNaturalSeconds; - case VINE -> getConfig().vineNaturalSeconds; - case CAVE_VINE -> getConfig().caveVineNaturalSeconds; - case KELP -> getConfig().kelpNaturalSeconds; - default -> getConfig().defaultGrowableNaturalSeconds; - }; - } - - private double getGrowthProfileCostMultiplier(GrowthProfile profile) { - return switch (profile) { - case CROP -> getConfig().cropCostMultiplier; - case NETHER_WART -> getConfig().netherWartCostMultiplier; - case SAPLING -> getConfig().saplingCostMultiplier; - case STEM -> getConfig().stemCostMultiplier; - case BERRY_BUSH -> getConfig().berryBushCostMultiplier; - case VINE -> getConfig().vineCostMultiplier; - case CAVE_VINE -> getConfig().caveVineCostMultiplier; - case KELP -> getConfig().kelpCostMultiplier; - default -> getConfig().defaultGrowableCostMultiplier; - }; - } - - private GrowthProfile detectGrowthProfile(Material type) { - String name = type.name(); - - if (type == Material.NETHER_WART) { - return GrowthProfile.NETHER_WART; - } - - if (type == Material.SWEET_BERRY_BUSH) { - return GrowthProfile.BERRY_BUSH; - } - - if (type == Material.KELP || type == Material.KELP_PLANT) { - return GrowthProfile.KELP; - } - - if (type == Material.CAVE_VINES || type == Material.CAVE_VINES_PLANT) { - return GrowthProfile.CAVE_VINE; - } - - if (type == Material.VINE || type == Material.WEEPING_VINES || type == Material.WEEPING_VINES_PLANT - || type == Material.TWISTING_VINES || type == Material.TWISTING_VINES_PLANT) { - return GrowthProfile.VINE; - } - - if (name.endsWith("_SAPLING") || type == Material.MANGROVE_PROPAGULE || type == Material.AZALEA - || type == Material.FLOWERING_AZALEA) { - return GrowthProfile.SAPLING; - } - - if (type == Material.PUMPKIN_STEM || type == Material.MELON_STEM || type == Material.ATTACHED_MELON_STEM - || type == Material.ATTACHED_PUMPKIN_STEM) { - return GrowthProfile.STEM; - } + e.setCancelled(true); + ChronoTimeBottle.setStoredSeconds(hand, Math.max(0, storedSeconds - result.spentSeconds())); + getPlayer(p).getData().addStat("chronos.time-bottle.charges-spent", 1); - if (type == Material.WHEAT || type == Material.CARROTS || type == Material.POTATOES || type == Material.BEETROOTS - || type == Material.COCOA || type == Material.TORCHFLOWER_CROP || type == Material.PITCHER_CROP) { - return GrowthProfile.CROP; - } - - return GrowthProfile.DEFAULT; + if (getConfig().playClockSounds) { + ChronosSoundFX.playBottleUse(p, clicked.getLocation().add(0.5, 1.0, 0.5), result.effectTicks()); } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerItemConsumeEvent e) { - if (ChronoTimeBottle.isBindableItem(e.getItem())) { - e.setCancelled(true); - if (getConfig().playClockSounds) { - ChronosSoundFX.playClockReject(e.getPlayer()); - } - } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.ENCHANT, clicked.getLocation().add(0.5, 1.0, 0.5), 32, 0.35, 0.3, 0.35, 0.08); + p.getWorld().spawnParticle(Particle.END_ROD, clicked.getLocation().add(0.5, 1.0, 0.5), 8, 0.1, 0.2, 0.1, 0.01); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { - return; - } - - Player p = e.getPlayer(); - EquipmentSlot handSlot = e.getHand(); - if (handSlot == null) { - return; - } - - ItemStack hand = handSlot == EquipmentSlot.OFF_HAND - ? p.getInventory().getItemInOffHand() - : p.getInventory().getItemInMainHand(); - if (!ChronoTimeBottle.isBindableItem(hand)) { - return; - } - - // Chrono bottles are never drinkable; always deny vanilla potion use. - e.setUseItemInHand(Event.Result.DENY); - - if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { - return; - } - - Block clicked = e.getClickedBlock(); - int level = getActiveInteractLevel(p, clicked.getLocation()); - if (level <= 0) { - return; - } - double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); - if (storedSeconds <= 0) { - return; - } - - TimeSpendResult result = accelerateTarget(clicked, storedSeconds, level); - if (!result.applied()) { - return; - } + xp(p, clicked.getLocation().add(0.5, 1.0, 0.5), Math.min(getConfig().maxXPPerUse, result.xpGain())); + } - e.setCancelled(true); - ChronoTimeBottle.setStoredSeconds(hand, Math.max(0, storedSeconds - result.spentSeconds())); - getPlayer(p).getData().addStat("chronos.time-bottle.charges-spent", 1); - - if (getConfig().playClockSounds) { - ChronosSoundFX.playBottleUse(p, clicked.getLocation().add(0.5, 1.0, 0.5), result.effectTicks()); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.ENCHANT, clicked.getLocation().add(0.5, 1.0, 0.5), 32, 0.35, 0.3, 0.35, 0.08); - p.getWorld().spawnParticle(Particle.END_ROD, clicked.getLocation().add(0.5, 1.0, 0.5), 8, 0.1, 0.2, 0.1, 0.01); - } - - xp(p, clicked.getLocation().add(0.5, 1.0, 0.5), Math.min(getConfig().maxXPPerUse, result.xpGain())); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEntityEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEntityEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - Player p = e.getPlayer(); - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!ChronoTimeBottle.isBindableItem(hand)) { - return; - } - - if (!(e.getRightClicked() instanceof org.bukkit.entity.Ageable ageable)) { - return; - } - - int level = getActiveInteractLevel(p, e.getRightClicked().getLocation()); - if (level <= 0) { - return; - } - - int currentAge = ageable.getAge(); - if (currentAge == 0) { - return; - } - double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); - if (storedSeconds <= 0) { - return; - } + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!ChronoTimeBottle.isBindableItem(hand)) { + return; + } - TimeSpendResult result = accelerateAgeableEntity(ageable, storedSeconds, level); - if (!result.applied()) { - return; - } + if (!(e.getRightClicked() instanceof org.bukkit.entity.Ageable ageable)) { + return; + } - e.setCancelled(true); - ChronoTimeBottle.setStoredSeconds(hand, Math.max(0, storedSeconds - result.spentSeconds())); - getPlayer(p).getData().addStat("chronos.time-bottle.charges-spent", 1); + int level = getActiveInteractLevel(p, e.getRightClicked().getLocation()); + if (level <= 0) { + return; + } - if (getConfig().playClockSounds) { - ChronosSoundFX.playBottleUse(p, ageable.getLocation().add(0, 1.0, 0), result.effectTicks()); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.ENCHANT, ageable.getLocation().add(0, 1.0, 0), 24, 0.3, 0.4, 0.3, 0.05); - p.getWorld().spawnParticle(Particle.END_ROD, ageable.getLocation().add(0, 1.0, 0), 7, 0.1, 0.25, 0.1, 0.01); - } + int currentAge = ageable.getAge(); + if (currentAge == 0) { + return; + } + double storedSeconds = ChronoTimeBottle.getStoredSeconds(hand); + if (storedSeconds <= 0) { + return; + } - xp(p, ageable.getLocation().add(0, 1.0, 0), Math.min(getConfig().maxXPPerUse, result.xpGain())); + TimeSpendResult result = accelerateAgeableEntity(ageable, storedSeconds, level); + if (!result.applied()) { + return; } - private TimeSpendResult accelerateTarget(Block clicked, double storedSeconds, int level) { - if (clicked.getState() instanceof Furnace furnace) { - return accelerateFurnace(furnace, storedSeconds, level); - } + e.setCancelled(true); + ChronoTimeBottle.setStoredSeconds(hand, Math.max(0, storedSeconds - result.spentSeconds())); + getPlayer(p).getData().addStat("chronos.time-bottle.charges-spent", 1); - if (clicked.getState() instanceof BrewingStand brewingStand) { - return accelerateBrewingStand(brewingStand, storedSeconds, level); - } + if (getConfig().playClockSounds) { + ChronosSoundFX.playBottleUse(p, ageable.getLocation().add(0, 1.0, 0), result.effectTicks()); + } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.ENCHANT, ageable.getLocation().add(0, 1.0, 0), 24, 0.3, 0.4, 0.3, 0.05); + p.getWorld().spawnParticle(Particle.END_ROD, ageable.getLocation().add(0, 1.0, 0), 7, 0.1, 0.25, 0.1, 0.01); + } - if (clicked.getState() instanceof Campfire campfire) { - return accelerateCampfire(campfire, storedSeconds, level); - } + xp(p, ageable.getLocation().add(0, 1.0, 0), Math.min(getConfig().maxXPPerUse, result.xpGain())); + } - return accelerateGrowables(clicked, storedSeconds, level); + private TimeSpendResult accelerateTarget(Block clicked, double storedSeconds, int level) { + if (clicked.getState() instanceof Furnace furnace) { + return accelerateFurnace(furnace, storedSeconds, level); } - private TimeSpendResult accelerateFurnace(Furnace furnace, double storedSeconds, int level) { - if (furnace.getInventory().getSmelting() == null || furnace.getInventory().getSmelting().getType().isAir()) { - return TimeSpendResult.none(); - } + if (clicked.getState() instanceof BrewingStand brewingStand) { + return accelerateBrewingStand(brewingStand, storedSeconds, level); + } - int totalCookTime = furnace.getCookTimeTotal(); - int currentCookTime = furnace.getCookTime(); - int remainingCookTime = Math.max(0, totalCookTime - currentCookTime); - if (remainingCookTime <= 0) { - return TimeSpendResult.none(); - } + if (clicked.getState() instanceof Campfire campfire) { + return accelerateCampfire(campfire, storedSeconds, level); + } - double cookTicksPerStoredSecond = getCookTicksPerStoredSecond(level); - double spendMultiplier = getFurnaceSpendMultiplier(); - int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * cookTicksPerStoredSecond); - int advanceTicks = Math.min(remainingCookTime, Math.min(affordableTicks, getMaxCookTicksPerUse(level))); - if (advanceTicks <= 0) { - return TimeSpendResult.none(); - } + return accelerateGrowables(clicked, storedSeconds, level); + } - furnace.setCookTime((short) Math.min(totalCookTime, currentCookTime + advanceTicks)); - furnace.update(true, true); + private TimeSpendResult accelerateFurnace(Furnace furnace, double storedSeconds, int level) { + if (furnace.getInventory().getSmelting() == null || furnace.getInventory().getSmelting().getType().isAir()) { + return TimeSpendResult.none(); + } - double spentSeconds = (advanceTicks / cookTicksPerStoredSecond) * spendMultiplier; - return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerCookTick); + int totalCookTime = furnace.getCookTimeTotal(); + int currentCookTime = furnace.getCookTime(); + int remainingCookTime = Math.max(0, totalCookTime - currentCookTime); + if (remainingCookTime <= 0) { + return TimeSpendResult.none(); } - private TimeSpendResult accelerateBrewingStand(BrewingStand stand, double storedSeconds, int level) { - int brewingTime = stand.getBrewingTime(); - if (brewingTime <= 0) { - return TimeSpendResult.none(); - } + double cookTicksPerStoredSecond = getCookTicksPerStoredSecond(level); + double spendMultiplier = getFurnaceSpendMultiplier(); + int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * cookTicksPerStoredSecond); + int advanceTicks = Math.min(remainingCookTime, Math.min(affordableTicks, getMaxCookTicksPerUse(level))); + if (advanceTicks <= 0) { + return TimeSpendResult.none(); + } - double brewTicksPerStoredSecond = getBrewingTicksPerStoredSecond(level); - double spendMultiplier = getBrewingSpendMultiplier(); - int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * brewTicksPerStoredSecond); - int advanceTicks = Math.min(brewingTime, Math.min(affordableTicks, getMaxBrewingTicksPerUse(level))); - if (advanceTicks <= 0) { - return TimeSpendResult.none(); - } + furnace.setCookTime((short) Math.min(totalCookTime, currentCookTime + advanceTicks)); + furnace.update(true, true); - stand.setBrewingTime(Math.max(0, brewingTime - advanceTicks)); - stand.update(true, true); + double spentSeconds = (advanceTicks / cookTicksPerStoredSecond) * spendMultiplier; + return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerCookTick); + } - double spentSeconds = (advanceTicks / brewTicksPerStoredSecond) * spendMultiplier; - return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerBrewTick); + private TimeSpendResult accelerateBrewingStand(BrewingStand stand, double storedSeconds, int level) { + int brewingTime = stand.getBrewingTime(); + if (brewingTime <= 0) { + return TimeSpendResult.none(); } - private TimeSpendResult accelerateCampfire(Campfire campfire, double storedSeconds, int level) { - double campfireTicksPerStoredSecond = getCampfireTicksPerStoredSecond(level); - double spendMultiplier = getCampfireSpendMultiplier(); - int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * campfireTicksPerStoredSecond); - int budgetTicks = Math.min(affordableTicks, getMaxCampfireTicksPerUse(level)); - if (budgetTicks <= 0) { - return TimeSpendResult.none(); - } + double brewTicksPerStoredSecond = getBrewingTicksPerStoredSecond(level); + double spendMultiplier = getBrewingSpendMultiplier(); + int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * brewTicksPerStoredSecond); + int advanceTicks = Math.min(brewingTime, Math.min(affordableTicks, getMaxBrewingTicksPerUse(level))); + if (advanceTicks <= 0) { + return TimeSpendResult.none(); + } - int usedTicks = 0; - for (int i = 0; i < campfire.getSize() && usedTicks < budgetTicks; i++) { - ItemStack item = campfire.getItem(i); - if (item == null || item.getType().isAir()) { - continue; - } - - int total = campfire.getCookTimeTotal(i); - int current = campfire.getCookTime(i); - int remaining = Math.max(0, total - current); - if (remaining <= 0) { - continue; - } - - int step = Math.min(remaining, budgetTicks - usedTicks); - campfire.setCookTime(i, current + step); - usedTicks += step; - } + stand.setBrewingTime(Math.max(0, brewingTime - advanceTicks)); + stand.update(true, true); - if (usedTicks <= 0) { - return TimeSpendResult.none(); - } + double spentSeconds = (advanceTicks / brewTicksPerStoredSecond) * spendMultiplier; + return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerBrewTick); + } - campfire.update(true, true); - double spentSeconds = (usedTicks / campfireTicksPerStoredSecond) * spendMultiplier; - return new TimeSpendResult(spentSeconds, usedTicks, usedTicks * getConfig().xpPerCampfireTick); + private TimeSpendResult accelerateCampfire(Campfire campfire, double storedSeconds, int level) { + double campfireTicksPerStoredSecond = getCampfireTicksPerStoredSecond(level); + double spendMultiplier = getCampfireSpendMultiplier(); + int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * campfireTicksPerStoredSecond); + int budgetTicks = Math.min(affordableTicks, getMaxCampfireTicksPerUse(level)); + if (budgetTicks <= 0) { + return TimeSpendResult.none(); } - private TimeSpendResult accelerateAgeableEntity(org.bukkit.entity.Ageable entity, double storedSeconds, int level) { - int currentAge = entity.getAge(); - if (currentAge == 0) { - return TimeSpendResult.none(); - } + int usedTicks = 0; + for (int i = 0; i < campfire.getSize() && usedTicks < budgetTicks; i++) { + ItemStack item = campfire.getItem(i); + if (item == null || item.getType().isAir()) { + continue; + } - double ageTicksPerStoredSecond = getEntityAgeTicksPerStoredSecond(level); - double spendMultiplier = getEntitySpendMultiplier(); - int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * ageTicksPerStoredSecond); - int advanceTicks = Math.min(Math.abs(currentAge), Math.min(affordableTicks, getMaxEntityAgeTicksPerUse(level))); - if (advanceTicks <= 0) { - return TimeSpendResult.none(); - } + int total = campfire.getCookTimeTotal(i); + int current = campfire.getCookTime(i); + int remaining = Math.max(0, total - current); + if (remaining <= 0) { + continue; + } - if (currentAge < 0) { - entity.setAge(Math.min(0, currentAge + advanceTicks)); - } else { - entity.setAge(Math.max(0, currentAge - advanceTicks)); - } - - double spentSeconds = (advanceTicks / ageTicksPerStoredSecond) * spendMultiplier; - return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerEntityAgeTick); + int step = Math.min(remaining, budgetTicks - usedTicks); + campfire.setCookTime(i, current + step); + usedTicks += step; } - private TimeSpendResult accelerateGrowables(Block clicked, double storedSeconds, int level) { - int attempts = getMaxGrowthStepsPerUse(level); - if (attempts <= 0 || storedSeconds <= 0) { - return TimeSpendResult.none(); - } + if (usedTicks <= 0) { + return TimeSpendResult.none(); + } - double remainingSeconds = storedSeconds; - double spentSeconds = 0; - int successes = 0; - for (int i = 0; i < attempts; i++) { - Block target = clicked.getWorld().getBlockAt(clicked.getLocation()); - double stepCost = getGrowthStepCostSeconds(target, level); - if (remainingSeconds + 1.0E-6 < stepCost) { - break; - } - - if (!applyDirectGrowthStep(target, level)) { - break; - } - - remainingSeconds -= stepCost; - spentSeconds += stepCost; - successes++; - } + campfire.update(true, true); + double spentSeconds = (usedTicks / campfireTicksPerStoredSecond) * spendMultiplier; + return new TimeSpendResult(spentSeconds, usedTicks, usedTicks * getConfig().xpPerCampfireTick); + } - if (successes <= 0 || spentSeconds <= 0) { - return TimeSpendResult.none(); - } + private TimeSpendResult accelerateAgeableEntity(org.bukkit.entity.Ageable entity, double storedSeconds, int level) { + int currentAge = entity.getAge(); + if (currentAge == 0) { + return TimeSpendResult.none(); + } - int effectTicks = Math.max(8, successes * 20); - return new TimeSpendResult(spentSeconds, effectTicks, successes * getConfig().xpPerGrowthStep); + double ageTicksPerStoredSecond = getEntityAgeTicksPerStoredSecond(level); + double spendMultiplier = getEntitySpendMultiplier(); + int affordableTicks = (int) Math.floor((storedSeconds / spendMultiplier) * ageTicksPerStoredSecond); + int advanceTicks = Math.min(Math.abs(currentAge), Math.min(affordableTicks, getMaxEntityAgeTicksPerUse(level))); + if (advanceTicks <= 0) { + return TimeSpendResult.none(); } - private boolean applyDirectGrowthStep(Block block, int level) { - BlockData data = block.getBlockData(); - if (data instanceof Sapling sapling) { - if (sapling.getStage() < sapling.getMaximumStage()) { - sapling.setStage(Math.min(sapling.getMaximumStage(), sapling.getStage() + 1)); - block.setBlockData(data, true); - return true; - } + if (currentAge < 0) { + entity.setAge(Math.min(0, currentAge + advanceTicks)); + } else { + entity.setAge(Math.max(0, currentAge - advanceTicks)); + } - if (!getConfig().allowSaplingTreeGeneration) { - return false; - } + double spentSeconds = (advanceTicks / ageTicksPerStoredSecond) * spendMultiplier; + return new TimeSpendResult(spentSeconds, advanceTicks, advanceTicks * getConfig().xpPerEntityAgeTick); + } - if (ThreadLocalRandom.current().nextDouble() > getSaplingGrowChance(level)) { - return true; - } + private TimeSpendResult accelerateGrowables(Block clicked, double storedSeconds, int level) { + int attempts = getMaxGrowthStepsPerUse(level); + if (attempts <= 0 || storedSeconds <= 0) { + return TimeSpendResult.none(); + } - TreeType treeType = getTreeType(block.getType()); - return treeType != null && block.getWorld().generateTree(block.getLocation(), treeType); - } + double remainingSeconds = storedSeconds; + double spentSeconds = 0; + int successes = 0; + for (int i = 0; i < attempts; i++) { + Block target = clicked.getWorld().getBlockAt(clicked.getLocation()); + double stepCost = getGrowthStepCostSeconds(target, level); + if (remainingSeconds + 1.0E-6 < stepCost) { + break; + } - if (data instanceof Ageable ageable && ageable.getAge() < ageable.getMaximumAge()) { - ageable.setAge(Math.min(ageable.getMaximumAge(), ageable.getAge() + 1)); - block.setBlockData(data, true); - return true; - } + if (!applyDirectGrowthStep(target, level)) { + break; + } - return false; + remainingSeconds -= stepCost; + spentSeconds += stepCost; + successes++; } - private record TimeSpendResult(double spentSeconds, int effectTicks, double xpGain) { - private static TimeSpendResult none() { - return new TimeSpendResult(0, 0, 0); - } - - private boolean applied() { - return spentSeconds > 0; - } + if (successes <= 0 || spentSeconds <= 0) { + return TimeSpendResult.none(); } - private enum GrowthProfile { - CROP, - NETHER_WART, - SAPLING, - STEM, - BERRY_BUSH, - VINE, - CAVE_VINE, - KELP, - DEFAULT - } - - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - int level = getActiveLevel(p); - if (level <= 0) { - continue; - } - - double chargePerSecond = getConfig().chargePerSecond + (level * getConfig().chargePerSecondPerLevel); - - for (ItemStack stack : p.getInventory().getContents()) { - if (!ChronoTimeBottle.isBindableItem(stack)) { - continue; - } - - double stored = ChronoTimeBottle.getStoredSeconds(stack); - double capped = Math.min(getConfig().maxStoredSeconds, stored + chargePerSecond); - if (capped > stored) { - ChronoTimeBottle.setStoredSeconds(stack, capped); - } - } - } - } + int effectTicks = Math.max(8, successes * 20); + return new TimeSpendResult(spentSeconds, effectTicks, successes * getConfig().xpPerGrowthStep); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Carry a temporal bottle that stores time to accelerate timed blocks and baby animals.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") - boolean playClockSounds = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stored Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxStoredSeconds = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double chargePerSecond = 0.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double chargePerSecondPerLevel = 0.02; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cook Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseCookTicksPerStoredSecond = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cook Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cookTicksPerSecondPerLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxCookTicksPerUse = 140; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxCookTicksPerUsePerLevel = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double furnaceSpendMultiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Brewing Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseBrewingTicksPerStoredSecond = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double brewingTicksPerSecondPerLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxBrewingTicksPerUse = 140; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxBrewingTicksPerUsePerLevel = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double brewingSpendMultiplier = 1.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Campfire Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseCampfireTicksPerStoredSecond = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double campfireTicksPerSecondPerLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxCampfireTicksPerUse = 160; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxCampfireTicksPerUsePerLevel = 40; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double campfireSpendMultiplier = 0.9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Entity Age Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseEntityAgeTicksPerStoredSecond = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Age Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double entityAgeTicksPerSecondPerLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxEntityAgeTicksPerUse = 180; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxEntityAgeTicksPerUsePerLevel = 55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double entitySpendMultiplier = 1.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxGrowthStepsPerUse = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxGrowthStepsPerUsePerLevel = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Sapling Tree Generation for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") - boolean allowSaplingTreeGeneration = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Base for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double saplingGrowChanceBase = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double saplingGrowChancePerLevel = 0.04; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthCostMultiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Reduction Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthCostReductionPerLevel = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Cost Level Scale for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minGrowthCostLevelScale = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Step Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minGrowthStepSeconds = 0.06; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int saplingGrowthSteps = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int stemGrowthSteps = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int berryGrowthSteps = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int vineGrowthSteps = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int caveVineGrowthSteps = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int kelpGrowthSteps = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int defaultGrowthSteps = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cropNaturalSeconds = 300; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double netherWartNaturalSeconds = 420; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double saplingNaturalSeconds = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stemNaturalSeconds = 660; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double berryBushNaturalSeconds = 260; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double vineNaturalSeconds = 300; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double caveVineNaturalSeconds = 280; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double kelpNaturalSeconds = 240; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double defaultGrowableNaturalSeconds = 420; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cropCostMultiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double netherWartCostMultiplier = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double saplingCostMultiplier = 2.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stemCostMultiplier = 1.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double berryBushCostMultiplier = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double vineCostMultiplier = 0.85; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double caveVineCostMultiplier = 0.9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double kelpCostMultiplier = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double defaultGrowableCostMultiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Cook Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerCookTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Brew Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBrewTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Campfire Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerCampfireTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Entity Age Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerEntityAgeTick = 0.06; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth Step for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerGrowthStep = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max XPPer Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxXPPerUse = 55; - } + private boolean applyDirectGrowthStep(Block block, int level) { + BlockData data = block.getBlockData(); + if (data instanceof Sapling sapling) { + if (sapling.getStage() < sapling.getMaximumStage()) { + sapling.setStage(Math.min(sapling.getMaximumStage(), sapling.getStage() + 1)); + block.setBlockData(data, true); + return true; + } + + if (!getConfig().allowSaplingTreeGeneration) { + return false; + } + + if (ThreadLocalRandom.current().nextDouble() > getSaplingGrowChance(level)) { + return true; + } + + TreeType treeType = getTreeType(block.getType()); + return treeType != null && block.getWorld().generateTree(block.getLocation(), treeType); + } + + if (data instanceof Ageable ageable && ageable.getAge() < ageable.getMaximumAge()) { + ageable.setAge(Math.min(ageable.getMaximumAge(), ageable.getAge() + 1)); + block.setBlockData(data, true); + return true; + } + + return false; + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } + + double chargePerSecond = getConfig().chargePerSecond + (level * getConfig().chargePerSecondPerLevel); + + for (ItemStack stack : p.getInventory().getContents()) { + if (!ChronoTimeBottle.isBindableItem(stack)) { + continue; + } + + double stored = ChronoTimeBottle.getStoredSeconds(stack); + double capped = Math.min(getConfig().maxStoredSeconds, stored + chargePerSecond); + if (capped > stored) { + ChronoTimeBottle.setStoredSeconds(stack, capped); + } + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private enum GrowthProfile { + CROP, + NETHER_WART, + SAPLING, + STEM, + BERRY_BUSH, + VINE, + CAVE_VINE, + KELP, + DEFAULT + } + + private record TimeSpendResult(double spentSeconds, int effectTicks, + double xpGain) { + private static TimeSpendResult none() { + return new TimeSpendResult(0, 0, 0); + } + + private boolean applied() { + return spentSeconds > 0; + } + } + + @NoArgsConstructor + @ConfigDescription("Carry a temporal bottle that stores time to accelerate timed blocks and baby animals.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stored Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxStoredSeconds = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double chargePerSecond = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Charge Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double chargePerSecondPerLevel = 0.02; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cook Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseCookTicksPerStoredSecond = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cook Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cookTicksPerSecondPerLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxCookTicksPerUse = 140; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Cook Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxCookTicksPerUsePerLevel = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double furnaceSpendMultiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Brewing Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseBrewingTicksPerStoredSecond = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double brewingTicksPerSecondPerLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxBrewingTicksPerUse = 140; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Brewing Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxBrewingTicksPerUsePerLevel = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Brewing Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double brewingSpendMultiplier = 1.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Campfire Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseCampfireTicksPerStoredSecond = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double campfireTicksPerSecondPerLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxCampfireTicksPerUse = 160; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Campfire Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxCampfireTicksPerUsePerLevel = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Campfire Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double campfireSpendMultiplier = 0.9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Entity Age Ticks Per Stored Second for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseEntityAgeTicksPerStoredSecond = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Age Ticks Per Second Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double entityAgeTicksPerSecondPerLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxEntityAgeTicksPerUse = 180; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Entity Age Ticks Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxEntityAgeTicksPerUsePerLevel = 55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entity Spend Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double entitySpendMultiplier = 1.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxGrowthStepsPerUse = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Growth Steps Per Use Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxGrowthStepsPerUsePerLevel = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Sapling Tree Generation for the Chronos Time In ABottle adaptation.", impact = "True enables this behavior and false disables it.") + boolean allowSaplingTreeGeneration = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Base for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double saplingGrowChanceBase = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Grow Chance Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double saplingGrowChancePerLevel = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthCostMultiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Cost Reduction Per Level for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthCostReductionPerLevel = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Cost Level Scale for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minGrowthCostLevelScale = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Growth Step Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minGrowthStepSeconds = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int saplingGrowthSteps = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int stemGrowthSteps = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int berryGrowthSteps = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int vineGrowthSteps = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int caveVineGrowthSteps = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int kelpGrowthSteps = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growth Steps for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int defaultGrowthSteps = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cropNaturalSeconds = 300; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double netherWartNaturalSeconds = 420; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double saplingNaturalSeconds = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stemNaturalSeconds = 660; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double berryBushNaturalSeconds = 260; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double vineNaturalSeconds = 300; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double caveVineNaturalSeconds = 280; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double kelpNaturalSeconds = 240; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Natural Seconds for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double defaultGrowableNaturalSeconds = 420; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cropCostMultiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Wart Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double netherWartCostMultiplier = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sapling Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double saplingCostMultiplier = 2.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stem Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stemCostMultiplier = 1.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Berry Bush Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double berryBushCostMultiplier = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double vineCostMultiplier = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cave Vine Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double caveVineCostMultiplier = 0.9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kelp Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double kelpCostMultiplier = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Default Growable Cost Multiplier for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double defaultGrowableCostMultiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Cook Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerCookTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Brew Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBrewTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Campfire Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerCampfireTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Entity Age Tick for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerEntityAgeTick = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth Step for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerGrowthStep = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max XPPer Use for the Chronos Time In ABottle adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxXPPerUse = 55; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java index 03dd3197a..12474a402 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingBackpacks.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -37,85 +37,85 @@ public class CraftingBackpacks extends SimpleAdaptation { - public CraftingBackpacks() { - super("crafting-backpacks"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.backpacks.description")); - setDisplayName(Localizer.dLocalize("crafting.backpacks.name")); - setIcon(Material.BUNDLE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(17779); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-backpacks") - .ingredient(new MaterialChar('I', Material.LEATHER)) - .ingredient(new MaterialChar('L', Material.LEAD)) - .ingredient(new MaterialChar('C', Material.CHEST)) - .ingredient(new MaterialChar('X', Material.BARREL)) - .shapes(List.of( - "ILI", - "IXI", - "ICI")) - .result(new ItemStack(Material.BUNDLE, 1)) - .build()); - AdvancementSpec backpacksCrafted = AdvancementSpec.challenge( - "challenge_crafting_backpack_25", - Material.BUNDLE, - Localizer.dLocalize("advancement.challenge_crafting_backpack_25.title"), - Localizer.dLocalize("advancement.challenge_crafting_backpack_25.description") - ); - registerMilestone(backpacksCrafted, "crafting.backpacks.bundles-crafted", 25, 300); - } + public CraftingBackpacks() { + super("crafting-backpacks"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.backpacks.description")); + setDisplayName(Localizer.dLocalize("crafting.backpacks.name")); + setIcon(Material.BUNDLE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(17779); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-backpacks") + .ingredient(new MaterialChar('I', Material.LEATHER)) + .ingredient(new MaterialChar('L', Material.LEAD)) + .ingredient(new MaterialChar('C', Material.CHEST)) + .ingredient(new MaterialChar('X', Material.BARREL)) + .shapes(List.of( + "ILI", + "IXI", + "ICI")) + .result(new ItemStack(Material.BUNDLE, 1)) + .build()); + AdvancementSpec backpacksCrafted = AdvancementSpec.challenge( + "challenge_crafting_backpack_25", + Material.BUNDLE, + Localizer.dLocalize("advancement.challenge_crafting_backpack_25.title"), + Localizer.dLocalize("advancement.challenge_crafting_backpack_25.description") + ); + registerMilestone(backpacksCrafted, "crafting.backpacks.bundles-crafted", 25, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore1")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore2")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore3")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore4")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore1")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore2")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore3")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.backpacks.lore4")); - } + } - @EventHandler - public void on(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - if (!hasActiveAdaptation(p)) return; - if (e.getRecipe() != null && e.getRecipe().getResult().getType() == Material.BUNDLE) { - getPlayer(p).getData().addStat("crafting.backpacks.bundles-crafted", 1); - } + @EventHandler + public void on(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); + if (!hasActiveAdaptation(p)) return; + if (e.getRecipe() != null && e.getRecipe().getResult().getType() == Material.BUNDLE) { + getPlayer(p).getData().addStat("crafting.backpacks.bundles-crafted", 1); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft Bundles for portable item storage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Craft Bundles for portable item storage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java index 4c2fa3b95..678ad4259 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingDeconstruction.java @@ -22,9 +22,9 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -40,178 +40,193 @@ import org.bukkit.inventory.meta.Damageable; import org.bukkit.util.RayTraceResult; +import java.util.HashMap; import java.util.Map; -import java.util.Objects; public class CraftingDeconstruction extends SimpleAdaptation { - public CraftingDeconstruction() { - super("crafting-deconstruction"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.deconstruction.description")); - setDisplayName(Localizer.dLocalize("crafting.deconstruction.name")); - setIcon(Material.SHEARS); - setBaseCost(getConfig().baseCost); - setMaxLevel(1); - setInterval(5590); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - AdvancementSpec deconstruction5k = AdvancementSpec.challenge( - "challenge_crafting_decon_5k", - Material.IRON_INGOT, - Localizer.dLocalize("advancement.challenge_crafting_decon_5k.title"), - Localizer.dLocalize("advancement.challenge_crafting_decon_5k.description") - ); - AdvancementSpec deconstruction200 = AdvancementSpec.challenge( - "challenge_crafting_decon_200", - Material.SHEARS, - Localizer.dLocalize("advancement.challenge_crafting_decon_200.title"), - Localizer.dLocalize("advancement.challenge_crafting_decon_200.description") - ).withChild(deconstruction5k); - registerAdvancementSpec(deconstruction200); - registerStatTracker(deconstruction200.statTracker("crafting.deconstruction.items-deconstructed", 200, 300)); - registerStatTracker(deconstruction5k.statTracker("crafting.deconstruction.items-deconstructed", 5000, 1000)); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("crafting.deconstruction.lore1")); - v.addLore(C.GREEN + Localizer.dLocalize("crafting.deconstruction.lore2")); - } - - public ItemStack getDeconstructionOffering(ItemStack forStuff) { - if (forStuff == null) return null; - - int maxPow = 0; - Recipe selectedRecipe = null; - - for (Recipe recipe : Bukkit.getRecipesFor(forStuff)) { - int currentPower = 0; - if (recipe instanceof ShapelessRecipe r) { - currentPower = r.getIngredientList().stream().mapToInt(ItemStack::getAmount).sum(); - } else if (recipe instanceof ShapedRecipe r) { - currentPower = r.getIngredientMap().values().stream().mapToInt(f -> f == null ? 0 : f.getAmount()).sum(); - } - if (currentPower > maxPow) { - selectedRecipe = recipe; - maxPow = currentPower; - } + public CraftingDeconstruction() { + super("crafting-deconstruction"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.deconstruction.description")); + setDisplayName(Localizer.dLocalize("crafting.deconstruction.name")); + setIcon(Material.SHEARS); + setBaseCost(getConfig().baseCost); + setMaxLevel(1); + setInterval(5590); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + AdvancementSpec deconstruction5k = AdvancementSpec.challenge( + "challenge_crafting_decon_5k", + Material.IRON_INGOT, + Localizer.dLocalize("advancement.challenge_crafting_decon_5k.title"), + Localizer.dLocalize("advancement.challenge_crafting_decon_5k.description") + ); + AdvancementSpec deconstruction200 = AdvancementSpec.challenge( + "challenge_crafting_decon_200", + Material.SHEARS, + Localizer.dLocalize("advancement.challenge_crafting_decon_200.title"), + Localizer.dLocalize("advancement.challenge_crafting_decon_200.description") + ).withChild(deconstruction5k); + registerAdvancementSpec(deconstruction200); + registerStatTracker(deconstruction200.statTracker("crafting.deconstruction.items-deconstructed", 200, 300)); + registerStatTracker(deconstruction5k.statTracker("crafting.deconstruction.items-deconstructed", 5000, 1000)); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("crafting.deconstruction.lore1")); + v.addLore(C.GREEN + Localizer.dLocalize("crafting.deconstruction.lore2")); + } + + public ItemStack getDeconstructionOffering(ItemStack forStuff) { + if (forStuff == null) return null; + + int maxPow = 0; + Recipe selectedRecipe = null; + + for (Recipe recipe : Bukkit.getRecipesFor(forStuff)) { + int currentPower = 0; + if (recipe instanceof ShapelessRecipe r) { + for (ItemStack ingredient : r.getIngredientList()) { + if (ingredient == null) { + continue; + } + currentPower += ingredient.getAmount(); } - - if (selectedRecipe == null) return null; - - int v = 0; - int outa = 1; - ItemStack sel = null; - - if (selectedRecipe instanceof ShapelessRecipe r) { - for (ItemStack i : r.getIngredientList()) { - int amount = i.getAmount() * forStuff.getAmount(); - if (amount > v) { - v = amount; - sel = i; - outa = r.getResult().getAmount(); - } - } - } else { - ShapedRecipe r = (ShapedRecipe) selectedRecipe; - Map ings = new java.util.concurrent.ConcurrentHashMap<>(); - r.getIngredientMap().values().stream().filter(Objects::nonNull).forEach(i -> ings.merge(i.getType(), i.getAmount(), Integer::sum)); - - for (Map.Entry entry : ings.entrySet()) { - int amount = entry.getValue() * forStuff.getAmount(); - if (amount > v) { - v = amount; - sel = new ItemStack(entry.getKey(), entry.getValue()); - outa = r.getResult().getAmount(); - } - } + } else if (recipe instanceof ShapedRecipe r) { + for (ItemStack ingredient : r.getIngredientMap().values()) { + if (ingredient == null) { + continue; + } + currentPower += ingredient.getAmount(); } - - if (sel != null && sel.getAmount() * forStuff.getAmount() > 1) { - int a = ((sel.getAmount() * forStuff.getAmount()) / outa) / 2; - if (a <= sel.getMaxStackSize() && getValue(sel) < getValue(forStuff)) { - sel.setAmount(a); - return sel.clone(); - } - } - - return null; + } + if (currentPower > maxPow) { + selectedRecipe = recipe; + maxPow = currentPower; + } } + if (selectedRecipe == null) return null; - @EventHandler - public void on(PlayerInteractEvent e) { - Player player = e.getPlayer(); - ItemStack mainHandItem = player.getInventory().getItemInMainHand(); - if (!hasActiveAdaptation(player)) { - return; - } + int v = 0; + int outa = 1; + ItemStack sel = null; - if (!player.isSneaking() || mainHandItem.getType() != Material.SHEARS) { - return; + if (selectedRecipe instanceof ShapelessRecipe r) { + for (ItemStack i : r.getIngredientList()) { + int amount = i.getAmount() * forStuff.getAmount(); + if (amount > v) { + v = amount; + sel = i; + outa = r.getResult().getAmount(); } - - // Perform a ray trace for 6 blocks looking for an item - RayTraceResult rayTrace = player.getWorld().rayTraceEntities(player.getEyeLocation(), player.getLocation().getDirection(), 6, entity -> entity instanceof Item); - if (rayTrace != null && rayTrace.getHitEntity() instanceof Item itemEntity) { - processItemInteraction(player, mainHandItem, itemEntity); + } + } else { + ShapedRecipe r = (ShapedRecipe) selectedRecipe; + Map ings = new HashMap<>(); + for (ItemStack ingredient : r.getIngredientMap().values()) { + if (ingredient == null) { + continue; } + ings.merge(ingredient.getType(), ingredient.getAmount(), Integer::sum); + } + + for (Map.Entry entry : ings.entrySet()) { + int amount = entry.getValue() * forStuff.getAmount(); + if (amount > v) { + v = amount; + sel = new ItemStack(entry.getKey(), entry.getValue()); + outa = r.getResult().getAmount(); + } + } } - private void processItemInteraction(Player player, ItemStack mainHandItem, Item itemEntity) { - ItemStack forStuff = itemEntity.getItemStack(); - ItemStack offering = getDeconstructionOffering(forStuff); - - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - if (offering != null) { - itemEntity.setItemStack(offering); - spw.play(itemEntity.getLocation(), Sound.BLOCK_BASALT_BREAK, 1F, 0.2f); - spw.play(itemEntity.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 1F, 0.7f); - xp(player, getValue(offering), "deconstruct"); - getPlayer(player).getData().addStat("crafting.deconstruction.items-deconstructed", 1); - - // Damage the shears - Damageable damageable = (Damageable) mainHandItem.getItemMeta(); - int newDamage = damageable.getDamage() + 8 * forStuff.getAmount(); - if (newDamage >= mainHandItem.getType().getMaxDurability()) { - player.getInventory().setItemInMainHand(null); // Break the shears - } else { - damageable.setDamage(newDamage); - mainHandItem.setItemMeta(damageable); - } - } else { - spw.play(itemEntity.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 1F, 1f); // Burnt torch sound - } + if (sel != null && sel.getAmount() * forStuff.getAmount() > 1) { + int a = ((sel.getAmount() * forStuff.getAmount()) / outa) / 2; + if (a <= sel.getMaxStackSize() && getValue(sel) < getValue(forStuff)) { + sel.setAmount(a); + return sel.clone(); + } } + return null; + } - @Override - public void onTick() { + @EventHandler + public void on(PlayerInteractEvent e) { + Player player = e.getPlayer(); + ItemStack mainHandItem = player.getInventory().getItemInMainHand(); + if (!hasActiveAdaptation(player)) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (!player.isSneaking() || mainHandItem.getType() != Material.SHEARS) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + // Perform a ray trace for 6 blocks looking for an item + RayTraceResult rayTrace = player.getWorld().rayTraceEntities(player.getEyeLocation(), player.getLocation().getDirection(), 6, entity -> entity instanceof Item); + if (rayTrace != null && rayTrace.getHitEntity() instanceof Item itemEntity) { + processItemInteraction(player, mainHandItem, itemEntity); } - - @NoArgsConstructor - @ConfigDescription("Deconstruct blocks and items into salvageable base components using shears.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1.0; + } + + private void processItemInteraction(Player player, ItemStack mainHandItem, Item itemEntity) { + ItemStack forStuff = itemEntity.getItemStack(); + ItemStack offering = getDeconstructionOffering(forStuff); + + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + if (offering != null) { + itemEntity.setItemStack(offering); + spw.play(itemEntity.getLocation(), Sound.BLOCK_BASALT_BREAK, 1F, 0.2f); + spw.play(itemEntity.getLocation(), Sound.BLOCK_BEEHIVE_SHEAR, 1F, 0.7f); + xp(player, getValue(offering), "deconstruct"); + getPlayer(player).getData().addStat("crafting.deconstruction.items-deconstructed", 1); + + // Damage the shears + Damageable damageable = (Damageable) mainHandItem.getItemMeta(); + int newDamage = damageable.getDamage() + 8 * forStuff.getAmount(); + if (newDamage >= mainHandItem.getType().getMaxDurability()) { + player.getInventory().setItemInMainHand(null); // Break the shears + } else { + damageable.setDamage(newDamage); + mainHandItem.setItemMeta(damageable); + } + } else { + spw.play(itemEntity.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 1F, 1f); // Burnt torch sound } + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Deconstruct blocks and items into salvageable base components using shears.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java index 39d7abcda..32545afb6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingLeather.java @@ -23,8 +23,8 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -33,78 +33,78 @@ public class CraftingLeather extends SimpleAdaptation { - public CraftingLeather() { - super("crafting-leather"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.leather.description")); - setDisplayName(Localizer.dLocalize("crafting.leather.name")); - setIcon(Material.LEATHER); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(17776); - registerRecipe(AdaptRecipe.campfire() - .key("crafting-leather") - .ingredient(Material.ROTTEN_FLESH) - .cookTime(100) - .experience(1) - .result(new ItemStack(Material.LEATHER, 1)) - .build()); - AdvancementSpec leatherCrafted = AdvancementSpec.challenge( - "challenge_crafting_leather_100", - Material.LEATHER, - Localizer.dLocalize("advancement.challenge_crafting_leather_100.title"), - Localizer.dLocalize("advancement.challenge_crafting_leather_100.description") - ); - registerMilestone(leatherCrafted, "crafting.leather.leather-crafted", 100, 300); - } + public CraftingLeather() { + super("crafting-leather"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.leather.description")); + setDisplayName(Localizer.dLocalize("crafting.leather.name")); + setIcon(Material.LEATHER); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(17776); + registerRecipe(AdaptRecipe.campfire() + .key("crafting-leather") + .ingredient(Material.ROTTEN_FLESH) + .cookTime(100) + .experience(1) + .result(new ItemStack(Material.LEATHER, 1)) + .build()); + AdvancementSpec leatherCrafted = AdvancementSpec.challenge( + "challenge_crafting_leather_100", + Material.LEATHER, + Localizer.dLocalize("advancement.challenge_crafting_leather_100.title"), + Localizer.dLocalize("advancement.challenge_crafting_leather_100.description") + ); + registerMilestone(leatherCrafted, "crafting.leather.leather-crafted", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.leather.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.leather.lore1")); + } - @EventHandler - public void on(PlayerInteractEvent e) { - if (e.getItem() != null && e.getItem().getType() == Material.ROTTEN_FLESH && e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.CAMPFIRE) { - if (getActiveLevel(e.getPlayer()) <= 0) { - e.setCancelled(true); - } else { - getPlayer(e.getPlayer()).getData().addStat("crafting.leather.leather-crafted", 1); - } - } + @EventHandler + public void on(PlayerInteractEvent e) { + if (e.getItem() != null && e.getItem().getType() == Material.ROTTEN_FLESH && e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.CAMPFIRE) { + if (getActiveLevel(e.getPlayer()) <= 0) { + e.setCancelled(true); + } else { + getPlayer(e.getPlayer()).getData().addStat("crafting.leather.leather-crafted", 1); + } } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft Leather from Rotten Flesh on a campfire.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Craft Leather from Rotten Flesh on a campfire.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java index 156cc69cc..45dfc32fc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingReconstruction.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -37,330 +37,330 @@ public class CraftingReconstruction extends SimpleAdaptation { - public CraftingReconstruction() { - super("crafting-reconstruction"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.reconstruction.description")); - setDisplayName(Localizer.dLocalize("crafting.reconstruction.name")); - setIcon(Material.COAL_ORE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(80248); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-iron-ore") - .ingredient(Material.STONE) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .result(new ItemStack(Material.IRON_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-gold-ore") - .ingredient(Material.STONE) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .result(new ItemStack(Material.GOLD_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-copper-ore") - .ingredient(Material.STONE) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .result(new ItemStack(Material.COPPER_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-lapis-ore") - .ingredient(Material.STONE) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .result(new ItemStack(Material.LAPIS_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-redstone-ore") - .ingredient(Material.STONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .result(new ItemStack(Material.REDSTONE_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-emerald-ore") - .ingredient(Material.STONE) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .result(new ItemStack(Material.EMERALD_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-diamond-ore") - .ingredient(Material.STONE) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .result(new ItemStack(Material.DIAMOND_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-coal-ore") - .ingredient(Material.STONE) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .result(new ItemStack(Material.COAL_ORE)) - .build()); + public CraftingReconstruction() { + super("crafting-reconstruction"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.reconstruction.description")); + setDisplayName(Localizer.dLocalize("crafting.reconstruction.name")); + setIcon(Material.COAL_ORE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(80248); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-iron-ore") + .ingredient(Material.STONE) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .result(new ItemStack(Material.IRON_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-gold-ore") + .ingredient(Material.STONE) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .result(new ItemStack(Material.GOLD_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-copper-ore") + .ingredient(Material.STONE) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .result(new ItemStack(Material.COPPER_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-lapis-ore") + .ingredient(Material.STONE) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .result(new ItemStack(Material.LAPIS_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-redstone-ore") + .ingredient(Material.STONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .result(new ItemStack(Material.REDSTONE_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-emerald-ore") + .ingredient(Material.STONE) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .result(new ItemStack(Material.EMERALD_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-diamond-ore") + .ingredient(Material.STONE) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .result(new ItemStack(Material.DIAMOND_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-coal-ore") + .ingredient(Material.STONE) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .result(new ItemStack(Material.COAL_ORE)) + .build()); - // Use Deepslate - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-iron-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .ingredient(Material.IRON_INGOT) - .result(new ItemStack(Material.DEEPSLATE_IRON_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-gold-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .result(new ItemStack(Material.DEEPSLATE_GOLD_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-copper-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .ingredient(Material.COPPER_INGOT) - .result(new ItemStack(Material.DEEPSLATE_COPPER_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-lapis-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .ingredient(Material.LAPIS_LAZULI) - .result(new ItemStack(Material.DEEPSLATE_LAPIS_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-redstone-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .ingredient(Material.REDSTONE) - .result(new ItemStack(Material.DEEPSLATE_REDSTONE_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-emerald-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .ingredient(Material.EMERALD) - .result(new ItemStack(Material.DEEPSLATE_EMERALD_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-diamond-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .ingredient(Material.DIAMOND) - .result(new ItemStack(Material.DEEPSLATE_DIAMOND_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-deepslate-coal-ore") - .ingredient(Material.DEEPSLATE) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .ingredient(Material.COAL) - .result(new ItemStack(Material.DEEPSLATE_COAL_ORE)) - .build()); + // Use Deepslate + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-iron-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .ingredient(Material.IRON_INGOT) + .result(new ItemStack(Material.DEEPSLATE_IRON_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-gold-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .result(new ItemStack(Material.DEEPSLATE_GOLD_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-copper-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .ingredient(Material.COPPER_INGOT) + .result(new ItemStack(Material.DEEPSLATE_COPPER_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-lapis-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .ingredient(Material.LAPIS_LAZULI) + .result(new ItemStack(Material.DEEPSLATE_LAPIS_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-redstone-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .ingredient(Material.REDSTONE) + .result(new ItemStack(Material.DEEPSLATE_REDSTONE_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-emerald-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .ingredient(Material.EMERALD) + .result(new ItemStack(Material.DEEPSLATE_EMERALD_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-diamond-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .ingredient(Material.DIAMOND) + .result(new ItemStack(Material.DEEPSLATE_DIAMOND_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-deepslate-coal-ore") + .ingredient(Material.DEEPSLATE) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .ingredient(Material.COAL) + .result(new ItemStack(Material.DEEPSLATE_COAL_ORE)) + .build()); // Use Nether Bricks - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-nether-gold-ore") - .ingredient(Material.NETHER_BRICKS) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .ingredient(Material.GOLD_INGOT) - .result(new ItemStack(Material.NETHER_GOLD_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-nether-quartz-ore") - .ingredient(Material.NETHER_BRICKS) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .ingredient(Material.QUARTZ) - .result(new ItemStack(Material.NETHER_QUARTZ_ORE)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("reconstruction-ancient-debris") - .ingredient(Material.NETHER_BRICKS) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .ingredient(Material.NETHERITE_SCRAP) - .result(new ItemStack(Material.ANCIENT_DEBRIS)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RAW_IRON) - .key("challenge_crafting_recon_100") - .title(Localizer.dLocalize("advancement.challenge_crafting_recon_100.title")) - .description(Localizer.dLocalize("advancement.challenge_crafting_recon_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_crafting_recon_100", "crafting.reconstruction.ores-reconstructed", 100, 300); - } + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-nether-gold-ore") + .ingredient(Material.NETHER_BRICKS) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .ingredient(Material.GOLD_INGOT) + .result(new ItemStack(Material.NETHER_GOLD_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-nether-quartz-ore") + .ingredient(Material.NETHER_BRICKS) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .ingredient(Material.QUARTZ) + .result(new ItemStack(Material.NETHER_QUARTZ_ORE)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("reconstruction-ancient-debris") + .ingredient(Material.NETHER_BRICKS) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .ingredient(Material.NETHERITE_SCRAP) + .result(new ItemStack(Material.ANCIENT_DEBRIS)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RAW_IRON) + .key("challenge_crafting_recon_100") + .title(Localizer.dLocalize("advancement.challenge_crafting_recon_100.title")) + .description(Localizer.dLocalize("advancement.challenge_crafting_recon_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_crafting_recon_100", "crafting.reconstruction.ores-reconstructed", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("crafting.reconstruction.lore1")); - v.addLore(C.UNDERLINE + Localizer.dLocalize("crafting.reconstruction.lore2")); - v.addLore(C.YELLOW + Localizer.dLocalize("crafting.reconstruction.lore3")); - v.addLore(C.YELLOW + Localizer.dLocalize("crafting.reconstruction.lore4")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("crafting.reconstruction.lore1")); + v.addLore(C.UNDERLINE + Localizer.dLocalize("crafting.reconstruction.lore2")); + v.addLore(C.YELLOW + Localizer.dLocalize("crafting.reconstruction.lore3")); + v.addLore(C.YELLOW + Localizer.dLocalize("crafting.reconstruction.lore4")); + } - @EventHandler - public void on(PlayerInteractEvent e) { + @EventHandler + public void on(PlayerInteractEvent e) { - } + } - @EventHandler - public void on(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - if (!hasActiveAdaptation(p)) return; - if (e.getRecipe() != null && (e.getRecipe().getResult().getType().name().contains("ORE") || e.getRecipe().getResult().getType() == Material.ANCIENT_DEBRIS)) { - getPlayer(p).getData().addStat("crafting.reconstruction.ores-reconstructed", 1); - } + @EventHandler + public void on(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); + if (!hasActiveAdaptation(p)) return; + if (e.getRecipe() != null && (e.getRecipe().getResult().getType().name().contains("ORE") || e.getRecipe().getResult().getType() == Material.ANCIENT_DEBRIS)) { + getPlayer(p).getData().addStat("crafting.reconstruction.ores-reconstructed", 1); } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Recraft ores from their base smelted components.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Recraft ores from their base smelted components.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java index ed79a1b08..4a771a059 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingSkulls.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -37,137 +37,137 @@ public class CraftingSkulls extends SimpleAdaptation { - public CraftingSkulls() { - super("crafting-skulls"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.skulls.description")); - setDisplayName(Localizer.dLocalize("crafting.skulls.name")); - setIcon(Material.SKELETON_SKULL); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(17776); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-skeletonskull") - .ingredient(new MaterialChar('I', Material.BONE)) - .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) - .shapes(List.of( - "III", - "IXI", - "III")) - .result(new ItemStack(Material.SKELETON_SKULL, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-witherskeletonskull") - .ingredient(new MaterialChar('I', Material.NETHER_BRICK)) - .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) - .shapes(List.of( - "III", - "IXI", - "III")) - .result(new ItemStack(Material.WITHER_SKELETON_SKULL, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-zombieskull") - .ingredient(new MaterialChar('I', Material.ROTTEN_FLESH)) - .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) - .shapes(List.of( - "III", - "IXI", - "III")) - .result(new ItemStack(Material.ZOMBIE_HEAD, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-creeperhead") - .ingredient(new MaterialChar('I', Material.GUNPOWDER)) - .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) - .shapes(List.of( - "III", - "IXI", - "III")) - .result(new ItemStack(Material.CREEPER_HEAD, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("crafting-dragonhead") - .ingredient(new MaterialChar('I', Material.DRAGON_BREATH)) - .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) - .shapes(List.of( - "III", - "IXI", - "III")) - .result(new ItemStack(Material.DRAGON_HEAD, 1)) - .build()); - AdvancementSpec skulls100 = AdvancementSpec.challenge( - "challenge_crafting_skulls_100", - Material.WITHER_SKELETON_SKULL, - Localizer.dLocalize("advancement.challenge_crafting_skulls_100.title"), - Localizer.dLocalize("advancement.challenge_crafting_skulls_100.description") - ); - AdvancementSpec skulls10 = AdvancementSpec.challenge( - "challenge_crafting_skulls_10", - Material.SKELETON_SKULL, - Localizer.dLocalize("advancement.challenge_crafting_skulls_10.title"), - Localizer.dLocalize("advancement.challenge_crafting_skulls_10.description") - ).withChild(skulls100); - registerAdvancementSpec(skulls10); - registerStatTracker(skulls10.statTracker("crafting.skulls.skulls-crafted", 10, 300)); - registerStatTracker(skulls100.statTracker("crafting.skulls.skulls-crafted", 100, 1000)); - } + public CraftingSkulls() { + super("crafting-skulls"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.skulls.description")); + setDisplayName(Localizer.dLocalize("crafting.skulls.name")); + setIcon(Material.SKELETON_SKULL); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(17776); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-skeletonskull") + .ingredient(new MaterialChar('I', Material.BONE)) + .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) + .shapes(List.of( + "III", + "IXI", + "III")) + .result(new ItemStack(Material.SKELETON_SKULL, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-witherskeletonskull") + .ingredient(new MaterialChar('I', Material.NETHER_BRICK)) + .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) + .shapes(List.of( + "III", + "IXI", + "III")) + .result(new ItemStack(Material.WITHER_SKELETON_SKULL, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-zombieskull") + .ingredient(new MaterialChar('I', Material.ROTTEN_FLESH)) + .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) + .shapes(List.of( + "III", + "IXI", + "III")) + .result(new ItemStack(Material.ZOMBIE_HEAD, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-creeperhead") + .ingredient(new MaterialChar('I', Material.GUNPOWDER)) + .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) + .shapes(List.of( + "III", + "IXI", + "III")) + .result(new ItemStack(Material.CREEPER_HEAD, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("crafting-dragonhead") + .ingredient(new MaterialChar('I', Material.DRAGON_BREATH)) + .ingredient(new MaterialChar('X', Material.BONE_BLOCK)) + .shapes(List.of( + "III", + "IXI", + "III")) + .result(new ItemStack(Material.DRAGON_HEAD, 1)) + .build()); + AdvancementSpec skulls100 = AdvancementSpec.challenge( + "challenge_crafting_skulls_100", + Material.WITHER_SKELETON_SKULL, + Localizer.dLocalize("advancement.challenge_crafting_skulls_100.title"), + Localizer.dLocalize("advancement.challenge_crafting_skulls_100.description") + ); + AdvancementSpec skulls10 = AdvancementSpec.challenge( + "challenge_crafting_skulls_10", + Material.SKELETON_SKULL, + Localizer.dLocalize("advancement.challenge_crafting_skulls_10.title"), + Localizer.dLocalize("advancement.challenge_crafting_skulls_10.description") + ).withChild(skulls100); + registerAdvancementSpec(skulls10); + registerStatTracker(skulls10.statTracker("crafting.skulls.skulls-crafted", 10, 300)); + registerStatTracker(skulls100.statTracker("crafting.skulls.skulls-crafted", 100, 1000)); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore1")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore2")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore3")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore4")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore5")); - v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore6")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore1")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore2")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore3")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore4")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore5")); + v.addLore(C.YELLOW + "- " + C.GRAY + Localizer.dLocalize("crafting.skulls.lore6")); + } - @EventHandler - public void on(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - if (!hasActiveAdaptation(p)) return; - if (e.getRecipe() != null) { - Material result = e.getRecipe().getResult().getType(); - if (result == Material.SKELETON_SKULL || result == Material.WITHER_SKELETON_SKULL - || result == Material.ZOMBIE_HEAD || result == Material.CREEPER_HEAD - || result == Material.DRAGON_HEAD) { - getPlayer(p).getData().addStat("crafting.skulls.skulls-crafted", 1); - } - } + @EventHandler + public void on(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); + if (!hasActiveAdaptation(p)) return; + if (e.getRecipe() != null) { + Material result = e.getRecipe().getResult().getType(); + if (result == Material.SKELETON_SKULL || result == Material.WITHER_SKELETON_SKULL + || result == Material.ZOMBIE_HEAD || result == Material.CREEPER_HEAD + || result == Material.DRAGON_HEAD) { + getPlayer(p).getData().addStat("crafting.skulls.skulls-crafted", 1); + } } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Craft Mob Skulls using materials surrounding a Bone Block.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Craft Mob Skulls using materials surrounding a Bone Block.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java index 2565eb5be..67e89b720 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java @@ -22,9 +22,9 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -39,140 +39,140 @@ public class CraftingStations extends SimpleAdaptation { - public CraftingStations() { - super("crafting-stations"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("crafting.stations.description")); - setDisplayName(Localizer.dLocalize("crafting.stations.name")); - setIcon(Material.CRAFTING_TABLE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(9248); - AdvancementSpec stations5k = AdvancementSpec.challenge( - "challenge_crafting_stations_5k", - Material.SMITHING_TABLE, - Localizer.dLocalize("advancement.challenge_crafting_stations_5k.title"), - Localizer.dLocalize("advancement.challenge_crafting_stations_5k.description") - ); - AdvancementSpec stations200 = AdvancementSpec.challenge( - "challenge_crafting_stations_200", - Material.CRAFTING_TABLE, - Localizer.dLocalize("advancement.challenge_crafting_stations_200.title"), - Localizer.dLocalize("advancement.challenge_crafting_stations_200.description") - ).withChild(stations5k); - registerAdvancementSpec(stations200); - registerStatTracker(stations200.statTracker("crafting.stations.portable-opens", 200, 300)); - registerStatTracker(stations5k.statTracker("crafting.stations.portable-opens", 5000, 1000)); - } + public CraftingStations() { + super("crafting-stations"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("crafting.stations.description")); + setDisplayName(Localizer.dLocalize("crafting.stations.name")); + setIcon(Material.CRAFTING_TABLE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(9248); + AdvancementSpec stations5k = AdvancementSpec.challenge( + "challenge_crafting_stations_5k", + Material.SMITHING_TABLE, + Localizer.dLocalize("advancement.challenge_crafting_stations_5k.title"), + Localizer.dLocalize("advancement.challenge_crafting_stations_5k.description") + ); + AdvancementSpec stations200 = AdvancementSpec.challenge( + "challenge_crafting_stations_200", + Material.CRAFTING_TABLE, + Localizer.dLocalize("advancement.challenge_crafting_stations_200.title"), + Localizer.dLocalize("advancement.challenge_crafting_stations_200.description") + ).withChild(stations5k); + registerAdvancementSpec(stations200); + registerStatTracker(stations200.statTracker("crafting.stations.portable-opens", 200, 300)); + registerStatTracker(stations5k.statTracker("crafting.stations.portable-opens", 5000, 1000)); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.RED + Localizer.dLocalize("crafting.stations.lore2")); - v.addLore(C.GRAY + Localizer.dLocalize("crafting.stations.lore3")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.RED + Localizer.dLocalize("crafting.stations.lore2")); + v.addLore(C.GRAY + Localizer.dLocalize("crafting.stations.lore3")); + } - @EventHandler - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } - ItemStack hand = p.getInventory().getItemInMainHand(); + ItemStack hand = p.getInventory().getItemInMainHand(); - if (p.hasCooldown(hand.getType())) { - e.setCancelled(true); - return; - } + if (p.hasCooldown(hand.getType())) { + e.setCancelled(true); + return; + } - if ((e.getAction().equals(Action.RIGHT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK))) { + if ((e.getAction().equals(Action.RIGHT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK))) { - SoundPlayer sp = SoundPlayer.of(p); - switch (hand.getType()) { - case CRAFTING_TABLE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - p.openWorkbench(null, true); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case GRINDSTONE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.GRINDSTONE); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case ANVIL -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.ANVIL); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case STONECUTTER -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.STONECUTTER); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case CARTOGRAPHY_TABLE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.CARTOGRAPHY); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case LOOM -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.LOOM); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - } + SoundPlayer sp = SoundPlayer.of(p); + switch (hand.getType()) { + case CRAFTING_TABLE -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + p.openWorkbench(null, true); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); + } + case GRINDSTONE -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + Inventory inv = Bukkit.createInventory(p, InventoryType.GRINDSTONE); + p.openInventory(inv); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); } + case ANVIL -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + Inventory inv = Bukkit.createInventory(p, InventoryType.ANVIL); + p.openInventory(inv); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); + } + case STONECUTTER -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + Inventory inv = Bukkit.createInventory(p, InventoryType.STONECUTTER); + p.openInventory(inv); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); + } + case CARTOGRAPHY_TABLE -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + Inventory inv = Bukkit.createInventory(p, InventoryType.CARTOGRAPHY); + p.openInventory(inv); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); + } + case LOOM -> { + p.setCooldown(hand.getType(), 1000); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + Inventory inv = Bukkit.createInventory(p, InventoryType.LOOM); + p.openInventory(inv); + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); + } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Use crafting tables, anvils, and other stations in the palm of your hand.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Crafting Stations adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int cooldown = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Use crafting tables, anvils, and other stations in the palm of your hand.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Crafting Stations adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int cooldown = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java index dcb761fc5..d75bf5af2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingXP.java @@ -22,8 +22,8 @@ import art.arcane.adapt.api.advancement.AdvancementSpec; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -37,98 +37,98 @@ public class CraftingXP extends SimpleAdaptation { - private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); - - - public CraftingXP() { - super("crafting-xp"); - registerConfiguration(CraftingXP.Config.class); - setDisplayName(Localizer.dLocalize("crafting.xp.name")); - setDescription(Localizer.dLocalize("crafting.xp.description")); - setIcon(Material.ENCHANTED_BOOK); - setInterval(5580); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - AdvancementSpec xp25k = AdvancementSpec.challenge( - "challenge_crafting_xp_25k", - Material.EXPERIENCE_BOTTLE, - Localizer.dLocalize("advancement.challenge_crafting_xp_25k.title"), - Localizer.dLocalize("advancement.challenge_crafting_xp_25k.description") - ); - AdvancementSpec xp1k = AdvancementSpec.challenge( - "challenge_crafting_xp_1k", - Material.CRAFTING_TABLE, - Localizer.dLocalize("advancement.challenge_crafting_xp_1k.title"), - Localizer.dLocalize("advancement.challenge_crafting_xp_1k.description") - ).withChild(xp25k); - registerAdvancementSpec(xp1k); - registerStatTracker(xp1k.statTracker("crafting.xp.items-crafted", 1000, 300)); - registerStatTracker(xp25k.statTracker("crafting.xp.items-crafted", 25000, 1500)); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("crafting.xp.lore1")); - } - - @EventHandler - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - cooldown.remove(p.getUniqueId()); - } - - - @EventHandler(priority = EventPriority.LOW) - public void on(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - if (e.getInventory().getResult() != null && hasActiveAdaptation(p) && e.getInventory().getResult().getAmount() > 0) { - if (e.getInventory().getResult() != null && e.getCursor() != null && e.getCursor().getAmount() < 64) { - if (p.getInventory().addItem(e.getCurrentItem()).isEmpty()) { - p.getInventory().removeItem(e.getCurrentItem()); - if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { - cooldown.remove(p.getUniqueId()); - } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { - return; - } - cooldown.put(p.getUniqueId(), System.currentTimeMillis()); - p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(getLevel(p) * 2); - getPlayer(p).getData().addStat("crafting.xp.items-crafted", 1); - } - } + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); + + + public CraftingXP() { + super("crafting-xp"); + registerConfiguration(CraftingXP.Config.class); + setDisplayName(Localizer.dLocalize("crafting.xp.name")); + setDescription(Localizer.dLocalize("crafting.xp.description")); + setIcon(Material.ENCHANTED_BOOK); + setInterval(5580); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + AdvancementSpec xp25k = AdvancementSpec.challenge( + "challenge_crafting_xp_25k", + Material.EXPERIENCE_BOTTLE, + Localizer.dLocalize("advancement.challenge_crafting_xp_25k.title"), + Localizer.dLocalize("advancement.challenge_crafting_xp_25k.description") + ); + AdvancementSpec xp1k = AdvancementSpec.challenge( + "challenge_crafting_xp_1k", + Material.CRAFTING_TABLE, + Localizer.dLocalize("advancement.challenge_crafting_xp_1k.title"), + Localizer.dLocalize("advancement.challenge_crafting_xp_1k.description") + ).withChild(xp25k); + registerAdvancementSpec(xp1k); + registerStatTracker(xp1k.statTracker("crafting.xp.items-crafted", 1000, 300)); + registerStatTracker(xp25k.statTracker("crafting.xp.items-crafted", 25000, 1500)); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("crafting.xp.lore1")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + cooldown.remove(p.getUniqueId()); + } + + + @EventHandler(priority = EventPriority.LOW) + public void on(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); + if (e.getInventory().getResult() != null && hasActiveAdaptation(p) && e.getInventory().getResult().getAmount() > 0) { + if (e.getInventory().getResult() != null && e.getCursor() != null && e.getCursor().getAmount() < 64) { + if (p.getInventory().addItem(e.getCurrentItem()).isEmpty()) { + p.getInventory().removeItem(e.getCurrentItem()); + if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { + cooldown.remove(p.getUniqueId()); + } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { + return; + } + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); + p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(getLevel(p) * 2); + getPlayer(p).getData().addStat("crafting.xp.items-crafted", 1); } + } } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - - @Override - public void onTick() { - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Gain passive XP when crafting items.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain passive XP when crafting items.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java index 7b04f3ebf..deab0ebc2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArchaeologist.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -50,379 +50,380 @@ import java.util.concurrent.atomic.AtomicBoolean; public class DiscoveryArchaeologist extends SimpleAdaptation { - private static final String BLOCK_BRUSH_EVENT_CLASS = "org.bukkit.event.block.BlockBrushEvent"; - private static final long BRUSH_FALLBACK_WINDOW_MILLIS = 25000L; - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map pendingBrushes = new java.util.concurrent.ConcurrentHashMap<>(); - private final AtomicBoolean brushEventFailureWarned = new AtomicBoolean(false); - private final BrushEventBridge brushEventBridge; - - public DiscoveryArchaeologist() { - super("discovery-archaeologist"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.archaeologist.description")); - setDisplayName(Localizer.dLocalize("discovery.archaeologist.name")); - setIcon(Material.BRUSH); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2300); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BRUSH) - .key("challenge_discovery_archaeologist_50") - .title(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_50.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DECORATED_POT) - .key("challenge_discovery_archaeologist_500") - .title(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_500.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_archaeologist_50", "discovery.archaeologist.bonus-finds", 50, 300); - registerMilestone("challenge_discovery_archaeologist_500", "discovery.archaeologist.bonus-finds", 500, 1000); - brushEventBridge = BrushEventBridge.create(); - registerBrushEventBridge(brushEventBridge); + private static final String BLOCK_BRUSH_EVENT_CLASS = "org.bukkit.event.block.BlockBrushEvent"; + private static final long BRUSH_FALLBACK_WINDOW_MILLIS = 25000L; + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map pendingBrushes = new java.util.concurrent.ConcurrentHashMap<>(); + private final AtomicBoolean brushEventFailureWarned = new AtomicBoolean(false); + private final BrushEventBridge brushEventBridge; + + public DiscoveryArchaeologist() { + super("discovery-archaeologist"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.archaeologist.description")); + setDisplayName(Localizer.dLocalize("discovery.archaeologist.name")); + setIcon(Material.BRUSH); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2300); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BRUSH) + .key("challenge_discovery_archaeologist_50") + .title(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_50.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DECORATED_POT) + .key("challenge_discovery_archaeologist_500") + .title(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_500.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_archaeologist_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_archaeologist_50", "discovery.archaeologist.bonus-finds", 50, 300); + registerMilestone("challenge_discovery_archaeologist_500", "discovery.archaeologist.bonus-finds", 500, 1000); + brushEventBridge = BrushEventBridge.create(); + registerBrushEventBridge(brushEventBridge); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getBonusRollChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getRareRewardChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + cooldowns.remove(id); + pendingBrushes.remove(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getBonusRollChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getRareRewardChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("discovery.archaeologist.lore3")); + ItemStack hand = e.getItem(); + if (hand == null || hand.getType() != Material.BRUSH) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - cooldowns.remove(id); - pendingBrushes.remove(id); + Block block = e.getClickedBlock(); + if (block == null || !isSuspiciousBlock(block.getType())) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK) { - return; - } - - ItemStack hand = e.getItem(); - if (hand == null || hand.getType() != Material.BRUSH) { - return; - } - - Block block = e.getClickedBlock(); - if (block == null || !isSuspiciousBlock(block.getType())) { - return; - } - - Player p = e.getPlayer(); - if (getActiveBlockBreakLevel(p, block.getLocation()) <= 0) { - return; - } - - pendingBrushes.put(p.getUniqueId(), PendingBrush.from(block, System.currentTimeMillis() + BRUSH_FALLBACK_WINDOW_MILLIS)); + Player p = e.getPlayer(); + if (getActiveBlockBreakLevel(p, block.getLocation()) <= 0) { + return; } - private void registerBrushEventBridge(BrushEventBridge bridge) { - if (bridge == null) { - Adapt.verbose("BlockBrushEvent not available; discovery-archaeologist will use interaction fallback tracking."); - return; - } + pendingBrushes.put(p.getUniqueId(), PendingBrush.from(block, System.currentTimeMillis() + BRUSH_FALLBACK_WINDOW_MILLIS)); + } - EventExecutor executor = (listener, event) -> onBrush(event, bridge); - Bukkit.getPluginManager().registerEvent(bridge.eventClass, this, EventPriority.HIGHEST, executor, Adapt.instance, true); + private void registerBrushEventBridge(BrushEventBridge bridge) { + if (bridge == null) { + Adapt.verbose("BlockBrushEvent not available; discovery-archaeologist will use interaction fallback tracking."); + return; } - private void onBrush(Event event, BrushEventBridge bridge) { - try { - Player p = bridge.player(event); - Block block = bridge.block(event); - Material originalType = block == null ? null : block.getType(); - if (p != null) { - PendingBrush pending = pendingBrushes.get(p.getUniqueId()); - if (pending != null && isSuspiciousBlock(pending.originalType)) { - originalType = pending.originalType; - } - } - - Material newStateType = bridge.newStateType(event); - handleBrush(p, block, originalType, newStateType); - if (p != null && newStateType != null && !isSuspiciousBlock(newStateType)) { - pendingBrushes.remove(p.getUniqueId()); - } - } catch (Throwable t) { - if (brushEventFailureWarned.compareAndSet(false, true)) { - Adapt.warn("DiscoveryArchaeologist brush event bridge failed once (" + t.getClass().getSimpleName() + ": " + t.getMessage() + ")."); - } + EventExecutor executor = (listener, event) -> onBrush(event, bridge); + Bukkit.getPluginManager().registerEvent(bridge.eventClass, this, EventPriority.HIGHEST, executor, Adapt.instance, true); + } + + private void onBrush(Event event, BrushEventBridge bridge) { + try { + Player p = bridge.player(event); + Block block = bridge.block(event); + Material originalType = block == null ? null : block.getType(); + if (p != null) { + PendingBrush pending = pendingBrushes.get(p.getUniqueId()); + if (pending != null && isSuspiciousBlock(pending.originalType)) { + originalType = pending.originalType; } + } + + Material newStateType = bridge.newStateType(event); + handleBrush(p, block, originalType, newStateType); + if (p != null && newStateType != null && !isSuspiciousBlock(newStateType)) { + pendingBrushes.remove(p.getUniqueId()); + } + } catch (Throwable t) { + if (brushEventFailureWarned.compareAndSet(false, true)) { + Adapt.warn("DiscoveryArchaeologist brush event bridge failed once (" + t.getClass().getSimpleName() + ": " + t.getMessage() + ")."); + } } + } - private void handleBrush(Player p, Block block, Material originalType, Material newStateType) { - if (p == null || block == null) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (!isSuspiciousBlock(originalType)) { - return; - } - - // Only award when brushing actually completes and the suspicious block resolves. - if (newStateType == null || isSuspiciousBlock(newStateType)) { - return; - } - - if (!canBlockBreak(p, block.getLocation())) { - return; - } - - long now = System.currentTimeMillis(); - long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); - if (now < nextReady) { - return; - } - - cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); - if (ThreadLocalRandom.current().nextDouble() > getBonusRollChance(level)) { - return; - } - - ItemStack reward = rollReward(level); - Map overflow = p.getInventory().addItem(reward); - overflow.values().forEach(item -> p.getWorld().dropItemNaturally(p.getLocation(), item)); - - if (areParticlesEnabled()) { - block.getWorld().spawnParticle(Particle.ENCHANT, block.getLocation().add(0.5, 0.65, 0.5), 18, 0.25, 0.2, 0.25, 0.2); - } - if (areParticlesEnabled()) { - block.getWorld().spawnParticle(Particle.CRIT, block.getLocation().add(0.5, 0.65, 0.5), 12, 0.22, 0.22, 0.22, 0.02); - } - SoundPlayer sp = SoundPlayer.of(block.getWorld()); - sp.play(block.getLocation().add(0.5, 0.5, 0.5), Sound.ITEM_BRUSH_BRUSHING_SAND_COMPLETE, 1f, 1.15f); - sp.play(block.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.7f, 1.55f); - xp(p, getConfig().xpPerReward + (getValue(reward.getType()) * getConfig().rewardValueXpMultiplier)); - getPlayer(p).getData().addStat("discovery.archaeologist.bonus-finds", 1); + private void handleBrush(Player p, Block block, Material originalType, Material newStateType) { + if (p == null || block == null) { + return; } - private static final class BrushEventBridge { - private final Class eventClass; - private final Method getPlayer; - private final Method getBlock; - private final Method getNewState; - private final Method getBlockStateType; - - private BrushEventBridge(Class eventClass, Method getPlayer, Method getBlock, Method getNewState, Method getBlockStateType) { - this.eventClass = eventClass; - this.getPlayer = getPlayer; - this.getBlock = getBlock; - this.getNewState = getNewState; - this.getBlockStateType = getBlockStateType; - } + int level = getActiveLevel(p); + if (level <= 0) { + return; + } - private static BrushEventBridge create() { - try { - Class eventType = Class.forName(BLOCK_BRUSH_EVENT_CLASS); - if (!Event.class.isAssignableFrom(eventType)) { - return null; - } - - Method getPlayer = eventType.getMethod("getPlayer"); - Method getBlock = eventType.getMethod("getBlock"); - Method getNewState = eventType.getMethod("getNewState"); - Class blockStateType = Class.forName("org.bukkit.block.BlockState"); - Method getBlockStateType = blockStateType.getMethod("getType"); - - @SuppressWarnings("unchecked") - Class typedEvent = (Class) eventType; - return new BrushEventBridge(typedEvent, getPlayer, getBlock, getNewState, getBlockStateType); - } catch (Throwable ignored) { - return null; - } - } + if (!isSuspiciousBlock(originalType)) { + return; + } - private Player player(Event event) throws ReflectiveOperationException { - Object value = getPlayer.invoke(event); - return value instanceof Player p ? p : null; - } + // Only award when brushing actually completes and the suspicious block resolves. + if (newStateType == null || isSuspiciousBlock(newStateType)) { + return; + } - private Block block(Event event) throws ReflectiveOperationException { - Object value = getBlock.invoke(event); - return value instanceof Block b ? b : null; - } + if (!canBlockBreak(p, block.getLocation())) { + return; + } - private Material newStateType(Event event) throws ReflectiveOperationException { - Object newState = getNewState.invoke(event); - if (newState == null) { - return null; - } + long now = System.currentTimeMillis(); + long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < nextReady) { + return; + } - Object material = getBlockStateType.invoke(newState); - return material instanceof Material m ? m : null; - } + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + if (ThreadLocalRandom.current().nextDouble() > getBonusRollChance(level)) { + return; } - private ItemStack rollReward(int level) { - ThreadLocalRandom random = ThreadLocalRandom.current(); - if (random.nextDouble() <= getRareRewardChance(level)) { - return switch (random.nextInt(4)) { - case 0 -> new ItemStack(Material.DIAMOND, 1); - case 1 -> new ItemStack(Material.EMERALD, 1); - case 2 -> new ItemStack(Material.GOLD_INGOT, 1 + random.nextInt(2)); - default -> new ItemStack(Material.AMETHYST_SHARD, 2 + random.nextInt(3)); - }; - } + ItemStack reward = rollReward(level); + Map overflow = p.getInventory().addItem(reward); + overflow.values().forEach(item -> p.getWorld().dropItemNaturally(p.getLocation(), item)); - return switch (random.nextInt(6)) { - case 0 -> new ItemStack(Material.BRICK, 1 + random.nextInt(2)); - case 1 -> new ItemStack(Material.CLAY_BALL, 2 + random.nextInt(3)); - case 2 -> new ItemStack(Material.BONE, 1 + random.nextInt(2)); - case 3 -> new ItemStack(Material.FLINT, 1 + random.nextInt(2)); - case 4 -> new ItemStack(Material.STRING, 1 + random.nextInt(2)); - default -> new ItemStack(Material.COAL, 1 + random.nextInt(2)); - }; + if (areParticlesEnabled()) { + block.getWorld().spawnParticle(Particle.ENCHANT, block.getLocation().add(0.5, 0.65, 0.5), 18, 0.25, 0.2, 0.25, 0.2); } - - private boolean isSuspiciousBlock(Material type) { - return type == Material.SUSPICIOUS_SAND || type == Material.SUSPICIOUS_GRAVEL; + if (areParticlesEnabled()) { + block.getWorld().spawnParticle(Particle.CRIT, block.getLocation().add(0.5, 0.65, 0.5), 12, 0.22, 0.22, 0.22, 0.02); } - - private double getBonusRollChance(int level) { - return Math.min(getConfig().maxBonusRollChance, - getConfig().bonusRollChanceBase + (getLevelPercent(level) * getConfig().bonusRollChanceFactor)); + SoundPlayer sp = SoundPlayer.of(block.getWorld()); + sp.play(block.getLocation().add(0.5, 0.5, 0.5), Sound.ITEM_BRUSH_BRUSHING_SAND_COMPLETE, 1f, 1.15f); + sp.play(block.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.7f, 1.55f); + xp(p, getConfig().xpPerReward + (getValue(reward.getType()) * getConfig().rewardValueXpMultiplier)); + getPlayer(p).getData().addStat("discovery.archaeologist.bonus-finds", 1); + } + + private ItemStack rollReward(int level) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (random.nextDouble() <= getRareRewardChance(level)) { + return switch (random.nextInt(4)) { + case 0 -> new ItemStack(Material.DIAMOND, 1); + case 1 -> new ItemStack(Material.EMERALD, 1); + case 2 -> new ItemStack(Material.GOLD_INGOT, 1 + random.nextInt(2)); + default -> + new ItemStack(Material.AMETHYST_SHARD, 2 + random.nextInt(3)); + }; } - private double getRareRewardChance(int level) { - return Math.min(getConfig().maxRareRewardChance, - getConfig().rareRewardChanceBase + (getLevelPercent(level) * getConfig().rareRewardChanceFactor)); + return switch (random.nextInt(6)) { + case 0 -> new ItemStack(Material.BRICK, 1 + random.nextInt(2)); + case 1 -> new ItemStack(Material.CLAY_BALL, 2 + random.nextInt(3)); + case 2 -> new ItemStack(Material.BONE, 1 + random.nextInt(2)); + case 3 -> new ItemStack(Material.FLINT, 1 + random.nextInt(2)); + case 4 -> new ItemStack(Material.STRING, 1 + random.nextInt(2)); + default -> new ItemStack(Material.COAL, 1 + random.nextInt(2)); + }; + } + + private boolean isSuspiciousBlock(Material type) { + return type == Material.SUSPICIOUS_SAND || type == Material.SUSPICIOUS_GRAVEL; + } + + private double getBonusRollChance(int level) { + return Math.min(getConfig().maxBonusRollChance, + getConfig().bonusRollChanceBase + (getLevelPercent(level) * getConfig().bonusRollChanceFactor)); + } + + private double getRareRewardChance(int level) { + return Math.min(getConfig().maxRareRewardChance, + getConfig().rareRewardChanceBase + (getLevelPercent(level) * getConfig().rareRewardChanceFactor)); + } + + private long getCooldownMillis(int level) { + return Math.max(250L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + if (pendingBrushes.isEmpty()) { + return; } - private long getCooldownMillis(int level) { - return Math.max(250L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + long now = System.currentTimeMillis(); + Iterator> iterator = pendingBrushes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + PendingBrush pending = entry.getValue(); + if (pending.expiresAt <= now) { + iterator.remove(); + continue; + } + + Player p = Bukkit.getPlayer(entry.getKey()); + if (p == null || !p.isOnline()) { + iterator.remove(); + continue; + } + + if (!hasActiveAdaptation(p)) { + iterator.remove(); + continue; + } + + Block current = pending.resolveBlock(); + if (current == null) { + iterator.remove(); + continue; + } + + Material currentType = current.getType(); + if (isSuspiciousBlock(currentType)) { + continue; + } + + handleBrush(p, current, pending.originalType, currentType); + iterator.remove(); + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private static final class BrushEventBridge { + private final Class eventClass; + private final Method getPlayer; + private final Method getBlock; + private final Method getNewState; + private final Method getBlockStateType; + + private BrushEventBridge(Class eventClass, Method getPlayer, Method getBlock, Method getNewState, Method getBlockStateType) { + this.eventClass = eventClass; + this.getPlayer = getPlayer; + this.getBlock = getBlock; + this.getNewState = getNewState; + this.getBlockStateType = getBlockStateType; } - @Override - public void onTick() { - if (pendingBrushes.isEmpty()) { - return; + private static BrushEventBridge create() { + try { + Class eventType = Class.forName(BLOCK_BRUSH_EVENT_CLASS); + if (!Event.class.isAssignableFrom(eventType)) { + return null; } - long now = System.currentTimeMillis(); - Iterator> iterator = pendingBrushes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - PendingBrush pending = entry.getValue(); - if (pending.expiresAt <= now) { - iterator.remove(); - continue; - } - - Player p = Bukkit.getPlayer(entry.getKey()); - if (p == null || !p.isOnline()) { - iterator.remove(); - continue; - } - - if (!hasActiveAdaptation(p)) { - iterator.remove(); - continue; - } - - Block current = pending.resolveBlock(); - if (current == null) { - iterator.remove(); - continue; - } - - Material currentType = current.getType(); - if (isSuspiciousBlock(currentType)) { - continue; - } - - handleBrush(p, current, pending.originalType, currentType); - iterator.remove(); - } + Method getPlayer = eventType.getMethod("getPlayer"); + Method getBlock = eventType.getMethod("getBlock"); + Method getNewState = eventType.getMethod("getNewState"); + Class blockStateType = Class.forName("org.bukkit.block.BlockState"); + Method getBlockStateType = blockStateType.getMethod("getType"); + + @SuppressWarnings("unchecked") + Class typedEvent = (Class) eventType; + return new BrushEventBridge(typedEvent, getPlayer, getBlock, getNewState, getBlockStateType); + } catch (Throwable ignored) { + return null; + } } - private static final class PendingBrush { - private final UUID worldId; - private final int x; - private final int y; - private final int z; - private final Material originalType; - private final long expiresAt; - - private PendingBrush(UUID worldId, int x, int y, int z, Material originalType, long expiresAt) { - this.worldId = worldId; - this.x = x; - this.y = y; - this.z = z; - this.originalType = originalType; - this.expiresAt = expiresAt; - } - - private static PendingBrush from(Block block, long expiresAt) { - return new PendingBrush(block.getWorld().getUID(), block.getX(), block.getY(), block.getZ(), block.getType(), expiresAt); - } + private Player player(Event event) throws ReflectiveOperationException { + Object value = getPlayer.invoke(event); + return value instanceof Player p ? p : null; + } - private Block resolveBlock() { - World world = Bukkit.getWorld(worldId); - return world == null ? null : world.getBlockAt(x, y, z); - } + private Block block(Event event) throws ReflectiveOperationException { + Object value = getBlock.invoke(event); + return value instanceof Block b ? b : null; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private Material newStateType(Event event) throws ReflectiveOperationException { + Object newState = getNewState.invoke(event); + if (newState == null) { + return null; + } + + Object material = getBlockStateType.invoke(newState); + return material instanceof Material m ? m : null; + } + } + + private static final class PendingBrush { + private final UUID worldId; + private final int x; + private final int y; + private final int z; + private final Material originalType; + private final long expiresAt; + + private PendingBrush(UUID worldId, int x, int y, int z, Material originalType, long expiresAt) { + this.worldId = worldId; + this.x = x; + this.y = y; + this.z = z; + this.originalType = originalType; + this.expiresAt = expiresAt; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + private static PendingBrush from(Block block, long expiresAt) { + return new PendingBrush(block.getWorld().getUID(), block.getX(), block.getY(), block.getZ(), block.getType(), expiresAt); } - @NoArgsConstructor - @ConfigDescription("Brushing suspicious blocks has a chance to grant bonus archaeology loot.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusRollChanceBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusRollChanceFactor = 0.43; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Bonus Roll Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBonusRollChance = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rareRewardChanceBase = 0.04; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rareRewardChanceFactor = 0.24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Reward Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRareRewardChance = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 1600; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reward for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerReward = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reward Value Xp Multiplier for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rewardValueXpMultiplier = 0.45; + private Block resolveBlock() { + World world = Bukkit.getWorld(worldId); + return world == null ? null : world.getBlockAt(x, y, z); } + } + + @NoArgsConstructor + @ConfigDescription("Brushing suspicious blocks has a chance to grant bonus archaeology loot.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusRollChanceBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Roll Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusRollChanceFactor = 0.43; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Bonus Roll Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBonusRollChance = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rareRewardChanceBase = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Reward Chance Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rareRewardChanceFactor = 0.24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Reward Chance for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRareRewardChance = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 1600; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Reward for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerReward = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reward Value Xp Multiplier for the Discovery Archaeologist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rewardValueXpMultiplier = 0.45; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java index 0ca29a616..964cc1f4e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryArmor.java @@ -26,13 +26,13 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.Sphere; import art.arcane.adapt.util.common.math.VectorMath; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Location; @@ -49,166 +49,168 @@ import java.util.concurrent.TimeUnit; public class DiscoveryArmor extends SimpleAdaptation { - private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-discovery-armor".getBytes()); - private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:discovery-armor"); - private static final long UPDATE_COOLDOWN = TimeUnit.SECONDS.toMillis(3); - private static final Sphere SPHERE = new Sphere(5); - - private final KMap playerData = new KMap<>(); - - public DiscoveryArmor() { - super("discovery-world-armor"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.armor.description")); - setDisplayName(Localizer.dLocalize("discovery.armor.name")); - setIcon(Material.TURTLE_HELMET); - setInterval(305); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_discovery_armor_1hr") - .title(Localizer.dLocalize("advancement.challenge_discovery_armor_1hr.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_armor_1hr.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_CHESTPLATE) - .key("challenge_discovery_armor_24hr") - .title(Localizer.dLocalize("advancement.challenge_discovery_armor_24hr.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_armor_24hr.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_armor_1hr", "discovery.armor.ticks-with-bonus", 72000, 400); - registerMilestone("challenge_discovery_armor_24hr", "discovery.armor.ticks-with-bonus", 1728000, 2000); + private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-discovery-armor".getBytes()); + private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString("adapt:discovery-armor"); + private static final long UPDATE_COOLDOWN = TimeUnit.SECONDS.toMillis(3); + private static final Sphere SPHERE = new Sphere(5); + + private final KMap playerData = new KMap<>(); + + public DiscoveryArmor() { + super("discovery-world-armor"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.armor.description")); + setDisplayName(Localizer.dLocalize("discovery.armor.name")); + setIcon(Material.TURTLE_HELMET); + setInterval(305); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_discovery_armor_1hr") + .title(Localizer.dLocalize("advancement.challenge_discovery_armor_1hr.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_armor_1hr.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_CHESTPLATE) + .key("challenge_discovery_armor_24hr") + .title(Localizer.dLocalize("advancement.challenge_discovery_armor_24hr.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_armor_24hr.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_armor_1hr", "discovery.armor.ticks-with-bonus", 72000, 400); + registerMilestone("challenge_discovery_armor_24hr", "discovery.armor.ticks-with-bonus", 1728000, 2000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("discovery.armor.lore1") + C.GRAY + ", " + Localizer.dLocalize("discovery.armor.lore2")); + v.addLore(C.YELLOW + "~ " + Localizer.dLocalize("discovery.armor.lore3") + C.BLUE + " +" + level * 0.25); + } + + public double getArmorPoints(Material m) { + return Math.log(Math.min(2000, m.getBlastResistance() * m.getBlastResistance())) + Math.log((m.getHardness() < 0 ? 50 : Math.min(50, m.getHardness() + 25)) * 0.33); + } + + public double getArmor(Location l, int level) { + Block center = l.getBlock(); + double armorValue = 0.0; + double count = 0; + + art.arcane.adapt.util.common.math.Sphere sphere = SPHERE.clone(); + + while (sphere.hasNext()) { + art.arcane.volmlib.util.math.BlockPosition r = sphere.next(); + Block b = center.getRelative(r.getX(), r.getY(), r.getZ()); + if (b.isEmpty() || b.isLiquid()) + continue; + + count++; + double a = getArmorPoints(b.getType()); + if (Double.isNaN(a) || a < 0) { + a = 0; + } + armorValue += a; + + if (a > 2 && M.r(0.005 * a)) { + Vector v = VectorMath.directionNoNormal(l, b.getLocation().add(0.5, 0.5, 0.5)); + if (areParticlesEnabled()) { + l.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, l.clone().add(0, 1, 0), 0, v.getX(), v.getY(), v.getZ()); + } + } } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("discovery.armor.lore1") + C.GRAY + ", " + Localizer.dLocalize("discovery.armor.lore2")); - v.addLore(C.YELLOW + "~ " + Localizer.dLocalize("discovery.armor.lore3") + C.BLUE + " +" + level * 0.25); - } + return Math.min((armorValue / count) * (level / 2D) * 0.65, 10); + } - public double getArmorPoints(Material m) { - return Math.log(Math.min(2000, m.getBlastResistance() * m.getBlastResistance())) + Math.log((m.getHardness() < 0 ? 50 : Math.min(50, m.getHardness() + 25)) * 0.33); - } - public double getArmor(Location l, int level) { - Block center = l.getBlock(); - double armorValue = 0.0; - double count = 0; - - var sphere = SPHERE.clone(); - - while (sphere.hasNext()) { - var r = sphere.next(); - Block b = center.getRelative(r.getX(), r.getY(), r.getZ()); - if (b.isEmpty() || b.isLiquid()) - continue; - - count++; - double a = getArmorPoints(b.getType()); - if (Double.isNaN(a) || a < 0) { - a = 0; - } - armorValue += a; - - if (a > 2 && M.r(0.005 * a)) { - Vector v = VectorMath.directionNoNormal(l, b.getLocation().add(0.5, 0.5, 0.5)); - if (areParticlesEnabled()) { - l.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, l.clone().add(0, 1, 0), 0, v.getX(), v.getY(), v.getZ()); - } - } - } + private double getRadius(double factor) { + return factor * getConfig().radiusFactor; + } - return Math.min((armorValue / count) * (level / 2D) * 0.65, 10); - } + private double getStrength(double factor) { + return Math.pow(factor, getConfig().strengthExponent); + } - private double getRadius(double factor) { - return factor * getConfig().radiusFactor; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) continue; - private double getStrength(double factor) { - return Math.pow(factor, getConfig().strengthExponent); - } + long now = M.ms(); + Long nextUpdate = playerData.getOrDefault(p.getUniqueId(), now); + if (nextUpdate > now) continue; + playerData.put(p.getUniqueId(), now + UPDATE_COOLDOWN); + IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + if (attribute == null) continue; - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) continue; - - long now = M.ms(); - var nextUpdate = playerData.getOrDefault(p.getUniqueId(), now); - if (nextUpdate > now) continue; - playerData.put(p.getUniqueId(), now + UPDATE_COOLDOWN); - - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); - if (attribute == null) continue; - - if (!hasActiveAdaptation(p)) { - attribute.removeModifier(MODIFIER, MODIFIER_KEY); - } else { - double oldArmor = attribute.getModifier(MODIFIER, MODIFIER_KEY) - .stream() - .mapToDouble(IAttribute.Modifier::getAmount) - .max() - .orElse(0); - - double armor = getArmor(p.getLocation(), getLevel(p)); - armor = Double.isNaN(armor) ? 0 : armor; - - double lArmor = M.lerp(oldArmor, armor, 0.3); - lArmor = Double.isNaN(lArmor) ? 0 : lArmor; - attribute.setModifier(MODIFIER, MODIFIER_KEY, lArmor, AttributeModifier.Operation.ADD_NUMBER); - if (lArmor > 0) { - adaptPlayer.getData().addStat("discovery.armor.ticks-with-bonus", 1); - } - } + if (!hasActiveAdaptation(p)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } else { + double oldArmor = 0; + for (IAttribute.Modifier modifier : attribute.getModifier(MODIFIER, MODIFIER_KEY)) { + double amount = modifier.getAmount(); + if (!Double.isNaN(amount) && amount > oldArmor) { + oldArmor = amount; + } } - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - playerData.remove(event.getPlayer().getUniqueId()); - } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + double armor = getArmor(p.getLocation(), getLevel(p)); + armor = Double.isNaN(armor) ? 0 : armor; - @NoArgsConstructor - @ConfigDescription("Gain passive armor based on nearby block hardness.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int radiusFactor = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Exponent for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double strengthExponent = 1.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery Armor adaptation.", impact = "True enables this behavior and false disables it.") - public boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; + double lArmor = M.lerp(oldArmor, armor, 0.3); + lArmor = Double.isNaN(lArmor) ? 0 : lArmor; + attribute.setModifier(MODIFIER, MODIFIER_KEY, lArmor, AttributeModifier.Operation.ADD_NUMBER); + if (lArmor > 0) { + adaptPlayer.getData().addStat("discovery.armor.ticks-with-bonus", 1); + } + } } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + playerData.remove(event.getPlayer().getUniqueId()); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain passive armor based on nearby block hardness.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int radiusFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Exponent for the Discovery Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double strengthExponent = 1.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery Armor adaptation.", impact = "True enables this behavior and false disables it.") + public boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java index 29d25d7a9..a0205d875 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryBetterMending.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,176 +42,176 @@ import org.bukkit.inventory.meta.Damageable; public class DiscoveryBetterMending extends SimpleAdaptation { - public DiscoveryBetterMending() { - super("discovery-better-mending"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.better_mending.description")); - setDisplayName(Localizer.dLocalize("discovery.better_mending.name")); - setIcon(Material.PHANTOM_MEMBRANE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ANVIL) - .key("challenge_discovery_mending_10k") - .title(Localizer.dLocalize("advancement.challenge_discovery_mending_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_mending_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTED_GOLDEN_APPLE) - .key("challenge_discovery_mending_100k") - .title(Localizer.dLocalize("advancement.challenge_discovery_mending_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_mending_100k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_mending_10k", "discovery.better-mending.durability-restored", 10000, 400); - registerMilestone("challenge_discovery_mending_100k", "discovery.better-mending.durability-restored", 100000, 1500); + public DiscoveryBetterMending() { + super("discovery-better-mending"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.better_mending.description")); + setDisplayName(Localizer.dLocalize("discovery.better_mending.name")); + setIcon(Material.PHANTOM_MEMBRANE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ANVIL) + .key("challenge_discovery_mending_10k") + .title(Localizer.dLocalize("advancement.challenge_discovery_mending_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_mending_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTED_GOLDEN_APPLE) + .key("challenge_discovery_mending_100k") + .title(Localizer.dLocalize("advancement.challenge_discovery_mending_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_mending_100k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_mending_10k", "discovery.better-mending.durability-restored", 10000, 400); + registerMilestone("challenge_discovery_mending_100k", "discovery.better-mending.durability-restored", 100000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRepairPerXp(level)) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore1")); + v.addLore(C.GREEN + "+ " + getMaxXpSpend(level) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRepairPerXp(level)) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore1")); - v.addLore(C.GREEN + "+ " + getMaxXpSpend(level) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("discovery.better_mending.lore3")); + Action action = e.getAction(); + if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - Action action = e.getAction(); - if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!canMend(hand) || p.hasCooldown(hand.getType())) { - return; - } - - Damageable damageable = (Damageable) hand.getItemMeta(); - if (damageable == null || damageable.getDamage() <= 0) { - return; - } - - int availableXp = getTotalExp(p); - if (availableXp <= 0) { - return; - } - - double repairPerXp = getRepairPerXp(level); - int maxXpSpend = Math.min(getMaxXpSpend(level), availableXp); - int currentDamage = damageable.getDamage(); - int xpNeeded = (int) Math.ceil(currentDamage / repairPerXp); - int xpSpent = Math.min(maxXpSpend, xpNeeded); - if (xpSpent <= 0) { - return; - } - - int repaired = Math.max(1, (int) Math.round(xpSpent * repairPerXp)); - int newDamage = Math.max(0, currentDamage - repaired); - - takeExp(p, xpSpent, true); - damageable.setDamage(newDamage); - hand.setItemMeta(damageable); - p.getInventory().setItemInMainHand(hand); - p.setCooldown(hand.getType(), getCooldownTicks(level)); - e.setCancelled(true); - - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.45f); - if (newDamage <= 0) { - sp.play(p.getLocation(), Sound.BLOCK_ANVIL_USE, 0.5f, 1.65f); - } - - xp(p, Math.max(1D, (currentDamage - newDamage) * getConfig().skillXpPerDurability)); - getPlayer(p).getData().addStat("discovery.better-mending.durability-restored", repaired); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + return; } - private boolean canMend(ItemStack hand) { - if (!isItem(hand) || hand.getType().getMaxDurability() <= 0) { - return false; - } - - if (!hand.containsEnchantment(Enchantment.MENDING)) { - return false; - } - - if (!(hand.getItemMeta() instanceof Damageable damageable)) { - return false; - } - - return damageable.getDamage() > 0; + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!canMend(hand) || p.hasCooldown(hand.getType())) { + return; } - private double getRepairPerXp(int level) { - return Math.max(0.1, getConfig().repairPerXpBase + (getLevelPercent(level) * getConfig().repairPerXpFactor)); + Damageable damageable = (Damageable) hand.getItemMeta(); + if (damageable == null || damageable.getDamage() <= 0) { + return; } - private int getMaxXpSpend(int level) { - return Math.max(1, (int) Math.round(getConfig().maxXpSpendBase + (getLevelPercent(level) * getConfig().maxXpSpendFactor))); + int availableXp = getTotalExp(p); + if (availableXp <= 0) { + return; } - private int getCooldownTicks(int level) { - return Math.max(6, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksReduction))); + double repairPerXp = getRepairPerXp(level); + int maxXpSpend = Math.min(getMaxXpSpend(level), availableXp); + int currentDamage = damageable.getDamage(); + int xpNeeded = (int) Math.ceil(currentDamage / repairPerXp); + int xpSpent = Math.min(maxXpSpend, xpNeeded); + if (xpSpent <= 0) { + return; } - @Override - public void onTick() { + int repaired = Math.max(1, (int) Math.round(xpSpent * repairPerXp)); + int newDamage = Math.max(0, currentDamage - repaired); + + takeExp(p, xpSpent, true); + damageable.setDamage(newDamage); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + p.setCooldown(hand.getType(), getCooldownTicks(level)); + e.setCancelled(true); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.45f); + if (newDamage <= 0) { + sp.play(p.getLocation(), Sound.BLOCK_ANVIL_USE, 0.5f, 1.65f); } - @Override - public boolean isEnabled() { - return getConfig().enabled; + xp(p, Math.max(1D, (currentDamage - newDamage) * getConfig().skillXpPerDurability)); + getPlayer(p).getData().addStat("discovery.better-mending.durability-restored", repaired); + } + + private boolean canMend(ItemStack hand) { + if (!isItem(hand) || hand.getType().getMaxDurability() <= 0) { + return false; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (!hand.containsEnchantment(Enchantment.MENDING)) { + return false; } - @NoArgsConstructor - @ConfigDescription("Sneak-left-click to spend XP and directly mend the Mending item in your hand.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double repairPerXpBase = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double repairPerXpFactor = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxXpSpendBase = 14.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxXpSpendFactor = 130.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 38.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksReduction = 26.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp Per Durability for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double skillXpPerDurability = 0.35; + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return false; } + + return damageable.getDamage() > 0; + } + + private double getRepairPerXp(int level) { + return Math.max(0.1, getConfig().repairPerXpBase + (getLevelPercent(level) * getConfig().repairPerXpFactor)); + } + + private int getMaxXpSpend(int level) { + return Math.max(1, (int) Math.round(getConfig().maxXpSpendBase + (getLevelPercent(level) * getConfig().maxXpSpendFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(6, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksReduction))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-left-click to spend XP and directly mend the Mending item in your hand.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double repairPerXpBase = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Repair Per Xp Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double repairPerXpFactor = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxXpSpendBase = 14.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Xp Spend Factor for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxXpSpendFactor = 130.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 38.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksReduction = 26.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp Per Durability for the Discovery Better Mending adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double skillXpPerDurability = 0.35; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java index e8fe8441b..b9bb22310 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -47,227 +47,227 @@ import java.util.UUID; public class DiscoveryCartographerPulse extends SimpleAdaptation { - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - - public DiscoveryCartographerPulse() { - super("discovery-cartographer-pulse"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.cartographer_pulse.description")); - setDisplayName(Localizer.dLocalize("discovery.cartographer_pulse.name")); - setIcon(Material.COMPASS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COMPASS) - .key("challenge_discovery_cartographer_100") - .title(Localizer.dLocalize("advancement.challenge_discovery_cartographer_100.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_cartographer_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FILLED_MAP) - .key("challenge_discovery_cartographer_1k") - .title(Localizer.dLocalize("advancement.challenge_discovery_cartographer_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_cartographer_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_cartographer_100", "discovery.cartographer-pulse.pulses", 100, 300); - registerMilestone("challenge_discovery_cartographer_1k", "discovery.cartographer-pulse.pulses", 1000, 1000); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + + public DiscoveryCartographerPulse() { + super("discovery-cartographer-pulse"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.cartographer_pulse.description")); + setDisplayName(Localizer.dLocalize("discovery.cartographer_pulse.name")); + setIcon(Material.COMPASS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COMPASS) + .key("challenge_discovery_cartographer_100") + .title(Localizer.dLocalize("advancement.challenge_discovery_cartographer_100.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_cartographer_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FILLED_MAP) + .key("challenge_discovery_cartographer_1k") + .title(Localizer.dLocalize("advancement.challenge_discovery_cartographer_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_cartographer_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_cartographer_100", "discovery.cartographer-pulse.pulses", 100, 300); + registerMilestone("challenge_discovery_cartographer_1k", "discovery.cartographer-pulse.pulses", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getSearchRange(level)) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getSearchRange(level)) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore2")); + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.COMPASS) { - return; - } - - long now = System.currentTimeMillis(); - if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { - return; - } - - Location target = locateNearestStructureFallback(p.getWorld(), p.getLocation(), getSearchRange(level)); - if (target == null) { - target = p.getWorld().getSpawnLocation(); - } - - p.setCompassTarget(target); - p.sendMessage(C.AQUA + "Compass pulse: " + C.WHITE + Form.f(target.getBlockX()) + ", " + Form.f(target.getBlockZ())); - cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_LODESTONE_COMPASS_LOCK, 0.8f, 1.3f); - xp(p, getConfig().xpPerPulse); - getPlayer(p).getData().addStat("discovery.cartographer-pulse.pulses", 1); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.COMPASS) { + return; } - private Location locateNearestStructureFallback(World world, Location from, int range) { - try { - for (Method m : world.getClass().getMethods()) { - if (!m.getName().equals("locateNearestStructure")) { - continue; - } - - Class[] p = m.getParameterTypes(); - if (p.length < 4 || p[0] != Location.class || p[2] != int.class || p[3] != boolean.class) { - continue; - } - - Object structure = resolvePreferredStructureType(p[1]); - if (structure == null) { - continue; - } - - Object out; - if (p.length == 4) { - out = m.invoke(world, from, structure, range, false); - } else if (p.length == 5 && p[4] == boolean.class) { - out = m.invoke(world, from, structure, range, false, false); - } else { - continue; - } - - Location location = extractLocation(out); - if (location != null) { - return location; - } - } - } catch (Throwable ignored) { - } - - return null; + long now = System.currentTimeMillis(); + if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { + return; } - private Object resolvePreferredStructureType(Class structureTypeClass) { - String[] preferred = {"VILLAGE", "STRONGHOLD", "RUINED_PORTAL", "MINESHAFT", "SHIPWRECK", "TRAIL_RUINS"}; - - if (structureTypeClass.isEnum()) { - Object[] constants = structureTypeClass.getEnumConstants(); - if (constants == null || constants.length == 0) { - return null; - } + Location target = locateNearestStructureFallback(p.getWorld(), p.getLocation(), getSearchRange(level)); + if (target == null) { + target = p.getWorld().getSpawnLocation(); + } - for (String name : preferred) { - Object c = Arrays.stream(constants).filter(i -> ((Enum) i).name().equals(name)).findFirst().orElse(null); - if (c != null) { - return c; - } - } + p.setCompassTarget(target); + p.sendMessage(C.AQUA + "Compass pulse: " + C.WHITE + Form.f(target.getBlockX()) + ", " + Form.f(target.getBlockZ())); + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_LODESTONE_COMPASS_LOCK, 0.8f, 1.3f); + xp(p, getConfig().xpPerPulse); + getPlayer(p).getData().addStat("discovery.cartographer-pulse.pulses", 1); + } + + private Location locateNearestStructureFallback(World world, Location from, int range) { + try { + for (Method m : world.getClass().getMethods()) { + if (!m.getName().equals("locateNearestStructure")) { + continue; + } - return constants[0]; + Class[] p = m.getParameterTypes(); + if (p.length < 4 || p[0] != Location.class || p[2] != int.class || p[3] != boolean.class) { + continue; } - for (String name : preferred) { - try { - Field f = structureTypeClass.getField(name); - Object value = f.get(null); - if (value != null) { - return value; - } - } catch (Throwable ignored) { - } + Object structure = resolvePreferredStructureType(p[1]); + if (structure == null) { + continue; } - try { - Method values = structureTypeClass.getMethod("values"); - Object out = values.invoke(null); - if (out instanceof Object[] a && a.length > 0) { - return a[0]; - } - } catch (Throwable ignored) { + Object out; + if (p.length == 4) { + out = m.invoke(world, from, structure, range, false); + } else if (p.length == 5 && p[4] == boolean.class) { + out = m.invoke(world, from, structure, range, false, false); + } else { + continue; } - return null; + Location location = extractLocation(out); + if (location != null) { + return location; + } + } + } catch (Throwable ignored) { } - private Location extractLocation(Object out) { - if (out instanceof Location location) { - return location; - } + return null; + } - if (out == null) { - return null; - } + private Object resolvePreferredStructureType(Class structureTypeClass) { + String[] preferred = {"VILLAGE", "STRONGHOLD", "RUINED_PORTAL", "MINESHAFT", "SHIPWRECK", "TRAIL_RUINS"}; + + if (structureTypeClass.isEnum()) { + Object[] constants = structureTypeClass.getEnumConstants(); + if (constants == null || constants.length == 0) { + return null; + } - try { - Method getter = out.getClass().getMethod("getLocation"); - Object loc = getter.invoke(out); - if (loc instanceof Location location) { - return location; - } - } catch (Throwable ignored) { + for (String name : preferred) { + Object c = Arrays.stream(constants).filter(i -> ((Enum) i).name().equals(name)).findFirst().orElse(null); + if (c != null) { + return c; } + } - return null; + return constants[0]; } - private int getSearchRange(int level) { - return Math.max(128, (int) Math.round(getConfig().searchRangeBase + (getLevelPercent(level) * getConfig().searchRangeFactor))); + for (String name : preferred) { + try { + Field f = structureTypeClass.getField(name); + Object value = f.get(null); + if (value != null) { + return value; + } + } catch (Throwable ignored) { + } } - private long getCooldownMillis(int level) { - return Math.max(1500L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + try { + Method values = structureTypeClass.getMethod("values"); + Object out = values.invoke(null); + if (out instanceof Object[] a && a.length > 0) { + return a[0]; + } + } catch (Throwable ignored) { } - @Override - public void onTick() { + return null; + } + private Location extractLocation(Object out) { + if (out instanceof Location location) { + return location; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (out == null) { + return null; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + try { + Method getter = out.getClass().getMethod("getLocation"); + Object loc = getter.invoke(out); + if (loc instanceof Location location) { + return location; + } + } catch (Throwable ignored) { } - @NoArgsConstructor - @ConfigDescription("Sneak-right-click with a compass to pulse toward a nearby structure target.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double searchRangeBase = 640; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double searchRangeFactor = 768; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 26000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 14000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Pulse for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerPulse = 25; - } + return null; + } + + private int getSearchRange(int level) { + return Math.max(128, (int) Math.round(getConfig().searchRangeBase + (getLevelPercent(level) * getConfig().searchRangeFactor))); + } + + private long getCooldownMillis(int level) { + return Math.max(1500L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click with a compass to pulse toward a nearby structure target.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double searchRangeBase = 640; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Search Range Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double searchRangeFactor = 768; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 26000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 14000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Pulse for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerPulse = 25; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java index f1fb9552e..59dd45d42 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryUnity.java @@ -27,10 +27,10 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,102 +44,102 @@ import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public class DiscoveryUnity extends SimpleAdaptation { - public DiscoveryUnity() { - super("discovery-unity"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.unity.description")); - setDisplayName(Localizer.dLocalize("discovery.unity.name")); - setIcon(Material.END_CRYSTAL); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInterval(666); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EXPERIENCE_BOTTLE) - .key("challenge_discovery_unity_5k") - .title(Localizer.dLocalize("advancement.challenge_discovery_unity_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_unity_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTING_TABLE) - .key("challenge_discovery_unity_50k") - .title(Localizer.dLocalize("advancement.challenge_discovery_unity_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_unity_50k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_unity_5k", "discovery.unity.orbs-distributed", 5000, 400); - registerMilestone("challenge_discovery_unity_50k", "discovery.unity.orbs-distributed", 50000, 1500); - } + public DiscoveryUnity() { + super("discovery-unity"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.unity.description")); + setDisplayName(Localizer.dLocalize("discovery.unity.name")); + setIcon(Material.END_CRYSTAL); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInterval(666); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EXPERIENCE_BOTTLE) + .key("challenge_discovery_unity_5k") + .title(Localizer.dLocalize("advancement.challenge_discovery_unity_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_unity_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTING_TABLE) + .key("challenge_discovery_unity_50k") + .title(Localizer.dLocalize("advancement.challenge_discovery_unity_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_unity_50k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_unity_5k", "discovery.unity.orbs-distributed", 5000, 400); + registerMilestone("challenge_discovery_unity_50k", "discovery.unity.orbs-distributed", 50000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getXPGained(getLevelPercent(level), 1), 0) + " " + Localizer.dLocalize("discovery.unity.lore1") + C.GRAY + " " + Localizer.dLocalize("discovery.unity.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getXPGained(getLevelPercent(level), 1), 0) + " " + Localizer.dLocalize("discovery.unity.lore1") + C.GRAY + " " + Localizer.dLocalize("discovery.unity.lore2")); + } - //Give random XP to the player when they gain XP! - @EventHandler(priority = EventPriority.LOW) - public void on(PlayerExpChangeEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - AdaptPlayer ap = getPlayer(p); - if (hasActiveAdaptation(p) && e.getAmount() > 0) { - xp(p, 5); - sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.9f); - //get a random skill that they have unlocked already - List skills = ap.getData().getSkillLines().sortV(); - if (skills.size() > 0) { - PlayerSkillLine skill = skills.get(RANDOM.nextInt(skills.size())); - //give them a random amount of XP in that skill - skill.giveXPFresh(Adapt.instance.getAdaptServer().getPlayer(p).getNot(), getXPGained(getLevelPercent(getLevel(p)), RANDOM.nextInt(3) + 1)); - getPlayer(p).getData().addStat("discovery.unity.orbs-distributed", 1); - } + //Give random XP to the player when they gain XP! + @EventHandler(priority = EventPriority.LOW) + public void on(PlayerExpChangeEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + AdaptPlayer ap = getPlayer(p); + if (hasActiveAdaptation(p) && e.getAmount() > 0) { + xp(p, 5); + sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1.9f); + //get a random skill that they have unlocked already + List skills = ap.getData().getSkillLines().sortV(); + if (skills.size() > 0) { + PlayerSkillLine skill = skills.get(RANDOM.nextInt(skills.size())); + //give them a random amount of XP in that skill + skill.giveXPFresh(Adapt.instance.getAdaptServer().getPlayer(p).getNot(), getXPGained(getLevelPercent(getLevel(p)), RANDOM.nextInt(3) + 1)); + getPlayer(p).getData().addStat("discovery.unity.orbs-distributed", 1); + } - } } + } - private double getXPGained(double factor, int amount) { - return amount * getConfig().xpGainedMultiplier * factor; - } + private double getXPGained(double factor, int amount) { + return amount * getConfig().xpGainedMultiplier * factor; + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Collecting Experience Orbs adds XP to random skills.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gained Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpGainedMultiplier = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpBoostMultiplier = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Duration for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int xpBoostDuration = 15000; - } + @NoArgsConstructor + @ConfigDescription("Collecting Experience Orbs adds XP to random skills.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gained Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpGainedMultiplier = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Multiplier for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpBoostMultiplier = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Boost Duration for the Discovery Unity adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int xpBoostDuration = 15000; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java index 554465bff..c151d70b0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryVillagerAtt.java @@ -26,12 +26,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -52,159 +52,159 @@ import java.util.concurrent.ThreadLocalRandom; public class DiscoveryVillagerAtt extends SimpleAdaptation { - private final KMap active = new KMap<>(); - - public DiscoveryVillagerAtt() { - super("discovery-villager-att"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.villager.description")); - setDisplayName(Localizer.dLocalize("discovery.villager.name")); - setIcon(Material.GLASS_BOTTLE); - setInterval(2432); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EMERALD) - .key("challenge_discovery_villager_100") - .title(Localizer.dLocalize("advancement.challenge_discovery_villager_100.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_villager_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.EMERALD_BLOCK) - .key("challenge_discovery_villager_2500") - .title(Localizer.dLocalize("advancement.challenge_discovery_villager_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_villager_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discovery_villager_100", "discovery.villager-att.improved-trades", 100, 300); - registerMilestone("challenge_discovery_villager_2500", "discovery.villager-att.improved-trades", 2500, 1000); - } - - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("discovery.villager.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.villager.lore2")); - v.addLore(C.GREEN + "+ " + getXpTaken(level) + " " + C.GRAY + Localizer.dLocalize("discovery.villager.lore3")); - } - - private double getEffectiveness(double multiplier) { - return Math.min(getConfig().maxEffectiveness, multiplier * multiplier + getConfig().effectivenessBase); - } - - private int getXpTaken(double level) { - double d = (getConfig().levelCostAdd * getConfig().amplifier) - (level * getConfig().levelDrain); - return (int) d; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEntityEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - int level = getActiveLevel(p); - if (e.getRightClicked() instanceof Villager v && level > 0) { - if (ThreadLocalRandom.current().nextDouble() <= getEffectiveness(getLevelPercent(level))) { - if (p.getLevel() - getXpTaken(level) > 0) { - BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood - blood.material = Material.EMERALD; - blood.setEntity(v); - p.setLevel((p.getLevel() - getXpTaken(level))); - sp.play(p.getLocation(), Sound.ENTITY_VILLAGER_CELEBRATE, 1f, 1f); - sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); - active.put(p.getUniqueId(), level); - p.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, level, true, true)); - getPlayer(p).getData().addStat("discovery.villager-att.improved-trades", 1); - } else { - BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood - blood.material = Material.STONE; - v.shakeHead(); - blood.setEntity(v); - sp.play(p.getLocation(), Sound.ENTITY_VILLAGER_NO, 1f, 1f); - } - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - @ReceiveCancelledEvents - public void on(InventoryOpenEvent event) { - if (!(event.getPlayer() instanceof Player p)) { - return; - } - int level = active.getOrDefault(p.getUniqueId(), 0); - if (level == 0) return; - - if (event.isCancelled()) { - active.remove(p.getUniqueId()); - p.removePotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE); + private final KMap active = new KMap<>(); + + public DiscoveryVillagerAtt() { + super("discovery-villager-att"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.villager.description")); + setDisplayName(Localizer.dLocalize("discovery.villager.name")); + setIcon(Material.GLASS_BOTTLE); + setInterval(2432); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EMERALD) + .key("challenge_discovery_villager_100") + .title(Localizer.dLocalize("advancement.challenge_discovery_villager_100.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_villager_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.EMERALD_BLOCK) + .key("challenge_discovery_villager_2500") + .title(Localizer.dLocalize("advancement.challenge_discovery_villager_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_villager_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discovery_villager_100", "discovery.villager-att.improved-trades", 100, 300); + registerMilestone("challenge_discovery_villager_2500", "discovery.villager-att.improved-trades", 2500, 1000); + } + + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("discovery.villager.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("discovery.villager.lore2")); + v.addLore(C.GREEN + "+ " + getXpTaken(level) + " " + C.GRAY + Localizer.dLocalize("discovery.villager.lore3")); + } + + private double getEffectiveness(double multiplier) { + return Math.min(getConfig().maxEffectiveness, multiplier * multiplier + getConfig().effectivenessBase); + } + + private int getXpTaken(double level) { + double d = (getConfig().levelCostAdd * getConfig().amplifier) - (level * getConfig().levelDrain); + return (int) d; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEntityEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + int level = getActiveLevel(p); + if (e.getRightClicked() instanceof Villager v && level > 0) { + if (ThreadLocalRandom.current().nextDouble() <= getEffectiveness(getLevelPercent(level))) { + if (p.getLevel() - getXpTaken(level) > 0) { + BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood + blood.material = Material.EMERALD; + blood.setEntity(v); + p.setLevel((p.getLevel() - getXpTaken(level))); + sp.play(p.getLocation(), Sound.ENTITY_VILLAGER_CELEBRATE, 1f, 1f); + sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); + active.put(p.getUniqueId(), level); + p.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, level, true, true)); + getPlayer(p).getData().addStat("discovery.villager-att.improved-trades", 1); } else { - p.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, level, true, true)); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player p) || !active.containsKey(p.getUniqueId())) { - return; + BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); // Enemy gets blood + blood.material = Material.STONE; + v.shakeHead(); + blood.setEntity(v); + sp.play(p.getLocation(), Sound.ENTITY_VILLAGER_NO, 1f, 1f); } - - active.remove(p.getUniqueId()); - p.removePotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE); - } - - @EventHandler - public void on(PlayerQuitEvent event) { - active.remove(event.getPlayer().getUniqueId()); + } } + } - @Override - public void onTick() { - active.forEach((p, lvl) -> { - var player = Bukkit.getPlayer(p); - if (player == null) return; - J.runEntity(player, () -> player.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, lvl, true, true))); - }); + @EventHandler(priority = EventPriority.MONITOR) + @ReceiveCancelledEvents + public void on(InventoryOpenEvent event) { + if (!(event.getPlayer() instanceof Player p)) { + return; } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + int level = active.getOrDefault(p.getUniqueId(), 0); + if (level == 0) return; + + if (event.isCancelled()) { + active.remove(p.getUniqueId()); + p.removePotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE); + } else { + p.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, level, true, true)); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; + @EventHandler(priority = EventPriority.MONITOR) + public void on(InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player p) || !active.containsKey(p.getUniqueId())) { + return; } - @NoArgsConstructor - @ConfigDescription("Get better villager trades at the cost of XP per interaction.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double effectivenessBase = 0.005; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxEffectiveness = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int levelDrain = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int levelCostAdd = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amplifier = 1.0; - } + active.remove(p.getUniqueId()); + p.removePotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE); + } + + @EventHandler + public void on(PlayerQuitEvent event) { + active.remove(event.getPlayer().getUniqueId()); + } + + @Override + public void onTick() { + active.forEach((p, lvl) -> { + org.bukkit.entity.Player player = Bukkit.getPlayer(p); + if (player == null) return; + J.runEntity(player, () -> player.addPotionEffect(new PotionEffect(PotionEffectType.HERO_OF_THE_VILLAGE, 60, lvl, true, true))); + }); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Get better villager trades at the cost of XP per interaction.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double effectivenessBase = 0.005; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxEffectiveness = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int levelDrain = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int levelCostAdd = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Villager Att adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amplifier = 1.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java index 59068ae85..b4b74e35d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryXpResist.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Color; @@ -43,162 +43,162 @@ import java.util.UUID; public class DiscoveryXpResist extends SimpleAdaptation { - private static final long COOLDOWN_MILLIS = 15000L; - private final Map cooldowns; - - public DiscoveryXpResist() { - super("discovery-xp-resist"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("discovery.resist.description")); - setDisplayName(Localizer.dLocalize("discovery.resist.name")); - setIcon(Material.TOTEM_OF_UNDYING); - setInterval(5215); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TOTEM_OF_UNDYING) - .key("challenge_discovery_xp_resist_25") - .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_25.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTED_GOLDEN_APPLE) - .key("challenge_discovery_xp_resist_250") - .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_250.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_250.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TOTEM_OF_UNDYING) - .key("challenge_discovery_xp_resist_clutch") - .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_clutch.title")) - .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_clutch.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_discovery_xp_resist_25", "discovery.xp-resist.saves", 25, 500); - registerMilestone("challenge_discovery_xp_resist_250", "discovery.xp-resist.saves", 250, 2000); - } - - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("discovery.resist.lore0")); - v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + Localizer.dLocalize("discovery.resist.lore1")); - v.addLore(C.GREEN + "+ " + getXpTaken(level) + " " + C.GRAY + Localizer.dLocalize("discovery.resist.lore2")); - } - - private double getEffectiveness(double factor) { - return Math.min(getConfig().maxEffectiveness, factor * factor + getConfig().effectivenessBase); + private static final long COOLDOWN_MILLIS = 15000L; + private final Map cooldowns; + + public DiscoveryXpResist() { + super("discovery-xp-resist"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("discovery.resist.description")); + setDisplayName(Localizer.dLocalize("discovery.resist.name")); + setIcon(Material.TOTEM_OF_UNDYING); + setInterval(5215); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TOTEM_OF_UNDYING) + .key("challenge_discovery_xp_resist_25") + .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_25.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTED_GOLDEN_APPLE) + .key("challenge_discovery_xp_resist_250") + .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_250.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_250.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TOTEM_OF_UNDYING) + .key("challenge_discovery_xp_resist_clutch") + .title(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_clutch.title")) + .description(Localizer.dLocalize("advancement.challenge_discovery_xp_resist_clutch.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_discovery_xp_resist_25", "discovery.xp-resist.saves", 25, 500); + registerMilestone("challenge_discovery_xp_resist_250", "discovery.xp-resist.saves", 250, 2000); + } + + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("discovery.resist.lore0")); + v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + Localizer.dLocalize("discovery.resist.lore1")); + v.addLore(C.GREEN + "+ " + getXpTaken(level) + " " + C.GRAY + Localizer.dLocalize("discovery.resist.lore2")); + } + + private double getEffectiveness(double factor) { + return Math.min(getConfig().maxEffectiveness, factor * factor + getConfig().effectivenessBase); + } + + private int getXpTaken(double level) { + double d = (getConfig().levelCostAdd * getConfig().amplifier) - (level * getConfig().levelDrain); + return Math.max(1, (int) Math.round(d)); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } - private int getXpTaken(double level) { - double d = (getConfig().levelCostAdd * getConfig().amplifier) - (level * getConfig().levelDrain); - return Math.max(1, (int) Math.round(d)); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (!isCriticalHealthDamage(p, e)) { - return; - } - - SoundPlayer sp = SoundPlayer.of(p); - int xpCost = getXpTaken(level); - if (p.getLevel() < xpCost) { - vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.RED); - sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 15, 0.01f); - return; - } - UUID id = p.getUniqueId(); - Long cooldown = cooldowns.get(id); - if (cooldown == null || M.ms() - cooldown > COOLDOWN_MILLIS) { - double effectiveness = getEffectiveness(getLevelPercent(level)); - double originalDamage = e.getDamage(); - e.setDamage(Math.max(0D, e.getDamage() * (1D - effectiveness))); - xp(p, 5); - cooldowns.put(id, M.ms()); - p.setLevel(p.getLevel() - xpCost); - vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.LIME); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_REPAIR, 3, 0.01f); - sp.play(p.getLocation(), Sound.BLOCK_SHROOMLIGHT_HIT, 15, 0.01f); - getPlayer(p).getData().addStat("discovery.xp-resist.saves", 1); - if (originalDamage >= 30.0 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_discovery_xp_resist_clutch")) { - getPlayer(p).getAdvancementHandler().grant("challenge_discovery_xp_resist_clutch"); - } - } else { - vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.RED); - sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 15, 0.01f); - } + if (!isCriticalHealthDamage(p, e)) { + return; } - private boolean isCriticalHealthDamage(Player p, EntityDamageEvent e) { - double threshold = Math.max(0D, getConfig().triggerHealthThreshold); - double absorption = Math.max(0D, p.getAbsorptionAmount()); - double rawDamage = Math.max(0D, e.getDamage()); - double finalDamage = Math.max(0D, e.getFinalDamage()); - double healthAfterRaw = p.getHealth() - Math.max(0D, rawDamage - absorption); - double healthAfterFinal = p.getHealth() - Math.max(0D, finalDamage - absorption); - double predictedHealth = Math.min(healthAfterRaw, healthAfterFinal); - return predictedHealth <= 0D || predictedHealth <= threshold || p.getHealth() <= threshold; + SoundPlayer sp = SoundPlayer.of(p); + int xpCost = getXpTaken(level); + if (p.getLevel() < xpCost) { + vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.RED); + sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 15, 0.01f); + return; } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Consume experience to mitigate damage when a hit would drop you below 5 hearts.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double effectivenessBase = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxEffectiveness = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int levelDrain = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int levelCostAdd = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amplifier = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Health Threshold for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double triggerHealthThreshold = 10.0; + UUID id = p.getUniqueId(); + Long cooldown = cooldowns.get(id); + if (cooldown == null || M.ms() - cooldown > COOLDOWN_MILLIS) { + double effectiveness = getEffectiveness(getLevelPercent(level)); + double originalDamage = e.getDamage(); + e.setDamage(Math.max(0D, e.getDamage() * (1D - effectiveness))); + xp(p, 5); + cooldowns.put(id, M.ms()); + p.setLevel(p.getLevel() - xpCost); + vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.LIME); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_REPAIR, 3, 0.01f); + sp.play(p.getLocation(), Sound.BLOCK_SHROOMLIGHT_HIT, 15, 0.01f); + getPlayer(p).getData().addStat("discovery.xp-resist.saves", 1); + if (originalDamage >= 30.0 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_discovery_xp_resist_clutch")) { + getPlayer(p).getAdvancementHandler().grant("challenge_discovery_xp_resist_clutch"); + } + } else { + vfxFastRing(p.getLocation().add(0, 0.05, 0), 1, Color.RED); + sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 15, 0.01f); } + } + + private boolean isCriticalHealthDamage(Player p, EntityDamageEvent e) { + double threshold = Math.max(0D, getConfig().triggerHealthThreshold); + double absorption = Math.max(0D, p.getAbsorptionAmount()); + double rawDamage = Math.max(0D, e.getDamage()); + double finalDamage = Math.max(0D, e.getFinalDamage()); + double healthAfterRaw = p.getHealth() - Math.max(0D, rawDamage - absorption); + double healthAfterFinal = p.getHealth() - Math.max(0D, finalDamage - absorption); + double predictedHealth = Math.min(healthAfterRaw, healthAfterFinal); + return predictedHealth <= 0D || predictedHealth <= threshold || p.getHealth() <= threshold; + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Consume experience to mitigate damage when a hit would drop you below 5 hearts.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double effectivenessBase = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxEffectiveness = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Drain for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int levelDrain = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cost Add for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int levelCostAdd = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amplifier = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Health Threshold for the Discovery Xp Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double triggerHealthThreshold = 10.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java index de808e690..aeb8aa73e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingAnvilSavant.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -36,132 +36,132 @@ import org.bukkit.inventory.AnvilInventory; public class EnchantingAnvilSavant extends SimpleAdaptation { - public EnchantingAnvilSavant() { - super("enchanting-anvil-savant"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.anvil_savant.description")); - setDisplayName(Localizer.dLocalize("enchanting.anvil_savant.name")); - setIcon(Material.ANVIL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ANVIL) - .key("challenge_enchanting_anvil_200") - .title(Localizer.dLocalize("advancement.challenge_enchanting_anvil_200.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_anvil_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ANVIL) - .key("challenge_enchanting_anvil_5k") - .title(Localizer.dLocalize("advancement.challenge_enchanting_anvil_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_anvil_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchanting_anvil_200", "enchanting.anvil-savant.levels-saved", 200, 400); - registerMilestone("challenge_enchanting_anvil_5k", "enchanting.anvil-savant.levels-saved", 5000, 1500); + public EnchantingAnvilSavant() { + super("enchanting-anvil-savant"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.anvil_savant.description")); + setDisplayName(Localizer.dLocalize("enchanting.anvil_savant.name")); + setIcon(Material.ANVIL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ANVIL) + .key("challenge_enchanting_anvil_200") + .title(Localizer.dLocalize("advancement.challenge_enchanting_anvil_200.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_anvil_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ANVIL) + .key("challenge_enchanting_anvil_5k") + .title(Localizer.dLocalize("advancement.challenge_enchanting_anvil_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_anvil_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchanting_anvil_200", "enchanting.anvil-savant.levels-saved", 200, 400); + registerMilestone("challenge_enchanting_anvil_5k", "enchanting.anvil-savant.levels-saved", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getCostReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("enchanting.anvil_savant.lore1")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PrepareAnvilEvent e) { + if (!(e.getView().getPlayer() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getCostReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("enchanting.anvil_savant.lore1")); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PrepareAnvilEvent e) { - if (!(e.getView().getPlayer() instanceof Player p)) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (!(e.getInventory() instanceof AnvilInventory inventory)) { - return; - } - - Integer current = readRepairCost(inventory); - if (current == null || current <= 0) { - return; - } - - int reduced = Math.max(getConfig().minimumCost, (int) Math.ceil(current * (1D - getCostReduction(level)))); - writeRepairCost(inventory, reduced); - int saved = current - reduced; - if (saved > 0) { - getPlayer(p).getData().addStat("enchanting.anvil-savant.levels-saved", saved); - } + if (!(e.getInventory() instanceof AnvilInventory inventory)) { + return; } - private Integer readRepairCost(AnvilInventory inventory) { - try { - Object value = inventory.getClass().getMethod("getRepairCost").invoke(inventory); - if (value instanceof Number number) { - return number.intValue(); - } - } catch (Throwable ignored) { - - } - - return null; + Integer current = readRepairCost(inventory); + if (current == null || current <= 0) { + return; } - private void writeRepairCost(AnvilInventory inventory, int cost) { - try { - inventory.getClass().getMethod("setRepairCost", int.class).invoke(inventory, cost); - } catch (Throwable ignored) { - - } + int reduced = Math.max(getConfig().minimumCost, (int) Math.ceil(current * (1D - getCostReduction(level)))); + writeRepairCost(inventory, reduced); + int saved = current - reduced; + if (saved > 0) { + getPlayer(p).getData().addStat("enchanting.anvil-savant.levels-saved", saved); } + } - private double getCostReduction(int level) { - return Math.min(getConfig().maximumReduction, getConfig().reductionBase + (getLevelPercent(level) * getConfig().reductionFactor)); - } - - @Override - public void onTick() { + private Integer readRepairCost(AnvilInventory inventory) { + try { + Object value = inventory.getClass().getMethod("getRepairCost").invoke(inventory); + if (value instanceof Number number) { + return number.intValue(); + } + } catch (Throwable ignored) { } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + return null; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + private void writeRepairCost(AnvilInventory inventory, int cost) { + try { + inventory.getClass().getMethod("setRepairCost", int.class).invoke(inventory, cost); + } catch (Throwable ignored) { - @NoArgsConstructor - @ConfigDescription("Reduce anvil XP cost when combining, repairing, and renaming.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reductionBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reductionFactor = 0.37; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Reduction for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maximumReduction = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Cost for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int minimumCost = 1; } + } + + private double getCostReduction(int level) { + return Math.min(getConfig().maximumReduction, getConfig().reductionBase + (getLevelPercent(level) * getConfig().reductionFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Reduce anvil XP cost when combining, repairing, and renaming.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionFactor = 0.37; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Reduction for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maximumReduction = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Cost for the Enchanting Anvil Savant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int minimumCost = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java index 155305596..e2b71a064 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingBookshelfAttunement.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.enchantments.EnchantmentOffer; @@ -35,100 +35,100 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent; public class EnchantingBookshelfAttunement extends SimpleAdaptation { - public EnchantingBookshelfAttunement() { - super("enchanting-bookshelf-attunement"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.bookshelf_attunement.description")); - setDisplayName(Localizer.dLocalize("enchanting.bookshelf_attunement.name")); - setIcon(Material.BOOKSHELF); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BOOKSHELF) - .key("challenge_enchanting_bookshelf_100") - .title(Localizer.dLocalize("advancement.challenge_enchanting_bookshelf_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_bookshelf_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_enchanting_bookshelf_100", "enchanting.bookshelf-attunement.enchants-boosted", 100, 400); - } + public EnchantingBookshelfAttunement() { + super("enchanting-bookshelf-attunement"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.bookshelf_attunement.description")); + setDisplayName(Localizer.dLocalize("enchanting.bookshelf_attunement.name")); + setIcon(Material.BOOKSHELF); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BOOKSHELF) + .key("challenge_enchanting_bookshelf_100") + .title(Localizer.dLocalize("advancement.challenge_enchanting_bookshelf_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_bookshelf_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_enchanting_bookshelf_100", "enchanting.bookshelf-attunement.enchants-boosted", 100, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getVirtualPower(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.bookshelf_attunement.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getVirtualPower(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.bookshelf_attunement.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PrepareItemEnchantEvent e) { - Player p = e.getEnchanter(); - if (!hasActiveAdaptation(p)) { - return; - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PrepareItemEnchantEvent e) { + Player p = e.getEnchanter(); + if (!hasActiveAdaptation(p)) { + return; + } - int power = getVirtualPower(getLevel(p)); - EnchantmentOffer[] offers = e.getOffers(); - if (offers == null) { - return; - } + int power = getVirtualPower(getLevel(p)); + EnchantmentOffer[] offers = e.getOffers(); + if (offers == null) { + return; + } - boolean boosted = false; - for (EnchantmentOffer offer : offers) { - if (offer == null) { - continue; - } + boolean boosted = false; + for (EnchantmentOffer offer : offers) { + if (offer == null) { + continue; + } - int newCost = Math.min(30, offer.getCost() + power); - int newLevel = Math.min(offer.getEnchantment().getMaxLevel(), offer.getEnchantmentLevel() + Math.max(0, power / 3)); - offer.setCost(newCost); - offer.setEnchantmentLevel(Math.max(1, newLevel)); - boosted = true; - } - if (boosted) { - getPlayer(p).getData().addStat("enchanting.bookshelf-attunement.enchants-boosted", 1); - } + int newCost = Math.min(30, offer.getCost() + power); + int newLevel = Math.min(offer.getEnchantment().getMaxLevel(), offer.getEnchantmentLevel() + Math.max(0, power / 3)); + offer.setCost(newCost); + offer.setEnchantmentLevel(Math.max(1, newLevel)); + boosted = true; } - - private int getVirtualPower(int level) { - return Math.max(1, (int) Math.round(getConfig().powerBase + (getLevelPercent(level) * getConfig().powerFactor))); + if (boosted) { + getPlayer(p).getData().addStat("enchanting.bookshelf-attunement.enchants-boosted", 1); } + } - @Override - public void onTick() { + private int getVirtualPower(int level) { + return Math.max(1, (int) Math.round(getConfig().powerBase + (getLevelPercent(level) * getConfig().powerFactor))); + } - } + @Override + public void onTick() { - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Gain virtual bookshelf power to improve enchanting table offer quality.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Base for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double powerBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Factor for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double powerFactor = 5; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain virtual bookshelf power to improve enchanting table offer quality.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Base for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double powerBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Power Factor for the Enchanting Bookshelf Attunement adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double powerFactor = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java index 01d5da6d4..6448579e1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingGrindstoneRecovery.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -46,180 +46,180 @@ import java.util.concurrent.ThreadLocalRandom; public class EnchantingGrindstoneRecovery extends SimpleAdaptation { - public EnchantingGrindstoneRecovery() { - super("enchanting-grindstone-recovery"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.grindstone_recovery.description")); - setDisplayName(Localizer.dLocalize("enchanting.grindstone_recovery.name")); - setIcon(Material.GRINDSTONE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1700); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GRINDSTONE) - .key("challenge_enchanting_grindstone_50") - .title(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_50.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GRINDSTONE) - .key("challenge_enchanting_grindstone_500") - .title(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_500.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchanting_grindstone_50", "enchanting.grindstone-recovery.enchants-recovered", 50, 300); - registerMilestone("challenge_enchanting_grindstone_500", "enchanting.grindstone-recovery.enchants-recovered", 500, 1000); + public EnchantingGrindstoneRecovery() { + super("enchanting-grindstone-recovery"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.grindstone_recovery.description")); + setDisplayName(Localizer.dLocalize("enchanting.grindstone_recovery.name")); + setIcon(Material.GRINDSTONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1700); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GRINDSTONE) + .key("challenge_enchanting_grindstone_50") + .title(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_50.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GRINDSTONE) + .key("challenge_enchanting_grindstone_500") + .title(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_500.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_grindstone_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchanting_grindstone_50", "enchanting.grindstone-recovery.enchants-recovered", 50, 300); + registerMilestone("challenge_enchanting_grindstone_500", "enchanting.grindstone-recovery.enchants-recovered", 500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getRecoverChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getBonusXp(level), 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(InventoryClickEvent e) { + if (!(e.getWhoClicked() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getRecoverChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getBonusXp(level), 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.grindstone_recovery.lore3")); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(InventoryClickEvent e) { - if (!(e.getWhoClicked() instanceof Player p)) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (e.getView().getTopInventory().getType() != InventoryType.GRINDSTONE || e.getRawSlot() != 2 || p.hasCooldown(Material.GRINDSTONE)) { - return; - } - - ItemStack source = getEnchantedSource(e.getView().getTopInventory().getItem(0), e.getView().getTopInventory().getItem(1)); - if (source == null) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getRecoverChance(level)) { - return; - } - - ItemStack recovered = makeBook(source.getEnchantments()); - if (recovered == null) { - return; - } - - Map overflow = p.getInventory().addItem(recovered); - overflow.values().forEach(item -> p.getWorld().dropItemNaturally(p.getLocation(), item)); - int xp = Math.max(0, (int) Math.round(getBonusXp(level))); - if (xp > 0) { - p.giveExp(xp); - } - - p.setCooldown(Material.GRINDSTONE, getCooldownTicks(level)); - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.BLOCK_GRINDSTONE_USE, 0.95f, 1.15f); - sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.45f); - xp(p, getConfig().skillXpOnRecovery); - getPlayer(p).getData().addStat("enchanting.grindstone-recovery.enchants-recovered", 1); + if (e.getView().getTopInventory().getType() != InventoryType.GRINDSTONE || e.getRawSlot() != 2 || p.hasCooldown(Material.GRINDSTONE)) { + return; } - private ItemStack getEnchantedSource(ItemStack a, ItemStack b) { - if (isEnchanted(a)) { - return a; - } - - if (isEnchanted(b)) { - return b; - } - - return null; + ItemStack source = getEnchantedSource(e.getView().getTopInventory().getItem(0), e.getView().getTopInventory().getItem(1)); + if (source == null) { + return; } - private boolean isEnchanted(ItemStack item) { - return isItem(item) && !item.getEnchantments().isEmpty(); + if (ThreadLocalRandom.current().nextDouble() > getRecoverChance(level)) { + return; } - private ItemStack makeBook(Map source) { - if (source.isEmpty()) { - return null; - } - - List> entries = new ArrayList<>(source.entrySet()); - Map.Entry picked = entries.get(ThreadLocalRandom.current().nextInt(entries.size())); - ItemStack book = new ItemStack(Material.ENCHANTED_BOOK); - EnchantmentStorageMeta meta = (EnchantmentStorageMeta) book.getItemMeta(); - if (meta == null) { - return null; - } - - int safeLevel = Math.max(1, Math.min(picked.getValue(), picked.getKey().getMaxLevel())); - meta.addStoredEnchant(picked.getKey(), safeLevel, true); - book.setItemMeta(meta); - return book; + ItemStack recovered = makeBook(source.getEnchantments()); + if (recovered == null) { + return; } - private double getRecoverChance(int level) { - return Math.min(getConfig().maxRecoverChance, getConfig().recoverChanceBase + (getLevelPercent(level) * getConfig().recoverChanceFactor)); + Map overflow = p.getInventory().addItem(recovered); + overflow.values().forEach(item -> p.getWorld().dropItemNaturally(p.getLocation(), item)); + int xp = Math.max(0, (int) Math.round(getBonusXp(level))); + if (xp > 0) { + p.giveExp(xp); } - private double getBonusXp(int level) { - return getConfig().bonusXpBase + (getLevelPercent(level) * getConfig().bonusXpFactor); + p.setCooldown(Material.GRINDSTONE, getCooldownTicks(level)); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.BLOCK_GRINDSTONE_USE, 0.95f, 1.15f); + sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.45f); + xp(p, getConfig().skillXpOnRecovery); + getPlayer(p).getData().addStat("enchanting.grindstone-recovery.enchants-recovered", 1); + } + + private ItemStack getEnchantedSource(ItemStack a, ItemStack b) { + if (isEnchanted(a)) { + return a; } - private int getCooldownTicks(int level) { - return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + if (isEnchanted(b)) { + return b; } - @Override - public void onTick() { + return null; + } - } + private boolean isEnchanted(ItemStack item) { + return isItem(item) && !item.getEnchantments().isEmpty(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private ItemStack makeBook(Map source) { + if (source.isEmpty()) { + return null; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + List> entries = new ArrayList<>(source.entrySet()); + Map.Entry picked = entries.get(ThreadLocalRandom.current().nextInt(entries.size())); + ItemStack book = new ItemStack(Material.ENCHANTED_BOOK); + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) book.getItemMeta(); + if (meta == null) { + return null; } - @NoArgsConstructor - @ConfigDescription("Using a grindstone can recover one removed enchant on a book with bonus XP.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.74; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double recoverChanceBase = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double recoverChanceFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Recover Chance for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRecoverChance = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusXpBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusXpFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 120; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 70; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Recovery for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double skillXpOnRecovery = 13; - } + int safeLevel = Math.max(1, Math.min(picked.getValue(), picked.getKey().getMaxLevel())); + meta.addStoredEnchant(picked.getKey(), safeLevel, true); + book.setItemMeta(meta); + return book; + } + + private double getRecoverChance(int level) { + return Math.min(getConfig().maxRecoverChance, getConfig().recoverChanceBase + (getLevelPercent(level) * getConfig().recoverChanceFactor)); + } + + private double getBonusXp(int level) { + return getConfig().bonusXpBase + (getLevelPercent(level) * getConfig().bonusXpFactor); + } + + private int getCooldownTicks(int level) { + return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Using a grindstone can recover one removed enchant on a book with bonus XP.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.74; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double recoverChanceBase = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Recover Chance Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double recoverChanceFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Recover Chance for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRecoverChance = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusXpBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Xp Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusXpFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 70; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Recovery for the Enchanting Grindstone Recovery adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double skillXpOnRecovery = 13; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java index 6b18d0334..bfe47ecbc 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingLapisReturn.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -40,103 +40,103 @@ import java.util.concurrent.ThreadLocalRandom; public class EnchantingLapisReturn extends SimpleAdaptation { - private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); - - public EnchantingLapisReturn() { - super("enchanting-lapis-return"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.lapis_return.description")); - setDisplayName(Localizer.dLocalize("enchanting.lapis_return.name")); - setIcon(Material.LAPIS_LAZULI); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(20999); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LAPIS_LAZULI) - .key("challenge_enchanting_lapis_100") - .title(Localizer.dLocalize("advancement.challenge_enchanting_lapis_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_lapis_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.LAPIS_BLOCK) - .key("challenge_enchanting_lapis_2500") - .title(Localizer.dLocalize("advancement.challenge_enchanting_lapis_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_lapis_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchanting_lapis_100", "enchanting.lapis-return.lapis-saved", 100, 300); - registerMilestone("challenge_enchanting_lapis_2500", "enchanting.lapis-return.lapis-saved", 2500, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("enchanting.lapis_return.lore1")); - } - - @EventHandler - public void on(PlayerQuitEvent e) { - cooldown.remove(e.getPlayer().getUniqueId()); - } - - - @EventHandler(priority = EventPriority.HIGH) - public void on(EnchantItemEvent e) { - - Player p = e.getEnchanter(); - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - - if (ThreadLocalRandom.current().nextDouble(100D) > 80D) { - long now = System.currentTimeMillis(); - UUID playerId = p.getUniqueId(); - Long nextAllowedAt = cooldown.get(playerId); - if (nextAllowedAt != null && nextAllowedAt > now) { - return; - } - - cooldown.put(playerId, now + 20000L); - p.getWorld().dropItemNaturally(p.getLocation(), new ItemStack(Material.LAPIS_LAZULI, level)); - getPlayer(p).getData().addStat("enchanting.lapis-return.lapis-saved", level); - } + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); + + public EnchantingLapisReturn() { + super("enchanting-lapis-return"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.lapis_return.description")); + setDisplayName(Localizer.dLocalize("enchanting.lapis_return.name")); + setIcon(Material.LAPIS_LAZULI); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(20999); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LAPIS_LAZULI) + .key("challenge_enchanting_lapis_100") + .title(Localizer.dLocalize("advancement.challenge_enchanting_lapis_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_lapis_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LAPIS_BLOCK) + .key("challenge_enchanting_lapis_2500") + .title(Localizer.dLocalize("advancement.challenge_enchanting_lapis_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_lapis_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchanting_lapis_100", "enchanting.lapis-return.lapis-saved", 100, 300); + registerMilestone("challenge_enchanting_lapis_2500", "enchanting.lapis-return.lapis-saved", 2500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("enchanting.lapis_return.lore1")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + cooldown.remove(e.getPlayer().getUniqueId()); + } + + + @EventHandler(priority = EventPriority.HIGH) + public void on(EnchantItemEvent e) { + + Player p = e.getEnchanter(); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @Override - public void onTick() { - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + if (ThreadLocalRandom.current().nextDouble(100D) > 80D) { + long now = System.currentTimeMillis(); + UUID playerId = p.getUniqueId(); + Long nextAllowedAt = cooldown.get(playerId); + if (nextAllowedAt != null && nextAllowedAt > now) { + return; + } - @NoArgsConstructor - @ConfigDescription("Chance to return free lapis when enchanting at the cost of 1 extra level.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.9; + cooldown.put(playerId, now + 20000L); + p.getWorld().dropItemNaturally(p.getLocation(), new ItemStack(Material.LAPIS_LAZULI, level)); + getPlayer(p).getData().addStat("enchanting.lapis-return.lapis-saved", level); } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Chance to return free lapis when enchanting at the cost of 1 extra level.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java index ff5886718..63d39e063 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,151 +42,151 @@ import java.util.concurrent.ThreadLocalRandom; public class EnchantingOfferReroll extends SimpleAdaptation { - public EnchantingOfferReroll() { - super("enchanting-offer-reroll"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.offer_reroll.description")); - setDisplayName(Localizer.dLocalize("enchanting.offer_reroll.name")); - setIcon(Material.ENCHANTING_TABLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1800); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENCHANTING_TABLE) - .key("challenge_enchanting_reroll_100") - .title(Localizer.dLocalize("advancement.challenge_enchanting_reroll_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_reroll_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTING_TABLE) - .key("challenge_enchanting_reroll_1k") - .title(Localizer.dLocalize("advancement.challenge_enchanting_reroll_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_reroll_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchanting_reroll_100", "enchanting.offer-reroll.rerolls", 100, 300); - registerMilestone("challenge_enchanting_reroll_1k", "enchanting.offer-reroll.rerolls", 1000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore1")); - v.addLore(C.YELLOW + "* " + getLapisCost(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore2")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || e.getClickedBlock().getType() != Material.ENCHANTING_TABLE) { - return; - } - - int lapisCost = getLapisCost(level); - if (p.getFoodLevel() < 0) { - return; - } - - if (!consumeLapis(p, lapisCost) || p.getLevel() < getConfig().xpLevelCost) { - return; - } - - if (!setSeed(p, ThreadLocalRandom.current().nextInt())) { - return; - } - - p.setLevel(Math.max(0, p.getLevel() - getConfig().xpLevelCost)); - p.setCooldown(Material.ENCHANTING_TABLE, getCooldownTicks(level)); - e.setCancelled(true); - - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.2f); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.5f, 0.85f); - xp(p, getConfig().xpGainOnReroll); - getPlayer(p).getData().addStat("enchanting.offer-reroll.rerolls", 1); + public EnchantingOfferReroll() { + super("enchanting-offer-reroll"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.offer_reroll.description")); + setDisplayName(Localizer.dLocalize("enchanting.offer_reroll.name")); + setIcon(Material.ENCHANTING_TABLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1800); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENCHANTING_TABLE) + .key("challenge_enchanting_reroll_100") + .title(Localizer.dLocalize("advancement.challenge_enchanting_reroll_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_reroll_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTING_TABLE) + .key("challenge_enchanting_reroll_1k") + .title(Localizer.dLocalize("advancement.challenge_enchanting_reroll_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_reroll_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchanting_reroll_100", "enchanting.offer-reroll.rerolls", 100, 300); + registerMilestone("challenge_enchanting_reroll_1k", "enchanting.offer-reroll.rerolls", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore1")); + v.addLore(C.YELLOW + "* " + getLapisCost(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + return; } - private boolean consumeLapis(Player p, int amount) { - int need = amount; - for (ItemStack stack : p.getInventory().getContents()) { - if (stack == null || stack.getType() != Material.LAPIS_LAZULI || need <= 0) { - continue; - } - - int used = Math.min(stack.getAmount(), need); - stack.setAmount(stack.getAmount() - used); - need -= used; - } - return need <= 0; + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || e.getClickedBlock().getType() != Material.ENCHANTING_TABLE) { + return; } - private boolean setSeed(Player p, int seed) { - try { - p.getClass().getMethod("setEnchantmentSeed", int.class).invoke(p, seed); - return true; - } catch (Throwable ignored) { - return false; - } + int lapisCost = getLapisCost(level); + if (p.getFoodLevel() < 0) { + return; } - private int getLapisCost(int level) { - return Math.max(1, (int) Math.round(getConfig().lapisCostBase - (getLevelPercent(level) * getConfig().lapisCostFactor))); + if (!consumeLapis(p, lapisCost) || p.getLevel() < getConfig().xpLevelCost) { + return; } - private int getCooldownTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + if (!setSeed(p, ThreadLocalRandom.current().nextInt())) { + return; } - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + p.setLevel(Math.max(0, p.getLevel() - getConfig().xpLevelCost)); + p.setCooldown(Material.ENCHANTING_TABLE, getCooldownTicks(level)); + e.setCancelled(true); + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.2f); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.5f, 0.85f); + xp(p, getConfig().xpGainOnReroll); + getPlayer(p).getData().addStat("enchanting.offer-reroll.rerolls", 1); + } + + private boolean consumeLapis(Player p, int amount) { + int need = amount; + for (ItemStack stack : p.getInventory().getContents()) { + if (stack == null || stack.getType() != Material.LAPIS_LAZULI || need <= 0) { + continue; + } + + int used = Math.min(stack.getAmount(), need); + stack.setAmount(stack.getAmount() - used); + need -= used; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sneak-right-click an enchanting table to reroll offers for lapis and XP.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lapisCostBase = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lapisCostFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 320; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 220; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Cost for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int xpLevelCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gain On Reroll for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpGainOnReroll = 15; + return need <= 0; + } + + private boolean setSeed(Player p, int seed) { + try { + p.getClass().getMethod("setEnchantmentSeed", int.class).invoke(p, seed); + return true; + } catch (Throwable ignored) { + return false; } + } + + private int getLapisCost(int level) { + return Math.max(1, (int) Math.round(getConfig().lapisCostBase - (getLevelPercent(level) * getConfig().lapisCostFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click an enchanting table to reroll offers for lapis and XP.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lapisCostBase = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Cost Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lapisCostFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 320; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 220; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Level Cost for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int xpLevelCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Gain On Reroll for the Enchanting Offer Reroll adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpGainOnReroll = 15; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java index 1e409d72b..2eb5caf8a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingQuickEnchant.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -45,175 +45,175 @@ import org.bukkit.inventory.meta.ItemMeta; public class EnchantingQuickEnchant extends SimpleAdaptation { - public EnchantingQuickEnchant() { - super("enchanting-quick-enchant"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.quick_enchant.description")); - setDisplayName(Localizer.dLocalize("enchanting.quick_enchant.name")); - setIcon(Material.WRITABLE_BOOK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(15100); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENCHANTED_BOOK) - .key("challenge_enchanting_quick_100") - .title(Localizer.dLocalize("advancement.challenge_enchanting_quick_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_quick_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BOOKSHELF) - .key("challenge_enchanting_quick_1k") - .title(Localizer.dLocalize("advancement.challenge_enchanting_quick_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_quick_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchanting_quick_100", "enchanting.quick-enchant.books-applied", 100, 300); - registerMilestone("challenge_enchanting_quick_1k", "enchanting.quick-enchant.books-applied", 1000, 1000); + public EnchantingQuickEnchant() { + super("enchanting-quick-enchant"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.quick_enchant.description")); + setDisplayName(Localizer.dLocalize("enchanting.quick_enchant.name")); + setIcon(Material.WRITABLE_BOOK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(15100); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENCHANTED_BOOK) + .key("challenge_enchanting_quick_100") + .title(Localizer.dLocalize("advancement.challenge_enchanting_quick_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_quick_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BOOKSHELF) + .key("challenge_enchanting_quick_1k") + .title(Localizer.dLocalize("advancement.challenge_enchanting_quick_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_quick_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchanting_quick_100", "enchanting.quick-enchant.books-applied", 100, 300); + registerMilestone("challenge_enchanting_quick_1k", "enchanting.quick-enchant.books-applied", 1000, 1000); + } + + private int getTotalLevelCount(int level) { + return level + (level > getConfig().maxPowerBonusLimit ? level / getConfig().maxPowerBonus1PerLevels : 0); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getTotalLevelCount(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.quick_enchant.lore1")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(InventoryClickEvent e) { + if (e.getClickedInventory() == null) { + return; } - private int getTotalLevelCount(int level) { - return level + (level > getConfig().maxPowerBonusLimit ? level / getConfig().maxPowerBonus1PerLevels : 0); + if (!(e.getWhoClicked() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getTotalLevelCount(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.quick_enchant.lore1")); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryClickEvent e) { - if (e.getClickedInventory() == null) { - return; - } - - if (!(e.getWhoClicked() instanceof Player p)) { - return; + if (e.getAction().equals(InventoryAction.SWAP_WITH_CURSOR) + && e.getClick().equals(ClickType.LEFT) + && (e.getSlotType().equals(InventoryType.SlotType.CONTAINER) + || e.getSlotType().equals(InventoryType.SlotType.ARMOR) + || e.getSlotType().equals(InventoryType.SlotType.QUICKBAR)) + && e.getCursor() != null + && e.getCurrentItem() != null + && e.getCursor().getType().equals(Material.ENCHANTED_BOOK) + && !e.getCurrentItem().getType().equals(Material.BOOK) + && !e.getCurrentItem().getType().equals(Material.ENCHANTED_BOOK) + && e.getCursor().getItemMeta() != null + && e.getCursor().getItemMeta() instanceof EnchantmentStorageMeta eb + && e.getCurrentItem().getItemMeta() != null + && e.getCurrentItem().getAmount() == 1 + && e.getCursor().getAmount() == 1) { + ItemStack item = e.getCurrentItem(); + ItemStack book = e.getCursor(); + KMap itemEnchants = new KMap<>(item.getType().equals(Material.ENCHANTED_BOOK) + ? ((EnchantmentStorageMeta) item.getItemMeta()).getStoredEnchants() + : item.getEnchantments()); + KMap bookEnchants = new KMap<>(eb.getStoredEnchants()); + KMap newEnchants = itemEnchants.copy(); + KMap addEnchants = new KMap<>(); + int power = itemEnchants.values().stream().mapToInt(i -> i).sum(); + + if (bookEnchants.isEmpty()) { + return; + } + + for (Enchantment i : bookEnchants.k()) { + if (itemEnchants.containsKey(i)) { + continue; } - int level = getActiveLevel(p); - if (level <= 0) { - return; + power += bookEnchants.get(i); + newEnchants.put(i, bookEnchants.get(i)); + addEnchants.put(i, bookEnchants.get(i)); + bookEnchants.remove(i); + } + + SoundPlayer sp = SoundPlayer.of(p); + if (power > getTotalLevelCount(level)) { + Adapt.actionbar(p, C.RED + Localizer.dLocalize("enchanting.quick_enchant.lore2") + getTotalLevelCount(level) + " " + Localizer.dLocalize("enchanting.quick_enchant.lore3")); + sp.play(p.getLocation(), Sound.BLOCK_CONDUIT_DEACTIVATE, 0.5f, 1.7f); + return; + } + + if (!itemEnchants.equals(newEnchants)) { + ItemMeta im = item.getItemMeta(); + + if (im instanceof EnchantmentStorageMeta sm) { + sm.getStoredEnchants().keySet().forEach(sm::removeStoredEnchant); + newEnchants.forEach((ec, l) -> sm.addStoredEnchant(ec, l, true)); + Adapt.messagePlayer(p, "---"); + sm.getStoredEnchants().forEach((k, v) -> Adapt.messagePlayer(p, k.getKey().getKey() + " " + v)); + } else { + im.getEnchants().keySet().forEach(im::removeEnchant); + newEnchants.forEach((ec, l) -> im.addEnchant(ec, l, true)); } - if (e.getAction().equals(InventoryAction.SWAP_WITH_CURSOR) - && e.getClick().equals(ClickType.LEFT) - && (e.getSlotType().equals(InventoryType.SlotType.CONTAINER) - || e.getSlotType().equals(InventoryType.SlotType.ARMOR) - || e.getSlotType().equals(InventoryType.SlotType.QUICKBAR)) - && e.getCursor() != null - && e.getCurrentItem() != null - && e.getCursor().getType().equals(Material.ENCHANTED_BOOK) - && !e.getCurrentItem().getType().equals(Material.BOOK) - && !e.getCurrentItem().getType().equals(Material.ENCHANTED_BOOK) - && e.getCursor().getItemMeta() != null - && e.getCursor().getItemMeta() instanceof EnchantmentStorageMeta eb - && e.getCurrentItem().getItemMeta() != null - && e.getCurrentItem().getAmount() == 1 - && e.getCursor().getAmount() == 1) { - ItemStack item = e.getCurrentItem(); - ItemStack book = e.getCursor(); - KMap itemEnchants = new KMap<>(item.getType().equals(Material.ENCHANTED_BOOK) - ? ((EnchantmentStorageMeta) item.getItemMeta()).getStoredEnchants() - : item.getEnchantments()); - KMap bookEnchants = new KMap<>(eb.getStoredEnchants()); - KMap newEnchants = itemEnchants.copy(); - KMap addEnchants = new KMap<>(); - int power = itemEnchants.values().stream().mapToInt(i -> i).sum(); - - if (bookEnchants.isEmpty()) { - return; - } - - for (Enchantment i : bookEnchants.k()) { - if (itemEnchants.containsKey(i)) { - continue; - } - - power += bookEnchants.get(i); - newEnchants.put(i, bookEnchants.get(i)); - addEnchants.put(i, bookEnchants.get(i)); - bookEnchants.remove(i); - } - - SoundPlayer sp = SoundPlayer.of(p); - if (power > getTotalLevelCount(level)) { - Adapt.actionbar(p, C.RED + Localizer.dLocalize("enchanting.quick_enchant.lore2") + getTotalLevelCount(level) + " " + Localizer.dLocalize("enchanting.quick_enchant.lore3")); - sp.play(p.getLocation(), Sound.BLOCK_CONDUIT_DEACTIVATE, 0.5f, 1.7f); - return; - } - - if (!itemEnchants.equals(newEnchants)) { - ItemMeta im = item.getItemMeta(); - - if (im instanceof EnchantmentStorageMeta sm) { - sm.getStoredEnchants().keySet().forEach(sm::removeStoredEnchant); - newEnchants.forEach((ec, l) -> sm.addStoredEnchant(ec, l, true)); - Adapt.messagePlayer(p, "---"); - sm.getStoredEnchants().forEach((k, v) -> Adapt.messagePlayer(p, k.getKey().getKey() + " " + v)); - } else { - im.getEnchants().keySet().forEach(im::removeEnchant); - newEnchants.forEach((ec, l) -> im.addEnchant(ec, l, true)); - } - - xp(p, 50); - item.setItemMeta(im); - e.setCurrentItem(item); - e.setCancelled(true); - getPlayer(p).getData().addStat("enchanting.quick-enchant.books-applied", 1); - sp.play(p.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.7f); - sp.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_TILES_BREAK, 0.5f, 0.7f); - xp(p, 320 * addEnchants.values().stream().mapToInt((i) -> i).sum(), "quick-apply"); - - if (bookEnchants.isEmpty()) { - e.setCursor(null); - } else if (!eb.getStoredEnchants().equals(bookEnchants)) { - eb.getStoredEnchants().keySet().forEach(eb::removeStoredEnchant); - bookEnchants.forEach((ec, l) -> eb.addStoredEnchant(ec, l, true)); - book.setItemMeta(eb); - e.setCursor(book); - } - } + xp(p, 50); + item.setItemMeta(im); + e.setCurrentItem(item); + e.setCancelled(true); + getPlayer(p).getData().addStat("enchanting.quick-enchant.books-applied", 1); + sp.play(p.getLocation(), Sound.BLOCK_ENCHANTMENT_TABLE_USE, 1f, 1.7f); + sp.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_TILES_BREAK, 0.5f, 0.7f); + xp(p, 320 * addEnchants.values().stream().mapToInt((i) -> i).sum(), "quick-apply"); + + if (bookEnchants.isEmpty()) { + e.setCursor(null); + } else if (!eb.getStoredEnchants().equals(bookEnchants)) { + eb.getStoredEnchants().keySet().forEach(eb::removeStoredEnchant); + bookEnchants.forEach((ec, l) -> eb.addStoredEnchant(ec, l, true)); + book.setItemMeta(eb); + e.setCursor(book); } + } } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Enchant items by clicking enchant books directly on them.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus Limit for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxPowerBonusLimit = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus1Per Levels for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxPowerBonus1PerLevels = 3; - } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Enchant items by clicking enchant books directly on them.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus Limit for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxPowerBonusLimit = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Power Bonus1Per Levels for the Enchanting Quick Enchant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxPowerBonus1PerLevels = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java index 96045d663..992a762a1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingXPReturn.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -38,93 +38,93 @@ import java.util.UUID; public class EnchantingXPReturn extends SimpleAdaptation { - private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map cooldown = new java.util.concurrent.ConcurrentHashMap<>(); - public EnchantingXPReturn() { - super("enchanting-xp-return"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("enchanting.return.description")); - setDisplayName(Localizer.dLocalize("enchanting.return.name")); - setIcon(Material.EXPERIENCE_BOTTLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(13001); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EXPERIENCE_BOTTLE) - .key("challenge_enchanting_xp_100") - .title(Localizer.dLocalize("advancement.challenge_enchanting_xp_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchanting_xp_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_enchanting_xp_100", "enchanting.xp-return.levels-saved", 100, 400); - } + public EnchantingXPReturn() { + super("enchanting-xp-return"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("enchanting.return.description")); + setDisplayName(Localizer.dLocalize("enchanting.return.name")); + setIcon(Material.EXPERIENCE_BOTTLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(13001); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EXPERIENCE_BOTTLE) + .key("challenge_enchanting_xp_100") + .title(Localizer.dLocalize("advancement.challenge_enchanting_xp_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchanting_xp_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_enchanting_xp_100", "enchanting.xp-return.levels-saved", 100, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("enchanting.return.lore1")); - v.addLore(C.GREEN + "" + getConfig().xpReturn * (level * level) + Localizer.dLocalize("enchanting.return.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("enchanting.return.lore1")); + v.addLore(C.GREEN + "" + getConfig().xpReturn * (level * level) + Localizer.dLocalize("enchanting.return.lore2")); + } - @EventHandler - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - cooldown.remove(p.getUniqueId()); - } + @EventHandler + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + cooldown.remove(p.getUniqueId()); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EnchantItemEvent e) { - Player p = e.getEnchanter(); - int level = getActiveLevel(p); - if (level <= 0) { - return; - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EnchantItemEvent e) { + Player p = e.getEnchanter(); + int level = getActiveLevel(p); + if (level <= 0) { + return; + } - if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { - cooldown.remove(p.getUniqueId()); - } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { - return; - } - cooldown.put(p.getUniqueId(), System.currentTimeMillis()); - int xpAmount = getConfig().xpReturn * (level * level); - p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(xpAmount); - getPlayer(p).getData().addStat("enchanting.xp-return.levels-saved", xpAmount); + if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 < System.currentTimeMillis()) { + cooldown.remove(p.getUniqueId()); + } else if (cooldown.containsKey(p.getUniqueId()) && cooldown.get(p.getUniqueId()) + 20000 > System.currentTimeMillis()) { + return; } + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); + int xpAmount = getConfig().xpReturn * (level * level); + p.getWorld().spawn(p.getLocation(), org.bukkit.entity.ExperienceOrb.class).setExperience(xpAmount); + getPlayer(p).getData().addStat("enchanting.xp-return.levels-saved", xpAmount); + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Enchanting XP is partially refunded when you enchant an item.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Return for the Enchanting XPReturn adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public int xpReturn = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.9; - } + @NoArgsConstructor + @ConfigDescription("Enchanting XP is partially refunded when you enchant an item.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Return for the Enchanting XPReturn adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public int xpReturn = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java index 00588a20e..5f023e8ac 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDropToInventory.java @@ -25,10 +25,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -41,83 +41,83 @@ import java.util.List; public class ExcavationDropToInventory extends SimpleAdaptation { - public ExcavationDropToInventory() { - super("excavation-drop-to-inventory"); - registerConfiguration(ExcavationDropToInventory.Config.class); - setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); - setDisplayName(Localizer.dLocalize("excavation.drop_to_inventory.name")); - setIcon(Material.CHEST); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(11777); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_excavation_dti_10k") - .title(Localizer.dLocalize("advancement.challenge_excavation_dti_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_dti_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_excavation_dti_10k", "excavation.drop-to-inv.items-caught", 10000, 500); - } + public ExcavationDropToInventory() { + super("excavation-drop-to-inventory"); + registerConfiguration(ExcavationDropToInventory.Config.class); + setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); + setDisplayName(Localizer.dLocalize("excavation.drop_to_inventory.name")); + setIcon(Material.CHEST); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(11777); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_excavation_dti_10k") + .title(Localizer.dLocalize("advancement.challenge_excavation_dti_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_dti_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_dti_10k", "excavation.drop-to-inv.items-caught", 10000, 500); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); - } + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (resolveInteractBreakContext(p, e.getBlock().getLocation(), null, true) == null) { - return; - } - if (ItemListings.toolShovels.contains(p.getInventory().getItemInMainHand().getType())) { - List items = new KList<>(e.getItems()); - e.getItems().clear(); - for (Item i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - xp(p, 2); - getPlayer(p).getData().addStat("excavation.drop-to-inv.items-caught", 1); - if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i.getItemStack()); - } - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDropItemEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (resolveInteractBreakContext(p, e.getBlock().getLocation(), null, true) == null) { + return; + } + if (ItemListings.toolShovels.contains(p.getInventory().getItemInMainHand().getType())) { + List items = new KList<>(e.getItems()); + e.getItems().clear(); + for (Item i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + xp(p, 2); + getPlayer(p).getData().addStat("excavation.drop-to-inv.items-caught", 1); + if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i.getItemStack()); } + } } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Excavated blocks drop directly into your inventory.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Excavated blocks drop directly into your inventory.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java index 50b709df1..1bf2597e8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationHaste.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -36,84 +36,84 @@ import org.bukkit.potion.PotionEffect; public class ExcavationHaste extends SimpleAdaptation { - public ExcavationHaste() { - super("excavation-haste"); - registerConfiguration(ExcavationHaste.Config.class); - setDisplayName(Localizer.dLocalize("excavation.haste.name")); - setDescription(Localizer.dLocalize("excavation.haste.description")); - setIcon(Material.GOLDEN_PICKAXE); - setInterval(4388); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SHOVEL) - .key("challenge_excavation_haste_5k") - .title(Localizer.dLocalize("advancement.challenge_excavation_haste_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_haste_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SHOVEL) - .key("challenge_excavation_haste_50k") - .title(Localizer.dLocalize("advancement.challenge_excavation_haste_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_haste_50k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_excavation_haste_5k", "excavation.haste.blocks-while-hasted", 5000, 400); - registerMilestone("challenge_excavation_haste_50k", "excavation.haste.blocks-while-hasted", 50000, 1500); - } + public ExcavationHaste() { + super("excavation-haste"); + registerConfiguration(ExcavationHaste.Config.class); + setDisplayName(Localizer.dLocalize("excavation.haste.name")); + setDescription(Localizer.dLocalize("excavation.haste.description")); + setIcon(Material.GOLDEN_PICKAXE); + setInterval(4388); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SHOVEL) + .key("challenge_excavation_haste_5k") + .title(Localizer.dLocalize("advancement.challenge_excavation_haste_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_haste_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SHOVEL) + .key("challenge_excavation_haste_50k") + .title(Localizer.dLocalize("advancement.challenge_excavation_haste_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_haste_50k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_excavation_haste_5k", "excavation.haste.blocks-while-hasted", 5000, 400); + registerMilestone("challenge_excavation_haste_50k", "excavation.haste.blocks-while-hasted", 50000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("excavation.haste.lore1")); - v.addLore(C.GREEN + "" + (level) + C.GRAY + Localizer.dLocalize("excavation.haste.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("excavation.haste.lore1")); + v.addLore(C.GREEN + "" + (level) + C.GRAY + Localizer.dLocalize("excavation.haste.lore2")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDamageEvent e) { - Player p = e.getPlayer(); - var context = resolveInteractContext(p, e.getBlock().getLocation()); - if (context == null) { - return; - } - p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 15, context.level(), false, false, true)); - getPlayer(p).getData().addStat("excavation.haste.blocks-while-hasted", 1); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDamageEvent e) { + Player p = e.getPlayer(); + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { + return; } + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 15, context.level(), false, false, true)); + getPlayer(p).getData().addStat("excavation.haste.blocks-while-hasted", 1); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain Haste while excavating blocks.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - } + @NoArgsConstructor + @ConfigDescription("Gain Haste while excavating blocks.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index 0110c6245..9c88d6227 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -27,10 +27,10 @@ import art.arcane.adapt.content.item.multiItems.OmniTool; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -56,330 +56,330 @@ import java.util.Map; public class ExcavationOmniTool extends SimpleAdaptation { - private static final OmniTool omniTool = new OmniTool(); - - public ExcavationOmniTool() { - super("excavation-omnitool"); - registerConfiguration(ExcavationOmniTool.Config.class); - setDisplayName(Localizer.dLocalize("excavation.omni_tool.name")); - setDescription(Localizer.dLocalize("excavation.omni_tool.description")); - setIcon(Material.DISC_FRAGMENT_5); - setInterval(20202); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_excavation_omni_1k") - .title(Localizer.dLocalize("advancement.challenge_excavation_omni_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_omni_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_PICKAXE) - .key("challenge_excavation_omni_25k") - .title(Localizer.dLocalize("advancement.challenge_excavation_omni_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_omni_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_excavation_omni_1k", "excavation.omni-tool.auto-swaps", 1000, 400); - registerMilestone("challenge_excavation_omni_25k", "excavation.omni-tool.auto-swaps", 25000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore1")); - v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore2")); - v.addLore(C.GREEN + Localizer.dLocalize("excavation.omni_tool.lore3")); - v.addLore(C.RED + Localizer.dLocalize("excavation.omni_tool.lore4")); - v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore5")); - v.addLore(C.GREEN + "" + (level + getConfig().startingSlots) + C.GRAY + " " + Localizer.dLocalize("excavation.omni_tool.lore6")); - v.addLore(C.UNDERLINE + Localizer.dLocalize("excavation.omni_tool.lore7")); - - - } + private static final OmniTool omniTool = new OmniTool(); + + public ExcavationOmniTool() { + super("excavation-omnitool"); + registerConfiguration(ExcavationOmniTool.Config.class); + setDisplayName(Localizer.dLocalize("excavation.omni_tool.name")); + setDescription(Localizer.dLocalize("excavation.omni_tool.description")); + setIcon(Material.DISC_FRAGMENT_5); + setInterval(20202); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_excavation_omni_1k") + .title(Localizer.dLocalize("advancement.challenge_excavation_omni_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_omni_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_PICKAXE) + .key("challenge_excavation_omni_25k") + .title(Localizer.dLocalize("advancement.challenge_excavation_omni_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_omni_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_excavation_omni_1k", "excavation.omni-tool.auto-swaps", 1000, 400); + registerMilestone("challenge_excavation_omni_25k", "excavation.omni-tool.auto-swaps", 25000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore1")); + v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore2")); + v.addLore(C.GREEN + Localizer.dLocalize("excavation.omni_tool.lore3")); + v.addLore(C.RED + Localizer.dLocalize("excavation.omni_tool.lore4")); + v.addLore(C.GRAY + Localizer.dLocalize("excavation.omni_tool.lore5")); + v.addLore(C.GREEN + "" + (level + getConfig().startingSlots) + C.GRAY + " " + Localizer.dLocalize("excavation.omni_tool.lore6")); + v.addLore(C.UNDERLINE + Localizer.dLocalize("excavation.omni_tool.lore7")); + + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && validateTool(p.getInventory().getItemInMainHand())) { + //deny if the tool durability is about to break + if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { + e.setCancelled(true); + return; + } + if (!hasActiveAdaptation(p) && validateTool(p.getInventory().getItemInMainHand())) { + e.setCancelled(true); + return; + } + ItemStack hand = p.getInventory().getItemInMainHand(); + Damageable inHand = (Damageable) hand.getItemMeta(); + + if (!validateTool(hand)) { + return; + } + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + if (inHand != null && inHand.hasDamage()) { + if ((hand.getType().getMaxDurability() - inHand.getDamage() - 2) <= 2) { + e.setCancelled(true); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); + } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; } - - @Override - public void onTick() { + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (validateTool(p.getInventory().getItemInMainHand())) { + //deny if the tool durability is about to break + if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { + e.setCancelled(true); + return; + } + + + //deny if they dont have the adaptation + if (!hasActiveAdaptation(p)) { + e.setCancelled(true); + return; + } } - - @EventHandler(priority = EventPriority.HIGH) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && validateTool(p.getInventory().getItemInMainHand())) { - //deny if the tool durability is about to break - if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { - e.setCancelled(true); - return; + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (validateTool(p.getInventory().getItemInMainHand())) { + //deny if the tool durability is about to break + if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { + e.setCancelled(true); + return; + } + + if (!hasActiveAdaptation(p)) { + return; + } + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + ItemStack hand = p.getInventory().getItemInMainHand(); + Damageable imHand = (Damageable) hand.getItemMeta(); + Block block = e.getClickedBlock(); + if (block != null) { + SoundPlayer sp = SoundPlayer.of(p); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + if (ItemListings.farmable.contains(block.getType())) { + if (isShovel(hand)) { + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextHoe(hand))); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + } else { + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); } - if (!hasActiveAdaptation(p) && validateTool(p.getInventory().getItemInMainHand())) { + if (imHand != null && imHand.hasDamage()) { + if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { e.setCancelled(true); - return; - } - ItemStack hand = p.getInventory().getItemInMainHand(); - Damageable inHand = (Damageable) hand.getItemMeta(); - - if (!validateTool(hand)) { - return; + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); + } } - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); + } else if (ItemListings.burnable.contains(block.getType())) { + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextFnS(hand))); spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - if (inHand != null && inHand.hasDamage()) { - if ((hand.getType().getMaxDurability() - inHand.getDamage() - 2) <= 2) { - e.setCancelled(true); - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); - } + if (imHand != null && imHand.hasDamage()) { + if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { + e.setCancelled(true); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); + } } - + } } + } } - @EventHandler(priority = EventPriority.HIGH) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - if (validateTool(p.getInventory().getItemInMainHand())) { - //deny if the tool durability is about to break - if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { - e.setCancelled(true); - return; - } - + } - //deny if they dont have the adaptation - if (!hasActiveAdaptation(p)) { - e.setCancelled(true); - return; - } - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - - @EventHandler(priority = EventPriority.HIGH) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (validateTool(p.getInventory().getItemInMainHand())) { - //deny if the tool durability is about to break - if (p.getInventory().getItemInMainHand().getType().getMaxDurability() - p.getInventory().getItemInMainHand().getDurability() <= 2) { - e.setCancelled(true); - return; + if (p.isSneaking()) { + if (validateTool(e.getItemDrop().getItemStack())) { + List drops = omniTool.explode(e.getItemDrop().getItemStack()); + for (ItemStack i : drops) { + Damageable iDmgable = (Damageable) i.getItemMeta(); + if (i.hasItemMeta()) { + ItemMeta im = i.getItemMeta().clone(); + ItemMeta im2 = im; + if (im.hasDisplayName()) { + im2.setDisplayName(im.getDisplayName()); } - - if (!hasActiveAdaptation(p)) { - return; + if (im.hasEnchants()) { + Map enchants = im.getEnchants(); + for (Enchantment enchant : enchants.keySet()) { + im2.addEnchant(enchant, enchants.get(enchant), true); + } } - if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { - ItemStack hand = p.getInventory().getItemInMainHand(); - Damageable imHand = (Damageable) hand.getItemMeta(); - Block block = e.getClickedBlock(); - if (block != null) { - SoundPlayer sp = SoundPlayer.of(p); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (ItemListings.farmable.contains(block.getType())) { - if (isShovel(hand)) { - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextHoe(hand))); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - } else { - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - } - if (imHand != null && imHand.hasDamage()) { - if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { - e.setCancelled(true); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); - } - } - } else if (ItemListings.burnable.contains(block.getType())) { - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextFnS(hand))); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - if (imHand != null && imHand.hasDamage()) { - if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { - e.setCancelled(true); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); - } - } - } - } + if (iDmgable != null && iDmgable.hasDamage()) { + ((Damageable) im2).setDamage(iDmgable.getDamage()); } + im2.setLore(null); + i.setItemMeta(im2); + } + drops.set(drops.indexOf(i), i); } + J.runEntity(p, () -> { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); + for (ItemStack i : drops) { + p.getWorld().dropItem(p.getLocation(), i); + } + }); + e.getItemDrop().setItemStack(new ItemStack(Material.AIR)); + } } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerDropItemEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - if (p.isSneaking()) { - if (validateTool(e.getItemDrop().getItemStack())) { - List drops = omniTool.explode(e.getItemDrop().getItemStack()); - for (ItemStack i : drops) { - Damageable iDmgable = (Damageable) i.getItemMeta(); - if (i.hasItemMeta()) { - ItemMeta im = i.getItemMeta().clone(); - ItemMeta im2 = im; - if (im.hasDisplayName()) { - im2.setDisplayName(im.getDisplayName()); - } - if (im.hasEnchants()) { - Map enchants = im.getEnchants(); - for (Enchantment enchant : enchants.keySet()) { - im2.addEnchant(enchant, enchants.get(enchant), true); - } - } - if (iDmgable != null && iDmgable.hasDamage()) { - ((Damageable) im2).setDamage(iDmgable.getDamage()); - } - im2.setLore(null); - i.setItemMeta(im2); - } - drops.set(drops.indexOf(i), i); - } - - J.runEntity(p, () -> { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_DEATH, 0.25f, 0.77f); - for (ItemStack i : drops) { - p.getWorld().dropItem(p.getLocation(), i); - } - }); - e.getItemDrop().setItemStack(new ItemStack(Material.AIR)); - } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockDamageEvent e) { + Player p = e.getPlayer(); + org.bukkit.block.Block b = e.getBlock(); // nms block for pref tool + ItemStack hand = p.getInventory().getItemInMainHand(); + + if (validateTool(hand)) { + if (!hasActiveAdaptation(p)) { + return; + } + + Damageable imHand = (Damageable) hand.getItemMeta(); + if (ItemListings.getAxePreference().contains(b.getType())) { + if (!isAxe(hand)) { + Adapt.verbose("Omnitool for " + p.getName() + " changed to axe"); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextAxe(hand))); + itemDelegate(e, hand, imHand); + } else { + Adapt.verbose("Omnitool for " + p.getName() + " is already axe"); } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockDamageEvent e) { - Player p = e.getPlayer(); - org.bukkit.block.Block b = e.getBlock(); // nms block for pref tool - ItemStack hand = p.getInventory().getItemInMainHand(); - - if (validateTool(hand)) { - if (!hasActiveAdaptation(p)) { - return; - } - - Damageable imHand = (Damageable) hand.getItemMeta(); - if (ItemListings.getAxePreference().contains(b.getType())) { - if (!isAxe(hand)) { - Adapt.verbose("Omnitool for " + p.getName() + " changed to axe"); - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextAxe(hand))); - itemDelegate(e, hand, imHand); - } else { - Adapt.verbose("Omnitool for " + p.getName() + " is already axe"); - } - } else if (ItemListings.getShovelPreference().contains(b.getType())) { - if (!isShovel(hand)) { - Adapt.verbose("Omnitool for " + p.getName() + " changed to shovel"); - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); - itemDelegate(e, hand, imHand); - } else { - Adapt.verbose("Omnitool for " + p.getName() + " is already shovel"); - } - } else if (ItemListings.getSwordPreference().contains(b.getType())) { - if (!isSword(hand)) { - Adapt.verbose("Omnitool for " + p.getName() + " changed to sword"); - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); - itemDelegate(e, hand, imHand); - } else { - Adapt.verbose("Omnitool for " + p.getName() + " is already sword"); - } - } else { // Default to pickaxe - if (!isPickaxe(hand)) { - Adapt.verbose("Omnitool for " + p.getName() + " changed to pickaxe"); - J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextPickaxe(hand))); - itemDelegate(e, hand, imHand); - } - } - } - } - - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(InventoryClickEvent e) { - Player player = (Player) e.getWhoClicked(); - int level = getActiveLevel(player); - if (level <= 0) { - return; + } else if (ItemListings.getShovelPreference().contains(b.getType())) { + if (!isShovel(hand)) { + Adapt.verbose("Omnitool for " + p.getName() + " changed to shovel"); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextShovel(hand))); + itemDelegate(e, hand, imHand); + } else { + Adapt.verbose("Omnitool for " + p.getName() + " is already shovel"); } - if (e.getClickedInventory() != null && e.getClick().equals(ClickType.SHIFT_LEFT) && e.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { - ItemStack cursor = e.getWhoClicked().getItemOnCursor().clone(); - ItemStack clicked = e.getClickedInventory().getItem(e.getSlot()).clone(); - - if (omniTool.explode(cursor).size() > 1 || omniTool.explode(clicked).size() > 1) { - if (omniTool.explode(cursor).size() >= getSlots(level) || omniTool.explode(clicked).size() >= getSlots(level)) { - e.setCancelled(true); - SoundPlayer sp = SoundPlayer.of(player); - sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); - return; - } - } - if (ItemListings.tool.contains(cursor.getType()) && ItemListings.tool.contains(clicked.getType())) { // TOOLS ONLY - if (!cursor.getType().isAir() && !clicked.getType().isAir() && omniTool.supportsItem(cursor) && omniTool.supportsItem(clicked)) { - e.setCancelled(true); - e.getWhoClicked().setItemOnCursor(new ItemStack(Material.AIR)); - e.getClickedInventory().setItem(e.getSlot(), omniTool.build(cursor, clicked)); - SoundPlayer spw = SoundPlayer.of(e.getWhoClicked().getWorld()); - spw.play(e.getWhoClicked().getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - } - } + } else if (ItemListings.getSwordPreference().contains(b.getType())) { + if (!isSword(hand)) { + Adapt.verbose("Omnitool for " + p.getName() + " changed to sword"); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextSword(hand))); + itemDelegate(e, hand, imHand); + } else { + Adapt.verbose("Omnitool for " + p.getName() + " is already sword"); } - - } - - private void itemDelegate(BlockDamageEvent e, ItemStack hand, Damageable imHand) { - Player p = e.getPlayer(); - getPlayer(p).getData().addStat("excavation.omni-tool.auto-swaps", 1); - SoundPlayer sp = SoundPlayer.of(p); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); - if (imHand != null && imHand.hasDamage()) { - if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { - e.setCancelled(true); - sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); - } + } else { // Default to pickaxe + if (!isPickaxe(hand)) { + Adapt.verbose("Omnitool for " + p.getName() + " changed to pickaxe"); + J.runEntity(p, () -> p.getInventory().setItemInMainHand(omniTool.nextPickaxe(hand))); + itemDelegate(e, hand, imHand); } + } } + } - private boolean validateTool(ItemStack item) { - return (item.getItemMeta() != null - && item.getItemMeta().getLore() != null - && item.getItemMeta().getLore().toString().contains("Leatherman")); - } - private double getSlots(double level) { - return getConfig().startingSlots + level; + @EventHandler(priority = EventPriority.HIGHEST) + public void on(InventoryClickEvent e) { + Player player = (Player) e.getWhoClicked(); + int level = getActiveLevel(player); + if (level <= 0) { + return; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (e.getClickedInventory() != null && e.getClick().equals(ClickType.SHIFT_LEFT) && e.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY)) { + ItemStack cursor = e.getWhoClicked().getItemOnCursor().clone(); + ItemStack clicked = e.getClickedInventory().getItem(e.getSlot()).clone(); + + if (omniTool.explode(cursor).size() > 1 || omniTool.explode(clicked).size() > 1) { + if (omniTool.explode(cursor).size() >= getSlots(level) || omniTool.explode(clicked).size() >= getSlots(level)) { + e.setCancelled(true); + SoundPlayer sp = SoundPlayer.of(player); + sp.play(e.getWhoClicked().getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1f, 0.77f); + return; + } + } + if (ItemListings.tool.contains(cursor.getType()) && ItemListings.tool.contains(clicked.getType())) { // TOOLS ONLY + if (!cursor.getType().isAir() && !clicked.getType().isAir() && omniTool.supportsItem(cursor) && omniTool.supportsItem(clicked)) { + e.setCancelled(true); + e.getWhoClicked().setItemOnCursor(new ItemStack(Material.AIR)); + e.getClickedInventory().setItem(e.getSlot(), omniTool.build(cursor, clicked)); + SoundPlayer spw = SoundPlayer.of(e.getWhoClicked().getWorld()); + spw.play(e.getWhoClicked().getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + } + } } - @NoArgsConstructor - @ConfigDescription("Dynamically merge and swap tools on the fly based on what you are mining.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Excavation Omni Tool adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int startingSlots = 1; + } + + private void itemDelegate(BlockDamageEvent e, ItemStack hand, Damageable imHand) { + Player p = e.getPlayer(); + getPlayer(p).getData().addStat("excavation.omni-tool.auto-swaps", 1); + SoundPlayer sp = SoundPlayer.of(p); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_ELYTRA, 1f, 0.77f); + if (imHand != null && imHand.hasDamage()) { + if ((hand.getType().getMaxDurability() - imHand.getDamage() - 2) <= 2) { + e.setCancelled(true); + sp.play(p.getLocation(), Sound.ENTITY_IRON_GOLEM_STEP, 0.25f, 0.77f); + } } + } + + private boolean validateTool(ItemStack item) { + return (item.getItemMeta() != null + && item.getItemMeta().getLore() != null + && item.getItemMeta().getLore().toString().contains("Leatherman")); + } + + private double getSlots(double level) { + return getConfig().startingSlots + level; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Dynamically merge and swap tools on the fly based on what you are mining.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Starting Slots for the Excavation Omni Tool adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int startingSlots = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java index 9d9b72fa0..5635c839d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; @@ -44,245 +44,245 @@ import java.util.concurrent.ThreadLocalRandom; public class ExcavationSeismicPing extends SimpleAdaptation { - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - - public ExcavationSeismicPing() { - super("excavation-seismic-ping"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("excavation.seismic_ping.description")); - setDisplayName(Localizer.dLocalize("excavation.seismic_ping.name")); - setIcon(Material.GOAT_HORN); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BELL) - .key("challenge_excavation_seismic_200") - .title(Localizer.dLocalize("advancement.challenge_excavation_seismic_200.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_seismic_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_excavation_seismic_200", "excavation.seismic-ping.pings-triggered", 200, 400); + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + + public ExcavationSeismicPing() { + super("excavation-seismic-ping"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.seismic_ping.description")); + setDisplayName(Localizer.dLocalize("excavation.seismic_ping.name")); + setIcon(Material.GOAT_HORN); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BELL) + .key("challenge_excavation_seismic_200") + .title(Localizer.dLocalize("advancement.challenge_excavation_seismic_200.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_seismic_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_seismic_200", "excavation.seismic-ping.pings-triggered", 200, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getScanRange(level) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getPingChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (!isExcavationTool(p.getInventory().getItemInMainHand())) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getScanRange(level) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getPingChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.seismic_ping.lore3")); + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - cooldowns.remove(e.getPlayer().getUniqueId()); + int level = context.level(); + long now = System.currentTimeMillis(); + long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < nextReady) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - if (!isExcavationTool(p.getInventory().getItemInMainHand())) { - return; - } - - var context = resolveBlockBreakContext(p, e.getBlock().getLocation()); - if (context == null) { - return; - } - - int level = context.level(); - long now = System.currentTimeMillis(); - long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); - if (now < nextReady) { - return; - } - - cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); - if (ThreadLocalRandom.current().nextDouble() > getPingChance(level)) { - return; - } - - Block target = findNearestOre(e.getBlock().getLocation(), getScanRange(level)); - if (target == null) { - return; - } - - Location origin = p.getEyeLocation(); - Location targetCenter = target.getLocation().add(0.5, 0.5, 0.5); - Vector direction = targetCenter.toVector().subtract(origin.toVector()); - if (direction.lengthSquared() <= 0.0000001) { - return; - } - - renderDirectionHint(p, origin, direction.normalize(), getHintSegments(level)); - playPingSound(p, origin.distance(targetCenter), getScanRange(level)); - getPlayer(p).getData().addStat("excavation.seismic-ping.pings-triggered", 1); - xp(p, getConfig().xpPerPing + (getValue(target.getType()) * getConfig().targetValueXpMultiplier)); + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + if (ThreadLocalRandom.current().nextDouble() > getPingChance(level)) { + return; } - private void renderDirectionHint(Player p, Location origin, Vector direction, int segments) { - Particle.DustOptions dust = new Particle.DustOptions(Color.fromRGB(110, 230, 255), (float) getConfig().particleSize); - Location at = origin.clone(); - for (int i = 0; i < segments; i++) { - at = at.add(direction.clone().multiply(getConfig().segmentSpacing)); - if (areParticlesEnabled()) { - p.spawnParticle(Particle.DUST, at, Math.max(1, getConfig().segmentParticleCount), 0.05, 0.05, 0.05, 0, dust); - } - } - - if (areParticlesEnabled()) { - - p.spawnParticle(Particle.ELECTRIC_SPARK, at, Math.max(1, getConfig().tipParticleCount), 0.1, 0.1, 0.1, 0.04); + Block target = findNearestOre(e.getBlock().getLocation(), getScanRange(level)); + if (target == null) { + return; + } - } + Location origin = p.getEyeLocation(); + Location targetCenter = target.getLocation().add(0.5, 0.5, 0.5); + Vector direction = targetCenter.toVector().subtract(origin.toVector()); + if (direction.lengthSquared() <= 0.0000001) { + return; } - private void playPingSound(Player p, double distance, int range) { - double normalized = Math.min(1.0, distance / Math.max(1.0, range)); - float pitch = (float) Math.max(0.45, Math.min(1.95, 1.9 - (normalized * 1.1))); - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.9f, pitch); - sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BIT, 0.65f, (float) Math.min(2.0, pitch + 0.2)); + renderDirectionHint(p, origin, direction.normalize(), getHintSegments(level)); + playPingSound(p, origin.distance(targetCenter), getScanRange(level)); + getPlayer(p).getData().addStat("excavation.seismic-ping.pings-triggered", 1); + xp(p, getConfig().xpPerPing + (getValue(target.getType()) * getConfig().targetValueXpMultiplier)); + } + + private void renderDirectionHint(Player p, Location origin, Vector direction, int segments) { + Particle.DustOptions dust = new Particle.DustOptions(Color.fromRGB(110, 230, 255), (float) getConfig().particleSize); + Location at = origin.clone(); + for (int i = 0; i < segments; i++) { + at = at.add(direction.clone().multiply(getConfig().segmentSpacing)); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.DUST, at, Math.max(1, getConfig().segmentParticleCount), 0.05, 0.05, 0.05, 0, dust); + } } - private Block findNearestOre(Location origin, int range) { - World world = origin.getWorld(); - if (world == null) { - return null; - } + if (areParticlesEnabled()) { - int ox = origin.getBlockX(); - int oy = origin.getBlockY(); - int oz = origin.getBlockZ(); - int minY = world.getMinHeight(); - int maxY = world.getMaxHeight() - 1; - int rangeSq = range * range; - - Block best = null; - double bestDistanceSq = Double.MAX_VALUE; - for (int x = -range; x <= range; x++) { - int bx = ox + x; - for (int z = -range; z <= range; z++) { - int bz = oz + z; - if (!world.isChunkLoaded(bx >> 4, bz >> 4)) { - continue; - } - - for (int y = -range; y <= range; y++) { - int by = oy + y; - if (by < minY || by > maxY) { - continue; - } - - int d2 = (x * x) + (y * y) + (z * z); - if (d2 > rangeSq || d2 >= bestDistanceSq) { - continue; - } - - Block block = world.getBlockAt(bx, by, bz); - if (!isOre(block.getType())) { - continue; - } - - best = block; - bestDistanceSq = d2; - } - } - } + p.spawnParticle(Particle.ELECTRIC_SPARK, at, Math.max(1, getConfig().tipParticleCount), 0.1, 0.1, 0.1, 0.04); - return best; } - - private boolean isOre(Material type) { - return type == Material.ANCIENT_DEBRIS || type.name().endsWith("_ORE"); + } + + private void playPingSound(Player p, double distance, int range) { + double normalized = Math.min(1.0, distance / Math.max(1.0, range)); + float pitch = (float) Math.max(0.45, Math.min(1.95, 1.9 - (normalized * 1.1))); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.9f, pitch); + sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BIT, 0.65f, (float) Math.min(2.0, pitch + 0.2)); + } + + private Block findNearestOre(Location origin, int range) { + World world = origin.getWorld(); + if (world == null) { + return null; } - private boolean isExcavationTool(ItemStack item) { - if (!isItem(item)) { - return false; + int ox = origin.getBlockX(); + int oy = origin.getBlockY(); + int oz = origin.getBlockZ(); + int minY = world.getMinHeight(); + int maxY = world.getMaxHeight() - 1; + int rangeSq = range * range; + + Block best = null; + double bestDistanceSq = Double.MAX_VALUE; + for (int x = -range; x <= range; x++) { + int bx = ox + x; + for (int z = -range; z <= range; z++) { + int bz = oz + z; + if (!world.isChunkLoaded(bx >> 4, bz >> 4)) { + continue; } - String name = item.getType().name(); - return name.endsWith("_SHOVEL") || name.endsWith("_PICKAXE"); - } - - private int getScanRange(int level) { - return Math.max(6, (int) Math.round(getConfig().scanRangeBase + (getLevelPercent(level) * getConfig().scanRangeFactor))); - } + for (int y = -range; y <= range; y++) { + int by = oy + y; + if (by < minY || by > maxY) { + continue; + } - private double getPingChance(int level) { - return Math.min(getConfig().maxPingChance, getConfig().pingChanceBase + (getLevelPercent(level) * getConfig().pingChanceFactor)); - } + int d2 = (x * x) + (y * y) + (z * z); + if (d2 > rangeSq || d2 >= bestDistanceSq) { + continue; + } - private long getCooldownMillis(int level) { - return Math.max(350L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); - } + Block block = world.getBlockAt(bx, by, bz); + if (!isOre(block.getType())) { + continue; + } - private int getHintSegments(int level) { - return Math.max(4, (int) Math.round(getConfig().hintSegmentsBase + (getLevelPercent(level) * getConfig().hintSegmentsFactor))); + best = block; + bestDistanceSq = d2; + } + } } - @Override - public void onTick() { + return best; + } - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private boolean isOre(Material type) { + return type == Material.ANCIENT_DEBRIS || type.name().endsWith("_ORE"); + } - @Override - public boolean isPermanent() { - return getConfig().permanent; + private boolean isExcavationTool(ItemStack item) { + if (!isItem(item)) { + return false; } - @NoArgsConstructor - @ConfigDescription("Mining can emit seismic pings that hint toward nearby ore direction.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.78; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double scanRangeBase = 11; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double scanRangeFactor = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pingChanceBase = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pingChanceFactor = 0.37; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ping Chance for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxPingChance = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 2600; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 1850; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hintSegmentsBase = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hintSegmentsFactor = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Spacing for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double segmentSpacing = 0.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double particleSize = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int segmentParticleCount = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tip Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int tipParticleCount = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ping for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerPing = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Value Xp Multiplier for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double targetValueXpMultiplier = 0.5; - } + String name = item.getType().name(); + return name.endsWith("_SHOVEL") || name.endsWith("_PICKAXE"); + } + + private int getScanRange(int level) { + return Math.max(6, (int) Math.round(getConfig().scanRangeBase + (getLevelPercent(level) * getConfig().scanRangeFactor))); + } + + private double getPingChance(int level) { + return Math.min(getConfig().maxPingChance, getConfig().pingChanceBase + (getLevelPercent(level) * getConfig().pingChanceFactor)); + } + + private long getCooldownMillis(int level) { + return Math.max(350L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + private int getHintSegments(int level) { + return Math.max(4, (int) Math.round(getConfig().hintSegmentsBase + (getLevelPercent(level) * getConfig().hintSegmentsFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Mining can emit seismic pings that hint toward nearby ore direction.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRangeBase = 11; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRangeFactor = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pingChanceBase = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ping Chance Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pingChanceFactor = 0.37; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ping Chance for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxPingChance = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 2600; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 1850; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Base for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hintSegmentsBase = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hint Segments Factor for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hintSegmentsFactor = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Spacing for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double segmentSpacing = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double particleSize = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int segmentParticleCount = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tip Particle Count for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int tipParticleCount = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ping for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerPing = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Target Value Xp Multiplier for the Excavation Seismic Ping adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double targetValueXpMultiplier = 0.5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index ce3c291eb..6b7360bd8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -26,11 +26,11 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.inventorygui.Element; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -51,193 +51,193 @@ import java.util.UUID; public class ExcavationSpelunker extends SimpleAdaptation { - private final Map cooldowns; - - public ExcavationSpelunker() { - super("excavation-spelunker"); - registerConfiguration(ExcavationSpelunker.Config.class); - setDisplayName(Localizer.dLocalize("excavation.spelunker.name")); - setDescription(Localizer.dLocalize("excavation.spelunker.description")); - setIcon(Material.GOLDEN_HELMET); - setInterval(20388); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPYGLASS) - .key("challenge_excavation_spelunker_1k") - .title(Localizer.dLocalize("advancement.challenge_excavation_spelunker_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_spelunker_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_ORE) - .key("challenge_excavation_spelunker_25k") - .title(Localizer.dLocalize("advancement.challenge_excavation_spelunker_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavation_spelunker_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_excavation_spelunker_1k", "excavation.spelunker.ores-revealed", 1000, 400); - registerMilestone("challenge_excavation_spelunker_25k", "excavation.spelunker.ores-revealed", 25000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("excavation.spelunker.lore1")); - v.addLore(C.YELLOW + Localizer.dLocalize("excavation.spelunker.lore2") + getConfig().rangeMultiplier * level); - v.addLore(C.YELLOW + Localizer.dLocalize("excavation.spelunker.lore3")); - } - - @EventHandler(priority = EventPriority.HIGH) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - int level = getActiveLevel(p, Player::isSneaking); - // Check if player is sneaking, has Glowberries in main hand, and an ore in offhand - if (level > 0 && hasGlowberries(p) && hasOreInOffhand(p)) { - // Check if player is on cooldown - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown > System.currentTimeMillis()) { - sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); - return; - } - int radius = getConfig().rangeMultiplier * level; - consumeGlowberry(p); - searchForOres(p, radius); - cooldowns.put(p.getUniqueId(), (long) (System.currentTimeMillis() + (1000 * getConfig().cooldown))); - } + private final Map cooldowns; + + public ExcavationSpelunker() { + super("excavation-spelunker"); + registerConfiguration(ExcavationSpelunker.Config.class); + setDisplayName(Localizer.dLocalize("excavation.spelunker.name")); + setDescription(Localizer.dLocalize("excavation.spelunker.description")); + setIcon(Material.GOLDEN_HELMET); + setInterval(20388); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPYGLASS) + .key("challenge_excavation_spelunker_1k") + .title(Localizer.dLocalize("advancement.challenge_excavation_spelunker_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_spelunker_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_ORE) + .key("challenge_excavation_spelunker_25k") + .title(Localizer.dLocalize("advancement.challenge_excavation_spelunker_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_spelunker_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_excavation_spelunker_1k", "excavation.spelunker.ores-revealed", 1000, 400); + registerMilestone("challenge_excavation_spelunker_25k", "excavation.spelunker.ores-revealed", 25000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("excavation.spelunker.lore1")); + v.addLore(C.YELLOW + Localizer.dLocalize("excavation.spelunker.lore2") + getConfig().rangeMultiplier * level); + v.addLore(C.YELLOW + Localizer.dLocalize("excavation.spelunker.lore3")); + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + int level = getActiveLevel(p, Player::isSneaking); + // Check if player is sneaking, has Glowberries in main hand, and an ore in offhand + if (level > 0 && hasGlowberries(p) && hasOreInOffhand(p)) { + // Check if player is on cooldown + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown > System.currentTimeMillis()) { + sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); + return; + } + int radius = getConfig().rangeMultiplier * level; + consumeGlowberry(p); + searchForOres(p, radius); + cooldowns.put(p.getUniqueId(), (long) (System.currentTimeMillis() + (1000 * getConfig().cooldown))); } - - private boolean hasGlowberries(Player player) { - return player.getInventory().getItemInMainHand().getType() == Material.GLOW_BERRIES; - } - - private void consumeGlowberry(Player player) { - ItemStack berries = player.getInventory().getItemInMainHand(); - berries.setAmount(berries.getAmount() - 1); - player.getInventory().setItemInMainHand(berries); - } - - private boolean hasOreInOffhand(Player player) { - Material offhandType = player.getInventory().getItemInOffHand().getType(); - return ItemListings.ores.contains(offhandType); - } - - private void searchForOres(Player p, int radius) { - Location playerLocation = p.getLocation(); - World world = p.getWorld(); - Material targetOre = p.getInventory().getItemInOffHand().getType(); - ChatColor c = ItemListings.oreColorsChatColor.get(targetOre); - Particle.DustOptions dustOptions = new Particle.DustOptions(Color.WHITE, 1); - for (int x = -radius; x <= radius; x++) { - for (int y = -radius; y <= radius; y++) { - for (int z = -radius; z <= radius; z++) { - if (x * x + y * y + z * z <= radius * radius) { - Location blockLocation = playerLocation.clone().add(x, y, z); - Block block = world.getBlockAt(blockLocation); - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - - if (block.getType() == targetOre) { - getPlayer(p).getData().addStat("excavation.spelunker.ores-revealed", 1); - // Raytrace particles from player to the found ore - Vector vector = blockLocation.clone().subtract(playerLocation).toVector().normalize().multiply(0.5); - Location particleLocation = playerLocation.clone(); - - while (particleLocation.distance(blockLocation) > 0.5) { - particleLocation.add(vector); - if (areParticlesEnabled()) { - p.spawnParticle(Particles.REDSTONE, particleLocation, 1, dustOptions); - } - } - - SoundPlayer spw = SoundPlayer.of(world); - spw.play(block.getLocation().add(0.5, 0, 0.5), Sound.BLOCK_BEACON_ACTIVATE, 1, 1); - Slime slime = block.getWorld().spawn(block.getLocation().add(0.5, 0, 0.5), Slime.class, (s) -> { - s.setRotation(0, 0); - s.setInvulnerable(true); - s.setCollidable(false); - s.setGravity(false); - s.setSilent(true); - s.setAI(false); - s.setSize(2); - s.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false)); - s.setMetadata("preventSuffocation", new FixedMetadataValue(Adapt.instance, true)); - }); - - try { - glowingEntities.setGlowing(slime, p, c); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - J.runEntity(slime, () -> { - try { - glowingEntities.unsetGlowing(slime, p); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - - slime.remove(); - }, 5 * 20); - } - } + } + + private boolean hasGlowberries(Player player) { + return player.getInventory().getItemInMainHand().getType() == Material.GLOW_BERRIES; + } + + private void consumeGlowberry(Player player) { + ItemStack berries = player.getInventory().getItemInMainHand(); + berries.setAmount(berries.getAmount() - 1); + player.getInventory().setItemInMainHand(berries); + } + + private boolean hasOreInOffhand(Player player) { + Material offhandType = player.getInventory().getItemInOffHand().getType(); + return ItemListings.ores.contains(offhandType); + } + + private void searchForOres(Player p, int radius) { + Location playerLocation = p.getLocation(); + World world = p.getWorld(); + Material targetOre = p.getInventory().getItemInOffHand().getType(); + ChatColor c = ItemListings.oreColorsChatColor.get(targetOre); + Particle.DustOptions dustOptions = new Particle.DustOptions(Color.WHITE, 1); + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + if (x * x + y * y + z * z <= radius * radius) { + Location blockLocation = playerLocation.clone().add(x, y, z); + Block block = world.getBlockAt(blockLocation); + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + + if (block.getType() == targetOre) { + getPlayer(p).getData().addStat("excavation.spelunker.ores-revealed", 1); + // Raytrace particles from player to the found ore + Vector vector = blockLocation.clone().subtract(playerLocation).toVector().normalize().multiply(0.5); + Location particleLocation = playerLocation.clone(); + + while (particleLocation.distance(blockLocation) > 0.5) { + particleLocation.add(vector); + if (areParticlesEnabled()) { + p.spawnParticle(Particles.REDSTONE, particleLocation, 1, dustOptions); + } + } + + SoundPlayer spw = SoundPlayer.of(world); + spw.play(block.getLocation().add(0.5, 0, 0.5), Sound.BLOCK_BEACON_ACTIVATE, 1, 1); + Slime slime = block.getWorld().spawn(block.getLocation().add(0.5, 0, 0.5), Slime.class, (s) -> { + s.setRotation(0, 0); + s.setInvulnerable(true); + s.setCollidable(false); + s.setGravity(false); + s.setSilent(true); + s.setAI(false); + s.setSize(2); + s.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false)); + s.setMetadata("preventSuffocation", new FixedMetadataValue(Adapt.instance, true)); + }); + + try { + glowingEntities.setGlowing(slime, p, c); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + + J.runEntity(slime, () -> { + try { + glowingEntities.unsetGlowing(slime, p); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); } - } - } - } - - @EventHandler - public void onEntityDamage(EntityDamageEvent e) { - if (e.getEntity() instanceof Slime && e.getCause() == EntityDamageEvent.DamageCause.SUFFOCATION) { - Slime slime = (Slime) e.getEntity(); - if (slime.hasMetadata("preventSuffocation")) { - e.setCancelled(true); - } else { - e.setCancelled(true); slime.remove(); + }, 5 * 20); } + } } + } } - - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - - @Override - public void onTick() { - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("See ores through the ground using Glowberries in your main hand.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldown = 6.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Multiplier for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rangeMultiplier = 5; + } + + + @EventHandler + public void onEntityDamage(EntityDamageEvent e) { + if (e.getEntity() instanceof Slime && e.getCause() == EntityDamageEvent.DamageCause.SUFFOCATION) { + Slime slime = (Slime) e.getEntity(); + if (slime.hasMetadata("preventSuffocation")) { + e.setCancelled(true); + } else { + e.setCancelled(true); + slime.remove(); + } } + } + + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("See ores through the ground using Glowberries in your main hand.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldown = 6.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Multiplier for the Excavation Spelunker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rangeMultiplier = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java index 1de3fa155..75a88551a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismBeeShepherd.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -45,228 +45,228 @@ import java.util.concurrent.ThreadLocalRandom; public class HerbalismBeeShepherd extends SimpleAdaptation { - private final Map lastPulse = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastPulse = new java.util.concurrent.ConcurrentHashMap<>(); - public HerbalismBeeShepherd() { - super("herbalism-bee-shepherd"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.bee_shepherd.description")); - setDisplayName(Localizer.dLocalize("herbalism.bee_shepherd.name")); - setIcon(Material.BEE_NEST); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(10); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.HONEYCOMB) - .key("challenge_herbalism_bee_100") - .title(Localizer.dLocalize("advancement.challenge_herbalism_bee_100.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_bee_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_bee_100", "herbalism.bee-shepherd.bees-attracted", 100, 300); - } + public HerbalismBeeShepherd() { + super("herbalism-bee-shepherd"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.bee_shepherd.description")); + setDisplayName(Localizer.dLocalize("herbalism.bee_shepherd.name")); + setIcon(Material.BEE_NEST); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(10); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.HONEYCOMB) + .key("challenge_herbalism_bee_100") + .title(Localizer.dLocalize("advancement.challenge_herbalism_bee_100.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_bee_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_bee_100", "herbalism.bee-shepherd.bees-attracted", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore1")); - v.addLore(C.GREEN + "+ " + getGrowthAttempts(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getPulseMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore3")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore1")); + v.addLore(C.GREEN + "+ " + getGrowthAttempts(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getPulseMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.bee_shepherd.lore3")); + } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (!hasActiveAdaptation(p) || !isHoldingFlower(p)) { - continue; - } + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (!hasActiveAdaptation(p) || !isHoldingFlower(p)) { + continue; + } - int level = getActiveLevel(p); - if (now - lastPulse.getOrDefault(p.getUniqueId(), 0L) < getPulseMillis(level)) { - continue; - } + int level = getActiveLevel(p); + if (now - lastPulse.getOrDefault(p.getUniqueId(), 0L) < getPulseMillis(level)) { + continue; + } - int foodCost = getFoodCost(level); - if (p.getFoodLevel() < foodCost) { - continue; - } + int foodCost = getFoodCost(level); + if (p.getFoodLevel() < foodCost) { + continue; + } - int grown = pulseGrowth(p, level); - int attracted = pullNearbyBees(p, level); - lastPulse.put(p.getUniqueId(), now); - if (attracted > 0) { - getPlayer(p).getData().addStat("herbalism.bee-shepherd.bees-attracted", attracted); - } - if (grown <= 0) { - continue; - } + int grown = pulseGrowth(p, level); + int attracted = pullNearbyBees(p, level); + lastPulse.put(p.getUniqueId(), now); + if (attracted > 0) { + getPlayer(p).getData().addStat("herbalism.bee-shepherd.bees-attracted", attracted); + } + if (grown <= 0) { + continue; + } - p.setFoodLevel(Math.max(0, p.getFoodLevel() - foodCost)); - if (areParticlesEnabled()) { - p.spawnParticle(Particle.HAPPY_VILLAGER, p.getLocation().add(0, 1, 0), 12, 0.5, 0.4, 0.5, 0.1); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_BEE_POLLINATE, 0.85f, 1.25f); - xp(p, grown * getConfig().xpPerGrowth); - } + p.setFoodLevel(Math.max(0, p.getFoodLevel() - foodCost)); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.HAPPY_VILLAGER, p.getLocation().add(0, 1, 0), 12, 0.5, 0.4, 0.5, 0.1); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_BEE_POLLINATE, 0.85f, 1.25f); + xp(p, grown * getConfig().xpPerGrowth); } + } - private int pulseGrowth(Player p, int level) { - ThreadLocalRandom random = ThreadLocalRandom.current(); - int radius = Math.max(1, (int) Math.round(getRadius(level))); - int grown = 0; - int attempts = getGrowthAttempts(level); - for (int i = 0; i < attempts; i++) { - int dx = random.nextInt(-radius, radius + 1); - int dz = random.nextInt(-radius, radius + 1); - int dy = random.nextInt(-1, 2); - Block block = p.getLocation().getBlock().getRelative(dx, dy, dz); - if (!(block.getBlockData() instanceof Ageable ageable) || ageable.getAge() >= ageable.getMaximumAge()) { - continue; - } + private int pulseGrowth(Player p, int level) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + int radius = Math.max(1, (int) Math.round(getRadius(level))); + int grown = 0; + int attempts = getGrowthAttempts(level); + for (int i = 0; i < attempts; i++) { + int dx = random.nextInt(-radius, radius + 1); + int dz = random.nextInt(-radius, radius + 1); + int dy = random.nextInt(-1, 2); + Block block = p.getLocation().getBlock().getRelative(dx, dy, dz); + if (!(block.getBlockData() instanceof Ageable ageable) || ageable.getAge() >= ageable.getMaximumAge()) { + continue; + } - int increase = Math.max(1, getGrowthStep(level)); - ageable.setAge(Math.min(ageable.getMaximumAge(), ageable.getAge() + increase)); - block.setBlockData(ageable, true); - grown++; - if (getConfig().showGrowthParticles) { - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.COMPOSTER, block.getLocation().add(0.5, 0.5, 0.5), 3, 0.1, 0.1, 0.1, 0.01); - } - } + int increase = Math.max(1, getGrowthStep(level)); + ageable.setAge(Math.min(ageable.getMaximumAge(), ageable.getAge() + increase)); + block.setBlockData(ageable, true); + grown++; + if (getConfig().showGrowthParticles) { + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.COMPOSTER, block.getLocation().add(0.5, 0.5, 0.5), 3, 0.1, 0.1, 0.1, 0.01); } - - return grown; + } } - private int pullNearbyBees(Player p, int level) { - double radius = getRadius(level); - int count = 0; - for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { - if (!(entity instanceof Bee bee)) { - continue; - } + return grown; + } - Vector toward = p.getLocation().add(0, 0.75, 0).toVector().subtract(bee.getLocation().toVector()); - if (toward.lengthSquared() <= 0.001) { - continue; - } + private int pullNearbyBees(Player p, int level) { + double radius = getRadius(level); + int count = 0; + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { + if (!(entity instanceof Bee bee)) { + continue; + } - toward.normalize().multiply(getBeePullStrength(level)); - bee.setVelocity(bee.getVelocity().multiply(0.6).add(toward)); - bee.setTarget(null); - count++; - } - return count; - } + Vector toward = p.getLocation().add(0, 0.75, 0).toVector().subtract(bee.getLocation().toVector()); + if (toward.lengthSquared() <= 0.001) { + continue; + } - private boolean isHoldingFlower(Player p) { - return isFlower(p.getInventory().getItemInMainHand()) || isFlower(p.getInventory().getItemInOffHand()); + toward.normalize().multiply(getBeePullStrength(level)); + bee.setVelocity(bee.getVelocity().multiply(0.6).add(toward)); + bee.setTarget(null); + count++; } + return count; + } - private boolean isFlower(ItemStack item) { - if (!isItem(item)) { - return false; - } + private boolean isHoldingFlower(Player p) { + return isFlower(p.getInventory().getItemInMainHand()) || isFlower(p.getInventory().getItemInOffHand()); + } - Material type = item.getType(); - return type.name().endsWith("_TULIP") - || type == Material.DANDELION - || type == Material.POPPY - || type == Material.BLUE_ORCHID - || type == Material.ALLIUM - || type == Material.AZURE_BLUET - || type == Material.OXEYE_DAISY - || type == Material.CORNFLOWER - || type == Material.LILY_OF_THE_VALLEY - || type == Material.WITHER_ROSE - || type == Material.SUNFLOWER - || type == Material.LILAC - || type == Material.ROSE_BUSH - || type == Material.PEONY - || type == Material.TORCHFLOWER - || type == Material.PINK_PETALS; + private boolean isFlower(ItemStack item) { + if (!isItem(item)) { + return false; } - private double getRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); - } + Material type = item.getType(); + return type.name().endsWith("_TULIP") + || type == Material.DANDELION + || type == Material.POPPY + || type == Material.BLUE_ORCHID + || type == Material.ALLIUM + || type == Material.AZURE_BLUET + || type == Material.OXEYE_DAISY + || type == Material.CORNFLOWER + || type == Material.LILY_OF_THE_VALLEY + || type == Material.WITHER_ROSE + || type == Material.SUNFLOWER + || type == Material.LILAC + || type == Material.ROSE_BUSH + || type == Material.PEONY + || type == Material.TORCHFLOWER + || type == Material.PINK_PETALS; + } - private int getGrowthAttempts(int level) { - return Math.max(1, (int) Math.round(getConfig().growthAttemptsBase + (getLevelPercent(level) * getConfig().growthAttemptsFactor))); - } + private double getRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } - private int getGrowthStep(int level) { - return Math.max(1, (int) Math.round(getConfig().growthStepBase + (getLevelPercent(level) * getConfig().growthStepFactor))); - } + private int getGrowthAttempts(int level) { + return Math.max(1, (int) Math.round(getConfig().growthAttemptsBase + (getLevelPercent(level) * getConfig().growthAttemptsFactor))); + } - private int getFoodCost(int level) { - return Math.max(1, (int) Math.round(getConfig().foodCostBase - (getLevelPercent(level) * getConfig().foodCostFactor))); - } + private int getGrowthStep(int level) { + return Math.max(1, (int) Math.round(getConfig().growthStepBase + (getLevelPercent(level) * getConfig().growthStepFactor))); + } - private long getPulseMillis(int level) { - return Math.max(250L, (long) Math.round(getConfig().pulseMillisBase - (getLevelPercent(level) * getConfig().pulseMillisFactor))); - } + private int getFoodCost(int level) { + return Math.max(1, (int) Math.round(getConfig().foodCostBase - (getLevelPercent(level) * getConfig().foodCostFactor))); + } - private double getBeePullStrength(int level) { - return Math.max(0.01, getConfig().beePullStrengthBase + (getLevelPercent(level) * getConfig().beePullStrengthFactor)); - } + private long getPulseMillis(int level) { + return Math.max(250L, (long) Math.round(getConfig().pulseMillisBase - (getLevelPercent(level) * getConfig().pulseMillisFactor))); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private double getBeePullStrength(int level) { + return Math.max(0.01, getConfig().beePullStrengthBase + (getLevelPercent(level) * getConfig().beePullStrengthFactor)); + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Holding flowers near crops emits growth pulses and draws nearby bees toward you.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Growth Particles for the Herbalism Bee Shepherd adaptation.", impact = "True enables this behavior and false disables it.") - boolean showGrowthParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.64; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthAttemptsBase = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthAttemptsFactor = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthStepBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double growthStepFactor = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodCostBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodCostFactor = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pulseMillisBase = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pulseMillisFactor = 650; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double beePullStrengthBase = 0.07; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double beePullStrengthFactor = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerGrowth = 0.9; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Holding flowers near crops emits growth pulses and draws nearby bees toward you.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Growth Particles for the Herbalism Bee Shepherd adaptation.", impact = "True enables this behavior and false disables it.") + boolean showGrowthParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthAttemptsBase = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Attempts Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthAttemptsFactor = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthStepBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Growth Step Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double growthStepFactor = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodCostBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodCostFactor = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pulseMillisBase = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Millis Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pulseMillisFactor = 650; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Base for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double beePullStrengthBase = 0.07; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bee Pull Strength Factor for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double beePullStrengthFactor = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Growth for the Herbalism Bee Shepherd adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerGrowth = 0.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index 5710cec18..2a4314a99 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -51,497 +51,497 @@ import java.util.concurrent.ThreadLocalRandom; public class HerbalismCompostCascade extends SimpleAdaptation { - public HerbalismCompostCascade() { - super("herbalism-compost-cascade"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.compost_cascade.description")); - setDisplayName(Localizer.dLocalize("herbalism.compost_cascade.name")); - setIcon(Material.COMPOSTER); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(600); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COMPOSTER) - .key("challenge_herbalism_compost_1k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_compost_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_compost_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BONE_MEAL) - .key("challenge_herbalism_compost_25k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_compost_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_compost_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_compost_1k", "herbalism.compost-cascade.items-composted", 1000, 300); - registerMilestone("challenge_herbalism_compost_25k", "herbalism.compost-cascade.items-composted", 25000, 1000); + public HerbalismCompostCascade() { + super("herbalism-compost-cascade"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.compost_cascade.description")); + setDisplayName(Localizer.dLocalize("herbalism.compost_cascade.name")); + setIcon(Material.COMPOSTER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(600); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COMPOSTER) + .key("challenge_herbalism_compost_1k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_compost_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_compost_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BONE_MEAL) + .key("challenge_herbalism_compost_25k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_compost_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_compost_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_compost_1k", "herbalism.compost-cascade.items-composted", 1000, 300); + registerMilestone("challenge_herbalism_compost_25k", "herbalism.compost-cascade.items-composted", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore1")); + v.addLore(C.GREEN + "+ " + getMaxItems(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getFillChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore3")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore4")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore1")); - v.addLore(C.GREEN + "+ " + getMaxItems(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore2")); - v.addLore(C.GREEN + "+ " + Form.pc(getFillChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore3")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.compost_cascade.lore4")); + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.COMPOSTER || p.hasCooldown(Material.COMPOSTER)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { - return; - } - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.COMPOSTER || p.hasCooldown(Material.COMPOSTER)) { - return; - } - - if (!(e.getClickedBlock().getBlockData() instanceof Levelled levelled)) { - return; - } - - int oldLevel = levelled.getLevel(); - if (oldLevel >= 8) { - return; - } - - int level = getActiveLevel(p); - double fillChance = getFillChance(level); - int maxItems = getMaxItems(level); - double radius = getRadius(level); - Location center = e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5); - World world = center.getWorld(); - if (world == null) { - return; - } - - CompostState state = new CompostState(oldLevel); - - processDroppedItems(world, center, radius, state, maxItems, fillChance); - processMatureCrops(p, world, center, radius, state, maxItems, fillChance); - processLeafBlocks(p, world, center, radius, level, state, maxItems, fillChance); - processInventoryItems(p, state, maxItems, fillChance); - - if (state.consumed <= 0) { - return; - } + if (!(e.getClickedBlock().getBlockData() instanceof Levelled levelled)) { + return; + } - Levelled updated = (Levelled) e.getClickedBlock().getBlockData(); - updated.setLevel(Math.min(8, Math.max(oldLevel, state.compostLevel))); - e.getClickedBlock().setBlockData(updated); + int oldLevel = levelled.getLevel(); + if (oldLevel >= 8) { + return; + } - p.setCooldown(Material.COMPOSTER, getCooldownTicks(level)); - e.setCancelled(true); + int level = getActiveLevel(p); + double fillChance = getFillChance(level); + int maxItems = getMaxItems(level); + double radius = getRadius(level); + Location center = e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5); + World world = center.getWorld(); + if (world == null) { + return; + } - getPlayer(p).getData().addStat("harvest.composted", state.consumed); - getPlayer(p).getData().addStat("herbalism.compost-cascade.items-composted", state.consumed); - xp(p, center, (state.consumed * getConfig().xpPerItemConsumed) + (state.levelGains * getConfig().xpPerLevelGain)); + CompostState state = new CompostState(oldLevel); - SoundPlayer sp = SoundPlayer.of(world); - sp.play(center, Sound.BLOCK_COMPOSTER_FILL, 0.8f, 1.25f); - if (updated.getLevel() >= 8) { - sp.play(center, Sound.BLOCK_COMPOSTER_READY, 1.0f, 1.12f); - } + processDroppedItems(world, center, radius, state, maxItems, fillChance); + processMatureCrops(p, world, center, radius, state, maxItems, fillChance); + processLeafBlocks(p, world, center, radius, level, state, maxItems, fillChance); + processInventoryItems(p, state, maxItems, fillChance); - dropRewards(world, center, level, oldLevel, updated.getLevel(), state.consumed); + if (state.consumed <= 0) { + return; } - private void processDroppedItems(World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { - if (isComposterDone(state, maxItems)) { - return; - } + Levelled updated = (Levelled) e.getClickedBlock().getBlockData(); + updated.setLevel(Math.min(8, Math.max(oldLevel, state.compostLevel))); + e.getClickedBlock().setBlockData(updated); - for (Entity entity : world.getNearbyEntities(center, radius, radius, radius)) { - if (!(entity instanceof Item item) || isComposterDone(state, maxItems)) { - continue; - } + p.setCooldown(Material.COMPOSTER, getCooldownTicks(level)); + e.setCancelled(true); - ItemStack stack = item.getItemStack(); - if (!isItem(stack) || !isCompostable(stack.getType())) { - continue; - } + getPlayer(p).getData().addStat("harvest.composted", state.consumed); + getPlayer(p).getData().addStat("herbalism.compost-cascade.items-composted", state.consumed); + xp(p, center, (state.consumed * getConfig().xpPerItemConsumed) + (state.levelGains * getConfig().xpPerLevelGain)); - compostStack(stack, state, maxItems, fillChance); - if (stack.getAmount() <= 0) { - item.remove(); - } else { - item.setItemStack(stack); - } - } + SoundPlayer sp = SoundPlayer.of(world); + sp.play(center, Sound.BLOCK_COMPOSTER_FILL, 0.8f, 1.25f); + if (updated.getLevel() >= 8) { + sp.play(center, Sound.BLOCK_COMPOSTER_READY, 1.0f, 1.12f); } - private void processMatureCrops(Player p, World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { - if (isComposterDone(state, maxItems)) { - return; - } + dropRewards(world, center, level, oldLevel, updated.getLevel(), state.consumed); + } - int r = Math.max(1, (int) Math.ceil(radius)); - double rs = radius * radius; - for (int x = -r; x <= r; x++) { - for (int y = -r; y <= r; y++) { - for (int z = -r; z <= r; z++) { - if (isComposterDone(state, maxItems)) { - return; - } - - if ((x * x) + (y * y) + (z * z) > rs) { - continue; - } - - Block b = world.getBlockAt(center.getBlockX() + x, center.getBlockY() + y, center.getBlockZ() + z); - if (!isMatureCrop(b)) { - continue; - } - - if (!canBlockBreak(p, b.getLocation()) || !canBlockPlace(p, b.getLocation())) { - continue; - } - - ItemStack[] drops = b.getDrops().toArray(ItemStack[]::new); - if (!replantCrop(b)) { - continue; - } - - for (ItemStack drop : drops) { - if (!isItem(drop)) { - continue; - } - - if (isCompostable(drop.getType()) && !isComposterDone(state, maxItems)) { - compostStack(drop, state, maxItems, fillChance); - } - - if (drop.getAmount() > 0) { - world.dropItemNaturally(b.getLocation().add(0.5, 0.5, 0.5), drop); - } - } - } - } - } + private void processDroppedItems(World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { + if (isComposterDone(state, maxItems)) { + return; } - private void processLeafBlocks(Player p, World world, Location center, double radius, int level, CompostState state, int maxItems, double fillChance) { - if (isComposterDone(state, maxItems)) { - return; - } + for (Entity entity : world.getNearbyEntities(center, radius, radius, radius)) { + if (!(entity instanceof Item item) || isComposterDone(state, maxItems)) { + continue; + } + + ItemStack stack = item.getItemStack(); + if (!isItem(stack) || !isCompostable(stack.getType())) { + continue; + } + + compostStack(stack, state, maxItems, fillChance); + if (stack.getAmount() <= 0) { + item.remove(); + } else { + item.setItemStack(stack); + } + } + } - int r = Math.max(1, (int) Math.ceil(radius)); - double rs = radius * radius; - int bursts = getLeafCompostBursts(level); - for (int x = -r; x <= r; x++) { - for (int y = -r; y <= r; y++) { - for (int z = -r; z <= r; z++) { - if (isComposterDone(state, maxItems)) { - return; - } - - if ((x * x) + (y * y) + (z * z) > rs) { - continue; - } - - Block b = world.getBlockAt(center.getBlockX() + x, center.getBlockY() + y, center.getBlockZ() + z); - if (!isLeafBlock(b.getType()) || !canBlockBreak(p, b.getLocation())) { - continue; - } - - b.setType(Material.AIR, false); - ItemStack leafMass = new ItemStack(Material.OAK_LEAVES, bursts); - compostStack(leafMass, state, maxItems, getLeafFillChance(level, fillChance)); - } - } - } + private void processMatureCrops(Player p, World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { + if (isComposterDone(state, maxItems)) { + return; } - private void processInventoryItems(Player p, CompostState state, int maxItems, double fillChance) { - if (isComposterDone(state, maxItems)) { + int r = Math.max(1, (int) Math.ceil(radius)); + double rs = radius * radius; + for (int x = -r; x <= r; x++) { + for (int y = -r; y <= r; y++) { + for (int z = -r; z <= r; z++) { + if (isComposterDone(state, maxItems)) { return; - } + } - ItemStack[] storage = p.getInventory().getStorageContents(); - boolean changed = false; - for (int i = 0; i < storage.length; i++) { - if (isComposterDone(state, maxItems)) { - break; - } - - ItemStack stack = storage[i]; - if (!isItem(stack) || !isCompostable(stack.getType())) { - continue; - } + if ((x * x) + (y * y) + (z * z) > rs) { + continue; + } - compostStack(stack, state, maxItems, fillChance); - changed = true; - if (stack.getAmount() <= 0) { - storage[i] = null; - } - } + Block b = world.getBlockAt(center.getBlockX() + x, center.getBlockY() + y, center.getBlockZ() + z); + if (!isMatureCrop(b)) { + continue; + } - if (changed) { - p.getInventory().setStorageContents(storage); - } - } + if (!canBlockBreak(p, b.getLocation()) || !canBlockPlace(p, b.getLocation())) { + continue; + } - private void compostStack(ItemStack stack, CompostState state, int maxItems, double fillChance) { - while (stack.getAmount() > 0 && !isComposterDone(state, maxItems)) { - stack.setAmount(stack.getAmount() - 1); - state.processed++; - state.consumed++; + ItemStack[] drops = b.getDrops().toArray(ItemStack[]::new); + if (!replantCrop(b)) { + continue; + } - if (ThreadLocalRandom.current().nextDouble() <= fillChance) { - state.compostLevel++; - state.levelGains++; + for (ItemStack drop : drops) { + if (!isItem(drop)) { + continue; } - } - } - - private void dropRewards(World world, Location center, int level, int oldLevel, int newLevel, int consumed) { - int boneMeal = getBaseBoneMeal(level) + Math.max(0, consumed / getItemsPerBoneMeal(level)); - if (newLevel >= 8 && oldLevel < 8) { - boneMeal += getReadyBonusBoneMeal(level); - } - - if (boneMeal > 0) { - world.dropItemNaturally(center, new ItemStack(Material.BONE_MEAL, Math.min(64, boneMeal))); - } - if (newLevel < 8) { - return; - } + if (isCompostable(drop.getType()) && !isComposterDone(state, maxItems)) { + compostStack(drop, state, maxItems, fillChance); + } - int rolls = getValuableRolls(level); - for (int i = 0; i < rolls; i++) { - if (ThreadLocalRandom.current().nextDouble() <= getValuableChance(level)) { - world.dropItemNaturally(center, rollValuableReward(level)); + if (drop.getAmount() > 0) { + world.dropItemNaturally(b.getLocation().add(0.5, 0.5, 0.5), drop); } + } } + } } + } - private ItemStack rollValuableReward(int level) { - double lp = getLevelPercent(level); - double r = ThreadLocalRandom.current().nextDouble(); - - if (r < 0.45) { - return new ItemStack(Material.MOSS_BLOCK, 1 + ThreadLocalRandom.current().nextInt(1 + Math.max(1, (int) Math.round(lp * 3)))); - } - - if (r < 0.7) { - return new ItemStack(Material.GLOW_BERRIES, 2 + ThreadLocalRandom.current().nextInt(2 + Math.max(1, (int) Math.round(lp * 4)))); - } - - if (r < 0.88) { - return new ItemStack(Material.AMETHYST_SHARD, 1 + ThreadLocalRandom.current().nextInt(1 + Math.max(1, (int) Math.round(lp * 4)))); - } - - if (r < 0.97) { - return new ItemStack(Material.EMERALD, 1); - } - - return new ItemStack(Material.DIAMOND, 1); + private void processLeafBlocks(Player p, World world, Location center, double radius, int level, CompostState state, int maxItems, double fillChance) { + if (isComposterDone(state, maxItems)) { + return; } - private boolean isMatureCrop(Block b) { - BlockData data = b.getBlockData(); - if (!(data instanceof Ageable ageable)) { - return false; - } + int r = Math.max(1, (int) Math.ceil(radius)); + double rs = radius * radius; + int bursts = getLeafCompostBursts(level); + for (int x = -r; x <= r; x++) { + for (int y = -r; y <= r; y++) { + for (int z = -r; z <= r; z++) { + if (isComposterDone(state, maxItems)) { + return; + } - Material type = b.getType(); - if (type == Material.CHORUS_PLANT || type == Material.SUGAR_CANE || type == Material.BAMBOO) { - return false; - } + if ((x * x) + (y * y) + (z * z) > rs) { + continue; + } - return ageable.getAge() >= ageable.getMaximumAge(); - } + Block b = world.getBlockAt(center.getBlockX() + x, center.getBlockY() + y, center.getBlockZ() + z); + if (!isLeafBlock(b.getType()) || !canBlockBreak(p, b.getLocation())) { + continue; + } - private boolean replantCrop(Block b) { - BlockData data = b.getBlockData(); - if (!(data instanceof Ageable ageable)) { - return false; + b.setType(Material.AIR, false); + ItemStack leafMass = new ItemStack(Material.OAK_LEAVES, bursts); + compostStack(leafMass, state, maxItems, getLeafFillChance(level, fillChance)); } - - ageable.setAge(0); - b.setBlockData(ageable, true); - return true; + } } + } - private boolean isLeafBlock(Material type) { - return type.name().endsWith("_LEAVES"); + private void processInventoryItems(Player p, CompostState state, int maxItems, double fillChance) { + if (isComposterDone(state, maxItems)) { + return; } - private boolean isComposterDone(CompostState state, int maxItems) { - return state.compostLevel >= 8 || state.processed >= maxItems; + ItemStack[] storage = p.getInventory().getStorageContents(); + boolean changed = false; + for (int i = 0; i < storage.length; i++) { + if (isComposterDone(state, maxItems)) { + break; + } + + ItemStack stack = storage[i]; + if (!isItem(stack) || !isCompostable(stack.getType())) { + continue; + } + + compostStack(stack, state, maxItems, fillChance); + changed = true; + if (stack.getAmount() <= 0) { + storage[i] = null; + } } - private boolean isCompostable(Material type) { - String n = type.name().toUpperCase(Locale.ROOT); - return n.contains("SEEDS") - || n.contains("SAPLING") - || n.contains("LEAVES") - || n.contains("FLOWER") - || n.contains("MUSHROOM") - || n.contains("ROOTS") - || n.contains("VINE") - || n.contains("KELP") - || n.contains("DRIPLEAF") - || n.contains("MOSS") - || type == Material.WHEAT - || type == Material.BEETROOT - || type == Material.CARROT - || type == Material.POTATO - || type == Material.POISONOUS_POTATO - || type == Material.NETHER_WART - || type == Material.CACTUS - || type == Material.SUGAR_CANE - || type == Material.BAMBOO - || type == Material.SHORT_GRASS - || type == Material.TALL_GRASS - || type == Material.SEA_PICKLE; + if (changed) { + p.getInventory().setStorageContents(storage); } - - private int getMaxItems(int level) { - return Math.max(1, (int) Math.round(getConfig().maxItemsBase + (getLevelPercent(level) * getConfig().maxItemsFactor))); + } + + private void compostStack(ItemStack stack, CompostState state, int maxItems, double fillChance) { + while (stack.getAmount() > 0 && !isComposterDone(state, maxItems)) { + stack.setAmount(stack.getAmount() - 1); + state.processed++; + state.consumed++; + + if (ThreadLocalRandom.current().nextDouble() <= fillChance) { + state.compostLevel++; + state.levelGains++; + } } + } - private double getRadius(int level) { - return Math.max(1, getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor)); + private void dropRewards(World world, Location center, int level, int oldLevel, int newLevel, int consumed) { + int boneMeal = getBaseBoneMeal(level) + Math.max(0, consumed / getItemsPerBoneMeal(level)); + if (newLevel >= 8 && oldLevel < 8) { + boneMeal += getReadyBonusBoneMeal(level); } - private double getFillChance(int level) { - return Math.min(getConfig().maxFillChance, getConfig().fillChanceBase + (getLevelPercent(level) * getConfig().fillChanceFactor)); + if (boneMeal > 0) { + world.dropItemNaturally(center, new ItemStack(Material.BONE_MEAL, Math.min(64, boneMeal))); } - private double getLeafFillChance(int level, double baseFillChance) { - return Math.min(1.0, baseFillChance * (getConfig().leafFillChanceMultiplierBase + (getLevelPercent(level) * getConfig().leafFillChanceMultiplierFactor))); + if (newLevel < 8) { + return; } - private int getLeafCompostBursts(int level) { - return Math.max(1, (int) Math.round(getConfig().leafCompostBurstsBase + (getLevelPercent(level) * getConfig().leafCompostBurstsFactor))); + int rolls = getValuableRolls(level); + for (int i = 0; i < rolls; i++) { + if (ThreadLocalRandom.current().nextDouble() <= getValuableChance(level)) { + world.dropItemNaturally(center, rollValuableReward(level)); + } } + } - private int getCooldownTicks(int level) { - return Math.max(4, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksReduction))); - } + private ItemStack rollValuableReward(int level) { + double lp = getLevelPercent(level); + double r = ThreadLocalRandom.current().nextDouble(); - private int getBaseBoneMeal(int level) { - return Math.max(0, (int) Math.round(getConfig().boneMealBase + (getLevelPercent(level) * getConfig().boneMealFactor))); + if (r < 0.45) { + return new ItemStack(Material.MOSS_BLOCK, 1 + ThreadLocalRandom.current().nextInt(1 + Math.max(1, (int) Math.round(lp * 3)))); } - private int getReadyBonusBoneMeal(int level) { - return Math.max(0, (int) Math.round(getConfig().readyBonusBoneMealBase + (getLevelPercent(level) * getConfig().readyBonusBoneMealFactor))); + if (r < 0.7) { + return new ItemStack(Material.GLOW_BERRIES, 2 + ThreadLocalRandom.current().nextInt(2 + Math.max(1, (int) Math.round(lp * 4)))); } - private int getItemsPerBoneMeal(int level) { - return Math.max(1, (int) Math.round(getConfig().itemsPerBoneMealBase - (getLevelPercent(level) * getConfig().itemsPerBoneMealReduction))); + if (r < 0.88) { + return new ItemStack(Material.AMETHYST_SHARD, 1 + ThreadLocalRandom.current().nextInt(1 + Math.max(1, (int) Math.round(lp * 4)))); } - private double getValuableChance(int level) { - return Math.min(getConfig().maxValuableChance, getConfig().valuableChanceBase + (getLevelPercent(level) * getConfig().valuableChanceFactor)); + if (r < 0.97) { + return new ItemStack(Material.EMERALD, 1); } - private int getValuableRolls(int level) { - return Math.max(0, (int) Math.round(getConfig().valuableRollsBase + (getLevelPercent(level) * getConfig().valuableRollsFactor))); - } - - @Override - public void onTick() { + return new ItemStack(Material.DIAMOND, 1); + } + private boolean isMatureCrop(Block b) { + BlockData data = b.getBlockData(); + if (!(data instanceof Ageable ageable)) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + Material type = b.getType(); + if (type == Material.CHORUS_PLANT || type == Material.SUGAR_CANE || type == Material.BAMBOO) { + return false; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + return ageable.getAge() >= ageable.getMaximumAge(); + } - @NoArgsConstructor - @ConfigDescription("Sneak-right-click a composter to process nearby drops, crops, leaves, and your own compostables.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 5.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 12.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxItemsBase = 80.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxItemsFactor = 240.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fillChanceBase = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fillChanceFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fill Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxFillChance = 0.98; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double leafCompostBurstsBase = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double leafCompostBurstsFactor = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double leafFillChanceMultiplierBase = 1.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double leafFillChanceMultiplierFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 36.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksReduction = 28.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double boneMealBase = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double boneMealFactor = 6.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double readyBonusBoneMealBase = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double readyBonusBoneMealFactor = 8.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double itemsPerBoneMealBase = 20.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double itemsPerBoneMealReduction = 14.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valuableChanceBase = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valuableChanceFactor = 0.09; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Valuable Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxValuableChance = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valuableRollsBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valuableRollsFactor = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Item Consumed for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerItemConsumed = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level Gain for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerLevelGain = 2.8; + private boolean replantCrop(Block b) { + BlockData data = b.getBlockData(); + if (!(data instanceof Ageable ageable)) { + return false; } - private static class CompostState { - private int compostLevel; - private int processed; - private int consumed; - private int levelGains; - - private CompostState(int compostLevel) { - this.compostLevel = compostLevel; - this.processed = 0; - this.consumed = 0; - this.levelGains = 0; - } + ageable.setAge(0); + b.setBlockData(ageable, true); + return true; + } + + private boolean isLeafBlock(Material type) { + return type.name().endsWith("_LEAVES"); + } + + private boolean isComposterDone(CompostState state, int maxItems) { + return state.compostLevel >= 8 || state.processed >= maxItems; + } + + private boolean isCompostable(Material type) { + String n = type.name().toUpperCase(Locale.ROOT); + return n.contains("SEEDS") + || n.contains("SAPLING") + || n.contains("LEAVES") + || n.contains("FLOWER") + || n.contains("MUSHROOM") + || n.contains("ROOTS") + || n.contains("VINE") + || n.contains("KELP") + || n.contains("DRIPLEAF") + || n.contains("MOSS") + || type == Material.WHEAT + || type == Material.BEETROOT + || type == Material.CARROT + || type == Material.POTATO + || type == Material.POISONOUS_POTATO + || type == Material.NETHER_WART + || type == Material.CACTUS + || type == Material.SUGAR_CANE + || type == Material.BAMBOO + || type == Material.SHORT_GRASS + || type == Material.TALL_GRASS + || type == Material.SEA_PICKLE; + } + + private int getMaxItems(int level) { + return Math.max(1, (int) Math.round(getConfig().maxItemsBase + (getLevelPercent(level) * getConfig().maxItemsFactor))); + } + + private double getRadius(int level) { + return Math.max(1, getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor)); + } + + private double getFillChance(int level) { + return Math.min(getConfig().maxFillChance, getConfig().fillChanceBase + (getLevelPercent(level) * getConfig().fillChanceFactor)); + } + + private double getLeafFillChance(int level, double baseFillChance) { + return Math.min(1.0, baseFillChance * (getConfig().leafFillChanceMultiplierBase + (getLevelPercent(level) * getConfig().leafFillChanceMultiplierFactor))); + } + + private int getLeafCompostBursts(int level) { + return Math.max(1, (int) Math.round(getConfig().leafCompostBurstsBase + (getLevelPercent(level) * getConfig().leafCompostBurstsFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(4, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksReduction))); + } + + private int getBaseBoneMeal(int level) { + return Math.max(0, (int) Math.round(getConfig().boneMealBase + (getLevelPercent(level) * getConfig().boneMealFactor))); + } + + private int getReadyBonusBoneMeal(int level) { + return Math.max(0, (int) Math.round(getConfig().readyBonusBoneMealBase + (getLevelPercent(level) * getConfig().readyBonusBoneMealFactor))); + } + + private int getItemsPerBoneMeal(int level) { + return Math.max(1, (int) Math.round(getConfig().itemsPerBoneMealBase - (getLevelPercent(level) * getConfig().itemsPerBoneMealReduction))); + } + + private double getValuableChance(int level) { + return Math.min(getConfig().maxValuableChance, getConfig().valuableChanceBase + (getLevelPercent(level) * getConfig().valuableChanceFactor)); + } + + private int getValuableRolls(int level) { + return Math.max(0, (int) Math.round(getConfig().valuableRollsBase + (getLevelPercent(level) * getConfig().valuableRollsFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click a composter to process nearby drops, crops, leaves, and your own compostables.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 5.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 12.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxItemsBase = 80.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxItemsFactor = 240.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fillChanceBase = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fill Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fillChanceFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fill Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxFillChance = 0.98; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double leafCompostBurstsBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Compost Bursts Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double leafCompostBurstsFactor = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double leafFillChanceMultiplierBase = 1.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaf Fill Chance Multiplier Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double leafFillChanceMultiplierFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 36.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksReduction = 28.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double boneMealBase = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double boneMealFactor = 6.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double readyBonusBoneMealBase = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ready Bonus Bone Meal Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double readyBonusBoneMealFactor = 8.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double itemsPerBoneMealBase = 20.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Items Per Bone Meal Reduction for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double itemsPerBoneMealReduction = 14.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valuableChanceBase = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Chance Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valuableChanceFactor = 0.09; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Valuable Chance for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxValuableChance = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Base for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valuableRollsBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Valuable Rolls Factor for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valuableRollsFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Item Consumed for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerItemConsumed = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Level Gain for the Herbalism Compost Cascade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerLevelGain = 2.8; + } + + private static class CompostState { + private int compostLevel; + private int processed; + private int consumed; + private int levelGains; + + private CompostState(int compostLevel) { + this.compostLevel = compostLevel; + this.processed = 0; + this.consumed = 0; + this.levelGains = 0; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java index d81515b36..1241349cb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableCobweb.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -40,81 +40,81 @@ public class HerbalismCraftableCobweb extends SimpleAdaptation { - public HerbalismCraftableCobweb() { - super("herbalism-cobweb"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.cobweb.description")); - setDisplayName(Localizer.dLocalize("herbalism.cobweb.name")); - setIcon(Material.STRING); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17771); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("herbalism-cobwebBlock") - .ingredient(new MaterialChar('I', Material.STRING)) - .shapes(List.of( - "III", - "III", - "III")) - .result(new ItemStack(Material.COBWEB, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COBWEB) - .key("challenge_herbalism_cobweb_100") - .title(Localizer.dLocalize("advancement.challenge_herbalism_cobweb_100.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_cobweb_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_cobweb_100", "herbalism.cobweb.cobwebs-crafted", 100, 300); - } + public HerbalismCraftableCobweb() { + super("herbalism-cobweb"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.cobweb.description")); + setDisplayName(Localizer.dLocalize("herbalism.cobweb.name")); + setIcon(Material.STRING); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17771); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("herbalism-cobwebBlock") + .ingredient(new MaterialChar('I', Material.STRING)) + .shapes(List.of( + "III", + "III", + "III")) + .result(new ItemStack(Material.COBWEB, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COBWEB) + .key("challenge_herbalism_cobweb_100") + .title(Localizer.dLocalize("advancement.challenge_herbalism_cobweb_100.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_cobweb_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_cobweb_100", "herbalism.cobweb.cobwebs-crafted", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.cobweb.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.cobweb.lore1")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { - return; - } - if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-cobwebBlock")) { - getPlayer(p).getData().addStat("herbalism.cobweb.cobwebs-crafted", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { + return; } - - @Override - public void onTick() { + if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-cobwebBlock")) { + getPlayer(p).getData().addStat("herbalism.cobweb.cobwebs-crafted", 1); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Craft Cobwebs from String in a Crafting Table.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft Cobwebs from String in a Crafting Table.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java index e2c8865d8..ca7ddb480 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCraftableMushroomBlocks.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -40,98 +40,98 @@ public class HerbalismCraftableMushroomBlocks extends SimpleAdaptation { - public HerbalismCraftableMushroomBlocks() { - super("herbalism-mushroom-blocks"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.mushroom_blocks.description")); - setDisplayName(Localizer.dLocalize("herbalism.mushroom_blocks.name")); - setIcon(Material.BROWN_MUSHROOM_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17772); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("herbalism-redmushblock") - .ingredient(new MaterialChar('I', Material.RED_MUSHROOM)) - .shapes(List.of( - "II", - "II")) - .result(new ItemStack(Material.RED_MUSHROOM_BLOCK, 1)) - .build()); - registerRecipe(AdaptRecipe.shaped() - .key("herbalism-brownmushblock") - .ingredient(new MaterialChar('I', Material.BROWN_MUSHROOM)) - .shapes(List.of( - "II", - "II")) - .result(new ItemStack(Material.BROWN_MUSHROOM_BLOCK, 1)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("herbalism-mushstemred") - .ingredient(Material.RED_MUSHROOM_BLOCK) - .result(new ItemStack(Material.MUSHROOM_STEM, 1)) - .build()); - registerRecipe(AdaptRecipe.shapeless() - .key("herbalism-mushstembrown") - .ingredient(Material.BROWN_MUSHROOM_BLOCK) - .result(new ItemStack(Material.MUSHROOM_STEM, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RED_MUSHROOM_BLOCK) - .key("challenge_herbalism_mushroom_100") - .title(Localizer.dLocalize("advancement.challenge_herbalism_mushroom_100.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_mushroom_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_mushroom_100", "herbalism.mushroom-blocks.crafted", 100, 300); - } + public HerbalismCraftableMushroomBlocks() { + super("herbalism-mushroom-blocks"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.mushroom_blocks.description")); + setDisplayName(Localizer.dLocalize("herbalism.mushroom_blocks.name")); + setIcon(Material.BROWN_MUSHROOM_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17772); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("herbalism-redmushblock") + .ingredient(new MaterialChar('I', Material.RED_MUSHROOM)) + .shapes(List.of( + "II", + "II")) + .result(new ItemStack(Material.RED_MUSHROOM_BLOCK, 1)) + .build()); + registerRecipe(AdaptRecipe.shaped() + .key("herbalism-brownmushblock") + .ingredient(new MaterialChar('I', Material.BROWN_MUSHROOM)) + .shapes(List.of( + "II", + "II")) + .result(new ItemStack(Material.BROWN_MUSHROOM_BLOCK, 1)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("herbalism-mushstemred") + .ingredient(Material.RED_MUSHROOM_BLOCK) + .result(new ItemStack(Material.MUSHROOM_STEM, 1)) + .build()); + registerRecipe(AdaptRecipe.shapeless() + .key("herbalism-mushstembrown") + .ingredient(Material.BROWN_MUSHROOM_BLOCK) + .result(new ItemStack(Material.MUSHROOM_STEM, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RED_MUSHROOM_BLOCK) + .key("challenge_herbalism_mushroom_100") + .title(Localizer.dLocalize("advancement.challenge_herbalism_mushroom_100.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_mushroom_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_mushroom_100", "herbalism.mushroom-blocks.crafted", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.mushroom_blocks.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.mushroom_blocks.lore1")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { - return; - } - if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && (recipe.getKey().getKey().equals("herbalism-redmushblock") || recipe.getKey().getKey().equals("herbalism-brownmushblock"))) { - getPlayer(p).getData().addStat("herbalism.mushroom-blocks.crafted", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { + return; } - - @Override - public void onTick() { + if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && (recipe.getKey().getKey().equals("herbalism-redmushblock") || recipe.getKey().getKey().equals("herbalism-brownmushblock"))) { + getPlayer(p).getData().addStat("herbalism.mushroom-blocks.crafted", 1); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Craft Mushroom Blocks from Mushrooms in a Crafting Table.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft Mushroom Blocks from Mushrooms in a Crafting Table.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java index 121899f05..cf015ba29 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismDropToInventory.java @@ -25,10 +25,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -42,85 +42,85 @@ import java.util.List; public class HerbalismDropToInventory extends SimpleAdaptation { - public HerbalismDropToInventory() { - super("herbalism-drop-to-inventory"); - registerConfiguration(HerbalismDropToInventory.Config.class); - setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); - setDisplayName(Localizer.dLocalize("herbalism.drop_to_inventory.name")); - setIcon(Material.HOPPER); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(7999); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_herbalism_dti_10k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_dti_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_dti_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_dti_10k", "herbalism.drop-to-inv.items-caught", 10000, 500); - } + public HerbalismDropToInventory() { + super("herbalism-drop-to-inventory"); + registerConfiguration(HerbalismDropToInventory.Config.class); + setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); + setDisplayName(Localizer.dLocalize("herbalism.drop_to_inventory.name")); + setIcon(Material.HOPPER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(7999); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_herbalism_dti_10k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_dti_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_dti_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_dti_10k", "herbalism.drop-to-inv.items-caught", 10000, 500); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); - } + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasActiveAdaptation(p)) { - return; - } - if (p.getGameMode() != GameMode.SURVIVAL) { - return; - } - if (ItemListings.toolHoes.contains(p.getInventory().getItemInMainHand().getType())) { - List items = new KList<>(e.getItems()); - e.getItems().clear(); - for (Item i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - xp(p, 2); - getPlayer(p).getData().addStat("herbalism.drop-to-inv.items-caught", 1); - if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i.getItemStack()); - } - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDropItemEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + return; + } + if (p.getGameMode() != GameMode.SURVIVAL) { + return; + } + if (ItemListings.toolHoes.contains(p.getInventory().getItemInMainHand().getType())) { + List items = new KList<>(e.getItems()); + e.getItems().clear(); + for (Item i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + xp(p, 2); + getPlayer(p).getData().addStat("herbalism.drop-to-inv.items-caught", 1); + if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i.getItemStack()); } + } } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Harvested crops drop directly into your inventory.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Harvested crops drop directly into your inventory.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java index 805efaecd..373a9d31a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismGrowthAura.java @@ -25,12 +25,12 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; @@ -45,156 +45,156 @@ import java.util.concurrent.ThreadLocalRandom; public class HerbalismGrowthAura extends SimpleAdaptation { - public HerbalismGrowthAura() { - super("herbalism-growth-aura"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.growth_aura.description")); - setDisplayName(Localizer.dLocalize("herbalism.growth_aura.name")); - setIcon(Material.BONE_MEAL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(850); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHEAT) - .key("challenge_herbalism_growth_1k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_growth_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_growth_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HAY_BLOCK) - .key("challenge_herbalism_growth_25k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_growth_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_growth_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_growth_1k", "herbalism.growth-aura.blocks-grown", 1000, 300); - registerMilestone("challenge_herbalism_growth_25k", "herbalism.growth-aura.blocks-grown", 25000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getStrength(level), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore2")); - v.addLore(C.YELLOW + "+ " + Form.f(getFoodCost(getLevelPercent(level)), 2) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore3")); - } - - private double getRadius(double factor) { - return factor * getConfig().radiusFactor; - } - - private double getStrength(int level) { - return level * getConfig().strengthFactor; - } - - private double getFoodCost(double factor) { - return M.lerp(1D - factor, getConfig().maxFoodCost, getConfig().minFoodCost); - } - + public HerbalismGrowthAura() { + super("herbalism-growth-aura"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.growth_aura.description")); + setDisplayName(Localizer.dLocalize("herbalism.growth_aura.name")); + setIcon(Material.BONE_MEAL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(850); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHEAT) + .key("challenge_herbalism_growth_1k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_growth_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_growth_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HAY_BLOCK) + .key("challenge_herbalism_growth_25k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_growth_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_growth_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_growth_1k", "herbalism.growth-aura.blocks-grown", 1000, 300); + registerMilestone("challenge_herbalism_growth_25k", "herbalism.growth-aura.blocks-grown", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getStrength(level), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore2")); + v.addLore(C.YELLOW + "+ " + Form.f(getFoodCost(getLevelPercent(level)), 2) + C.GRAY + " " + Localizer.dLocalize("herbalism.growth_aura.lore3")); + } + + private double getRadius(double factor) { + return factor * getConfig().radiusFactor; + } + + private double getStrength(int level) { + return level * getConfig().strengthFactor; + } + + private double getFoodCost(double factor) { + return M.lerp(1D - factor, getConfig().maxFoodCost, getConfig().minFoodCost); + } + + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + try { + if (hasActiveAdaptation(p)) { + double rad = getRadius(getLevelPercent(p)); + double strength = getStrength(getLevel(p)); + ThreadLocalRandom random = ThreadLocalRandom.current(); + double angle = Math.toRadians(random.nextDouble(360D)); + double foodCost = getFoodCost(getLevelPercent(p)); + + + for (int i = 0; i < Math.min(Math.min(rad * rad, 256), 3); i++) { + Location m = p.getLocation().clone().add(new Vector(Math.sin(angle), RNG.r.i(-1, 1), Math.cos(angle)).multiply(random.nextDouble(rad))); + Block a = m.getBlock(); + if (getConfig().surfaceOnly) { + int max = a.getWorld().getHighestBlockYAt(m); + + if (max + 1 != a.getY()) + continue; + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - try { - if (hasActiveAdaptation(p)) { - double rad = getRadius(getLevelPercent(p)); - double strength = getStrength(getLevel(p)); - ThreadLocalRandom random = ThreadLocalRandom.current(); - double angle = Math.toRadians(random.nextDouble(360D)); - double foodCost = getFoodCost(getLevelPercent(p)); - - - for (int i = 0; i < Math.min(Math.min(rad * rad, 256), 3); i++) { - Location m = p.getLocation().clone().add(new Vector(Math.sin(angle), RNG.r.i(-1, 1), Math.cos(angle)).multiply(random.nextDouble(rad))); - Block a = m.getBlock(); - if (getConfig().surfaceOnly) { - int max = a.getWorld().getHighestBlockYAt(m); - - if (max + 1 != a.getY()) - continue; - } - - SoundPlayer spw = SoundPlayer.of(a.getWorld()); - if (a.getBlockData() instanceof Ageable) { - Ageable ab = (Ageable) a.getBlockData(); - int toGrowLeft = ab.getMaximumAge() - ab.getAge(); - - if (toGrowLeft > 0) { - int add = (int) Math.max(1, Math.min(strength, toGrowLeft)); - AdaptPlayer player = getPlayer(p); - if (ab.getMaximumAge() > ab.getAge() && player.canConsumeFood(foodCost, 10)) { - while (add-- > 0) { - J.runEntity(p, () -> { - if (!p.isOnline() - || !player.consumeFood(foodCost, 10) - || !(a.getBlockData() instanceof Ageable aab) - || aab.getAge() == aab.getMaximumAge()) - return; - - aab.setAge(aab.getAge() + 1); - a.setBlockData(aab, true); - getPlayer(p).getData().addStat("herbalism.growth-aura.blocks-grown", 1); - spw.play(a.getLocation(), Sound.BLOCK_CHORUS_FLOWER_DEATH, 0.25f, RNG.r.f(0.3f, 0.7f)); - if (areParticlesEnabled()) { - p.spawnParticle(Particles.VILLAGER_HAPPY, a.getLocation().clone().add(0.5, 0.5, 0.5), 3, 0.3, 0.3, 0.3, 0.9); - } + SoundPlayer spw = SoundPlayer.of(a.getWorld()); + if (a.getBlockData() instanceof Ageable) { + Ageable ab = (Ageable) a.getBlockData(); + int toGrowLeft = ab.getMaximumAge() - ab.getAge(); + + if (toGrowLeft > 0) { + int add = (int) Math.max(1, Math.min(strength, toGrowLeft)); + AdaptPlayer player = getPlayer(p); + if (ab.getMaximumAge() > ab.getAge() && player.canConsumeFood(foodCost, 10)) { + while (add-- > 0) { + J.runEntity(p, () -> { + if (!p.isOnline() + || !player.consumeFood(foodCost, 10) + || !(a.getBlockData() instanceof Ageable aab) + || aab.getAge() == aab.getMaximumAge()) + return; + + aab.setAge(aab.getAge() + 1); + a.setBlockData(aab, true); + getPlayer(p).getData().addStat("herbalism.growth-aura.blocks-grown", 1); + spw.play(a.getLocation(), Sound.BLOCK_CHORUS_FLOWER_DEATH, 0.25f, RNG.r.f(0.3f, 0.7f)); + if (areParticlesEnabled()) { + p.spawnParticle(Particles.VILLAGER_HAPPY, a.getLocation().clone().add(0.5, 0.5, 0.5), 3, 0.3, 0.3, 0.3, 0.9); + } // xp(p, 1); // JESUS THIS IS FUCKING BUSTED - }, RNG.r.i(30, 60)); - } - } - } + }, RNG.r.i(30, 60)); + } + } + } - } - } - } - } catch (Throwable e) { - e.printStackTrace(); } + } } + } catch (Throwable e) { + e.printStackTrace(); + } } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Grow nature around you in an aura at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Surface Only for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") - boolean surfaceOnly = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.325; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minFoodCost = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxFoodCost = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double strengthFactor = 0.75; - } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Grow nature around you in an aura at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Surface Only for the Herbalism Growth Aura adaptation.", impact = "True enables this behavior and false disables it.") + boolean surfaceOnly = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.325; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minFoodCost = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Food Cost for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxFoodCost = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strength Factor for the Herbalism Growth Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double strengthFactor = 0.75; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java index 16756a090..461b37804 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryHippo.java @@ -25,9 +25,9 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -39,79 +39,79 @@ public class HerbalismHungryHippo extends SimpleAdaptation { - public HerbalismHungryHippo() { - super("herbalism-hippo"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.hippo.description")); - setDisplayName(Localizer.dLocalize("herbalism.hippo.name")); - setIcon(Material.POTATO); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(8111); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_herbalism_hippo_500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_hippo_500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_hippo_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_hippo_500", "herbalism.hungry-hippo.bonus-saturation", 500, 400); - } + public HerbalismHungryHippo() { + super("herbalism-hippo"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.hippo.description")); + setDisplayName(Localizer.dLocalize("herbalism.hippo.name")); + setIcon(Material.POTATO); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(8111); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_herbalism_hippo_500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_hippo_500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_hippo_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_hippo_500", "herbalism.hungry-hippo.bonus-saturation", 500, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ (" + (2 + level) + C.GRAY + " + " + Localizer.dLocalize("herbalism.hippo.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ (" + (2 + level) + C.GRAY + " + " + Localizer.dLocalize("herbalism.hippo.lore1")); + } - @EventHandler(priority = EventPriority.NORMAL) - public void on(PlayerItemConsumeEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasActiveAdaptation(p)) { - return; - } - if (ItemListings.getFood().contains(e.getItem().getType())) { - p.setFoodLevel(p.getFoodLevel() + 2 + getLevel(p)); - sp.play(p.getLocation(), Sound.BLOCK_POINTED_DRIPSTONE_LAND, 1, 0.25f); - vfxFastRing(p.getLocation().add(0, 0.25, 0), 2, Color.GREEN); - getPlayer(p).getData().addStat("herbalism.hungry-hippo.bonus-saturation", 2 + getLevel(p)); - xp(p, 5); - } + @EventHandler(priority = EventPriority.NORMAL) + public void on(PlayerItemConsumeEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + return; + } + if (ItemListings.getFood().contains(e.getItem().getType())) { + p.setFoodLevel(p.getFoodLevel() + 2 + getLevel(p)); + sp.play(p.getLocation(), Sound.BLOCK_POINTED_DRIPSTONE_LAND, 1, 0.25f); + vfxFastRing(p.getLocation().add(0, 0.25, 0), 2, Color.GREEN); + getPlayer(p).getData().addStat("herbalism.hungry-hippo.bonus-saturation", 2 + getLevel(p)); + xp(p, 5); } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Consuming food gives you more saturation.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - } + @NoArgsConstructor + @ConfigDescription("Consuming food gives you more saturation.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java index e20fb114a..edb40533a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismHungryShield.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -36,97 +36,97 @@ public class HerbalismHungryShield extends SimpleAdaptation { - public HerbalismHungryShield() { - super("herbalism-hungry-shield"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.hungry_shield.description")); - setDisplayName(Localizer.dLocalize("herbalism.hungry_shield.name")); - setIcon(Material.APPLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(875); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BREAD) - .key("challenge_herbalism_shield_500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_shield_500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_shield_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_herbalism_shield_5k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_shield_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_shield_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_shield_500", "herbalism.hungry-shield.damage-absorbed", 500, 400); - registerMilestone("challenge_herbalism_shield_5k", "herbalism.hungry-shield.damage-absorbed", 5000, 1500); - } + public HerbalismHungryShield() { + super("herbalism-hungry-shield"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.hungry_shield.description")); + setDisplayName(Localizer.dLocalize("herbalism.hungry_shield.name")); + setIcon(Material.APPLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(875); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BREAD) + .key("challenge_herbalism_shield_500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_shield_500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_shield_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_herbalism_shield_5k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_shield_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_shield_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_shield_500", "herbalism.hungry-shield.damage-absorbed", 500, 400); + registerMilestone("challenge_herbalism_shield_5k", "herbalism.hungry-shield.damage-absorbed", 5000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.hungry_shield.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getEffectiveness(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.hungry_shield.lore1")); + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - private double getEffectiveness(double factor) { - return Math.min(getConfig().maxEffectiveness, factor * factor + getConfig().effectivenessBase); - } + private double getEffectiveness(double factor) { + return Math.min(getConfig().maxEffectiveness, factor * factor + getConfig().effectivenessBase); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof Player p && hasActiveAdaptation(p)) { - double f = getEffectiveness(getLevelPercent(p)); - double h = e.getDamage() * f; - double d = e.getDamage() - h; + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof Player p && hasActiveAdaptation(p)) { + double f = getEffectiveness(getLevelPercent(p)); + double h = e.getDamage() * f; + double d = e.getDamage() - h; - if (getPlayer(p).consumeFood(h, 6)) { - d += h; - e.setDamage(d); - getPlayer(p).getData().addStat("herbalism.hungry-shield.damage-absorbed", (int) Math.ceil(h)); - xp(p, d); - } - } + if (getPlayer(p).consumeFood(h, 6)) { + d += h; + e.setDamage(d); + getPlayer(p).getData().addStat("herbalism.hungry-shield.damage-absorbed", (int) Math.ceil(h)); + xp(p, d); + } } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Take damage to your hunger before your health.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.78; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double effectivenessBase = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxEffectiveness = 0.95; - } + @NoArgsConstructor + @ConfigDescription("Take damage to your hunger before your health.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effectiveness Base for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double effectivenessBase = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Effectiveness for the Herbalism Hungry Shield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxEffectiveness = 0.95; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java index c5928923e..800e046ed 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismLuck.java @@ -25,9 +25,9 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -41,114 +41,114 @@ public class HerbalismLuck extends SimpleAdaptation { - public HerbalismLuck() { - super("herbalism-luck"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.luck.description")); - setDisplayName(Localizer.dLocalize("herbalism.luck.name")); - setIcon(Material.EMERALD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(8121); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RABBIT_FOOT) - .key("challenge_herbalism_luck_100") - .title(Localizer.dLocalize("advancement.challenge_herbalism_luck_100.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_luck_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.EMERALD) - .key("challenge_herbalism_luck_2500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_luck_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_luck_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_luck_100", "herbalism.luck.lucky-drops", 100, 300); - registerMilestone("challenge_herbalism_luck_2500", "herbalism.luck.lucky-drops", 2500, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.luck.lore0")); - v.addLore(C.GREEN + "+ (" + (getEffectiveness(level)) + C.GRAY + "%) + " + Localizer.dLocalize("herbalism.luck.lore1")); - v.addLore(C.GREEN + "+ (" + (getEffectiveness(level)) + C.GRAY + "%) + " + Localizer.dLocalize("herbalism.luck.lore2")); - } - - private double getEffectiveness(double factor) { - return Math.min(getConfig().highChance, factor * factor + getConfig().lowChance); - } - - @EventHandler(priority = EventPriority.NORMAL) - public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - - Block broken = e.getBlock(); - if (broken.getType() == Materials.GRASS || broken.getType() == Material.TALL_GRASS) { - var d = ThreadLocalRandom.current().nextDouble(100D); - Material m = ItemListings.getHerbalLuckSeeds().getRandom(); - if (d < getEffectiveness(getLevel(p))) { - xp(p, 100); - getPlayer(p).getData().addStat("herbalism.luck.lucky-drops", 1); - ItemStack luckDrop = new ItemStack(m, 1); - e.getBlock().getWorld().dropItem(e.getBlock().getLocation(), luckDrop); - } - } - - if (ItemListings.getFlowers().contains(broken.getType())) { - var d = ThreadLocalRandom.current().nextDouble(100D); - Material m = ItemListings.getHerbalLuckFood().getRandom(); - if (d < getEffectiveness(getLevel(p))) { - xp(p, 100); - getPlayer(p).getData().addStat("herbalism.luck.lucky-drops", 1); - ItemStack luckDrop = new ItemStack(m, 1); - e.getBlock().getWorld().dropItem(e.getBlock().getLocation(), luckDrop); - } - } - + public HerbalismLuck() { + super("herbalism-luck"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.luck.description")); + setDisplayName(Localizer.dLocalize("herbalism.luck.name")); + setIcon(Material.EMERALD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(8121); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RABBIT_FOOT) + .key("challenge_herbalism_luck_100") + .title(Localizer.dLocalize("advancement.challenge_herbalism_luck_100.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_luck_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.EMERALD) + .key("challenge_herbalism_luck_2500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_luck_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_luck_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_luck_100", "herbalism.luck.lucky-drops", 100, 300); + registerMilestone("challenge_herbalism_luck_2500", "herbalism.luck.lucky-drops", 2500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.luck.lore0")); + v.addLore(C.GREEN + "+ (" + (getEffectiveness(level)) + C.GRAY + "%) + " + Localizer.dLocalize("herbalism.luck.lore1")); + v.addLore(C.GREEN + "+ (" + (getEffectiveness(level)) + C.GRAY + "%) + " + Localizer.dLocalize("herbalism.luck.lore2")); + } + + private double getEffectiveness(double factor) { + return Math.min(getConfig().highChance, factor * factor + getConfig().lowChance); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void on(BlockDropItemEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - - @Override - public void onTick() { - + Block broken = e.getBlock(); + if (broken.getType() == Materials.GRASS || broken.getType() == Material.TALL_GRASS) { + double d = ThreadLocalRandom.current().nextDouble(100D); + Material m = ItemListings.getHerbalLuckSeeds().getRandom(); + if (d < getEffectiveness(getLevel(p))) { + xp(p, 100); + getPlayer(p).getData().addStat("herbalism.luck.lucky-drops", 1); + ItemStack luckDrop = new ItemStack(m, 1); + e.getBlock().getWorld().dropItem(e.getBlock().getLocation(), luckDrop); + } } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (ItemListings.getFlowers().contains(broken.getType())) { + double d = ThreadLocalRandom.current().nextDouble(100D); + Material m = ItemListings.getHerbalLuckFood().getRandom(); + if (d < getEffectiveness(getLevel(p))) { + xp(p, 100); + getPlayer(p).getData().addStat("herbalism.luck.lucky-drops", 1); + ItemStack luckDrop = new ItemStack(m, 1); + e.getBlock().getWorld().dropItem(e.getBlock().getLocation(), luckDrop); + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Breaking Grass or Flowers has a chance to drop random items.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Low Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lowChance = 0.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls High Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double highChance = 90; - } + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Breaking Grass or Flowers has a chance to drop random items.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Low Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lowChance = 0.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls High Chance for the Herbalism Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double highChance = 90; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java index 29edeaa29..b73dfefa4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismMyconid.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.recipe.AdaptRecipe; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -37,79 +37,79 @@ public class HerbalismMyconid extends SimpleAdaptation { - public HerbalismMyconid() { - super("herbalism-myconid"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.myconid.description")); - setDisplayName(Localizer.dLocalize("herbalism.myconid.name")); - setIcon(Material.MYCELIUM); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17771); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shapeless() - .key("herbalism-dirt-myconid") - .ingredient(Material.DIRT) - .ingredient(Material.RED_MUSHROOM) - .ingredient(Material.BROWN_MUSHROOM) - .result(new ItemStack(Material.MYCELIUM, 1)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.MYCELIUM) - .key("challenge_herbalism_myconid_100") - .title(Localizer.dLocalize("advancement.challenge_herbalism_myconid_100.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_myconid_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_myconid_100", "herbalism.myconid.mycelium-crafted", 100, 300); - } + public HerbalismMyconid() { + super("herbalism-myconid"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.myconid.description")); + setDisplayName(Localizer.dLocalize("herbalism.myconid.name")); + setIcon(Material.MYCELIUM); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17771); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shapeless() + .key("herbalism-dirt-myconid") + .ingredient(Material.DIRT) + .ingredient(Material.RED_MUSHROOM) + .ingredient(Material.BROWN_MUSHROOM) + .result(new ItemStack(Material.MYCELIUM, 1)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.MYCELIUM) + .key("challenge_herbalism_myconid_100") + .title(Localizer.dLocalize("advancement.challenge_herbalism_myconid_100.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_myconid_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_myconid_100", "herbalism.myconid.mycelium-crafted", 100, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.myconid.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.myconid.lore1")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { - return; - } - if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-myconid")) { - getPlayer(p).getData().addStat("herbalism.myconid.mycelium-crafted", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { + return; } - - @Override - public void onTick() { + if (e.getRecipe() instanceof org.bukkit.inventory.ShapelessRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-myconid")) { + getPlayer(p).getData().addStat("herbalism.myconid.mycelium-crafted", 1); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Craft Mycelium from Dirt and Mushrooms.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft Mycelium from Dirt and Mushrooms.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index e9c45a2ed..0a0044fa5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -27,12 +27,12 @@ import art.arcane.adapt.content.skill.SkillHerbalism; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.data.Cuboid; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -50,192 +50,192 @@ public class HerbalismReplant extends SimpleAdaptation { - public HerbalismReplant() { - super("herbalism-replant"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.replant.description")); - setDisplayName(Localizer.dLocalize("herbalism.replant.name")); - setIcon(Material.PUMPKIN_SEEDS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(6090); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHEAT_SEEDS) - .key("challenge_herbalism_replant_500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_replant_500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_replant_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.COMPOSTER) - .key("challenge_herbalism_replant_25k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_replant_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_replant_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_replant_500", "herbalism.replant.crops-replanted", 500, 300); - registerMilestone("challenge_herbalism_replant_25k", "herbalism.replant.crops-replanted", 25000, 1000); + public HerbalismReplant() { + super("herbalism-replant"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.replant.description")); + setDisplayName(Localizer.dLocalize("herbalism.replant.name")); + setIcon(Material.PUMPKIN_SEEDS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(6090); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHEAT_SEEDS) + .key("challenge_herbalism_replant_500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_replant_500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_replant_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.COMPOSTER) + .key("challenge_herbalism_replant_25k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_replant_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_replant_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_replant_500", "herbalism.replant.crops-replanted", 500, 300); + registerMilestone("challenge_herbalism_replant_25k", "herbalism.replant.crops-replanted", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + Localizer.dLocalize("herbalism.replant.lore1")); + } + + + private int getCooldown(double factor, int level) { + if (level == 1) { + return (int) getConfig().cooldownLvl1; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + Localizer.dLocalize("herbalism.replant.lore1")); - } + return (int) ((getConfig().baseCooldown - (getConfig().cooldownFactor * factor)) + getConfig().bonusCooldown); + } + private float getRadius(int lvl) { + return lvl - getConfig().radiusSub; + } - private int getCooldown(double factor, int level) { - if (level == 1) { - return (int) getConfig().cooldownLvl1; - } - - return (int) ((getConfig().baseCooldown - (getConfig().cooldownFactor * factor)) + getConfig().bonusCooldown); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + if (e.getClickedBlock() == null) { + return; + } + if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { // you need to right-click to harvest! + return; } - private float getRadius(int lvl) { - return lvl - getConfig().radiusSub; + if (!(e.getClickedBlock().getBlockData() instanceof Ageable)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (e.getClickedBlock() == null) { - return; + int lvl = getLevel(p); + + if (lvl > 0) { + ItemStack right = p.getInventory().getItemInMainHand(); + ItemStack left = p.getInventory().getItemInOffHand(); + + if (isTool(left) && isHoe(left) && !p.hasCooldown(left.getType())) { + damageOffHand(p, 1 + ((lvl - 1) * 7)); + p.setCooldown(left.getType(), getCooldown(getLevelPercent(p), getLevel(p))); + } else if (isTool(right) && isHoe(right) && !p.hasCooldown(right.getType())) { + damageHand(p, 1 + ((lvl - 1) * 7)); + p.setCooldown(right.getType(), getCooldown(getLevelPercent(p), getLevel(p))); + } else { + return; + } + + if (lvl > 1) { + Cuboid c = new Cuboid(e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5)); + c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.North, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.South, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.East, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.West, Math.round(getRadius(lvl))); + + for (Block i : c) { + J.runEntity(p, () -> hit(p, i), M.irand(1, 6)); } - if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { // you need to right-click to harvest! - return; - } - - if (!(e.getClickedBlock().getBlockData() instanceof Ageable)) { - return; - } - - int lvl = getLevel(p); - - if (lvl > 0) { - ItemStack right = p.getInventory().getItemInMainHand(); - ItemStack left = p.getInventory().getItemInOffHand(); - - if (isTool(left) && isHoe(left) && !p.hasCooldown(left.getType())) { - damageOffHand(p, 1 + ((lvl - 1) * 7)); - p.setCooldown(left.getType(), getCooldown(getLevelPercent(p), getLevel(p))); - } else if (isTool(right) && isHoe(right) && !p.hasCooldown(right.getType())) { - damageHand(p, 1 + ((lvl - 1) * 7)); - p.setCooldown(right.getType(), getCooldown(getLevelPercent(p), getLevel(p))); - } else { - return; - } - - if (lvl > 1) { - Cuboid c = new Cuboid(e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5)); - c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.North, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.South, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.East, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.West, Math.round(getRadius(lvl))); - - for (Block i : c) { - J.runEntity(p, () -> hit(p, i), M.irand(1, 6)); - } - spw.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 1f, 0.66f); - spw.play(p.getLocation(), Sound.BLOCK_BAMBOO_SAPLING_BREAK, 1f, 0.66f); - if (areParticlesEnabled()) { - p.spawnParticle(Particles.VILLAGER_HAPPY, p.getLocation().clone().add(0.5, 0.5, 0.5), getLevel(p) * 3, 0.3 * getLevel(p), 0.3 * getLevel(p), 0.3 * getLevel(p), 0.9); - } - } else { - hit(p, e.getClickedBlock()); - } + spw.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 1f, 0.66f); + spw.play(p.getLocation(), Sound.BLOCK_BAMBOO_SAPLING_BREAK, 1f, 0.66f); + if (areParticlesEnabled()) { + p.spawnParticle(Particles.VILLAGER_HAPPY, p.getLocation().clone().add(0.5, 0.5, 0.5), getLevel(p) * 3, 0.3 * getLevel(p), 0.3 * getLevel(p), 0.3 * getLevel(p), 0.9); } + } else { + hit(p, e.getClickedBlock()); + } } - - private void hit(Player p, Block b) { - if (b != null && b.getBlockData() instanceof Ageable aa && hasActiveAdaptation(p)) { - if (aa.getAge() != aa.getMaximumAge()) { - return; - } - - xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).harvestPerAgeXP * aa.getAge()); - xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).plantCropSeedsXP); - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("herbalism"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("herbalism-drop-to-inventory") : null; - if (adaptation != null && adaptation.getLevel() > 0) { - Collection items = b.getDrops(); - SoundPlayer sp = SoundPlayer.of(p); - for (ItemStack i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - i.setAmount(1); - if (!p.getInventory().addItem(i).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i); - } - } - aa.setAge(0); - J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); - - } else { - p.breakBlock(b); - } - - aa.setAge(0); - J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); - - getPlayer(p).getData().addStat("harvest.blocks", 1); - getPlayer(p).getData().addStat("harvest.planted", 1); - getPlayer(p).getData().addStat("herbalism.replant.crops-replanted", 1); - - if (M.r(1D / (double) getLevel(p))) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(b.getLocation(), Sound.ITEM_CROP_PLANT, 1f, 0.7f); - } + } + + private void hit(Player p, Block b) { + if (b != null && b.getBlockData() instanceof Ageable aa && hasActiveAdaptation(p)) { + if (aa.getAge() != aa.getMaximumAge()) { + return; + } + + xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).harvestPerAgeXP * aa.getAge()); + xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).plantCropSeedsXP); + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("herbalism"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("herbalism-drop-to-inventory") : null; + if (adaptation != null && adaptation.getLevel() > 0) { + Collection items = b.getDrops(); + SoundPlayer sp = SoundPlayer.of(p); + for (ItemStack i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + i.setAmount(1); + if (!p.getInventory().addItem(i).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i); + } } - } + aa.setAge(0); + J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); + } else { + p.breakBlock(b); + } - @Override - public void onTick() { + aa.setAge(0); + J.runAt(b.getLocation(), () -> b.setBlockData(aa, true)); - } + getPlayer(p).getData().addStat("harvest.blocks", 1); + getPlayer(p).getData().addStat("harvest.planted", 1); + getPlayer(p).getData().addStat("herbalism.replant.crops-replanted", 1); - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Right-click a crop with a hoe to harvest and replant it.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Replant adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Lvl1 for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownLvl1 = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseCooldown = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Factor for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownFactor = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusCooldown = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Sub for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int radiusSub = 1; + if (M.r(1D / (double) getLevel(p))) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(b.getLocation(), Sound.ITEM_CROP_PLANT, 1f, 0.7f); + } } + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Right-click a crop with a hoe to harvest and replant it.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Herbalism Replant adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Lvl1 for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownLvl1 = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseCooldown = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Factor for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownFactor = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Cooldown for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusCooldown = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Sub for the Herbalism Replant adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int radiusSub = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java index adcb4d6d1..de579d890 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismRootedFooting.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -38,135 +38,135 @@ import org.bukkit.event.player.PlayerInteractEvent; public class HerbalismRootedFooting extends SimpleAdaptation { - public HerbalismRootedFooting() { - super("herbalism-rooted-footing"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.rooted_footing.description")); - setDisplayName(Localizer.dLocalize("herbalism.rooted_footing.name")); - setIcon(Material.FARMLAND); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2050); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FARMLAND) - .key("challenge_herbalism_rooted_500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_rooted_500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_rooted_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_rooted_500", "herbalism.rooted-footing.farmland-saved", 500, 300); - } - - @Override - public void addStats(int level, Element v) { - double absorb = getFallAbsorb(level); - v.addLore(C.GREEN + "+ " + Form.pc(absorb, 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.rooted_footing.lore1")); - v.addLore(C.YELLOW + "* " + getConfig().foodPerDamage + C.GRAY + " " + Localizer.dLocalize("herbalism.rooted_footing.lore2")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("herbalism.rooted_footing.lore3")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - - if (e.getAction() != Action.PHYSICAL || e.getClickedBlock() == null || !(e.getPlayer() instanceof Player p)) { - return; - } - - if (!hasActiveAdaptation(p)) { - return; - } - - if (e.getClickedBlock().getType() == Material.FARMLAND) { - e.setCancelled(true); - getPlayer(p).getData().addStat("herbalism.rooted-footing.farmland-saved", 1); - } + public HerbalismRootedFooting() { + super("herbalism-rooted-footing"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.rooted_footing.description")); + setDisplayName(Localizer.dLocalize("herbalism.rooted_footing.name")); + setIcon(Material.FARMLAND); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2050); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FARMLAND) + .key("challenge_herbalism_rooted_500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_rooted_500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_rooted_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_rooted_500", "herbalism.rooted-footing.farmland-saved", 500, 300); + } + + @Override + public void addStats(int level, Element v) { + double absorb = getFallAbsorb(level); + v.addLore(C.GREEN + "+ " + Form.pc(absorb, 0) + C.GRAY + " " + Localizer.dLocalize("herbalism.rooted_footing.lore1")); + v.addLore(C.YELLOW + "* " + getConfig().foodPerDamage + C.GRAY + " " + Localizer.dLocalize("herbalism.rooted_footing.lore2")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("herbalism.rooted_footing.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + + if (e.getAction() != Action.PHYSICAL || e.getClickedBlock() == null || !(e.getPlayer() instanceof Player p)) { + return; } - @EventHandler(priority = EventPriority.HIGH) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0 || !isNatureGround(p)) { - return; - } - - double absorbCap = e.getDamage() * getFallAbsorb(level); - int foodRequired = (int) Math.ceil(absorbCap * getConfig().foodPerDamage); - if (foodRequired <= 0 || p.getFoodLevel() <= 0) { - return; - } - - int usableFood = Math.min(p.getFoodLevel(), foodRequired); - double absorbed = usableFood / getConfig().foodPerDamage; - if (absorbed <= 0) { - return; - } - - p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); - e.setDamage(Math.max(0, e.getDamage() - absorbed)); - if (e.getDamage() <= 0.01) { - e.setCancelled(true); - } + if (!hasActiveAdaptation(p)) { + return; } - private boolean isNatureGround(Player p) { - Block under = p.getLocation().clone().add(0, -1, 0).getBlock(); - Material type = under.getType(); - return type == Material.FARMLAND - || type == Material.GRASS_BLOCK - || type == Material.MOSS_BLOCK - || type == Material.MYCELIUM - || type == Material.DIRT - || type == Material.ROOTED_DIRT; + if (e.getClickedBlock().getType() == Material.FARMLAND) { + e.setCancelled(true); + getPlayer(p).getData().addStat("herbalism.rooted-footing.farmland-saved", 1); } + } - private double getFallAbsorb(int level) { - return Math.min(getConfig().maxAbsorbPercent, getConfig().absorbBase + (getLevelPercent(level) * getConfig().absorbFactor)); + @EventHandler(priority = EventPriority.HIGH) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p) || e.getCause() != EntityDamageEvent.DamageCause.FALL) { + return; } - @Override - public void onTick() { - + int level = getActiveLevel(p); + if (level <= 0 || !isNatureGround(p)) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + double absorbCap = e.getDamage() * getFallAbsorb(level); + int foodRequired = (int) Math.ceil(absorbCap * getConfig().foodPerDamage); + if (foodRequired <= 0 || p.getFoodLevel() <= 0) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + int usableFood = Math.min(p.getFoodLevel(), foodRequired); + double absorbed = usableFood / getConfig().foodPerDamage; + if (absorbed <= 0) { + return; } - @NoArgsConstructor - @ConfigDescription("Protect farmland and convert fall damage into hunger on natural ground.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Base for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double absorbBase = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Factor for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double absorbFactor = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Absorb Percent for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxAbsorbPercent = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Per Damage for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodPerDamage = 1.8; + p.setFoodLevel(Math.max(0, p.getFoodLevel() - usableFood)); + e.setDamage(Math.max(0, e.getDamage() - absorbed)); + if (e.getDamage() <= 0.01) { + e.setCancelled(true); } + } + + private boolean isNatureGround(Player p) { + Block under = p.getLocation().clone().add(0, -1, 0).getBlock(); + Material type = under.getType(); + return type == Material.FARMLAND + || type == Material.GRASS_BLOCK + || type == Material.MOSS_BLOCK + || type == Material.MYCELIUM + || type == Material.DIRT + || type == Material.ROOTED_DIRT; + } + + private double getFallAbsorb(int level) { + return Math.min(getConfig().maxAbsorbPercent, getConfig().absorbBase + (getLevelPercent(level) * getConfig().absorbFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Protect farmland and convert fall damage into hunger on natural ground.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Base for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double absorbBase = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Absorb Factor for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double absorbFactor = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Absorb Percent for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxAbsorbPercent = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Per Damage for the Herbalism Rooted Footing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodPerDamage = 1.8; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java index e55d2c295..66a19aeaf 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -42,198 +42,198 @@ import org.bukkit.inventory.ItemStack; public class HerbalismSeedSower extends SimpleAdaptation { - public HerbalismSeedSower() { - super("herbalism-seed-sower"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.seed_sower.description")); - setDisplayName(Localizer.dLocalize("herbalism.seed_sower.name")); - setIcon(Material.WHEAT_SEEDS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(6920); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHEAT_SEEDS) - .key("challenge_herbalism_seed_1k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_seed_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_seed_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FARMLAND) - .key("challenge_herbalism_seed_25k") - .title(Localizer.dLocalize("advancement.challenge_herbalism_seed_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_seed_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_herbalism_seed_1k", "herbalism.seed-sower.seeds-planted", 1000, 300); - registerMilestone("challenge_herbalism_seed_25k", "herbalism.seed-sower.seeds-planted", 25000, 1000); + public HerbalismSeedSower() { + super("herbalism-seed-sower"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.seed_sower.description")); + setDisplayName(Localizer.dLocalize("herbalism.seed_sower.name")); + setIcon(Material.WHEAT_SEEDS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(6920); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHEAT_SEEDS) + .key("challenge_herbalism_seed_1k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_seed_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_seed_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FARMLAND) + .key("challenge_herbalism_seed_25k") + .title(Localizer.dLocalize("advancement.challenge_herbalism_seed_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_seed_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_herbalism_seed_1k", "herbalism.seed-sower.seeds-planted", 1000, 300); + registerMilestone("challenge_herbalism_seed_25k", "herbalism.seed-sower.seeds-planted", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + double factor = getLevelPercent(level); + v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore1")); + v.addLore(C.GREEN + "+ " + getMaxCrops(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(factor) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p) || !p.isSneaking()) { + return; } - @Override - public void addStats(int level, Element v) { - double factor = getLevelPercent(level); - v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore1")); - v.addLore(C.GREEN + "+ " + getMaxCrops(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(factor) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.seed_sower.lore3")); + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p) || !p.isSneaking()) { - return; - } - - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!isItem(hand)) { - return; - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isItem(hand)) { + return; + } - Material seedType = hand.getType(); - Material cropType = getCropType(seedType); - if (cropType == null || p.hasCooldown(seedType)) { - return; - } + Material seedType = hand.getType(); + Material cropType = getCropType(seedType); + if (cropType == null || p.hasCooldown(seedType)) { + return; + } - int planted = plantNearby(p, e.getClickedBlock(), hand, seedType, cropType, getRadius(getLevel(p)), getMaxCrops(getLevel(p))); - if (planted <= 0) { - return; - } + int planted = plantNearby(p, e.getClickedBlock(), hand, seedType, cropType, getRadius(getLevel(p)), getMaxCrops(getLevel(p))); + if (planted <= 0) { + return; + } - e.setCancelled(true); - p.setCooldown(seedType, getCooldownTicks(getLevelPercent(p))); - getPlayer(p).getData().addStat("harvest.planted", planted); - getPlayer(p).getData().addStat("herbalism.seed-sower.seeds-planted", planted); - xp(p, planted * getConfig().xpPerCrop); + e.setCancelled(true); + p.setCooldown(seedType, getCooldownTicks(getLevelPercent(p))); + getPlayer(p).getData().addStat("harvest.planted", planted); + getPlayer(p).getData().addStat("herbalism.seed-sower.seeds-planted", planted); + xp(p, planted * getConfig().xpPerCrop); - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ITEM_CROP_PLANT, 0.6f, 1.25f); - if (planted > 4) { - sp.play(p.getLocation(), Sound.BLOCK_ROOTED_DIRT_PLACE, 0.5f, 1.35f); - } + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ITEM_CROP_PLANT, 0.6f, 1.25f); + if (planted > 4) { + sp.play(p.getLocation(), Sound.BLOCK_ROOTED_DIRT_PLACE, 0.5f, 1.35f); } + } - private int plantNearby(Player p, Block origin, ItemStack seeds, Material seedType, Material cropType, int radius, int maxCrops) { - int planted = 0; - int available = p.getGameMode() == GameMode.CREATIVE ? Integer.MAX_VALUE : seeds.getAmount(); - int y = origin.getY(); - - for (int x = -radius; x <= radius && planted < maxCrops; x++) { - for (int z = -radius; z <= radius && planted < maxCrops; z++) { - if (available <= 0) { - break; - } - - Block base = origin.getWorld().getBlockAt(origin.getX() + x, y, origin.getZ() + z); - if (!isValidBase(seedType, base.getType())) { - continue; - } - - Block crop = base.getRelative(0, 1, 0); - if (!crop.isEmpty() || !canBlockPlace(p, crop.getLocation())) { - continue; - } - - crop.setType(cropType); - planted++; - available--; - } - } + private int plantNearby(Player p, Block origin, ItemStack seeds, Material seedType, Material cropType, int radius, int maxCrops) { + int planted = 0; + int available = p.getGameMode() == GameMode.CREATIVE ? Integer.MAX_VALUE : seeds.getAmount(); + int y = origin.getY(); - if (p.getGameMode() != GameMode.CREATIVE && planted > 0) { - seeds.setAmount(Math.max(0, seeds.getAmount() - planted)); - p.getInventory().setItemInMainHand(seeds.getAmount() <= 0 ? new ItemStack(Material.AIR) : seeds); + for (int x = -radius; x <= radius && planted < maxCrops; x++) { + for (int z = -radius; z <= radius && planted < maxCrops; z++) { + if (available <= 0) { + break; } - return planted; - } - - private boolean isValidBase(Material seedType, Material base) { - if (seedType == Material.NETHER_WART) { - return base == Material.SOUL_SAND; + Block base = origin.getWorld().getBlockAt(origin.getX() + x, y, origin.getZ() + z); + if (!isValidBase(seedType, base.getType())) { + continue; } - return base == Material.FARMLAND; - } - - private Material getCropType(Material seedType) { - return switch (seedType) { - case WHEAT_SEEDS -> Material.WHEAT; - case CARROT -> Material.CARROTS; - case POTATO -> Material.POTATOES; - case BEETROOT_SEEDS -> Material.BEETROOTS; - case MELON_SEEDS -> Material.MELON_STEM; - case PUMPKIN_SEEDS -> Material.PUMPKIN_STEM; - case TORCHFLOWER_SEEDS -> Material.TORCHFLOWER_CROP; - case NETHER_WART -> Material.NETHER_WART; - default -> null; - }; - } - - private int getRadius(int level) { - return Math.max(1, (int) Math.round(getConfig().baseRadius + (getLevelPercent(level) * getConfig().radiusFactor))); - } + Block crop = base.getRelative(0, 1, 0); + if (!crop.isEmpty() || !canBlockPlace(p, crop.getLocation())) { + continue; + } - private int getMaxCrops(int level) { - return Math.max(1, (int) Math.round(getConfig().baseCropCount + (getLevelPercent(level) * getConfig().cropCountFactor))); + crop.setType(cropType); + planted++; + available--; + } } - private int getCooldownTicks(double factor) { - return Math.max(2, (int) Math.round(getConfig().cooldownTicksBase - (factor * getConfig().cooldownTicksReduction))); + if (p.getGameMode() != GameMode.CREATIVE && planted > 0) { + seeds.setAmount(Math.max(0, seeds.getAmount() - planted)); + p.getInventory().setItemInMainHand(seeds.getAmount() <= 0 ? new ItemStack(Material.AIR) : seeds); } - @Override - public void onTick() { - - } + return planted; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private boolean isValidBase(Material seedType, Material base) { + if (seedType == Material.NETHER_WART) { + return base == Material.SOUL_SAND; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sneak-right-click with seeds to plant nearby farmland and soul-sand plots.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.675; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseRadius = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crop Count for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseCropCount = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Count Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cropCountFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksReduction = 42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Crop for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerCrop = 1.45; - } + return base == Material.FARMLAND; + } + + private Material getCropType(Material seedType) { + return switch (seedType) { + case WHEAT_SEEDS -> Material.WHEAT; + case CARROT -> Material.CARROTS; + case POTATO -> Material.POTATOES; + case BEETROOT_SEEDS -> Material.BEETROOTS; + case MELON_SEEDS -> Material.MELON_STEM; + case PUMPKIN_SEEDS -> Material.PUMPKIN_STEM; + case TORCHFLOWER_SEEDS -> Material.TORCHFLOWER_CROP; + case NETHER_WART -> Material.NETHER_WART; + default -> null; + }; + } + + private int getRadius(int level) { + return Math.max(1, (int) Math.round(getConfig().baseRadius + (getLevelPercent(level) * getConfig().radiusFactor))); + } + + private int getMaxCrops(int level) { + return Math.max(1, (int) Math.round(getConfig().baseCropCount + (getLevelPercent(level) * getConfig().cropCountFactor))); + } + + private int getCooldownTicks(double factor) { + return Math.max(2, (int) Math.round(getConfig().cooldownTicksBase - (factor * getConfig().cooldownTicksReduction))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click with seeds to plant nearby farmland and soul-sand plots.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.675; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Radius for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseRadius = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crop Count for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseCropCount = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crop Count Factor for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cropCountFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Reduction for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksReduction = 42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Crop for the Herbalism Seed Sower adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerCrop = 1.45; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index 83d57e045..647578770 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -24,11 +24,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -44,543 +44,543 @@ import java.util.concurrent.ThreadLocalRandom; public class HerbalismSporeBloom extends SimpleAdaptation { - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - - public HerbalismSporeBloom() { - super("herbalism-spore-bloom"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.spore_bloom.description")); - setDisplayName(Localizer.dLocalize("herbalism.spore_bloom.name")); - setIcon(Material.RED_MUSHROOM_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2100); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BROWN_MUSHROOM) - .key("challenge_herbalism_spore_500") - .title(Localizer.dLocalize("advancement.challenge_herbalism_spore_500.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_spore_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_spore_500", "herbalism.spore-bloom.blocks-spread", 500, 300); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getBloomAttempts(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getBloomRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore3")); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(BlockPlaceEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - if (getActiveLevel(e.getPlayer()) <= 0 || !e.getPlayer().isSneaking()) { - return; - } - - if (!isSporeItem(e.getItemInHand())) { - return; - } - - Block floor = e.getBlockPlaced().getRelative(0, -1, 0); - if (!isBloomFloor(floor.getType())) { - return; - } - - // Place-trigger activation: sneak-place mushroom on valid floor to bloom. - e.setCancelled(true); - attemptBloom(e.getPlayer(), floor, e.getItemInHand().getType()); - } - - private void startBloom(org.bukkit.entity.Player player, Block center, Material catalyst, Material spreadSurface, int level) { - List path = buildSpiderPath(center, getBloomRadius(level), getSpokes(level), getBloomAttempts(level), getGuaranteedReach(level)); - if (path.isEmpty()) { - return; - } - - int intervalTicks = Math.max(1, getSpreadIntervalTicks(level)); - int[] cursor = {0}; - int[] totalChanged = {0}; - Runnable[] bloomTask = new Runnable[1]; - bloomTask[0] = () -> { - if (!player.isOnline() || center.getWorld() == null) { - return; - } - - int pulseChanged = 0; - int batch = getBlocksPerPulse(level); - for (int i = 0; i < batch && cursor[0] < path.size(); i++) { - pulseChanged += spreadAt(path.get(cursor[0]++), catalyst, spreadSurface); - } - - if (pulseChanged > 0) { - totalChanged[0] += pulseChanged; - if (areParticlesEnabled()) { - center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.02); - } - if (areParticlesEnabled()) { - center.getWorld().spawnParticle(Particle.CRIMSON_SPORE, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.01); - } - SoundPlayer sp = SoundPlayer.of(center.getWorld()); - sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_FUNGUS_PLACE, 0.45f, 0.75f); - sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.22f, 0.45f + ThreadLocalRandom.current().nextFloat() * 0.45f); - } - - if (cursor[0] >= path.size()) { - if (totalChanged[0] > 0) { - getPlayer(player).getData().addStat("herbalism.spore-bloom.blocks-spread", totalChanged[0]); - xp(player, totalChanged[0] * getConfig().xpPerMushroomPlaced); - } - return; - } - - J.runAt(center.getLocation(), bloomTask[0], intervalTicks); - }; - J.runAt(center.getLocation(), bloomTask[0]); - } - - private List buildSpiderPath(Block center, double radius, int spokes, int max, int guaranteedReach) { - int r = Math.max(1, (int) Math.ceil(radius)); - int sectors = Math.max(8, Math.min(48, Math.max(1, spokes) * 3)); - double maxDistance = radius + 0.35D; - double maxDistanceSq = maxDistance * maxDistance; - List out = new ArrayList<>(Math.max(8, max)); - Set seen = new HashSet<>(); - out.add(center); - seen.add(key(center)); - - if (out.size() >= max) { - return out; + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + + public HerbalismSporeBloom() { + super("herbalism-spore-bloom"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.spore_bloom.description")); + setDisplayName(Localizer.dLocalize("herbalism.spore_bloom.name")); + setIcon(Material.RED_MUSHROOM_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2100); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BROWN_MUSHROOM) + .key("challenge_herbalism_spore_500") + .title(Localizer.dLocalize("advancement.challenge_herbalism_spore_500.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_spore_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_spore_500", "herbalism.spore-bloom.blocks-spread", 500, 300); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getBloomAttempts(level) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getBloomRadius(level)) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("herbalism.spore_bloom.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; + } + + if (getActiveLevel(e.getPlayer()) <= 0 || !e.getPlayer().isSneaking()) { + return; + } + + if (!isSporeItem(e.getItemInHand())) { + return; + } + + Block floor = e.getBlockPlaced().getRelative(0, -1, 0); + if (!isBloomFloor(floor.getType())) { + return; + } + + // Place-trigger activation: sneak-place mushroom on valid floor to bloom. + e.setCancelled(true); + attemptBloom(e.getPlayer(), floor, e.getItemInHand().getType()); + } + + private void startBloom(org.bukkit.entity.Player player, Block center, Material catalyst, Material spreadSurface, int level) { + List path = buildSpiderPath(center, getBloomRadius(level), getSpokes(level), getBloomAttempts(level), getGuaranteedReach(level)); + if (path.isEmpty()) { + return; + } + + int intervalTicks = Math.max(1, getSpreadIntervalTicks(level)); + int[] cursor = {0}; + int[] totalChanged = {0}; + Runnable[] bloomTask = new Runnable[1]; + bloomTask[0] = () -> { + if (!player.isOnline() || center.getWorld() == null) { + return; + } + + int pulseChanged = 0; + int batch = getBlocksPerPulse(level); + for (int i = 0; i < batch && cursor[0] < path.size(); i++) { + pulseChanged += spreadAt(path.get(cursor[0]++), catalyst, spreadSurface); + } + + if (pulseChanged > 0) { + totalChanged[0] += pulseChanged; + if (areParticlesEnabled()) { + center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.02); } - - ThreadLocalRandom random = ThreadLocalRandom.current(); - double offset = random.nextDouble(Math.PI * 2D); - - int forcedReach = Math.max(0, Math.min(r, guaranteedReach)); - for (int step = 1; step <= forcedReach; step++) { - addRingSamples(center, out, seen, step, sectors, offset, maxDistanceSq, max); - if (out.size() >= max) { - return out; - } + if (areParticlesEnabled()) { + center.getWorld().spawnParticle(Particle.CRIMSON_SPORE, center.getLocation().add(0.5, 1.0, 0.5), 8, 0.55, 0.2, 0.55, 0.01); } - - for (int step = 1; step <= r; step++) { - addRingSamples(center, out, seen, step, sectors, offset, maxDistanceSq, max); - if (out.size() >= max) { - return out; - } - - // Offset pass fills sector rounding gaps so rings look uniform. - addRingSamples(center, out, seen, step, sectors, offset + (Math.PI / sectors), maxDistanceSq, max); - if (out.size() >= max) { - return out; - } + SoundPlayer sp = SoundPlayer.of(center.getWorld()); + sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_FUNGUS_PLACE, 0.45f, 0.75f); + sp.play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.22f, 0.45f + ThreadLocalRandom.current().nextFloat() * 0.45f); + } + + if (cursor[0] >= path.size()) { + if (totalChanged[0] > 0) { + getPlayer(player).getData().addStat("herbalism.spore-bloom.blocks-spread", totalChanged[0]); + xp(player, totalChanged[0] * getConfig().xpPerMushroomPlaced); } - - fillRemainingFromCircle(center, out, seen, r, offset, maxDistanceSq, max); + return; + } + + J.runAt(center.getLocation(), bloomTask[0], intervalTicks); + }; + J.runAt(center.getLocation(), bloomTask[0]); + } + + private List buildSpiderPath(Block center, double radius, int spokes, int max, int guaranteedReach) { + int r = Math.max(1, (int) Math.ceil(radius)); + int sectors = Math.max(8, Math.min(48, Math.max(1, spokes) * 3)); + double maxDistance = radius + 0.35D; + double maxDistanceSq = maxDistance * maxDistance; + List out = new ArrayList<>(Math.max(8, max)); + Set seen = new HashSet<>(); + out.add(center); + seen.add(key(center)); + + if (out.size() >= max) { + return out; + } + + ThreadLocalRandom random = ThreadLocalRandom.current(); + double offset = random.nextDouble(Math.PI * 2D); + + int forcedReach = Math.max(0, Math.min(r, guaranteedReach)); + for (int step = 1; step <= forcedReach; step++) { + addRingSamples(center, out, seen, step, sectors, offset, maxDistanceSq, max); + if (out.size() >= max) { return out; + } } - private void addRingSamples(Block center, List out, Set seen, int step, int sectors, double offset, double maxDistanceSq, int max) { - for (int i = 0; i < sectors && out.size() < max; i++) { - double angle = offset + ((Math.PI * 2D) * i / sectors); - int dx = (int) Math.round(Math.cos(angle) * step); - int dz = (int) Math.round(Math.sin(angle) * step); - if (dx == 0 && dz == 0) { - continue; - } - - if ((dx * dx) + (dz * dz) > maxDistanceSq) { - continue; - } - - Block block = center.getRelative(dx, 0, dz); - if (seen.add(key(block))) { - out.add(block); - } - } - } - - private void fillRemainingFromCircle(Block center, List out, Set seen, int r, double offset, double maxDistanceSq, int max) { - List candidates = new ArrayList<>(); - for (int dx = -r; dx <= r; dx++) { - for (int dz = -r; dz <= r; dz++) { - if (dx == 0 && dz == 0) { - continue; - } - - if ((dx * dx) + (dz * dz) > maxDistanceSq) { - continue; - } - - candidates.add(new int[]{dx, dz}); - } - } - - candidates.sort(Comparator.comparingInt(v -> (v[0] * v[0]) + (v[1] * v[1])) - .thenComparingDouble(v -> normalizeAngle(Math.atan2(v[1], v[0]) - offset))); - - for (int[] c : candidates) { - if (out.size() >= max) { - return; - } - - Block block = center.getRelative(c[0], 0, c[1]); - if (seen.add(key(block))) { - out.add(block); - } - } - } - - private double normalizeAngle(double angle) { - double out = angle % (Math.PI * 2D); - return out < 0 ? out + (Math.PI * 2D) : out; - } - - private int spreadAt(Block floor, Material catalyst, Material spreadSurface) { - int changed = 0; - Block ground = resolveTopSurfaceSoil(floor); - if (ground == null) { - return 0; - } - Block above = ground.getRelative(0, 1, 0); - - if (spreadSurface != null && isConvertibleSoil(ground.getType()) && ground.getType() != spreadSurface) { - ground.setType(spreadSurface, false); - changed++; - } - - if (getConfig().swapFlowersToMushrooms && isFlower(above.getType())) { - Material replacement = getFlowerReplacement(above.getType(), catalyst); - if (replacement != null && above.getType() != replacement) { - above.setType(replacement, false); - changed++; - } - } - - return changed; - } - - private Block resolveTopSurfaceSoil(Block sample) { - int x = sample.getX(); - int z = sample.getZ(); - int highestY = sample.getWorld().getHighestBlockYAt(x, z); - int minY = sample.getWorld().getMinHeight(); - - for (int y = highestY; y >= minY; y--) { - Block block = sample.getWorld().getBlockAt(x, y, z); - if (!isConvertibleSoil(block.getType())) { - continue; - } - - Block above = block.getRelative(0, 1, 0); - if (above.getType().isAir() || isReplaceablePlant(above.getType()) || isFlower(above.getType())) { - return block; - } - } - - return null; - } - - private boolean isBloomFloor(Material type) { - return type == Material.MYCELIUM || type == Material.PODZOL; - } - - private boolean isSporeItem(ItemStack hand) { - return isItem(hand) && (hand.getType() == Material.RED_MUSHROOM || hand.getType() == Material.BROWN_MUSHROOM); - } - - private boolean consumeOne(ItemStack hand) { - if (hand.getAmount() <= 0) { - return false; - } - - hand.setAmount(hand.getAmount() - 1); - return true; - } - - private boolean attemptBloom(org.bukkit.entity.Player player, Block center, Material catalyst) { - Material spreadSurface = resolveSpreadSurface(center.getType()); - if (spreadSurface == null) { - return false; - } - - int level = getActiveLevel(player); - long now = System.currentTimeMillis(); - long ready = cooldowns.getOrDefault(player.getUniqueId(), 0L); - if (now < ready) { - SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.75f); - return false; - } - - if (player.getFoodLevel() < getFoodCost(level)) { - SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.75f); - return false; - } - - if (!consumeCatalystFromMainHandIfPresent(player, catalyst)) { - return false; - } - - player.setFoodLevel(Math.max(0, player.getFoodLevel() - getFoodCost(level))); - cooldowns.put(player.getUniqueId(), now + getCooldownMillis(level)); - - if (areParticlesEnabled()) { - center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 30, 0.35, 0.15, 0.35, 0.01); - } - SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.45f, 0.55f); - startBloom(player, center, catalyst, spreadSurface, level); - return true; - } + for (int step = 1; step <= r; step++) { + addRingSamples(center, out, seen, step, sectors, offset, maxDistanceSq, max); + if (out.size() >= max) { + return out; + } - private boolean consumeCatalystFromMainHandIfPresent(org.bukkit.entity.Player player, Material catalyst) { - ItemStack held = player.getInventory().getItemInMainHand(); - if (!isItem(held)) { - // Some server flows decrement the placed stack before cancelled placement is finalized. - // In that case, allow activation without double-consuming. - return true; + // Offset pass fills sector rounding gaps so rings look uniform. + addRingSamples(center, out, seen, step, sectors, offset + (Math.PI / sectors), maxDistanceSq, max); + if (out.size() >= max) { + return out; + } + } + + fillRemainingFromCircle(center, out, seen, r, offset, maxDistanceSq, max); + return out; + } + + private void addRingSamples(Block center, List out, Set seen, int step, int sectors, double offset, double maxDistanceSq, int max) { + for (int i = 0; i < sectors && out.size() < max; i++) { + double angle = offset + ((Math.PI * 2D) * i / sectors); + int dx = (int) Math.round(Math.cos(angle) * step); + int dz = (int) Math.round(Math.sin(angle) * step); + if (dx == 0 && dz == 0) { + continue; + } + + if ((dx * dx) + (dz * dz) > maxDistanceSq) { + continue; + } + + Block block = center.getRelative(dx, 0, dz); + if (seen.add(key(block))) { + out.add(block); + } + } + } + + private void fillRemainingFromCircle(Block center, List out, Set seen, int r, double offset, double maxDistanceSq, int max) { + List candidates = new ArrayList<>(); + for (int dx = -r; dx <= r; dx++) { + for (int dz = -r; dz <= r; dz++) { + if (dx == 0 && dz == 0) { + continue; } - if (held.getType() != catalyst) { - return true; + if ((dx * dx) + (dz * dz) > maxDistanceSq) { + continue; } - return consumeOne(held); + candidates.add(new int[]{dx, dz}); + } } - private Material resolveSpreadSurface(Material floorType) { - if (floorType == Material.MYCELIUM) { - return Material.MYCELIUM; - } + candidates.sort(Comparator.comparingInt(v -> (v[0] * v[0]) + (v[1] * v[1])) + .thenComparingDouble(v -> normalizeAngle(Math.atan2(v[1], v[0]) - offset))); - if (floorType == Material.PODZOL) { - return Material.PODZOL; - } + for (int[] c : candidates) { + if (out.size() >= max) { + return; + } - return null; + Block block = center.getRelative(c[0], 0, c[1]); + if (seen.add(key(block))) { + out.add(block); + } } + } - private int getGuaranteedReach(int level) { - return level >= 5 ? 6 : 0; - } + private double normalizeAngle(double angle) { + double out = angle % (Math.PI * 2D); + return out < 0 ? out + (Math.PI * 2D) : out; + } - private boolean isConvertibleSoil(Material type) { - return type == Material.DIRT - || type == Material.GRASS_BLOCK - || type == Material.COARSE_DIRT - || type == Material.ROOTED_DIRT - || type == Material.MYCELIUM - || type == Material.PODZOL; + private int spreadAt(Block floor, Material catalyst, Material spreadSurface) { + int changed = 0; + Block ground = resolveTopSurfaceSoil(floor); + if (ground == null) { + return 0; } + Block above = ground.getRelative(0, 1, 0); - private boolean isFlower(Material type) { - String n = type.name(); - return n.endsWith("_FLOWER") - || n.endsWith("_TULIP") - || type == Material.DANDELION - || type == Material.POPPY - || type == Material.BLUE_ORCHID - || type == Material.ALLIUM - || type == Material.AZURE_BLUET - || type == Material.OXEYE_DAISY - || type == Material.CORNFLOWER - || type == Material.LILY_OF_THE_VALLEY - || type == Material.WITHER_ROSE - || type == Material.SUNFLOWER - || type == Material.LILAC - || type == Material.ROSE_BUSH - || type == Material.PEONY - || type == Material.TORCHFLOWER - || type == Material.PINK_PETALS - || type == Material.SPORE_BLOSSOM; + if (spreadSurface != null && isConvertibleSoil(ground.getType()) && ground.getType() != spreadSurface) { + ground.setType(spreadSurface, false); + changed++; } - private Material getFlowerReplacement(Material flower, Material catalyst) { - ThreadLocalRandom random = ThreadLocalRandom.current(); - if (isWarmFlower(flower)) { - // Warm flowers lean red. - return random.nextDouble() <= 0.7 ? Material.RED_MUSHROOM : Material.BROWN_MUSHROOM; - } - - if (isCoolFlower(flower)) { - // Cool flowers lean brown. - return random.nextDouble() <= 0.7 ? Material.BROWN_MUSHROOM : Material.RED_MUSHROOM; - } - - // Fallback uses catalyst flavor. - return catalyst == Material.BROWN_MUSHROOM ? Material.BROWN_MUSHROOM : Material.RED_MUSHROOM; + if (getConfig().swapFlowersToMushrooms && isFlower(above.getType())) { + Material replacement = getFlowerReplacement(above.getType(), catalyst); + if (replacement != null && above.getType() != replacement) { + above.setType(replacement, false); + changed++; + } } - private boolean isWarmFlower(Material flower) { - return flower == Material.DANDELION - || flower == Material.POPPY - || flower == Material.RED_TULIP - || flower == Material.ORANGE_TULIP - || flower == Material.PINK_TULIP - || flower == Material.SUNFLOWER - || flower == Material.ROSE_BUSH - || flower == Material.PEONY - || flower == Material.WITHER_ROSE - || flower == Material.TORCHFLOWER - || flower == Material.PINK_PETALS; - } + return changed; + } - private boolean isCoolFlower(Material flower) { - return flower == Material.BLUE_ORCHID - || flower == Material.ALLIUM - || flower == Material.AZURE_BLUET - || flower == Material.WHITE_TULIP - || flower == Material.OXEYE_DAISY - || flower == Material.CORNFLOWER - || flower == Material.LILY_OF_THE_VALLEY - || flower == Material.LILAC - || flower == Material.SPORE_BLOSSOM; - } + private Block resolveTopSurfaceSoil(Block sample) { + int x = sample.getX(); + int z = sample.getZ(); + int highestY = sample.getWorld().getHighestBlockYAt(x, z); + int minY = sample.getWorld().getMinHeight(); - private boolean isWoodLike(Material type) { - String n = type.name(); - return n.endsWith("_LOG") || n.endsWith("_WOOD") || n.endsWith("_STEM") || n.endsWith("_HYPHAE") || n.endsWith("_PLANKS"); - } + for (int y = highestY; y >= minY; y--) { + Block block = sample.getWorld().getBlockAt(x, y, z); + if (!isConvertibleSoil(block.getType())) { + continue; + } - private boolean isReplaceablePlant(Material type) { - if (type == Material.RED_MUSHROOM || type == Material.BROWN_MUSHROOM || type == Material.CRIMSON_FUNGUS || type == Material.WARPED_FUNGUS) { - return true; - } - - String n = type.name(); - return n.endsWith("_FLOWER") - || n.endsWith("_TULIP") - || n.endsWith("_SAPLING") - || n.endsWith("_SEEDS") - || n.endsWith("_ROOTS") - || n.endsWith("_CROP") - || n.equals("TALL_GRASS") - || n.equals("GRASS") - || n.equals("FERN") - || n.equals("LARGE_FERN") - || n.equals("WHEAT") - || n.equals("CARROTS") - || n.equals("POTATOES") - || n.equals("BEETROOTS") - || n.equals("NETHER_WART") - || n.equals("TORCHFLOWER_CROP") - || n.equals("PITCHER_CROP"); + Block above = block.getRelative(0, 1, 0); + if (above.getType().isAir() || isReplaceablePlant(above.getType()) || isFlower(above.getType())) { + return block; + } } - private String key(Block block) { - return block.getX() + ":" + block.getY() + ":" + block.getZ(); - } + return null; + } - private int getBloomAttempts(int level) { - double scaled = getConfig().bloomAttemptsBase + (getLevelPercent(level) * getConfig().bloomAttemptsFactor); - double perLevel = Math.max(0, level - 1) * getConfig().bloomAttemptsPerLevel; - return Math.max(1, (int) Math.round(scaled + perLevel)); - } + private boolean isBloomFloor(Material type) { + return type == Material.MYCELIUM || type == Material.PODZOL; + } - private double getBloomRadius(int level) { - double radius = getConfig().bloomRadiusBase + (getLevelPercent(level) * getConfig().bloomRadiusFactor); - if (level >= 5) { - radius = Math.max(6D, radius); - } - return radius; - } + private boolean isSporeItem(ItemStack hand) { + return isItem(hand) && (hand.getType() == Material.RED_MUSHROOM || hand.getType() == Material.BROWN_MUSHROOM); + } - private int getSpokes(int level) { - return Math.max(4, (int) Math.round(getConfig().spokesBase + (getLevelPercent(level) * getConfig().spokesFactor))); + private boolean consumeOne(ItemStack hand) { + if (hand.getAmount() <= 0) { + return false; } - private int getBlocksPerPulse(int level) { - return Math.max(1, (int) Math.round(getConfig().blocksPerPulseBase + (getLevelPercent(level) * getConfig().blocksPerPulseFactor))); - } + hand.setAmount(hand.getAmount() - 1); + return true; + } - private int getSpreadIntervalTicks(int level) { - return Math.max(1, (int) Math.round(getConfig().spreadIntervalTicksBase - (getLevelPercent(level) * getConfig().spreadIntervalTicksFactor))); + private boolean attemptBloom(org.bukkit.entity.Player player, Block center, Material catalyst) { + Material spreadSurface = resolveSpreadSurface(center.getType()); + if (spreadSurface == null) { + return false; } - private int getFoodCost(int level) { - return Math.max(1, (int) Math.round(getConfig().foodCostBase - (getLevelPercent(level) * getConfig().foodCostFactor))); + int level = getActiveLevel(player); + long now = System.currentTimeMillis(); + long ready = cooldowns.getOrDefault(player.getUniqueId(), 0L); + if (now < ready) { + SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.75f); + return false; } - private long getCooldownMillis(int level) { - return Math.max(250L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + if (player.getFoodLevel() < getFoodCost(level)) { + SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.75f); + return false; } - @Override - public void onTick() { - + if (!consumeCatalystFromMainHandIfPresent(player, catalyst)) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + player.setFoodLevel(Math.max(0, player.getFoodLevel() - getFoodCost(level))); + cooldowns.put(player.getUniqueId(), now + getCooldownMillis(level)); - @NoArgsConstructor - @ConfigDescription("Sneak-right-click mycelium with mushrooms to spread an outward spore-web that mutates nearby growth.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Convert Wood To Hyphae for the Herbalism Spore Bloom adaptation.", impact = "True enables this behavior and false disables it.") - boolean convertWoodToHyphae = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows flowers hit by the bloom to be replaced with mushrooms.", impact = "Disable this to keep flowers untouched while still converting soil into mushroom blocks.") - boolean swapFlowersToMushrooms = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Branch Chance for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double branchChance = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mushroom Choices for the Herbalism Spore Bloom adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String[] mushroomChoices = {"RED_MUSHROOM", "BROWN_MUSHROOM", "CRIMSON_FUNGUS", "WARPED_FUNGUS"}; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bloomAttemptsBase = 26; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bloomAttemptsFactor = 58; - @art.arcane.adapt.util.config.ConfigDoc(value = "Additional bloom attempts granted each adaptation level.", impact = "Higher values make each level spread across more total blocks.") - double bloomAttemptsPerLevel = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bloomRadiusBase = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bloomRadiusFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spokesBase = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spokesFactor = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double blocksPerPulseBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double blocksPerPulseFactor = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spreadIntervalTicksBase = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spreadIntervalTicksFactor = 1.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodCostBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodCostFactor = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 1700; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 1100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mushroom Placed for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerMushroomPlaced = 1.4; + if (areParticlesEnabled()) { + center.getWorld().spawnParticle(Particle.SPORE_BLOSSOM_AIR, center.getLocation().add(0.5, 1.0, 0.5), 30, 0.35, 0.15, 0.35, 0.01); } + SoundPlayer.of(center.getWorld()).play(center.getLocation().add(0.5, 0.5, 0.5), Sound.ENTITY_ENDERMAN_AMBIENT, 0.45f, 0.55f); + startBloom(player, center, catalyst, spreadSurface, level); + return true; + } + + private boolean consumeCatalystFromMainHandIfPresent(org.bukkit.entity.Player player, Material catalyst) { + ItemStack held = player.getInventory().getItemInMainHand(); + if (!isItem(held)) { + // Some server flows decrement the placed stack before cancelled placement is finalized. + // In that case, allow activation without double-consuming. + return true; + } + + if (held.getType() != catalyst) { + return true; + } + + return consumeOne(held); + } + + private Material resolveSpreadSurface(Material floorType) { + if (floorType == Material.MYCELIUM) { + return Material.MYCELIUM; + } + + if (floorType == Material.PODZOL) { + return Material.PODZOL; + } + + return null; + } + + private int getGuaranteedReach(int level) { + return level >= 5 ? 6 : 0; + } + + private boolean isConvertibleSoil(Material type) { + return type == Material.DIRT + || type == Material.GRASS_BLOCK + || type == Material.COARSE_DIRT + || type == Material.ROOTED_DIRT + || type == Material.MYCELIUM + || type == Material.PODZOL; + } + + private boolean isFlower(Material type) { + String n = type.name(); + return n.endsWith("_FLOWER") + || n.endsWith("_TULIP") + || type == Material.DANDELION + || type == Material.POPPY + || type == Material.BLUE_ORCHID + || type == Material.ALLIUM + || type == Material.AZURE_BLUET + || type == Material.OXEYE_DAISY + || type == Material.CORNFLOWER + || type == Material.LILY_OF_THE_VALLEY + || type == Material.WITHER_ROSE + || type == Material.SUNFLOWER + || type == Material.LILAC + || type == Material.ROSE_BUSH + || type == Material.PEONY + || type == Material.TORCHFLOWER + || type == Material.PINK_PETALS + || type == Material.SPORE_BLOSSOM; + } + + private Material getFlowerReplacement(Material flower, Material catalyst) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (isWarmFlower(flower)) { + // Warm flowers lean red. + return random.nextDouble() <= 0.7 ? Material.RED_MUSHROOM : Material.BROWN_MUSHROOM; + } + + if (isCoolFlower(flower)) { + // Cool flowers lean brown. + return random.nextDouble() <= 0.7 ? Material.BROWN_MUSHROOM : Material.RED_MUSHROOM; + } + + // Fallback uses catalyst flavor. + return catalyst == Material.BROWN_MUSHROOM ? Material.BROWN_MUSHROOM : Material.RED_MUSHROOM; + } + + private boolean isWarmFlower(Material flower) { + return flower == Material.DANDELION + || flower == Material.POPPY + || flower == Material.RED_TULIP + || flower == Material.ORANGE_TULIP + || flower == Material.PINK_TULIP + || flower == Material.SUNFLOWER + || flower == Material.ROSE_BUSH + || flower == Material.PEONY + || flower == Material.WITHER_ROSE + || flower == Material.TORCHFLOWER + || flower == Material.PINK_PETALS; + } + + private boolean isCoolFlower(Material flower) { + return flower == Material.BLUE_ORCHID + || flower == Material.ALLIUM + || flower == Material.AZURE_BLUET + || flower == Material.WHITE_TULIP + || flower == Material.OXEYE_DAISY + || flower == Material.CORNFLOWER + || flower == Material.LILY_OF_THE_VALLEY + || flower == Material.LILAC + || flower == Material.SPORE_BLOSSOM; + } + + private boolean isWoodLike(Material type) { + String n = type.name(); + return n.endsWith("_LOG") || n.endsWith("_WOOD") || n.endsWith("_STEM") || n.endsWith("_HYPHAE") || n.endsWith("_PLANKS"); + } + + private boolean isReplaceablePlant(Material type) { + if (type == Material.RED_MUSHROOM || type == Material.BROWN_MUSHROOM || type == Material.CRIMSON_FUNGUS || type == Material.WARPED_FUNGUS) { + return true; + } + + String n = type.name(); + return n.endsWith("_FLOWER") + || n.endsWith("_TULIP") + || n.endsWith("_SAPLING") + || n.endsWith("_SEEDS") + || n.endsWith("_ROOTS") + || n.endsWith("_CROP") + || n.equals("TALL_GRASS") + || n.equals("GRASS") + || n.equals("FERN") + || n.equals("LARGE_FERN") + || n.equals("WHEAT") + || n.equals("CARROTS") + || n.equals("POTATOES") + || n.equals("BEETROOTS") + || n.equals("NETHER_WART") + || n.equals("TORCHFLOWER_CROP") + || n.equals("PITCHER_CROP"); + } + + private String key(Block block) { + return block.getX() + ":" + block.getY() + ":" + block.getZ(); + } + + private int getBloomAttempts(int level) { + double scaled = getConfig().bloomAttemptsBase + (getLevelPercent(level) * getConfig().bloomAttemptsFactor); + double perLevel = Math.max(0, level - 1) * getConfig().bloomAttemptsPerLevel; + return Math.max(1, (int) Math.round(scaled + perLevel)); + } + + private double getBloomRadius(int level) { + double radius = getConfig().bloomRadiusBase + (getLevelPercent(level) * getConfig().bloomRadiusFactor); + if (level >= 5) { + radius = Math.max(6D, radius); + } + return radius; + } + + private int getSpokes(int level) { + return Math.max(4, (int) Math.round(getConfig().spokesBase + (getLevelPercent(level) * getConfig().spokesFactor))); + } + + private int getBlocksPerPulse(int level) { + return Math.max(1, (int) Math.round(getConfig().blocksPerPulseBase + (getLevelPercent(level) * getConfig().blocksPerPulseFactor))); + } + + private int getSpreadIntervalTicks(int level) { + return Math.max(1, (int) Math.round(getConfig().spreadIntervalTicksBase - (getLevelPercent(level) * getConfig().spreadIntervalTicksFactor))); + } + + private int getFoodCost(int level) { + return Math.max(1, (int) Math.round(getConfig().foodCostBase - (getLevelPercent(level) * getConfig().foodCostFactor))); + } + + private long getCooldownMillis(int level) { + return Math.max(250L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click mycelium with mushrooms to spread an outward spore-web that mutates nearby growth.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Convert Wood To Hyphae for the Herbalism Spore Bloom adaptation.", impact = "True enables this behavior and false disables it.") + boolean convertWoodToHyphae = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows flowers hit by the bloom to be replaced with mushrooms.", impact = "Disable this to keep flowers untouched while still converting soil into mushroom blocks.") + boolean swapFlowersToMushrooms = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Branch Chance for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double branchChance = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mushroom Choices for the Herbalism Spore Bloom adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String[] mushroomChoices = {"RED_MUSHROOM", "BROWN_MUSHROOM", "CRIMSON_FUNGUS", "WARPED_FUNGUS"}; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bloomAttemptsBase = 26; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Attempts Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bloomAttemptsFactor = 58; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional bloom attempts granted each adaptation level.", impact = "Higher values make each level spread across more total blocks.") + double bloomAttemptsPerLevel = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bloomRadiusBase = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bloom Radius Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bloomRadiusFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spokesBase = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spokes Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spokesFactor = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double blocksPerPulseBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blocks Per Pulse Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double blocksPerPulseFactor = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spreadIntervalTicksBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spread Interval Ticks Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spreadIntervalTicksFactor = 1.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodCostBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Cost Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodCostFactor = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 1700; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 1100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mushroom Placed for the Herbalism Spore Bloom adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMushroomPlaced = 1.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java index 3a1d4310c..80f5166de 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismTerralid.java @@ -26,8 +26,8 @@ import art.arcane.adapt.api.recipe.MaterialChar; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -40,81 +40,81 @@ public class HerbalismTerralid extends SimpleAdaptation { - public HerbalismTerralid() { - super("herbalism-terralid"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("herbalism.terralid.description")); - setDisplayName(Localizer.dLocalize("herbalism.terralid.name")); - setIcon(Material.GRASS_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(17771); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("herbalism-dirt-terralid") - .ingredient(new MaterialChar('S', Material.WHEAT_SEEDS)) - .ingredient(new MaterialChar('D', Material.DIRT)) - .shapes(List.of( - "SSS", - "DDD")) - .result(new ItemStack(Material.GRASS_BLOCK, 3)) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GRASS_BLOCK) - .key("challenge_herbalism_terralid_200") - .title(Localizer.dLocalize("advancement.challenge_herbalism_terralid_200.title")) - .description(Localizer.dLocalize("advancement.challenge_herbalism_terralid_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_herbalism_terralid_200", "herbalism.terralid.grass-crafted", 200, 300); - } + public HerbalismTerralid() { + super("herbalism-terralid"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("herbalism.terralid.description")); + setDisplayName(Localizer.dLocalize("herbalism.terralid.name")); + setIcon(Material.GRASS_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(17771); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("herbalism-dirt-terralid") + .ingredient(new MaterialChar('S', Material.WHEAT_SEEDS)) + .ingredient(new MaterialChar('D', Material.DIRT)) + .shapes(List.of( + "SSS", + "DDD")) + .result(new ItemStack(Material.GRASS_BLOCK, 3)) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GRASS_BLOCK) + .key("challenge_herbalism_terralid_200") + .title(Localizer.dLocalize("advancement.challenge_herbalism_terralid_200.title")) + .description(Localizer.dLocalize("advancement.challenge_herbalism_terralid_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_herbalism_terralid_200", "herbalism.terralid.grass-crafted", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.terralid.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + Localizer.dLocalize("herbalism.terralid.lore1")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { - return; - } - if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-terralid")) { - getPlayer(p).getData().addStat("herbalism.terralid.grass-crafted", 1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p) || !hasActiveAdaptation(p)) { + return; } - - @Override - public void onTick() { + if (e.getRecipe() instanceof org.bukkit.inventory.ShapedRecipe recipe && recipe.getKey().getNamespace().equals("adapt") && recipe.getKey().getKey().equals("herbalism-dirt-terralid")) { + getPlayer(p).getData().addStat("herbalism.terralid.grass-crafted", 1); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Craft Grass Blocks from Seeds and Dirt.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft Grass Blocks from Seeds and Dirt.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java index a044f83a0..43be2baa2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterAdrenaline.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -34,99 +34,99 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; public class HunterAdrenaline extends SimpleAdaptation { - public HunterAdrenaline() { - super("hunter-adrenaline"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.adrenaline.description")); - setDisplayName(Localizer.dLocalize("hunter.adrenaline.name")); - setIcon(Material.LEATHER_HELMET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1911); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_hunter_adrenaline_100") - .title(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_100.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_hunter_adrenaline_2500") - .title(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_hunter_adrenaline_100", "hunter.adrenaline.low-health-kills", 100, 400); - registerMilestone("challenge_hunter_adrenaline_2500", "hunter.adrenaline.low-health-kills", 2500, 1500); - } + public HunterAdrenaline() { + super("hunter-adrenaline"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.adrenaline.description")); + setDisplayName(Localizer.dLocalize("hunter.adrenaline.name")); + setIcon(Material.LEATHER_HELMET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1911); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_hunter_adrenaline_100") + .title(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_100.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_hunter_adrenaline_2500") + .title(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_adrenaline_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_hunter_adrenaline_100", "hunter.adrenaline.low-health-kills", 100, 400); + registerMilestone("challenge_hunter_adrenaline_2500", "hunter.adrenaline.low-health-kills", 2500, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getDamage(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.adrenaline.lore1")); - } - - private double getDamage(int level) { - return ((getLevelPercent(level) * getConfig().damageFactor) + getConfig().damageBase); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getDamage(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.adrenaline.lore1")); + } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } + private double getDamage(int level) { + return ((getLevelPercent(level) * getConfig().damageFactor) + getConfig().damageBase); + } - Player p = attack.attacker(); - double damageMax = getDamage(attack.level()); - double hpp = p.getHealth() / p.getMaxHealth(); + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } - if (hpp >= 1) { - return; - } + Player p = attack.attacker(); + double damageMax = getDamage(attack.level()); + double hpp = p.getHealth() / p.getMaxHealth(); - damageMax *= (1D - hpp); - e.setDamage(e.getDamage() * (damageMax + 1D)); - getPlayer(p).getData().addStat("hunter.adrenaline.low-health-kills", 1); + if (hpp >= 1) { + return; } - @Override - public void onTick() { + damageMax *= (1D - hpp); + e.setDamage(e.getDamage() * (damageMax + 1D)); + getPlayer(p).getData().addStat("hunter.adrenaline.low-health-kills", 1); + } - } + @Override + public void onTick() { - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Deal more melee damage the lower your health is.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 0.21; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Deal more melee damage the lower your health is.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Hunter Adrenaline adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 0.21; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java index d9ede317a..302757191 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterDropToInventory.java @@ -25,10 +25,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,108 +44,108 @@ import java.util.List; public class HunterDropToInventory extends SimpleAdaptation { - public HunterDropToInventory() { - super("hunter-drop-to-inventory"); - registerConfiguration(HunterDropToInventory.Config.class); - setDescription(Localizer.dLocalize("hunter.drop_to_inventory.description")); - setDisplayName(Localizer.dLocalize("hunter.drop_to_inventory.name")); - setIcon(Material.TRAPPED_CHEST); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(18440); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_hunter_dti_10k") - .title(Localizer.dLocalize("advancement.challenge_hunter_dti_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_dti_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_dti_10k", "hunter.drop-to-inv.items-caught", 10000, 500); - } + public HunterDropToInventory() { + super("hunter-drop-to-inventory"); + registerConfiguration(HunterDropToInventory.Config.class); + setDescription(Localizer.dLocalize("hunter.drop_to_inventory.description")); + setDisplayName(Localizer.dLocalize("hunter.drop_to_inventory.name")); + setIcon(Material.TRAPPED_CHEST); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(18440); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_hunter_dti_10k") + .title(Localizer.dLocalize("advancement.challenge_hunter_dti_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_dti_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_dti_10k", "hunter.drop-to-inv.items-caught", 10000, 500); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.drop_to_inventory.lore1")); - } + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.drop_to_inventory.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDropItemEvent e) { + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - if (resolveInteractContext(p, e.getBlock().getLocation(), null, true) == null - || !canPVP(p, e.getBlock().getLocation())) { - return; - } + Player p = e.getPlayer(); + if (resolveInteractContext(p, e.getBlock().getLocation(), null, true) == null + || !canPVP(p, e.getBlock().getLocation())) { + return; + } - SoundPlayer sp = SoundPlayer.of(p); - if (ItemListings.toolSwords.contains(p.getInventory().getItemInMainHand().getType())) { - List items = new KList<>(e.getItems()); - e.getItems().clear(); - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - for (Item i : items) { - if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i.getItemStack()); - } - } - getPlayer(p).getData().addStat("hunter.drop-to-inv.items-caught", items.size()); + SoundPlayer sp = SoundPlayer.of(p); + if (ItemListings.toolSwords.contains(p.getInventory().getItemInMainHand().getType())) { + List items = new KList<>(e.getItems()); + e.getItems().clear(); + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + for (Item i : items) { + if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i.getItemStack()); } + } + getPlayer(p).getData().addStat("hunter.drop-to-inv.items-caught", items.size()); } + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDeathEvent e) { - LivingEntity k = e.getEntity(); - if (k.getKiller() == null || k.getKiller().getType() != EntityType.PLAYER) { - return; - } - Player p = k.getKiller(); - if (e.getEntity() instanceof Player || getActiveDamageLevel(p, e.getEntity()) <= 0) { - return; - } - if (e.getEntity().getKiller() != null && e.getEntity().getKiller().getClass().getSimpleName().equals("CraftPlayer")) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - int itemCount = e.getDrops().size(); - e.getDrops().forEach(i -> { - if (!p.getInventory().addItem(i).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i); - } - }); - e.getDrops().clear(); - getPlayer(p).getData().addStat("hunter.drop-to-inv.items-caught", itemCount); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDeathEvent e) { + LivingEntity k = e.getEntity(); + if (k.getKiller() == null || k.getKiller().getType() != EntityType.PLAYER) { + return; + } + Player p = k.getKiller(); + if (e.getEntity() instanceof Player || getActiveDamageLevel(p, e.getEntity()) <= 0) { + return; + } + if (e.getEntity().getKiller() != null && e.getEntity().getKiller().getClass().getSimpleName().equals("CraftPlayer")) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + int itemCount = e.getDrops().size(); + e.getDrops().forEach(i -> { + if (!p.getInventory().addItem(i).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i); } + }); + e.getDrops().clear(); + getPlayer(p).getData().addStat("hunter.drop-to-inv.items-caught", itemCount); } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Mob and block drops teleport directly into your inventory.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Mob and block drops teleport directly into your inventory.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java index 5f8ed9b2a..4bb8f3565 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterInvis.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -35,123 +35,123 @@ import org.bukkit.potion.PotionEffectType; public class HunterInvis extends SimpleAdaptation { - public HunterInvis() { - super("hunter-invis"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.invisibility.description")); - setDisplayName(Localizer.dLocalize("hunter.invisibility.name")); - setIcon(Material.TROPICAL_FISH_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9444); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_hunter_invis_200") - .title(Localizer.dLocalize("advancement.challenge_hunter_invis_200.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_invis_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_invis_200", "hunter.invis.activations", 200, 300); - } + public HunterInvis() { + super("hunter-invis"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.invisibility.description")); + setDisplayName(Localizer.dLocalize("hunter.invisibility.name")); + setIcon(Material.TROPICAL_FISH_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9444); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_hunter_invis_200") + .title(Localizer.dLocalize("advancement.challenge_hunter_invis_200.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_invis_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_invis_200", "hunter.invis.activations", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.invisibility.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.invisibility.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.invisibility.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.invisibility.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.invisibility.lore5")); - v.addLore(C.RED + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.penalty.lore1")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.invisibility.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.invisibility.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.invisibility.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.invisibility.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.invisibility.lore5")); + v.addLore(C.RED + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.penalty.lore1")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectType.INVISIBILITY, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.invis.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectType.INVISIBILITY, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.invis.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectType.INVISIBILITY, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.invis.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectType.INVISIBILITY, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.invis.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain invisibility when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Invis adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain invisibility when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Invis adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Invis adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Invis adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java index 949041c4a..2f80ef87f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterJumpBoost.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -36,124 +36,124 @@ import org.bukkit.potion.PotionEffectType; public class HunterJumpBoost extends SimpleAdaptation { - public HunterJumpBoost() { - super("hunter-jumpboost"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.jump_boost.description")); - setDisplayName(Localizer.dLocalize("hunter.jump_boost.name")); - setIcon(Material.PUFFERFISH_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9544); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RABBIT_FOOT) - .key("challenge_hunter_jump_200") - .title(Localizer.dLocalize("advancement.challenge_hunter_jump_200.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_jump_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_jump_200", "hunter.jump-boost.activations", 200, 300); - } + public HunterJumpBoost() { + super("hunter-jumpboost"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.jump_boost.description")); + setDisplayName(Localizer.dLocalize("hunter.jump_boost.name")); + setIcon(Material.PUFFERFISH_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9544); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RABBIT_FOOT) + .key("challenge_hunter_jump_200") + .title(Localizer.dLocalize("advancement.challenge_hunter_jump_200.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_jump_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_jump_200", "hunter.jump-boost.activations", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.jump_boost.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.jump_boost.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.jump_boost.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.jump_boost.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.jump_boost.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectTypes.JUMP, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.jump-boost.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectTypes.JUMP, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.jump-boost.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectTypes.JUMP, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.jump-boost.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectTypes.JUMP, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.jump-boost.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain jump boost when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Jump Boost adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain jump boost when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Jump Boost adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Jump Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Jump Boost adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java index 9d56ea8d9..bf966c720 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterLuck.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -35,126 +35,126 @@ import org.bukkit.potion.PotionEffectType; public class HunterLuck extends SimpleAdaptation { - public HunterLuck() { - super("hunter-luck"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.luck.description")); - setDisplayName(Localizer.dLocalize("hunter.luck.name")); - setIcon(Material.TADPOLE_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9644); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EMERALD) - .key("challenge_hunter_luck_200") - .title(Localizer.dLocalize("advancement.challenge_hunter_luck_200.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_luck_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_luck_200", "hunter.luck.activations", 200, 300); - } + public HunterLuck() { + super("hunter-luck"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.luck.description")); + setDisplayName(Localizer.dLocalize("hunter.luck.name")); + setIcon(Material.TADPOLE_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9644); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EMERALD) + .key("challenge_hunter_luck_200") + .title(Localizer.dLocalize("advancement.challenge_hunter_luck_200.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_luck_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_luck_200", "hunter.luck.activations", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.luck.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.luck.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.luck.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.luck.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.luck.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.luck.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.luck.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.luck.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.luck.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.luck.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - addPotionStacks(p, PotionEffectType.UNLUCK, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + addPotionStacks(p, PotionEffectType.UNLUCK, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectType.LUCK, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.luck.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectType.LUCK, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.luck.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - addPotionStacks(p, PotionEffectType.UNLUCK, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectType.LUCK, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.luck.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectType.LUCK, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.luck.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + addPotionStacks(p, PotionEffectType.UNLUCK, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain luck when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Luck adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain luck when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Luck adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Luck adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Luck adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java index 40bd61b88..c8fd0b4ef 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterRegen.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -35,124 +35,124 @@ import org.bukkit.potion.PotionEffectType; public class HunterRegen extends SimpleAdaptation { - public HunterRegen() { - super("hunter-regen"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.regen.description")); - setDisplayName(Localizer.dLocalize("hunter.regen.name")); - setIcon(Material.AXOLOTL_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9744); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_hunter_regen_500") - .title(Localizer.dLocalize("advancement.challenge_hunter_regen_500.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_regen_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_regen_500", "hunter.regen.health-regened", 500, 400); - } + public HunterRegen() { + super("hunter-regen"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.regen.description")); + setDisplayName(Localizer.dLocalize("hunter.regen.name")); + setIcon(Material.AXOLOTL_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9744); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_hunter_regen_500") + .title(Localizer.dLocalize("advancement.challenge_hunter_regen_500.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_regen_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_regen_500", "hunter.regen.health-regened", 500, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.regen.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.regen.lore2")); - v.addLore(C.RED + "- " + (getConfig().basePoisonFromLevel - level) + C.GRAY + Localizer.dLocalize("hunter.regen.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.regen.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.regen.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.regen.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.regen.lore2")); + v.addLore(C.RED + "- " + (getConfig().basePoisonFromLevel - level) + C.GRAY + Localizer.dLocalize("hunter.regen.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.regen.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.regen.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectType.REGENERATION, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.regen.health-regened", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectType.REGENERATION, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.regen.health-regened", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectType.REGENERATION, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.regen.health-regened", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectType.REGENERATION, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.regen.health-regened", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain regeneration when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Regen adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain regeneration when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Regen adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Regen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Regen adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java index 79664e6fd..247817d20 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterResistance.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -36,124 +36,124 @@ import org.bukkit.potion.PotionEffectType; public class HunterResistance extends SimpleAdaptation { - public HunterResistance() { - super("hunter-resistance"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.resistance.description")); - setDisplayName(Localizer.dLocalize("hunter.resistance.name")); - setIcon(Material.POWDER_SNOW_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9844); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_hunter_resistance_500") - .title(Localizer.dLocalize("advancement.challenge_hunter_resistance_500.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_resistance_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_resistance_500", "hunter.resistance.activations", 500, 400); - } + public HunterResistance() { + super("hunter-resistance"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.resistance.description")); + setDisplayName(Localizer.dLocalize("hunter.resistance.name")); + setIcon(Material.POWDER_SNOW_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9844); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_hunter_resistance_500") + .title(Localizer.dLocalize("advancement.challenge_hunter_resistance_500.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_resistance_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_resistance_500", "hunter.resistance.activations", 500, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.resistance.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.resistance.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.resistance.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.resistance.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.resistance.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.resistance.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.resistance.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.resistance.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.resistance.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.resistance.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectTypes.DAMAGE_RESISTANCE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.resistance.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectTypes.DAMAGE_RESISTANCE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.resistance.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectTypes.DAMAGE_RESISTANCE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.resistance.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectTypes.DAMAGE_RESISTANCE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.resistance.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain resistance when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Resistance adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain resistance when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Resistance adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Resistance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Resistance adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java index 2b2b997a7..22a95d012 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterSpeed.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -43,280 +43,280 @@ import java.util.UUID; public class HunterSpeed extends SimpleAdaptation { - private final Map speedBursts = new java.util.concurrent.ConcurrentHashMap<>(); - - public HunterSpeed() { - super("hunter-speed"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.speed.description")); - setDisplayName(Localizer.dLocalize("hunter.speed.name")); - setIcon(Material.SUGAR); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(getConfig().setInterval); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SUGAR) - .key("challenge_hunter_speed_200") - .title(Localizer.dLocalize("advancement.challenge_hunter_speed_200.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_speed_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_speed_200", "hunter.speed.activations", 200, 300); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.speed.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.speed.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.speed.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.speed.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.speed.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - + private final Map speedBursts = new java.util.concurrent.ConcurrentHashMap<>(); + + public HunterSpeed() { + super("hunter-speed"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.speed.description")); + setDisplayName(Localizer.dLocalize("hunter.speed.name")); + setIcon(Material.SUGAR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(getConfig().setInterval); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SUGAR) + .key("challenge_hunter_speed_200") + .title(Localizer.dLocalize("advancement.challenge_hunter_speed_200.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_speed_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_speed_200", "hunter.speed.activations", 200, 300); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.speed.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.speed.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.speed.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.speed.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.speed.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + + } + + + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } + + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } + + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + grantSpeedBurst(p, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.speed.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + grantSpeedBurst(p, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.speed.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } + } + } + } } + } + @EventHandler + public void on(PlayerQuitEvent e) { + speedBursts.remove(e.getPlayer().getUniqueId()); + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(PlayerDeathEvent e) { + speedBursts.remove(e.getEntity().getUniqueId()); + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - grantSpeedBurst(p, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.speed.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - grantSpeedBurst(p, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.speed.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } - } - } + private void grantSpeedBurst(org.bukkit.entity.Player p, int amplifier, int durationTicks, boolean overlap) { + if (durationTicks <= 0) { + return; } - @EventHandler - public void on(PlayerQuitEvent e) { - speedBursts.remove(e.getPlayer().getUniqueId()); + UUID id = p.getUniqueId(); + long now = System.currentTimeMillis(); + long durationMs = Math.max(50L, durationTicks * 50L); + SpeedBurst current = speedBursts.get(id); + if (current != null && current.expiresAt > now) { + if (!overlap) { + return; + } + + current.expiresAt += durationMs; + current.amplifier = Math.max(current.amplifier, amplifier); + return; } - @EventHandler - public void on(PlayerDeathEvent e) { - speedBursts.remove(e.getEntity().getUniqueId()); + speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + org.bukkit.entity.Player p = adaptPlayer.getPlayer(); + SpeedBurst burst = speedBursts.get(p.getUniqueId()); + if (burst == null) { + continue; + } + + if (burst.expiresAt <= now) { + invalidateBurst(p, burst, false); + speedBursts.remove(p.getUniqueId()); + continue; + } + + if (!isVelocityEligible(p)) { + invalidateBurst(p, burst, true); + continue; + } + + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeBurst(p, burst); + continue; + } + + applyBurst(p, burst, input); } + } - private void grantSpeedBurst(org.bukkit.entity.Player p, int amplifier, int durationTicks, boolean overlap) { - if (durationTicks <= 0) { - return; - } - - UUID id = p.getUniqueId(); - long now = System.currentTimeMillis(); - long durationMs = Math.max(50L, durationTicks * 50L); - SpeedBurst current = speedBursts.get(id); - if (current != null && current.expiresAt > now) { - if (!overlap) { - return; - } - - current.expiresAt += durationMs; - current.amplifier = Math.max(current.amplifier, amplifier); - return; - } - - speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); + private void applyBurst(org.bukkit.entity.Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { + Vector desiredDirection = VelocitySpeed.resolveHorizontalDirection(p, input); + if (desiredDirection.lengthSquared() <= VelocitySpeed.EPSILON) { + brakeBurst(p, burst); + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - org.bukkit.entity.Player p = adaptPlayer.getPlayer(); - SpeedBurst burst = speedBursts.get(p.getUniqueId()); - if (burst == null) { - continue; - } - - if (burst.expiresAt <= now) { - invalidateBurst(p, burst, false); - speedBursts.remove(p.getUniqueId()); - continue; - } - - if (!isVelocityEligible(p)) { - invalidateBurst(p, burst, true); - continue; - } - - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeBurst(p, burst); - continue; - } - - applyBurst(p, burst, input); - } + double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, + Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); + Vector velocity = p.getVelocity(); + Vector currentHorizontal = VelocitySpeed.horizontalOnly(velocity); + Vector targetHorizontal = desiredDirection.multiply(targetSpeed); + Vector nextHorizontal = VelocitySpeed.moveTowards(currentHorizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); + nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + burst.boosting = true; + } + + private void invalidateBurst(org.bukkit.entity.Player p, SpeedBurst burst, boolean invalidState) { + if (!burst.boosting) { + return; } - private void applyBurst(org.bukkit.entity.Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { - Vector desiredDirection = VelocitySpeed.resolveHorizontalDirection(p, input); - if (desiredDirection.lengthSquared() <= VelocitySpeed.EPSILON) { - brakeBurst(p, burst); - return; - } - - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, - Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); - Vector velocity = p.getVelocity(); - Vector currentHorizontal = VelocitySpeed.horizontalOnly(velocity); - Vector targetHorizontal = desiredDirection.multiply(targetSpeed); - Vector nextHorizontal = VelocitySpeed.moveTowards(currentHorizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - burst.boosting = true; + if (invalidState && getConfig().hardStopOnInvalidState) { + VelocitySpeed.hardStopHorizontal(p); } - private void invalidateBurst(org.bukkit.entity.Player p, SpeedBurst burst, boolean invalidState) { - if (!burst.boosting) { - return; - } + burst.boosting = false; + } - if (invalidState && getConfig().hardStopOnInvalidState) { - VelocitySpeed.hardStopHorizontal(p); - } - - burst.boosting = false; + private void brakeBurst(org.bukkit.entity.Player p, SpeedBurst burst) { + if (!burst.boosting) { + return; } - private void brakeBurst(org.bukkit.entity.Player p, SpeedBurst burst) { - if (!burst.boosting) { - return; - } - - Vector velocity = p.getVelocity(); - Vector currentHorizontal = VelocitySpeed.horizontalOnly(velocity); - double stopThreshold = Math.max(0, getConfig().stopThreshold); - if (currentHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } - - Vector nextHorizontal = VelocitySpeed.moveTowards(currentHorizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); - if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } + Vector velocity = p.getVelocity(); + Vector currentHorizontal = VelocitySpeed.horizontalOnly(velocity); + double stopThreshold = Math.max(0, getConfig().stopThreshold); + if (currentHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; + } - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + Vector nextHorizontal = VelocitySpeed.moveTowards(currentHorizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); + if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; } - private boolean isVelocityEligible(org.bukkit.entity.Player p) { - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + } - return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + private boolean isVelocityEligible(org.bukkit.entity.Player p) { + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; } - private static class SpeedBurst { - private long expiresAt; - private int amplifier; - private boolean boosting; + return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + } - private SpeedBurst(long expiresAt, int amplifier) { - this.expiresAt = expiresAt; - this.amplifier = amplifier; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private static class SpeedBurst { + private long expiresAt; + private int amplifier; + private boolean boosting; - @Override - public boolean isPermanent() { - return getConfig().permanent; + private SpeedBurst(long expiresAt, int amplifier) { + this.expiresAt = expiresAt; + this.amplifier = amplifier; } - @NoArgsConstructor - @ConfigDescription("Gain speed when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update velocity speed bursts.", impact = "Lower values feel more responsive but run updates more frequently.") - long setInterval = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for hunter bursts before amplifier scaling.", impact = "Higher values increase movement speed while a burst is active.") - double baseHorizontalSpeed = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") - double maxHorizontalSpeed = 0.32; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double accelPerTick = 0.045; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") - double brakePerTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny momentum longer.") - double stopThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed when state changes skip expected flow.") - boolean hardStopOnInvalidState = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Speed adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + } + + @NoArgsConstructor + @ConfigDescription("Gain speed when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update velocity speed bursts.", impact = "Lower values feel more responsive but run updates more frequently.") + long setInterval = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Speed adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Speed adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for hunter bursts before amplifier scaling.", impact = "Higher values increase movement speed while a burst is active.") + double baseHorizontalSpeed = 0.13; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + double maxHorizontalSpeed = 0.32; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + double accelPerTick = 0.045; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + double brakePerTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny momentum longer.") + double stopThreshold = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed when state changes skip expected flow.") + boolean hardStopOnInvalidState = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + double fallbackInputVelocityThreshold = 0.0008; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Speed adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + + double fallbackInputVelocityThresholdSquared() { + double threshold = Math.max(0, fallbackInputVelocityThreshold); + return threshold * threshold; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java index 0f9b8ca9a..0a3f0cae9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterStrength.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -36,124 +36,124 @@ import org.bukkit.potion.PotionEffectType; public class HunterStrength extends SimpleAdaptation { - public HunterStrength() { - super("hunter-strength"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.strength.description")); - setDisplayName(Localizer.dLocalize("hunter.strength.name")); - setIcon(Material.COD_BUCKET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(9044); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_hunter_strength_200") - .title(Localizer.dLocalize("advancement.challenge_hunter_strength_200.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_strength_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_hunter_strength_200", "hunter.strength.activations", 200, 300); - } + public HunterStrength() { + super("hunter-strength"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.strength.description")); + setDisplayName(Localizer.dLocalize("hunter.strength.name")); + setIcon(Material.COD_BUCKET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9044); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_hunter_strength_200") + .title(Localizer.dLocalize("advancement.challenge_hunter_strength_200.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_strength_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_hunter_strength_200", "hunter.strength.activations", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("hunter.strength.lore1")); - v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.strength.lore2")); - v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.strength.lore3")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.strength.lore4")); - v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.strength.lore5")); - v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("hunter.strength.lore1")); + v.addLore(C.GREEN + "+ " + level + C.GRAY + Localizer.dLocalize("hunter.strength.lore2")); + v.addLore(C.RED + "- " + (5 + level) + C.GRAY + Localizer.dLocalize("hunter.strength.lore3")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.strength.lore4")); + v.addLore(C.GRAY + "* " + level + C.GRAY + " " + Localizer.dLocalize("hunter.strength.lore5")); + v.addLore(C.GRAY + "- " + level + C.RED + " " + Localizer.dLocalize("hunter.penalty.lore1")); - } + } - @EventHandler - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { - if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { - return; - } + @EventHandler + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof org.bukkit.entity.Player p && isAdaptableDamageCause(e) && hasActiveAdaptation(p)) { + if (AdaptConfig.get().isPreventHunterSkillsWhenHungerApplied() && p.hasPotionEffect(PotionEffectType.HUNGER)) { + return; + } - if (!getConfig().useConsumable) { - if (p.getFoodLevel() == 0) { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } + if (!getConfig().useConsumable) { + if (p.getFoodLevel() == 0) { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); + } - } else { - addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); - addPotionStacks(p, PotionEffectTypes.INCREASE_DAMAGE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.strength.activations", 1); - } - } else { - if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { - Material mat = Material.getMaterial(getConfig().consumable); - if (mat != null && p.getInventory().contains(mat)) { - p.getInventory().removeItem(new ItemStack(mat, 1)); - addPotionStacks(p, PotionEffectTypes.INCREASE_DAMAGE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); - getPlayer(p).getData().addStat("hunter.strength.activations", 1); - } else { - if (getConfig().poisonPenalty) { - addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); - } - } - } + } else { + addPotionStacks(p, PotionEffectType.HUNGER, getConfig().baseHungerFromLevel - getLevel(p), getConfig().baseHungerDuration * getLevel(p), getConfig().stackHungerPenalty); + addPotionStacks(p, PotionEffectTypes.INCREASE_DAMAGE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.strength.activations", 1); + } + } else { + if (getConfig().consumable != null && Material.getMaterial(getConfig().consumable) != null) { + Material mat = Material.getMaterial(getConfig().consumable); + if (mat != null && p.getInventory().contains(mat)) { + p.getInventory().removeItem(new ItemStack(mat, 1)); + addPotionStacks(p, PotionEffectTypes.INCREASE_DAMAGE, getLevel(p), getConfig().baseEffectbyLevel * getLevel(p), getConfig().stackBuff); + getPlayer(p).getData().addStat("hunter.strength.activations", 1); + } else { + if (getConfig().poisonPenalty) { + addPotionStacks(p, PotionEffectType.POISON, getConfig().basePoisonFromLevel - getLevel(p), getConfig().baseHungerDuration, getConfig().stackPoisonPenalty); } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Gain strength when struck, at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") - boolean useConsumable = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") - boolean poisonPenalty = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackHungerPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackPoisonPenalty = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") - boolean stackBuff = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseEffectbyLevel = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerFromLevel = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int basePoisonFromLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseHungerDuration = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Strength adaptation.", impact = "Changing this alters the identifier or text used by the feature.") - String consumable = "ROTTEN_FLESH"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - } + @NoArgsConstructor + @ConfigDescription("Gain strength when struck, at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Use Consumable for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + boolean useConsumable = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + boolean poisonPenalty = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Hunger Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackHungerPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Poison Penalty for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackPoisonPenalty = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stack Buff for the Hunter Strength adaptation.", impact = "True enables this behavior and false disables it.") + boolean stackBuff = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Effectby Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseEffectbyLevel = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerFromLevel = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Poison From Level for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int basePoisonFromLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Hunger Duration for the Hunter Strength adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseHungerDuration = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consumable for the Hunter Strength adaptation.", impact = "Changing this alters the identifier or text used by the feature.") + String consumable = "ROTTEN_FLESH"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java index 1546571c5..0c817d429 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/hunter/HunterTrophySkinner.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,197 +43,197 @@ import java.util.concurrent.ThreadLocalRandom; public class HunterTrophySkinner extends SimpleAdaptation { - public HunterTrophySkinner() { - super("hunter-trophy-skinner"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("hunter.trophy_skinner.description")); - setDisplayName(Localizer.dLocalize("hunter.trophy_skinner.name")); - setIcon(Material.ZOMBIE_HEAD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SKELETON_SKULL) - .key("challenge_hunter_trophy_50") - .title(Localizer.dLocalize("advancement.challenge_hunter_trophy_50.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_trophy_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ZOMBIE_HEAD) - .key("challenge_hunter_trophy_heads_100") - .title(Localizer.dLocalize("advancement.challenge_hunter_trophy_heads_100.title")) - .description(Localizer.dLocalize("advancement.challenge_hunter_trophy_heads_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_hunter_trophy_50", "hunter.trophy-skinner.trophies-collected", 50, 400); - registerMilestone("challenge_hunter_trophy_heads_100", "hunter.trophy-skinner.heads-collected", 100, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getDropChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getHeadChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore2")); - v.addLore(C.YELLOW + "* " + Form.f(getMinimumRange(level), 1) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore3")); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - Player killer = e.getEntity().getKiller(); - if (killer == null || !hasActiveAdaptation(killer) || e.getEntity() instanceof Player || !canDamageTarget(killer, e.getEntity())) { - return; - } - - int level = getActiveLevel(killer); - PrecisionContext precision = readPrecisionContext(e, killer, level); - if (!precision.precise()) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getDropChance(level)) { - return; - } - - ItemStack trophy = buildTrophyDrop(e.getEntityType(), level, precision.projectileKill()); - if (trophy != null) { - e.getDrops().add(trophy); - getPlayer(killer).getData().addStat("hunter.trophy-skinner.trophies-collected", 1); - } - - if (ThreadLocalRandom.current().nextDouble() <= getHeadChance(level)) { - ItemStack head = buildHeadDrop(e.getEntityType()); - if (head != null) { - e.getDrops().add(head); - getPlayer(killer).getData().addStat("hunter.trophy-skinner.heads-collected", 1); - } - } - - SoundPlayer.of(killer).play(killer.getLocation(), Sound.ENTITY_WOLF_SHAKE, 0.55f, 1.35f); - xp(killer, getConfig().xpPerTrophy); - } - - private PrecisionContext readPrecisionContext(EntityDeathEvent e, Player killer, int level) { - boolean projectileKill = false; - double range = 0; - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent damageByEntity) { - if (damageByEntity.getDamager() instanceof Projectile projectile && projectile.getShooter() == killer) { - projectileKill = true; - } - } - - if (killer.getWorld() == e.getEntity().getWorld()) { - range = killer.getLocation().distance(e.getEntity().getLocation()); - } - - boolean rangedPrecision = projectileKill && range >= getMinimumRange(level); - boolean stealthPrecision = killer.isSneaking(); - return new PrecisionContext(projectileKill, rangedPrecision || stealthPrecision); - } - - private ItemStack buildTrophyDrop(EntityType type, int level, boolean projectileKill) { - Material material = switch (type) { - case CREEPER -> Material.GUNPOWDER; - case SKELETON, BOGGED, WITHER_SKELETON, STRAY -> Material.BONE; - case ZOMBIE, ZOMBIFIED_PIGLIN, HUSK, DROWNED -> Material.ROTTEN_FLESH; - case SPIDER, CAVE_SPIDER -> Material.STRING; - case BLAZE -> Material.BLAZE_POWDER; - case ENDERMAN -> Material.ENDER_PEARL; - case WITCH -> Material.REDSTONE; - case PIGLIN, PIGLIN_BRUTE, HOGLIN, ZOGLIN -> Material.PORKCHOP; - default -> Material.LEATHER; - }; - - int amount = Math.max(1, (int) Math.round(getConfig().trophyAmountBase + (getLevelPercent(level) * getConfig().trophyAmountFactor))); - if (projectileKill) { - amount += 1; - } - - return new ItemStack(material, Math.min(8, amount)); + public HunterTrophySkinner() { + super("hunter-trophy-skinner"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("hunter.trophy_skinner.description")); + setDisplayName(Localizer.dLocalize("hunter.trophy_skinner.name")); + setIcon(Material.ZOMBIE_HEAD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SKELETON_SKULL) + .key("challenge_hunter_trophy_50") + .title(Localizer.dLocalize("advancement.challenge_hunter_trophy_50.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_trophy_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ZOMBIE_HEAD) + .key("challenge_hunter_trophy_heads_100") + .title(Localizer.dLocalize("advancement.challenge_hunter_trophy_heads_100.title")) + .description(Localizer.dLocalize("advancement.challenge_hunter_trophy_heads_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_hunter_trophy_50", "hunter.trophy-skinner.trophies-collected", 50, 400); + registerMilestone("challenge_hunter_trophy_heads_100", "hunter.trophy-skinner.heads-collected", 100, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getDropChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getHeadChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore2")); + v.addLore(C.YELLOW + "* " + Form.f(getMinimumRange(level), 1) + C.GRAY + " " + Localizer.dLocalize("hunter.trophy_skinner.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + Player killer = e.getEntity().getKiller(); + if (killer == null || !hasActiveAdaptation(killer) || e.getEntity() instanceof Player || !canDamageTarget(killer, e.getEntity())) { + return; } - private ItemStack buildHeadDrop(EntityType type) { - Material material = switch (type) { - case CREEPER -> Material.CREEPER_HEAD; - case SKELETON, STRAY, BOGGED -> Material.SKELETON_SKULL; - case WITHER_SKELETON -> Material.WITHER_SKELETON_SKULL; - case ZOMBIE, HUSK, DROWNED, ZOMBIFIED_PIGLIN -> Material.ZOMBIE_HEAD; - case PIGLIN, PIGLIN_BRUTE -> Material.PIGLIN_HEAD; - default -> null; - }; - - return material == null ? null : new ItemStack(material); + int level = getActiveLevel(killer); + PrecisionContext precision = readPrecisionContext(e, killer, level); + if (!precision.precise()) { + return; } - private double getDropChance(int level) { - return Math.min(getConfig().maxDropChance, getConfig().dropChanceBase + (getLevelPercent(level) * getConfig().dropChanceFactor)); + if (ThreadLocalRandom.current().nextDouble() > getDropChance(level)) { + return; } - private double getHeadChance(int level) { - return Math.min(getConfig().maxHeadChance, getConfig().headChanceBase + (getLevelPercent(level) * getConfig().headChanceFactor)); + ItemStack trophy = buildTrophyDrop(e.getEntityType(), level, precision.projectileKill()); + if (trophy != null) { + e.getDrops().add(trophy); + getPlayer(killer).getData().addStat("hunter.trophy-skinner.trophies-collected", 1); } - private double getMinimumRange(int level) { - return Math.max(4, getConfig().minimumRangeBase - (getLevelPercent(level) * getConfig().minimumRangeFactor)); + if (ThreadLocalRandom.current().nextDouble() <= getHeadChance(level)) { + ItemStack head = buildHeadDrop(e.getEntityType()); + if (head != null) { + e.getDrops().add(head); + getPlayer(killer).getData().addStat("hunter.trophy-skinner.heads-collected", 1); + } } - @Override - public void onTick() { - + SoundPlayer.of(killer).play(killer.getLocation(), Sound.ENTITY_WOLF_SHAKE, 0.55f, 1.35f); + xp(killer, getConfig().xpPerTrophy); + } + + private PrecisionContext readPrecisionContext(EntityDeathEvent e, Player killer, int level) { + boolean projectileKill = false; + double range = 0; + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent damageByEntity) { + if (damageByEntity.getDamager() instanceof Projectile projectile && projectile.getShooter() == killer) { + projectileKill = true; + } } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (killer.getWorld() == e.getEntity().getWorld()) { + range = killer.getLocation().distance(e.getEntity().getLocation()); } - @Override - public boolean isPermanent() { - return getConfig().permanent; + boolean rangedPrecision = projectileKill && range >= getMinimumRange(level); + boolean stealthPrecision = killer.isSneaking(); + return new PrecisionContext(projectileKill, rangedPrecision || stealthPrecision); + } + + private ItemStack buildTrophyDrop(EntityType type, int level, boolean projectileKill) { + Material material = switch (type) { + case CREEPER -> Material.GUNPOWDER; + case SKELETON, BOGGED, WITHER_SKELETON, STRAY -> Material.BONE; + case ZOMBIE, ZOMBIFIED_PIGLIN, HUSK, DROWNED -> Material.ROTTEN_FLESH; + case SPIDER, CAVE_SPIDER -> Material.STRING; + case BLAZE -> Material.BLAZE_POWDER; + case ENDERMAN -> Material.ENDER_PEARL; + case WITCH -> Material.REDSTONE; + case PIGLIN, PIGLIN_BRUTE, HOGLIN, ZOGLIN -> Material.PORKCHOP; + default -> Material.LEATHER; + }; + + int amount = Math.max(1, (int) Math.round(getConfig().trophyAmountBase + (getLevelPercent(level) * getConfig().trophyAmountFactor))); + if (projectileKill) { + amount += 1; } - @NoArgsConstructor - @ConfigDescription("Precision kills can grant bonus trophy drops and occasional heads from elite targets.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dropChanceBase = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dropChanceFactor = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Drop Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxDropChance = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double headChanceBase = 0.015; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double headChanceFactor = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Head Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHeadChance = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double trophyAmountBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double trophyAmountFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumRangeBase = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumRangeFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Trophy for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerTrophy = 16; - } - - private record PrecisionContext(boolean projectileKill, boolean precise) { - } + return new ItemStack(material, Math.min(8, amount)); + } + + private ItemStack buildHeadDrop(EntityType type) { + Material material = switch (type) { + case CREEPER -> Material.CREEPER_HEAD; + case SKELETON, STRAY, BOGGED -> Material.SKELETON_SKULL; + case WITHER_SKELETON -> Material.WITHER_SKELETON_SKULL; + case ZOMBIE, HUSK, DROWNED, ZOMBIFIED_PIGLIN -> Material.ZOMBIE_HEAD; + case PIGLIN, PIGLIN_BRUTE -> Material.PIGLIN_HEAD; + default -> null; + }; + + return material == null ? null : new ItemStack(material); + } + + private double getDropChance(int level) { + return Math.min(getConfig().maxDropChance, getConfig().dropChanceBase + (getLevelPercent(level) * getConfig().dropChanceFactor)); + } + + private double getHeadChance(int level) { + return Math.min(getConfig().maxHeadChance, getConfig().headChanceBase + (getLevelPercent(level) * getConfig().headChanceFactor)); + } + + private double getMinimumRange(int level) { + return Math.max(4, getConfig().minimumRangeBase - (getLevelPercent(level) * getConfig().minimumRangeFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Precision kills can grant bonus trophy drops and occasional heads from elite targets.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dropChanceBase = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dropChanceFactor = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Drop Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxDropChance = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double headChanceBase = 0.015; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Head Chance Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double headChanceFactor = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Head Chance for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHeadChance = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double trophyAmountBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trophy Amount Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double trophyAmountFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Base for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumRangeBase = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Range Factor for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumRangeFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Trophy for the Hunter Trophy Skinner adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTrophy = 16; + } + + private record PrecisionContext(boolean projectileKill, boolean precise) { + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java index 9cab3f5ac..e575a6cc4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherBlazeLeech.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,198 +43,198 @@ import java.util.concurrent.ThreadLocalRandom; public class NetherBlazeLeech extends SimpleAdaptation { - public NetherBlazeLeech() { - super("nether-blaze-leech"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.blaze_leech.description")); - setDisplayName(Localizer.dLocalize("nether.blaze_leech.name")); - setIcon(Material.BLAZE_POWDER); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(900); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_ROD) - .key("challenge_nether_blaze_200") - .title(Localizer.dLocalize("advancement.challenge_nether_blaze_200.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_blaze_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_nether_blaze_2500") - .title(Localizer.dLocalize("advancement.challenge_nether_blaze_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_blaze_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_nether_blaze_200", "nether.blaze-leech.health-from-fire", 200, 300); - registerMilestone("challenge_nether_blaze_2500", "nether.blaze-leech.health-from-fire", 2500, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getTriggerChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getRegenTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore2")); - v.addLore(C.GREEN + "+ " + Form.f(getFoodRestore(level)) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore3")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } - - withAdaptedPlayer(p, e, () -> { - int level = getActiveLevel(p); - if (!isFireCause(e.getCause()) || !isReady(p, level)) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { - return; - } - - applyLeech(p, level, true); - }); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { - return; - } - - withAdaptedPlayer(p, e, () -> { - if (!canDamageTarget(p, target)) { - return; - } - - int level = getActiveLevel(p); - if (target.getFireTicks() <= 0 || !isReady(p, level)) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { - return; - } - - applyLeech(p, level, false); - }); - } - - private boolean isFireCause(EntityDamageEvent.DamageCause cause) { - return cause == EntityDamageEvent.DamageCause.FIRE - || cause == EntityDamageEvent.DamageCause.FIRE_TICK - || cause == EntityDamageEvent.DamageCause.LAVA - || cause == EntityDamageEvent.DamageCause.HOT_FLOOR; - } - - private boolean isReady(Player p, int level) { - long now = System.currentTimeMillis(); - long next = getStorageLong(p, "blazeLeechNext", 0L); - if (next > now) { - return false; - } - - setStorage(p, "blazeLeechNext", now + getCooldownMillis(level)); - return true; - } - - private void applyLeech(Player p, int level, boolean defensive) { - int newFood = Math.min(20, p.getFoodLevel() + (int) Math.round(getFoodRestore(level))); - p.setFoodLevel(newFood); - float sat = Math.min(20f, p.getSaturation() + (float) getConfig().saturationRestore); - p.setSaturation(sat); - - int amp = Math.max(0, (int) Math.floor(getRegenAmplifier(level))); - p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getRegenTicks(level), amp, true, false, true), true); - - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ENTITY_BLAZE_AMBIENT, 0.45f, defensive ? 1.4f : 1.7f); - xp(p, defensive ? getConfig().xpOnDefensiveProc : getConfig().xpOnOffensiveProc); - getPlayer(p).getData().addStat("nether.blaze-leech.health-from-fire", 1); - } - - private double getTriggerChance(int level) { - return Math.min(getConfig().maxTriggerChance, getConfig().triggerChanceBase + (getLevelPercent(level) * getConfig().triggerChanceFactor)); - } - - private int getRegenTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().regenTicksBase + (getLevelPercent(level) * getConfig().regenTicksFactor))); - } - - private double getRegenAmplifier(int level) { - return getConfig().regenAmplifierBase + (getLevelPercent(level) * getConfig().regenAmplifierFactor); - } - - private double getFoodRestore(int level) { - return getConfig().foodRestoreBase + (getLevelPercent(level) * getConfig().foodRestoreFactor); - } - - private long getCooldownMillis(int level) { - return Math.max(100L, Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Fire interactions can grant hunger and regeneration in short bursts.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.62; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double triggerChanceBase = 0.16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double triggerChanceFactor = 0.34; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Trigger Chance for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxTriggerChance = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenTicksBase = 28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenTicksFactor = 42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenAmplifierFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodRestoreBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double foodRestoreFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Saturation Restore for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double saturationRestore = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 1400; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Defensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnDefensiveProc = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Offensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnOffensiveProc = 5; - } + public NetherBlazeLeech() { + super("nether-blaze-leech"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.blaze_leech.description")); + setDisplayName(Localizer.dLocalize("nether.blaze_leech.name")); + setIcon(Material.BLAZE_POWDER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(900); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_ROD) + .key("challenge_nether_blaze_200") + .title(Localizer.dLocalize("advancement.challenge_nether_blaze_200.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_blaze_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_nether_blaze_2500") + .title(Localizer.dLocalize("advancement.challenge_nether_blaze_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_blaze_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_nether_blaze_200", "nether.blaze-leech.health-from-fire", 200, 300); + registerMilestone("challenge_nether_blaze_2500", "nether.blaze-leech.health-from-fire", 2500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getTriggerChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getRegenTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore2")); + v.addLore(C.GREEN + "+ " + Form.f(getFoodRestore(level)) + C.GRAY + " " + Localizer.dLocalize("nether.blaze_leech.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (!isFireCause(e.getCause()) || !isReady(p, level)) { + return; + } + + if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { + return; + } + + applyLeech(p, level, true); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { + return; + } + + withAdaptedPlayer(p, e, () -> { + if (!canDamageTarget(p, target)) { + return; + } + + int level = getActiveLevel(p); + if (target.getFireTicks() <= 0 || !isReady(p, level)) { + return; + } + + if (ThreadLocalRandom.current().nextDouble() > getTriggerChance(level)) { + return; + } + + applyLeech(p, level, false); + }); + } + + private boolean isFireCause(EntityDamageEvent.DamageCause cause) { + return cause == EntityDamageEvent.DamageCause.FIRE + || cause == EntityDamageEvent.DamageCause.FIRE_TICK + || cause == EntityDamageEvent.DamageCause.LAVA + || cause == EntityDamageEvent.DamageCause.HOT_FLOOR; + } + + private boolean isReady(Player p, int level) { + long now = System.currentTimeMillis(); + long next = getStorageLong(p, "blazeLeechNext", 0L); + if (next > now) { + return false; + } + + setStorage(p, "blazeLeechNext", now + getCooldownMillis(level)); + return true; + } + + private void applyLeech(Player p, int level, boolean defensive) { + int newFood = Math.min(20, p.getFoodLevel() + (int) Math.round(getFoodRestore(level))); + p.setFoodLevel(newFood); + float sat = Math.min(20f, p.getSaturation() + (float) getConfig().saturationRestore); + p.setSaturation(sat); + + int amp = Math.max(0, (int) Math.floor(getRegenAmplifier(level))); + p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getRegenTicks(level), amp, true, false, true), true); + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ENTITY_BLAZE_AMBIENT, 0.45f, defensive ? 1.4f : 1.7f); + xp(p, defensive ? getConfig().xpOnDefensiveProc : getConfig().xpOnOffensiveProc); + getPlayer(p).getData().addStat("nether.blaze-leech.health-from-fire", 1); + } + + private double getTriggerChance(int level) { + return Math.min(getConfig().maxTriggerChance, getConfig().triggerChanceBase + (getLevelPercent(level) * getConfig().triggerChanceFactor)); + } + + private int getRegenTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().regenTicksBase + (getLevelPercent(level) * getConfig().regenTicksFactor))); + } + + private double getRegenAmplifier(int level) { + return getConfig().regenAmplifierBase + (getLevelPercent(level) * getConfig().regenAmplifierFactor); + } + + private double getFoodRestore(int level) { + return getConfig().foodRestoreBase + (getLevelPercent(level) * getConfig().foodRestoreFactor); + } + + private long getCooldownMillis(int level) { + return Math.max(100L, Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Fire interactions can grant hunger and regeneration in short bursts.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.62; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double triggerChanceBase = 0.16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trigger Chance Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double triggerChanceFactor = 0.34; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Trigger Chance for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxTriggerChance = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenTicksBase = 28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Ticks Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenTicksFactor = 42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Amplifier Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenAmplifierFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodRestoreBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Restore Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double foodRestoreFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Saturation Restore for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double saturationRestore = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 1400; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Defensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnDefensiveProc = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Offensive Proc for the Nether Blaze Leech adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnOffensiveProc = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java index aebc7b399..c0876ff8f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherFireResist.java @@ -23,9 +23,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -37,100 +37,100 @@ import java.util.concurrent.ThreadLocalRandom; public class NetherFireResist extends SimpleAdaptation { - public NetherFireResist() { - super("nether-fire-resist"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.fire_resist.description")); - setDisplayName(Localizer.dLocalize("nether.fire_resist.name")); - setIcon(Material.FIRE_CHARGE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(4333); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FIRE_CHARGE) - .key("challenge_nether_fire_200") - .title(Localizer.dLocalize("advancement.challenge_nether_fire_200.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_fire_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.MAGMA_CREAM) - .key("challenge_nether_fire_5k") - .title(Localizer.dLocalize("advancement.challenge_nether_fire_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_fire_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_nether_fire_200", "nether.fire-resist.negated", 200, 300); - registerMilestone("challenge_nether_fire_5k", "nether.fire-resist.negated", 5000, 1000); - } + public NetherFireResist() { + super("nether-fire-resist"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.fire_resist.description")); + setDisplayName(Localizer.dLocalize("nether.fire_resist.name")); + setIcon(Material.FIRE_CHARGE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(4333); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FIRE_CHARGE) + .key("challenge_nether_fire_200") + .title(Localizer.dLocalize("advancement.challenge_nether_fire_200.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_fire_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.MAGMA_CREAM) + .key("challenge_nether_fire_5k") + .title(Localizer.dLocalize("advancement.challenge_nether_fire_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_fire_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_nether_fire_200", "nether.fire-resist.negated", 200, 300); + registerMilestone("challenge_nether_fire_5k", "nether.fire-resist.negated", 5000, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.RED + "+ " + Form.pc(getFireResist(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.fire_resist.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.RED + "+ " + Form.pc(getFireResist(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.fire_resist.lore1")); + } - @EventHandler(priority = EventPriority.HIGH) - public void on(EntityDamageEvent e) { + @EventHandler(priority = EventPriority.HIGH) + public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } + if (!(e.getEntity() instanceof Player p)) { + return; + } - withAdaptedPlayer(p, e, () -> { - if (e.getCause() != EntityDamageEvent.DamageCause.FIRE && e.getCause() != EntityDamageEvent.DamageCause.FIRE_TICK) { - return; - } + withAdaptedPlayer(p, e, () -> { + if (e.getCause() != EntityDamageEvent.DamageCause.FIRE && e.getCause() != EntityDamageEvent.DamageCause.FIRE_TICK) { + return; + } - if (ThreadLocalRandom.current().nextDouble() < getFireResist(getLevel(p))) { - e.setCancelled(true); - getPlayer(p).getData().addStat("nether.fire-resist.negated", 1); - } - }); - } + if (ThreadLocalRandom.current().nextDouble() < getFireResist(getLevel(p))) { + e.setCancelled(true); + getPlayer(p).getData().addStat("nether.fire-resist.negated", 1); + } + }); + } - public double getFireResist(double level) { - return getConfig().fireResistBase + (getConfig().fireResistFactor * level); - } + public double getFireResist(double level) { + return getConfig().fireResistBase + (getConfig().fireResistFactor * level); + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Data - @NoArgsConstructor - @ConfigDescription("Chance to negate the burning effect.") - public static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Base for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fireResistBase = 0.10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Factor for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fireResistFactor = 0.25; - } + @Data + @NoArgsConstructor + @ConfigDescription("Chance to negate the burning effect.") + public static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Base for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fireResistBase = 0.10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Factor for the Nether Fire Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fireResistFactor = 0.25; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java index 212aeb592..daa48dee8 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherGhastWard.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.*; @@ -36,169 +36,169 @@ import org.bukkit.event.entity.EntityDamageEvent; public class NetherGhastWard extends SimpleAdaptation { - public NetherGhastWard() { - super("nether-ghast-ward"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.ghast_ward.description")); - setDisplayName(Localizer.dLocalize("nether.ghast_ward.name")); - setIcon(Material.GHAST_TEAR); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GHAST_TEAR) - .key("challenge_nether_ghast_500") - .title(Localizer.dLocalize("advancement.challenge_nether_ghast_500.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_ghast_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_nether_ghast_500", "nether.ghast-ward.damage-reduced", 500, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getGhastProjectileReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getExplosionReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore2")); - v.addLore(C.GREEN + "+ " + Form.pc(getWitherSkeletonReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore3")); + public NetherGhastWard() { + super("nether-ghast-ward"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.ghast_ward.description")); + setDisplayName(Localizer.dLocalize("nether.ghast_ward.name")); + setIcon(Material.GHAST_TEAR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GHAST_TEAR) + .key("challenge_nether_ghast_500") + .title(Localizer.dLocalize("advancement.challenge_nether_ghast_500.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_ghast_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_nether_ghast_500", "nether.ghast-ward.damage-reduced", 500, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getGhastProjectileReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getExplosionReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getWitherSkeletonReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.ghast_ward.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; + withAdaptedPlayer(p, e, () -> { + if (!isNether(p)) { + return; + } + + int level = getActiveLevel(p); + if (e.getDamager() instanceof Fireball fireball && fireball.getShooter() instanceof Ghast) { + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getGhastProjectileReduction(level)))); + p.setFireTicks(Math.min(p.getFireTicks(), getMaxFireTicks(level))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); } - - withAdaptedPlayer(p, e, () -> { - if (!isNether(p)) { - return; - } - - int level = getActiveLevel(p); - if (e.getDamager() instanceof Fireball fireball && fireball.getShooter() instanceof Ghast) { - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getGhastProjectileReduction(level)))); - p.setFireTicks(Math.min(p.getFireTicks(), getMaxFireTicks(level))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); - } - return; - } - - if (e.getDamager() instanceof AbstractArrow arrow && arrow.getShooter() instanceof WitherSkeleton) { - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getWitherSkeletonReduction(level)))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); - } - } - }); - } - - @EventHandler(priority = EventPriority.HIGH) - public void on(EntityDamageEvent e) { - if (e instanceof EntityDamageByEntityEvent || !(e.getEntity() instanceof Player p)) { - return; + return; + } + + if (e.getDamager() instanceof AbstractArrow arrow && arrow.getShooter() instanceof WitherSkeleton) { + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getWitherSkeletonReduction(level)))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); } - - withAdaptedPlayer(p, e, () -> { - if (!isNether(p)) { - return; - } - - if (e.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION && e.getCause() != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION) { - return; - } - - double before = e.getDamage(); - e.setDamage(Math.max(0, e.getDamage() * (1D - getExplosionReduction(getLevel(p))))); - xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); - int reduced = (int) Math.round(before - e.getDamage()); - if (reduced > 0) { - getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); - } - }); - } - - private boolean isNether(Player p) { - return p.getWorld().getEnvironment().name().contains("NETHER"); - } - - private double getGhastProjectileReduction(int level) { - return Math.min(getConfig().maxGhastProjectileReduction, getConfig().ghastProjectileReductionBase + (getLevelPercent(level) * getConfig().ghastProjectileReductionFactor)); - } - - private double getExplosionReduction(int level) { - return Math.min(getConfig().maxExplosionReduction, getConfig().explosionReductionBase + (getLevelPercent(level) * getConfig().explosionReductionFactor)); - } - - private double getWitherSkeletonReduction(int level) { - return Math.min(getConfig().maxWitherSkeletonReduction, getConfig().witherSkeletonReductionBase + (getLevelPercent(level) * getConfig().witherSkeletonReductionFactor)); + } + }); + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(EntityDamageEvent e) { + if (e instanceof EntityDamageByEntityEvent || !(e.getEntity() instanceof Player p)) { + return; } - private int getMaxFireTicks(int level) { - return Math.max(0, (int) Math.round(getConfig().maxFireTicksBase - (getLevelPercent(level) * getConfig().maxFireTicksFactor))); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Harden against ghast blasts and wither-skeleton pressure in the Nether.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.73; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double ghastProjectileReductionBase = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double ghastProjectileReductionFactor = 0.54; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ghast Projectile Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxGhastProjectileReduction = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double explosionReductionBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double explosionReductionFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Explosion Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxExplosionReduction = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double witherSkeletonReductionBase = 0.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double witherSkeletonReductionFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Wither Skeleton Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxWitherSkeletonReduction = 0.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxFireTicksBase = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxFireTicksFactor = 70; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerMitigatedDamage = 4.2; - } + withAdaptedPlayer(p, e, () -> { + if (!isNether(p)) { + return; + } + + if (e.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION && e.getCause() != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION) { + return; + } + + double before = e.getDamage(); + e.setDamage(Math.max(0, e.getDamage() * (1D - getExplosionReduction(getLevel(p))))); + xp(p, e.getDamage() * getConfig().xpPerMitigatedDamage); + int reduced = (int) Math.round(before - e.getDamage()); + if (reduced > 0) { + getPlayer(p).getData().addStat("nether.ghast-ward.damage-reduced", reduced); + } + }); + } + + private boolean isNether(Player p) { + return p.getWorld().getEnvironment().name().contains("NETHER"); + } + + private double getGhastProjectileReduction(int level) { + return Math.min(getConfig().maxGhastProjectileReduction, getConfig().ghastProjectileReductionBase + (getLevelPercent(level) * getConfig().ghastProjectileReductionFactor)); + } + + private double getExplosionReduction(int level) { + return Math.min(getConfig().maxExplosionReduction, getConfig().explosionReductionBase + (getLevelPercent(level) * getConfig().explosionReductionFactor)); + } + + private double getWitherSkeletonReduction(int level) { + return Math.min(getConfig().maxWitherSkeletonReduction, getConfig().witherSkeletonReductionBase + (getLevelPercent(level) * getConfig().witherSkeletonReductionFactor)); + } + + private int getMaxFireTicks(int level) { + return Math.max(0, (int) Math.round(getConfig().maxFireTicksBase - (getLevelPercent(level) * getConfig().maxFireTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Harden against ghast blasts and wither-skeleton pressure in the Nether.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.73; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double ghastProjectileReductionBase = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ghast Projectile Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double ghastProjectileReductionFactor = 0.54; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ghast Projectile Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxGhastProjectileReduction = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double explosionReductionBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Explosion Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double explosionReductionFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Explosion Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxExplosionReduction = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double witherSkeletonReductionBase = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Reduction Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double witherSkeletonReductionFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Wither Skeleton Reduction for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxWitherSkeletonReduction = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Base for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxFireTicksBase = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Fire Ticks Factor for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxFireTicksFactor = 70; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mitigated Damage for the Nether Ghast Ward adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMitigatedDamage = 4.2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java index ad937cb71..e11eeb889 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherLavaWalker.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -39,142 +39,142 @@ import org.bukkit.util.Vector; public class NetherLavaWalker extends SimpleAdaptation { - public NetherLavaWalker() { - super("nether-lava-walker"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.lava_walker.description")); - setDisplayName(Localizer.dLocalize("nether.lava_walker.name")); - setIcon(Material.MAGMA_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.MAGMA_BLOCK) - .key("challenge_nether_lava_1k") - .title(Localizer.dLocalize("advancement.challenge_nether_lava_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_lava_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_INGOT) - .key("challenge_nether_lava_25k") - .title(Localizer.dLocalize("advancement.challenge_nether_lava_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_lava_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_nether_lava_1k", "nether.lava-walker.blocks-walked", 1000, 300); - registerMilestone("challenge_nether_lava_25k", "nether.lava-walker.blocks-walked", 25000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getStride(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.lava_walker.lore1")); - v.addLore(C.YELLOW + "* " + getHungerCost(level) + C.GRAY + " " + Localizer.dLocalize("nether.lava_walker.lore2")); - } - - @EventHandler(priority = EventPriority.HIGH) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (!p.getWorld().getEnvironment().name().contains("NETHER")) { - return; - } - - if (p.isFlying() || p.isGliding() || p.isInsideVehicle() || p.getFoodLevel() <= 0) { - return; - } - - Block feet = p.getLocation().getBlock(); - Block below = p.getLocation().clone().add(0, -1, 0).getBlock(); - if (!(isLava(feet) || isLava(below))) { - return; - } - - int level = getActiveLevel(p); - if (getStorageLong(p, "lavaWalkerCooldown", 0L) > System.currentTimeMillis()) { - return; - } - - Vector velocity = p.getVelocity(); - Vector dir = p.getLocation().getDirection().setY(0).normalize().multiply(getStride(level)); - p.setVelocity(new Vector(dir.getX(), Math.max(0.16, velocity.getY()), dir.getZ())); - p.setFallDistance(0); - p.setFireTicks(0); - p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, getConfig().fireResistTicks, 0, false, false)); - - int hungerCost = getHungerCost(level); - p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); - setStorage(p, "lavaWalkerCooldown", System.currentTimeMillis() + getCooldownMillis(level)); - xp(p, getConfig().xpPerStride); - getPlayer(p).getData().addStat("nether.lava-walker.blocks-walked", 1); - }); - } - - private boolean isLava(Block b) { - return b.getType() == Material.LAVA; - } - - private double getStride(int level) { - return getConfig().strideBase + (getLevelPercent(level) * getConfig().strideFactor); - } - - private int getHungerCost(int level) { - return Math.max(1, (int) Math.round(getConfig().hungerCostBase - (getLevelPercent(level) * getConfig().hungerCostFactor))); - } - - private long getCooldownMillis(int level) { - return Math.max(100, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Stride over lava in the Nether at the cost of hunger.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double strideBase = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double strideFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerCostBase = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerCostFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisBase = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownMillisFactor = 700; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Ticks for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fireResistTicks = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Stride for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerStride = 3.5; - } + public NetherLavaWalker() { + super("nether-lava-walker"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.lava_walker.description")); + setDisplayName(Localizer.dLocalize("nether.lava_walker.name")); + setIcon(Material.MAGMA_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.MAGMA_BLOCK) + .key("challenge_nether_lava_1k") + .title(Localizer.dLocalize("advancement.challenge_nether_lava_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_lava_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_INGOT) + .key("challenge_nether_lava_25k") + .title(Localizer.dLocalize("advancement.challenge_nether_lava_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_lava_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_nether_lava_1k", "nether.lava-walker.blocks-walked", 1000, 300); + registerMilestone("challenge_nether_lava_25k", "nether.lava-walker.blocks-walked", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getStride(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.lava_walker.lore1")); + v.addLore(C.YELLOW + "* " + getHungerCost(level) + C.GRAY + " " + Localizer.dLocalize("nether.lava_walker.lore2")); + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (!p.getWorld().getEnvironment().name().contains("NETHER")) { + return; + } + + if (p.isFlying() || p.isGliding() || p.isInsideVehicle() || p.getFoodLevel() <= 0) { + return; + } + + Block feet = p.getLocation().getBlock(); + Block below = p.getLocation().clone().add(0, -1, 0).getBlock(); + if (!(isLava(feet) || isLava(below))) { + return; + } + + int level = getActiveLevel(p); + if (getStorageLong(p, "lavaWalkerCooldown", 0L) > System.currentTimeMillis()) { + return; + } + + Vector velocity = p.getVelocity(); + Vector dir = p.getLocation().getDirection().setY(0).normalize().multiply(getStride(level)); + p.setVelocity(new Vector(dir.getX(), Math.max(0.16, velocity.getY()), dir.getZ())); + p.setFallDistance(0); + p.setFireTicks(0); + p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, getConfig().fireResistTicks, 0, false, false)); + + int hungerCost = getHungerCost(level); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + setStorage(p, "lavaWalkerCooldown", System.currentTimeMillis() + getCooldownMillis(level)); + xp(p, getConfig().xpPerStride); + getPlayer(p).getData().addStat("nether.lava-walker.blocks-walked", 1); + }); + } + + private boolean isLava(Block b) { + return b.getType() == Material.LAVA; + } + + private double getStride(int level) { + return getConfig().strideBase + (getLevelPercent(level) * getConfig().strideFactor); + } + + private int getHungerCost(int level) { + return Math.max(1, (int) Math.round(getConfig().hungerCostBase - (getLevelPercent(level) * getConfig().hungerCostFactor))); + } + + private long getCooldownMillis(int level) { + return Math.max(100, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Stride over lava in the Nether at the cost of hunger.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double strideBase = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Stride Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double strideFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerCostBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerCostFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 700; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fire Resist Ticks for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fireResistTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Stride for the Nether Lava Walker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerStride = 3.5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java index 77e772c7c..ba516a4d1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherPiglinBroker.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,182 +43,182 @@ import java.util.concurrent.ThreadLocalRandom; public class NetherPiglinBroker extends SimpleAdaptation { - public NetherPiglinBroker() { - super("nether-piglin-broker"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.piglin_broker.description")); - setDisplayName(Localizer.dLocalize("nether.piglin_broker.name")); - setIcon(Material.GOLD_INGOT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2300); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLD_INGOT) - .key("challenge_nether_piglin_100") - .title(Localizer.dLocalize("advancement.challenge_nether_piglin_100.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_piglin_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLD_BLOCK) - .key("challenge_nether_piglin_2500") - .title(Localizer.dLocalize("advancement.challenge_nether_piglin_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_piglin_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_nether_piglin_100", "nether.piglin-broker.improved-barters", 100, 300); - registerMilestone("challenge_nether_piglin_2500", "nether.piglin-broker.improved-barters", 2500, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getExtraRollChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.piglin_broker.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getRareBonusChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.piglin_broker.lore2")); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PiglinBarterEvent e) { - Piglin piglin = e.getEntity(); - Player broker = findBroker(piglin, getConfig().brokerRange); - if (broker == null) { - return; - } - - int level = getActiveLevel(broker); - if (level <= 0) { - return; - } - - List outcome = e.getOutcome(); - if (outcome.isEmpty()) { - return; - } - - ThreadLocalRandom random = ThreadLocalRandom.current(); - boolean changed = false; - if (random.nextDouble() <= getExtraRollChance(level)) { - ItemStack bonus = outcome.get(random.nextInt(outcome.size())).clone(); - int amount = Math.max(1, (int) Math.round(bonus.getAmount() * getAmountMultiplier(level))); - bonus.setAmount(Math.min(bonus.getMaxStackSize(), amount)); - outcome.add(bonus); - changed = true; - } - - if (random.nextDouble() <= getRareBonusChance(level)) { - outcome.add(getRareBonusRoll()); - changed = true; - } - - if (!changed) { - return; - } - - SoundPlayer.of(broker.getWorld()).play(broker.getLocation(), Sound.ENTITY_PIGLIN_ADMIRING_ITEM, 0.9f, 1.25f); - xp(broker, getConfig().xpOnBoostedBarter); - getPlayer(broker).getData().addStat("nether.piglin-broker.improved-barters", 1); + public NetherPiglinBroker() { + super("nether-piglin-broker"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.piglin_broker.description")); + setDisplayName(Localizer.dLocalize("nether.piglin_broker.name")); + setIcon(Material.GOLD_INGOT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2300); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLD_INGOT) + .key("challenge_nether_piglin_100") + .title(Localizer.dLocalize("advancement.challenge_nether_piglin_100.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_piglin_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLD_BLOCK) + .key("challenge_nether_piglin_2500") + .title(Localizer.dLocalize("advancement.challenge_nether_piglin_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_piglin_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_nether_piglin_100", "nether.piglin-broker.improved-barters", 100, 300); + registerMilestone("challenge_nether_piglin_2500", "nether.piglin-broker.improved-barters", 2500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getExtraRollChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.piglin_broker.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getRareBonusChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("nether.piglin_broker.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PiglinBarterEvent e) { + Piglin piglin = e.getEntity(); + Player broker = findBroker(piglin, getConfig().brokerRange); + if (broker == null) { + return; } - private Player findBroker(Piglin piglin, double range) { - Player best = null; - double bestDist = Double.MAX_VALUE; - double rangeSquared = range * range; - var piglinLocation = piglin.getLocation(); - for (Entity nearby : piglin.getNearbyEntities(range, range, range)) { - if (!(nearby instanceof Player p)) { - continue; - } - if (!hasActiveAdaptation(p)) { - continue; - } - - double d = p.getLocation().distanceSquared(piglinLocation); - if (d > rangeSquared) { - continue; - } - if (d < bestDist) { - best = p; - bestDist = d; - } - } - - return best; + int level = getActiveLevel(broker); + if (level <= 0) { + return; } - private ItemStack getRareBonusRoll() { - return switch (ThreadLocalRandom.current().nextInt(5)) { - case 0 -> new ItemStack(Material.ENDER_PEARL, 1); - case 1 -> new ItemStack(Material.OBSIDIAN, 2); - case 2 -> new ItemStack(Material.STRING, 4); - case 3 -> new ItemStack(Material.IRON_NUGGET, 6); - default -> new ItemStack(Material.SPECTRAL_ARROW, 2); - }; + List outcome = e.getOutcome(); + if (outcome.isEmpty()) { + return; } - private double getExtraRollChance(int level) { - return Math.min(getConfig().maxExtraRollChance, getConfig().extraRollChanceBase + (getLevelPercent(level) * getConfig().extraRollChanceFactor)); + ThreadLocalRandom random = ThreadLocalRandom.current(); + boolean changed = false; + if (random.nextDouble() <= getExtraRollChance(level)) { + ItemStack bonus = outcome.get(random.nextInt(outcome.size())).clone(); + int amount = Math.max(1, (int) Math.round(bonus.getAmount() * getAmountMultiplier(level))); + bonus.setAmount(Math.min(bonus.getMaxStackSize(), amount)); + outcome.add(bonus); + changed = true; } - private double getRareBonusChance(int level) { - return Math.min(getConfig().maxRareBonusChance, getConfig().rareBonusChanceBase + (getLevelPercent(level) * getConfig().rareBonusChanceFactor)); + if (random.nextDouble() <= getRareBonusChance(level)) { + outcome.add(getRareBonusRoll()); + changed = true; } - private double getAmountMultiplier(int level) { - return Math.max(1.0, getConfig().amountMultiplierBase + (getLevelPercent(level) * getConfig().amountMultiplierFactor)); + if (!changed) { + return; } - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + SoundPlayer.of(broker.getWorld()).play(broker.getLocation(), Sound.ENTITY_PIGLIN_ADMIRING_ITEM, 0.9f, 1.25f); + xp(broker, getConfig().xpOnBoostedBarter); + getPlayer(broker).getData().addStat("nether.piglin-broker.improved-barters", 1); + } + + private Player findBroker(Piglin piglin, double range) { + Player best = null; + double bestDist = Double.MAX_VALUE; + double rangeSquared = range * range; + org.bukkit.Location piglinLocation = piglin.getLocation(); + for (Entity nearby : piglin.getNearbyEntities(range, range, range)) { + if (!(nearby instanceof Player p)) { + continue; + } + if (!hasActiveAdaptation(p)) { + continue; + } + + double d = p.getLocation().distanceSquared(piglinLocation); + if (d > rangeSquared) { + continue; + } + if (d < bestDist) { + best = p; + bestDist = d; + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Nearby piglin bartering can yield extra or improved rolls.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Broker Range for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double brokerRange = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double extraRollChanceBase = 0.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double extraRollChanceFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Extra Roll Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxExtraRollChance = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rareBonusChanceBase = 0.03; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rareBonusChanceFactor = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Bonus Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRareBonusChance = 0.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amountMultiplierBase = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amountMultiplierFactor = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Boosted Barter for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnBoostedBarter = 12; - } + return best; + } + + private ItemStack getRareBonusRoll() { + return switch (ThreadLocalRandom.current().nextInt(5)) { + case 0 -> new ItemStack(Material.ENDER_PEARL, 1); + case 1 -> new ItemStack(Material.OBSIDIAN, 2); + case 2 -> new ItemStack(Material.STRING, 4); + case 3 -> new ItemStack(Material.IRON_NUGGET, 6); + default -> new ItemStack(Material.SPECTRAL_ARROW, 2); + }; + } + + private double getExtraRollChance(int level) { + return Math.min(getConfig().maxExtraRollChance, getConfig().extraRollChanceBase + (getLevelPercent(level) * getConfig().extraRollChanceFactor)); + } + + private double getRareBonusChance(int level) { + return Math.min(getConfig().maxRareBonusChance, getConfig().rareBonusChanceBase + (getLevelPercent(level) * getConfig().rareBonusChanceFactor)); + } + + private double getAmountMultiplier(int level) { + return Math.max(1.0, getConfig().amountMultiplierBase + (getLevelPercent(level) * getConfig().amountMultiplierFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Nearby piglin bartering can yield extra or improved rolls.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Broker Range for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double brokerRange = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double extraRollChanceBase = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Extra Roll Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double extraRollChanceFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Extra Roll Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxExtraRollChance = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rareBonusChanceBase = 0.03; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rare Bonus Chance Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rareBonusChanceFactor = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Rare Bonus Chance for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRareBonusChance = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Base for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amountMultiplierBase = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amount Multiplier Factor for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amountMultiplierFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Boosted Barter for the Nether Piglin Broker adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnBoostedBarter = 12; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java index 45c003965..153920835 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherSkullYeet.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.Data; import lombok.NoArgsConstructor; @@ -54,171 +54,171 @@ public class NetherSkullYeet extends SimpleAdaptation { - private final Map lastJump = new ConcurrentHashMap<>(); - - public NetherSkullYeet() { - super("nether-skull-toss"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.skull_toss.description1") + C.ITALIC + " " + Localizer.dLocalize("nether.skull_toss.description2") + " " + C.GRAY + Localizer.dLocalize("nether.skull_toss.description3")); - setDisplayName(Localizer.dLocalize("nether.skull_toss.name")); - setIcon(Material.WITHER_SKELETON_SKULL); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(2314); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_nether_skull_100") - .title(Localizer.dLocalize("advancement.challenge_nether_skull_100.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_skull_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_nether_skull_kills_50") - .title(Localizer.dLocalize("advancement.challenge_nether_skull_kills_50.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_skull_kills_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_nether_skull_long_bomb") - .title(Localizer.dLocalize("advancement.challenge_nether_skull_long_bomb.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_skull_long_bomb.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.HIDDEN) - .build()); - registerMilestone("challenge_nether_skull_100", "nether.skull-yeet.skulls-thrown", 100, 300); - registerMilestone("challenge_nether_skull_kills_50", "nether.skull-yeet.skull-kills", 50, 500); - } - - @Override - public void addStats(int level, Element v) { - int chance = getConfig().getBaseCooldown() - getConfig().getLevelCooldown() * level; - v.addLore(C.GREEN + String.valueOf(chance) + C.GRAY + " " + Localizer.dLocalize("nether.skull_toss.lore1")); - v.addLore(C.GRAY + Localizer.dLocalize("nether.skull_toss.lore2") + C.DARK_GRAY + Localizer.dLocalize("nether.skull_toss.lore3") + C.GRAY + ", " + Localizer.dLocalize("nether.skull_toss.lore4")); - } - - private int getCooldownDuration(Player p) { - return (getConfig().getBaseCooldown() - getConfig().getLevelCooldown() * getLevel(p)) * 20; - } - - @EventHandler - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - lastJump.remove(p.getUniqueId()); - } - - @EventHandler - public void onRightClick(PlayerInteractEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (e.useItemInHand() == Event.Result.DENY) { - return; - } - - if (e.getAction() != Action.LEFT_CLICK_AIR && e.getAction() != Action.LEFT_CLICK_BLOCK) { - return; - } - if (e.getHand() != EquipmentSlot.HAND || e.getItem() == null || e.getMaterial() != Material.WITHER_SKELETON_SKULL) { - return; - } - - SoundPlayer sp = SoundPlayer.of(p); - - if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { - sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); - return; - } - - if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { - return; - } - - if (p.hasCooldown(p.getInventory().getItemInMainHand().getType())) { - e.setCancelled(true); - sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); - return; - } else { - p.setCooldown(Material.WITHER_SKELETON_SKULL, getCooldownDuration(p)); - } - - - if (p.getGameMode() != GameMode.CREATIVE) { - e.getItem().setAmount(e.getItem().getAmount() - 1); - lastJump.put(p.getUniqueId(), M.ms()); - } - - Vector dir = p.getEyeLocation().getDirection(); - Location spawn = p.getEyeLocation().add(new Vector(.5, -.5, .5)).add(dir); - p.getWorld().spawn(spawn, WitherSkull.class, entity -> { - sp.play(entity, Sound.ENTITY_WITHER_SHOOT, 1, 1); - entity.setRotation(p.getEyeLocation().getYaw(), p.getEyeLocation().getPitch()); - entity.setCharged(false); - entity.setBounce(false); - entity.setDirection(dir); - entity.setShooter(p); - xp(p, 100); - }); - getPlayer(p).getData().addStat("nether.skull-yeet.skulls-thrown", 1); - }); - } - - @EventHandler - public void onEntityDeath(EntityDeathEvent e) { - LivingEntity dead = e.getEntity(); - if (dead.getLastDamageCause() instanceof EntityDamageByEntityEvent dbe - && dbe.getDamager() instanceof WitherSkull skull - && skull.getShooter() instanceof Player p) { - withAdaptedPlayer(p, () -> { - getPlayer(p).getData().addStat("nether.skull-yeet.skull-kills", 1); - - double distance = p.getLocation().distance(dead.getLocation()); - if (distance >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_nether_skull_long_bomb")) { - getPlayer(p).getAdvancementHandler().grant("challenge_nether_skull_long_bomb"); - } - }); + private final Map lastJump = new ConcurrentHashMap<>(); + + public NetherSkullYeet() { + super("nether-skull-toss"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.skull_toss.description1") + C.ITALIC + " " + Localizer.dLocalize("nether.skull_toss.description2") + " " + C.GRAY + Localizer.dLocalize("nether.skull_toss.description3")); + setDisplayName(Localizer.dLocalize("nether.skull_toss.name")); + setIcon(Material.WITHER_SKELETON_SKULL); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(2314); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_nether_skull_100") + .title(Localizer.dLocalize("advancement.challenge_nether_skull_100.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_skull_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_nether_skull_kills_50") + .title(Localizer.dLocalize("advancement.challenge_nether_skull_kills_50.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_skull_kills_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_nether_skull_long_bomb") + .title(Localizer.dLocalize("advancement.challenge_nether_skull_long_bomb.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_skull_long_bomb.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.HIDDEN) + .build()); + registerMilestone("challenge_nether_skull_100", "nether.skull-yeet.skulls-thrown", 100, 300); + registerMilestone("challenge_nether_skull_kills_50", "nether.skull-yeet.skull-kills", 50, 500); + } + + @Override + public void addStats(int level, Element v) { + int chance = getConfig().getBaseCooldown() - getConfig().getLevelCooldown() * level; + v.addLore(C.GREEN + String.valueOf(chance) + C.GRAY + " " + Localizer.dLocalize("nether.skull_toss.lore1")); + v.addLore(C.GRAY + Localizer.dLocalize("nether.skull_toss.lore2") + C.DARK_GRAY + Localizer.dLocalize("nether.skull_toss.lore3") + C.GRAY + ", " + Localizer.dLocalize("nether.skull_toss.lore4")); + } + + private int getCooldownDuration(Player p) { + return (getConfig().getBaseCooldown() - getConfig().getLevelCooldown() * getLevel(p)) * 20; + } + + @EventHandler + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + lastJump.remove(p.getUniqueId()); + } + + @EventHandler + public void onRightClick(PlayerInteractEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (e.useItemInHand() == Event.Result.DENY) { + return; + } + + if (e.getAction() != Action.LEFT_CLICK_AIR && e.getAction() != Action.LEFT_CLICK_BLOCK) { + return; + } + if (e.getHand() != EquipmentSlot.HAND || e.getItem() == null || e.getMaterial() != Material.WITHER_SKELETON_SKULL) { + return; + } + + SoundPlayer sp = SoundPlayer.of(p); + + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); + return; + } + + if (lastJump.get(p.getUniqueId()) != null && M.ms() - lastJump.get(p.getUniqueId()) <= getCooldownDuration(p)) { + return; + } + + if (p.hasCooldown(p.getInventory().getItemInMainHand().getType())) { + e.setCancelled(true); + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 1F, 1F); + return; + } else { + p.setCooldown(Material.WITHER_SKELETON_SKULL, getCooldownDuration(p)); + } + + + if (p.getGameMode() != GameMode.CREATIVE) { + e.getItem().setAmount(e.getItem().getAmount() - 1); + lastJump.put(p.getUniqueId(), M.ms()); + } + + Vector dir = p.getEyeLocation().getDirection(); + Location spawn = p.getEyeLocation().add(new Vector(.5, -.5, .5)).add(dir); + p.getWorld().spawn(spawn, WitherSkull.class, entity -> { + sp.play(entity, Sound.ENTITY_WITHER_SHOOT, 1, 1); + entity.setRotation(p.getEyeLocation().getYaw(), p.getEyeLocation().getPitch()); + entity.setCharged(false); + entity.setBounce(false); + entity.setDirection(dir); + entity.setShooter(p); + xp(p, 100); + }); + getPlayer(p).getData().addStat("nether.skull-yeet.skulls-thrown", 1); + }); + } + + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + LivingEntity dead = e.getEntity(); + if (dead.getLastDamageCause() instanceof EntityDamageByEntityEvent dbe + && dbe.getDamager() instanceof WitherSkull skull + && skull.getShooter() instanceof Player p) { + withAdaptedPlayer(p, () -> { + getPlayer(p).getData().addStat("nether.skull-yeet.skull-kills", 1); + + double distance = p.getLocation().distance(dead.getLocation()); + if (distance >= 40 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_nether_skull_long_bomb")) { + getPlayer(p).getAdvancementHandler().grant("challenge_nether_skull_long_bomb"); } + }); } - - @Override - public boolean isEnabled() { - return getConfig().isEnabled(); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - - @Data - @NoArgsConstructor - @ConfigDescription("Throw Wither Skulls that explode on impact.") - public static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - public boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - private boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private int baseCooldown = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private int levelCooldown = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - private int baseCost = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - private double costFactor = 0.92; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - private int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - private int initialCost = 5; - } + } + + @Override + public boolean isEnabled() { + return getConfig().isEnabled(); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + + @Data + @NoArgsConstructor + @ConfigDescription("Throw Wither Skulls that explode on impact.") + public static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + public boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + private boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private int baseCooldown = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Level Cooldown for the Nether Skull Yeet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private int levelCooldown = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + private int baseCost = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + private double costFactor = 0.92; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + private int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + private int initialCost = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java index 7fe480ca8..bc57e960e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/nether/NetherWitherResist.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.Data; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -39,103 +39,103 @@ public class NetherWitherResist extends SimpleAdaptation { - public NetherWitherResist() { - super("nether-wither-resist"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("nether.wither_resist.description")); - setDisplayName(Localizer.dLocalize("nether.wither_resist.name")); - setIcon(Material.NETHERITE_CHESTPLATE); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(9283); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_ROSE) - .key("challenge_nether_wither_100") - .title(Localizer.dLocalize("advancement.challenge_nether_wither_100.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_wither_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_nether_wither_1k") - .title(Localizer.dLocalize("advancement.challenge_nether_wither_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_wither_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_nether_wither_100", "nether.wither-resist.negated", 100, 300); - registerMilestone("challenge_nether_wither_1k", "nether.wither-resist.negated", 1000, 1000); - } + public NetherWitherResist() { + super("nether-wither-resist"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("nether.wither_resist.description")); + setDisplayName(Localizer.dLocalize("nether.wither_resist.name")); + setIcon(Material.NETHERITE_CHESTPLATE); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(9283); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_nether_wither_100") + .title(Localizer.dLocalize("advancement.challenge_nether_wither_100.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_wither_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_nether_wither_1k") + .title(Localizer.dLocalize("advancement.challenge_nether_wither_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_wither_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_nether_wither_100", "nether.wither-resist.negated", 100, 300); + registerMilestone("challenge_nether_wither_1k", "nether.wither-resist.negated", 1000, 1000); + } - @Override - public void addStats(int level, Element v) { - int chance = (int) (getConfig().basePieceChance + getConfig().getChanceAddition() * level); - v.addLore(C.GREEN + "+ " + chance + "%" + C.GRAY + Localizer.dLocalize("nether.wither_resist.lore1")); - v.addLore(C.GRAY + " " + Localizer.dLocalize("nether.wither_resist.lore1") + C.DARK_GRAY + Localizer.dLocalize("nether.wither_resist.lore2")); - } + @Override + public void addStats(int level, Element v) { + int chance = (int) (getConfig().basePieceChance + getConfig().getChanceAddition() * level); + v.addLore(C.GREEN + "+ " + chance + "%" + C.GRAY + Localizer.dLocalize("nether.wither_resist.lore1")); + v.addLore(C.GRAY + " " + Localizer.dLocalize("nether.wither_resist.lore1") + C.DARK_GRAY + Localizer.dLocalize("nether.wither_resist.lore2")); + } - @EventHandler - public void onEntityDamage(EntityDamageEvent e) { - if (e.getCause() == EntityDamageEvent.DamageCause.WITHER && e.getEntity() instanceof Player p) { - withAdaptedPlayer(p, e, () -> { - double chance = getTotalChange(p); - if (ThreadLocalRandom.current().nextInt(101) <= chance) { - e.setCancelled(true); - getPlayer(p).getData().addStat("nether.wither-resist.negated", 1); - } - }); + @EventHandler + public void onEntityDamage(EntityDamageEvent e) { + if (e.getCause() == EntityDamageEvent.DamageCause.WITHER && e.getEntity() instanceof Player p) { + withAdaptedPlayer(p, e, () -> { + double chance = getTotalChange(p); + if (ThreadLocalRandom.current().nextInt(101) <= chance) { + e.setCancelled(true); + getPlayer(p).getData().addStat("nether.wither-resist.negated", 1); } + }); } + } - @Override - public boolean isEnabled() { - return getConfig().isEnabled(); - } + @Override + public boolean isEnabled() { + return getConfig().isEnabled(); + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - private double getTotalChange(Player p) { - return getChance(p, EquipmentSlot.HEAD) + getChance(p, EquipmentSlot.CHEST) + getChance(p, EquipmentSlot.LEGS) + getChance(p, EquipmentSlot.FEET); - } + private double getTotalChange(Player p) { + return getChance(p, EquipmentSlot.HEAD) + getChance(p, EquipmentSlot.CHEST) + getChance(p, EquipmentSlot.LEGS) + getChance(p, EquipmentSlot.FEET); + } - private double getChance(Player p, EquipmentSlot slot) { - if (p.getEquipment() == null) - return 0.0; - ItemStack item = p.getEquipment().getItem(slot); - if (item.getType() == Material.NETHERITE_HELMET || item.getType() == Material.NETHERITE_CHESTPLATE || item.getType() == Material.NETHERITE_LEGGINGS || item.getType() == Material.NETHERITE_BOOTS) - return getConfig().basePieceChance + getConfig().chanceAddition * getLevel(p); - return 0.0D; - } + private double getChance(Player p, EquipmentSlot slot) { + if (p.getEquipment() == null) + return 0.0; + ItemStack item = p.getEquipment().getItem(slot); + if (item.getType() == Material.NETHERITE_HELMET || item.getType() == Material.NETHERITE_CHESTPLATE || item.getType() == Material.NETHERITE_LEGGINGS || item.getType() == Material.NETHERITE_BOOTS) + return getConfig().basePieceChance + getConfig().chanceAddition * getLevel(p); + return 0.0D; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Data - @NoArgsConstructor - @ConfigDescription("Wearing Netherite Armor has a chance to negate the wither effect.") - public static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - public boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - private boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - private int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - private double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - private int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - private int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Piece Chance for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double basePieceChance = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Addition for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double chanceAddition = 5; - } + @Data + @NoArgsConstructor + @ConfigDescription("Wearing Netherite Armor has a chance to negate the wither effect.") + public static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + public boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + private boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + private int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + private double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + private int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + private int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Piece Chance for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double basePieceChance = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Addition for the Nether Wither Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double chanceAddition = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java index 0df705dc2..cf3077f5c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeAutosmelt.java @@ -28,10 +28,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -47,218 +47,218 @@ import java.util.concurrent.ThreadLocalRandom; public class PickaxeAutosmelt extends SimpleAdaptation { - public PickaxeAutosmelt() { - super("pickaxe-autosmelt"); - registerConfiguration(PickaxeAutosmelt.Config.class); - setDescription(Localizer.dLocalize("pickaxe.auto_smelt.description")); - setDisplayName(Localizer.dLocalize("pickaxe.auto_smelt.name")); - setIcon(Material.RAW_GOLD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(7444); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FURNACE) - .key("challenge_pickaxe_autosmelt_1k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BLAST_FURNACE) - .key("challenge_pickaxe_autosmelt_25k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pickaxe_autosmelt_1k", "pickaxe.autosmelt.ores-smelted", 1000, 400); - registerMilestone("challenge_pickaxe_autosmelt_25k", "pickaxe.autosmelt.ores-smelted", 25000, 1500); - } + public PickaxeAutosmelt() { + super("pickaxe-autosmelt"); + registerConfiguration(PickaxeAutosmelt.Config.class); + setDescription(Localizer.dLocalize("pickaxe.auto_smelt.description")); + setDisplayName(Localizer.dLocalize("pickaxe.auto_smelt.name")); + setIcon(Material.RAW_GOLD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(7444); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FURNACE) + .key("challenge_pickaxe_autosmelt_1k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BLAST_FURNACE) + .key("challenge_pickaxe_autosmelt_25k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_autosmelt_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pickaxe_autosmelt_1k", "pickaxe.autosmelt.ores-smelted", 1000, 400); + registerMilestone("challenge_pickaxe_autosmelt_25k", "pickaxe.autosmelt.ores-smelted", 25000, 1500); + } - static void autosmeltBlockDTI(Block b, Player p) { - int fortune = getFortuneOreMultiplier(p.getInventory().getItemInMainHand() - .getEnchantments().get(Enchantments.LOOT_BONUS_BLOCKS)); - SoundPlayer spw = SoundPlayer.of(b.getWorld()); - switch (b.getType()) { - case IRON_ORE, DEEPSLATE_IRON_ORE -> { - if (b.getLocation().getWorld() == null) { - return; - } - - b.setType(Material.AIR); - HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.IRON_INGOT, fortune)); - excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } - case GOLD_ORE, DEEPSLATE_GOLD_ORE -> { - if (b.getLocation().getWorld() == null) { - return; - } + static void autosmeltBlockDTI(Block b, Player p) { + int fortune = getFortuneOreMultiplier(p.getInventory().getItemInMainHand() + .getEnchantments().get(Enchantments.LOOT_BONUS_BLOCKS)); + SoundPlayer spw = SoundPlayer.of(b.getWorld()); + switch (b.getType()) { + case IRON_ORE, DEEPSLATE_IRON_ORE -> { + if (b.getLocation().getWorld() == null) { + return; + } - b.setType(Material.AIR); - HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.GOLD_INGOT, fortune)); - excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } - case COPPER_ORE, DEEPSLATE_COPPER_ORE -> { - if (b.getLocation().getWorld() == null) { - return; - } - b.setType(Material.AIR); - HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.COPPER_INGOT, fortune)); - excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } + b.setType(Material.AIR); + HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.IRON_INGOT, fortune)); + excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); + } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } + case GOLD_ORE, DEEPSLATE_GOLD_ORE -> { + if (b.getLocation().getWorld() == null) { + return; + } + b.setType(Material.AIR); + HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.GOLD_INGOT, fortune)); + excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); } - } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } + case COPPER_ORE, DEEPSLATE_COPPER_ORE -> { + if (b.getLocation().getWorld() == null) { + return; + } + b.setType(Material.AIR); + HashMap excessItems = p.getInventory().addItem(new ItemStack(Material.COPPER_INGOT, fortune)); + excessItems.values().forEach(itemStack -> b.getLocation().getWorld().dropItemNaturally(b.getLocation(), itemStack)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); + } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } - static void autosmeltBlock(Block b, Player p) { - int fortune = getFortuneOreMultiplier(p.getInventory().getItemInMainHand() - .getEnchantments().get(Enchantments.LOOT_BONUS_BLOCKS)); - SoundPlayer spw = SoundPlayer.of(b.getWorld()); - switch (b.getType()) { - case IRON_ORE, DEEPSLATE_IRON_ORE -> { + } + } - if (b.getLocation().getWorld() == null) { - return; - } + static void autosmeltBlock(Block b, Player p) { + int fortune = getFortuneOreMultiplier(p.getInventory().getItemInMainHand() + .getEnchantments().get(Enchantments.LOOT_BONUS_BLOCKS)); + SoundPlayer spw = SoundPlayer.of(b.getWorld()); + switch (b.getType()) { + case IRON_ORE, DEEPSLATE_IRON_ORE -> { - b.setType(Material.AIR); - b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.IRON_INGOT, fortune)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } - case GOLD_ORE, DEEPSLATE_GOLD_ORE -> { - if (b.getLocation().getWorld() == null) { - return; - } + if (b.getLocation().getWorld() == null) { + return; + } - b.setType(Material.AIR); - b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.GOLD_INGOT, fortune)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } - case COPPER_ORE, DEEPSLATE_COPPER_ORE -> { - if (b.getLocation().getWorld() == null) { - return; - } - b.setType(Material.AIR); - b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.COPPER_INGOT, fortune)); - if (soundsEnabled()) { - spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); - } - if (particlesEnabled()) { - b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); - } - } + b.setType(Material.AIR); + b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.IRON_INGOT, fortune)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); + } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } + case GOLD_ORE, DEEPSLATE_GOLD_ORE -> { + if (b.getLocation().getWorld() == null) { + return; + } + b.setType(Material.AIR); + b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.GOLD_INGOT, fortune)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } + case COPPER_ORE, DEEPSLATE_COPPER_ORE -> { + if (b.getLocation().getWorld() == null) { + return; + } + b.setType(Material.AIR); + b.getLocation().getWorld().dropItemNaturally(b.getLocation(), new ItemStack(Material.COPPER_INGOT, fortune)); + if (soundsEnabled()) { + spw.play(b.getLocation(), Sound.BLOCK_LAVA_POP, 1, 1); + } + if (particlesEnabled()) { + b.getWorld().spawnParticle(Particle.LAVA, b.getLocation(), 3, 0.5, 0.5, 0.5); + } + } + } + } - // https://minecraft.fandom.com/wiki/Fortune?oldid=2359015#Ore - private static int getFortuneOreMultiplier(Integer fortuneLevel) { - if (fortuneLevel == null || fortuneLevel < 1) return 1; + // https://minecraft.fandom.com/wiki/Fortune?oldid=2359015#Ore + private static int getFortuneOreMultiplier(Integer fortuneLevel) { + if (fortuneLevel == null || fortuneLevel < 1) return 1; - double averageBonusMultiplier = (1.0/(fortuneLevel+2) + (fortuneLevel+1)/2.0) - 1; - int sumOfBonusMultipliers = (fortuneLevel*(fortuneLevel+1))/2; - double chancePerMultiplier = averageBonusMultiplier/sumOfBonusMultipliers; + double averageBonusMultiplier = (1.0 / (fortuneLevel + 2) + (fortuneLevel + 1) / 2.0) - 1; + int sumOfBonusMultipliers = (fortuneLevel * (fortuneLevel + 1)) / 2; + double chancePerMultiplier = averageBonusMultiplier / sumOfBonusMultipliers; - int bonusMultiplier = ((int) (ThreadLocalRandom.current().nextDouble()/chancePerMultiplier)) + 1; + int bonusMultiplier = ((int) (ThreadLocalRandom.current().nextDouble() / chancePerMultiplier)) + 1; - return bonusMultiplier <= fortuneLevel ? bonusMultiplier+1 : 1; - } + return bonusMultiplier <= fortuneLevel ? bonusMultiplier + 1 : 1; + } - private static boolean particlesEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isParticlesEnabled(); - } + private static boolean particlesEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isParticlesEnabled(); + } - private static boolean soundsEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isSoundsEnabled(); - } + private static boolean soundsEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isSoundsEnabled(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.auto_smelt.lore1")); - v.addLore(C.GREEN + "" + (level * 1.25) + C.GRAY + Localizer.dLocalize("pickaxe.auto_smelt.lore2")); - } + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.auto_smelt.lore1")); + v.addLore(C.GREEN + "" + (level * 1.25) + C.GRAY + Localizer.dLocalize("pickaxe.auto_smelt.lore2")); + } - @EventHandler(priority = EventPriority.HIGH) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE") && !ItemListings.getSmeltOre().contains(e.getBlock().getType())) { - return; - } - if (resolveBlockBreakContext(p, e.getBlock().getLocation()) == null) { - return; - } + @EventHandler(priority = EventPriority.HIGH) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE") && !ItemListings.getSmeltOre().contains(e.getBlock().getType())) { + return; + } + if (resolveBlockBreakContext(p, e.getBlock().getLocation()) == null) { + return; + } - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("pickaxe"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("pickaxe-drop-to-inventory") : null; - if (adaptation != null && adaptation.getLevel() > 0) { - PickaxeAutosmelt.autosmeltBlockDTI(e.getBlock(), p); - } else { - PickaxeAutosmelt.autosmeltBlock(e.getBlock(), p); - } - getPlayer(p).getData().addStat("pickaxe.autosmelt.ores-smelted", 1); + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("pickaxe"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("pickaxe-drop-to-inventory") : null; + if (adaptation != null && adaptation.getLevel() > 0) { + PickaxeAutosmelt.autosmeltBlockDTI(e.getBlock(), p); + } else { + PickaxeAutosmelt.autosmeltBlock(e.getBlock(), p); } + getPlayer(p).getData().addStat("pickaxe.autosmelt.ores-smelted", 1); + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Automatically smelt mined ores with a chance for extra drops.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - } + @NoArgsConstructor + @ConfigDescription("Automatically smelt mined ores with a chance for extra drops.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java index d4920cad0..5be47a487 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java @@ -24,11 +24,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Location; @@ -43,161 +43,166 @@ import org.bukkit.inventory.ItemStack; public class PickaxeChisel extends SimpleAdaptation { - public PickaxeChisel() { - super("pickaxe-chisel"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("pickaxe.chisel.description")); - setDisplayName(Localizer.dLocalize("pickaxe.chisel.name")); - setIcon(Material.IRON_NUGGET); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(7433); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_pickaxe_chisel_500") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_chisel_500.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_chisel_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_pickaxe_chisel_500", "pickaxe.chisel.extra-ores", 500, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getDropChance(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("pickaxe.chisel.lore1")); - v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("pickaxe.chisel.lore2")); - } - - private int getCooldownTime(double levelPercent) { - return getConfig().cooldownTime; - } - - private double getDropChance(double levelPercent) { - return ((levelPercent) * getConfig().dropChanceFactor) + getConfig().dropChanceBase; - } - - private double getBreakChance(double levelPercent) { - return getConfig().breakChance; - } - - private int getDamagePerBlock(double levelPercent) { - return (int) (getConfig().damagePerBlockBase + (getConfig().damageFactorInverseMultiplier * ((1D - levelPercent)))); - } - - - @EventHandler - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isPickaxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { - if (p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH) || p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.MENDING)) { - return; - } - if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { - return; - } - if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { - return; - } - BlockData b = e.getClickedBlock().getBlockData(); - if (isOre(b)) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 1.4f); - spw.play(p.getLocation(), Sound.BLOCK_METAL_HIT, 1.25f, 1.7f); - - p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); - damageHand(p, getDamagePerBlock(getLevelPercent(p))); - - Location c = p.rayTraceBlocks(8).getHitPosition().toLocation(p.getWorld()); - - ItemStack is = getDropFor(b); - if (M.r(getDropChance(getLevelPercent(p)))) { - if (areParticlesEnabled()) { - e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 14, 0.10, 0.01, 0.01, 0.1, is); - } - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.787f); - spw.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_PLACE, 0.55f, 1.89f); - e.getClickedBlock().getWorld().dropItemNaturally(c.clone().subtract(p.getLocation().getDirection().clone().multiply(0.1)), is); - getPlayer(p).getData().addStat("pickaxe.chisel.extra-ores", 1); - } else { - if (areParticlesEnabled()) { - e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 3, 0.01, 0.01, 0.01, 0.1, is); - e.getClickedBlock().getWorld().spawnParticle(Particles.BLOCK_CRACK, c, 9, 0.1, 0.1, 0.1, e.getClickedBlock().getBlockData()); - } - } - - if (M.r(getBreakChance(getLevelPercent(p)))) { - spw.play(p.getLocation(), Sound.BLOCK_BASALT_BREAK, 1.25f, 0.4f); - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.887f); - e.getClickedBlock().breakNaturally(p.getInventory().getItemInMainHand()); - } - } - + public PickaxeChisel() { + super("pickaxe-chisel"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("pickaxe.chisel.description")); + setDisplayName(Localizer.dLocalize("pickaxe.chisel.name")); + setIcon(Material.IRON_NUGGET); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(7433); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_pickaxe_chisel_500") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_chisel_500.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_chisel_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_chisel_500", "pickaxe.chisel.extra-ores", 500, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getDropChance(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("pickaxe.chisel.lore1")); + v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("pickaxe.chisel.lore2")); + } + + private int getCooldownTime(double levelPercent) { + return getConfig().cooldownTime; + } + + private double getDropChance(double levelPercent) { + return ((levelPercent) * getConfig().dropChanceFactor) + getConfig().dropChanceBase; + } + + private double getBreakChance(double levelPercent) { + return getConfig().breakChance; + } + + private int getDamagePerBlock(double levelPercent) { + return (int) (getConfig().damagePerBlockBase + (getConfig().damageFactorInverseMultiplier * ((1D - levelPercent)))); + } + + + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + + if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isPickaxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { + if (p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH) || p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.MENDING)) { + return; + } + if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { + return; + } + if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { + return; + } + BlockData b = e.getClickedBlock().getBlockData(); + if (isOre(b)) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 1.4f); + spw.play(p.getLocation(), Sound.BLOCK_METAL_HIT, 1.25f, 1.7f); + + p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); + damageHand(p, getDamagePerBlock(getLevelPercent(p))); + + Location c = p.rayTraceBlocks(8).getHitPosition().toLocation(p.getWorld()); + + ItemStack is = getDropFor(b); + if (M.r(getDropChance(getLevelPercent(p)))) { + if (areParticlesEnabled()) { + e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 14, 0.10, 0.01, 0.01, 0.1, is); + } + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.787f); + spw.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_PLACE, 0.55f, 1.89f); + e.getClickedBlock().getWorld().dropItemNaturally(c.clone().subtract(p.getLocation().getDirection().clone().multiply(0.1)), is); + getPlayer(p).getData().addStat("pickaxe.chisel.extra-ores", 1); + } else { + if (areParticlesEnabled()) { + e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 3, 0.01, 0.01, 0.01, 0.1, is); + e.getClickedBlock().getWorld().spawnParticle(Particles.BLOCK_CRACK, c, 9, 0.1, 0.1, 0.1, e.getClickedBlock().getBlockData()); + } } - } - - private ItemStack getDropFor(BlockData b) { - return switch (b.getMaterial()) { - case COAL_ORE, DEEPSLATE_COAL_ORE -> new ItemStack(Material.COAL); - case COPPER_ORE, DEEPSLATE_COPPER_ORE -> new ItemStack(Material.RAW_COPPER); - case GOLD_ORE, DEEPSLATE_GOLD_ORE, NETHER_GOLD_ORE -> new ItemStack(Material.RAW_GOLD); - case IRON_ORE, DEEPSLATE_IRON_ORE -> new ItemStack(Material.RAW_IRON); - case DIAMOND_ORE, DEEPSLATE_DIAMOND_ORE -> new ItemStack(Material.DIAMOND); - case LAPIS_ORE, DEEPSLATE_LAPIS_ORE -> new ItemStack(Material.LAPIS_LAZULI); - case EMERALD_ORE, DEEPSLATE_EMERALD_ORE -> new ItemStack(Material.EMERALD); - case NETHER_QUARTZ_ORE -> new ItemStack(Material.QUARTZ); - case REDSTONE_ORE -> new ItemStack(Material.REDSTONE); - - default -> new ItemStack(Material.AIR); - }; - } - - @Override - public void onTick() { - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + if (M.r(getBreakChance(getLevelPercent(p)))) { + spw.play(p.getLocation(), Sound.BLOCK_BASALT_BREAK, 1.25f, 0.4f); + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.887f); + e.getClickedBlock().breakNaturally(p.getInventory().getItemInMainHand()); + } + } - @NoArgsConstructor - @ConfigDescription("Right-click ores to chisel extra ore at a severe durability cost.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Chisel adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Time for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int cooldownTime = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dropChanceBase = 0.07; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dropChanceFactor = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Break Chance for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double breakChance = 0.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerBlockBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor Inverse Multiplier for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactorInverseMultiplier = 2; } + } + + private ItemStack getDropFor(BlockData b) { + return switch (b.getMaterial()) { + case COAL_ORE, DEEPSLATE_COAL_ORE -> new ItemStack(Material.COAL); + case COPPER_ORE, DEEPSLATE_COPPER_ORE -> + new ItemStack(Material.RAW_COPPER); + case GOLD_ORE, DEEPSLATE_GOLD_ORE, NETHER_GOLD_ORE -> + new ItemStack(Material.RAW_GOLD); + case IRON_ORE, DEEPSLATE_IRON_ORE -> new ItemStack(Material.RAW_IRON); + case DIAMOND_ORE, DEEPSLATE_DIAMOND_ORE -> + new ItemStack(Material.DIAMOND); + case LAPIS_ORE, DEEPSLATE_LAPIS_ORE -> + new ItemStack(Material.LAPIS_LAZULI); + case EMERALD_ORE, DEEPSLATE_EMERALD_ORE -> + new ItemStack(Material.EMERALD); + case NETHER_QUARTZ_ORE -> new ItemStack(Material.QUARTZ); + case REDSTONE_ORE -> new ItemStack(Material.REDSTONE); + + default -> new ItemStack(Material.AIR); + }; + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Right-click ores to chisel extra ore at a severe durability cost.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Chisel adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Time for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int cooldownTime = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dropChanceBase = 0.07; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drop Chance Factor for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dropChanceFactor = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Break Chance for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double breakChance = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Block Base for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerBlockBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor Inverse Multiplier for the Pickaxe Chisel adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactorInverseMultiplier = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java index 1860bc9ff..9af349de1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDropToInventory.java @@ -25,10 +25,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -41,85 +41,85 @@ import java.util.List; public class PickaxeDropToInventory extends SimpleAdaptation { - public PickaxeDropToInventory() { - super("pickaxe-drop-to-inventory"); - registerConfiguration(PickaxeDropToInventory.Config.class); - setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); - setDisplayName(Localizer.dLocalize("pickaxe.drop_to_inventory.name")); - setIcon(Material.MINECART); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(7944); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_pickaxe_dti_25k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_dti_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_dti_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_pickaxe_dti_25k", "pickaxe.drop-to-inv.items-caught", 25000, 500); - } + public PickaxeDropToInventory() { + super("pickaxe-drop-to-inventory"); + registerConfiguration(PickaxeDropToInventory.Config.class); + setDescription(Localizer.dLocalize("pickaxe.drop_to_inventory.description")); + setDisplayName(Localizer.dLocalize("pickaxe.drop_to_inventory.name")); + setIcon(Material.MINECART); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(7944); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_pickaxe_dti_25k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_dti_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_dti_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_dti_25k", "pickaxe.drop-to-inv.items-caught", 25000, 500); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); - } + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("pickaxe.drop_to_inventory.lore1")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockDropItemEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { - return; - } - if (ItemListings.toolPickaxes.contains(p.getInventory().getItemInMainHand().getType())) { - List items = new KList<>(e.getItems()); - e.getItems().clear(); - int caught = 0; - for (Item i : items) { - sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); - if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { - p.getWorld().dropItem(p.getLocation(), i.getItemStack()); - } - caught++; - } - if (caught > 0) { - getPlayer(p).getData().addStat("pickaxe.drop-to-inv.items-caught", caught); - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDropItemEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (resolveBlockBreakContext(p, e.getBlock().getLocation(), null, true) == null) { + return; + } + if (ItemListings.toolPickaxes.contains(p.getInventory().getItemInMainHand().getType())) { + List items = new KList<>(e.getItems()); + e.getItems().clear(); + int caught = 0; + for (Item i : items) { + sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.05f, 0.01f); + if (!p.getInventory().addItem(i.getItemStack()).isEmpty()) { + p.getWorld().dropItem(p.getLocation(), i.getItemStack()); } + caught++; + } + if (caught > 0) { + getPlayer(p).getData().addStat("pickaxe.drop-to-inv.items-caught", caught); + } } + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Mined blocks drop directly into your inventory.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @NoArgsConstructor + @ConfigDescription("Mined blocks drop directly into your inventory.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index 912a009bc..e6203f8e0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -53,336 +53,336 @@ import java.util.List; public class PickaxeQuarrySense extends SimpleAdaptation { - private static final String MARKER_META = "adapt-quarry-sense-marker"; - - public PickaxeQuarrySense() { - super("pickaxe-quarry-sense"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("pickaxe.quarry_sense.description")); - setDisplayName(Localizer.dLocalize("pickaxe.quarry_sense.name")); - setIcon(Material.MAP); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPYGLASS) - .key("challenge_pickaxe_quarry_200") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_quarry_200.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_quarry_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_pickaxe_quarry_200", "pickaxe.quarry-sense.scans", 200, 300); + private static final String MARKER_META = "adapt-quarry-sense-marker"; + + public PickaxeQuarrySense() { + super("pickaxe-quarry-sense"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("pickaxe.quarry_sense.description")); + setDisplayName(Localizer.dLocalize("pickaxe.quarry_sense.name")); + setIcon(Material.MAP); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPYGLASS) + .key("challenge_pickaxe_quarry_200") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_quarry_200.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_quarry_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_quarry_200", "pickaxe.quarry-sense.scans", 200, 300); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getScanRadius(level)) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getDurabilityCostPercent(level), 2) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getScanRadius(level)) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getDurabilityCostPercent(level), 2) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("pickaxe.quarry_sense.lore3")); + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { - return; - } - - if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!isEligiblePickaxe(hand) || p.hasCooldown(hand.getType())) { - return; - } - - if (areParticlesEnabled()) { - p.spawnParticle(Particle.ENCHANT, p.getEyeLocation(), 14, 0.2, 0.25, 0.2, 0.15); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.8f, 1.35f); - - int durabilityCost = getDurabilityCost(hand, level); - if (!applyPickaxeCost(p, hand, durabilityCost)) { - if (areParticlesEnabled()) { - p.spawnParticle(Particle.SMOKE, p.getEyeLocation(), 8, 0.2, 0.2, 0.2, 0.03); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ANVIL_PLACE, 0.5f, 0.65f); - return; - } - - List ores = findNearbyOres(p.getLocation(), getScanRadius(level), getMaxHighlights(level)); - if (ores.isEmpty()) { - if (areParticlesEnabled()) { - p.spawnParticle(Particle.SMOKE, p.getEyeLocation(), 12, 0.22, 0.22, 0.22, 0.02); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.6f, 0.75f); - p.setCooldown(hand.getType(), getCooldownTicks(level)); - e.setCancelled(true); - return; - } - - for (Block ore : ores) { - showOreMarker(p, ore, getHighlightTicks(level)); - } - - p.setCooldown(hand.getType(), getCooldownTicks(level)); - if (areParticlesEnabled()) { - p.spawnParticle(Particle.GLOW, p.getEyeLocation(), 8, 0.15, 0.15, 0.15, 0.01); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.9f, 1.6f); - xp(p, ores.size() * getConfig().xpPerFoundOre); - getPlayer(p).getData().addStat("pickaxe.quarry-sense.scans", 1); - e.setCancelled(true); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof Slime slime && slime.hasMetadata(MARKER_META)) { - e.setCancelled(true); - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isEligiblePickaxe(hand) || p.hasCooldown(hand.getType())) { + return; } - private List findNearbyOres(Location origin, int radius, int maxResults) { - List ores = new ArrayList<>(); - for (int x = -radius; x <= radius; x++) { - for (int y = -radius; y <= radius; y++) { - for (int z = -radius; z <= radius; z++) { - Block b = origin.getWorld().getBlockAt(origin.getBlockX() + x, origin.getBlockY() + y, origin.getBlockZ() + z); - if (!isOre(b.getBlockData())) { - continue; - } - - ores.add(b); - } - } - } - - ores.sort(Comparator.comparingDouble(b -> b.getLocation().distanceSquared(origin))); - if (ores.size() > maxResults) { - return new ArrayList<>(ores.subList(0, maxResults)); - } - - return ores; + if (areParticlesEnabled()) { + p.spawnParticle(Particle.ENCHANT, p.getEyeLocation(), 14, 0.2, 0.25, 0.2, 0.15); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.8f, 1.35f); + + int durabilityCost = getDurabilityCost(hand, level); + if (!applyPickaxeCost(p, hand, durabilityCost)) { + if (areParticlesEnabled()) { + p.spawnParticle(Particle.SMOKE, p.getEyeLocation(), 8, 0.2, 0.2, 0.2, 0.03); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ANVIL_PLACE, 0.5f, 0.65f); + return; } - private void showOreMarker(Player p, Block ore, int durationTicks) { - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - if (glowingEntities == null) { - showFallbackMarker(p, ore, durationTicks); - return; - } - - Slime slime = ore.getWorld().spawn(ore.getLocation().add(0.5, 0.5, 0.5), Slime.class, s -> { - s.setInvulnerable(true); - s.setCollidable(false); - s.setGravity(false); - s.setSilent(true); - s.setAI(false); - s.setSize(2); - s.setRotation(0, 0); - s.setMetadata(MARKER_META, new FixedMetadataValue(Adapt.instance, true)); - s.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false)); - }); - - try { - glowingEntities.setGlowing(slime, p, ChatColor.AQUA); - } catch (ReflectiveOperationException ex) { - Adapt.verbose("Failed to enable glowing marker for QuarrySense: " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - slime.remove(); - showFallbackMarker(p, ore, durationTicks); - return; - } - - if (areParticlesEnabled()) { - - p.spawnParticle(Particle.GLOW, ore.getLocation().add(0.5, 0.5, 0.5), 20, 0.25, 0.25, 0.25, 0.001); + List ores = findNearbyOres(p.getLocation(), getScanRadius(level), getMaxHighlights(level)); + if (ores.isEmpty()) { + if (areParticlesEnabled()) { + p.spawnParticle(Particle.SMOKE, p.getEyeLocation(), 12, 0.22, 0.22, 0.22, 0.02); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.6f, 0.75f); + p.setCooldown(hand.getType(), getCooldownTicks(level)); + e.setCancelled(true); + return; + } - } - if (areParticlesEnabled()) { - p.spawnParticle(Particle.END_ROD, ore.getLocation().add(0.5, 0.5, 0.5), 8, 0.15, 0.15, 0.15, 0.003); + for (Block ore : ores) { + showOreMarker(p, ore, getHighlightTicks(level)); + } + p.setCooldown(hand.getType(), getCooldownTicks(level)); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.GLOW, p.getEyeLocation(), 8, 0.15, 0.15, 0.15, 0.01); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.9f, 1.6f); + xp(p, ores.size() * getConfig().xpPerFoundOre); + getPlayer(p).getData().addStat("pickaxe.quarry-sense.scans", 1); + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof Slime slime && slime.hasMetadata(MARKER_META)) { + e.setCancelled(true); + } + } + + private List findNearbyOres(Location origin, int radius, int maxResults) { + List ores = new ArrayList<>(); + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + for (int z = -radius; z <= radius; z++) { + Block b = origin.getWorld().getBlockAt(origin.getBlockX() + x, origin.getBlockY() + y, origin.getBlockZ() + z); + if (!isOre(b.getBlockData())) { + continue; + } + + ores.add(b); } - J.runEntity(slime, () -> { - try { - glowingEntities.unsetGlowing(slime, p); - } catch (ReflectiveOperationException ex) { - Adapt.verbose("Failed to clear glowing marker for QuarrySense: " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - slime.remove(); - }, durationTicks); + } } - private void showFallbackMarker(Player p, Block ore, int durationTicks) { - Location loc = ore.getLocation().add(0.5, 0.5, 0.5); - for (int t = 0; t <= durationTicks; t += 8) { - J.runEntity(p, () -> { - if (!p.isOnline()) { - return; - } - - if (areParticlesEnabled()) { + ores.sort(Comparator.comparingDouble(b -> b.getLocation().distanceSquared(origin))); + if (ores.size() > maxResults) { + return new ArrayList<>(ores.subList(0, maxResults)); + } - p.spawnParticle(Particle.GLOW, loc, 14, 0.22, 0.22, 0.22, 0.001); + return ores; + } - } - if (areParticlesEnabled()) { - p.spawnParticle(Particle.END_ROD, loc, 4, 0.12, 0.12, 0.12, 0.001); - } - }, t); - } + private void showOreMarker(Player p, Block ore, int durationTicks) { + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + if (glowingEntities == null) { + showFallbackMarker(p, ore, durationTicks); + return; } - private boolean isEligiblePickaxe(ItemStack hand) { - if (!isItem(hand)) { - return false; - } - - return switch (hand.getType()) { - case IRON_PICKAXE, DIAMOND_PICKAXE, NETHERITE_PICKAXE -> true; - default -> false; - }; + Slime slime = ore.getWorld().spawn(ore.getLocation().add(0.5, 0.5, 0.5), Slime.class, s -> { + s.setInvulnerable(true); + s.setCollidable(false); + s.setGravity(false); + s.setSilent(true); + s.setAI(false); + s.setSize(2); + s.setRotation(0, 0); + s.setMetadata(MARKER_META, new FixedMetadataValue(Adapt.instance, true)); + s.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false)); + }); + + try { + glowingEntities.setGlowing(slime, p, ChatColor.AQUA); + } catch (ReflectiveOperationException ex) { + Adapt.verbose("Failed to enable glowing marker for QuarrySense: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + slime.remove(); + showFallbackMarker(p, ore, durationTicks); + return; } - private boolean applyPickaxeCost(Player p, ItemStack hand, int durabilityCost) { - if (getConfig().costsReduceMaxDurability) { - return tryReduceMaxDurability(p, hand, durabilityCost); - } + if (areParticlesEnabled()) { + + p.spawnParticle(Particle.GLOW, ore.getLocation().add(0.5, 0.5, 0.5), 20, 0.25, 0.25, 0.25, 0.001); - return tryDamagePickaxe(p, hand, durabilityCost); } + if (areParticlesEnabled()) { + p.spawnParticle(Particle.END_ROD, ore.getLocation().add(0.5, 0.5, 0.5), 8, 0.15, 0.15, 0.15, 0.003); - private boolean tryDamagePickaxe(Player p, ItemStack hand, int durabilityCost) { - if (!(hand.getItemMeta() instanceof Damageable damageable)) { - return false; + } + J.runEntity(slime, () -> { + try { + glowingEntities.unsetGlowing(slime, p); + } catch (ReflectiveOperationException ex) { + Adapt.verbose("Failed to clear glowing marker for QuarrySense: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + slime.remove(); + }, durationTicks); + } + + private void showFallbackMarker(Player p, Block ore, int durationTicks) { + Location loc = ore.getLocation().add(0.5, 0.5, 0.5); + for (int t = 0; t <= durationTicks; t += 8) { + J.runEntity(p, () -> { + if (!p.isOnline()) { + return; } - int maxDurability = hand.getType().getMaxDurability(); - int currentDamage = damageable.getDamage(); - if (currentDamage + durabilityCost >= maxDurability) { - return false; - } + if (areParticlesEnabled()) { - damageable.setDamage(currentDamage + durabilityCost); - hand.setItemMeta(damageable); - p.getInventory().setItemInMainHand(hand); - return true; - } + p.spawnParticle(Particle.GLOW, loc, 14, 0.22, 0.22, 0.22, 0.001); - private boolean tryReduceMaxDurability(Player p, ItemStack hand, int durabilityCost) { - if (!(hand.getItemMeta() instanceof Damageable damageable)) { - return false; } - - int fallbackMax = Math.max(1, hand.getType().getMaxDurability()); - int currentMax = damageable.hasMaxDamage() ? Math.max(1, damageable.getMaxDamage()) : fallbackMax; - int currentDamage = Math.max(0, damageable.getDamage()); - int newMax = currentMax - durabilityCost; - if (newMax <= currentDamage + 1) { - return false; + if (areParticlesEnabled()) { + p.spawnParticle(Particle.END_ROD, loc, 4, 0.12, 0.12, 0.12, 0.001); } - - damageable.setMaxDamage(Math.max(1, newMax)); - hand.setItemMeta(damageable); - p.getInventory().setItemInMainHand(hand); - return true; + }, t); } + } - private int getScanRadius(int level) { - return Math.max(4, (int) Math.round(getConfig().scanRadiusBase + (getLevelPercent(level) * getConfig().scanRadiusFactor))); + private boolean isEligiblePickaxe(ItemStack hand) { + if (!isItem(hand)) { + return false; } - private int getMaxHighlights(int level) { - return Math.max(1, (int) Math.round(getConfig().maxHighlightsBase + (getLevelPercent(level) * getConfig().maxHighlightsFactor))); - } + return switch (hand.getType()) { + case IRON_PICKAXE, DIAMOND_PICKAXE, NETHERITE_PICKAXE -> true; + default -> false; + }; + } - private int getHighlightTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().highlightTicksBase + (getLevelPercent(level) * getConfig().highlightTicksFactor))); + private boolean applyPickaxeCost(Player p, ItemStack hand, int durabilityCost) { + if (getConfig().costsReduceMaxDurability) { + return tryReduceMaxDurability(p, hand, durabilityCost); } - private int getCooldownTicks(int level) { - return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); - } + return tryDamagePickaxe(p, hand, durabilityCost); + } - private int getDurabilityCost(ItemStack hand, int level) { - int maxDurability = Math.max(1, hand.getType().getMaxDurability()); - return Math.max(1, (int) Math.round(maxDurability * getDurabilityCostPercent(level))); + private boolean tryDamagePickaxe(Player p, ItemStack hand, int durabilityCost) { + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return false; } - private double getDurabilityCostPercent(int level) { - return Math.max(getConfig().minDurabilityCostPercent, - getConfig().durabilityCostPercentBase - (getLevelPercent(level) * getConfig().durabilityCostPercentFactor)); + int maxDurability = hand.getType().getMaxDurability(); + int currentDamage = damageable.getDamage(); + if (currentDamage + durabilityCost >= maxDurability) { + return false; } - @Override - public void onTick() { + damageable.setDamage(currentDamage + durabilityCost); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + return true; + } + private boolean tryReduceMaxDurability(Player p, ItemStack hand, int durabilityCost) { + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + int fallbackMax = Math.max(1, hand.getType().getMaxDurability()); + int currentMax = damageable.hasMaxDamage() ? Math.max(1, damageable.getMaxDamage()) : fallbackMax; + int currentDamage = Math.max(0, damageable.getDamage()); + int newMax = currentMax - durabilityCost; + if (newMax <= currentDamage + 1) { + return false; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sneak-right-click a block with an iron+ pickaxe to highlight nearby ores.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Costs Reduce Max Durability for the Pickaxe Quarry Sense adaptation.", impact = "True reduces max durability instead of adding normal damage.") - boolean costsReduceMaxDurability = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double scanRadiusBase = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double scanRadiusFactor = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHighlightsBase = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHighlightsFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double highlightTicksBase = 90; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double highlightTicksFactor = 90; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 40; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durabilityCostPercentBase = 0.006; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durabilityCostPercentFactor = 0.0045; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Durability Cost Percent for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minDurabilityCostPercent = 0.001; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Found Ore for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerFoundOre = 6; - } + damageable.setMaxDamage(Math.max(1, newMax)); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + return true; + } + + private int getScanRadius(int level) { + return Math.max(4, (int) Math.round(getConfig().scanRadiusBase + (getLevelPercent(level) * getConfig().scanRadiusFactor))); + } + + private int getMaxHighlights(int level) { + return Math.max(1, (int) Math.round(getConfig().maxHighlightsBase + (getLevelPercent(level) * getConfig().maxHighlightsFactor))); + } + + private int getHighlightTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().highlightTicksBase + (getLevelPercent(level) * getConfig().highlightTicksFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + private int getDurabilityCost(ItemStack hand, int level) { + int maxDurability = Math.max(1, hand.getType().getMaxDurability()); + return Math.max(1, (int) Math.round(maxDurability * getDurabilityCostPercent(level))); + } + + private double getDurabilityCostPercent(int level) { + return Math.max(getConfig().minDurabilityCostPercent, + getConfig().durabilityCostPercentBase - (getLevelPercent(level) * getConfig().durabilityCostPercentFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click a block with an iron+ pickaxe to highlight nearby ores.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Costs Reduce Max Durability for the Pickaxe Quarry Sense adaptation.", impact = "True reduces max durability instead of adding normal damage.") + boolean costsReduceMaxDurability = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRadiusBase = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Radius Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRadiusFactor = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHighlightsBase = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Highlights Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHighlightsFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double highlightTicksBase = 90; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Highlight Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double highlightTicksFactor = 90; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Base for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durabilityCostPercentBase = 0.006; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Percent Factor for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durabilityCostPercentFactor = 0.0045; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Durability Cost Percent for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minDurabilityCostPercent = 0.001; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Found Ore for the Pickaxe Quarry Sense adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerFoundOre = 6; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java index a23133087..19bb614b9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeSilkSpawner.java @@ -6,9 +6,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -23,117 +23,117 @@ import org.bukkit.inventory.meta.BlockStateMeta; public class PickaxeSilkSpawner extends SimpleAdaptation { - private final RNG rng = new RNG(); + private final RNG rng = new RNG(); - public PickaxeSilkSpawner() { - super("pickaxe-silk-spawner"); - registerConfiguration(PickaxeSilkSpawner.Config.class); - setDescription(Localizer.dLocalize("pickaxe.silk_spawner.description")); - setDisplayName(Localizer.dLocalize("pickaxe.silk_spawner.name")); - setIcon(Material.SPAWNER); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(8444); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPAWNER) - .key("challenge_pickaxe_spawner_10") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_10.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPAWNER) - .key("challenge_pickaxe_spawner_50") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_50.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pickaxe_spawner_10", "pickaxe.silk-spawner.spawners-collected", 10, 500); - registerMilestone("challenge_pickaxe_spawner_50", "pickaxe.silk-spawner.spawners-collected", 50, 2000); - } + public PickaxeSilkSpawner() { + super("pickaxe-silk-spawner"); + registerConfiguration(PickaxeSilkSpawner.Config.class); + setDescription(Localizer.dLocalize("pickaxe.silk_spawner.description")); + setDisplayName(Localizer.dLocalize("pickaxe.silk_spawner.name")); + setIcon(Material.SPAWNER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(8444); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPAWNER) + .key("challenge_pickaxe_spawner_10") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_10.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPAWNER) + .key("challenge_pickaxe_spawner_50") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_50.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_spawner_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pickaxe_spawner_10", "pickaxe.silk-spawner.spawners-collected", 10, 500); + registerMilestone("challenge_pickaxe_spawner_50", "pickaxe.silk-spawner.spawners-collected", 50, 2000); + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent event) { - var player = event.getPlayer(); - var block = event.getBlock(); - var context = resolveBlockBreakContext(player, block.getLocation()); - if (!event.isDropItems() || block.getType() != Material.SPAWNER || context == null) - return; - var level = context.level(); - if (level == 1 && !player.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { - return; - } else if (level > 1 && !player.isSneaking()) { - return; - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + org.bukkit.entity.Player player = event.getPlayer(); + org.bukkit.block.Block block = event.getBlock(); + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(player, block.getLocation()); + if (!event.isDropItems() || block.getType() != Material.SPAWNER || context == null) + return; + int level = context.level(); + if (level == 1 && !player.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { + return; + } else if (level > 1 && !player.isSneaking()) { + return; + } - event.setDropItems(false); - var spawner = new ItemStack(Material.SPAWNER); - var state = block.getState(); - if (spawner.getItemMeta() instanceof BlockStateMeta meta) { - meta.setBlockState(state); - spawner.setItemMeta(meta); - } + event.setDropItems(false); + org.bukkit.inventory.ItemStack spawner = new ItemStack(Material.SPAWNER); + org.bukkit.block.BlockState state = block.getState(); + if (spawner.getItemMeta() instanceof BlockStateMeta meta) { + meta.setBlockState(state); + spawner.setItemMeta(meta); + } - var loc = block.getLocation().add( - rng.d(-0.25D, 0.25D), - rng.d(-0.25D, 0.25D) - 0.125D, - rng.d(-0.25D, 0.25D) - ); - var item = block.getWorld().createEntity(loc, Item.class); - item.setItemStack(spawner); - item.setOwner(player.getUniqueId()); + org.bukkit.Location loc = block.getLocation().add( + rng.d(-0.25D, 0.25D), + rng.d(-0.25D, 0.25D) - 0.125D, + rng.d(-0.25D, 0.25D) + ); + org.bukkit.entity.Item item = block.getWorld().createEntity(loc, Item.class); + item.setItemStack(spawner); + item.setOwner(player.getUniqueId()); - var dropEvent = new BlockDropItemEvent(block, state, player, new KList().qadd(item)); - Bukkit.getPluginManager().callEvent(dropEvent); - if (dropEvent.isCancelled()) { - for (Item i : dropEvent.getItems()) { - if (i.isValid()) i.remove(); - } - } else { - for (Item i : dropEvent.getItems()) { - if (!i.isValid()) block.getWorld().addEntity(i); - } - getPlayer(player).getData().addStat("pickaxe.silk-spawner.spawners-collected", 1); - } + org.bukkit.event.block.BlockDropItemEvent dropEvent = new BlockDropItemEvent(block, state, player, new KList().qadd(item)); + Bukkit.getPluginManager().callEvent(dropEvent); + if (dropEvent.isCancelled()) { + for (Item i : dropEvent.getItems()) { + if (i.isValid()) i.remove(); + } + } else { + for (Item i : dropEvent.getItems()) { + if (!i.isValid()) block.getWorld().addEntity(i); + } + getPlayer(player).getData().addStat("pickaxe.silk-spawner.spawners-collected", 1); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.silk_spawner.lore" + (level < 2 ? 1 : 2))); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.silk_spawner.lore" + (level < 2 ? 1 : 2))); + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Spawners drop when broken with silk touch or while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - } + @NoArgsConstructor + @ConfigDescription("Spawners drop when broken with silk touch or while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index 17cc821a9..6478f749d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -29,10 +29,10 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,170 +44,200 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import static art.arcane.adapt.util.data.Metadata.VEIN_MINED; public class PickaxeVeinminer extends SimpleAdaptation { - public PickaxeVeinminer() { - super("pickaxe-veinminer"); - registerConfiguration(PickaxeVeinminer.Config.class); - setDescription(Localizer.dLocalize("pickaxe.vein_miner.description")); - setDisplayName(Localizer.dLocalize("pickaxe.vein_miner.name")); - setIcon(Material.IRON_PICKAXE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(8484); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_pickaxe_veinminer_2500") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_pickaxe_veinminer_20") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_20.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_20.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_pickaxe_veinminer_2500", "pickaxe.veinminer.ores-veinmined", 2500, 500); + public PickaxeVeinminer() { + super("pickaxe-veinminer"); + registerConfiguration(PickaxeVeinminer.Config.class); + setDescription(Localizer.dLocalize("pickaxe.vein_miner.description")); + setDisplayName(Localizer.dLocalize("pickaxe.vein_miner.name")); + setIcon(Material.IRON_PICKAXE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(8484); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_pickaxe_veinminer_2500") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_pickaxe_veinminer_20") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_20.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_veinminer_20.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_veinminer_2500", "pickaxe.veinminer.ores-veinmined", 2500, 500); + } + + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.vein_miner.lore1")); + v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("pickaxe.vein_miner.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("pickaxe.vein_miner.lore3")); + } + + private int getRadius(int lvl) { + return lvl + getConfig().baseRange; + } + + @EventHandler + public void on(BlockBreakEvent e) { + if (VEIN_MINED.get(e.getBlock())) { + return; } - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.vein_miner.lore1")); - v.addLore(C.GREEN + "" + (level + getConfig().baseRange) + C.GRAY + " " + Localizer.dLocalize("pickaxe.vein_miner.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("pickaxe.vein_miner.lore3")); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + return; } - private int getRadius(int lvl) { - return lvl + getConfig().baseRange; + if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE")) { + if (!e.getBlock().getType().equals(Material.OBSIDIAN)) { + return; + } } - - @EventHandler - public void on(BlockBreakEvent e) { - if (VEIN_MINED.get(e.getBlock())) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0) { - return; - } - - if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE")) { - if (!e.getBlock().getType().equals(Material.OBSIDIAN)) { - return; - } - } - VEIN_MINED.add(e.getBlock()); - - Block block = e.getBlock(); - Map blockMap = new java.util.concurrent.ConcurrentHashMap<>(); - blockMap.put(block.getLocation(), block); - - int radius = getRadius(level); - for (int i = 0; i < radius; i++) { - for (int x = -i; x <= i; x++) { - for (int y = -i; y <= i; y++) { - for (int z = -i; z <= i; z++) { - Block b = block.getRelative(x, y, z); - if (b.getType() == block.getType()) { - if (!canBlockBreak(p, e.getBlock().getLocation())) { - continue; - } - blockMap.put(b.getLocation(), b); - } - } - } + VEIN_MINED.add(e.getBlock()); + + Block block = e.getBlock(); + Material targetType = block.getType(); + Location origin = block.getLocation(); + Map blockMap = new HashMap<>(); + Set queued = new HashSet<>(); + Deque queue = new ArrayDeque<>(); + queue.add(block); + queued.add(origin); + int radius = getRadius(level); + int radiusSquared = radius * radius; + int maxBlocks = Math.max(1, ((radius * 2) + 1) * ((radius * 2) + 1) * ((radius * 2) + 1)); + while (!queue.isEmpty() && blockMap.size() < maxBlocks) { + Block current = queue.poll(); + if (current == null) { + continue; + } + + Location currentLocation = current.getLocation(); + if (current.getType() != targetType || blockMap.containsKey(currentLocation)) { + continue; + } + + if (currentLocation.distanceSquared(origin) > radiusSquared || !canBlockBreak(p, currentLocation)) { + continue; + } + + blockMap.put(currentLocation, current); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && y == 0 && z == 0) { + continue; } - } - - int veinSize = blockMap.size(); - getPlayer(p).getData().addStat("pickaxe.veinminer.ores-veinmined", veinSize); - if (veinSize >= 20 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_pickaxe_veinminer_20")) { - getPlayer(p).getAdvancementHandler().grant("challenge_pickaxe_veinminer_20"); - } - J.runEntity(p, () -> { - for (Location l : blockMap.keySet()) { - if (!canBlockBreak(p, l)) { - Adapt.verbose("Player " + p.getName() + " doesn't have permission."); - continue; - } - Block b = block.getWorld().getBlockAt(l); - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("pickaxe"); - PlayerAdaptation autoSmelt = line != null ? line.getAdaptation("pickaxe-autosmelt") : null; - PlayerAdaptation drop2Inv = line != null ? line.getAdaptation("pickaxe-drop-to-inventory") : null; - VEIN_MINED.add(b); - if (autoSmelt != null && autoSmelt.getLevel() > 0 && ItemListings.getSmeltOre().contains(b.getType())) { - if (drop2Inv != null && drop2Inv.getLevel() > 0) { - PickaxeAutosmelt.autosmeltBlockDTI(b, p); - } else { - PickaxeAutosmelt.autosmeltBlock(b, p); - } - } else { - if (drop2Inv != null && drop2Inv.getLevel() > 0) { - b.getDrops(p.getInventory().getItemInMainHand(), p).forEach(item -> { - HashMap extra = p.getInventory().addItem(item); - extra.forEach((k, v) -> p.getWorld().dropItem(p.getLocation(), v)); - }); - b.setType(Material.AIR); - } else { - b.breakNaturally(p.getItemInUse()); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(block.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.4f, 0.25f); - if (areParticlesEnabled()) { - block.getWorld().spawnParticle(Particle.ASH, b.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); - } - } - } - VEIN_MINED.remove(b); + Block next = current.getRelative(x, y, z); + if (next.getType() != targetType) { + continue; } - VEIN_MINED.remove(block); - }); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + Location nextLocation = next.getLocation(); + if (nextLocation.distanceSquared(origin) > radiusSquared) { + continue; + } - @Override - public void onTick() { + if (queued.add(nextLocation)) { + queue.add(next); + } + } + } + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; + int veinSize = blockMap.size(); + getPlayer(p).getData().addStat("pickaxe.veinminer.ores-veinmined", veinSize); + if (veinSize >= 20 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_pickaxe_veinminer_20")) { + getPlayer(p).getAdvancementHandler().grant("challenge_pickaxe_veinminer_20"); } - @NoArgsConstructor - @ConfigDescription("Break connected ore veins at once while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Veinminer adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Pickaxe Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int baseRange = 2; - } + J.runEntity(p, () -> { + for (Location l : blockMap.keySet()) { + if (!canBlockBreak(p, l)) { + Adapt.verbose("Player " + p.getName() + " doesn't have permission."); + continue; + } + Block b = block.getWorld().getBlockAt(l); + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("pickaxe"); + PlayerAdaptation autoSmelt = line != null ? line.getAdaptation("pickaxe-autosmelt") : null; + PlayerAdaptation drop2Inv = line != null ? line.getAdaptation("pickaxe-drop-to-inventory") : null; + VEIN_MINED.add(b); + if (autoSmelt != null && autoSmelt.getLevel() > 0 && ItemListings.getSmeltOre().contains(b.getType())) { + if (drop2Inv != null && drop2Inv.getLevel() > 0) { + PickaxeAutosmelt.autosmeltBlockDTI(b, p); + } else { + PickaxeAutosmelt.autosmeltBlock(b, p); + } + } else { + if (drop2Inv != null && drop2Inv.getLevel() > 0) { + b.getDrops(p.getInventory().getItemInMainHand(), p).forEach(item -> { + HashMap extra = p.getInventory().addItem(item); + extra.forEach((k, v) -> p.getWorld().dropItem(p.getLocation(), v)); + }); + b.setType(Material.AIR); + } else { + b.breakNaturally(p.getItemInUse()); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 0.4f, 0.25f); + if (areParticlesEnabled()) { + block.getWorld().spawnParticle(Particle.ASH, b.getLocation().add(0.5, 0.5, 0.5), 25, 0.5, 0.5, 0.5, 0.1); + } + } + } + VEIN_MINED.remove(b); + } + VEIN_MINED.remove(block); + }); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Break connected ore veins at once while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Pickaxe Veinminer adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Range for the Pickaxe Veinminer adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int baseRange = 2; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java index 602f71390..0c4154fb0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedArrowRecovery.java @@ -7,9 +7,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Enchantments; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -27,109 +27,109 @@ import static xyz.xenondevs.particle.utils.MathUtils.RANDOM; public class RangedArrowRecovery extends SimpleAdaptation { - private final Map shotArrows; + private final Map shotArrows; - public RangedArrowRecovery() { - super("ranged-recovery"); - registerConfiguration(RangedArrowRecovery.Config.class); - setDescription(Localizer.dLocalize("ranged.arrow_recovery.description")); - setDisplayName(Localizer.dLocalize("ranged.arrow_recovery.name")); - setIcon(Material.ARROW); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - shotArrows = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW) - .key("challenge_ranged_arrow_500") - .title(Localizer.dLocalize("advancement.challenge_ranged_arrow_500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_arrow_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_arrow_10k") - .title(Localizer.dLocalize("advancement.challenge_ranged_arrow_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_arrow_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_arrow_500", "ranged.arrow-recovery.arrows-recovered", 500, 300); - registerMilestone("challenge_ranged_arrow_10k", "ranged.arrow-recovery.arrows-recovered", 10000, 1000); - } + public RangedArrowRecovery() { + super("ranged-recovery"); + registerConfiguration(RangedArrowRecovery.Config.class); + setDescription(Localizer.dLocalize("ranged.arrow_recovery.description")); + setDisplayName(Localizer.dLocalize("ranged.arrow_recovery.name")); + setIcon(Material.ARROW); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + shotArrows = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW) + .key("challenge_ranged_arrow_500") + .title(Localizer.dLocalize("advancement.challenge_ranged_arrow_500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_arrow_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_arrow_10k") + .title(Localizer.dLocalize("advancement.challenge_ranged_arrow_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_arrow_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_arrow_500", "ranged.arrow-recovery.arrows-recovered", 500, 300); + registerMilestone("challenge_ranged_arrow_10k", "ranged.arrow-recovery.arrows-recovered", 10000, 1000); + } - @EventHandler - public void onEntityShootBow(EntityShootBowEvent event) { - if (event.getEntity() instanceof Player player && hasActiveAdaptation(player)) { - if (!event.getBow().containsEnchantment(Enchantments.ARROW_INFINITE)) { - if (event.getProjectile() instanceof Arrow arrow) { - shotArrows.put(arrow.getUniqueId(), player.getUniqueId()); - } - } + @EventHandler + public void onEntityShootBow(EntityShootBowEvent event) { + if (event.getEntity() instanceof Player player && hasActiveAdaptation(player)) { + if (!event.getBow().containsEnchantment(Enchantments.ARROW_INFINITE)) { + if (event.getProjectile() instanceof Arrow arrow) { + shotArrows.put(arrow.getUniqueId(), player.getUniqueId()); } + } } + } - @EventHandler - public void onProjectileHit(ProjectileHitEvent event) { - if (event.getEntity() instanceof Arrow arrow) { - UUID shooterId = shotArrows.get(arrow.getUniqueId()); - Player shooter = shooterId == null ? null : Bukkit.getPlayer(shooterId); - int level = shooter == null ? 0 : getActiveLevel(shooter); - if (level > 0) { - double chance = getConfig().hitChance[level - 1] / 100.0; - if (RANDOM.nextDouble() < chance) { - ItemStack arrowStack = new ItemStack(Material.ARROW, 1); - shooter.getInventory().addItem(arrowStack); - getPlayer(shooter).getData().addStat("ranged.arrow-recovery.arrows-recovered", 1); - Adapt.info("Arrow added to inventory."); - } - } - shotArrows.remove(arrow.getUniqueId()); + @EventHandler + public void onProjectileHit(ProjectileHitEvent event) { + if (event.getEntity() instanceof Arrow arrow) { + UUID shooterId = shotArrows.get(arrow.getUniqueId()); + Player shooter = shooterId == null ? null : Bukkit.getPlayer(shooterId); + int level = shooter == null ? 0 : getActiveLevel(shooter); + if (level > 0) { + double chance = getConfig().hitChance[level - 1] / 100.0; + if (RANDOM.nextDouble() < chance) { + ItemStack arrowStack = new ItemStack(Material.ARROW, 1); + shooter.getInventory().addItem(arrowStack); + getPlayer(shooter).getData().addStat("ranged.arrow-recovery.arrows-recovered", 1); + Adapt.info("Arrow added to inventory."); } + } + shotArrows.remove(arrow.getUniqueId()); } + } - private double chancePerLevel(int level) { - return (getConfig().hitChance[level - 1] / 100.0); - } + private double chancePerLevel(int level) { + return (getConfig().hitChance[level - 1] / 100.0); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public void onTick() { - } + @Override + public void onTick() { + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("ranged.arrow_recovery.lore1")); - v.addLore(C.GREEN + Localizer.dLocalize("ranged.arrow_recovery.lore2") + chancePerLevel(level)); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("ranged.arrow_recovery.lore1")); + v.addLore(C.GREEN + Localizer.dLocalize("ranged.arrow_recovery.lore2") + chancePerLevel(level)); + } - @NoArgsConstructor - @ConfigDescription("Chance to recover arrows after hitting or killing an enemy.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.78; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Chance for the Ranged Arrow Recovery adaptation.", impact = "Add or remove entries to control which values are included.") - double[] hitChance = {10, 20, 30, 40, 50, 60, 70, 80}; - } + @NoArgsConstructor + @ConfigDescription("Chance to recover arrows after hitting or killing an enemy.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Chance for the Ranged Arrow Recovery adaptation.", impact = "Add or remove entries to control which values are included.") + double[] hitChance = {10, 20, 30, 40, 50, 60, 70, 80}; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java index a85a9ac88..6372ffcdb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedFloaters.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -43,124 +43,124 @@ import java.util.concurrent.ThreadLocalRandom; public class RangedFloaters extends SimpleAdaptation { - public RangedFloaters() { - super("ranged-floaters"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.floaters.description")); - setDisplayName(Localizer.dLocalize("ranged.floaters.name")); - setIcon(Material.SHULKER_SHELL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHULKER_SHELL) - .key("challenge_ranged_floaters_200") - .title(Localizer.dLocalize("advancement.challenge_ranged_floaters_200.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_floaters_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_floaters_200", "ranged.floaters.targets-levitated", 200, 300); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore2")); - v.addLore(C.GREEN + "+ " + (1 + getAmplifier(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore3")); - } - - @EventHandler(priority = EventPriority.HIGH) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveProjectileContext(e); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - LivingEntity target = combat.target(); - int level = combat.level(); - if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { - return; - } - - target.addPotionEffect(new PotionEffect( - PotionEffectType.LEVITATION, - getDurationTicks(level), - getAmplifier(level), - true, - true, - true - ), true); - getPlayer(p).getData().addStat("ranged.floaters.targets-levitated", 1); - - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.END_ROD, target.getLocation().add(0, 1, 0), 10, 0.2, 0.5, 0.2, 0.02); - } - - SoundPlayer.of(target.getWorld()).play(target.getLocation(), Sound.ENTITY_SHULKER_SHOOT, 0.6f, 1.45f); - xp(p, getConfig().skillXpOnProc); - } - - private double getProcChance(int level) { - return Math.min(getConfig().maxChance, getConfig().chanceBase + (getLevelPercent(level) * getConfig().chanceFactor)); + public RangedFloaters() { + super("ranged-floaters"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.floaters.description")); + setDisplayName(Localizer.dLocalize("ranged.floaters.name")); + setIcon(Material.SHULKER_SHELL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHULKER_SHELL) + .key("challenge_ranged_floaters_200") + .title(Localizer.dLocalize("advancement.challenge_ranged_floaters_200.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_floaters_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_floaters_200", "ranged.floaters.targets-levitated", 200, 300); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore2")); + v.addLore(C.GREEN + "+ " + (1 + getAmplifier(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.floaters.lore3")); + } + + @EventHandler(priority = EventPriority.HIGH) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.ProjectileContext combat = resolveProjectileContext(e); + if (combat == null) { + return; } - private int getDurationTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().durationTicksBase + (getLevelPercent(level) * getConfig().durationTicksFactor))); + Player p = combat.attacker(); + LivingEntity target = combat.target(); + int level = combat.level(); + if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { + return; } - private int getAmplifier(int level) { - return Math.max(0, (int) Math.floor(getLevelPercent(level) * getConfig().maxAmplifier)); + target.addPotionEffect(new PotionEffect( + PotionEffectType.LEVITATION, + getDurationTicks(level), + getAmplifier(level), + true, + true, + true + ), true); + getPlayer(p).getData().addStat("ranged.floaters.targets-levitated", 1); + + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.END_ROD, target.getLocation().add(0, 1, 0), 10, 0.2, 0.5, 0.2, 0.02); } - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Projectiles have a chance to apply levitation to targets.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Floaters adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.78; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double chanceBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double chanceFactor = 0.58; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Chance for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxChance = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationTicksBase = 26.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationTicksFactor = 110.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxAmplifier = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Proc for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double skillXpOnProc = 8.0; - } + SoundPlayer.of(target.getWorld()).play(target.getLocation(), Sound.ENTITY_SHULKER_SHOOT, 0.6f, 1.45f); + xp(p, getConfig().skillXpOnProc); + } + + private double getProcChance(int level) { + return Math.min(getConfig().maxChance, getConfig().chanceBase + (getLevelPercent(level) * getConfig().chanceFactor)); + } + + private int getDurationTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().durationTicksBase + (getLevelPercent(level) * getConfig().durationTicksFactor))); + } + + private int getAmplifier(int level) { + return Math.max(0, (int) Math.floor(getLevelPercent(level) * getConfig().maxAmplifier)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Projectiles have a chance to apply levitation to targets.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Floaters adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double chanceBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Chance Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double chanceFactor = 0.58; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Chance for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxChance = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationTicksBase = 26.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationTicksFactor = 110.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxAmplifier = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Skill Xp On Proc for the Ranged Floaters adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double skillXpOnProc = 8.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java index 2ba27a324..6f6324d5f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedForce.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -40,116 +40,116 @@ public class RangedForce extends SimpleAdaptation { - public RangedForce() { - super("ranged-force"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.force_shot.description")); - setDisplayName(Localizer.dLocalize("ranged.force_shot.name")); - setIcon(Material.TIPPED_ARROW); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(4900); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_force_30") - .title(Localizer.dLocalize("ranged.force_shot.advancementname")) - .description(Localizer.dLocalize("ranged.force_shot.advancementlore")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_force_500") - .title(Localizer.dLocalize("advancement.challenge_ranged_force_500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_force_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_force_500", "ranged.force.long-range-hits", 500, 500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.force_shot.lore1")); - } - - private double getSpeed(double factor) { - return (factor * getConfig().speedFactor); - } - - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var combat = resolveProjectileContext(e); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - Location a = e.getEntity().getLocation().clone(); - Location b = p.getLocation().clone(); - a.setY(0); - b.setY(0); - xp(p, 5); - double distSq = a.distanceSquared(b); - - if (distSq > 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_force_30")) { - getPlayer(p).getAdvancementHandler().grant("challenge_force_30"); - xp(p, getConfig().challengeRewardLongShotReward, "challenge-long-shot"); - } - - if (distSq > 900) { - getPlayer(p).getData().addStat("ranged.force.long-range-hits", 1); - } - } - - @EventHandler - public void on(ProjectileLaunchEvent e) { - if (e.getEntity().getShooter() instanceof Player p) { - int level = getActiveLevel(p); - if (level > 0) { - double factor = getLevelPercent(level); - e.getEntity().setVelocity(e.getEntity().getVelocity().clone().multiply(1 + getSpeed(factor))); - SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_SNOWBALL_THROW, 0.5f + ((float) factor * 0.25f), 0.7f + (float) (factor / 2f)); - } - } + public RangedForce() { + super("ranged-force"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.force_shot.description")); + setDisplayName(Localizer.dLocalize("ranged.force_shot.name")); + setIcon(Material.TIPPED_ARROW); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(4900); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_force_30") + .title(Localizer.dLocalize("ranged.force_shot.advancementname")) + .description(Localizer.dLocalize("ranged.force_shot.advancementlore")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_force_500") + .title(Localizer.dLocalize("advancement.challenge_ranged_force_500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_force_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_force_500", "ranged.force.long-range-hits", 500, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.force_shot.lore1")); + } + + private double getSpeed(double factor) { + return (factor * getConfig().speedFactor); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.ProjectileContext combat = resolveProjectileContext(e); + if (combat == null) { + return; } - @Override - public void onTick() { - + Player p = combat.attacker(); + Location a = e.getEntity().getLocation().clone(); + Location b = p.getLocation().clone(); + a.setY(0); + b.setY(0); + xp(p, 5); + double distSq = a.distanceSquared(b); + + if (distSq > 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_force_30")) { + getPlayer(p).getAdvancementHandler().grant("challenge_force_30"); + xp(p, getConfig().challengeRewardLongShotReward, "challenge-long-shot"); } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (distSq > 900) { + getPlayer(p).getData().addStat("ranged.force.long-range-hits", 1); } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Shoot projectiles further and faster.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Reward Long Shot Reward for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRewardLongShotReward = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Factor for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedFactor = 1.135; + } + + @EventHandler + public void on(ProjectileLaunchEvent e) { + if (e.getEntity().getShooter() instanceof Player p) { + int level = getActiveLevel(p); + if (level > 0) { + double factor = getLevelPercent(level); + e.getEntity().setVelocity(e.getEntity().getVelocity().clone().multiply(1 + getSpeed(factor))); + SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_SNOWBALL_THROW, 0.5f + ((float) factor * 0.25f), 0.7f + (float) (factor / 2f)); + } } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Shoot projectiles further and faster.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Reward Long Shot Reward for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRewardLongShotReward = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Factor for the Ranged Force adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedFactor = 1.135; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java index c25b57c2a..b3362699e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedLungeShot.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -39,105 +39,105 @@ import org.bukkit.util.Vector; public class RangedLungeShot extends SimpleAdaptation { - public RangedLungeShot() { - super("ranged-lunge-shot"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.lunge_shot.description")); - setDisplayName(Localizer.dLocalize("ranged.lunge_shot.name")); - setIcon(Material.RABBIT_HIDE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(4859); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW) - .key("challenge_ranged_lunge_200") - .title(Localizer.dLocalize("advancement.challenge_ranged_lunge_200.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_lunge_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FEATHER) - .key("challenge_ranged_lunge_2500") - .title(Localizer.dLocalize("advancement.challenge_ranged_lunge_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_lunge_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_lunge_200", "ranged.lunge-shot.lunges", 200, 300); - registerMilestone("challenge_ranged_lunge_2500", "ranged.lunge-shot.lunges", 2500, 1000); - } + public RangedLungeShot() { + super("ranged-lunge-shot"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.lunge_shot.description")); + setDisplayName(Localizer.dLocalize("ranged.lunge_shot.name")); + setIcon(Material.RABBIT_HIDE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(4859); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW) + .key("challenge_ranged_lunge_200") + .title(Localizer.dLocalize("advancement.challenge_ranged_lunge_200.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_lunge_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FEATHER) + .key("challenge_ranged_lunge_2500") + .title(Localizer.dLocalize("advancement.challenge_ranged_lunge_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_lunge_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_lunge_200", "ranged.lunge-shot.lunges", 200, 300); + registerMilestone("challenge_ranged_lunge_2500", "ranged.lunge-shot.lunges", 2500, 1000); + } - private double getSpeed(double factor) { - return (factor * getConfig().factor); - } + private double getSpeed(double factor) { + return (factor * getConfig().factor); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.lunge_shot.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.lunge_shot.lore1")); + } - @EventHandler - public void on(ProjectileLaunchEvent e) { - if (e.getEntity().getShooter() instanceof Player p) { - if (e.getEntity() instanceof AbstractArrow a) { - if (hasActiveAdaptation(p)) { - if (!p.isOnGround()) { - Vector velocity = p.getPlayer().getLocation().getDirection().normalize().multiply(getSpeed(getLevelPercent(p))); - p.setVelocity(p.getVelocity().subtract(velocity)); - getPlayer(p).getData().addStat("ranged.lunge-shot.lunges", 1); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_TURTLE, 1f, 0.75f); - spw.play(p.getLocation(), Sound.ITEM_CROSSBOW_SHOOT, 1f, 1.95f); + @EventHandler + public void on(ProjectileLaunchEvent e) { + if (e.getEntity().getShooter() instanceof Player p) { + if (e.getEntity() instanceof AbstractArrow a) { + if (hasActiveAdaptation(p)) { + if (!p.isOnGround()) { + Vector velocity = p.getPlayer().getLocation().getDirection().normalize().multiply(getSpeed(getLevelPercent(p))); + p.setVelocity(p.getVelocity().subtract(velocity)); + getPlayer(p).getData().addStat("ranged.lunge-shot.lunges", 1); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_TURTLE, 1f, 0.75f); + spw.play(p.getLocation(), Sound.ITEM_CROSSBOW_SHOOT, 1f, 1.95f); - for (int i = 0; i < 9; i++) { - Vector v = velocity.clone().add(Vector.getRandom().subtract(Vector.getRandom()).multiply(0.3)).normalize(); - if (areParticlesEnabled()) { + for (int i = 0; i < 9; i++) { + Vector v = velocity.clone().add(Vector.getRandom().subtract(Vector.getRandom()).multiply(0.3)).normalize(); + if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().clone().add(0, 1, 0), 0, v.getX(), v.getY(), v.getZ(), 0.2); - } - } - } - } + p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().clone().add(0, 1, 0), 0, v.getX(), v.getY(), v.getZ(), 0.2); + } } + } } + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("While falling, firing arrows launches you in a random direction.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Lunge Shot adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Factor for the Ranged Lunge Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double factor = 0.935; - } + @NoArgsConstructor + @ConfigDescription("While falling, firing arrows launches you in a random direction.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Lunge Shot adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Factor for the Ranged Lunge Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double factor = 0.935; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java index 64a61242d..95dc10f49 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPiercing.java @@ -25,8 +25,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.AbstractArrow; @@ -39,107 +39,107 @@ import java.util.UUID; public class RangedPiercing extends SimpleAdaptation { - private final Map arrowHitCounts = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map arrowHitCounts = new java.util.concurrent.ConcurrentHashMap<>(); - public RangedPiercing() { - super("ranged-piercing"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.arrow_piercing.description")); - setDisplayName(Localizer.dLocalize("ranged.arrow_piercing.name")); - setIcon(Material.FLETCHING_TABLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(4791); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_piercing_500") - .title(Localizer.dLocalize("advancement.challenge_ranged_piercing_500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_piercing_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_piercing_4") - .title(Localizer.dLocalize("advancement.challenge_ranged_piercing_4.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_piercing_4.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_piercing_500", "ranged.piercing.extra-hits", 500, 400); - } + public RangedPiercing() { + super("ranged-piercing"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.arrow_piercing.description")); + setDisplayName(Localizer.dLocalize("ranged.arrow_piercing.name")); + setIcon(Material.FLETCHING_TABLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(4791); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_piercing_500") + .title(Localizer.dLocalize("advancement.challenge_ranged_piercing_500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_piercing_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_piercing_4") + .title(Localizer.dLocalize("advancement.challenge_ranged_piercing_4.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_piercing_4.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_piercing_500", "ranged.piercing.extra-hits", 500, 400); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + level + C.GRAY + " " + Localizer.dLocalize("ranged.arrow_piercing.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + level + C.GRAY + " " + Localizer.dLocalize("ranged.arrow_piercing.lore1")); + } - @EventHandler - public void on(ProjectileLaunchEvent e) { - if (e.getEntity().getShooter() instanceof Player p) { - if (e.getEntity() instanceof AbstractArrow a) { - xp(p, 5); - int level = getActiveLevel(p); - if (level > 0) { - a.setPierceLevel(((AbstractArrow) e.getEntity()).getPierceLevel() + level); - } - } + @EventHandler + public void on(ProjectileLaunchEvent e) { + if (e.getEntity().getShooter() instanceof Player p) { + if (e.getEntity() instanceof AbstractArrow a) { + xp(p, 5); + int level = getActiveLevel(p); + if (level > 0) { + a.setPierceLevel(((AbstractArrow) e.getEntity()).getPierceLevel() + level); } + } } + } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var combat = resolveProjectileContext(e, projectile -> projectile instanceof AbstractArrow); - if (combat == null) { - return; - } + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.ProjectileContext combat = resolveProjectileContext(e, projectile -> projectile instanceof AbstractArrow); + if (combat == null) { + return; + } - AbstractArrow arrow = (AbstractArrow) combat.projectile(); - if (arrow.getPierceLevel() > 0) { - UUID arrowId = arrow.getUniqueId(); - int hits = arrowHitCounts.getOrDefault(arrowId, 0) + 1; - arrowHitCounts.put(arrowId, hits); - Player p = combat.attacker(); - if (hits > 1) { - getPlayer(p).getData().addStat("ranged.piercing.extra-hits", 1); - } - if (hits >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_ranged_piercing_4")) { - getPlayer(p).getAdvancementHandler().grant("challenge_ranged_piercing_4"); - } - } + AbstractArrow arrow = (AbstractArrow) combat.projectile(); + if (arrow.getPierceLevel() > 0) { + UUID arrowId = arrow.getUniqueId(); + int hits = arrowHitCounts.getOrDefault(arrowId, 0) + 1; + arrowHitCounts.put(arrowId, hits); + Player p = combat.attacker(); + if (hits > 1) { + getPlayer(p).getData().addStat("ranged.piercing.extra-hits", 1); + } + if (hits >= 4 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_ranged_piercing_4")) { + getPlayer(p).getAdvancementHandler().grant("challenge_ranged_piercing_4"); + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Projectiles pierce through multiple targets.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.5; - } + @NoArgsConstructor + @ConfigDescription("Projectiles pierce through multiple targets.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java index 05e93e808..892b34fa7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedPinningShot.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -47,169 +47,169 @@ import java.util.concurrent.ThreadLocalRandom; public class RangedPinningShot extends SimpleAdaptation { - private final Map targetProcTimes = new java.util.concurrent.ConcurrentHashMap<>(); - - public RangedPinningShot() { - super("ranged-pinning-shot"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.pinning_shot.description")); - setDisplayName(Localizer.dLocalize("ranged.pinning_shot.name")); - setIcon(Material.TRIPWIRE_HOOK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW) - .key("challenge_ranged_pinning_300") - .title(Localizer.dLocalize("advancement.challenge_ranged_pinning_300.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_pinning_300.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_pinning_300", "ranged.pinning-shot.targets-pinned", 300, 400); + private final Map targetProcTimes = new java.util.concurrent.ConcurrentHashMap<>(); + + public RangedPinningShot() { + super("ranged-pinning-shot"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.pinning_shot.description")); + setDisplayName(Localizer.dLocalize("ranged.pinning_shot.name")); + setIcon(Material.TRIPWIRE_HOOK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW) + .key("challenge_ranged_pinning_300") + .title(Localizer.dLocalize("advancement.challenge_ranged_pinning_300.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_pinning_300.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_pinning_300", "ranged.pinning-shot.targets-pinned", 300, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getReapplyCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getReapplyCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("ranged.pinning_shot.lore3")); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (!canDamageTarget(p, target)) { - return; - } - - long now = System.currentTimeMillis(); - cleanupExpired(now); - long reapply = getReapplyCooldownMillis(level); - Long last = targetProcTimes.get(target.getUniqueId()); - if (last != null && last + reapply > now) { - return; - } - - if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { - return; - } - - targetProcTimes.put(target.getUniqueId(), now); - target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getDurationTicks(level), getAmplifier(level), true, true, true), true); - getPlayer(p).getData().addStat("ranged.pinning-shot.targets-pinned", 1); - - if (getConfig().dampenVelocityOnProc) { - Vector v = target.getVelocity(); - target.setVelocity(new Vector(v.getX() * getConfig().horizontalVelocityFactor, v.getY(), v.getZ() * getConfig().horizontalVelocityFactor)); - } - - if (areParticlesEnabled()) { - - target.getWorld().spawnParticle(Particle.CRIT, target.getLocation().add(0, 0.9, 0), 18, 0.3, 0.45, 0.3, 0.08); - - } - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.ENCHANT, target.getLocation().add(0, 1.0, 0), 28, 0.35, 0.5, 0.35, 0.35); - - } - SoundPlayer sp = SoundPlayer.of(target.getWorld()); - sp.play(target.getLocation(), Sound.BLOCK_BELL_USE, 1.1f, 0.48f); - sp.play(target.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.55f); - xp(p, getConfig().xpOnProc); + if (!canDamageTarget(p, target)) { + return; } - private void cleanupExpired(long now) { - if (targetProcTimes.size() < getConfig().cleanupThreshold) { - return; - } - - targetProcTimes.entrySet().removeIf(entry -> entry.getValue() + getConfig().entryTtlMillis < now); + long now = System.currentTimeMillis(); + cleanupExpired(now); + long reapply = getReapplyCooldownMillis(level); + Long last = targetProcTimes.get(target.getUniqueId()); + if (last != null && last + reapply > now) { + return; } - private double getProcChance(int level) { - return Math.min(getConfig().maxProcChance, getConfig().procChanceBase + (getLevelPercent(level) * getConfig().procChanceFactor)); + if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { + return; } - private int getDurationTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().durationTicksBase + (getLevelPercent(level) * getConfig().durationTicksFactor))); - } + targetProcTimes.put(target.getUniqueId(), now); + target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getDurationTicks(level), getAmplifier(level), true, true, true), true); + getPlayer(p).getData().addStat("ranged.pinning-shot.targets-pinned", 1); - private int getAmplifier(int level) { - return Math.max(0, (int) Math.floor(getConfig().amplifierBase + (getLevelPercent(level) * getConfig().amplifierFactor))); + if (getConfig().dampenVelocityOnProc) { + Vector v = target.getVelocity(); + target.setVelocity(new Vector(v.getX() * getConfig().horizontalVelocityFactor, v.getY(), v.getZ() * getConfig().horizontalVelocityFactor)); } - private long getReapplyCooldownMillis(int level) { - return Math.max(1000, (long) Math.round(getConfig().reapplyCooldownMillisBase - (getLevelPercent(level) * getConfig().reapplyCooldownMillisFactor))); - } + if (areParticlesEnabled()) { - @Override - public void onTick() { + target.getWorld().spawnParticle(Particle.CRIT, target.getLocation().add(0, 0.9, 0), 18, 0.3, 0.45, 0.3, 0.08); } + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.ENCHANT, target.getLocation().add(0, 1.0, 0), 28, 0.35, 0.5, 0.35, 0.35); - @Override - public boolean isEnabled() { - return getConfig().enabled; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + SoundPlayer sp = SoundPlayer.of(target.getWorld()); + sp.play(target.getLocation(), Sound.BLOCK_BELL_USE, 1.1f, 0.48f); + sp.play(target.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.55f); + xp(p, getConfig().xpOnProc); + } + + private void cleanupExpired(long now) { + if (targetProcTimes.size() < getConfig().cleanupThreshold) { + return; } - @NoArgsConstructor - @ConfigDescription("Projectiles can pin targets with heavy slowness.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dampen Velocity On Proc for the Ranged Pinning Shot adaptation.", impact = "True enables this behavior and false disables it.") - boolean dampenVelocityOnProc = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.74; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procChanceBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procChanceFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxProcChance = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationTicksBase = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durationTicksFactor = 90; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amplifierBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double amplifierFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reapplyCooldownMillisBase = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double reapplyCooldownMillisFactor = 2800; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horizontal Velocity Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double horizontalVelocityFactor = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cleanup Threshold for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int cleanupThreshold = 128; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entry Ttl Millis for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long entryTtlMillis = 60000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Proc for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnProc = 12; - } + targetProcTimes.entrySet().removeIf(entry -> entry.getValue() + getConfig().entryTtlMillis < now); + } + + private double getProcChance(int level) { + return Math.min(getConfig().maxProcChance, getConfig().procChanceBase + (getLevelPercent(level) * getConfig().procChanceFactor)); + } + + private int getDurationTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().durationTicksBase + (getLevelPercent(level) * getConfig().durationTicksFactor))); + } + + private int getAmplifier(int level) { + return Math.max(0, (int) Math.floor(getConfig().amplifierBase + (getLevelPercent(level) * getConfig().amplifierFactor))); + } + + private long getReapplyCooldownMillis(int level) { + return Math.max(1000, (long) Math.round(getConfig().reapplyCooldownMillisBase - (getLevelPercent(level) * getConfig().reapplyCooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Projectiles can pin targets with heavy slowness.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dampen Velocity On Proc for the Ranged Pinning Shot adaptation.", impact = "True enables this behavior and false disables it.") + boolean dampenVelocityOnProc = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.74; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procChanceBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procChanceFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxProcChance = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationTicksBase = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration Ticks Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durationTicksFactor = 90; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amplifierBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplifier Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double amplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Base for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reapplyCooldownMillisBase = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reapply Cooldown Millis Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reapplyCooldownMillisFactor = 2800; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horizontal Velocity Factor for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double horizontalVelocityFactor = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cleanup Threshold for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int cleanupThreshold = 128; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Entry Ttl Millis for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long entryTtlMillis = 60000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Proc for the Ranged Pinning Shot adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnProc = 12; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java index 9e85a072a..6f0836630 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedRicochetBolt.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -46,391 +46,391 @@ import org.bukkit.util.Vector; public class RangedRicochetBolt extends SimpleAdaptation { - private static final String RICOCHET_COUNT_META = "adapt-ricochet-count"; - private static final String RICOCHET_MAX_META = "adapt-ricochet-max"; - private static final String BONUS_DAMAGE_META = "adapt-ricochet-bonus-damage"; - - public RangedRicochetBolt() { - super("ranged-ricochet-bolt"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.ricochet_bolt.description")); - setDisplayName(Localizer.dLocalize("ranged.ricochet_bolt.name")); - setIcon(Material.SPECTRAL_ARROW); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_ricochet_kills_50") - .title(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_50.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_ricochet_kills_500") - .title(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_ricochet_kills_50", "ranged.ricochet-bolt.ricochet-kills", 50, 500); - registerMilestone("challenge_ranged_ricochet_kills_500", "ranged.ricochet-bolt.ricochet-kills", 500, 2000); + private static final String RICOCHET_COUNT_META = "adapt-ricochet-count"; + private static final String RICOCHET_MAX_META = "adapt-ricochet-max"; + private static final String BONUS_DAMAGE_META = "adapt-ricochet-bonus-damage"; + + public RangedRicochetBolt() { + super("ranged-ricochet-bolt"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.ricochet_bolt.description")); + setDisplayName(Localizer.dLocalize("ranged.ricochet_bolt.name")); + setIcon(Material.SPECTRAL_ARROW); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_ricochet_kills_50") + .title(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_50.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_ricochet_kills_500") + .title(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_ricochet_kills_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_ricochet_kills_50", "ranged.ricochet-bolt.ricochet-kills", 50, 500); + registerMilestone("challenge_ranged_ricochet_kills_500", "ranged.ricochet-bolt.ricochet-kills", 500, 2000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxRicochets(level) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getSpeedBonusPerRicochet(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore2")); + v.addLore(C.GREEN + "+ " + Form.f(getDamageBonusPerRicochet(level), 2) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(ProjectileHitEvent e) { + if (!(e.getEntity() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxRicochets(level) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getSpeedBonusPerRicochet(level), 0) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore2")); - v.addLore(C.GREEN + "+ " + Form.f(getDamageBonusPerRicochet(level), 2) + C.GRAY + " " + Localizer.dLocalize("ranged.ricochet_bolt.lore3")); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(ProjectileHitEvent e) { - if (!(e.getEntity() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p)) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (e.getHitBlock() == null || !supportsRicochet(projectile)) { - return; - } - - int ricochetCount = Math.max(0, getMetadataInt(projectile, RICOCHET_COUNT_META, 0)); - int maxRicochets = Math.max(1, getMetadataInt(projectile, RICOCHET_MAX_META, getMaxRicochets(level))); - if (ricochetCount >= maxRicochets) { - return; - } - - Vector incoming = resolveIncomingVector(projectile); - if (incoming.lengthSquared() < getConfig().minRicochetVelocitySquared) { - return; - } - - BlockFace hitFace = resolveHitFace(e, incoming); - if (hitFace == null) { - return; - } - - Vector reflectedDir = reflect(incoming.clone().normalize(), hitFace); - if (reflectedDir.lengthSquared() <= 0.0000001) { - return; - } - - reflectedDir.normalize(); - double nextSpeed = Math.max(getConfig().minimumPostBounceSpeed, incoming.length()) * (1D + getSpeedBonusPerRicochet(level)); - if (nextSpeed <= 0) { - return; - } - - Vector ricochetVelocity = reflectedDir.clone().multiply(nextSpeed); - int nextRicochetCount = ricochetCount + 1; - double bonusDamage = getMetadataDouble(projectile, BONUS_DAMAGE_META, 0D) + getDamageBonusPerRicochet(level); - - Location bounceLocation = projectile.getLocation().clone() - .add(hitFace.getDirection().normalize().multiply(getConfig().spawnOffsetFromSurface)) - .add(reflectedDir.clone().multiply(getConfig().spawnOffsetAlongDirection)); - Projectile ricochet = spawnRicochetProjectile(projectile, bounceLocation, ricochetVelocity, p); - if (ricochet == null) { - return; - } - - ricochet.setMetadata(RICOCHET_COUNT_META, new FixedMetadataValue(Adapt.instance, nextRicochetCount)); - ricochet.setMetadata(RICOCHET_MAX_META, new FixedMetadataValue(Adapt.instance, maxRicochets)); - ricochet.setMetadata(BONUS_DAMAGE_META, new FixedMetadataValue(Adapt.instance, bonusDamage)); - - Location fx = e.getHitBlock().getLocation().add(0.5, 0.5, 0.5); - if (areParticlesEnabled()) { - projectile.getWorld().spawnParticle(Particle.ELECTRIC_SPARK, fx, Math.max(1, getConfig().sparkParticleCount), - getConfig().sparkSpread, getConfig().sparkSpread, getConfig().sparkSpread, 0.02); - projectile.getWorld().spawnParticle(Particle.CRIT, fx, Math.max(1, getConfig().critParticleCount), - getConfig().critSpread, getConfig().critSpread, getConfig().critSpread, 0.08); - } - if (areSoundsEnabled()) { - SoundPlayer sp = SoundPlayer.of(projectile.getWorld()); - sp.play(fx, Sound.BLOCK_ANVIL_HIT, 0.85f, (float) Math.max(0.4, getConfig().bouncePitchBase - (nextRicochetCount * getConfig().bouncePitchDropPerRicochet))); - sp.play(fx, Sound.BLOCK_AMETHYST_BLOCK_HIT, 0.9f, (float) Math.min(2.0, getConfig().sparkPitchBase + (nextRicochetCount * getConfig().sparkPitchRaisePerRicochet))); - } - xp(p, getConfig().xpPerRicochet + (nextRicochetCount * getConfig().xpPerRicochetStep)); - getPlayer(p).getData().addStat("ranged.ricochet-bolt.total-ricochets", 1); - projectile.remove(); + if (e.getHitBlock() == null || !supportsRicochet(projectile)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !projectile.hasMetadata(BONUS_DAMAGE_META)) { - return; - } - - if (!canDamageTarget(p, e.getEntity())) { - return; - } - - double bonusDamage = getMetadataDouble(projectile, BONUS_DAMAGE_META, 0D); - if (bonusDamage > 0 && e.getDamage() > 0) { - e.setDamage(e.getDamage() + bonusDamage); - } + int ricochetCount = Math.max(0, getMetadataInt(projectile, RICOCHET_COUNT_META, 0)); + int maxRicochets = Math.max(1, getMetadataInt(projectile, RICOCHET_MAX_META, getMaxRicochets(level))); + if (ricochetCount >= maxRicochets) { + return; } - @EventHandler - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Projectile projectile - && projectile.hasMetadata(RICOCHET_COUNT_META) - && projectile.getShooter() instanceof Player) { - getPlayer(p).getData().addStat("ranged.ricochet-bolt.ricochet-kills", 1); - } - } + Vector incoming = resolveIncomingVector(projectile); + if (incoming.lengthSquared() < getConfig().minRicochetVelocitySquared) { + return; } - private Projectile spawnRicochetProjectile(Projectile original, Location spawnLocation, Vector velocity, Player shooter) { - Vector dir = velocity.clone().normalize(); - float speed = (float) Math.max(0.2, velocity.length()); - if (original instanceof SpectralArrow sourceSpectral && sourceSpectral instanceof AbstractArrow sourceAbstract) { - SpectralArrow spectral = original.getWorld().spawn(spawnLocation, SpectralArrow.class); - copyArrowState(sourceAbstract, spectral, shooter, velocity); - spectral.setGlowingTicks(sourceSpectral.getGlowingTicks()); - return spectral; - } - - if (original instanceof Trident sourceTrident) { - Trident trident = original.getWorld().spawn(spawnLocation, Trident.class); - copyArrowState(sourceTrident, trident, shooter, velocity); - trident.setItem(sourceTrident.getItem()); - return trident; - } - - if (original instanceof Arrow sourceArrow) { - Arrow arrow = original.getWorld().spawnArrow(spawnLocation, dir, speed, 0f); - copyArrowState(sourceArrow, arrow, shooter, velocity); - arrow.setBasePotionType(sourceArrow.getBasePotionType()); - sourceArrow.getCustomEffects().forEach(effect -> arrow.addCustomEffect(effect, true)); - return arrow; - } - - if (original instanceof Snowball) { - Snowball snowball = original.getWorld().spawn(spawnLocation, Snowball.class); - copyProjectileState(original, snowball, shooter, velocity); - return snowball; - } - - if (original instanceof Egg) { - Egg egg = original.getWorld().spawn(spawnLocation, Egg.class); - copyProjectileState(original, egg, shooter, velocity); - return egg; - } - - if (original instanceof EnderPearl) { - EnderPearl pearl = original.getWorld().spawn(spawnLocation, EnderPearl.class); - copyProjectileState(original, pearl, shooter, velocity); - return pearl; - } - - if (original instanceof ThrownPotion sourcePotion) { - ThrownPotion potion = original.getWorld().spawn(spawnLocation, ThrownPotion.class); - copyProjectileState(sourcePotion, potion, shooter, velocity); - potion.setItem(sourcePotion.getItem().clone()); - return potion; - } - - if (original instanceof ThrownExpBottle) { - ThrownExpBottle bottle = original.getWorld().spawn(spawnLocation, ThrownExpBottle.class); - copyProjectileState(original, bottle, shooter, velocity); - return bottle; - } - - return null; + BlockFace hitFace = resolveHitFace(e, incoming); + if (hitFace == null) { + return; } - private void copyArrowState(AbstractArrow source, AbstractArrow target, Player shooter, Vector velocity) { - copyProjectileState(source, target, shooter, velocity); - target.setDamage(source.getDamage()); - target.setCritical(source.isCritical()); - target.setKnockbackStrength(source.getKnockbackStrength()); - target.setPierceLevel(source.getPierceLevel()); - target.setPickupStatus(AbstractArrow.PickupStatus.CREATIVE_ONLY); + Vector reflectedDir = reflect(incoming.clone().normalize(), hitFace); + if (reflectedDir.lengthSquared() <= 0.0000001) { + return; } - private void copyProjectileState(Projectile source, Projectile target, Player shooter, Vector velocity) { - target.setShooter(shooter); - target.setVelocity(velocity); - target.setBounce(source.doesBounce()); - target.setGravity(source.hasGravity()); - target.setFireTicks(source.getFireTicks()); + reflectedDir.normalize(); + double nextSpeed = Math.max(getConfig().minimumPostBounceSpeed, incoming.length()) * (1D + getSpeedBonusPerRicochet(level)); + if (nextSpeed <= 0) { + return; } - private Vector reflect(Vector incoming, BlockFace face) { - Vector normal = face.getDirection().normalize(); - double dot = incoming.dot(normal); - return incoming.clone().subtract(normal.multiply(2D * dot)); - } + Vector ricochetVelocity = reflectedDir.clone().multiply(nextSpeed); + int nextRicochetCount = ricochetCount + 1; + double bonusDamage = getMetadataDouble(projectile, BONUS_DAMAGE_META, 0D) + getDamageBonusPerRicochet(level); - private Vector resolveIncomingVector(Projectile projectile) { - Vector liveVelocity = projectile.getVelocity().clone(); - if (liveVelocity.lengthSquared() >= getConfig().minimumLiveVelocitySquared) { - return liveVelocity; - } + Location bounceLocation = projectile.getLocation().clone() + .add(hitFace.getDirection().normalize().multiply(getConfig().spawnOffsetFromSurface)) + .add(reflectedDir.clone().multiply(getConfig().spawnOffsetAlongDirection)); + Projectile ricochet = spawnRicochetProjectile(projectile, bounceLocation, ricochetVelocity, p); + if (ricochet == null) { + return; + } - Vector facing = projectile.getLocation().getDirection().clone(); - if (facing.lengthSquared() > 0.0000001) { - return facing.normalize().multiply(Math.max(getConfig().minimumPostBounceSpeed, liveVelocity.length())); - } + ricochet.setMetadata(RICOCHET_COUNT_META, new FixedMetadataValue(Adapt.instance, nextRicochetCount)); + ricochet.setMetadata(RICOCHET_MAX_META, new FixedMetadataValue(Adapt.instance, maxRicochets)); + ricochet.setMetadata(BONUS_DAMAGE_META, new FixedMetadataValue(Adapt.instance, bonusDamage)); - return liveVelocity; + Location fx = e.getHitBlock().getLocation().add(0.5, 0.5, 0.5); + if (areParticlesEnabled()) { + projectile.getWorld().spawnParticle(Particle.ELECTRIC_SPARK, fx, Math.max(1, getConfig().sparkParticleCount), + getConfig().sparkSpread, getConfig().sparkSpread, getConfig().sparkSpread, 0.02); + projectile.getWorld().spawnParticle(Particle.CRIT, fx, Math.max(1, getConfig().critParticleCount), + getConfig().critSpread, getConfig().critSpread, getConfig().critSpread, 0.08); + } + if (areSoundsEnabled()) { + SoundPlayer sp = SoundPlayer.of(projectile.getWorld()); + sp.play(fx, Sound.BLOCK_ANVIL_HIT, 0.85f, (float) Math.max(0.4, getConfig().bouncePitchBase - (nextRicochetCount * getConfig().bouncePitchDropPerRicochet))); + sp.play(fx, Sound.BLOCK_AMETHYST_BLOCK_HIT, 0.9f, (float) Math.min(2.0, getConfig().sparkPitchBase + (nextRicochetCount * getConfig().sparkPitchRaisePerRicochet))); + } + xp(p, getConfig().xpPerRicochet + (nextRicochetCount * getConfig().xpPerRicochetStep)); + getPlayer(p).getData().addStat("ranged.ricochet-bolt.total-ricochets", 1); + projectile.remove(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Projectile projectile) || !(projectile.getShooter() instanceof Player p) || !projectile.hasMetadata(BONUS_DAMAGE_META)) { + return; } - private boolean supportsRicochet(Projectile projectile) { - if (projectile instanceof AbstractArrow) { - return true; - } - - return getConfig().applyToAllProjectiles - && (projectile instanceof Snowball - || projectile instanceof Egg - || projectile instanceof EnderPearl - || projectile instanceof ThrownPotion - || projectile instanceof ThrownExpBottle); + if (!canDamageTarget(p, e.getEntity())) { + return; } - private BlockFace resolveHitFace(ProjectileHitEvent e, Vector incoming) { - if (e.getHitBlockFace() != null) { - return e.getHitBlockFace(); - } + double bonusDamage = getMetadataDouble(projectile, BONUS_DAMAGE_META, 0D); + if (bonusDamage > 0 && e.getDamage() > 0) { + e.setDamage(e.getDamage() + bonusDamage); + } + } + + @EventHandler + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Projectile projectile + && projectile.hasMetadata(RICOCHET_COUNT_META) + && projectile.getShooter() instanceof Player) { + getPlayer(p).getData().addStat("ranged.ricochet-bolt.ricochet-kills", 1); + } + } + } + + private Projectile spawnRicochetProjectile(Projectile original, Location spawnLocation, Vector velocity, Player shooter) { + Vector dir = velocity.clone().normalize(); + float speed = (float) Math.max(0.2, velocity.length()); + if (original instanceof SpectralArrow sourceSpectral && sourceSpectral instanceof AbstractArrow sourceAbstract) { + SpectralArrow spectral = original.getWorld().spawn(spawnLocation, SpectralArrow.class); + copyArrowState(sourceAbstract, spectral, shooter, velocity); + spectral.setGlowingTicks(sourceSpectral.getGlowingTicks()); + return spectral; + } - double ax = Math.abs(incoming.getX()); - double ay = Math.abs(incoming.getY()); - double az = Math.abs(incoming.getZ()); + if (original instanceof Trident sourceTrident) { + Trident trident = original.getWorld().spawn(spawnLocation, Trident.class); + copyArrowState(sourceTrident, trident, shooter, velocity); + trident.setItem(sourceTrident.getItem()); + return trident; + } - if (ay >= ax && ay >= az) { - return incoming.getY() > 0 ? BlockFace.DOWN : BlockFace.UP; - } + if (original instanceof Arrow sourceArrow) { + Arrow arrow = original.getWorld().spawnArrow(spawnLocation, dir, speed, 0f); + copyArrowState(sourceArrow, arrow, shooter, velocity); + arrow.setBasePotionType(sourceArrow.getBasePotionType()); + sourceArrow.getCustomEffects().forEach(effect -> arrow.addCustomEffect(effect, true)); + return arrow; + } - if (ax >= az) { - return incoming.getX() > 0 ? BlockFace.WEST : BlockFace.EAST; - } + if (original instanceof Snowball) { + Snowball snowball = original.getWorld().spawn(spawnLocation, Snowball.class); + copyProjectileState(original, snowball, shooter, velocity); + return snowball; + } - return incoming.getZ() > 0 ? BlockFace.NORTH : BlockFace.SOUTH; + if (original instanceof Egg) { + Egg egg = original.getWorld().spawn(spawnLocation, Egg.class); + copyProjectileState(original, egg, shooter, velocity); + return egg; } - private int getMetadataInt(Projectile projectile, String key, int fallback) { - for (MetadataValue value : projectile.getMetadata(key)) { - if (value.getOwningPlugin() == Adapt.instance) { - return value.asInt(); - } - } + if (original instanceof EnderPearl) { + EnderPearl pearl = original.getWorld().spawn(spawnLocation, EnderPearl.class); + copyProjectileState(original, pearl, shooter, velocity); + return pearl; + } - return fallback; + if (original instanceof ThrownPotion sourcePotion) { + ThrownPotion potion = original.getWorld().spawn(spawnLocation, ThrownPotion.class); + copyProjectileState(sourcePotion, potion, shooter, velocity); + potion.setItem(sourcePotion.getItem().clone()); + return potion; } - private double getMetadataDouble(Projectile projectile, String key, double fallback) { - for (MetadataValue value : projectile.getMetadata(key)) { - if (value.getOwningPlugin() == Adapt.instance) { - return value.asDouble(); - } - } + if (original instanceof ThrownExpBottle) { + ThrownExpBottle bottle = original.getWorld().spawn(spawnLocation, ThrownExpBottle.class); + copyProjectileState(original, bottle, shooter, velocity); + return bottle; + } - return fallback; + return null; + } + + private void copyArrowState(AbstractArrow source, AbstractArrow target, Player shooter, Vector velocity) { + copyProjectileState(source, target, shooter, velocity); + target.setDamage(source.getDamage()); + target.setCritical(source.isCritical()); + target.setKnockbackStrength(source.getKnockbackStrength()); + target.setPierceLevel(source.getPierceLevel()); + target.setPickupStatus(AbstractArrow.PickupStatus.CREATIVE_ONLY); + } + + private void copyProjectileState(Projectile source, Projectile target, Player shooter, Vector velocity) { + target.setShooter(shooter); + target.setVelocity(velocity); + target.setBounce(source.doesBounce()); + target.setGravity(source.hasGravity()); + target.setFireTicks(source.getFireTicks()); + } + + private Vector reflect(Vector incoming, BlockFace face) { + Vector normal = face.getDirection().normalize(); + double dot = incoming.dot(normal); + return incoming.clone().subtract(normal.multiply(2D * dot)); + } + + private Vector resolveIncomingVector(Projectile projectile) { + Vector liveVelocity = projectile.getVelocity().clone(); + if (liveVelocity.lengthSquared() >= getConfig().minimumLiveVelocitySquared) { + return liveVelocity; } - private int getMaxRicochets(int level) { - return Math.max(1, (int) Math.round(getConfig().maxRicochetsBase + (getLevelPercent(level) * getConfig().maxRicochetsFactor))); + Vector facing = projectile.getLocation().getDirection().clone(); + if (facing.lengthSquared() > 0.0000001) { + return facing.normalize().multiply(Math.max(getConfig().minimumPostBounceSpeed, liveVelocity.length())); } - private double getSpeedBonusPerRicochet(int level) { - return Math.min(getConfig().maxSpeedBonusPerRicochet, - getConfig().speedBonusPerRicochetBase + (getLevelPercent(level) * getConfig().speedBonusPerRicochetFactor)); + return liveVelocity; + } + + private boolean supportsRicochet(Projectile projectile) { + if (projectile instanceof AbstractArrow) { + return true; } - private double getDamageBonusPerRicochet(int level) { - return Math.min(getConfig().maxDamageBonusPerRicochet, - getConfig().damageBonusPerRicochetBase + (getLevelPercent(level) * getConfig().damageBonusPerRicochetFactor)); + return getConfig().applyToAllProjectiles + && (projectile instanceof Snowball + || projectile instanceof Egg + || projectile instanceof EnderPearl + || projectile instanceof ThrownPotion + || projectile instanceof ThrownExpBottle); + } + + private BlockFace resolveHitFace(ProjectileHitEvent e, Vector incoming) { + if (e.getHitBlockFace() != null) { + return e.getHitBlockFace(); } - @Override - public void onTick() { + double ax = Math.abs(incoming.getX()); + double ay = Math.abs(incoming.getY()); + double az = Math.abs(incoming.getZ()); + if (ay >= ax && ay >= az) { + return incoming.getY() > 0 ? BlockFace.DOWN : BlockFace.UP; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (ax >= az) { + return incoming.getX() > 0 ? BlockFace.WEST : BlockFace.EAST; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + return incoming.getZ() > 0 ? BlockFace.NORTH : BlockFace.SOUTH; + } + + private int getMetadataInt(Projectile projectile, String key, int fallback) { + for (MetadataValue value : projectile.getMetadata(key)) { + if (value.getOwningPlugin() == Adapt.instance) { + return value.asInt(); + } } - @NoArgsConstructor - @ConfigDescription("Projectiles ricochet from block impacts with chained bounces, scaling speed, and bonus damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.74; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRicochetsBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRicochetsFactor = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedBonusPerRicochetBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedBonusPerRicochetFactor = 0.27; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Speed Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxSpeedBonusPerRicochet = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusPerRicochetBase = 0.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusPerRicochetFactor = 2.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxDamageBonusPerRicochet = 3.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Ricochet Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minRicochetVelocitySquared = 0.09; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Live Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumLiveVelocitySquared = 0.0004; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Post Bounce Speed for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumPostBounceSpeed = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset From Surface for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spawnOffsetFromSurface = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset Along Direction for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spawnOffsetAlongDirection = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int sparkParticleCount = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sparkSpread = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int critParticleCount = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double critSpread = 0.14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bouncePitchBase = 1.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Drop Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bouncePitchDropPerRicochet = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sparkPitchBase = 1.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Raise Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sparkPitchRaisePerRicochet = 0.07; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerRicochet = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet Step for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerRicochetStep = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) so all supported player projectiles can bounce.", impact = "True enables universal ricochet across most player-thrown projectiles.") - boolean applyToAllProjectiles = true; + return fallback; + } + + private double getMetadataDouble(Projectile projectile, String key, double fallback) { + for (MetadataValue value : projectile.getMetadata(key)) { + if (value.getOwningPlugin() == Adapt.instance) { + return value.asDouble(); + } } + + return fallback; + } + + private int getMaxRicochets(int level) { + return Math.max(1, (int) Math.round(getConfig().maxRicochetsBase + (getLevelPercent(level) * getConfig().maxRicochetsFactor))); + } + + private double getSpeedBonusPerRicochet(int level) { + return Math.min(getConfig().maxSpeedBonusPerRicochet, + getConfig().speedBonusPerRicochetBase + (getLevelPercent(level) * getConfig().speedBonusPerRicochetFactor)); + } + + private double getDamageBonusPerRicochet(int level) { + return Math.min(getConfig().maxDamageBonusPerRicochet, + getConfig().damageBonusPerRicochetBase + (getLevelPercent(level) * getConfig().damageBonusPerRicochetFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Projectiles ricochet from block impacts with chained bounces, scaling speed, and bonus damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.74; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRicochetsBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Ricochets Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRicochetsFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedBonusPerRicochetBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedBonusPerRicochetFactor = 0.27; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Speed Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxSpeedBonusPerRicochet = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusPerRicochetBase = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Per Ricochet Factor for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusPerRicochetFactor = 2.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Bonus Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxDamageBonusPerRicochet = 3.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Ricochet Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minRicochetVelocitySquared = 0.09; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Live Velocity Squared for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumLiveVelocitySquared = 0.0004; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Post Bounce Speed for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumPostBounceSpeed = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset From Surface for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spawnOffsetFromSurface = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawn Offset Along Direction for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spawnOffsetAlongDirection = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int sparkParticleCount = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sparkSpread = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Particle Count for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int critParticleCount = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crit Spread for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double critSpread = 0.14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bouncePitchBase = 1.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bounce Pitch Drop Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bouncePitchDropPerRicochet = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Base for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sparkPitchBase = 1.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spark Pitch Raise Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sparkPitchRaisePerRicochet = 0.07; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerRicochet = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ricochet Step for the Ranged Ricochet Bolt adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerRicochetStep = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allow ricochet behavior to apply to throwables (snowballs, eggs, pearls, potions, exp bottles) so all supported player projectiles can bounce.", impact = "True enables universal ricochet across most player-thrown projectiles.") + boolean applyToAllProjectiles = true; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index 843bcaaef..655b5e824 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -28,9 +28,9 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -41,15 +41,11 @@ import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityShootBowEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerItemHeldEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerSwapHandItemsEvent; -import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.util.RayTraceResult; @@ -62,973 +58,975 @@ import java.util.concurrent.ConcurrentHashMap; public class RangedTrajectorySight extends SimpleAdaptation { - private static final double EPSILON = 0.0000001D; - private final Map drawStartedMillis = new ConcurrentHashMap<>(); - private final Map previewGlowTargets = new ConcurrentHashMap<>(); - private final Set previewCandidates = ConcurrentHashMap.newKeySet(); - private final Map previewState = new ConcurrentHashMap<>(); - private volatile RangedForce cachedRangedForce; - private volatile RangedRicochetBolt cachedRicochetBolt; - private volatile long lastPreviewCandidateRefreshMs; - - public RangedTrajectorySight() { - super("ranged-trajectory-sight"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.trajectory_sight.description")); - setDisplayName(Localizer.dLocalize("ranged.trajectory_sight.name")); - setIcon(Material.SPYGLASS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(20); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPYGLASS) - .key("challenge_ranged_trajectory_100") - .title(Localizer.dLocalize("advancement.challenge_ranged_trajectory_100.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_trajectory_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_trajectory_100", "ranged.trajectory-sight.kills-while-aiming", 100, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getVelocityMultiplier(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.trajectory_sight.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getSegments(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.trajectory_sight.lore2")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - clearPreviewState(p); + private static final double EPSILON = 0.0000001D; + private final Map drawStartedMillis = new ConcurrentHashMap<>(); + private final Map previewGlowTargets = new ConcurrentHashMap<>(); + private final Set previewCandidates = ConcurrentHashMap.newKeySet(); + private final Map previewState = new ConcurrentHashMap<>(); + private volatile RangedForce cachedRangedForce; + private volatile RangedRicochetBolt cachedRicochetBolt; + private volatile long lastPreviewCandidateRefreshMs; + + public RangedTrajectorySight() { + super("ranged-trajectory-sight"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.trajectory_sight.description")); + setDisplayName(Localizer.dLocalize("ranged.trajectory_sight.name")); + setIcon(Material.SPYGLASS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(20); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPYGLASS) + .key("challenge_ranged_trajectory_100") + .title(Localizer.dLocalize("advancement.challenge_ranged_trajectory_100.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_trajectory_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_trajectory_100", "ranged.trajectory-sight.kills-while-aiming", 100, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getVelocityMultiplier(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.trajectory_sight.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getSegments(level)) + C.GRAY + " " + Localizer.dLocalize("ranged.trajectory_sight.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + clearPreviewState(p); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { - return; - } + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isItem(hand)) { + return; + } - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!isItem(hand)) { - return; - } + Material type = hand.getType(); + if (type != Material.BOW && type != Material.CROSSBOW) { + return; + } - Material type = hand.getType(); - if (type != Material.BOW && type != Material.CROSSBOW) { - return; - } + if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) { + drawStartedMillis.put(p.getUniqueId(), System.currentTimeMillis()); + markPreviewCandidate(p); + } + } - switch (e.getAction()) { - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> { - drawStartedMillis.put(p.getUniqueId(), System.currentTimeMillis()); - markPreviewCandidate(p); - } - default -> { - } - } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerItemHeldEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerItemHeldEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } + markPreviewCandidate(p); + } - markPreviewCandidate(p); + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerSwapHandItemsEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerSwapHandItemsEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } + markPreviewCandidate(p); + } - markPreviewCandidate(p); + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } + if (e.isSneaking()) { + markPreviewCandidate(p); + return; + } - if (e.isSneaking()) { - markPreviewCandidate(p); - return; - } + if (resolvePreviewContext(p) == null) { + previewCandidates.remove(p.getUniqueId()); + previewState.remove(p.getUniqueId()); + clearPreviewGlow(p); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + boolean tracked = previewCandidates.contains(id); + if (!tracked && !p.isSneaking() && !p.isHandRaised()) { + return; + } - if (resolvePreviewContext(p) == null) { - previewCandidates.remove(p.getUniqueId()); - previewState.remove(p.getUniqueId()); - clearPreviewGlow(p); - } + if (!hasActiveAdaptation(p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - boolean tracked = previewCandidates.contains(id); - if (!tracked && !p.isSneaking() && !p.isHandRaised()) { - return; - } + markPreviewCandidate(p); + } - if (!hasActiveAdaptation(p)) { - return; - } - - markPreviewCandidate(p); + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityShootBowEvent e) { + if (e.getEntity() instanceof Player p) { + drawStartedMillis.remove(p.getUniqueId()); + markPreviewCandidate(p); } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityShootBowEvent e) { - if (e.getEntity() instanceof Player p) { - drawStartedMillis.remove(p.getUniqueId()); - markPreviewCandidate(p); - } + } + + @EventHandler + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Projectile projectile + && projectile.getShooter() instanceof Player) { + getPlayer(p).getData().addStat("ranged.trajectory-sight.kills-while-aiming", 1); + } } - - @EventHandler - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() instanceof Player p && hasActiveAdaptation(p)) { - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Projectile projectile - && projectile.getShooter() instanceof Player) { - getPlayer(p).getData().addStat("ranged.trajectory-sight.kills-while-aiming", 1); - } - } + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + refreshPreviewCandidates(now); + if (previewCandidates.isEmpty()) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - refreshPreviewCandidates(now); - if (previewCandidates.isEmpty()) { - return; - } + Set candidates = new HashSet<>(previewCandidates); + for (UUID id : candidates) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + clearPreviewState(id); + continue; + } - Set candidates = new HashSet<>(previewCandidates); - for (UUID id : candidates) { - Player p = Bukkit.getPlayer(id); - if (p == null || !p.isOnline()) { - clearPreviewState(id); - continue; - } + int level = getActiveLevel(p); + if (level <= 0) { + clearPreviewState(p); + continue; + } - int level = getActiveLevel(p); - if (level <= 0) { - clearPreviewState(p); - continue; - } + PreviewContext context = resolvePreviewContext(p); + if (context == null) { + previewState.remove(id); + previewCandidates.remove(id); + drawStartedMillis.remove(id); + clearPreviewGlow(p); + continue; + } - PreviewContext context = resolvePreviewContext(p); - if (context == null) { - previewState.remove(id); - previewCandidates.remove(id); - drawStartedMillis.remove(id); - clearPreviewGlow(p); - continue; - } + if (context.trigger() != PreviewTrigger.DRAWING_BOW) { + drawStartedMillis.remove(p.getUniqueId()); + } - if (context.trigger() != PreviewTrigger.DRAWING_BOW) { - drawStartedMillis.remove(p.getUniqueId()); - } + ShotPreview shot = getShotPreview(p, context); + if (shot == null) { + clearPreviewGlow(p); + continue; + } - ShotPreview shot = getShotPreview(p, context); - if (shot == null) { - clearPreviewGlow(p); - continue; - } + if (!shouldRenderPreview(p, level, context, now)) { + continue; + } - if (!shouldRenderPreview(p, level, context, now)) { - continue; - } - - UUID predictedHit = renderTrajectory(p, getRenderSegments(level), shot); - updatePreviewGlow(p, predictedHit); - previewState.put(id, PreviewState.capture(now, level, context, p.getEyeLocation())); - } + UUID predictedHit = renderTrajectory(p, getRenderSegments(level), shot); + updatePreviewGlow(p, predictedHit); + previewState.put(id, PreviewState.capture(now, level, context, p.getEyeLocation())); } + } - private void refreshPreviewCandidates(long now) { - long refreshEvery = Math.max(250L, getConfig().previewCandidateRefreshMillis); - if (now - lastPreviewCandidateRefreshMs < refreshEvery) { - return; - } + private void refreshPreviewCandidates(long now) { + long refreshEvery = Math.max(250L, getConfig().previewCandidateRefreshMillis); + if (now - lastPreviewCandidateRefreshMs < refreshEvery) { + return; + } - lastPreviewCandidateRefreshMs = now; - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } + lastPreviewCandidateRefreshMs = now; + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } - int level = getActiveLevel(p); - if (level <= 0) { - continue; - } + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } - if (resolvePreviewContext(p) != null) { - previewCandidates.add(p.getUniqueId()); - } - } + if (resolvePreviewContext(p) != null) { + previewCandidates.add(p.getUniqueId()); + } } + } - private void markPreviewCandidate(Player p) { - if (p == null) { - return; - } - - previewCandidates.add(p.getUniqueId()); + private void markPreviewCandidate(Player p) { + if (p == null) { + return; } - private boolean shouldRenderPreview(Player p, int level, PreviewContext context, long now) { - PreviewState state = previewState.get(p.getUniqueId()); - if (state == null) { - return true; - } + previewCandidates.add(p.getUniqueId()); + } - long refreshEvery = Math.max(10L, getConfig().previewRenderIntervalMillis); - if (now - state.renderedAtMs() >= refreshEvery) { - return true; - } + private boolean shouldRenderPreview(Player p, int level, PreviewContext context, long now) { + PreviewState state = previewState.get(p.getUniqueId()); + if (state == null) { + return true; + } - if (state.level() != level) { - return true; - } + long refreshEvery = Math.max(10L, getConfig().previewRenderIntervalMillis); + if (now - state.renderedAtMs() >= refreshEvery) { + return true; + } - Material material = context.item().getType(); - if (state.material() != material || state.trigger() != context.trigger()) { - return true; - } + if (state.level() != level) { + return true; + } - Location eye = p.getEyeLocation(); - if (angleDelta(eye.getYaw(), state.yaw()) >= Math.max(0.01D, getConfig().previewYawDeltaDegrees)) { - return true; - } + Material material = context.item().getType(); + if (state.material() != material || state.trigger() != context.trigger()) { + return true; + } - if (angleDelta(eye.getPitch(), state.pitch()) >= Math.max(0.01D, getConfig().previewPitchDeltaDegrees)) { - return true; - } + Location eye = p.getEyeLocation(); + if (angleDelta(eye.getYaw(), state.yaw()) >= Math.max(0.01D, getConfig().previewYawDeltaDegrees)) { + return true; + } - double dx = eye.getX() - state.x(); - double dy = eye.getY() - state.y(); - double dz = eye.getZ() - state.z(); - double movedSq = (dx * dx) + (dy * dy) + (dz * dz); - return movedSq >= Math.max(0D, getConfig().previewPositionDeltaSquared); + if (angleDelta(eye.getPitch(), state.pitch()) >= Math.max(0.01D, getConfig().previewPitchDeltaDegrees)) { + return true; } - private double angleDelta(float from, float to) { - double delta = Math.abs((double) from - to) % 360D; - return delta > 180D ? 360D - delta : delta; + double dx = eye.getX() - state.x(); + double dy = eye.getY() - state.y(); + double dz = eye.getZ() - state.z(); + double movedSq = (dx * dx) + (dy * dy) + (dz * dz); + return movedSq >= Math.max(0D, getConfig().previewPositionDeltaSquared); + } + + private double angleDelta(float from, float to) { + double delta = Math.abs((double) from - to) % 360D; + return delta > 180D ? 360D - delta : delta; + } + + private int getRenderSegments(int level) { + int minSegments = Math.max(6, getConfig().minimumRenderedSegments); + int maxSegments = Math.max(minSegments, getConfig().maxRenderedSegments); + int segments = Math.max(minSegments, Math.min(getSegments(level), maxSegments)); + if (Adapt.instance.getTicker() == null) { + return segments; } - private int getRenderSegments(int level) { - int minSegments = Math.max(6, getConfig().minimumRenderedSegments); - int maxSegments = Math.max(minSegments, getConfig().maxRenderedSegments); - int segments = Math.max(minSegments, Math.min(getSegments(level), maxSegments)); - if (Adapt.instance.getTicker() == null) { - return segments; - } + double load = Adapt.instance.getTicker().getWindowLoadPercent(); + if (load < Math.max(0D, getConfig().previewHighLoadPercent)) { + return segments; + } - double load = Adapt.instance.getTicker().getWindowLoadPercent(); - if (load < Math.max(0D, getConfig().previewHighLoadPercent)) { - return segments; - } + double scale = Math.max(0.2D, Math.min(1D, getConfig().previewHighLoadSegmentScale)); + int scaled = (int) Math.round(segments * scale); + return Math.max(minSegments, Math.min(maxSegments, scaled)); + } - double scale = Math.max(0.2D, Math.min(1D, getConfig().previewHighLoadSegmentScale)); - int scaled = (int) Math.round(segments * scale); - return Math.max(minSegments, Math.min(maxSegments, scaled)); + private void clearPreviewState(Player p) { + if (p == null) { + return; } - private void clearPreviewState(Player p) { - if (p == null) { - return; - } + clearPreviewState(p.getUniqueId()); + clearPreviewGlow(p); + } - clearPreviewState(p.getUniqueId()); - clearPreviewGlow(p); + private void clearPreviewState(UUID id) { + if (id == null) { + return; } - private void clearPreviewState(UUID id) { - if (id == null) { - return; - } + drawStartedMillis.remove(id); + previewCandidates.remove(id); + previewState.remove(id); + } - drawStartedMillis.remove(id); - previewCandidates.remove(id); - previewState.remove(id); + private PreviewContext resolvePreviewContext(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + + if (isDrawingBow(p, main)) { + return new PreviewContext(main, PreviewTrigger.DRAWING_BOW); } - private PreviewContext resolvePreviewContext(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); + if (!p.isSneaking()) { + return null; + } - if (isDrawingBow(p, main)) { - return new PreviewContext(main, PreviewTrigger.DRAWING_BOW); - } + if (isSneakProjectile(main)) { + return new PreviewContext(main, PreviewTrigger.SNEAK_PROJECTILE); + } - if (!p.isSneaking()) { - return null; - } + if (isSneakProjectile(off)) { + return new PreviewContext(off, PreviewTrigger.SNEAK_PROJECTILE); + } - if (isSneakProjectile(main)) { - return new PreviewContext(main, PreviewTrigger.SNEAK_PROJECTILE); - } + return null; + } - if (isSneakProjectile(off)) { - return new PreviewContext(off, PreviewTrigger.SNEAK_PROJECTILE); - } + private ShotPreview getShotPreview(Player p, PreviewContext context) { + Material launchType = context.item().getType(); + double launchVelocity = getLaunchVelocity(p, launchType); + if (launchVelocity <= 0.01) { + return null; + } - return null; + BallisticsProfile profile = resolveBallisticsProfile(launchType); + Vector direction = applyLaunchDirectionTuning(launchType, p.getEyeLocation().getDirection().clone()); + if (direction.lengthSquared() <= EPSILON) { + return null; } - private ShotPreview getShotPreview(Player p, PreviewContext context) { - Material launchType = context.item().getType(); - double launchVelocity = getLaunchVelocity(p, launchType); - if (launchVelocity <= 0.01) { - return null; - } + Vector velocity = direction.normalize().multiply(launchVelocity); + velocity.multiply(getRangedForceLaunchMultiplier(p)); + RicochetPreview ricochet = getRicochetPreview(p); + if (!supportsRicochet(launchType, ricochet)) { + ricochet = RicochetPreview.disabled(); + } + return new ShotPreview(velocity, ricochet, profile, context.trigger()); + } - BallisticsProfile profile = resolveBallisticsProfile(launchType); - Vector direction = applyLaunchDirectionTuning(launchType, p.getEyeLocation().getDirection().clone()); - if (direction.lengthSquared() <= EPSILON) { - return null; - } + private double getLaunchVelocity(Player p, Material type) { + if (type == Material.SNOWBALL || type == Material.EGG || type == Material.ENDER_PEARL) { + return getConfig().thrownProjectileVelocity; + } - Vector velocity = direction.normalize().multiply(launchVelocity); - velocity.multiply(getRangedForceLaunchMultiplier(p)); - RicochetPreview ricochet = getRicochetPreview(p); - if (!supportsRicochet(launchType, ricochet)) { - ricochet = RicochetPreview.disabled(); - } - return new ShotPreview(velocity, ricochet, profile, context.trigger()); + if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { + return getConfig().thrownPotionVelocity; } - private double getLaunchVelocity(Player p, Material type) { - if (type == Material.SNOWBALL || type == Material.EGG || type == Material.ENDER_PEARL) { - return getConfig().thrownProjectileVelocity; - } + if (type == Material.TRIDENT) { + return getConfig().tridentVelocity; + } - if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { - return getConfig().thrownPotionVelocity; - } + if (type == Material.BOW) { + double force = getBowForce(p); + if (force <= 0) { + return 0; + } - if (type == Material.TRIDENT) { - return getConfig().tridentVelocity; - } + return force * 3.0; + } - if (type == Material.BOW) { - double force = getBowForce(p); - if (force <= 0) { - return 0; - } + if (type == Material.CROSSBOW) { + return getConfig().crossbowVelocity; + } - return force * 3.0; - } + return getConfig().fallbackVelocity; + } - if (type == Material.CROSSBOW) { - return getConfig().crossbowVelocity; - } + private double getBowForce(Player p) { + UUID id = p.getUniqueId(); + long now = System.currentTimeMillis(); + long start = drawStartedMillis.computeIfAbsent(id, k -> now); + double chargeTicks = Math.max(0, (now - start) / 50.0); - return getConfig().fallbackVelocity; + if (!p.isHandRaised() && p.isSneaking()) { + chargeTicks = getConfig().sneakPreviewChargeTicks; } - private double getBowForce(Player p) { - UUID id = p.getUniqueId(); - long now = System.currentTimeMillis(); - long start = drawStartedMillis.computeIfAbsent(id, k -> now); - double chargeTicks = Math.max(0, (now - start) / 50.0); - - if (!p.isHandRaised() && p.isSneaking()) { - chargeTicks = getConfig().sneakPreviewChargeTicks; + double force = chargeTicks / 20.0; + force = (force * force + force * 2.0) / 3.0; + return Math.min(1.0, force); + } + + private UUID renderTrajectory(Player p, int segments, ShotPreview shot) { + Location eye = p.getEyeLocation().clone(); + Location current = eye.clone().add(p.getEyeLocation().getDirection().normalize().multiply(getConfig().previewStartOffset)); + Vector velocity = shot.initialVelocity().clone(); + Color trailColor = shot.trigger() == PreviewTrigger.DRAWING_BOW + ? Color.fromRGB(120, 225, 255) + : Color.fromRGB(255, 190, 125); + int every = Math.max(1, getConfig().trailParticleEvery); + double minDistanceSq = Math.max(0, getConfig().minPreviewDistanceFromEye); + minDistanceSq *= minDistanceSq; + int ricochets = 0; + Location lastVisiblePoint = null; + UUID hitEntityId = null; + + for (int i = 0; i < segments; i++) { + Vector step = velocity.clone(); + double stepLength = step.length(); + if (stepLength <= EPSILON) { + return hitEntityId; + } + + Vector stepDirection = step.clone().normalize(); + Location from = current.clone(); + RayTraceResult entityHit = p.getWorld().rayTraceEntities(current, stepDirection, stepLength, entity -> isValidPreviewTarget(p, entity)); + RayTraceResult hit = p.getWorld().rayTraceBlocks(current, stepDirection, stepLength, FluidCollisionMode.NEVER, true); + if (isEntityFirstHit(current, hit, entityHit)) { + Entity target = entityHit.getHitEntity(); + if (target != null) { + hitEntityId = target.getUniqueId(); + } + + if (entityHit.getHitPosition() != null) { + current = entityHit.getHitPosition().toLocation(p.getWorld()); + } else if (target != null) { + current = target.getLocation().clone(); + } + + if (areParticlesEnabled() && eye.distanceSquared(current) >= minDistanceSq) { + float impactSize = getScaledParticleSize(eye.distance(current), 1.15D); + Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), impactSize); + p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount + 1), 0.02, 0.02, 0.02, 0.0, impact); + } + break; + } + + if (hit != null && hit.getHitBlock() != null) { + current = hit.getHitPosition().toLocation(p.getWorld()); + if (areParticlesEnabled()) { + if (eye.distanceSquared(current) >= minDistanceSq) { + float impactSize = getScaledParticleSize(eye.distance(current), 1.1D); + Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), impactSize); + p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount), 0.02, 0.02, 0.02, 0.0, impact); + } + } + + RicochetPreview ricochet = shot.ricochetPreview(); + if (canRicochet(ricochet, ricochets, velocity)) { + BlockFace hitFace = hit.getHitBlockFace() == null ? resolveHitFace(velocity) : hit.getHitBlockFace(); + if (hitFace == null) { + break; + } + + Vector reflectedDir = reflect(velocity.clone().normalize(), hitFace); + if (reflectedDir.lengthSquared() <= EPSILON) { + break; + } + + reflectedDir.normalize(); + double nextSpeed = Math.max(ricochet.minimumPostBounceSpeed(), velocity.length()) * (1D + ricochet.speedBonusPerRicochet()); + velocity = reflectedDir.clone().multiply(nextSpeed); + current.add(hitFace.getDirection().normalize().multiply(ricochet.spawnOffsetFromSurface())) + .add(reflectedDir.clone().multiply(ricochet.spawnOffsetAlongDirection())); + ricochets++; + if (areParticlesEnabled()) { + if (eye.distanceSquared(current) >= minDistanceSq) { + float bounceSize = getScaledParticleSize(eye.distance(current), 1.15D); + Particle.DustOptions bounce = new Particle.DustOptions(Color.fromRGB(170, 200, 255), bounceSize); + p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount), 0.02, 0.02, 0.02, 0.0, bounce); + } + } + continue; } - double force = chargeTicks / 20.0; - force = (force * force + force * 2.0) / 3.0; - return Math.min(1.0, force); - } - - private UUID renderTrajectory(Player p, int segments, ShotPreview shot) { - Location eye = p.getEyeLocation().clone(); - Location current = eye.clone().add(p.getEyeLocation().getDirection().normalize().multiply(getConfig().previewStartOffset)); - Vector velocity = shot.initialVelocity().clone(); - Color trailColor = shot.trigger() == PreviewTrigger.DRAWING_BOW - ? Color.fromRGB(120, 225, 255) - : Color.fromRGB(255, 190, 125); - int every = Math.max(1, getConfig().trailParticleEvery); - double minDistanceSq = Math.max(0, getConfig().minPreviewDistanceFromEye); - minDistanceSq *= minDistanceSq; - int ricochets = 0; - Location lastVisiblePoint = null; - UUID hitEntityId = null; - - for (int i = 0; i < segments; i++) { - Vector step = velocity.clone(); - double stepLength = step.length(); - if (stepLength <= EPSILON) { - return hitEntityId; - } + break; + } - Vector stepDirection = step.clone().normalize(); - Location from = current.clone(); - RayTraceResult entityHit = p.getWorld().rayTraceEntities(current, stepDirection, stepLength, entity -> isValidPreviewTarget(p, entity)); - RayTraceResult hit = p.getWorld().rayTraceBlocks(current, stepDirection, stepLength, FluidCollisionMode.NEVER, true); - if (isEntityFirstHit(current, hit, entityHit)) { - Entity target = entityHit.getHitEntity(); - if (target != null) { - hitEntityId = target.getUniqueId(); - } - - if (entityHit.getHitPosition() != null) { - current = entityHit.getHitPosition().toLocation(p.getWorld()); - } else if (target != null) { - current = target.getLocation().clone(); - } - - if (areParticlesEnabled() && eye.distanceSquared(current) >= minDistanceSq) { - float impactSize = getScaledParticleSize(eye.distance(current), 1.15D); - Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), impactSize); - p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount + 1), 0.02, 0.02, 0.02, 0.0, impact); - } - break; - } + current.add(step); - if (hit != null && hit.getHitBlock() != null) { - current = hit.getHitPosition().toLocation(p.getWorld()); - if (areParticlesEnabled()) { - if (eye.distanceSquared(current) >= minDistanceSq) { - float impactSize = getScaledParticleSize(eye.distance(current), 1.1D); - Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), impactSize); - p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount), 0.02, 0.02, 0.02, 0.0, impact); - } - } - - RicochetPreview ricochet = shot.ricochetPreview(); - if (canRicochet(ricochet, ricochets, velocity)) { - BlockFace hitFace = hit.getHitBlockFace() == null ? resolveHitFace(velocity) : hit.getHitBlockFace(); - if (hitFace == null) { - break; - } - - Vector reflectedDir = reflect(velocity.clone().normalize(), hitFace); - if (reflectedDir.lengthSquared() <= EPSILON) { - break; - } - - reflectedDir.normalize(); - double nextSpeed = Math.max(ricochet.minimumPostBounceSpeed(), velocity.length()) * (1D + ricochet.speedBonusPerRicochet()); - velocity = reflectedDir.clone().multiply(nextSpeed); - current.add(hitFace.getDirection().normalize().multiply(ricochet.spawnOffsetFromSurface())) - .add(reflectedDir.clone().multiply(ricochet.spawnOffsetAlongDirection())); - ricochets++; - if (areParticlesEnabled()) { - if (eye.distanceSquared(current) >= minDistanceSq) { - float bounceSize = getScaledParticleSize(eye.distance(current), 1.15D); - Particle.DustOptions bounce = new Particle.DustOptions(Color.fromRGB(170, 200, 255), bounceSize); - p.spawnParticle(Particle.DUST, current, Math.max(1, getConfig().impactParticleCount), 0.02, 0.02, 0.02, 0.0, bounce); - } - } - continue; - } - - break; - } + if (i % every == 0) { + if (areParticlesEnabled()) { + if (eye.distanceSquared(current) >= minDistanceSq) { + Location delta = current.clone().subtract(from); + int subSteps = Math.max(1, getConfig().trailSubSteps); + for (int s = 1; s <= subSteps; s++) { + double f = (double) s / (double) subSteps; + Location point = from.clone().add(delta.clone().multiply(f)); + if (eye.distanceSquared(point) < minDistanceSq) { + continue; + } - current.add(step); - - if (i % every == 0) { - if (areParticlesEnabled()) { - if (eye.distanceSquared(current) >= minDistanceSq) { - Location delta = current.clone().subtract(from); - int subSteps = Math.max(1, getConfig().trailSubSteps); - for (int s = 1; s <= subSteps; s++) { - double f = (double) s / (double) subSteps; - Location point = from.clone().add(delta.clone().multiply(f)); - if (eye.distanceSquared(point) < minDistanceSq) { - continue; - } - - float trailSize = getScaledParticleSize(eye.distance(point), 1D); - Particle.DustOptions trail = new Particle.DustOptions(trailColor, trailSize); - p.spawnParticle(Particle.DUST, point, Math.max(1, getConfig().trailParticleCount), 0.0, 0.0, 0.0, 0.0, trail); - lastVisiblePoint = point; - } - } - } + float trailSize = getScaledParticleSize(eye.distance(point), 1D); + Particle.DustOptions trail = new Particle.DustOptions(trailColor, trailSize); + p.spawnParticle(Particle.DUST, point, Math.max(1, getConfig().trailParticleCount), 0.0, 0.0, 0.0, 0.0, trail); + lastVisiblePoint = point; } - - velocity.multiply(shot.profile().dragFactor()); - velocity.setY(velocity.getY() - shot.profile().gravityStep()); + } } + } - if (areParticlesEnabled() && lastVisiblePoint != null) { - float tipSize = getScaledParticleSize(eye.distance(lastVisiblePoint), 1.2D); - Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), tipSize); - p.spawnParticle(Particle.DUST, lastVisiblePoint, 1, 0.0, 0.0, 0.0, 0.0, impact); - } - return hitEntityId; + velocity.multiply(shot.profile().dragFactor()); + velocity.setY(velocity.getY() - shot.profile().gravityStep()); } - private BallisticsProfile resolveBallisticsProfile(Material type) { - if (type == Material.SNOWBALL || type == Material.EGG || type == Material.ENDER_PEARL) { - return new BallisticsProfile(getConfig().lightProjectileDragFactor, getConfig().lightProjectileGravityStep); - } - - if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { - return new BallisticsProfile(getConfig().heavyProjectileDragFactor, getConfig().heavyProjectileGravityStep); - } - - return new BallisticsProfile(getConfig().dragFactor, getConfig().gravityStep); + if (areParticlesEnabled() && lastVisiblePoint != null) { + float tipSize = getScaledParticleSize(eye.distance(lastVisiblePoint), 1.2D); + Particle.DustOptions impact = new Particle.DustOptions(Color.fromRGB(255, 236, 128), tipSize); + p.spawnParticle(Particle.DUST, lastVisiblePoint, 1, 0.0, 0.0, 0.0, 0.0, impact); } + return hitEntityId; + } - private Vector applyLaunchDirectionTuning(Material type, Vector direction) { - if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { - direction.setY(direction.getY() - getConfig().heavyProjectilePitchDrop); - } - - return direction; + private BallisticsProfile resolveBallisticsProfile(Material type) { + if (type == Material.SNOWBALL || type == Material.EGG || type == Material.ENDER_PEARL) { + return new BallisticsProfile(getConfig().lightProjectileDragFactor, getConfig().lightProjectileGravityStep); } - private double getRangedForceLaunchMultiplier(Player p) { - RangedForce force = getRangedForceAdaptation(); - if (force == null || !force.isEnabled() || !force.getSkill().isEnabled()) { - return 1D; - } - - int level = getAdaptationLevel(p, force.getName()); - if (level <= 0) { - return 1D; - } - - double levelPercent = Math.min(1D, Math.max(0D, (double) level / (double) Math.max(1, force.getMaxLevel()))); - double speedBonus = levelPercent * force.getConfig().speedFactor; - return Math.max(0.1D, 1D + speedBonus); + if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { + return new BallisticsProfile(getConfig().heavyProjectileDragFactor, getConfig().heavyProjectileGravityStep); } - private RicochetPreview getRicochetPreview(Player p) { - RangedRicochetBolt ricochet = getRicochetAdaptation(); - if (ricochet == null || !ricochet.isEnabled() || !ricochet.getSkill().isEnabled()) { - return RicochetPreview.disabled(); - } - - int level = getAdaptationLevel(p, ricochet.getName()); - if (level <= 0) { - return RicochetPreview.disabled(); - } + return new BallisticsProfile(getConfig().dragFactor, getConfig().gravityStep); + } - RangedRicochetBolt.Config cfg = ricochet.getConfig(); - double levelPercent = Math.min(1D, Math.max(0D, (double) level / (double) Math.max(1, ricochet.getMaxLevel()))); - int maxRicochets = Math.max(1, (int) Math.round(cfg.maxRicochetsBase + (levelPercent * cfg.maxRicochetsFactor))); - double speedBonus = Math.min(cfg.maxSpeedBonusPerRicochet, - cfg.speedBonusPerRicochetBase + (levelPercent * cfg.speedBonusPerRicochetFactor)); - return new RicochetPreview( - true, - maxRicochets, - speedBonus, - cfg.minRicochetVelocitySquared, - cfg.minimumPostBounceSpeed, - cfg.spawnOffsetFromSurface, - cfg.spawnOffsetAlongDirection, - cfg.applyToAllProjectiles - ); - } - - private int getAdaptationLevel(Player p, String adaptationId) { - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("ranged"); - return line == null ? 0 : line.getAdaptationLevel(adaptationId); - } - - private RangedForce getRangedForceAdaptation() { - RangedForce cached = cachedRangedForce; - if (cached != null) { - return cached; - } - - RangedForce found = resolveRangedAdaptation(RangedForce.class); - if (found != null) { - cachedRangedForce = found; - } - return found; + private Vector applyLaunchDirectionTuning(Material type, Vector direction) { + if (type == Material.SPLASH_POTION || type == Material.LINGERING_POTION || type == Material.EXPERIENCE_BOTTLE) { + direction.setY(direction.getY() - getConfig().heavyProjectilePitchDrop); } - private RangedRicochetBolt getRicochetAdaptation() { - RangedRicochetBolt cached = cachedRicochetBolt; - if (cached != null) { - return cached; - } + return direction; + } - RangedRicochetBolt found = resolveRangedAdaptation(RangedRicochetBolt.class); - if (found != null) { - cachedRicochetBolt = found; - } - return found; + private double getRangedForceLaunchMultiplier(Player p) { + RangedForce force = getRangedForceAdaptation(); + if (force == null || !force.isEnabled() || !force.getSkill().isEnabled()) { + return 1D; } - private T resolveRangedAdaptation(Class type) { - Skill ranged = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill("ranged"); - if (ranged == null) { - return null; - } + int level = getAdaptationLevel(p, force.getName()); + if (level <= 0) { + return 1D; + } - for (Adaptation adaptation : ranged.getAdaptations()) { - if (type.isInstance(adaptation)) { - return type.cast(adaptation); - } - } + double levelPercent = Math.min(1D, Math.max(0D, (double) level / (double) Math.max(1, force.getMaxLevel()))); + double speedBonus = levelPercent * force.getConfig().speedFactor; + return Math.max(0.1D, 1D + speedBonus); + } - return null; + private RicochetPreview getRicochetPreview(Player p) { + RangedRicochetBolt ricochet = getRicochetAdaptation(); + if (ricochet == null || !ricochet.isEnabled() || !ricochet.getSkill().isEnabled()) { + return RicochetPreview.disabled(); } - private boolean canRicochet(RicochetPreview ricochet, int ricochetCount, Vector incomingVelocity) { - return ricochet.enabled() - && ricochetCount < ricochet.maxRicochets() - && incomingVelocity.lengthSquared() >= ricochet.minRicochetVelocitySquared(); + int level = getAdaptationLevel(p, ricochet.getName()); + if (level <= 0) { + return RicochetPreview.disabled(); } - private float getScaledParticleSize(double distance, double multiplier) { - double size = getConfig().particleSize + (Math.max(0D, distance) * getConfig().particleSizePerBlock); - size *= multiplier; - size = Math.max(0.05D, Math.min(getConfig().maxParticleSize, size)); - return (float) size; + RangedRicochetBolt.Config cfg = ricochet.getConfig(); + double levelPercent = Math.min(1D, Math.max(0D, (double) level / (double) Math.max(1, ricochet.getMaxLevel()))); + int maxRicochets = Math.max(1, (int) Math.round(cfg.maxRicochetsBase + (levelPercent * cfg.maxRicochetsFactor))); + double speedBonus = Math.min(cfg.maxSpeedBonusPerRicochet, + cfg.speedBonusPerRicochetBase + (levelPercent * cfg.speedBonusPerRicochetFactor)); + return new RicochetPreview( + true, + maxRicochets, + speedBonus, + cfg.minRicochetVelocitySquared, + cfg.minimumPostBounceSpeed, + cfg.spawnOffsetFromSurface, + cfg.spawnOffsetAlongDirection, + cfg.applyToAllProjectiles + ); + } + + private int getAdaptationLevel(Player p, String adaptationId) { + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("ranged"); + return line == null ? 0 : line.getAdaptationLevel(adaptationId); + } + + private RangedForce getRangedForceAdaptation() { + RangedForce cached = cachedRangedForce; + if (cached != null) { + return cached; } - private boolean isEntityFirstHit(Location from, RayTraceResult blockHit, RayTraceResult entityHit) { - if (entityHit == null || entityHit.getHitEntity() == null) { - return false; - } - - if (blockHit == null || blockHit.getHitPosition() == null) { - return true; - } - - if (entityHit.getHitPosition() == null) { - return false; - } - - double blockDistSq = blockHit.getHitPosition().distanceSquared(from.toVector()); - double entityDistSq = entityHit.getHitPosition().distanceSquared(from.toVector()); - return entityDistSq <= blockDistSq; + RangedForce found = resolveRangedAdaptation(RangedForce.class); + if (found != null) { + cachedRangedForce = found; } + return found; + } - private boolean isValidPreviewTarget(Player shooter, Entity entity) { - return entity instanceof LivingEntity - && entity.isValid() - && !entity.isDead() - && entity.getUniqueId() != shooter.getUniqueId(); + private RangedRicochetBolt getRicochetAdaptation() { + RangedRicochetBolt cached = cachedRicochetBolt; + if (cached != null) { + return cached; } - private void updatePreviewGlow(Player p, UUID targetId) { - if (!getConfig().glowPredictedTarget) { - clearPreviewGlow(p); - return; - } - - UUID viewerId = p.getUniqueId(); - UUID current = previewGlowTargets.get(viewerId); - if (current != null && current.equals(targetId)) { - return; - } + RangedRicochetBolt found = resolveRangedAdaptation(RangedRicochetBolt.class); + if (found != null) { + cachedRicochetBolt = found; + } + return found; + } - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - if (glowingEntities == null) { - return; - } + private T resolveRangedAdaptation(Class type) { + Skill ranged = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill("ranged"); + if (ranged == null) { + return null; + } - if (current != null) { - Entity stale = Bukkit.getEntity(current); - if (stale != null) { - try { - glowingEntities.unsetGlowing(stale, p); - } catch (ReflectiveOperationException ignored) { - // Ignore and continue; preview should never hard-fail from packet glow. - } - } - previewGlowTargets.remove(viewerId); - } + for (Adaptation adaptation : ranged.getAdaptations()) { + if (type.isInstance(adaptation)) { + return type.cast(adaptation); + } + } - if (targetId == null) { - return; - } + return null; + } + + private boolean canRicochet(RicochetPreview ricochet, int ricochetCount, Vector incomingVelocity) { + return ricochet.enabled() + && ricochetCount < ricochet.maxRicochets() + && incomingVelocity.lengthSquared() >= ricochet.minRicochetVelocitySquared(); + } + + private float getScaledParticleSize(double distance, double multiplier) { + double size = getConfig().particleSize + (Math.max(0D, distance) * getConfig().particleSizePerBlock); + size *= multiplier; + size = Math.max(0.05D, Math.min(getConfig().maxParticleSize, size)); + return (float) size; + } + + private boolean isEntityFirstHit(Location from, RayTraceResult blockHit, RayTraceResult entityHit) { + if (entityHit == null || entityHit.getHitEntity() == null) { + return false; + } - Entity target = Bukkit.getEntity(targetId); - if (target == null || !target.isValid()) { - return; - } + if (blockHit == null || blockHit.getHitPosition() == null) { + return true; + } - try { - glowingEntities.setGlowing(target, p, ChatColor.GOLD); - previewGlowTargets.put(viewerId, targetId); - } catch (ReflectiveOperationException ignored) { - // Ignore and continue; preview should never hard-fail from packet glow. - } + if (entityHit.getHitPosition() == null) { + return false; } - private void clearPreviewGlow(Player p) { - UUID viewerId = p.getUniqueId(); - UUID targetId = previewGlowTargets.remove(viewerId); - if (targetId == null) { - return; - } + double blockDistSq = blockHit.getHitPosition().distanceSquared(from.toVector()); + double entityDistSq = entityHit.getHitPosition().distanceSquared(from.toVector()); + return entityDistSq <= blockDistSq; + } + + private boolean isValidPreviewTarget(Player shooter, Entity entity) { + return entity instanceof LivingEntity + && entity.isValid() + && !entity.isDead() + && entity.getUniqueId() != shooter.getUniqueId(); + } + + private void updatePreviewGlow(Player p, UUID targetId) { + if (!getConfig().glowPredictedTarget) { + clearPreviewGlow(p); + return; + } - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - if (glowingEntities == null) { - return; - } + UUID viewerId = p.getUniqueId(); + UUID current = previewGlowTargets.get(viewerId); + if (current != null && current.equals(targetId)) { + return; + } - Entity entity = Bukkit.getEntity(targetId); - if (entity == null) { - return; - } + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + if (glowingEntities == null) { + return; + } + if (current != null) { + Entity stale = Bukkit.getEntity(current); + if (stale != null) { try { - glowingEntities.unsetGlowing(entity, p); + glowingEntities.unsetGlowing(stale, p); } catch (ReflectiveOperationException ignored) { - // Ignore and continue; preview should never hard-fail from packet glow. + // Ignore and continue; preview should never hard-fail from packet glow. } + } + previewGlowTargets.remove(viewerId); } - private boolean supportsRicochet(Material launchType, RicochetPreview ricochet) { - if (!ricochet.enabled()) { - return false; - } - - if (launchType == Material.BOW || launchType == Material.CROSSBOW || launchType == Material.TRIDENT) { - return true; - } - - return ricochet.applyToAllProjectiles() - && (launchType == Material.SNOWBALL - || launchType == Material.EGG - || launchType == Material.ENDER_PEARL - || launchType == Material.SPLASH_POTION - || launchType == Material.LINGERING_POTION - || launchType == Material.EXPERIENCE_BOTTLE); + if (targetId == null) { + return; } - private boolean isDrawingBow(Player p, ItemStack main) { - if (!isItem(main)) { - return false; - } - - Material type = main.getType(); - if (type != Material.BOW && type != Material.CROSSBOW) { - return false; - } - - if (!p.isHandRaised()) { - return false; - } - - ItemStack active = p.getItemInUse(); - return isItem(active) && active.getType() == type; + Entity target = Bukkit.getEntity(targetId); + if (target == null || !target.isValid()) { + return; } - private boolean isSneakProjectile(ItemStack item) { - if (!isItem(item)) { - return false; - } + try { + glowingEntities.setGlowing(target, p, ChatColor.GOLD); + previewGlowTargets.put(viewerId, targetId); + } catch (ReflectiveOperationException ignored) { + // Ignore and continue; preview should never hard-fail from packet glow. + } + } - Material type = item.getType(); - return type == Material.BOW - || type == Material.CROSSBOW - || type == Material.TRIDENT - || type == Material.SNOWBALL - || type == Material.EGG - || type == Material.ENDER_PEARL - || type == Material.SPLASH_POTION - || type == Material.LINGERING_POTION - || type == Material.EXPERIENCE_BOTTLE; + private void clearPreviewGlow(Player p) { + UUID viewerId = p.getUniqueId(); + UUID targetId = previewGlowTargets.remove(viewerId); + if (targetId == null) { + return; } - private Vector reflect(Vector incoming, BlockFace face) { - Vector normal = face.getDirection().normalize(); - double dot = incoming.dot(normal); - return incoming.clone().subtract(normal.multiply(2D * dot)); + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + if (glowingEntities == null) { + return; } - private BlockFace resolveHitFace(Vector incoming) { - double ax = Math.abs(incoming.getX()); - double ay = Math.abs(incoming.getY()); - double az = Math.abs(incoming.getZ()); + Entity entity = Bukkit.getEntity(targetId); + if (entity == null) { + return; + } - if (ay >= ax && ay >= az) { - return incoming.getY() > 0 ? BlockFace.DOWN : BlockFace.UP; - } + try { + glowingEntities.unsetGlowing(entity, p); + } catch (ReflectiveOperationException ignored) { + // Ignore and continue; preview should never hard-fail from packet glow. + } + } - if (ax >= az) { - return incoming.getX() > 0 ? BlockFace.WEST : BlockFace.EAST; - } + private boolean supportsRicochet(Material launchType, RicochetPreview ricochet) { + if (!ricochet.enabled()) { + return false; + } - return incoming.getZ() > 0 ? BlockFace.NORTH : BlockFace.SOUTH; + if (launchType == Material.BOW || launchType == Material.CROSSBOW || launchType == Material.TRIDENT) { + return true; } - private record PreviewContext(ItemStack item, PreviewTrigger trigger) { + return ricochet.applyToAllProjectiles() + && (launchType == Material.SNOWBALL + || launchType == Material.EGG + || launchType == Material.ENDER_PEARL + || launchType == Material.SPLASH_POTION + || launchType == Material.LINGERING_POTION + || launchType == Material.EXPERIENCE_BOTTLE); + } + + private boolean isDrawingBow(Player p, ItemStack main) { + if (!isItem(main)) { + return false; } - private enum PreviewTrigger { - SNEAK_PROJECTILE, - DRAWING_BOW + Material type = main.getType(); + if (type != Material.BOW && type != Material.CROSSBOW) { + return false; } - private record BallisticsProfile(double dragFactor, double gravityStep) { + if (!p.isHandRaised()) { + return false; } - private record ShotPreview(Vector initialVelocity, RicochetPreview ricochetPreview, BallisticsProfile profile, PreviewTrigger trigger) { + ItemStack active = p.getItemInUse(); + return isItem(active) && active.getType() == type; + } + + private boolean isSneakProjectile(ItemStack item) { + if (!isItem(item)) { + return false; } - private record RicochetPreview(boolean enabled, int maxRicochets, double speedBonusPerRicochet, - double minRicochetVelocitySquared, double minimumPostBounceSpeed, - double spawnOffsetFromSurface, double spawnOffsetAlongDirection, - boolean applyToAllProjectiles) { - private static RicochetPreview disabled() { - return new RicochetPreview(false, 0, 0D, Double.MAX_VALUE, 0D, 0D, 0D, false); - } + Material type = item.getType(); + return type == Material.BOW + || type == Material.CROSSBOW + || type == Material.TRIDENT + || type == Material.SNOWBALL + || type == Material.EGG + || type == Material.ENDER_PEARL + || type == Material.SPLASH_POTION + || type == Material.LINGERING_POTION + || type == Material.EXPERIENCE_BOTTLE; + } + + private Vector reflect(Vector incoming, BlockFace face) { + Vector normal = face.getDirection().normalize(); + double dot = incoming.dot(normal); + return incoming.clone().subtract(normal.multiply(2D * dot)); + } + + private BlockFace resolveHitFace(Vector incoming) { + double ax = Math.abs(incoming.getX()); + double ay = Math.abs(incoming.getY()); + double az = Math.abs(incoming.getZ()); + + if (ay >= ax && ay >= az) { + return incoming.getY() > 0 ? BlockFace.DOWN : BlockFace.UP; } - private record PreviewState( - long renderedAtMs, - int level, - Material material, - PreviewTrigger trigger, - double x, - double y, - double z, - float yaw, - float pitch - ) { - private static PreviewState capture(long renderedAtMs, int level, PreviewContext context, Location eye) { - return new PreviewState( - renderedAtMs, - level, - context.item().getType(), - context.trigger(), - eye.getX(), - eye.getY(), - eye.getZ(), - eye.getYaw(), - eye.getPitch() - ); - } + if (ax >= az) { + return incoming.getX() > 0 ? BlockFace.WEST : BlockFace.EAST; } - private int getSegments(int level) { - return Math.max(10, (int) Math.round(getConfig().segmentsBase + (getLevelPercent(level) * getConfig().segmentsFactor))); - } - - private double getVelocityMultiplier(int level) { - return Math.max(0.1, getConfig().velocityBase + (getLevelPercent(level) * getConfig().velocityFactor)); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Preview ranged projectile flight while sneaking or drawing your shot.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double segmentsBase = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double segmentsFactor = 26; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double velocityBase = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double velocityFactor = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gravity Step for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double gravityStep = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Step Scale for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double stepScale = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drag Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dragFactor = 0.99; - @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values keep thrown arcs flatter and longer; lower values make them lose speed faster.") - double lightProjectileDragFactor = 0.99; - @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values keep heavier throws moving faster; lower values shorten their travel.") - double heavyProjectileDragFactor = 0.99; - @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values produce steeper arcs for light projectiles.") - double lightProjectileGravityStep = 0.03; - @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values cause potions and bottles to drop faster.") - double heavyProjectileGravityStep = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crossbow Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double crossbowVelocity = 3.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for trident previews while sneaking.", impact = "Higher values extend trident prediction distance.") - double tridentVelocity = 2.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for light thrown projectile previews.", impact = "Higher values extend snowball, egg, and pearl prediction distance.") - double thrownProjectileVelocity = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for potion and experience bottle previews.", impact = "Higher values extend heavy throw prediction distance.") - double thrownPotionVelocity = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Additional downward launch offset for heavy thrown projectile previews.", impact = "Higher values tilt potion and bottle trajectories downward more strongly.") - double heavyProjectilePitchDrop = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fallbackVelocity = 1.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak Preview Charge Ticks for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sneakPreviewChargeTicks = 16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double particleSize = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "How much particle size grows per block of distance from the viewer.", impact = "Higher values make far trajectory points easier to see.") - double particleSizePerBlock = 0.008; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum particle size used for the trajectory preview.", impact = "Caps distance scaling to prevent oversized particles.") - double maxParticleSize = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int trailParticleCount = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Impact Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int impactParticleCount = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Every for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int trailParticleEvery = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum distance from the player's eye before preview particles are shown.", impact = "Higher values keep particles out of your sightline and reduce visual obstruction.") - double minPreviewDistanceFromEye = 1.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "How many interpolated points are drawn between each simulated physics step.", impact = "Higher values smooth the line while increasing particle density.") - int trailSubSteps = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Offset forward from the eye where trajectory simulation begins.", impact = "Higher values start the preview further from your face.") - double previewStartOffset = 0.55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Highlights the predicted hit target entity with per-player glow.", impact = "Enable to glow whichever entity the preview would hit first.") - boolean glowPredictedTarget = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between preview renders for a player when aim and context have not changed.", impact = "Lower values make previews smoother but increase CPU and ray-trace load.") - int previewRenderIntervalMillis = 75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between full-candidate refresh scans.", impact = "Lower values discover eligible preview players faster but increase baseline scan cost.") - int previewCandidateRefreshMillis = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Yaw delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small camera turns; higher values reduce recompute frequency.") - double previewYawDeltaDegrees = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Pitch delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small vertical aim changes; higher values reduce recompute frequency.") - double previewPitchDeltaDegrees = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Movement distance squared required to force a preview recompute before the normal render interval.", impact = "Lower values recompute more while strafing; higher values reduce recomputes while moving.") - double previewPositionDeltaSquared = 0.0125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Lowest number of simulation segments used when rendering a trajectory.", impact = "Higher values preserve long previews under load; lower values cut work more aggressively.") - int minimumRenderedSegments = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on simulation segments used for rendering.", impact = "Lower values reduce ray-trace workload; higher values produce longer and more precise previews.") - int maxRenderedSegments = 36; - @art.arcane.adapt.util.config.ConfigDoc(value = "Ticker load percentage at which trajectory segments are scaled down.", impact = "Lower values engage load-shedding sooner; higher values preserve preview fidelity longer.") - double previewHighLoadPercent = 42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Segment scale applied once high-load shedding is active.", impact = "Lower values reduce trajectory compute cost more aggressively during load spikes.") - double previewHighLoadSegmentScale = 0.7; + return incoming.getZ() > 0 ? BlockFace.NORTH : BlockFace.SOUTH; + } + + private int getSegments(int level) { + return Math.max(10, (int) Math.round(getConfig().segmentsBase + (getLevelPercent(level) * getConfig().segmentsFactor))); + } + + private double getVelocityMultiplier(int level) { + return Math.max(0.1, getConfig().velocityBase + (getLevelPercent(level) * getConfig().velocityFactor)); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private enum PreviewTrigger { + SNEAK_PROJECTILE, + DRAWING_BOW + } + + private record PreviewContext(ItemStack item, PreviewTrigger trigger) { + } + + private record BallisticsProfile(double dragFactor, double gravityStep) { + } + + private record ShotPreview(Vector initialVelocity, + RicochetPreview ricochetPreview, + BallisticsProfile profile, + PreviewTrigger trigger) { + } + + private record RicochetPreview(boolean enabled, int maxRicochets, + double speedBonusPerRicochet, + double minRicochetVelocitySquared, + double minimumPostBounceSpeed, + double spawnOffsetFromSurface, + double spawnOffsetAlongDirection, + boolean applyToAllProjectiles) { + private static RicochetPreview disabled() { + return new RicochetPreview(false, 0, 0D, Double.MAX_VALUE, 0D, 0D, 0D, false); + } + } + + private record PreviewState( + long renderedAtMs, + int level, + Material material, + PreviewTrigger trigger, + double x, + double y, + double z, + float yaw, + float pitch + ) { + private static PreviewState capture(long renderedAtMs, int level, PreviewContext context, Location eye) { + return new PreviewState( + renderedAtMs, + level, + context.item().getType(), + context.trigger(), + eye.getX(), + eye.getY(), + eye.getZ(), + eye.getYaw(), + eye.getPitch() + ); } + } + + @NoArgsConstructor + @ConfigDescription("Preview ranged projectile flight while sneaking or drawing your shot.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double segmentsBase = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segments Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double segmentsFactor = 26; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Base for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double velocityBase = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Velocity Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double velocityFactor = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gravity Step for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double gravityStep = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Step Scale for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double stepScale = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Drag Factor for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dragFactor = 0.99; + @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values keep thrown arcs flatter and longer; lower values make them lose speed faster.") + double lightProjectileDragFactor = 0.99; + @art.arcane.adapt.util.config.ConfigDoc(value = "Drag factor used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values keep heavier throws moving faster; lower values shorten their travel.") + double heavyProjectileDragFactor = 0.99; + @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for lighter thrown projectiles (snowballs, eggs, pearls).", impact = "Higher values produce steeper arcs for light projectiles.") + double lightProjectileGravityStep = 0.03; + @art.arcane.adapt.util.config.ConfigDoc(value = "Gravity step used for heavier thrown projectiles (potions, experience bottles).", impact = "Higher values cause potions and bottles to drop faster.") + double heavyProjectileGravityStep = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crossbow Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double crossbowVelocity = 3.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for trident previews while sneaking.", impact = "Higher values extend trident prediction distance.") + double tridentVelocity = 2.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for light thrown projectile previews.", impact = "Higher values extend snowball, egg, and pearl prediction distance.") + double thrownProjectileVelocity = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Launch velocity used for potion and experience bottle previews.", impact = "Higher values extend heavy throw prediction distance.") + double thrownPotionVelocity = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional downward launch offset for heavy thrown projectile previews.", impact = "Higher values tilt potion and bottle trajectories downward more strongly.") + double heavyProjectilePitchDrop = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fallback Velocity for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fallbackVelocity = 1.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak Preview Charge Ticks for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sneakPreviewChargeTicks = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double particleSize = 0.13; + @art.arcane.adapt.util.config.ConfigDoc(value = "How much particle size grows per block of distance from the viewer.", impact = "Higher values make far trajectory points easier to see.") + double particleSizePerBlock = 0.008; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum particle size used for the trajectory preview.", impact = "Caps distance scaling to prevent oversized particles.") + double maxParticleSize = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int trailParticleCount = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Impact Particle Count for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int impactParticleCount = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Particle Every for the Ranged Trajectory Sight adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int trailParticleEvery = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum distance from the player's eye before preview particles are shown.", impact = "Higher values keep particles out of your sightline and reduce visual obstruction.") + double minPreviewDistanceFromEye = 1.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "How many interpolated points are drawn between each simulated physics step.", impact = "Higher values smooth the line while increasing particle density.") + int trailSubSteps = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Offset forward from the eye where trajectory simulation begins.", impact = "Higher values start the preview further from your face.") + double previewStartOffset = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Highlights the predicted hit target entity with per-player glow.", impact = "Enable to glow whichever entity the preview would hit first.") + boolean glowPredictedTarget = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between preview renders for a player when aim and context have not changed.", impact = "Lower values make previews smoother but increase CPU and ray-trace load.") + int previewRenderIntervalMillis = 75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum milliseconds between full-candidate refresh scans.", impact = "Lower values discover eligible preview players faster but increase baseline scan cost.") + int previewCandidateRefreshMillis = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Yaw delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small camera turns; higher values reduce recompute frequency.") + double previewYawDeltaDegrees = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Pitch delta in degrees required to force a preview recompute before the normal render interval.", impact = "Lower values react to small vertical aim changes; higher values reduce recompute frequency.") + double previewPitchDeltaDegrees = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Movement distance squared required to force a preview recompute before the normal render interval.", impact = "Lower values recompute more while strafing; higher values reduce recomputes while moving.") + double previewPositionDeltaSquared = 0.0125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Lowest number of simulation segments used when rendering a trajectory.", impact = "Higher values preserve long previews under load; lower values cut work more aggressively.") + int minimumRenderedSegments = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on simulation segments used for rendering.", impact = "Lower values reduce ray-trace workload; higher values produce longer and more precise previews.") + int maxRenderedSegments = 36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Ticker load percentage at which trajectory segments are scaled down.", impact = "Lower values engage load-shedding sooner; higher values preserve preview fidelity longer.") + double previewHighLoadPercent = 42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Segment scale applied once high-load shedding is active.", impact = "Lower values reduce trajectory compute cost more aggressively during load spikes.") + double previewHighLoadSegmentScale = 0.7; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java index fa5f819c7..c5bac17ac 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedWebBomb.java @@ -28,11 +28,11 @@ import art.arcane.adapt.content.item.BoundSnowBall; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -56,228 +56,228 @@ import java.util.concurrent.ConcurrentHashMap; public class RangedWebBomb extends SimpleAdaptation { - private static final BlockData AIR = Material.AIR.createBlockData(); - private static final BlockData BLOCK = Material.COBWEB.createBlockData(); - private final Map activeSnowballs; - private final Set activeBlocks; - - public RangedWebBomb() { - super("ranged-webshot"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("ranged.web_shot.description")); - setDisplayName(Localizer.dLocalize("ranged.web_shot.name")); - setIcon(Material.COBWEB); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(4900); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerRecipe(AdaptRecipe.shaped() - .key("ranged-web-bomb") - .ingredient(new MaterialChar('I', Material.COBWEB)) - .ingredient(new MaterialChar('S', Material.SNOWBALL)) - .shapes(List.of( - "III", - "ISI", - "III")) - .result(BoundSnowBall.io.withData(new BoundSnowBall.Data(null))) - .build()); - activeBlocks = ConcurrentHashMap.newKeySet(); - activeSnowballs = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COBWEB) - .key("challenge_ranged_web_200") - .title(Localizer.dLocalize("advancement.challenge_ranged_web_200.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_web_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_ranged_web_200", "ranged.web-bomb.mobs-trapped", 200, 300); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("ranged.web_shot.lore1")); - v.addLore(C.YELLOW + "+ " + level + C.GRAY + " " + Localizer.dLocalize("ranged.web_shot.lore2")); - } - - - @EventHandler - public void on(ProjectileHitEvent e) { - if (!(e.getEntity() instanceof Snowball snowball)) { - return; - } - UUID shooterId = activeSnowballs.remove(snowball.getUniqueId()); - if (shooterId == null) { - return; - } - Player p = Bukkit.getPlayer(shooterId); - if (p == null || !p.isOnline()) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - Block block; - - if (e.getHitEntity() != null) { - block = e.getHitEntity().getLocation().add(0, 1, 0).getBlock(); - } else if (e.getHitBlock() != null) { - block = e.getHitBlock().getLocation().add(0, 1, 0).getBlock(); - } else { - block = e.getEntity().getLocation().add(0, 1, 0).getBlock(); - } - - vfxCuboidOutline(block, Particle.REVERSE_PORTAL); - Adapt.verbose("Snowball Got: " + snowball.getEntityId() + " " + snowball.getUniqueId()); - Adapt.verbose("Detected snowball hit"); - if (e.getHitEntity() != null) { - getPlayer(p).getData().addStat("ranged.web-bomb.mobs-trapped", 1); - } - snowball.remove(); - Set locs = new HashSet<>(); - locs.add(block.getLocation().add(0, 1, 0).getBlock()); - locs.add(block.getLocation().add(0, -1, 0).getBlock()); - locs.add(block.getLocation().add(0, 0, 1).getBlock()); - locs.add(block.getLocation().add(0, 0, -1).getBlock()); - locs.add(block.getLocation().add(1, 0, 0).getBlock()); - locs.add(block.getLocation().add(-1, 0, 0).getBlock()); - - for (Block i : locs) { - addWebFoundation(i, level); - } + private static final BlockData AIR = Material.AIR.createBlockData(); + private static final BlockData BLOCK = Material.COBWEB.createBlockData(); + private final Map activeSnowballs; + private final Set activeBlocks; + + public RangedWebBomb() { + super("ranged-webshot"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("ranged.web_shot.description")); + setDisplayName(Localizer.dLocalize("ranged.web_shot.name")); + setIcon(Material.COBWEB); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(4900); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerRecipe(AdaptRecipe.shaped() + .key("ranged-web-bomb") + .ingredient(new MaterialChar('I', Material.COBWEB)) + .ingredient(new MaterialChar('S', Material.SNOWBALL)) + .shapes(List.of( + "III", + "ISI", + "III")) + .result(BoundSnowBall.io.withData(new BoundSnowBall.Data(null))) + .build()); + activeBlocks = ConcurrentHashMap.newKeySet(); + activeSnowballs = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COBWEB) + .key("challenge_ranged_web_200") + .title(Localizer.dLocalize("advancement.challenge_ranged_web_200.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_web_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_ranged_web_200", "ranged.web-bomb.mobs-trapped", 200, 300); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("ranged.web_shot.lore1")); + v.addLore(C.YELLOW + "+ " + level + C.GRAY + " " + Localizer.dLocalize("ranged.web_shot.lore2")); + } + + + @EventHandler + public void on(ProjectileHitEvent e) { + if (!(e.getEntity() instanceof Snowball snowball)) { + return; } - - - @EventHandler - public void on(ProjectileLaunchEvent e) { - if (e.getEntity().getShooter() instanceof Player p && e.getEntity() instanceof Snowball snowball && hasActiveAdaptation(p)) { - Adapt.verbose("Snowball Launched: " + snowball.getEntityId() + " " + snowball.getUniqueId()); - if (BoundSnowBall.isBindableItem(snowball.getItem())) { - Adapt.verbose("Snowball is bound"); - activeSnowballs.put(snowball.getUniqueId(), p.getUniqueId()); - } else { - Adapt.verbose("Snowball is not bound"); - } - } + UUID shooterId = activeSnowballs.remove(snowball.getUniqueId()); + if (shooterId == null) { + return; } - - public void addWebFoundation(Block block, int seconds) { - if (!block.getType().isAir()) { - return; - } - - J.runAt(block.getLocation(), () -> { - block.setBlockData(BLOCK); - activeBlocks.add(block); - }); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(block.getLocation(), Sound.BLOCK_ROOTED_DIRT_PLACE, 1.0f, 1.0f); - if (areParticlesEnabled()) { - - vfxCuboidOutline(block, Particle.CLOUD); - vfxCuboidOutline(block, Particle.WHITE_ASH); - } - J.runAt(block.getLocation(), () -> removeFoundation(block), seconds * 16); + Player p = Bukkit.getPlayer(shooterId); + if (p == null || !p.isOnline()) { + return; } - public void removeFoundation(Block block) { - if (!block.getBlockData().equals(BLOCK)) { - return; - } - - J.runAt(block.getLocation(), () -> { - block.setBlockData(AIR); - activeBlocks.remove(block); - }); - SoundPlayer spw = SoundPlayer.of(block.getWorld()); - spw.play(block.getLocation(), Sound.BLOCK_ROOTED_DIRT_BREAK, 1.0f, 1.0f); - if (areParticlesEnabled()) { - vfxCuboidOutline(block, Particles.ENCHANTMENT_TABLE); - } + int level = getActiveLevel(p); + if (level <= 0) { + return; } + Block block; - //prevent piston from moving blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockPistonExtendEvent e) { - e.getBlocks().forEach(b -> { - if (activeBlocks.contains(b)) { - Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); - e.setCancelled(true); - } - }); + if (e.getHitEntity() != null) { + block = e.getHitEntity().getLocation().add(0, 1, 0).getBlock(); + } else if (e.getHitBlock() != null) { + block = e.getHitBlock().getLocation().add(0, 1, 0).getBlock(); + } else { + block = e.getEntity().getLocation().add(0, 1, 0).getBlock(); } - //prevent piston from pulling blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockPistonRetractEvent e) { - e.getBlocks().forEach(b -> { - if (activeBlocks.contains(b)) { - Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); - e.setCancelled(true); - } - }); + vfxCuboidOutline(block, Particle.REVERSE_PORTAL); + Adapt.verbose("Snowball Got: " + snowball.getEntityId() + " " + snowball.getUniqueId()); + Adapt.verbose("Detected snowball hit"); + if (e.getHitEntity() != null) { + getPlayer(p).getData().addStat("ranged.web-bomb.mobs-trapped", 1); } - - //prevent TNT from destroying blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockExplodeEvent e) { - if (activeBlocks.contains(e.getBlock())) { - Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); - e.setCancelled(true); - } + snowball.remove(); + Set locs = new HashSet<>(); + locs.add(block.getLocation().add(0, 1, 0).getBlock()); + locs.add(block.getLocation().add(0, -1, 0).getBlock()); + locs.add(block.getLocation().add(0, 0, 1).getBlock()); + locs.add(block.getLocation().add(0, 0, -1).getBlock()); + locs.add(block.getLocation().add(1, 0, 0).getBlock()); + locs.add(block.getLocation().add(-1, 0, 0).getBlock()); + + for (Block i : locs) { + addWebFoundation(i, level); } - - //prevent block from being destroyed // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(BlockBreakEvent e) { - if (activeBlocks.contains(e.getBlock())) { - e.setCancelled(true); - } + } + + + @EventHandler + public void on(ProjectileLaunchEvent e) { + if (e.getEntity().getShooter() instanceof Player p && e.getEntity() instanceof Snowball snowball && hasActiveAdaptation(p)) { + Adapt.verbose("Snowball Launched: " + snowball.getEntityId() + " " + snowball.getUniqueId()); + if (BoundSnowBall.isBindableItem(snowball.getItem())) { + Adapt.verbose("Snowball is bound"); + activeSnowballs.put(snowball.getUniqueId(), p.getUniqueId()); + } else { + Adapt.verbose("Snowball is not bound"); + } } + } - //prevent Entities from destroying blocks // Dupe fix - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityExplodeEvent e) { - e.blockList().removeIf(activeBlocks::contains); + public void addWebFoundation(Block block, int seconds) { + if (!block.getType().isAir()) { + return; } + J.runAt(block.getLocation(), () -> { + block.setBlockData(BLOCK); + activeBlocks.add(block); + }); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_ROOTED_DIRT_PLACE, 1.0f, 1.0f); + if (areParticlesEnabled()) { - @Override - public void onTick() { - + vfxCuboidOutline(block, Particle.CLOUD); + vfxCuboidOutline(block, Particle.WHITE_ASH); } + J.runAt(block.getLocation(), () -> removeFoundation(block), seconds * 16); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + public void removeFoundation(Block block) { + if (!block.getBlockData().equals(BLOCK)) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + J.runAt(block.getLocation(), () -> { + block.setBlockData(AIR); + activeBlocks.remove(block); + }); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_ROOTED_DIRT_BREAK, 1.0f, 1.0f); + if (areParticlesEnabled()) { + vfxCuboidOutline(block, Particles.ENCHANTMENT_TABLE); + } + } + + + //prevent piston from moving blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockPistonExtendEvent e) { + e.getBlocks().forEach(b -> { + if (activeBlocks.contains(b)) { + Adapt.verbose("Cancelled Piston Extend on Adaptation Foundation Block"); + e.setCancelled(true); + } + }); + } + + //prevent piston from pulling blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockPistonRetractEvent e) { + e.getBlocks().forEach(b -> { + if (activeBlocks.contains(b)) { + Adapt.verbose("Cancelled Piston Retract on Adaptation Foundation Block"); + e.setCancelled(true); + } + }); + } + + //prevent TNT from destroying blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockExplodeEvent e) { + if (activeBlocks.contains(e.getBlock())) { + Adapt.verbose("Cancelled Block Explosion on Adaptation Foundation Block"); + e.setCancelled(true); } + } - @NoArgsConstructor - @ConfigDescription("Throw a crafted web snare to trap targets in cobwebs.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Web Bomb adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.9; + //prevent block from being destroyed // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockBreakEvent e) { + if (activeBlocks.contains(e.getBlock())) { + e.setCancelled(true); } + } + + //prevent Entities from destroying blocks // Dupe fix + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityExplodeEvent e) { + e.blockList().removeIf(activeBlocks::contains); + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Throw a crafted web snare to trap targets in cobwebs.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Ranged Web Bomb adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java index 8f2620ca7..4dfe8365a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java @@ -28,9 +28,9 @@ import art.arcane.adapt.content.item.BoundEnderPearl; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import manifold.rt.api.util.Pair; @@ -58,296 +58,289 @@ import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; public class RiftAccess extends SimpleAdaptation { - private final Map, List> activeViewsMap = new ConcurrentHashMap<>(); - private final Map tickets = new ConcurrentHashMap<>(); - - public RiftAccess() { - super("rift-access"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.remote_access.description")); - setDisplayName(Localizer.dLocalize("rift.remote_access.name")); - setMaxLevel(1); - setIcon(Material.NETHER_STAR); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setInitialCost(getConfig().initialCost); - setInterval(1000); - registerRecipe(AdaptRecipe.shapeless() - .key("rift-remote-access") - .ingredient(Material.ENDER_PEARL) - .ingredient(Material.COMPASS) - .result(BoundEnderPearl.io.withData(new BoundEnderPearl.Data(null))) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_rift_access_100") - .title(Localizer.dLocalize("advancement.challenge_rift_access_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_access_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_CHEST) - .key("challenge_rift_access_2500") - .title(Localizer.dLocalize("advancement.challenge_rift_access_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_access_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_access_100", "rift.access.remote-opens", 100, 300); - registerMilestone("challenge_rift_access_2500", "rift.access.remote-opens", 2500, 1000); + private final Map, List> activeViewsMap = new ConcurrentHashMap<>(); + private final Map tickets = new ConcurrentHashMap<>(); + + public RiftAccess() { + super("rift-access"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.remote_access.description")); + setDisplayName(Localizer.dLocalize("rift.remote_access.name")); + setMaxLevel(1); + setIcon(Material.NETHER_STAR); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setInitialCost(getConfig().initialCost); + setInterval(1000); + registerRecipe(AdaptRecipe.shapeless() + .key("rift-remote-access") + .ingredient(Material.ENDER_PEARL) + .ingredient(Material.COMPASS) + .result(BoundEnderPearl.io.withData(new BoundEnderPearl.Data(null))) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_rift_access_100") + .title(Localizer.dLocalize("advancement.challenge_rift_access_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_access_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_CHEST) + .key("challenge_rift_access_2500") + .title(Localizer.dLocalize("advancement.challenge_rift_access_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_access_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_access_100", "rift.access.remote-opens", 100, 300); + registerMilestone("challenge_rift_access_2500", "rift.access.remote-opens", 2500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore1")); + v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore3")); + } + + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack mainHand = p.getInventory().getItemInMainHand(); + ItemStack offHand = p.getInventory().getItemInOffHand(); + Block block = e.getClickedBlock(); + + boolean mainHandBound = BoundEnderPearl.isBindableItem(mainHand); + boolean offHandBound = BoundEnderPearl.isBindableItem(offHand); + + // Cancel event if the enderpearl is in the offhand + if (offHandBound && e.getHand() != null && e.getHand().equals(EquipmentSlot.OFF_HAND)) { + e.setCancelled(true); + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore1")); - v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("rift.remote_access.lore3")); + // If the main hand is holding a bound enderpearl + if (mainHandBound) { + e.setCancelled(true); + if (hasActiveAdaptation(p)) { + Adapt.verbose("Player using bound enderpearl."); + handleEnderPearlInteraction(e, p, block); + } } + } + private void handleEnderPearlInteraction(PlayerInteractEvent event, Player player, Block block) { + boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; + boolean isCreative = player.getGameMode() == GameMode.CREATIVE; + boolean sneaking = player.isSneaking(); + boolean allowed = canUseInCreative || !isCreative; - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - ItemStack mainHand = p.getInventory().getItemInMainHand(); - ItemStack offHand = p.getInventory().getItemInOffHand(); - Block block = e.getClickedBlock(); - - boolean mainHandBound = BoundEnderPearl.isBindableItem(mainHand); - boolean offHandBound = BoundEnderPearl.isBindableItem(offHand); - // Cancel event if the enderpearl is in the offhand - if (offHandBound && e.getHand() != null && e.getHand().equals(EquipmentSlot.OFF_HAND)) { - e.setCancelled(true); - return; - } - - // If the main hand is holding a bound enderpearl - if (mainHandBound) { - e.setCancelled(true); - if (hasActiveAdaptation(p)) { - Adapt.verbose("Player using bound enderpearl."); - handleEnderPearlInteraction(e, p, block); - } - } + // Check if the player is allowed to use the bound item in creative + if (!allowed) { + Adapt.info("Player " + player.getName() + " tried to use the bound item in creative mode."); + return; } - private void handleEnderPearlInteraction(PlayerInteractEvent event, Player player, Block block) { - boolean canUseInCreative = AdaptConfig.get().allowAdaptationsInCreative; - boolean isCreative = player.getGameMode() == GameMode.CREATIVE; - boolean sneaking = player.isSneaking(); - boolean allowed = canUseInCreative || !isCreative; - - - // Check if the player is allowed to use the bound item in creative - if (!allowed) { - Adapt.info("Player " + player.getName() + " tried to use the bound item in creative mode."); - return; - } - - switch (event.getAction()) { - case LEFT_CLICK_BLOCK -> { - // If player is sneaking and left-clicking a container - if (sneaking && isStorage(block.getBlockData())) { - if (canAccessChest(player, block.getLocation())) { - linkPearl(player, block, event); - } else { - Adapt.verbose("Player " + player.getName() + " doesn't have permission."); - } - } - } - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> - // If player right-clicks on air or any block - openPearl(player); - default -> { - } - } - } - - private void linkPearl(Player p, Block block, PlayerInteractEvent event) { - event.setCancelled(true); - if (areParticlesEnabled()) { - vfxCuboidOutline(block, Particle.REVERSE_PORTAL); - } - ItemStack hand = p.getInventory().getItemInMainHand(); - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_CLOSE, 0.5f, 0.8f); - - if (hand.getAmount() == 1) { - BoundEnderPearl.setData(hand, block); + Action action = event.getAction(); + if (action == Action.LEFT_CLICK_BLOCK) { + if (sneaking && isStorage(block.getBlockData())) { + if (canAccessChest(player, block.getLocation())) { + linkPearl(player, block, event); } else { - hand.setAmount(hand.getAmount() - 1); - ItemStack pearl = BoundEnderPearl.withData(block); - p.getInventory().addItem(pearl).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); + Adapt.verbose("Player " + player.getName() + " doesn't have permission."); } + } + } else if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { + openPearl(player); } + } - private void openPearl(Player p) { - SoundPlayer sp = SoundPlayer.of(p); - Block b = BoundEnderPearl.getBlock(p.getInventory().getItemInMainHand()); - if (b == null || !canAccessChest(p, b.getLocation())) { - sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f); - return; - } - loadChunkAsync(b.getLocation(), chunk -> { - if (Bukkit.getPluginManager().isPluginEnabled("AdvancedChests") && - AdvancedChestsAPI.getChestManager().getAdvancedChest(b.getLocation()) != null) { - AdvancedChestsAPI.getChestManager().getAdvancedChest(b.getLocation()).openPage(p, 1); - Adapt.verbose("Opening AdvancedChests GUI"); - } else if (b.getState() instanceof InventoryHolder holder) { - InventoryView view = p.openInventory(holder.getInventory()); - if (view == null) return; - activeViewsMap.computeIfAbsent(Pair.make(new ChunkPos(chunk).add(), b.getLocation()), k -> new ArrayList<>()).add(view); - } - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - getPlayer(p).getData().addStat("rift.access.remote-opens", 1); - }); + private void linkPearl(Player p, Block block, PlayerInteractEvent event) { + event.setCancelled(true); + if (areParticlesEnabled()) { + vfxCuboidOutline(block, Particle.REVERSE_PORTAL); } - - @Override - public void onTick() { - checkActiveViews(); + ItemStack hand = p.getInventory().getItemInMainHand(); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_CLOSE, 0.5f, 0.8f); + + if (hand.getAmount() == 1) { + BoundEnderPearl.setData(hand, block); + } else { + hand.setAmount(hand.getAmount() - 1); + ItemStack pearl = BoundEnderPearl.withData(block); + p.getInventory().addItem(pearl).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); } - - private void checkActiveViews() { - Iterator, List>> mapIterator = activeViewsMap.entrySet().iterator(); - while (mapIterator.hasNext()) { - Map.Entry, List> entry = mapIterator.next(); - removeInvalidViews(entry); - removeEntryIfViewsEmpty(mapIterator, entry); - } + } + + private void openPearl(Player p) { + SoundPlayer sp = SoundPlayer.of(p); + Block b = BoundEnderPearl.getBlock(p.getInventory().getItemInMainHand()); + if (b == null || !canAccessChest(p, b.getLocation())) { + sp.play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1f, 1f); + return; } - - private void removeInvalidViews(Map.Entry, List> entry) { - List views = entry.getValue(); - for (int ii = views.size() - 1; ii >= 0; ii--) { - InventoryView i = views.get(ii); - if (shouldRemoveView(i)) { - views.remove(ii); - } - } + loadChunkAsync(b.getLocation(), chunk -> { + if (Bukkit.getPluginManager().isPluginEnabled("AdvancedChests") && + AdvancedChestsAPI.getChestManager().getAdvancedChest(b.getLocation()) != null) { + AdvancedChestsAPI.getChestManager().getAdvancedChest(b.getLocation()).openPage(p, 1); + Adapt.verbose("Opening AdvancedChests GUI"); + } else if (b.getState() instanceof InventoryHolder holder) { + InventoryView view = p.openInventory(holder.getInventory()); + if (view == null) return; + activeViewsMap.computeIfAbsent(Pair.make(new ChunkPos(chunk).add(), b.getLocation()), k -> new ArrayList<>()).add(view); + } + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + getPlayer(p).getData().addStat("rift.access.remote-opens", 1); + }); + } + + @Override + public void onTick() { + checkActiveViews(); + } + + private void checkActiveViews() { + Iterator, List>> mapIterator = activeViewsMap.entrySet().iterator(); + while (mapIterator.hasNext()) { + Map.Entry, List> entry = mapIterator.next(); + removeInvalidViews(entry); + removeEntryIfViewsEmpty(mapIterator, entry); } - - private boolean shouldRemoveView(InventoryView i) { - Location location = i.getTopInventory().getLocation(); - return !i.getPlayer().getOpenInventory().equals(i) || (location == null || !isStorage(location.getBlock().getBlockData())); + } + + private void removeInvalidViews(Map.Entry, List> entry) { + List views = entry.getValue(); + for (int ii = views.size() - 1; ii >= 0; ii--) { + InventoryView i = views.get(ii); + if (shouldRemoveView(i)) { + views.remove(ii); + } } - - private void removeEntryIfViewsEmpty(Iterator, List>> mapIterator, Map.Entry, List> entry) { - List views = entry.getValue(); - if (views.isEmpty()) { - mapIterator.remove(); - entry.getKey().getFirst().remove(); - } - } - - - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBurnEvent event) { - invClose(event.getBlock()); + } + + private boolean shouldRemoveView(InventoryView i) { + Location location = i.getTopInventory().getLocation(); + return !i.getPlayer().getOpenInventory().equals(i) || (location == null || !isStorage(location.getBlock().getBlockData())); + } + + private void removeEntryIfViewsEmpty(Iterator, List>> mapIterator, Map.Entry, List> entry) { + List views = entry.getValue(); + if (views.isEmpty()) { + mapIterator.remove(); + entry.getKey().getFirst().remove(); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockPistonRetractEvent event) { - for (Block b : event.getBlocks()) { - invClose(b); - } - } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockPistonExtendEvent event) { - for (Block b : event.getBlocks()) { - invClose(b); - } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBurnEvent event) { + invClose(event.getBlock()); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockExplodeEvent event) { - for (Block b : event.blockList()) { - invClose(b); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockPistonRetractEvent event) { + for (Block b : event.getBlocks()) { + invClose(b); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent event) { - invClose(event.getBlock()); + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockPistonExtendEvent event) { + for (Block b : event.getBlocks()) { + invClose(b); } + } - - private void invClose(Block block) { - List views = activeViewsMap.get(block.getLocation()); - if (views != null) { - for (InventoryView view : views) { - view.getPlayer().closeInventory(); - } - activeViewsMap.remove(block.getLocation()); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockExplodeEvent event) { + for (Block b : event.blockList()) { + invClose(b); } + } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent event) { + invClose(event.getBlock()); + } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + private void invClose(Block block) { + List views = activeViewsMap.get(block.getLocation()); + if (views != null) { + for (InventoryView view : views) { + view.getPlayer().closeInventory(); + } + activeViewsMap.remove(block.getLocation()); } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + } + + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft a Reliquary Portkey to access marked containers remotely.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Access adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 15; + } + + @EqualsAndHashCode + private class ChunkPos { + @EqualsAndHashCode.Exclude + private final WeakReference world; + private final String name; + private final int x, z; + + private ChunkPos(Chunk chunk) { + this.world = new WeakReference<>(chunk.getWorld()); + this.name = chunk.getWorld().getName(); + this.x = chunk.getX(); + this.z = chunk.getZ(); } - @NoArgsConstructor - @ConfigDescription("Craft a Reliquary Portkey to access marked containers remotely.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Access adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 15; + public ChunkPos add() { + World world = this.world.get(); + if (world == null) return this; + if (tickets.computeIfAbsent(this, k -> new AtomicInteger()).getAndIncrement() == 0) + world.addPluginChunkTicket(x, z, Adapt.instance); + return this; } - @EqualsAndHashCode - private class ChunkPos { - @EqualsAndHashCode.Exclude - private final WeakReference world; - private final String name; - private final int x, z; - - private ChunkPos(Chunk chunk) { - this.world = new WeakReference<>(chunk.getWorld()); - this.name = chunk.getWorld().getName(); - this.x = chunk.getX(); - this.z = chunk.getZ(); - } - - public ChunkPos add() { - World world = this.world.get(); - if (world == null) return this; - if (tickets.computeIfAbsent(this, k -> new AtomicInteger()).getAndIncrement() == 0) - world.addPluginChunkTicket(x, z, Adapt.instance); - return this; - } - - public void remove() { - World world = this.world.get(); - if (world == null) { - tickets.remove(this); - return; - } - if (tickets.computeIfAbsent(this, k -> new AtomicInteger()).decrementAndGet() <= 0) { - world.removePluginChunkTicket(x, z, Adapt.instance); - world.unloadChunkRequest(x, z); - tickets.remove(this); - } - } + public void remove() { + World world = this.world.get(); + if (world == null) { + tickets.remove(this); + return; + } + if (tickets.computeIfAbsent(this, k -> new AtomicInteger()).decrementAndGet() <= 0) { + world.removePluginChunkTicket(x, z, Adapt.instance); + world.unloadChunkRequest(x, z); + tickets.remove(this); + } } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index c35f99ceb..d11830185 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -27,10 +27,10 @@ import art.arcane.adapt.content.event.AdaptAdaptationTeleportEvent; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -50,515 +50,515 @@ public class RiftBlink extends SimpleAdaptation { - private final Map lastBlink = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map jumpArmUntil = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map lastOnGround = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastBlink = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map jumpArmUntil = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastOnGround = new java.util.concurrent.ConcurrentHashMap<>(); + + public RiftBlink() { + super("rift-blink"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.blink.description")); + setDisplayName(Localizer.dLocalize("rift.blink.name")); + setIcon(Material.FEATHER); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(9288); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_blink_500") + .title(Localizer.dLocalize("advancement.challenge_rift_blink_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_blink_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_blink_5k") + .title(Localizer.dLocalize("advancement.challenge_rift_blink_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_blink_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_blink_500", "rift.blink.blinks", 500, 400); + registerMilestone("challenge_rift_blink_5k", "rift.blink.distance-blinked", 5000, 1500); + } + + private double getBlinkDistance(int level) { + return getConfig().baseDistance + (getLevelPercent(level) * getConfig().distanceFactor); + } + + private long getCooldownDuration() { + return Math.max(0L, getConfig().cooldownMillis); + } + + private boolean isBlinkEligible(Player p) { + return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + } + + private boolean isOnCooldown(UUID id) { + return M.ms() - lastBlink.getOrDefault(id, 0L) <= getCooldownDuration(); + } + + private void clearDoubleJumpArm(Player p, UUID id) { + if (jumpArmUntil.remove(id) == null) { + return; + } + + if (p.getGameMode() == GameMode.SURVIVAL) { + p.setAllowFlight(false); + p.setFlying(false); + } + } + + private void armDoubleJump(Player p, UUID id) { + int triggerWindowMillis = Math.max(150, getConfig().doubleJumpWindowMillis); + long expires = M.ms() + triggerWindowMillis; + jumpArmUntil.put(id, expires); + p.setAllowFlight(true); + J.runEntity(p, () -> { + if (!p.isOnline()) { + return; + } + + Long armUntil = jumpArmUntil.get(id); + if (armUntil != null && armUntil <= M.ms()) { + clearDoubleJumpArm(p, id); + } + }, Math.max(1, (int) Math.ceil(triggerWindowMillis / 50D))); + } - public RiftBlink() { - super("rift-blink"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.blink.description")); - setDisplayName(Localizer.dLocalize("rift.blink.name")); - setIcon(Material.FEATHER); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(9288); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_blink_500") - .title(Localizer.dLocalize("advancement.challenge_rift_blink_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_blink_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_blink_5k") - .title(Localizer.dLocalize("advancement.challenge_rift_blink_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_blink_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_blink_500", "rift.blink.blinks", 500, 400); - registerMilestone("challenge_rift_blink_5k", "rift.blink.distance-blinked", 5000, 1500); - } + private boolean isClickAction(Action action) { + return action == Action.LEFT_CLICK_AIR + || action == Action.LEFT_CLICK_BLOCK + || action == Action.RIGHT_CLICK_AIR + || action == Action.RIGHT_CLICK_BLOCK; + } + + private boolean isLeftClick(Action action) { + return action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK; + } - private double getBlinkDistance(int level) { - return getConfig().baseDistance + (getLevelPercent(level) * getConfig().distanceFactor); + private boolean isRightClick(Action action) { + return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; + } + + private boolean isBlockClick(Action action) { + return action == Action.LEFT_CLICK_BLOCK || action == Action.RIGHT_CLICK_BLOCK; + } + + private boolean isActionAllowed(Action action) { + if (!getConfig().allowAirClicks && !isBlockClick(action)) { + return false; } - private long getCooldownDuration() { - return Math.max(0L, getConfig().cooldownMillis); + if (!getConfig().allowBlockClicks && isBlockClick(action)) { + return false; } - private boolean isBlinkEligible(Player p) { - return hasActiveAdaptation(p) && p.getGameMode() == GameMode.SURVIVAL; + return true; + } + + private boolean shouldTriggerSprintClick(PlayerInteractEvent e) { + if (!getConfig().enableSprintClickTrigger || !e.getPlayer().isSprinting()) { + return false; } - private boolean isOnCooldown(UUID id) { - return M.ms() - lastBlink.getOrDefault(id, 0L) <= getCooldownDuration(); + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return false; } - private void clearDoubleJumpArm(Player p, UUID id) { - if (jumpArmUntil.remove(id) == null) { - return; - } + Action action = e.getAction(); + if (!isClickAction(action) || !isActionAllowed(action)) { + return false; + } - if (p.getGameMode() == GameMode.SURVIVAL) { - p.setAllowFlight(false); - p.setFlying(false); - } + if (isLeftClick(action) && !getConfig().sprintClickLeftClick) { + return false; } - private void armDoubleJump(Player p, UUID id) { - int triggerWindowMillis = Math.max(150, getConfig().doubleJumpWindowMillis); - long expires = M.ms() + triggerWindowMillis; - jumpArmUntil.put(id, expires); - p.setAllowFlight(true); - J.runEntity(p, () -> { - if (!p.isOnline()) { - return; - } + return !isRightClick(action) || getConfig().sprintClickRightClick; + } - Long armUntil = jumpArmUntil.get(id); - if (armUntil != null && armUntil <= M.ms()) { - clearDoubleJumpArm(p, id); - } - }, Math.max(1, (int) Math.ceil(triggerWindowMillis / 50D))); + private boolean shouldTriggerPearlClick(PlayerInteractEvent e) { + if (!getConfig().enableEnderPearlClickTrigger || e.getItem() == null || e.getItem().getType() != Material.ENDER_PEARL) { + return false; } - private boolean isClickAction(Action action) { - return action == Action.LEFT_CLICK_AIR - || action == Action.LEFT_CLICK_BLOCK - || action == Action.RIGHT_CLICK_AIR - || action == Action.RIGHT_CLICK_BLOCK; + Action action = e.getAction(); + if (!isClickAction(action) || !isActionAllowed(action)) { + return false; } - private boolean isLeftClick(Action action) { - return action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK; + if (isLeftClick(action) && !getConfig().enderPearlClickLeftClick) { + return false; } - private boolean isRightClick(Action action) { - return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; - } + return !isRightClick(action) || getConfig().enderPearlClickRightClick; + } - private boolean isBlockClick(Action action) { - return action == Action.LEFT_CLICK_BLOCK || action == Action.RIGHT_CLICK_BLOCK; + private Location findBlinkGround(Player player) { + Location start = player.getLocation().clone(); + Vector direction = start.getDirection().clone().setY(0); + if (direction.lengthSquared() <= 0.0001) { + double yawRadians = Math.toRadians(start.getYaw()); + direction = new Vector(-Math.sin(yawRadians), 0, Math.cos(yawRadians)); } + direction.normalize(); + + int maxVerticalAdjustment = Math.max(0, getConfig().maxVerticalAdjustment); + double step = Math.max(0.25, getConfig().distanceSearchStep); + double maxDistance = getBlinkDistance(getLevel(player)); - private boolean isActionAllowed(Action action) { - if (!getConfig().allowAirClicks && !isBlockClick(action)) { - return false; - } + for (double distance = maxDistance; distance >= 1; distance -= step) { + Location horizontalTarget = start.clone().add(direction.clone().multiply(distance)); + Location safe = findSafeGroundNear(horizontalTarget, maxVerticalAdjustment); + if (safe != null) { + return safe; + } + } - if (!getConfig().allowBlockClicks && isBlockClick(action)) { - return false; - } + return null; + } - return true; + private Location findSafeGroundNear(Location base, int maxVerticalAdjustment) { + if (isSafe(base)) { + return base; } - private boolean shouldTriggerSprintClick(PlayerInteractEvent e) { - if (!getConfig().enableSprintClickTrigger || !e.getPlayer().isSprinting()) { - return false; - } + for (int y = 1; y <= maxVerticalAdjustment; y++) { + Location down = base.clone().subtract(0, y, 0); + if (isSafe(down)) { + return down; + } - if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { - return false; - } + Location up = base.clone().add(0, y, 0); + if (isSafe(up)) { + return up; + } + } - Action action = e.getAction(); - if (!isClickAction(action) || !isActionAllowed(action)) { - return false; - } + return null; + } - if (isLeftClick(action) && !getConfig().sprintClickLeftClick) { - return false; - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getBlinkDistance(level)) + C.GRAY + " " + Localizer.dLocalize("rift.blink.lore1")); + java.util.List combos = getTriggerCombos(); + if (combos.isEmpty()) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); + return; + } - return !isRightClick(action) || getConfig().sprintClickRightClick; + for (String combo : combos) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); } + } - private boolean shouldTriggerPearlClick(PlayerInteractEvent e) { - if (!getConfig().enableEnderPearlClickTrigger || e.getItem() == null || e.getItem().getType() != Material.ENDER_PEARL) { - return false; - } + @Override + public String getDescription() { + return "Short-ranged instant teleportation to safe ground. " + summarizeTriggerDescription(); + } - Action action = e.getAction(); - if (!isClickAction(action) || !isActionAllowed(action)) { - return false; - } + private String summarizeTriggerDescription() { + java.util.List combos = getTriggerCombos(); + if (combos.isEmpty()) { + return "No active triggers are currently enabled."; + } - if (isLeftClick(action) && !getConfig().enderPearlClickLeftClick) { - return false; - } + if (combos.size() == 1) { + return "Trigger: " + combos.get(0) + "."; + } - return !isRightClick(action) || getConfig().enderPearlClickRightClick; + if (combos.size() == 2) { + return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; } - private Location findBlinkGround(Player player) { - Location start = player.getLocation().clone(); - Vector direction = start.getDirection().clone().setY(0); - if (direction.lengthSquared() <= 0.0001) { - double yawRadians = Math.toRadians(start.getYaw()); - direction = new Vector(-Math.sin(yawRadians), 0, Math.cos(yawRadians)); - } - direction.normalize(); + return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; + } - int maxVerticalAdjustment = Math.max(0, getConfig().maxVerticalAdjustment); - double step = Math.max(0.25, getConfig().distanceSearchStep); - double maxDistance = getBlinkDistance(getLevel(player)); + private java.util.List getTriggerCombos() { + java.util.List triggers = new java.util.ArrayList<>(); + String clickSurface = getClickSurfaceLabel(); + if (getConfig().enableDoubleJumpTrigger) { + triggers.add(getConfig().doubleJumpRequiresSprint ? "Double Jump + Sprint" : "Double Jump"); + } - for (double distance = maxDistance; distance >= 1; distance -= step) { - Location horizontalTarget = start.clone().add(direction.clone().multiply(distance)); - Location safe = findSafeGroundNear(horizontalTarget, maxVerticalAdjustment); - if (safe != null) { - return safe; - } - } + if (getConfig().enableSprintClickTrigger) { + appendClickCombos(triggers, "Sprint", getConfig().sprintClickLeftClick, getConfig().sprintClickRightClick, clickSurface); + } - return null; + if (getConfig().enableSingleSneakTrigger) { + triggers.add(getConfig().singleSneakRequiresSprint ? "Sprint + Sneak" : "Sneak"); } - private Location findSafeGroundNear(Location base, int maxVerticalAdjustment) { - if (isSafe(base)) { - return base; - } + if (getConfig().enableEnderPearlClickTrigger) { + appendClickCombos(triggers, "Ender Pearl", getConfig().enderPearlClickLeftClick, getConfig().enderPearlClickRightClick, clickSurface); + } - for (int y = 1; y <= maxVerticalAdjustment; y++) { - Location down = base.clone().subtract(0, y, 0); - if (isSafe(down)) { - return down; - } + return triggers; + } - Location up = base.clone().add(0, y, 0); - if (isSafe(up)) { - return up; - } - } + private void appendClickCombos(java.util.List triggers, String prefix, boolean allowLeft, boolean allowRight, String clickSurface) { + if (clickSurface.isBlank()) { + return; + } - return null; + if (allowLeft) { + triggers.add(prefix + " + Left Click" + clickSurface); } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + (getBlinkDistance(level)) + C.GRAY + " " + Localizer.dLocalize("rift.blink.lore1")); - java.util.List combos = getTriggerCombos(); - if (combos.isEmpty()) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); - return; - } + if (allowRight) { + triggers.add(prefix + " + Right Click" + clickSurface); + } + } - for (String combo : combos) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); - } + private String getClickSurfaceLabel() { + if (getConfig().allowAirClicks && getConfig().allowBlockClicks) { + return " (air/block)"; } - @Override - public String getDescription() { - return "Short-ranged instant teleportation to safe ground. " + summarizeTriggerDescription(); + if (getConfig().allowAirClicks) { + return " (air)"; } - private String summarizeTriggerDescription() { - java.util.List combos = getTriggerCombos(); - if (combos.isEmpty()) { - return "No active triggers are currently enabled."; - } + if (getConfig().allowBlockClicks) { + return " (block)"; + } - if (combos.size() == 1) { - return "Trigger: " + combos.get(0) + "."; - } + return ""; + } - if (combos.size() == 2) { - return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; - } + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + lastBlink.remove(id); + jumpArmUntil.remove(id); + lastOnGround.remove(id); + } - return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerToggleFlightEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (!isBlinkEligible(p) || !getConfig().enableDoubleJumpTrigger) { + return; } - private java.util.List getTriggerCombos() { - java.util.List triggers = new java.util.ArrayList<>(); - String clickSurface = getClickSurfaceLabel(); - if (getConfig().enableDoubleJumpTrigger) { - triggers.add(getConfig().doubleJumpRequiresSprint ? "Double Jump + Sprint" : "Double Jump"); - } - - if (getConfig().enableSprintClickTrigger) { - appendClickCombos(triggers, "Sprint", getConfig().sprintClickLeftClick, getConfig().sprintClickRightClick, clickSurface); - } - - if (getConfig().enableSingleSneakTrigger) { - triggers.add(getConfig().singleSneakRequiresSprint ? "Sprint + Sneak" : "Sneak"); - } + Long armUntil = jumpArmUntil.get(id); + if (armUntil == null) { + return; + } - if (getConfig().enableEnderPearlClickTrigger) { - appendClickCombos(triggers, "Ender Pearl", getConfig().enderPearlClickLeftClick, getConfig().enderPearlClickRightClick, clickSurface); - } + e.setCancelled(true); + p.setFlying(false); + clearDoubleJumpArm(p, id); + if (armUntil > M.ms()) { + attemptBlink(p); + } + } - return triggers; + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (!isBlinkEligible(p)) { + return; } - private void appendClickCombos(java.util.List triggers, String prefix, boolean allowLeft, boolean allowRight, String clickSurface) { - if (clickSurface.isBlank()) { - return; - } + if (shouldTriggerPearlClick(e)) { + e.setCancelled(true); + attemptBlink(p); + return; + } - if (allowLeft) { - triggers.add(prefix + " + Left Click" + clickSurface); - } + if (shouldTriggerSprintClick(e)) { + attemptBlink(p); + } + } - if (allowRight) { - triggers.add(prefix + " + Right Click" + clickSurface); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!e.isSneaking() || !isBlinkEligible(p) || !getConfig().enableSingleSneakTrigger) { + return; } - private String getClickSurfaceLabel() { - if (getConfig().allowAirClicks && getConfig().allowBlockClicks) { - return " (air/block)"; - } + if (getConfig().singleSneakRequiresSprint && !p.isSprinting()) { + return; + } - if (getConfig().allowAirClicks) { - return " (air)"; - } + attemptBlink(p); + } - if (getConfig().allowBlockClicks) { - return " (block)"; - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + boolean wasOnGround = lastOnGround.getOrDefault(id, true); + boolean onGround = p.isOnGround(); + lastOnGround.put(id, onGround); - return ""; + if (!isBlinkEligible(p) || !getConfig().enableDoubleJumpTrigger) { + clearDoubleJumpArm(p, id); + return; } - @EventHandler - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - lastBlink.remove(id); - jumpArmUntil.remove(id); - lastOnGround.remove(id); + if (!wasOnGround && onGround) { + clearDoubleJumpArm(p, id); + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerToggleFlightEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (!isBlinkEligible(p) || !getConfig().enableDoubleJumpTrigger) { - return; - } - - Long armUntil = jumpArmUntil.get(id); - if (armUntil == null) { - return; - } - - e.setCancelled(true); - p.setFlying(false); - clearDoubleJumpArm(p, id); - if (armUntil > M.ms()) { - attemptBlink(p); - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (!isBlinkEligible(p)) { - return; - } - - if (shouldTriggerPearlClick(e)) { - e.setCancelled(true); - attemptBlink(p); - return; - } - - if (shouldTriggerSprintClick(e)) { - attemptBlink(p); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (!e.isSneaking() || !isBlinkEligible(p) || !getConfig().enableSingleSneakTrigger) { - return; - } - - if (getConfig().singleSneakRequiresSprint && !p.isSprinting()) { - return; - } - - attemptBlink(p); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - boolean wasOnGround = lastOnGround.getOrDefault(id, true); - boolean onGround = p.isOnGround(); - lastOnGround.put(id, onGround); + if (isOnCooldown(id)) { + clearDoubleJumpArm(p, id); + return; + } - if (!isBlinkEligible(p) || !getConfig().enableDoubleJumpTrigger) { - clearDoubleJumpArm(p, id); - return; - } + if (isDoubleJumpStart(wasOnGround, onGround, p)) { + if (getConfig().doubleJumpRequiresSprint && !p.isSprinting()) { + return; + } - if (!wasOnGround && onGround) { - clearDoubleJumpArm(p, id); - return; - } + armDoubleJump(p, id); + return; + } - if (isOnCooldown(id)) { - clearDoubleJumpArm(p, id); - return; - } - - if (isDoubleJumpStart(wasOnGround, onGround, p)) { - if (getConfig().doubleJumpRequiresSprint && !p.isSprinting()) { - return; - } - - armDoubleJump(p, id); - return; - } - - Long armUntil = jumpArmUntil.get(id); - if (armUntil != null && armUntil <= M.ms()) { - clearDoubleJumpArm(p, id); - } - } - - private boolean isDoubleJumpStart(boolean wasOnGround, boolean onGround, Player p) { - return wasOnGround - && !onGround - && p.getVelocity().getY() >= getConfig().doubleJumpMinVerticalVelocity; - } - - private boolean attemptBlink(Player p) { - UUID id = p.getUniqueId(); - if (isOnCooldown(id)) { - return false; - } - - Location locOG = p.getLocation().clone(); - SoundPlayer spw = SoundPlayer.of(p); - Location destinationGround = findBlinkGround(p); - if (destinationGround == null) { - spw.play(p.getLocation(), Sound.BLOCK_CONDUIT_DEACTIVATE, 1f, 1.24f); - lastBlink.put(id, M.ms()); - return false; - } - - PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("rift"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; - if (adaptation != null && adaptation.getLevel() > 0) { - RiftResist.riftResistStackAdd(p, 10, 5); - } - - if (areParticlesEnabled()) { - vfxParticleLine(locOG, destinationGround, Particle.REVERSE_PORTAL, 50, 8, 0.1D, 1D, 0.1D, 0D, null, false, l -> l.getBlock().isPassable()); - } - - Vector v = p.getVelocity().clone(); - loadChunkAsync(destinationGround, chunk -> J.runEntity(p, () -> { - Location toLoc = destinationGround.clone().add(0, 1, 0); - - AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, locOG, destinationGround.clone()); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - - J.teleport(p, toLoc, PlayerTeleportEvent.TeleportCause.PLUGIN); - p.setVelocity(v.multiply(3)); - })); - - getPlayer(p).getData().addStat("rift.teleports", 1); - getPlayer(p).getData().addStat("rift.blink.blinks", 1); - getPlayer(p).getData().addStat("rift.blink.distance-blinked", (int) locOG.distance(destinationGround)); - lastBlink.put(id, M.ms()); - spw.play(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.50f, 1.0f); - vfxLevelUp(p); - return true; - } - - private boolean isSafe(Location l) { - return l.getBlock().getType().isSolid() - && !l.getBlock().getRelative(BlockFace.UP).getType().isSolid() - && !l.getBlock().getRelative(BlockFace.UP).getRelative(BlockFace.UP).getType().isSolid(); - } - - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Short-ranged instant teleportation by double-tapping jump while sprinting.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Blink adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown between successful Rift Blink triggers in milliseconds.", impact = "Higher values reduce blink frequency; lower values allow faster reuse.") - int cooldownMillis = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump detection for Rift Blink.", impact = "True allows jump-based activation; false disables jump activation.") - boolean enableDoubleJumpTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for the double-tap jump trigger.", impact = "True requires sprinting while double-tapping jump; false allows it without sprint.") - boolean doubleJumpRequiresSprint = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum time window between jump taps in milliseconds.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") - int doubleJumpWindowMillis = 450; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump blink.", impact = "Higher values reduce accidental arming; lower values make detection more sensitive.") - double doubleJumpMinVerticalVelocity = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for Rift Blink.", impact = "True allows clicking while sprinting to blink; false disables this trigger.") - boolean enableSprintClickTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for Rift Blink.", impact = "True allows pressing sneak once to trigger blink.") - boolean enableSingleSneakTrigger = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak trigger.", impact = "True requires sprint state when using single-sneak activation.") - boolean singleSneakRequiresSprint = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click as a sprint-click trigger.", impact = "True allows left-click activation while sprinting; false disables left-click activation.") - boolean sprintClickLeftClick = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click as a sprint-click trigger.", impact = "True allows right-click activation while sprinting; false disables right-click activation.") - boolean sprintClickRightClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables click-with-ender-pearl activation for Rift Blink.", impact = "True allows pearl-click activation; false disables pearl-click activation.") - boolean enableEnderPearlClickTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click with an ender pearl to trigger Rift Blink.", impact = "True enables left-click pearl activation; false disables it.") - boolean enderPearlClickLeftClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click with an ender pearl to trigger Rift Blink.", impact = "True enables right-click pearl activation; false disables it.") - boolean enderPearlClickRightClick = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows air-click interactions to trigger Rift Blink.", impact = "True lets air clicks trigger enabled click modes; false blocks air-click triggers.") - boolean allowAirClicks = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows block-click interactions to trigger Rift Blink.", impact = "True lets block clicks trigger enabled click modes; false blocks block-click triggers.") - boolean allowBlockClicks = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDistance = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Factor for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double distanceFactor = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Adjustment for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxVerticalAdjustment = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Search Step for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double distanceSearchStep = 0.5; + Long armUntil = jumpArmUntil.get(id); + if (armUntil != null && armUntil <= M.ms()) { + clearDoubleJumpArm(p, id); } + } + + private boolean isDoubleJumpStart(boolean wasOnGround, boolean onGround, Player p) { + return wasOnGround + && !onGround + && p.getVelocity().getY() >= getConfig().doubleJumpMinVerticalVelocity; + } + + private boolean attemptBlink(Player p) { + UUID id = p.getUniqueId(); + if (isOnCooldown(id)) { + return false; + } + + Location locOG = p.getLocation().clone(); + SoundPlayer spw = SoundPlayer.of(p); + Location destinationGround = findBlinkGround(p); + if (destinationGround == null) { + spw.play(p.getLocation(), Sound.BLOCK_CONDUIT_DEACTIVATE, 1f, 1.24f); + lastBlink.put(id, M.ms()); + return false; + } + + PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("rift"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; + if (adaptation != null && adaptation.getLevel() > 0) { + RiftResist.riftResistStackAdd(p, 10, 5); + } + + if (areParticlesEnabled()) { + vfxParticleLine(locOG, destinationGround, Particle.REVERSE_PORTAL, 50, 8, 0.1D, 1D, 0.1D, 0D, null, false, l -> l.getBlock().isPassable()); + } + + Vector v = p.getVelocity().clone(); + loadChunkAsync(destinationGround, chunk -> J.runEntity(p, () -> { + Location toLoc = destinationGround.clone().add(0, 1, 0); + + AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, locOG, destinationGround.clone()); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + J.teleport(p, toLoc, PlayerTeleportEvent.TeleportCause.PLUGIN); + p.setVelocity(v.multiply(3)); + })); + + getPlayer(p).getData().addStat("rift.teleports", 1); + getPlayer(p).getData().addStat("rift.blink.blinks", 1); + getPlayer(p).getData().addStat("rift.blink.distance-blinked", (int) locOG.distance(destinationGround)); + lastBlink.put(id, M.ms()); + spw.play(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.50f, 1.0f); + vfxLevelUp(p); + return true; + } + + private boolean isSafe(Location l) { + return l.getBlock().getType().isSolid() + && !l.getBlock().getRelative(BlockFace.UP).getType().isSolid() + && !l.getBlock().getRelative(BlockFace.UP).getRelative(BlockFace.UP).getType().isSolid(); + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Short-ranged instant teleportation by double-tapping jump while sprinting.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Blink adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown between successful Rift Blink triggers in milliseconds.", impact = "Higher values reduce blink frequency; lower values allow faster reuse.") + int cooldownMillis = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump detection for Rift Blink.", impact = "True allows jump-based activation; false disables jump activation.") + boolean enableDoubleJumpTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for the double-tap jump trigger.", impact = "True requires sprinting while double-tapping jump; false allows it without sprint.") + boolean doubleJumpRequiresSprint = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum time window between jump taps in milliseconds.", impact = "Higher values make double-tap detection easier; lower values make it stricter.") + int doubleJumpWindowMillis = 450; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum upward velocity required to arm double-jump blink.", impact = "Higher values reduce accidental arming; lower values make detection more sensitive.") + double doubleJumpMinVerticalVelocity = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sprint + click activation for Rift Blink.", impact = "True allows clicking while sprinting to blink; false disables this trigger.") + boolean enableSprintClickTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables single-sneak activation for Rift Blink.", impact = "True allows pressing sneak once to trigger blink.") + boolean enableSingleSneakTrigger = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for single-sneak trigger.", impact = "True requires sprint state when using single-sneak activation.") + boolean singleSneakRequiresSprint = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click as a sprint-click trigger.", impact = "True allows left-click activation while sprinting; false disables left-click activation.") + boolean sprintClickLeftClick = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click as a sprint-click trigger.", impact = "True allows right-click activation while sprinting; false disables right-click activation.") + boolean sprintClickRightClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables click-with-ender-pearl activation for Rift Blink.", impact = "True allows pearl-click activation; false disables pearl-click activation.") + boolean enableEnderPearlClickTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows left-click with an ender pearl to trigger Rift Blink.", impact = "True enables left-click pearl activation; false disables it.") + boolean enderPearlClickLeftClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows right-click with an ender pearl to trigger Rift Blink.", impact = "True enables right-click pearl activation; false disables it.") + boolean enderPearlClickRightClick = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows air-click interactions to trigger Rift Blink.", impact = "True lets air clicks trigger enabled click modes; false blocks air-click triggers.") + boolean allowAirClicks = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows block-click interactions to trigger Rift Blink.", impact = "True lets block clicks trigger enabled click modes; false blocks block-click triggers.") + boolean allowBlockClicks = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Distance for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDistance = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Factor for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double distanceFactor = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Vertical Adjustment for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxVerticalAdjustment = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Search Step for the Rift Blink adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double distanceSearchStep = 0.5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java index bbffda3c0..88e02a47e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftDescent.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -43,108 +43,108 @@ import java.util.concurrent.ConcurrentHashMap; public class RiftDescent extends SimpleAdaptation { - private final Set cooldown = ConcurrentHashMap.newKeySet(); - - public RiftDescent() { - super("rift-descent"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.descent.description")); - setDisplayName(Localizer.dLocalize("rift.descent.name")); - setMaxLevel(1); - setIcon(Material.SHULKER_BOX); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setInitialCost(getConfig().initialCost); - setInterval(9544); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_descent_100") - .title(Localizer.dLocalize("advancement.challenge_rift_descent_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_descent_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SHULKER_SHELL) - .key("challenge_rift_descent_1k") - .title(Localizer.dLocalize("advancement.challenge_rift_descent_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_descent_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_descent_100", "rift.descent.levitation-cancelled", 100, 300); - registerMilestone("challenge_rift_descent_1k", "rift.descent.levitation-cancelled", 1000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.YELLOW + Localizer.dLocalize("rift.descent.lore1")); - v.addLore(C.GREEN + Localizer.dLocalize("rift.descent.lore2") + " " + C.WHITE + getConfig().cooldown + "s"); + private final Set cooldown = ConcurrentHashMap.newKeySet(); + + public RiftDescent() { + super("rift-descent"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.descent.description")); + setDisplayName(Localizer.dLocalize("rift.descent.name")); + setMaxLevel(1); + setIcon(Material.SHULKER_BOX); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setInitialCost(getConfig().initialCost); + setInterval(9544); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_descent_100") + .title(Localizer.dLocalize("advancement.challenge_rift_descent_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_descent_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SHULKER_SHELL) + .key("challenge_rift_descent_1k") + .title(Localizer.dLocalize("advancement.challenge_rift_descent_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_descent_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_descent_100", "rift.descent.levitation-cancelled", 100, 300); + registerMilestone("challenge_rift_descent_1k", "rift.descent.levitation-cancelled", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.YELLOW + Localizer.dLocalize("rift.descent.lore1")); + v.addLore(C.GREEN + Localizer.dLocalize("rift.descent.lore2") + " " + C.WHITE + getConfig().cooldown + "s"); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + if (p.getPotionEffect(PotionEffectType.LEVITATION) == null) { + return; } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - if (p.getPotionEffect(PotionEffectType.LEVITATION) == null) { - return; - } - if (!hasActiveAdaptation(p)) { - return; - } - UUID playerId = p.getUniqueId(); - if (cooldown.contains(playerId)) { - return; - } - - PotionEffect levi = p.getPotionEffect(PotionEffectType.LEVITATION); - - if (!e.isSneaking() && (levi != null)) { - p.removePotionEffect(PotionEffectType.LEVITATION); - getPlayer(p).getData().addStat("rift.descent.levitation-cancelled", 1); - cooldown.add(playerId); - J.runEntity(p, () -> { - sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); - cooldown.remove(playerId); - }, Math.max(1, (int) Math.round(getConfig().cooldown * 20D))); - - J.runEntity(p, () -> { - p.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, (int) (20 * getConfig().cooldown), 0)); - sp.play(p.getLocation(), Sound.ENTITY_ENDER_DRAGON_FLAP, 1f, 1f); - }); - } + if (!hasActiveAdaptation(p)) { + return; } - - - @Override - public void onTick() { - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + UUID playerId = p.getUniqueId(); + if (cooldown.contains(playerId)) { + return; } - @NoArgsConstructor - @ConfigDescription("Sneak to descend and negate levitation effects.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Rift Descent adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldown = 5.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; + PotionEffect levi = p.getPotionEffect(PotionEffectType.LEVITATION); + + if (!e.isSneaking() && (levi != null)) { + p.removePotionEffect(PotionEffectType.LEVITATION); + getPlayer(p).getData().addStat("rift.descent.levitation-cancelled", 1); + cooldown.add(playerId); + J.runEntity(p, () -> { + sp.play(p.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); + cooldown.remove(playerId); + }, Math.max(1, (int) Math.round(getConfig().cooldown * 20D))); + + J.runEntity(p, () -> { + p.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, (int) (20 * getConfig().cooldown), 0)); + sp.play(p.getLocation(), Sound.ENTITY_ENDER_DRAGON_FLAP, 1f, 1f); + }); } + } + + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak to descend and negate levitation effects.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Rift Descent adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldown = 5.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java index 8afc70707..415ecc565 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderTaglock.java @@ -26,11 +26,11 @@ import art.arcane.adapt.content.item.BoundEnderPearl; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.entity.*; @@ -59,404 +59,404 @@ import java.util.concurrent.ConcurrentHashMap; public class RiftEnderTaglock extends SimpleAdaptation { - private static final String PROJECTILE_TARGET_META = "adapt-rift-taglock-target"; - private final NamespacedKey targetKey; - private final Map suppressPearlTeleportUntil = new ConcurrentHashMap<>(); - - public RiftEnderTaglock() { - super("rift-ender-taglock"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.ender_taglock.description")); - setDisplayName(Localizer.dLocalize("rift.ender_taglock.name")); - setIcon(Material.ENDER_PEARL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1200); - targetKey = new NamespacedKey(Adapt.instance, "rift_taglock_target_uuid"); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_taglock_100") - .title(Localizer.dLocalize("advancement.challenge_rift_taglock_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_taglock_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_taglock_500") - .title(Localizer.dLocalize("advancement.challenge_rift_taglock_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_taglock_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_taglock_100", "rift.ender-taglock.entities-tagged", 100, 400); - registerMilestone("challenge_rift_taglock_500", "rift.ender-taglock.taglocked-teleports", 500, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore1")); - if (level >= 2) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore2")); - } - if (level >= 3) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore3")); - } - v.addLore(C.YELLOW + "* " + Form.duration(getThrowCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("rift.ender_taglock.lore4")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - suppressPearlTeleportUntil.remove(e.getPlayer().getUniqueId()); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { - return; - } - - int level = getActiveDamageLevel(p, target); - if (level <= 0 || !p.isSneaking()) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!isItem(hand) || hand.getType() != Material.ENDER_PEARL || BoundEnderPearl.isBindableItem(hand)) { - return; - } - - if (!isTaggable(target, level)) { - return; - } - - e.setCancelled(true); - tagIntoPearl(p, hand, target); - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.55f, 1.4f); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.PORTAL, target.getLocation().add(0, 1, 0), 14, 0.25, 0.4, 0.25, 0.04); - } - getPlayer(p).getData().addStat("rift.ender-taglock.entities-tagged", 1); - xp(p, getConfig().xpOnTag); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - EquipmentSlot slot = e.getHand(); - if (slot == null) { - return; - } - - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - ItemStack hand = slot == EquipmentSlot.HAND - ? p.getInventory().getItemInMainHand() - : p.getInventory().getItemInOffHand(); - - UUID target = getTaggedTarget(hand); - if (target == null) { - return; - } - - e.setCancelled(true); - if (p.hasCooldown(Material.ENDER_PEARL)) { - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, 0.55f, 0.6f); - return; - } - - decrementTaggedPearl(p, slot, hand); - - EnderPearl pearl = p.launchProjectile(EnderPearl.class); - pearl.setMetadata(PROJECTILE_TARGET_META, new FixedMetadataValue(Adapt.instance, target.toString())); - suppressPearlTeleportUntil.put(p.getUniqueId(), System.currentTimeMillis() + getSuppressPearlTeleportWindowMillis()); - p.setCooldown(Material.ENDER_PEARL, getThrowCooldownTicks(level)); - SoundPlayer.of(p).play(p.getLocation(), Sound.ENTITY_ENDER_EYE_LAUNCH, 0.65f, 1.25f); - xp(p, getConfig().xpOnThrow); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerTeleportEvent e) { - if (e.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { - return; - } - - UUID id = e.getPlayer().getUniqueId(); - long until = suppressPearlTeleportUntil.getOrDefault(id, 0L); - if (until <= System.currentTimeMillis()) { - suppressPearlTeleportUntil.remove(id); - return; - } - - e.setCancelled(true); - e.getPlayer().setFallDistance(0f); - suppressPearlTeleportUntil.remove(id); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(ProjectileHitEvent e) { - if (!(e.getEntity() instanceof EnderPearl pearl) || !(pearl.getShooter() instanceof Player p)) { - return; - } - - if (!hasActiveAdaptation(p)) { - return; - } - - String raw = getMetadataString(pearl, PROJECTILE_TARGET_META); - if (raw == null) { - return; - } - - UUID targetId; - try { - targetId = UUID.fromString(raw); - } catch (IllegalArgumentException ex) { - return; - } - - Entity entity = Bukkit.getEntity(targetId); - if (!(entity instanceof LivingEntity target) || !target.isValid() || target.isDead()) { - return; - } - - Location destination = resolveDestination(e); - if (!canDamageTarget(p, target)) { - return; - } - - if (target instanceof Player) { - if (!canPVP(p, destination)) { - return; - } - } else if (!canPVE(p, destination)) { - return; - } - - destination.getChunk().load(); - J.teleport(target, destination); - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.REVERSE_PORTAL, destination.clone().add(0, 0.75, 0), 18, 0.3, 0.35, 0.3, 0.05); - } - SoundPlayer.of(target.getWorld()).play(destination, Sound.ENTITY_ENDERMAN_TELEPORT, 0.75f, 1.35f); - SoundPlayer.of(target.getWorld()).play(destination, Sound.ENTITY_ELDER_GUARDIAN_CURSE, 0.5f, 1.9f); - if (target instanceof Player victim && areSoundsEnabled()) { - victim.playSound(victim.getLocation(), Sound.ENTITY_ELDER_GUARDIAN_CURSE, 0.75f, 1.9f); - } - getPlayer(p).getData().addStat("rift.ender-taglock.taglocked-teleports", 1); - xp(p, getConfig().xpOnTeleport); - J.runEntity(p, () -> suppressPearlTeleportUntil.remove(p.getUniqueId()), 2); - } - - private Location resolveDestination(ProjectileHitEvent e) { - Location base = e.getEntity().getLocation().clone(); - if (e.getHitBlock() != null && e.getHitBlockFace() != null) { - Vector n = e.getHitBlockFace().getDirection().clone().normalize(); - base = e.getHitBlock().getLocation().add(0.5, 0.5, 0.5).add(n.multiply(0.6)); - } - - return base.add(0, 0.05, 0); - } - - private void decrementTaggedPearl(Player p, EquipmentSlot slot, ItemStack stack) { - if (!isItem(stack)) { - return; - } - - if (stack.getAmount() <= 1) { - if (slot == EquipmentSlot.HAND) { - p.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); - } else { - p.getInventory().setItemInOffHand(new ItemStack(Material.AIR)); - } - return; - } - - stack.setAmount(stack.getAmount() - 1); - if (slot == EquipmentSlot.HAND) { - p.getInventory().setItemInMainHand(stack); - } else { - p.getInventory().setItemInOffHand(stack); - } - } - - private void tagIntoPearl(Player p, ItemStack hand, LivingEntity target) { - ItemStack tagged = makeTaggedPearl(target); - - if (hand.getAmount() <= 1) { - p.getInventory().setItemInMainHand(tagged); - return; - } - - hand.setAmount(hand.getAmount() - 1); - p.getInventory().addItem(tagged).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); - } - - private ItemStack makeTaggedPearl(LivingEntity target) { - ItemStack item = new ItemStack(Material.ENDER_PEARL, 1); - ItemMeta meta = item.getItemMeta(); - if (meta == null) { - return item; - } - - PersistentDataContainer pdc = meta.getPersistentDataContainer(); - pdc.set(targetKey, PersistentDataType.STRING, target.getUniqueId().toString()); + private static final String PROJECTILE_TARGET_META = "adapt-rift-taglock-target"; + private final NamespacedKey targetKey; + private final Map suppressPearlTeleportUntil = new ConcurrentHashMap<>(); + + public RiftEnderTaglock() { + super("rift-ender-taglock"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.ender_taglock.description")); + setDisplayName(Localizer.dLocalize("rift.ender_taglock.name")); + setIcon(Material.ENDER_PEARL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1200); + targetKey = new NamespacedKey(Adapt.instance, "rift_taglock_target_uuid"); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_taglock_100") + .title(Localizer.dLocalize("advancement.challenge_rift_taglock_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_taglock_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_taglock_500") + .title(Localizer.dLocalize("advancement.challenge_rift_taglock_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_taglock_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_taglock_100", "rift.ender-taglock.entities-tagged", 100, 400); + registerMilestone("challenge_rift_taglock_500", "rift.ender-taglock.taglocked-teleports", 500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore1")); + if (level >= 2) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore2")); + } + if (level >= 3) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.ender_taglock.lore3")); + } + v.addLore(C.YELLOW + "* " + Form.duration(getThrowCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("rift.ender_taglock.lore4")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + suppressPearlTeleportUntil.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p) || !(e.getEntity() instanceof LivingEntity target)) { + return; + } + + int level = getActiveDamageLevel(p, target); + if (level <= 0 || !p.isSneaking()) { + return; + } + + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isItem(hand) || hand.getType() != Material.ENDER_PEARL || BoundEnderPearl.isBindableItem(hand)) { + return; + } + + if (!isTaggable(target, level)) { + return; + } + + e.setCancelled(true); + tagIntoPearl(p, hand, target); + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.55f, 1.4f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.PORTAL, target.getLocation().add(0, 1, 0), 14, 0.25, 0.4, 0.25, 0.04); + } + getPlayer(p).getData().addStat("rift.ender-taglock.entities-tagged", 1); + xp(p, getConfig().xpOnTag); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + EquipmentSlot slot = e.getHand(); + if (slot == null) { + return; + } - meta.setDisplayName(C.LIGHT_PURPLE + "Taglocked Ender Pearl"); - List lore = new ArrayList<>(); - lore.add(C.GRAY + "Target: " + C.WHITE + getTargetName(target)); - lore.add(C.DARK_GRAY + "Right click to throw"); - meta.setLore(lore); - item.setItemMeta(meta); - return item; + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; } - private UUID getTaggedTarget(ItemStack item) { - if (!isItem(item) || item.getType() != Material.ENDER_PEARL || item.getItemMeta() == null) { - return null; - } + Player p = e.getPlayer(); + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + ItemStack hand = slot == EquipmentSlot.HAND + ? p.getInventory().getItemInMainHand() + : p.getInventory().getItemInOffHand(); - String raw = item.getItemMeta().getPersistentDataContainer().get(targetKey, PersistentDataType.STRING); - if (raw == null || raw.isEmpty()) { - return null; - } + UUID target = getTaggedTarget(hand); + if (target == null) { + return; + } - try { - return UUID.fromString(raw); - } catch (IllegalArgumentException e) { - return null; - } + e.setCancelled(true); + if (p.hasCooldown(Material.ENDER_PEARL)) { + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, 0.55f, 0.6f); + return; } - private String getTargetName(LivingEntity target) { - if (target instanceof Player player) { - return player.getName(); - } + decrementTaggedPearl(p, slot, hand); - if (target.getCustomName() != null && !target.getCustomName().isEmpty()) { - return target.getCustomName(); - } + EnderPearl pearl = p.launchProjectile(EnderPearl.class); + pearl.setMetadata(PROJECTILE_TARGET_META, new FixedMetadataValue(Adapt.instance, target.toString())); + suppressPearlTeleportUntil.put(p.getUniqueId(), System.currentTimeMillis() + getSuppressPearlTeleportWindowMillis()); + p.setCooldown(Material.ENDER_PEARL, getThrowCooldownTicks(level)); + SoundPlayer.of(p).play(p.getLocation(), Sound.ENTITY_ENDER_EYE_LAUNCH, 0.65f, 1.25f); + xp(p, getConfig().xpOnThrow); + } - return Form.capitalizeWords(target.getType().name().toLowerCase().replaceAll("\\Q_\\E", " ")); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerTeleportEvent e) { + if (e.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { + return; } - private boolean isTaggable(LivingEntity target, int level) { - if (target instanceof Player) { - return level >= 3; - } + UUID id = e.getPlayer().getUniqueId(); + long until = suppressPearlTeleportUntil.getOrDefault(id, 0L); + if (until <= System.currentTimeMillis()) { + suppressPearlTeleportUntil.remove(id); + return; + } + + e.setCancelled(true); + e.getPlayer().setFallDistance(0f); + suppressPearlTeleportUntil.remove(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(ProjectileHitEvent e) { + if (!(e.getEntity() instanceof EnderPearl pearl) || !(pearl.getShooter() instanceof Player p)) { + return; + } - if (level >= 3) { - return true; - } - - if (level == 2) { - return isLevel1Taggable(target) || target instanceof Villager || isLargeTarget(target); - } + if (!hasActiveAdaptation(p)) { + return; + } - return isLevel1Taggable(target); + String raw = getMetadataString(pearl, PROJECTILE_TARGET_META); + if (raw == null) { + return; } - private boolean isLevel1Taggable(LivingEntity target) { - if (!(target instanceof Mob)) { - return false; - } - - if (target instanceof Villager) { - return false; - } - - if (isLargeTarget(target)) { - return false; - } + UUID targetId; + try { + targetId = UUID.fromString(raw); + } catch (IllegalArgumentException ex) { + return; + } - return target instanceof Animals - || target instanceof Monster - || target instanceof WaterMob - || target instanceof Ambient - || target instanceof Slime; + Entity entity = Bukkit.getEntity(targetId); + if (!(entity instanceof LivingEntity target) || !target.isValid() || target.isDead()) { + return; } - private boolean isLargeTarget(LivingEntity target) { - BoundingBox box = target.getBoundingBox(); - double width = Math.max(box.getWidthX(), box.getWidthZ()); - double height = box.getHeight(); - return width >= getConfig().largeWidthThreshold || height >= getConfig().largeHeightThreshold; + Location destination = resolveDestination(e); + if (!canDamageTarget(p, target)) { + return; } - private String getMetadataString(Entity entity, String key) { - for (MetadataValue value : entity.getMetadata(key)) { - if (value.getOwningPlugin() == Adapt.instance) { - return value.asString(); - } - } + if (target instanceof Player) { + if (!canPVP(p, destination)) { + return; + } + } else if (!canPVE(p, destination)) { + return; + } - return null; + destination.getChunk().load(); + J.teleport(target, destination); + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.REVERSE_PORTAL, destination.clone().add(0, 0.75, 0), 18, 0.3, 0.35, 0.3, 0.05); + } + SoundPlayer.of(target.getWorld()).play(destination, Sound.ENTITY_ENDERMAN_TELEPORT, 0.75f, 1.35f); + SoundPlayer.of(target.getWorld()).play(destination, Sound.ENTITY_ELDER_GUARDIAN_CURSE, 0.5f, 1.9f); + if (target instanceof Player victim && areSoundsEnabled()) { + victim.playSound(victim.getLocation(), Sound.ENTITY_ELDER_GUARDIAN_CURSE, 0.75f, 1.9f); } - - private int getThrowCooldownTicks(int level) { - return Math.max(4, (int) Math.round(getConfig().throwCooldownTicksBase - (getLevelPercent(level) * getConfig().throwCooldownTicksFactor))); + getPlayer(p).getData().addStat("rift.ender-taglock.taglocked-teleports", 1); + xp(p, getConfig().xpOnTeleport); + J.runEntity(p, () -> suppressPearlTeleportUntil.remove(p.getUniqueId()), 2); + } + + private Location resolveDestination(ProjectileHitEvent e) { + Location base = e.getEntity().getLocation().clone(); + if (e.getHitBlock() != null && e.getHitBlockFace() != null) { + Vector n = e.getHitBlockFace().getDirection().clone().normalize(); + base = e.getHitBlock().getLocation().add(0.5, 0.5, 0.5).add(n.multiply(0.6)); } - private long getSuppressPearlTeleportWindowMillis() { - return Math.max(1000L, getConfig().suppressPearlTeleportWindowMillis); + return base.add(0, 0.05, 0); + } + + private void decrementTaggedPearl(Player p, EquipmentSlot slot, ItemStack stack) { + if (!isItem(stack)) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - suppressPearlTeleportUntil.entrySet().removeIf(i -> i.getValue() <= now); + if (stack.getAmount() <= 1) { + if (slot == EquipmentSlot.HAND) { + p.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); + } else { + p.getInventory().setItemInOffHand(new ItemStack(Material.AIR)); + } + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + stack.setAmount(stack.getAmount() - 1); + if (slot == EquipmentSlot.HAND) { + p.getInventory().setItemInMainHand(stack); + } else { + p.getInventory().setItemInOffHand(stack); } + } + + private void tagIntoPearl(Player p, ItemStack hand, LivingEntity target) { + ItemStack tagged = makeTaggedPearl(target); - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (hand.getAmount() <= 1) { + p.getInventory().setItemInMainHand(tagged); + return; } - @NoArgsConstructor - @ConfigDescription("Tag entities into ender pearls and throw those pearls to reposition the tagged target.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.95; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Base for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double throwCooldownTicksBase = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Factor for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double throwCooldownTicksFactor = 14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Suppress Pearl Teleport Window Millis for the Rift Ender Taglock adaptation.", impact = "This should be long enough to catch the teleport event from a taglocked throw.") - long suppressPearlTeleportWindowMillis = 180000L; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Width Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double largeWidthThreshold = 1.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Height Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double largeHeightThreshold = 2.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Tag for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnTag = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Throw for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnThrow = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Teleport for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnTeleport = 14; + hand.setAmount(hand.getAmount() - 1); + p.getInventory().addItem(tagged).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); + } + + private ItemStack makeTaggedPearl(LivingEntity target) { + ItemStack item = new ItemStack(Material.ENDER_PEARL, 1); + ItemMeta meta = item.getItemMeta(); + if (meta == null) { + return item; } + + PersistentDataContainer pdc = meta.getPersistentDataContainer(); + pdc.set(targetKey, PersistentDataType.STRING, target.getUniqueId().toString()); + + meta.setDisplayName(C.LIGHT_PURPLE + "Taglocked Ender Pearl"); + List lore = new ArrayList<>(); + lore.add(C.GRAY + "Target: " + C.WHITE + getTargetName(target)); + lore.add(C.DARK_GRAY + "Right click to throw"); + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } + + private UUID getTaggedTarget(ItemStack item) { + if (!isItem(item) || item.getType() != Material.ENDER_PEARL || item.getItemMeta() == null) { + return null; + } + + String raw = item.getItemMeta().getPersistentDataContainer().get(targetKey, PersistentDataType.STRING); + if (raw == null || raw.isEmpty()) { + return null; + } + + try { + return UUID.fromString(raw); + } catch (IllegalArgumentException e) { + return null; + } + } + + private String getTargetName(LivingEntity target) { + if (target instanceof Player player) { + return player.getName(); + } + + if (target.getCustomName() != null && !target.getCustomName().isEmpty()) { + return target.getCustomName(); + } + + return Form.capitalizeWords(target.getType().name().toLowerCase().replaceAll("\\Q_\\E", " ")); + } + + private boolean isTaggable(LivingEntity target, int level) { + if (target instanceof Player) { + return level >= 3; + } + + if (level >= 3) { + return true; + } + + if (level == 2) { + return isLevel1Taggable(target) || target instanceof Villager || isLargeTarget(target); + } + + return isLevel1Taggable(target); + } + + private boolean isLevel1Taggable(LivingEntity target) { + if (!(target instanceof Mob)) { + return false; + } + + if (target instanceof Villager) { + return false; + } + + if (isLargeTarget(target)) { + return false; + } + + return target instanceof Animals + || target instanceof Monster + || target instanceof WaterMob + || target instanceof Ambient + || target instanceof Slime; + } + + private boolean isLargeTarget(LivingEntity target) { + BoundingBox box = target.getBoundingBox(); + double width = Math.max(box.getWidthX(), box.getWidthZ()); + double height = box.getHeight(); + return width >= getConfig().largeWidthThreshold || height >= getConfig().largeHeightThreshold; + } + + private String getMetadataString(Entity entity, String key) { + for (MetadataValue value : entity.getMetadata(key)) { + if (value.getOwningPlugin() == Adapt.instance) { + return value.asString(); + } + } + + return null; + } + + private int getThrowCooldownTicks(int level) { + return Math.max(4, (int) Math.round(getConfig().throwCooldownTicksBase - (getLevelPercent(level) * getConfig().throwCooldownTicksFactor))); + } + + private long getSuppressPearlTeleportWindowMillis() { + return Math.max(1000L, getConfig().suppressPearlTeleportWindowMillis); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + suppressPearlTeleportUntil.entrySet().removeIf(i -> i.getValue() <= now); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Tag entities into ender pearls and throw those pearls to reposition the tagged target.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.95; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Base for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double throwCooldownTicksBase = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Cooldown Ticks Factor for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double throwCooldownTicksFactor = 14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Suppress Pearl Teleport Window Millis for the Rift Ender Taglock adaptation.", impact = "This should be long enough to catch the teleport event from a taglocked throw.") + long suppressPearlTeleportWindowMillis = 180000L; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Width Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double largeWidthThreshold = 1.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Large Height Threshold for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double largeHeightThreshold = 2.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Tag for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnTag = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Throw for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnThrow = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP On Teleport for the Rift Ender Taglock adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnTeleport = 14; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java index 503ddf9ea..e4ef594da 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java @@ -26,9 +26,9 @@ import art.arcane.adapt.api.world.PlayerSkillLine; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -40,84 +40,84 @@ public class RiftEnderchest extends SimpleAdaptation { - public RiftEnderchest() { - super("rift-enderchest"); - setDescription(Localizer.dLocalize("rift.chest.description")); - setDisplayName(Localizer.dLocalize("rift.chest.name")); - setIcon(Material.ENDER_CHEST); - setBaseCost(0); - setCostFactor(0); - setMaxLevel(1); - setInitialCost(10); - setInterval(9248); - registerConfiguration(Config.class); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_CHEST) - .key("challenge_rift_enderchest_200") - .title(Localizer.dLocalize("advancement.challenge_rift_enderchest_200.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_enderchest_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_rift_enderchest_200", "rift.enderchest.opens", 200, 300); - } + public RiftEnderchest() { + super("rift-enderchest"); + setDescription(Localizer.dLocalize("rift.chest.description")); + setDisplayName(Localizer.dLocalize("rift.chest.name")); + setIcon(Material.ENDER_CHEST); + setBaseCost(0); + setCostFactor(0); + setMaxLevel(1); + setInitialCost(10); + setInterval(9248); + registerConfiguration(Config.class); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_CHEST) + .key("challenge_rift_enderchest_200") + .title(Localizer.dLocalize("advancement.challenge_rift_enderchest_200.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_enderchest_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_rift_enderchest_200", "rift.enderchest.opens", 200, 300); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.ITALIC + Localizer.dLocalize("rift.chest.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.ITALIC + Localizer.dLocalize("rift.chest.lore1")); + } - @EventHandler - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); - ItemStack hand = p.getInventory().getItemInMainHand(); + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + SoundPlayer sp = SoundPlayer.of(p); + ItemStack hand = p.getInventory().getItemInMainHand(); - if (hand.getType() != Material.ENDER_CHEST || !hasActiveAdaptation(p)) { - return; - } + if (hand.getType() != Material.ENDER_CHEST || !hasActiveAdaptation(p)) { + return; + } - if (p.hasCooldown(hand.getType())) { - e.setCancelled(true); - } else { - p.setCooldown(Material.ENDER_CHEST, 100); + if (p.hasCooldown(hand.getType())) { + e.setCancelled(true); + } else { + p.setCooldown(Material.ENDER_CHEST, 100); - if ((e.getAction() == Action.RIGHT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_BLOCK)) { - PlayerSkillLine line = getPlayer(p).getData().getSkillLine("rift"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; - if (adaptation != null && adaptation.getLevel() > 0) { - RiftResist.riftResistStackAdd(p, 10, 2); - } - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - p.openInventory(p.getEnderChest()); - getPlayer(p).getData().addStat("rift.enderchest.opens", 1); - } + if ((e.getAction() == Action.RIGHT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_BLOCK)) { + PlayerSkillLine line = getPlayer(p).getData().getSkillLine("rift"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; + if (adaptation != null && adaptation.getLevel() > 0) { + RiftResist.riftResistStackAdd(p, 10, 2); } + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + p.openInventory(p.getEnderChest()); + getPlayer(p).getData().addStat("rift.enderchest.opens", 1); + } } + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Open an enderchest by left-clicking it in your hand.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - } + @NoArgsConstructor + @ConfigDescription("Open an enderchest by left-clicking it in your hand.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java index aaa0480dd..0e6d3f013 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftGate.java @@ -28,15 +28,16 @@ import art.arcane.adapt.content.item.BoundEyeOfEnder; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.EquipmentSlot; @@ -45,241 +46,234 @@ import org.bukkit.potion.PotionEffectType; public class RiftGate extends SimpleAdaptation { - public RiftGate() { - super("rift-gate"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.gate.description")); - setDisplayName(Localizer.dLocalize("rift.gate.name")); - setIcon(Material.RESPAWN_ANCHOR); - setBaseCost(0); - setCostFactor(0); - setMaxLevel(1); - setInitialCost(30); - setInterval(1322); - registerRecipe(AdaptRecipe.shapeless() - .key("rift-recall-gate") - .ingredient(Material.ENDER_PEARL) - .ingredient(Material.AMETHYST_SHARD) - .ingredient(Material.EMERALD) - .result(BoundEyeOfEnder.io.withData(new BoundEyeOfEnder.Data(null))) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_gate_100") - .title(Localizer.dLocalize("advancement.challenge_rift_gate_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_gate_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_gate_50k_dist") - .title(Localizer.dLocalize("advancement.challenge_rift_gate_50k_dist.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_gate_50k_dist.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_gate_100", "rift.gate.teleports", 100, 400); - registerMilestone("challenge_rift_gate_50k_dist", "rift.gate.total-distance", 50000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.YELLOW + Localizer.dLocalize("rift.gate.lore1")); - v.addLore(C.RED + Localizer.dLocalize("rift.gate.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("rift.gate.lore3") + C.UNDERLINE + C.RED + Localizer.dLocalize("rift.gate.lore4")); + public RiftGate() { + super("rift-gate"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.gate.description")); + setDisplayName(Localizer.dLocalize("rift.gate.name")); + setIcon(Material.RESPAWN_ANCHOR); + setBaseCost(0); + setCostFactor(0); + setMaxLevel(1); + setInitialCost(30); + setInterval(1322); + registerRecipe(AdaptRecipe.shapeless() + .key("rift-recall-gate") + .ingredient(Material.ENDER_PEARL) + .ingredient(Material.AMETHYST_SHARD) + .ingredient(Material.EMERALD) + .result(BoundEyeOfEnder.io.withData(new BoundEyeOfEnder.Data(null))) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_gate_100") + .title(Localizer.dLocalize("advancement.challenge_rift_gate_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_gate_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_gate_50k_dist") + .title(Localizer.dLocalize("advancement.challenge_rift_gate_50k_dist.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_gate_50k_dist.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_gate_100", "rift.gate.teleports", 100, 400); + registerMilestone("challenge_rift_gate_50k_dist", "rift.gate.total-distance", 50000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.YELLOW + Localizer.dLocalize("rift.gate.lore1")); + v.addLore(C.RED + Localizer.dLocalize("rift.gate.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("rift.gate.lore3") + C.UNDERLINE + C.RED + Localizer.dLocalize("rift.gate.lore4")); + } + + + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + ItemStack offHand = p.getInventory().getItemInOffHand(); + Location location = e.getClickedBlock() == null ? p.getLocation() : e.getClickedBlock().getLocation(); + + // Deny usage if the offhand contains a bindable item + if (BoundEyeOfEnder.isBindableItem(offHand) && e.getHand() != null && e.getHand().equals(EquipmentSlot.OFF_HAND)) { + e.setCancelled(true); + return; } - - @EventHandler - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - ItemStack hand = p.getInventory().getItemInMainHand(); - ItemStack offHand = p.getInventory().getItemInOffHand(); - Location location = e.getClickedBlock() == null ? p.getLocation() : e.getClickedBlock().getLocation(); - - // Deny usage if the offhand contains a bindable item - if (BoundEyeOfEnder.isBindableItem(offHand) && e.getHand() != null && e.getHand().equals(EquipmentSlot.OFF_HAND)) { - e.setCancelled(true); - return; + if (p.getInventory().getItemInMainHand().getType().equals(Material.ENDER_EYE) + && !p.hasCooldown(Material.ENDER_EYE) + && hasActiveAdaptation(p) + && BoundEyeOfEnder.isBindableItem(hand)) { + + e.setCancelled(true); + Adapt.verbose(" - Player Main hand: " + hand.getType()); + Action action = e.getAction(); + if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { + if (p.isSneaking()) { + Adapt.verbose("Linking eye"); + linkEye(p, location); } - - if (p.getInventory().getItemInMainHand().getType().equals(Material.ENDER_EYE) - && !p.hasCooldown(Material.ENDER_EYE) - && hasActiveAdaptation(p) - && BoundEyeOfEnder.isBindableItem(hand)) { - - e.setCancelled(true); - Adapt.verbose(" - Player Main hand: " + hand.getType()); - switch (e.getAction()) { - case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> { - if (p.isSneaking()) { - Adapt.verbose("Linking eye"); - linkEye(p, location); - } - } - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> // use - { - if (isBound(hand)) { - openEye(p); - } - } - } + } else if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { + if (isBound(hand)) { + openEye(p); } + } } + } - private void handleEyeOfEnderInteraction(PlayerInteractEvent event, Player player, Block block) { - boolean sneaking = player.isSneaking(); - ItemStack mainHand = player.getInventory().getItemInMainHand(); - Location location = block == null ? player.getLocation() : block.getLocation(); - - switch (event.getAction()) { - case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> { - if (sneaking) { - if (isBound(mainHand)) { - unlinkEye(player); - } else { - linkEye(player, location); - } - } - } - case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> { - if (isBound(mainHand)) { - openEye(player); - } - } - default -> { - } - } - } - - private boolean isBound(ItemStack stack) { - return stack.getType().equals(Material.ENDER_EYE) && BoundEyeOfEnder.getLocation(stack) != null; - } - - private void unlinkEye(Player p) { - ItemStack hand = p.getInventory().getItemInMainHand(); - decrementItemstack(hand, p); - ItemStack eye = new ItemStack(Material.ENDER_EYE); - p.getInventory().addItem(eye).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); - } - - private void linkEye(Player p, Location location) { - if (areParticlesEnabled()) { - vfxCuboidOutline(location.getBlock(), location.add(0, 1, 0).getBlock(), Particle.REVERSE_PORTAL); - } - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ENTITY_ENDER_EYE_DEATH, 0.50f, 0.22f); - ItemStack hand = p.getInventory().getItemInMainHand(); + private void handleEyeOfEnderInteraction(PlayerInteractEvent event, Player player, Block block) { + boolean sneaking = player.isSneaking(); + ItemStack mainHand = player.getInventory().getItemInMainHand(); + Location location = block == null ? player.getLocation() : block.getLocation(); - if (hand.getAmount() == 1) { - BoundEyeOfEnder.setData(hand, location); + Action action = event.getAction(); + if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { + if (sneaking) { + if (isBound(mainHand)) { + unlinkEye(player); } else { - hand.setAmount(hand.getAmount() - 1); - ItemStack eye = BoundEyeOfEnder.withData(location); - p.getInventory().addItem(eye).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); + linkEye(player, location); } + } + } else if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { + if (isBound(mainHand)) { + openEye(player); + } } - - - private void openEye(Player p) { - Adapt.verbose("Using eye"); - SoundPlayer sp = SoundPlayer.of(p); - Location l = BoundEyeOfEnder.getLocation(p.getInventory().getItemInMainHand()); - ItemStack hand = p.getInventory().getItemInMainHand(); - - if (getConfig().consumeOnUse) { - xp(p, 75); - decrementItemstack(hand, p); - } else { - if (p.getCooldown(Material.ENDER_EYE) > 0) { - sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 1, 1); - return; - } - } - p.setCooldown(Material.ENDER_EYE, 150); - - - if (RiftResist.hasRiftResistPerk(getPlayer(p))) { - RiftResist.riftResistStackAdd(p, 150, 3); - } - - p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 10, true, false, false)); - p.addPotionEffect(new PotionEffect(PotionEffectType.LEVITATION, 85, 0, true, false, false)); - sp.play(l, Sound.BLOCK_LODESTONE_PLACE, 1f, 0.1f); - sp.play(l, Sound.BLOCK_BELL_RESONATE, 1f, 0.1f); - - int[] remainingSteps = {80}; - double[] radius = {2.0}; - double[] adder = {0.0}; - boolean[] initialRingShown = {false}; - final Color color = Color.fromBGR(0, 0, 0); - Runnable[] ringTask = new Runnable[1]; - ringTask[0] = () -> { - if (!p.isOnline()) { - return; - } - - if (!initialRingShown[0]) { - vfxFastRing(p.getLocation(), radius[0], color); - initialRingShown[0] = true; - } - - remainingSteps[0]--; - if (remainingSteps[0] <= 0) { - return; - } - - adder[0] += 0.02; - radius[0] *= 0.9; - vfxFastRing(p.getLocation().add(0, adder[0], 0), radius[0], color); - J.runEntity(p, ringTask[0], 1); - }; - J.runEntity(p, ringTask[0]); - vfxLevelUp(p); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); - J.runEntity(p, () -> { - AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, p.getLocation(), l); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - - getPlayer(p).getData().addStat("rift.teleports", 1); - getPlayer(p).getData().addStat("rift.gate.teleports", 1); - getPlayer(p).getData().addStat("rift.gate.total-distance", (int) p.getLocation().distance(l)); - J.teleport(p, l, PlayerTeleportEvent.TeleportCause.PLUGIN); - vfxLevelUp(p); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); - }, 85); + } + + private boolean isBound(ItemStack stack) { + return stack.getType().equals(Material.ENDER_EYE) && BoundEyeOfEnder.getLocation(stack) != null; + } + + private void unlinkEye(Player p) { + ItemStack hand = p.getInventory().getItemInMainHand(); + decrementItemstack(hand, p); + ItemStack eye = new ItemStack(Material.ENDER_EYE); + p.getInventory().addItem(eye).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); + } + + private void linkEye(Player p, Location location) { + if (areParticlesEnabled()) { + vfxCuboidOutline(location.getBlock(), location.add(0, 1, 0).getBlock(), Particle.REVERSE_PORTAL); } - - - @Override - public void onTick() { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ENTITY_ENDER_EYE_DEATH, 0.50f, 0.22f); + ItemStack hand = p.getInventory().getItemInMainHand(); + + if (hand.getAmount() == 1) { + BoundEyeOfEnder.setData(hand, location); + } else { + hand.setAmount(hand.getAmount() - 1); + ItemStack eye = BoundEyeOfEnder.withData(location); + p.getInventory().addItem(eye).values().forEach(i -> p.getWorld().dropItemNaturally(p.getLocation(), i)); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + } + + + private void openEye(Player p) { + Adapt.verbose("Using eye"); + SoundPlayer sp = SoundPlayer.of(p); + Location l = BoundEyeOfEnder.getLocation(p.getInventory().getItemInMainHand()); + ItemStack hand = p.getInventory().getItemInMainHand(); + + if (getConfig().consumeOnUse) { + xp(p, 75); + decrementItemstack(hand, p); + } else { + if (p.getCooldown(Material.ENDER_EYE) > 0) { + sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 1, 1); + return; + } } + p.setCooldown(Material.ENDER_EYE, 150); - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - @NoArgsConstructor - @ConfigDescription("Craft a gate item to teleport to a marked location.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consume On Use for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") - boolean consumeOnUse = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; + if (RiftResist.hasRiftResistPerk(getPlayer(p))) { + RiftResist.riftResistStackAdd(p, 150, 3); } + + p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 100, 10, true, false, false)); + p.addPotionEffect(new PotionEffect(PotionEffectType.LEVITATION, 85, 0, true, false, false)); + sp.play(l, Sound.BLOCK_LODESTONE_PLACE, 1f, 0.1f); + sp.play(l, Sound.BLOCK_BELL_RESONATE, 1f, 0.1f); + + int[] remainingSteps = {80}; + double[] radius = {2.0}; + double[] adder = {0.0}; + boolean[] initialRingShown = {false}; + final Color color = Color.fromBGR(0, 0, 0); + Runnable[] ringTask = new Runnable[1]; + ringTask[0] = () -> { + if (!p.isOnline()) { + return; + } + + if (!initialRingShown[0]) { + vfxFastRing(p.getLocation(), radius[0], color); + initialRingShown[0] = true; + } + + remainingSteps[0]--; + if (remainingSteps[0] <= 0) { + return; + } + + adder[0] += 0.02; + radius[0] *= 0.9; + vfxFastRing(p.getLocation().add(0, adder[0], 0), radius[0], color); + J.runEntity(p, ringTask[0], 1); + }; + J.runEntity(p, ringTask[0]); + vfxLevelUp(p); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); + J.runEntity(p, () -> { + AdaptAdaptationTeleportEvent event = new AdaptAdaptationTeleportEvent(!Bukkit.isPrimaryThread(), getPlayer(p), this, p.getLocation(), l); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + getPlayer(p).getData().addStat("rift.teleports", 1); + getPlayer(p).getData().addStat("rift.gate.teleports", 1); + getPlayer(p).getData().addStat("rift.gate.total-distance", (int) p.getLocation().distance(l)); + J.teleport(p, l, PlayerTeleportEvent.TeleportCause.PLUGIN); + vfxLevelUp(p); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 5.35f, 0.1f); + }, 85); + } + + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Craft a gate item to teleport to a marked location.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Consume On Use for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") + boolean consumeOnUse = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Rift Gate adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index 2b1a5a09e..86b3d5297 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -44,272 +44,284 @@ import java.util.Map; public class RiftInflatedPocketDimension extends SimpleAdaptation { - public RiftInflatedPocketDimension() { - super("rift-inflated-pocket-dimension"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.inflated_pocket_dimension.description")); - setDisplayName(Localizer.dLocalize("rift.inflated_pocket_dimension.name")); - setIcon(Material.ENDER_EYE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(600); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_CHEST) - .key("challenge_rift_pocket_5k") - .title(Localizer.dLocalize("advancement.challenge_rift_pocket_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_pocket_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_CHEST) - .key("challenge_rift_pocket_store_10k") - .title(Localizer.dLocalize("advancement.challenge_rift_pocket_store_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_pocket_store_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_pocket_5k", "rift.inflated-pocket.items-pulled", 5000, 400); - registerMilestone("challenge_rift_pocket_store_10k", "rift.inflated-pocket.items-stored", 10000, 1000); + public RiftInflatedPocketDimension() { + super("rift-inflated-pocket-dimension"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.inflated_pocket_dimension.description")); + setDisplayName(Localizer.dLocalize("rift.inflated_pocket_dimension.name")); + setIcon(Material.ENDER_EYE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(600); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_CHEST) + .key("challenge_rift_pocket_5k") + .title(Localizer.dLocalize("advancement.challenge_rift_pocket_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_pocket_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_CHEST) + .key("challenge_rift_pocket_store_10k") + .title(Localizer.dLocalize("advancement.challenge_rift_pocket_store_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_pocket_store_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_pocket_5k", "rift.inflated-pocket.items-pulled", 5000, 400); + registerMilestone("challenge_rift_pocket_store_10k", "rift.inflated-pocket.items-stored", 10000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore1")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore2")); + v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore1")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore2")); - v.addLore(C.GREEN + "+ " + Localizer.dLocalize("rift.inflated_pocket_dimension.lore3")); + if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerInteractEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { - return; - } - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - - ItemStack hand = p.getInventory().getItemInMainHand(); - if (!hand.getType().isAir()) { - return; - } - - Material requested = e.getClickedBlock().getType(); - if (!requested.isItem() || requested.isAir()) { - return; - } - - int moved = moveFromEnderToPlayer(p, requested, getConfig().rightClickPullAmount, true); - if (moved <= 0) { - return; - } - - e.setCancelled(true); - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.4f, 1.7f); - getPlayer(p).getData().addStat("rift.inflated-pocket.items-pulled", moved); - xp(p, moved * getConfig().xpPerTransferredItem, "rift:inflated-pocket:pull"); + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(BlockPlaceEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - - Material placed = e.getBlockPlaced().getType(); - if (!placed.isItem() || placed.isAir()) { - return; - } - - J.runEntity(p, () -> { - ItemStack hand = p.getInventory().getItemInMainHand(); - int needed = 0; - if (hand.getType().isAir()) { - needed = Math.min(getConfig().buildRefillAmount, placed.getMaxStackSize()); - } else if (hand.getType() == placed && hand.getAmount() < placed.getMaxStackSize()) { - needed = Math.min(getConfig().buildRefillAmount, placed.getMaxStackSize() - hand.getAmount()); - } - - if (needed <= 0) { - return; - } - - int moved = moveFromEnderToPlayer(p, placed, needed, true); - if (moved <= 0) { - return; - } - - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.3f, 1.9f); - getPlayer(p).getData().addStat("rift.inflated-pocket.items-pulled", moved); - xp(p, moved * getConfig().xpPerTransferredItem, "rift:inflated-pocket:build-refill"); - }, 1); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!hand.getType().isAir()) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerDropItemEvent e) { - Player p = e.getPlayer(); - if (getActiveLevel(p, Player::isSneaking) <= 0) { - return; - } - - ItemStack dropped = e.getItemDrop().getItemStack().clone(); - if (!canFullyFitInInventory(p.getEnderChest().getContents(), dropped, p.getEnderChest().getMaxStackSize())) { - e.setCancelled(true); - e.getItemDrop().remove(); - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.8f, 0.8f); - return; - } + Material requested = e.getClickedBlock().getType(); + if (!requested.isItem() || requested.isAir()) { + return; + } - e.getItemDrop().remove(); - p.getEnderChest().addItem(dropped); + int moved = moveFromEnderToPlayer(p, requested, getConfig().rightClickPullAmount, true); + if (moved <= 0) { + return; + } - SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_CLOSE, 0.5f, 1.4f); - getPlayer(p).getData().addStat("rift.inflated-pocket.items-stored", dropped.getAmount()); - xp(p, dropped.getAmount() * getConfig().xpPerTransferredItem, "rift:inflated-pocket:store"); + e.setCancelled(true); + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.4f, 1.7f); + getPlayer(p).getData().addStat("rift.inflated-pocket.items-pulled", moved); + xp(p, moved * getConfig().xpPerTransferredItem, "rift:inflated-pocket:pull"); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - private int moveFromEnderToPlayer(Player p, Material type, int amount, boolean preferMainHand) { - if (amount <= 0) { - return 0; - } + Material placed = e.getBlockPlaced().getType(); + if (!placed.isItem() || placed.isAir()) { + return; + } - int moved = 0; - ItemStack[] chest = p.getEnderChest().getContents(); - for (int slot = 0; slot < chest.length && moved < amount; slot++) { - ItemStack stack = chest[slot]; - if (!isItem(stack) || stack.getType() != type) { - continue; - } - - int take = Math.min(amount - moved, stack.getAmount()); - ItemStack transfer = stack.clone(); - transfer.setAmount(take); - - int inserted = insertIntoPlayerInventory(p, transfer, preferMainHand); - if (inserted <= 0) { - continue; - } - - moved += inserted; - if (stack.getAmount() <= inserted) { - chest[slot] = null; - } else { - stack.setAmount(stack.getAmount() - inserted); - chest[slot] = stack; - } - } + J.runEntity(p, () -> { + ItemStack hand = p.getInventory().getItemInMainHand(); + int needed = 0; + if (hand.getType().isAir()) { + needed = Math.min(getConfig().buildRefillAmount, placed.getMaxStackSize()); + } else if (hand.getType() == placed && hand.getAmount() < placed.getMaxStackSize()) { + needed = Math.min(getConfig().buildRefillAmount, placed.getMaxStackSize() - hand.getAmount()); + } + + if (needed <= 0) { + return; + } + + int moved = moveFromEnderToPlayer(p, placed, needed, true); + if (moved <= 0) { + return; + } + + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.3f, 1.9f); + getPlayer(p).getData().addStat("rift.inflated-pocket.items-pulled", moved); + xp(p, moved * getConfig().xpPerTransferredItem, "rift:inflated-pocket:build-refill"); + }, 1); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + if (getActiveLevel(p, Player::isSneaking) <= 0) { + return; + } - p.getEnderChest().setContents(chest); - return moved; + ItemStack dropped = e.getItemDrop().getItemStack().clone(); + if (!canFullyFitInInventory(p.getEnderChest().getContents(), dropped, p.getEnderChest().getMaxStackSize())) { + e.setCancelled(true); + e.getItemDrop().remove(); + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.8f, 0.8f); + return; } - private int insertIntoPlayerInventory(Player p, ItemStack transfer, boolean preferMainHand) { - int requested = transfer.getAmount(); - if (requested <= 0) { - return 0; - } + e.getItemDrop().remove(); + p.getEnderChest().addItem(dropped); - ItemStack hand = p.getInventory().getItemInMainHand(); - if (preferMainHand && (hand == null || hand.getType().isAir())) { - p.getInventory().setItemInMainHand(transfer); - return requested; - } + SoundPlayer.of(p).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_CLOSE, 0.5f, 1.4f); + getPlayer(p).getData().addStat("rift.inflated-pocket.items-stored", dropped.getAmount()); + xp(p, dropped.getAmount() * getConfig().xpPerTransferredItem, "rift:inflated-pocket:store"); + } - if (preferMainHand && hand.getType() == transfer.getType() && hand.getAmount() < hand.getMaxStackSize()) { - int room = hand.getMaxStackSize() - hand.getAmount(); - int moved = Math.min(room, transfer.getAmount()); - hand.setAmount(hand.getAmount() + moved); - p.getInventory().setItemInMainHand(hand); - if (moved >= requested) { - return requested; - } - - transfer.setAmount(requested - moved); - Map overflow = p.getInventory().addItem(transfer); - int remaining = overflow.values().stream().mapToInt(ItemStack::getAmount).sum(); - return moved + Math.max(0, transfer.getAmount() - remaining); - } + private int moveFromEnderToPlayer(Player p, Material type, int amount, boolean preferMainHand) { + if (amount <= 0) { + return 0; + } - Map overflow = p.getInventory().addItem(transfer); - int remaining = overflow.values().stream().mapToInt(ItemStack::getAmount).sum(); - return Math.max(0, requested - remaining); + int moved = 0; + ItemStack[] chest = p.getEnderChest().getContents(); + for (int slot = 0; slot < chest.length && moved < amount; slot++) { + ItemStack stack = chest[slot]; + if (!isItem(stack) || stack.getType() != type) { + continue; + } + + int take = Math.min(amount - moved, stack.getAmount()); + ItemStack transfer = stack.clone(); + transfer.setAmount(take); + + int inserted = insertIntoPlayerInventory(p, transfer, preferMainHand); + if (inserted <= 0) { + continue; + } + + moved += inserted; + if (stack.getAmount() <= inserted) { + chest[slot] = null; + } else { + stack.setAmount(stack.getAmount() - inserted); + chest[slot] = stack; + } } - private boolean canFullyFitInInventory(ItemStack[] contents, ItemStack stack, int inventoryMaxStackSize) { - if (!isItem(stack)) { - return false; - } + p.getEnderChest().setContents(chest); + return moved; + } - int remaining = stack.getAmount(); - int maxPerSlot = Math.min(stack.getMaxStackSize(), inventoryMaxStackSize); + private int insertIntoPlayerInventory(Player p, ItemStack transfer, boolean preferMainHand) { + int requested = transfer.getAmount(); + if (requested <= 0) { + return 0; + } - for (ItemStack existing : contents) { - if (!isItem(existing) || !existing.isSimilar(stack)) { - continue; - } + ItemStack hand = p.getInventory().getItemInMainHand(); + if (preferMainHand && (hand == null || hand.getType().isAir())) { + p.getInventory().setItemInMainHand(transfer); + return requested; + } - remaining -= Math.max(0, maxPerSlot - existing.getAmount()); - if (remaining <= 0) { - return true; - } - } + if (preferMainHand && hand.getType() == transfer.getType() && hand.getAmount() < hand.getMaxStackSize()) { + int room = hand.getMaxStackSize() - hand.getAmount(); + int moved = Math.min(room, transfer.getAmount()); + hand.setAmount(hand.getAmount() + moved); + p.getInventory().setItemInMainHand(hand); + if (moved >= requested) { + return requested; + } + + int remainingRequest = requested - moved; + transfer.setAmount(remainingRequest); + Map overflow = p.getInventory().addItem(transfer); + int remaining = sumItemAmounts(overflow); + return moved + Math.max(0, remainingRequest - remaining); + } - for (ItemStack existing : contents) { - if (existing == null || existing.getType().isAir()) { - remaining -= maxPerSlot; - if (remaining <= 0) { - return true; - } - } - } + Map overflow = p.getInventory().addItem(transfer); + int remaining = sumItemAmounts(overflow); + return Math.max(0, requested - remaining); + } + + private int sumItemAmounts(Map overflow) { + int sum = 0; + for (ItemStack itemStack : overflow.values()) { + if (itemStack == null || itemStack.getType().isAir()) { + continue; + } + sum += itemStack.getAmount(); + } + return sum; + } - return false; + private boolean canFullyFitInInventory(ItemStack[] contents, ItemStack stack, int inventoryMaxStackSize) { + if (!isItem(stack)) { + return false; } - @Override - public void onTick() { + int remaining = stack.getAmount(); + int maxPerSlot = Math.min(stack.getMaxStackSize(), inventoryMaxStackSize); - } + for (ItemStack existing : contents) { + if (!isItem(existing) || !existing.isSimilar(stack)) { + continue; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + remaining -= Math.max(0, maxPerSlot - existing.getAmount()); + if (remaining <= 0) { + return true; + } } - @Override - public boolean isPermanent() { - return getConfig().permanent; + for (ItemStack existing : contents) { + if (existing == null || existing.getType().isAir()) { + remaining -= maxPerSlot; + if (remaining <= 0) { + return true; + } + } } - @NoArgsConstructor - @ConfigDescription("Building and empty-hand block targeting can fetch materials from your ender chest, and sneak-drop stores items into it.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Build Refill Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int buildRefillAmount = 64; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Right Click Pull Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int rightClickPullAmount = 64; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Transferred Item for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerTransferredItem = 0.08; - } + return false; + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Building and empty-hand block targeting can fetch materials from your ender chest, and sneak-drop stores items into it.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Build Refill Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int buildRefillAmount = 64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Right Click Pull Amount for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int rightClickPullAmount = 64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Transferred Item for the Rift Inflated Pocket Dimension adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTransferredItem = 0.08; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java index 4c5791493..528903b05 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftResist.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -42,102 +42,102 @@ public class RiftResist extends SimpleAdaptation { - public RiftResist() { - super("rift-resist"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.resist.description")); - setDisplayName(Localizer.dLocalize("rift.resist.name")); - setIcon(Material.SCULK_VEIN); - setBaseCost(getConfig().baseCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(10288); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_resist_200") - .title(Localizer.dLocalize("advancement.challenge_rift_resist_200.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_resist_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_rift_resist_200", "rift.resist.activations", 200, 300); - } + public RiftResist() { + super("rift-resist"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.resist.description")); + setDisplayName(Localizer.dLocalize("rift.resist.name")); + setIcon(Material.SCULK_VEIN); + setBaseCost(getConfig().baseCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(10288); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_resist_200") + .title(Localizer.dLocalize("advancement.challenge_rift_resist_200.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_resist_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_rift_resist_200", "rift.resist.activations", 200, 300); + } - static void riftResistStackAdd(Player p, int duration, int amplifier) { - if (p.getLocation().getWorld() == null) { - return; - } - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_IRON, 1f, 1.24f); - spw.play(p.getLocation(), Sound.BLOCK_CONDUIT_AMBIENT_SHORT, 1f, 0.01f); - spw.play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1f, 0.01f); - p.addPotionEffect(new PotionEffect(PotionEffectTypes.DAMAGE_RESISTANCE, duration, amplifier, true, false, false)); + static void riftResistStackAdd(Player p, int duration, int amplifier) { + if (p.getLocation().getWorld() == null) { + return; } + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_IRON, 1f, 1.24f); + spw.play(p.getLocation(), Sound.BLOCK_CONDUIT_AMBIENT_SHORT, 1f, 0.01f); + spw.play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1f, 0.01f); + p.addPotionEffect(new PotionEffect(PotionEffectTypes.DAMAGE_RESISTANCE, duration, amplifier, true, false, false)); + } - public static boolean hasRiftResistPerk(AdaptPlayer p) { - return p.getData().getLevel() > 0; - } + public static boolean hasRiftResistPerk(AdaptPlayer p) { + return p.getData().getLevel() > 0; + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.ITALIC + Localizer.dLocalize("rift.resist.lore1")); - v.addLore(C.UNDERLINE + Localizer.dLocalize("rift.resist.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.ITALIC + Localizer.dLocalize("rift.resist.lore1")); + v.addLore(C.UNDERLINE + Localizer.dLocalize("rift.resist.lore2")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - ItemStack hand = p.getInventory().getItemInMainHand(); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; + } + ItemStack hand = p.getInventory().getItemInMainHand(); - if (e.getAction() == Action.RIGHT_CLICK_AIR) { - switch (hand.getType()) { - case ENDER_EYE, ENDER_PEARL -> { - xp(p, 3); - riftResistStackAdd(p, getConfig().duration, getConfig().amplitude); - getPlayer(p).getData().addStat("rift.resist.activations", 1); - } - } + if (e.getAction() == Action.RIGHT_CLICK_AIR) { + switch (hand.getType()) { + case ENDER_EYE, ENDER_PEARL -> { + xp(p, 3); + riftResistStackAdd(p, getConfig().duration, getConfig().amplitude); + getPlayer(p).getData().addStat("rift.resist.activations", 1); } - + } } - @Override - public void onTick() { + } - } + @Override + public void onTick() { - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Gain resistance when using Ender items and abilities.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplitude for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int amplitude = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int duration = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain resistance when using Ender items and abilities.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Amplitude for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int amplitude = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Duration for the Rift Resist adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int duration = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java index 0553d7645..682b22d73 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVisage.java @@ -6,8 +6,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Enderman; @@ -30,21 +30,21 @@ public RiftVisage() { setInitialCost(getConfig().initialCost); setInterval(1000); registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_visage_100") - .title(Localizer.dLocalize("advancement.challenge_rift_visage_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_visage_100.description")) + .icon(Material.ENDER_EYE) + .key("challenge_rift_visage_100") + .title(Localizer.dLocalize("advancement.challenge_rift_visage_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_visage_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DRAGON_HEAD) + .key("challenge_rift_visage_1k") + .title(Localizer.dLocalize("advancement.challenge_rift_visage_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_visage_1k.description")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DRAGON_HEAD) - .key("challenge_rift_visage_1k") - .title(Localizer.dLocalize("advancement.challenge_rift_visage_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_visage_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); + .build()) + .build()); registerMilestone("challenge_rift_visage_100", "rift.visage.stares-survived", 100, 300); registerMilestone("challenge_rift_visage_1k", "rift.visage.stares-survived", 1000, 1000); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index a5011bbe6..aec735cd6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -40,174 +40,185 @@ import java.util.Map; public class RiftVoidMagnet extends SimpleAdaptation { - public RiftVoidMagnet() { - super("rift-void-magnet"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("rift.void_magnet.description")); - setDisplayName(Localizer.dLocalize("rift.void_magnet.name")); - setIcon(Material.HOPPER_MINECART); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(20); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_void_magnet_5k") - .title(Localizer.dLocalize("advancement.challenge_rift_void_magnet_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_void_magnet_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_void_magnet_50k") - .title(Localizer.dLocalize("advancement.challenge_rift_void_magnet_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_void_magnet_50k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_void_magnet_5k", "rift.void-magnet.items-pulled", 5000, 400); - registerMilestone("challenge_rift_void_magnet_50k", "rift.void-magnet.items-pulled", 50000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore1")); - v.addLore(C.GREEN + "+ " + getMaxItems(level) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getPulseTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore3")); - } - - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || p.getTicksLived() % getPulseTicks(level) != 0) { - continue; - } - - int moved = collectNearbyItems(p, level); - if (moved <= 0) { - continue; - } - - if (areParticlesEnabled()) { - - p.spawnParticle(Particle.PORTAL, p.getLocation().add(0, 1, 0), 8, 0.3, 0.5, 0.3, 0.05); - - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.45f, 1.6f); - getPlayer(p).getData().addStat("rift.void-magnet.items-pulled", moved); - xp(p, moved * getConfig().xpPerMovedItem, "rift:void-magnet:item-pull"); - } - } - - private int collectNearbyItems(Player p, int level) { - int moved = 0; - int max = getMaxItems(level); - double r = getRadius(level); - for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), r, r, r)) { - if (!(entity instanceof Item item)) { - continue; - } - - if (moved >= max || item.isDead() || !item.isValid()) { - continue; - } - - ItemStack stack = item.getItemStack(); - if (stack == null || stack.getType().isAir()) { - continue; - } - - int requestAmount = Math.min(stack.getAmount(), max - moved); - if (requestAmount <= 0) { - continue; - } - - ItemStack toChest = stack.clone(); - toChest.setAmount(requestAmount); - Map chestOverflow = p.getEnderChest().addItem(toChest); - int chestRemaining = chestOverflow.values().stream().mapToInt(ItemStack::getAmount).sum(); - int movedAmount = Math.max(0, requestAmount - chestRemaining); - - if (chestRemaining > 0 && getConfig().allowEnderChestOverflow) { - ItemStack toInventory = stack.clone(); - toInventory.setAmount(chestRemaining); - Map inventoryOverflow = p.getInventory().addItem(toInventory); - int inventoryRemaining = inventoryOverflow.values().stream().mapToInt(ItemStack::getAmount).sum(); - movedAmount += Math.max(0, chestRemaining - inventoryRemaining); - } - - if (movedAmount <= 0) { - continue; - } - - if (movedAmount >= stack.getAmount()) { - item.remove(); - } else { - stack.setAmount(stack.getAmount() - movedAmount); - item.setItemStack(stack); - } - moved += movedAmount; - } - - return moved; + public RiftVoidMagnet() { + super("rift-void-magnet"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("rift.void_magnet.description")); + setDisplayName(Localizer.dLocalize("rift.void_magnet.name")); + setIcon(Material.HOPPER_MINECART); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(20); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_void_magnet_5k") + .title(Localizer.dLocalize("advancement.challenge_rift_void_magnet_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_void_magnet_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_void_magnet_50k") + .title(Localizer.dLocalize("advancement.challenge_rift_void_magnet_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_void_magnet_50k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_void_magnet_5k", "rift.void-magnet.items-pulled", 5000, 400); + registerMilestone("challenge_rift_void_magnet_50k", "rift.void-magnet.items-pulled", 50000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore1")); + v.addLore(C.GREEN + "+ " + getMaxItems(level) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getPulseTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("rift.void_magnet.lore3")); + } + + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getTicksLived() % getPulseTicks(level) != 0) { + continue; + } + + int moved = collectNearbyItems(p, level); + if (moved <= 0) { + continue; + } + + if (areParticlesEnabled()) { + + p.spawnParticle(Particle.PORTAL, p.getLocation().add(0, 1, 0), 8, 0.3, 0.5, 0.3, 0.05); + + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 0.45f, 1.6f); + getPlayer(p).getData().addStat("rift.void-magnet.items-pulled", moved); + xp(p, moved * getConfig().xpPerMovedItem, "rift:void-magnet:item-pull"); } - - private double getRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); - } - - private int getMaxItems(int level) { - return Math.max(1, (int) Math.round(getConfig().maxItemsBase + (getLevelPercent(level) * getConfig().maxItemsFactor))); - } - - private int getPulseTicks(int level) { - return Math.max(2, (int) Math.round(getConfig().pulseTicksBase - (getLevelPercent(level) * getConfig().pulseTicksFactor))); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + } + + private int collectNearbyItems(Player p, int level) { + int moved = 0; + int max = getMaxItems(level); + double r = getRadius(level); + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), r, r, r)) { + if (!(entity instanceof Item item)) { + continue; + } + + if (moved >= max || item.isDead() || !item.isValid()) { + continue; + } + + ItemStack stack = item.getItemStack(); + if (stack == null || stack.getType().isAir()) { + continue; + } + + int requestAmount = Math.min(stack.getAmount(), max - moved); + if (requestAmount <= 0) { + continue; + } + + ItemStack toChest = stack.clone(); + toChest.setAmount(requestAmount); + Map chestOverflow = p.getEnderChest().addItem(toChest); + int chestRemaining = sumItemAmounts(chestOverflow); + int movedAmount = Math.max(0, requestAmount - chestRemaining); + + if (chestRemaining > 0 && getConfig().allowEnderChestOverflow) { + ItemStack toInventory = stack.clone(); + toInventory.setAmount(chestRemaining); + Map inventoryOverflow = p.getInventory().addItem(toInventory); + int inventoryRemaining = sumItemAmounts(inventoryOverflow); + movedAmount += Math.max(0, chestRemaining - inventoryRemaining); + } + + if (movedAmount <= 0) { + continue; + } + + if (movedAmount >= stack.getAmount()) { + item.remove(); + } else { + stack.setAmount(stack.getAmount() - movedAmount); + item.setItemStack(stack); + } + moved += movedAmount; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + return moved; + } - @NoArgsConstructor - @ConfigDescription("Sneak to periodically pull nearby dropped items into your ender chest first.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Ender Chest Overflow for the Rift Void Magnet adaptation.", impact = "When true, leftovers that do not fit in ender chest can spill into player inventory.") - boolean allowEnderChestOverflow = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxItemsBase = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxItemsFactor = 22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pulseTicksBase = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pulseTicksFactor = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Moved Item for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerMovedItem = 0.7; + private int sumItemAmounts(Map overflow) { + int sum = 0; + for (ItemStack itemStack : overflow.values()) { + if (itemStack == null || itemStack.getType().isAir()) { + continue; + } + sum += itemStack.getAmount(); } + return sum; + } + + private double getRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private int getMaxItems(int level) { + return Math.max(1, (int) Math.round(getConfig().maxItemsBase + (getLevelPercent(level) * getConfig().maxItemsFactor))); + } + + private int getPulseTicks(int level) { + return Math.max(2, (int) Math.round(getConfig().pulseTicksBase - (getLevelPercent(level) * getConfig().pulseTicksFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak to periodically pull nearby dropped items into your ender chest first.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Allow Ender Chest Overflow for the Rift Void Magnet adaptation.", impact = "When true, leftovers that do not fit in ender chest can spill into player inventory.") + boolean allowEnderChestOverflow = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxItemsBase = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Items Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxItemsFactor = 22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Base for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pulseTicksBase = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pulse Ticks Factor for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pulseTicksFactor = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Moved Item for the Rift Void Magnet adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMovedItem = 0.7; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java index 114119701..16ed633dd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneFishersFantasy.java @@ -26,8 +26,8 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.ExperienceOrb; @@ -40,92 +40,92 @@ public class SeaborneFishersFantasy extends SimpleAdaptation { - public SeaborneFishersFantasy() { - super("seaborne-fishers-fantasy"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.fishers_fantasy.description")); - setDisplayName(Localizer.dLocalize("seaborn.fishers_fantasy.name")); - setIcon(Material.FISHING_ROD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(8080); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FISHING_ROD) - .key("challenge_seaborne_fish_500") - .title(Localizer.dLocalize("advancement.challenge_seaborne_fish_500.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_fish_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TROPICAL_FISH) - .key("challenge_seaborne_fish_5k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_fish_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_fish_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_seaborne_fish_500", "seaborne.fishers-fantasy.fish-caught", 500, 300); - registerMilestone("challenge_seaborne_fish_5k", "seaborne.fishers-fantasy.fish-caught", 5000, 1000); - } + public SeaborneFishersFantasy() { + super("seaborne-fishers-fantasy"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.fishers_fantasy.description")); + setDisplayName(Localizer.dLocalize("seaborn.fishers_fantasy.name")); + setIcon(Material.FISHING_ROD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(8080); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FISHING_ROD) + .key("challenge_seaborne_fish_500") + .title(Localizer.dLocalize("advancement.challenge_seaborne_fish_500.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_fish_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TROPICAL_FISH) + .key("challenge_seaborne_fish_5k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_fish_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_fish_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_seaborne_fish_500", "seaborne.fishers-fantasy.fish-caught", 500, 300); + registerMilestone("challenge_seaborne_fish_5k", "seaborne.fishers-fantasy.fish-caught", 5000, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("seaborn.fishers_fantasy.lore1")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("seaborn.fishers_fantasy.lore1")); + } - @EventHandler - public void on(PlayerFishEvent e) { - Player p = e.getPlayer(); - withAdaptedPlayer(p, e, () -> { - if (e.getState() == PlayerFishEvent.State.CAUGHT_FISH) { - getPlayer(p).getData().addStat("seaborne.fishers-fantasy.fish-caught", 1); - int level = getActiveLevel(p); - ThreadLocalRandom random = ThreadLocalRandom.current(); - for (int i = 0; i < level; i++) { - ItemStack item = new ItemStack(ItemListings.getFishingDrops().getRandom(), 1); - if (random.nextBoolean()) { - p.getWorld().dropItemNaturally(p.getLocation(), item); - p.getWorld().spawn(p.getLocation(), ExperienceOrb.class); - Adapt.verbose("Fishing Gift Donated!"); - xp(p, 15 * level); - } - } - } - }); - } + @EventHandler + public void on(PlayerFishEvent e) { + Player p = e.getPlayer(); + withAdaptedPlayer(p, e, () -> { + if (e.getState() == PlayerFishEvent.State.CAUGHT_FISH) { + getPlayer(p).getData().addStat("seaborne.fishers-fantasy.fish-caught", 1); + int level = getActiveLevel(p); + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (int i = 0; i < level; i++) { + ItemStack item = new ItemStack(ItemListings.getFishingDrops().getRandom(), 1); + if (random.nextBoolean()) { + p.getWorld().dropItemNaturally(p.getLocation(), item); + p.getWorld().spawn(p.getLocation(), ExperienceOrb.class); + Adapt.verbose("Fishing Gift Donated!"); + xp(p, 15 * level); + } + } + } + }); + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Earn more XP from fishing and catch more fish.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.9; - } + @NoArgsConstructor + @ConfigDescription("Earn more XP from fishing and catch more fish.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java index f0dd5f41f..73bf6f101 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneOxygen.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -35,88 +35,88 @@ public class SeaborneOxygen extends SimpleAdaptation { - public SeaborneOxygen() { - super("seaborne-oxygen"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.oxygen.description")); - setDisplayName(Localizer.dLocalize("seaborn.oxygen.name")); - setIcon(Material.GLASS_PANE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(3750); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TURTLE_HELMET) - .key("challenge_seaborne_oxygen_12k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_oxygen_12k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_oxygen_12k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_seaborne_oxygen_12k", "seaborne.oxygen.bonus-air-ticks", 12000, 300); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getAirBoost(level), 0) + C.GRAY + Localizer.dLocalize("seaborn.oxygen.lore1")); - } + public SeaborneOxygen() { + super("seaborne-oxygen"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.oxygen.description")); + setDisplayName(Localizer.dLocalize("seaborn.oxygen.name")); + setIcon(Material.GLASS_PANE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(3750); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TURTLE_HELMET) + .key("challenge_seaborne_oxygen_12k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_oxygen_12k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_oxygen_12k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_seaborne_oxygen_12k", "seaborne.oxygen.bonus-air-ticks", 12000, 300); + } - public double getAirBoost(int level) { - return getLevelPercent(level); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getAirBoost(level), 0) + C.GRAY + Localizer.dLocalize("seaborn.oxygen.lore1")); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player player = adaptPlayer.getPlayer(); - if (player == null || !player.isOnline()) { - continue; - } + public double getAirBoost(int level) { + return getLevelPercent(level); + } - withPlayerThread(player, () -> { - if (!player.isOnline() || player.getWorld() == null) { - return; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player player = adaptPlayer.getPlayer(); + if (player == null || !player.isOnline()) { + continue; + } - int level = getActiveLevel(player); - if (level <= 0 || (!player.isInWater() && !player.isSwimming())) { - return; - } + withPlayerThread(player, () -> { + if (!player.isOnline() || player.getWorld() == null) { + return; + } - int airTicks = level * getConfig().airPerLevelTics; - player.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, airTicks, level)); - getPlayer(player).getData().addStat("seaborne.oxygen.bonus-air-ticks", airTicks); - }); + int level = getActiveLevel(player); + if (level <= 0 || (!player.isInWater() && !player.isSwimming())) { + return; } - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + int airTicks = level * getConfig().airPerLevelTics; + player.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, airTicks, level)); + getPlayer(player).getData().addStat("seaborne.oxygen.bonus-air-ticks", airTicks); + }); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Hold more oxygen underwater.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.525; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Air Per Level Tics for the Seaborne Oxygen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int airPerLevelTics = 15; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Hold more oxygen underwater.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.525; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Air Per Level Tics for the Seaborne Oxygen adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int airPerLevelTics = 15; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index ce3b4565c..6f428fa5d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -43,220 +43,220 @@ import java.util.concurrent.ThreadLocalRandom; public class SeabornePressureDiver extends SimpleAdaptation { - private final Map xpCooldowns = new ConcurrentHashMap<>(); - - public SeabornePressureDiver() { - super("seaborne-pressure-diver"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.pressure_diver.description")); - setDisplayName(Localizer.dLocalize("seaborn.pressure_diver.name")); - setIcon(Material.NAUTILUS_SHELL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(20); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_seaborne_pressure_1k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_pressure_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_pressure_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_seaborne_pressure_1k", "seaborne.pressure-diver.deep-blocks-mined", 1000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getDepthThreshold(level), 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getDamageReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore2")); - v.addLore(C.GREEN + "+ " + Form.pc(getFatigueTrimChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore3")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - xpCooldowns.remove(e.getPlayer().getUniqueId()); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } - - withPlayerThread(p, e, () -> { - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (!p.isInWater()) { - return; - } - - if (!isDeepEnough(p, level)) { - return; - } - - e.setDamage(e.getDamage() * (1D - getDamageReduction(level))); - }); + private final Map xpCooldowns = new ConcurrentHashMap<>(); + + public SeabornePressureDiver() { + super("seaborne-pressure-diver"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.pressure_diver.description")); + setDisplayName(Localizer.dLocalize("seaborn.pressure_diver.name")); + setIcon(Material.NAUTILUS_SHELL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(20); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_seaborne_pressure_1k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_pressure_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_pressure_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_seaborne_pressure_1k", "seaborne.pressure-diver.deep-blocks-mined", 1000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getDepthThreshold(level), 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getDamageReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getFatigueTrimChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("seaborn.pressure_diver.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + xpCooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - - withPlayerThread(p, () -> { - if (!p.isOnline()) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0 || !p.isInWater()) { - return; - } - - if (!isDeepEnough(p, level)) { - return; - } - - applyDepthBuffs(p, level); - awardDepthXp(p, now); - getPlayer(p).getData().addStat("seaborne.pressure-diver.deep-blocks-mined", 1); - }); + withPlayerThread(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (!p.isInWater()) { + return; + } + + if (!isDeepEnough(p, level)) { + return; + } + + e.setDamage(e.getDamage() * (1D - getDamageReduction(level))); + }); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + withPlayerThread(p, () -> { + if (!p.isOnline()) { + return; } - } - private void applyDepthBuffs(Player p, int level) { - int resistanceAmp = getResistanceAmplifier(level, p); - p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, getConfig().effectTicks, resistanceAmp, false, false, true), true); - p.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, getConfig().effectTicks, 0, false, false, true), true); - - PotionEffect fatigue = p.getPotionEffect(PotionEffectType.MINING_FATIGUE); - if (fatigue == null) { - return; + int level = getActiveLevel(p); + if (level <= 0 || !p.isInWater()) { + return; } - if (ThreadLocalRandom.current().nextDouble() > getFatigueTrimChance(level)) { - return; + if (!isDeepEnough(p, level)) { + return; } - int reducedAmp = Math.max(0, fatigue.getAmplifier() - getFatigueTrimAmount(level)); - p.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, - Math.max(20, Math.min(fatigue.getDuration(), getConfig().fatigueReplaceTicks)), - reducedAmp, - false, - true, - true), true); + applyDepthBuffs(p, level); + awardDepthXp(p, now); + getPlayer(p).getData().addStat("seaborne.pressure-diver.deep-blocks-mined", 1); + }); } + } - private void awardDepthXp(Player p, long now) { - UUID id = p.getUniqueId(); - long next = xpCooldowns.getOrDefault(id, 0L); - if (now < next) { - return; - } + private void applyDepthBuffs(Player p, int level) { + int resistanceAmp = getResistanceAmplifier(level, p); + p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, getConfig().effectTicks, resistanceAmp, false, false, true), true); + p.addPotionEffect(new PotionEffect(PotionEffectType.WATER_BREATHING, getConfig().effectTicks, 0, false, false, true), true); - xp(p, getConfig().xpPerDepthPulse); - xpCooldowns.put(id, now + getConfig().xpPulseCooldownMillis); + PotionEffect fatigue = p.getPotionEffect(PotionEffectType.MINING_FATIGUE); + if (fatigue == null) { + return; } - private boolean isDeepEnough(Player p, int level) { - double seaLevel = p.getWorld().getSeaLevel(); - double depth = seaLevel - p.getEyeLocation().getY(); - return depth >= getDepthThreshold(level); + if (ThreadLocalRandom.current().nextDouble() > getFatigueTrimChance(level)) { + return; } - private int getResistanceAmplifier(int level, Player p) { - double seaLevel = p.getWorld().getSeaLevel(); - double depth = seaLevel - p.getEyeLocation().getY(); - if (depth >= getDeepThreshold(level)) { - return 1; - } - - return 0; - } - - private double getDepthThreshold(int level) { - return Math.max(2, getConfig().depthThresholdBase - (getLevelPercent(level) * getConfig().depthThresholdFactor)); - } - - private double getDeepThreshold(int level) { - return Math.max(4, getConfig().deepThresholdBase - (getLevelPercent(level) * getConfig().deepThresholdFactor)); - } - - private double getDamageReduction(int level) { - return Math.min(getConfig().maxDamageReduction, getConfig().damageReductionBase + (getLevelPercent(level) * getConfig().damageReductionFactor)); + int reducedAmp = Math.max(0, fatigue.getAmplifier() - getFatigueTrimAmount(level)); + p.addPotionEffect(new PotionEffect(PotionEffectType.MINING_FATIGUE, + Math.max(20, Math.min(fatigue.getDuration(), getConfig().fatigueReplaceTicks)), + reducedAmp, + false, + true, + true), true); + } + + private void awardDepthXp(Player p, long now) { + UUID id = p.getUniqueId(); + long next = xpCooldowns.getOrDefault(id, 0L); + if (now < next) { + return; } - private double getFatigueTrimChance(int level) { - return Math.min(1.0, getConfig().fatigueTrimChanceBase + (getLevelPercent(level) * getConfig().fatigueTrimChanceFactor)); + xp(p, getConfig().xpPerDepthPulse); + xpCooldowns.put(id, now + getConfig().xpPulseCooldownMillis); + } + + private boolean isDeepEnough(Player p, int level) { + double seaLevel = p.getWorld().getSeaLevel(); + double depth = seaLevel - p.getEyeLocation().getY(); + return depth >= getDepthThreshold(level); + } + + private int getResistanceAmplifier(int level, Player p) { + double seaLevel = p.getWorld().getSeaLevel(); + double depth = seaLevel - p.getEyeLocation().getY(); + if (depth >= getDeepThreshold(level)) { + return 1; } - private int getFatigueTrimAmount(int level) { - return Math.max(1, (int) Math.round(getConfig().fatigueTrimAmountBase + (getLevelPercent(level) * getConfig().fatigueTrimAmountFactor))); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Gain depth scaling protection underwater and partially counter mining fatigue in deep ocean play.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double depthThresholdBase = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double depthThresholdFactor = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double deepThresholdBase = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double deepThresholdFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageReductionBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageReductionFactor = 0.26; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Damage Reduction for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxDamageReduction = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fatigueTrimChanceBase = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fatigueTrimChanceFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fatigueTrimAmountBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double fatigueTrimAmountFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int effectTicks = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Replace Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int fatigueReplaceTicks = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Depth Pulse for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerDepthPulse = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Pulse Cooldown Millis for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long xpPulseCooldownMillis = 3000; - } + return 0; + } + + private double getDepthThreshold(int level) { + return Math.max(2, getConfig().depthThresholdBase - (getLevelPercent(level) * getConfig().depthThresholdFactor)); + } + + private double getDeepThreshold(int level) { + return Math.max(4, getConfig().deepThresholdBase - (getLevelPercent(level) * getConfig().deepThresholdFactor)); + } + + private double getDamageReduction(int level) { + return Math.min(getConfig().maxDamageReduction, getConfig().damageReductionBase + (getLevelPercent(level) * getConfig().damageReductionFactor)); + } + + private double getFatigueTrimChance(int level) { + return Math.min(1.0, getConfig().fatigueTrimChanceBase + (getLevelPercent(level) * getConfig().fatigueTrimChanceFactor)); + } + + private int getFatigueTrimAmount(int level) { + return Math.max(1, (int) Math.round(getConfig().fatigueTrimAmountBase + (getLevelPercent(level) * getConfig().fatigueTrimAmountFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain depth scaling protection underwater and partially counter mining fatigue in deep ocean play.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double depthThresholdBase = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double depthThresholdFactor = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double deepThresholdBase = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deep Threshold Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double deepThresholdFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageReductionBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Reduction Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageReductionFactor = 0.26; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Maximum Damage Reduction for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxDamageReduction = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fatigueTrimChanceBase = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Chance Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fatigueTrimChanceFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Base for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fatigueTrimAmountBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Trim Amount Factor for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double fatigueTrimAmountFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int effectTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Fatigue Replace Ticks for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int fatigueReplaceTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Depth Pulse for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDepthPulse = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Pulse Cooldown Millis for the Seaborne Pressure Diver adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long xpPulseCooldownMillis = 3000; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java index 9894fda29..0b9bee3a3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneSpeed.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -35,95 +35,95 @@ public class SeaborneSpeed extends SimpleAdaptation { - public SeaborneSpeed() { - super("seaborne-speed"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.dolphin_grace.description")); - setDisplayName(Localizer.dLocalize("seaborn.dolphin_grace.name")); - setIcon(Material.PRISMARINE_CRYSTALS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(1020); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.HEART_OF_THE_SEA) - .key("challenge_seaborne_speed_10k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_speed_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_speed_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TRIDENT) - .key("challenge_seaborne_speed_100k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_speed_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_speed_100k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_seaborne_speed_10k", "seaborne.speed.blocks-swum", 10000, 300); - registerMilestone("challenge_seaborne_speed_100k", "seaborne.speed.blocks-swum", 100000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("seaborn.dolphin_grace.lore1") + C.GREEN + (level) + C.GRAY + Localizer.dLocalize("seaborn.dolphin_grace.lore2")); - v.addLore(C.ITALIC + Localizer.dLocalize("seaborn.dolphin_grace.lore3")); - } + public SeaborneSpeed() { + super("seaborne-speed"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.dolphin_grace.description")); + setDisplayName(Localizer.dLocalize("seaborn.dolphin_grace.name")); + setIcon(Material.PRISMARINE_CRYSTALS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(1020); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.HEART_OF_THE_SEA) + .key("challenge_seaborne_speed_10k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_speed_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_speed_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TRIDENT) + .key("challenge_seaborne_speed_100k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_speed_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_speed_100k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_seaborne_speed_10k", "seaborne.speed.blocks-swum", 10000, 300); + registerMilestone("challenge_seaborne_speed_100k", "seaborne.speed.blocks-swum", 100000, 1500); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player player = adaptPlayer.getPlayer(); - if (player == null || !player.isOnline()) { - continue; - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("seaborn.dolphin_grace.lore1") + C.GREEN + (level) + C.GRAY + Localizer.dLocalize("seaborn.dolphin_grace.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("seaborn.dolphin_grace.lore3")); + } - withPlayerThread(player, () -> { - if (!player.isOnline()) { - return; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player player = adaptPlayer.getPlayer(); + if (player == null || !player.isOnline()) { + continue; + } - int level = getActiveLevel(player); - if (level <= 0 || !player.isInWater()) { - return; - } + withPlayerThread(player, () -> { + if (!player.isOnline()) { + return; + } - if (player.getInventory().getBoots() != null && player.getInventory().getBoots().containsEnchantment(Enchantment.DEPTH_STRIDER)) { - return; - } + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { + return; + } - player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, level)); - getPlayer(player).getData().addStat("seaborne.speed.blocks-swum", 1); - }); + if (player.getInventory().getBoots() != null && player.getInventory().getBoots().containsEnchantment(Enchantment.DEPTH_STRIDER)) { + return; } - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + player.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE, 62, level)); + getPlayer(player).getData().addStat("seaborne.speed.blocks-swum", 1); + }); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Swim faster with dolphin-like grace.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.525; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Swim faster with dolphin-like grace.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.525; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java index 21de4ba47..08aab25a9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTidecaller.java @@ -24,11 +24,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.FluidCollisionMode; import org.bukkit.Material; @@ -44,426 +44,426 @@ import org.bukkit.util.Vector; public class SeaborneTidecaller extends SimpleAdaptation { - public SeaborneTidecaller() { - super("seaborne-tidecaller"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.tidecaller.description")); - setDisplayName(Localizer.dLocalize("seaborn.tidecaller.name")); - setIcon(Material.HEART_OF_THE_SEA); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1600); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TRIDENT) - .key("challenge_seaborne_tidecaller_200") - .title(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_200.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HEART_OF_THE_SEA) - .key("challenge_seaborne_tidecaller_5k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_seaborne_tidecaller_200", "seaborne.tidecaller.dashes", 200, 300); - registerMilestone("challenge_seaborne_tidecaller_5k", "seaborne.tidecaller.dashes", 5000, 1000); + public SeaborneTidecaller() { + super("seaborne-tidecaller"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.tidecaller.description")); + setDisplayName(Localizer.dLocalize("seaborn.tidecaller.name")); + setIcon(Material.HEART_OF_THE_SEA); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1600); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TRIDENT) + .key("challenge_seaborne_tidecaller_200") + .title(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_200.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HEART_OF_THE_SEA) + .key("challenge_seaborne_tidecaller_5k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_tidecaller_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_seaborne_tidecaller_200", "seaborne.tidecaller.dashes", 200, 300); + registerMilestone("challenge_seaborne_tidecaller_5k", "seaborne.tidecaller.dashes", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getDashDistance(level), 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.tidecaller.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.tidecaller.lore2")); + java.util.List combos = getTriggerCombos(); + if (combos.isEmpty()) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); + } else { + for (String combo : combos) { + v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); + } } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getDashDistance(level), 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.tidecaller.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("seaborn.tidecaller.lore2")); - java.util.List combos = getTriggerCombos(); - if (combos.isEmpty()) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); - } else { - for (String combo : combos) { - v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + combo); - } - } - v.addLore(C.AQUA + "* " + C.GRAY + "Environment: " + C.WHITE + getEnvironmentSummary()); + v.addLore(C.AQUA + "* " + C.GRAY + "Environment: " + C.WHITE + getEnvironmentSummary()); + } + + @Override + public String getDescription() { + return "Surge forward with a water burst in valid environments. " + summarizeTriggerDescription(); + } + + private String summarizeTriggerDescription() { + java.util.List combos = getTriggerCombos(); + if (combos.isEmpty()) { + return "No active triggers are currently enabled."; } - @Override - public String getDescription() { - return "Surge forward with a water burst in valid environments. " + summarizeTriggerDescription(); + if (combos.size() == 1) { + return "Trigger: " + combos.get(0) + "."; } - private String summarizeTriggerDescription() { - java.util.List combos = getTriggerCombos(); - if (combos.isEmpty()) { - return "No active triggers are currently enabled."; - } - - if (combos.size() == 1) { - return "Trigger: " + combos.get(0) + "."; - } - - if (combos.size() == 2) { - return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; - } - - return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; + if (combos.size() == 2) { + return "Triggers: " + combos.get(0) + " or " + combos.get(1) + "."; } - private java.util.List getTriggerCombos() { - java.util.List triggers = new java.util.ArrayList<>(); - String env = getEnvironmentSummary(); - if (getConfig().enableSneakTrigger) { - triggers.add("Sneak" + (env.equals("none") ? "" : " (" + env + ")")); - } + return "Triggers: " + combos.get(0) + ", " + combos.get(1) + ", +" + (combos.size() - 2) + " more."; + } - if (getConfig().enableAttackTrigger) { - String combo = getConfig().attackTriggerRequiresSneak ? "Sneak + Attack" : "Attack"; - if (getConfig().attackTriggerWaterOnly) { - combo += " (water only)"; - } else if (!env.equals("none")) { - combo += " (" + env + ")"; - } - triggers.add(combo); - } + private java.util.List getTriggerCombos() { + java.util.List triggers = new java.util.ArrayList<>(); + String env = getEnvironmentSummary(); + if (getConfig().enableSneakTrigger) { + triggers.add("Sneak" + (env.equals("none") ? "" : " (" + env + ")")); + } - return triggers; + if (getConfig().enableAttackTrigger) { + String combo = getConfig().attackTriggerRequiresSneak ? "Sneak + Attack" : "Attack"; + if (getConfig().attackTriggerWaterOnly) { + combo += " (water only)"; + } else if (!env.equals("none")) { + combo += " (" + env + ")"; + } + triggers.add(combo); } - private String getEnvironmentSummary() { - java.util.List modes = new java.util.ArrayList<>(); - if (getConfig().allowWaterTrigger) { - modes.add("water"); - } + return triggers; + } - if (getConfig().allowRainTrigger) { - modes.add("rain"); - } + private String getEnvironmentSummary() { + java.util.List modes = new java.util.ArrayList<>(); + if (getConfig().allowWaterTrigger) { + modes.add("water"); + } - if (modes.isEmpty()) { - return "none"; - } + if (getConfig().allowRainTrigger) { + modes.add("rain"); + } - return String.join("/", modes); + if (modes.isEmpty()) { + return "none"; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (!e.isSneaking()) { - return; - } + return String.join("/", modes); + } - tryDash(p, TriggerType.SNEAK); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!e.isSneaking()) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerAnimationEvent e) { - if (e.getAnimationType() != PlayerAnimationType.ARM_SWING) { - return; - } + tryDash(p, TriggerType.SNEAK); + } - tryDash(e.getPlayer(), TriggerType.ATTACK); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerAnimationEvent e) { + if (e.getAnimationType() != PlayerAnimationType.ARM_SWING) { + return; } - private void tryDash(Player p, TriggerType triggerType) { - withAdaptedPlayer(p, () -> { - if (p.hasCooldown(Material.HEART_OF_THE_SEA)) { - return; - } - - if (triggerType == TriggerType.SNEAK && !getConfig().enableSneakTrigger) { - return; - } - - if (triggerType == TriggerType.ATTACK) { - if (!getConfig().enableAttackTrigger) { - return; - } - - if (getConfig().attackTriggerRequiresSneak && !p.isSneaking()) { - return; - } - - if (getConfig().attackTriggerWaterOnly && !isInWaterDashState(p)) { - return; - } - } - - if (!isDashEnvironmentValid(p)) { - return; - } - - int level = getActiveLevel(p); - Vector direction = resolveDashDirection(p); - if (direction.lengthSquared() <= 0.000001) { - return; - } - - if (isBlockedAhead(p, direction)) { - return; - } - - boolean wasSwimming = p.isSwimming(); - org.bukkit.Location target; - org.bukkit.Location origin = null; - - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.SPLASH, p.getLocation().add(0, 1, 0), 20, 0.25, 0.35, 0.25, 0.08); - } - - if (getConfig().useVelocityDash) { - applyVelocityDash(p, direction, level); - target = p.getLocation().clone().add(direction.clone().multiply(Math.max(0.35, getDashDistance(level) * 0.35))); - } else { - origin = p.getLocation().clone(); - target = findSafeDashTarget(p, getDashDistance(level)); - if (target == null) { - return; - } - J.teleport(p, target); - applyDashMomentum(p, origin, target); - } - - preserveSwimStateAfterDash(p, wasSwimming); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.SPLASH, target.clone().add(0, 1, 0), 30, 0.35, 0.45, 0.35, 0.08); - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.BUBBLE, target.clone().add(0, 0.9, 0), 18, 0.4, 0.35, 0.4, 0.05); - } - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ITEM_TRIDENT_RIPTIDE_2, 0.75f, 1.2f); - sp.play(target, Sound.ENTITY_DOLPHIN_SPLASH, 0.65f, 1.15f); - p.setCooldown(Material.HEART_OF_THE_SEA, getCooldownTicks(level)); - xp(p, getConfig().xpPerBurst); - getPlayer(p).getData().addStat("seaborne.tidecaller.dashes", 1); - }); - } + tryDash(e.getPlayer(), TriggerType.ATTACK); + } - private void applyVelocityDash(Player p, Vector direction, int level) { - Vector velocity = direction.clone().normalize(); - double scalar = Math.max(0, getConfig().velocityStrengthBase + (getLevelPercent(level) * getConfig().velocityStrengthFactor)); - velocity.multiply(scalar); + private void tryDash(Player p, TriggerType triggerType) { + withAdaptedPlayer(p, () -> { + if (p.hasCooldown(Material.HEART_OF_THE_SEA)) { + return; + } - double y = getConfig().velocityVerticalBase + (getLevelPercent(level) * getConfig().velocityVerticalFactor); - if (getConfig().velocityAdditive) { - velocity = p.getVelocity().clone().add(velocity).add(new Vector(0, y, 0)); - } else { - velocity.setY(y); - } + if (triggerType == TriggerType.SNEAK && !getConfig().enableSneakTrigger) { + return; + } - double max = Math.max(0, getConfig().maxResultingVelocity); - if (max > 0 && velocity.lengthSquared() > max * max) { - velocity = velocity.normalize().multiply(max); + if (triggerType == TriggerType.ATTACK) { + if (!getConfig().enableAttackTrigger) { + return; } - p.setVelocity(velocity); - } - - private void applyDashMomentum(Player p, org.bukkit.Location origin, org.bukkit.Location target) { - if (!getConfig().applyForwardMomentumAfterDash) { - return; + if (getConfig().attackTriggerRequiresSneak && !p.isSneaking()) { + return; } - Vector momentum = target.toVector().subtract(origin.toVector()); - if (momentum.lengthSquared() <= 0.000001) { - momentum = origin.getDirection().clone(); + if (getConfig().attackTriggerWaterOnly && !isInWaterDashState(p)) { + return; } - - momentum.normalize().multiply(Math.max(0, getConfig().forwardMomentum)); - double y = p.getVelocity().getY(); - if (getConfig().replaceVerticalMomentum) { - y = getConfig().verticalMomentum; - } else { - y += getConfig().verticalMomentum; + } + + if (!isDashEnvironmentValid(p)) { + return; + } + + int level = getActiveLevel(p); + Vector direction = resolveDashDirection(p); + if (direction.lengthSquared() <= 0.000001) { + return; + } + + if (isBlockedAhead(p, direction)) { + return; + } + + boolean wasSwimming = p.isSwimming(); + org.bukkit.Location target; + org.bukkit.Location origin = null; + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SPLASH, p.getLocation().add(0, 1, 0), 20, 0.25, 0.35, 0.25, 0.08); + } + + if (getConfig().useVelocityDash) { + applyVelocityDash(p, direction, level); + target = p.getLocation().clone().add(direction.clone().multiply(Math.max(0.35, getDashDistance(level) * 0.35))); + } else { + origin = p.getLocation().clone(); + target = findSafeDashTarget(p, getDashDistance(level)); + if (target == null) { + return; } - - momentum.setY(y); - p.setVelocity(momentum); + J.teleport(p, target); + applyDashMomentum(p, origin, target); + } + + preserveSwimStateAfterDash(p, wasSwimming); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SPLASH, target.clone().add(0, 1, 0), 30, 0.35, 0.45, 0.35, 0.08); + } + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.BUBBLE, target.clone().add(0, 0.9, 0), 18, 0.4, 0.35, 0.4, 0.05); + } + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ITEM_TRIDENT_RIPTIDE_2, 0.75f, 1.2f); + sp.play(target, Sound.ENTITY_DOLPHIN_SPLASH, 0.65f, 1.15f); + p.setCooldown(Material.HEART_OF_THE_SEA, getCooldownTicks(level)); + xp(p, getConfig().xpPerBurst); + getPlayer(p).getData().addStat("seaborne.tidecaller.dashes", 1); + }); + } + + private void applyVelocityDash(Player p, Vector direction, int level) { + Vector velocity = direction.clone().normalize(); + double scalar = Math.max(0, getConfig().velocityStrengthBase + (getLevelPercent(level) * getConfig().velocityStrengthFactor)); + velocity.multiply(scalar); + + double y = getConfig().velocityVerticalBase + (getLevelPercent(level) * getConfig().velocityVerticalFactor); + if (getConfig().velocityAdditive) { + velocity = p.getVelocity().clone().add(velocity).add(new Vector(0, y, 0)); + } else { + velocity.setY(y); } - private Vector resolveDashDirection(Player p) { - Vector direction = p.getLocation().getDirection().clone(); - if (getConfig().flattenVelocityDashDirection) { - direction.setY(0); - } - - if (direction.lengthSquared() <= 0.000001) { - return new Vector(); - } - - return direction.normalize(); + double max = Math.max(0, getConfig().maxResultingVelocity); + if (max > 0 && velocity.lengthSquared() > max * max) { + velocity = velocity.normalize().multiply(max); } - private boolean isBlockedAhead(Player p, Vector direction) { - if (!getConfig().blockDashWhenWallAhead) { - return false; - } + p.setVelocity(velocity); + } - double distance = Math.max(0.1, getConfig().wallCheckDistance); - var hit = p.getWorld().rayTraceBlocks(p.getEyeLocation(), direction, distance, FluidCollisionMode.NEVER, true); - return hit != null && hit.getHitBlock() != null; + private void applyDashMomentum(Player p, org.bukkit.Location origin, org.bukkit.Location target) { + if (!getConfig().applyForwardMomentumAfterDash) { + return; } - private void preserveSwimStateAfterDash(Player p, boolean wasSwimming) { - if (!getConfig().preserveSwimmingAfterDash || !wasSwimming) { - return; - } - - if (!isInWaterDashState(p)) { - return; - } + Vector momentum = target.toVector().subtract(origin.toVector()); + if (momentum.lengthSquared() <= 0.000001) { + momentum = origin.getDirection().clone(); + } - p.setSwimming(true); - J.runEntity(p, () -> { - if (p.isOnline() && isInWaterDashState(p)) { - p.setSwimming(true); - } - }, 1); + momentum.normalize().multiply(Math.max(0, getConfig().forwardMomentum)); + double y = p.getVelocity().getY(); + if (getConfig().replaceVerticalMomentum) { + y = getConfig().verticalMomentum; + } else { + y += getConfig().verticalMomentum; } - private boolean isDashEnvironmentValid(Player p) { - if (getConfig().allowWaterTrigger && isInWaterDashState(p)) { - return true; - } + momentum.setY(y); + p.setVelocity(momentum); + } - return getConfig().allowRainTrigger && isRainingAt(p); + private Vector resolveDashDirection(Player p) { + Vector direction = p.getLocation().getDirection().clone(); + if (getConfig().flattenVelocityDashDirection) { + direction.setY(0); } - private boolean isInWaterDashState(Player p) { - return p.isInWater() || p.isSwimming() || p.getLocation().getBlock().isLiquid() || p.getEyeLocation().getBlock().isLiquid(); + if (direction.lengthSquared() <= 0.000001) { + return new Vector(); } - private boolean isRainingAt(Player p) { - if (!p.getWorld().hasStorm()) { - return false; - } + return direction.normalize(); + } - int topY = p.getWorld().getHighestBlockYAt(p.getLocation()); - return p.getLocation().getY() >= topY - 1; + private boolean isBlockedAhead(Player p, Vector direction) { + if (!getConfig().blockDashWhenWallAhead) { + return false; } - private org.bukkit.Location findSafeDashTarget(Player p, double maxDistance) { - Vector direction = p.getLocation().getDirection().clone(); - if (direction.lengthSquared() <= 0.0001) { - return null; - } - - direction.normalize(); - for (double d = maxDistance; d >= 1.0; d -= 0.5) { - org.bukkit.Location c = p.getLocation().clone().add(direction.clone().multiply(d)); - if (isSafe(c)) { - return c; - } - } + double distance = Math.max(0.1, getConfig().wallCheckDistance); + org.bukkit.util.RayTraceResult hit = p.getWorld().rayTraceBlocks(p.getEyeLocation(), direction, distance, FluidCollisionMode.NEVER, true); + return hit != null && hit.getHitBlock() != null; + } - return null; + private void preserveSwimStateAfterDash(Player p, boolean wasSwimming) { + if (!getConfig().preserveSwimmingAfterDash || !wasSwimming) { + return; } - private boolean isSafe(org.bukkit.Location location) { - Block feet = location.getBlock(); - Block head = location.clone().add(0, 1, 0).getBlock(); - Block floor = location.clone().subtract(0, 1, 0).getBlock(); - return feet.isPassable() && head.isPassable() && (floor.getType().isSolid() || floor.isLiquid()); + if (!isInWaterDashState(p)) { + return; } - private double getDashDistance(int level) { - return getConfig().dashDistanceBase + (getLevelPercent(level) * getConfig().dashDistanceFactor); - } + p.setSwimming(true); + J.runEntity(p, () -> { + if (p.isOnline() && isInWaterDashState(p)) { + p.setSwimming(true); + } + }, 1); + } - private int getCooldownTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + private boolean isDashEnvironmentValid(Player p) { + if (getConfig().allowWaterTrigger && isInWaterDashState(p)) { + return true; } - private enum TriggerType { - SNEAK, - ATTACK - } + return getConfig().allowRainTrigger && isRainingAt(p); + } - @Override - public void onTick() { + private boolean isInWaterDashState(Player p) { + return p.isInWater() || p.isSwimming() || p.getLocation().getBlock().isLiquid() || p.getEyeLocation().getBlock().isLiquid(); + } + private boolean isRainingAt(Player p) { + if (!p.getWorld().hasStorm()) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + int topY = p.getWorld().getHighestBlockYAt(p.getLocation()); + return p.getLocation().getY() >= topY - 1; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; + private org.bukkit.Location findSafeDashTarget(Player p, double maxDistance) { + Vector direction = p.getLocation().getDirection().clone(); + if (direction.lengthSquared() <= 0.0001) { + return null; } - @NoArgsConstructor - @ConfigDescription("Sneak while it is raining to dash like a water blink through the storm.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dashDistanceBase = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dashDistanceFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 140; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Burst for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBurst = 11; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows the original rain-based trigger for Tidecaller dashes.", impact = "Disable this to make dashes water-only.") - boolean allowRainTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows Tidecaller dashes while the player is in water.", impact = "Enable this to make sneak/attack water woosh work consistently.") - boolean allowWaterTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sneak-to-dash trigger.", impact = "Disable this if you only want attack-based trigger behavior.") - boolean enableSneakTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables attack-swing trigger (any item or empty hand).", impact = "Enable this for left-click water flings without requiring special items.") - boolean enableAttackTrigger = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Requires sneaking for attack-swing trigger.", impact = "True makes attack trigger only fire while sneaking; false allows it anytime in valid dash environments.") - boolean attackTriggerRequiresSneak = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Restricts attack-swing trigger to water states only.", impact = "True prevents accidental attack-trigger dashes on land even if rain trigger is enabled.") - boolean attackTriggerWaterOnly = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Uses velocity-based dash movement instead of teleporting to a target.", impact = "True makes tidecaller behave like a movement burst and prevents blink-style wall bypass.") - boolean useVelocityDash = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, removes pitch from velocity dash direction.", impact = "True keeps movement mostly horizontal; false follows exact look direction including up/down.") - boolean flattenVelocityDashDirection = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base forward velocity strength of a velocity dash.", impact = "Higher values produce faster bursts.") - double velocityStrengthBase = 1.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Additional forward velocity strength gained by adaptation level.", impact = "Higher values make higher levels burst farther/faster.") - double velocityStrengthFactor = 0.85; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base vertical velocity contribution for velocity dashes.", impact = "Small positive values keep water movement fluid; lower values stay flatter.") - double velocityVerticalBase = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Additional vertical velocity contribution gained by adaptation level.", impact = "Higher values increase upward kick at higher levels.") - double velocityVerticalFactor = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Adds dash velocity on top of current velocity when true.", impact = "True preserves momentum chains; false applies a fresh velocity vector.") - boolean velocityAdditive = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on resulting velocity magnitude after dash.", impact = "Lower values are safer for anticheat and collisions.") - double maxResultingVelocity = 2.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Cancels dash if a solid block is detected directly ahead.", impact = "Prevents wall clipping and blink-like wall bypass.") - boolean blockDashWhenWallAhead = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Distance ahead used to detect blocking walls.", impact = "Higher values are safer but can block dashes near tight spaces.") - double wallCheckDistance = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Applies forward velocity after the dash teleport.", impact = "True makes the dash feel fluid instead of instantly stopping at the target.") - boolean applyForwardMomentumAfterDash = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal forward momentum applied after the dash teleport.", impact = "Higher values create a stronger forward burst after each dash.") - double forwardMomentum = 1.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical momentum added or set after the dash teleport.", impact = "Small positive values help keep water movement smooth; negative values push downward.") - double verticalMomentum = 0.02; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, replaces current vertical velocity with verticalMomentum.", impact = "False adds verticalMomentum on top of existing vertical motion.") - boolean replaceVerticalMomentum = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Re-applies swimming pose after dash when the player started swimming and remains in water.", impact = "Prevents dash teleports from popping swimmers out of swim posture.") - boolean preserveSwimmingAfterDash = true; + direction.normalize(); + for (double d = maxDistance; d >= 1.0; d -= 0.5) { + org.bukkit.Location c = p.getLocation().clone().add(direction.clone().multiply(d)); + if (isSafe(c)) { + return c; + } } + + return null; + } + + private boolean isSafe(org.bukkit.Location location) { + Block feet = location.getBlock(); + Block head = location.clone().add(0, 1, 0).getBlock(); + Block floor = location.clone().subtract(0, 1, 0).getBlock(); + return feet.isPassable() && head.isPassable() && (floor.getType().isSolid() || floor.isLiquid()); + } + + private double getDashDistance(int level) { + return getConfig().dashDistanceBase + (getLevelPercent(level) * getConfig().dashDistanceFactor); + } + + private int getCooldownTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private enum TriggerType { + SNEAK, + ATTACK + } + + @NoArgsConstructor + @ConfigDescription("Sneak while it is raining to dash like a water blink through the storm.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dashDistanceBase = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dash Distance Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dashDistanceFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 140; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Burst for the Seaborne Tidecaller adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBurst = 11; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows the original rain-based trigger for Tidecaller dashes.", impact = "Disable this to make dashes water-only.") + boolean allowRainTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows Tidecaller dashes while the player is in water.", impact = "Enable this to make sneak/attack water woosh work consistently.") + boolean allowWaterTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables sneak-to-dash trigger.", impact = "Disable this if you only want attack-based trigger behavior.") + boolean enableSneakTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables attack-swing trigger (any item or empty hand).", impact = "Enable this for left-click water flings without requiring special items.") + boolean enableAttackTrigger = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires sneaking for attack-swing trigger.", impact = "True makes attack trigger only fire while sneaking; false allows it anytime in valid dash environments.") + boolean attackTriggerRequiresSneak = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Restricts attack-swing trigger to water states only.", impact = "True prevents accidental attack-trigger dashes on land even if rain trigger is enabled.") + boolean attackTriggerWaterOnly = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses velocity-based dash movement instead of teleporting to a target.", impact = "True makes tidecaller behave like a movement burst and prevents blink-style wall bypass.") + boolean useVelocityDash = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, removes pitch from velocity dash direction.", impact = "True keeps movement mostly horizontal; false follows exact look direction including up/down.") + boolean flattenVelocityDashDirection = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base forward velocity strength of a velocity dash.", impact = "Higher values produce faster bursts.") + double velocityStrengthBase = 1.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional forward velocity strength gained by adaptation level.", impact = "Higher values make higher levels burst farther/faster.") + double velocityStrengthFactor = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base vertical velocity contribution for velocity dashes.", impact = "Small positive values keep water movement fluid; lower values stay flatter.") + double velocityVerticalBase = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional vertical velocity contribution gained by adaptation level.", impact = "Higher values increase upward kick at higher levels.") + double velocityVerticalFactor = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Adds dash velocity on top of current velocity when true.", impact = "True preserves momentum chains; false applies a fresh velocity vector.") + boolean velocityAdditive = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on resulting velocity magnitude after dash.", impact = "Lower values are safer for anticheat and collisions.") + double maxResultingVelocity = 2.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cancels dash if a solid block is detected directly ahead.", impact = "Prevents wall clipping and blink-like wall bypass.") + boolean blockDashWhenWallAhead = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Distance ahead used to detect blocking walls.", impact = "Higher values are safer but can block dashes near tight spaces.") + double wallCheckDistance = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Applies forward velocity after the dash teleport.", impact = "True makes the dash feel fluid instead of instantly stopping at the target.") + boolean applyForwardMomentumAfterDash = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal forward momentum applied after the dash teleport.", impact = "Higher values create a stronger forward burst after each dash.") + double forwardMomentum = 1.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical momentum added or set after the dash teleport.", impact = "Small positive values help keep water movement smooth; negative values push downward.") + double verticalMomentum = 0.02; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, replaces current vertical velocity with verticalMomentum.", impact = "False adds verticalMomentum on top of existing vertical motion.") + boolean replaceVerticalMomentum = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Re-applies swimming pose after dash when the player started swimming and remains in water.", impact = "Prevents dash teleports from popping swimmers out of swim posture.") + boolean preserveSwimmingAfterDash = true; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java index 797c155b6..1f14ee72f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesMiningSpeed.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -34,91 +34,91 @@ public class SeaborneTurtlesMiningSpeed extends SimpleAdaptation { - public SeaborneTurtlesMiningSpeed() { - super("seaborne-turtles-mining-speed"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.haste.description")); - setDisplayName(Localizer.dLocalize("seaborn.haste.name")); - setIcon(Material.PRISMARINE_SHARD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(3000); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_seaborne_mining_2500") - .title(Localizer.dLocalize("advancement.challenge_seaborne_mining_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_mining_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_seaborne_mining_25k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_mining_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_mining_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_seaborne_mining_2500", "seaborne.turtles-mining.blocks-underwater", 2500, 300); - registerMilestone("challenge_seaborne_mining_25k", "seaborne.turtles-mining.blocks-underwater", 25000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("seaborn.haste.lore1")); - } + public SeaborneTurtlesMiningSpeed() { + super("seaborne-turtles-mining-speed"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.haste.description")); + setDisplayName(Localizer.dLocalize("seaborn.haste.name")); + setIcon(Material.PRISMARINE_SHARD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(3000); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_seaborne_mining_2500") + .title(Localizer.dLocalize("advancement.challenge_seaborne_mining_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_mining_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_seaborne_mining_25k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_mining_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_mining_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_seaborne_mining_2500", "seaborne.turtles-mining.blocks-underwater", 2500, 300); + registerMilestone("challenge_seaborne_mining_25k", "seaborne.turtles-mining.blocks-underwater", 25000, 1000); + } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("seaborn.haste.lore1")); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player player = adaptPlayer.getPlayer(); - if (player == null || !player.isOnline()) { - continue; - } - withPlayerThread(player, () -> { - if (!player.isOnline()) { - return; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player player = adaptPlayer.getPlayer(); + if (player == null || !player.isOnline()) { + continue; + } - int level = getActiveLevel(player); - if (level <= 0 || !player.isInWater()) { - return; - } + withPlayerThread(player, () -> { + if (!player.isOnline()) { + return; + } - player.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 62, 1, false, false)); - getPlayer(player).getData().addStat("seaborne.turtles-mining.blocks-underwater", 1); - }); + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { + return; } - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + player.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, 62, 1, false, false)); + getPlayer(player).getData().addStat("seaborne.turtles-mining.blocks-underwater", 1); + }); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Gain haste while mining underwater.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain haste while mining underwater.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java index 9bca9bcfe..439a28a56 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeaborneTurtlesVision.java @@ -24,8 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -34,82 +34,82 @@ public class SeaborneTurtlesVision extends SimpleAdaptation { - public SeaborneTurtlesVision() { - super("seaborne-turtles-vision"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("seaborn.night_vision.description")); - setDisplayName(Localizer.dLocalize("seaborn.night_vision.name")); - setIcon(Material.DIAMOND_HORSE_ARMOR); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(3000); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TURTLE_HELMET) - .key("challenge_seaborne_vision_72k") - .title(Localizer.dLocalize("advancement.challenge_seaborne_vision_72k.title")) - .description(Localizer.dLocalize("advancement.challenge_seaborne_vision_72k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_seaborne_vision_72k", "seaborne.turtles-vision.time-underwater", 72000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("seaborn.night_vision.lore1")); - } + public SeaborneTurtlesVision() { + super("seaborne-turtles-vision"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("seaborn.night_vision.description")); + setDisplayName(Localizer.dLocalize("seaborn.night_vision.name")); + setIcon(Material.DIAMOND_HORSE_ARMOR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(3000); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TURTLE_HELMET) + .key("challenge_seaborne_vision_72k") + .title(Localizer.dLocalize("advancement.challenge_seaborne_vision_72k.title")) + .description(Localizer.dLocalize("advancement.challenge_seaborne_vision_72k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_seaborne_vision_72k", "seaborne.turtles-vision.time-underwater", 72000, 400); + } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("seaborn.night_vision.lore1")); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player player = adaptPlayer.getPlayer(); - if (player == null || !player.isOnline()) { - continue; - } - withPlayerThread(player, () -> { - if (!player.isOnline()) { - return; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player player = adaptPlayer.getPlayer(); + if (player == null || !player.isOnline()) { + continue; + } - int level = getActiveLevel(player); - if (level <= 0 || !player.isInWater()) { - return; - } + withPlayerThread(player, () -> { + if (!player.isOnline()) { + return; + } - player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 62, 0, false, false)); - getPlayer(player).getData().addStat("seaborne.turtles-vision.time-underwater", 1); - }); + int level = getActiveLevel(player); + if (level <= 0 || !player.isInWater()) { + return; } - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + player.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 62, 0, false, false)); + getPlayer(player).getData().addStat("seaborne.turtles-vision.time-underwater", 1); + }); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Gain night vision while underwater.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain night vision while underwater.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java index f7041b8ac..26a76662f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthEnderVeil.java @@ -6,10 +6,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.events.api.ReflectiveHandler; import art.arcane.adapt.util.reflect.events.api.entity.EndermanAttackPlayerEvent; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.EntityType; @@ -20,97 +20,97 @@ public class StealthEnderVeil extends SimpleAdaptation { - public StealthEnderVeil() { - super("stealth-enderveil"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.ender_veil.description")); - setDisplayName(Localizer.dLocalize("stealth.ender_veil.name")); - setIcon(Material.CARVED_PUMPKIN); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - setInterval(9182); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_stealth_ender_veil_200") - .title(Localizer.dLocalize("advancement.challenge_stealth_ender_veil_200.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_ender_veil_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_stealth_ender_veil_200", "stealth.ender-veil.stares-survived", 200, 300); - } + public StealthEnderVeil() { + super("stealth-enderveil"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.ender_veil.description")); + setDisplayName(Localizer.dLocalize("stealth.ender_veil.name")); + setIcon(Material.CARVED_PUMPKIN); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + setInterval(9182); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_stealth_ender_veil_200") + .title(Localizer.dLocalize("advancement.challenge_stealth_ender_veil_200.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_ender_veil_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_stealth_ender_veil_200", "stealth.ender-veil.stares-survived", 200, 300); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("stealth.ender_veil.lore" + (level < 2 ? 1 : 2))); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("stealth.ender_veil.lore" + (level < 2 ? 1 : 2))); + } - @Override - public void onTick() { + @Override + public void onTick() { - } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void onTarget(EntityTargetLivingEntityEvent event) { - var target = event.getTarget(); - if (target == null - || target.getType() != EntityType.PLAYER - || event.getEntityType() != EntityType.ENDERMAN - || !(event.getTarget() instanceof Player player)) { - return; - } + } - int level = getActiveLevel(player); - if (level <= 0) { - return; - } + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onTarget(EntityTargetLivingEntityEvent event) { + org.bukkit.entity.LivingEntity target = event.getTarget(); + if (target == null + || target.getType() != EntityType.PLAYER + || event.getEntityType() != EntityType.ENDERMAN + || !(event.getTarget() instanceof Player player)) { + return; + } - if (level > 1 || player.isSneaking()) { - event.setCancelled(true); - getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); - } + int level = getActiveLevel(player); + if (level <= 0) { + return; } - @ReflectiveHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void onTarget(EndermanAttackPlayerEvent event) { - var player = event.getPlayer(); - int level = getActiveLevel(player); - if (level <= 0) { - return; - } + if (level > 1 || player.isSneaking()) { + event.setCancelled(true); + getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); + } + } - if (level > 1 || player.isSneaking()) { - event.setCancelled(true); - getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); - } + @ReflectiveHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onTarget(EndermanAttackPlayerEvent event) { + org.bukkit.entity.Player player = event.getPlayer(); + int level = getActiveLevel(player); + if (level <= 0) { + return; } - @NoArgsConstructor - @ConfigDescription("Prevent Enderman aggression without wearing a pumpkin.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 1.0; + if (level > 1 || player.isSneaking()) { + event.setCancelled(true); + getPlayer(player).getData().addStat("stealth.ender-veil.stares-survived", 1); } + } + + @NoArgsConstructor + @ConfigDescription("Prevent Enderman aggression without wearing a pumpkin.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 1.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java index 44e7a2aca..d0cb6f834 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthGhostArmor.java @@ -26,11 +26,11 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -44,128 +44,132 @@ import java.util.UUID; public class StealthGhostArmor extends SimpleAdaptation { - private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-ghost-armor".getBytes()); - private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:ghost-armor"); - - public StealthGhostArmor() { - super("stealth-ghost-armor"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.ghost_armor.description")); - setDisplayName(Localizer.dLocalize("stealth.ghost_armor.name")); - setIcon(Material.CHAINMAIL_HELMET); - setInterval(5353); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_CHESTPLATE) - .key("challenge_stealth_ghost_100") - .title(Localizer.dLocalize("advancement.challenge_stealth_ghost_100.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_ghost_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHAINMAIL_CHESTPLATE) - .key("challenge_stealth_ghost_500") - .title(Localizer.dLocalize("advancement.challenge_stealth_ghost_500.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_ghost_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_ghost_100", "stealth.ghost-armor.armor-consumed", 100, 300); - registerMilestone("challenge_stealth_ghost_500", "stealth.ghost-armor.armor-consumed", 500, 1000); - } + private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-ghost-armor".getBytes()); + private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString("adapt:ghost-armor"); - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getMaxArmorPoints(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("stealth.ghost_armor.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getMaxArmorPerTick(getLevelPercent(level)), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.ghost_armor.lore2")); - } + public StealthGhostArmor() { + super("stealth-ghost-armor"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.ghost_armor.description")); + setDisplayName(Localizer.dLocalize("stealth.ghost_armor.name")); + setIcon(Material.CHAINMAIL_HELMET); + setInterval(5353); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_CHESTPLATE) + .key("challenge_stealth_ghost_100") + .title(Localizer.dLocalize("advancement.challenge_stealth_ghost_100.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_ghost_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHAINMAIL_CHESTPLATE) + .key("challenge_stealth_ghost_500") + .title(Localizer.dLocalize("advancement.challenge_stealth_ghost_500.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_ghost_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_ghost_100", "stealth.ghost-armor.armor-consumed", 100, 300); + registerMilestone("challenge_stealth_ghost_500", "stealth.ghost-armor.armor-consumed", 500, 1000); + } - public double getMaxArmorPoints(double factor) { - return M.lerp(getConfig().minArmor, getConfig().maxArmor, factor); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getMaxArmorPoints(getLevelPercent(level)), 0) + C.GRAY + " " + Localizer.dLocalize("stealth.ghost_armor.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getMaxArmorPerTick(getLevelPercent(level)), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.ghost_armor.lore2")); + } - public double getMaxArmorPerTick(double factor) { - return M.lerp(getConfig().minArmorPerTick, getConfig().maxArmorPerTick, factor); - } + public double getMaxArmorPoints(double factor) { + return M.lerp(getConfig().minArmor, getConfig().maxArmor, factor); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + public double getMaxArmorPerTick(double factor) { + return M.lerp(getConfig().minArmorPerTick, getConfig().maxArmorPerTick, factor); + } - if (!hasActiveAdaptation(p)) { - attribute.removeModifier(MODIFIER, MODIFIER_KEY); - continue; - } - double oldArmor = attribute.getModifier(MODIFIER, MODIFIER_KEY) - .stream() - .mapToDouble(IAttribute.Modifier::getAmount) - .filter(d -> !Double.isNaN(d)) - .max() - .orElse(0);; - double armor = getMaxArmorPoints(getLevelPercent(p)); - armor = Double.isNaN(armor) ? 0 : armor; + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + if (attribute == null) { + continue; + } - if (oldArmor < armor) { - attribute.setModifier(MODIFIER, MODIFIER_KEY, Math.min(armor, oldArmor + getMaxArmorPerTick(getLevelPercent(p))), AttributeModifier.Operation.ADD_NUMBER); - } else if (oldArmor > armor) { - attribute.setModifier(MODIFIER, MODIFIER_KEY, armor, AttributeModifier.Operation.ADD_NUMBER); - } + if (!hasActiveAdaptation(p)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + continue; + } + double oldArmor = 0; + for (IAttribute.Modifier modifier : attribute.getModifier(MODIFIER, MODIFIER_KEY)) { + double amount = modifier.getAmount(); + if (!Double.isNaN(amount) && amount > oldArmor) { + oldArmor = amount; } - } + } + double armor = getMaxArmorPoints(getLevelPercent(p)); + armor = Double.isNaN(armor) ? 0 : armor; - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (e.getEntity() instanceof Player p && hasActiveAdaptation(p) && e.getDamage() > 0) { - // Check if 2.5 * e.getDamage() is greater than 10 if so just set it to 10 otherwise use the value of 2.5 * e.getDamage() - int damageXP = (int) Math.min(10, 2.5 * e.getDamage()); - xp(p,damageXP ); - getPlayer(p).getData().addStat("stealth.ghost-armor.armor-consumed", 1); - J.runEntity(p, () -> { - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); - if (attribute == null) return; - attribute.removeModifier(MODIFIER, MODIFIER_KEY); - }); - } + if (oldArmor < armor) { + attribute.setModifier(MODIFIER, MODIFIER_KEY, Math.min(armor, oldArmor + getMaxArmorPerTick(getLevelPercent(p))), AttributeModifier.Operation.ADD_NUMBER); + } else if (oldArmor > armor) { + attribute.setModifier(MODIFIER, MODIFIER_KEY, armor, AttributeModifier.Operation.ADD_NUMBER); + } } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (e.getEntity() instanceof Player p && hasActiveAdaptation(p) && e.getDamage() > 0) { + // Check if 2.5 * e.getDamage() is greater than 10 if so just set it to 10 otherwise use the value of 2.5 * e.getDamage() + int damageXP = (int) Math.min(10, 2.5 * e.getDamage()); + xp(p, damageXP); + getPlayer(p).getData().addStat("stealth.ghost-armor.armor-consumed", 1); + J.runEntity(p, () -> { + IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_ARMOR); + if (attribute == null) return; + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + }); } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - @ConfigDescription("Slowly build armor when not taking damage, consumed on the next hit.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxArmor = 16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int minArmor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int maxArmorPerTick = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int minArmorPerTick = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.335; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Slowly build armor when not taking damage, consumed on the next hit.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxArmor = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int minArmor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxArmorPerTick = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Armor Per Tick for the Stealth Ghost Armor adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int minArmorPerTick = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.335; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index 22065182f..c1af34994 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -18,17 +18,16 @@ package art.arcane.adapt.content.adaptation.stealth; -import art.arcane.adapt.Adapt; import art.arcane.adapt.api.adaptation.SimpleAdaptation; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.*; import org.bukkit.entity.*; @@ -40,1267 +39,439 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.inventory.EntityEquipment; -import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; public class StealthShadowDecoy extends SimpleAdaptation { - private static final PacketDecoyBridge PACKET_DECOY = PacketDecoyBridge.create(); - - private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map activeDecoys = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map anchorOwners = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map ownerEquipmentMaskSync = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map ownerTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map ownerAggroNextAt = new java.util.concurrent.ConcurrentHashMap<>(); - - public StealthShadowDecoy() { - super("stealth-shadow-decoy"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.shadow_decoy.description")); - setDisplayName(Localizer.dLocalize("stealth.shadow_decoy.name")); - setIcon(Material.PLAYER_HEAD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(5); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARMOR_STAND) - .key("challenge_stealth_decoy_100") - .title(Localizer.dLocalize("advancement.challenge_stealth_decoy_100.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_decoy_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARMOR_STAND) - .key("challenge_stealth_decoy_distract_500") - .title(Localizer.dLocalize("advancement.challenge_stealth_decoy_distract_500.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_decoy_distract_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_stealth_decoy_100", "stealth.shadow-decoy.decoys-spawned", 100, 300); - registerMilestone("challenge_stealth_decoy_distract_500", "stealth.shadow-decoy.mobs-distracted", 500, 1000); + private static final PacketDecoyBridge PACKET_DECOY = PacketDecoyBridge.create(); + + private final Map cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map activeDecoys = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map anchorOwners = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerEquipmentMaskSync = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map ownerAggroNextAt = new java.util.concurrent.ConcurrentHashMap<>(); + + public StealthShadowDecoy() { + super("stealth-shadow-decoy"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.shadow_decoy.description")); + setDisplayName(Localizer.dLocalize("stealth.shadow_decoy.name")); + setIcon(Material.PLAYER_HEAD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARMOR_STAND) + .key("challenge_stealth_decoy_100") + .title(Localizer.dLocalize("advancement.challenge_stealth_decoy_100.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_decoy_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARMOR_STAND) + .key("challenge_stealth_decoy_distract_500") + .title(Localizer.dLocalize("advancement.challenge_stealth_decoy_distract_500.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_decoy_distract_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_stealth_decoy_100", "stealth.shadow-decoy.decoys-spawned", 100, 300); + registerMilestone("challenge_stealth_decoy_distract_500", "stealth.shadow-decoy.mobs-distracted", 500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getDecoyTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getDecoyRadius(level)) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + cooldowns.remove(id); + ownerEquipmentMaskSync.remove(id); + ownerTrailNextAt.remove(id); + ownerAggroNextAt.remove(id); + DecoyState state = activeDecoys.remove(id); + if (state != null) { + removeDecoy(state, null); } + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration(getDecoyTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getDecoyRadius(level)) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.shadow_decoy.lore3")); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (anchorOwners.containsKey(e.getEntity().getUniqueId())) { + e.setCancelled(true); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - cooldowns.remove(id); - ownerEquipmentMaskSync.remove(id); - ownerTrailNextAt.remove(id); - ownerAggroNextAt.remove(id); - DecoyState state = activeDecoys.remove(id); - if (state != null) { - removeDecoy(state, null); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + UUID ownerId = anchorOwners.get(e.getEntity().getUniqueId()); + if (ownerId == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageEvent e) { - if (anchorOwners.containsKey(e.getEntity().getUniqueId())) { - e.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - UUID ownerId = anchorOwners.get(e.getEntity().getUniqueId()); - if (ownerId == null) { - return; - } - - e.setCancelled(true); - DecoyState state = activeDecoys.get(ownerId); - if (state == null) { - return; - } - - if (!(e.getEntity() instanceof ArmorStand stand) || !(e.getDamager() instanceof LivingEntity attacker)) { - return; - } - - reactToDecoyHit(state, stand, attacker); + e.setCancelled(true); + DecoyState state = activeDecoys.get(ownerId); + if (state == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerAnimationEvent e) { - Player attacker = e.getPlayer(); - if (activeDecoys.isEmpty()) { - return; - } - - RayTraceResult hit = attacker.getWorld().rayTraceEntities( - attacker.getEyeLocation(), - attacker.getEyeLocation().getDirection(), - Math.max(1.0, getConfig().decoySwingDetectionReach), - entity -> anchorOwners.containsKey(entity.getUniqueId()) - ); - - if (hit == null || !(hit.getHitEntity() instanceof ArmorStand stand)) { - return; - } - - UUID ownerId = anchorOwners.get(stand.getUniqueId()); - if (ownerId == null) { - return; - } - - DecoyState state = activeDecoys.get(ownerId); - if (state == null) { - return; - } - - reactToDecoyHit(state, stand, attacker); + if (!(e.getEntity() instanceof ArmorStand stand) || !(e.getDamager() instanceof LivingEntity attacker)) { + return; } - private void reactToDecoyHit(DecoyState state, ArmorStand stand, LivingEntity attacker) { - if (state.packetDecoy() != null) { - state.packetDecoy().hitFrom(attacker.getLocation()); - } + reactToDecoyHit(state, stand, attacker); + } - Vector push = stand.getLocation().toVector().subtract(attacker.getLocation().toVector()); - if (push.lengthSquared() < 0.0001) { - push = attacker.getLocation().getDirection().multiply(-1); - } - - push.setY(0); - push.normalize().multiply(Math.max(0, getConfig().decoyHitKnockback)); - push.setY(Math.max(0, getConfig().decoyHitLift)); - stand.setVelocity(push); - SoundPlayer.of(stand.getWorld()).play(stand.getLocation(), Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerAnimationEvent e) { + Player attacker = e.getPlayer(); + if (activeDecoys.isEmpty()) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (e.isSneaking()) { - return; - } - - int level = getActiveLevel(p); - if (level <= 0) { - return; - } + RayTraceResult hit = attacker.getWorld().rayTraceEntities( + attacker.getEyeLocation(), + attacker.getEyeLocation().getDirection(), + Math.max(1.0, getConfig().decoySwingDetectionReach), + entity -> anchorOwners.containsKey(entity.getUniqueId()) + ); - long now = System.currentTimeMillis(); - if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { - return; - } - - spawnDecoy(p, level); - cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); - xp(p, getConfig().xpOnDecoy); - getPlayer(p).getData().addStat("stealth.shadow-decoy.decoys-spawned", 1); + if (hit == null || !(hit.getHitEntity() instanceof ArmorStand stand)) { + return; } - private void spawnDecoy(Player owner, int level) { - DecoyState previous = activeDecoys.remove(owner.getUniqueId()); - if (previous != null) { - removeDecoy(previous, owner); - } - - ArmorStand anchor = spawnAnchor(owner.getLocation()); - anchorOwners.put(anchor.getUniqueId(), owner.getUniqueId()); - PacketPlayerDecoy packetDecoy = PACKET_DECOY.spawnDecoy(owner, anchor, getConfig().tabListRemoveDelayTicks, getConfig().decoySkinLayerMask); - - if (packetDecoy == null && getConfig().legacyFallbackEnabled) { - configureLegacyVisual(anchor, owner); - } - - long expiresAt = System.currentTimeMillis() + (getDecoyTicks(level) * 50L); - UUID ownerId = owner.getUniqueId(); - activeDecoys.put(ownerId, new DecoyState(ownerId, anchor.getUniqueId(), packetDecoy, expiresAt, level)); - ownerTrailNextAt.put(ownerId, 0L); - ownerAggroNextAt.put(ownerId, 0L); - - redirectAggro(owner, anchor, level); - if (areParticlesEnabled()) { - anchor.getWorld().spawnParticle(Particle.SMOKE, anchor.getLocation().add(0, 1, 0), 18, 0.2, 0.4, 0.2, 0.03); - } - SoundPlayer.of(owner.getWorld()).play(owner.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.6f, 1.7f); + UUID ownerId = anchorOwners.get(stand.getUniqueId()); + if (ownerId == null) { + return; } - private ArmorStand spawnAnchor(Location location) { - return location.getWorld().spawn(location, ArmorStand.class, stand -> { - stand.setMarker(false); - stand.setVisible(false); - stand.setInvisible(true); - stand.setGravity(true); - stand.setInvulnerable(false); - stand.setSilent(true); - stand.setBasePlate(false); - stand.setSmall(false); - stand.setArms(false); - stand.setCollidable(true); - }); + DecoyState state = activeDecoys.get(ownerId); + if (state == null) { + return; } - private void configureLegacyVisual(ArmorStand stand, Player owner) { - stand.setMarker(false); - stand.setVisible(true); - stand.setInvisible(false); - stand.setSmall(false); - stand.setArms(true); - stand.setCustomNameVisible(true); - stand.setCustomName(C.GRAY + owner.getName()); + reactToDecoyHit(state, stand, attacker); + } - EntityEquipment equipment = stand.getEquipment(); - if (equipment == null) { - return; - } - - equipment.setHelmet(owner.getInventory().getHelmet()); - equipment.setChestplate(owner.getInventory().getChestplate()); - equipment.setLeggings(owner.getInventory().getLeggings()); - equipment.setBoots(owner.getInventory().getBoots()); - equipment.setItemInMainHand(owner.getInventory().getItemInMainHand()); - equipment.setItemInOffHand(owner.getInventory().getItemInOffHand()); + private void reactToDecoyHit(DecoyState state, ArmorStand stand, LivingEntity attacker) { + if (state.packetDecoy() != null) { + state.packetDecoy().hitFrom(attacker.getLocation()); } - private void redirectAggro(Player owner, LivingEntity target, int level) { - double radius = getDecoyRadius(level); - Location center = target.getLocation(); - for (Entity entity : owner.getWorld().getNearbyEntities(center, radius, radius, radius)) { - if (!(entity instanceof Mob mob)) { - continue; - } - - if (mob.getTarget() == owner || mob.hasLineOfSight(owner)) { - mob.setTarget(target); - getPlayer(owner).getData().addStat("stealth.shadow-decoy.mobs-distracted", 1); - } - } + Vector push = stand.getLocation().toVector().subtract(attacker.getLocation().toVector()); + if (push.lengthSquared() < 0.0001) { + push = attacker.getLocation().getDirection().multiply(-1); } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - Iterator> it = activeDecoys.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - UUID ownerId = entry.getKey(); - DecoyState state = entry.getValue(); - Player owner = Bukkit.getPlayer(ownerId); - - if (owner == null || !owner.isOnline() || state.expiresAt() <= now) { - removeDecoy(state, owner); - it.remove(); - continue; - } - - Entity entity = Bukkit.getEntity(state.anchorId()); - if (!(entity instanceof ArmorStand anchor) || !anchor.isValid()) { - removeDecoy(state, owner); - it.remove(); - continue; - } - - PacketPlayerDecoy packetDecoy = state.packetDecoy(); - if (packetDecoy != null) { - packetDecoy.tick(); - packetDecoy.syncToAnchor(anchor.getLocation(), anchor.isOnGround()); - packetDecoy.lookAtViewers(anchor.getLocation().add(0, getConfig().decoyEyeHeight, 0)); - } - - applyOwnerInvisibility(owner); - syncOwnerEquipmentHidden(owner); - if (now >= ownerTrailNextAt.getOrDefault(ownerId, 0L)) { - spawnOwnerTrail(owner); - ownerTrailNextAt.put(ownerId, now + Math.max(25L, getConfig().ownerTrailIntervalMillis)); - } - if (now >= ownerAggroNextAt.getOrDefault(ownerId, 0L)) { - redirectAggro(owner, anchor, state.level()); - ownerAggroNextAt.put(ownerId, now + Math.max(25L, getConfig().aggroRedirectIntervalMillis)); - } - } + push.setY(0); + push.normalize().multiply(Math.max(0, getConfig().decoyHitKnockback)); + push.setY(Math.max(0, getConfig().decoyHitLift)); + stand.setVelocity(push); + SoundPlayer.of(stand.getWorld()).play(stand.getLocation(), Sound.ENTITY_PLAYER_HURT, 0.8f, 1.2f); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (e.isSneaking()) { + return; } - private void applyOwnerInvisibility(Player owner) { - int duration = Math.max(20, getConfig().ownerInvisibilityRefreshTicks); - PotionEffect current = owner.getPotionEffect(PotionEffectType.INVISIBILITY); - if (current != null && current.getDuration() > duration + 5) { - return; - } - - owner.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, duration, getConfig().ownerInvisibilityAmplifier, false, false, false), true); + int level = getActiveLevel(p); + if (level <= 0) { + return; } - private void spawnOwnerTrail(Player owner) { - owner.getWorld().spawnParticle( - Particle.SMOKE, - owner.getLocation().add(0, getConfig().ownerTrailYOffset, 0), - Math.max(1, getConfig().ownerTrailParticles), - Math.max(0, getConfig().ownerTrailHorizontalSpread), - Math.max(0, getConfig().ownerTrailVerticalSpread), - Math.max(0, getConfig().ownerTrailHorizontalSpread), - Math.max(0, getConfig().ownerTrailSpeed) - ); + long now = System.currentTimeMillis(); + if (now < cooldowns.getOrDefault(p.getUniqueId(), 0L)) { + return; } - private void syncOwnerEquipmentHidden(Player owner) { - long now = System.currentTimeMillis(); - long nextAt = ownerEquipmentMaskSync.getOrDefault(owner.getUniqueId(), 0L); - if (now < nextAt) { - return; - } + spawnDecoy(p, level); + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + xp(p, getConfig().xpOnDecoy); + getPlayer(p).getData().addStat("stealth.shadow-decoy.decoys-spawned", 1); + } - PACKET_DECOY.sendOwnerEquipment(owner, true); - ownerEquipmentMaskSync.put(owner.getUniqueId(), now + Math.max(100L, getConfig().ownerEquipmentHideResendMillis)); + private void spawnDecoy(Player owner, int level) { + DecoyState previous = activeDecoys.remove(owner.getUniqueId()); + if (previous != null) { + removeDecoy(previous, owner); } - private void restoreOwnerEquipment(Player owner) { - if (owner == null || !owner.isOnline()) { - return; - } + ArmorStand anchor = spawnAnchor(owner.getLocation()); + anchorOwners.put(anchor.getUniqueId(), owner.getUniqueId()); + PacketPlayerDecoy packetDecoy = PACKET_DECOY.spawnDecoy(owner, anchor, getConfig().tabListRemoveDelayTicks, getConfig().decoySkinLayerMask); - ownerEquipmentMaskSync.remove(owner.getUniqueId()); - PACKET_DECOY.sendOwnerEquipment(owner, false); + if (packetDecoy == null && getConfig().legacyFallbackEnabled) { + configureLegacyVisual(anchor, owner); } - private void removeDecoy(DecoyState state, Player owner) { - if (state.packetDecoy() != null) { - state.packetDecoy().destroy(); - } - - Entity entity = Bukkit.getEntity(state.anchorId()); - anchorOwners.remove(state.anchorId()); - ownerTrailNextAt.remove(state.ownerId()); - ownerAggroNextAt.remove(state.ownerId()); - if (entity instanceof ArmorStand stand && stand.isValid()) { - stand.remove(); - } + long expiresAt = System.currentTimeMillis() + (getDecoyTicks(level) * 50L); + UUID ownerId = owner.getUniqueId(); + activeDecoys.put(ownerId, new DecoyState(ownerId, anchor.getUniqueId(), packetDecoy, expiresAt, level)); + ownerTrailNextAt.put(ownerId, 0L); + ownerAggroNextAt.put(ownerId, 0L); - restoreOwnerEquipment(owner); + redirectAggro(owner, anchor, level); + if (areParticlesEnabled()) { + anchor.getWorld().spawnParticle(Particle.SMOKE, anchor.getLocation().add(0, 1, 0), 18, 0.2, 0.4, 0.2, 0.03); } - - private long getCooldownMillis(int level) { - return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + SoundPlayer.of(owner.getWorld()).play(owner.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.6f, 1.7f); + } + + private ArmorStand spawnAnchor(Location location) { + return location.getWorld().spawn(location, ArmorStand.class, stand -> { + stand.setMarker(false); + stand.setVisible(false); + stand.setInvisible(true); + stand.setGravity(true); + stand.setInvulnerable(false); + stand.setSilent(true); + stand.setBasePlate(false); + stand.setSmall(false); + stand.setArms(false); + stand.setCollidable(true); + }); + } + + private void configureLegacyVisual(ArmorStand stand, Player owner) { + stand.setMarker(false); + stand.setVisible(true); + stand.setInvisible(false); + stand.setSmall(false); + stand.setArms(true); + stand.setCustomNameVisible(true); + stand.setCustomName(C.GRAY + owner.getName()); + + EntityEquipment equipment = stand.getEquipment(); + if (equipment == null) { + return; } - private int getDecoyTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().decoyTicksBase + (getLevelPercent(level) * getConfig().decoyTicksFactor))); + equipment.setHelmet(owner.getInventory().getHelmet()); + equipment.setChestplate(owner.getInventory().getChestplate()); + equipment.setLeggings(owner.getInventory().getLeggings()); + equipment.setBoots(owner.getInventory().getBoots()); + equipment.setItemInMainHand(owner.getInventory().getItemInMainHand()); + equipment.setItemInOffHand(owner.getInventory().getItemInOffHand()); + } + + private void redirectAggro(Player owner, LivingEntity target, int level) { + double radius = getDecoyRadius(level); + Location center = target.getLocation(); + for (Entity entity : owner.getWorld().getNearbyEntities(center, radius, radius, radius)) { + if (!(entity instanceof Mob mob)) { + continue; + } + + if (mob.getTarget() == owner || mob.hasLineOfSight(owner)) { + mob.setTarget(target); + getPlayer(owner).getData().addStat("stealth.shadow-decoy.mobs-distracted", 1); + } } - - private double getDecoyRadius(int level) { - return getConfig().decoyRadiusBase + (getLevelPercent(level) * getConfig().decoyRadiusFactor); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + Iterator> it = activeDecoys.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + UUID ownerId = entry.getKey(); + DecoyState state = entry.getValue(); + Player owner = Bukkit.getPlayer(ownerId); + + if (owner == null || !owner.isOnline() || state.expiresAt() <= now) { + removeDecoy(state, owner); + it.remove(); + continue; + } + + Entity entity = Bukkit.getEntity(state.anchorId()); + if (!(entity instanceof ArmorStand anchor) || !anchor.isValid()) { + removeDecoy(state, owner); + it.remove(); + continue; + } + + PacketPlayerDecoy packetDecoy = state.packetDecoy(); + if (packetDecoy != null) { + packetDecoy.tick(); + packetDecoy.syncToAnchor(anchor.getLocation(), anchor.isOnGround()); + packetDecoy.lookAtViewers(anchor.getLocation().add(0, getConfig().decoyEyeHeight, 0)); + } + + applyOwnerInvisibility(owner); + syncOwnerEquipmentHidden(owner); + if (now >= ownerTrailNextAt.getOrDefault(ownerId, 0L)) { + spawnOwnerTrail(owner); + ownerTrailNextAt.put(ownerId, now + Math.max(25L, getConfig().ownerTrailIntervalMillis)); + } + if (now >= ownerAggroNextAt.getOrDefault(ownerId, 0L)) { + redirectAggro(owner, anchor, state.level()); + ownerAggroNextAt.put(ownerId, now + Math.max(25L, getConfig().aggroRedirectIntervalMillis)); + } } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + private void applyOwnerInvisibility(Player owner) { + int duration = Math.max(20, getConfig().ownerInvisibilityRefreshTicks); + PotionEffect current = owner.getPotionEffect(PotionEffectType.INVISIBILITY); + if (current != null && current.getDuration() > duration + 5) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + owner.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, duration, getConfig().ownerInvisibilityAmplifier, false, false, false), true); + } + + private void spawnOwnerTrail(Player owner) { + owner.getWorld().spawnParticle( + Particle.SMOKE, + owner.getLocation().add(0, getConfig().ownerTrailYOffset, 0), + Math.max(1, getConfig().ownerTrailParticles), + Math.max(0, getConfig().ownerTrailHorizontalSpread), + Math.max(0, getConfig().ownerTrailVerticalSpread), + Math.max(0, getConfig().ownerTrailHorizontalSpread), + Math.max(0, getConfig().ownerTrailSpeed) + ); + } + + private void syncOwnerEquipmentHidden(Player owner) { + long now = System.currentTimeMillis(); + long nextAt = ownerEquipmentMaskSync.getOrDefault(owner.getUniqueId(), 0L); + if (now < nextAt) { + return; } - @NoArgsConstructor - @ConfigDescription("Stopping sneak spawns a short-lived shadow decoy that pulls your current aggro.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base cooldown after creating a decoy, in milliseconds.", impact = "Higher values mean longer time between activations.") - double cooldownMillisBase = 18000; - @art.arcane.adapt.util.config.ConfigDoc(value = "How much cooldown is reduced by leveling.", impact = "Higher values reduce cooldown more at higher levels.") - double cooldownMillisFactor = 12000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base active duration in ticks.", impact = "Higher values keep decoys active longer.") - double decoyTicksBase = 60; - @art.arcane.adapt.util.config.ConfigDoc(value = "Duration scaling from level, in ticks.", impact = "Higher values extend duration more per level.") - double decoyTicksFactor = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base aggro redirect radius.", impact = "Higher values pull aggro from farther away.") - double decoyRadiusBase = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Aggro radius scaling from level.", impact = "Higher values expand pull range more per level.") - double decoyRadiusFactor = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Visual eye height used for fake player facing.", impact = "Adjust if head rotation appears too high or too low.") - double decoyEyeHeight = 1.62; - @art.arcane.adapt.util.config.ConfigDoc(value = "Delay before removing the fake player from tab list, in ticks.", impact = "Small values hide tab entries faster; larger values help skins load.") - int tabListRemoveDelayTicks = -1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows armor stand visual fallback if packet NPC creation fails.", impact = "Turn off to disable fallback visuals on incompatible server builds.") - boolean legacyFallbackEnabled = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Refresh duration for owner invisibility while a decoy is active.", impact = "Higher values keep invisibility active longer between refreshes.") - int ownerInvisibilityRefreshTicks = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier for the temporary invisibility effect.", impact = "Most servers should leave this at 0.") - int ownerInvisibilityAmplifier = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Smoke particles emitted around the invisible owner each tick while decoy is active.", impact = "Higher values create a stronger visible trail.") - int ownerTrailParticles = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal spread for owner smoke trail.", impact = "Higher values make the trail wider.") - double ownerTrailHorizontalSpread = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical spread for owner smoke trail.", impact = "Higher values make the trail taller.") - double ownerTrailVerticalSpread = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for smoke trail spawn location.", impact = "Adjust to move trail closer to feet or torso.") - double ownerTrailYOffset = 0.1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Particle speed for owner smoke trail.", impact = "Higher values make trail movement more turbulent.") - double ownerTrailSpeed = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between owner trail particle bursts while decoy is active.", impact = "Lower values make the owner trail denser; higher values reduce particle cost.") - long ownerTrailIntervalMillis = 75; - @art.arcane.adapt.util.config.ConfigDoc(value = "How often owner equipment-hide packets are resent while invisible, in milliseconds.", impact = "Lower values keep visuals tighter for joining viewers, higher values reduce packet traffic.") - long ownerEquipmentHideResendMillis = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between aggro redirect scans while a decoy is active.", impact = "Lower values pull mobs more aggressively; higher values reduce nearby-entity scan cost.") - long aggroRedirectIntervalMillis = 150; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal knockback applied when the decoy is hit.", impact = "Higher values make the decoy react more dramatically when struck.") - double decoyHitKnockback = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical lift applied when the decoy is hit.", impact = "Higher values make impacts pop the decoy upward more.") - double decoyHitLift = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Swing ray distance used to detect decoy hits.", impact = "Higher values make swings connect from farther away.") - double decoySwingDetectionReach = 4.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Bitmask for visible skin layers on the fake player decoy.", impact = "127 enables all standard skin layers (hat, jacket, sleeves, pants).") - int decoySkinLayerMask = 127; - @art.arcane.adapt.util.config.ConfigDoc(value = "Experience granted on each decoy spawn.", impact = "Higher values level the adaptation faster.") - double xpOnDecoy = 18; - } + PACKET_DECOY.sendOwnerEquipment(owner, true); + ownerEquipmentMaskSync.put(owner.getUniqueId(), now + Math.max(100L, getConfig().ownerEquipmentHideResendMillis)); + } - private record DecoyState(UUID ownerId, UUID anchorId, PacketPlayerDecoy packetDecoy, long expiresAt, int level) { + private void restoreOwnerEquipment(Player owner) { + if (owner == null || !owner.isOnline()) { + return; } - private static final class PacketPlayerDecoy { - private final PacketDecoyBridge bridge; - private final World world; - private final UUID profileId; - private final int entityId; - private final Object nmsEntity; - private final long removeTabAt; - private final Set knownViewers; - private boolean removedFromTab; - private Object spawnPlayerInfoPacket; - private Object spawnAddEntityPacket; - private Object spawnMetadataPacket; - private Object spawnEquipmentPacket; - private long lastPositionSyncAt; - private double lastX; - private double lastY; - private double lastZ; - private float lastYaw; - private float lastPitch; - - private PacketPlayerDecoy(PacketDecoyBridge bridge, World world, UUID profileId, int entityId, Object nmsEntity, int tabListRemoveDelayTicks) { - this.bridge = bridge; - this.world = world; - this.profileId = profileId; - this.entityId = entityId; - this.nmsEntity = nmsEntity; - this.removeTabAt = System.currentTimeMillis() + Math.max(0, tabListRemoveDelayTicks) * 50L; - this.knownViewers = java.util.concurrent.ConcurrentHashMap.newKeySet(); - this.removedFromTab = false; - this.spawnPlayerInfoPacket = null; - this.spawnAddEntityPacket = null; - this.spawnMetadataPacket = null; - this.spawnEquipmentPacket = null; - this.lastPositionSyncAt = 0L; - this.lastX = Double.NaN; - this.lastY = Double.NaN; - this.lastZ = Double.NaN; - this.lastYaw = Float.NaN; - this.lastPitch = Float.NaN; - } - - public void spawn(Object playerInfoPacket, Object addEntityPacket, Object metadataPacket, Object equipmentPacket) { - this.spawnPlayerInfoPacket = playerInfoPacket; - this.spawnAddEntityPacket = addEntityPacket; - this.spawnMetadataPacket = metadataPacket; - this.spawnEquipmentPacket = equipmentPacket; - ensureViewerState(); - } - - public void tick() { - ensureViewerState(); - if (removeTabAt < 0 || removedFromTab || System.currentTimeMillis() < removeTabAt) { - return; - } - - Object removePacket = bridge.createPlayerInfoRemovePacket(profileId); - if (removePacket != null) { - for (Player viewer : spawnedViewerPlayers()) { - bridge.sendPacket(viewer, removePacket); - } - } + ownerEquipmentMaskSync.remove(owner.getUniqueId()); + PACKET_DECOY.sendOwnerEquipment(owner, false); + } - removedFromTab = true; - } - - public void lookAtViewers(Location origin) { - ensureViewerState(); - for (Player viewer : spawnedViewerPlayers()) { - Location to = viewer.getEyeLocation(); - if (origin.getWorld() != to.getWorld()) { - continue; - } - - double dx = to.getX() - origin.getX(); - double dy = to.getY() - origin.getY(); - double dz = to.getZ() - origin.getZ(); - double horizontal = Math.sqrt(dx * dx + dz * dz); - float yaw = (float) Math.toDegrees(Math.atan2(-dx, dz)); - float pitch = (float) Math.toDegrees(-Math.atan2(dy, horizontal)); - bridge.applyLook(this, yaw, pitch, viewer); - } - } - - public void syncToAnchor(Location anchor, boolean onGround) { - ensureViewerState(); - long now = System.currentTimeMillis(); - double dx = Double.isFinite(lastX) ? anchor.getX() - lastX : 1; - double dy = Double.isFinite(lastY) ? anchor.getY() - lastY : 1; - double dz = Double.isFinite(lastZ) ? anchor.getZ() - lastZ : 1; - double distanceSq = (dx * dx) + (dy * dy) + (dz * dz); - float yawDiff = Float.isFinite(lastYaw) ? Math.abs(anchor.getYaw() - lastYaw) : 360f; - float pitchDiff = Float.isFinite(lastPitch) ? Math.abs(anchor.getPitch() - lastPitch) : 360f; - - if (distanceSq < 0.0004 && yawDiff < 0.8f && pitchDiff < 0.8f && now - lastPositionSyncAt < 45L) { - return; - } - - if (bridge.syncPosition(this, anchor, onGround, spawnedViewerPlayers())) { - lastPositionSyncAt = now; - lastX = anchor.getX(); - lastY = anchor.getY(); - lastZ = anchor.getZ(); - lastYaw = anchor.getYaw(); - lastPitch = anchor.getPitch(); - } - } - - public void hitFrom(Location source) { - ensureViewerState(); - if (!Double.isFinite(lastX) || !Double.isFinite(lastZ)) { - bridge.sendHurtAnimation(this, 0f, spawnedViewerPlayers()); - return; - } - - double dx = source.getX() - lastX; - double dz = source.getZ() - lastZ; - float yaw = (float) Math.toDegrees(Math.atan2(-dx, dz)); - bridge.sendHurtAnimation(this, yaw, spawnedViewerPlayers()); - } - - public void destroy() { - Object removeEntityPacket = bridge.createRemoveEntityPacket(entityId); - Object removePlayerInfoPacket = bridge.createPlayerInfoRemovePacket(profileId); - - for (Player viewer : spawnedViewerPlayers()) { - if (removeEntityPacket != null) { - bridge.sendPacket(viewer, removeEntityPacket); - } - if (removePlayerInfoPacket != null) { - bridge.sendPacket(viewer, removePlayerInfoPacket); - } - } - - knownViewers.clear(); - } - - private void ensureViewerState() { - Set online = new HashSet<>(); - for (Player viewer : world.getPlayers()) { - if (!viewer.isOnline()) { - continue; - } - - UUID id = viewer.getUniqueId(); - online.add(id); - if (!knownViewers.contains(id)) { - spawnFor(viewer); - knownViewers.add(id); - } - } - - knownViewers.retainAll(online); - } - - private void spawnFor(Player viewer) { - if (spawnPlayerInfoPacket != null) { - bridge.sendPacket(viewer, spawnPlayerInfoPacket); - } - - if (spawnAddEntityPacket != null) { - bridge.sendPacket(viewer, spawnAddEntityPacket); - } - - if (spawnMetadataPacket != null) { - bridge.sendPacket(viewer, spawnMetadataPacket); - } - - if (spawnEquipmentPacket != null) { - bridge.sendPacket(viewer, spawnEquipmentPacket); - } - - if (removedFromTab) { - Object removePacket = bridge.createPlayerInfoRemovePacket(profileId); - if (removePacket != null) { - bridge.sendPacket(viewer, removePacket); - } - } - } - - private List spawnedViewerPlayers() { - List viewers = new ArrayList<>(); - for (Player viewer : world.getPlayers()) { - if (viewer.isOnline() && knownViewers.contains(viewer.getUniqueId())) { - viewers.add(viewer); - } - } - - return viewers; - } + private void removeDecoy(DecoyState state, Player owner) { + if (state.packetDecoy() != null) { + state.packetDecoy().destroy(); } - private static final class PacketDecoyBridge { - private final boolean supported; - - private final Method craftServerGetServer; - private final Method craftWorldGetHandle; - private final Method craftPlayerGetHandle; - - private final Constructor serverPlayerConstructor; - private final Method clientInformationCreateDefault; - - private final Constructor gameProfileBasicConstructor; - private final Constructor gameProfileWithPropertiesConstructor; - private final Method gameProfilePropertiesAccessor; - private final Method playerGetGameProfile; - - private final Method entitySetPos; - private final Method entitySetRot; - private final Method entitySetOnGround; - private final Method livingSetYHeadRot; - private final Method livingSetYBodyRot; - private final Method entityGetId; - private final Method entityGetType; - private final Method entityGetEntityData; - private final Method synchedEntityDataGetNonDefaultValues; - private final Method synchedEntityDataPackAll; - private final Method synchedEntityDataSet; - - private final Method playerInfoCreateSingleInitializing; - private final Constructor playerInfoActionConstructor; - private final Constructor playerInfoFromEntriesConstructor; - private final Constructor playerInfoEntryExplicitConstructor; - private final Class playerInfoActionClass; - private final Object defaultGameType; - - private final Constructor addEntityConstructor; - private final Constructor addEntityExplicitConstructor; - private final Field blockPosZero; - private final Field vec3Zero; - private final Constructor setEntityDataConstructor; - private final Constructor moveEntityRotConstructor; - private final Constructor rotateHeadConstructor; - private final Constructor removeEntitiesConstructor; - private final Constructor playerInfoRemoveConstructor; - private final Method entityPositionSyncOf; - private final Constructor hurtAnimationConstructor; - private final Constructor setEquipmentConstructor; - private final Method pairOfMethod; - private final Method craftItemStackAsNmsCopy; - private final Class equipmentSlotClass; - private final Field avatarModelCustomizationAccessor; - - private final Field serverPlayerConnectionField; - private final Method connectionSendPacket; - - private PacketDecoyBridge() throws ReflectiveOperationException { - String craftPackage = Bukkit.getServer().getClass().getPackage().getName(); - if (!craftPackage.startsWith("org.bukkit.craftbukkit")) { - throw new ClassNotFoundException("CraftBukkit package not detected: " + craftPackage); - } - - Class craftServerClass = Class.forName(craftPackage + ".CraftServer"); - Class craftWorldClass = Class.forName(craftPackage + ".CraftWorld"); - Class craftPlayerClass = Class.forName(craftPackage + ".entity.CraftPlayer"); - - Class minecraftServerClass = Class.forName("net.minecraft.server.MinecraftServer"); - Class serverLevelClass = Class.forName("net.minecraft.server.level.ServerLevel"); - Class serverPlayerClass = Class.forName("net.minecraft.server.level.ServerPlayer"); - Class clientInformationClass = Class.forName("net.minecraft.server.level.ClientInformation"); - Class nmsPlayerClass = Class.forName("net.minecraft.world.entity.player.Player"); - Class livingEntityClass = Class.forName("net.minecraft.world.entity.LivingEntity"); - Class entityClass = Class.forName("net.minecraft.world.entity.Entity"); - Class entityTypeClass = Class.forName("net.minecraft.world.entity.EntityType"); - Class avatarClass = Class.forName("net.minecraft.world.entity.Avatar"); - Class equipmentSlotClass = Class.forName("net.minecraft.world.entity.EquipmentSlot"); - Class entityDataAccessorClass = Class.forName("net.minecraft.network.syncher.EntityDataAccessor"); - Class synchedEntityDataClass = Class.forName("net.minecraft.network.syncher.SynchedEntityData"); - Class gameProfileClass = Class.forName("com.mojang.authlib.GameProfile"); - Class vec3Class = Class.forName("net.minecraft.world.phys.Vec3"); - Class pairClass = Class.forName("com.mojang.datafixers.util.Pair"); - Class packetClass = Class.forName("net.minecraft.network.protocol.Packet"); - Class connectionClass = Class.forName("net.minecraft.server.network.ServerCommonPacketListenerImpl"); - Class playerInfoPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket"); - Class playerInfoEntryClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry"); - Class playerInfoActionEnumClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action"); - Class gameTypeClass = Class.forName("net.minecraft.world.level.GameType"); - Class componentClass = Class.forName("net.minecraft.network.chat.Component"); - Class remoteChatSessionDataClass = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data"); - Class addEntityPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket"); - Class entityPositionSyncPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket"); - Class hurtAnimationPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket"); - Class blockPosClass = Class.forName("net.minecraft.core.BlockPos"); - Class setEntityDataPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket"); - Class moveEntityRotClass = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot"); - Class rotateHeadPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket"); - Class removeEntitiesPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket"); - Class playerInfoRemovePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket"); - Class setEquipmentPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket"); - Class craftItemStackClass = Class.forName(craftPackage + ".inventory.CraftItemStack"); - - this.craftServerGetServer = craftServerClass.getMethod("getServer"); - this.craftWorldGetHandle = craftWorldClass.getMethod("getHandle"); - this.craftPlayerGetHandle = craftPlayerClass.getMethod("getHandle"); - - this.serverPlayerConstructor = serverPlayerClass.getConstructor(minecraftServerClass, serverLevelClass, gameProfileClass, clientInformationClass); - this.clientInformationCreateDefault = clientInformationClass.getMethod("createDefault"); - - this.gameProfileBasicConstructor = findConstructor(gameProfileClass, UUID.class, String.class); - this.gameProfileWithPropertiesConstructor = findOptionalConstructor(gameProfileClass, UUID.class, String.class, findPropertyMapClass(gameProfileClass)); - this.gameProfilePropertiesAccessor = findOptionalMethod(gameProfileClass, "properties", "getProperties"); - this.playerGetGameProfile = nmsPlayerClass.getMethod("getGameProfile"); - - this.entitySetPos = entityClass.getMethod("setPos", double.class, double.class, double.class); - this.entitySetRot = entityClass.getMethod("setRot", float.class, float.class); - this.entitySetOnGround = entityClass.getMethod("setOnGround", boolean.class); - this.livingSetYHeadRot = livingEntityClass.getMethod("setYHeadRot", float.class); - this.livingSetYBodyRot = livingEntityClass.getMethod("setYBodyRot", float.class); - this.entityGetId = entityClass.getMethod("getId"); - this.entityGetType = entityClass.getMethod("getType"); - this.entityGetEntityData = entityClass.getMethod("getEntityData"); - this.synchedEntityDataGetNonDefaultValues = synchedEntityDataClass.getMethod("getNonDefaultValues"); - this.synchedEntityDataPackAll = findOptionalMethod(synchedEntityDataClass, "packAll", new Class[0]); - this.synchedEntityDataSet = synchedEntityDataClass.getMethod("set", entityDataAccessorClass, Object.class); - - this.playerInfoCreateSingleInitializing = findOptionalMethod(playerInfoPacketClass, "createSinglePlayerInitializing", serverPlayerClass, boolean.class); - this.playerInfoActionConstructor = findOptionalConstructor(playerInfoPacketClass, playerInfoActionEnumClass, serverPlayerClass); - this.playerInfoFromEntriesConstructor = findOptionalConstructor(playerInfoPacketClass, EnumSet.class, List.class); - this.playerInfoEntryExplicitConstructor = findOptionalConstructor(playerInfoEntryClass, UUID.class, gameProfileClass, boolean.class, int.class, gameTypeClass, componentClass, boolean.class, int.class, remoteChatSessionDataClass); - this.playerInfoActionClass = playerInfoActionEnumClass; - this.defaultGameType = resolveDefaultGameType(gameTypeClass); - - this.addEntityConstructor = addEntityPacketClass.getConstructor(entityClass, int.class, blockPosClass); - this.addEntityExplicitConstructor = findOptionalConstructor(addEntityPacketClass, int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, entityTypeClass, int.class, vec3Class, double.class); - this.blockPosZero = blockPosClass.getField("ZERO"); - this.vec3Zero = vec3Class.getField("ZERO"); - this.setEntityDataConstructor = setEntityDataPacketClass.getConstructor(int.class, List.class); - this.moveEntityRotConstructor = moveEntityRotClass.getConstructor(int.class, byte.class, byte.class, boolean.class); - this.rotateHeadConstructor = rotateHeadPacketClass.getConstructor(entityClass, byte.class); - this.removeEntitiesConstructor = removeEntitiesPacketClass.getConstructor(int[].class); - this.playerInfoRemoveConstructor = playerInfoRemovePacketClass.getConstructor(List.class); - this.entityPositionSyncOf = entityPositionSyncPacketClass.getMethod("of", entityClass); - this.hurtAnimationConstructor = findOptionalConstructor(hurtAnimationPacketClass, int.class, float.class); - this.setEquipmentConstructor = findOptionalConstructor(setEquipmentPacketClass, int.class, List.class); - this.pairOfMethod = pairClass.getMethod("of", Object.class, Object.class); - this.craftItemStackAsNmsCopy = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class); - this.equipmentSlotClass = equipmentSlotClass; - this.avatarModelCustomizationAccessor = avatarClass.getField("DATA_PLAYER_MODE_CUSTOMISATION"); - - this.serverPlayerConnectionField = serverPlayerClass.getField("connection"); - this.connectionSendPacket = connectionClass.getMethod("send", packetClass); - this.supported = true; - } - - private PacketDecoyBridge(boolean supported) { - this.supported = supported; - this.craftServerGetServer = null; - this.craftWorldGetHandle = null; - this.craftPlayerGetHandle = null; - this.serverPlayerConstructor = null; - this.clientInformationCreateDefault = null; - this.gameProfileBasicConstructor = null; - this.gameProfileWithPropertiesConstructor = null; - this.gameProfilePropertiesAccessor = null; - this.playerGetGameProfile = null; - this.entitySetPos = null; - this.entitySetRot = null; - this.entitySetOnGround = null; - this.livingSetYHeadRot = null; - this.livingSetYBodyRot = null; - this.entityGetId = null; - this.entityGetType = null; - this.entityGetEntityData = null; - this.synchedEntityDataGetNonDefaultValues = null; - this.synchedEntityDataPackAll = null; - this.synchedEntityDataSet = null; - this.playerInfoCreateSingleInitializing = null; - this.playerInfoActionConstructor = null; - this.playerInfoFromEntriesConstructor = null; - this.playerInfoEntryExplicitConstructor = null; - this.playerInfoActionClass = null; - this.defaultGameType = null; - this.addEntityConstructor = null; - this.addEntityExplicitConstructor = null; - this.blockPosZero = null; - this.vec3Zero = null; - this.setEntityDataConstructor = null; - this.moveEntityRotConstructor = null; - this.rotateHeadConstructor = null; - this.removeEntitiesConstructor = null; - this.playerInfoRemoveConstructor = null; - this.entityPositionSyncOf = null; - this.hurtAnimationConstructor = null; - this.setEquipmentConstructor = null; - this.pairOfMethod = null; - this.craftItemStackAsNmsCopy = null; - this.equipmentSlotClass = null; - this.avatarModelCustomizationAccessor = null; - this.serverPlayerConnectionField = null; - this.connectionSendPacket = null; - } - - public static PacketDecoyBridge create() { - try { - return new PacketDecoyBridge(); - } catch (Throwable e) { - Adapt.warn("Shadow decoy fake-player bridge unavailable: " + e.getClass().getSimpleName() + " " + e.getMessage()); - return new PacketDecoyBridge(false); - } - } - - public PacketPlayerDecoy spawnDecoy(Player owner, ArmorStand anchor, int tabListRemoveDelayTicks, int skinLayerMask) { - Location location = anchor.getLocation(); - if (!supported || location.getWorld() == null) { - return null; - } - - try { - Object ownerHandle = craftPlayerGetHandle.invoke(owner); - Object ownerProfile = playerGetGameProfile.invoke(ownerHandle); - UUID profileId = UUID.randomUUID(); - Object profile = createProfile(profileId, owner.getName(), ownerProfile); - - Object minecraftServer = craftServerGetServer.invoke(Bukkit.getServer()); - Object worldHandle = craftWorldGetHandle.invoke(location.getWorld()); - Object clientInfo = clientInformationCreateDefault.invoke(null); - Object nmsDecoy = serverPlayerConstructor.newInstance(minecraftServer, worldHandle, profile, clientInfo); - - entitySetPos.invoke(nmsDecoy, location.getX(), location.getY(), location.getZ()); - entitySetRot.invoke(nmsDecoy, location.getYaw(), location.getPitch()); - livingSetYHeadRot.invoke(nmsDecoy, location.getYaw()); - livingSetYBodyRot.invoke(nmsDecoy, location.getYaw()); - applySkinLayers(nmsDecoy, skinLayerMask); - - int entityId = (int) entityGetId.invoke(nmsDecoy); - Object playerInfoPacket = createPlayerInfoAddPacket(nmsDecoy, profileId, profile); - Object addEntityPacket = createAddEntityPacket(nmsDecoy, entityId, profileId, location); - Object metadataPacket = createMetadataPacket(nmsDecoy, entityId); - Object equipmentPacket = createEquipmentPacket(entityId, owner, false, true); - - PacketPlayerDecoy decoy = new PacketPlayerDecoy(this, location.getWorld(), profileId, entityId, nmsDecoy, tabListRemoveDelayTicks); - decoy.spawn(playerInfoPacket, addEntityPacket, metadataPacket, equipmentPacket); - return decoy; - } catch (Throwable e) { - Throwable root = unwrapRootCause(e); - Adapt.warn("Failed to spawn fake-player shadow decoy: " + root.getClass().getSimpleName() + " " + String.valueOf(root.getMessage())); - return null; - } - } - - private Object createProfile(UUID id, String ownerName, Object ownerProfile) throws ReflectiveOperationException { - String profileName = ownerName; - if (profileName.length() > 16) { - profileName = profileName.substring(0, 16); - } - - if (gameProfileWithPropertiesConstructor != null && gameProfilePropertiesAccessor != null && ownerProfile != null) { - Object properties = gameProfilePropertiesAccessor.invoke(ownerProfile); - if (properties != null) { - return gameProfileWithPropertiesConstructor.newInstance(id, profileName, properties); - } - } - - return gameProfileBasicConstructor.newInstance(id, profileName); - } - - private Object createPlayerInfoAddPacket(Object nmsDecoy, UUID profileId, Object profile) throws ReflectiveOperationException { - ReflectiveOperationException last = null; - - if (playerInfoFromEntriesConstructor != null && playerInfoEntryExplicitConstructor != null && playerInfoActionClass != null && defaultGameType != null) { - try { - Enum addAction = Enum.valueOf((Class) playerInfoActionClass, "ADD_PLAYER"); - EnumSet actions = buildInitializationActions(addAction); - Object entry = playerInfoEntryExplicitConstructor.newInstance(profileId, profile, true, 0, defaultGameType, null, true, 0, null); - return playerInfoFromEntriesConstructor.newInstance(actions, List.of(entry)); - } catch (ReflectiveOperationException e) { - last = e; - } - } - - if (playerInfoCreateSingleInitializing != null) { - try { - return playerInfoCreateSingleInitializing.invoke(null, nmsDecoy, true); - } catch (ReflectiveOperationException e) { - last = e; - } - } - - if (playerInfoActionConstructor != null && playerInfoActionClass != null) { - try { - Object addAction = Enum.valueOf((Class) playerInfoActionClass, "ADD_PLAYER"); - return playerInfoActionConstructor.newInstance(addAction, nmsDecoy); - } catch (ReflectiveOperationException e) { - last = e; - } - } - - if (last != null) { - throw last; - } - - throw new ReflectiveOperationException("No supported player-info add packet constructor found."); - } - - private Object createAddEntityPacket(Object nmsDecoy, int entityId, UUID profileId, Location location) throws ReflectiveOperationException { - if (addEntityExplicitConstructor != null && entityGetType != null && vec3Zero != null) { - Object entityType = entityGetType.invoke(nmsDecoy); - Object velocity = vec3Zero.get(null); - return addEntityExplicitConstructor.newInstance( - entityId, - profileId, - location.getX(), - location.getY(), - location.getZ(), - location.getPitch(), - location.getYaw(), - entityType, - 0, - velocity, - (double) location.getYaw() - ); - } - - return addEntityConstructor.newInstance(nmsDecoy, 0, blockPosZero.get(null)); - } - - private void applySkinLayers(Object nmsDecoy, int mask) throws ReflectiveOperationException { - if (avatarModelCustomizationAccessor == null || synchedEntityDataSet == null) { - return; - } - - Object accessor = avatarModelCustomizationAccessor.get(null); - Object synchedData = entityGetEntityData.invoke(nmsDecoy); - synchedEntityDataSet.invoke(synchedData, accessor, (byte) (mask & 0xFF)); - } - - private Object createEquipmentPacket(int entityId, Player owner, boolean hide, boolean includeEmptySlots) throws ReflectiveOperationException { - if (setEquipmentConstructor == null || pairOfMethod == null || craftItemStackAsNmsCopy == null || equipmentSlotClass == null) { - return null; - } - - List slots = new ArrayList<>(); - ItemStack air = new ItemStack(Material.AIR); - appendEquipment(slots, "HEAD", hide ? air : owner.getInventory().getHelmet(), includeEmptySlots); - appendEquipment(slots, "CHEST", hide ? air : owner.getInventory().getChestplate(), includeEmptySlots); - appendEquipment(slots, "LEGS", hide ? air : owner.getInventory().getLeggings(), includeEmptySlots); - appendEquipment(slots, "FEET", hide ? air : owner.getInventory().getBoots(), includeEmptySlots); - appendEquipment(slots, "MAINHAND", hide ? air : owner.getInventory().getItemInMainHand(), includeEmptySlots); - appendEquipment(slots, "OFFHAND", hide ? air : owner.getInventory().getItemInOffHand(), includeEmptySlots); - - if (slots.isEmpty()) { - return null; - } - - return setEquipmentConstructor.newInstance(entityId, slots); - } - - private void appendEquipment(List slots, String slotName, ItemStack stack, boolean includeEmptySlots) throws ReflectiveOperationException { - ItemStack resolved = stack == null ? new ItemStack(Material.AIR) : stack; - if (!includeEmptySlots && resolved.getType().isAir()) { - return; - } - - Object slot = Enum.valueOf((Class) equipmentSlotClass, slotName); - Object nmsStack = craftItemStackAsNmsCopy.invoke(null, resolved.clone()); - Object pair = pairOfMethod.invoke(null, slot, nmsStack); - slots.add(pair); - } - - private Object createMetadataPacket(Object nmsDecoy, int entityId) throws ReflectiveOperationException { - Object synchedData = entityGetEntityData.invoke(nmsDecoy); - Object packed = synchedEntityDataPackAll != null - ? synchedEntityDataPackAll.invoke(synchedData) - : synchedEntityDataGetNonDefaultValues.invoke(synchedData); - if (!(packed instanceof List values) || values.isEmpty()) { - return null; - } - - return setEntityDataConstructor.newInstance(entityId, values); - } - - public Object createPlayerInfoRemovePacket(UUID profileId) { - if (!supported) { - return null; - } - - try { - return playerInfoRemoveConstructor.newInstance(List.of(profileId)); - } catch (Throwable e) { - return null; - } - } - - public Object createRemoveEntityPacket(int entityId) { - if (!supported) { - return null; - } - - try { - return removeEntitiesConstructor.newInstance((Object) new int[]{entityId}); - } catch (Throwable e) { - return null; - } - } - - public boolean applyLook(PacketPlayerDecoy decoy, float yaw, float pitch, Player viewer) { - if (!supported) { - return false; - } - - try { - entitySetRot.invoke(decoy.nmsEntity, yaw, pitch); - livingSetYHeadRot.invoke(decoy.nmsEntity, yaw); - livingSetYBodyRot.invoke(decoy.nmsEntity, yaw); - - byte yawByte = toAngle(yaw); - byte pitchByte = toAngle(pitch); - Object rotatePacket = moveEntityRotConstructor.newInstance(decoy.entityId, yawByte, pitchByte, true); - Object headPacket = rotateHeadConstructor.newInstance(decoy.nmsEntity, yawByte); - - sendPacket(viewer, rotatePacket); - sendPacket(viewer, headPacket); - - return true; - } catch (Throwable e) { - return false; - } - } - - public boolean syncPosition(PacketPlayerDecoy decoy, Location location, boolean onGround, List viewers) { - if (!supported || entityPositionSyncOf == null) { - return false; - } - - try { - entitySetPos.invoke(decoy.nmsEntity, location.getX(), location.getY(), location.getZ()); - entitySetRot.invoke(decoy.nmsEntity, location.getYaw(), location.getPitch()); - if (entitySetOnGround != null) { - entitySetOnGround.invoke(decoy.nmsEntity, onGround); - } - - Object packet = entityPositionSyncOf.invoke(null, decoy.nmsEntity); - if (packet == null) { - return false; - } - - for (Player viewer : viewers) { - sendPacket(viewer, packet); - } - - return true; - } catch (Throwable e) { - return false; - } - } - - public void sendHurtAnimation(PacketPlayerDecoy decoy, float yaw, List viewers) { - if (!supported || hurtAnimationConstructor == null) { - return; - } - - try { - Object packet = hurtAnimationConstructor.newInstance(decoy.entityId, yaw); - for (Player viewer : viewers) { - sendPacket(viewer, packet); - } - } catch (Throwable ignored) { - } - } - - public void sendOwnerEquipment(Player owner, boolean hide) { - if (!supported || owner == null || !owner.isOnline()) { - return; - } - - try { - Object packet = createEquipmentPacket(owner.getEntityId(), owner, hide, true); - if (packet == null) { - return; - } - - for (Player viewer : new ArrayList<>(owner.getWorld().getPlayers())) { - if (viewer.getUniqueId().equals(owner.getUniqueId())) { - continue; - } - sendPacket(viewer, packet); - } - } catch (Throwable ignored) { - } - } - - public void sendPacket(Player viewer, Object packet) { - if (!supported || packet == null || viewer == null || !viewer.isOnline()) { - return; - } - - try { - Object handle = craftPlayerGetHandle.invoke(viewer); - Object connection = serverPlayerConnectionField.get(handle); - if (connection == null) { - return; - } - - connectionSendPacket.invoke(connection, packet); - } catch (Throwable ignored) { - } - } - - private static byte toAngle(float degrees) { - return (byte) (degrees * 256.0F / 360.0F); - } - - private static Constructor findConstructor(Class type, Class... parameterTypes) throws NoSuchMethodException { - Constructor constructor = type.getConstructor(parameterTypes); - constructor.setAccessible(true); - return constructor; - } - - private static Constructor findOptionalConstructor(Class type, Class... parameterTypes) { - if (Arrays.stream(parameterTypes).anyMatch(c -> c == null)) { - return null; - } - - try { - Constructor constructor = type.getConstructor(parameterTypes); - constructor.setAccessible(true); - return constructor; - } catch (NoSuchMethodException e) { - return null; - } - } - - private static Method findOptionalMethod(Class type, String name, Class... parameterTypes) { - try { - Method method = type.getMethod(name, parameterTypes); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException e) { - return null; - } - } - - private static Method findOptionalMethod(Class type, String... methodNames) { - for (String methodName : methodNames) { - try { - Method method = type.getMethod(methodName); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException ignored) { - } - } - - return null; - } - - private static Class findPropertyMapClass(Class gameProfileClass) { - for (Constructor constructor : gameProfileClass.getConstructors()) { - Class[] params = constructor.getParameterTypes(); - if (params.length == 3 && params[0] == UUID.class && params[1] == String.class) { - return params[2]; - } - } - - return null; - } - - private static Object resolveDefaultGameType(Class gameTypeClass) throws ReflectiveOperationException { - try { - Field defaultMode = gameTypeClass.getField("DEFAULT_MODE"); - Object value = defaultMode.get(null); - if (value != null) { - return value; - } - } catch (NoSuchFieldException ignored) { - } - - if (gameTypeClass.isEnum()) { - try { - return Enum.valueOf((Class) gameTypeClass, "SURVIVAL"); - } catch (IllegalArgumentException ignored) { - } - - Object[] values = gameTypeClass.getEnumConstants(); - if (values != null && values.length > 0) { - return values[0]; - } - } - - throw new ReflectiveOperationException("No default game mode could be resolved."); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private EnumSet buildInitializationActions(Enum addAction) { - EnumSet actions = EnumSet.noneOf((Class) playerInfoActionClass); - actions.add(addAction); - - String[] optionalActions = new String[]{ - "INITIALIZE_CHAT", - "UPDATE_GAME_MODE", - "UPDATE_LISTED", - "UPDATE_LATENCY", - "UPDATE_DISPLAY_NAME", - "UPDATE_HAT", - "UPDATE_LIST_ORDER" - }; - - for (String name : optionalActions) { - try { - actions.add(Enum.valueOf((Class) playerInfoActionClass, name)); - } catch (IllegalArgumentException ignored) { - } - } - - return actions; - } - - private static Throwable unwrapRootCause(Throwable throwable) { - Throwable cursor = throwable; - while (true) { - if (cursor instanceof InvocationTargetException invocation && invocation.getCause() != null) { - cursor = invocation.getCause(); - continue; - } + Entity entity = Bukkit.getEntity(state.anchorId()); + anchorOwners.remove(state.anchorId()); + ownerTrailNextAt.remove(state.ownerId()); + ownerAggroNextAt.remove(state.ownerId()); + if (entity instanceof ArmorStand stand && stand.isValid()) { + stand.remove(); + } - Throwable cause = cursor.getCause(); - if (cause == null || cause == cursor) { - return cursor; - } + restoreOwnerEquipment(owner); + } + + private long getCooldownMillis(int level) { + return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + private int getDecoyTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().decoyTicksBase + (getLevelPercent(level) * getConfig().decoyTicksFactor))); + } + + private double getDecoyRadius(int level) { + return getConfig().decoyRadiusBase + (getLevelPercent(level) * getConfig().decoyRadiusFactor); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Stopping sneak spawns a short-lived shadow decoy that pulls your current aggro.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base cooldown after creating a decoy, in milliseconds.", impact = "Higher values mean longer time between activations.") + double cooldownMillisBase = 18000; + @art.arcane.adapt.util.config.ConfigDoc(value = "How much cooldown is reduced by leveling.", impact = "Higher values reduce cooldown more at higher levels.") + double cooldownMillisFactor = 12000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base active duration in ticks.", impact = "Higher values keep decoys active longer.") + double decoyTicksBase = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration scaling from level, in ticks.", impact = "Higher values extend duration more per level.") + double decoyTicksFactor = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base aggro redirect radius.", impact = "Higher values pull aggro from farther away.") + double decoyRadiusBase = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Aggro radius scaling from level.", impact = "Higher values expand pull range more per level.") + double decoyRadiusFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Visual eye height used for fake player facing.", impact = "Adjust if head rotation appears too high or too low.") + double decoyEyeHeight = 1.62; + @art.arcane.adapt.util.config.ConfigDoc(value = "Delay before removing the fake player from tab list, in ticks.", impact = "Small values hide tab entries faster; larger values help skins load.") + int tabListRemoveDelayTicks = -1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows armor stand visual fallback if packet NPC creation fails.", impact = "Turn off to disable fallback visuals on incompatible server builds.") + boolean legacyFallbackEnabled = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Refresh duration for owner invisibility while a decoy is active.", impact = "Higher values keep invisibility active longer between refreshes.") + int ownerInvisibilityRefreshTicks = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier for the temporary invisibility effect.", impact = "Most servers should leave this at 0.") + int ownerInvisibilityAmplifier = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Smoke particles emitted around the invisible owner each tick while decoy is active.", impact = "Higher values create a stronger visible trail.") + int ownerTrailParticles = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal spread for owner smoke trail.", impact = "Higher values make the trail wider.") + double ownerTrailHorizontalSpread = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical spread for owner smoke trail.", impact = "Higher values make the trail taller.") + double ownerTrailVerticalSpread = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for smoke trail spawn location.", impact = "Adjust to move trail closer to feet or torso.") + double ownerTrailYOffset = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Particle speed for owner smoke trail.", impact = "Higher values make trail movement more turbulent.") + double ownerTrailSpeed = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between owner trail particle bursts while decoy is active.", impact = "Lower values make the owner trail denser; higher values reduce particle cost.") + long ownerTrailIntervalMillis = 75; + @art.arcane.adapt.util.config.ConfigDoc(value = "How often owner equipment-hide packets are resent while invisible, in milliseconds.", impact = "Lower values keep visuals tighter for joining viewers, higher values reduce packet traffic.") + long ownerEquipmentHideResendMillis = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between aggro redirect scans while a decoy is active.", impact = "Lower values pull mobs more aggressively; higher values reduce nearby-entity scan cost.") + long aggroRedirectIntervalMillis = 150; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal knockback applied when the decoy is hit.", impact = "Higher values make the decoy react more dramatically when struck.") + double decoyHitKnockback = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical lift applied when the decoy is hit.", impact = "Higher values make impacts pop the decoy upward more.") + double decoyHitLift = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Swing ray distance used to detect decoy hits.", impact = "Higher values make swings connect from farther away.") + double decoySwingDetectionReach = 4.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bitmask for visible skin layers on the fake player decoy.", impact = "127 enables all standard skin layers (hat, jacket, sleeves, pants).") + int decoySkinLayerMask = 127; + @art.arcane.adapt.util.config.ConfigDoc(value = "Experience granted on each decoy spawn.", impact = "Higher values level the adaptation faster.") + double xpOnDecoy = 18; + } - cursor = cause; - } - } - } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java new file mode 100644 index 000000000..2d913134a --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java @@ -0,0 +1,843 @@ +package art.arcane.adapt.content.adaptation.stealth; + +import art.arcane.adapt.Adapt; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +record DecoyState(UUID ownerId, UUID anchorId, PacketPlayerDecoy packetDecoy, + long expiresAt, int level) { +} + +final class PacketPlayerDecoy { + final PacketDecoyBridge bridge; + final int entityId; + final Object nmsEntity; + private final World world; + private final UUID profileId; + private final long removeTabAt; + private final Set knownViewers; + private boolean removedFromTab; + private Object spawnPlayerInfoPacket; + private Object spawnAddEntityPacket; + private Object spawnMetadataPacket; + private Object spawnEquipmentPacket; + private long lastPositionSyncAt; + private double lastX; + private double lastY; + private double lastZ; + private float lastYaw; + private float lastPitch; + + PacketPlayerDecoy(PacketDecoyBridge bridge, World world, UUID profileId, int entityId, Object nmsEntity, int tabListRemoveDelayTicks) { + this.bridge = bridge; + this.world = world; + this.profileId = profileId; + this.entityId = entityId; + this.nmsEntity = nmsEntity; + this.removeTabAt = System.currentTimeMillis() + Math.max(0, tabListRemoveDelayTicks) * 50L; + this.knownViewers = java.util.concurrent.ConcurrentHashMap.newKeySet(); + this.removedFromTab = false; + this.spawnPlayerInfoPacket = null; + this.spawnAddEntityPacket = null; + this.spawnMetadataPacket = null; + this.spawnEquipmentPacket = null; + this.lastPositionSyncAt = 0L; + this.lastX = Double.NaN; + this.lastY = Double.NaN; + this.lastZ = Double.NaN; + this.lastYaw = Float.NaN; + this.lastPitch = Float.NaN; + } + + public void spawn(Object playerInfoPacket, Object addEntityPacket, Object metadataPacket, Object equipmentPacket) { + this.spawnPlayerInfoPacket = playerInfoPacket; + this.spawnAddEntityPacket = addEntityPacket; + this.spawnMetadataPacket = metadataPacket; + this.spawnEquipmentPacket = equipmentPacket; + ensureViewerState(); + } + + public void tick() { + ensureViewerState(); + if (removeTabAt < 0 || removedFromTab || System.currentTimeMillis() < removeTabAt) { + return; + } + + Object removePacket = bridge.createPlayerInfoRemovePacket(profileId); + if (removePacket != null) { + for (Player viewer : spawnedViewerPlayers()) { + bridge.sendPacket(viewer, removePacket); + } + } + + removedFromTab = true; + } + + public void lookAtViewers(Location origin) { + ensureViewerState(); + for (Player viewer : spawnedViewerPlayers()) { + Location to = viewer.getEyeLocation(); + if (origin.getWorld() != to.getWorld()) { + continue; + } + + double dx = to.getX() - origin.getX(); + double dy = to.getY() - origin.getY(); + double dz = to.getZ() - origin.getZ(); + double horizontal = Math.sqrt(dx * dx + dz * dz); + float yaw = (float) Math.toDegrees(Math.atan2(-dx, dz)); + float pitch = (float) Math.toDegrees(-Math.atan2(dy, horizontal)); + bridge.applyLook(this, yaw, pitch, viewer); + } + } + + public void syncToAnchor(Location anchor, boolean onGround) { + ensureViewerState(); + long now = System.currentTimeMillis(); + double dx = Double.isFinite(lastX) ? anchor.getX() - lastX : 1; + double dy = Double.isFinite(lastY) ? anchor.getY() - lastY : 1; + double dz = Double.isFinite(lastZ) ? anchor.getZ() - lastZ : 1; + double distanceSq = (dx * dx) + (dy * dy) + (dz * dz); + float yawDiff = Float.isFinite(lastYaw) ? Math.abs(anchor.getYaw() - lastYaw) : 360f; + float pitchDiff = Float.isFinite(lastPitch) ? Math.abs(anchor.getPitch() - lastPitch) : 360f; + + if (distanceSq < 0.0004 && yawDiff < 0.8f && pitchDiff < 0.8f && now - lastPositionSyncAt < 45L) { + return; + } + + if (bridge.syncPosition(this, anchor, onGround, spawnedViewerPlayers())) { + lastPositionSyncAt = now; + lastX = anchor.getX(); + lastY = anchor.getY(); + lastZ = anchor.getZ(); + lastYaw = anchor.getYaw(); + lastPitch = anchor.getPitch(); + } + } + + public void hitFrom(Location source) { + ensureViewerState(); + if (!Double.isFinite(lastX) || !Double.isFinite(lastZ)) { + bridge.sendHurtAnimation(this, 0f, spawnedViewerPlayers()); + return; + } + + double dx = source.getX() - lastX; + double dz = source.getZ() - lastZ; + float yaw = (float) Math.toDegrees(Math.atan2(-dx, dz)); + bridge.sendHurtAnimation(this, yaw, spawnedViewerPlayers()); + } + + public void destroy() { + Object removeEntityPacket = bridge.createRemoveEntityPacket(entityId); + Object removePlayerInfoPacket = bridge.createPlayerInfoRemovePacket(profileId); + + for (Player viewer : spawnedViewerPlayers()) { + if (removeEntityPacket != null) { + bridge.sendPacket(viewer, removeEntityPacket); + } + if (removePlayerInfoPacket != null) { + bridge.sendPacket(viewer, removePlayerInfoPacket); + } + } + + knownViewers.clear(); + } + + private void ensureViewerState() { + Set online = new HashSet<>(); + for (Player viewer : world.getPlayers()) { + if (!viewer.isOnline()) { + continue; + } + + UUID id = viewer.getUniqueId(); + online.add(id); + if (!knownViewers.contains(id)) { + spawnFor(viewer); + knownViewers.add(id); + } + } + + knownViewers.retainAll(online); + } + + private void spawnFor(Player viewer) { + if (spawnPlayerInfoPacket != null) { + bridge.sendPacket(viewer, spawnPlayerInfoPacket); + } + + if (spawnAddEntityPacket != null) { + bridge.sendPacket(viewer, spawnAddEntityPacket); + } + + if (spawnMetadataPacket != null) { + bridge.sendPacket(viewer, spawnMetadataPacket); + } + + if (spawnEquipmentPacket != null) { + bridge.sendPacket(viewer, spawnEquipmentPacket); + } + + if (removedFromTab) { + Object removePacket = bridge.createPlayerInfoRemovePacket(profileId); + if (removePacket != null) { + bridge.sendPacket(viewer, removePacket); + } + } + } + + private List spawnedViewerPlayers() { + List viewers = new ArrayList<>(); + for (Player viewer : world.getPlayers()) { + if (viewer.isOnline() && knownViewers.contains(viewer.getUniqueId())) { + viewers.add(viewer); + } + } + + return viewers; + } +} + +final class PacketDecoyBridge { + private final boolean supported; + + private final Method craftServerGetServer; + private final Method craftWorldGetHandle; + private final Method craftPlayerGetHandle; + + private final Constructor serverPlayerConstructor; + private final Method clientInformationCreateDefault; + + private final Constructor gameProfileBasicConstructor; + private final Constructor gameProfileWithPropertiesConstructor; + private final Method gameProfilePropertiesAccessor; + private final Method playerGetGameProfile; + + private final Method entitySetPos; + private final Method entitySetRot; + private final Method entitySetOnGround; + private final Method livingSetYHeadRot; + private final Method livingSetYBodyRot; + private final Method entityGetId; + private final Method entityGetType; + private final Method entityGetEntityData; + private final Method synchedEntityDataGetNonDefaultValues; + private final Method synchedEntityDataPackAll; + private final Method synchedEntityDataSet; + + private final Method playerInfoCreateSingleInitializing; + private final Constructor playerInfoActionConstructor; + private final Constructor playerInfoFromEntriesConstructor; + private final Constructor playerInfoEntryExplicitConstructor; + private final Class playerInfoActionClass; + private final Object defaultGameType; + + private final Constructor addEntityConstructor; + private final Constructor addEntityExplicitConstructor; + private final Field blockPosZero; + private final Field vec3Zero; + private final Constructor setEntityDataConstructor; + private final Constructor moveEntityRotConstructor; + private final Constructor rotateHeadConstructor; + private final Constructor removeEntitiesConstructor; + private final Constructor playerInfoRemoveConstructor; + private final Method entityPositionSyncOf; + private final Constructor hurtAnimationConstructor; + private final Constructor setEquipmentConstructor; + private final Method pairOfMethod; + private final Method craftItemStackAsNmsCopy; + private final Class equipmentSlotClass; + private final Field avatarModelCustomizationAccessor; + + private final Field serverPlayerConnectionField; + private final Method connectionSendPacket; + + private PacketDecoyBridge() throws ReflectiveOperationException { + String craftPackage = Bukkit.getServer().getClass().getPackage().getName(); + if (!craftPackage.startsWith("org.bukkit.craftbukkit")) { + throw new ClassNotFoundException("CraftBukkit package not detected: " + craftPackage); + } + + Class craftServerClass = Class.forName(craftPackage + ".CraftServer"); + Class craftWorldClass = Class.forName(craftPackage + ".CraftWorld"); + Class craftPlayerClass = Class.forName(craftPackage + ".entity.CraftPlayer"); + + Class minecraftServerClass = Class.forName("net.minecraft.server.MinecraftServer"); + Class serverLevelClass = Class.forName("net.minecraft.server.level.ServerLevel"); + Class serverPlayerClass = Class.forName("net.minecraft.server.level.ServerPlayer"); + Class clientInformationClass = Class.forName("net.minecraft.server.level.ClientInformation"); + Class nmsPlayerClass = Class.forName("net.minecraft.world.entity.player.Player"); + Class livingEntityClass = Class.forName("net.minecraft.world.entity.LivingEntity"); + Class entityClass = Class.forName("net.minecraft.world.entity.Entity"); + Class entityTypeClass = Class.forName("net.minecraft.world.entity.EntityType"); + Class avatarClass = Class.forName("net.minecraft.world.entity.Avatar"); + Class equipmentSlotClass = Class.forName("net.minecraft.world.entity.EquipmentSlot"); + Class entityDataAccessorClass = Class.forName("net.minecraft.network.syncher.EntityDataAccessor"); + Class synchedEntityDataClass = Class.forName("net.minecraft.network.syncher.SynchedEntityData"); + Class gameProfileClass = Class.forName("com.mojang.authlib.GameProfile"); + Class vec3Class = Class.forName("net.minecraft.world.phys.Vec3"); + Class pairClass = Class.forName("com.mojang.datafixers.util.Pair"); + Class packetClass = Class.forName("net.minecraft.network.protocol.Packet"); + Class connectionClass = Class.forName("net.minecraft.server.network.ServerCommonPacketListenerImpl"); + Class playerInfoPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket"); + Class playerInfoEntryClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry"); + Class playerInfoActionEnumClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action"); + Class gameTypeClass = Class.forName("net.minecraft.world.level.GameType"); + Class componentClass = Class.forName("net.minecraft.network.chat.Component"); + Class remoteChatSessionDataClass = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data"); + Class addEntityPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket"); + Class entityPositionSyncPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket"); + Class hurtAnimationPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket"); + Class blockPosClass = Class.forName("net.minecraft.core.BlockPos"); + Class setEntityDataPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket"); + Class moveEntityRotClass = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot"); + Class rotateHeadPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket"); + Class removeEntitiesPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket"); + Class playerInfoRemovePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket"); + Class setEquipmentPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket"); + Class craftItemStackClass = Class.forName(craftPackage + ".inventory.CraftItemStack"); + + this.craftServerGetServer = craftServerClass.getMethod("getServer"); + this.craftWorldGetHandle = craftWorldClass.getMethod("getHandle"); + this.craftPlayerGetHandle = craftPlayerClass.getMethod("getHandle"); + + this.serverPlayerConstructor = serverPlayerClass.getConstructor(minecraftServerClass, serverLevelClass, gameProfileClass, clientInformationClass); + this.clientInformationCreateDefault = clientInformationClass.getMethod("createDefault"); + + this.gameProfileBasicConstructor = findConstructor(gameProfileClass, UUID.class, String.class); + this.gameProfileWithPropertiesConstructor = findOptionalConstructor(gameProfileClass, UUID.class, String.class, findPropertyMapClass(gameProfileClass)); + this.gameProfilePropertiesAccessor = findOptionalMethod(gameProfileClass, "properties", "getProperties"); + this.playerGetGameProfile = nmsPlayerClass.getMethod("getGameProfile"); + + this.entitySetPos = entityClass.getMethod("setPos", double.class, double.class, double.class); + this.entitySetRot = entityClass.getMethod("setRot", float.class, float.class); + this.entitySetOnGround = entityClass.getMethod("setOnGround", boolean.class); + this.livingSetYHeadRot = livingEntityClass.getMethod("setYHeadRot", float.class); + this.livingSetYBodyRot = livingEntityClass.getMethod("setYBodyRot", float.class); + this.entityGetId = entityClass.getMethod("getId"); + this.entityGetType = entityClass.getMethod("getType"); + this.entityGetEntityData = entityClass.getMethod("getEntityData"); + this.synchedEntityDataGetNonDefaultValues = synchedEntityDataClass.getMethod("getNonDefaultValues"); + this.synchedEntityDataPackAll = findOptionalMethod(synchedEntityDataClass, "packAll", new Class[0]); + this.synchedEntityDataSet = synchedEntityDataClass.getMethod("set", entityDataAccessorClass, Object.class); + + this.playerInfoCreateSingleInitializing = findOptionalMethod(playerInfoPacketClass, "createSinglePlayerInitializing", serverPlayerClass, boolean.class); + this.playerInfoActionConstructor = findOptionalConstructor(playerInfoPacketClass, playerInfoActionEnumClass, serverPlayerClass); + this.playerInfoFromEntriesConstructor = findOptionalConstructor(playerInfoPacketClass, EnumSet.class, List.class); + this.playerInfoEntryExplicitConstructor = findOptionalConstructor(playerInfoEntryClass, UUID.class, gameProfileClass, boolean.class, int.class, gameTypeClass, componentClass, boolean.class, int.class, remoteChatSessionDataClass); + this.playerInfoActionClass = playerInfoActionEnumClass; + this.defaultGameType = resolveDefaultGameType(gameTypeClass); + + this.addEntityConstructor = addEntityPacketClass.getConstructor(entityClass, int.class, blockPosClass); + this.addEntityExplicitConstructor = findOptionalConstructor(addEntityPacketClass, int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, entityTypeClass, int.class, vec3Class, double.class); + this.blockPosZero = blockPosClass.getField("ZERO"); + this.vec3Zero = vec3Class.getField("ZERO"); + this.setEntityDataConstructor = setEntityDataPacketClass.getConstructor(int.class, List.class); + this.moveEntityRotConstructor = moveEntityRotClass.getConstructor(int.class, byte.class, byte.class, boolean.class); + this.rotateHeadConstructor = rotateHeadPacketClass.getConstructor(entityClass, byte.class); + this.removeEntitiesConstructor = removeEntitiesPacketClass.getConstructor(int[].class); + this.playerInfoRemoveConstructor = playerInfoRemovePacketClass.getConstructor(List.class); + this.entityPositionSyncOf = entityPositionSyncPacketClass.getMethod("of", entityClass); + this.hurtAnimationConstructor = findOptionalConstructor(hurtAnimationPacketClass, int.class, float.class); + this.setEquipmentConstructor = findOptionalConstructor(setEquipmentPacketClass, int.class, List.class); + this.pairOfMethod = pairClass.getMethod("of", Object.class, Object.class); + this.craftItemStackAsNmsCopy = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class); + this.equipmentSlotClass = equipmentSlotClass; + this.avatarModelCustomizationAccessor = avatarClass.getField("DATA_PLAYER_MODE_CUSTOMISATION"); + + this.serverPlayerConnectionField = serverPlayerClass.getField("connection"); + this.connectionSendPacket = connectionClass.getMethod("send", packetClass); + this.supported = true; + } + + private PacketDecoyBridge(boolean supported) { + this.supported = supported; + this.craftServerGetServer = null; + this.craftWorldGetHandle = null; + this.craftPlayerGetHandle = null; + this.serverPlayerConstructor = null; + this.clientInformationCreateDefault = null; + this.gameProfileBasicConstructor = null; + this.gameProfileWithPropertiesConstructor = null; + this.gameProfilePropertiesAccessor = null; + this.playerGetGameProfile = null; + this.entitySetPos = null; + this.entitySetRot = null; + this.entitySetOnGround = null; + this.livingSetYHeadRot = null; + this.livingSetYBodyRot = null; + this.entityGetId = null; + this.entityGetType = null; + this.entityGetEntityData = null; + this.synchedEntityDataGetNonDefaultValues = null; + this.synchedEntityDataPackAll = null; + this.synchedEntityDataSet = null; + this.playerInfoCreateSingleInitializing = null; + this.playerInfoActionConstructor = null; + this.playerInfoFromEntriesConstructor = null; + this.playerInfoEntryExplicitConstructor = null; + this.playerInfoActionClass = null; + this.defaultGameType = null; + this.addEntityConstructor = null; + this.addEntityExplicitConstructor = null; + this.blockPosZero = null; + this.vec3Zero = null; + this.setEntityDataConstructor = null; + this.moveEntityRotConstructor = null; + this.rotateHeadConstructor = null; + this.removeEntitiesConstructor = null; + this.playerInfoRemoveConstructor = null; + this.entityPositionSyncOf = null; + this.hurtAnimationConstructor = null; + this.setEquipmentConstructor = null; + this.pairOfMethod = null; + this.craftItemStackAsNmsCopy = null; + this.equipmentSlotClass = null; + this.avatarModelCustomizationAccessor = null; + this.serverPlayerConnectionField = null; + this.connectionSendPacket = null; + } + + public static PacketDecoyBridge create() { + try { + return new PacketDecoyBridge(); + } catch (Throwable e) { + Adapt.warn("Shadow decoy fake-player bridge unavailable: " + e.getClass().getSimpleName() + " " + e.getMessage()); + return new PacketDecoyBridge(false); + } + } + + private static byte toAngle(float degrees) { + return (byte) (degrees * 256.0F / 360.0F); + } + + private static Constructor findConstructor(Class type, Class... parameterTypes) throws NoSuchMethodException { + Constructor constructor = type.getConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } + + private static Constructor findOptionalConstructor(Class type, Class... parameterTypes) { + if (Arrays.stream(parameterTypes).anyMatch(c -> c == null)) { + return null; + } + + try { + Constructor constructor = type.getConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Method findOptionalMethod(Class type, String name, Class... parameterTypes) { + try { + Method method = type.getMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Method findOptionalMethod(Class type, String... methodNames) { + for (String methodName : methodNames) { + try { + Method method = type.getMethod(methodName); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ignored) { + } + } + + return null; + } + + private static Class findPropertyMapClass(Class gameProfileClass) { + for (Constructor constructor : gameProfileClass.getConstructors()) { + Class[] params = constructor.getParameterTypes(); + if (params.length == 3 && params[0] == UUID.class && params[1] == String.class) { + return params[2]; + } + } + + return null; + } + + private static Object resolveDefaultGameType(Class gameTypeClass) throws ReflectiveOperationException { + try { + Field defaultMode = gameTypeClass.getField("DEFAULT_MODE"); + Object value = defaultMode.get(null); + if (value != null) { + return value; + } + } catch (NoSuchFieldException ignored) { + } + + if (gameTypeClass.isEnum()) { + try { + return Enum.valueOf((Class) gameTypeClass, "SURVIVAL"); + } catch (IllegalArgumentException ignored) { + } + + Object[] values = gameTypeClass.getEnumConstants(); + if (values != null && values.length > 0) { + return values[0]; + } + } + + throw new ReflectiveOperationException("No default game mode could be resolved."); + } + + private static Throwable unwrapRootCause(Throwable throwable) { + Throwable cursor = throwable; + while (true) { + if (cursor instanceof InvocationTargetException invocation && invocation.getCause() != null) { + cursor = invocation.getCause(); + continue; + } + + Throwable cause = cursor.getCause(); + if (cause == null || cause == cursor) { + return cursor; + } + + cursor = cause; + } + } + + public PacketPlayerDecoy spawnDecoy(Player owner, ArmorStand anchor, int tabListRemoveDelayTicks, int skinLayerMask) { + Location location = anchor.getLocation(); + if (!supported || location.getWorld() == null) { + return null; + } + + try { + Object ownerHandle = craftPlayerGetHandle.invoke(owner); + Object ownerProfile = playerGetGameProfile.invoke(ownerHandle); + UUID profileId = UUID.randomUUID(); + Object profile = createProfile(profileId, owner.getName(), ownerProfile); + + Object minecraftServer = craftServerGetServer.invoke(Bukkit.getServer()); + Object worldHandle = craftWorldGetHandle.invoke(location.getWorld()); + Object clientInfo = clientInformationCreateDefault.invoke(null); + Object nmsDecoy = serverPlayerConstructor.newInstance(minecraftServer, worldHandle, profile, clientInfo); + + entitySetPos.invoke(nmsDecoy, location.getX(), location.getY(), location.getZ()); + entitySetRot.invoke(nmsDecoy, location.getYaw(), location.getPitch()); + livingSetYHeadRot.invoke(nmsDecoy, location.getYaw()); + livingSetYBodyRot.invoke(nmsDecoy, location.getYaw()); + applySkinLayers(nmsDecoy, skinLayerMask); + + int entityId = (int) entityGetId.invoke(nmsDecoy); + Object playerInfoPacket = createPlayerInfoAddPacket(nmsDecoy, profileId, profile); + Object addEntityPacket = createAddEntityPacket(nmsDecoy, entityId, profileId, location); + Object metadataPacket = createMetadataPacket(nmsDecoy, entityId); + Object equipmentPacket = createEquipmentPacket(entityId, owner, false, true); + + PacketPlayerDecoy decoy = new PacketPlayerDecoy(this, location.getWorld(), profileId, entityId, nmsDecoy, tabListRemoveDelayTicks); + decoy.spawn(playerInfoPacket, addEntityPacket, metadataPacket, equipmentPacket); + return decoy; + } catch (Throwable e) { + Throwable root = unwrapRootCause(e); + Adapt.warn("Failed to spawn fake-player shadow decoy: " + root.getClass().getSimpleName() + " " + String.valueOf(root.getMessage())); + return null; + } + } + + private Object createProfile(UUID id, String ownerName, Object ownerProfile) throws ReflectiveOperationException { + String profileName = ownerName; + if (profileName.length() > 16) { + profileName = profileName.substring(0, 16); + } + + if (gameProfileWithPropertiesConstructor != null && gameProfilePropertiesAccessor != null && ownerProfile != null) { + Object properties = gameProfilePropertiesAccessor.invoke(ownerProfile); + if (properties != null) { + return gameProfileWithPropertiesConstructor.newInstance(id, profileName, properties); + } + } + + return gameProfileBasicConstructor.newInstance(id, profileName); + } + + private Object createPlayerInfoAddPacket(Object nmsDecoy, UUID profileId, Object profile) throws ReflectiveOperationException { + ReflectiveOperationException last = null; + + if (playerInfoFromEntriesConstructor != null && playerInfoEntryExplicitConstructor != null && playerInfoActionClass != null && defaultGameType != null) { + try { + Enum addAction = Enum.valueOf((Class) playerInfoActionClass, "ADD_PLAYER"); + EnumSet actions = buildInitializationActions(addAction); + Object entry = playerInfoEntryExplicitConstructor.newInstance(profileId, profile, true, 0, defaultGameType, null, true, 0, null); + return playerInfoFromEntriesConstructor.newInstance(actions, List.of(entry)); + } catch (ReflectiveOperationException e) { + last = e; + } + } + + if (playerInfoCreateSingleInitializing != null) { + try { + return playerInfoCreateSingleInitializing.invoke(null, nmsDecoy, true); + } catch (ReflectiveOperationException e) { + last = e; + } + } + + if (playerInfoActionConstructor != null && playerInfoActionClass != null) { + try { + Object addAction = Enum.valueOf((Class) playerInfoActionClass, "ADD_PLAYER"); + return playerInfoActionConstructor.newInstance(addAction, nmsDecoy); + } catch (ReflectiveOperationException e) { + last = e; + } + } + + if (last != null) { + throw last; + } + + throw new ReflectiveOperationException("No supported player-info add packet constructor found."); + } + + private Object createAddEntityPacket(Object nmsDecoy, int entityId, UUID profileId, Location location) throws ReflectiveOperationException { + if (addEntityExplicitConstructor != null && entityGetType != null && vec3Zero != null) { + Object entityType = entityGetType.invoke(nmsDecoy); + Object velocity = vec3Zero.get(null); + return addEntityExplicitConstructor.newInstance( + entityId, + profileId, + location.getX(), + location.getY(), + location.getZ(), + location.getPitch(), + location.getYaw(), + entityType, + 0, + velocity, + (double) location.getYaw() + ); + } + + return addEntityConstructor.newInstance(nmsDecoy, 0, blockPosZero.get(null)); + } + + private void applySkinLayers(Object nmsDecoy, int mask) throws ReflectiveOperationException { + if (avatarModelCustomizationAccessor == null || synchedEntityDataSet == null) { + return; + } + + Object accessor = avatarModelCustomizationAccessor.get(null); + Object synchedData = entityGetEntityData.invoke(nmsDecoy); + synchedEntityDataSet.invoke(synchedData, accessor, (byte) (mask & 0xFF)); + } + + private Object createEquipmentPacket(int entityId, Player owner, boolean hide, boolean includeEmptySlots) throws ReflectiveOperationException { + if (setEquipmentConstructor == null || pairOfMethod == null || craftItemStackAsNmsCopy == null || equipmentSlotClass == null) { + return null; + } + + List slots = new ArrayList<>(); + ItemStack air = new ItemStack(Material.AIR); + appendEquipment(slots, "HEAD", hide ? air : owner.getInventory().getHelmet(), includeEmptySlots); + appendEquipment(slots, "CHEST", hide ? air : owner.getInventory().getChestplate(), includeEmptySlots); + appendEquipment(slots, "LEGS", hide ? air : owner.getInventory().getLeggings(), includeEmptySlots); + appendEquipment(slots, "FEET", hide ? air : owner.getInventory().getBoots(), includeEmptySlots); + appendEquipment(slots, "MAINHAND", hide ? air : owner.getInventory().getItemInMainHand(), includeEmptySlots); + appendEquipment(slots, "OFFHAND", hide ? air : owner.getInventory().getItemInOffHand(), includeEmptySlots); + + if (slots.isEmpty()) { + return null; + } + + return setEquipmentConstructor.newInstance(entityId, slots); + } + + private void appendEquipment(List slots, String slotName, ItemStack stack, boolean includeEmptySlots) throws ReflectiveOperationException { + ItemStack resolved = stack == null ? new ItemStack(Material.AIR) : stack; + if (!includeEmptySlots && resolved.getType().isAir()) { + return; + } + + Object slot = Enum.valueOf((Class) equipmentSlotClass, slotName); + Object nmsStack = craftItemStackAsNmsCopy.invoke(null, resolved.clone()); + Object pair = pairOfMethod.invoke(null, slot, nmsStack); + slots.add(pair); + } + + private Object createMetadataPacket(Object nmsDecoy, int entityId) throws ReflectiveOperationException { + Object synchedData = entityGetEntityData.invoke(nmsDecoy); + Object packed = synchedEntityDataPackAll != null + ? synchedEntityDataPackAll.invoke(synchedData) + : synchedEntityDataGetNonDefaultValues.invoke(synchedData); + if (!(packed instanceof List values) || values.isEmpty()) { + return null; + } + + return setEntityDataConstructor.newInstance(entityId, values); + } + + public Object createPlayerInfoRemovePacket(UUID profileId) { + if (!supported) { + return null; + } + + try { + return playerInfoRemoveConstructor.newInstance(List.of(profileId)); + } catch (Throwable e) { + return null; + } + } + + public Object createRemoveEntityPacket(int entityId) { + if (!supported) { + return null; + } + + try { + return removeEntitiesConstructor.newInstance((Object) new int[]{entityId}); + } catch (Throwable e) { + return null; + } + } + + public boolean applyLook(PacketPlayerDecoy decoy, float yaw, float pitch, Player viewer) { + if (!supported) { + return false; + } + + try { + entitySetRot.invoke(decoy.nmsEntity, yaw, pitch); + livingSetYHeadRot.invoke(decoy.nmsEntity, yaw); + livingSetYBodyRot.invoke(decoy.nmsEntity, yaw); + + byte yawByte = toAngle(yaw); + byte pitchByte = toAngle(pitch); + Object rotatePacket = moveEntityRotConstructor.newInstance(decoy.entityId, yawByte, pitchByte, true); + Object headPacket = rotateHeadConstructor.newInstance(decoy.nmsEntity, yawByte); + + sendPacket(viewer, rotatePacket); + sendPacket(viewer, headPacket); + + return true; + } catch (Throwable e) { + return false; + } + } + + public boolean syncPosition(PacketPlayerDecoy decoy, Location location, boolean onGround, List viewers) { + if (!supported || entityPositionSyncOf == null) { + return false; + } + + try { + entitySetPos.invoke(decoy.nmsEntity, location.getX(), location.getY(), location.getZ()); + entitySetRot.invoke(decoy.nmsEntity, location.getYaw(), location.getPitch()); + if (entitySetOnGround != null) { + entitySetOnGround.invoke(decoy.nmsEntity, onGround); + } + + Object packet = entityPositionSyncOf.invoke(null, decoy.nmsEntity); + if (packet == null) { + return false; + } + + for (Player viewer : viewers) { + sendPacket(viewer, packet); + } + + return true; + } catch (Throwable e) { + return false; + } + } + + public void sendHurtAnimation(PacketPlayerDecoy decoy, float yaw, List viewers) { + if (!supported || hurtAnimationConstructor == null) { + return; + } + + try { + Object packet = hurtAnimationConstructor.newInstance(decoy.entityId, yaw); + for (Player viewer : viewers) { + sendPacket(viewer, packet); + } + } catch (Throwable ignored) { + } + } + + public void sendOwnerEquipment(Player owner, boolean hide) { + if (!supported || owner == null || !owner.isOnline()) { + return; + } + + try { + Object packet = createEquipmentPacket(owner.getEntityId(), owner, hide, true); + if (packet == null) { + return; + } + + for (Player viewer : new ArrayList<>(owner.getWorld().getPlayers())) { + if (viewer.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + sendPacket(viewer, packet); + } + } catch (Throwable ignored) { + } + } + + public void sendPacket(Player viewer, Object packet) { + if (!supported || packet == null || viewer == null || !viewer.isOnline()) { + return; + } + + try { + Object handle = craftPlayerGetHandle.invoke(viewer); + Object connection = serverPlayerConnectionField.get(handle); + if (connection == null) { + return; + } + + connectionSendPacket.invoke(connection, packet); + } catch (Throwable ignored) { + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private EnumSet buildInitializationActions(Enum addAction) { + EnumSet actions = EnumSet.noneOf((Class) playerInfoActionClass); + actions.add(addAction); + + String[] optionalActions = new String[]{ + "INITIALIZE_CHAT", + "UPDATE_GAME_MODE", + "UPDATE_LISTED", + "UPDATE_LATENCY", + "UPDATE_DISPLAY_NAME", + "UPDATE_HAT", + "UPDATE_LIST_ORDER" + }; + + for (String name : optionalActions) { + try { + actions.add(Enum.valueOf((Class) playerInfoActionClass, name)); + } catch (IllegalArgumentException ignored) { + } + } + + return actions; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index 9e3636563..5c9553a97 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -44,114 +44,113 @@ import java.util.concurrent.ConcurrentHashMap; public class StealthSight extends SimpleAdaptation { - private final Set sneaking; - - - public StealthSight() { - super("stealth-vision"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.night_vision.description")); - setDisplayName(Localizer.dLocalize("stealth.night_vision.name")); - setIcon(Material.POTION); - setBaseCost(getConfig().baseCost); - setInterval(1500); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setMaxLevel(getConfig().maxLevel); - sneaking = ConcurrentHashMap.newKeySet(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_stealth_sight_72k") - .title(Localizer.dLocalize("advancement.challenge_stealth_sight_72k.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_sight_72k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_stealth_sight_72k", "stealth.sight.time-in-darkness", 72000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GRAY + Localizer.dLocalize("stealth.night_vision.lore1") + C.GREEN + Localizer.dLocalize("stealth.night_vision.lore2") + C.GRAY + Localizer.dLocalize("stealth.night_vision.lore3")); - } - - @EventHandler - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - withPlayerThread(p, e, () -> { - UUID id = p.getUniqueId(); - SoundPlayer sp = SoundPlayer.of(p); - if (!hasActiveAdaptation(p)) { - sneaking.remove(id); - p.removePotionEffect(PotionEffectType.NIGHT_VISION); - return; - } - - if (e.isSneaking()) { - sneaking.add(id); - sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 1, 0.99f); - p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false)); - getPlayer(p).getData().addStat("stealth.sight.time-in-darkness", 1); - return; - } - - sneaking.remove(id); - p.removePotionEffect(PotionEffectType.NIGHT_VISION); - }); - } - - - @Override - public void onTick() { - Set snapshot = new HashSet<>(sneaking); - for (UUID id : snapshot) { - Player p = Bukkit.getPlayer(id); - if (p == null || !p.isOnline()) { - sneaking.remove(id); - continue; - } - - Runnable check = () -> { - if (getActiveLevel(p, Player::isSneaking) <= 0) { - sneaking.remove(id); - J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); - } - }; - - if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { - J.runEntity(p, check); - } else { - check.run(); - } + private final Set sneaking; + + + public StealthSight() { + super("stealth-vision"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.night_vision.description")); + setDisplayName(Localizer.dLocalize("stealth.night_vision.name")); + setIcon(Material.POTION); + setBaseCost(getConfig().baseCost); + setInterval(1500); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setMaxLevel(getConfig().maxLevel); + sneaking = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_stealth_sight_72k") + .title(Localizer.dLocalize("advancement.challenge_stealth_sight_72k.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_sight_72k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_stealth_sight_72k", "stealth.sight.time-in-darkness", 72000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GRAY + Localizer.dLocalize("stealth.night_vision.lore1") + C.GREEN + Localizer.dLocalize("stealth.night_vision.lore2") + C.GRAY + Localizer.dLocalize("stealth.night_vision.lore3")); + } + + @EventHandler + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + withPlayerThread(p, e, () -> { + UUID id = p.getUniqueId(); + SoundPlayer sp = SoundPlayer.of(p); + if (!hasActiveAdaptation(p)) { + sneaking.remove(id); + p.removePotionEffect(PotionEffectType.NIGHT_VISION); + return; + } + + if (e.isSneaking()) { + sneaking.add(id); + sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 1, 0.99f); + p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false)); + getPlayer(p).getData().addStat("stealth.sight.time-in-darkness", 1); + return; + } + + sneaking.remove(id); + p.removePotionEffect(PotionEffectType.NIGHT_VISION); + }); + } + + + @Override + public void onTick() { + Set snapshot = new HashSet<>(sneaking); + for (UUID id : snapshot) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + sneaking.remove(id); + continue; + } + + Runnable check = () -> { + if (getActiveLevel(p, Player::isSneaking) <= 0) { + sneaking.remove(id); + J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); } - } - - - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + }; - @NoArgsConstructor - @ConfigDescription("Gain night vision while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 1; + if (J.isFoliaThreading() && !J.isOwnedByCurrentRegion(p)) { + J.runEntity(p, check); + } else { + check.run(); + } } + } + + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain night vision while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java index f828a6efa..a04642829 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSilentStep.java @@ -26,9 +26,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import fr.skytasul.glowingentities.GlowingEntities; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -49,516 +49,516 @@ import java.util.*; public class StealthSilentStep extends SimpleAdaptation { - private final Map dimmed = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map> recentBackstabs = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map> threatGlows = new java.util.concurrent.ConcurrentHashMap<>(); - private final Set activeSneakers = java.util.concurrent.ConcurrentHashMap.newKeySet(); - private final Map lastTargetDropScan = new java.util.concurrent.ConcurrentHashMap<>(); - - public StealthSilentStep() { - super("stealth-silent-step"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.silent_step.description")); - setDisplayName(Localizer.dLocalize("stealth.silent_step.name")); - setIcon(Material.WHITE_WOOL); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(50); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_stealth_silent_200") - .title(Localizer.dLocalize("advancement.challenge_stealth_silent_200.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_silent_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_stealth_silent_5in10") - .title(Localizer.dLocalize("advancement.challenge_stealth_silent_5in10.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_silent_5in10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_stealth_silent_200", "stealth.silent-step.backstabs", 200, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getStealthRadius(level)) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getMobBackstabMultiplier(level) - 1D, 0) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore2")); - v.addLore(C.GREEN + "+ " + Form.pc(getPlayerBackstabMultiplier(level) - 1D, 0) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore3")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - Player player = e.getPlayer(); - UUID id = player.getUniqueId(); - clearDimming(player); - clearThreatGlows(player); - recentBackstabs.remove(id); - activeSneakers.remove(id); - lastTargetDropScan.remove(id); + private final Map dimmed = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map> recentBackstabs = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map> threatGlows = new java.util.concurrent.ConcurrentHashMap<>(); + private final Set activeSneakers = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map lastTargetDropScan = new java.util.concurrent.ConcurrentHashMap<>(); + + public StealthSilentStep() { + super("stealth-silent-step"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.silent_step.description")); + setDisplayName(Localizer.dLocalize("stealth.silent_step.name")); + setIcon(Material.WHITE_WOOL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(50); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_stealth_silent_200") + .title(Localizer.dLocalize("advancement.challenge_stealth_silent_200.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_silent_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_stealth_silent_5in10") + .title(Localizer.dLocalize("advancement.challenge_stealth_silent_5in10.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_silent_5in10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_stealth_silent_200", "stealth.silent-step.backstabs", 200, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getStealthRadius(level)) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getMobBackstabMultiplier(level) - 1D, 0) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getPlayerBackstabMultiplier(level) - 1D, 0) + C.GRAY + " " + Localizer.dLocalize("stealth.silent_step.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + Player player = e.getPlayer(); + UUID id = player.getUniqueId(); + clearDimming(player); + clearThreatGlows(player); + recentBackstabs.remove(id); + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + if (e.isSneaking()) { + activeSneakers.add(id); + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - if (e.isSneaking()) { - activeSneakers.add(id); - return; - } + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + clearDimming(p); + clearThreatGlows(p); + } - activeSneakers.remove(id); - lastTargetDropScan.remove(id); - clearDimming(p); - clearThreatGlows(p); + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityTargetLivingEntityEvent e) { + if (!(e.getTarget() instanceof Player p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityTargetLivingEntityEvent e) { - if (!(e.getTarget() instanceof Player p)) { - return; - } - - if (getActiveLevel(p, Player::isSneaking) <= 0) { - return; - } - - if (isTargetBlacklistType(e.getEntity().getType())) { - return; - } + if (getActiveLevel(p, Player::isSneaking) <= 0) { + return; + } - e.setCancelled(true); - if (e.getEntity() instanceof Mob mob && mob.getTarget() == p) { - mob.setTarget(null); - } + if (isTargetBlacklistType(e.getEntity().getType())) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerMoveEvent e) { - if (e.getTo() == null) { - return; - } + e.setCancelled(true); + if (e.getEntity() instanceof Mob mob && mob.getTarget() == p) { + mob.setTarget(null); + } + } - if (e.getFrom().getWorld() == e.getTo().getWorld() - && e.getFrom().distanceSquared(e.getTo()) < Math.max(0D, getConfig().minimumMoveSquared)) { - return; - } - Player p = e.getPlayer(); - UUID id = p.getUniqueId(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0) { - activeSneakers.remove(id); - lastTargetDropScan.remove(id); - return; - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerMoveEvent e) { + if (e.getTo() == null) { + return; + } - activeSneakers.add(id); - long now = System.currentTimeMillis(); - long lastScan = lastTargetDropScan.getOrDefault(id, 0L); - if (now - lastScan < Math.max(20L, getConfig().targetDropScanIntervalMillis)) { - return; - } - lastTargetDropScan.put(id, now); - - double radius = getStealthRadius(level); - for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { - if (!(entity instanceof Mob mob)) { - continue; - } - - if (isTargetBlacklistType(mob.getType())) { - continue; - } - - if (mob.getTarget() == p) { - mob.setTarget(null); - xp(p, getConfig().xpPerTargetDrop); - } - } + if (e.getFrom().getWorld() == e.getTo().getWorld() + && e.getFrom().distanceSquared(e.getTo()) < Math.max(0D, getConfig().minimumMoveSquared)) { + return; + } + Player p = e.getPlayer(); + UUID id = p.getUniqueId(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e); - if (combat == null) { - return; - } + activeSneakers.add(id); + long now = System.currentTimeMillis(); + long lastScan = lastTargetDropScan.getOrDefault(id, 0L); + if (now - lastScan < Math.max(20L, getConfig().targetDropScanIntervalMillis)) { + return; + } + lastTargetDropScan.put(id, now); + + double radius = getStealthRadius(level); + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { + if (!(entity instanceof Mob mob)) { + continue; + } + + if (isTargetBlacklistType(mob.getType())) { + continue; + } + + if (mob.getTarget() == p) { + mob.setTarget(null); + xp(p, getConfig().xpPerTargetDrop); + } + } + } - Player attacker = combat.attacker(); - LivingEntity target = combat.target(); - boolean unseen = attacker.hasPotionEffect(PotionEffectType.INVISIBILITY) || !isLookingAt(target, attacker); - if (target == attacker || !unseen) { - return; - } + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e); + if (combat == null) { + return; + } - int level = combat.level(); - double multiplier = (target instanceof Player) ? getPlayerBackstabMultiplier(level) : getMobBackstabMultiplier(level); - e.setDamage(e.getDamage() * multiplier); - xp(attacker, e.getDamage() * getConfig().xpPerBonusDamage); - getPlayer(attacker).getData().addStat("stealth.silent-step.backstabs", 1); - - long now = System.currentTimeMillis(); - UUID uid = attacker.getUniqueId(); - recentBackstabs.computeIfAbsent(uid, k -> new ArrayList<>()).add(now); - recentBackstabs.get(uid).removeIf(t -> now - t > 10000); - if (recentBackstabs.get(uid).size() >= 5 - && AdaptConfig.get().isAdvancements() - && !getPlayer(attacker).getData().isGranted("challenge_stealth_silent_5in10")) { - getPlayer(attacker).getAdvancementHandler().grant("challenge_stealth_silent_5in10"); - } + Player attacker = combat.attacker(); + LivingEntity target = combat.target(); + boolean unseen = attacker.hasPotionEffect(PotionEffectType.INVISIBILITY) || !isLookingAt(target, attacker); + if (target == attacker || !unseen) { + return; } - @Override - public void onTick() { - Set tracked = new HashSet<>(activeSneakers); - for (UUID id : tracked) { - Player p = Bukkit.getPlayer(id); - if (p == null || !p.isOnline()) { - activeSneakers.remove(id); - lastTargetDropScan.remove(id); - continue; - } - - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0) { - clearDimming(p); - clearThreatGlows(p); - activeSneakers.remove(id); - lastTargetDropScan.remove(id); - continue; - } - - p.setFallDistance(Math.min(p.getFallDistance(), getConfig().maxSilentFallDistance)); - ThreatSnapshot threatSnapshot = collectThreatSnapshot(p, level); - if (threatSnapshot.canDetect.isEmpty()) { - applyDimming(p, level); - } else { - clearDimming(p); - } - updateThreatGlows(p, threatSnapshot); - } + int level = combat.level(); + double multiplier = (target instanceof Player) ? getPlayerBackstabMultiplier(level) : getMobBackstabMultiplier(level); + e.setDamage(e.getDamage() * multiplier); + xp(attacker, e.getDamage() * getConfig().xpPerBonusDamage); + getPlayer(attacker).getData().addStat("stealth.silent-step.backstabs", 1); + + long now = System.currentTimeMillis(); + UUID uid = attacker.getUniqueId(); + recentBackstabs.computeIfAbsent(uid, k -> new ArrayList<>()).add(now); + recentBackstabs.get(uid).removeIf(t -> now - t > 10000); + if (recentBackstabs.get(uid).size() >= 5 + && AdaptConfig.get().isAdvancements() + && !getPlayer(attacker).getData().isGranted("challenge_stealth_silent_5in10")) { + getPlayer(attacker).getAdvancementHandler().grant("challenge_stealth_silent_5in10"); } + } + + @Override + public void onTick() { + Set tracked = new HashSet<>(activeSneakers); + for (UUID id : tracked) { + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline()) { + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + continue; + } - private ThreatSnapshot collectThreatSnapshot(Player p, int level) { - ThreatSnapshot snapshot = new ThreatSnapshot(); - double detectionLookDotThreshold = getDetectionLookDotThreshold(); - double mobRadius = getStealthRadius(level); - for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), mobRadius, mobRadius, mobRadius)) { - if (!(entity instanceof Mob mob)) { - continue; - } + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + clearDimming(p); + clearThreatGlows(p); + activeSneakers.remove(id); + lastTargetDropScan.remove(id); + continue; + } + + p.setFallDistance(Math.min(p.getFallDistance(), getConfig().maxSilentFallDistance)); + ThreatSnapshot threatSnapshot = collectThreatSnapshot(p, level); + if (threatSnapshot.canDetect.isEmpty()) { + applyDimming(p, level); + } else { + clearDimming(p); + } + updateThreatGlows(p, threatSnapshot); + } + } + + private ThreatSnapshot collectThreatSnapshot(Player p, int level) { + ThreatSnapshot snapshot = new ThreatSnapshot(); + double detectionLookDotThreshold = getDetectionLookDotThreshold(); + double mobRadius = getStealthRadius(level); + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), mobRadius, mobRadius, mobRadius)) { + if (!(entity instanceof Mob mob)) { + continue; + } + + if (!getConfig().allMobsAffectStealthVisibility && !isTargetBlacklistType(mob.getType())) { + continue; + } + + snapshot.add(mob, getThreatLevel(mob, p, detectionLookDotThreshold)); + } - if (!getConfig().allMobsAffectStealthVisibility && !isTargetBlacklistType(mob.getType())) { - continue; - } + double playerRadius = getPlayerDetectionRadius(level); + for (Entity nearby : p.getWorld().getNearbyEntities(p.getLocation(), playerRadius, playerRadius, playerRadius)) { + if (!(nearby instanceof Player other)) { + continue; + } + if (other == p || other.isDead()) { + continue; + } - snapshot.add(mob, getThreatLevel(mob, p, detectionLookDotThreshold)); - } + snapshot.add(other, getThreatLevel(other, p, detectionLookDotThreshold)); + } - double playerRadius = getPlayerDetectionRadius(level); - for (Entity nearby : p.getWorld().getNearbyEntities(p.getLocation(), playerRadius, playerRadius, playerRadius)) { - if (!(nearby instanceof Player other)) { - continue; - } - if (other == p || other.isDead()) { - continue; - } + return snapshot; + } - snapshot.add(other, getThreatLevel(other, p, detectionLookDotThreshold)); - } + private void applyDimming(Player p, int level) { + p.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, getDimDurationTicks(level), getConfig().dimAmplifier, false, false, false), true); + dimmed.put(p.getUniqueId(), true); + } - return snapshot; + private void clearDimming(Player p) { + if (dimmed.remove(p.getUniqueId()) != null) { + p.removePotionEffect(PotionEffectType.DARKNESS); } + } - private void applyDimming(Player p, int level) { - p.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, getDimDurationTicks(level), getConfig().dimAmplifier, false, false, false), true); - dimmed.put(p.getUniqueId(), true); + private void updateThreatGlows(Player p, ThreatSnapshot snapshot) { + if (!getConfig().showThreatGlows) { + clearThreatGlows(p); + return; } - private void clearDimming(Player p) { - if (dimmed.remove(p.getUniqueId()) != null) { - p.removePotionEffect(PotionEffectType.DARKNESS); - } + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + if (glowingEntities == null) { + clearThreatGlows(p); + return; } - private void updateThreatGlows(Player p, ThreatSnapshot snapshot) { - if (!getConfig().showThreatGlows) { - clearThreatGlows(p); - return; - } - - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - if (glowingEntities == null) { - clearThreatGlows(p); - return; - } - - UUID viewerId = p.getUniqueId(); - Map active = threatGlows.computeIfAbsent(viewerId, k -> new java.util.concurrent.ConcurrentHashMap<>()); - - List stale = new ArrayList<>(); - for (UUID entityId : active.keySet()) { - if (!snapshot.threats.containsKey(entityId)) { - stale.add(entityId); - } - } - - for (UUID entityId : stale) { - Entity entity = Bukkit.getEntity(entityId); - if (entity != null) { - try { - glowingEntities.unsetGlowing(entity, p); - } catch (ReflectiveOperationException ignored) { - // Ignore reflective failures and continue clearing other entities. - } - } - active.remove(entityId); - } - - for (Map.Entry entry : snapshot.threats.entrySet()) { - UUID entityId = entry.getKey(); - ThreatLevel desired = entry.getValue(); - ThreatLevel current = active.get(entityId); - if (desired == current) { - continue; - } - - Entity entity = snapshot.entities.get(entityId); - if (entity == null) { - entity = Bukkit.getEntity(entityId); - } - if (entity == null || !entity.isValid()) { - continue; - } - - try { - glowingEntities.setGlowing(entity, p, getThreatColor(desired)); - active.put(entityId, desired); - } catch (ReflectiveOperationException ignored) { - // Ignore reflective failures and keep runtime behavior intact. - } - } + UUID viewerId = p.getUniqueId(); + Map active = threatGlows.computeIfAbsent(viewerId, k -> new java.util.concurrent.ConcurrentHashMap<>()); - if (active.isEmpty()) { - threatGlows.remove(viewerId); - } + List stale = new ArrayList<>(); + for (UUID entityId : active.keySet()) { + if (!snapshot.threats.containsKey(entityId)) { + stale.add(entityId); + } } - private void clearThreatGlows(Player p) { - Map active = threatGlows.remove(p.getUniqueId()); - if (active == null || active.isEmpty()) { - return; + for (UUID entityId : stale) { + Entity entity = Bukkit.getEntity(entityId); + if (entity != null) { + try { + glowingEntities.unsetGlowing(entity, p); + } catch (ReflectiveOperationException ignored) { + // Ignore reflective failures and continue clearing other entities. } + } + active.remove(entityId); + } - GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); - if (glowingEntities == null) { - return; - } + for (Map.Entry entry : snapshot.threats.entrySet()) { + UUID entityId = entry.getKey(); + ThreatLevel desired = entry.getValue(); + ThreatLevel current = active.get(entityId); + if (desired == current) { + continue; + } + + Entity entity = snapshot.entities.get(entityId); + if (entity == null) { + entity = Bukkit.getEntity(entityId); + } + if (entity == null || !entity.isValid()) { + continue; + } + + try { + glowingEntities.setGlowing(entity, p, getThreatColor(desired)); + active.put(entityId, desired); + } catch (ReflectiveOperationException ignored) { + // Ignore reflective failures and keep runtime behavior intact. + } + } - for (UUID entityId : active.keySet()) { - Entity entity = Bukkit.getEntity(entityId); - if (entity == null) { - continue; - } - - try { - glowingEntities.unsetGlowing(entity, p); - } catch (ReflectiveOperationException ignored) { - // Ignore reflective failures and continue clearing other entities. - } - } + if (active.isEmpty()) { + threatGlows.remove(viewerId); } + } - private ThreatLevel getThreatLevel(LivingEntity observer, LivingEntity target, double detectThreshold) { - if (!observer.hasLineOfSight(target)) { - return ThreatLevel.NONE; - } + private void clearThreatGlows(Player p) { + Map active = threatGlows.remove(p.getUniqueId()); + if (active == null || active.isEmpty()) { + return; + } - double lookDot = getLookDot(observer, target); - if (lookDot >= detectThreshold) { - return ThreatLevel.CAN_DETECT; - } + GlowingEntities glowingEntities = Adapt.instance.getGlowingEntities(); + if (glowingEntities == null) { + return; + } - double almostThreshold = Math.max(-1, detectThreshold - Math.max(0, getConfig().almostLookDotMargin)); - if (lookDot >= almostThreshold) { - return ThreatLevel.ALMOST_DETECT; - } + for (UUID entityId : active.keySet()) { + Entity entity = Bukkit.getEntity(entityId); + if (entity == null) { + continue; + } + + try { + glowingEntities.unsetGlowing(entity, p); + } catch (ReflectiveOperationException ignored) { + // Ignore reflective failures and continue clearing other entities. + } + } + } - return ThreatLevel.NONE; + private ThreatLevel getThreatLevel(LivingEntity observer, LivingEntity target, double detectThreshold) { + if (!observer.hasLineOfSight(target)) { + return ThreatLevel.NONE; } - private double getDetectionLookDotThreshold() { - return Math.max(-1, Math.min(1, getConfig().detectionLookDotThreshold)); + double lookDot = getLookDot(observer, target); + if (lookDot >= detectThreshold) { + return ThreatLevel.CAN_DETECT; } - private ChatColor getThreatColor(ThreatLevel level) { - return switch (level) { - case CAN_DETECT -> ChatColor.RED; - case ALMOST_DETECT -> ChatColor.GRAY; - default -> ChatColor.WHITE; - }; + double almostThreshold = Math.max(-1, detectThreshold - Math.max(0, getConfig().almostLookDotMargin)); + if (lookDot >= almostThreshold) { + return ThreatLevel.ALMOST_DETECT; } - private boolean isLookingAt(LivingEntity observer, LivingEntity target) { - return getLookDot(observer, target) >= getConfig().lookDotThreshold; + return ThreatLevel.NONE; + } + + private double getDetectionLookDotThreshold() { + return Math.max(-1, Math.min(1, getConfig().detectionLookDotThreshold)); + } + + private ChatColor getThreatColor(ThreatLevel level) { + return switch (level) { + case CAN_DETECT -> ChatColor.RED; + case ALMOST_DETECT -> ChatColor.GRAY; + default -> ChatColor.WHITE; + }; + } + + private boolean isLookingAt(LivingEntity observer, LivingEntity target) { + return getLookDot(observer, target) >= getConfig().lookDotThreshold; + } + + private double getLookDot(LivingEntity observer, LivingEntity target) { + Vector look = observer.getEyeLocation().getDirection().normalize(); + Vector toTarget = target.getEyeLocation().toVector().subtract(observer.getEyeLocation().toVector()); + if (toTarget.lengthSquared() <= 0.0001) { + return 1; } - private double getLookDot(LivingEntity observer, LivingEntity target) { - Vector look = observer.getEyeLocation().getDirection().normalize(); - Vector toTarget = target.getEyeLocation().toVector().subtract(observer.getEyeLocation().toVector()); - if (toTarget.lengthSquared() <= 0.0001) { - return 1; - } + toTarget.normalize(); + return look.dot(toTarget); + } - toTarget.normalize(); - return look.dot(toTarget); + private boolean isTargetBlacklistType(EntityType type) { + if (type == null) { + return false; } - private boolean isTargetBlacklistType(EntityType type) { - if (type == null) { - return false; - } + for (String raw : getConfig().targetingBlacklistTypes) { + if (raw == null || raw.isBlank()) { + continue; + } - for (String raw : getConfig().targetingBlacklistTypes) { - if (raw == null || raw.isBlank()) { - continue; - } - - try { - EntityType configured = EntityType.valueOf(raw.trim().toUpperCase(Locale.ROOT)); - if (configured == type) { - return true; - } - } catch (IllegalArgumentException ignored) { - // Ignore invalid enum names in config. - } + try { + EntityType configured = EntityType.valueOf(raw.trim().toUpperCase(Locale.ROOT)); + if (configured == type) { + return true; } + } catch (IllegalArgumentException ignored) { + // Ignore invalid enum names in config. + } + } - return false; - } - - private double getStealthRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); - } - - private double getPlayerDetectionRadius(int level) { - return getConfig().playerDetectionRadiusBase + (getLevelPercent(level) * getConfig().playerDetectionRadiusFactor); - } - - private int getDimDurationTicks(int level) { - return Math.max(10, (int) Math.round(getConfig().dimDurationTicksBase + (getLevelPercent(level) * getConfig().dimDurationTicksFactor))); - } - - private double getMobBackstabMultiplier(int level) { - return getConfig().mobBackstabBase + (getLevelPercent(level) * getConfig().mobBackstabFactor); - } - - private double getPlayerBackstabMultiplier(int level) { - return getConfig().playerBackstabBase + (getLevelPercent(level) * getConfig().playerBackstabFactor); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sneaking prevents hostile mob detection, and unseen hits deal backstab damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double playerDetectionRadiusBase = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double playerDetectionRadiusFactor = 14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dimDurationTicksBase = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double dimDurationTicksFactor = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Amplifier for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int dimAmplifier = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mobBackstabBase = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mobBackstabFactor = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double playerBackstabBase = 1.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double playerBackstabFactor = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Look Dot Threshold for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lookDotThreshold = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Drop for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerTargetDrop = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBonusDamage = 3.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Silent Fall Distance for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - float maxSilentFallDistance = 1.6f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Shows nearby threats with per-player glowing while sneaking (red = can detect, gray = almost).", impact = "Enable to get visual awareness of entities that can or almost can spot you.") - boolean showThreatGlows = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot margin below the full detection threshold used for gray 'almost detect' glow.", impact = "Higher values make gray warnings appear earlier; lower values make warnings stricter.") - double almostLookDotMargin = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot threshold for stealth visibility checks while sneaking.", impact = "Lower values make crossing an entity's view count as seen more easily; higher values require a more direct look.") - double detectionLookDotThreshold = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, all nearby mobs (including passive) can break hidden state when they have line-of-sight.", impact = "Enable to prevent stealth from feeling hidden in front of passive mobs like pigs.") - boolean allMobsAffectStealthVisibility = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Entity types that are NOT ignored by stealth targeting suppression.", impact = "Mobs listed here can still detect/target sneaking players with Silent Step.") - List targetingBlacklistTypes = new ArrayList<>(List.of("WARDEN", "WITHER", "PHANTOM", "ENDER_DRAGON")); - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum squared movement distance required before running target-drop scans.", impact = "Higher values skip tiny movement jitter and reduce move-event scan pressure.") - double minimumMoveSquared = 0.0025; - @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between mob target-drop scans while sneaking.", impact = "Lower values react faster but increase nearby-entity scan frequency.") - long targetDropScanIntervalMillis = 120; - } - - private enum ThreatLevel { - NONE, - ALMOST_DETECT, - CAN_DETECT - } - - private static class ThreatSnapshot { - private final Map threats = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map entities = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map canDetect = new java.util.concurrent.ConcurrentHashMap<>(); - - private void add(Entity entity, ThreatLevel level) { - if (entity == null || level == ThreatLevel.NONE) { - return; - } - - UUID id = entity.getUniqueId(); - entities.put(id, entity); - ThreatLevel existing = threats.get(id); - if (existing == null || level.ordinal() > existing.ordinal()) { - threats.put(id, level); - } - - if (level == ThreatLevel.CAN_DETECT) { - canDetect.put(id, level); - } - } + return false; + } + + private double getStealthRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private double getPlayerDetectionRadius(int level) { + return getConfig().playerDetectionRadiusBase + (getLevelPercent(level) * getConfig().playerDetectionRadiusFactor); + } + + private int getDimDurationTicks(int level) { + return Math.max(10, (int) Math.round(getConfig().dimDurationTicksBase + (getLevelPercent(level) * getConfig().dimDurationTicksFactor))); + } + + private double getMobBackstabMultiplier(int level) { + return getConfig().mobBackstabBase + (getLevelPercent(level) * getConfig().mobBackstabFactor); + } + + private double getPlayerBackstabMultiplier(int level) { + return getConfig().playerBackstabBase + (getLevelPercent(level) * getConfig().playerBackstabFactor); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private enum ThreatLevel { + NONE, + ALMOST_DETECT, + CAN_DETECT + } + + @NoArgsConstructor + @ConfigDescription("Sneaking prevents hostile mob detection, and unseen hits deal backstab damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double playerDetectionRadiusBase = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Detection Radius Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double playerDetectionRadiusFactor = 14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dimDurationTicksBase = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Duration Ticks Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double dimDurationTicksFactor = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Dim Amplifier for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int dimAmplifier = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mobBackstabBase = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mob Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mobBackstabFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Base for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double playerBackstabBase = 1.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Player Backstab Factor for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double playerBackstabFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Look Dot Threshold for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lookDotThreshold = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Drop for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTargetDrop = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBonusDamage = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Silent Fall Distance for the Stealth Silent Step adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + float maxSilentFallDistance = 1.6f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Shows nearby threats with per-player glowing while sneaking (red = can detect, gray = almost).", impact = "Enable to get visual awareness of entities that can or almost can spot you.") + boolean showThreatGlows = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot margin below the full detection threshold used for gray 'almost detect' glow.", impact = "Higher values make gray warnings appear earlier; lower values make warnings stricter.") + double almostLookDotMargin = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Look-dot threshold for stealth visibility checks while sneaking.", impact = "Lower values make crossing an entity's view count as seen more easily; higher values require a more direct look.") + double detectionLookDotThreshold = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, all nearby mobs (including passive) can break hidden state when they have line-of-sight.", impact = "Enable to prevent stealth from feeling hidden in front of passive mobs like pigs.") + boolean allMobsAffectStealthVisibility = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Entity types that are NOT ignored by stealth targeting suppression.", impact = "Mobs listed here can still detect/target sneaking players with Silent Step.") + List targetingBlacklistTypes = new ArrayList<>(List.of("WARDEN", "WITHER", "PHANTOM", "ENDER_DRAGON")); + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum squared movement distance required before running target-drop scans.", impact = "Higher values skip tiny movement jitter and reduce move-event scan pressure.") + double minimumMoveSquared = 0.0025; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between mob target-drop scans while sneaking.", impact = "Lower values react faster but increase nearby-entity scan frequency.") + long targetDropScanIntervalMillis = 120; + } + + private static class ThreatSnapshot { + private final Map threats = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map entities = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map canDetect = new java.util.concurrent.ConcurrentHashMap<>(); + + private void add(Entity entity, ThreatLevel level) { + if (entity == null || level == ThreatLevel.NONE) { + return; + } + + UUID id = entity.getUniqueId(); + entities.put(id, entity); + ThreatLevel existing = threats.get(id); + if (existing == null || level.ordinal() > existing.ordinal()) { + threats.put(id, level); + } + + if (level == ThreatLevel.CAN_DETECT) { + canDetect.put(id, level); + } } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index ecbc1c8bb..bd9fd302c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -24,12 +24,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.inventorygui.Inventories; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -47,102 +47,103 @@ import java.util.concurrent.ThreadLocalRandom; public class StealthSnatch extends SimpleAdaptation { - private final Set holds; - - public StealthSnatch() { - super("stealth-snatch"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.snatch.description")); - setDisplayName(Localizer.dLocalize("stealth.snatch.name")); - setIcon(Material.CHEST_MINECART); - setBaseCost(getConfig().baseCost); - setInterval(getConfig().snatchRate); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - holds = ConcurrentHashMap.newKeySet(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_stealth_snatch_2500") - .title(Localizer.dLocalize("advancement.challenge_stealth_snatch_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_snatch_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HOPPER) - .key("challenge_stealth_snatch_25k") - .title(Localizer.dLocalize("advancement.challenge_stealth_snatch_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_snatch_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_snatch_2500", "stealth.snatch.items-snatched", 2500, 400); - registerMilestone("challenge_stealth_snatch_25k", "stealth.snatch.items-snatched", 25000, 1500); + private final Set holds; + + public StealthSnatch() { + super("stealth-snatch"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.snatch.description")); + setDisplayName(Localizer.dLocalize("stealth.snatch.name")); + setIcon(Material.CHEST_MINECART); + setBaseCost(getConfig().baseCost); + setInterval(getConfig().snatchRate); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + holds = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_stealth_snatch_2500") + .title(Localizer.dLocalize("advancement.challenge_stealth_snatch_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_snatch_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HOPPER) + .key("challenge_stealth_snatch_25k") + .title(Localizer.dLocalize("advancement.challenge_stealth_snatch_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_snatch_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_snatch_2500", "stealth.snatch.items-snatched", 2500, 400); + registerMilestone("challenge_stealth_snatch_25k", "stealth.snatch.items-snatched", 25000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRange(getLevelPercent(level)), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.snatch.lore1")); + } + + @EventHandler + public void on(PlayerToggleSneakEvent e) { + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p)) { + return; } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRange(getLevelPercent(level)), 1) + C.GRAY + " " + Localizer.dLocalize("stealth.snatch.lore1")); + if (!canAccessChest(p, p.getLocation())) { + return; } - - @EventHandler - public void on(PlayerToggleSneakEvent e) { - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p)) { - return; - } - if (!canAccessChest(p, p.getLocation())) { - return; - } - if (e.isSneaking()) { - snatch(p); - } + if (e.isSneaking()) { + snatch(p); } + } - private void snatch(Player player) { - double factor = getLevelPercent(player); + private void snatch(Player player) { + double factor = getLevelPercent(player); - if (factor == 0) { - return; - } + if (factor == 0) { + return; + } - double range = getRange(factor); - HashSet items = new HashSet<>(); - for (Entity droppedItemEntity : player.getWorld().getNearbyEntities(player.getLocation(), range, range / 1.5, range)) { - if (droppedItemEntity instanceof Item droppedItem) { - if (droppedItem.getPickupDelay() <= 0 || droppedItem.getTicksLived() > 1) { - UUID owner = droppedItem.getOwner(); - if (owner == null || owner.equals(player.getUniqueId())) items.add(droppedItem); - } - } + double range = getRange(factor); + HashSet items = new HashSet<>(); + for (Entity droppedItemEntity : player.getWorld().getNearbyEntities(player.getLocation(), range, range / 1.5, range)) { + if (droppedItemEntity instanceof Item droppedItem) { + if (droppedItem.getPickupDelay() <= 0 || droppedItem.getTicksLived() > 1) { + UUID owner = droppedItem.getOwner(); + if (owner == null || owner.equals(player.getUniqueId())) + items.add(droppedItem); } + } + } - for (Item droppedItemEntity : items) { - if (!holds.contains(droppedItemEntity.getEntityId())) { - double dist = droppedItemEntity.getLocation().distanceSquared(player.getLocation()); - if (dist < range * range) { - ItemStack is = droppedItemEntity.getItemStack().clone(); - - if (Inventories.hasSpace(player.getInventory(), is)) { - holds.add(droppedItemEntity.getEntityId()); - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.BLOCK_LAVA_POP, 1f, (float) (1.0 + (ThreadLocalRandom.current().nextDouble() / 3D))); - safeGiveItem(player, droppedItemEntity, is); - getPlayer(player).getData().addStat("stealth.snatch.items-snatched", 1); - //sendCollected(player, droppedItemEntity); - int id = droppedItemEntity.getEntityId(); - J.runEntity(player, () -> holds.remove(Integer.valueOf(id)), 1); - } - } - } + for (Item droppedItemEntity : items) { + if (!holds.contains(droppedItemEntity.getEntityId())) { + double dist = droppedItemEntity.getLocation().distanceSquared(player.getLocation()); + if (dist < range * range) { + ItemStack is = droppedItemEntity.getItemStack().clone(); + + if (Inventories.hasSpace(player.getInventory(), is)) { + holds.add(droppedItemEntity.getEntityId()); + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.BLOCK_LAVA_POP, 1f, (float) (1.0 + (ThreadLocalRandom.current().nextDouble() / 3D))); + safeGiveItem(player, droppedItemEntity, is); + getPlayer(player).getData().addStat("stealth.snatch.items-snatched", 1); + //sendCollected(player, droppedItemEntity); + int id = droppedItemEntity.getEntityId(); + J.runEntity(player, () -> holds.remove(Integer.valueOf(id)), 1); + } } - + } } - private double getRange(double factor) { - return (factor * getConfig().radiusFactor) + 1; - } + } + + private double getRange(double factor) { + return (factor * getConfig().radiusFactor) + 1; + } /* public void sendCollected(Player p, Item item) { @@ -157,50 +158,50 @@ public void sendCollected(Player p, Item item) { } }*/ - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - if (i.isSneaking()) { - J.runEntity(i, () -> snatch(i)); - } - } - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - protected void onConfigReload(Config previousConfig, Config newConfig) { - super.onConfigReload(previousConfig, newConfig); - setInterval(newConfig.snatchRate); - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Snatch dropped items instantly while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snatch Rate for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int snatchRate = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 5.55; + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + if (i.isSneaking()) { + J.runEntity(i, () -> snatch(i)); + } } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + protected void onConfigReload(Config previousConfig, Config newConfig) { + super.onConfigReload(previousConfig, newConfig); + setInterval(newConfig.snatchRate); + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Snatch dropped items instantly while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Snatch Rate for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int snatchRate = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Stealth Snatch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 5.55; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java index 27a6aa699..4fcd8ef32 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSpeed.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.*; @@ -43,432 +43,432 @@ import java.util.UUID; public class StealthSpeed extends SimpleAdaptation { - private static final Sound DEFAULT_ACTIVATION_SOUND = Sound.PARTICLE_SOUL_ESCAPE; - private final Map states; - - public StealthSpeed() { - super("stealth-speed"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("stealth.speed.description")); - setDisplayName(Localizer.dLocalize("stealth.speed.name")); - setIcon(Material.MUSHROOM_STEW); - setBaseCost(getConfig().baseCost); - setInterval(getConfig().setInterval); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - states = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_BOOTS) - .key("challenge_stealth_speed_5k") - .title(Localizer.dLocalize("advancement.challenge_stealth_speed_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_speed_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_stealth_speed_5k", "stealth.speed.blocks-sneak-sprinted", 5000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + Localizer.dLocalize("stealth.speed.lore1")); - } - - @EventHandler - public void on(PlayerQuitEvent e) { - clearAndRemoveState(e.getPlayer()); - } - - @EventHandler - public void on(PlayerDeathEvent e) { - clearAndRemoveState(e.getEntity()); - } - - @Override - public void onTick() { - long now = System.currentTimeMillis(); - long statIntervalMs = Math.max(50L, getConfig().statIntervalMs); - - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - RuntimeState state = states.computeIfAbsent(p.getUniqueId(), key -> new RuntimeState()); - - if (!isEligible(p)) { - clearBoost(p, state); - continue; - } - - double levelFactor = getLevelPercent(p); - if (levelFactor <= 0) { - clearBoost(p, state); - continue; - } - - boolean crawling = isCrawlingOnLand(p); - float targetWalkSpeed = computeTargetWalkSpeed(state, p, levelFactor, crawling); - applyBoost(p, state, targetWalkSpeed, now); - applyAutoStep(p, state, now); - - if (!isMovingHorizontally(p, getConfig().movementVelocityThreshold)) { - continue; - } - - if (getConfig().showSoulParticles && M.r(getConfig().soulParticleChance)) { - p.spawnParticle(Particle.SOUL, p.getLocation().clone().add(0, getConfig().soulParticleYOffset, 0), 1, 0.14, 0.02, 0.14, 0); - } + private static final Sound DEFAULT_ACTIVATION_SOUND = Sound.PARTICLE_SOUL_ESCAPE; + private final Map states; + + public StealthSpeed() { + super("stealth-speed"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("stealth.speed.description")); + setDisplayName(Localizer.dLocalize("stealth.speed.name")); + setIcon(Material.MUSHROOM_STEW); + setBaseCost(getConfig().baseCost); + setInterval(getConfig().setInterval); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + states = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_BOOTS) + .key("challenge_stealth_speed_5k") + .title(Localizer.dLocalize("advancement.challenge_stealth_speed_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_speed_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_stealth_speed_5k", "stealth.speed.blocks-sneak-sprinted", 5000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getSpeed(getLevelPercent(level)), 0) + C.GRAY + Localizer.dLocalize("stealth.speed.lore1")); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + clearAndRemoveState(e.getPlayer()); + } + + @EventHandler + public void on(PlayerDeathEvent e) { + clearAndRemoveState(e.getEntity()); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + long statIntervalMs = Math.max(50L, getConfig().statIntervalMs); + + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + RuntimeState state = states.computeIfAbsent(p.getUniqueId(), key -> new RuntimeState()); + + if (!isEligible(p)) { + clearBoost(p, state); + continue; + } - if (now - state.lastStatMillis >= statIntervalMs) { - getPlayer(p).getData().addStat("stealth.speed.blocks-sneak-sprinted", 1); - state.lastStatMillis = now; - } - } + double levelFactor = getLevelPercent(p); + if (levelFactor <= 0) { + clearBoost(p, state); + continue; + } + + boolean crawling = isCrawlingOnLand(p); + float targetWalkSpeed = computeTargetWalkSpeed(state, p, levelFactor, crawling); + applyBoost(p, state, targetWalkSpeed, now); + applyAutoStep(p, state, now); + + if (!isMovingHorizontally(p, getConfig().movementVelocityThreshold)) { + continue; + } + + if (getConfig().showSoulParticles && M.r(getConfig().soulParticleChance)) { + p.spawnParticle(Particle.SOUL, p.getLocation().clone().add(0, getConfig().soulParticleYOffset, 0), 1, 0.14, 0.02, 0.14, 0); + } + + if (now - state.lastStatMillis >= statIntervalMs) { + getPlayer(p).getData().addStat("stealth.speed.blocks-sneak-sprinted", 1); + state.lastStatMillis = now; + } } - - private void applyBoost(Player p, RuntimeState state, float targetWalkSpeed, long now) { - if (!state.boosting) { - state.boosting = true; - state.originalWalkSpeed = p.getWalkSpeed(); - - long cooldown = Math.max(0, getConfig().activationSoundCooldownMs); - if (cooldown <= 0 || now - state.lastSoundMillis >= cooldown) { - p.playSound(p.getLocation(), DEFAULT_ACTIVATION_SOUND, getConfig().activationSoundVolume, getConfig().activationSoundPitch); - state.lastSoundMillis = now; - } - } - - float current = p.getWalkSpeed(); - if (Math.abs(current - targetWalkSpeed) > 0.0001f) { - p.setWalkSpeed(targetWalkSpeed); - } + } + + private void applyBoost(Player p, RuntimeState state, float targetWalkSpeed, long now) { + if (!state.boosting) { + state.boosting = true; + state.originalWalkSpeed = p.getWalkSpeed(); + + long cooldown = Math.max(0, getConfig().activationSoundCooldownMs); + if (cooldown <= 0 || now - state.lastSoundMillis >= cooldown) { + p.playSound(p.getLocation(), DEFAULT_ACTIVATION_SOUND, getConfig().activationSoundVolume, getConfig().activationSoundPitch); + state.lastSoundMillis = now; + } } - private void clearBoost(Player p, RuntimeState state) { - if (!state.boosting) { - return; - } + float current = p.getWalkSpeed(); + if (Math.abs(current - targetWalkSpeed) > 0.0001f) { + p.setWalkSpeed(targetWalkSpeed); + } + } - state.boosting = false; - float restore = clampWalkSpeed(state.originalWalkSpeed); - float current = p.getWalkSpeed(); - if (Math.abs(current - restore) > 0.0001f) { - p.setWalkSpeed(restore); - } + private void clearBoost(Player p, RuntimeState state) { + if (!state.boosting) { + return; } - private void clearAndRemoveState(Player p) { - if (p == null) { - return; - } + state.boosting = false; + float restore = clampWalkSpeed(state.originalWalkSpeed); + float current = p.getWalkSpeed(); + if (Math.abs(current - restore) > 0.0001f) { + p.setWalkSpeed(restore); + } + } - RuntimeState state = states.remove(p.getUniqueId()); - if (state == null) { - return; - } + private void clearAndRemoveState(Player p) { + if (p == null) { + return; + } - clearBoost(p, state); + RuntimeState state = states.remove(p.getUniqueId()); + if (state == null) { + return; } - private boolean isEligible(Player p) { - if (!hasActiveAdaptation(p)) { - return false; - } + clearBoost(p, state); + } - boolean crawlingOnLand = isCrawlingOnLand(p); - if (!p.isSneaking() && !crawlingOnLand) { - return false; - } + private boolean isEligible(Player p) { + if (!hasActiveAdaptation(p)) { + return false; + } - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } + boolean crawlingOnLand = isCrawlingOnLand(p); + if (!p.isSneaking() && !crawlingOnLand) { + return false; + } - if (p.isDead() || p.getVehicle() != null || p.isFlying() || p.isGliding()) { - return false; - } + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; + } - if ((p.isSwimming() || p.isInWater()) && !crawlingOnLand && !getConfig().allowWhileInWater) { - return false; - } + if (p.isDead() || p.getVehicle() != null || p.isFlying() || p.isGliding()) { + return false; + } - return !getConfig().requireGrounded || p.isOnGround(); + if ((p.isSwimming() || p.isInWater()) && !crawlingOnLand && !getConfig().allowWhileInWater) { + return false; } - private boolean isCrawlingOnLand(Player p) { - if (p.getBoundingBox().getHeight() > getConfig().crawlHeightMax) { - return false; - } + return !getConfig().requireGrounded || p.isOnGround(); + } - return !p.getEyeLocation().getBlock().isLiquid() && !p.getLocation().getBlock().isLiquid(); + private boolean isCrawlingOnLand(Player p) { + if (p.getBoundingBox().getHeight() > getConfig().crawlHeightMax) { + return false; } - private float computeTargetWalkSpeed(RuntimeState state, Player p, double levelFactor, boolean crawling) { - float base = state.boosting ? state.originalWalkSpeed : clampWalkSpeed(p.getWalkSpeed()); - if (!state.boosting && Math.abs(base) < 0.0001f) { - base = clampWalkSpeed(getConfig().baselineWalkSpeed); - } + return !p.getEyeLocation().getBlock().isLiquid() && !p.getLocation().getBlock().isLiquid(); + } - double bonus = getSpeed(levelFactor); - if (crawling) { - bonus *= Math.max(0, getConfig().crawlBonusMultiplier); - } + private float computeTargetWalkSpeed(RuntimeState state, Player p, double levelFactor, boolean crawling) { + float base = state.boosting ? state.originalWalkSpeed : clampWalkSpeed(p.getWalkSpeed()); + if (!state.boosting && Math.abs(base) < 0.0001f) { + base = clampWalkSpeed(getConfig().baselineWalkSpeed); + } - return clampWalkSpeed(base + (float) bonus); + double bonus = getSpeed(levelFactor); + if (crawling) { + bonus *= Math.max(0, getConfig().crawlBonusMultiplier); } - private void applyAutoStep(Player p, RuntimeState state, long now) { - if (!getConfig().enableAutoStep || !p.isOnGround()) { - return; - } + return clampWalkSpeed(base + (float) bonus); + } - long cooldown = Math.max(0, getConfig().autoStepCooldownMs); - if (cooldown > 0 && now - state.lastStepMillis < cooldown) { - return; - } + private void applyAutoStep(Player p, RuntimeState state, long now) { + if (!getConfig().enableAutoStep || !p.isOnGround()) { + return; + } - Vector direction = resolveAutoStepDirection(p); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - return; - } + long cooldown = Math.max(0, getConfig().autoStepCooldownMs); + if (cooldown > 0 && now - state.lastStepMillis < cooldown) { + return; + } - double probe = Math.max(0.1, getConfig().autoStepProbeDistance); - org.bukkit.Location feet = p.getLocation(); - org.bukkit.Location front = feet.clone().add(direction.multiply(probe)); + Vector direction = resolveAutoStepDirection(p); + if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { + return; + } - if (getConfig().enableAutoStepUp && tryStepUp(p, front, direction)) { - state.lastStepMillis = now; - return; - } + double probe = Math.max(0.1, getConfig().autoStepProbeDistance); + org.bukkit.Location feet = p.getLocation(); + org.bukkit.Location front = feet.clone().add(direction.multiply(probe)); - if (getConfig().enableAutoStepDown && tryStepDown(p, front, direction)) { - state.lastStepMillis = now; - } + if (getConfig().enableAutoStepUp && tryStepUp(p, front, direction)) { + state.lastStepMillis = now; + return; } - private Vector resolveAutoStepDirection(Player p) { - if (getConfig().autoStepUseInput) { - try { - Input input = p.getCurrentInput(); - if (input != null) { - VelocitySpeed.InputSnapshot snapshot = new VelocitySpeed.InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); - if (snapshot.hasHorizontal()) { - Vector inputDirection = VelocitySpeed.resolveHorizontalDirection(p, snapshot); - if (inputDirection.lengthSquared() > VelocitySpeed.EPSILON) { - return inputDirection; - } - } - } - } catch (NoSuchMethodError ex) { - // Runtime does not expose input API. Use velocity-based fallback. - Adapt.verbose("Player input API is unavailable on this runtime; using velocity fallback for stealth auto-step."); + if (getConfig().enableAutoStepDown && tryStepDown(p, front, direction)) { + state.lastStepMillis = now; + } + } + + private Vector resolveAutoStepDirection(Player p) { + if (getConfig().autoStepUseInput) { + try { + Input input = p.getCurrentInput(); + if (input != null) { + VelocitySpeed.InputSnapshot snapshot = new VelocitySpeed.InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); + if (snapshot.hasHorizontal()) { + Vector inputDirection = VelocitySpeed.resolveHorizontalDirection(p, snapshot); + if (inputDirection.lengthSquared() > VelocitySpeed.EPSILON) { + return inputDirection; } + } } + } catch (NoSuchMethodError ex) { + // Runtime does not expose input API. Use velocity-based fallback. + Adapt.verbose("Player input API is unavailable on this runtime; using velocity fallback for stealth auto-step."); + } + } - Vector movement = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); - double velocityThreshold = Math.max(0, getConfig().autoStepVelocityThreshold); - if (movement.lengthSquared() <= velocityThreshold * velocityThreshold) { - return new Vector(); - } - - return movement.normalize(); + Vector movement = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); + double velocityThreshold = Math.max(0, getConfig().autoStepVelocityThreshold); + if (movement.lengthSquared() <= velocityThreshold * velocityThreshold) { + return new Vector(); } - private boolean tryStepUp(Player p, org.bukkit.Location front, Vector direction) { - if (!isStepObstacle(front, 0)) { - return false; - } + return movement.normalize(); + } - if (!hasStepHeadroom(p, front)) { - return false; - } + private boolean tryStepUp(Player p, org.bukkit.Location front, Vector direction) { + if (!isStepObstacle(front, 0)) { + return false; + } - org.bukkit.Location destination = p.getLocation().clone() - .add(direction.clone().multiply(Math.max(0.05, getConfig().autoStepForwardPush))) - .add(0, 1, 0); - if (!isDestinationSafe(p, destination, true)) { - return false; - } + if (!hasStepHeadroom(p, front)) { + return false; + } - J.teleport(p, destination); - return true; + org.bukkit.Location destination = p.getLocation().clone() + .add(direction.clone().multiply(Math.max(0.05, getConfig().autoStepForwardPush))) + .add(0, 1, 0); + if (!isDestinationSafe(p, destination, true)) { + return false; } - private boolean tryStepDown(Player p, org.bukkit.Location front, Vector direction) { - if (!isPassable(front, 0)) { - return false; - } + J.teleport(p, destination); + return true; + } - if (!isPassable(front, -1)) { - return false; - } + private boolean tryStepDown(Player p, org.bukkit.Location front, Vector direction) { + if (!isPassable(front, 0)) { + return false; + } - if (!isSolid(front, -2)) { - return false; - } + if (!isPassable(front, -1)) { + return false; + } - org.bukkit.Location destination = p.getLocation().clone() - .add(direction.clone().multiply(Math.max(0.05, getConfig().autoStepForwardPush))) - .add(0, -1, 0); - if (!isDestinationSafe(p, destination, true)) { - return false; - } + if (!isSolid(front, -2)) { + return false; + } - J.teleport(p, destination); - p.setFallDistance(0); - return true; + org.bukkit.Location destination = p.getLocation().clone() + .add(direction.clone().multiply(Math.max(0.05, getConfig().autoStepForwardPush))) + .add(0, -1, 0); + if (!isDestinationSafe(p, destination, true)) { + return false; } - private boolean isStepObstacle(org.bukkit.Location base, int yOffset) { - var block = base.clone().add(0, yOffset, 0).getBlock(); - if (block.isLiquid() || block.isPassable() || !block.getType().isSolid()) { - return false; - } + J.teleport(p, destination); + p.setFallDistance(0); + return true; + } - double obstacleHeight = block.getBoundingBox().getHeight(); - return obstacleHeight >= Math.max(0.05, getConfig().stepObstacleMinHeight); + private boolean isStepObstacle(org.bukkit.Location base, int yOffset) { + org.bukkit.block.Block block = base.clone().add(0, yOffset, 0).getBlock(); + if (block.isLiquid() || block.isPassable() || !block.getType().isSolid()) { + return false; } - private boolean isSolid(org.bukkit.Location base, int yOffset) { - var block = base.clone().add(0, yOffset, 0).getBlock(); - return block.getType().isSolid() && !block.isLiquid() && !block.isPassable(); - } + double obstacleHeight = block.getBoundingBox().getHeight(); + return obstacleHeight >= Math.max(0.05, getConfig().stepObstacleMinHeight); + } - private boolean isPassable(org.bukkit.Location base, int yOffset) { - var block = base.clone().add(0, yOffset, 0).getBlock(); - return block.isPassable() && !block.isLiquid(); - } + private boolean isSolid(org.bukkit.Location base, int yOffset) { + org.bukkit.block.Block block = base.clone().add(0, yOffset, 0).getBlock(); + return block.getType().isSolid() && !block.isLiquid() && !block.isPassable(); + } - private boolean hasStepHeadroom(Player p, org.bukkit.Location front) { - if (!isPassable(front, 1)) { - return false; - } + private boolean isPassable(org.bukkit.Location base, int yOffset) { + org.bukkit.block.Block block = base.clone().add(0, yOffset, 0).getBlock(); + return block.isPassable() && !block.isLiquid(); + } - if (requiresDoubleHeadroom(p) && !isPassable(front, 2)) { - return false; - } + private boolean hasStepHeadroom(Player p, org.bukkit.Location front) { + if (!isPassable(front, 1)) { + return false; + } - return true; + if (requiresDoubleHeadroom(p) && !isPassable(front, 2)) { + return false; } - private boolean isDestinationSafe(Player p, org.bukkit.Location destination, boolean requireFloor) { - if (!isPassable(destination, 0)) { - return false; - } + return true; + } - if (requiresDoubleHeadroom(p) && !isPassable(destination, 1)) { - return false; - } + private boolean isDestinationSafe(Player p, org.bukkit.Location destination, boolean requireFloor) { + if (!isPassable(destination, 0)) { + return false; + } - if (requireFloor && !isSolid(destination, -1)) { - return false; - } + if (requiresDoubleHeadroom(p) && !isPassable(destination, 1)) { + return false; + } - return true; - } - - private boolean requiresDoubleHeadroom(Player p) { - return p.getBoundingBox().getHeight() >= Math.max(0.5, getConfig().doubleHeadroomHeightThreshold); - } - - private boolean isMovingHorizontally(Player p, double threshold) { - Vector horizontal = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); - double t = Math.max(0, threshold); - return horizontal.lengthSquared() > t * t; - } - - private float clampWalkSpeed(float value) { - float min = Math.max(-1f, getConfig().minWalkSpeed); - float max = Math.min(1f, Math.max(min, getConfig().maxWalkSpeed)); - return Math.max(min, Math.min(max, value)); - } - - private double getSpeed(double factor) { - return Math.max(0, factor * getConfig().maxSpeedBonus); - } - - private static class RuntimeState { - private boolean boosting; - private float originalWalkSpeed = 0.2f; - private long lastSoundMillis; - private long lastStatMillis; - private long lastStepMillis; - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Gain speed while sneaking.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update stealth speed.", impact = "Lower values feel more responsive but run updates more frequently.") - long setInterval = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback baseline walk speed if no original speed has been captured yet.", impact = "Usually keep this at vanilla default unless another plugin changes baseline speeds globally.") - float baselineWalkSpeed = 0.2f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum additional walk speed granted at max level.", impact = "Higher values make stealth speed more noticeable.") - double maxSpeedBonus = 0.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to bonus speed while crawling on land.", impact = "Higher values make crawling keep pace with sneaking.") - double crawlBonusMultiplier = 1.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum walk speed clamp used when applying the boost.", impact = "Keep near default to avoid unexpected slowdowns from conflicting systems.") - float minWalkSpeed = -1f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed clamp used when applying the boost.", impact = "Lower values are safer for anticheat; higher values feel faster.") - float maxWalkSpeed = 1f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables automatic vertical stepping while stealth speed is active.", impact = "Helps smooth sneaking over one-block terrain changes.") - boolean enableAutoStep = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping up one block while moving.", impact = "Reduces sneak interruption when encountering small ledges.") - boolean enableAutoStepUp = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping down one block while moving.", impact = "Only steps down when the drop is exactly one block.") - boolean enableAutoStepDown = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Forward probe distance for auto-step checks.", impact = "Higher values detect ledges earlier but can feel more aggressive.") - double autoStepProbeDistance = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal push applied during each auto-step teleport.", impact = "Higher values move farther onto/off the next block and reduce repeat stepping in place.") - double autoStepForwardPush = 0.36; - @art.arcane.adapt.util.config.ConfigDoc(value = "Uses direct movement input for auto-step direction when available.", impact = "Helps auto-step trigger while pressing into obstacles, even when velocity is near zero.") - boolean autoStepUseInput = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity required before auto-step runs.", impact = "Higher values avoid accidental stepping while nearly idle.") - double autoStepVelocityThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum delay between auto-step teleports.", impact = "Higher values reduce repeated stepping in tight terrain.") - long autoStepCooldownMs = 90; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum obstacle collision height that counts as a step-up blocker.", impact = "Higher values ignore small lips/slabs; lower values step up more aggressively.") - double stepObstacleMinHeight = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Bounding-box height above which two-block headroom is required for step-up.", impact = "Lower values are stricter; higher values allow sneaking/crawling to step in tighter spaces.") - double doubleHeadroomHeightThreshold = 1.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum bounding-box height counted as crawling on land.", impact = "Higher values make crawl detection more permissive.") - double crawlHeightMax = 0.61; - @art.arcane.adapt.util.config.ConfigDoc(value = "Requires players to be grounded for stealth speed to run.", impact = "True avoids midair acceleration and keeps behavior stable.") - boolean requireGrounded = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stealth speed to run while the player is in water.", impact = "False prevents stealth from overriding seaborne-style underwater movement effects.") - boolean allowWhileInWater = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity used to count the player as moving for FX/stat tracking.", impact = "Higher values reduce effects while nearly stationary.") - double movementVelocityThreshold = 0.005; - @art.arcane.adapt.util.config.ConfigDoc(value = "Shows a subtle soul particle near the player's feet while stealth speed is active.", impact = "Visual feedback visible only to the boosted player.") - boolean showSoulParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Chance per tick to spawn a soul particle while moving.", impact = "Higher values make the effect denser; lower values are subtler.") - double soulParticleChance = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for the soul particle effect.", impact = "Small positive values keep particles around floor level.") - double soulParticleYOffset = 0.02; - @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound volume heard by the boosted player.", impact = "Higher values are louder; lower values are subtler.") - float activationSoundVolume = 1.6f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound pitch heard by the boosted player.", impact = "Higher values raise tone; lower values deepen it.") - float activationSoundPitch = 0.9f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between activation sounds.", impact = "Higher values reduce audio spam when repeatedly starting/stopping.") - long activationSoundCooldownMs = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between progression stat increments while moving with stealth speed.", impact = "Controls how quickly the sneak-speed progression stat accumulates.") - long statIntervalMs = 200; + if (requireFloor && !isSolid(destination, -1)) { + return false; } + + return true; + } + + private boolean requiresDoubleHeadroom(Player p) { + return p.getBoundingBox().getHeight() >= Math.max(0.5, getConfig().doubleHeadroomHeightThreshold); + } + + private boolean isMovingHorizontally(Player p, double threshold) { + Vector horizontal = new Vector(p.getVelocity().getX(), 0, p.getVelocity().getZ()); + double t = Math.max(0, threshold); + return horizontal.lengthSquared() > t * t; + } + + private float clampWalkSpeed(float value) { + float min = Math.max(-1f, getConfig().minWalkSpeed); + float max = Math.min(1f, Math.max(min, getConfig().maxWalkSpeed)); + return Math.max(min, Math.min(max, value)); + } + + private double getSpeed(double factor) { + return Math.max(0, factor * getConfig().maxSpeedBonus); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private static class RuntimeState { + private boolean boosting; + private float originalWalkSpeed = 0.2f; + private long lastSoundMillis; + private long lastStatMillis; + private long lastStepMillis; + } + + @NoArgsConstructor + @ConfigDescription("Gain speed while sneaking.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval (ms) used to update stealth speed.", impact = "Lower values feel more responsive but run updates more frequently.") + long setInterval = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback baseline walk speed if no original speed has been captured yet.", impact = "Usually keep this at vanilla default unless another plugin changes baseline speeds globally.") + float baselineWalkSpeed = 0.2f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum additional walk speed granted at max level.", impact = "Higher values make stealth speed more noticeable.") + double maxSpeedBonus = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Multiplier applied to bonus speed while crawling on land.", impact = "Higher values make crawling keep pace with sneaking.") + double crawlBonusMultiplier = 1.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum walk speed clamp used when applying the boost.", impact = "Keep near default to avoid unexpected slowdowns from conflicting systems.") + float minWalkSpeed = -1f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum walk speed clamp used when applying the boost.", impact = "Lower values are safer for anticheat; higher values feel faster.") + float maxWalkSpeed = 1f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables automatic vertical stepping while stealth speed is active.", impact = "Helps smooth sneaking over one-block terrain changes.") + boolean enableAutoStep = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping up one block while moving.", impact = "Reduces sneak interruption when encountering small ledges.") + boolean enableAutoStepUp = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stepping down one block while moving.", impact = "Only steps down when the drop is exactly one block.") + boolean enableAutoStepDown = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Forward probe distance for auto-step checks.", impact = "Higher values detect ledges earlier but can feel more aggressive.") + double autoStepProbeDistance = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal push applied during each auto-step teleport.", impact = "Higher values move farther onto/off the next block and reduce repeat stepping in place.") + double autoStepForwardPush = 0.36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Uses direct movement input for auto-step direction when available.", impact = "Helps auto-step trigger while pressing into obstacles, even when velocity is near zero.") + boolean autoStepUseInput = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity required before auto-step runs.", impact = "Higher values avoid accidental stepping while nearly idle.") + double autoStepVelocityThreshold = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum delay between auto-step teleports.", impact = "Higher values reduce repeated stepping in tight terrain.") + long autoStepCooldownMs = 90; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum obstacle collision height that counts as a step-up blocker.", impact = "Higher values ignore small lips/slabs; lower values step up more aggressively.") + double stepObstacleMinHeight = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bounding-box height above which two-block headroom is required for step-up.", impact = "Lower values are stricter; higher values allow sneaking/crawling to step in tighter spaces.") + double doubleHeadroomHeightThreshold = 1.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum bounding-box height counted as crawling on land.", impact = "Higher values make crawl detection more permissive.") + double crawlHeightMax = 0.61; + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires players to be grounded for stealth speed to run.", impact = "True avoids midair acceleration and keeps behavior stable.") + boolean requireGrounded = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows stealth speed to run while the player is in water.", impact = "False prevents stealth from overriding seaborne-style underwater movement effects.") + boolean allowWhileInWater = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum horizontal velocity used to count the player as moving for FX/stat tracking.", impact = "Higher values reduce effects while nearly stationary.") + double movementVelocityThreshold = 0.005; + @art.arcane.adapt.util.config.ConfigDoc(value = "Shows a subtle soul particle near the player's feet while stealth speed is active.", impact = "Visual feedback visible only to the boosted player.") + boolean showSoulParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance per tick to spawn a soul particle while moving.", impact = "Higher values make the effect denser; lower values are subtler.") + double soulParticleChance = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset for the soul particle effect.", impact = "Small positive values keep particles around floor level.") + double soulParticleYOffset = 0.02; + @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound volume heard by the boosted player.", impact = "Higher values are louder; lower values are subtler.") + float activationSoundVolume = 1.6f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Activation sound pitch heard by the boosted player.", impact = "Higher values raise tone; lower values deepen it.") + float activationSoundPitch = 0.9f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between activation sounds.", impact = "Higher values reduce audio spam when repeatedly starting/stopping.") + long activationSoundCooldownMs = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum time between progression stat increments while moving with stealth speed.", impact = "Controls how quickly the sneak-speed progression stat accumulates.") + long statIntervalMs = 200; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java index a39101517..b82de6083 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/util/EntityListing.java @@ -25,42 +25,42 @@ public class EntityListing { - @Getter - public static List aggroMobs = List.of( - EntityType.EVOKER, - EntityType.VINDICATOR, - EntityType.PILLAGER, - EntityType.RAVAGER, - EntityType.VEX, - EntityType.ENDERMITE, - EntityType.GUARDIAN, - EntityType.ELDER_GUARDIAN, - EntityType.SKELETON, - EntityType.SHULKER, - EntityType.SKELETON_HORSE, - EntityType.HUSK, - EntityType.STRAY, - EntityType.PHANTOM, - EntityType.BLAZE, - EntityType.CREEPER, - EntityType.GHAST, - EntityType.MAGMA_CUBE, - EntityType.SILVERFISH, - EntityType.SLIME, - EntityType.SPIDER, - EntityType.CAVE_SPIDER, - EntityType.ZOMBIE, - EntityType.ZOMBIE_HORSE, - EntityType.ZOMBIE_VILLAGER, - EntityType.DROWNED, - EntityType.WITHER_SKELETON, - EntityType.WITCH, - EntityType.HOGLIN, - EntityType.ZOGLIN, - EntityType.PIGLIN, - EntityType.PIGLIN_BRUTE, - EntityType.ENDERMAN - ); + @Getter + public static List aggroMobs = List.of( + EntityType.EVOKER, + EntityType.VINDICATOR, + EntityType.PILLAGER, + EntityType.RAVAGER, + EntityType.VEX, + EntityType.ENDERMITE, + EntityType.GUARDIAN, + EntityType.ELDER_GUARDIAN, + EntityType.SKELETON, + EntityType.SHULKER, + EntityType.SKELETON_HORSE, + EntityType.HUSK, + EntityType.STRAY, + EntityType.PHANTOM, + EntityType.BLAZE, + EntityType.CREEPER, + EntityType.GHAST, + EntityType.MAGMA_CUBE, + EntityType.SILVERFISH, + EntityType.SLIME, + EntityType.SPIDER, + EntityType.CAVE_SPIDER, + EntityType.ZOMBIE, + EntityType.ZOMBIE_HORSE, + EntityType.ZOMBIE_VILLAGER, + EntityType.DROWNED, + EntityType.WITHER_SKELETON, + EntityType.WITCH, + EntityType.HOGLIN, + EntityType.ZOGLIN, + EntityType.PIGLIN, + EntityType.PIGLIN_BRUTE, + EntityType.ENDERMAN + ); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java index ce0899475..dfe3b42d0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsBloodyBlade.java @@ -27,9 +27,9 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -47,148 +47,148 @@ import java.util.UUID; public class SwordsBloodyBlade extends SimpleAdaptation { - private final Map cooldowns; - private final Set bleedingEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); - private final Map bleedSource = new java.util.concurrent.ConcurrentHashMap<>(); - - public SwordsBloodyBlade() { - super("sword-bloody-blade"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.bloody_blade.description")); - setDisplayName(Localizer.dLocalize("sword.bloody_blade.name")); - setIcon(Material.RED_DYE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(5534); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_bloody_500") - .title(Localizer.dLocalize("advancement.challenge_swords_bloody_500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_bloody_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_swords_bloody_500", "swords.bloody-blade.bleed-damage", 500, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_swords_bloody_kills_100") - .title(Localizer.dLocalize("advancement.challenge_swords_bloody_kills_100.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_bloody_kills_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_swords_bloody_kills_100", "swords.bloody-blade.bleed-kills", 100, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getDurationOfEffect(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore2")); - v.addLore(C.RED + "* " + Form.duration(getCooldown(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore3")); - } - - public long getCooldown(int level) { - return getConfig().cooldown * level; - } - - public long getDurationOfEffect(int level) { - return getConfig().effectDuration * level; - } - - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown > System.currentTimeMillis()) - return; - Entity victim = e.getEntity(); - cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); - if (!canDamageTarget(p, victim)) return; - if (areParticlesEnabled()) { - BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getConfig().damagePerBleedProc, l) : new BleedEffect(Adapt.instance.adaptEffectManager); - blood.setEntity(victim); - blood.material = Material.CRIMSON_ROOTS; - blood.height = -1; - blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); - blood.period = 5; //5 Every second, make a proc - blood.hurt = false; + private final Map cooldowns; + private final Set bleedingEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map bleedSource = new java.util.concurrent.ConcurrentHashMap<>(); + + public SwordsBloodyBlade() { + super("sword-bloody-blade"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.bloody_blade.description")); + setDisplayName(Localizer.dLocalize("sword.bloody_blade.name")); + setIcon(Material.RED_DYE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(5534); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_bloody_500") + .title(Localizer.dLocalize("advancement.challenge_swords_bloody_500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_bloody_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_swords_bloody_500", "swords.bloody-blade.bleed-damage", 500, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_swords_bloody_kills_100") + .title(Localizer.dLocalize("advancement.challenge_swords_bloody_kills_100.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_bloody_kills_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_swords_bloody_kills_100", "swords.bloody-blade.bleed-kills", 100, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getDurationOfEffect(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore2")); + v.addLore(C.RED + "* " + Form.duration(getCooldown(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.bloody_blade.lore3")); + } + + public long getCooldown(int level) { + return getConfig().cooldown * level; + } + + public long getDurationOfEffect(int level) { + return getConfig().effectDuration * level; + } + + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown > System.currentTimeMillis()) + return; + Entity victim = e.getEntity(); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); + if (!canDamageTarget(p, victim)) return; + if (areParticlesEnabled()) { + BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getConfig().damagePerBleedProc, l) : new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(victim); + blood.material = Material.CRIMSON_ROOTS; + blood.height = -1; + blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); + blood.period = 5; //5 Every second, make a proc + blood.hurt = false; // blood.callback = () -> { // Adapt.mAdapt.msgp(sender.player(),(p,"You bled out.."); // p.setHealth(1d); // }; - blood.start(); - } else { - BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getConfig().damagePerBleedProc, l) : new BleedEffect(Adapt.instance.adaptEffectManager); - blood.setEntity(victim); - blood.material = Material.VOID_AIR; - blood.height = -1; - blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); - blood.period = 5; //5 Every second, make a proc - blood.hurt = false; - blood.start(); - } - bleedingEntities.add(victim.getUniqueId()); - bleedSource.put(victim.getUniqueId(), p.getUniqueId()); - getPlayer(p).getData().addStat("swords.bloody-blade.bleed-damage", 1); - - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - UUID victimId = e.getEntity().getUniqueId(); - if (bleedingEntities.remove(victimId)) { - UUID sourceId = bleedSource.remove(victimId); - Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); - if (source != null && source.isOnline()) { - getPlayer(source).getData().addStat("swords.bloody-blade.bleed-kills", 1); - } - } - } - - - @Override - public void onTick() { + blood.start(); + } else { + BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getConfig().damagePerBleedProc, l) : new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(victim); + blood.material = Material.VOID_AIR; + blood.height = -1; + blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); + blood.period = 5; //5 Every second, make a proc + blood.hurt = false; + blood.start(); + } + bleedingEntities.add(victim.getUniqueId()); + bleedSource.put(victim.getUniqueId(), p.getUniqueId()); + getPlayer(p).getData().addStat("swords.bloody-blade.bleed-damage", 1); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sword strikes cause bleeding over time.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long cooldown = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Bleed Proc for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double damagePerBleedProc = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long effectDuration = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Bloody Blade adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.325; - + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + UUID victimId = e.getEntity().getUniqueId(); + if (bleedingEntities.remove(victimId)) { + UUID sourceId = bleedSource.remove(victimId); + Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); + if (source != null && source.isOnline()) { + getPlayer(source).getData().addStat("swords.bloody-blade.bleed-kills", 1); + } } + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sword strikes cause bleeding over time.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long cooldown = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Bleed Proc for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double damagePerBleedProc = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Bloody Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long effectDuration = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Bloody Blade adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.325; + + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java index 54f862ff0..58bff34fe 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsCrimsonCyclone.java @@ -27,10 +27,10 @@ import art.arcane.adapt.content.adaptation.sword.effects.DamagingBleedEffect; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -46,260 +46,260 @@ import org.bukkit.inventory.meta.Damageable; public class SwordsCrimsonCyclone extends SimpleAdaptation { - public SwordsCrimsonCyclone() { - super("sword-crimson-cyclone"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.crimson_cyclone.description")); - setDisplayName(Localizer.dLocalize("sword.crimson_cyclone.name")); - setIcon(Material.NETHERITE_SWORD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_cyclone_500") - .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_swords_cyclone_5k") - .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_swords_cyclone_500", "swords.crimson-cyclone.mobs-hit", 500, 400); - registerMilestone("challenge_swords_cyclone_5k", "swords.crimson-cyclone.mobs-hit", 5000, 1500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_swords_cyclone_6") - .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_6.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_6.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + public SwordsCrimsonCyclone() { + super("sword-crimson-cyclone"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.crimson_cyclone.description")); + setDisplayName(Localizer.dLocalize("sword.crimson_cyclone.name")); + setIcon(Material.NETHERITE_SWORD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_cyclone_500") + .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_swords_cyclone_5k") + .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_swords_cyclone_500", "swords.crimson-cyclone.mobs-hit", 500, 400); + registerMilestone("challenge_swords_cyclone_5k", "swords.crimson-cyclone.mobs-hit", 5000, 1500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_swords_cyclone_6") + .title(Localizer.dLocalize("advancement.challenge_swords_cyclone_6.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_cyclone_6.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getBaseDamage(level), 2) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getBaseDamage(level), 2) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("sword.crimson_cyclone.lore3")); + Player p = combat.attacker(); + LivingEntity primaryTarget = combat.target(); + ItemStack hand = combat.mainHand(); + if (!isCritTrigger(p)) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e, this::isSword); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - LivingEntity primaryTarget = combat.target(); - ItemStack hand = combat.mainHand(); - if (!isCritTrigger(p)) { - return; - } - - int level = combat.level(); - int hungerCost = getHungerCost(level); - if (p.getFoodLevel() < hungerCost) { - return; - } - - if (!applyDurabilityCost(hand, getDurabilityCost(level))) { - return; - } - - int hits = 0; - double radius = getRadius(level); - double damage = getBaseDamage(level); - p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); - p.setCooldown(hand.getType(), getCooldownTicks(level)); - - e.setDamage(e.getDamage() + damage); - applyBleed(primaryTarget, level); - if (areParticlesEnabled()) { - primaryTarget.getWorld().spawnParticle(Particle.CRIMSON_SPORE, primaryTarget.getLocation().add(0, 0.8, 0), 8, 0.2, 0.35, 0.2, 0.01); - } - hits++; - - for (Entity entity : primaryTarget.getWorld().getNearbyEntities(primaryTarget.getLocation(), radius, radius, radius)) { - if (!(entity instanceof LivingEntity target)) { - continue; - } - - if (target == p || target == primaryTarget) { - continue; - } - - if (!canDamageTarget(p, target)) { - continue; - } - - target.damage(damage, p); - applyBleed(target, level); - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.CRIMSON_SPORE, target.getLocation().add(0, 0.8, 0), 8, 0.2, 0.35, 0.2, 0.01); - } - hits++; - } - - if (hits <= 0) { - return; - } - - if (areParticlesEnabled()) { - - p.getWorld().spawnParticle(Particle.SWEEP_ATTACK, primaryTarget.getLocation().add(0, 1, 0), 2, 0.4, 0.1, 0.4, 0.02); - - } - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, primaryTarget.getLocation().add(0, 1, 0), 36, 0.8, 0.4, 0.8, 0.02); - } - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(primaryTarget.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.7f); - sp.play(primaryTarget.getLocation(), Sound.ENTITY_WITHER_HURT, 0.65f, 1.45f); - xp(p, hits * getConfig().xpPerTargetHit); - getPlayer(p).getData().addStat("swords.crimson-cyclone.mobs-hit", hits); - - // Special achievement: hit 6+ mobs with one activation - if (hits >= 6 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_swords_cyclone_6")) { - getPlayer(p).getAdvancementHandler().grant("challenge_swords_cyclone_6"); - } + int level = combat.level(); + int hungerCost = getHungerCost(level); + if (p.getFoodLevel() < hungerCost) { + return; } - private boolean isCritTrigger(Player p) { - return p.getFallDistance() >= getConfig().minFallDistanceForCrit - && !p.isOnGround() - && !p.isInWater() - && !p.isInsideVehicle() - && !p.isClimbing(); + if (!applyDurabilityCost(hand, getDurabilityCost(level))) { + return; } - private boolean applyDurabilityCost(ItemStack hand, int durabilityCost) { - if (!(hand.getItemMeta() instanceof Damageable damageable) || hand.getType().getMaxDurability() <= 0) { - return true; - } + int hits = 0; + double radius = getRadius(level); + double damage = getBaseDamage(level); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + p.setCooldown(hand.getType(), getCooldownTicks(level)); - int max = hand.getType().getMaxDurability(); - int next = damageable.getDamage() + durabilityCost; - if (next >= max) { - return false; - } - - damageable.setDamage(next); - hand.setItemMeta(damageable); - return true; + e.setDamage(e.getDamage() + damage); + applyBleed(primaryTarget, level); + if (areParticlesEnabled()) { + primaryTarget.getWorld().spawnParticle(Particle.CRIMSON_SPORE, primaryTarget.getLocation().add(0, 0.8, 0), 8, 0.2, 0.35, 0.2, 0.01); } - - private double getRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + hits++; + + for (Entity entity : primaryTarget.getWorld().getNearbyEntities(primaryTarget.getLocation(), radius, radius, radius)) { + if (!(entity instanceof LivingEntity target)) { + continue; + } + + if (target == p || target == primaryTarget) { + continue; + } + + if (!canDamageTarget(p, target)) { + continue; + } + + target.damage(damage, p); + applyBleed(target, level); + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.CRIMSON_SPORE, target.getLocation().add(0, 0.8, 0), 8, 0.2, 0.35, 0.2, 0.01); + } + hits++; } - private double getBaseDamage(int level) { - return getConfig().baseDamage + (getLevelPercent(level) * getConfig().damageFactor); + if (hits <= 0) { + return; } - private void applyBleed(LivingEntity target, int level) { - BleedEffect bleed = new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getBleedDamagePerProc(level), target); - bleed.setEntity(target); - bleed.material = getConfig().showBleedParticles ? Material.CRIMSON_ROOTS : Material.VOID_AIR; - bleed.height = -1; - bleed.period = 5; - bleed.hurt = false; - bleed.iterations = Math.max(4, (int) Math.ceil(getBleedTicks(level) / 5D)); - bleed.start(); - } + if (areParticlesEnabled()) { - private int getBleedTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().bleedTicksBase + (getLevelPercent(level) * getConfig().bleedTicksFactor))); - } + p.getWorld().spawnParticle(Particle.SWEEP_ATTACK, primaryTarget.getLocation().add(0, 1, 0), 2, 0.4, 0.1, 0.4, 0.02); - private double getBleedDamagePerProc(int level) { - return Math.max(0.01, getConfig().bleedDamagePerProcBase + (getLevelPercent(level) * getConfig().bleedDamagePerProcFactor)); } - - private int getHungerCost(int level) { - return Math.max(1, (int) Math.round(getConfig().hungerCostBase - (getLevelPercent(level) * getConfig().hungerCostFactor))); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, primaryTarget.getLocation().add(0, 1, 0), 36, 0.8, 0.4, 0.8, 0.02); } - - private int getDurabilityCost(int level) { - return Math.max(1, (int) Math.round(getConfig().durabilityCostBase - (getLevelPercent(level) * getConfig().durabilityCostFactor))); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(primaryTarget.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.7f); + sp.play(primaryTarget.getLocation(), Sound.ENTITY_WITHER_HURT, 0.65f, 1.45f); + xp(p, hits * getConfig().xpPerTargetHit); + getPlayer(p).getData().addStat("swords.crimson-cyclone.mobs-hit", hits); + + // Special achievement: hit 6+ mobs with one activation + if (hits >= 6 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_swords_cyclone_6")) { + getPlayer(p).getAdvancementHandler().grant("challenge_swords_cyclone_6"); } - - private int getCooldownTicks(int level) { - return Math.max(40, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); - } - - @Override - public void onTick() { - + } + + private boolean isCritTrigger(Player p) { + return p.getFallDistance() >= getConfig().minFallDistanceForCrit + && !p.isOnGround() + && !p.isInWater() + && !p.isInsideVehicle() + && !p.isClimbing(); + } + + private boolean applyDurabilityCost(ItemStack hand, int durabilityCost) { + if (!(hand.getItemMeta() instanceof Damageable damageable) || hand.getType().getMaxDurability() <= 0) { + return true; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + int max = hand.getType().getMaxDurability(); + int next = damageable.getDamage() + durabilityCost; + if (next >= max) { + return false; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Land a sword crit while falling to unleash a bleeding crimson cyclone around your target.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Bleed Particles for the Swords Crimson Cyclone adaptation.", impact = "True enables this behavior and false disables it.") - boolean showBleedParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.76; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 2.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 2.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDamage = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bleedTicksBase = 40; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bleedTicksFactor = 90; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bleedDamagePerProcBase = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bleedDamagePerProcFactor = 0.45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerCostBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hungerCostFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durabilityCostBase = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double durabilityCostFactor = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 320; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 160; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Swords Crimson Cyclone adaptation.", impact = "Minimum fall distance required to trigger the cyclone on hit.") - float minFallDistanceForCrit = 0.08f; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerTargetHit = 10; - } + damageable.setDamage(next); + hand.setItemMeta(damageable); + return true; + } + + private double getRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private double getBaseDamage(int level) { + return getConfig().baseDamage + (getLevelPercent(level) * getConfig().damageFactor); + } + + private void applyBleed(LivingEntity target, int level) { + BleedEffect bleed = new DamagingBleedEffect(Adapt.instance.adaptEffectManager, getBleedDamagePerProc(level), target); + bleed.setEntity(target); + bleed.material = getConfig().showBleedParticles ? Material.CRIMSON_ROOTS : Material.VOID_AIR; + bleed.height = -1; + bleed.period = 5; + bleed.hurt = false; + bleed.iterations = Math.max(4, (int) Math.ceil(getBleedTicks(level) / 5D)); + bleed.start(); + } + + private int getBleedTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().bleedTicksBase + (getLevelPercent(level) * getConfig().bleedTicksFactor))); + } + + private double getBleedDamagePerProc(int level) { + return Math.max(0.01, getConfig().bleedDamagePerProcBase + (getLevelPercent(level) * getConfig().bleedDamagePerProcFactor)); + } + + private int getHungerCost(int level) { + return Math.max(1, (int) Math.round(getConfig().hungerCostBase - (getLevelPercent(level) * getConfig().hungerCostFactor))); + } + + private int getDurabilityCost(int level) { + return Math.max(1, (int) Math.round(getConfig().durabilityCostBase - (getLevelPercent(level) * getConfig().durabilityCostFactor))); + } + + private int getCooldownTicks(int level) { + return Math.max(40, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Land a sword crit while falling to unleash a bleeding crimson cyclone around your target.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Bleed Particles for the Swords Crimson Cyclone adaptation.", impact = "True enables this behavior and false disables it.") + boolean showBleedParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.76; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 2.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 2.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDamage = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bleedTicksBase = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bleedTicksFactor = 90; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bleedDamagePerProcBase = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bleed Damage Per Proc Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bleedDamagePerProcFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerCostBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hunger Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hungerCostFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durabilityCostBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double durabilityCostFactor = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 320; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 160; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Fall Distance For Crit for the Swords Crimson Cyclone adaptation.", impact = "Minimum fall distance required to trigger the cyclone on hit.") + float minFallDistanceForCrit = 0.08f; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Target Hit for the Swords Crimson Cyclone adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTargetHit = 10; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java index 21263b887..138ca4c4e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsDualWield.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -36,113 +36,113 @@ import org.bukkit.inventory.ItemStack; public class SwordsDualWield extends SimpleAdaptation { - public SwordsDualWield() { - super("sword-dual-wield"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.dual_wield.description")); - setDisplayName(Localizer.dLocalize("sword.dual_wield.name")); - setIcon(Material.GOLDEN_SWORD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1800); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_dual_1k") - .title(Localizer.dLocalize("advancement.challenge_swords_dual_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_dual_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_swords_dual_25k") - .title(Localizer.dLocalize("advancement.challenge_swords_dual_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_dual_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_swords_dual_1k", "swords.dual-wield.bonus-damage", 1000, 400); - registerMilestone("challenge_swords_dual_25k", "swords.dual-wield.bonus-damage", 25000, 1500); - } + public SwordsDualWield() { + super("sword-dual-wield"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.dual_wield.description")); + setDisplayName(Localizer.dLocalize("sword.dual_wield.name")); + setIcon(Material.GOLDEN_SWORD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1800); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_dual_1k") + .title(Localizer.dLocalize("advancement.challenge_swords_dual_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_dual_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_swords_dual_25k") + .title(Localizer.dLocalize("advancement.challenge_swords_dual_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_dual_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_swords_dual_1k", "swords.dual-wield.bonus-damage", 1000, 400); + registerMilestone("challenge_swords_dual_25k", "swords.dual-wield.bonus-damage", 25000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getSameMultiplier(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.dual_wield.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getMixedMultiplier(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.dual_wield.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getSameMultiplier(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.dual_wield.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getMixedMultiplier(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.dual_wield.lore2")); + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - if (!isItem(main) || !isItem(off) || main.getType() == Material.AIR || off.getType() == Material.AIR) { - return; - } - - boolean sameWeapon = main.getType() == off.getType(); - double multiplier = sameWeapon ? getSameMultiplier(combat.level()) : getMixedMultiplier(combat.level()); - double originalDamage = e.getDamage(); - e.setDamage(originalDamage * multiplier); - double bonusDamage = e.getDamage() - originalDamage; - xp(p, e.getDamage() * getConfig().xpPerDamage); - getPlayer(p).getData().addStat("swords.dual-wield.bonus-damage", bonusDamage); + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e); + if (combat == null) { + return; } - private double getSameMultiplier(int level) { - return getConfig().sameWeaponBase + (getLevelPercent(level) * getConfig().sameWeaponFactor); + Player p = combat.attacker(); + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + if (!isItem(main) || !isItem(off) || main.getType() == Material.AIR || off.getType() == Material.AIR) { + return; } - private double getMixedMultiplier(int level) { - return getConfig().mixedWeaponBase + (getLevelPercent(level) * getConfig().mixedWeaponFactor); - } + boolean sameWeapon = main.getType() == off.getType(); + double multiplier = sameWeapon ? getSameMultiplier(combat.level()) : getMixedMultiplier(combat.level()); + double originalDamage = e.getDamage(); + e.setDamage(originalDamage * multiplier); + double bonusDamage = e.getDamage() - originalDamage; + xp(p, e.getDamage() * getConfig().xpPerDamage); + getPlayer(p).getData().addStat("swords.dual-wield.bonus-damage", bonusDamage); + } - @Override - public void onTick() { + private double getSameMultiplier(int level) { + return getConfig().sameWeaponBase + (getLevelPercent(level) * getConfig().sameWeaponFactor); + } - } + private double getMixedMultiplier(int level) { + return getConfig().mixedWeaponBase + (getLevelPercent(level) * getConfig().mixedWeaponFactor); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + } - @NoArgsConstructor - @ConfigDescription("Wield a sword in both hands for increased damage output.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sameWeaponBase = 1.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sameWeaponFactor = 0.43; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mixedWeaponBase = 1.06; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mixedWeaponFactor = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerDamage = 2.0; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Wield a sword in both hands for increased damage output.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sameWeaponBase = 1.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Same Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sameWeaponFactor = 0.43; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Base for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mixedWeaponBase = 1.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mixed Weapon Factor for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mixedWeaponFactor = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Swords Dual Wield adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDamage = 2.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java index 502015813..ff2464b70 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsExecutionersEdge.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.attribute.Attribute; @@ -39,148 +39,148 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; public class SwordsExecutionersEdge extends SimpleAdaptation { - public SwordsExecutionersEdge() { - super("sword-executioners-edge"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.executioners_edge.description")); - setDisplayName(Localizer.dLocalize("sword.executioners_edge.name")); - setIcon(Material.STONE_SWORD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1900); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_execute_200") - .title(Localizer.dLocalize("advancement.challenge_swords_execute_200.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_execute_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_swords_execute_2500") - .title(Localizer.dLocalize("advancement.challenge_swords_execute_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_execute_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_swords_execute_200", "swords.executioners-edge.executions", 200, 400); - registerMilestone("challenge_swords_execute_2500", "swords.executioners-edge.executions", 2500, 1500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHERITE_AXE) - .key("challenge_swords_execute_5in10") - .title(Localizer.dLocalize("advancement.challenge_swords_execute_5in10.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_execute_5in10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + public SwordsExecutionersEdge() { + super("sword-executioners-edge"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.executioners_edge.description")); + setDisplayName(Localizer.dLocalize("sword.executioners_edge.name")); + setIcon(Material.STONE_SWORD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1900); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_execute_200") + .title(Localizer.dLocalize("advancement.challenge_swords_execute_200.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_execute_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_swords_execute_2500") + .title(Localizer.dLocalize("advancement.challenge_swords_execute_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_execute_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_swords_execute_200", "swords.executioners-edge.executions", 200, 400); + registerMilestone("challenge_swords_execute_2500", "swords.executioners-edge.executions", 2500, 1500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_AXE) + .key("challenge_swords_execute_5in10") + .title(Localizer.dLocalize("advancement.challenge_swords_execute_5in10.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_execute_5in10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getBonusDamage(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.executioners_edge.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getThreshold(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.executioners_edge.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getBonusDamage(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.executioners_edge.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getThreshold(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.executioners_edge.lore2")); - } + Player p = combat.attacker(); + LivingEntity target = combat.target(); - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var combat = resolveMeleeContext(e, this::isSword); - if (combat == null) { - return; - } - - Player p = combat.attacker(); - LivingEntity target = combat.target(); - - double maxHealth = getMaxHealth(target); - if (maxHealth <= 0) { - return; - } - - double hpPercent = Math.max(0, target.getHealth() / maxHealth); - double threshold = getThreshold(combat.level()); - if (hpPercent > threshold) { - return; - } - - double multiplier = 1D + getBonusDamage(combat.level()); - e.setDamage(e.getDamage() * multiplier); - xp(p, e.getDamage() * getConfig().xpPerBuffedDamage); - getPlayer(p).getData().addStat("swords.executioners-edge.executions", 1); - - // Special achievement: execute 5 in 10 seconds - long now = System.currentTimeMillis(); - long windowStart = getStorageLong(p, "executeWindowStart", 0L); - int windowCount = getStorageInt(p, "executeWindowCount", 0); - if (now - windowStart > 10000L) { - windowStart = now; - windowCount = 1; - } else { - windowCount++; - } - setStorage(p, "executeWindowStart", windowStart); - setStorage(p, "executeWindowCount", windowCount); - if (windowCount >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_swords_execute_5in10")) { - getPlayer(p).getAdvancementHandler().grant("challenge_swords_execute_5in10"); - } + double maxHealth = getMaxHealth(target); + if (maxHealth <= 0) { + return; } - private double getMaxHealth(LivingEntity entity) { - AttributeInstance attr = entity.getAttribute(Attribute.MAX_HEALTH); - return attr == null ? entity.getHealth() : attr.getValue(); + double hpPercent = Math.max(0, target.getHealth() / maxHealth); + double threshold = getThreshold(combat.level()); + if (hpPercent > threshold) { + return; } - private double getBonusDamage(int level) { - return getConfig().bonusDamageBase + (getLevelPercent(level) * getConfig().bonusDamageFactor); + double multiplier = 1D + getBonusDamage(combat.level()); + e.setDamage(e.getDamage() * multiplier); + xp(p, e.getDamage() * getConfig().xpPerBuffedDamage); + getPlayer(p).getData().addStat("swords.executioners-edge.executions", 1); + + // Special achievement: execute 5 in 10 seconds + long now = System.currentTimeMillis(); + long windowStart = getStorageLong(p, "executeWindowStart", 0L); + int windowCount = getStorageInt(p, "executeWindowCount", 0); + if (now - windowStart > 10000L) { + windowStart = now; + windowCount = 1; + } else { + windowCount++; } - - private double getThreshold(int level) { - return Math.min(getConfig().maxThreshold, getConfig().thresholdBase + (getLevelPercent(level) * getConfig().thresholdFactor)); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sword strikes deal bonus damage to low-health targets.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusDamageBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusDamageFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double thresholdBase = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double thresholdFactor = 0.33; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Threshold for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxThreshold = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBuffedDamage = 1.9; + setStorage(p, "executeWindowStart", windowStart); + setStorage(p, "executeWindowCount", windowCount); + if (windowCount >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_swords_execute_5in10")) { + getPlayer(p).getAdvancementHandler().grant("challenge_swords_execute_5in10"); } + } + + private double getMaxHealth(LivingEntity entity) { + AttributeInstance attr = entity.getAttribute(Attribute.MAX_HEALTH); + return attr == null ? entity.getHealth() : attr.getValue(); + } + + private double getBonusDamage(int level) { + return getConfig().bonusDamageBase + (getLevelPercent(level) * getConfig().bonusDamageFactor); + } + + private double getThreshold(int level) { + return Math.min(getConfig().maxThreshold, getConfig().thresholdBase + (getLevelPercent(level) * getConfig().thresholdFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sword strikes deal bonus damage to low-health targets.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusDamageBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusDamageFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Base for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double thresholdBase = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Threshold Factor for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double thresholdFactor = 0.33; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Threshold for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxThreshold = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Executioners Edge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBuffedDamage = 1.9; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index 1a1e87613..432332b74 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -24,13 +24,13 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Materials; import art.arcane.volmlib.util.data.Cuboid; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.RNG; import lombok.NoArgsConstructor; @@ -50,198 +50,198 @@ import java.util.concurrent.ThreadLocalRandom; public class SwordsMachete extends SimpleAdaptation { - public SwordsMachete() { - super("sword-machete"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.machete.description")); - setDisplayName(Localizer.dLocalize("sword.machete.name")); - setIcon(Material.IRON_SWORD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(5234); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_machete_2500") - .title(Localizer.dLocalize("advancement.challenge_swords_machete_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_machete_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_swords_machete_25k") - .title(Localizer.dLocalize("advancement.challenge_swords_machete_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_machete_25k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_swords_machete_2500", "swords.machete.foliage-cut", 2500, 300); - registerMilestone("challenge_swords_machete_25k", "swords.machete.foliage-cut", 25000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore2")); - v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore3")); - } - - public double getRadius(int level) { - return (getLevelPercent(level) * getConfig().radiusFactor) + getConfig().radiusBase; - } + public SwordsMachete() { + super("sword-machete"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.machete.description")); + setDisplayName(Localizer.dLocalize("sword.machete.name")); + setIcon(Material.IRON_SWORD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(5234); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_machete_2500") + .title(Localizer.dLocalize("advancement.challenge_swords_machete_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_machete_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_swords_machete_25k") + .title(Localizer.dLocalize("advancement.challenge_swords_machete_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_machete_25k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_swords_machete_2500", "swords.machete.foliage-cut", 2500, 300); + registerMilestone("challenge_swords_machete_25k", "swords.machete.foliage-cut", 25000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getRadius(level) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTime(getLevelPercent(level)) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore2")); + v.addLore(C.RED + "- " + getDamagePerBlock(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("sword.machete.lore3")); + } + + public double getRadius(int level) { + return (getLevelPercent(level) * getConfig().radiusFactor) + getConfig().radiusBase; + } + + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + if (e.getHand() != null && e.getHand().equals(EquipmentSlot.HAND) && e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { + int dmg = 0; + ItemStack is = e.getItem(); + if (isSword(is)) { + if (is != null && !p.hasCooldown(is.getType()) && hasActiveAdaptation(p)) { + Location ctr = p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(2.25)).add(0, -0.5, 0); + + int lvl = getLevel(p); + Cuboid c = new Cuboid(ctr); + c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.North, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.South, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.East, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.West, (int) Math.round(getRadius(lvl))); + + if (dmg > 0) { + return; + } + + for (Block i : c) { + if (M.r((getLevelPercent(lvl) * 2.8) / (i.getLocation().distanceSquared(ctr)))) { + if (i.getType().equals(Material.TALL_GRASS) + || i.getType().equals(Material.CACTUS) + || i.getType().equals(Material.SUGAR_CANE) + || i.getType().equals(Material.CARROT) + || i.getType().equals(Material.POTATO) + || i.getType().equals(Material.NETHER_WART) + || i.getType().equals(Materials.GRASS) + || i.getType().equals(Material.FERN) + || i.getType().equals(Material.LARGE_FERN) + || i.getType().equals(Material.VINE) + || i.getType().equals(Material.ROSE_BUSH) + || i.getType().equals(Material.WITHER_ROSE) + || i.getType().equals(Material.ACACIA_LEAVES) + || i.getType().equals(Material.BIRCH_LEAVES) + || i.getType().equals(Material.DARK_OAK_LEAVES) + || i.getType().equals(Material.JUNGLE_LEAVES) + || i.getType().equals(Material.OAK_LEAVES) + || i.getType().equals(Material.SPRUCE_LEAVES) + || i.getType().equals(Material.BROWN_MUSHROOM) + || i.getType().equals(Material.RED_MUSHROOM) + || i.getType().equals(Material.DEAD_BUSH) + || i.getType().equals(Material.DANDELION) + || i.getType().equals(Material.TALL_SEAGRASS) + || i.getType().equals(Material.SEAGRASS) + || i.getType().equals(Material.WHITE_TULIP) + || i.getType().equals(Material.RED_TULIP) + || i.getType().equals(Material.PINK_TULIP) + || i.getType().equals(Material.ORANGE_TULIP) + || i.getType().equals(Material.LILY_OF_THE_VALLEY) + || i.getType().equals(Material.ALLIUM) + || i.getType().equals(Material.AZURE_BLUET) + || i.getType().equals(Material.SUNFLOWER) + || i.getType().equals(Material.CORNFLOWER) + || i.getType().equals(Material.CHORUS_FLOWER) + || i.getType().equals(Material.BAMBOO) + || i.getType().equals(Material.BAMBOO_SAPLING) + || i.getType().equals(Material.LILAC) + || i.getType().equals(Material.PEONY) + || i.getType().equals(Material.LILY_PAD) + || i.getType().equals(Material.COCOA) + || i.getType().equals(Material.MANGROVE_LEAVES) + + ) { + if (!canBlockBreak(p, i.getLocation())) continue; + BlockBreakEvent ee = new BlockBreakEvent(i, p); + Bukkit.getPluginManager().callEvent(ee); + + if (!ee.isCancelled()) { + dmg += 1; + J.runAt(i.getLocation(), () -> { + i.breakNaturally(); + spw.play(i.getLocation(), Sound.BLOCK_GRASS_BREAK, 0.4f, (float) (ThreadLocalRandom.current().nextDouble() * 1.85D)); + }, RNG.r.i(0, (getMaxLevel() - lvl * 2) + 1)); + } + } + } + } - @EventHandler - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (e.getHand() != null && e.getHand().equals(EquipmentSlot.HAND) && e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { - int dmg = 0; - ItemStack is = e.getItem(); - if (isSword(is)) { - if (is != null && !p.hasCooldown(is.getType()) && hasActiveAdaptation(p)) { - Location ctr = p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(2.25)).add(0, -0.5, 0); - - int lvl = getLevel(p); - Cuboid c = new Cuboid(ctr); - c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.North, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.South, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.East, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.West, (int) Math.round(getRadius(lvl))); - - if (dmg > 0) { - return; - } - - for (Block i : c) { - if (M.r((getLevelPercent(lvl) * 2.8) / (i.getLocation().distanceSquared(ctr)))) { - if (i.getType().equals(Material.TALL_GRASS) - || i.getType().equals(Material.CACTUS) - || i.getType().equals(Material.SUGAR_CANE) - || i.getType().equals(Material.CARROT) - || i.getType().equals(Material.POTATO) - || i.getType().equals(Material.NETHER_WART) - || i.getType().equals(Materials.GRASS) - || i.getType().equals(Material.FERN) - || i.getType().equals(Material.LARGE_FERN) - || i.getType().equals(Material.VINE) - || i.getType().equals(Material.ROSE_BUSH) - || i.getType().equals(Material.WITHER_ROSE) - || i.getType().equals(Material.ACACIA_LEAVES) - || i.getType().equals(Material.BIRCH_LEAVES) - || i.getType().equals(Material.DARK_OAK_LEAVES) - || i.getType().equals(Material.JUNGLE_LEAVES) - || i.getType().equals(Material.OAK_LEAVES) - || i.getType().equals(Material.SPRUCE_LEAVES) - || i.getType().equals(Material.BROWN_MUSHROOM) - || i.getType().equals(Material.RED_MUSHROOM) - || i.getType().equals(Material.DEAD_BUSH) - || i.getType().equals(Material.DANDELION) - || i.getType().equals(Material.TALL_SEAGRASS) - || i.getType().equals(Material.SEAGRASS) - || i.getType().equals(Material.WHITE_TULIP) - || i.getType().equals(Material.RED_TULIP) - || i.getType().equals(Material.PINK_TULIP) - || i.getType().equals(Material.ORANGE_TULIP) - || i.getType().equals(Material.LILY_OF_THE_VALLEY) - || i.getType().equals(Material.ALLIUM) - || i.getType().equals(Material.AZURE_BLUET) - || i.getType().equals(Material.SUNFLOWER) - || i.getType().equals(Material.CORNFLOWER) - || i.getType().equals(Material.CHORUS_FLOWER) - || i.getType().equals(Material.BAMBOO) - || i.getType().equals(Material.BAMBOO_SAPLING) - || i.getType().equals(Material.LILAC) - || i.getType().equals(Material.PEONY) - || i.getType().equals(Material.LILY_PAD) - || i.getType().equals(Material.COCOA) - || i.getType().equals(Material.MANGROVE_LEAVES) - - ) { - if (!canBlockBreak(p, i.getLocation())) continue; - BlockBreakEvent ee = new BlockBreakEvent(i, p); - Bukkit.getPluginManager().callEvent(ee); - - if (!ee.isCancelled()) { - dmg += 1; - J.runAt(i.getLocation(), () -> { - i.breakNaturally(); - spw.play(i.getLocation(), Sound.BLOCK_GRASS_BREAK, 0.4f, (float) (ThreadLocalRandom.current().nextDouble() * 1.85D)); - }, RNG.r.i(0, (getMaxLevel() - lvl * 2) + 1)); - } - } - } - } - - if (dmg > 0) { - p.setCooldown(is.getType(), getCooldownTime(getLevelPercent(lvl))); + if (dmg > 0) { + p.setCooldown(is.getType(), getCooldownTime(getLevelPercent(lvl))); // if (areParticlesEnabled()) { // ParticleEffect.SWEEP_ATTACK.display(p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(1.25)).add(0, -0.5, 0), 0f, 0f, 0f, 0.1f, 1, null); // } - spw.play(p.getEyeLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, (float) (ThreadLocalRandom.current().nextDouble() / 2D) + 0.65f); - damageHand(p, dmg * getDamagePerBlock(getLevelPercent(lvl))); - xp(p, dmg * 11.25, "foliage-cut"); - getPlayer(p).getData().addStat("swords.machete.foliage-cut", dmg); - } - } - } + spw.play(p.getEyeLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, (float) (ThreadLocalRandom.current().nextDouble() / 2D) + 0.65f); + damageHand(p, dmg * getDamagePerBlock(getLevelPercent(lvl))); + xp(p, dmg * 11.25, "foliage-cut"); + getPlayer(p).getData().addStat("swords.machete.foliage-cut", dmg); + } } + } } - - private int getCooldownTime(double levelPercent) { - return (int) (((int) ((1D - levelPercent) * getConfig().cooldownTicksSlowest)) + getConfig().cooldownTicksBase); - } - - private int getDamagePerBlock(double levelPercent) { - return (int) (getConfig().toolDamageBase + (getConfig().toolDamageInverseLevelFactor * ((1D - levelPercent)))); - } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Cut through foliage with ease using a sword.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Machete adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 2.36; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Slowest for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksSlowest = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double toolDamageBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Inverse Level Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double toolDamageInverseLevelFactor = 5; - } + } + + private int getCooldownTime(double levelPercent) { + return (int) (((int) ((1D - levelPercent) * getConfig().cooldownTicksSlowest)) + getConfig().cooldownTicksBase); + } + + private int getDamagePerBlock(double levelPercent) { + return (int) (getConfig().toolDamageBase + (getConfig().toolDamageInverseLevelFactor * ((1D - levelPercent)))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Cut through foliage with ease using a sword.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Swords Machete adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 2.36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Slowest for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksSlowest = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Base for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double toolDamageBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tool Damage Inverse Level Factor for the Swords Machete adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double toolDamageInverseLevelFactor = 5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java index 4d5678671..eec569eb1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsPoisonedBlade.java @@ -27,9 +27,9 @@ import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; @@ -48,139 +48,139 @@ import java.util.UUID; public class SwordsPoisonedBlade extends SimpleAdaptation { - private final Map cooldowns; - private final Set poisonedEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); - private final Map poisonSource = new java.util.concurrent.ConcurrentHashMap<>(); - - public SwordsPoisonedBlade() { - super("sword-poison-blade"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.poisoned_blade.description")); - setDisplayName(Localizer.dLocalize("sword.poisoned_blade.name")); - setIcon(Material.GREEN_DYE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInterval(4984); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPIDER_EYE) - .key("challenge_swords_poison_500") - .title(Localizer.dLocalize("advancement.challenge_swords_poison_500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_poison_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_swords_poison_500", "swords.poisoned-blade.poison-applied", 500, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FERMENTED_SPIDER_EYE) - .key("challenge_swords_poison_kills_50") - .title(Localizer.dLocalize("advancement.challenge_swords_poison_kills_50.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_poison_kills_50.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_swords_poison_kills_50", "swords.poisoned-blade.poison-kills", 50, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getDurationOfEffect(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore2")); - v.addLore(C.RED + "* " + Form.duration(getCooldown(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore3")); - } - - public long getCooldown(int level) { - return getConfig().cooldown * level; - } - - public long getDurationOfEffect(int level) { - return getConfig().effectDuration * level; - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown > System.currentTimeMillis()) - return; - Entity victim = e.getEntity(); - cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); - if (!canDamageTarget(p, victim)) return; - if (victim instanceof Player pvic) { - BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); - blood.setEntity(pvic); - blood.material = Material.LARGE_FERN; - blood.height = -1; - blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); - blood.period = 5; //5 Every second, make a proc - blood.hurt = false; - blood.start(); - addPotionStacks(pvic, PotionEffectType.POISON, 2, 50 * getLevel(p), true); - } else { - BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, 1, l) : new BleedEffect(Adapt.instance.adaptEffectManager); - blood.setEntity(victim); - blood.material = Material.LARGE_FERN; - blood.height = -1; - blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); - blood.period = 5; //5 Every second, make a proc - blood.hurt = false; - blood.start(); - } - poisonedEntities.add(victim.getUniqueId()); - poisonSource.put(victim.getUniqueId(), p.getUniqueId()); - getPlayer(p).getData().addStat("swords.poisoned-blade.poison-applied", 1); - - } - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - UUID victimId = e.getEntity().getUniqueId(); - if (poisonedEntities.remove(victimId)) { - UUID sourceId = poisonSource.remove(victimId); - Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); - if (source != null && source.isOnline()) { - getPlayer(source).getData().addStat("swords.poisoned-blade.poison-kills", 1); - } - } - } - - - @Override - public void onTick() { + private final Map cooldowns; + private final Set poisonedEntities = java.util.concurrent.ConcurrentHashMap.newKeySet(); + private final Map poisonSource = new java.util.concurrent.ConcurrentHashMap<>(); + + public SwordsPoisonedBlade() { + super("sword-poison-blade"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.poisoned_blade.description")); + setDisplayName(Localizer.dLocalize("sword.poisoned_blade.name")); + setIcon(Material.GREEN_DYE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInterval(4984); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new java.util.concurrent.ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPIDER_EYE) + .key("challenge_swords_poison_500") + .title(Localizer.dLocalize("advancement.challenge_swords_poison_500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_poison_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_swords_poison_500", "swords.poisoned-blade.poison-applied", 500, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FERMENTED_SPIDER_EYE) + .key("challenge_swords_poison_kills_50") + .title(Localizer.dLocalize("advancement.challenge_swords_poison_kills_50.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_poison_kills_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_swords_poison_kills_50", "swords.poisoned-blade.poison-kills", 50, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getDurationOfEffect(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore2")); + v.addLore(C.RED + "* " + Form.duration(getCooldown(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.poisoned_blade.lore3")); + } + + public long getCooldown(int level) { + return getConfig().cooldown * level; + } + + public long getDurationOfEffect(int level) { + return getConfig().effectDuration * level; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && hasActiveAdaptation(p) && ItemListings.getToolSwords().contains(p.getInventory().getItemInMainHand().getType())) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown > System.currentTimeMillis()) + return; + Entity victim = e.getEntity(); + cooldowns.put(p.getUniqueId(), System.currentTimeMillis() + getCooldown(getLevel(p))); + if (!canDamageTarget(p, victim)) return; + if (victim instanceof Player pvic) { + BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(pvic); + blood.material = Material.LARGE_FERN; + blood.height = -1; + blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); + blood.period = 5; //5 Every second, make a proc + blood.hurt = false; + blood.start(); + addPotionStacks(pvic, PotionEffectType.POISON, 2, 50 * getLevel(p), true); + } else { + BleedEffect blood = victim instanceof LivingEntity l ? new DamagingBleedEffect(Adapt.instance.adaptEffectManager, 1, l) : new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(victim); + blood.material = Material.LARGE_FERN; + blood.height = -1; + blood.iterations = Math.toIntExact(2 * (3 + (getDurationOfEffect(getLevel(p)) / 1000))); + blood.period = 5; //5 Every second, make a proc + blood.hurt = false; + blood.start(); + } + poisonedEntities.add(victim.getUniqueId()); + poisonSource.put(victim.getUniqueId(), p.getUniqueId()); + getPlayer(p).getData().addStat("swords.poisoned-blade.poison-applied", 1); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sword strikes apply poison.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long cooldown = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long effectDuration = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.325; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + UUID victimId = e.getEntity().getUniqueId(); + if (poisonedEntities.remove(victimId)) { + UUID sourceId = poisonSource.remove(victimId); + Player source = sourceId == null ? null : Bukkit.getPlayer(sourceId); + if (source != null && source.isOnline()) { + getPlayer(source).getData().addStat("swords.poisoned-blade.poison-kills", 1); + } } + } + + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sword strikes apply poison.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long cooldown = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration for the Swords Poisoned Blade adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long effectDuration = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.325; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java index 17ccb779d..068d1fc9a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsRiposteWindow.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -43,166 +43,166 @@ import java.util.UUID; public class SwordsRiposteWindow extends SimpleAdaptation { - private final Map riposteUntil = new java.util.concurrent.ConcurrentHashMap<>(); - - public SwordsRiposteWindow() { - super("sword-riposte-window"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("sword.riposte_window.description")); - setDisplayName(Localizer.dLocalize("sword.riposte_window.name")); - setIcon(Material.GOLDEN_CHESTPLATE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2100); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_swords_riposte_200") - .title(Localizer.dLocalize("advancement.challenge_swords_riposte_200.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_riposte_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_swords_riposte_2500") - .title(Localizer.dLocalize("advancement.challenge_swords_riposte_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_riposte_2500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_swords_riposte_200", "swords.riposte.ripostes-landed", 200, 400); - registerMilestone("challenge_swords_riposte_2500", "swords.riposte.ripostes-landed", 2500, 1500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_swords_riposte_3in5") - .title(Localizer.dLocalize("advancement.challenge_swords_riposte_3in5.title")) - .description(Localizer.dLocalize("advancement.challenge_swords_riposte_3in5.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.duration(getWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.riposte_window.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.riposte_window.lore2")); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - riposteUntil.remove(e.getPlayer().getUniqueId()); + private final Map riposteUntil = new java.util.concurrent.ConcurrentHashMap<>(); + + public SwordsRiposteWindow() { + super("sword-riposte-window"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("sword.riposte_window.description")); + setDisplayName(Localizer.dLocalize("sword.riposte_window.name")); + setIcon(Material.GOLDEN_CHESTPLATE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2100); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_swords_riposte_200") + .title(Localizer.dLocalize("advancement.challenge_swords_riposte_200.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_riposte_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_swords_riposte_2500") + .title(Localizer.dLocalize("advancement.challenge_swords_riposte_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_riposte_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_swords_riposte_200", "swords.riposte.ripostes-landed", 200, 400); + registerMilestone("challenge_swords_riposte_2500", "swords.riposte.ripostes-landed", 2500, 1500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_swords_riposte_3in5") + .title(Localizer.dLocalize("advancement.challenge_swords_riposte_3in5.title")) + .description(Localizer.dLocalize("advancement.challenge_swords_riposte_3in5.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("sword.riposte_window.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("sword.riposte_window.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + riposteUntil.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player defender) { + armRiposte(defender); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - if (e.getEntity() instanceof Player defender) { - armRiposte(defender); - } - - if (!(e.getDamager() instanceof Player attacker)) { - return; - } - - long now = System.currentTimeMillis(); - long until = riposteUntil.getOrDefault(attacker.getUniqueId(), 0L); - if (until < now) { - return; - } - - var combat = resolveMeleeContext(e, this::isSword); - if (combat == null) { - return; - } - - attacker = combat.attacker(); - e.setDamage(e.getDamage() * (1D + getDamageBonus(combat.level()))); - riposteUntil.remove(attacker.getUniqueId()); - if (areParticlesEnabled()) { - attacker.getWorld().spawnParticle(Particle.SWEEP_ATTACK, attacker.getLocation().add(0, 1, 0), 1, 0, 0, 0, 0); - } - SoundPlayer sp = SoundPlayer.of(attacker.getWorld()); - sp.play(attacker.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.7f, 1.6f); - sp.play(attacker.getLocation(), Sound.ENTITY_PLAYER_ATTACK_CRIT, 0.8f, 1.2f); - xp(attacker, e.getDamage() * getConfig().xpPerBuffedDamage); - getPlayer(attacker).getData().addStat("swords.riposte.ripostes-landed", 1); - - // Special achievement: 3 ripostes within 5 seconds - long riposteWindowStart = getStorageLong(attacker, "riposteWindowStart", 0L); - int riposteWindowCount = getStorageInt(attacker, "riposteWindowCount", 0); - if (now - riposteWindowStart > 5000L) { - riposteWindowStart = now; - riposteWindowCount = 1; - } else { - riposteWindowCount++; - } - setStorage(attacker, "riposteWindowStart", riposteWindowStart); - setStorage(attacker, "riposteWindowCount", riposteWindowCount); - if (riposteWindowCount >= 3 && AdaptConfig.get().isAdvancements() && !getPlayer(attacker).getData().isGranted("challenge_swords_riposte_3in5")) { - getPlayer(attacker).getAdvancementHandler().grant("challenge_swords_riposte_3in5"); - } + if (!(e.getDamager() instanceof Player attacker)) { + return; } - private void armRiposte(Player defender) { - boolean hasShield = defender.getInventory().getItemInOffHand().getType() == Material.SHIELD - || defender.getInventory().getItemInMainHand().getType() == Material.SHIELD; - int level = getActiveLevel(defender); - if (level <= 0 || !defender.isBlocking() || !hasShield) { - return; - } - - riposteUntil.put(defender.getUniqueId(), System.currentTimeMillis() + getWindowMillis(level)); - SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.6f, 0.9f); + long now = System.currentTimeMillis(); + long until = riposteUntil.getOrDefault(attacker.getUniqueId(), 0L); + if (until < now) { + return; } - private long getWindowMillis(int level) { - return Math.max(150L, (long) Math.round(getConfig().windowMillisBase + (getLevelPercent(level) * getConfig().windowMillisFactor))); + art.arcane.adapt.api.adaptation.Adaptation.MeleeContext combat = resolveMeleeContext(e, this::isSword); + if (combat == null) { + return; } - private double getDamageBonus(int level) { - return getConfig().damageBonusBase + (getLevelPercent(level) * getConfig().damageBonusFactor); + attacker = combat.attacker(); + e.setDamage(e.getDamage() * (1D + getDamageBonus(combat.level()))); + riposteUntil.remove(attacker.getUniqueId()); + if (areParticlesEnabled()) { + attacker.getWorld().spawnParticle(Particle.SWEEP_ATTACK, attacker.getLocation().add(0, 1, 0), 1, 0, 0, 0, 0); } - - @Override - public void onTick() { - + SoundPlayer sp = SoundPlayer.of(attacker.getWorld()); + sp.play(attacker.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.7f, 1.6f); + sp.play(attacker.getLocation(), Sound.ENTITY_PLAYER_ATTACK_CRIT, 0.8f, 1.2f); + xp(attacker, e.getDamage() * getConfig().xpPerBuffedDamage); + getPlayer(attacker).getData().addStat("swords.riposte.ripostes-landed", 1); + + // Special achievement: 3 ripostes within 5 seconds + long riposteWindowStart = getStorageLong(attacker, "riposteWindowStart", 0L); + int riposteWindowCount = getStorageInt(attacker, "riposteWindowCount", 0); + if (now - riposteWindowStart > 5000L) { + riposteWindowStart = now; + riposteWindowCount = 1; + } else { + riposteWindowCount++; } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + setStorage(attacker, "riposteWindowStart", riposteWindowStart); + setStorage(attacker, "riposteWindowCount", riposteWindowCount); + if (riposteWindowCount >= 3 && AdaptConfig.get().isAdvancements() && !getPlayer(attacker).getData().isGranted("challenge_swords_riposte_3in5")) { + getPlayer(attacker).getAdvancementHandler().grant("challenge_swords_riposte_3in5"); } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + } + + private void armRiposte(Player defender) { + boolean hasShield = defender.getInventory().getItemInOffHand().getType() == Material.SHIELD + || defender.getInventory().getItemInMainHand().getType() == Material.SHIELD; + int level = getActiveLevel(defender); + if (level <= 0 || !defender.isBlocking() || !hasShield) { + return; } - @NoArgsConstructor - @ConfigDescription("Blocking with a shield arms a short riposte window for your next sword strike.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.71; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windowMillisBase = 350; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double windowMillisFactor = 550; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusBase = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBonusFactor = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBuffedDamage = 1.8; - } + riposteUntil.put(defender.getUniqueId(), System.currentTimeMillis() + getWindowMillis(level)); + SoundPlayer.of(defender.getWorld()).play(defender.getLocation(), Sound.ITEM_SHIELD_BLOCK, 0.6f, 0.9f); + } + + private long getWindowMillis(int level) { + return Math.max(150L, (long) Math.round(getConfig().windowMillisBase + (getLevelPercent(level) * getConfig().windowMillisFactor))); + } + + private double getDamageBonus(int level) { + return getConfig().damageBonusBase + (getLevelPercent(level) * getConfig().damageBonusFactor); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Blocking with a shield arms a short riposte window for your next sword strike.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.71; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windowMillisBase = 350; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Millis Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double windowMillisFactor = 550; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Base for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusBase = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Bonus Factor for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBonusFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Buffed Damage for the Swords Riposte Window adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBuffedDamage = 1.8; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java index aa2632f5c..6a6d305e6 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/effects/DamagingBleedEffect.java @@ -24,18 +24,18 @@ import org.bukkit.entity.LivingEntity; public class DamagingBleedEffect extends BleedEffect { - private final double damage; - private final LivingEntity target; + private final double damage; + private final LivingEntity target; - public DamagingBleedEffect(EffectManager effectManager, double damage, LivingEntity target) { - super(effectManager); - this.damage = damage; - this.target = target; - } + public DamagingBleedEffect(EffectManager effectManager, double damage, LivingEntity target) { + super(effectManager); + this.damage = damage; + this.target = target; + } - @Override - public void onRun() { - super.onRun(); - J.runEntity(target, () -> target.damage(damage)); - } + @Override + public void onRun() { + super.onRun(); + J.runEntity(target, () -> target.damage(damage)); + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index 290e300e9..16f432cf3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -24,11 +24,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,179 +44,179 @@ import org.bukkit.inventory.EquipmentSlot; public class TamingBeastRecall extends SimpleAdaptation { - public TamingBeastRecall() { - super("tame-beast-recall"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.beast_recall.description")); - setDisplayName(Localizer.dLocalize("taming.beast_recall.name")); - setIcon(Material.LEAD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2200); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEAD) - .key("challenge_taming_recall_100") - .title(Localizer.dLocalize("advancement.challenge_taming_recall_100.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_recall_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_taming_recall_1k") - .title(Localizer.dLocalize("advancement.challenge_taming_recall_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_recall_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_taming_recall_100", "taming.beast-recall.recalls", 100, 300); - registerMilestone("challenge_taming_recall_1k", "taming.beast-recall.recalls", 1000, 1000); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getSearchRadius(level)) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore1")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore2")); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(PlayerInteractEvent e) { - if (e.getHand() != EquipmentSlot.HAND) { - return; - } - - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { - return; - } - - Player p = e.getPlayer(); - int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.LEAD || p.hasCooldown(Material.LEAD)) { - return; - } - - Tameable tameable = findNearestOwnedTameable(p, getSearchRadius(level)); - if (tameable == null) { - return; - } - - Location safe = findSafeRecallLocation(p); - if (safe == null || !canPVE(p, safe)) { - return; - } - - J.teleport(tameable, safe); - tameable.setFallDistance(0); - p.setCooldown(Material.LEAD, getCooldownTicks(level)); - e.setCancelled(true); - - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.75f, 1.45f); - sp.play(safe, Sound.ITEM_LEAD_BREAK, 0.6f, 1.2f); - xp(p, getConfig().xpOnRecall); - getPlayer(p).getData().addStat("taming.beast-recall.recalls", 1); - } - - private Tameable findNearestOwnedTameable(Player p, double radius) { - double best = Double.MAX_VALUE; - Tameable out = null; - double minDistSq = getConfig().minRecallDistanceSquared; - for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { - if (!(entity instanceof Tameable t) || !t.isTamed() || !(t.getOwner() instanceof Player owner) || !owner.getUniqueId().equals(p.getUniqueId())) { - continue; - } - - double d = t.getLocation().distanceSquared(p.getLocation()); - if (d <= minDistSq || d >= best) { - continue; - } - - out = t; - best = d; - } - - return out; + public TamingBeastRecall() { + super("tame-beast-recall"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.beast_recall.description")); + setDisplayName(Localizer.dLocalize("taming.beast_recall.name")); + setIcon(Material.LEAD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2200); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEAD) + .key("challenge_taming_recall_100") + .title(Localizer.dLocalize("advancement.challenge_taming_recall_100.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_recall_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_taming_recall_1k") + .title(Localizer.dLocalize("advancement.challenge_taming_recall_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_recall_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_taming_recall_100", "taming.beast-recall.recalls", 100, 300); + registerMilestone("challenge_taming_recall_1k", "taming.beast-recall.recalls", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getSearchRadius(level)) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; } - private Location findSafeRecallLocation(Player p) { - Location base = p.getLocation(); - int[][] offsets = { - {0, 0}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}, - {1, 1}, {1, -1}, {-1, 1}, {-1, -1}, - {2, 0}, {-2, 0}, {0, 2}, {0, -2} - }; - - for (int y = 0; y <= 2; y++) { - for (int[] offset : offsets) { - Location candidate = base.clone().add(offset[0], y, offset[1]); - if (isSafeSpot(candidate)) { - return candidate; - } - } - } - - return null; + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; } - private boolean isSafeSpot(Location location) { - Block feet = location.getBlock(); - Block head = location.clone().add(0, 1, 0).getBlock(); - Block below = location.clone().add(0, -1, 0).getBlock(); - return feet.isPassable() && head.isPassable() && below.getType().isSolid(); + Player p = e.getPlayer(); + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0 || p.getInventory().getItemInMainHand().getType() != Material.LEAD || p.hasCooldown(Material.LEAD)) { + return; } - private double getSearchRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + Tameable tameable = findNearestOwnedTameable(p, getSearchRadius(level)); + if (tameable == null) { + return; } - private int getCooldownTicks(int level) { - return Math.max(40, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + Location safe = findSafeRecallLocation(p); + if (safe == null || !canPVE(p, safe)) { + return; } - @Override - public void onTick() { - + J.teleport(tameable, safe); + tameable.setFallDistance(0); + p.setCooldown(Material.LEAD, getCooldownTicks(level)); + e.setCancelled(true); + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.75f, 1.45f); + sp.play(safe, Sound.ITEM_LEAD_BREAK, 0.6f, 1.2f); + xp(p, getConfig().xpOnRecall); + getPlayer(p).getData().addStat("taming.beast-recall.recalls", 1); + } + + private Tameable findNearestOwnedTameable(Player p, double radius) { + double best = Double.MAX_VALUE; + Tameable out = null; + double minDistSq = getConfig().minRecallDistanceSquared; + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { + if (!(entity instanceof Tameable t) || !t.isTamed() || !(t.getOwner() instanceof Player owner) || !owner.getUniqueId().equals(p.getUniqueId())) { + continue; + } + + double d = t.getLocation().distanceSquared(p.getLocation()); + if (d <= minDistSq || d >= best) { + continue; + } + + out = t; + best = d; } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + return out; + } + + private Location findSafeRecallLocation(Player p) { + Location base = p.getLocation(); + int[][] offsets = { + {0, 0}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {1, -1}, {-1, 1}, {-1, -1}, + {2, 0}, {-2, 0}, {0, 2}, {0, -2} + }; + + for (int y = 0; y <= 2; y++) { + for (int[] offset : offsets) { + Location candidate = base.clone().add(offset[0], y, offset[1]); + if (isSafeSpot(candidate)) { + return candidate; + } + } } - @NoArgsConstructor - @ConfigDescription("Sneak-right-click with a lead to recall your nearest tamed companion.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 38; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Recall Distance Squared for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minRecallDistanceSquared = 9.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 420; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 280; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Recall for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnRecall = 26; - } + return null; + } + + private boolean isSafeSpot(Location location) { + Block feet = location.getBlock(); + Block head = location.clone().add(0, 1, 0).getBlock(); + Block below = location.clone().add(0, -1, 0).getBlock(); + return feet.isPassable() && head.isPassable() && below.getType().isSolid(); + } + + private double getSearchRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private int getCooldownTicks(int level) { + return Math.max(40, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click with a lead to recall your nearest tamed companion.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 38; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Recall Distance Squared for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minRecallDistanceSquared = 9.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 420; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 280; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Recall for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnRecall = 26; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java index 9d16e8e17..1d3624b41 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingDamage.java @@ -22,15 +22,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -45,152 +46,233 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; -import java.util.Collection; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class TamingDamage extends SimpleAdaptation { - private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-damage-boost".getBytes()); - private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-damage-boost"); - private static final double FOLIA_SCAN_RADIUS = 48D; - - public TamingDamage() { - super("tame-damage"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.damage.description")); - setDisplayName(Localizer.dLocalize("taming.damage.name")); - setIcon(Material.FLINT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(6119); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_taming_damage_500") - .title(Localizer.dLocalize("advancement.challenge_taming_damage_500.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_damage_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_taming_damage_5k") - .title(Localizer.dLocalize("advancement.challenge_taming_damage_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_damage_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_taming_damage_500", "taming.damage.pet-kills", 500, 400); - registerMilestone("challenge_taming_damage_5k", "taming.damage.pet-kills", 5000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getDamageBoost(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.damage.lore1")); - } - - private double getDamageBoost(int level) { - return ((getLevelPercent(level) * getConfig().damageFactor) + getConfig().baseDamage); - } - - @Override - public void onTick() { - if (J.isFoliaThreading()) { - onFoliaTick(); - return; - } + private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-damage-boost".getBytes()); + private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString("adapt:tame-damage-boost"); + private static final double FOLIA_SCAN_RADIUS = 48D; + private final Map appliedLevels = new ConcurrentHashMap<>(); - Map ownerLevels = new java.util.concurrent.ConcurrentHashMap<>(); - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - ownerLevels.put(owner.getUniqueId(), getActiveLevel(owner)); - } + public TamingDamage() { + super("tame-damage"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.damage.description")); + setDisplayName(Localizer.dLocalize("taming.damage.name")); + setIcon(Material.FLINT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(6119); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_taming_damage_500") + .title(Localizer.dLocalize("advancement.challenge_taming_damage_500.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_damage_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_taming_damage_5k") + .title(Localizer.dLocalize("advancement.challenge_taming_damage_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_damage_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_taming_damage_500", "taming.damage.pet-kills", 500, 400); + registerMilestone("challenge_taming_damage_5k", "taming.damage.pet-kills", 5000, 1500); + } - for (World world : Bukkit.getServer().getWorlds()) { - Collection tameables = world.getEntitiesByClass(Tameable.class); - for (Tameable tameable : tameables) { - if (tameable.isTamed() && tameable.getOwner() instanceof Player p) { - update(tameable, ownerLevels.getOrDefault(p.getUniqueId(), 0)); - } - } - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getDamageBoost(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.damage.lore1")); + } + + private double getDamageBoost(int level) { + return ((getLevelPercent(level) * getConfig().damageFactor) + getConfig().baseDamage); + } + + @Override + public void onTick() { + if (J.isFoliaThreading()) { + onFoliaTick(); + pruneInvalidAppliedLevels(); + return; } - private void onFoliaTick() { - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - int level = getActiveLevel(owner); - J.runEntity(owner, () -> updateNearbyOwnedTameables(owner, level)); - } + Map ownerLevels = new HashMap<>(); + boolean hasActiveOwners = false; + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getActiveLevel(owner); + if (level > 0) { + ownerLevels.put(owner.getUniqueId(), level); + hasActiveOwners = true; + } + } + + if (!hasActiveOwners) { + clearAppliedLevels(); + return; } - private void updateNearbyOwnedTameables(Player owner, int level) { - if (owner == null || !owner.isOnline()) { - return; + Set seen = new HashSet<>(); + for (World world : Bukkit.getServer().getWorlds()) { + Collection tameables = world.getEntitiesByClass(Tameable.class); + for (Tameable tameable : tameables) { + if (tameable.isTamed() && tameable.getOwner() instanceof Player p) { + seen.add(tameable.getUniqueId()); + update(tameable, ownerLevels.getOrDefault(p.getUniqueId(), 0)); } + } + } + clearMissingAppliedLevels(seen); + } - for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { - if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { - continue; - } - if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { - continue; - } + private void onFoliaTick() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getActiveLevel(owner); + J.runEntity(owner, () -> updateNearbyOwnedTameables(owner, level)); + } + } - update(tameable, level); - } + private void updateNearbyOwnedTameables(Player owner, int level) { + if (owner == null || !owner.isOnline()) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent - && dmgEvent.getDamager() instanceof Tameable tam - && tam.isTamed() - && tam.getOwner() instanceof Player p - && hasActiveAdaptation(p)) { - getPlayer(p).getData().addStat("taming.damage.pet-kills", 1); - } + for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + + update(tameable, level); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent + && dmgEvent.getDamager() instanceof Tameable tam + && tam.isTamed() + && tam.getOwner() instanceof Player p + && hasActiveAdaptation(p)) { + getPlayer(p).getData().addStat("taming.damage.pet-kills", 1); + } + appliedLevels.remove(e.getEntity().getUniqueId()); + } + + private void update(Tameable j, int level) { + UUID tameableId = j.getUniqueId(); + IAttribute attribute = Version.get().getAttribute(j, Attributes.GENERIC_ATTACK_DAMAGE); + if (attribute == null) { + appliedLevels.remove(tameableId); + return; } - private void update(Tameable j, int level) { - var attribute = Version.get().getAttribute(j, Attributes.GENERIC_ATTACK_DAMAGE); - if (attribute == null) return; + Integer appliedLevel = appliedLevels.get(tameableId); + if (level <= 0) { + if (appliedLevel != null || attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } + appliedLevels.remove(tameableId); + return; + } + + if (appliedLevel != null && appliedLevel == level) { + return; + } + + attribute.setModifier(MODIFIER, MODIFIER_KEY, getDamageBoost(level), AttributeModifier.Operation.ADD_SCALAR); + appliedLevels.put(tameableId, level); + } + + private void clearAppliedLevels() { + if (appliedLevels.isEmpty()) { + return; + } + + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + Entity entity = Bukkit.getEntity(tameableId); + if (entity instanceof Tameable tameable) { + IAttribute attribute = Version.get().getAttribute(tameable, Attributes.GENERIC_ATTACK_DAMAGE); + if (attribute != null && attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } + } + appliedLevels.remove(tameableId); + } + } + + private void clearMissingAppliedLevels(Set seen) { + if (appliedLevels.isEmpty()) { + return; + } - if (level > 0) { - attribute.addModifier(MODIFIER, MODIFIER_KEY, getDamageBoost(level), AttributeModifier.Operation.ADD_SCALAR); + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + if (seen.contains(tameableId)) { + continue; + } + + Entity entity = Bukkit.getEntity(tameableId); + if (entity instanceof Tameable tameable) { + IAttribute attribute = Version.get().getAttribute(tameable, Attributes.GENERIC_ATTACK_DAMAGE); + if (attribute != null && attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); } + } + appliedLevels.remove(tameableId); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Increase your tamed animal damage dealt.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDamage = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 0.65; + private void pruneInvalidAppliedLevels() { + if (appliedLevels.isEmpty()) { + return; } + + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + Entity entity = Bukkit.getEntity(tameableId); + if (!(entity instanceof Tameable tameable) || !tameable.isValid() || tameable.isDead()) { + appliedLevels.remove(tameableId); + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Increase your tamed animal damage dealt.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDamage = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Taming Damage adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 0.65; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java index 44404b38f..3e54e5f58 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthBoost.java @@ -22,15 +22,16 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -41,144 +42,224 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Tameable; -import java.util.Collection; -import java.util.Map; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class TamingHealthBoost extends SimpleAdaptation { - private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-health-boost".getBytes()); - private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString( "adapt:tame-health-boost"); - private static final double FOLIA_SCAN_RADIUS = 48D; - - public TamingHealthBoost() { - super("tame-health"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.health.description")); - setDisplayName(Localizer.dLocalize("taming.health.name")); - setIcon(Material.COOKED_BEEF); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(4753); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_taming_health_boost_1728k") - .title(Localizer.dLocalize("advancement.challenge_taming_health_boost_1728k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_health_boost_1728k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_taming_health_boost_1728k", "taming.health-boost.ticks-active", 1728000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getHealthBoost(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.health.lore1")); - } - - private double getHealthBoost(int level) { - return ((getLevelPercent(level) * getConfig().healthBoostFactor) + getConfig().healthBoostBase); - } - - @Override - public void onTick() { - if (J.isFoliaThreading()) { - onFoliaTick(); - return; - } + private static final UUID MODIFIER = UUID.nameUUIDFromBytes("adapt-tame-health-boost".getBytes()); + private static final NamespacedKey MODIFIER_KEY = NamespacedKey.fromString("adapt:tame-health-boost"); + private static final double FOLIA_SCAN_RADIUS = 48D; + private final Map appliedLevels = new ConcurrentHashMap<>(); - Map ownerStates = new java.util.concurrent.ConcurrentHashMap<>(); - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - ownerStates.put(owner.getUniqueId(), new OwnerState(adaptPlayer, owner, getLevel(owner))); - } + public TamingHealthBoost() { + super("tame-health"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.health.description")); + setDisplayName(Localizer.dLocalize("taming.health.name")); + setIcon(Material.COOKED_BEEF); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(4753); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_taming_health_boost_1728k") + .title(Localizer.dLocalize("advancement.challenge_taming_health_boost_1728k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_health_boost_1728k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_taming_health_boost_1728k", "taming.health-boost.ticks-active", 1728000, 400); + } - for (World world : Bukkit.getServer().getWorlds()) { - Collection tameables = world.getEntitiesByClass(Tameable.class); - for (Tameable tameable : tameables) { - if (tameable.isTamed() && tameable.getOwner() instanceof Player p) { - OwnerState state = ownerStates.get(p.getUniqueId()); - int level = state == null ? 0 : state.level(); - update(tameable, level); - if (level > 0 && state != null) { - state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); - } - } - } - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getHealthBoost(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.health.lore1")); + } + + private double getHealthBoost(int level) { + return ((getLevelPercent(level) * getConfig().healthBoostFactor) + getConfig().healthBoostBase); + } + + @Override + public void onTick() { + if (J.isFoliaThreading()) { + onFoliaTick(); + pruneInvalidAppliedLevels(); + return; } - private void onFoliaTick() { - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - OwnerState state = new OwnerState(adaptPlayer, owner, getLevel(owner)); - J.runEntity(owner, () -> updateNearbyOwnedTameables(state)); - } + Map ownerStates = new HashMap<>(); + boolean hasActiveOwners = false; + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getLevel(owner); + if (level > 0) { + ownerStates.put(owner.getUniqueId(), new OwnerState(adaptPlayer, owner, level)); + hasActiveOwners = true; + } } - private void updateNearbyOwnedTameables(OwnerState state) { - Player owner = state.owner(); - if (owner == null || !owner.isOnline()) { - return; - } + if (!hasActiveOwners) { + clearAppliedLevels(); + return; + } - for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { - if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { - continue; - } - if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { - continue; - } - - update(tameable, state.level()); - if (state.level() > 0) { - state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); - } + Set seen = new HashSet<>(); + for (World world : Bukkit.getServer().getWorlds()) { + Collection tameables = world.getEntitiesByClass(Tameable.class); + for (Tameable tameable : tameables) { + if (tameable.isTamed() && tameable.getOwner() instanceof Player p) { + seen.add(tameable.getUniqueId()); + OwnerState state = ownerStates.get(p.getUniqueId()); + int level = state == null ? 0 : state.level(); + update(tameable, level); + if (level > 0 && state != null) { + state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); + } } + } + } + clearMissingAppliedLevels(seen); + } + + private void onFoliaTick() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + OwnerState state = new OwnerState(adaptPlayer, owner, getLevel(owner)); + J.runEntity(owner, () -> updateNearbyOwnedTameables(state)); } + } - private record OwnerState(AdaptPlayer ownerData, Player owner, int level) { + private void updateNearbyOwnedTameables(OwnerState state) { + Player owner = state.owner(); + if (owner == null || !owner.isOnline()) { + return; } - private void update(Tameable j, int level) { - var attribute = Version.get().getAttribute(j, Attributes.GENERIC_MAX_HEALTH); - if (attribute == null) return; + for (Entity nearby : owner.getNearbyEntities(FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS, FOLIA_SCAN_RADIUS)) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player tameOwner) || !tameOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + + update(tameable, state.level()); + if (state.level() > 0) { + state.ownerData().getData().addStat("taming.health-boost.ticks-active", 1); + } + } + } + + private void update(Tameable j, int level) { + UUID tameableId = j.getUniqueId(); + IAttribute attribute = Version.get().getAttribute(j, Attributes.GENERIC_MAX_HEALTH); + if (attribute == null) { + appliedLevels.remove(tameableId); + return; + } + + Integer appliedLevel = appliedLevels.get(tameableId); + if (level <= 0) { + if (appliedLevel != null || attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } + appliedLevels.remove(tameableId); + return; + } - if (level > 0) { - attribute.addModifier(MODIFIER, MODIFIER_KEY, getHealthBoost(level), AttributeModifier.Operation.ADD_SCALAR); + if (appliedLevel != null && appliedLevel == level) { + return; + } + + attribute.setModifier(MODIFIER, MODIFIER_KEY, getHealthBoost(level), AttributeModifier.Operation.ADD_SCALAR); + appliedLevels.put(tameableId, level); + } + + private void clearAppliedLevels() { + if (appliedLevels.isEmpty()) { + return; + } + + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + Entity entity = Bukkit.getEntity(tameableId); + if (entity instanceof Tameable tameable) { + IAttribute attribute = Version.get().getAttribute(tameable, Attributes.GENERIC_MAX_HEALTH); + if (attribute != null && attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); } + } + appliedLevels.remove(tameableId); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Increase your tamed animal maximum health.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Factor for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double healthBoostFactor = 2.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Base for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double healthBoostBase = 0.57; + private void clearMissingAppliedLevels(Set seen) { + if (appliedLevels.isEmpty()) { + return; + } + + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + if (seen.contains(tameableId)) { + continue; + } + + Entity entity = Bukkit.getEntity(tameableId); + if (entity instanceof Tameable tameable) { + IAttribute attribute = Version.get().getAttribute(tameable, Attributes.GENERIC_MAX_HEALTH); + if (attribute != null && attribute.hasModifier(MODIFIER, MODIFIER_KEY)) { + attribute.removeModifier(MODIFIER, MODIFIER_KEY); + } + } + appliedLevels.remove(tameableId); } + } + + private void pruneInvalidAppliedLevels() { + if (appliedLevels.isEmpty()) { + return; + } + + for (UUID tameableId : new HashSet<>(appliedLevels.keySet())) { + Entity entity = Bukkit.getEntity(tameableId); + if (!(entity instanceof Tameable tameable) || !tameable.isValid() || tameable.isDead()) { + appliedLevels.remove(tameableId); + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record OwnerState(AdaptPlayer ownerData, Player owner, int level) { + } + + @NoArgsConstructor + @ConfigDescription("Increase your tamed animal maximum health.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Factor for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double healthBoostFactor = 2.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Health Boost Base for the Taming Health Boost adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double healthBoostBase = 0.57; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java index c718d1c66..80b48ed1e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingHealthRegeneration.java @@ -26,10 +26,10 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -47,112 +47,112 @@ import static org.bukkit.Particle.HEART; public class TamingHealthRegeneration extends SimpleAdaptation { - private final Map lastDamage = new java.util.concurrent.ConcurrentHashMap<>(); - - public TamingHealthRegeneration() { - super("tame-health-regeneration"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.regeneration.description")); - setDisplayName(Localizer.dLocalize("taming.regeneration.name")); - setIcon(Material.GOLDEN_APPLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setInterval(1033); - setCostFactor(getConfig().costFactor); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLISTERING_MELON_SLICE) - .key("challenge_taming_regen_1k") - .title(Localizer.dLocalize("advancement.challenge_taming_regen_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_regen_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_taming_regen_1k", "taming.health-regen.health-regened", 1000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRegenSpeed(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.regeneration.lore1")); - } - - @EventHandler - public void on(EntityDamageByEntityEvent e) { - if (e.getEntity() instanceof Tameable tam - && tam.getOwner() instanceof Player p) { - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - if (lastDamage.containsKey(tam.getUniqueId())) { - Adapt.verbose("Tamed Entity " + tam.getUniqueId() + " last damaged " + (M.ms() - lastDamage.get(tam.getUniqueId())) + "ms ago"); - return; - } - var attribute = Version.get().getAttribute(tam, Attributes.GENERIC_MAX_HEALTH); - double mh = attribute == null ? tam.getHealth() : attribute.getValue(); - if (tam.isTamed() && tam.getOwner() instanceof Player && tam.getHealth() < mh) { - Adapt.verbose("Successfully healed tamed entity " + tam.getUniqueId()); - Adapt.verbose("[PRE] Current Health: " + tam.getHealth() + " Max Health: " + mh); - tam.addPotionEffect(PotionEffectType.REGENERATION.createEffect(25 * level, 3)); - getPlayer(p).getData().addStat("taming.health-regen.health-regened", 1); - - if (areParticlesEnabled()) { - Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " with particles"); - tam.getWorld().spawnParticle(HEART, tam.getLocation().add(0, 1, 0), 2 * p.getLevel()); - } else { - Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " without particles"); - } - } - lastDamage.put(e.getEntity().getUniqueId(), M.ms()); + private final Map lastDamage = new java.util.concurrent.ConcurrentHashMap<>(); + + public TamingHealthRegeneration() { + super("tame-health-regeneration"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.regeneration.description")); + setDisplayName(Localizer.dLocalize("taming.regeneration.name")); + setIcon(Material.GOLDEN_APPLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setInterval(1033); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLISTERING_MELON_SLICE) + .key("challenge_taming_regen_1k") + .title(Localizer.dLocalize("advancement.challenge_taming_regen_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_regen_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_taming_regen_1k", "taming.health-regen.health-regened", 1000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRegenSpeed(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.regeneration.lore1")); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Tameable tam + && tam.getOwner() instanceof Player p) { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (lastDamage.containsKey(tam.getUniqueId())) { + Adapt.verbose("Tamed Entity " + tam.getUniqueId() + " last damaged " + (M.ms() - lastDamage.get(tam.getUniqueId())) + "ms ago"); + return; + } + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(tam, Attributes.GENERIC_MAX_HEALTH); + double mh = attribute == null ? tam.getHealth() : attribute.getValue(); + if (tam.isTamed() && tam.getOwner() instanceof Player && tam.getHealth() < mh) { + Adapt.verbose("Successfully healed tamed entity " + tam.getUniqueId()); + Adapt.verbose("[PRE] Current Health: " + tam.getHealth() + " Max Health: " + mh); + tam.addPotionEffect(PotionEffectType.REGENERATION.createEffect(25 * level, 3)); + getPlayer(p).getData().addStat("taming.health-regen.health-regened", 1); + + if (areParticlesEnabled()) { + Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " with particles"); + tam.getWorld().spawnParticle(HEART, tam.getLocation().add(0, 1, 0), 2 * p.getLevel()); + } else { + Adapt.verbose("Healing tamed entity " + tam.getUniqueId() + " without particles"); } + } + lastDamage.put(e.getEntity().getUniqueId(), M.ms()); } - - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDeathEvent e) { - lastDamage.remove(e.getEntity().getUniqueId()); - } - - - private double getRegenSpeed(int level) { - return ((getLevelPercent(level) * (getLevelPercent(level)) * getConfig().regenFactor) + getConfig().regenBase); - } - - @Override - public void onTick() { - lastDamage.entrySet().removeIf(i -> M.ms() - i.getValue() > 8000); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Increase your tamed animal regeneration rate.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Taming Health Regeneration adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Factor for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenFactor = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Base for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double regenBase = 1; - } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDeathEvent e) { + lastDamage.remove(e.getEntity().getUniqueId()); + } + + + private double getRegenSpeed(int level) { + return ((getLevelPercent(level) * (getLevelPercent(level)) * getConfig().regenFactor) + getConfig().regenBase); + } + + @Override + public void onTick() { + lastDamage.entrySet().removeIf(i -> M.ms() - i.getValue() > 8000); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Increase your tamed animal regeneration rate.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Taming Health Regeneration adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Factor for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenFactor = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Regen Base for the Taming Health Regeneration adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double regenBase = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java index 70d262ea1..ec32ae232 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingMountedTactics.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -44,258 +44,258 @@ import java.util.UUID; public class TamingMountedTactics extends SimpleAdaptation { - private final Map lastMountedLocation = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastMountedLocation = new java.util.concurrent.ConcurrentHashMap<>(); - public TamingMountedTactics() { - super("tame-mounted-tactics"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.mounted_tactics.description")); - setDisplayName(Localizer.dLocalize("taming.mounted_tactics.name")); - setIcon(Material.SADDLE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(10); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SADDLE) - .key("challenge_taming_mounted_200") - .title(Localizer.dLocalize("advancement.challenge_taming_mounted_200.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_mounted_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_HORSE_ARMOR) - .key("challenge_taming_mounted_50k") - .title(Localizer.dLocalize("advancement.challenge_taming_mounted_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_mounted_50k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_taming_mounted_200", "taming.mounted-tactics.mounted-kills", 200, 400); - registerMilestone("challenge_taming_mounted_50k", "taming.mounted-tactics.distance", 50000, 1000); - } + public TamingMountedTactics() { + super("tame-mounted-tactics"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.mounted_tactics.description")); + setDisplayName(Localizer.dLocalize("taming.mounted_tactics.name")); + setIcon(Material.SADDLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(10); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SADDLE) + .key("challenge_taming_mounted_200") + .title(Localizer.dLocalize("advancement.challenge_taming_mounted_200.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_mounted_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_HORSE_ARMOR) + .key("challenge_taming_mounted_50k") + .title(Localizer.dLocalize("advancement.challenge_taming_mounted_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_mounted_50k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_taming_mounted_200", "taming.mounted-tactics.mounted-kills", 200, 400); + registerMilestone("challenge_taming_mounted_50k", "taming.mounted-tactics.distance", 50000, 1000); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getMountedDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.mounted_tactics.lore1")); - v.addLore(C.GREEN + "+ " + Form.pc(getMountedDamageReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.mounted_tactics.lore2")); - } + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getMountedDamageBonus(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.mounted_tactics.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getMountedDamageReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.mounted_tactics.lore2")); + } - @Override - public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - int level = getActiveLevel(p); - if (level <= 0) { - continue; - } + @Override + public void onTick() { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } - Entity vehicle = p.getVehicle(); - if (vehicle != null) { - Location last = lastMountedLocation.get(p.getUniqueId()); - Location current = p.getLocation(); - if (last != null && last.getWorld() == current.getWorld()) { - double dist = last.distance(current); - if (dist > 0.1 && dist < 100) { - getPlayer(p).getData().addStat("taming.mounted-tactics.distance", dist); - } - } - lastMountedLocation.put(p.getUniqueId(), current); - } else { - lastMountedLocation.remove(p.getUniqueId()); - } - if (vehicle instanceof AbstractHorse horse) { - if (hasForwardInput(p)) { - applyMountForwardSpeed(horse, p, getHorseTargetSpeed(level)); - } - if (p.isSprinting()) { - Vector push = p.getLocation().getDirection().clone().setY(0).normalize().multiply(getHorsePush(level)); - horse.setVelocity(horse.getVelocity().multiply(0.8).add(push)); - } - } else if (vehicle instanceof Strider strider) { - p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 40, 0, false, false, true), true); - if (hasForwardInput(p)) { - applyMountForwardSpeed(strider, p, getStriderTargetSpeed(level)); - } - if (strider.getLocation().getBlock().getType() == Material.LAVA || strider.getLocation().clone().subtract(0, 1, 0).getBlock().getType() == Material.LAVA) { - strider.setShivering(false); - } - } else if (vehicle instanceof Pig pig) { - p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, getPigResistanceAmplifier(level), false, false, true), true); - if (p.isSprinting()) { - Vector forward = p.getLocation().getDirection().clone().setY(0).normalize().multiply(getPigPush(level)); - pig.setVelocity(pig.getVelocity().multiply(0.7).add(forward)); - } - } + Entity vehicle = p.getVehicle(); + if (vehicle != null) { + Location last = lastMountedLocation.get(p.getUniqueId()); + Location current = p.getLocation(); + if (last != null && last.getWorld() == current.getWorld()) { + double dist = last.distance(current); + if (dist > 0.1 && dist < 100) { + getPlayer(p).getData().addStat("taming.mounted-tactics.distance", dist); + } } - } - - private void applyMountForwardSpeed(Entity mount, Player rider, double targetSpeed) { - Vector direction = rider.getLocation().getDirection().setY(0); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - return; + lastMountedLocation.put(p.getUniqueId(), current); + } else { + lastMountedLocation.remove(p.getUniqueId()); + } + if (vehicle instanceof AbstractHorse horse) { + if (hasForwardInput(p)) { + applyMountForwardSpeed(horse, p, getHorseTargetSpeed(level)); } - - direction.normalize(); - Vector velocity = mount.getVelocity(); - Vector horizontal = VelocitySpeed.horizontalOnly(velocity); - Vector targetHorizontal = direction.multiply(Math.max(0, targetSpeed)); - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().mountAccelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().mountMaxHorizontalSpeed); - mount.setVelocity(new Vector(nextHorizontal.getX(), velocity.getY(), nextHorizontal.getZ())); + if (p.isSprinting()) { + Vector push = p.getLocation().getDirection().clone().setY(0).normalize().multiply(getHorsePush(level)); + horse.setVelocity(horse.getVelocity().multiply(0.8).add(push)); + } + } else if (vehicle instanceof Strider strider) { + p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 40, 0, false, false, true), true); + if (hasForwardInput(p)) { + applyMountForwardSpeed(strider, p, getStriderTargetSpeed(level)); + } + if (strider.getLocation().getBlock().getType() == Material.LAVA || strider.getLocation().clone().subtract(0, 1, 0).getBlock().getType() == Material.LAVA) { + strider.setShivering(false); + } + } else if (vehicle instanceof Pig pig) { + p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, getPigResistanceAmplifier(level), false, false, true), true); + if (p.isSprinting()) { + Vector forward = p.getLocation().getDirection().clone().setY(0).normalize().multiply(getPigPush(level)); + pig.setVelocity(pig.getVelocity().multiply(0.7).add(forward)); + } + } } + } - private boolean hasForwardInput(Player p) { - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - return input.forward() && !input.backward(); + private void applyMountForwardSpeed(Entity mount, Player rider, double targetSpeed) { + Vector direction = rider.getLocation().getDirection().setY(0); + if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() instanceof Player p - && p.getVehicle() != null - && hasActiveAdaptation(p)) { - getPlayer(p).getData().addStat("taming.mounted-tactics.mounted-kills", 1); - } - } + direction.normalize(); + Vector velocity = mount.getVelocity(); + Vector horizontal = VelocitySpeed.horizontalOnly(velocity); + Vector targetHorizontal = direction.multiply(Math.max(0, targetSpeed)); + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().mountAccelPerTick)); + nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().mountMaxHorizontalSpeed); + mount.setVelocity(new Vector(nextHorizontal.getX(), velocity.getY(), nextHorizontal.getZ())); + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player attacker && attacker.getVehicle() != null) { - int level = getActiveLevel(attacker); - if (level > 0) { - if (!canDamageTarget(attacker, e.getEntity())) { - return; - } + private boolean hasForwardInput(Player p) { + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + return input.forward() && !input.backward(); + } - e.setDamage(e.getDamage() * (1D + getMountedDamageBonus(level))); - xp(attacker, e.getDamage() * getConfig().xpPerMountedDamage); - } - } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() instanceof Player p + && p.getVehicle() != null + && hasActiveAdaptation(p)) { + getPlayer(p).getData().addStat("taming.mounted-tactics.mounted-kills", 1); + } + } - if (e.getEntity() instanceof Player defender && defender.getVehicle() != null) { - int level = getActiveLevel(defender); - if (level > 0) { - e.setDamage(e.getDamage() * (1D - getMountedDamageReduction(level))); - } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player attacker && attacker.getVehicle() != null) { + int level = getActiveLevel(attacker); + if (level > 0) { + if (!canDamageTarget(attacker, e.getEntity())) { + return; } - } - private double getMountedDamageBonus(int level) { - return Math.min(getConfig().maxMountedDamageBonus, getConfig().mountedDamageBonusBase + (getLevelPercent(level) * getConfig().mountedDamageBonusFactor)); + e.setDamage(e.getDamage() * (1D + getMountedDamageBonus(level))); + xp(attacker, e.getDamage() * getConfig().xpPerMountedDamage); + } } - private double getMountedDamageReduction(int level) { - return Math.min(getConfig().maxMountedDamageReduction, getConfig().mountedDamageReductionBase + (getLevelPercent(level) * getConfig().mountedDamageReductionFactor)); + if (e.getEntity() instanceof Player defender && defender.getVehicle() != null) { + int level = getActiveLevel(defender); + if (level > 0) { + e.setDamage(e.getDamage() * (1D - getMountedDamageReduction(level))); + } } + } - private int getHorseSpeedAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().horseSpeedAmplifierBase + (getLevelPercent(level) * getConfig().horseSpeedAmplifierFactor))); - } + private double getMountedDamageBonus(int level) { + return Math.min(getConfig().maxMountedDamageBonus, getConfig().mountedDamageBonusBase + (getLevelPercent(level) * getConfig().mountedDamageBonusFactor)); + } - private int getStriderSpeedAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().striderSpeedAmplifierBase + (getLevelPercent(level) * getConfig().striderSpeedAmplifierFactor))); - } + private double getMountedDamageReduction(int level) { + return Math.min(getConfig().maxMountedDamageReduction, getConfig().mountedDamageReductionBase + (getLevelPercent(level) * getConfig().mountedDamageReductionFactor)); + } - private int getPigResistanceAmplifier(int level) { - return Math.max(0, (int) Math.round(getConfig().pigResistanceAmplifierBase + (getLevelPercent(level) * getConfig().pigResistanceAmplifierFactor))); - } + private int getHorseSpeedAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().horseSpeedAmplifierBase + (getLevelPercent(level) * getConfig().horseSpeedAmplifierFactor))); + } - private double getHorseTargetSpeed(int level) { - int amplifier = getHorseSpeedAmplifier(level); - double base = Math.max(0, getConfig().horseBaseHorizontalSpeed); - return Math.min(getConfig().mountMaxHorizontalSpeed, base * VelocitySpeed.speedAmplifierScalar(amplifier)); - } + private int getStriderSpeedAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().striderSpeedAmplifierBase + (getLevelPercent(level) * getConfig().striderSpeedAmplifierFactor))); + } - private double getStriderTargetSpeed(int level) { - int amplifier = getStriderSpeedAmplifier(level); - double base = Math.max(0, getConfig().striderBaseHorizontalSpeed); - return Math.min(getConfig().mountMaxHorizontalSpeed, base * VelocitySpeed.speedAmplifierScalar(amplifier)); - } + private int getPigResistanceAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().pigResistanceAmplifierBase + (getLevelPercent(level) * getConfig().pigResistanceAmplifierFactor))); + } - private double getHorsePush(int level) { - return getConfig().horsePushBase + (getLevelPercent(level) * getConfig().horsePushFactor); - } + private double getHorseTargetSpeed(int level) { + int amplifier = getHorseSpeedAmplifier(level); + double base = Math.max(0, getConfig().horseBaseHorizontalSpeed); + return Math.min(getConfig().mountMaxHorizontalSpeed, base * VelocitySpeed.speedAmplifierScalar(amplifier)); + } - private double getPigPush(int level) { - return getConfig().pigPushBase + (getLevelPercent(level) * getConfig().pigPushFactor); - } + private double getStriderTargetSpeed(int level) { + int amplifier = getStriderSpeedAmplifier(level); + double base = Math.max(0, getConfig().striderBaseHorizontalSpeed); + return Math.min(getConfig().mountMaxHorizontalSpeed, base * VelocitySpeed.speedAmplifierScalar(amplifier)); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private double getHorsePush(int level) { + return getConfig().horsePushBase + (getLevelPercent(level) * getConfig().horsePushFactor); + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + private double getPigPush(int level) { + return getConfig().pigPushBase + (getLevelPercent(level) * getConfig().pigPushFactor); + } - @NoArgsConstructor - @ConfigDescription("Gain mount-specific combat and control bonuses while riding.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mountedDamageBonusBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mountedDamageBonusFactor = 0.22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Bonus for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxMountedDamageBonus = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mountedDamageReductionBase = 0.06; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double mountedDamageReductionFactor = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Reduction for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxMountedDamageReduction = 0.28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double horseSpeedAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double horseSpeedAmplifierFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double striderSpeedAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double striderSpeedAmplifierFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pigResistanceAmplifierBase = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pigResistanceAmplifierFactor = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for horse mounted speed scaling.", impact = "Higher values increase steady mounted horse acceleration when moving forward.") - double horseBaseHorizontalSpeed = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for strider mounted speed scaling.", impact = "Higher values increase steady mounted strider acceleration when moving forward.") - double striderBaseHorizontalSpeed = 0.24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force on mounts.", impact = "Acts as a hard cap to prevent runaway mounted momentum.") - double mountMaxHorizontalSpeed = 0.78; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast mounts accelerate toward the target speed per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double mountAccelPerTick = 0.065; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double horsePushBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double horsePushFactor = 0.16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pigPushBase = 0.05; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double pigPushFactor = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mounted Damage for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerMountedDamage = 1.5; + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain mount-specific combat and control bonuses while riding.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mountedDamageBonusBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Bonus Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mountedDamageBonusFactor = 0.22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Bonus for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxMountedDamageBonus = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mountedDamageReductionBase = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Mounted Damage Reduction Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double mountedDamageReductionFactor = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Mounted Damage Reduction for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxMountedDamageReduction = 0.28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double horseSpeedAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double horseSpeedAmplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double striderSpeedAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Strider Speed Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double striderSpeedAmplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pigResistanceAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Resistance Amplifier Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pigResistanceAmplifierFactor = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for horse mounted speed scaling.", impact = "Higher values increase steady mounted horse acceleration when moving forward.") + double horseBaseHorizontalSpeed = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed target used for strider mounted speed scaling.", impact = "Higher values increase steady mounted strider acceleration when moving forward.") + double striderBaseHorizontalSpeed = 0.24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force on mounts.", impact = "Acts as a hard cap to prevent runaway mounted momentum.") + double mountMaxHorizontalSpeed = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast mounts accelerate toward the target speed per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + double mountAccelPerTick = 0.065; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + double fallbackInputVelocityThreshold = 0.0008; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double horsePushBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Horse Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double horsePushFactor = 0.16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Base for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pigPushBase = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Pig Push Factor for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double pigPushFactor = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mounted Damage for the Taming Mounted Tactics adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMountedDamage = 1.5; + + double fallbackInputVelocityThresholdSquared() { + double threshold = Math.max(0, fallbackInputVelocityThreshold); + return threshold * threshold; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java index 252be8e26..40e6df26f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingPackLeaderAura.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.world.AdaptPlayer; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; @@ -42,159 +42,161 @@ import java.util.List; public class TamingPackLeaderAura extends SimpleAdaptation { - private int ownerCursor = 0; - - public TamingPackLeaderAura() { - super("tame-pack-leader-aura"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.pack_leader_aura.description")); - setDisplayName(Localizer.dLocalize("taming.pack_leader_aura.name")); - setIcon(Material.BONE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(30); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_taming_pack_72k") - .title(Localizer.dLocalize("advancement.challenge_taming_pack_72k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_pack_72k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_taming_pack_72k", "taming.pack-leader.buffed-ticks", 72000, 400); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("taming.pack_leader_aura.lore1")); - v.addLore(C.GREEN + "+ " + (1 + getAmplifier(level)) + C.GRAY + " " + Localizer.dLocalize("taming.pack_leader_aura.lore2")); - } - - @Override - public void onTick() { - List owners = collectOwners(); - if (owners.isEmpty()) { - return; - } - - List batch = selectBatch(owners); - if (J.isFoliaThreading()) { - for (OwnerAuraState state : batch) { - J.runEntity(state.owner(), () -> applyAura(state)); - } - return; - } - - if (!J.isPrimaryThread()) { - J.s(this::onTick); - return; - } - - for (OwnerAuraState state : batch) { - applyAura(state); - } - } - - private List collectOwners() { - List owners = new ArrayList<>(); - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player owner = adaptPlayer.getPlayer(); - int level = getActiveLevel(owner); - if (level <= 0) { - continue; - } - - double radius = getRadius(level); - owners.add(new OwnerAuraState(adaptPlayer, owner, radius, radius * radius, getAmplifier(level))); - } - - return owners; + private int ownerCursor = 0; + + public TamingPackLeaderAura() { + super("tame-pack-leader-aura"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.pack_leader_aura.description")); + setDisplayName(Localizer.dLocalize("taming.pack_leader_aura.name")); + setIcon(Material.BONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(30); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_taming_pack_72k") + .title(Localizer.dLocalize("advancement.challenge_taming_pack_72k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_pack_72k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_taming_pack_72k", "taming.pack-leader.buffed-ticks", 72000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + C.GRAY + " " + Localizer.dLocalize("taming.pack_leader_aura.lore1")); + v.addLore(C.GREEN + "+ " + (1 + getAmplifier(level)) + C.GRAY + " " + Localizer.dLocalize("taming.pack_leader_aura.lore2")); + } + + @Override + public void onTick() { + List owners = collectOwners(); + if (owners.isEmpty()) { + return; } - private List selectBatch(List owners) { - int size = owners.size(); - int limit = Math.max(1, Math.min(size, getConfig().maxOwnersPerPass)); - int start = Math.floorMod(ownerCursor, size); - List batch = new ArrayList<>(limit); - for (int i = 0; i < limit; i++) { - int index = (start + i) % size; - batch.add(owners.get(index)); - } - ownerCursor = (start + limit) % size; - return batch; + List batch = selectBatch(owners); + if (J.isFoliaThreading()) { + for (OwnerAuraState state : batch) { + J.runEntity(state.owner(), () -> applyAura(state)); + } + return; } - private void applyAura(OwnerAuraState state) { - Player owner = state.owner(); - if (owner == null || !owner.isOnline()) { - return; - } - - Location ownerLocation = owner.getLocation(); - for (Entity nearby : owner.getNearbyEntities(state.radius(), state.radius(), state.radius())) { - if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { - continue; - } - if (!(tameable.getOwner() instanceof Player petOwner) || !petOwner.getUniqueId().equals(owner.getUniqueId())) { - continue; - } - if (ownerLocation.distanceSquared(tameable.getLocation()) > state.radiusSquared()) { - continue; - } - - tameable.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, getConfig().effectTicks, state.amplifier(), false, false)); - tameable.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().effectTicks, state.amplifier(), false, false)); - state.ownerData().getData().addStat("taming.pack-leader.buffed-ticks", 1); - } + if (!J.isPrimaryThread()) { + J.s(this::onTick); + return; } - private record OwnerAuraState(AdaptPlayer ownerData, Player owner, double radius, double radiusSquared, int amplifier) { + for (OwnerAuraState state : batch) { + applyAura(state); } - - private double getRadius(int level) { - return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private List collectOwners() { + List owners = new ArrayList<>(); + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player owner = adaptPlayer.getPlayer(); + int level = getActiveLevel(owner); + if (level <= 0) { + continue; + } + + double radius = getRadius(level); + owners.add(new OwnerAuraState(adaptPlayer, owner, radius, radius * radius, getAmplifier(level))); } - private int getAmplifier(int level) { - return Math.max(0, (int) Math.floor(getLevelPercent(level) * getConfig().maxAmplifier)); + return owners; + } + + private List selectBatch(List owners) { + int size = owners.size(); + int limit = Math.max(1, Math.min(size, getConfig().maxOwnersPerPass)); + int start = Math.floorMod(ownerCursor, size); + List batch = new ArrayList<>(limit); + for (int i = 0; i < limit; i++) { + int index = (start + i) % size; + batch.add(owners.get(index)); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; + ownerCursor = (start + limit) % size; + return batch; + } + + private void applyAura(OwnerAuraState state) { + Player owner = state.owner(); + if (owner == null || !owner.isOnline()) { + return; } - @NoArgsConstructor - @ConfigDescription("Nearby tamed companions gain speed and regeneration near their owner.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusBase = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double radiusFactor = 14; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxAmplifier = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int effectTicks = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum owners processed per aura pass.", impact = "Lower values reduce burst workload but spread updates across more passes.") - int maxOwnersPerPass = 120; + Location ownerLocation = owner.getLocation(); + for (Entity nearby : owner.getNearbyEntities(state.radius(), state.radius(), state.radius())) { + if (!(nearby instanceof Tameable tameable) || !tameable.isTamed()) { + continue; + } + if (!(tameable.getOwner() instanceof Player petOwner) || !petOwner.getUniqueId().equals(owner.getUniqueId())) { + continue; + } + if (ownerLocation.distanceSquared(tameable.getLocation()) > state.radiusSquared()) { + continue; + } + + tameable.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, getConfig().effectTicks, state.amplifier(), false, false)); + tameable.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().effectTicks, state.amplifier(), false, false)); + state.ownerData().getData().addStat("taming.pack-leader.buffed-ticks", 1); } + } + + private double getRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private int getAmplifier(int level) { + return Math.max(0, (int) Math.floor(getLevelPercent(level) * getConfig().maxAmplifier)); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record OwnerAuraState(AdaptPlayer ownerData, Player owner, + double radius, double radiusSquared, + int amplifier) { + } + + @NoArgsConstructor + @ConfigDescription("Nearby tamed companions gain speed and regeneration near their owner.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 14; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Amplifier for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxAmplifier = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Ticks for the Taming Pack Leader Aura adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int effectTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum owners processed per aura pass.", impact = "Lower values reduce burst workload but spread updates across more passes.") + int maxOwnersPerPass = 120; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java index ba27ea9b5..6fd90e55e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingSharedPain.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; @@ -38,128 +38,128 @@ import org.bukkit.event.entity.EntityDamageEvent; public class TamingSharedPain extends SimpleAdaptation { - public TamingSharedPain() { - super("tame-shared-pain"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("taming.shared_pain.description")); - setDisplayName(Localizer.dLocalize("taming.shared_pain.name")); - setIcon(Material.POPPY); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1700); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHIELD) - .key("challenge_taming_shared_500") - .title(Localizer.dLocalize("advancement.challenge_taming_shared_500.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_shared_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TOTEM_OF_UNDYING) - .key("challenge_taming_shared_5k") - .title(Localizer.dLocalize("advancement.challenge_taming_shared_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_shared_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_taming_shared_500", "taming.shared-pain.damage-taken", 500, 400); - registerMilestone("challenge_taming_shared_5k", "taming.shared-pain.damage-taken", 5000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getRedirectPercent(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.shared_pain.lore1")); - v.addLore(C.YELLOW + "* " + Form.f(getOwnerHealthFloor(level), 1) + C.GRAY + " " + Localizer.dLocalize("taming.shared_pain.lore2")); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Tameable tameable) || !tameable.isTamed() || !(tameable.getOwner() instanceof Player owner)) { - return; - } - - int level = getActiveDamageLevel(owner, tameable); - if (level <= 0) { - return; - } - - double redirect = e.getDamage() * getRedirectPercent(level); - if (redirect <= 0) { - return; - } - - double floor = getOwnerHealthFloor(level); - double allowed = Math.max(0, owner.getHealth() - floor); - redirect = Math.min(redirect, allowed); - if (redirect <= 0.01) { - return; - } - - e.setDamage(Math.max(0, e.getDamage() - redirect)); - if (e.getDamage() <= 0.01) { - e.setCancelled(true); - } - - owner.damage(redirect); - getPlayer(owner).getData().addStat("taming.shared-pain.damage-taken", redirect); - SoundPlayer sp = SoundPlayer.of(owner.getWorld()); - sp.play(owner.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_HIT, 0.65f, 0.7f); - sp.play(tameable.getLocation(), Sound.ENTITY_WOLF_WHINE, 0.55f, 1.2f); - xp(owner, redirect * getConfig().xpPerRedirectedDamage); + public TamingSharedPain() { + super("tame-shared-pain"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("taming.shared_pain.description")); + setDisplayName(Localizer.dLocalize("taming.shared_pain.name")); + setIcon(Material.POPPY); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1700); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHIELD) + .key("challenge_taming_shared_500") + .title(Localizer.dLocalize("advancement.challenge_taming_shared_500.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_shared_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TOTEM_OF_UNDYING) + .key("challenge_taming_shared_5k") + .title(Localizer.dLocalize("advancement.challenge_taming_shared_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_shared_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_taming_shared_500", "taming.shared-pain.damage-taken", 500, 400); + registerMilestone("challenge_taming_shared_5k", "taming.shared-pain.damage-taken", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getRedirectPercent(level), 0) + C.GRAY + " " + Localizer.dLocalize("taming.shared_pain.lore1")); + v.addLore(C.YELLOW + "* " + Form.f(getOwnerHealthFloor(level), 1) + C.GRAY + " " + Localizer.dLocalize("taming.shared_pain.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Tameable tameable) || !tameable.isTamed() || !(tameable.getOwner() instanceof Player owner)) { + return; } - private double getRedirectPercent(int level) { - return Math.min(getConfig().maxRedirectPercent, getConfig().redirectPercentBase + (getLevelPercent(level) * getConfig().redirectPercentFactor)); + int level = getActiveDamageLevel(owner, tameable); + if (level <= 0) { + return; } - private double getOwnerHealthFloor(int level) { - return Math.max(1.0, getConfig().ownerHealthFloorBase + (getLevelPercent(level) * getConfig().ownerHealthFloorFactor)); + double redirect = e.getDamage() * getRedirectPercent(level); + if (redirect <= 0) { + return; } - @Override - public void onTick() { - + double floor = getOwnerHealthFloor(level); + double allowed = Math.max(0, owner.getHealth() - floor); + redirect = Math.min(redirect, allowed); + if (redirect <= 0.01) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + e.setDamage(Math.max(0, e.getDamage() - redirect)); + if (e.getDamage() <= 0.01) { + e.setCancelled(true); } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Redirect part of your pet's incoming damage to you, preserving companion survivability.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double redirectPercentBase = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double redirectPercentFactor = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Redirect Percent for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxRedirectPercent = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double ownerHealthFloorBase = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double ownerHealthFloorFactor = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Redirected Damage for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerRedirectedDamage = 2.0; - } + owner.damage(redirect); + getPlayer(owner).getData().addStat("taming.shared-pain.damage-taken", redirect); + SoundPlayer sp = SoundPlayer.of(owner.getWorld()); + sp.play(owner.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_HIT, 0.65f, 0.7f); + sp.play(tameable.getLocation(), Sound.ENTITY_WOLF_WHINE, 0.55f, 1.2f); + xp(owner, redirect * getConfig().xpPerRedirectedDamage); + } + + private double getRedirectPercent(int level) { + return Math.min(getConfig().maxRedirectPercent, getConfig().redirectPercentBase + (getLevelPercent(level) * getConfig().redirectPercentFactor)); + } + + private double getOwnerHealthFloor(int level) { + return Math.max(1.0, getConfig().ownerHealthFloorBase + (getLevelPercent(level) * getConfig().ownerHealthFloorFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Redirect part of your pet's incoming damage to you, preserving companion survivability.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double redirectPercentBase = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redirect Percent Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double redirectPercentFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Redirect Percent for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxRedirectPercent = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Base for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double ownerHealthFloorBase = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Owner Health Floor Factor for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double ownerHealthFloorFactor = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Redirected Damage for the Taming Shared Pain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerRedirectedDamage = 2.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java index 4fd58692c..cb8a17250 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java @@ -25,11 +25,11 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -52,391 +52,391 @@ import java.util.concurrent.ThreadLocalRandom; public class TragoulBloodPact extends SimpleAdaptation { - private static final PotionEffectType[] EFFECT_POOL = { - PotionEffectType.SPEED, - PotionEffectType.REGENERATION, - PotionEffectType.RESISTANCE, - PotionEffectType.FIRE_RESISTANCE, - PotionEffectType.ABSORPTION, - PotionEffectType.JUMP_BOOST, - PotionEffectType.NIGHT_VISION - }; - - private final Map procCooldowns = new ConcurrentHashMap<>(); - private final Map lowHealthProcs = new ConcurrentHashMap<>(); - private final Map speedBursts = new ConcurrentHashMap<>(); - - public TragoulBloodPact() { - super("tragoul-blood-pact"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("tragoul.blood_pact.description")); - setDisplayName(Localizer.dLocalize("tragoul.blood_pact.name")); - setIcon(Material.NETHER_WART); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(20); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.REDSTONE) - .key("challenge_tragoul_pact_200") - .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_200.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_tragoul_pact_kills_500") - .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_kills_500.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_kills_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_tragoul_pact_200", "tragoul.blood-pact.health-sacrificed", 200, 400); - registerMilestone("challenge_tragoul_pact_kills_500", "tragoul.blood-pact.empowered-kills", 500, 1000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.REDSTONE) - .key("challenge_tragoul_pact_all_in") - .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_all_in.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_all_in.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + private static final PotionEffectType[] EFFECT_POOL = { + PotionEffectType.SPEED, + PotionEffectType.REGENERATION, + PotionEffectType.RESISTANCE, + PotionEffectType.FIRE_RESISTANCE, + PotionEffectType.ABSORPTION, + PotionEffectType.JUMP_BOOST, + PotionEffectType.NIGHT_VISION + }; + + private final Map procCooldowns = new ConcurrentHashMap<>(); + private final Map lowHealthProcs = new ConcurrentHashMap<>(); + private final Map speedBursts = new ConcurrentHashMap<>(); + + public TragoulBloodPact() { + super("tragoul-blood-pact"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.blood_pact.description")); + setDisplayName(Localizer.dLocalize("tragoul.blood_pact.name")); + setIcon(Material.NETHER_WART); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(20); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.REDSTONE) + .key("challenge_tragoul_pact_200") + .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_200.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_tragoul_pact_kills_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_kills_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_kills_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_tragoul_pact_200", "tragoul.blood-pact.health-sacrificed", 200, 400); + registerMilestone("challenge_tragoul_pact_kills_500", "tragoul.blood-pact.empowered-kills", 500, 1000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.REDSTONE) + .key("challenge_tragoul_pact_all_in") + .title(Localizer.dLocalize("advancement.challenge_tragoul_pact_all_in.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_pact_all_in.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getEffectDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getProcCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + procCooldowns.remove(e.getPlayer().getUniqueId()); + lowHealthProcs.remove(e.getPlayer().getUniqueId()); + speedBursts.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerDeathEvent e) { + speedBursts.remove(e.getEntity().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getProcChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getEffectDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getProcCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.blood_pact.lore3")); + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || e.getFinalDamage() < getMinTriggerDamage()) { + return; + } + + long now = System.currentTimeMillis(); + long until = procCooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < until) { + return; + } + + if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { + return; + } + + procCooldowns.put(p.getUniqueId(), now + getProcCooldownMillis(level)); + getPlayer(p).getData().addStat("tragoul.blood-pact.health-sacrificed", (int) e.getFinalDamage()); + if (p.getHealth() - e.getFinalDamage() <= 6.0) { + lowHealthProcs.put(p.getUniqueId(), true); + } + applyRandomBuffs(p, level, e.getFinalDamage()); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, p.getLocation().add(0, 1.0, 0), 22, 0.28, 0.42, 0.28, 0.02); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.62f, 1.25f); + xp(p, getConfig().xpPerProc); + }); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent) { + if (dmgEvent.getDamager() instanceof Player p) { + withAdaptedPlayer(p, () -> { + if (p.hasPotionEffect(PotionEffectType.ABSORPTION) || p.hasPotionEffect(PotionEffectType.RESISTANCE)) { + getPlayer(p).getData().addStat("tragoul.blood-pact.empowered-kills", 1); + if (lowHealthProcs.getOrDefault(p.getUniqueId(), false) && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_pact_all_in")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_pact_all_in"); + } + } + }); + } } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - procCooldowns.remove(e.getPlayer().getUniqueId()); - lowHealthProcs.remove(e.getPlayer().getUniqueId()); - speedBursts.remove(e.getPlayer().getUniqueId()); + private void applyRandomBuffs(Player p, int level, double takenDamage) { + int count = getBuffCount(level); + if (takenDamage >= (getMinTriggerDamage() * 1.6)) { + count++; + } + if (ThreadLocalRandom.current().nextDouble() <= getBonusBuffChance(level)) { + count++; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerDeathEvent e) { - speedBursts.remove(e.getEntity().getUniqueId()); + List pool = new ArrayList<>(List.of(EFFECT_POOL)); + Collections.shuffle(pool); + count = Math.min(count, pool.size()); + + int duration = getEffectDurationTicks(level); + for (int i = 0; i < count; i++) { + PotionEffectType type = pool.get(i); + int amplifier = getEffectAmplifier(type, level); + int d = type == PotionEffectType.ABSORPTION ? Math.max(40, duration - 20) : duration; + if (type == PotionEffectType.SPEED) { + grantSpeedBurst(p, amplifier, d); + continue; + } + p.addPotionEffect(new PotionEffect(type, d, amplifier, false, true, true), true); } + } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } + private void grantSpeedBurst(Player p, int amplifier, int durationTicks) { + if (durationTicks <= 0) { + return; + } - withAdaptedPlayer(p, e, () -> { - int level = getActiveLevel(p); - if (level <= 0 || e.getFinalDamage() < getMinTriggerDamage()) { - return; - } + UUID id = p.getUniqueId(); + long now = System.currentTimeMillis(); + long durationMs = Math.max(50L, durationTicks * 50L); + SpeedBurst burst = speedBursts.get(id); + if (burst != null && burst.expiresAt > now) { + burst.expiresAt += durationMs; + burst.amplifier = Math.max(burst.amplifier, amplifier); + return; + } - long now = System.currentTimeMillis(); - long until = procCooldowns.getOrDefault(p.getUniqueId(), 0L); - if (now < until) { - return; - } + speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); + } - if (ThreadLocalRandom.current().nextDouble() > getProcChance(level)) { - return; - } + private double getProcChance(int level) { + return Math.min(getConfig().maxProcChance, + Math.max(0, getConfig().procChanceBase + (getLevelPercent(level) * getConfig().procChanceFactor))); + } - procCooldowns.put(p.getUniqueId(), now + getProcCooldownMillis(level)); - getPlayer(p).getData().addStat("tragoul.blood-pact.health-sacrificed", (int) e.getFinalDamage()); - if (p.getHealth() - e.getFinalDamage() <= 6.0) { - lowHealthProcs.put(p.getUniqueId(), true); - } - applyRandomBuffs(p, level, e.getFinalDamage()); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CRIMSON_SPORE, p.getLocation().add(0, 1.0, 0), 22, 0.28, 0.42, 0.28, 0.02); - } - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.62f, 1.25f); - xp(p, getConfig().xpPerProc); - }); - } + private long getProcCooldownMillis(int level) { + return Math.max(500L, (long) Math.round(getConfig().procCooldownMillisBase - (getLevelPercent(level) * getConfig().procCooldownMillisFactor))); + } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent dmgEvent) { - if (dmgEvent.getDamager() instanceof Player p) { - withAdaptedPlayer(p, () -> { - if (p.hasPotionEffect(PotionEffectType.ABSORPTION) || p.hasPotionEffect(PotionEffectType.RESISTANCE)) { - getPlayer(p).getData().addStat("tragoul.blood-pact.empowered-kills", 1); - if (lowHealthProcs.getOrDefault(p.getUniqueId(), false) && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_pact_all_in")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_pact_all_in"); - } - } - }); - } - } - } + private int getEffectDurationTicks(int level) { + return Math.max(40, (int) Math.round(getConfig().effectDurationTicksBase + (getLevelPercent(level) * getConfig().effectDurationTicksFactor))); + } - private void applyRandomBuffs(Player p, int level, double takenDamage) { - int count = getBuffCount(level); - if (takenDamage >= (getMinTriggerDamage() * 1.6)) { - count++; - } - if (ThreadLocalRandom.current().nextDouble() <= getBonusBuffChance(level)) { - count++; - } - - List pool = new ArrayList<>(List.of(EFFECT_POOL)); - Collections.shuffle(pool); - count = Math.min(count, pool.size()); - - int duration = getEffectDurationTicks(level); - for (int i = 0; i < count; i++) { - PotionEffectType type = pool.get(i); - int amplifier = getEffectAmplifier(type, level); - int d = type == PotionEffectType.ABSORPTION ? Math.max(40, duration - 20) : duration; - if (type == PotionEffectType.SPEED) { - grantSpeedBurst(p, amplifier, d); - continue; - } - p.addPotionEffect(new PotionEffect(type, d, amplifier, false, true, true), true); - } - } + private int getBuffCount(int level) { + return Math.max(1, (int) Math.round(getConfig().buffCountBase + (getLevelPercent(level) * getConfig().buffCountFactor))); + } - private void grantSpeedBurst(Player p, int amplifier, int durationTicks) { - if (durationTicks <= 0) { - return; - } + private double getBonusBuffChance(int level) { + return Math.min(0.9, Math.max(0, getConfig().bonusBuffChanceBase + (getLevelPercent(level) * getConfig().bonusBuffChanceFactor))); + } + private int getEffectAmplifier(PotionEffectType type, int level) { + double progress = getLevelPercent(level); + if (type == PotionEffectType.ABSORPTION || type == PotionEffectType.RESISTANCE || type == PotionEffectType.REGENERATION) { + return progress >= 0.85 ? 1 : 0; + } + return progress >= 0.7 ? 1 : 0; + } + + private double getMinTriggerDamage() { + return Math.max(1, getConfig().minDamageTriggerHearts * 2D); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + procCooldowns.entrySet().removeIf(i -> i.getValue() <= now); + applySpeedBursts(now); + } + + private void applySpeedBursts(long now) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + withPlayerThread(p, () -> { UUID id = p.getUniqueId(); - long now = System.currentTimeMillis(); - long durationMs = Math.max(50L, durationTicks * 50L); SpeedBurst burst = speedBursts.get(id); - if (burst != null && burst.expiresAt > now) { - burst.expiresAt += durationMs; - burst.amplifier = Math.max(burst.amplifier, amplifier); - return; + if (burst == null) { + return; } - speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); - } - - private double getProcChance(int level) { - return Math.min(getConfig().maxProcChance, - Math.max(0, getConfig().procChanceBase + (getLevelPercent(level) * getConfig().procChanceFactor))); - } - - private long getProcCooldownMillis(int level) { - return Math.max(500L, (long) Math.round(getConfig().procCooldownMillisBase - (getLevelPercent(level) * getConfig().procCooldownMillisFactor))); - } + if (burst.expiresAt <= now) { + invalidateSpeedBurst(p, burst, false); + speedBursts.remove(id); + return; + } - private int getEffectDurationTicks(int level) { - return Math.max(40, (int) Math.round(getConfig().effectDurationTicksBase + (getLevelPercent(level) * getConfig().effectDurationTicksFactor))); - } + if (!isVelocityEligible(p)) { + invalidateSpeedBurst(p, burst, true); + return; + } - private int getBuffCount(int level) { - return Math.max(1, (int) Math.round(getConfig().buffCountBase + (getLevelPercent(level) * getConfig().buffCountFactor))); - } + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeSpeedBurst(p, burst); + return; + } - private double getBonusBuffChance(int level) { - return Math.min(0.9, Math.max(0, getConfig().bonusBuffChanceBase + (getLevelPercent(level) * getConfig().bonusBuffChanceFactor))); + applySpeedBurst(p, burst, input); + }); } + } - private int getEffectAmplifier(PotionEffectType type, int level) { - double progress = getLevelPercent(level); - if (type == PotionEffectType.ABSORPTION || type == PotionEffectType.RESISTANCE || type == PotionEffectType.REGENERATION) { - return progress >= 0.85 ? 1 : 0; - } - return progress >= 0.7 ? 1 : 0; + private void applySpeedBurst(Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { + Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); + if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { + brakeSpeedBurst(p, burst); + return; } - private double getMinTriggerDamage() { - return Math.max(1, getConfig().minDamageTriggerHearts * 2D); + double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, + Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + Vector targetHorizontal = direction.multiply(targetSpeed); + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); + nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + burst.boosting = true; + } + + private void brakeSpeedBurst(Player p, SpeedBurst burst) { + if (!burst.boosting) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - procCooldowns.entrySet().removeIf(i -> i.getValue() <= now); - applySpeedBursts(now); + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + double stopThreshold = Math.max(0, getConfig().stopThreshold); + if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; } - private void applySpeedBursts(long now) { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - - withPlayerThread(p, () -> { - UUID id = p.getUniqueId(); - SpeedBurst burst = speedBursts.get(id); - if (burst == null) { - return; - } - - if (burst.expiresAt <= now) { - invalidateSpeedBurst(p, burst, false); - speedBursts.remove(id); - return; - } - - if (!isVelocityEligible(p)) { - invalidateSpeedBurst(p, burst, true); - return; - } - - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeSpeedBurst(p, burst); - return; - } - - applySpeedBurst(p, burst, input); - }); - } + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); + if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; } - private void applySpeedBurst(Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { - Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - brakeSpeedBurst(p, burst); - return; - } + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + } - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, - Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - Vector targetHorizontal = direction.multiply(targetSpeed); - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - burst.boosting = true; + private void invalidateSpeedBurst(Player p, SpeedBurst burst, boolean invalidState) { + if (!burst.boosting) { + return; } - private void brakeSpeedBurst(Player p, SpeedBurst burst) { - if (!burst.boosting) { - return; - } - - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - double stopThreshold = Math.max(0, getConfig().stopThreshold); - if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } - - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); - if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } - - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + if (invalidState && getConfig().hardStopOnInvalidState) { + VelocitySpeed.hardStopHorizontal(p); } - private void invalidateSpeedBurst(Player p, SpeedBurst burst, boolean invalidState) { - if (!burst.boosting) { - return; - } + burst.boosting = false; + } - if (invalidState && getConfig().hardStopOnInvalidState) { - VelocitySpeed.hardStopHorizontal(p); - } - - burst.boosting = false; + private boolean isVelocityEligible(Player p) { + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; } - private boolean isVelocityEligible(Player p) { - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } - - return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; - } + return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + } - private static class SpeedBurst { - private long expiresAt; - private int amplifier; - private boolean boosting; + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - private SpeedBurst(long expiresAt, int amplifier) { - this.expiresAt = expiresAt; - this.amplifier = amplifier; - } - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + private static class SpeedBurst { + private long expiresAt; + private int amplifier; + private boolean boosting; - @Override - public boolean isPermanent() { - return getConfig().permanent; + private SpeedBurst(long expiresAt, int amplifier) { + this.expiresAt = expiresAt; + this.amplifier = amplifier; } - - @NoArgsConstructor - @ConfigDescription("Taking at least 2 hearts of damage can trigger temporary beneficial effects.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.62; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Damage Trigger Hearts for the Tragoul Blood Pact adaptation.", impact = "Minimum damage taken in hearts required before the proc roll happens.") - double minDamageTriggerHearts = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procChanceBase = 0.12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procChanceFactor = 0.38; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Tragoul Blood Pact adaptation.", impact = "Caps chance at the requested maximum.") - double maxProcChance = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procCooldownMillisBase = 18000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double procCooldownMillisFactor = 12000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double effectDurationTicksBase = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double effectDurationTicksFactor = 150; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double buffCountBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double buffCountFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusBuffChanceBase = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusBuffChanceFactor = 0.34; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Proc for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerProc = 24; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for blood pact speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") - double baseHorizontalSpeed = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") - double maxHorizontalSpeed = 0.33; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double accelPerTick = 0.045; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") - double brakePerTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") - double stopThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") - boolean hardStopOnInvalidState = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + } + + @NoArgsConstructor + @ConfigDescription("Taking at least 2 hearts of damage can trigger temporary beneficial effects.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.62; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Damage Trigger Hearts for the Tragoul Blood Pact adaptation.", impact = "Minimum damage taken in hearts required before the proc roll happens.") + double minDamageTriggerHearts = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procChanceBase = 0.12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procChanceFactor = 0.38; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Proc Chance for the Tragoul Blood Pact adaptation.", impact = "Caps chance at the requested maximum.") + double maxProcChance = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procCooldownMillisBase = 18000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Proc Cooldown Millis Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double procCooldownMillisFactor = 12000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double effectDurationTicksBase = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Effect Duration Ticks Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double effectDurationTicksFactor = 150; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double buffCountBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Buff Count Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double buffCountFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Base for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusBuffChanceBase = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Buff Chance Factor for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusBuffChanceFactor = 0.34; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP Per Proc for the Tragoul Blood Pact adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerProc = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for blood pact speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") + double baseHorizontalSpeed = 0.13; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + double maxHorizontalSpeed = 0.33; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + double accelPerTick = 0.045; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + double brakePerTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + double stopThreshold = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") + boolean hardStopOnInvalidState = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + double fallbackInputVelocityThreshold = 0.0008; + + double fallbackInputVelocityThresholdSquared() { + double threshold = Math.max(0, fallbackInputVelocityThreshold); + return threshold * threshold; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java index 6c73b7c5f..6b9a13244 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBoneHarvest.java @@ -24,12 +24,12 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.math.VelocitySpeed; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -53,371 +53,371 @@ import java.util.concurrent.ThreadLocalRandom; public class TragoulBoneHarvest extends SimpleAdaptation { - private static final PotionEffectType[] BONE_EFFECT_POOL = { - PotionEffectType.SPEED, - PotionEffectType.REGENERATION, - PotionEffectType.RESISTANCE, - PotionEffectType.FIRE_RESISTANCE, - PotionEffectType.ABSORPTION, - PotionEffectType.JUMP_BOOST, - PotionEffectType.NIGHT_VISION - }; - - private final Set bloodGlobes = ConcurrentHashMap.newKeySet(); - private final Set boneGlobes = ConcurrentHashMap.newKeySet(); - private final Map speedBursts = new ConcurrentHashMap<>(); - - public TragoulBoneHarvest() { - super("tragoul-bone-harvest"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("tragoul.bone_harvest.description")); - setDisplayName(Localizer.dLocalize("tragoul.bone_harvest.name")); - setIcon(Material.BONE_BLOCK); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(2000); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_tragoul_bone_500") - .title(Localizer.dLocalize("advancement.challenge_tragoul_bone_500.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_bone_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BONE_BLOCK) - .key("challenge_tragoul_bone_5k") - .title(Localizer.dLocalize("advancement.challenge_tragoul_bone_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_bone_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_tragoul_bone_500", "tragoul.bone-harvest.orbs-collected", 500, 300); - registerMilestone("challenge_tragoul_bone_5k", "tragoul.bone-harvest.orbs-collected", 5000, 1000); + private static final PotionEffectType[] BONE_EFFECT_POOL = { + PotionEffectType.SPEED, + PotionEffectType.REGENERATION, + PotionEffectType.RESISTANCE, + PotionEffectType.FIRE_RESISTANCE, + PotionEffectType.ABSORPTION, + PotionEffectType.JUMP_BOOST, + PotionEffectType.NIGHT_VISION + }; + + private final Set bloodGlobes = ConcurrentHashMap.newKeySet(); + private final Set boneGlobes = ConcurrentHashMap.newKeySet(); + private final Map speedBursts = new ConcurrentHashMap<>(); + + public TragoulBoneHarvest() { + super("tragoul-bone-harvest"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.bone_harvest.description")); + setDisplayName(Localizer.dLocalize("tragoul.bone_harvest.name")); + setIcon(Material.BONE_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_tragoul_bone_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_bone_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_bone_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BONE_BLOCK) + .key("challenge_tragoul_bone_5k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_bone_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_bone_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_bone_500", "tragoul.bone-harvest.orbs-collected", 500, 300); + registerMilestone("challenge_tragoul_bone_5k", "tragoul.bone-harvest.orbs-collected", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getGlobeChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.bone_harvest.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getGlobeLifetimeTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.bone_harvest.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + Player killer = e.getEntity().getKiller(); + if (killer == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getGlobeChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.bone_harvest.lore1")); - v.addLore(C.GREEN + "+ " + Form.duration(getGlobeLifetimeTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.bone_harvest.lore2")); + withAdaptedPlayer(killer, () -> { + if (!canDamageTarget(killer, e.getEntity())) { + return; + } + + int level = getActiveLevel(killer); + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (random.nextDouble() > getGlobeChance(level)) { + return; + } + + spawnGlobe(killer, e, random.nextBoolean(), level); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityPickupItemEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - Player killer = e.getEntity().getKiller(); - if (killer == null) { - return; - } - - withAdaptedPlayer(killer, () -> { - if (!canDamageTarget(killer, e.getEntity())) { - return; - } - - int level = getActiveLevel(killer); - ThreadLocalRandom random = ThreadLocalRandom.current(); - if (random.nextDouble() > getGlobeChance(level)) { - return; - } - - spawnGlobe(killer, e, random.nextBoolean(), level); - }); + withAdaptedPlayer(p, e, () -> { + UUID id = e.getItem().getUniqueId(); + boolean blood = bloodGlobes.contains(id); + boolean bone = boneGlobes.contains(id); + if (!blood && !bone) { + return; + } + + e.setCancelled(true); + e.getItem().remove(); + bloodGlobes.remove(id); + boneGlobes.remove(id); + applyBuff(p, blood, getLevel(p)); + getPlayer(p).getData().addStat("tragoul.bone-harvest.orbs-collected", 1); + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + speedBursts.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerDeathEvent e) { + speedBursts.remove(e.getEntity().getUniqueId()); + } + + private void spawnGlobe(Player owner, EntityDeathEvent e, boolean blood, int level) { + ItemStack item = new ItemStack(blood ? Material.MAGMA_CREAM : Material.SNOWBALL); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName((blood ? C.RED : C.WHITE) + (blood ? "Blood Globe" : "Bone Globe")); + item.setItemMeta(meta); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void on(EntityPickupItemEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } + Item dropped = e.getEntity().getWorld().dropItemNaturally(e.getEntity().getLocation().add(0, 0.35, 0), item); + dropped.setPickupDelay(10); + if (blood) { + bloodGlobes.add(dropped.getUniqueId()); + } else { + boneGlobes.add(dropped.getUniqueId()); + } - withAdaptedPlayer(p, e, () -> { - UUID id = e.getItem().getUniqueId(); - boolean blood = bloodGlobes.contains(id); - boolean bone = boneGlobes.contains(id); - if (!blood && !bone) { - return; - } - - e.setCancelled(true); - e.getItem().remove(); - bloodGlobes.remove(id); - boneGlobes.remove(id); - applyBuff(p, blood, getLevel(p)); - getPlayer(p).getData().addStat("tragoul.bone-harvest.orbs-collected", 1); - }); + int life = getGlobeLifetimeTicks(level); + J.runEntity(dropped, () -> { + bloodGlobes.remove(dropped.getUniqueId()); + boneGlobes.remove(dropped.getUniqueId()); + if (dropped.isValid()) { + dropped.remove(); + } + }, life); + xp(owner, getConfig().xpPerGlobeSpawned); + } + + private void applyBuff(Player p, boolean blood, int level) { + if (blood) { + p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().bloodBuffTicks, getConfig().bloodBuffAmplifier, false, true, true), true); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.4f); + } else { + applyRandomBoneBuffs(p, level); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_SKELETON_HURT, 0.7f, 1.2f); } + } + + private void applyRandomBoneBuffs(Player p, int level) { + int buffs = Math.max(1, (int) Math.round(getConfig().boneBuffCountBase + (getLevelPercent(level) * getConfig().boneBuffCountFactor))); + List pool = new ArrayList<>(List.of(BONE_EFFECT_POOL)); + Collections.shuffle(pool); + buffs = Math.min(pool.size(), buffs); + + int duration = getConfig().boneBuffTicks; + int amp = Math.max(0, getConfig().boneBuffAmplifier); + for (int i = 0; i < buffs; i++) { + PotionEffectType type = pool.get(i); + int a = type == PotionEffectType.ABSORPTION && getLevelPercent(level) >= 0.75 ? amp + 1 : amp; + if (type == PotionEffectType.SPEED) { + grantSpeedBurst(p, a, duration); + continue; + } + p.addPotionEffect(new PotionEffect(type, duration, a, false, true, true), true); + } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - speedBursts.remove(e.getPlayer().getUniqueId()); + private void grantSpeedBurst(Player p, int amplifier, int durationTicks) { + if (durationTicks <= 0) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerDeathEvent e) { - speedBursts.remove(e.getEntity().getUniqueId()); + UUID id = p.getUniqueId(); + long now = System.currentTimeMillis(); + long durationMs = Math.max(50L, durationTicks * 50L); + SpeedBurst burst = speedBursts.get(id); + if (burst != null && burst.expiresAt > now) { + burst.expiresAt += durationMs; + burst.amplifier = Math.max(burst.amplifier, amplifier); + return; } - private void spawnGlobe(Player owner, EntityDeathEvent e, boolean blood, int level) { - ItemStack item = new ItemStack(blood ? Material.MAGMA_CREAM : Material.SNOWBALL); - ItemMeta meta = item.getItemMeta(); - if (meta != null) { - meta.setDisplayName((blood ? C.RED : C.WHITE) + (blood ? "Blood Globe" : "Bone Globe")); - item.setItemMeta(meta); - } + speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); + } - Item dropped = e.getEntity().getWorld().dropItemNaturally(e.getEntity().getLocation().add(0, 0.35, 0), item); - dropped.setPickupDelay(10); - if (blood) { - bloodGlobes.add(dropped.getUniqueId()); - } else { - boneGlobes.add(dropped.getUniqueId()); - } + private double getGlobeChance(int level) { + return Math.min(getConfig().maxGlobeChance, getConfig().globeChanceBase + (getLevelPercent(level) * getConfig().globeChanceFactor)); + } - int life = getGlobeLifetimeTicks(level); - J.runEntity(dropped, () -> { - bloodGlobes.remove(dropped.getUniqueId()); - boneGlobes.remove(dropped.getUniqueId()); - if (dropped.isValid()) { - dropped.remove(); - } - }, life); - xp(owner, getConfig().xpPerGlobeSpawned); - } + private int getGlobeLifetimeTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().globeLifetimeTicksBase + (getLevelPercent(level) * getConfig().globeLifetimeTicksFactor))); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } - private void applyBuff(Player p, boolean blood, int level) { - if (blood) { - p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, getConfig().bloodBuffTicks, getConfig().bloodBuffAmplifier, false, true, true), true); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.7f, 1.4f); - } else { - applyRandomBoneBuffs(p, level); - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_SKELETON_HURT, 0.7f, 1.2f); + withPlayerThread(p, () -> { + UUID id = p.getUniqueId(); + SpeedBurst burst = speedBursts.get(id); + if (burst == null) { + return; } - } - private void applyRandomBoneBuffs(Player p, int level) { - int buffs = Math.max(1, (int) Math.round(getConfig().boneBuffCountBase + (getLevelPercent(level) * getConfig().boneBuffCountFactor))); - List pool = new ArrayList<>(List.of(BONE_EFFECT_POOL)); - Collections.shuffle(pool); - buffs = Math.min(pool.size(), buffs); - - int duration = getConfig().boneBuffTicks; - int amp = Math.max(0, getConfig().boneBuffAmplifier); - for (int i = 0; i < buffs; i++) { - PotionEffectType type = pool.get(i); - int a = type == PotionEffectType.ABSORPTION && getLevelPercent(level) >= 0.75 ? amp + 1 : amp; - if (type == PotionEffectType.SPEED) { - grantSpeedBurst(p, a, duration); - continue; - } - p.addPotionEffect(new PotionEffect(type, duration, a, false, true, true), true); + if (burst.expiresAt <= now) { + invalidateSpeedBurst(p, burst, false); + speedBursts.remove(id); + return; } - } - private void grantSpeedBurst(Player p, int amplifier, int durationTicks) { - if (durationTicks <= 0) { - return; + if (!isVelocityEligible(p)) { + invalidateSpeedBurst(p, burst, true); + return; } - UUID id = p.getUniqueId(); - long now = System.currentTimeMillis(); - long durationMs = Math.max(50L, durationTicks * 50L); - SpeedBurst burst = speedBursts.get(id); - if (burst != null && burst.expiresAt > now) { - burst.expiresAt += durationMs; - burst.amplifier = Math.max(burst.amplifier, amplifier); - return; + VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); + if (!input.hasHorizontal()) { + brakeSpeedBurst(p, burst); + return; } - speedBursts.put(id, new SpeedBurst(now + durationMs, amplifier)); + applySpeedBurst(p, burst, input); + }); } + } - private double getGlobeChance(int level) { - return Math.min(getConfig().maxGlobeChance, getConfig().globeChanceBase + (getLevelPercent(level) * getConfig().globeChanceFactor)); + private void applySpeedBurst(Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { + Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); + if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { + brakeSpeedBurst(p, burst); + return; } - private int getGlobeLifetimeTicks(int level) { - return Math.max(20, (int) Math.round(getConfig().globeLifetimeTicksBase + (getLevelPercent(level) * getConfig().globeLifetimeTicksFactor))); + double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, + Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + Vector targetHorizontal = direction.multiply(targetSpeed); + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); + nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + burst.boosting = true; + } + + private void brakeSpeedBurst(Player p, SpeedBurst burst) { + if (!burst.boosting) { + return; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - if (p == null || !p.isOnline()) { - continue; - } - - withPlayerThread(p, () -> { - UUID id = p.getUniqueId(); - SpeedBurst burst = speedBursts.get(id); - if (burst == null) { - return; - } - - if (burst.expiresAt <= now) { - invalidateSpeedBurst(p, burst, false); - speedBursts.remove(id); - return; - } - - if (!isVelocityEligible(p)) { - invalidateSpeedBurst(p, burst, true); - return; - } - - VelocitySpeed.InputSnapshot input = VelocitySpeed.readInput(p, getConfig().fallbackInputVelocityThresholdSquared()); - if (!input.hasHorizontal()) { - brakeSpeedBurst(p, burst); - return; - } - - applySpeedBurst(p, burst, input); - }); - } + Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); + double stopThreshold = Math.max(0, getConfig().stopThreshold); + if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; } - private void applySpeedBurst(Player p, SpeedBurst burst, VelocitySpeed.InputSnapshot input) { - Vector direction = VelocitySpeed.resolveHorizontalDirection(p, input); - if (direction.lengthSquared() <= VelocitySpeed.EPSILON) { - brakeSpeedBurst(p, burst); - return; - } - - double targetSpeed = Math.min(getConfig().maxHorizontalSpeed, - Math.max(0, getConfig().baseHorizontalSpeed * VelocitySpeed.speedAmplifierScalar(burst.amplifier))); - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - Vector targetHorizontal = direction.multiply(targetSpeed); - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, targetHorizontal, Math.max(0, getConfig().accelPerTick)); - nextHorizontal = VelocitySpeed.clampHorizontal(nextHorizontal, getConfig().maxHorizontalSpeed); - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); - burst.boosting = true; + Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); + if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { + VelocitySpeed.hardStopHorizontal(p); + burst.boosting = false; + return; } - private void brakeSpeedBurst(Player p, SpeedBurst burst) { - if (!burst.boosting) { - return; - } - - Vector horizontal = VelocitySpeed.horizontalOnly(p.getVelocity()); - double stopThreshold = Math.max(0, getConfig().stopThreshold); - if (horizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } - - Vector nextHorizontal = VelocitySpeed.moveTowards(horizontal, new Vector(), Math.max(0, getConfig().brakePerTick)); - if (nextHorizontal.lengthSquared() <= stopThreshold * stopThreshold) { - VelocitySpeed.hardStopHorizontal(p); - burst.boosting = false; - return; - } + VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + } - VelocitySpeed.setHorizontalVelocity(p, nextHorizontal); + private void invalidateSpeedBurst(Player p, SpeedBurst burst, boolean invalidState) { + if (!burst.boosting) { + return; } - private void invalidateSpeedBurst(Player p, SpeedBurst burst, boolean invalidState) { - if (!burst.boosting) { - return; - } - - if (invalidState && getConfig().hardStopOnInvalidState) { - VelocitySpeed.hardStopHorizontal(p); - } - - burst.boosting = false; + if (invalidState && getConfig().hardStopOnInvalidState) { + VelocitySpeed.hardStopHorizontal(p); } - private boolean isVelocityEligible(Player p) { - GameMode mode = p.getGameMode(); - if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { - return false; - } + burst.boosting = false; + } - return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + private boolean isVelocityEligible(Player p) { + GameMode mode = p.getGameMode(); + if (mode != GameMode.SURVIVAL && mode != GameMode.ADVENTURE) { + return false; } - private static class SpeedBurst { - private long expiresAt; - private int amplifier; - private boolean boosting; + return !p.isDead() && !p.isFlying() && !p.isGliding() && !p.isSwimming() && p.getVehicle() == null; + } - private SpeedBurst(long expiresAt, int amplifier) { - this.expiresAt = expiresAt; - this.amplifier = amplifier; - } - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + private static class SpeedBurst { + private long expiresAt; + private int amplifier; + private boolean boosting; - @NoArgsConstructor - @ConfigDescription("Kills can spawn temporary blood and bone globes that grant short buffs when picked up.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double globeChanceBase = 0.16; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double globeChanceFactor = 0.42; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Globe Chance for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxGlobeChance = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double globeLifetimeTicksBase = 120; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double globeLifetimeTicksFactor = 220; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int bloodBuffTicks = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int bloodBuffAmplifier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int boneBuffTicks = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int boneBuffAmplifier = 0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double boneBuffCountBase = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double boneBuffCountFactor = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Globe Spawned for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerGlobeSpawned = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for bone-harvest speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") - double baseHorizontalSpeed = 0.13; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") - double maxHorizontalSpeed = 0.33; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") - double accelPerTick = 0.045; - @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") - double brakePerTick = 0.08; - @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") - double stopThreshold = 0.01; - @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") - boolean hardStopOnInvalidState = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") - double fallbackInputVelocityThreshold = 0.0008; - - double fallbackInputVelocityThresholdSquared() { - double threshold = Math.max(0, fallbackInputVelocityThreshold); - return threshold * threshold; - } + private SpeedBurst(long expiresAt, int amplifier) { + this.expiresAt = expiresAt; + this.amplifier = amplifier; + } + } + + @NoArgsConstructor + @ConfigDescription("Kills can spawn temporary blood and bone globes that grant short buffs when picked up.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double globeChanceBase = 0.16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Chance Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double globeChanceFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Globe Chance for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxGlobeChance = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double globeLifetimeTicksBase = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Globe Lifetime Ticks Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double globeLifetimeTicksFactor = 220; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int bloodBuffTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Blood Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int bloodBuffAmplifier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Ticks for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int boneBuffTicks = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Amplifier for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int boneBuffAmplifier = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Base for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double boneBuffCountBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bone Buff Count Factor for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double boneBuffCountFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Globe Spawned for the Tragoul Bone Harvest adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerGlobeSpawned = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base horizontal speed used for bone-harvest speed bursts.", impact = "Higher values increase movement speed when a speed burst is active.") + double baseHorizontalSpeed = 0.13; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum horizontal speed this adaptation can force.", impact = "Acts as a hard cap to prevent runaway momentum.") + double maxHorizontalSpeed = 0.33; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity accelerates toward the burst target per tick.", impact = "Higher values accelerate faster; lower values feel smoother.") + double accelPerTick = 0.045; + @art.arcane.adapt.util.config.ConfigDoc(value = "How fast velocity decays when movement input is released.", impact = "Higher values reduce carry momentum more aggressively.") + double brakePerTick = 0.08; + @art.arcane.adapt.util.config.ConfigDoc(value = "Horizontal velocity threshold considered fully stopped.", impact = "Higher values stop sooner; lower values preserve tiny motion longer.") + double stopThreshold = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "If true, burst velocity is force-cleared when entering invalid states.", impact = "Prevents retained speed from skipped state transitions.") + boolean hardStopOnInvalidState = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fallback movement threshold used when direct input API is unavailable.", impact = "Only used on runtimes without Player input access.") + double fallbackInputVelocityThreshold = 0.0008; + + double fallbackInputVelocityThresholdSquared() { + double threshold = Math.max(0, fallbackInputVelocityThreshold); + return threshold * threshold; } + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java index f54b497d3..3c5432bbe 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulGlobe.java @@ -25,9 +25,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -43,139 +43,139 @@ import java.util.concurrent.ConcurrentHashMap; public class TragoulGlobe extends SimpleAdaptation { - private final Map cooldowns; - - public TragoulGlobe() { - super("tragoul-globe"); - registerConfiguration(TragoulGlobe.Config.class); - setDescription(Localizer.dLocalize("tragoul.globe.description")); - setDisplayName(Localizer.dLocalize("tragoul.globe.name")); - setIcon(Material.CRYING_OBSIDIAN); - setInterval(25000); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_tragoul_globe_1k") - .title(Localizer.dLocalize("advancement.challenge_tragoul_globe_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_globe_1k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_tragoul_globe_1k", "tragoul.globe.mobs-shared-with", 1000, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_tragoul_globe_5") - .title(Localizer.dLocalize("advancement.challenge_tragoul_globe_5.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_globe_5.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + private final Map cooldowns; + + public TragoulGlobe() { + super("tragoul-globe"); + registerConfiguration(TragoulGlobe.Config.class); + setDescription(Localizer.dLocalize("tragoul.globe.description")); + setDisplayName(Localizer.dLocalize("tragoul.globe.name")); + setIcon(Material.CRYING_OBSIDIAN); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_tragoul_globe_1k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_globe_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_globe_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_tragoul_globe_1k", "tragoul.globe.mobs-shared-with", 1000, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_tragoul_globe_5") + .title(Localizer.dLocalize("advancement.challenge_tragoul_globe_5.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_globe_5.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.globe.lore1")); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.globe.lore2") + ((getConfig().rangePerLevel * level) + getConfig().initalRange)); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.globe.lore3") + (getConfig().bonusDamagePerLevel * level)); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p)) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("tragoul.globe.lore1")); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.globe.lore2") + ((getConfig().rangePerLevel * level) + getConfig().initalRange)); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.globe.lore3") + (getConfig().bonusDamagePerLevel * level)); - } + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, e.getEntity())) { + return; + } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p)) { - return; - } - - withAdaptedPlayer(p, e, () -> { - int level = getActiveLevel(p); - if (level <= 0 || !canDamageTarget(p, e.getEntity())) { - return; - } - - Long cooldownTime = cooldowns.get(p.getUniqueId()); - if (cooldownTime != null && cooldownTime + (1000 * getConfig().cooldown) > System.currentTimeMillis()) { - return; - } - - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - double range = (getConfig().rangePerLevel * level) + getConfig().initalRange; - - int entitiesCount = 0; - for (Entity entity : p.getNearbyEntities(range, range, range)) { - if (entity instanceof LivingEntity && !entity.equals(p)) { - entitiesCount++; - } - } - - if (entitiesCount <= 1) { - return; - } - - double damagePerEntity = e.getDamage() / entitiesCount + (getConfig().bonusDamagePerLevel * level); - e.setDamage(damagePerEntity); - - int mobsSharedWith = 0; - for (Entity entity : p.getNearbyEntities(range, range, range)) { - if (entity instanceof LivingEntity && !entity.equals(p) && canDamageTarget(p, entity)) { - ((LivingEntity) entity).damage(damagePerEntity, p); - mobsSharedWith++; - } - } - - getPlayer(p).getData().addStat("tragoul.globe.mobs-shared-with", mobsSharedWith); - if (mobsSharedWith >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_globe_5")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_globe_5"); - } - - if (areParticlesEnabled()) { - J.runEntity(p, () -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); - } - }); - } + Long cooldownTime = cooldowns.get(p.getUniqueId()); + if (cooldownTime != null && cooldownTime + (1000 * getConfig().cooldown) > System.currentTimeMillis()) { + return; + } + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + double range = (getConfig().rangePerLevel * level) + getConfig().initalRange; - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public void onTick() { - } + int entitiesCount = 0; + for (Entity entity : p.getNearbyEntities(range, range, range)) { + if (entity instanceof LivingEntity && !entity.equals(p)) { + entitiesCount++; + } + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + if (entitiesCount <= 1) { + return; + } + double damagePerEntity = e.getDamage() / entitiesCount + (getConfig().bonusDamagePerLevel * level); + e.setDamage(damagePerEntity); - @NoArgsConstructor - @ConfigDescription("Spread your damage among all nearby enemies.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Globe adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldown = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double rangePerLevel = 3.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Inital Range for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double initalRange = 5.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bonusDamagePerLevel = 1; - } + int mobsSharedWith = 0; + for (Entity entity : p.getNearbyEntities(range, range, range)) { + if (entity instanceof LivingEntity && !entity.equals(p) && canDamageTarget(p, entity)) { + ((LivingEntity) entity).damage(damagePerEntity, p); + mobsSharedWith++; + } + } + + getPlayer(p).getData().addStat("tragoul.globe.mobs-shared-with", mobsSharedWith); + if (mobsSharedWith >= 5 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_globe_5")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_globe_5"); + } + + if (areParticlesEnabled()) { + J.runEntity(p, () -> vfxFastSphere(p.getLocation(), range, Color.BLACK, 400)); + } + }); + } + + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + + @NoArgsConstructor + @ConfigDescription("Spread your damage among all nearby enemies.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Globe adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldown = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Range Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double rangePerLevel = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Inital Range for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double initalRange = 5.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Damage Per Level for the Tragoul Globe adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusDamagePerLevel = 1; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java index cde2cb298..7f48c2067 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulHealing.java @@ -26,10 +26,10 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -42,133 +42,133 @@ import java.util.concurrent.ConcurrentHashMap; public class TragoulHealing extends SimpleAdaptation { - private final Map cooldowns; - private final Map healingWindow; - - public TragoulHealing() { - super("tragoul-healing"); - registerConfiguration(TragoulHealing.Config.class); - setDescription(Localizer.dLocalize("tragoul.healing.description")); - setDisplayName(Localizer.dLocalize("tragoul.healing.name")); - setIcon(Material.GLISTERING_MELON_SLICE); - setInterval(25000); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new ConcurrentHashMap<>(); - healingWindow = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.REDSTONE) - .key("challenge_tragoul_healing_500") - .title(Localizer.dLocalize("advancement.challenge_tragoul_healing_500.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_healing_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RED_DYE) - .key("challenge_tragoul_healing_10k") - .title(Localizer.dLocalize("advancement.challenge_tragoul_healing_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_healing_10k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_tragoul_healing_500", "tragoul.healing.health-stolen", 500, 400); - registerMilestone("challenge_tragoul_healing_10k", "tragoul.healing.health-stolen", 10000, 1500); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("tragoul.healing.lore1")); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.healing.lore2")); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.healing.lore3") + (getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (level - 1) / (getConfig().maxLevel - 1)) + "%"); - } - - @EventHandler - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p) { - withAdaptedPlayer(p, e, () -> { - int level = getActiveLevel(p); - if (level <= 0 || !canDamageTarget(p, e.getEntity())) { - return; - } - - if (isOnCooldown(p)) { - return; - } - - if (!healingWindow.containsKey(p.getUniqueId())) { - Adapt.verbose("Starting healing window for " + p.getName()); - startHealingWindow(p); - } - - if (areParticlesEnabled()) { - vfxParticleLine(p.getLocation(), e.getEntity().getLocation(), 25, Particle.WHITE_ASH); - } - - double healPercentage = getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (level - 1) / (getConfig().maxLevel - 1); - double healAmount = e.getDamage() * healPercentage; - Adapt.verbose("Healing " + p.getName() + " for " + healAmount + " (" + healPercentage * 100 + "% of " + e.getDamage() + " damage)"); - var attribute = Version.get().getAttribute(p, Attributes.GENERIC_MAX_HEALTH); - p.setHealth(Math.min(attribute == null ? p.getHealth() : attribute.getValue(), p.getHealth() + healAmount)); - getPlayer(p).getData().addStat("tragoul.healing.health-stolen", (int) healAmount); - }); + private final Map cooldowns; + private final Map healingWindow; + + public TragoulHealing() { + super("tragoul-healing"); + registerConfiguration(TragoulHealing.Config.class); + setDescription(Localizer.dLocalize("tragoul.healing.description")); + setDisplayName(Localizer.dLocalize("tragoul.healing.name")); + setIcon(Material.GLISTERING_MELON_SLICE); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new ConcurrentHashMap<>(); + healingWindow = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.REDSTONE) + .key("challenge_tragoul_healing_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_healing_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_healing_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RED_DYE) + .key("challenge_tragoul_healing_10k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_healing_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_healing_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_healing_500", "tragoul.healing.health-stolen", 500, 400); + registerMilestone("challenge_tragoul_healing_10k", "tragoul.healing.health-stolen", 10000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.healing.lore1")); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.healing.lore2")); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.healing.lore3") + (getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (level - 1) / (getConfig().maxLevel - 1)) + "%"); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p) { + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, e.getEntity())) { + return; } - } - - private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p.getUniqueId()); - return cooldown != null && cooldown > System.currentTimeMillis(); - } - private void startHealingWindow(Player p) { - long currentTime = System.currentTimeMillis(); - healingWindow.put(p.getUniqueId(), currentTime + getConfig().windowDuration); - J.runEntity(p, () -> { - healingWindow.remove(p.getUniqueId()); - cooldowns.put(p.getUniqueId(), currentTime + getConfig().windowDuration + getConfig().cooldownDuration); - }, getConfig().windowDuration / 50); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + if (isOnCooldown(p)) { + return; + } - @Override - public void onTick() { - } + if (!healingWindow.containsKey(p.getUniqueId())) { + Adapt.verbose("Starting healing window for " + p.getName()); + startHealingWindow(p); + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + if (areParticlesEnabled()) { + vfxParticleLine(p.getLocation(), e.getEntity().getLocation(), 25, Particle.WHITE_ASH); + } - @NoArgsConstructor - @ConfigDescription("Regain health based on the damage you deal.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Healing adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minHealPercent = 0.10; // 0.10% - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHealPercent = 0.45; // 0.45% - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int cooldownDuration = 1000; // 1 second - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int windowDuration = 3000; // 3 seconds + double healPercentage = getConfig().minHealPercent + (getConfig().maxHealPercent - getConfig().minHealPercent) * (level - 1) / (getConfig().maxLevel - 1); + double healAmount = e.getDamage() * healPercentage; + Adapt.verbose("Healing " + p.getName() + " for " + healAmount + " (" + healPercentage * 100 + "% of " + e.getDamage() + " damage)"); + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_MAX_HEALTH); + p.setHealth(Math.min(attribute == null ? p.getHealth() : attribute.getValue(), p.getHealth() + healAmount)); + getPlayer(p).getData().addStat("tragoul.healing.health-stolen", (int) healAmount); + }); } + } + + private boolean isOnCooldown(Player p) { + Long cooldown = cooldowns.get(p.getUniqueId()); + return cooldown != null && cooldown > System.currentTimeMillis(); + } + + private void startHealingWindow(Player p) { + long currentTime = System.currentTimeMillis(); + healingWindow.put(p.getUniqueId(), currentTime + getConfig().windowDuration); + J.runEntity(p, () -> { + healingWindow.remove(p.getUniqueId()); + cooldowns.put(p.getUniqueId(), currentTime + getConfig().windowDuration + getConfig().cooldownDuration); + }, getConfig().windowDuration / 50); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Regain health based on the damage you deal.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Healing adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Min Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minHealPercent = 0.10; // 0.10% + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Heal Percent for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHealPercent = 0.45; // 0.45% + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int cooldownDuration = 1000; // 1 second + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Window Duration for the Tragoul Healing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int windowDuration = 3000; // 3 seconds + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java index bfaff8c0d..926088abd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLance.java @@ -23,9 +23,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Color; import org.bukkit.Material; @@ -42,148 +42,147 @@ import java.util.concurrent.ConcurrentHashMap; public class TragoulLance extends SimpleAdaptation { - private final Map cooldowns; - - public TragoulLance() { - super("tragoul-lance"); - registerConfiguration(TragoulLance.Config.class); - setDescription(Localizer.dLocalize("tragoul.lance.description")); - setDisplayName(Localizer.dLocalize("tragoul.lance.name")); - setIcon(Material.TRIDENT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_tragoul_lance_200") - .title(Localizer.dLocalize("advancement.challenge_tragoul_lance_200.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_lance_200.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_tragoul_lance_kills_100") - .title(Localizer.dLocalize("advancement.challenge_tragoul_lance_kills_100.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_lance_kills_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_tragoul_lance_200", "tragoul.lance.lances-spawned", 200, 400); - registerMilestone("challenge_tragoul_lance_kills_100", "tragoul.lance.lance-kills", 100, 1000); - } - - - @EventHandler (priority = EventPriority.LOWEST) - public void onEntityDeath(EntityDeathEvent event) { - if (event.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p) { - withAdaptedPlayer(p, () -> { - int level = getActiveLevel(p); - if (level <= 0 || !canDamageTarget(p, event.getEntity())) { - return; - } - - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + 5000 > System.currentTimeMillis()) { - return; - } - - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - double baseSeekerRange = 5 + 4 * level; - double damageDealt = e.getDamage(); - double seekerDamage = getConfig().seekerDamageMultiplier * damageDealt; - - triggerSeeker(p, event.getEntity(), seekerDamage, level, baseSeekerRange); - getPlayer(p).getData().addStat("tragoul.lance.lance-kills", 1); - }); - } - } - } - - private void triggerSeeker(Player p, Entity origin, double damage, int remainingSeekers, double range) { - if (remainingSeekers <= 0) { + private final Map cooldowns; + + public TragoulLance() { + super("tragoul-lance"); + registerConfiguration(TragoulLance.Config.class); + setDescription(Localizer.dLocalize("tragoul.lance.description")); + setDisplayName(Localizer.dLocalize("tragoul.lance.name")); + setIcon(Material.TRIDENT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_tragoul_lance_200") + .title(Localizer.dLocalize("advancement.challenge_tragoul_lance_200.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_lance_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_tragoul_lance_kills_100") + .title(Localizer.dLocalize("advancement.challenge_tragoul_lance_kills_100.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_lance_kills_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_tragoul_lance_200", "tragoul.lance.lances-spawned", 200, 400); + registerMilestone("challenge_tragoul_lance_kills_100", "tragoul.lance.lance-kills", 100, 1000); + } + + + @EventHandler(priority = EventPriority.LOWEST) + public void onEntityDeath(EntityDeathEvent event) { + if (event.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p) { + withAdaptedPlayer(p, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, event.getEntity())) { return; - } - - LivingEntity nearest = null; - double minDistance = range; - - for (Entity e : origin.getNearbyEntities(range, range, range)) { - if (e instanceof LivingEntity le && le != p) { - double distance = origin.getLocation().distance(le.getLocation()); - if (distance < minDistance) { - nearest = le; - minDistance = distance; - } - } - } - - if (nearest != null) { - getPlayer(p).getData().addStat("tragoul.lance.lances-spawned", 1); - vfxMovingSphere(origin.getLocation(), nearest.getLocation(), getConfig().seekerDelay, Color.MAROON, 0.25, 4); - double seekerDamage = getConfig().seekerDamageMultiplier * damage; - double selfDamage = getConfig().selfDamageMultiplier * seekerDamage; - Adapt.verbose("Seeker damage: " + seekerDamage + " Self damage: " + selfDamage); - - p.damage(selfDamage, p); - - LivingEntity finalNearest = nearest; - J.runEntity(finalNearest, () -> { - double remainingHealth = finalNearest.getHealth() - damage; - finalNearest.damage(damage, p); - if (remainingHealth <= 0) { - triggerSeeker(p, finalNearest, damage * 0.5, remainingSeekers - 1, range); - } - }, getConfig().seekerDelay); - } - } + } + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + 5000 > System.currentTimeMillis()) { + return; + } + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + double baseSeekerRange = 5 + 4 * level; + double damageDealt = e.getDamage(); + double seekerDamage = getConfig().seekerDamageMultiplier * damageDealt; - @Override - public boolean isEnabled() { - return getConfig().enabled; + triggerSeeker(p, event.getEntity(), seekerDamage, level, baseSeekerRange); + getPlayer(p).getData().addStat("tragoul.lance.lance-kills", 1); + }); + } } + } - @Override - public void onTick() { + private void triggerSeeker(Player p, Entity origin, double damage, int remainingSeekers, double range) { + if (remainingSeekers <= 0) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + LivingEntity nearest = null; + double minDistance = range; - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + Localizer.dLocalize("tragoul.lance.lore1")); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.lance.lore2") ); - v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.lance.lore3") + level); + for (Entity e : origin.getNearbyEntities(range, range, range)) { + if (e instanceof LivingEntity le && le != p) { + double distance = origin.getLocation().distance(le.getLocation()); + if (distance < minDistance) { + nearest = le; + minDistance = distance; + } + } } - @NoArgsConstructor - @ConfigDescription("Killing an enemy spawns a lance that seeks and damages a nearby enemy.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Delay for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int seekerDelay = 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double seekerDamageMultiplier = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Self Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double selfDamageMultiplier = 0.5; + if (nearest != null) { + getPlayer(p).getData().addStat("tragoul.lance.lances-spawned", 1); + vfxMovingSphere(origin.getLocation(), nearest.getLocation(), getConfig().seekerDelay, Color.MAROON, 0.25, 4); + double seekerDamage = getConfig().seekerDamageMultiplier * damage; + double selfDamage = getConfig().selfDamageMultiplier * seekerDamage; + Adapt.verbose("Seeker damage: " + seekerDamage + " Self damage: " + selfDamage); + + p.damage(selfDamage, p); + + LivingEntity finalNearest = nearest; + J.runEntity(finalNearest, () -> { + double remainingHealth = finalNearest.getHealth() - damage; + finalNearest.damage(damage, p); + if (remainingHealth <= 0) { + triggerSeeker(p, finalNearest, damage * 0.5, remainingSeekers - 1, range); + } + }, getConfig().seekerDelay); } + } + + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.lance.lore1")); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.lance.lore2")); + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.lance.lore3") + level); + } + + @NoArgsConstructor + @ConfigDescription("Killing an enemy spawns a lance that seeks and damages a nearby enemy.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Delay for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int seekerDelay = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Seeker Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double seekerDamageMultiplier = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Self Damage Multiplier for the Tragoul Lance adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double selfDamageMultiplier = 0.5; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java index e3b55ce5b..b425620da 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulThorns.java @@ -26,9 +26,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; import de.slikey.effectlib.effect.BleedEffect; import lombok.NoArgsConstructor; import org.bukkit.Material; @@ -45,156 +45,155 @@ public class TragoulThorns extends SimpleAdaptation { - private final Map cooldowns; - - public TragoulThorns() { - super("tragoul-thorns"); - registerConfiguration(TragoulThorns.Config.class); - setDescription(Localizer.dLocalize("tragoul.thorns.description")); - setDisplayName(Localizer.dLocalize("tragoul.thorns.name")); - setIcon(Material.CACTUS); - setInterval(25000); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - cooldowns = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CACTUS) - .key("challenge_tragoul_thorns_500") - .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_500.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_tragoul_thorns_5k") - .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_tragoul_thorns_500", "tragoul.thorns.damage-reflected", 500, 400); - registerMilestone("challenge_tragoul_thorns_5k", "tragoul.thorns.damage-reflected", 5000, 1500); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CACTUS) - .key("challenge_tragoul_thorns_kill") - .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_kill.title")) - .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_kill.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - } - - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "" + getConfig().damageMultiplierPerLevel * level + "x " + Localizer.dLocalize("tragoul.thorns.lore1")); - } - - + private final Map cooldowns; + + public TragoulThorns() { + super("tragoul-thorns"); + registerConfiguration(TragoulThorns.Config.class); + setDescription(Localizer.dLocalize("tragoul.thorns.description")); + setDisplayName(Localizer.dLocalize("tragoul.thorns.name")); + setIcon(Material.CACTUS); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CACTUS) + .key("challenge_tragoul_thorns_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_tragoul_thorns_5k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_thorns_500", "tragoul.thorns.damage-reflected", 500, 400); + registerMilestone("challenge_tragoul_thorns_5k", "tragoul.thorns.damage-reflected", 5000, 1500); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CACTUS) + .key("challenge_tragoul_thorns_kill") + .title(Localizer.dLocalize("advancement.challenge_tragoul_thorns_kill.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_thorns_kill.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "" + getConfig().damageMultiplierPerLevel * level + "x " + Localizer.dLocalize("tragoul.thorns.lore1")); + } + + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player p) { + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - if (e.getEntity() instanceof Player p) { - withAdaptedPlayer(p, e, () -> { - int level = getActiveLevel(p); - if (level <= 0) { - return; - } - - UUID id = p.getUniqueId(); - Long cooldown = cooldowns.get(id); - if (cooldown != null && cooldown + 1500 > System.currentTimeMillis()) { - return; - } - - cooldowns.put(id, System.currentTimeMillis()); - - LivingEntity le = null; - - if (e.getDamager() instanceof LivingEntity) { - le = (LivingEntity) e.getDamager(); - } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity) { - le = (LivingEntity) projectile.getShooter(); - } - - if (le != null && canDamageTarget(p, le)) { - if (areParticlesEnabled()) { - playThornsParticles(le); - } - double reflectedDamage = getConfig().damageMultiplierPerLevel * level; - double healthBefore = le.getHealth(); - le.damage(reflectedDamage, p); - getPlayer(p).getData().addStat("tragoul.thorns.damage-reflected", (int) reflectedDamage); - if (healthBefore <= reflectedDamage && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_thorns_kill")) { - getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_thorns_kill"); - } - } - }); + UUID id = p.getUniqueId(); + Long cooldown = cooldowns.get(id); + if (cooldown != null && cooldown + 1500 > System.currentTimeMillis()) { + return; } - } - private void playThornsParticles(LivingEntity target) { - Runnable burst = () -> { - if (target == null || !target.isValid()) { - return; - } + cooldowns.put(id, System.currentTimeMillis()); - target.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR, target.getLocation().clone().add(0, 1, 0), 8, 0.35, 0.45, 0.35, 0.01); - target.getWorld().spawnParticle(Particle.CRIT, target.getLocation().clone().add(0, 1, 0), 10, 0.4, 0.5, 0.4, 0.06); - }; + LivingEntity le = null; - if (J.isFoliaThreading()) { - J.runEntity(target, burst); - return; + if (e.getDamager() instanceof LivingEntity) { + le = (LivingEntity) e.getDamager(); + } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity) { + le = (LivingEntity) projectile.getShooter(); } - try { - BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); - blood.setEntity(target); - blood.height = -1; - blood.iterations = 1; - blood.start(); - } catch (UnsupportedOperationException | IllegalStateException ex) { - Adapt.verbose("Falling back to native thorns particles: " + ex.getMessage()); - burst.run(); + if (le != null && canDamageTarget(p, le)) { + if (areParticlesEnabled()) { + playThornsParticles(le); + } + double reflectedDamage = getConfig().damageMultiplierPerLevel * level; + double healthBefore = le.getHealth(); + le.damage(reflectedDamage, p); + getPlayer(p).getData().addStat("tragoul.thorns.damage-reflected", (int) reflectedDamage); + if (healthBefore <= reflectedDamage && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_tragoul_thorns_kill")) { + getPlayer(p).getAdvancementHandler().grant("challenge_tragoul_thorns_kill"); + } } + }); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - + private void playThornsParticles(LivingEntity target) { + Runnable burst = () -> { + if (target == null || !target.isValid()) { + return; + } - @Override - public void onTick() { - } + target.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR, target.getLocation().clone().add(0, 1, 0), 8, 0.35, 0.45, 0.35, 0.01); + target.getWorld().spawnParticle(Particle.CRIT, target.getLocation().clone().add(0, 1, 0), 10, 0.4, 0.5, 0.4, 0.06); + }; - @Override - public boolean isPermanent() { - return getConfig().permanent; + if (J.isFoliaThreading()) { + J.runEntity(target, burst); + return; } - @NoArgsConstructor - @ConfigDescription("Reflect damage back to your attacker.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Thorns adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Multiplier Per Level for the Tragoul Thorns adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageMultiplierPerLevel = 1.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.72; + try { + BleedEffect blood = new BleedEffect(Adapt.instance.adaptEffectManager); + blood.setEntity(target); + blood.height = -1; + blood.iterations = 1; + blood.start(); + } catch (UnsupportedOperationException | IllegalStateException ex) { + Adapt.verbose("Falling back to native thorns particles: " + ex.getMessage()); + burst.run(); } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Reflect damage back to your attacker.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Tragoul Thorns adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Multiplier Per Level for the Tragoul Thorns adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageMultiplierPerLevel = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java index e94d2dc28..4e858500f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/utils/EntityThings.java @@ -29,21 +29,21 @@ public class EntityThings { - public static LivingEntity findNearestEntity(Entity referenceEntity, double range, Player player) { - Location location = referenceEntity.getLocation(); - List nearbyEntities = referenceEntity.getNearbyEntities(range, range, range); - - // Filter out non-living entities and the player - List livingEntities = nearbyEntities.stream() - .filter(entity -> entity instanceof LivingEntity && !entity.equals(player)) - .map(entity -> (LivingEntity) entity) - .collect(Collectors.toList()); - - // Sort by distance to the reference entity - livingEntities.sort(Comparator.comparingDouble(entity -> entity.getLocation().distance(location))); - - // Return the nearest living entity, or null if no valid entity is found - return livingEntities.isEmpty() ? null : livingEntities.get(0); - } + public static LivingEntity findNearestEntity(Entity referenceEntity, double range, Player player) { + Location location = referenceEntity.getLocation(); + List nearbyEntities = referenceEntity.getNearbyEntities(range, range, range); + + // Filter out non-living entities and the player + List livingEntities = nearbyEntities.stream() + .filter(entity -> entity instanceof LivingEntity && !entity.equals(player)) + .map(entity -> (LivingEntity) entity) + .collect(Collectors.toList()); + + // Sort by distance to the reference entity + livingEntities.sort(Comparator.comparingDouble(entity -> entity.getLocation().distance(location))); + + // Return the nearest living entity, or null if no valid entity is found + return livingEntities.isEmpty() ? null : livingEntities.get(0); + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index 854d361fa..e5d13c694 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -47,242 +47,242 @@ import java.util.UUID; public class UnarmedBatteringCharge extends SimpleAdaptation { - private final Map primedState = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map primedTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); - - public UnarmedBatteringCharge() { - super("unarmed-battering-charge"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("unarmed.battering_charge.description")); - setDisplayName(Localizer.dLocalize("unarmed.battering_charge.name")); - setIcon(Material.BLAZE_ROD); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(50); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_INGOT) - .key("challenge_unarmed_charge_300") - .title(Localizer.dLocalize("advancement.challenge_unarmed_charge_300.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_charge_300.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_unarmed_charge_300", "unarmed.battering-charge.charges", 300, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND) - .key("challenge_unarmed_charge_kills_100") - .title(Localizer.dLocalize("advancement.challenge_unarmed_charge_kills_100.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_charge_kills_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_unarmed_charge_kills_100", "unarmed.battering-charge.charge-kills", 100, 1000); + private final Map primedState = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map primedTrailNextAt = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedBatteringCharge() { + super("unarmed-battering-charge"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.battering_charge.description")); + setDisplayName(Localizer.dLocalize("unarmed.battering_charge.name")); + setIcon(Material.BLAZE_ROD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(50); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_charge_300") + .title(Localizer.dLocalize("advancement.challenge_unarmed_charge_300.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_charge_300.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_unarmed_charge_300", "unarmed.battering-charge.charges", 300, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_charge_kills_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_charge_kills_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_charge_kills_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_unarmed_charge_kills_100", "unarmed.battering-charge.charge-kills", 100, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getDamageBonus(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getKnockback(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.f(getDamageBonus(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getKnockback(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.battering_charge.lore3")); + Player p = attack.attacker(); + if (p.isInsideVehicle()) { + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } - - Player p = attack.attacker(); - if (p.isInsideVehicle()) { - return; - } - - if (!isChargeLoadout(p) || !p.isSprinting()) { - return; - } - - ItemStack cooldownItem = getCooldownItem(p); - if (cooldownItem != null && p.hasCooldown(cooldownItem.getType())) { - return; - } + if (!isChargeLoadout(p) || !p.isSprinting()) { + return; + } - Vector v = p.getVelocity(); - if (v.lengthSquared() < getConfig().minimumVelocitySquared) { - return; - } + ItemStack cooldownItem = getCooldownItem(p); + if (cooldownItem != null && p.hasCooldown(cooldownItem.getType())) { + return; + } - int level = attack.level(); - e.setDamage(e.getDamage() + getDamageBonus(level)); - Entity target = e.getEntity(); - target.setVelocity(target.getVelocity().add(p.getLocation().getDirection().normalize().multiply(getKnockback(level)))); + Vector v = p.getVelocity(); + if (v.lengthSquared() < getConfig().minimumVelocitySquared) { + return; + } - if (cooldownItem != null) { - p.setCooldown(cooldownItem.getType(), getCooldownTicks(level)); - } + int level = attack.level(); + e.setDamage(e.getDamage() + getDamageBonus(level)); + Entity target = e.getEntity(); + target.setVelocity(target.getVelocity().add(p.getLocation().getDirection().normalize().multiply(getKnockback(level)))); - SoundPlayer sp = SoundPlayer.of(p.getWorld()); - sp.play(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_KNOCKBACK, 1f, 0.95f); - sp.play(target.getLocation(), Sound.ENTITY_ZOGLIN_ATTACK, 0.5f, 0.8f); - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.EXPLOSION, target.getLocation().add(0, 0.8, 0), 1, 0.06, 0.06, 0.06, 0.02); - } - if (areParticlesEnabled()) { - target.getWorld().spawnParticle(Particle.CLOUD, target.getLocation().add(0, 0.6, 0), 14, 0.25, 0.25, 0.25, 0.05); - } - primedState.put(p.getUniqueId(), false); - xp(p, e.getDamage() * getConfig().xpPerDamage); - getPlayer(p).getData().addStat("unarmed.battering-charge.charges", 1); + if (cooldownItem != null) { + p.setCooldown(cooldownItem.getType(), getCooldownTicks(level)); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (!(e.getEntity() instanceof LivingEntity victim)) { - return; - } - if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Player p - && hasActiveAdaptation(p) - && isChargeLoadout(p)) { - getPlayer(p).getData().addStat("unarmed.battering-charge.charge-kills", 1); - } + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_KNOCKBACK, 1f, 0.95f); + sp.play(target.getLocation(), Sound.ENTITY_ZOGLIN_ATTACK, 0.5f, 0.8f); + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.EXPLOSION, target.getLocation().add(0, 0.8, 0), 1, 0.06, 0.06, 0.06, 0.02); } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID id = e.getPlayer().getUniqueId(); - primedState.remove(id); - primedTrailNextAt.remove(id); + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.CLOUD, target.getLocation().add(0, 0.6, 0), 14, 0.25, 0.25, 0.25, 0.05); } - - private boolean isChargeLoadout(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - boolean fists = (!isItem(main) || main.getType() == Material.AIR) && (!isItem(off) || off.getType() == Material.AIR); - boolean shieldLoadout = (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); - return fists || shieldLoadout; + primedState.put(p.getUniqueId(), false); + xp(p, e.getDamage() * getConfig().xpPerDamage); + getPlayer(p).getData().addStat("unarmed.battering-charge.charges", 1); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim)) { + return; } - - private ItemStack getCooldownItem(Player p) { - ItemStack main = p.getInventory().getItemInMainHand(); - ItemStack off = p.getInventory().getItemInOffHand(); - if (isItem(main) && main.getType() == Material.SHIELD) { - return main; - } - - if (isItem(off) && off.getType() == Material.SHIELD) { - return off; - } - - return null; + if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Player p + && hasActiveAdaptation(p) + && isChargeLoadout(p)) { + getPlayer(p).getData().addStat("unarmed.battering-charge.charge-kills", 1); } - - private boolean isChargeReady(Player p) { - if (p.isInsideVehicle() || !isChargeLoadout(p) || !p.isSprinting()) { - return false; - } - - ItemStack cooldownItem = getCooldownItem(p); - if (cooldownItem != null && p.hasCooldown(cooldownItem.getType())) { - return false; - } - - Vector v = p.getVelocity(); - return v.lengthSquared() >= getConfig().minimumVelocitySquared; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + primedState.remove(id); + primedTrailNextAt.remove(id); + } + + private boolean isChargeLoadout(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + boolean fists = (!isItem(main) || main.getType() == Material.AIR) && (!isItem(off) || off.getType() == Material.AIR); + boolean shieldLoadout = (isItem(main) && main.getType() == Material.SHIELD) || (isItem(off) && off.getType() == Material.SHIELD); + return fists || shieldLoadout; + } + + private ItemStack getCooldownItem(Player p) { + ItemStack main = p.getInventory().getItemInMainHand(); + ItemStack off = p.getInventory().getItemInOffHand(); + if (isItem(main) && main.getType() == Material.SHIELD) { + return main; } - private double getDamageBonus(int level) { - return getConfig().damageBase + (getLevelPercent(level) * getConfig().damageFactor); + if (isItem(off) && off.getType() == Material.SHIELD) { + return off; } - private double getKnockback(int level) { - return getConfig().knockbackBase + (getLevelPercent(level) * getConfig().knockbackFactor); - } + return null; + } - private int getCooldownTicks(int level) { - return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + private boolean isChargeReady(Player p) { + if (p.isInsideVehicle() || !isChargeLoadout(p) || !p.isSprinting()) { + return false; } - @Override - public void onTick() { - long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - UUID id = p.getUniqueId(); - int level = getActiveLevel(p); - if (level <= 0) { - primedState.remove(id); - primedTrailNextAt.remove(id); - continue; - } - - boolean primed = isChargeReady(p); - boolean wasPrimed = primedState.getOrDefault(id, false); - - if (primed) { - long nextTrail = primedTrailNextAt.getOrDefault(id, 0L); - if (areParticlesEnabled() && now >= nextTrail) { - p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().add(0, 0.2, 0), 2, 0.2, 0.05, 0.2, 0.02); - primedTrailNextAt.put(id, now + Math.max(25L, getConfig().primedTrailIntervalMillis)); - } - if (!wasPrimed) { - SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASEDRUM, 0.55f, 1.15f); - if (areParticlesEnabled()) { - p.getWorld().spawnParticle(Particle.CRIT, p.getLocation().add(0, 1.0, 0), 8, 0.2, 0.3, 0.2, 0.1); - } - } - } else { - primedTrailNextAt.remove(id); - } - - primedState.put(id, primed); - } + ItemStack cooldownItem = getCooldownItem(p); + if (cooldownItem != null && p.hasCooldown(cooldownItem.getType())) { + return false; } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + Vector v = p.getVelocity(); + return v.lengthSquared() >= getConfig().minimumVelocitySquared; + } + + private double getDamageBonus(int level) { + return getConfig().damageBase + (getLevelPercent(level) * getConfig().damageFactor); + } + + private double getKnockback(int level) { + return getConfig().knockbackBase + (getLevelPercent(level) * getConfig().knockbackFactor); + } + + private int getCooldownTicks(int level) { + return Math.max(10, (int) Math.round(getConfig().cooldownTicksBase - (getLevelPercent(level) * getConfig().cooldownTicksFactor))); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + UUID id = p.getUniqueId(); + int level = getActiveLevel(p); + if (level <= 0) { + primedState.remove(id); + primedTrailNextAt.remove(id); + continue; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + boolean primed = isChargeReady(p); + boolean wasPrimed = primedState.getOrDefault(id, false); + + if (primed) { + long nextTrail = primedTrailNextAt.getOrDefault(id, 0L); + if (areParticlesEnabled() && now >= nextTrail) { + p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation().add(0, 0.2, 0), 2, 0.2, 0.05, 0.2, 0.02); + primedTrailNextAt.put(id, now + Math.max(25L, getConfig().primedTrailIntervalMillis)); + } + if (!wasPrimed) { + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASEDRUM, 0.55f, 1.15f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CRIT, p.getLocation().add(0, 1.0, 0), 8, 0.2, 0.3, 0.2, 0.1); + } + } + } else { + primedTrailNextAt.remove(id); + } - @NoArgsConstructor - @ConfigDescription("Sprint into enemies with fists or a shield to deal impact damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageBase = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 4.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackBase = 0.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double knockbackFactor = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksBase = 80; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double cooldownTicksFactor = 50; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Velocity Squared for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumVelocitySquared = 0.18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerDamage = 3.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between primed trail particle pulses while charge is ready.", impact = "Lower values increase visual frequency and particle cost; higher values reduce trail spam.") - long primedTrailIntervalMillis = 120; + primedState.put(id, primed); } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sprint into enemies with fists or a shield to deal impact damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageBase = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 4.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackBase = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Knockback Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double knockbackFactor = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Base for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksBase = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Ticks Factor for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownTicksFactor = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Velocity Squared for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumVelocitySquared = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage for the Unarmed Battering Charge adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDamage = 3.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between primed trail particle pulses while charge is ready.", impact = "Lower values increase visual frequency and particle cost; higher values reduce trail spam.") + long primedTrailIntervalMillis = 120; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java index f15edc016..885c77fa2 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedComboChain.java @@ -25,10 +25,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -46,199 +46,199 @@ import java.util.UUID; public class UnarmedComboChain extends SimpleAdaptation { - private final Map combos = new java.util.concurrent.ConcurrentHashMap<>(); - - public UnarmedComboChain() { - super("unarmed-combo-chain"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("unarmed.combo_chain.description")); - setDisplayName(Localizer.dLocalize("unarmed.combo_chain.name")); - setIcon(Material.CHAINMAIL_BOOTS); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(1800); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_INGOT) - .key("challenge_unarmed_combo_5k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_unarmed_combo_5k", "unarmed.combo-chain.total-combo-hits", 5000, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_unarmed_combo_10") - .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_10.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_10.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_ROD) - .key("challenge_unarmed_combo_25") - .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_25.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_25.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); + private final Map combos = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedComboChain() { + super("unarmed-combo-chain"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.combo_chain.description")); + setDisplayName(Localizer.dLocalize("unarmed.combo_chain.name")); + setIcon(Material.CHAINMAIL_BOOTS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1800); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_combo_5k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_unarmed_combo_5k", "unarmed.combo-chain.total-combo-hits", 5000, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_unarmed_combo_10") + .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_10.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_ROD) + .key("challenge_unarmed_combo_25") + .title(Localizer.dLocalize("advancement.challenge_unarmed_combo_25.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_combo_25.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxStacks(level) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getDamagePerStack(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getComboWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + getMaxStacks(level) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(getDamagePerStack(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore2")); - v.addLore(C.YELLOW + "* " + Form.duration(getComboWindowMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.combo_chain.lore3")); + Player p = attack.attacker(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (isMelee(hand)) { + combos.remove(p.getUniqueId()); + return; } - @EventHandler(priority = EventPriority.HIGHEST) - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } - - Player p = attack.attacker(); - ItemStack hand = p.getInventory().getItemInMainHand(); - if (isMelee(hand)) { - combos.remove(p.getUniqueId()); - return; - } - - long now = System.currentTimeMillis(); - int level = attack.level(); - ComboState state = combos.computeIfAbsent(p.getUniqueId(), id -> new ComboState()); - - if (now - state.lastHitMillis > getComboWindowMillis(level)) { - state.stacks = 0; - } - - state.lastHitMillis = now; - state.stacks = Math.min(getMaxStacks(level), state.stacks + 1); - - double bonus = state.stacks * getDamagePerStack(level); - e.setDamage(e.getDamage() + bonus); - playComboFeedback(p, e.getEntity().getLocation(), state.stacks, getMaxStacks(level)); - xp(p, bonus * getConfig().xpPerBonusDamage); - getPlayer(p).getData().addStat("unarmed.combo-chain.total-combo-hits", 1); - - // Special achievements: reach a 10-hit or 25-hit combo - if (state.stacks >= 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_unarmed_combo_10")) { - getPlayer(p).getAdvancementHandler().grant("challenge_unarmed_combo_10"); - } - if (state.stacks >= 25 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_unarmed_combo_25")) { - getPlayer(p).getAdvancementHandler().grant("challenge_unarmed_combo_25"); - } - } + long now = System.currentTimeMillis(); + int level = attack.level(); + ComboState state = combos.computeIfAbsent(p.getUniqueId(), id -> new ComboState()); - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - Action action = e.getAction(); - if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { - return; - } - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p) || isMelee(p.getInventory().getItemInMainHand())) { - return; - } - - ComboState state = combos.get(p.getUniqueId()); - if (state == null) { - return; - } - - long now = System.currentTimeMillis(); - if (now - state.lastHitMillis > getConfig().missResetGraceMillis) { - combos.remove(p.getUniqueId()); - } + if (now - state.lastHitMillis > getComboWindowMillis(level)) { + state.stacks = 0; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - combos.remove(e.getPlayer().getUniqueId()); - } + state.lastHitMillis = now; + state.stacks = Math.min(getMaxStacks(level), state.stacks + 1); - private int getMaxStacks(int level) { - return Math.max(1, (int) Math.round(getConfig().maxStacksBase + (getLevelPercent(level) * getConfig().maxStacksFactor))); - } + double bonus = state.stacks * getDamagePerStack(level); + e.setDamage(e.getDamage() + bonus); + playComboFeedback(p, e.getEntity().getLocation(), state.stacks, getMaxStacks(level)); + xp(p, bonus * getConfig().xpPerBonusDamage); + getPlayer(p).getData().addStat("unarmed.combo-chain.total-combo-hits", 1); - private double getDamagePerStack(int level) { - return getConfig().damagePerStackBase + (getLevelPercent(level) * getConfig().damagePerStackFactor); + // Special achievements: reach a 10-hit or 25-hit combo + if (state.stacks >= 10 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_unarmed_combo_10")) { + getPlayer(p).getAdvancementHandler().grant("challenge_unarmed_combo_10"); } - - private long getComboWindowMillis(int level) { - return Math.max(250, (long) Math.round(getConfig().comboWindowMillisBase + (getLevelPercent(level) * getConfig().comboWindowMillisFactor))); + if (state.stacks >= 25 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_unarmed_combo_25")) { + getPlayer(p).getAdvancementHandler().grant("challenge_unarmed_combo_25"); } + } - private void playComboFeedback(Player p, org.bukkit.Location hitLocation, int stacks, int maxStacks) { - float pitch = Math.min(2.0f, 0.85f + (stacks * 0.09f)); - if (stacks >= maxStacks) { - SoundPlayer.of(p.getWorld()).play(hitLocation, Sound.BLOCK_ANVIL_PLACE, 0.55f, 1.7f); - if (areParticlesEnabled()) { - p.spawnParticle(Particle.TOTEM_OF_UNDYING, hitLocation.clone().add(0, 1, 0), 5, 0.2, 0.4, 0.2, 0.05); - } - return; - } - - SoundPlayer.of(p.getWorld()).play(hitLocation, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.55f, pitch); - if (areParticlesEnabled()) { - p.spawnParticle(Particle.CRIT, hitLocation.clone().add(0, 0.9, 0), 6 + Math.min(16, stacks * 2), 0.22, 0.34, 0.22, 0.1); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; } - @Override - public void onTick() { - + Player p = e.getPlayer(); + if (!hasActiveAdaptation(p) || isMelee(p.getInventory().getItemInMainHand())) { + return; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + ComboState state = combos.get(p.getUniqueId()); + if (state == null) { + return; } - @Override - public boolean isPermanent() { - return getConfig().permanent; + long now = System.currentTimeMillis(); + if (now - state.lastHitMillis > getConfig().missResetGraceMillis) { + combos.remove(p.getUniqueId()); } - - @NoArgsConstructor - @ConfigDescription("Consecutive unarmed hits build combo stacks for increased punch damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxStacksBase = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxStacksFactor = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerStackBase = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagePerStackFactor = 0.85; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double comboWindowMillisBase = 1300; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double comboWindowMillisFactor = 1400; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Miss Reset Grace Millis for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long missResetGraceMillis = 280; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpPerBonusDamage = 4.1; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + combos.remove(e.getPlayer().getUniqueId()); + } + + private int getMaxStacks(int level) { + return Math.max(1, (int) Math.round(getConfig().maxStacksBase + (getLevelPercent(level) * getConfig().maxStacksFactor))); + } + + private double getDamagePerStack(int level) { + return getConfig().damagePerStackBase + (getLevelPercent(level) * getConfig().damagePerStackFactor); + } + + private long getComboWindowMillis(int level) { + return Math.max(250, (long) Math.round(getConfig().comboWindowMillisBase + (getLevelPercent(level) * getConfig().comboWindowMillisFactor))); + } + + private void playComboFeedback(Player p, org.bukkit.Location hitLocation, int stacks, int maxStacks) { + float pitch = Math.min(2.0f, 0.85f + (stacks * 0.09f)); + if (stacks >= maxStacks) { + SoundPlayer.of(p.getWorld()).play(hitLocation, Sound.BLOCK_ANVIL_PLACE, 0.55f, 1.7f); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.TOTEM_OF_UNDYING, hitLocation.clone().add(0, 1, 0), 5, 0.2, 0.4, 0.2, 0.05); + } + return; } - private static class ComboState { - private int stacks = 0; - private long lastHitMillis = 0L; + SoundPlayer.of(p.getWorld()).play(hitLocation, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.55f, pitch); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.CRIT, hitLocation.clone().add(0, 0.9, 0), 6 + Math.min(16, stacks * 2), 0.22, 0.34, 0.22, 0.1); } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Consecutive unarmed hits build combo stacks for increased punch damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxStacksBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Stacks Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxStacksFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerStackBase = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Per Stack Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagePerStackFactor = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Base for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double comboWindowMillisBase = 1300; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Combo Window Millis Factor for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double comboWindowMillisFactor = 1400; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Miss Reset Grace Millis for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long missResetGraceMillis = 280; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Damage for the Unarmed Combo Chain adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBonusDamage = 4.1; + } + + private static class ComboState { + private int stacks = 0; + private long lastHitMillis = 0L; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java index ef00a0582..9f15ace0a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGlassCannon.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -37,118 +37,118 @@ import org.bukkit.event.entity.EntityDeathEvent; public class UnarmedGlassCannon extends SimpleAdaptation { - public UnarmedGlassCannon() { - super("unarmed-glass-cannon"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("unarmed.glass_cannon.description")); - setDisplayName(Localizer.dLocalize("unarmed.glass_cannon.name")); - setIcon(Material.POINTED_DRIPSTONE); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(4544); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLASS) - .key("challenge_unarmed_glass_100") - .title(Localizer.dLocalize("advancement.challenge_unarmed_glass_100.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_glass_100.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GLASS_PANE) - .key("challenge_unarmed_glass_500") - .title(Localizer.dLocalize("advancement.challenge_unarmed_glass_500.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_glass_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_glass_100", "unarmed.glass-cannon.naked-kills", 100, 300); - registerMilestone("challenge_unarmed_glass_500", "unarmed.glass-cannon.naked-kills", 500, 1000); + public UnarmedGlassCannon() { + super("unarmed-glass-cannon"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.glass_cannon.description")); + setDisplayName(Localizer.dLocalize("unarmed.glass_cannon.name")); + setIcon(Material.POINTED_DRIPSTONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4544); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLASS) + .key("challenge_unarmed_glass_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_glass_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_glass_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GLASS_PANE) + .key("challenge_unarmed_glass_500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_glass_500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_glass_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_glass_100", "unarmed.glass-cannon.naked-kills", 100, 300); + registerMilestone("challenge_unarmed_glass_500", "unarmed.glass-cannon.naked-kills", 500, 1000); + } + + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getConfig().maxDamageFactor + (level * getConfig().maxDamagePerLevelMultiplier)) + C.GRAY + " " + Localizer.dLocalize("unarmed.glass_cannon.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(level * getConfig().perLevelBonusMultiplier) + C.GRAY + " " + Localizer.dLocalize("unarmed.glass_cannon.lore2")); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; } + Player p = attack.attacker(); - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + (getConfig().maxDamageFactor + (level * getConfig().maxDamagePerLevelMultiplier)) + C.GRAY + " " + Localizer.dLocalize("unarmed.glass_cannon.lore1")); - v.addLore(C.GREEN + "+ " + Form.f(level * getConfig().perLevelBonusMultiplier) + C.GRAY + " " + Localizer.dLocalize("unarmed.glass_cannon.lore2")); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } - - Player p = attack.attacker(); - - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } + double armor = getArmorValue(p); + double damage = e.getDamage(); - double armor = getArmorValue(p); - double damage = e.getDamage(); - - if (armor == 0) { - e.setDamage(damage * getConfig().maxDamageFactor); - } else { - e.setDamage(damage - (damage * armor)); - } + if (armor == 0) { + e.setDamage(damage * getConfig().maxDamageFactor); + } else { + e.setDamage(damage - (damage * armor)); } + } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (!(e.getEntity() instanceof LivingEntity victim)) { - return; - } - if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Player p - && hasActiveAdaptation(p) - && !isTool(p.getInventory().getItemInMainHand()) - && !isTool(p.getInventory().getItemInOffHand()) - && getArmorValue(p) == 0) { - getPlayer(p).getData().addStat("unarmed.glass-cannon.naked-kills", 1); - } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim)) { + return; } - - @Override - public void onTick() { - - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Bonus unarmed damage the lower your armor value is.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.425; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Per Level Bonus Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double perLevelBonusMultiplier = 0.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Factor for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxDamageFactor = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Per Level Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxDamagePerLevelMultiplier = 0.15; + if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Player p + && hasActiveAdaptation(p) + && !isTool(p.getInventory().getItemInMainHand()) + && !isTool(p.getInventory().getItemInOffHand()) + && getArmorValue(p) == 0) { + getPlayer(p).getData().addStat("unarmed.glass-cannon.naked-kills", 1); } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bonus unarmed damage the lower your armor value is.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.425; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Per Level Bonus Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double perLevelBonusMultiplier = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Factor for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxDamageFactor = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Damage Per Level Multiplier for the Unarmed Glass Cannon adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxDamagePerLevelMultiplier = 0.15; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java index fd6bd58eb..2b7778ece 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPower.java @@ -24,9 +24,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -37,112 +37,112 @@ import org.bukkit.event.entity.EntityDeathEvent; public class UnarmedPower extends SimpleAdaptation { - public UnarmedPower() { - super("unarmed-power"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("unarmed.power.description")); - setDisplayName(Localizer.dLocalize("unarmed.power.name")); - setIcon(Material.IRON_INGOT); - setBaseCost(getConfig().baseCost); - setMaxLevel(getConfig().maxLevel); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(4444); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_INGOT) - .key("challenge_unarmed_power_500") - .title(Localizer.dLocalize("advancement.challenge_unarmed_power_500.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_power_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND) - .key("challenge_unarmed_power_5k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_power_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_power_5k.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_power_500", "unarmed.power.unarmed-kills", 500, 400); - registerMilestone("challenge_unarmed_power_5k", "unarmed.power.unarmed-kills", 5000, 1500); - } + public UnarmedPower() { + super("unarmed-power"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.power.description")); + setDisplayName(Localizer.dLocalize("unarmed.power.name")); + setIcon(Material.IRON_INGOT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4444); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_power_500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_power_500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_power_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_power_5k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_power_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_power_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_power_500", "unarmed.power.unarmed-kills", 500, 400); + registerMilestone("challenge_unarmed_power_5k", "unarmed.power.unarmed-kills", 5000, 1500); + } - @Override - public void addStats(int level, Element v) { - v.addLore(C.GREEN + "+ " + Form.pc(getUnarmedDamage(level), 0) + C.GRAY + Localizer.dLocalize("unarmed.power.lore1")); + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getUnarmedDamage(level), 0) + C.GRAY + Localizer.dLocalize("unarmed.power.lore1")); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + double factor = getLevelPercent(attack.level()); - Player p = attack.attacker(); - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } - double factor = getLevelPercent(attack.level()); + if (factor <= 0) { + return; + } + e.setDamage(e.getDamage() * (1 + getUnarmedDamage(attack.level()))); + xp(p, 0.321 * factor * e.getDamage(), "unarmed-hit"); - if (factor <= 0) { - return; - } - e.setDamage(e.getDamage() * (1 + getUnarmedDamage(attack.level()))); - xp(p, 0.321 * factor * e.getDamage(), "unarmed-hit"); + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim)) { + return; } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (!(e.getEntity() instanceof LivingEntity victim)) { - return; - } - if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Player p - && hasActiveAdaptation(p) - && !isTool(p.getInventory().getItemInMainHand()) - && !isTool(p.getInventory().getItemInOffHand())) { - getPlayer(p).getData().addStat("unarmed.power.unarmed-kills", 1); - } + if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Player p + && hasActiveAdaptation(p) + && !isTool(p.getInventory().getItemInMainHand()) + && !isTool(p.getInventory().getItemInOffHand())) { + getPlayer(p).getData().addStat("unarmed.power.unarmed-kills", 1); } + } - private double getUnarmedDamage(int level) { - return getLevelPercent(level) * getConfig().damageFactor; - } + private double getUnarmedDamage(int level) { + return getLevelPercent(level) * getConfig().damageFactor; + } - @Override - public void onTick() { + @Override + public void onTick() { - } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @Override - public boolean isPermanent() { - return getConfig().permanent; - } + @Override + public boolean isPermanent() { + return getConfig().permanent; + } - @NoArgsConstructor - @ConfigDescription("Improved base unarmed damage.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") - int maxLevel = 7; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.425; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Power adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 2.57; - } + @NoArgsConstructor + @ConfigDescription("Improved base unarmed damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.425; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Power adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 2.57; + } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java index 7aad04d5a..17fa16dc5 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSuckerPunch.java @@ -24,10 +24,10 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.config.ConfigDescription; import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Particle; @@ -40,139 +40,139 @@ import org.bukkit.event.entity.EntityDeathEvent; public class UnarmedSuckerPunch extends SimpleAdaptation { - public UnarmedSuckerPunch() { - super("unarmed-sucker-punch"); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("unarmed.sucker_punch.description")); - setDisplayName(Localizer.dLocalize("unarmed.sucker_punch.name")); - setIcon(Material.OBSIDIAN); - setBaseCost(getConfig().baseCost); - setInitialCost(getConfig().initialCost); - setCostFactor(getConfig().costFactor); - setInterval(4944); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_INGOT) - .key("challenge_unarmed_sucker_500") - .title(Localizer.dLocalize("advancement.challenge_unarmed_sucker_500.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_sucker_500.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_unarmed_sucker_500", "unarmed.sucker-punch.sucker-punches", 500, 400); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND) - .key("challenge_unarmed_knockout") - .title(Localizer.dLocalize("advancement.challenge_unarmed_knockout.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_knockout.description")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()); - registerMilestone("challenge_unarmed_knockout", "unarmed.sucker-punch.one-punch-kills", 50, 1000); + public UnarmedSuckerPunch() { + super("unarmed-sucker-punch"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.sucker_punch.description")); + setDisplayName(Localizer.dLocalize("unarmed.sucker_punch.name")); + setIcon(Material.OBSIDIAN); + setBaseCost(getConfig().baseCost); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4944); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_sucker_500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_sucker_500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_sucker_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_unarmed_sucker_500", "unarmed.sucker-punch.sucker-punches", 500, 400); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_knockout") + .title(Localizer.dLocalize("advancement.challenge_unarmed_knockout.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_knockout.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_unarmed_knockout", "unarmed.sucker-punch.one-punch-kills", 50, 1000); + } + + + @Override + public void addStats(int level, Element v) { + double f = getLevelPercent(level); + double d = getDamage(f); + v.addLore(C.GREEN + "+ " + Form.pc(d, 0) + C.GRAY + " " + Localizer.dLocalize("unarmed.sucker_punch.lore1")); + v.addLore(C.GRAY + Localizer.dLocalize("unarmed.sucker_punch.lore2")); + } + + private double getDamage(double f) { + return getConfig().baseDamage + (f * getConfig().damageFactor); + } + + @EventHandler + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; } - - @Override - public void addStats(int level, Element v) { - double f = getLevelPercent(level); - double d = getDamage(f); - v.addLore(C.GREEN + "+ " + Form.pc(d, 0) + C.GRAY + " " + Localizer.dLocalize("unarmed.sucker_punch.lore1")); - v.addLore(C.GRAY + Localizer.dLocalize("unarmed.sucker_punch.lore2")); + Player p = attack.attacker(); + if (p.getInventory().getItemInMainHand().getType() != Material.AIR && p.getInventory().getItemInOffHand().getType() != Material.AIR) { + return; } + double factor = getLevelPercent(attack.level()); - private double getDamage(double f) { - return getConfig().baseDamage + (f * getConfig().damageFactor); + if (!p.isSprinting()) { + return; } - @EventHandler - public void on(EntityDamageByEntityEvent e) { - var attack = resolveAttackContext(e); - if (attack == null) { - return; - } - - Player p = attack.attacker(); - if (p.getInventory().getItemInMainHand().getType() != Material.AIR && p.getInventory().getItemInOffHand().getType() != Material.AIR) { - return; - } - double factor = getLevelPercent(attack.level()); - - if (!p.isSprinting()) { - return; - } - - if (factor <= 0) { - return; - } - - if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { - return; - } - - e.setDamage(e.getDamage() * getDamage(factor)); - SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); - spw.play(e.getEntity().getLocation(), Sound.ENTITY_PLAYER_ATTACK_STRONG, 1f, 1.8f); - spw.play(e.getEntity().getLocation(), Sound.BLOCK_BASALT_BREAK, 1f, 0.6f); - xp(p, 6.221 * e.getDamage(), "sucker-punch"); - getPlayer(p).getData().addStat("unarmed.sucker-punch.sucker-punches", 1); - if (e.getDamage() > 5) { - xp(p, 0.42 * e.getDamage(), "bonus-damage"); - if (areParticlesEnabled()) { - e.getEntity().getWorld().spawnParticle(Particle.FLASH, e.getEntity().getLocation(), 1); - } - } + if (factor <= 0) { + return; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void on(EntityDeathEvent e) { - if (!(e.getEntity() instanceof LivingEntity victim)) { - return; - } - if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg - && dmg.getDamager() instanceof Player p - && hasActiveAdaptation(p) - && p.isSprinting() - && !isTool(p.getInventory().getItemInMainHand()) - && !isTool(p.getInventory().getItemInOffHand())) { - // One-punch kill: entity was at full health before the killing blow - if (victim.getMaxHealth() <= dmg.getFinalDamage()) { - getPlayer(p).getData().addStat("unarmed.sucker-punch.one-punch-kills", 1); - } - } + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; } - @Override - public void onTick() { - + e.setDamage(e.getDamage() * getDamage(factor)); + SoundPlayer spw = SoundPlayer.of(e.getEntity().getWorld()); + spw.play(e.getEntity().getLocation(), Sound.ENTITY_PLAYER_ATTACK_STRONG, 1f, 1.8f); + spw.play(e.getEntity().getLocation(), Sound.BLOCK_BASALT_BREAK, 1f, 0.6f); + xp(p, 6.221 * e.getDamage(), "sucker-punch"); + getPlayer(p).getData().addStat("unarmed.sucker-punch.sucker-punches", 1); + if (e.getDamage() > 5) { + xp(p, 0.42 * e.getDamage(), "bonus-damage"); + if (areParticlesEnabled()) { + e.getEntity().getWorld().spawnParticle(Particle.FLASH, e.getEntity().getLocation(), 1); + } } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim)) { + return; } - - @Override - public boolean isPermanent() { - return getConfig().permanent; - } - - @NoArgsConstructor - @ConfigDescription("Sprint punches deal extra damage based on your speed.") - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") - boolean permanent = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Unarmed Sucker Punch adaptation.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") - int baseCost = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") - int initialCost = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") - double costFactor = 0.225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseDamage = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageFactor = 0.55; + if (victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg + && dmg.getDamager() instanceof Player p + && hasActiveAdaptation(p) + && p.isSprinting() + && !isTool(p.getInventory().getItemInMainHand()) + && !isTool(p.getInventory().getItemInOffHand())) { + // One-punch kill: entity was at full health before the killing blow + if (victim.getMaxHealth() <= dmg.getFinalDamage()) { + getPlayer(p).getData().addStat("unarmed.sucker-punch.one-punch-kills", 1); + } } + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sprint punches deal extra damage based on your speed.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Unarmed Sucker Punch adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Damage for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseDamage = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Factor for the Unarmed Sucker Punch adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageFactor = 0.55; + } } diff --git a/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java b/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java index 10ba635de..53a9d59c2 100644 --- a/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java +++ b/src/main/java/art/arcane/adapt/content/block/ScaffoldMatter.java @@ -30,32 +30,32 @@ import java.util.UUID; public class ScaffoldMatter extends RawMatter { - public ScaffoldMatter(int width, int height, int depth) { - super(width, height, depth, ScaffoldData.class); - } + public ScaffoldMatter(int width, int height, int depth) { + super(width, height, depth, ScaffoldData.class); + } - @Override - public void writeNode(ScaffoldData scaffoldData, DataOutputStream dos) throws IOException { - dos.writeLong(scaffoldData.getUuid().getMostSignificantBits()); - dos.writeLong(scaffoldData.getUuid().getLeastSignificantBits()); - dos.writeLong(scaffoldData.getTime()); - } + @Override + public void writeNode(ScaffoldData scaffoldData, DataOutputStream dos) throws IOException { + dos.writeLong(scaffoldData.getUuid().getMostSignificantBits()); + dos.writeLong(scaffoldData.getUuid().getLeastSignificantBits()); + dos.writeLong(scaffoldData.getTime()); + } - @Override - public ScaffoldData readNode(DataInputStream din) throws IOException { - return ScaffoldData.builder() - .uuid(new UUID(din.readLong(), din.readLong())) - .time(din.readLong()) - .build(); - } + @Override + public ScaffoldData readNode(DataInputStream din) throws IOException { + return ScaffoldData.builder() + .uuid(new UUID(din.readLong(), din.readLong())) + .time(din.readLong()) + .build(); + } - @Data - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class ScaffoldData { - private UUID uuid; - @Builder.Default - private long time = 0; - } + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ScaffoldData { + private UUID uuid; + @Builder.Default + private long time = 0; + } } diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java index 3386f8583..8dcbae91a 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationEvent.java @@ -24,26 +24,26 @@ import art.arcane.adapt.api.world.PlayerSkillLine; public class AdaptAdaptationEvent extends AdaptPlayerEvent { - private final Skill skill; - private final PlayerSkillLine playerSkill; - private final Adaptation adaptation; + private final Skill skill; + private final PlayerSkillLine playerSkill; + private final Adaptation adaptation; - public AdaptAdaptationEvent(boolean async, AdaptPlayer player, Adaptation adaptation) { - super(async, player); - this.adaptation = adaptation; - this.playerSkill = player.getSkillLine(adaptation.getSkill().getId()); - this.skill = adaptation.getSkill(); - } + public AdaptAdaptationEvent(boolean async, AdaptPlayer player, Adaptation adaptation) { + super(async, player); + this.adaptation = adaptation; + this.playerSkill = player.getSkillLine(adaptation.getSkill().getId()); + this.skill = adaptation.getSkill(); + } - public Skill getSkill() { - return skill; - } + public Skill getSkill() { + return skill; + } - public Adaptation getAdaptation() { - return adaptation; - } + public Adaptation getAdaptation() { + return adaptation; + } - public PlayerSkillLine getPlayerSkill() { - return playerSkill; - } + public PlayerSkillLine getPlayerSkill() { + return playerSkill; + } } diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java index c43a3f3e2..61d1812f5 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationTeleportEvent.java @@ -24,12 +24,12 @@ import org.bukkit.Location; public class AdaptAdaptationTeleportEvent extends AdaptAdaptationEvent { - @Getter - Location fromLocation, toLocation; + @Getter + Location fromLocation, toLocation; - public AdaptAdaptationTeleportEvent(boolean async, AdaptPlayer player, Adaptation adaptation, Location fromLocation, Location toLocation) { - super(async, player, adaptation); - this.fromLocation = fromLocation; - this.toLocation = toLocation; - } + public AdaptAdaptationTeleportEvent(boolean async, AdaptPlayer player, Adaptation adaptation, Location fromLocation, Location toLocation) { + super(async, player, adaptation); + this.fromLocation = fromLocation; + this.toLocation = toLocation; + } } diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java index 5a4221dde..94e8b53d5 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptAdaptationUseEvent.java @@ -22,7 +22,7 @@ import art.arcane.adapt.api.world.AdaptPlayer; public class AdaptAdaptationUseEvent extends AdaptAdaptationEvent { - public AdaptAdaptationUseEvent(boolean async, AdaptPlayer player, Adaptation adaptation) { - super(async, player, adaptation); - } + public AdaptAdaptationUseEvent(boolean async, AdaptPlayer player, Adaptation adaptation) { + super(async, player, adaptation); + } } diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java index 38c9e5b6a..41bd61a83 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptEvent.java @@ -25,35 +25,35 @@ import org.bukkit.event.HandlerList; public class AdaptEvent extends Event implements Cancellable { - private static final HandlerList HANDLERS = new HandlerList(); - private boolean canceled; + private static final HandlerList HANDLERS = new HandlerList(); + private boolean canceled; - public AdaptEvent(boolean async) { - super(async); - canceled = false; - } + public AdaptEvent(boolean async) { + super(async); + canceled = false; + } - public AdaptServer getServer() { - return Adapt.instance.getAdaptServer(); - } + public static HandlerList getHandlerList() { + return HANDLERS; + } - @Override - public boolean isCancelled() { - return canceled; - } + public AdaptServer getServer() { + return Adapt.instance.getAdaptServer(); + } - @Override - public void setCancelled(boolean b) { - canceled = b; - } + @Override + public boolean isCancelled() { + return canceled; + } - public static HandlerList getHandlerList() { - return HANDLERS; - } + @Override + public void setCancelled(boolean b) { + canceled = b; + } - @Override - public HandlerList getHandlers() { - return HANDLERS; - } + @Override + public HandlerList getHandlers() { + return HANDLERS; + } } diff --git a/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java b/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java index 42e75eef2..3b99ebc73 100644 --- a/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java +++ b/src/main/java/art/arcane/adapt/content/event/AdaptPlayerEvent.java @@ -21,14 +21,14 @@ import art.arcane.adapt.api.world.AdaptPlayer; public class AdaptPlayerEvent extends AdaptEvent { - private final AdaptPlayer player; + private final AdaptPlayer player; - public AdaptPlayerEvent(boolean async, AdaptPlayer player) { - super(async); - this.player = player; - } + public AdaptPlayerEvent(boolean async, AdaptPlayer player) { + super(async); + this.player = player; + } - public AdaptPlayer getPlayer() { - return player; - } + public AdaptPlayer getPlayer() { + return player; + } } diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index f0f957243..d826d6ad1 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -8,1708 +8,1502 @@ import art.arcane.adapt.service.ConfigInputSVC; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.UIElement; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.inventorygui.GuiEffects; import art.arcane.adapt.util.common.inventorygui.GuiLayout; import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.volmlib.util.data.MaterialBlock; -import art.arcane.volmlib.util.inventorygui.Element; -import art.arcane.volmlib.util.inventorygui.Window; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDocumentation; import art.arcane.adapt.util.config.TomlCodec; +import art.arcane.volmlib.util.data.MaterialBlock; +import art.arcane.volmlib.util.format.ColorFormatter; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.volmlib.util.inventorygui.Window; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.math.M; -import art.arcane.volmlib.util.format.ColorFormatter; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; -import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BooleanSupplier; public final class ConfigGui { - private static final String TAG_PREFIX = "config/adapt"; - private static final String SOURCE_TAG_CORE = "core-config"; - private static final String ROOT_CORE = "core"; - private static final String ROOT_CORE_GENERAL = ROOT_CORE + ".$general"; - private static final String ROOT_SKILLS = "skills"; - private static final String ROOT_ADAPTATIONS = "adaptations"; - private static final String ROOT_ADAPTATIONS_SKILLS = ROOT_ADAPTATIONS + ".$skills"; - private static final String ROOT_ADAPTATIONS_ALL = ROOT_ADAPTATIONS + ".$all"; - private static final Object WRITE_LOCK = new Object(); - private static final int MAX_VALUE_PREVIEW = 64; - private static final int PAGE_JUMP = 5; - private static final int CONFIG_CONTENT_ROWS = GuiLayout.MAX_ROWS - 1; - private static final long CLOSE_SUPPRESS_MS = 1200L; - private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; - private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); + private static final String TAG_PREFIX = "config/adapt"; + private static final String SOURCE_TAG_CORE = "core-config"; + private static final String ROOT_CORE = "core"; + private static final String ROOT_CORE_GENERAL = ROOT_CORE + ".$general"; + private static final String ROOT_SKILLS = "skills"; + private static final String ROOT_ADAPTATIONS = "adaptations"; + private static final String ROOT_ADAPTATIONS_SKILLS = ROOT_ADAPTATIONS + ".$skills"; + private static final String ROOT_ADAPTATIONS_ALL = ROOT_ADAPTATIONS + ".$all"; + private static final Object WRITE_LOCK = new Object(); + private static final int MAX_VALUE_PREVIEW = 64; + private static final int PAGE_JUMP = 5; + private static final int CONFIG_CONTENT_ROWS = GuiLayout.MAX_ROWS - 1; + private static final long CLOSE_SUPPRESS_MS = 1200L; + private static final int CLOSE_SUPPRESS_CLEAR_TICKS = 4; + private static final Map CLOSE_SUPPRESS_UNTIL = new ConcurrentHashMap<>(); - private ConfigGui() { - } + private ConfigGui() { + } - public static boolean canConfigure(Player player) { - return player != null && (player.isOp() || player.hasPermission("adapt.configurator")); - } + public static boolean canConfigure(Player player) { + return player != null && (player.isOp() || player.hasPermission("adapt.configurator")); + } - public static void open(Player player) { - open(player, "", 0); - } - - public static void open(Player player, String sectionPath) { - open(player, sectionPath, 0); - } + public static void open(Player player) { + open(player, "", 0); + } - public static void open(Player player, String sectionPath, int page) { - if (player == null) { - return; - } + public static void open(Player player, String sectionPath) { + open(player, sectionPath, 0); + } - if (!canConfigure(player)) { - Adapt.messagePlayer(player, C.RED + "You do not have permission to use the config menu."); - return; - } - - if (!J.isPrimaryThread()) { - String path = sectionPath; - int targetPage = page; - J.s(() -> open(player, path, targetPage)); - return; - } - - playPageTurn(player); - String safePath = normalizePath(sectionPath); - if (safePath.isBlank()) { - openRoot(player, page); - return; - } - - if (safePath.equals(ROOT_SKILLS)) { - openSkillIndex(player, page); - return; - } - - if (safePath.equals(ROOT_CORE)) { - openCoreIndex(player, page); - return; - } - - if (safePath.equals(ROOT_CORE_GENERAL)) { - openCoreGeneral(player, page); - return; - } - - if (safePath.equals(ROOT_ADAPTATIONS) || safePath.equals(ROOT_ADAPTATIONS_SKILLS)) { - openAdaptationSkillIndex(player, page); - return; - } - - if (safePath.equals(ROOT_ADAPTATIONS_ALL)) { - openAdaptationIndex(player, page); - return; - } - - if (safePath.startsWith(ROOT_ADAPTATIONS_SKILLS + ".")) { - String skillName = safePath.substring(ROOT_ADAPTATIONS_SKILLS.length() + 1); - openAdaptationIndexForSkill(player, skillName, page); - return; - } + public static void open(Player player, String sectionPath, int page) { + if (player == null) { + return; + } - SectionTarget target = resolveSectionTarget(safePath, false); - if (target == null || target.sectionObject() == null) { - Adapt.messagePlayer(player, C.RED + "Unable to open config section: " + C.WHITE + safePath); - return; - } + if (!canConfigure(player)) { + Adapt.messagePlayer(player, C.RED + "You do not have permission to use the config menu."); + return; + } - List entries = buildEntries(safePath, target.sectionObject(), target.sourceTag()); - openFieldEntries(player, safePath, entries, page); + if (!J.isPrimaryThread()) { + String path = sectionPath; + int targetPage = page; + J.s(() -> open(player, path, targetPage)); + return; } - public static void reopenFromTag(Player player, String tag) { - if (player == null) { - return; - } + playPageTurn(player); + String safePath = normalizePath(sectionPath); + if (safePath.isBlank()) { + openRoot(player, page); + return; + } - if (tag == null || tag.isBlank()) { - open(player); - return; - } + if (safePath.equals(ROOT_SKILLS)) { + openSkillIndex(player, page); + return; + } - if (!tag.startsWith(TAG_PREFIX)) { - open(player); - return; - } + if (safePath.equals(ROOT_CORE)) { + openCoreIndex(player, page); + return; + } - String path = ""; - if (tag.length() > TAG_PREFIX.length()) { - path = tag.substring(TAG_PREFIX.length()); - if (path.startsWith("/")) { - path = path.substring(1); - } - } + if (safePath.equals(ROOT_CORE_GENERAL)) { + openCoreGeneral(player, page); + return; + } - navigateTo(player, path, 0); + if (safePath.equals(ROOT_ADAPTATIONS) || safePath.equals(ROOT_ADAPTATIONS_SKILLS)) { + openAdaptationSkillIndex(player, page); + return; } - public static ParseResult parseInputValue(Class type, String raw) { - if (type == null) { - return ParseResult.fail("Unknown target type."); - } + if (safePath.equals(ROOT_ADAPTATIONS_ALL)) { + openAdaptationIndex(player, page); + return; + } - Class normalized = normalizeType(type); - String trimmed = raw == null ? "" : raw.trim(); - - try { - if (normalized == String.class) { - return ParseResult.ok(raw == null ? "" : raw); - } - - if (normalized == Character.class) { - if (trimmed.length() != 1) { - return ParseResult.fail("Expected exactly one character."); - } - return ParseResult.ok(trimmed.charAt(0)); - } - - if (normalized == Boolean.class) { - if (trimmed.equalsIgnoreCase("true") || trimmed.equalsIgnoreCase("yes") || trimmed.equalsIgnoreCase("on")) { - return ParseResult.ok(true); - } - if (trimmed.equalsIgnoreCase("false") || trimmed.equalsIgnoreCase("no") || trimmed.equalsIgnoreCase("off")) { - return ParseResult.ok(false); - } - return ParseResult.fail("Expected boolean value: true/false."); - } - - if (normalized.isEnum()) { - Object constant = parseEnumConstant(normalized, trimmed); - if (constant == null) { - return ParseResult.fail("Expected one of: " + enumConstants(normalized)); - } - return ParseResult.ok(constant); - } - - if (normalized == Integer.class) { - return ParseResult.ok(Integer.parseInt(trimmed)); - } - if (normalized == Long.class) { - return ParseResult.ok(Long.parseLong(trimmed)); - } - if (normalized == Double.class) { - double v = Double.parseDouble(trimmed); - if (!Double.isFinite(v)) { - return ParseResult.fail("Expected a finite number."); - } - return ParseResult.ok(v); - } - if (normalized == Float.class) { - float v = Float.parseFloat(trimmed); - if (!Float.isFinite(v)) { - return ParseResult.fail("Expected a finite number."); - } - return ParseResult.ok(v); - } - if (normalized == Short.class) { - return ParseResult.ok(Short.parseShort(trimmed)); - } - if (normalized == Byte.class) { - return ParseResult.ok(Byte.parseByte(trimmed)); - } - } catch (Throwable e) { - return ParseResult.fail("Invalid value for type " + typeName(type) + "."); - } + if (safePath.startsWith(ROOT_ADAPTATIONS_SKILLS + ".")) { + String skillName = safePath.substring(ROOT_ADAPTATIONS_SKILLS.length() + 1); + openAdaptationIndexForSkill(player, skillName, page); + return; + } - return ParseResult.fail("Unsupported type: " + typeName(type) + "."); + SectionTarget target = resolveSectionTarget(safePath, false); + if (target == null || target.sectionObject() == null) { + Adapt.messagePlayer(player, C.RED + "Unable to open config section: " + C.WHITE + safePath); + return; } - public static boolean applyAndSave(Player actor, String valuePath, Object value) { - String path = normalizePath(valuePath); - if (path.isBlank()) { - return false; - } + List entries = buildEntries(safePath, target.sectionObject(), target.sourceTag()); + openFieldEntries(player, safePath, entries, page); + } - synchronized (WRITE_LOCK) { - EditTarget target = resolveEditTarget(path); - if (target == null) { - if (actor != null) { - Adapt.messagePlayer(actor, C.RED + "Failed to set config value at " + C.WHITE + path); - } - return false; - } - - Object before = readPathValue(target.rootObject(), target.objectPath()); - String beforeToml = TomlCodec.toToml(target.rootObject(), target.sourceTag()); - - if (!setPathValue(target.rootObject(), target.objectPath(), value, true)) { - if (actor != null) { - Adapt.messagePlayer(actor, C.RED + "Failed to set config value at " + C.WHITE + path); - } - return false; - } - - try { - String updatedToml = TomlCodec.toToml(target.rootObject(), target.sourceTag()); - IO.writeAll(target.file(), updatedToml); - } catch (Throwable e) { - J.attempt(() -> IO.writeAll(target.file(), beforeToml)); - target.reload().getAsBoolean(); - if (actor != null) { - Adapt.messagePlayer(actor, C.RED + "Failed to persist config update: " + C.WHITE + e.getMessage()); - } - return false; - } - - if (!target.reload().getAsBoolean()) { - J.attempt(() -> IO.writeAll(target.file(), beforeToml)); - target.reload().getAsBoolean(); - if (actor != null) { - Adapt.messagePlayer(actor, C.RED + "Config reload failed. Reverted file changes."); - } - return false; - } - - target.afterReload().run(); - if (actor != null) { - Adapt.messagePlayer(actor, C.GREEN + "Updated " + C.WHITE + path - + C.GRAY + " [" + summarizeValue(before) + C.GRAY + " -> " + summarizeValue(value) + C.GRAY + "]"); - } - return true; - } + public static void reopenFromTag(Player player, String tag) { + if (player == null) { + return; } - public static void confirmAndApply(Player actor, String returnSectionPath, String valuePath, Object value) { - confirmAndApply(actor, returnSectionPath, 0, valuePath, value); + if (tag == null || tag.isBlank()) { + open(player); + return; } - public static void confirmAndApply(Player actor, String returnSectionPath, int returnPage, String valuePath, Object value) { - String path = normalizePath(valuePath); - if (path.isBlank()) { - return; - } + if (!tag.startsWith(TAG_PREFIX)) { + open(player); + return; + } - String section = normalizePath(returnSectionPath); - applyAndSave(actor, path, value); - navigateTo(actor, section, Math.max(0, returnPage)); + String path = ""; + if (tag.length() > TAG_PREFIX.length()) { + path = tag.substring(TAG_PREFIX.length()); + if (path.startsWith("/")) { + path = path.substring(1); + } } - public static String typeName(Class type) { - if (type == null) { - return "unknown"; - } + navigateTo(player, path, 0); + } - Class normalized = normalizeType(type); - if (normalized.isEnum()) { - return "enum"; - } - return normalized.getSimpleName().toLowerCase(Locale.ROOT); + public static ParseResult parseInputValue(Class type, String raw) { + return ConfigGuiValueCodec.parseInputValue(type, raw); + } + + public static boolean applyAndSave(Player actor, String valuePath, Object value) { + String path = normalizePath(valuePath); + if (path.isBlank()) { + return false; } - private static ElementDescriptor describe(Field field, Object value) { - Class type = normalizeType(field.getType()); - if (type == Boolean.class) { - return new ElementDescriptor(ElementKind.BOOLEAN, true); + synchronized (WRITE_LOCK) { + EditTarget target = resolveEditTarget(path); + if (target == null) { + if (actor != null) { + Adapt.messagePlayer(actor, C.RED + "Failed to set config value at " + C.WHITE + path); } + return false; + } - if (isNumericType(type)) { - return new ElementDescriptor(ElementKind.NUMBER, true); - } + Object before = readPathValue(target.rootObject(), target.objectPath()); + String beforeToml = TomlCodec.toToml(target.rootObject(), target.sourceTag()); - if (type == String.class || type == Character.class) { - return new ElementDescriptor(ElementKind.STRING, true); + if (!setPathValue(target.rootObject(), target.objectPath(), value, true)) { + if (actor != null) { + Adapt.messagePlayer(actor, C.RED + "Failed to set config value at " + C.WHITE + path); } + return false; + } - if (type.isEnum()) { - return new ElementDescriptor(ElementKind.ENUM, true); + try { + String updatedToml = TomlCodec.toToml(target.rootObject(), target.sourceTag()); + IO.writeAll(target.file(), updatedToml); + } catch (Throwable e) { + J.attempt(() -> IO.writeAll(target.file(), beforeToml)); + target.reload().getAsBoolean(); + if (actor != null) { + Adapt.messagePlayer(actor, C.RED + "Failed to persist config update: " + C.WHITE + e.getMessage()); } + return false; + } - if (Map.class.isAssignableFrom(type)) { - return new ElementDescriptor(ElementKind.MAP, false); + if (!target.reload().getAsBoolean()) { + J.attempt(() -> IO.writeAll(target.file(), beforeToml)); + target.reload().getAsBoolean(); + if (actor != null) { + Adapt.messagePlayer(actor, C.RED + "Config reload failed. Reverted file changes."); } + return false; + } - if (Collection.class.isAssignableFrom(type) || type.isArray()) { - return new ElementDescriptor(ElementKind.LIST, false); - } + target.afterReload().run(); + if (actor != null) { + Adapt.messagePlayer(actor, C.GREEN + "Updated " + C.WHITE + path + + C.GRAY + " [" + summarizeValue(before) + C.GRAY + " -> " + summarizeValue(value) + C.GRAY + "]"); + } + return true; + } + } - if (value != null || isSectionType(type)) { - return new ElementDescriptor(ElementKind.SECTION, false); - } + public static void confirmAndApply(Player actor, String returnSectionPath, String valuePath, Object value) { + confirmAndApply(actor, returnSectionPath, 0, valuePath, value); + } - return new ElementDescriptor(ElementKind.UNSUPPORTED, false); - } - - private static Element createElementForEntry(Player player, String sectionPath, int currentPage, FieldEntry entry) { - Material material = materialFor(entry); - String typePrefix = switch (entry.descriptor().kind()) { - case BOOLEAN -> C.GREEN + "[Boolean] "; - case NUMBER -> C.AQUA + "[Number] "; - case STRING -> C.YELLOW + "[Text] "; - case ENUM -> C.LIGHT_PURPLE + "[Enum] "; - case SECTION -> C.BLUE + "[Section] "; - case MAP -> C.GOLD + "[Map] "; - case LIST -> C.GOLD + "[List] "; - case UNSUPPORTED -> C.RED + "[Unsupported] "; - }; - String name = displayName(entry.field().getName()); - String value = summarizeValue(entry.value()); - - Element element = new UIElement("cfg-" + entry.path()) - .setMaterial(new MaterialBlock(material)) - .setName(typePrefix + C.WHITE + name); - element.addLore(C.GRAY + "Value: " + C.AQUA + value); - element.addLore(C.DARK_GRAY + "Path: " + entry.path()); - element.setProgress(1D); - if (entry.descriptor().kind() == ElementKind.BOOLEAN && Boolean.TRUE.equals(entry.value())) { - element.setEnchanted(true); - } + public static void confirmAndApply(Player actor, String returnSectionPath, int returnPage, String valuePath, Object value) { + String path = normalizePath(valuePath); + if (path.isBlank()) { + return; + } - if (entry.descriptor().kind() == ElementKind.SECTION && entry.value() != null) { - int nested = getSerializableFields(entry.value().getClass()).size(); - element.addLore(C.GRAY + "Contains " + C.WHITE + nested + C.GRAY + " setting" + (nested == 1 ? "" : "s")); - element.addLore(C.DARK_GRAY + "Category: " + sectionCategory(entry.field().getName())); - } + String section = normalizePath(returnSectionPath); + applyAndSave(actor, path, value); + navigateTo(actor, section, Math.max(0, returnPage)); + } - int docsShown = 0; - for (String line : entry.docs()) { - if (line == null || line.isBlank()) { - continue; - } - if (docsShown >= 3) { - element.addLore(C.DARK_GRAY + "..."); - break; - } - element.addLore(C.GRAY + line); - docsShown++; - } - - switch (entry.descriptor().kind()) { - case BOOLEAN -> { - element.addLore(Boolean.TRUE.equals(entry.value()) - ? C.GREEN + "State: Enabled" - : C.RED + "State: Disabled"); - element.addLore(C.GREEN + "Left click: toggle"); - element.onLeftClick((e) -> { - boolean toggled = !Boolean.TRUE.equals(entry.value()); - confirmAndApply(player, sectionPath, currentPage, entry.path(), toggled); - }); - } - case ENUM -> { - element.addLore(C.GREEN + "Left click: next value"); - element.addLore(C.GREEN + "Right click: previous value"); - element.onLeftClick((e) -> { - Object next = cycleEnum(entry.field().getType(), entry.value(), 1); - if (next != null) { - confirmAndApply(player, sectionPath, currentPage, entry.path(), next); - } - }); - element.onRightClick((e) -> { - Object previous = cycleEnum(entry.field().getType(), entry.value(), -1); - if (previous != null) { - confirmAndApply(player, sectionPath, currentPage, entry.path(), previous); - } - }); - } - case NUMBER, STRING -> { - element.addLore(C.YELLOW + "Left click: edit in chat"); - element.onLeftClick((e) -> { - ConfigInputSVC service = Adapt.service(ConfigInputSVC.class); - if (service == null) { - Adapt.messagePlayer(player, C.RED + "Config input service is unavailable."); - return; - } - - service.beginSession(player, entry.path(), sectionPath, currentPage, entry.field().getType(), displayName(entry.field().getName())); - }); - } - case SECTION -> { - element.addLore(C.GREEN + "Left click: open section"); - element.onLeftClick((e) -> navigateTo(player, entry.path(), 0)); - } - case MAP, LIST -> element.addLore(C.RED + "Read-only in Phase 1"); - case UNSUPPORTED -> element.addLore(C.RED + "Unsupported type"); - } + public static String typeName(Class type) { + return ConfigGuiValueCodec.typeName(type); + } + + private static ElementDescriptor describe(Field field, Object value) { + Class type = normalizeType(field.getType()); + if (type == Boolean.class) { + return new ElementDescriptor(ElementKind.BOOLEAN, true); + } + + if (isNumericType(type)) { + return new ElementDescriptor(ElementKind.NUMBER, true); + } + + if (type == String.class || type == Character.class) { + return new ElementDescriptor(ElementKind.STRING, true); + } + + if (type.isEnum()) { + return new ElementDescriptor(ElementKind.ENUM, true); + } + + if (Map.class.isAssignableFrom(type)) { + return new ElementDescriptor(ElementKind.MAP, false); + } + + if (Collection.class.isAssignableFrom(type) || type.isArray()) { + return new ElementDescriptor(ElementKind.LIST, false); + } + + if (value != null || isSectionType(type)) { + return new ElementDescriptor(ElementKind.SECTION, false); + } + + return new ElementDescriptor(ElementKind.UNSUPPORTED, false); + } + + private static Element createElementForEntry(Player player, String sectionPath, int currentPage, FieldEntry entry) { + Material material = materialFor(entry); + String typePrefix = switch (entry.descriptor().kind()) { + case BOOLEAN -> C.GREEN + "[Boolean] "; + case NUMBER -> C.AQUA + "[Number] "; + case STRING -> C.YELLOW + "[Text] "; + case ENUM -> C.LIGHT_PURPLE + "[Enum] "; + case SECTION -> C.BLUE + "[Section] "; + case MAP -> C.GOLD + "[Map] "; + case LIST -> C.GOLD + "[List] "; + case UNSUPPORTED -> C.RED + "[Unsupported] "; + }; + String name = displayName(entry.field().getName()); + String value = summarizeValue(entry.value()); + + Element element = new UIElement("cfg-" + entry.path()) + .setMaterial(new MaterialBlock(material)) + .setName(typePrefix + C.WHITE + name); + element.addLore(C.GRAY + "Value: " + C.AQUA + value); + element.addLore(C.DARK_GRAY + "Path: " + entry.path()); + element.setProgress(1D); + if (entry.descriptor().kind() == ElementKind.BOOLEAN && Boolean.TRUE.equals(entry.value())) { + element.setEnchanted(true); + } + + if (entry.descriptor().kind() == ElementKind.SECTION && entry.value() != null) { + int nested = getSerializableFields(entry.value().getClass()).size(); + element.addLore(C.GRAY + "Contains " + C.WHITE + nested + C.GRAY + " setting" + (nested == 1 ? "" : "s")); + element.addLore(C.DARK_GRAY + "Category: " + sectionCategory(entry.field().getName())); + } + + int docsShown = 0; + for (String line : entry.docs()) { + if (line == null || line.isBlank()) { + continue; + } + if (docsShown >= 3) { + element.addLore(C.DARK_GRAY + "..."); + break; + } + element.addLore(C.GRAY + line); + docsShown++; + } + + ElementKind kind = entry.descriptor().kind(); + if (kind == ElementKind.BOOLEAN) { + element.addLore(Boolean.TRUE.equals(entry.value()) + ? C.GREEN + "State: Enabled" + : C.RED + "State: Disabled"); + element.addLore(C.GREEN + "Left click: toggle"); + element.onLeftClick((e) -> { + boolean toggled = !Boolean.TRUE.equals(entry.value()); + confirmAndApply(player, sectionPath, currentPage, entry.path(), toggled); + }); + } else if (kind == ElementKind.ENUM) { + element.addLore(C.GREEN + "Left click: next value"); + element.addLore(C.GREEN + "Right click: previous value"); + element.onLeftClick((e) -> { + Object next = cycleEnum(entry.field().getType(), entry.value(), 1); + if (next != null) { + confirmAndApply(player, sectionPath, currentPage, entry.path(), next); + } + }); + element.onRightClick((e) -> { + Object previous = cycleEnum(entry.field().getType(), entry.value(), -1); + if (previous != null) { + confirmAndApply(player, sectionPath, currentPage, entry.path(), previous); + } + }); + } else if (kind == ElementKind.NUMBER || kind == ElementKind.STRING) { + element.addLore(C.YELLOW + "Left click: edit in chat"); + element.onLeftClick((e) -> { + ConfigInputSVC service = Adapt.service(ConfigInputSVC.class); + if (service == null) { + Adapt.messagePlayer(player, C.RED + "Config input service is unavailable."); + return; + } + + service.beginSession(player, entry.path(), sectionPath, currentPage, entry.field().getType(), displayName(entry.field().getName())); + }); + } else if (kind == ElementKind.SECTION) { + element.addLore(C.GREEN + "Left click: open section"); + element.onLeftClick((e) -> navigateTo(player, entry.path(), 0)); + } else if (kind == ElementKind.MAP || kind == ElementKind.LIST) { + element.addLore(C.RED + "Read-only in Phase 1"); + } else if (kind == ElementKind.UNSUPPORTED) { + element.addLore(C.RED + "Unsupported type"); + } + + return element; + } + + private static Material materialFor(FieldEntry entry) { + return switch (entry.descriptor().kind()) { + case BOOLEAN -> + Boolean.TRUE.equals(entry.value()) ? Material.LIME_DYE : Material.GRAY_DYE; + case NUMBER -> Material.CLOCK; + case STRING -> Material.NAME_TAG; + case ENUM -> Material.BOOK; + case SECTION -> materialForSection(entry.field().getName()); + case MAP -> Material.CHEST_MINECART; + case LIST -> Material.BARREL; + case UNSUPPORTED -> Material.BARRIER; + }; + } + + private static Material materialForSection(String name) { + String key = normalizeSortKey(name); + if (key.contains("gui") || key.contains("menu") || key.contains("display") || key.contains("hud")) { + return Material.BOOKSHELF; + } + if (key.contains("sound") || key.contains("audio")) { + return Material.JUKEBOX; + } + if (key.contains("lang") || key.contains("locale") || key.contains("translation")) { + return Material.WRITABLE_BOOK; + } + if (key.contains("sql") || key.contains("database") || key.contains("storage") || key.contains("mysql")) { + return Material.ENDER_CHEST; + } + if (key.contains("xp") || key.contains("level") || key.contains("knowledge") || key.contains("power")) { + return Material.EXPERIENCE_BOTTLE; + } + if (key.contains("world") || key.contains("biome") || key.contains("dimension") || key.contains("region")) { + return Material.GRASS_BLOCK; + } + if (key.contains("thread") || key.contains("tick") || key.contains("async") || key.contains("performance") || key.contains("cache")) { + return Material.REDSTONE; + } + if (key.contains("permission") || key.contains("blacklist") || key.contains("whitelist") || key.contains("security")) { + return Material.SHIELD; + } + if (key.contains("debug") || key.contains("dev") || key.contains("test") || key.contains("verbose")) { + return Material.SPYGLASS; + } + return Material.CHEST; + } + + private static String sectionCategory(String name) { + String key = normalizeSortKey(name); + if (key.contains("gui") || key.contains("menu") || key.contains("display") || key.contains("hud")) { + return "UI"; + } + if (key.contains("sound") || key.contains("audio")) { + return "Audio"; + } + if (key.contains("lang") || key.contains("locale") || key.contains("translation")) { + return "Localization"; + } + if (key.contains("sql") || key.contains("database") || key.contains("storage") || key.contains("mysql")) { + return "Storage"; + } + if (key.contains("xp") || key.contains("level") || key.contains("knowledge") || key.contains("power")) { + return "Progression"; + } + if (key.contains("world") || key.contains("biome") || key.contains("dimension") || key.contains("region")) { + return "World"; + } + if (key.contains("thread") || key.contains("tick") || key.contains("async") || key.contains("performance") || key.contains("cache")) { + return "Performance"; + } + if (key.contains("permission") || key.contains("blacklist") || key.contains("whitelist") || key.contains("security")) { + return "Access"; + } + if (key.contains("debug") || key.contains("dev") || key.contains("test") || key.contains("verbose")) { + return "Debug"; + } + return "General"; + } + + private static List buildEntries(String sectionPath, Object sectionObject, String sourceTag) { + List sections = new ArrayList<>(); + List values = new ArrayList<>(); + for (Field field : getSerializableFields(sectionObject.getClass())) { + Object value = getFieldValue(field, sectionObject); + String childPath = joinPath(sectionPath, field.getName()); + ElementDescriptor descriptor = describe(field, value); + List docs = ConfigDocumentation.buildFieldComments(sourceTag, childPath, field, value); + FieldEntry entry = new FieldEntry(field, childPath, value, descriptor, docs); + if (descriptor.kind() == ElementKind.SECTION) { + sections.add(entry); + } else { + values.add(entry); + } + } + + sections.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); + values.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); + sections.addAll(values); + return sections; + } + + private static List buildCoreGeneralEntries() { + List values = new ArrayList<>(); + Object root = AdaptConfig.get(); + for (Field field : getSerializableFields(root.getClass())) { + Object value = getFieldValue(field, root); + ElementDescriptor descriptor = describe(field, value); + if (descriptor.kind() == ElementKind.SECTION) { + continue; + } + + String childPath = joinPath(ROOT_CORE, field.getName()); + List docs = ConfigDocumentation.buildFieldComments(SOURCE_TAG_CORE, childPath, field, value); + values.add(new FieldEntry(field, childPath, value, descriptor, docs)); + } + + values.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); + return values; + } + + private static void openFieldEntries(Player player, String safePath, List entries, int page) { + GuiLayout.PagePlan plan = configPagePlan(entries.size()); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(entries.size(), start + plan.itemsPerPage()); + + UIWindow w = new UIWindow(Adapt.instance, player); + GuiTheme.apply(w, tagForSection(safePath)); + w.setViewportHeight(plan.rows()); + + if (entries.isEmpty()) { + w.setElement(0, 0, new UIElement("cfg-empty") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.GRAY + "No settings in this section")); + } else { + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; + } + + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + FieldEntry entry = entries.get(rowStart + i); + int pos = GuiLayout.centeredPosition(i, rowCount); + Element element = createElementForEntry(player, safePath, currentPage, entry); + reveal.add(new GuiEffects.Placement(pos, row, element)); + } + } + GuiEffects.applyReveal(w, reveal); + } + + int navRow = plan.rows() - 1; + applyPageControls(w, player, safePath, navRow, currentPage, plan.pageCount(), entries.size(), start, end); + if (!safePath.isBlank()) { + String parent = parentPath(safePath); + w.setElement(0, navRow, new UIElement("cfg-back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.GRAY + "Back") + .onLeftClick((e) -> navigateTo(player, parent, 0))); + } + addSectionOverview(w, navRow, safePath, entries, currentPage, plan.pageCount()); + + String titlePath = safePath.isBlank() ? "root" : safePath; + if (safePath.equals(ROOT_CORE_GENERAL)) { + titlePath = "core.general"; + } + if (titlePath.length() > 24) { + titlePath = "..." + titlePath.substring(titlePath.length() - 21); + } + w.setTitle(C.GRAY + "Configure: " + C.WHITE + titlePath); + w.onClosed((window) -> onGuiClosed(player, safePath)); + w.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } + + private static void openCoreIndex(Player player, int page) { + Object root = AdaptConfig.get(); + List entries = new ArrayList<>(); + int generalValues = 0; + for (Field field : getSerializableFields(root.getClass())) { + Object value = getFieldValue(field, root); + ElementDescriptor descriptor = describe(field, value); + if (descriptor.kind() == ElementKind.SECTION) { + int nested = value == null ? 0 : getSerializableFields(value.getClass()).size(); + entries.add(new SectionIndexEntry( + ROOT_CORE + "." + field.getName(), + displayName(field.getName()), + materialForSection(field.getName()), + "Open " + nested + " setting" + (nested == 1 ? "" : "s") + )); + } else { + generalValues++; + } + } + + if (generalValues > 0) { + entries.add(new SectionIndexEntry( + ROOT_CORE_GENERAL, + "General Settings", + Material.COMPARATOR, + "Open " + generalValues + " global option" + (generalValues == 1 ? "" : "s") + )); + } + + entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + openSectionIndex(player, ROOT_CORE, page, "Configure: core", entries); + } + + private static void openCoreGeneral(Player player, int page) { + openFieldEntries(player, ROOT_CORE_GENERAL, buildCoreGeneralEntries(), page); + } + + private static void openRoot(Player player, int page) { + List entries = new ArrayList<>(); + entries.add(new SectionIndexEntry(ROOT_ADAPTATIONS_SKILLS, "Adaptations", Material.NETHER_STAR, "Configure adaptation settings")); + entries.add(new SectionIndexEntry(ROOT_CORE, "Core", Material.COMPARATOR, "Configure global Adapt settings")); + entries.add(new SectionIndexEntry(ROOT_SKILLS, "Skills", Material.ENCHANTED_BOOK, "Configure skill settings")); + entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + openSectionIndex(player, "", page, "Configure Adapt", entries); + } + + private static void openSkillIndex(Player player, int page) { + List> skills = getLoadedSkills(); + List entries = new ArrayList<>(); + for (Skill skill : skills) { + if (skill == null) { + continue; + } + + entries.add(new SectionIndexEntry( + ROOT_SKILLS + "." + skill.getName(), + ColorFormatter.stripColor(skill.getDisplayName()), + skill.getIcon(), + "Configure " + skill.getName() + )); + } + + entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + openSectionIndex(player, ROOT_SKILLS, page, "Configure: skills", entries); + } + + private static void openAdaptationSkillIndex(Player player, int page) { + List entries = new ArrayList<>(); + entries.add(new SectionIndexEntry( + ROOT_ADAPTATIONS_ALL, + "All Adaptations (A-Z)", + Material.NETHER_STAR, + "Browse every adaptation alphabetically" + )); + + for (Skill skill : getLoadedSkills()) { + if (skill == null) { + continue; + } + + int adaptationCount = skill.getAdaptations() == null ? 0 : skill.getAdaptations().size(); + if (adaptationCount <= 0) { + continue; + } + + entries.add(new SectionIndexEntry( + ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), + ColorFormatter.stripColor(skill.getDisplayName()), + skill.getIcon(), + "Browse " + adaptationCount + " adaptation" + (adaptationCount == 1 ? "" : "s") + )); + } + + List head = new ArrayList<>(); + List skillEntries = new ArrayList<>(); + for (SectionIndexEntry entry : entries) { + if (ROOT_ADAPTATIONS_ALL.equals(entry.path())) { + head.add(entry); + } else { + skillEntries.add(entry); + } + } + skillEntries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + head.addAll(skillEntries); + openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS, page, "Configure: adaptations", head); + } + + private static void openAdaptationIndexForSkill(Player player, String skillName, int page) { + Skill skill = resolveSkill(skillName); + if (skill == null) { + Adapt.messagePlayer(player, C.RED + "Unknown skill for adaptation config: " + C.WHITE + skillName); + navigateTo(player, ROOT_ADAPTATIONS_SKILLS, 0); + return; + } + + List entries = new ArrayList<>(); + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation == null) { + continue; + } + entries.add(new SectionIndexEntry( + ROOT_ADAPTATIONS + "." + adaptation.getName(), + ColorFormatter.stripColor(adaptation.getDisplayName()), + adaptation.getIcon(), + "Open " + adaptation.getName() + )); + } + + entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), page, "Configure: " + ColorFormatter.stripColor(skill.getDisplayName()), entries); + } + + private static void openAdaptationIndex(Player player, int page) { + List entries = new ArrayList<>(); + for (Adaptation adaptation : getLoadedAdaptations()) { + if (adaptation == null || adaptation.getSkill() == null) { + continue; + } + + entries.add(new SectionIndexEntry( + ROOT_ADAPTATIONS + "." + adaptation.getName(), + ColorFormatter.stripColor(adaptation.getDisplayName()), + adaptation.getIcon(), + "Skill: " + adaptation.getSkill().getName() + )); + } + + entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); + openSectionIndex(player, ROOT_ADAPTATIONS_ALL, page, "Configure: all adaptations", entries); + } + + private static void openSectionIndex(Player player, String sectionPath, int page, String title, List entries) { + String safePath = normalizePath(sectionPath); + GuiLayout.PagePlan plan = configPagePlan(entries.size()); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(entries.size(), start + plan.itemsPerPage()); + + UIWindow w = new UIWindow(Adapt.instance, player); + GuiTheme.apply(w, tagForSection(safePath)); + w.setViewportHeight(plan.rows()); + + if (entries.isEmpty()) { + w.setElement(0, 0, new UIElement("cfg-empty") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.GRAY + "No entries")); + } else { + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; + } + + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + SectionIndexEntry entry = entries.get(rowStart + i); + int pos = GuiLayout.centeredPosition(i, rowCount); + Element element = new UIElement("cfg-index-" + entry.path()) + .setMaterial(new MaterialBlock(entry.material())) + .setName(C.WHITE + entry.displayName()) + .addLore(C.GRAY + entry.lore()) + .addLore(C.DARK_GRAY + "Path: " + entry.path()) + .setProgress(1D) + .onLeftClick((e) -> navigateTo(player, entry.path(), 0)); + reveal.add(new GuiEffects.Placement(pos, row, element)); + } + } + GuiEffects.applyReveal(w, reveal); + } + + int navRow = plan.rows() - 1; + applyPageControls(w, player, safePath, navRow, currentPage, plan.pageCount(), entries.size(), start, end); + if (!safePath.isBlank()) { + w.setElement(0, navRow, new UIElement("cfg-back") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.GRAY + "Back") + .onLeftClick((e) -> navigateTo(player, parentPath(safePath), 0))); + } + addIndexOverview(w, navRow, safePath, entries.size(), currentPage, plan.pageCount(), title); + + w.setTitle(C.GRAY + title); + w.onClosed((window) -> onGuiClosed(player, safePath)); + w.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } + + private static GuiLayout.PagePlan configPagePlan(int totalEntries) { + int items = Math.max(0, totalEntries); + int rows = GuiLayout.MAX_ROWS; + int contentRows = CONFIG_CONTENT_ROWS; + int itemsPerPage = contentRows * GuiLayout.WIDTH; + int pageCount = Math.max(1, (int) Math.ceil(items / (double) itemsPerPage)); + return new GuiLayout.PagePlan(rows, contentRows, true, itemsPerPage, pageCount); + } + + private static void applyPageControls( + Window window, + Player player, + String safePath, + int navRow, + int currentPage, + int pageCount, + int totalEntries, + int start, + int end + ) { + if (pageCount <= 1) { + return; + } + + int jumpBack = Math.max(0, currentPage - PAGE_JUMP); + int jumpForward = Math.min(pageCount - 1, currentPage + PAGE_JUMP); + + if (currentPage > 0) { + window.setElement(-4, navRow, new UIElement("cfg-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Left click: previous page") + .addLore(C.GRAY + "Right click: jump -" + PAGE_JUMP + " pages") + .onLeftClick((e) -> navigateTo(player, safePath, currentPage - 1)) + .onRightClick((e) -> navigateTo(player, safePath, jumpBack))); + window.setElement(-3, navRow, new UIElement("cfg-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> navigateTo(player, safePath, 0))); + } + + if (currentPage < pageCount - 1) { + window.setElement(4, navRow, new UIElement("cfg-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Left click: next page") + .addLore(C.GRAY + "Right click: jump +" + PAGE_JUMP + " pages") + .onLeftClick((e) -> navigateTo(player, safePath, currentPage + 1)) + .onRightClick((e) -> navigateTo(player, safePath, jumpForward))); + window.setElement(3, navRow, new UIElement("cfg-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> navigateTo(player, safePath, pageCount - 1))); + } + + int from = totalEntries <= 0 ? 0 : (start + 1); + int to = totalEntries <= 0 ? 0 : end; + window.setElement(-1, navRow, new UIElement("cfg-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + pageCount) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + totalEntries) + .setProgress(1D)); + } + + private static void addSectionOverview( + Window window, + int navRow, + String path, + List entries, + int currentPage, + int pageCount + ) { + int sections = 0; + int editable = 0; + for (FieldEntry entry : entries) { + if (entry.descriptor().kind() == ElementKind.SECTION) { + sections++; + } + if (entry.descriptor().editable()) { + editable++; + } + } + + String safePath = path == null || path.isBlank() ? "root" : path; + window.setElement(1, navRow, new UIElement("cfg-overview") + .setMaterial(new MaterialBlock(Material.BOOK)) + .setName(C.AQUA + "Overview") + .addLore(C.GRAY + "Path: " + C.WHITE + safePath) + .addLore(C.GRAY + "Sections: " + C.WHITE + sections) + .addLore(C.GRAY + "Editable: " + C.WHITE + editable) + .addLore(C.GRAY + "Entries: " + C.WHITE + entries.size()) + .setProgress(1D)); + + window.setElement(2, navRow, new UIElement("cfg-help") + .setMaterial(new MaterialBlock(Material.KNOWLEDGE_BOOK)) + .setName(C.GRAY + "Help") + .addLore(C.GRAY + "LMB: open/edit/toggle") + .addLore(C.GRAY + "RMB: enum prev / page jump") + .addLore(C.GRAY + "ESC: back to parent page") + .addLore(C.DARK_GRAY + "Page " + (currentPage + 1) + "/" + pageCount) + .setProgress(1D)); + } + + private static void addIndexOverview( + Window window, + int navRow, + String path, + int totalEntries, + int currentPage, + int pageCount, + String title + ) { + String safePath = path == null || path.isBlank() ? "root" : path; + window.setElement(1, navRow, new UIElement("cfg-index-overview") + .setMaterial(new MaterialBlock(Material.BOOK)) + .setName(C.AQUA + "Directory") + .addLore(C.GRAY + "Path: " + C.WHITE + safePath) + .addLore(C.GRAY + "Entries: " + C.WHITE + totalEntries) + .addLore(C.GRAY + "Page: " + C.WHITE + (currentPage + 1) + "/" + pageCount) + .addLore(C.DARK_GRAY + title) + .setProgress(1D)); + + window.setElement(2, navRow, new UIElement("cfg-index-help") + .setMaterial(new MaterialBlock(Material.KNOWLEDGE_BOOK)) + .setName(C.GRAY + "Navigation") + .addLore(C.GRAY + "LMB: open section") + .addLore(C.GRAY + "RMB on arrows: jump pages") + .addLore(C.GRAY + "ESC: back to parent page") + .setProgress(1D)); + } + + private static String tagForSection(String sectionPath) { + String path = normalizePath(sectionPath); + if (path.isBlank()) { + return TAG_PREFIX; + } + return TAG_PREFIX + "/" + path; + } + + private static String normalizePath(String path) { + if (path == null) { + return ""; + } + + String normalized = path.trim(); + while (normalized.startsWith(".")) { + normalized = normalized.substring(1); + } + while (normalized.endsWith(".")) { + normalized = normalized.substring(0, normalized.length() - 1); + } + return normalized; + } + + private static String parentPath(String path) { + String normalized = normalizePath(path); + if (normalized.isBlank()) { + return ""; + } + + if (normalized.equals(ROOT_ADAPTATIONS) || normalized.equals(ROOT_ADAPTATIONS_SKILLS)) { + return ""; + } + + if (normalized.equals(ROOT_CORE_GENERAL)) { + return ROOT_CORE; + } + + if (normalized.equals(ROOT_ADAPTATIONS_ALL)) { + return ROOT_ADAPTATIONS_SKILLS; + } + + if (normalized.startsWith(ROOT_ADAPTATIONS_SKILLS + ".")) { + return ROOT_ADAPTATIONS_SKILLS; + } + + int dot = normalized.lastIndexOf('.'); + if (dot < 0) { + return ""; + } + return normalized.substring(0, dot); + } - return element; + private static String joinPath(String base, String child) { + String left = normalizePath(base); + if (left.isBlank()) { + return child; } + return left + "." + child; + } - private static Material materialFor(FieldEntry entry) { - return switch (entry.descriptor().kind()) { - case BOOLEAN -> Boolean.TRUE.equals(entry.value()) ? Material.LIME_DYE : Material.GRAY_DYE; - case NUMBER -> Material.CLOCK; - case STRING -> Material.NAME_TAG; - case ENUM -> Material.BOOK; - case SECTION -> materialForSection(entry.field().getName()); - case MAP -> Material.CHEST_MINECART; - case LIST -> Material.BARREL; - case UNSUPPORTED -> Material.BARRIER; - }; + private static Object resolveSectionObject(Object root, String sectionPath, boolean createMissing) { + if (root == null) { + return null; } - private static Material materialForSection(String name) { - String key = normalizeSortKey(name); - if (key.contains("gui") || key.contains("menu") || key.contains("display") || key.contains("hud")) { - return Material.BOOKSHELF; - } - if (key.contains("sound") || key.contains("audio")) { - return Material.JUKEBOX; - } - if (key.contains("lang") || key.contains("locale") || key.contains("translation")) { - return Material.WRITABLE_BOOK; - } - if (key.contains("sql") || key.contains("database") || key.contains("storage") || key.contains("mysql")) { - return Material.ENDER_CHEST; - } - if (key.contains("xp") || key.contains("level") || key.contains("knowledge") || key.contains("power")) { - return Material.EXPERIENCE_BOTTLE; - } - if (key.contains("world") || key.contains("biome") || key.contains("dimension") || key.contains("region")) { - return Material.GRASS_BLOCK; - } - if (key.contains("thread") || key.contains("tick") || key.contains("async") || key.contains("performance") || key.contains("cache")) { - return Material.REDSTONE; - } - if (key.contains("permission") || key.contains("blacklist") || key.contains("whitelist") || key.contains("security")) { - return Material.SHIELD; - } - if (key.contains("debug") || key.contains("dev") || key.contains("test") || key.contains("verbose")) { - return Material.SPYGLASS; - } - return Material.CHEST; + String normalized = normalizePath(sectionPath); + if (normalized.isBlank()) { + return root; } - private static String sectionCategory(String name) { - String key = normalizeSortKey(name); - if (key.contains("gui") || key.contains("menu") || key.contains("display") || key.contains("hud")) { - return "UI"; - } - if (key.contains("sound") || key.contains("audio")) { - return "Audio"; - } - if (key.contains("lang") || key.contains("locale") || key.contains("translation")) { - return "Localization"; - } - if (key.contains("sql") || key.contains("database") || key.contains("storage") || key.contains("mysql")) { - return "Storage"; - } - if (key.contains("xp") || key.contains("level") || key.contains("knowledge") || key.contains("power")) { - return "Progression"; - } - if (key.contains("world") || key.contains("biome") || key.contains("dimension") || key.contains("region")) { - return "World"; - } - if (key.contains("thread") || key.contains("tick") || key.contains("async") || key.contains("performance") || key.contains("cache")) { - return "Performance"; - } - if (key.contains("permission") || key.contains("blacklist") || key.contains("whitelist") || key.contains("security")) { - return "Access"; - } - if (key.contains("debug") || key.contains("dev") || key.contains("test") || key.contains("verbose")) { - return "Debug"; - } - return "General"; - } - - private static List buildEntries(String sectionPath, Object sectionObject, String sourceTag) { - List sections = new ArrayList<>(); - List values = new ArrayList<>(); - for (Field field : getSerializableFields(sectionObject.getClass())) { - Object value = getFieldValue(field, sectionObject); - String childPath = joinPath(sectionPath, field.getName()); - ElementDescriptor descriptor = describe(field, value); - List docs = ConfigDocumentation.buildFieldComments(sourceTag, childPath, field, value); - FieldEntry entry = new FieldEntry(field, childPath, value, descriptor, docs); - if (descriptor.kind() == ElementKind.SECTION) { - sections.add(entry); - } else { - values.add(entry); - } - } + Object current = root; + for (String segment : normalized.split("\\Q.\\E")) { + Field field = findField(current.getClass(), segment); + if (field == null) { + return null; + } - sections.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); - values.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); - sections.addAll(values); - return sections; - } - - private static List buildCoreGeneralEntries() { - List values = new ArrayList<>(); - Object root = AdaptConfig.get(); - for (Field field : getSerializableFields(root.getClass())) { - Object value = getFieldValue(field, root); - ElementDescriptor descriptor = describe(field, value); - if (descriptor.kind() == ElementKind.SECTION) { - continue; - } - - String childPath = joinPath(ROOT_CORE, field.getName()); - List docs = ConfigDocumentation.buildFieldComments(SOURCE_TAG_CORE, childPath, field, value); - values.add(new FieldEntry(field, childPath, value, descriptor, docs)); + Object next = getFieldValue(field, current); + if (next == null && createMissing) { + next = instantiate(field.getType()); + if (next == null) { + return null; } - - values.sort(Comparator.comparing(e -> normalizeSortKey(e.field().getName()))); - return values; - } - - private static void openFieldEntries(Player player, String safePath, List entries, int page) { - GuiLayout.PagePlan plan = configPagePlan(entries.size()); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(entries.size(), start + plan.itemsPerPage()); - - UIWindow w = new UIWindow(Adapt.instance, player); - GuiTheme.apply(w, tagForSection(safePath)); - w.setViewportHeight(plan.rows()); - - if (entries.isEmpty()) { - w.setElement(0, 0, new UIElement("cfg-empty") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.GRAY + "No settings in this section")); - } else { - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - FieldEntry entry = entries.get(rowStart + i); - int pos = GuiLayout.centeredPosition(i, rowCount); - Element element = createElementForEntry(player, safePath, currentPage, entry); - reveal.add(new GuiEffects.Placement(pos, row, element)); - } - } - GuiEffects.applyReveal(w, reveal); + if (!setFieldValue(field, current, next)) { + return null; } + } - int navRow = plan.rows() - 1; - applyPageControls(w, player, safePath, navRow, currentPage, plan.pageCount(), entries.size(), start, end); - if (!safePath.isBlank()) { - String parent = parentPath(safePath); - w.setElement(0, navRow, new UIElement("cfg-back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.GRAY + "Back") - .onLeftClick((e) -> navigateTo(player, parent, 0))); - } - addSectionOverview(w, navRow, safePath, entries, currentPage, plan.pageCount()); + if (next == null) { + return null; + } - String titlePath = safePath.isBlank() ? "root" : safePath; - if (safePath.equals(ROOT_CORE_GENERAL)) { - titlePath = "core.general"; - } - if (titlePath.length() > 24) { - titlePath = "..." + titlePath.substring(titlePath.length() - 21); - } - w.setTitle(C.GRAY + "Configure: " + C.WHITE + titlePath); - w.onClosed((window) -> onGuiClosed(player, safePath)); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); - } - - private static void openCoreIndex(Player player, int page) { - Object root = AdaptConfig.get(); - List entries = new ArrayList<>(); - int generalValues = 0; - for (Field field : getSerializableFields(root.getClass())) { - Object value = getFieldValue(field, root); - ElementDescriptor descriptor = describe(field, value); - if (descriptor.kind() == ElementKind.SECTION) { - int nested = value == null ? 0 : getSerializableFields(value.getClass()).size(); - entries.add(new SectionIndexEntry( - ROOT_CORE + "." + field.getName(), - displayName(field.getName()), - materialForSection(field.getName()), - "Open " + nested + " setting" + (nested == 1 ? "" : "s") - )); - } else { - generalValues++; - } - } + current = next; + } - if (generalValues > 0) { - entries.add(new SectionIndexEntry( - ROOT_CORE_GENERAL, - "General Settings", - Material.COMPARATOR, - "Open " + generalValues + " global option" + (generalValues == 1 ? "" : "s") - )); - } + return current; + } - entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - openSectionIndex(player, ROOT_CORE, page, "Configure: core", entries); + private static boolean setPathValue(Object root, String path, Object value, boolean createMissing) { + String normalized = normalizePath(path); + if (normalized.isBlank()) { + return false; } - private static void openCoreGeneral(Player player, int page) { - openFieldEntries(player, ROOT_CORE_GENERAL, buildCoreGeneralEntries(), page); + String[] segments = normalized.split("\\Q.\\E"); + if (segments.length == 0) { + return false; } - private static void openRoot(Player player, int page) { - List entries = new ArrayList<>(); - entries.add(new SectionIndexEntry(ROOT_ADAPTATIONS_SKILLS, "Adaptations", Material.NETHER_STAR, "Configure adaptation settings")); - entries.add(new SectionIndexEntry(ROOT_CORE, "Core", Material.COMPARATOR, "Configure global Adapt settings")); - entries.add(new SectionIndexEntry(ROOT_SKILLS, "Skills", Material.ENCHANTED_BOOK, "Configure skill settings")); - entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - openSectionIndex(player, "", page, "Configure Adapt", entries); + StringBuilder parentPath = new StringBuilder(); + for (int i = 0; i < segments.length - 1; i++) { + if (i > 0) { + parentPath.append('.'); + } + parentPath.append(segments[i]); } - private static void openSkillIndex(Player player, int page) { - List> skills = getLoadedSkills(); - List entries = new ArrayList<>(); - for (Skill skill : skills) { - if (skill == null) { - continue; - } - - entries.add(new SectionIndexEntry( - ROOT_SKILLS + "." + skill.getName(), - ColorFormatter.stripColor(skill.getDisplayName()), - skill.getIcon(), - "Configure " + skill.getName() - )); - } - - entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - openSectionIndex(player, ROOT_SKILLS, page, "Configure: skills", entries); + Object section = resolveSectionObject(root, parentPath.toString(), createMissing); + if (section == null) { + return false; } - private static void openAdaptationSkillIndex(Player player, int page) { - List entries = new ArrayList<>(); - entries.add(new SectionIndexEntry( - ROOT_ADAPTATIONS_ALL, - "All Adaptations (A-Z)", - Material.NETHER_STAR, - "Browse every adaptation alphabetically" - )); - - for (Skill skill : getLoadedSkills()) { - if (skill == null) { - continue; - } - - int adaptationCount = skill.getAdaptations() == null ? 0 : skill.getAdaptations().size(); - if (adaptationCount <= 0) { - continue; - } - - entries.add(new SectionIndexEntry( - ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), - ColorFormatter.stripColor(skill.getDisplayName()), - skill.getIcon(), - "Browse " + adaptationCount + " adaptation" + (adaptationCount == 1 ? "" : "s") - )); - } - - List head = new ArrayList<>(); - List skillEntries = new ArrayList<>(); - for (SectionIndexEntry entry : entries) { - if (ROOT_ADAPTATIONS_ALL.equals(entry.path())) { - head.add(entry); - } else { - skillEntries.add(entry); - } - } - skillEntries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - head.addAll(skillEntries); - openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS, page, "Configure: adaptations", head); + Field targetField = findField(section.getClass(), segments[segments.length - 1]); + if (targetField == null) { + return false; } - private static void openAdaptationIndexForSkill(Player player, String skillName, int page) { - Skill skill = resolveSkill(skillName); - if (skill == null) { - Adapt.messagePlayer(player, C.RED + "Unknown skill for adaptation config: " + C.WHITE + skillName); - navigateTo(player, ROOT_ADAPTATIONS_SKILLS, 0); - return; - } + Object typedValue = coerceValue(value, targetField.getType()); + return setFieldValue(targetField, section, typedValue); + } - List entries = new ArrayList<>(); - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation == null) { - continue; - } - entries.add(new SectionIndexEntry( - ROOT_ADAPTATIONS + "." + adaptation.getName(), - ColorFormatter.stripColor(adaptation.getDisplayName()), - adaptation.getIcon(), - "Open " + adaptation.getName() - )); - } - - entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - openSectionIndex(player, ROOT_ADAPTATIONS_SKILLS + "." + skill.getName(), page, "Configure: " + ColorFormatter.stripColor(skill.getDisplayName()), entries); + private static Object readPathValue(Object root, String path) { + String normalized = normalizePath(path); + if (normalized.isBlank()) { + return null; } - private static void openAdaptationIndex(Player player, int page) { - List entries = new ArrayList<>(); - for (Adaptation adaptation : getLoadedAdaptations()) { - if (adaptation == null || adaptation.getSkill() == null) { - continue; - } - - entries.add(new SectionIndexEntry( - ROOT_ADAPTATIONS + "." + adaptation.getName(), - ColorFormatter.stripColor(adaptation.getDisplayName()), - adaptation.getIcon(), - "Skill: " + adaptation.getSkill().getName() - )); - } - - entries.sort(Comparator.comparing(e -> normalizeSortKey(e.displayName()))); - openSectionIndex(player, ROOT_ADAPTATIONS_ALL, page, "Configure: all adaptations", entries); - } - - private static void openSectionIndex(Player player, String sectionPath, int page, String title, List entries) { - String safePath = normalizePath(sectionPath); - GuiLayout.PagePlan plan = configPagePlan(entries.size()); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(entries.size(), start + plan.itemsPerPage()); - - UIWindow w = new UIWindow(Adapt.instance, player); - GuiTheme.apply(w, tagForSection(safePath)); - w.setViewportHeight(plan.rows()); - - if (entries.isEmpty()) { - w.setElement(0, 0, new UIElement("cfg-empty") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.GRAY + "No entries")); - } else { - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - SectionIndexEntry entry = entries.get(rowStart + i); - int pos = GuiLayout.centeredPosition(i, rowCount); - Element element = new UIElement("cfg-index-" + entry.path()) - .setMaterial(new MaterialBlock(entry.material())) - .setName(C.WHITE + entry.displayName()) - .addLore(C.GRAY + entry.lore()) - .addLore(C.DARK_GRAY + "Path: " + entry.path()) - .setProgress(1D) - .onLeftClick((e) -> navigateTo(player, entry.path(), 0)); - reveal.add(new GuiEffects.Placement(pos, row, element)); - } - } - GuiEffects.applyReveal(w, reveal); - } - - int navRow = plan.rows() - 1; - applyPageControls(w, player, safePath, navRow, currentPage, plan.pageCount(), entries.size(), start, end); - if (!safePath.isBlank()) { - w.setElement(0, navRow, new UIElement("cfg-back") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.GRAY + "Back") - .onLeftClick((e) -> navigateTo(player, parentPath(safePath), 0))); - } - addIndexOverview(w, navRow, safePath, entries.size(), currentPage, plan.pageCount(), title); - - w.setTitle(C.GRAY + title); - w.onClosed((window) -> onGuiClosed(player, safePath)); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); - } - - private static GuiLayout.PagePlan configPagePlan(int totalEntries) { - int items = Math.max(0, totalEntries); - int rows = GuiLayout.MAX_ROWS; - int contentRows = CONFIG_CONTENT_ROWS; - int itemsPerPage = contentRows * GuiLayout.WIDTH; - int pageCount = Math.max(1, (int) Math.ceil(items / (double) itemsPerPage)); - return new GuiLayout.PagePlan(rows, contentRows, true, itemsPerPage, pageCount); - } - - private static void applyPageControls( - Window window, - Player player, - String safePath, - int navRow, - int currentPage, - int pageCount, - int totalEntries, - int start, - int end - ) { - if (pageCount <= 1) { - return; - } - - int jumpBack = Math.max(0, currentPage - PAGE_JUMP); - int jumpForward = Math.min(pageCount - 1, currentPage + PAGE_JUMP); - - if (currentPage > 0) { - window.setElement(-4, navRow, new UIElement("cfg-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Left click: previous page") - .addLore(C.GRAY + "Right click: jump -" + PAGE_JUMP + " pages") - .onLeftClick((e) -> navigateTo(player, safePath, currentPage - 1)) - .onRightClick((e) -> navigateTo(player, safePath, jumpBack))); - window.setElement(-3, navRow, new UIElement("cfg-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> navigateTo(player, safePath, 0))); - } + String[] segments = normalized.split("\\Q.\\E"); + if (segments.length == 0) { + return null; + } - if (currentPage < pageCount - 1) { - window.setElement(4, navRow, new UIElement("cfg-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Left click: next page") - .addLore(C.GRAY + "Right click: jump +" + PAGE_JUMP + " pages") - .onLeftClick((e) -> navigateTo(player, safePath, currentPage + 1)) - .onRightClick((e) -> navigateTo(player, safePath, jumpForward))); - window.setElement(3, navRow, new UIElement("cfg-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> navigateTo(player, safePath, pageCount - 1))); - } + StringBuilder parentPath = new StringBuilder(); + for (int i = 0; i < segments.length - 1; i++) { + if (i > 0) { + parentPath.append('.'); + } + parentPath.append(segments[i]); + } - int from = totalEntries <= 0 ? 0 : (start + 1); - int to = totalEntries <= 0 ? 0 : end; - window.setElement(-1, navRow, new UIElement("cfg-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + pageCount) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + totalEntries) - .setProgress(1D)); - } - - private static void addSectionOverview( - Window window, - int navRow, - String path, - List entries, - int currentPage, - int pageCount - ) { - int sections = 0; - int editable = 0; - for (FieldEntry entry : entries) { - if (entry.descriptor().kind() == ElementKind.SECTION) { - sections++; - } - if (entry.descriptor().editable()) { - editable++; - } - } + Object section = resolveSectionObject(root, parentPath.toString(), false); + if (section == null) { + return null; + } - String safePath = path == null || path.isBlank() ? "root" : path; - window.setElement(1, navRow, new UIElement("cfg-overview") - .setMaterial(new MaterialBlock(Material.BOOK)) - .setName(C.AQUA + "Overview") - .addLore(C.GRAY + "Path: " + C.WHITE + safePath) - .addLore(C.GRAY + "Sections: " + C.WHITE + sections) - .addLore(C.GRAY + "Editable: " + C.WHITE + editable) - .addLore(C.GRAY + "Entries: " + C.WHITE + entries.size()) - .setProgress(1D)); - - window.setElement(2, navRow, new UIElement("cfg-help") - .setMaterial(new MaterialBlock(Material.KNOWLEDGE_BOOK)) - .setName(C.GRAY + "Help") - .addLore(C.GRAY + "LMB: open/edit/toggle") - .addLore(C.GRAY + "RMB: enum prev / page jump") - .addLore(C.GRAY + "ESC: back to parent page") - .addLore(C.DARK_GRAY + "Page " + (currentPage + 1) + "/" + pageCount) - .setProgress(1D)); - } - - private static void addIndexOverview( - Window window, - int navRow, - String path, - int totalEntries, - int currentPage, - int pageCount, - String title - ) { - String safePath = path == null || path.isBlank() ? "root" : path; - window.setElement(1, navRow, new UIElement("cfg-index-overview") - .setMaterial(new MaterialBlock(Material.BOOK)) - .setName(C.AQUA + "Directory") - .addLore(C.GRAY + "Path: " + C.WHITE + safePath) - .addLore(C.GRAY + "Entries: " + C.WHITE + totalEntries) - .addLore(C.GRAY + "Page: " + C.WHITE + (currentPage + 1) + "/" + pageCount) - .addLore(C.DARK_GRAY + title) - .setProgress(1D)); - - window.setElement(2, navRow, new UIElement("cfg-index-help") - .setMaterial(new MaterialBlock(Material.KNOWLEDGE_BOOK)) - .setName(C.GRAY + "Navigation") - .addLore(C.GRAY + "LMB: open section") - .addLore(C.GRAY + "RMB on arrows: jump pages") - .addLore(C.GRAY + "ESC: back to parent page") - .setProgress(1D)); - } - - private static String tagForSection(String sectionPath) { - String path = normalizePath(sectionPath); - if (path.isBlank()) { - return TAG_PREFIX; - } - return TAG_PREFIX + "/" + path; + Field field = findField(section.getClass(), segments[segments.length - 1]); + if (field == null) { + return null; } - private static String normalizePath(String path) { - if (path == null) { - return ""; - } + return getFieldValue(field, section); + } - String normalized = path.trim(); - while (normalized.startsWith(".")) { - normalized = normalized.substring(1); - } - while (normalized.endsWith(".")) { - normalized = normalized.substring(0, normalized.length() - 1); - } - return normalized; + private static Field findField(Class type, String name) { + Class current = type; + while (current != null && current != Object.class) { + try { + Field field = current.getDeclaredField(name); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException ex) { + current = current.getSuperclass(); + } } - private static String parentPath(String path) { - String normalized = normalizePath(path); - if (normalized.isBlank()) { - return ""; - } + return null; + } - if (normalized.equals(ROOT_ADAPTATIONS) || normalized.equals(ROOT_ADAPTATIONS_SKILLS)) { - return ""; - } + private static List getSerializableFields(Class type) { + List fields = new ArrayList<>(); + collectFields(type, fields); + return fields; + } - if (normalized.equals(ROOT_CORE_GENERAL)) { - return ROOT_CORE; - } + private static void collectFields(Class type, List out) { + if (type == null || type == Object.class) { + return; + } - if (normalized.equals(ROOT_ADAPTATIONS_ALL)) { - return ROOT_ADAPTATIONS_SKILLS; - } + collectFields(type.getSuperclass(), out); + for (Field field : type.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } - if (normalized.startsWith(ROOT_ADAPTATIONS_SKILLS + ".")) { - return ROOT_ADAPTATIONS_SKILLS; - } + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { + continue; + } - int dot = normalized.lastIndexOf('.'); - if (dot < 0) { - return ""; - } - return normalized.substring(0, dot); + field.setAccessible(true); + out.add(field); } + } - private static String joinPath(String base, String child) { - String left = normalizePath(base); - if (left.isBlank()) { - return child; - } - return left + "." + child; + private static Object instantiate(Class type) { + Class normalized = normalizeType(type); + if (normalized.isPrimitive() || normalized.isEnum() || normalized == String.class || isNumericType(normalized) || normalized == Boolean.class) { + return null; } - private static Object resolveSectionObject(Object root, String sectionPath, boolean createMissing) { - if (root == null) { - return null; - } - - String normalized = normalizePath(sectionPath); - if (normalized.isBlank()) { - return root; - } - - Object current = root; - for (String segment : normalized.split("\\Q.\\E")) { - Field field = findField(current.getClass(), segment); - if (field == null) { - return null; - } - - Object next = getFieldValue(field, current); - if (next == null && createMissing) { - next = instantiate(field.getType()); - if (next == null) { - return null; - } - if (!setFieldValue(field, current, next)) { - return null; - } - } - - if (next == null) { - return null; - } - - current = next; - } - - return current; + try { + return normalized.getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + Adapt.verbose("Failed to instantiate config type " + normalized.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; } + } - private static boolean setPathValue(Object root, String path, Object value, boolean createMissing) { - String normalized = normalizePath(path); - if (normalized.isBlank()) { - return false; - } - - String[] segments = normalized.split("\\Q.\\E"); - if (segments.length == 0) { - return false; - } - - StringBuilder parentPath = new StringBuilder(); - for (int i = 0; i < segments.length - 1; i++) { - if (i > 0) { - parentPath.append('.'); - } - parentPath.append(segments[i]); - } - - Object section = resolveSectionObject(root, parentPath.toString(), createMissing); - if (section == null) { - return false; - } - - Field targetField = findField(section.getClass(), segments[segments.length - 1]); - if (targetField == null) { - return false; - } + private static boolean setFieldValue(Field field, Object target, Object value) { + try { + field.setAccessible(true); + field.set(target, value); + return true; + } catch (Throwable ex) { + Adapt.verbose("Failed to set field '" + field.getName() + "' on " + + (target == null ? "null" : target.getClass().getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return false; + } + } - Object typedValue = coerceValue(value, targetField.getType()); - return setFieldValue(targetField, section, typedValue); + private static Object getFieldValue(Field field, Object target) { + try { + field.setAccessible(true); + return field.get(target); + } catch (Throwable ex) { + Adapt.verbose("Failed to read field '" + field.getName() + "' on " + + (target == null ? "null" : target.getClass().getName()) + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + return null; } + } - private static Object readPathValue(Object root, String path) { - String normalized = normalizePath(path); - if (normalized.isBlank()) { - return null; - } + private static Object coerceValue(Object value, Class targetType) { + return ConfigGuiValueCodec.coerceValue(value, targetType); + } - String[] segments = normalized.split("\\Q.\\E"); - if (segments.length == 0) { - return null; - } + private static Class normalizeType(Class type) { + return ConfigGuiValueCodec.normalizeType(type); + } - StringBuilder parentPath = new StringBuilder(); - for (int i = 0; i < segments.length - 1; i++) { - if (i > 0) { - parentPath.append('.'); - } - parentPath.append(segments[i]); - } + private static boolean isNumericType(Class type) { + return ConfigGuiValueCodec.isNumericType(type); + } - Object section = resolveSectionObject(root, parentPath.toString(), false); - if (section == null) { - return null; - } + private static boolean isSectionType(Class type) { + return ConfigGuiValueCodec.isSectionType(type); + } - Field field = findField(section.getClass(), segments[segments.length - 1]); - if (field == null) { - return null; - } + private static Object cycleEnum(Class enumType, Object current, int direction) { + return ConfigGuiValueCodec.cycleEnum(enumType, current, direction); + } - return getFieldValue(field, section); - } + private static Object parseEnumConstant(Class enumType, String value) { + return ConfigGuiValueCodec.parseEnumConstant(enumType, value); + } - private static Field findField(Class type, String name) { - Class current = type; - while (current != null && current != Object.class) { - try { - Field field = current.getDeclaredField(name); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException ex) { - current = current.getSuperclass(); - } - } + private static String enumConstants(Class enumType) { + return ConfigGuiValueCodec.enumConstants(enumType); + } - return null; + private static String displayName(String key) { + if (key == null || key.isBlank()) { + return "Unnamed"; } - private static List getSerializableFields(Class type) { - List fields = new ArrayList<>(); - collectFields(type, fields); - return fields; + String spaced = key + .replace('_', ' ') + .replace('-', ' ') + .replaceAll("([a-z])([A-Z])", "$1 $2") + .trim(); + if (spaced.isBlank()) { + return key; } + return Character.toUpperCase(spaced.charAt(0)) + spaced.substring(1); + } - private static void collectFields(Class type, List out) { - if (type == null || type == Object.class) { - return; - } - - collectFields(type.getSuperclass(), out); - for (Field field : type.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; - } - - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { - continue; - } - - field.setAccessible(true); - out.add(field); - } + private static String summarizeValue(Object value) { + if (value == null) { + return "null"; } - private static Object instantiate(Class type) { - Class normalized = normalizeType(type); - if (normalized.isPrimitive() || normalized.isEnum() || normalized == String.class || isNumericType(normalized) || normalized == Boolean.class) { - return null; - } - - try { - return normalized.getDeclaredConstructor().newInstance(); - } catch (Throwable ex) { - Adapt.verbose("Failed to instantiate config type " + normalized.getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } + if (value instanceof Map map) { + return "map(" + map.size() + ")"; + } + if (value instanceof Collection collection) { + return "list(" + collection.size() + ")"; + } + if (value.getClass().isArray()) { + return "array"; } - private static boolean setFieldValue(Field field, Object target, Object value) { - try { - field.setAccessible(true); - field.set(target, value); - return true; - } catch (Throwable ex) { - Adapt.verbose("Failed to set field '" + field.getName() + "' on " - + (target == null ? "null" : target.getClass().getName()) + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return false; - } + String text = String.valueOf(value) + .replace("\n", "\\n") + .replace("\r", "\\r"); + if (text.length() > MAX_VALUE_PREVIEW) { + return text.substring(0, MAX_VALUE_PREVIEW - 3) + "..."; } + return text; + } - private static Object getFieldValue(Field field, Object target) { - try { - field.setAccessible(true); - return field.get(target); - } catch (Throwable ex) { - Adapt.verbose("Failed to read field '" + field.getName() + "' on " - + (target == null ? "null" : target.getClass().getName()) + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - return null; - } + private static void refreshGlobalRuntimeSettings() { + Adapt.wordKey.clear(); + if (AdaptConfig.get().isAutoUpdateLanguage()) { + Localizer.updateLanguageFile(); } - private static Object coerceValue(Object value, Class targetType) { - if (value == null) { - return null; - } + if (AdaptConfig.get().isCustomModels()) { + CustomModel.reloadFromDisk(); + } else { + CustomModel.clear(); + } + } - Class normalizedTarget = normalizeType(targetType); - Class valueType = value.getClass(); - if (normalizedTarget.isAssignableFrom(valueType)) { - return value; - } + private static SectionTarget resolveSectionTarget(String path, boolean createMissing) { + String normalized = normalizePath(path); + if (normalized.isBlank()) { + return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), "", createMissing)); + } - ParseResult parsed = parseInputValue(targetType, String.valueOf(value)); - return parsed.success() ? parsed.value() : value; + if (normalized.equals(ROOT_CORE) || normalized.startsWith(ROOT_CORE + ".")) { + String objectPath = stripPrefix(normalized, ROOT_CORE); + return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), objectPath, createMissing)); } - private static Class normalizeType(Class type) { - if (type == null || !type.isPrimitive()) { - return type; - } + if (normalized.equals(ROOT_SKILLS) || normalized.startsWith(ROOT_SKILLS + ".")) { + String payload = stripPrefix(normalized, ROOT_SKILLS); + if (payload.isBlank()) { + return null; + } - if (type == int.class) return Integer.class; - if (type == long.class) return Long.class; - if (type == double.class) return Double.class; - if (type == float.class) return Float.class; - if (type == short.class) return Short.class; - if (type == byte.class) return Byte.class; - if (type == boolean.class) return Boolean.class; - if (type == char.class) return Character.class; - return type; - } - - private static boolean isNumericType(Class type) { - return type == Integer.class - || type == Long.class - || type == Double.class - || type == Float.class - || type == Short.class - || type == Byte.class; - } - - private static boolean isSectionType(Class type) { - Class normalized = normalizeType(type); - if (normalized == null) { - return false; - } + String[] parts = payload.split("\\Q.\\E", 2); + Skill skill = resolveSkill(parts[0]); + if (skill == null || skill.getConfig() == null) { + return null; + } - if (normalized.isPrimitive() || normalized.isEnum()) { - return false; - } + String objectPath = parts.length > 1 ? parts[1] : ""; + return new SectionTarget("skill:" + skill.getName(), resolveSectionObject(skill.getConfig(), objectPath, createMissing)); + } - if (normalized == String.class || normalized == Character.class || normalized == Boolean.class || isNumericType(normalized)) { - return false; - } + if (normalized.equals(ROOT_ADAPTATIONS) || normalized.startsWith(ROOT_ADAPTATIONS + ".")) { + String payload = stripPrefix(normalized, ROOT_ADAPTATIONS); + if (payload.isBlank()) { + return null; + } - if (Map.class.isAssignableFrom(normalized) || Collection.class.isAssignableFrom(normalized) || normalized.isArray()) { - return false; - } + String[] parts = payload.split("\\Q.\\E", 2); + Adaptation adaptation = resolveAdaptation(parts[0]); + if (adaptation == null || adaptation.getConfig() == null) { + return null; + } - return true; + String objectPath = parts.length > 1 ? parts[1] : ""; + return new SectionTarget("adaptation:" + adaptation.getName(), resolveSectionObject(adaptation.getConfig(), objectPath, createMissing)); } - private static Object cycleEnum(Class enumType, Object current, int direction) { - Class normalized = normalizeType(enumType); - if (normalized == null || !normalized.isEnum()) { - return null; - } - - Object[] constants = normalized.getEnumConstants(); - if (constants == null || constants.length == 0) { - return null; - } + return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), normalized, createMissing)); + } - int currentIndex = 0; - if (current != null) { - for (int i = 0; i < constants.length; i++) { - if (Objects.equals(constants[i], current)) { - currentIndex = i; - break; - } - } - } - - int nextIndex = currentIndex + direction; - if (nextIndex < 0) { - nextIndex = constants.length - 1; - } else if (nextIndex >= constants.length) { - nextIndex = 0; - } - return constants[nextIndex]; + private static EditTarget resolveEditTarget(String path) { + String normalized = normalizePath(path); + if (normalized.isBlank()) { + return null; } - private static Object parseEnumConstant(Class enumType, String value) { - if (enumType == null || !enumType.isEnum() || value == null) { - return null; - } + if (normalized.equals(ROOT_CORE) || normalized.startsWith(ROOT_CORE + ".")) { + String objectPath = stripPrefix(normalized, ROOT_CORE); + if (objectPath.isBlank()) { + return null; + } + return new EditTarget( + SOURCE_TAG_CORE, + AdaptConfig.get(), + objectPath, + Adapt.instance.getDataFile("adapt", "adapt.toml"), + AdaptConfig::reload, + ConfigGui::refreshGlobalRuntimeSettings + ); + } + + if (normalized.equals(ROOT_SKILLS) || normalized.startsWith(ROOT_SKILLS + ".")) { + String payload = stripPrefix(normalized, ROOT_SKILLS); + if (payload.isBlank()) { + return null; + } - for (Object constant : enumType.getEnumConstants()) { - if (constant == null) { - continue; - } + String[] parts = payload.split("\\Q.\\E", 2); + Skill skill = resolveSkill(parts[0]); + if (skill == null || skill.getConfig() == null || Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + return null; + } - if (constant.toString().equalsIgnoreCase(value)) { - return constant; - } - } + String objectPath = parts.length > 1 ? parts[1] : ""; + if (objectPath.isBlank()) { + return null; + } + + return new EditTarget( + "skill:" + skill.getName(), + skill.getConfig(), + objectPath, + Adapt.instance.getDataFile("adapt", "skills", skill.getName() + ".toml"), + () -> Adapt.instance.getAdaptServer().getSkillRegistry().hotReloadSkillConfig(skill.getName()), + () -> { + } + ); + } + + if (normalized.equals(ROOT_ADAPTATIONS) || normalized.startsWith(ROOT_ADAPTATIONS + ".")) { + String payload = stripPrefix(normalized, ROOT_ADAPTATIONS); + if (payload.isBlank()) { + return null; + } + String[] parts = payload.split("\\Q.\\E", 2); + Adaptation adaptation = resolveAdaptation(parts[0]); + if (adaptation == null || adaptation.getConfig() == null || !(adaptation instanceof SimpleAdaptation simpleAdaptation)) { return null; - } + } - private static String enumConstants(Class enumType) { - if (enumType == null || !enumType.isEnum()) { - return ""; - } + String objectPath = parts.length > 1 ? parts[1] : ""; + if (objectPath.isBlank()) { + return null; + } - List values = new ArrayList<>(); - for (Object constant : enumType.getEnumConstants()) { - if (constant == null) { - continue; - } - values.add(constant.toString()); - } - return String.join(", ", values); + return new EditTarget( + "adaptation:" + adaptation.getName(), + adaptation.getConfig(), + objectPath, + Adapt.instance.getDataFile("adapt", "adaptations", adaptation.getName() + ".toml"), + () -> simpleAdaptation.reloadConfigFromDisk(false), + () -> { + } + ); } - private static String displayName(String key) { - if (key == null || key.isBlank()) { - return "Unnamed"; - } + return new EditTarget( + SOURCE_TAG_CORE, + AdaptConfig.get(), + normalized, + Adapt.instance.getDataFile("adapt", "adapt.toml"), + AdaptConfig::reload, + ConfigGui::refreshGlobalRuntimeSettings + ); + } - String spaced = key - .replace('_', ' ') - .replace('-', ' ') - .replaceAll("([a-z])([A-Z])", "$1 $2") - .trim(); - if (spaced.isBlank()) { - return key; - } - return Character.toUpperCase(spaced.charAt(0)) + spaced.substring(1); + private static String stripPrefix(String path, String prefix) { + String normalized = normalizePath(path); + if (normalized.equals(prefix)) { + return ""; } - private static String summarizeValue(Object value) { - if (value == null) { - return "null"; - } - - if (value instanceof Map map) { - return "map(" + map.size() + ")"; - } - if (value instanceof Collection collection) { - return "list(" + collection.size() + ")"; - } - if (value.getClass().isArray()) { - return "array"; - } - - String text = String.valueOf(value) - .replace("\n", "\\n") - .replace("\r", "\\r"); - if (text.length() > MAX_VALUE_PREVIEW) { - return text.substring(0, MAX_VALUE_PREVIEW - 3) + "..."; - } - return text; + String withDot = prefix + "."; + if (normalized.startsWith(withDot)) { + return normalized.substring(withDot.length()); } + return normalized; + } - private static void refreshGlobalRuntimeSettings() { - Adapt.wordKey.clear(); - if (AdaptConfig.get().isAutoUpdateLanguage()) { - Localizer.updateLanguageFile(); - } - - if (AdaptConfig.get().isCustomModels()) { - CustomModel.reloadFromDisk(); - } else { - CustomModel.clear(); - } + private static Skill resolveSkill(String skillName) { + if (skillName == null || skillName.isBlank()) { + return null; } - - private static SectionTarget resolveSectionTarget(String path, boolean createMissing) { - String normalized = normalizePath(path); - if (normalized.isBlank()) { - return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), "", createMissing)); - } - - if (normalized.equals(ROOT_CORE) || normalized.startsWith(ROOT_CORE + ".")) { - String objectPath = stripPrefix(normalized, ROOT_CORE); - return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), objectPath, createMissing)); - } - - if (normalized.equals(ROOT_SKILLS) || normalized.startsWith(ROOT_SKILLS + ".")) { - String payload = stripPrefix(normalized, ROOT_SKILLS); - if (payload.isBlank()) { - return null; - } - - String[] parts = payload.split("\\Q.\\E", 2); - Skill skill = resolveSkill(parts[0]); - if (skill == null || skill.getConfig() == null) { - return null; - } - - String objectPath = parts.length > 1 ? parts[1] : ""; - return new SectionTarget("skill:" + skill.getName(), resolveSectionObject(skill.getConfig(), objectPath, createMissing)); - } - - if (normalized.equals(ROOT_ADAPTATIONS) || normalized.startsWith(ROOT_ADAPTATIONS + ".")) { - String payload = stripPrefix(normalized, ROOT_ADAPTATIONS); - if (payload.isBlank()) { - return null; - } - - String[] parts = payload.split("\\Q.\\E", 2); - Adaptation adaptation = resolveAdaptation(parts[0]); - if (adaptation == null || adaptation.getConfig() == null) { - return null; - } - - String objectPath = parts.length > 1 ? parts[1] : ""; - return new SectionTarget("adaptation:" + adaptation.getName(), resolveSectionObject(adaptation.getConfig(), objectPath, createMissing)); - } - - return new SectionTarget(SOURCE_TAG_CORE, resolveSectionObject(AdaptConfig.get(), normalized, createMissing)); + if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + return null; } + return Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName); + } - private static EditTarget resolveEditTarget(String path) { - String normalized = normalizePath(path); - if (normalized.isBlank()) { - return null; - } - - if (normalized.equals(ROOT_CORE) || normalized.startsWith(ROOT_CORE + ".")) { - String objectPath = stripPrefix(normalized, ROOT_CORE); - if (objectPath.isBlank()) { - return null; - } - return new EditTarget( - SOURCE_TAG_CORE, - AdaptConfig.get(), - objectPath, - Adapt.instance.getDataFile("adapt", "adapt.toml"), - AdaptConfig::reload, - ConfigGui::refreshGlobalRuntimeSettings - ); - } - - if (normalized.equals(ROOT_SKILLS) || normalized.startsWith(ROOT_SKILLS + ".")) { - String payload = stripPrefix(normalized, ROOT_SKILLS); - if (payload.isBlank()) { - return null; - } - - String[] parts = payload.split("\\Q.\\E", 2); - Skill skill = resolveSkill(parts[0]); - if (skill == null || skill.getConfig() == null || Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - return null; - } - - String objectPath = parts.length > 1 ? parts[1] : ""; - if (objectPath.isBlank()) { - return null; - } - - return new EditTarget( - "skill:" + skill.getName(), - skill.getConfig(), - objectPath, - Adapt.instance.getDataFile("adapt", "skills", skill.getName() + ".toml"), - () -> Adapt.instance.getAdaptServer().getSkillRegistry().hotReloadSkillConfig(skill.getName()), - () -> { - } - ); - } - - if (normalized.equals(ROOT_ADAPTATIONS) || normalized.startsWith(ROOT_ADAPTATIONS + ".")) { - String payload = stripPrefix(normalized, ROOT_ADAPTATIONS); - if (payload.isBlank()) { - return null; - } - - String[] parts = payload.split("\\Q.\\E", 2); - Adaptation adaptation = resolveAdaptation(parts[0]); - if (adaptation == null || adaptation.getConfig() == null || !(adaptation instanceof SimpleAdaptation simpleAdaptation)) { - return null; - } - - String objectPath = parts.length > 1 ? parts[1] : ""; - if (objectPath.isBlank()) { - return null; - } - - return new EditTarget( - "adaptation:" + adaptation.getName(), - adaptation.getConfig(), - objectPath, - Adapt.instance.getDataFile("adapt", "adaptations", adaptation.getName() + ".toml"), - () -> simpleAdaptation.reloadConfigFromDisk(false), - () -> { - } - ); - } - - return new EditTarget( - SOURCE_TAG_CORE, - AdaptConfig.get(), - normalized, - Adapt.instance.getDataFile("adapt", "adapt.toml"), - AdaptConfig::reload, - ConfigGui::refreshGlobalRuntimeSettings - ); + private static Adaptation resolveAdaptation(String adaptationName) { + if (adaptationName == null || adaptationName.isBlank()) { + return null; } - private static String stripPrefix(String path, String prefix) { - String normalized = normalizePath(path); - if (normalized.equals(prefix)) { - return ""; - } + for (Skill skill : getLoadedSkills()) { + if (skill == null) { + continue; + } - String withDot = prefix + "."; - if (normalized.startsWith(withDot)) { - return normalized.substring(withDot.length()); + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation == null || adaptation.getName() == null) { + continue; } - return normalized; - } - private static Skill resolveSkill(String skillName) { - if (skillName == null || skillName.isBlank()) { - return null; + if (adaptation.getName().equalsIgnoreCase(adaptationName)) { + return adaptation; } - if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - return null; - } - return Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillName); + } } - private static Adaptation resolveAdaptation(String adaptationName) { - if (adaptationName == null || adaptationName.isBlank()) { - return null; - } + return null; + } - for (Skill skill : getLoadedSkills()) { - if (skill == null) { - continue; - } + private static List> getLoadedSkills() { + if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { + return List.of(); + } - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation == null || adaptation.getName() == null) { - continue; - } + List> skills = new ArrayList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); + skills.sort(Comparator.comparing(skill -> normalizeSortKey(ColorFormatter.stripColor(skill.getDisplayName())))); + return skills; + } - if (adaptation.getName().equalsIgnoreCase(adaptationName)) { - return adaptation; - } - } + private static List> getLoadedAdaptations() { + List> adaptations = new ArrayList<>(); + for (Skill skill : getLoadedSkills()) { + if (skill == null) { + continue; + } + for (Adaptation adaptation : skill.getAdaptations()) { + if (adaptation == null) { + continue; } - - return null; + adaptations.add(adaptation); + } } - private static List> getLoadedSkills() { - if (Adapt.instance == null || Adapt.instance.getAdaptServer() == null || Adapt.instance.getAdaptServer().getSkillRegistry() == null) { - return List.of(); - } + adaptations.sort(Comparator.comparing(adaptation -> normalizeSortKey(ColorFormatter.stripColor(adaptation.getDisplayName())))); + return adaptations; + } - List> skills = new ArrayList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); - skills.sort(Comparator.comparing(skill -> normalizeSortKey(ColorFormatter.stripColor(skill.getDisplayName())))); - return skills; - } - - private static List> getLoadedAdaptations() { - List> adaptations = new ArrayList<>(); - for (Skill skill : getLoadedSkills()) { - if (skill == null) { - continue; - } - for (Adaptation adaptation : skill.getAdaptations()) { - if (adaptation == null) { - continue; - } - adaptations.add(adaptation); - } - } - - adaptations.sort(Comparator.comparing(adaptation -> normalizeSortKey(ColorFormatter.stripColor(adaptation.getDisplayName())))); - return adaptations; + private static String normalizeSortKey(String value) { + if (value == null) { + return ""; } - private static String normalizeSortKey(String value) { - if (value == null) { - return ""; - } + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); + return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + } - String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); - return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); - } + private static void playPageTurn(Player player) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); + spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); + spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + } - private static void playPageTurn(Player player) { - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 1.1f, 1.255f); - spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 0.7f, 1.455f); - spw.play(player.getLocation(), org.bukkit.Sound.ITEM_BOOK_PAGE_TURN, 0.3f, 1.855f); + private static void navigateTo(Player player, String path, int page) { + if (player == null) { + return; } + suppressClose(player); + open(player, path, page); + } - private static void navigateTo(Player player, String path, int page) { - if (player == null) { - return; - } - suppressClose(player); - open(player, path, page); + public static void suppressClose(Player player) { + if (player == null) { + return; } - public static void suppressClose(Player player) { - if (player == null) { - return; - } + UUID playerId = player.getUniqueId(); + long suppressUntil = M.ms() + CLOSE_SUPPRESS_MS; + CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); + J.s(() -> { + Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); + if (current != null && current == suppressUntil) { + CLOSE_SUPPRESS_UNTIL.remove(playerId); + } + }, CLOSE_SUPPRESS_CLEAR_TICKS); + } - UUID playerId = player.getUniqueId(); - long suppressUntil = M.ms() + CLOSE_SUPPRESS_MS; - CLOSE_SUPPRESS_UNTIL.put(playerId, suppressUntil); - J.s(() -> { - Long current = CLOSE_SUPPRESS_UNTIL.get(playerId); - if (current != null && current == suppressUntil) { - CLOSE_SUPPRESS_UNTIL.remove(playerId); - } - }, CLOSE_SUPPRESS_CLEAR_TICKS); + private static boolean consumeCloseSuppression(Player player) { + if (player == null) { + return false; } - private static boolean consumeCloseSuppression(Player player) { - if (player == null) { - return false; - } - - Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); - if (until == null) { - return false; - } - - if (until >= M.ms()) { - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return true; - } - - CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); - return false; + Long until = CLOSE_SUPPRESS_UNTIL.get(player.getUniqueId()); + if (until == null) { + return false; } - private static void onGuiClosed(Player player, String currentPath) { - if (player == null) { - return; - } - - Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); - if (consumeCloseSuppression(player)) { - return; - } - - if (AdaptConfig.get().isEscClosesAllGuis()) { - return; - } + if (until >= M.ms()) { + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return true; + } - String safePath = normalizePath(currentPath); - if (safePath.isBlank()) { - return; - } + CLOSE_SUPPRESS_UNTIL.remove(player.getUniqueId()); + return false; + } - String parent = parentPath(safePath); - J.s(() -> { - if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { - open(player, parent, 0); - } - }, 1); + private static void onGuiClosed(Player player, String currentPath) { + if (player == null) { + return; } - private record FieldEntry(Field field, String path, Object value, ElementDescriptor descriptor, List docs) { + Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString()); + if (consumeCloseSuppression(player)) { + return; } - private record ElementDescriptor(ElementKind kind, boolean editable) { + if (AdaptConfig.get().isEscClosesAllGuis()) { + return; } - private record SectionIndexEntry(String path, String displayName, Material material, String lore) { + String safePath = normalizePath(currentPath); + if (safePath.isBlank()) { + return; } - private record SectionTarget(String sourceTag, Object sectionObject) { - } + String parent = parentPath(safePath); + J.s(() -> { + if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { + open(player, parent, 0); + } + }, 1); + } - private record EditTarget( - String sourceTag, - Object rootObject, - String objectPath, - File file, - BooleanSupplier reload, - Runnable afterReload - ) { + public record ParseResult(boolean success, Object value, String error) { + public static ParseResult ok(Object value) { + return new ParseResult(true, value, ""); } - private enum ElementKind { - BOOLEAN, - NUMBER, - STRING, - ENUM, - SECTION, - MAP, - LIST, - UNSUPPORTED + public static ParseResult fail(String error) { + return new ParseResult(false, null, error == null ? "Invalid value." : error); } + } - public record ParseResult(boolean success, Object value, String error) { - public static ParseResult ok(Object value) { - return new ParseResult(true, value, ""); - } - - public static ParseResult fail(String error) { - return new ParseResult(false, null, error == null ? "Invalid value." : error); - } - } } diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGuiTypes.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGuiTypes.java new file mode 100644 index 000000000..0eb3653ec --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGuiTypes.java @@ -0,0 +1,43 @@ +package art.arcane.adapt.content.gui; + +import org.bukkit.Material; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.List; +import java.util.function.BooleanSupplier; + +enum ElementKind { + BOOLEAN, + NUMBER, + STRING, + ENUM, + SECTION, + MAP, + LIST, + UNSUPPORTED +} + +record FieldEntry(Field field, String path, Object value, + ElementDescriptor descriptor, List docs) { +} + +record ElementDescriptor(ElementKind kind, boolean editable) { +} + +record SectionIndexEntry(String path, String displayName, Material material, + String lore) { +} + +record SectionTarget(String sourceTag, Object sectionObject) { +} + +record EditTarget( + String sourceTag, + Object rootObject, + String objectPath, + File file, + BooleanSupplier reload, + Runnable afterReload +) { +} diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGuiValueCodec.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGuiValueCodec.java new file mode 100644 index 000000000..359816b53 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGuiValueCodec.java @@ -0,0 +1,215 @@ +package art.arcane.adapt.content.gui; + +import java.util.*; + +final class ConfigGuiValueCodec { + private ConfigGuiValueCodec() { + } + + static ConfigGui.ParseResult parseInputValue(Class type, String raw) { + if (type == null) { + return ConfigGui.ParseResult.fail("Unknown target type."); + } + + Class normalized = normalizeType(type); + String trimmed = raw == null ? "" : raw.trim(); + + try { + if (normalized == String.class) { + return ConfigGui.ParseResult.ok(raw == null ? "" : raw); + } + + if (normalized == Character.class) { + if (trimmed.length() != 1) { + return ConfigGui.ParseResult.fail("Expected exactly one character."); + } + return ConfigGui.ParseResult.ok(trimmed.charAt(0)); + } + + if (normalized == Boolean.class) { + if (trimmed.equalsIgnoreCase("true") || trimmed.equalsIgnoreCase("yes") || trimmed.equalsIgnoreCase("on")) { + return ConfigGui.ParseResult.ok(true); + } + if (trimmed.equalsIgnoreCase("false") || trimmed.equalsIgnoreCase("no") || trimmed.equalsIgnoreCase("off")) { + return ConfigGui.ParseResult.ok(false); + } + return ConfigGui.ParseResult.fail("Expected boolean value: true/false."); + } + + if (normalized.isEnum()) { + Object constant = parseEnumConstant(normalized, trimmed); + if (constant == null) { + return ConfigGui.ParseResult.fail("Expected one of: " + enumConstants(normalized)); + } + return ConfigGui.ParseResult.ok(constant); + } + + if (normalized == Integer.class) { + return ConfigGui.ParseResult.ok(Integer.parseInt(trimmed)); + } + if (normalized == Long.class) { + return ConfigGui.ParseResult.ok(Long.parseLong(trimmed)); + } + if (normalized == Double.class) { + double v = Double.parseDouble(trimmed); + if (!Double.isFinite(v)) { + return ConfigGui.ParseResult.fail("Expected a finite number."); + } + return ConfigGui.ParseResult.ok(v); + } + if (normalized == Float.class) { + float v = Float.parseFloat(trimmed); + if (!Float.isFinite(v)) { + return ConfigGui.ParseResult.fail("Expected a finite number."); + } + return ConfigGui.ParseResult.ok(v); + } + if (normalized == Short.class) { + return ConfigGui.ParseResult.ok(Short.parseShort(trimmed)); + } + if (normalized == Byte.class) { + return ConfigGui.ParseResult.ok(Byte.parseByte(trimmed)); + } + } catch (Throwable e) { + return ConfigGui.ParseResult.fail("Invalid value for type " + typeName(type) + "."); + } + + return ConfigGui.ParseResult.fail("Unsupported type: " + typeName(type) + "."); + } + + static String typeName(Class type) { + if (type == null) { + return "unknown"; + } + + Class normalized = normalizeType(type); + if (normalized.isEnum()) { + return "enum"; + } + return normalized.getSimpleName().toLowerCase(Locale.ROOT); + } + + static Object coerceValue(Object value, Class targetType) { + if (value == null) { + return null; + } + + Class normalizedTarget = normalizeType(targetType); + Class valueType = value.getClass(); + if (normalizedTarget.isAssignableFrom(valueType)) { + return value; + } + + ConfigGui.ParseResult parsed = parseInputValue(targetType, String.valueOf(value)); + return parsed.success() ? parsed.value() : value; + } + + static Class normalizeType(Class type) { + if (type == null || !type.isPrimitive()) { + return type; + } + + if (type == int.class) return Integer.class; + if (type == long.class) return Long.class; + if (type == double.class) return Double.class; + if (type == float.class) return Float.class; + if (type == short.class) return Short.class; + if (type == byte.class) return Byte.class; + if (type == boolean.class) return Boolean.class; + if (type == char.class) return Character.class; + return type; + } + + static boolean isNumericType(Class type) { + return type == Integer.class + || type == Long.class + || type == Double.class + || type == Float.class + || type == Short.class + || type == Byte.class; + } + + static boolean isSectionType(Class type) { + Class normalized = normalizeType(type); + if (normalized == null) { + return false; + } + + if (normalized.isPrimitive() || normalized.isEnum()) { + return false; + } + + if (normalized == String.class || normalized == Character.class || normalized == Boolean.class || isNumericType(normalized)) { + return false; + } + + if (Map.class.isAssignableFrom(normalized) || Collection.class.isAssignableFrom(normalized) || normalized.isArray()) { + return false; + } + + return true; + } + + static Object cycleEnum(Class enumType, Object current, int direction) { + Class normalized = normalizeType(enumType); + if (normalized == null || !normalized.isEnum()) { + return null; + } + + Object[] constants = normalized.getEnumConstants(); + if (constants == null || constants.length == 0) { + return null; + } + + int currentIndex = 0; + if (current != null) { + for (int i = 0; i < constants.length; i++) { + if (Objects.equals(constants[i], current)) { + currentIndex = i; + break; + } + } + } + + int nextIndex = currentIndex + direction; + if (nextIndex < 0) { + nextIndex = constants.length - 1; + } else if (nextIndex >= constants.length) { + nextIndex = 0; + } + return constants[nextIndex]; + } + + static Object parseEnumConstant(Class enumType, String value) { + if (enumType == null || !enumType.isEnum() || value == null) { + return null; + } + + for (Object constant : enumType.getEnumConstants()) { + if (constant == null) { + continue; + } + + if (constant.toString().equalsIgnoreCase(value)) { + return constant; + } + } + + return null; + } + + static String enumConstants(Class enumType) { + if (enumType == null || !enumType.isEnum()) { + return ""; + } + + List values = new ArrayList<>(); + for (Object constant : enumType.getEnumConstants()) { + if (constant == null) { + continue; + } + values.add(constant.toString()); + } + return String.join(", ", values); + } +} diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 4b7100f38..2805dc6d8 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -26,16 +26,16 @@ import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.UIElement; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.inventorygui.GuiEffects; import art.arcane.adapt.util.common.inventorygui.GuiLayout; import art.arcane.adapt.util.common.inventorygui.GuiTheme; -import art.arcane.volmlib.util.inventorygui.Element; -import art.arcane.volmlib.util.inventorygui.Window; -import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.volmlib.util.format.ColorFormatter; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.inventorygui.UIElement; +import art.arcane.volmlib.util.inventorygui.UIWindow; +import art.arcane.volmlib.util.inventorygui.Window; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -45,184 +45,185 @@ import java.util.Locale; public class SkillsGui { - private static final int PAGE_JUMP = 5; + private static final int PAGE_JUMP = 5; - public static void open(Player player) { - open(player, 0); - } + public static void open(Player player) { + open(player, 0); + } - public static void open(Player player, int page) { - if (!J.isPrimaryThread()) { - int targetPage = page; - J.s(() -> open(player, targetPage)); - return; - } + public static void open(Player player, int page) { + if (!J.isPrimaryThread()) { + int targetPage = page; + J.s(() -> open(player, targetPage)); + return; + } - AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(player); - if (adaptPlayer == null) { - Adapt.error("Failed to open skills gui for " + player.getName() + " because they are not Online, Were Kicked, Or are a fake player."); - return; - } + AdaptPlayer adaptPlayer = Adapt.instance.getAdaptServer().getPlayer(player); + if (adaptPlayer == null) { + Adapt.error("Failed to open skills gui for " + player.getName() + " because they are not Online, Were Kicked, Or are a fake player."); + return; + } - List entries = new ArrayList<>(); - for (Skill skill : adaptPlayer.getServer().getSkillRegistry().getSkills()) { - if (skill == null) { - continue; - } - if (!skill.isEnabled()) { - continue; - } - PlayerSkillLine line = adaptPlayer.getData().getSkillLineNullable(skill.getName()); - if (line == null) { - continue; - } - if (skill.hasBlacklistPermission(adaptPlayer.getPlayer(), skill) || line.getLevel() < 0) { - continue; - } - - int adaptationLevel = sumAdaptationLevels(line); - if (!hasVisibleProgress(line, adaptationLevel)) { - continue; - } - - entries.add(new SkillPageEntry(skill, line, adaptationLevel)); - } + List entries = new ArrayList<>(); + for (Skill skill : adaptPlayer.getServer().getSkillRegistry().getSkills()) { + if (skill == null) { + continue; + } + if (!skill.isEnabled()) { + continue; + } + PlayerSkillLine line = adaptPlayer.getData().getSkillLineNullable(skill.getName()); + if (line == null) { + continue; + } + if (skill.hasBlacklistPermission(adaptPlayer.getPlayer(), skill) || line.getLevel() < 0) { + continue; + } + + int adaptationLevel = sumAdaptationLevels(line); + if (!hasVisibleProgress(line, adaptationLevel)) { + continue; + } + + entries.add(new SkillPageEntry(skill, line, adaptationLevel)); + } - entries.sort( - Comparator.comparing((SkillPageEntry entry) -> normalizeSortKey(entry.skill().getDisplayName())) - .thenComparing(entry -> entry.skill().getName(), String.CASE_INSENSITIVE_ORDER) - ); - - boolean reserveNavigation = false; - GuiLayout.PagePlan plan = GuiLayout.plan(entries.size(), reserveNavigation); - int currentPage = GuiLayout.clampPage(page, plan.pageCount()); - int start = currentPage * plan.itemsPerPage(); - int end = Math.min(entries.size(), start + plan.itemsPerPage()); - - UIWindow w = new UIWindow(Adapt.instance, player); - GuiTheme.apply(w, "/"); - w.setViewportHeight(plan.rows()); - - if (entries.isEmpty()) { - w.setElement(0, 0, new UIElement("skills-empty") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.GRAY + "No skills available") - .addLore(C.DARK_GRAY + "No eligible skills were found for this player.")); - } else { - List reveal = new ArrayList<>(); - for (int row = 0; row < plan.contentRows(); row++) { - int rowStart = start + (row * GuiLayout.WIDTH); - if (rowStart >= end) { - break; - } - - int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); - for (int i = 0; i < rowCount; i++) { - SkillPageEntry entry = entries.get(rowStart + i); - int pos = GuiLayout.centeredPosition(i, rowCount); - Element element = new UIElement("skill-" + entry.skill().getName()) - .setMaterial(new MaterialBlock(entry.skill().getIcon())) - .setBaseItemStack(entry.skill().getModel().toItemStack()) - .setName(entry.skill().getDisplayName(entry.line().getLevel())) - .setProgress(1D) - .addLore(C.ITALIC + "" + C.GRAY + entry.skill().getDescription()) - .addLore(C.UNDERLINE + "" + C.WHITE + entry.line().getKnowledge() + C.RESET + " " + C.GRAY + Localizer.dLocalize("snippets.gui.knowledge")) - .addLore(C.ITALIC + "" + C.GRAY + Localizer.dLocalize("snippets.gui.power_used") + " " + C.DARK_GREEN + entry.adaptationLevel()) - .onLeftClick((e) -> entry.skill().openGui(player)); - reveal.add(new GuiEffects.Placement(pos, row, element)); - } - } - GuiEffects.applyReveal(w, reveal); + entries.sort( + Comparator.comparing((SkillPageEntry entry) -> normalizeSortKey(entry.skill().getDisplayName())) + .thenComparing(entry -> entry.skill().getName(), String.CASE_INSENSITIVE_ORDER) + ); + + boolean reserveNavigation = false; + GuiLayout.PagePlan plan = GuiLayout.plan(entries.size(), reserveNavigation); + int currentPage = GuiLayout.clampPage(page, plan.pageCount()); + int start = currentPage * plan.itemsPerPage(); + int end = Math.min(entries.size(), start + plan.itemsPerPage()); + + UIWindow w = new UIWindow(Adapt.instance, player); + GuiTheme.apply(w, "/"); + w.setViewportHeight(plan.rows()); + + if (entries.isEmpty()) { + w.setElement(0, 0, new UIElement("skills-empty") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.GRAY + "No skills available") + .addLore(C.DARK_GRAY + "No eligible skills were found for this player.")); + } else { + List reveal = new ArrayList<>(); + for (int row = 0; row < plan.contentRows(); row++) { + int rowStart = start + (row * GuiLayout.WIDTH); + if (rowStart >= end) { + break; } - if (plan.hasNavigationRow()) { - int navRow = plan.rows() - 1; - applyPageControls(w, player, navRow, currentPage, plan.pageCount(), entries.size(), start, end); - + int rowCount = Math.min(GuiLayout.WIDTH, end - rowStart); + for (int i = 0; i < rowCount; i++) { + SkillPageEntry entry = entries.get(rowStart + i); + int pos = GuiLayout.centeredPosition(i, rowCount); + Element element = new UIElement("skill-" + entry.skill().getName()) + .setMaterial(new MaterialBlock(entry.skill().getIcon())) + .setBaseItemStack(entry.skill().getModel().toItemStack()) + .setName(entry.skill().getDisplayName(entry.line().getLevel())) + .setProgress(1D) + .addLore(C.ITALIC + "" + C.GRAY + entry.skill().getDescription()) + .addLore(C.UNDERLINE + "" + C.WHITE + entry.line().getKnowledge() + C.RESET + " " + C.GRAY + Localizer.dLocalize("snippets.gui.knowledge")) + .addLore(C.ITALIC + "" + C.GRAY + Localizer.dLocalize("snippets.gui.power_used") + " " + C.DARK_GREEN + entry.adaptationLevel()) + .onLeftClick((e) -> entry.skill().openGui(player)); + reveal.add(new GuiEffects.Placement(pos, row, element)); } - - w.setTitle(Localizer.dLocalize("snippets.gui.level") + " " + (int) XP.getLevelForXp(adaptPlayer.getData().getMasterXp()) + " (" + adaptPlayer.getData().getUsedPower() + "/" + adaptPlayer.getData().getMaxPower() + " " + Localizer.dLocalize("snippets.gui.power_used") + ")"); - w.open(); - w.onClosed((e) -> Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString())); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } + GuiEffects.applyReveal(w, reveal); } - private record SkillPageEntry(Skill skill, PlayerSkillLine line, int adaptationLevel) { - } + if (plan.hasNavigationRow()) { + int navRow = plan.rows() - 1; + applyPageControls(w, player, navRow, currentPage, plan.pageCount(), entries.size(), start, end); - private static int sumAdaptationLevels(PlayerSkillLine line) { - int total = 0; - for (PlayerAdaptation adaptation : line.getAdaptations().values()) { - if (adaptation == null) { - continue; - } - total += Math.max(0, adaptation.getLevel()); - } - return total; } - private static boolean hasVisibleProgress(PlayerSkillLine line, int adaptationLevel) { - return line.getXp() > 0D || line.getKnowledge() > 0L || adaptationLevel > 0; + w.setTitle(Localizer.dLocalize("snippets.gui.level") + " " + (int) XP.getLevelForXp(adaptPlayer.getData().getMasterXp()) + " (" + adaptPlayer.getData().getUsedPower() + "/" + adaptPlayer.getData().getMaxPower() + " " + Localizer.dLocalize("snippets.gui.power_used") + ")"); + w.open(); + w.onClosed((e) -> Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString())); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } + + private static int sumAdaptationLevels(PlayerSkillLine line) { + int total = 0; + for (PlayerAdaptation adaptation : line.getAdaptations().values()) { + if (adaptation == null) { + continue; + } + total += Math.max(0, adaptation.getLevel()); } + return total; + } - private static String normalizeSortKey(String value) { - if (value == null) { - return ""; - } + private static boolean hasVisibleProgress(PlayerSkillLine line, int adaptationLevel) { + return line.getXp() > 0D || line.getKnowledge() > 0L || adaptationLevel > 0; + } - String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); - return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + private static String normalizeSortKey(String value) { + if (value == null) { + return ""; } - private static void applyPageControls( - Window window, - Player player, - int navRow, - int currentPage, - int pageCount, - int totalEntries, - int start, - int end - ) { - if (pageCount <= 1) { - return; - } - - int jumpBack = Math.max(0, currentPage - PAGE_JUMP); - int jumpForward = Math.min(pageCount - 1, currentPage + PAGE_JUMP); - - if (currentPage > 0) { - window.setElement(-4, navRow, new UIElement("skills-prev") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Previous") - .addLore(C.GRAY + "Right click: jump -" + PAGE_JUMP + " pages") - .onLeftClick((e) -> open(player, currentPage - 1)) - .onRightClick((e) -> open(player, jumpBack))); - window.setElement(-3, navRow, new UIElement("skills-first") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "First") - .onLeftClick((e) -> open(player, 0))); - } + String normalized = ColorFormatter.stripColor(value).toLowerCase(Locale.ROOT).trim(); + return normalized.replaceFirst("^[^\\p{L}\\p{N}]+", ""); + } + + private static void applyPageControls( + Window window, + Player player, + int navRow, + int currentPage, + int pageCount, + int totalEntries, + int start, + int end + ) { + if (pageCount <= 1) { + return; + } - if (currentPage < pageCount - 1) { - window.setElement(4, navRow, new UIElement("skills-next") - .setMaterial(new MaterialBlock(Material.ARROW)) - .setName(C.WHITE + "Next") - .addLore(C.GRAY + "Right click: jump +" + PAGE_JUMP + " pages") - .onLeftClick((e) -> open(player, currentPage + 1)) - .onRightClick((e) -> open(player, jumpForward))); - window.setElement(3, navRow, new UIElement("skills-last") - .setMaterial(new MaterialBlock(Material.LECTERN)) - .setName(C.GRAY + "Last") - .onLeftClick((e) -> open(player, pageCount - 1))); - } + int jumpBack = Math.max(0, currentPage - PAGE_JUMP); + int jumpForward = Math.min(pageCount - 1, currentPage + PAGE_JUMP); + + if (currentPage > 0) { + window.setElement(-4, navRow, new UIElement("skills-prev") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Previous") + .addLore(C.GRAY + "Right click: jump -" + PAGE_JUMP + " pages") + .onLeftClick((e) -> open(player, currentPage - 1)) + .onRightClick((e) -> open(player, jumpBack))); + window.setElement(-3, navRow, new UIElement("skills-first") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "First") + .onLeftClick((e) -> open(player, 0))); + } - int from = totalEntries <= 0 ? 0 : (start + 1); - int to = totalEntries <= 0 ? 0 : end; - window.setElement(-1, navRow, new UIElement("skills-page-info") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + pageCount) - .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + totalEntries) - .setProgress(1D)); + if (currentPage < pageCount - 1) { + window.setElement(4, navRow, new UIElement("skills-next") + .setMaterial(new MaterialBlock(Material.ARROW)) + .setName(C.WHITE + "Next") + .addLore(C.GRAY + "Right click: jump +" + PAGE_JUMP + " pages") + .onLeftClick((e) -> open(player, currentPage + 1)) + .onRightClick((e) -> open(player, jumpForward))); + window.setElement(3, navRow, new UIElement("skills-last") + .setMaterial(new MaterialBlock(Material.LECTERN)) + .setName(C.GRAY + "Last") + .onLeftClick((e) -> open(player, pageCount - 1))); } + + int from = totalEntries <= 0 ? 0 : (start + 1); + int to = totalEntries <= 0 ? 0 : end; + window.setElement(-1, navRow, new UIElement("skills-page-info") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.AQUA + "Page " + (currentPage + 1) + "/" + pageCount) + .addLore(C.GRAY + "Showing " + from + "-" + to + " of " + totalEntries) + .setProgress(1D)); + } + + private record SkillPageEntry(Skill skill, PlayerSkillLine line, + int adaptationLevel) { + } } diff --git a/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java b/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java index b8e053ee8..b4e74de55 100644 --- a/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundEnderPearl.java @@ -36,68 +36,68 @@ @AllArgsConstructor @Data public class BoundEnderPearl implements DataItem { - public static BoundEnderPearl io = new BoundEnderPearl(); + public static BoundEnderPearl io = new BoundEnderPearl(); - public static Block getBlock(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getBlock(); - } - - return null; + public static Block getBlock(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getBlock(); } - public static void setData(ItemStack item, Block t) { - io.setData(item, new Data(t)); - } + return null; + } - public static ItemStack withData(Block t) { - return io.withData(new Data(t)); - } + public static void setData(ItemStack item, Block t) { + io.setData(item, new Data(t)); + } - public static boolean isBindableItem(ItemStack t) { - if (t.getType().equals(Material.ENDER_PEARL)) { - if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { - if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_ender_peral.name"))) { - Adapt.verbose("Enderpearl is bindable: " + t.getType().name()); - return true; - } - } - } - return false; - } - - @Override - public Material getMaterial() { - return Material.ENDER_PEARL; - } + public static ItemStack withData(Block t) { + return io.withData(new Data(t)); + } - @Override - public Class getType() { - return BoundEnderPearl.Data.class; - } - - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.bound_ender_peral.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_ender_peral.usage1")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_ender_peral.usage2")); - } - - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); - meta.setDisplayName(Localizer.dLocalize("items.bound_ender_peral.name")); - - } - - @AllArgsConstructor - @lombok.Data - public static class Data { - private Block block; - - public static BoundEnderPearl.Data at(Block l) { - return new BoundEnderPearl.Data(l); + public static boolean isBindableItem(ItemStack t) { + if (t.getType().equals(Material.ENDER_PEARL)) { + if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { + if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_ender_peral.name"))) { + Adapt.verbose("Enderpearl is bindable: " + t.getType().name()); + return true; } + } + } + return false; + } + + @Override + public Material getMaterial() { + return Material.ENDER_PEARL; + } + + @Override + public Class getType() { + return BoundEnderPearl.Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.bound_ender_peral.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_ender_peral.usage1")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_ender_peral.usage2")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); + meta.setDisplayName(Localizer.dLocalize("items.bound_ender_peral.name")); + + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private Block block; + + public static BoundEnderPearl.Data at(Block l) { + return new BoundEnderPearl.Data(l); } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java b/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java index a8a6526ae..5b0dca711 100644 --- a/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundEyeOfEnder.java @@ -36,67 +36,67 @@ @AllArgsConstructor @Data public class BoundEyeOfEnder implements DataItem { - public static BoundEyeOfEnder io = new BoundEyeOfEnder(); + public static BoundEyeOfEnder io = new BoundEyeOfEnder(); - public static Location getLocation(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getLocation(); - } - - return null; + public static Location getLocation(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getLocation(); } - public static void setData(ItemStack item, Location t) { - io.setData(item, new Data(t)); - } + return null; + } - public static ItemStack withData(Location t) { - return io.withData(new Data(t)); - } + public static void setData(ItemStack item, Location t) { + io.setData(item, new Data(t)); + } + + public static ItemStack withData(Location t) { + return io.withData(new Data(t)); + } - public static boolean isBindableItem(ItemStack t) { - if (t.getType().equals(Material.ENDER_EYE)) { - if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { - if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_eye_of_ender.name"))) { - Adapt.verbose("Eye of ender is bindable: " + t.getType().name()); - return true; - } - } + public static boolean isBindableItem(ItemStack t) { + if (t.getType().equals(Material.ENDER_EYE)) { + if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { + if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_eye_of_ender.name"))) { + Adapt.verbose("Eye of ender is bindable: " + t.getType().name()); + return true; } - return false; + } } + return false; + } - @Override - public Material getMaterial() { - return Material.ENDER_EYE; - } + @Override + public Material getMaterial() { + return Material.ENDER_EYE; + } - @Override - public Class getType() { - return BoundEyeOfEnder.Data.class; - } + @Override + public Class getType() { + return BoundEyeOfEnder.Data.class; + } - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.bound_eye_of_ender.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_eye_of_ender.usage1")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_eye_of_ender.usage2")); - } + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.bound_eye_of_ender.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_eye_of_ender.usage1")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_eye_of_ender.usage2")); + } - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); - meta.setDisplayName(Localizer.dLocalize("items.bound_eye_of_ender.name")); - } + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); + meta.setDisplayName(Localizer.dLocalize("items.bound_eye_of_ender.name")); + } - @AllArgsConstructor - @lombok.Data - public static class Data { - private Location location; + @AllArgsConstructor + @lombok.Data + public static class Data { + private Location location; - public static BoundEyeOfEnder.Data at(Location l) { - return new BoundEyeOfEnder.Data(l); - } + public static BoundEyeOfEnder.Data at(Location l) { + return new BoundEyeOfEnder.Data(l); } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java b/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java index c8546992b..42eef5dad 100644 --- a/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundRedstoneTorch.java @@ -36,74 +36,74 @@ @AllArgsConstructor @Data public class BoundRedstoneTorch implements DataItem { - public static BoundRedstoneTorch io = new BoundRedstoneTorch(); + public static BoundRedstoneTorch io = new BoundRedstoneTorch(); - public static Location getLocation(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getLocation(); - } - - return null; - } - - /* - renamed from hasData as the types are the same (ItemStack -> boolean), but this is static - */ - public static boolean hasItemData(ItemStack stack) { - return io.hasData(stack); - } - - public static void setData(ItemStack item, Location t) { - io.setData(item, new Data(t)); - } - - public static ItemStack withData(Location t) { - return io.withData(new Data(t)); + public static Location getLocation(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getLocation(); } - public static boolean isBindableItem(ItemStack t) { - if (t.getType().equals(Material.REDSTONE_TORCH)) { - if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { - if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_redstone_torch.name"))) { - Adapt.verbose("Torch is bindable: " + t.getType().name()); - return true; - } - } + return null; + } + + /* + renamed from hasData as the types are the same (ItemStack -> boolean), but this is static + */ + public static boolean hasItemData(ItemStack stack) { + return io.hasData(stack); + } + + public static void setData(ItemStack item, Location t) { + io.setData(item, new Data(t)); + } + + public static ItemStack withData(Location t) { + return io.withData(new Data(t)); + } + + public static boolean isBindableItem(ItemStack t) { + if (t.getType().equals(Material.REDSTONE_TORCH)) { + if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { + if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_redstone_torch.name"))) { + Adapt.verbose("Torch is bindable: " + t.getType().name()); + return true; } - return false; - } - - @Override - public Material getMaterial() { - return Material.REDSTONE_TORCH; + } } - - @Override - public Class getType() { - return BoundRedstoneTorch.Data.class; - } - - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.bound_redstone_torch.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_redstone_torch.usage1")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_redstone_torch.usage2")); - } - - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); - meta.setDisplayName(Localizer.dLocalize("items.bound_redstone_torch.name")); - } - - @AllArgsConstructor - @lombok.Data - public static class Data { - private Location location; - - public static BoundRedstoneTorch.Data at(Location l) { - return new BoundRedstoneTorch.Data(l); - } + return false; + } + + @Override + public Material getMaterial() { + return Material.REDSTONE_TORCH; + } + + @Override + public Class getType() { + return BoundRedstoneTorch.Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.bound_redstone_torch.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_redstone_torch.usage1")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_redstone_torch.usage2")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); + meta.setDisplayName(Localizer.dLocalize("items.bound_redstone_torch.name")); + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private Location location; + + public static BoundRedstoneTorch.Data at(Location l) { + return new BoundRedstoneTorch.Data(l); } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java b/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java index de2b91e1f..cd3383b81 100644 --- a/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java +++ b/src/main/java/art/arcane/adapt/content/item/BoundSnowBall.java @@ -36,67 +36,67 @@ @AllArgsConstructor @Data public class BoundSnowBall implements DataItem { - public static BoundSnowBall io = new BoundSnowBall(); + public static BoundSnowBall io = new BoundSnowBall(); - public static Player getPlayer(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getPlayer(); - } - - return null; + public static Player getPlayer(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getPlayer(); } - public static void setData(ItemStack item, Player t) { - io.setData(item, new Data(t)); - } + return null; + } - public static ItemStack withData(Player t) { - return io.withData(new Data(t)); - } + public static void setData(ItemStack item, Player t) { + io.setData(item, new Data(t)); + } - public static boolean isBindableItem(ItemStack t) { - if (t.getType().equals(Material.SNOWBALL)) { - if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { - if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_snowball.name"))) { - Adapt.verbose("Snowball is bindable: " + t.getType().name()); - return true; - } - } - } - return false; - } - - - @Override - public Material getMaterial() { - return Material.SNOWBALL; - } + public static ItemStack withData(Player t) { + return io.withData(new Data(t)); + } - @Override - public Class getType() { - return BoundSnowBall.Data.class; - } - - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.bound_snowball.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.bound_snowball.usage1")); - } - - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); - meta.setDisplayName(Localizer.dLocalize("items.bound_snowball.name")); - } - - @AllArgsConstructor - @lombok.Data - public static class Data { - private Player player; - - public static BoundSnowBall.Data at(Player p) { - return new BoundSnowBall.Data(p); + public static boolean isBindableItem(ItemStack t) { + if (t.getType().equals(Material.SNOWBALL)) { + if (t.getItemMeta() != null && t.getItemMeta().getLore() != null) { + if (t.getItemMeta().getLore().get(0).contains(Localizer.dLocalize("items.bound_snowball.name"))) { + Adapt.verbose("Snowball is bindable: " + t.getType().name()); + return true; } + } + } + return false; + } + + + @Override + public Material getMaterial() { + return Material.SNOWBALL; + } + + @Override + public Class getType() { + return BoundSnowBall.Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.bound_snowball.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.bound_snowball.usage1")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_DYE); + meta.setDisplayName(Localizer.dLocalize("items.bound_snowball.name")); + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private Player player; + + public static BoundSnowBall.Data at(Player p) { + return new BoundSnowBall.Data(p); } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java index 248b3ef07..3b01fd070 100644 --- a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java +++ b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBombItem.java @@ -40,64 +40,64 @@ @AllArgsConstructor @Data public class ChronoTimeBombItem implements DataItem { - public static ChronoTimeBombItem io = new ChronoTimeBombItem(); + public static ChronoTimeBombItem io = new ChronoTimeBombItem(); - public static boolean isBindableItem(ItemStack stack) { - if (stack == null || stack.getItemMeta() == null) { - return false; - } - - if (stack.getType() == Material.LINGERING_POTION) { - return io.hasData(stack); - } - - if (stack.getType() == Material.CLOCK) { - if (Adapt.instance == null) { - return false; - } - - NamespacedKey key = new NamespacedKey(Adapt.instance, Data.class.getCanonicalName().hashCode() + ""); - return stack.getItemMeta().getPersistentDataContainer().has(key, PersistentDataType.STRING); - } - - return false; + public static boolean isBindableItem(ItemStack stack) { + if (stack == null || stack.getItemMeta() == null) { + return false; } - public static ItemStack withData() { - return io.withData(new Data(System.currentTimeMillis())); + if (stack.getType() == Material.LINGERING_POTION) { + return io.hasData(stack); } - @Override - public Material getMaterial() { - return Material.LINGERING_POTION; - } + if (stack.getType() == Material.CLOCK) { + if (Adapt.instance == null) { + return false; + } - @Override - public Class getType() { - return Data.class; + NamespacedKey key = new NamespacedKey(Adapt.instance, Data.class.getCanonicalName().hashCode() + ""); + return stack.getItemMeta().getPersistentDataContainer().has(key, PersistentDataType.STRING); } - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.chrono_time_bomb.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bomb.usage1")); + return false; + } + + public static ItemStack withData() { + return io.withData(new Data(System.currentTimeMillis())); + } + + @Override + public Material getMaterial() { + return Material.LINGERING_POTION; + } + + @Override + public Class getType() { + return Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.chrono_time_bomb.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bomb.usage1")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + if (meta instanceof PotionMeta potionMeta) { + potionMeta.setBasePotionType(PotionType.WEAKNESS); + meta = potionMeta; } - @Override - public void applyMeta(Data data, ItemMeta meta) { - if (meta instanceof PotionMeta potionMeta) { - potionMeta.setBasePotionType(PotionType.WEAKNESS); - meta = potionMeta; - } + meta.addEnchant(Enchantment.BINDING_CURSE, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlags.HIDE_POTION_EFFECTS); + meta.setDisplayName(Localizer.dLocalize("items.chrono_time_bomb.name")); + } - meta.addEnchant(Enchantment.BINDING_CURSE, 1, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlags.HIDE_POTION_EFFECTS); - meta.setDisplayName(Localizer.dLocalize("items.chrono_time_bomb.name")); - } - - @AllArgsConstructor - @lombok.Data - public static class Data { - private long created; - } + @AllArgsConstructor + @lombok.Data + public static class Data { + private long created; + } } diff --git a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java index c4131a790..976ebf877 100644 --- a/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java +++ b/src/main/java/art/arcane/adapt/content/item/ChronoTimeBottle.java @@ -39,59 +39,59 @@ @AllArgsConstructor @Data public class ChronoTimeBottle implements DataItem { - public static ChronoTimeBottle io = new ChronoTimeBottle(); + public static ChronoTimeBottle io = new ChronoTimeBottle(); - public static boolean isBindableItem(ItemStack stack) { - return stack != null && stack.getType() == Material.POTION && io.hasData(stack); - } + public static boolean isBindableItem(ItemStack stack) { + return stack != null && stack.getType() == Material.POTION && io.hasData(stack); + } - public static double getStoredSeconds(ItemStack stack) { - Data data = io.getData(stack); - return data == null ? 0 : Math.max(0, data.getStoredSeconds()); - } + public static double getStoredSeconds(ItemStack stack) { + Data data = io.getData(stack); + return data == null ? 0 : Math.max(0, data.getStoredSeconds()); + } - public static void setStoredSeconds(ItemStack stack, double seconds) { - io.setData(stack, new Data(Math.max(0, seconds))); - } - - public static ItemStack withStoredSeconds(double seconds) { - return io.withData(new Data(Math.max(0, seconds))); - } + public static void setStoredSeconds(ItemStack stack, double seconds) { + io.setData(stack, new Data(Math.max(0, seconds))); + } - @Override - public Material getMaterial() { - return Material.POTION; - } + public static ItemStack withStoredSeconds(double seconds) { + return io.withData(new Data(Math.max(0, seconds))); + } - @Override - public Class getType() { - return Data.class; - } + @Override + public Material getMaterial() { + return Material.POTION; + } - @Override - public void applyLore(Data data, List lore) { - lore.add(C.WHITE + Localizer.dLocalize("items.chrono_time_bottle.name")); - lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bottle.usage1")); - lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bottle.usage2")); - lore.add(C.AQUA + Localizer.dLocalize("items.chrono_time_bottle.stored") + ": " + C.WHITE + Form.duration((long) (Math.max(0, data.getStoredSeconds()) * 1000D), 1)); - } + @Override + public Class getType() { + return Data.class; + } - @Override - public void applyMeta(Data data, ItemMeta meta) { - if (meta instanceof PotionMeta potionMeta) { - potionMeta.setBasePotionType(PotionType.WATER); - potionMeta.setColor(Color.fromRGB(235, 245, 255)); - meta = potionMeta; - } + @Override + public void applyLore(Data data, List lore) { + lore.add(C.WHITE + Localizer.dLocalize("items.chrono_time_bottle.name")); + lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bottle.usage1")); + lore.add(C.GRAY + Localizer.dLocalize("items.chrono_time_bottle.usage2")); + lore.add(C.AQUA + Localizer.dLocalize("items.chrono_time_bottle.stored") + ": " + C.WHITE + Form.duration((long) (Math.max(0, data.getStoredSeconds()) * 1000D), 1)); + } - meta.addEnchant(Enchantment.BINDING_CURSE, 1, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlags.HIDE_POTION_EFFECTS); - meta.setDisplayName(Localizer.dLocalize("items.chrono_time_bottle.name")); + @Override + public void applyMeta(Data data, ItemMeta meta) { + if (meta instanceof PotionMeta potionMeta) { + potionMeta.setBasePotionType(PotionType.WATER); + potionMeta.setColor(Color.fromRGB(235, 245, 255)); + meta = potionMeta; } - @AllArgsConstructor - @lombok.Data - public static class Data { - private double storedSeconds; - } + meta.addEnchant(Enchantment.BINDING_CURSE, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlags.HIDE_POTION_EFFECTS); + meta.setDisplayName(Localizer.dLocalize("items.chrono_time_bottle.name")); + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private double storedSeconds; + } } diff --git a/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java index 37c22b962..c3d08fbef 100644 --- a/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java +++ b/src/main/java/art/arcane/adapt/content/item/ExperienceOrb.java @@ -39,75 +39,75 @@ @AllArgsConstructor @Data public class ExperienceOrb implements DataItem { - public static ExperienceOrb io = new ExperienceOrb(); - - public static Data get(ItemStack is) { - return io.getData(is); - } - - public static void set(ItemStack item, String skill, double xp) { - io.setData(item, new Data(skill, xp)); - } - - public static ItemStack with(String skill, double xp) { - return io.withData(new Data(skill, xp)); + public static ExperienceOrb io = new ExperienceOrb(); + + public static Data get(ItemStack is) { + return io.getData(is); + } + + public static void set(ItemStack item, String skill, double xp) { + io.setData(item, new Data(skill, xp)); + } + + public static ItemStack with(String skill, double xp) { + return io.withData(new Data(skill, xp)); + } + + public static ItemStack with(Map experienceMap) { + return io.withData(new Data(experienceMap)); + } + + @Override + public Material getMaterial() { + return Material.SNOWBALL; + } + + @Override + public Class getType() { + return ExperienceOrb.Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + for (Map.Entry entry : data.getExperienceMap().entrySet()) { + String skill = entry.getKey(); + double experience = entry.getValue(); + lore.add(C.WHITE + Form.capitalize(Localizer.dLocalize("snippets.experience_orb.contains")) + " " + C.UNDERLINE + C.WHITE + Form.f(experience, 0) + " " + Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skill).getDisplayName() + C.GRAY + " " + Localizer.dLocalize("snippets.experience_orb.xp")); } - - public static ItemStack with(Map experienceMap) { - return io.withData(new Data(experienceMap)); - } - - @Override - public Material getMaterial() { - return Material.SNOWBALL; - } - - @Override - public Class getType() { - return ExperienceOrb.Data.class; + lore.add(C.LIGHT_PURPLE + Localizer.dLocalize("snippets.experience_orb.rightclick") + " " + C.GRAY + Localizer.dLocalize("snippets.experience_orb.togainxp")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); + meta.setDisplayName(Localizer.dLocalize("snippets.experience_orb.xporb")); + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private Map experienceMap; + + public Data(String skill, double experience) { + this.experienceMap = new HashMap<>(); + this.experienceMap.put(skill, experience); } - @Override - public void applyLore(Data data, List lore) { - for (Map.Entry entry : data.getExperienceMap().entrySet()) { - String skill = entry.getKey(); - double experience = entry.getValue(); - lore.add(C.WHITE + Form.capitalize(Localizer.dLocalize("snippets.experience_orb.contains")) + " " + C.UNDERLINE + C.WHITE + Form.f(experience, 0) + " " + Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skill).getDisplayName() + C.GRAY + " " + Localizer.dLocalize("snippets.experience_orb.xp")); - } - lore.add(C.LIGHT_PURPLE + Localizer.dLocalize("snippets.experience_orb.rightclick") + " " + C.GRAY + Localizer.dLocalize("snippets.experience_orb.togainxp")); + public String getSkill() { + return experienceMap.keySet().iterator().next(); } - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); - meta.setDisplayName(Localizer.dLocalize("snippets.experience_orb.xporb")); + public double getExperience() { + return experienceMap.values().iterator().next(); } - @AllArgsConstructor - @lombok.Data - public static class Data { - private Map experienceMap; - - public Data(String skill, double experience) { - this.experienceMap = new HashMap<>(); - this.experienceMap.put(skill, experience); - } - - public String getSkill() { - return experienceMap.keySet().iterator().next(); - } - - public double getExperience() { - return experienceMap.values().iterator().next(); - } - - public void apply(Player p) { - for (Map.Entry entry : experienceMap.entrySet()) { - String skill = entry.getKey(); - double experience = entry.getValue(); - Adapt.instance.getAdaptServer().getPlayer(p).getSkillLine(skill).giveXPFresh(Adapt.instance.getAdaptServer().getPlayer(p).getNot(), experience); - } - } + public void apply(Player p) { + for (Map.Entry entry : experienceMap.entrySet()) { + String skill = entry.getKey(); + double experience = entry.getValue(); + Adapt.instance.getAdaptServer().getPlayer(p).getSkillLine(skill).giveXPFresh(Adapt.instance.getAdaptServer().getPlayer(p).getNot(), experience); + } } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/ItemListings.java b/src/main/java/art/arcane/adapt/content/item/ItemListings.java index 3294632c2..80666b629 100644 --- a/src/main/java/art/arcane/adapt/content/item/ItemListings.java +++ b/src/main/java/art/arcane/adapt/content/item/ItemListings.java @@ -32,729 +32,729 @@ public class ItemListings { - @Getter - public static final List shearList = new KList<>( - Material.ACACIA_LEAVES, - Material.AZALEA_LEAVES, - Material.BIRCH_LEAVES, - Material.DARK_OAK_LEAVES, - Material.JUNGLE_LEAVES, - Material.OAK_LEAVES, - Material.SPRUCE_LEAVES, - Material.MANGROVE_LEAVES, - Materials.CHERRY_LEAVES, - Materials.PALE_OAK_LEAVES - ).nonNull(); - - @Getter - public static final List invalidDamageableEntities = Version.get().getInvalidDamageableEntities(); - - @Getter - public static final List smeltOre = List.of( - Material.IRON_ORE, - Material.GOLD_ORE, - Material.COPPER_ORE, - Material.DEEPSLATE_IRON_ORE, - Material.DEEPSLATE_GOLD_ORE, - Material.DEEPSLATE_COPPER_ORE - ); - - @Getter - public static final List ores = List.of( - Material.IRON_ORE, - Material.GOLD_ORE, - Material.COPPER_ORE, - Material.LAPIS_ORE, - Material.REDSTONE_ORE, - Material.EMERALD_ORE, - Material.DIAMOND_ORE, - Material.COAL_ORE, - Material.DEEPSLATE_IRON_ORE, - Material.DEEPSLATE_GOLD_ORE, - Material.DEEPSLATE_COPPER_ORE, - Material.DEEPSLATE_LAPIS_ORE, - Material.DEEPSLATE_REDSTONE_ORE, - Material.DEEPSLATE_EMERALD_ORE, - Material.DEEPSLATE_DIAMOND_ORE, - Material.DEEPSLATE_COAL_ORE, - Material.NETHER_GOLD_ORE, - Material.NETHER_QUARTZ_ORE, - Material.ANCIENT_DEBRIS - ); - - @Getter - public static HashMap oreColorsChatColor = new HashMap<>() {{ - put(Material.IRON_ORE, ChatColor.GRAY); - put(Material.GOLD_ORE, ChatColor.YELLOW); - put(Material.COPPER_ORE, ChatColor.GOLD); - put(Material.LAPIS_ORE, ChatColor.BLUE); - put(Material.REDSTONE_ORE, ChatColor.RED); - put(Material.EMERALD_ORE, ChatColor.GREEN); - put(Material.DIAMOND_ORE, ChatColor.AQUA); - put(Material.COAL_ORE, ChatColor.DARK_GRAY); - put(Material.DEEPSLATE_IRON_ORE, ChatColor.GRAY); - put(Material.DEEPSLATE_GOLD_ORE, ChatColor.YELLOW); - put(Material.DEEPSLATE_COPPER_ORE, ChatColor.GOLD); - put(Material.DEEPSLATE_LAPIS_ORE, ChatColor.BLUE); - put(Material.DEEPSLATE_REDSTONE_ORE, ChatColor.RED); - put(Material.DEEPSLATE_EMERALD_ORE, ChatColor.GREEN); - put(Material.DEEPSLATE_DIAMOND_ORE, ChatColor.AQUA); - put(Material.DEEPSLATE_COAL_ORE, ChatColor.DARK_GRAY); - put(Material.NETHER_GOLD_ORE, ChatColor.YELLOW); - put(Material.NETHER_QUARTZ_ORE, ChatColor.WHITE); - put(Material.ANCIENT_DEBRIS, ChatColor.DARK_PURPLE); - }}; - - @Getter - public static HashMap oreColorColor = new HashMap<>() {{ - put(Material.IRON_ORE, C.GRAY); - put(Material.GOLD_ORE, C.YELLOW); - put(Material.COPPER_ORE, C.GOLD); - put(Material.LAPIS_ORE, C.BLUE); - put(Material.REDSTONE_ORE, C.RED); - put(Material.EMERALD_ORE, C.GREEN); - put(Material.DIAMOND_ORE, C.AQUA); - put(Material.COAL_ORE, C.DARK_GRAY); - put(Material.DEEPSLATE_IRON_ORE, C.GRAY); - put(Material.DEEPSLATE_GOLD_ORE, C.YELLOW); - put(Material.DEEPSLATE_COPPER_ORE, C.GOLD); - put(Material.DEEPSLATE_LAPIS_ORE, C.BLUE); - put(Material.DEEPSLATE_REDSTONE_ORE, C.RED); - put(Material.DEEPSLATE_EMERALD_ORE, C.GREEN); - put(Material.DEEPSLATE_DIAMOND_ORE, C.AQUA); - put(Material.DEEPSLATE_COAL_ORE, C.DARK_GRAY); - put(Material.NETHER_GOLD_ORE, C.YELLOW); - put(Material.NETHER_QUARTZ_ORE, C.WHITE); - put(Material.ANCIENT_DEBRIS, C.DARK_PURPLE); - }}; - - @Getter - public static KList herbalLuckSeeds = new KList<>( - Material.MELON_SEEDS, - Material.PUMPKIN_SEEDS, - Material.COCOA_BEANS - ); - - @Getter - public static List swordPreference = List.of( - Material.COBWEB, - Material.CAVE_VINES, - Material.CAVE_VINES_PLANT, - Material.BAMBOO, - Material.COCOA, - Material.COCOA_BEANS, - Material.HAY_BLOCK - ); - - @Getter - public static KList herbalLuckFood = new KList<>( - Material.POTATOES, - Material.CARROTS, - Material.BEETROOTS, - Material.APPLE - ); - - @Getter - public static List flowers = List.of( - Material.DANDELION, - Material.POPPY, - Material.BLUE_ORCHID, - Material.ALLIUM, - Material.AZURE_BLUET, - Material.RED_TULIP, - Material.ORANGE_TULIP, - Material.WHITE_TULIP, - Material.PINK_TULIP, - Material.OXEYE_DAISY, - Material.CORNFLOWER, - Material.LILY_OF_THE_VALLEY, - Material.LILAC, - Material.ROSE_BUSH, - Material.PEONY, - Material.WITHER_ROSE - ); - - @Getter - public static List food = List.of( - Material.APPLE, - Material.BAKED_POTATO, - Material.BEETROOT, - Material.BEETROOT_SOUP, - Material.BREAD, - Material.CARROT, - Material.CHORUS_FRUIT, - Material.COOKED_CHICKEN, - Material.COOKED_COD, - Material.COOKED_MUTTON, - Material.COOKED_PORKCHOP, - Material.COOKED_RABBIT, - Material.COOKED_SALMON, - Material.COOKIE, - Material.DRIED_KELP, - Material.GOLDEN_APPLE, - Material.GLOW_BERRIES, - Material.GOLDEN_CARROT, - Material.HONEY_BLOCK, - Material.MELON_SLICE, - Material.MUSHROOM_STEW, - Material.POISONOUS_POTATO, - Material.POTATO, - Material.PUFFERFISH, - Material.PUMPKIN_PIE, - Material.RABBIT_STEW, - Material.BEEF, - Material.CHICKEN, - Material.COD, - Material.MUTTON, - Material.PORKCHOP, - Material.SALMON, - Material.ROTTEN_FLESH, - Material.SPIDER_EYE, - Material.COOKED_BEEF, - Material.SUSPICIOUS_STEW, - Material.SWEET_BERRIES, - Material.TROPICAL_FISH - - ); - - @Getter - public static List stripList = new KList<>( - Material.ACACIA_LOG, - Material.ACACIA_WOOD, - Material.STRIPPED_ACACIA_LOG, - Material.STRIPPED_ACACIA_WOOD, - Material.BIRCH_LOG, - Material.BIRCH_WOOD, - Material.STRIPPED_BIRCH_LOG, - Material.STRIPPED_BIRCH_WOOD, - Material.DARK_OAK_LOG, - Material.DARK_OAK_WOOD, - Material.STRIPPED_DARK_OAK_LOG, - Material.STRIPPED_DARK_OAK_WOOD, - Material.JUNGLE_LOG, - Material.JUNGLE_WOOD, - Material.STRIPPED_JUNGLE_LOG, - Material.STRIPPED_JUNGLE_WOOD, - Material.OAK_LOG, - Material.OAK_WOOD, - Material.STRIPPED_OAK_LOG, - Material.STRIPPED_OAK_WOOD, - Material.SPRUCE_LOG, - Material.SPRUCE_WOOD, - Material.STRIPPED_SPRUCE_LOG, - Material.STRIPPED_SPRUCE_WOOD, - Material.MANGROVE_LOG, - Material.MANGROVE_WOOD, - Material.STRIPPED_MANGROVE_LOG, - Material.STRIPPED_MANGROVE_WOOD, - Material.CRIMSON_STEM, - Material.CRIMSON_HYPHAE, - Materials.CHERRY_LOG, - Materials.CHERRY_WOOD, - Materials.STRIPPED_CHERRY_LOG, - Materials.STRIPPED_CHERRY_WOOD, - Materials.BAMBOO_BLOCK, - Materials.STRIPPED_BAMBOO_BLOCK, - Materials.PALE_OAK_LOG, - Materials.PALE_OAK_WOOD, - Materials.STRIPPED_PALE_OAK_LOG, - Materials.STRIPPED_PALE_OAK_WOOD - ).nonNull(); - - - @Getter - public static List ignitable = List.of( - Material.OBSIDIAN, - Material.NETHERRACK, - Material.SOUL_SAND, - Material.TNT - ); - - @Getter - public static List multiArmorable = List.of( - Material.ELYTRA, - Material.CHAINMAIL_CHESTPLATE, - Material.DIAMOND_CHESTPLATE, - Material.GOLDEN_CHESTPLATE, - Material.IRON_CHESTPLATE, - Material.LEATHER_CHESTPLATE, - Material.NETHERITE_CHESTPLATE - ); - - @Getter - public static List farmable = List.of( - Material.GRASS_BLOCK, - Material.DIRT, - Material.COARSE_DIRT, - Material.ROOTED_DIRT, - Material.WHEAT, - Material.ATTACHED_MELON_STEM, - Material.ATTACHED_PUMPKIN_STEM, - Material.MELON_STEM, - Material.PUMPKIN_STEM, - Material.POTATOES, - Material.SWEET_BERRY_BUSH, - Material.CARROTS, - Material.BEETROOTS, - Material.DIRT_PATH - - ); - - @Getter - public static List tillable = List.of( - ); - - @Getter - public static List burnable = new KList<>( - Material.OBSIDIAN, - Material.NETHERRACK, - Material.SOUL_SAND, - Material.ACACIA_LEAVES, - Material.BIRCH_LEAVES, - Material.DARK_OAK_LEAVES, - Material.JUNGLE_LEAVES, - Material.OAK_LEAVES, - Material.SPRUCE_LEAVES, - Material.MANGROVE_LEAVES, - Materials.CHERRY_LEAVES, - Materials.PALE_OAK_LEAVES, - Material.WHITE_WOOL, - Material.ORANGE_WOOL, - Material.MAGENTA_WOOL, - Material.LIGHT_BLUE_WOOL, - Material.YELLOW_WOOL, - Material.LIME_WOOL, - Material.PINK_WOOL, - Material.GRAY_WOOL, - Material.LIGHT_GRAY_WOOL, - Material.CYAN_WOOL, - Material.PURPLE_WOOL, - Material.BLUE_WOOL, - Material.BROWN_WOOL, - Material.GREEN_WOOL, - Material.RED_WOOL, - Material.BLACK_WOOL - ).nonNull(); - - @Getter - public static List toolPickaxes = List.of( - Material.WOODEN_PICKAXE, - Material.STONE_PICKAXE, - Material.IRON_PICKAXE, - Material.GOLDEN_PICKAXE, - Material.DIAMOND_PICKAXE, - Material.NETHERITE_PICKAXE - ); - - @Getter - public static List toolAxes = List.of( - Material.WOODEN_AXE, - Material.STONE_AXE, - Material.IRON_AXE, - Material.GOLDEN_AXE, - Material.DIAMOND_AXE, - Material.NETHERITE_AXE - ); - - @Getter - public static List toolSwords = List.of( - Material.WOODEN_SWORD, - Material.STONE_SWORD, - Material.IRON_SWORD, - Material.GOLDEN_SWORD, - Material.DIAMOND_SWORD, - Material.NETHERITE_SWORD - ); - - @Getter - public static List toolShovels = List.of( - Material.WOODEN_SHOVEL, - Material.STONE_SHOVEL, - Material.IRON_SHOVEL, - Material.GOLDEN_SHOVEL, - Material.DIAMOND_SHOVEL, - Material.NETHERITE_SHOVEL - ); - - @Getter - public static List toolHoes = List.of( - Material.WOODEN_HOE, - Material.STONE_HOE, - Material.IRON_HOE, - Material.GOLDEN_HOE, - Material.DIAMOND_HOE, - Material.NETHERITE_HOE - ); - - @Getter - public static List tool = List.of( - Material.WOODEN_PICKAXE, - Material.STONE_PICKAXE, - Material.IRON_PICKAXE, - Material.GOLDEN_PICKAXE, - Material.DIAMOND_PICKAXE, - Material.NETHERITE_PICKAXE, - //AXE - Material.WOODEN_AXE, - Material.STONE_AXE, - Material.IRON_AXE, - Material.GOLDEN_AXE, - Material.DIAMOND_AXE, - Material.NETHERITE_AXE, - //SWORD - Material.WOODEN_SWORD, - Material.STONE_SWORD, - Material.IRON_SWORD, - Material.GOLDEN_SWORD, - Material.DIAMOND_SWORD, - Material.NETHERITE_SWORD, - //SHOVEL - Material.WOODEN_SHOVEL, - Material.STONE_SHOVEL, - Material.IRON_SHOVEL, - Material.GOLDEN_SHOVEL, - Material.DIAMOND_SHOVEL, - Material.NETHERITE_SHOVEL, - //HOE - Material.WOODEN_HOE, - Material.STONE_HOE, - Material.IRON_HOE, - Material.GOLDEN_HOE, - Material.DIAMOND_HOE, - Material.NETHERITE_HOE, - - //EXTRA - Material.SHEARS - ); - - @Getter - public static List shovelPreference = List.of( - Material.CLAY, - Material.DIRT, - Material.FARMLAND, - Material.GRASS_BLOCK, - Material.GRAVEL, - Material.MYCELIUM, - Material.SAND, - Material.SOUL_SAND, - Material.SOUL_SOIL, - Material.SNOW, - Material.SNOW_BLOCK, - Material.POWDER_SNOW, - Material.PODZOL, - Material.RED_SAND, - Material.MUD, - Material.MUDDY_MANGROVE_ROOTS - ); - - @Getter - public static KList fishingDrops = new KList<>( - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.PUFFERFISH, - Material.TROPICAL_FISH, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.PUFFERFISH, - Material.TROPICAL_FISH, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.PUFFERFISH, - Material.TROPICAL_FISH, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.PUFFERFISH, - Material.TROPICAL_FISH, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.COD, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.SALMON, - Material.PUFFERFISH, - Material.TROPICAL_FISH, - - Material.BOW, - Material.FISHING_ROD, - Material.NAME_TAG, - Material.NAUTILUS_SHELL, - Material.SADDLE, - - Material.LILY_PAD, - Material.BOWL, - Material.LEATHER, - Material.STICK, - Material.ROTTEN_FLESH, - Material.STRING, - Material.GLASS_BOTTLE, - Material.BONE, - Material.INK_SAC, - Material.TRIPWIRE_HOOK - ); - - @Getter - public static List axePreference = new KList<>( - //FENCES - Material.ACACIA_FENCE, - Material.BIRCH_FENCE, - Material.DARK_OAK_FENCE, - Material.JUNGLE_FENCE, - Material.SPRUCE_FENCE, - Material.MANGROVE_FENCE, - Material.OAK_FENCE, - Material.CRIMSON_FENCE, - Material.WARPED_FENCE, - Materials.CHERRY_FENCE, - Materials.BAMBOO_FENCE, - Materials.PALE_OAK_FENCE, - //GATES - Material.ACACIA_FENCE_GATE, - Material.BIRCH_FENCE_GATE, - Material.DARK_OAK_FENCE_GATE, - Material.JUNGLE_FENCE_GATE, - Material.SPRUCE_FENCE_GATE, - Material.MANGROVE_FENCE_GATE, - Material.OAK_FENCE_GATE, - Material.CRIMSON_FENCE_GATE, - Material.WARPED_FENCE_GATE, - Materials.CHERRY_FENCE_GATE, - Materials.BAMBOO_FENCE_GATE, - Materials.PALE_OAK_FENCE_GATE, - //WOODS - Material.ACACIA_LOG, - Material.ACACIA_WOOD, - Material.STRIPPED_ACACIA_LOG, - Material.BIRCH_LOG, - Material.BIRCH_WOOD, - Material.STRIPPED_BIRCH_LOG, - Material.DARK_OAK_LOG, - Material.DARK_OAK_WOOD, - Material.STRIPPED_DARK_OAK_LOG, - Material.JUNGLE_LOG, - Material.JUNGLE_WOOD, - Material.STRIPPED_JUNGLE_LOG, - Material.OAK_LOG, - Material.OAK_WOOD, - Material.STRIPPED_OAK_LOG, - Material.SPRUCE_LOG, - Material.SPRUCE_WOOD, - Material.STRIPPED_SPRUCE_LOG, - Material.MANGROVE_LOG, - Material.MANGROVE_WOOD, - Material.STRIPPED_MANGROVE_LOG, - Material.CRIMSON_STEM, - Material.CRIMSON_HYPHAE, - Materials.CHERRY_LOG, - Materials.CHERRY_WOOD, - Materials.STRIPPED_CHERRY_LOG, - Materials.STRIPPED_CHERRY_WOOD, - Materials.BAMBOO_BLOCK, - Materials.STRIPPED_BAMBOO_BLOCK, - Materials.PALE_OAK_LOG, - Materials.PALE_OAK_WOOD, - Materials.STRIPPED_PALE_OAK_LOG, - Materials.STRIPPED_PALE_OAK_WOOD, - //SIGNS - Material.ACACIA_SIGN, - Material.ACACIA_WALL_SIGN, - Materials.ACACIA_HANGING_SIGN, - Materials.ACACIA_WALL_HANGING_SIGN, - Material.BIRCH_SIGN, - Material.BIRCH_WALL_SIGN, - Materials.BIRCH_HANGING_SIGN, - Materials.BIRCH_WALL_HANGING_SIGN, - Material.DARK_OAK_SIGN, - Material.DARK_OAK_WALL_SIGN, - Materials.DARK_OAK_HANGING_SIGN, - Materials.DARK_OAK_WALL_HANGING_SIGN, - Material.JUNGLE_SIGN, - Material.JUNGLE_WALL_SIGN, - Materials.JUNGLE_HANGING_SIGN, - Materials.JUNGLE_WALL_HANGING_SIGN, - Material.OAK_SIGN, - Material.OAK_WALL_SIGN, - Materials.OAK_HANGING_SIGN, - Materials.OAK_WALL_HANGING_SIGN, - Material.SPRUCE_SIGN, - Material.SPRUCE_WALL_SIGN, - Materials.SPRUCE_HANGING_SIGN, - Materials.SPRUCE_WALL_HANGING_SIGN, - Material.MANGROVE_SIGN, - Material.MANGROVE_WALL_SIGN, - Materials.MANGROVE_HANGING_SIGN, - Materials.MANGROVE_WALL_HANGING_SIGN, - Material.CRIMSON_SIGN, - Material.CRIMSON_WALL_SIGN, - Materials.CRIMSON_HANGING_SIGN, - Materials.CRIMSON_WALL_HANGING_SIGN, - Material.WARPED_SIGN, - Material.WARPED_WALL_SIGN, - Materials.WARPED_HANGING_SIGN, - Materials.WARPED_WALL_HANGING_SIGN, - Materials.CHERRY_SIGN, - Materials.CHERRY_WALL_SIGN, - Materials.CHERRY_HANGING_SIGN, - Materials.CHERRY_WALL_HANGING_SIGN, - Materials.BAMBOO_SIGN, - Materials.BAMBOO_WALL_SIGN, - Materials.BAMBOO_HANGING_SIGN, - Materials.BAMBOO_WALL_HANGING_SIGN, - Materials.PALE_OAK_SIGN, - Materials.PALE_OAK_WALL_SIGN, - Materials.PALE_OAK_HANGING_SIGN, - Materials.PALE_OAK_WALL_HANGING_SIGN, - //WOODEN_BUTTONS - Material.ACACIA_BUTTON, - Material.BIRCH_BUTTON, - Material.DARK_OAK_BUTTON, - Material.JUNGLE_BUTTON, - Material.OAK_BUTTON, - Material.SPRUCE_BUTTON, - Material.MANGROVE_BUTTON, - Material.CRIMSON_BUTTON, - Material.WARPED_BUTTON, - Materials.CHERRY_BUTTON, - Materials.BAMBOO_BUTTON, - Materials.PALE_OAK_BUTTON, - //WOODEN_DOORS - Material.ACACIA_DOOR, - Material.BIRCH_DOOR, - Material.DARK_OAK_DOOR, - Material.JUNGLE_DOOR, - Material.OAK_DOOR, - Material.SPRUCE_DOOR, - Material.MANGROVE_DOOR, - Material.CRIMSON_DOOR, - Material.WARPED_DOOR, - Materials.CHERRY_DOOR, - Materials.BAMBOO_DOOR, - Materials.PALE_OAK_DOOR, - //WOODEN_PRESSURE_PLATES - Material.ACACIA_PRESSURE_PLATE, - Material.BIRCH_PRESSURE_PLATE, - Material.DARK_OAK_PRESSURE_PLATE, - Material.JUNGLE_PRESSURE_PLATE, - Material.OAK_PRESSURE_PLATE, - Material.SPRUCE_PRESSURE_PLATE, - Material.MANGROVE_PRESSURE_PLATE, - Material.CRIMSON_PRESSURE_PLATE, - Material.WARPED_PRESSURE_PLATE, - Materials.CHERRY_PRESSURE_PLATE, - Materials.BAMBOO_PRESSURE_PLATE, - Materials.PALE_OAK_PRESSURE_PLATE, - //WOODEN_TRAPDOORS - Material.ACACIA_TRAPDOOR, - Material.BIRCH_TRAPDOOR, - Material.DARK_OAK_TRAPDOOR, - Material.JUNGLE_TRAPDOOR, - Material.OAK_TRAPDOOR, - Material.SPRUCE_TRAPDOOR, - Material.MANGROVE_TRAPDOOR, - Material.CRIMSON_TRAPDOOR, - Material.WARPED_TRAPDOOR, - Materials.CHERRY_TRAPDOOR, - Materials.BAMBOO_TRAPDOOR, - Materials.PALE_OAK_TRAPDOOR, - //WOODEN_STAIRS - Material.ACACIA_STAIRS, - Material.BIRCH_STAIRS, - Material.DARK_OAK_STAIRS, - Material.JUNGLE_STAIRS, - Material.OAK_STAIRS, - Material.SPRUCE_STAIRS, - Material.MANGROVE_STAIRS, - Material.CRIMSON_STAIRS, - Material.WARPED_STAIRS, - Materials.CHERRY_STAIRS, - Materials.BAMBOO_STAIRS, - Materials.BAMBOO_MOSAIC_STAIRS, - Materials.PALE_OAK_STAIRS, - //WOODEN_SLABS - Material.ACACIA_SLAB, - Material.BIRCH_SLAB, - Material.DARK_OAK_SLAB, - Material.JUNGLE_SLAB, - Material.OAK_SLAB, - Material.SPRUCE_SLAB, - Material.MANGROVE_SLAB, - Material.CRIMSON_SLAB, - Material.WARPED_SLAB, - Materials.CHERRY_SLAB, - Materials.BAMBOO_SLAB, - Materials.BAMBOO_MOSAIC_SLAB, - Materials.PALE_OAK_SLAB, - //PLANKS - Material.ACACIA_PLANKS, - Material.BIRCH_PLANKS, - Material.DARK_OAK_PLANKS, - Material.JUNGLE_PLANKS, - Material.OAK_PLANKS, - Material.SPRUCE_PLANKS, - Material.MANGROVE_PLANKS, - Material.CRIMSON_PLANKS, - Material.WARPED_PLANKS, - Materials.CHERRY_PLANKS, - Materials.BAMBOO_PLANKS, - Materials.BAMBOO_MOSAIC, - Materials.PALE_OAK_PLANKS, - //MISC - Material.BEE_NEST, - Material.DRIED_KELP_BLOCK, - Material.BEEHIVE, - Material.CHEST, - Material.CRAFTING_TABLE, - Material.JUKEBOX, - Material.LADDER, - Material.LOOM, - Material.NOTE_BLOCK, - Material.BARREL, - Material.BOOKSHELF, - Material.CARTOGRAPHY_TABLE, - Material.FLETCHING_TABLE, - Material.CAMPFIRE, - Material.SOUL_CAMPFIRE, - Material.HONEYCOMB, - Material.LECTERN, - Material.COCOA, - Material.JACK_O_LANTERN, - Material.PUMPKIN, - Material.MELON, - Material.TRAPPED_CHEST - ).nonNull(); + @Getter + public static final List shearList = new KList<>( + Material.ACACIA_LEAVES, + Material.AZALEA_LEAVES, + Material.BIRCH_LEAVES, + Material.DARK_OAK_LEAVES, + Material.JUNGLE_LEAVES, + Material.OAK_LEAVES, + Material.SPRUCE_LEAVES, + Material.MANGROVE_LEAVES, + Materials.CHERRY_LEAVES, + Materials.PALE_OAK_LEAVES + ).nonNull(); + + @Getter + public static final List invalidDamageableEntities = Version.get().getInvalidDamageableEntities(); + + @Getter + public static final List smeltOre = List.of( + Material.IRON_ORE, + Material.GOLD_ORE, + Material.COPPER_ORE, + Material.DEEPSLATE_IRON_ORE, + Material.DEEPSLATE_GOLD_ORE, + Material.DEEPSLATE_COPPER_ORE + ); + + @Getter + public static final List ores = List.of( + Material.IRON_ORE, + Material.GOLD_ORE, + Material.COPPER_ORE, + Material.LAPIS_ORE, + Material.REDSTONE_ORE, + Material.EMERALD_ORE, + Material.DIAMOND_ORE, + Material.COAL_ORE, + Material.DEEPSLATE_IRON_ORE, + Material.DEEPSLATE_GOLD_ORE, + Material.DEEPSLATE_COPPER_ORE, + Material.DEEPSLATE_LAPIS_ORE, + Material.DEEPSLATE_REDSTONE_ORE, + Material.DEEPSLATE_EMERALD_ORE, + Material.DEEPSLATE_DIAMOND_ORE, + Material.DEEPSLATE_COAL_ORE, + Material.NETHER_GOLD_ORE, + Material.NETHER_QUARTZ_ORE, + Material.ANCIENT_DEBRIS + ); + + @Getter + public static HashMap oreColorsChatColor = new HashMap<>() {{ + put(Material.IRON_ORE, ChatColor.GRAY); + put(Material.GOLD_ORE, ChatColor.YELLOW); + put(Material.COPPER_ORE, ChatColor.GOLD); + put(Material.LAPIS_ORE, ChatColor.BLUE); + put(Material.REDSTONE_ORE, ChatColor.RED); + put(Material.EMERALD_ORE, ChatColor.GREEN); + put(Material.DIAMOND_ORE, ChatColor.AQUA); + put(Material.COAL_ORE, ChatColor.DARK_GRAY); + put(Material.DEEPSLATE_IRON_ORE, ChatColor.GRAY); + put(Material.DEEPSLATE_GOLD_ORE, ChatColor.YELLOW); + put(Material.DEEPSLATE_COPPER_ORE, ChatColor.GOLD); + put(Material.DEEPSLATE_LAPIS_ORE, ChatColor.BLUE); + put(Material.DEEPSLATE_REDSTONE_ORE, ChatColor.RED); + put(Material.DEEPSLATE_EMERALD_ORE, ChatColor.GREEN); + put(Material.DEEPSLATE_DIAMOND_ORE, ChatColor.AQUA); + put(Material.DEEPSLATE_COAL_ORE, ChatColor.DARK_GRAY); + put(Material.NETHER_GOLD_ORE, ChatColor.YELLOW); + put(Material.NETHER_QUARTZ_ORE, ChatColor.WHITE); + put(Material.ANCIENT_DEBRIS, ChatColor.DARK_PURPLE); + }}; + + @Getter + public static HashMap oreColorColor = new HashMap<>() {{ + put(Material.IRON_ORE, C.GRAY); + put(Material.GOLD_ORE, C.YELLOW); + put(Material.COPPER_ORE, C.GOLD); + put(Material.LAPIS_ORE, C.BLUE); + put(Material.REDSTONE_ORE, C.RED); + put(Material.EMERALD_ORE, C.GREEN); + put(Material.DIAMOND_ORE, C.AQUA); + put(Material.COAL_ORE, C.DARK_GRAY); + put(Material.DEEPSLATE_IRON_ORE, C.GRAY); + put(Material.DEEPSLATE_GOLD_ORE, C.YELLOW); + put(Material.DEEPSLATE_COPPER_ORE, C.GOLD); + put(Material.DEEPSLATE_LAPIS_ORE, C.BLUE); + put(Material.DEEPSLATE_REDSTONE_ORE, C.RED); + put(Material.DEEPSLATE_EMERALD_ORE, C.GREEN); + put(Material.DEEPSLATE_DIAMOND_ORE, C.AQUA); + put(Material.DEEPSLATE_COAL_ORE, C.DARK_GRAY); + put(Material.NETHER_GOLD_ORE, C.YELLOW); + put(Material.NETHER_QUARTZ_ORE, C.WHITE); + put(Material.ANCIENT_DEBRIS, C.DARK_PURPLE); + }}; + + @Getter + public static KList herbalLuckSeeds = new KList<>( + Material.MELON_SEEDS, + Material.PUMPKIN_SEEDS, + Material.COCOA_BEANS + ); + + @Getter + public static List swordPreference = List.of( + Material.COBWEB, + Material.CAVE_VINES, + Material.CAVE_VINES_PLANT, + Material.BAMBOO, + Material.COCOA, + Material.COCOA_BEANS, + Material.HAY_BLOCK + ); + + @Getter + public static KList herbalLuckFood = new KList<>( + Material.POTATOES, + Material.CARROTS, + Material.BEETROOTS, + Material.APPLE + ); + + @Getter + public static List flowers = List.of( + Material.DANDELION, + Material.POPPY, + Material.BLUE_ORCHID, + Material.ALLIUM, + Material.AZURE_BLUET, + Material.RED_TULIP, + Material.ORANGE_TULIP, + Material.WHITE_TULIP, + Material.PINK_TULIP, + Material.OXEYE_DAISY, + Material.CORNFLOWER, + Material.LILY_OF_THE_VALLEY, + Material.LILAC, + Material.ROSE_BUSH, + Material.PEONY, + Material.WITHER_ROSE + ); + + @Getter + public static List food = List.of( + Material.APPLE, + Material.BAKED_POTATO, + Material.BEETROOT, + Material.BEETROOT_SOUP, + Material.BREAD, + Material.CARROT, + Material.CHORUS_FRUIT, + Material.COOKED_CHICKEN, + Material.COOKED_COD, + Material.COOKED_MUTTON, + Material.COOKED_PORKCHOP, + Material.COOKED_RABBIT, + Material.COOKED_SALMON, + Material.COOKIE, + Material.DRIED_KELP, + Material.GOLDEN_APPLE, + Material.GLOW_BERRIES, + Material.GOLDEN_CARROT, + Material.HONEY_BLOCK, + Material.MELON_SLICE, + Material.MUSHROOM_STEW, + Material.POISONOUS_POTATO, + Material.POTATO, + Material.PUFFERFISH, + Material.PUMPKIN_PIE, + Material.RABBIT_STEW, + Material.BEEF, + Material.CHICKEN, + Material.COD, + Material.MUTTON, + Material.PORKCHOP, + Material.SALMON, + Material.ROTTEN_FLESH, + Material.SPIDER_EYE, + Material.COOKED_BEEF, + Material.SUSPICIOUS_STEW, + Material.SWEET_BERRIES, + Material.TROPICAL_FISH + + ); + + @Getter + public static List stripList = new KList<>( + Material.ACACIA_LOG, + Material.ACACIA_WOOD, + Material.STRIPPED_ACACIA_LOG, + Material.STRIPPED_ACACIA_WOOD, + Material.BIRCH_LOG, + Material.BIRCH_WOOD, + Material.STRIPPED_BIRCH_LOG, + Material.STRIPPED_BIRCH_WOOD, + Material.DARK_OAK_LOG, + Material.DARK_OAK_WOOD, + Material.STRIPPED_DARK_OAK_LOG, + Material.STRIPPED_DARK_OAK_WOOD, + Material.JUNGLE_LOG, + Material.JUNGLE_WOOD, + Material.STRIPPED_JUNGLE_LOG, + Material.STRIPPED_JUNGLE_WOOD, + Material.OAK_LOG, + Material.OAK_WOOD, + Material.STRIPPED_OAK_LOG, + Material.STRIPPED_OAK_WOOD, + Material.SPRUCE_LOG, + Material.SPRUCE_WOOD, + Material.STRIPPED_SPRUCE_LOG, + Material.STRIPPED_SPRUCE_WOOD, + Material.MANGROVE_LOG, + Material.MANGROVE_WOOD, + Material.STRIPPED_MANGROVE_LOG, + Material.STRIPPED_MANGROVE_WOOD, + Material.CRIMSON_STEM, + Material.CRIMSON_HYPHAE, + Materials.CHERRY_LOG, + Materials.CHERRY_WOOD, + Materials.STRIPPED_CHERRY_LOG, + Materials.STRIPPED_CHERRY_WOOD, + Materials.BAMBOO_BLOCK, + Materials.STRIPPED_BAMBOO_BLOCK, + Materials.PALE_OAK_LOG, + Materials.PALE_OAK_WOOD, + Materials.STRIPPED_PALE_OAK_LOG, + Materials.STRIPPED_PALE_OAK_WOOD + ).nonNull(); + + + @Getter + public static List ignitable = List.of( + Material.OBSIDIAN, + Material.NETHERRACK, + Material.SOUL_SAND, + Material.TNT + ); + + @Getter + public static List multiArmorable = List.of( + Material.ELYTRA, + Material.CHAINMAIL_CHESTPLATE, + Material.DIAMOND_CHESTPLATE, + Material.GOLDEN_CHESTPLATE, + Material.IRON_CHESTPLATE, + Material.LEATHER_CHESTPLATE, + Material.NETHERITE_CHESTPLATE + ); + + @Getter + public static List farmable = List.of( + Material.GRASS_BLOCK, + Material.DIRT, + Material.COARSE_DIRT, + Material.ROOTED_DIRT, + Material.WHEAT, + Material.ATTACHED_MELON_STEM, + Material.ATTACHED_PUMPKIN_STEM, + Material.MELON_STEM, + Material.PUMPKIN_STEM, + Material.POTATOES, + Material.SWEET_BERRY_BUSH, + Material.CARROTS, + Material.BEETROOTS, + Material.DIRT_PATH + + ); + + @Getter + public static List tillable = List.of( + ); + + @Getter + public static List burnable = new KList<>( + Material.OBSIDIAN, + Material.NETHERRACK, + Material.SOUL_SAND, + Material.ACACIA_LEAVES, + Material.BIRCH_LEAVES, + Material.DARK_OAK_LEAVES, + Material.JUNGLE_LEAVES, + Material.OAK_LEAVES, + Material.SPRUCE_LEAVES, + Material.MANGROVE_LEAVES, + Materials.CHERRY_LEAVES, + Materials.PALE_OAK_LEAVES, + Material.WHITE_WOOL, + Material.ORANGE_WOOL, + Material.MAGENTA_WOOL, + Material.LIGHT_BLUE_WOOL, + Material.YELLOW_WOOL, + Material.LIME_WOOL, + Material.PINK_WOOL, + Material.GRAY_WOOL, + Material.LIGHT_GRAY_WOOL, + Material.CYAN_WOOL, + Material.PURPLE_WOOL, + Material.BLUE_WOOL, + Material.BROWN_WOOL, + Material.GREEN_WOOL, + Material.RED_WOOL, + Material.BLACK_WOOL + ).nonNull(); + + @Getter + public static List toolPickaxes = List.of( + Material.WOODEN_PICKAXE, + Material.STONE_PICKAXE, + Material.IRON_PICKAXE, + Material.GOLDEN_PICKAXE, + Material.DIAMOND_PICKAXE, + Material.NETHERITE_PICKAXE + ); + + @Getter + public static List toolAxes = List.of( + Material.WOODEN_AXE, + Material.STONE_AXE, + Material.IRON_AXE, + Material.GOLDEN_AXE, + Material.DIAMOND_AXE, + Material.NETHERITE_AXE + ); + + @Getter + public static List toolSwords = List.of( + Material.WOODEN_SWORD, + Material.STONE_SWORD, + Material.IRON_SWORD, + Material.GOLDEN_SWORD, + Material.DIAMOND_SWORD, + Material.NETHERITE_SWORD + ); + + @Getter + public static List toolShovels = List.of( + Material.WOODEN_SHOVEL, + Material.STONE_SHOVEL, + Material.IRON_SHOVEL, + Material.GOLDEN_SHOVEL, + Material.DIAMOND_SHOVEL, + Material.NETHERITE_SHOVEL + ); + + @Getter + public static List toolHoes = List.of( + Material.WOODEN_HOE, + Material.STONE_HOE, + Material.IRON_HOE, + Material.GOLDEN_HOE, + Material.DIAMOND_HOE, + Material.NETHERITE_HOE + ); + + @Getter + public static List tool = List.of( + Material.WOODEN_PICKAXE, + Material.STONE_PICKAXE, + Material.IRON_PICKAXE, + Material.GOLDEN_PICKAXE, + Material.DIAMOND_PICKAXE, + Material.NETHERITE_PICKAXE, + //AXE + Material.WOODEN_AXE, + Material.STONE_AXE, + Material.IRON_AXE, + Material.GOLDEN_AXE, + Material.DIAMOND_AXE, + Material.NETHERITE_AXE, + //SWORD + Material.WOODEN_SWORD, + Material.STONE_SWORD, + Material.IRON_SWORD, + Material.GOLDEN_SWORD, + Material.DIAMOND_SWORD, + Material.NETHERITE_SWORD, + //SHOVEL + Material.WOODEN_SHOVEL, + Material.STONE_SHOVEL, + Material.IRON_SHOVEL, + Material.GOLDEN_SHOVEL, + Material.DIAMOND_SHOVEL, + Material.NETHERITE_SHOVEL, + //HOE + Material.WOODEN_HOE, + Material.STONE_HOE, + Material.IRON_HOE, + Material.GOLDEN_HOE, + Material.DIAMOND_HOE, + Material.NETHERITE_HOE, + + //EXTRA + Material.SHEARS + ); + + @Getter + public static List shovelPreference = List.of( + Material.CLAY, + Material.DIRT, + Material.FARMLAND, + Material.GRASS_BLOCK, + Material.GRAVEL, + Material.MYCELIUM, + Material.SAND, + Material.SOUL_SAND, + Material.SOUL_SOIL, + Material.SNOW, + Material.SNOW_BLOCK, + Material.POWDER_SNOW, + Material.PODZOL, + Material.RED_SAND, + Material.MUD, + Material.MUDDY_MANGROVE_ROOTS + ); + + @Getter + public static KList fishingDrops = new KList<>( + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.PUFFERFISH, + Material.TROPICAL_FISH, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.PUFFERFISH, + Material.TROPICAL_FISH, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.PUFFERFISH, + Material.TROPICAL_FISH, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.PUFFERFISH, + Material.TROPICAL_FISH, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.COD, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.SALMON, + Material.PUFFERFISH, + Material.TROPICAL_FISH, + + Material.BOW, + Material.FISHING_ROD, + Material.NAME_TAG, + Material.NAUTILUS_SHELL, + Material.SADDLE, + + Material.LILY_PAD, + Material.BOWL, + Material.LEATHER, + Material.STICK, + Material.ROTTEN_FLESH, + Material.STRING, + Material.GLASS_BOTTLE, + Material.BONE, + Material.INK_SAC, + Material.TRIPWIRE_HOOK + ); + + @Getter + public static List axePreference = new KList<>( + //FENCES + Material.ACACIA_FENCE, + Material.BIRCH_FENCE, + Material.DARK_OAK_FENCE, + Material.JUNGLE_FENCE, + Material.SPRUCE_FENCE, + Material.MANGROVE_FENCE, + Material.OAK_FENCE, + Material.CRIMSON_FENCE, + Material.WARPED_FENCE, + Materials.CHERRY_FENCE, + Materials.BAMBOO_FENCE, + Materials.PALE_OAK_FENCE, + //GATES + Material.ACACIA_FENCE_GATE, + Material.BIRCH_FENCE_GATE, + Material.DARK_OAK_FENCE_GATE, + Material.JUNGLE_FENCE_GATE, + Material.SPRUCE_FENCE_GATE, + Material.MANGROVE_FENCE_GATE, + Material.OAK_FENCE_GATE, + Material.CRIMSON_FENCE_GATE, + Material.WARPED_FENCE_GATE, + Materials.CHERRY_FENCE_GATE, + Materials.BAMBOO_FENCE_GATE, + Materials.PALE_OAK_FENCE_GATE, + //WOODS + Material.ACACIA_LOG, + Material.ACACIA_WOOD, + Material.STRIPPED_ACACIA_LOG, + Material.BIRCH_LOG, + Material.BIRCH_WOOD, + Material.STRIPPED_BIRCH_LOG, + Material.DARK_OAK_LOG, + Material.DARK_OAK_WOOD, + Material.STRIPPED_DARK_OAK_LOG, + Material.JUNGLE_LOG, + Material.JUNGLE_WOOD, + Material.STRIPPED_JUNGLE_LOG, + Material.OAK_LOG, + Material.OAK_WOOD, + Material.STRIPPED_OAK_LOG, + Material.SPRUCE_LOG, + Material.SPRUCE_WOOD, + Material.STRIPPED_SPRUCE_LOG, + Material.MANGROVE_LOG, + Material.MANGROVE_WOOD, + Material.STRIPPED_MANGROVE_LOG, + Material.CRIMSON_STEM, + Material.CRIMSON_HYPHAE, + Materials.CHERRY_LOG, + Materials.CHERRY_WOOD, + Materials.STRIPPED_CHERRY_LOG, + Materials.STRIPPED_CHERRY_WOOD, + Materials.BAMBOO_BLOCK, + Materials.STRIPPED_BAMBOO_BLOCK, + Materials.PALE_OAK_LOG, + Materials.PALE_OAK_WOOD, + Materials.STRIPPED_PALE_OAK_LOG, + Materials.STRIPPED_PALE_OAK_WOOD, + //SIGNS + Material.ACACIA_SIGN, + Material.ACACIA_WALL_SIGN, + Materials.ACACIA_HANGING_SIGN, + Materials.ACACIA_WALL_HANGING_SIGN, + Material.BIRCH_SIGN, + Material.BIRCH_WALL_SIGN, + Materials.BIRCH_HANGING_SIGN, + Materials.BIRCH_WALL_HANGING_SIGN, + Material.DARK_OAK_SIGN, + Material.DARK_OAK_WALL_SIGN, + Materials.DARK_OAK_HANGING_SIGN, + Materials.DARK_OAK_WALL_HANGING_SIGN, + Material.JUNGLE_SIGN, + Material.JUNGLE_WALL_SIGN, + Materials.JUNGLE_HANGING_SIGN, + Materials.JUNGLE_WALL_HANGING_SIGN, + Material.OAK_SIGN, + Material.OAK_WALL_SIGN, + Materials.OAK_HANGING_SIGN, + Materials.OAK_WALL_HANGING_SIGN, + Material.SPRUCE_SIGN, + Material.SPRUCE_WALL_SIGN, + Materials.SPRUCE_HANGING_SIGN, + Materials.SPRUCE_WALL_HANGING_SIGN, + Material.MANGROVE_SIGN, + Material.MANGROVE_WALL_SIGN, + Materials.MANGROVE_HANGING_SIGN, + Materials.MANGROVE_WALL_HANGING_SIGN, + Material.CRIMSON_SIGN, + Material.CRIMSON_WALL_SIGN, + Materials.CRIMSON_HANGING_SIGN, + Materials.CRIMSON_WALL_HANGING_SIGN, + Material.WARPED_SIGN, + Material.WARPED_WALL_SIGN, + Materials.WARPED_HANGING_SIGN, + Materials.WARPED_WALL_HANGING_SIGN, + Materials.CHERRY_SIGN, + Materials.CHERRY_WALL_SIGN, + Materials.CHERRY_HANGING_SIGN, + Materials.CHERRY_WALL_HANGING_SIGN, + Materials.BAMBOO_SIGN, + Materials.BAMBOO_WALL_SIGN, + Materials.BAMBOO_HANGING_SIGN, + Materials.BAMBOO_WALL_HANGING_SIGN, + Materials.PALE_OAK_SIGN, + Materials.PALE_OAK_WALL_SIGN, + Materials.PALE_OAK_HANGING_SIGN, + Materials.PALE_OAK_WALL_HANGING_SIGN, + //WOODEN_BUTTONS + Material.ACACIA_BUTTON, + Material.BIRCH_BUTTON, + Material.DARK_OAK_BUTTON, + Material.JUNGLE_BUTTON, + Material.OAK_BUTTON, + Material.SPRUCE_BUTTON, + Material.MANGROVE_BUTTON, + Material.CRIMSON_BUTTON, + Material.WARPED_BUTTON, + Materials.CHERRY_BUTTON, + Materials.BAMBOO_BUTTON, + Materials.PALE_OAK_BUTTON, + //WOODEN_DOORS + Material.ACACIA_DOOR, + Material.BIRCH_DOOR, + Material.DARK_OAK_DOOR, + Material.JUNGLE_DOOR, + Material.OAK_DOOR, + Material.SPRUCE_DOOR, + Material.MANGROVE_DOOR, + Material.CRIMSON_DOOR, + Material.WARPED_DOOR, + Materials.CHERRY_DOOR, + Materials.BAMBOO_DOOR, + Materials.PALE_OAK_DOOR, + //WOODEN_PRESSURE_PLATES + Material.ACACIA_PRESSURE_PLATE, + Material.BIRCH_PRESSURE_PLATE, + Material.DARK_OAK_PRESSURE_PLATE, + Material.JUNGLE_PRESSURE_PLATE, + Material.OAK_PRESSURE_PLATE, + Material.SPRUCE_PRESSURE_PLATE, + Material.MANGROVE_PRESSURE_PLATE, + Material.CRIMSON_PRESSURE_PLATE, + Material.WARPED_PRESSURE_PLATE, + Materials.CHERRY_PRESSURE_PLATE, + Materials.BAMBOO_PRESSURE_PLATE, + Materials.PALE_OAK_PRESSURE_PLATE, + //WOODEN_TRAPDOORS + Material.ACACIA_TRAPDOOR, + Material.BIRCH_TRAPDOOR, + Material.DARK_OAK_TRAPDOOR, + Material.JUNGLE_TRAPDOOR, + Material.OAK_TRAPDOOR, + Material.SPRUCE_TRAPDOOR, + Material.MANGROVE_TRAPDOOR, + Material.CRIMSON_TRAPDOOR, + Material.WARPED_TRAPDOOR, + Materials.CHERRY_TRAPDOOR, + Materials.BAMBOO_TRAPDOOR, + Materials.PALE_OAK_TRAPDOOR, + //WOODEN_STAIRS + Material.ACACIA_STAIRS, + Material.BIRCH_STAIRS, + Material.DARK_OAK_STAIRS, + Material.JUNGLE_STAIRS, + Material.OAK_STAIRS, + Material.SPRUCE_STAIRS, + Material.MANGROVE_STAIRS, + Material.CRIMSON_STAIRS, + Material.WARPED_STAIRS, + Materials.CHERRY_STAIRS, + Materials.BAMBOO_STAIRS, + Materials.BAMBOO_MOSAIC_STAIRS, + Materials.PALE_OAK_STAIRS, + //WOODEN_SLABS + Material.ACACIA_SLAB, + Material.BIRCH_SLAB, + Material.DARK_OAK_SLAB, + Material.JUNGLE_SLAB, + Material.OAK_SLAB, + Material.SPRUCE_SLAB, + Material.MANGROVE_SLAB, + Material.CRIMSON_SLAB, + Material.WARPED_SLAB, + Materials.CHERRY_SLAB, + Materials.BAMBOO_SLAB, + Materials.BAMBOO_MOSAIC_SLAB, + Materials.PALE_OAK_SLAB, + //PLANKS + Material.ACACIA_PLANKS, + Material.BIRCH_PLANKS, + Material.DARK_OAK_PLANKS, + Material.JUNGLE_PLANKS, + Material.OAK_PLANKS, + Material.SPRUCE_PLANKS, + Material.MANGROVE_PLANKS, + Material.CRIMSON_PLANKS, + Material.WARPED_PLANKS, + Materials.CHERRY_PLANKS, + Materials.BAMBOO_PLANKS, + Materials.BAMBOO_MOSAIC, + Materials.PALE_OAK_PLANKS, + //MISC + Material.BEE_NEST, + Material.DRIED_KELP_BLOCK, + Material.BEEHIVE, + Material.CHEST, + Material.CRAFTING_TABLE, + Material.JUKEBOX, + Material.LADDER, + Material.LOOM, + Material.NOTE_BLOCK, + Material.BARREL, + Material.BOOKSHELF, + Material.CARTOGRAPHY_TABLE, + Material.FLETCHING_TABLE, + Material.CAMPFIRE, + Material.SOUL_CAMPFIRE, + Material.HONEYCOMB, + Material.LECTERN, + Material.COCOA, + Material.JACK_O_LANTERN, + Material.PUMPKIN, + Material.MELON, + Material.TRAPPED_CHEST + ).nonNull(); } diff --git a/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java b/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java index 6ab493b59..98ac2ec2a 100644 --- a/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java +++ b/src/main/java/art/arcane/adapt/content/item/KnowledgeOrb.java @@ -38,91 +38,91 @@ @AllArgsConstructor @Data public class KnowledgeOrb implements DataItem { - public static KnowledgeOrb io = new KnowledgeOrb(); + public static KnowledgeOrb io = new KnowledgeOrb(); - public static Data get(ItemStack is) { - return io.getData(is); - } - - public static String getSkill(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getSkill(); - } + public static Data get(ItemStack is) { + return io.getData(is); + } - return null; + public static String getSkill(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getSkill(); } - public static long getKnowledge(ItemStack stack) { - if (io.getData(stack) != null) { - return io.getData(stack).getKnowledge(); - } + return null; + } - return 0; + public static long getKnowledge(ItemStack stack) { + if (io.getData(stack) != null) { + return io.getData(stack).getKnowledge(); } - public static void set(ItemStack item, String skill, int knowledge) { - io.setData(item, new Data(skill, knowledge)); + return 0; + } + + public static void set(ItemStack item, String skill, int knowledge) { + io.setData(item, new Data(skill, knowledge)); + } + + public static ItemStack with(String skill, int knowledge) { + return io.withData(new Data(skill, knowledge)); + } + + public static ItemStack with(Map knowledgeMap) { + return io.withData(new Data(knowledgeMap)); + } + + @Override + public Material getMaterial() { + return Material.SNOWBALL; + } + + @Override + public Class getType() { + return KnowledgeOrb.Data.class; + } + + @Override + public void applyLore(Data data, List lore) { + for (Map.Entry entry : data.getKnowledgeMap().entrySet()) { + String skill = entry.getKey(); + int knowledge = entry.getValue(); + lore.add(C.WHITE + Localizer.dLocalize("snippets.knowledge_orb.contains") + " " + C.UNDERLINE + C.WHITE + "" + knowledge + " " + Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skill).getDisplayName() + " " + Localizer.dLocalize("snippets.knowledge_orb.knowledge")); } - - public static ItemStack with(String skill, int knowledge) { - return io.withData(new Data(skill, knowledge)); - } - - public static ItemStack with(Map knowledgeMap) { - return io.withData(new Data(knowledgeMap)); - } - - @Override - public Material getMaterial() { - return Material.SNOWBALL; - } - - @Override - public Class getType() { - return KnowledgeOrb.Data.class; + lore.add(C.LIGHT_PURPLE + Localizer.dLocalize("snippets.knowledge_orb.rightclick") + " " + C.GRAY + Localizer.dLocalize("snippets.knowledge_orb.togainknowledge")); + } + + @Override + public void applyMeta(Data data, ItemMeta meta) { + meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); + meta.setDisplayName(Localizer.dLocalize("snippets.knowledge_orb.knowledge_orb")); + } + + @AllArgsConstructor + @lombok.Data + public static class Data { + private Map knowledgeMap; + + public Data(String skill, int knowledge) { + this.knowledgeMap = new HashMap<>(); + this.knowledgeMap.put(skill, knowledge); } - @Override - public void applyLore(Data data, List lore) { - for (Map.Entry entry : data.getKnowledgeMap().entrySet()) { - String skill = entry.getKey(); - int knowledge = entry.getValue(); - lore.add(C.WHITE + Localizer.dLocalize("snippets.knowledge_orb.contains") + " " + C.UNDERLINE + C.WHITE + "" + knowledge + " " + Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(skill).getDisplayName() + " " + Localizer.dLocalize("snippets.knowledge_orb.knowledge")); - } - lore.add(C.LIGHT_PURPLE + Localizer.dLocalize("snippets.knowledge_orb.rightclick") + " " + C.GRAY + Localizer.dLocalize("snippets.knowledge_orb.togainknowledge")); + public String getSkill() { + return knowledgeMap.keySet().iterator().next(); } - @Override - public void applyMeta(Data data, ItemMeta meta) { - meta.addEnchant(Enchantment.BINDING_CURSE, 10, true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS); - meta.setDisplayName(Localizer.dLocalize("snippets.knowledge_orb.knowledge_orb")); + public int getKnowledge() { + return knowledgeMap.values().iterator().next(); } - @AllArgsConstructor - @lombok.Data - public static class Data { - private Map knowledgeMap; - - public Data(String skill, int knowledge) { - this.knowledgeMap = new HashMap<>(); - this.knowledgeMap.put(skill, knowledge); - } - - public String getSkill() { - return knowledgeMap.keySet().iterator().next(); - } - - public int getKnowledge() { - return knowledgeMap.values().iterator().next(); - } - - public void apply(Player p) { - for (Map.Entry entry : knowledgeMap.entrySet()) { - String skill = entry.getKey(); - int knowledge = entry.getValue(); - Adapt.instance.getAdaptServer().getPlayer(p).getSkillLine(skill).giveKnowledge(knowledge); - } - } + public void apply(Player p) { + for (Map.Entry entry : knowledgeMap.entrySet()) { + String skill = entry.getKey(); + int knowledge = entry.getValue(); + Adapt.instance.getAdaptServer().getPlayer(p).getSkillLine(skill).giveKnowledge(knowledge); + } } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java index 9b51f58f8..489c76c02 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiArmor.java @@ -27,36 +27,36 @@ import java.util.List; public class MultiArmor implements MultiItem { - @Override - public boolean supportsItem(ItemStack itemStack) { - return true; + @Override + public boolean supportsItem(ItemStack itemStack) { + return true; + } + + @Override + public String getKey() { + return "multiarmor"; + } + + @Override + public void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems) { + List lore = new ArrayList<>(); + lore.add("MultiArmor (" + (otherItems.size() + 1) + " Items)"); + lore.add("-> " + Form.capitalizeWords(item.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); + + for (ItemStack i : otherItems) { + lore.add("- " + Form.capitalizeWords(i.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); } - @Override - public String getKey() { - return "multiarmor"; - } - - @Override - public void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems) { - List lore = new ArrayList<>(); - lore.add("MultiArmor (" + (otherItems.size() + 1) + " Items)"); - lore.add("-> " + Form.capitalizeWords(item.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); + meta.setLore(lore); + } - for (ItemStack i : otherItems) { - lore.add("- " + Form.capitalizeWords(i.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); - } - - meta.setLore(lore); - } + public ItemStack nextElytra(ItemStack item) { + return nextMatching(item, i -> i.getType().equals(Material.ELYTRA)); + } - public ItemStack nextElytra(ItemStack item) { - return nextMatching(item, i -> i.getType().equals(Material.ELYTRA)); - } - - public ItemStack nextChestplate(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_CHESTPLATE")); - } + public ItemStack nextChestplate(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_CHESTPLATE")); + } } diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java index 8dee0144c..5fc482232 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/MultiItem.java @@ -20,9 +20,9 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.nms.NMS; -import art.arcane.volmlib.util.inventorygui.WindowResolution; import art.arcane.adapt.util.common.io.BukkitGson; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.inventorygui.WindowResolution; import lombok.*; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -34,149 +34,149 @@ import java.util.stream.Collectors; public interface MultiItem { - boolean supportsItem(ItemStack itemStack); + boolean supportsItem(ItemStack itemStack); - String getKey(); + String getKey(); - default WindowResolution getWindowType() { - return WindowResolution.W5_H1; - } + default WindowResolution getWindowType() { + return WindowResolution.W5_H1; + } - default ItemStack build(ItemStack... stacks) { - ItemStack s = stacks[0]; + default ItemStack build(ItemStack... stacks) { + ItemStack s = stacks[0]; - for (int i = 1; i < stacks.length; i++) { - add(s, stacks[i]); - } - - return s; + for (int i = 1; i < stacks.length; i++) { + add(s, stacks[i]); } - default boolean remove(ItemStack multi, ItemStack toRemove) { - int ind = getItems(multi).indexOf(toRemove); - if (ind == -1) { - return false; - } + return s; + } - remove(multi, ind); - return true; + default boolean remove(ItemStack multi, ItemStack toRemove) { + int ind = getItems(multi).indexOf(toRemove); + if (ind == -1) { + return false; } - default void remove(ItemStack multi, int index) { - List it = getItems(multi); - it.remove(index); - setItems(multi, it); - } + remove(multi, ind); + return true; + } + + default void remove(ItemStack multi, int index) { + List it = getItems(multi); + it.remove(index); + setItems(multi, it); + } + + default void add(ItemStack multi, ItemStack item) { + if (isMultiItem(item)) { + explode(item).forEach(i -> add(multi, i)); + } else { + setItems(multi, getItems(multi).qadd(item)); + } + } - default void add(ItemStack multi, ItemStack item) { - if (isMultiItem(item)) { - explode(item).forEach(i -> add(multi, i)); - } else { - setItems(multi, getItems(multi).qadd(item)); - } + default ItemStack nextMatching(ItemStack item, Predicate predicate) { + KList items = getItems(item); + for (int i = 0; i < items.size(); i++) { + if (predicate.test(items.get(i))) { + return switchTo(item, i); + } } - default ItemStack nextMatching(ItemStack item, Predicate predicate) { - KList items = getItems(item); - for (int i = 0; i < items.size(); i++) { - if (predicate.test(items.get(i))) { - return switchTo(item, i); - } - } + return item; + } - return item; - } + default ItemStack nextTool(ItemStack multi) { + return switchTo(multi, 0); + } - default ItemStack nextTool(ItemStack multi) { - return switchTo(multi, 0); - } + default ItemStack switchTo(ItemStack multi, int index) { + List items = getItems(multi); + ItemStack next = items.remove(index); + items.add(getRealItem(multi)); + setItems(next, items); + return next; + } - default ItemStack switchTo(ItemStack multi, int index) { - List items = getItems(multi); - ItemStack next = items.remove(index); - items.add(getRealItem(multi)); - setItems(next, items); - return next; - } + default void setItems(ItemStack multi, List itemStacks) { + setMultiItemData(multi, MultiItemData.builder() + .rawItems(itemStacks.stream().filter(this::supportsItem) + .map(NMS::serializeStack) + .collect(Collectors.toList())) + .build()); + } - default void setItems(ItemStack multi, List itemStacks) { - setMultiItemData(multi, MultiItemData.builder() - .rawItems(itemStacks.stream().filter(this::supportsItem) - .map(NMS::serializeStack) - .collect(Collectors.toList())) - .build()); - } + default KList getItems(ItemStack multi) { + MultiItemData d = getMultiItemData(multi); - default KList getItems(ItemStack multi) { - MultiItemData d = getMultiItemData(multi); - - if (d == null) { - return new KList<>(); - } - - return d.getItems(); - } - - default KList explode(ItemStack multi) { - KList it = new KList<>(); - it.add(getRealItem(multi)); - it.add(getItems(multi)); - return it; + if (d == null) { + return new KList<>(); } - void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems); + return d.getItems(); + } - default boolean isMultiItem(ItemStack item) { - return supportsItem(item) && getMultiItemData(item) != null; - } + default KList explode(ItemStack multi) { + KList it = new KList<>(); + it.add(getRealItem(multi)); + it.add(getItems(multi)); + return it; + } - default ItemStack getRealItem(ItemStack multi) { - ItemStack c = multi.clone(); - if (c.hasItemMeta()) { - ItemMeta meta = c.getItemMeta(); - meta.getPersistentDataContainer().remove(new NamespacedKey(Adapt.instance, getKey())); - c.setItemMeta(meta); - } + void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems); - return c; - } + default boolean isMultiItem(ItemStack item) { + return supportsItem(item) && getMultiItemData(item) != null; + } - default MultiItemData getMultiItemData(ItemStack multi) { - try { - ItemMeta meta = multi.getItemMeta(); - String st = meta.getPersistentDataContainer() - .get(new NamespacedKey(Adapt.instance, getKey()), PersistentDataType.STRING); - return BukkitGson.gson.fromJson(st, MultiItemData.class); - } catch (Throwable e) { - return null; - } + default ItemStack getRealItem(ItemStack multi) { + ItemStack c = multi.clone(); + if (c.hasItemMeta()) { + ItemMeta meta = c.getItemMeta(); + meta.getPersistentDataContainer().remove(new NamespacedKey(Adapt.instance, getKey())); + c.setItemMeta(meta); } - default void setMultiItemData(ItemStack multi, MultiItemData data) { - String s = BukkitGson.gson.toJson(data); - ItemMeta meta = multi.getItemMeta(); - meta.getPersistentDataContainer() - .set(new NamespacedKey(Adapt.instance, getKey()), PersistentDataType.STRING, s); - multi.setItemMeta(meta); - meta = multi.getItemMeta(); - onApplyMeta(multi, meta, getItems(multi)); - multi.setItemMeta(meta); - } + return c; + } - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - class MultiItemData { - @Singular - List rawItems; - - KList getItems() { - return rawItems.stream().map(NMS::deserializeStack).collect(KList.collector()); - } - - void setItems(List is) { - rawItems = is.stream().map(NMS::serializeStack).collect(KList.collector()); - } - } + default MultiItemData getMultiItemData(ItemStack multi) { + try { + ItemMeta meta = multi.getItemMeta(); + String st = meta.getPersistentDataContainer() + .get(new NamespacedKey(Adapt.instance, getKey()), PersistentDataType.STRING); + return BukkitGson.gson.fromJson(st, MultiItemData.class); + } catch (Throwable e) { + return null; + } + } + + default void setMultiItemData(ItemStack multi, MultiItemData data) { + String s = BukkitGson.gson.toJson(data); + ItemMeta meta = multi.getItemMeta(); + meta.getPersistentDataContainer() + .set(new NamespacedKey(Adapt.instance, getKey()), PersistentDataType.STRING, s); + multi.setItemMeta(meta); + meta = multi.getItemMeta(); + onApplyMeta(multi, meta, getItems(multi)); + multi.setItemMeta(meta); + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class MultiItemData { + @Singular + List rawItems; + + KList getItems() { + return rawItems.stream().map(NMS::deserializeStack).collect(KList.collector()); + } + + void setItems(List is) { + rawItems = is.stream().map(NMS::serializeStack).collect(KList.collector()); + } + } } diff --git a/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java index 5a6c3fded..9b0bfb7d3 100644 --- a/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java +++ b/src/main/java/art/arcane/adapt/content/item/multiItems/OmniTool.java @@ -27,78 +27,78 @@ import java.util.List; public class OmniTool implements MultiItem { - @Override - public boolean supportsItem(ItemStack itemStack) { - return true; + @Override + public boolean supportsItem(ItemStack itemStack) { + return true; + } + + @Override + public String getKey() { + return "omnitool"; + } + + @Override + public void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems) { + List lore = new ArrayList<>(); + lore.add("Leatherman (" + (otherItems.size() + 1) + " Items)"); + lore.add("-> " + Form.capitalizeWords(item.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); + + for (ItemStack i : otherItems) { + lore.add("- " + Form.capitalizeWords(i.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); } - @Override - public String getKey() { - return "omnitool"; - } - - @Override - public void onApplyMeta(ItemStack item, ItemMeta meta, List otherItems) { - List lore = new ArrayList<>(); - lore.add("Leatherman (" + (otherItems.size() + 1) + " Items)"); - lore.add("-> " + Form.capitalizeWords(item.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); - - for (ItemStack i : otherItems) { - lore.add("- " + Form.capitalizeWords(i.getType().name().toLowerCase().replaceAll("\\Q_\\E", " "))); - } - - meta.setLore(lore); - } - - public ItemStack nextPickaxe(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_PICKAXE")); - } - - public ItemStack nextAxe(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_AXE")); - } - - public ItemStack nextSword(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_SWORD")); - } - - public ItemStack nextShovel(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_SHOVEL")); - } - - public ItemStack nextHoe(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_HOE")); - } - - public ItemStack nextShears(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("SHEARS")); - } - - public ItemStack nextFnS(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("FLINT_AND_STEEL")); - } - - public ItemStack nextItem(ItemStack item) { - return nextMatching(item, i -> i.getType().name().endsWith("_PICKAXE") || i.getType().name().endsWith("_AXE") || i.getType().name().endsWith("_SWORD") || i.getType().name().endsWith("_SHOVEL") || i.getType().name().endsWith("_HOE") || i.getType().name().endsWith("SHEARS")); - } - - public ItemStack nextNonMatchingItem(ItemStack item, Material material) { - if (material.toString().contains("_PICKAXE")) { - return nextAxe(item); - } else if (material.toString().contains("_AXE")) { - return nextSword(item); - } else if (material.toString().contains("_SWORD")) { - return nextShovel(item); - } else if (material.toString().contains("_SHOVEL")) { - return nextHoe(item); - } else if (material.toString().contains("_HOE")) { - return nextShears(item); - } else if (material.toString().contains("SHEARS")) { - return nextPickaxe(item); - } else if (material.toString().contains("FLINT_AND_STEEL")) { - return nextFnS(item); - } else { - return nextItem(item); - } + meta.setLore(lore); + } + + public ItemStack nextPickaxe(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_PICKAXE")); + } + + public ItemStack nextAxe(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_AXE")); + } + + public ItemStack nextSword(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_SWORD")); + } + + public ItemStack nextShovel(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_SHOVEL")); + } + + public ItemStack nextHoe(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_HOE")); + } + + public ItemStack nextShears(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("SHEARS")); + } + + public ItemStack nextFnS(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("FLINT_AND_STEEL")); + } + + public ItemStack nextItem(ItemStack item) { + return nextMatching(item, i -> i.getType().name().endsWith("_PICKAXE") || i.getType().name().endsWith("_AXE") || i.getType().name().endsWith("_SWORD") || i.getType().name().endsWith("_SHOVEL") || i.getType().name().endsWith("_HOE") || i.getType().name().endsWith("SHEARS")); + } + + public ItemStack nextNonMatchingItem(ItemStack item, Material material) { + if (material.toString().contains("_PICKAXE")) { + return nextAxe(item); + } else if (material.toString().contains("_AXE")) { + return nextSword(item); + } else if (material.toString().contains("_SWORD")) { + return nextShovel(item); + } else if (material.toString().contains("_SHOVEL")) { + return nextHoe(item); + } else if (material.toString().contains("_HOE")) { + return nextShears(item); + } else if (material.toString().contains("SHEARS")) { + return nextPickaxe(item); + } else if (material.toString().contains("FLINT_AND_STEEL")) { + return nextFnS(item); + } else { + return nextItem(item); } + } } diff --git a/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java index cea0e84d8..683620b60 100644 --- a/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java +++ b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwner.java @@ -26,5 +26,5 @@ @AllArgsConstructor @Data public class BrewingStandOwner { - private UUID owner; + private UUID owner; } diff --git a/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java index 5631433db..681aefcf0 100644 --- a/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java +++ b/src/main/java/art/arcane/adapt/content/matter/BrewingStandOwnerMatter.java @@ -26,22 +26,22 @@ import java.util.UUID; public class BrewingStandOwnerMatter extends RawMatter { - public BrewingStandOwnerMatter(int w, int h, int d) { - super(w, h, d, BrewingStandOwner.class); - } + public BrewingStandOwnerMatter(int w, int h, int d) { + super(w, h, d, BrewingStandOwner.class); + } - public BrewingStandOwnerMatter() { - this(1, 1, 1); - } + public BrewingStandOwnerMatter() { + this(1, 1, 1); + } - @Override - public void writeNode(BrewingStandOwner brewingStandOwner, DataOutputStream dataOutputStream) throws IOException { - dataOutputStream.writeLong(brewingStandOwner.getOwner().getMostSignificantBits()); - dataOutputStream.writeLong(brewingStandOwner.getOwner().getLeastSignificantBits()); - } + @Override + public void writeNode(BrewingStandOwner brewingStandOwner, DataOutputStream dataOutputStream) throws IOException { + dataOutputStream.writeLong(brewingStandOwner.getOwner().getMostSignificantBits()); + dataOutputStream.writeLong(brewingStandOwner.getOwner().getLeastSignificantBits()); + } - @Override - public BrewingStandOwner readNode(DataInputStream dataInputStream) throws IOException { - return new BrewingStandOwner(new UUID(dataInputStream.readLong(), dataInputStream.readLong())); - } + @Override + public BrewingStandOwner readNode(DataInputStream dataInputStream) throws IOException { + return new BrewingStandOwner(new UUID(dataInputStream.readLong(), dataInputStream.readLong())); + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java b/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java index 5c83ec12d..7127b8dca 100644 --- a/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/ChestProtectProtector.java @@ -10,27 +10,27 @@ import org.bukkit.entity.Player; public class ChestProtectProtector implements Protector { - private final ChestProtectAddon chestProtect; + private final ChestProtectAddon chestProtect; - public ChestProtectProtector() { - this.chestProtect = new ChestProtectAddon(Adapt.instance); - } + public ChestProtectProtector() { + this.chestProtect = new ChestProtectAddon(Adapt.instance); + } - @Override - public boolean canAccessChest(Player player, Location chestlocation, Adaptation adaptation) { - if (!chestProtect.isProtectable(chestlocation.getBlock())) return true; - BlockProtection blockProtection = chestProtect.getProtection(chestlocation); - if (blockProtection == null) return true; - return blockProtection.isTrusted(player.getUniqueId()); - } + @Override + public boolean canAccessChest(Player player, Location chestlocation, Adaptation adaptation) { + if (!chestProtect.isProtectable(chestlocation.getBlock())) return true; + BlockProtection blockProtection = chestProtect.getProtection(chestlocation); + if (blockProtection == null) return true; + return blockProtection.isTrusted(player.getUniqueId()); + } - @Override - public String getName() { - return "ChestProtect"; - } + @Override + public String getName() { + return "ChestProtect"; + } - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isChestProtect(); - } + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isChestProtect(); + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java index 231db4ce7..09ded074f 100644 --- a/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/FactionsClaimProtector.java @@ -27,32 +27,32 @@ public class FactionsClaimProtector implements Protector { - @Override - public boolean checkRegion(Player player, Location location, Adaptation adaptation) { - Faction f = Board.getInstance().getFactionAt(new FLocation(player.getLocation())); - return checkPerm(player, f, adaptation) || f.isWilderness(); - } - - @Override - public boolean canPVP(Player player, Location victimLocation, Adaptation adaptation) { - Faction f = Board.getInstance().getFactionAt(new FLocation(victimLocation)); - return checkPerm(player, f, adaptation) || !f.noPvPInTerritory(); - } - - private boolean checkPerm(Player player, Faction f, Adaptation adaptation) { - FPlayer fp = FPlayers.getInstance().getByPlayer(player); - return f == null - || fp.getFaction() == f - || fp.isAdminBypassing(); - } - - @Override - public String getName() { - return "Factions"; - } - - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isFactionsClaim(); - } + @Override + public boolean checkRegion(Player player, Location location, Adaptation adaptation) { + Faction f = Board.getInstance().getFactionAt(new FLocation(player.getLocation())); + return checkPerm(player, f, adaptation) || f.isWilderness(); + } + + @Override + public boolean canPVP(Player player, Location victimLocation, Adaptation adaptation) { + Faction f = Board.getInstance().getFactionAt(new FLocation(victimLocation)); + return checkPerm(player, f, adaptation) || !f.noPvPInTerritory(); + } + + private boolean checkPerm(Player player, Faction f, Adaptation adaptation) { + FPlayer fp = FPlayers.getInstance().getByPlayer(player); + return f == null + || fp.getFaction() == f + || fp.isAdminBypassing(); + } + + @Override + public String getName() { + return "Factions"; + } + + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isFactionsClaim(); + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java index 84e762e31..310b198dd 100644 --- a/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/GriefDefenderProtector.java @@ -29,74 +29,74 @@ import java.util.UUID; public class GriefDefenderProtector implements Protector { - /** - * This api is garbage, and obfuscated. - * If i can get a jar ill improve it, but for now this is the best i can do. - * Or if someone wants to make a PR feel free. - * - * I as an author do not support this api, and do not recommend it, - * as they are making ME pay $15(spigot) + $5(patreon) per month to be - * able to ask questions in their discord, and get unobfuscated jars. - * - */ + /** + * This api is garbage, and obfuscated. If i can get a jar ill improve it, but + * for now this is the best i can do. Or if someone wants to make a PR feel + * free. + *

+ * I as an author do not support this api, and do not recommend it, as they + * are making ME pay $15(spigot) + $5(patreon) per month to be able to ask + * questions in their discord, and get unobfuscated jars. + * + */ - @Override - public boolean checkRegion(Player player, Location location, Adaptation adaptation) { - final Claim claim = GriefDefender.getCore().getClaimAt(location); - return checkPerm(player, claim, adaptation) || claim.isWilderness(); - } + @Override + public boolean checkRegion(Player player, Location location, Adaptation adaptation) { + final Claim claim = GriefDefender.getCore().getClaimAt(location); + return checkPerm(player, claim, adaptation) || claim.isWilderness(); + } - @Override - public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { - final Claim claim = GriefDefender.getCore().getClaimAt(entityLocation); - if (checkPerm(player, claim, adaptation)) { - return claim.isPvpAllowed(); - } - return false; + @Override + public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { + final Claim claim = GriefDefender.getCore().getClaimAt(entityLocation); + if (checkPerm(player, claim, adaptation)) { + return claim.isPvpAllowed(); } + return false; + } - private boolean checkPerm(Player player, Claim claim, Adaptation adaptation) { - if (claim == null) { - return true; - } - UUID uuid = player.getUniqueId(); - return claim.isWilderness() - || claim.getOwnerUniqueId().equals(uuid) - || claim.getUserTrusts().contains(uuid); + private boolean checkPerm(Player player, Claim claim, Adaptation adaptation) { + if (claim == null) { + return true; } + UUID uuid = player.getUniqueId(); + return claim.isWilderness() + || claim.getOwnerUniqueId().equals(uuid) + || claim.getUserTrusts().contains(uuid); + } - @Override - public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { - return checkPerm(player, GriefDefender.getCore().getClaimAt(entityLocation), adaptation); - } + @Override + public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { + return checkPerm(player, GriefDefender.getCore().getClaimAt(entityLocation), adaptation); + } - @Override - public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { - return checkPerm(player, GriefDefender.getCore().getClaimAt(targetLocation), adaptation); - } + @Override + public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { + return checkPerm(player, GriefDefender.getCore().getClaimAt(targetLocation), adaptation); + } - @Override - public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { - return checkPerm(player, GriefDefender.getCore().getClaimAt(chestLocation), adaptation); - } + @Override + public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { + return checkPerm(player, GriefDefender.getCore().getClaimAt(chestLocation), adaptation); + } - @Override - public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { - return checkPerm(player, GriefDefender.getCore().getClaimAt(blockLocation), adaptation); - } + @Override + public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { + return checkPerm(player, GriefDefender.getCore().getClaimAt(blockLocation), adaptation); + } - @Override - public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { - return checkPerm(player, GriefDefender.getCore().getClaimAt(blockLocation), adaptation); - } + @Override + public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { + return checkPerm(player, GriefDefender.getCore().getClaimAt(blockLocation), adaptation); + } - @Override - public String getName() { - return "GriefDefender"; - } + @Override + public String getName() { + return "GriefDefender"; + } - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isFactionsClaim(); - } + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isFactionsClaim(); + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java b/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java index 6308ac395..a7e2b7aba 100644 --- a/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/GriefPreventionProtector.java @@ -33,51 +33,49 @@ public class GriefPreventionProtector implements Protector { - @Override - public boolean canBlockBreak(Player player, Location location, Adaptation adaptation) { - return canEditClaim(player, location); + @Override + public boolean canBlockBreak(Player player, Location location, Adaptation adaptation) { + return canEditClaim(player, location); + } + + @Override + public boolean canBlockPlace(Player player, Location location, Adaptation adaptation) { + return canEditClaim(player, location); + } + + @Override + public String getName() { + return "GriefPrevention"; + } + + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isGriefprevention(); + } + + @Override + public void unregister() { + Protector.super.unregister(); + } + + + private boolean canEditClaim(Player player, Location location) { + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(location, true, null); + PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); + + if (claim == null) { + return true; } - - @Override - public boolean canBlockPlace(Player player, Location location, Adaptation adaptation) { - return canEditClaim(player, location); - } - - @Override - public String getName() { - return "GriefPrevention"; - } - - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isGriefprevention(); - } - - @Override - public void unregister() { - Protector.super.unregister(); + //If doesn't check is adminclaim getting ownerid return null + if (!claim.isAdminClaim() && Objects.equals(claim.getOwnerID(), player.getUniqueId())) { + return true; + } else if (claim.getPermission(player.getUniqueId().toString()) == ClaimPermission.Build) { + return true; } + return playerData.ignoreClaims || claim.isAdminClaim() && player.hasPermission("griefprevention.adminclaims"); - - private boolean canEditClaim(Player player, Location location) { - Claim claim = GriefPrevention.instance.dataStore.getClaimAt(location, true, null); - PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); - - if (claim == null) { - return true; - } - //If doesn't check is adminclaim getting ownerid return null - if (!claim.isAdminClaim() && Objects.equals(claim.getOwnerID(), player.getUniqueId())) { - return true; - } - else if (claim.getPermission(player.getUniqueId().toString()) == ClaimPermission.Build) { - return true; - } - - return playerData.ignoreClaims || claim.isAdminClaim() && player.hasPermission("griefprevention.adminclaims"); - - } + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java index 82c810efd..e7477f745 100644 --- a/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/LocketteProProtector.java @@ -8,18 +8,18 @@ import org.bukkit.entity.Player; public class LocketteProProtector implements Protector { - @Override - public boolean canAccessChest(Player player, Location chestlocation, Adaptation adaptation) { - return LocketteProAPI.isOwner(chestlocation.getBlock(), player); - } + @Override + public boolean canAccessChest(Player player, Location chestlocation, Adaptation adaptation) { + return LocketteProAPI.isOwner(chestlocation.getBlock(), player); + } - @Override - public String getName() { - return "LockettePro"; - } + @Override + public String getName() { + return "LockettePro"; + } - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isLockettePro(); - } + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isLockettePro(); + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java index 21113939b..14fe9e05f 100644 --- a/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/ResidenceProtector.java @@ -15,79 +15,79 @@ public class ResidenceProtector implements Protector { - public ResidenceProtector() { - FlagPermissions.addFlag("use-adaptations"); - } - - @Override - public boolean checkRegion(Player player, Location location, Adaptation adaptation) { - return checkPerm(player, location, "use-adaptations"); - } - - @Override - public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.destroy); - } - - @Override - public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.place); - } - - @Override - public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.pvp); - } - - @Override - public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.damage); - } - - @Override - public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { - return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.use); - } - - @Override - public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { - return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.container); - } - - private boolean checkPerm(Player player, Location location, Flags flag) { - AtomicBoolean perm = new AtomicBoolean(true); - J.s(() -> { - if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); - if (res != null) { - perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); - } - } - }); - return perm.get(); - } - - private boolean checkPerm(Player player, Location location, String flag) { - AtomicBoolean perm = new AtomicBoolean(true); - J.s(() -> { - if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { - ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); - if (res != null) { - perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); - } - } - }); - return perm.get(); - } - - @Override - public String getName() { - return "Residence"; - } - - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isResidence(); - } + public ResidenceProtector() { + FlagPermissions.addFlag("use-adaptations"); + } + + @Override + public boolean checkRegion(Player player, Location location, Adaptation adaptation) { + return checkPerm(player, location, "use-adaptations"); + } + + @Override + public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.destroy); + } + + @Override + public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.place); + } + + @Override + public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.pvp); + } + + @Override + public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.damage); + } + + @Override + public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { + return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.use); + } + + @Override + public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { + return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.container); + } + + private boolean checkPerm(Player player, Location location, Flags flag) { + AtomicBoolean perm = new AtomicBoolean(true); + J.s(() -> { + if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { + ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); + if (res != null) { + perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); + } + } + }); + return perm.get(); + } + + private boolean checkPerm(Player player, Location location, String flag) { + AtomicBoolean perm = new AtomicBoolean(true); + J.s(() -> { + if (!Residence.getInstance().isDisabledWorld(location.getWorld())) { + ClaimedResidence res = Residence.getInstance().getResidenceManager().getByLoc(location); + if (res != null) { + perm.set(res.getPermissions().playerHas(player.getName(), flag, true)); + } + } + }); + return perm.get(); + } + + @Override + public String getName() { + return "Residence"; + } + + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isResidence(); + } } diff --git a/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java index e663025b7..06ec781c1 100644 --- a/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java +++ b/src/main/java/art/arcane/adapt/content/protector/WorldGuardProtector.java @@ -40,92 +40,91 @@ public class WorldGuardProtector implements Protector { - private final RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); - private final StateFlag flag = registerFlag(); - - - @Override - public boolean checkRegion(Player player, Location location, Adaptation adaptation) { - return checkPerm(player, location, flag); - } - - @Override - public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.BLOCK_BREAK); - } - - @Override - public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { - return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.BLOCK_PLACE); - } - - @Override - public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.PVP); - } - - @Override - public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { - return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.DAMAGE_ANIMALS); - } - - @Override - public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { - return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.INTERACT); - } - - @Override - public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { - return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.CHEST_ACCESS); - } - - private boolean checkPerm(Player player, Location location, StateFlag flag) { - RegionQuery regionQuery = container.createQuery(); - com.sk89q.worldedit.util.Location loc = BukkitAdapter.adapt(location); - if (!hasBypass(player, location)) - return regionQuery.queryState(loc, WorldGuardPlugin.inst().wrapPlayer(player), flag) != StateFlag.State.DENY; - return true; - } - - @Override - public String getName() { - return "WorldGuard"; - } - - @Override - public boolean isEnabledByDefault() { - return AdaptConfig.get().getProtectorSupport().isWorldguard(); - } - - private boolean hasBypass(Player p, Location l) { - LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(p); - com.sk89q.worldedit.world.World world = BukkitAdapter.adapt(l.getWorld()); - return WorldGuard.getInstance().getPlatform().getSessionManager().hasBypass(localPlayer, world); - } - - public static StateFlag registerFlag() { - FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry(); - StateFlag flag = (StateFlag) registry.get("use-adaptations"); - if (flag != null) return flag; - flag = new StateFlag("use-adaptations", false); - - try { - registry.register(flag); - } catch (IllegalStateException ignored) { - Adapt.warn("WorldGuard flag was not registered! Injecting it now..."); - try { - // Access the flags field of the registry - Field field = registry.getClass().getDeclaredField("flags"); - // This line makes the private field accessible - field.setAccessible(true); - // Get the flags from the registry - ConcurrentMap> flags = (ConcurrentMap>) field.get(registry); - // Add it to the registry - flags.put(flag.getName().toLowerCase(), flag); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - } - return flag; + private final RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + private final StateFlag flag = registerFlag(); + + public static StateFlag registerFlag() { + FlagRegistry registry = WorldGuard.getInstance().getFlagRegistry(); + StateFlag flag = (StateFlag) registry.get("use-adaptations"); + if (flag != null) return flag; + flag = new StateFlag("use-adaptations", false); + + try { + registry.register(flag); + } catch (IllegalStateException ignored) { + Adapt.warn("WorldGuard flag was not registered! Injecting it now..."); + try { + // Access the flags field of the registry + Field field = registry.getClass().getDeclaredField("flags"); + // This line makes the private field accessible + field.setAccessible(true); + // Get the flags from the registry + ConcurrentMap> flags = (ConcurrentMap>) field.get(registry); + // Add it to the registry + flags.put(flag.getName().toLowerCase(), flag); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } } + return flag; + } + + @Override + public boolean checkRegion(Player player, Location location, Adaptation adaptation) { + return checkPerm(player, location, flag); + } + + @Override + public boolean canBlockBreak(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.BLOCK_BREAK); + } + + @Override + public boolean canBlockPlace(Player player, Location blockLocation, Adaptation adaptation) { + return checkRegion(player, blockLocation, adaptation) && checkPerm(player, blockLocation, Flags.BLOCK_PLACE); + } + + @Override + public boolean canPVP(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.PVP); + } + + @Override + public boolean canPVE(Player player, Location entityLocation, Adaptation adaptation) { + return checkRegion(player, entityLocation, adaptation) && checkPerm(player, entityLocation, Flags.DAMAGE_ANIMALS); + } + + @Override + public boolean canInteract(Player player, Location targetLocation, Adaptation adaptation) { + return checkRegion(player, targetLocation, adaptation) && checkPerm(player, targetLocation, Flags.INTERACT); + } + + @Override + public boolean canAccessChest(Player player, Location chestLocation, Adaptation adaptation) { + return checkRegion(player, chestLocation, adaptation) && checkPerm(player, chestLocation, Flags.CHEST_ACCESS); + } + + private boolean checkPerm(Player player, Location location, StateFlag flag) { + RegionQuery regionQuery = container.createQuery(); + com.sk89q.worldedit.util.Location loc = BukkitAdapter.adapt(location); + if (!hasBypass(player, location)) + return regionQuery.queryState(loc, WorldGuardPlugin.inst().wrapPlayer(player), flag) != StateFlag.State.DENY; + return true; + } + + @Override + public String getName() { + return "WorldGuard"; + } + + @Override + public boolean isEnabledByDefault() { + return AdaptConfig.get().getProtectorSupport().isWorldguard(); + } + + private boolean hasBypass(Player p, Location l) { + LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(p); + com.sk89q.worldedit.world.World world = BukkitAdapter.adapt(l.getWorld()); + return WorldGuard.getInstance().getPlatform().getSessionManager().hasBypass(localPlayer, world); + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java index ad4d36232..bae6a0177 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAgility.java @@ -35,229 +35,229 @@ import org.bukkit.event.player.PlayerMoveEvent; public class SkillAgility extends SimpleSkill { - public SkillAgility() { - super("agility", Localizer.dLocalize("skill.agility.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.agility.description")); - setDisplayName(Localizer.dLocalize("skill.agility.name")); - setColor(C.GREEN); - setInterval(975); - setIcon(Material.FEATHER); - registerAdaptation(new AgilityWindUp()); - registerAdaptation(new AgilityWallJump()); - registerAdaptation(new AgilitySuperJump()); - registerAdaptation(new AgilityArmorUp()); - registerAdaptation(new AgilityLadderSlide()); - registerAdaptation(new AgilityParkourMomentum()); - registerAdaptation(new AgilityRollLanding()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_BOOTS) - .key("challenge_move_1k") - .title(Localizer.dLocalize("advancement.challenge_move_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_move_1k.description")) - .model(CustomModel.get(Material.LEATHER_BOOTS, "advancement", "agility", "challenge_move_1k")) + public SkillAgility() { + super("agility", Localizer.dLocalize("skill.agility.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.agility.description")); + setDisplayName(Localizer.dLocalize("skill.agility.name")); + setColor(C.GREEN); + setInterval(975); + setIcon(Material.FEATHER); + registerAdaptation(new AgilityWindUp()); + registerAdaptation(new AgilityWallJump()); + registerAdaptation(new AgilitySuperJump()); + registerAdaptation(new AgilityArmorUp()); + registerAdaptation(new AgilityLadderSlide()); + registerAdaptation(new AgilityParkourMomentum()); + registerAdaptation(new AgilityRollLanding()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_BOOTS) + .key("challenge_move_1k") + .title(Localizer.dLocalize("advancement.challenge_move_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_move_1k.description")) + .model(CustomModel.get(Material.LEATHER_BOOTS, "advancement", "agility", "challenge_move_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_BOOTS) + .key("challenge_sprint_5k") + .title(Localizer.dLocalize("advancement.challenge_sprint_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_5k.description")) + .model(CustomModel.get(Material.IRON_BOOTS, "advancement", "agility", "challenge_sprint_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_BOOTS) + .key("challenge_sprint_50k") + .title(Localizer.dLocalize("advancement.challenge_sprint_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_50k.description")) + .model(CustomModel.get(Material.DIAMOND_BOOTS, "advancement", "agility", "challenge_sprint_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_BOOTS) - .key("challenge_sprint_5k") - .title(Localizer.dLocalize("advancement.challenge_sprint_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_5k.description")) - .model(CustomModel.get(Material.IRON_BOOTS, "advancement", "agility", "challenge_sprint_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_BOOTS) - .key("challenge_sprint_50k") - .title(Localizer.dLocalize("advancement.challenge_sprint_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_50k.description")) - .model(CustomModel.get(Material.DIAMOND_BOOTS, "advancement", "agility", "challenge_sprint_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_BOOTS) - .key("challenge_sprint_500k") - .title(Localizer.dLocalize("advancement.challenge_sprint_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_500k.description")) - .model(CustomModel.get(Material.NETHERITE_BOOTS, "advancement", "agility", "challenge_sprint_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_BOOTS) - .key("challenge_sprint_marathon") - .title(Localizer.dLocalize("advancement.challenge_sprint_marathon.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_marathon.description")) - .model(CustomModel.get(Material.GOLDEN_BOOTS, "advancement", "agility", "challenge_sprint_marathon")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_move_1k", "move", 1000, getConfig().challengeMove1kReward); - registerMilestone("challenge_sprint_5k", "move", 5000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_sprint_50k", "move", 50000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_sprint_500k", "move", 500000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_sprint_marathon", "move", 42195, getConfig().challengeSprintMarathonReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_BOOTS).key("challenge_sprint_dist_5k") - .title(Localizer.dLocalize("advancement.challenge_sprint_dist_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_dist_5k.description")) - .model(CustomModel.get(Material.GOLDEN_BOOTS, "advancement", "agility", "challenge_sprint_dist_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_BOOTS) - .key("challenge_sprint_dist_50k") - .title(Localizer.dLocalize("advancement.challenge_sprint_dist_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_sprint_dist_50k.description")) - .model(CustomModel.get(Material.DIAMOND_BOOTS, "advancement", "agility", "challenge_sprint_dist_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_sprint_dist_5k", "move.sprint", 5000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_sprint_dist_50k", "move.sprint", 50000, getConfig().challengeSprint5kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LILY_PAD).key("challenge_agility_swim_1k") - .title(Localizer.dLocalize("advancement.challenge_agility_swim_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_swim_1k.description")) - .model(CustomModel.get(Material.LILY_PAD, "advancement", "agility", "challenge_agility_swim_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HEART_OF_THE_SEA) - .key("challenge_agility_swim_10k") - .title(Localizer.dLocalize("advancement.challenge_agility_swim_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_swim_10k.description")) - .model(CustomModel.get(Material.HEART_OF_THE_SEA, "advancement", "agility", "challenge_agility_swim_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_swim_1k", "move.swim", 1000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_agility_swim_10k", "move.swim", 10000, getConfig().challengeSprint5kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FEATHER).key("challenge_fly_1k") - .title(Localizer.dLocalize("advancement.challenge_fly_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_fly_1k.description")) - .model(CustomModel.get(Material.FEATHER, "advancement", "agility", "challenge_fly_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ELYTRA) - .key("challenge_fly_10k") - .title(Localizer.dLocalize("advancement.challenge_fly_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_fly_10k.description")) - .model(CustomModel.get(Material.ELYTRA, "advancement", "agility", "challenge_fly_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_fly_1k", "move.fly", 1000, getConfig().challengeSprint5kReward); - registerMilestone("challenge_fly_10k", "move.fly", 10000, getConfig().challengeSprint5kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_LEGGINGS).key("challenge_agility_sneak_500") - .title(Localizer.dLocalize("advancement.challenge_agility_sneak_500.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_sneak_500.description")) - .model(CustomModel.get(Material.LEATHER_LEGGINGS, "advancement", "agility", "challenge_agility_sneak_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_LEGGINGS) - .key("challenge_agility_sneak_5k") - .title(Localizer.dLocalize("advancement.challenge_agility_sneak_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_agility_sneak_5k.description")) - .model(CustomModel.get(Material.IRON_LEGGINGS, "advancement", "agility", "challenge_agility_sneak_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_agility_sneak_500", "move.sneak", 500, getConfig().challengeSprint5kReward); - registerMilestone("challenge_agility_sneak_5k", "move.sneak", 5000, getConfig().challengeSprint5kReward * 2); - } + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_BOOTS) + .key("challenge_sprint_500k") + .title(Localizer.dLocalize("advancement.challenge_sprint_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_500k.description")) + .model(CustomModel.get(Material.NETHERITE_BOOTS, "advancement", "agility", "challenge_sprint_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()) + .build()) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_BOOTS) + .key("challenge_sprint_marathon") + .title(Localizer.dLocalize("advancement.challenge_sprint_marathon.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_marathon.description")) + .model(CustomModel.get(Material.GOLDEN_BOOTS, "advancement", "agility", "challenge_sprint_marathon")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_move_1k", "move", 1000, getConfig().challengeMove1kReward); + registerMilestone("challenge_sprint_5k", "move", 5000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_sprint_50k", "move", 50000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_sprint_500k", "move", 500000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_sprint_marathon", "move", 42195, getConfig().challengeSprintMarathonReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_BOOTS).key("challenge_sprint_dist_5k") + .title(Localizer.dLocalize("advancement.challenge_sprint_dist_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_dist_5k.description")) + .model(CustomModel.get(Material.GOLDEN_BOOTS, "advancement", "agility", "challenge_sprint_dist_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_BOOTS) + .key("challenge_sprint_dist_50k") + .title(Localizer.dLocalize("advancement.challenge_sprint_dist_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_sprint_dist_50k.description")) + .model(CustomModel.get(Material.DIAMOND_BOOTS, "advancement", "agility", "challenge_sprint_dist_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_sprint_dist_5k", "move.sprint", 5000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_sprint_dist_50k", "move.sprint", 50000, getConfig().challengeSprint5kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LILY_PAD).key("challenge_agility_swim_1k") + .title(Localizer.dLocalize("advancement.challenge_agility_swim_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_swim_1k.description")) + .model(CustomModel.get(Material.LILY_PAD, "advancement", "agility", "challenge_agility_swim_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HEART_OF_THE_SEA) + .key("challenge_agility_swim_10k") + .title(Localizer.dLocalize("advancement.challenge_agility_swim_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_swim_10k.description")) + .model(CustomModel.get(Material.HEART_OF_THE_SEA, "advancement", "agility", "challenge_agility_swim_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_swim_1k", "move.swim", 1000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_agility_swim_10k", "move.swim", 10000, getConfig().challengeSprint5kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FEATHER).key("challenge_fly_1k") + .title(Localizer.dLocalize("advancement.challenge_fly_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_fly_1k.description")) + .model(CustomModel.get(Material.FEATHER, "advancement", "agility", "challenge_fly_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ELYTRA) + .key("challenge_fly_10k") + .title(Localizer.dLocalize("advancement.challenge_fly_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_fly_10k.description")) + .model(CustomModel.get(Material.ELYTRA, "advancement", "agility", "challenge_fly_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_fly_1k", "move.fly", 1000, getConfig().challengeSprint5kReward); + registerMilestone("challenge_fly_10k", "move.fly", 10000, getConfig().challengeSprint5kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_LEGGINGS).key("challenge_agility_sneak_500") + .title(Localizer.dLocalize("advancement.challenge_agility_sneak_500.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_sneak_500.description")) + .model(CustomModel.get(Material.LEATHER_LEGGINGS, "advancement", "agility", "challenge_agility_sneak_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_LEGGINGS) + .key("challenge_agility_sneak_5k") + .title(Localizer.dLocalize("advancement.challenge_agility_sneak_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_agility_sneak_5k.description")) + .model(CustomModel.get(Material.IRON_LEGGINGS, "advancement", "agility", "challenge_agility_sneak_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_agility_sneak_500", "move.sneak", 500, getConfig().challengeSprint5kReward); + registerMilestone("challenge_agility_sneak_5k", "move.sneak", 5000, getConfig().challengeSprint5kReward * 2); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerMoveEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, e, () -> { - if (e.getFrom().getWorld() != null && e.getTo() != null && e.getFrom().getWorld().equals(e.getTo().getWorld())) { - double d = e.getFrom().distance(e.getTo()); - AdaptPlayer adaptPlayer = getPlayer(p); - adaptPlayer.getData().addStat("move", d); + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerMoveEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, e, () -> { + if (e.getFrom().getWorld() != null && e.getTo() != null && e.getFrom().getWorld().equals(e.getTo().getWorld())) { + double d = e.getFrom().distance(e.getTo()); + AdaptPlayer adaptPlayer = getPlayer(p); + adaptPlayer.getData().addStat("move", d); - if (p.isSneaking()) { - adaptPlayer.getData().addStat("move.sneak", d); - } else if (p.isFlying()) { - adaptPlayer.getData().addStat("move.fly", d); - } else if (p.isSwimming()) { - adaptPlayer.getData().addStat("move.swim", d); - } else if (p.isSprinting()) { - adaptPlayer.getData().addStat("move.sprint", d); - } + if (p.isSneaking()) { + adaptPlayer.getData().addStat("move.sneak", d); + } else if (p.isFlying()) { + adaptPlayer.getData().addStat("move.fly", d); + } else if (p.isSwimming()) { + adaptPlayer.getData().addStat("move.swim", d); + } else if (p.isSprinting()) { + adaptPlayer.getData().addStat("move.sprint", d); + } - // Add XP for moving - xpSilent(p, getConfig().moveXpPassive * d, "agility:move"); - } - }); - } + // Add XP for moving + xpSilent(p, getConfig().moveXpPassive * d, "agility:move"); + } + }); + } - @Override - public void onTick() { - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> { - checkStatTrackers(adaptPlayer); + @Override + public void onTick() { + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> { + checkStatTrackers(adaptPlayer); - // Check for sprinting - if (i.isSprinting() && !i.isFlying() && !i.isSwimming() && !i.isSneaking()) { - xpSilent(i, getConfig().sprintXpPassive, "agility:sprint"); - } + // Check for sprinting + if (i.isSprinting() && !i.isFlying() && !i.isSwimming() && !i.isSneaking()) { + xpSilent(i, getConfig().sprintXpPassive, "agility:sprint"); + } - // Check for swimming - if (i.isSwimming() && !i.isFlying() && !i.isSprinting() && !i.isSneaking()) { - xpSilent(i, getConfig().swimXpPassive, "agility:swim"); - } + // Check for swimming + if (i.isSwimming() && !i.isFlying() && !i.isSprinting() && !i.isSneaking()) { + xpSilent(i, getConfig().swimXpPassive, "agility:swim"); + } - // Check for jumping - if (!i.isOnGround() && !i.isFlying() && !i.isSneaking()) { - xpSilent(i, getConfig().jumpXpPassive, "agility:jump"); - } + // Check for jumping + if (!i.isOnGround() && !i.isFlying() && !i.isSneaking()) { + xpSilent(i, getConfig().jumpXpPassive, "agility:jump"); + } - // Check for climbing ladders - if (i.isClimbing() && !i.isFlying() && !i.isSneaking()) { - xpSilent(i, getConfig().climbXpPassive, "agility:climb"); - } - }); + // Check for climbing ladders + if (i.isClimbing() && !i.isFlying() && !i.isSneaking()) { + xpSilent(i, getConfig().climbXpPassive, "agility:climb"); } + }); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&a"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Move1k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeMove1kReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint5k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSprint5kReward = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint Marathon Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSprintMarathonReward = 6500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sprint Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sprintXpPassive = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double swimXpPassive = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double jumpXpPassive = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Climb Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double climbXpPassive = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Move Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double moveXpPassive = 0.05; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&a"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Move1k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeMove1kReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint5k Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSprint5kReward = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sprint Marathon Reward for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSprintMarathonReward = 6500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sprint Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sprintXpPassive = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double swimXpPassive = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Jump Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double jumpXpPassive = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Climb Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double climbXpPassive = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Move Xp Passive for the Agility skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double moveXpPassive = 0.05; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index 39b38d8b8..4308cde39 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -41,217 +41,217 @@ import java.util.UUID; public class SkillArchitect extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillArchitect() { - super("architect", Localizer.dLocalize("skill.architect.icon")); - registerConfiguration(Config.class); - setColor(C.AQUA); - setDescription(Localizer.dLocalize("skill.architect.description")); - setDisplayName(Localizer.dLocalize("skill.architect.name")); - setInterval(3100); - setIcon(Material.IRON_BARS); - cooldowns = new HashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BRICK).key("challenge_place_1k") - .title(Localizer.dLocalize("advancement.challenge_place_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_place_1k.description")) - .model(CustomModel.get(Material.BRICK, "advancement", "architect", "challenge_place_1k")) + public SkillArchitect() { + super("architect", Localizer.dLocalize("skill.architect.icon")); + registerConfiguration(Config.class); + setColor(C.AQUA); + setDescription(Localizer.dLocalize("skill.architect.description")); + setDisplayName(Localizer.dLocalize("skill.architect.name")); + setInterval(3100); + setIcon(Material.IRON_BARS); + cooldowns = new HashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BRICK).key("challenge_place_1k") + .title(Localizer.dLocalize("advancement.challenge_place_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_place_1k.description")) + .model(CustomModel.get(Material.BRICK, "advancement", "architect", "challenge_place_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.BRICK) + .key("challenge_place_5k") + .title(Localizer.dLocalize("advancement.challenge_place_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_place_5k.description")) + .model(CustomModel.get(Material.BRICK, "advancement", "architect", "challenge_place_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.NETHER_BRICK) + .key("challenge_place_50k") + .title(Localizer.dLocalize("advancement.challenge_place_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_place_50k.description")) + .model(CustomModel.get(Material.NETHER_BRICK, "advancement", "architect", "challenge_place_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.BRICK) - .key("challenge_place_5k") - .title(Localizer.dLocalize("advancement.challenge_place_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_place_5k.description")) - .model(CustomModel.get(Material.BRICK, "advancement", "architect", "challenge_place_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.NETHER_BRICK) - .key("challenge_place_50k") - .title(Localizer.dLocalize("advancement.challenge_place_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_place_50k.description")) - .model(CustomModel.get(Material.NETHER_BRICK, "advancement", "architect", "challenge_place_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.NETHER_BRICK) - .key("challenge_place_500k") - .title(Localizer.dLocalize("advancement.challenge_place_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_place_500k.description")) - .model(CustomModel.get(Material.NETHER_BRICK, "advancement", "architect", "challenge_place_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.IRON_INGOT) - .key("challenge_place_5m") - .title(Localizer.dLocalize("advancement.challenge_place_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_place_5m.description")) - .model(CustomModel.get(Material.IRON_INGOT, "advancement", "architect", "challenge_place_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_place_1k", "blocks.placed", 1000, getConfig().challengePlace1kReward); - registerMilestone("challenge_place_5k", "blocks.placed", 5000, getConfig().challengePlace1kReward); - registerMilestone("challenge_place_50k", "blocks.placed", 50000, getConfig().challengePlace1kReward); - registerMilestone("challenge_place_500k", "blocks.placed", 500000, getConfig().challengePlace1kReward); - registerMilestone("challenge_place_5m", "blocks.placed", 5000000, getConfig().challengePlace1kReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE).key("challenge_demolish_500") - .title(Localizer.dLocalize("advancement.challenge_demolish_500.title")) - .description(Localizer.dLocalize("advancement.challenge_demolish_500.description")) - .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "architect", "challenge_demolish_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TNT) - .key("challenge_demolish_5k") - .title(Localizer.dLocalize("advancement.challenge_demolish_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_demolish_5k.description")) - .model(CustomModel.get(Material.TNT, "advancement", "architect", "challenge_demolish_5k")) + .icon(Material.NETHER_BRICK) + .key("challenge_place_500k") + .title(Localizer.dLocalize("advancement.challenge_place_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_place_500k.description")) + .model(CustomModel.get(Material.NETHER_BRICK, "advancement", "architect", "challenge_place_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_place_5m") + .title(Localizer.dLocalize("advancement.challenge_place_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_place_5m.description")) + .model(CustomModel.get(Material.IRON_INGOT, "advancement", "architect", "challenge_place_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_demolish_500", "blocks.broken", 500, getConfig().challengePlace1kReward); - registerMilestone("challenge_demolish_5k", "blocks.broken", 5000, getConfig().challengePlace1kReward * 2); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_place_1k", "blocks.placed", 1000, getConfig().challengePlace1kReward); + registerMilestone("challenge_place_5k", "blocks.placed", 5000, getConfig().challengePlace1kReward); + registerMilestone("challenge_place_50k", "blocks.placed", 50000, getConfig().challengePlace1kReward); + registerMilestone("challenge_place_500k", "blocks.placed", 500000, getConfig().challengePlace1kReward); + registerMilestone("challenge_place_5m", "blocks.placed", 5000000, getConfig().challengePlace1kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLD_INGOT).key("challenge_value_placed_10k") - .title(Localizer.dLocalize("advancement.challenge_value_placed_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_value_placed_10k.description")) - .model(CustomModel.get(Material.GOLD_INGOT, "advancement", "architect", "challenge_value_placed_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND) - .key("challenge_value_placed_100k") - .title(Localizer.dLocalize("advancement.challenge_value_placed_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_value_placed_100k.description")) - .model(CustomModel.get(Material.DIAMOND, "advancement", "architect", "challenge_value_placed_100k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_value_placed_10k", "blocks.placed.value", 10000, getConfig().challengePlace1kReward); - registerMilestone("challenge_value_placed_100k", "blocks.placed.value", 100000, getConfig().challengePlace1kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE).key("challenge_demolish_500") + .title(Localizer.dLocalize("advancement.challenge_demolish_500.title")) + .description(Localizer.dLocalize("advancement.challenge_demolish_500.description")) + .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "architect", "challenge_demolish_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TNT) + .key("challenge_demolish_5k") + .title(Localizer.dLocalize("advancement.challenge_demolish_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_demolish_5k.description")) + .model(CustomModel.get(Material.TNT, "advancement", "architect", "challenge_demolish_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_demolish_500", "blocks.broken", 500, getConfig().challengePlace1kReward); + registerMilestone("challenge_demolish_5k", "blocks.broken", 5000, getConfig().challengePlace1kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TNT_MINECART).key("challenge_demolish_val_5k") - .title(Localizer.dLocalize("advancement.challenge_demolish_val_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_demolish_val_5k.description")) - .model(CustomModel.get(Material.TNT_MINECART, "advancement", "architect", "challenge_demolish_val_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.END_CRYSTAL) - .key("challenge_demolish_val_50k") - .title(Localizer.dLocalize("advancement.challenge_demolish_val_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_demolish_val_50k.description")) - .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "architect", "challenge_demolish_val_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_demolish_val_5k", "architect.demolish.value", 5000, getConfig().challengePlace1kReward); - registerMilestone("challenge_demolish_val_50k", "architect.demolish.value", 50000, getConfig().challengePlace1kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLD_INGOT).key("challenge_value_placed_10k") + .title(Localizer.dLocalize("advancement.challenge_value_placed_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_value_placed_10k.description")) + .model(CustomModel.get(Material.GOLD_INGOT, "advancement", "architect", "challenge_value_placed_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_value_placed_100k") + .title(Localizer.dLocalize("advancement.challenge_value_placed_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_value_placed_100k.description")) + .model(CustomModel.get(Material.DIAMOND, "advancement", "architect", "challenge_value_placed_100k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_value_placed_10k", "blocks.placed.value", 10000, getConfig().challengePlace1kReward); + registerMilestone("challenge_value_placed_100k", "blocks.placed.value", 100000, getConfig().challengePlace1kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SCAFFOLDING).key("challenge_high_build_100") - .title(Localizer.dLocalize("advancement.challenge_high_build_100.title")) - .description(Localizer.dLocalize("advancement.challenge_high_build_100.description")) - .model(CustomModel.get(Material.SCAFFOLDING, "advancement", "architect", "challenge_high_build_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.LIGHTNING_ROD) - .key("challenge_high_build_1k") - .title(Localizer.dLocalize("advancement.challenge_high_build_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_high_build_1k.description")) - .model(CustomModel.get(Material.LIGHTNING_ROD, "advancement", "architect", "challenge_high_build_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_high_build_100", "architect.builds.high", 100, getConfig().challengePlace1kReward); - registerMilestone("challenge_high_build_1k", "architect.builds.high", 1000, getConfig().challengePlace1kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TNT_MINECART).key("challenge_demolish_val_5k") + .title(Localizer.dLocalize("advancement.challenge_demolish_val_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_demolish_val_5k.description")) + .model(CustomModel.get(Material.TNT_MINECART, "advancement", "architect", "challenge_demolish_val_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.END_CRYSTAL) + .key("challenge_demolish_val_50k") + .title(Localizer.dLocalize("advancement.challenge_demolish_val_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_demolish_val_50k.description")) + .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "architect", "challenge_demolish_val_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_demolish_val_5k", "architect.demolish.value", 5000, getConfig().challengePlace1kReward); + registerMilestone("challenge_demolish_val_50k", "architect.demolish.value", 50000, getConfig().challengePlace1kReward * 2); - setIcon(Material.SMITHING_TABLE); - registerAdaptation(new ArchitectGlass()); - registerAdaptation(new ArchitectFoundation()); - registerAdaptation(new ArchitectPlacement()); - registerAdaptation(new ArchitectWirelessRedstone()); - registerAdaptation(new ArchitectElevator()); - registerAdaptation(new ArchitectSmartShape()); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SCAFFOLDING).key("challenge_high_build_100") + .title(Localizer.dLocalize("advancement.challenge_high_build_100.title")) + .description(Localizer.dLocalize("advancement.challenge_high_build_100.description")) + .model(CustomModel.get(Material.SCAFFOLDING, "advancement", "architect", "challenge_high_build_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LIGHTNING_ROD) + .key("challenge_high_build_1k") + .title(Localizer.dLocalize("advancement.challenge_high_build_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_high_build_1k.description")) + .model(CustomModel.get(Material.LIGHTNING_ROD, "advancement", "architect", "challenge_high_build_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_high_build_100", "architect.builds.high", 100, getConfig().challengePlace1kReward); + registerMilestone("challenge_high_build_1k", "architect.builds.high", 1000, getConfig().challengePlace1kReward * 2); - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockPlaceEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, e, () -> { - if (!isStorage(e.getBlock().getType().createBlockData())) { - double v = getValue(e.getBlock()) * getConfig().xpValueMultiplier; - AdaptPlayer adaptPlayer = getPlayer(p); - adaptPlayer.getData().addStat("blocks.placed", 1); - adaptPlayer.getData().addStat("blocks.placed.value", v); - if (e.getBlock().getY() > 128) { - adaptPlayer.getData().addStat("architect.builds.high", 1); - } + setIcon(Material.SMITHING_TABLE); + registerAdaptation(new ArchitectGlass()); + registerAdaptation(new ArchitectFoundation()); + registerAdaptation(new ArchitectPlacement()); + registerAdaptation(new ArchitectWirelessRedstone()); + registerAdaptation(new ArchitectElevator()); + registerAdaptation(new ArchitectSmartShape()); + } - handleBlockCooldown(p, () -> { - try { - xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), getConfig().xpBase + v)); - } catch (Exception ignored) { - Adapt.verbose("Failed to give XP to " + p.getName() + " for placing " + e.getBlock().getType().name()); - } - }); - } - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, e, () -> { + if (!isStorage(e.getBlock().getType().createBlockData())) { + double v = getValue(e.getBlock()) * getConfig().xpValueMultiplier; + AdaptPlayer adaptPlayer = getPlayer(p); + adaptPlayer.getData().addStat("blocks.placed", 1); + adaptPlayer.getData().addStat("blocks.placed.value", v); + if (e.getBlock().getY() > 128) { + adaptPlayer.getData().addStat("architect.builds.high", 1); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, e, () -> { - AdaptPlayer adaptPlayer = getPlayer(p); - adaptPlayer.getData().addStat("blocks.broken", 1); - adaptPlayer.getData().addStat("architect.demolish.value", getValue(e.getBlock())); + handleBlockCooldown(p, () -> { + try { + xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), getConfig().xpBase + v)); + } catch (Exception ignored) { + Adapt.verbose("Failed to give XP to " + p.getName() + " for placing " + e.getBlock().getType().name()); + } }); - } + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, e, () -> { + AdaptPlayer adaptPlayer = getPlayer(p); + adaptPlayer.getData().addStat("blocks.broken", 1); + adaptPlayer.getData().addStat("architect.demolish.value", getValue(e.getBlock())); + }); + } - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); - } + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } - private void handleBlockCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - action.run(); - } + private void handleBlockCooldown(Player p, Runnable action) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + action.run(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&b"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Place1k Reward for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengePlace1kReward = 1750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Value Multiplier for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpValueMultiplier = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Base for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpBase = 3; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&b"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Place1k Reward for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengePlace1kReward = 1750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Value Multiplier for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpValueMultiplier = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Base for the Architect skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpBase = 3; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java index 39ebe4c62..cada80007 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java @@ -41,255 +41,255 @@ import java.util.UUID; public class SkillAxes extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillAxes() { - super("axes", Localizer.dLocalize("skill.axes.icon")); - registerConfiguration(Config.class); - setColor(C.YELLOW); - setDescription(Localizer.dLocalize("skill.axes.description1") + C.ITALIC + Localizer.dLocalize("skill.axes.description2") + C.GRAY + " " + Localizer.dLocalize("skill.axes.description3")); - setDisplayName(Localizer.dLocalize("skill.axes.name")); - setInterval(5251); - setIcon(Material.GOLDEN_AXE); - cooldowns = new HashMap<>(); - registerAdaptation(new AxeGroundSmash()); - registerAdaptation(new AxeChop()); - registerAdaptation(new AxeDropToInventory()); - registerAdaptation(new AxeLeafVeinminer()); - registerAdaptation(new AxeWoodVeinminer()); - registerAdaptation(new AxeCraftLogSwap()); - registerAdaptation(new AxeTimberMark()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_AXE).key("challenge_chop_1k") - .title(Localizer.dLocalize("advancement.challenge_chop_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_chop_1k.description")) - .model(CustomModel.get(Material.WOODEN_AXE, "advancement", "axes", "challenge_chop_1k")) + public SkillAxes() { + super("axes", Localizer.dLocalize("skill.axes.icon")); + registerConfiguration(Config.class); + setColor(C.YELLOW); + setDescription(Localizer.dLocalize("skill.axes.description1") + C.ITALIC + Localizer.dLocalize("skill.axes.description2") + C.GRAY + " " + Localizer.dLocalize("skill.axes.description3")); + setDisplayName(Localizer.dLocalize("skill.axes.name")); + setInterval(5251); + setIcon(Material.GOLDEN_AXE); + cooldowns = new HashMap<>(); + registerAdaptation(new AxeGroundSmash()); + registerAdaptation(new AxeChop()); + registerAdaptation(new AxeDropToInventory()); + registerAdaptation(new AxeLeafVeinminer()); + registerAdaptation(new AxeWoodVeinminer()); + registerAdaptation(new AxeCraftLogSwap()); + registerAdaptation(new AxeTimberMark()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_AXE).key("challenge_chop_1k") + .title(Localizer.dLocalize("advancement.challenge_chop_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_chop_1k.description")) + .model(CustomModel.get(Material.WOODEN_AXE, "advancement", "axes", "challenge_chop_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.STONE_AXE) + .key("challenge_chop_5k") + .title(Localizer.dLocalize("advancement.challenge_chop_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_chop_5k.description")) + .model(CustomModel.get(Material.STONE_AXE, "advancement", "axes", "challenge_chop_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.IRON_AXE) + .key("challenge_chop_50k") + .title(Localizer.dLocalize("advancement.challenge_chop_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_chop_50k.description")) + .model(CustomModel.get(Material.IRON_AXE, "advancement", "axes", "challenge_chop_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.STONE_AXE) - .key("challenge_chop_5k") - .title(Localizer.dLocalize("advancement.challenge_chop_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_chop_5k.description")) - .model(CustomModel.get(Material.STONE_AXE, "advancement", "axes", "challenge_chop_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.IRON_AXE) - .key("challenge_chop_50k") - .title(Localizer.dLocalize("advancement.challenge_chop_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_chop_50k.description")) - .model(CustomModel.get(Material.IRON_AXE, "advancement", "axes", "challenge_chop_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_AXE) - .key("challenge_chop_500k") - .title(Localizer.dLocalize("advancement.challenge_chop_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_chop_500k.description")) - .model(CustomModel.get(Material.DIAMOND_AXE, "advancement", "axes", "challenge_chop_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_AXE) - .key("challenge_chop_5m") - .title(Localizer.dLocalize("advancement.challenge_chop_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_chop_5m.description")) - .model(CustomModel.get(Material.NETHERITE_AXE, "advancement", "axes", "challenge_chop_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_chop_1k", "axes.blocks.broken", 1000, getConfig().challengeChopReward); - registerMilestone("challenge_chop_5k", "axes.blocks.broken", 5000, getConfig().challengeChopReward); - registerMilestone("challenge_chop_50k", "axes.blocks.broken", 50000, getConfig().challengeChopReward); - registerMilestone("challenge_chop_500k", "axes.blocks.broken", 500000, getConfig().challengeChopReward); - registerMilestone("challenge_chop_5m", "axes.blocks.broken", 5000000, getConfig().challengeChopReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_AXE).key("challenge_axe_swing_500") - .title(Localizer.dLocalize("advancement.challenge_axe_swing_500.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_swing_500.description")) - .model(CustomModel.get(Material.WOODEN_AXE, "advancement", "axes", "challenge_axe_swing_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_AXE) - .key("challenge_axe_swing_5k") - .title(Localizer.dLocalize("advancement.challenge_axe_swing_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_swing_5k.description")) - .model(CustomModel.get(Material.IRON_AXE, "advancement", "axes", "challenge_axe_swing_5k")) + .icon(Material.DIAMOND_AXE) + .key("challenge_chop_500k") + .title(Localizer.dLocalize("advancement.challenge_chop_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_chop_500k.description")) + .model(CustomModel.get(Material.DIAMOND_AXE, "advancement", "axes", "challenge_chop_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_AXE) + .key("challenge_chop_5m") + .title(Localizer.dLocalize("advancement.challenge_chop_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_chop_5m.description")) + .model(CustomModel.get(Material.NETHERITE_AXE, "advancement", "axes", "challenge_chop_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_axe_swing_500", "axes.swings", 500, getConfig().challengeChopReward); - registerMilestone("challenge_axe_swing_5k", "axes.swings", 5000, getConfig().challengeChopReward * 2); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_chop_1k", "axes.blocks.broken", 1000, getConfig().challengeChopReward); + registerMilestone("challenge_chop_5k", "axes.blocks.broken", 5000, getConfig().challengeChopReward); + registerMilestone("challenge_chop_50k", "axes.blocks.broken", 50000, getConfig().challengeChopReward); + registerMilestone("challenge_chop_500k", "axes.blocks.broken", 500000, getConfig().challengeChopReward); + registerMilestone("challenge_chop_5m", "axes.blocks.broken", 5000000, getConfig().challengeChopReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_AXE).key("challenge_axe_damage_1k") - .title(Localizer.dLocalize("advancement.challenge_axe_damage_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_damage_1k.description")) - .model(CustomModel.get(Material.GOLDEN_AXE, "advancement", "axes", "challenge_axe_damage_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_AXE) - .key("challenge_axe_damage_10k") - .title(Localizer.dLocalize("advancement.challenge_axe_damage_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_damage_10k.description")) - .model(CustomModel.get(Material.DIAMOND_AXE, "advancement", "axes", "challenge_axe_damage_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_axe_damage_1k", "axes.damage", 1000, getConfig().challengeChopReward); - registerMilestone("challenge_axe_damage_10k", "axes.damage", 10000, getConfig().challengeChopReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_AXE).key("challenge_axe_swing_500") + .title(Localizer.dLocalize("advancement.challenge_axe_swing_500.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_swing_500.description")) + .model(CustomModel.get(Material.WOODEN_AXE, "advancement", "axes", "challenge_axe_swing_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_AXE) + .key("challenge_axe_swing_5k") + .title(Localizer.dLocalize("advancement.challenge_axe_swing_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_swing_5k.description")) + .model(CustomModel.get(Material.IRON_AXE, "advancement", "axes", "challenge_axe_swing_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_axe_swing_500", "axes.swings", 500, getConfig().challengeChopReward); + registerMilestone("challenge_axe_swing_5k", "axes.swings", 5000, getConfig().challengeChopReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_LOG).key("challenge_axe_value_5k") - .title(Localizer.dLocalize("advancement.challenge_axe_value_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_value_5k.description")) - .model(CustomModel.get(Material.OAK_LOG, "advancement", "axes", "challenge_axe_value_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DARK_OAK_LOG) - .key("challenge_axe_value_50k") - .title(Localizer.dLocalize("advancement.challenge_axe_value_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_axe_value_50k.description")) - .model(CustomModel.get(Material.DARK_OAK_LOG, "advancement", "axes", "challenge_axe_value_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_axe_value_5k", "axes.blocks.value", 5000, getConfig().challengeChopReward); - registerMilestone("challenge_axe_value_50k", "axes.blocks.value", 50000, getConfig().challengeChopReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_AXE).key("challenge_axe_damage_1k") + .title(Localizer.dLocalize("advancement.challenge_axe_damage_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_damage_1k.description")) + .model(CustomModel.get(Material.GOLDEN_AXE, "advancement", "axes", "challenge_axe_damage_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_AXE) + .key("challenge_axe_damage_10k") + .title(Localizer.dLocalize("advancement.challenge_axe_damage_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_damage_10k.description")) + .model(CustomModel.get(Material.DIAMOND_AXE, "advancement", "axes", "challenge_axe_damage_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_axe_damage_1k", "axes.damage", 1000, getConfig().challengeChopReward); + registerMilestone("challenge_axe_damage_10k", "axes.damage", 10000, getConfig().challengeChopReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.OAK_LEAVES).key("challenge_leaves_500") - .title(Localizer.dLocalize("advancement.challenge_leaves_500.title")) - .description(Localizer.dLocalize("advancement.challenge_leaves_500.description")) - .model(CustomModel.get(Material.OAK_LEAVES, "advancement", "axes", "challenge_leaves_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.AZALEA_LEAVES) - .key("challenge_leaves_5k") - .title(Localizer.dLocalize("advancement.challenge_leaves_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_leaves_5k.description")) - .model(CustomModel.get(Material.AZALEA_LEAVES, "advancement", "axes", "challenge_leaves_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_leaves_500", "axes.leaves", 500, getConfig().challengeChopReward); - registerMilestone("challenge_leaves_5k", "axes.leaves", 5000, getConfig().challengeChopReward * 2); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_LOG).key("challenge_axe_value_5k") + .title(Localizer.dLocalize("advancement.challenge_axe_value_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_value_5k.description")) + .model(CustomModel.get(Material.OAK_LOG, "advancement", "axes", "challenge_axe_value_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DARK_OAK_LOG) + .key("challenge_axe_value_50k") + .title(Localizer.dLocalize("advancement.challenge_axe_value_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_axe_value_50k.description")) + .model(CustomModel.get(Material.DARK_OAK_LOG, "advancement", "axes", "challenge_axe_value_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_axe_value_5k", "axes.blocks.value", 5000, getConfig().challengeChopReward); + registerMilestone("challenge_axe_value_50k", "axes.blocks.value", 50000, getConfig().challengeChopReward * 2); - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { - if (!getConfig().getXpForAttackingWithTools) { - return; - } - shouldReturnForPlayer(p, () -> { - if (e.getEntity().isDead() || e.getEntity().isInvulnerable() || p.isDead() || p.isInvulnerable()) { - return; - } - AdaptPlayer a = getPlayer(p); - ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OAK_LEAVES).key("challenge_leaves_500") + .title(Localizer.dLocalize("advancement.challenge_leaves_500.title")) + .description(Localizer.dLocalize("advancement.challenge_leaves_500.description")) + .model(CustomModel.get(Material.OAK_LEAVES, "advancement", "axes", "challenge_leaves_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.AZALEA_LEAVES) + .key("challenge_leaves_5k") + .title(Localizer.dLocalize("advancement.challenge_leaves_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_leaves_5k.description")) + .model(CustomModel.get(Material.AZALEA_LEAVES, "advancement", "axes", "challenge_leaves_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_leaves_500", "axes.leaves", 500, getConfig().challengeChopReward); + registerMilestone("challenge_leaves_5k", "axes.leaves", 5000, getConfig().challengeChopReward * 2); + } - if (isAxe(hand)) { - handleCooldown(p, () -> { - a.getData().addStat("axes.swings", 1); - a.getData().addStat("axes.damage", e.getDamage()); - xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().axeDamageXPMultiplier * e.getDamage()); - }); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { + if (!getConfig().getXpForAttackingWithTools) { + return; + } + shouldReturnForPlayer(p, () -> { + if (e.getEntity().isDead() || e.getEntity().isInvulnerable() || p.isDead() || p.isInvulnerable()) { + return; } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, () -> { - if (isAxe(p.getInventory().getItemInMainHand())) { - if (isLog(new ItemStack(e.getBlock().getType()))) { - double v = getValue(e.getBlock().getType()); - AdaptPlayer a = getPlayer(p); - a.getData().addStat("axes.blocks.broken", 1); - a.getData().addStat("axes.blocks.value", getValue(e.getBlock().getBlockData())); - handleCooldown(p, () -> xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v))); - } - if (e.getBlock().getType().name().endsWith("_LEAVES")) { - getPlayer(p).getData().addStat("axes.leaves", 1); - } - } - }); - } + AdaptPlayer a = getPlayer(p); + ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); - private void handleCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - action.run(); + if (isAxe(hand)) { + handleCooldown(p, () -> { + a.getData().addStat("axes.swings", 1); + a.getData().addStat("axes.damage", e.getDamage()); + xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().axeDamageXPMultiplier * e.getDamage()); + }); + } + }); } + } - public double getValue(Material type) { - double value = super.getValue(type) * getConfig().valueXPMultiplier; - value += Math.min(getConfig().maxHardnessBonus, type.getHardness()); - value += Math.min(getConfig().maxBlastResistanceBonus, type.getBlastResistance()); - - if (type.name().endsWith("_LOG") || type.name().endsWith("_WOOD")) { - value += getConfig().logOrWoodXPMultiplier; + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, () -> { + if (isAxe(p.getInventory().getItemInMainHand())) { + if (isLog(new ItemStack(e.getBlock().getType()))) { + double v = getValue(e.getBlock().getType()); + AdaptPlayer a = getPlayer(p); + a.getData().addStat("axes.blocks.broken", 1); + a.getData().addStat("axes.blocks.value", getValue(e.getBlock().getBlockData())); + handleCooldown(p, () -> xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v))); } - if (type.name().endsWith("_LEAVES")) { - value += getConfig().leavesMultiplier; + if (e.getBlock().getType().name().endsWith("_LEAVES")) { + getPlayer(p).getData().addStat("axes.leaves", 1); } + } + }); + } - if (type.getHardness() == 0) { - value = 0; - } + private void handleCooldown(Player p, Runnable action) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + action.run(); + } - return value; - } + public double getValue(Material type) { + double value = super.getValue(type) * getConfig().valueXPMultiplier; + value += Math.min(getConfig().maxHardnessBonus, type.getHardness()); + value += Math.min(getConfig().maxBlastResistanceBonus, type.getBlastResistance()); - - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); + if (type.name().endsWith("_LOG") || type.name().endsWith("_WOOD")) { + value += getConfig().logOrWoodXPMultiplier; + } + if (type.name().endsWith("_LEAVES")) { + value += getConfig().leavesMultiplier; } - @Override - public boolean isEnabled() { - return getConfig().enabled; + if (type.getHardness() == 0) { + value = 0; } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&e"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Axes skill.", impact = "True enables this behavior and false disables it.") - boolean getXpForAttackingWithTools = true; + return value; + } - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHardnessBonus = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBlastResistanceBonus = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chop Reward for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeChopReward = 1750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Log Or Wood XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double logOrWoodXPMultiplier = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaves Multiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double leavesMultiplier = 0.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valueXPMultiplier = 0.175; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double axeDamageXPMultiplier = 7.0; - } + + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&e"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Axes skill.", impact = "True enables this behavior and false disables it.") + boolean getXpForAttackingWithTools = true; + + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHardnessBonus = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBlastResistanceBonus = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chop Reward for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeChopReward = 1750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Log Or Wood XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double logOrWoodXPMultiplier = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Leaves Multiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double leavesMultiplier = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valueXPMultiplier = 0.175; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Axes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double axeDamageXPMultiplier = 7.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java index 614954044..0c36cddb5 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBlocking.java @@ -42,227 +42,227 @@ import java.util.UUID; public class SkillBlocking extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillBlocking() { - super("blocking", Localizer.dLocalize("skill.blocking.icon")); - registerConfiguration(Config.class); - setColor(C.DARK_GRAY); - setDescription(Localizer.dLocalize("skill.blocking.description")); - setDisplayName(Localizer.dLocalize("skill.blocking.name")); - setInterval(5000); - setIcon(Material.SHIELD); - registerAdaptation(new BlockingMultiArmor()); - registerAdaptation(new BlockingChainArmorer()); - registerAdaptation(new BlockingSaddlecrafter()); - registerAdaptation(new BlockingHorseArmorer()); - registerAdaptation(new BlockingCounterGuard()); - registerAdaptation(new BlockingBastionStance()); - registerAdaptation(new BlockingMirrorBlock()); - registerAdaptation(new BlockingBulwarkBash()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_CHESTPLATE).key("challenge_block_1k") - .title(Localizer.dLocalize("advancement.challenge_block_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_1k.description")) - .model(CustomModel.get(Material.LEATHER_CHESTPLATE, "advancement", "blocking", "challenge_block_1k")) + public SkillBlocking() { + super("blocking", Localizer.dLocalize("skill.blocking.icon")); + registerConfiguration(Config.class); + setColor(C.DARK_GRAY); + setDescription(Localizer.dLocalize("skill.blocking.description")); + setDisplayName(Localizer.dLocalize("skill.blocking.name")); + setInterval(5000); + setIcon(Material.SHIELD); + registerAdaptation(new BlockingMultiArmor()); + registerAdaptation(new BlockingChainArmorer()); + registerAdaptation(new BlockingSaddlecrafter()); + registerAdaptation(new BlockingHorseArmorer()); + registerAdaptation(new BlockingCounterGuard()); + registerAdaptation(new BlockingBastionStance()); + registerAdaptation(new BlockingMirrorBlock()); + registerAdaptation(new BlockingBulwarkBash()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_CHESTPLATE).key("challenge_block_1k") + .title(Localizer.dLocalize("advancement.challenge_block_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_1k.description")) + .model(CustomModel.get(Material.LEATHER_CHESTPLATE, "advancement", "blocking", "challenge_block_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.CHAINMAIL_CHESTPLATE) + .key("challenge_block_5k") + .title(Localizer.dLocalize("advancement.challenge_block_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_5k.description")) + .model(CustomModel.get(Material.CHAINMAIL_CHESTPLATE, "advancement", "blocking", "challenge_block_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE) + .key("challenge_block_50k") + .title(Localizer.dLocalize("advancement.challenge_block_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_50k.description")) + .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "blocking", "challenge_block_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.CHAINMAIL_CHESTPLATE) - .key("challenge_block_5k") - .title(Localizer.dLocalize("advancement.challenge_block_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_5k.description")) - .model(CustomModel.get(Material.CHAINMAIL_CHESTPLATE, "advancement", "blocking", "challenge_block_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE) - .key("challenge_block_50k") - .title(Localizer.dLocalize("advancement.challenge_block_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_50k.description")) - .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "blocking", "challenge_block_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_CHESTPLATE) - .key("challenge_block_500k") - .title(Localizer.dLocalize("advancement.challenge_block_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_500k.description")) - .model(CustomModel.get(Material.GOLDEN_CHESTPLATE, "advancement", "blocking", "challenge_block_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_CHESTPLATE) - .key("challenge_block_5m") - .title(Localizer.dLocalize("advancement.challenge_block_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_block_5m.description")) - .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "blocking", "challenge_block_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_block_1k", "blocked.hits", 1000, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_5k", "blocked.hits", 5000, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_50k", "blocked.hits", 50000, getConfig().challengeBlock5kReward); - registerMilestone("challenge_block_500k", "blocked.hits", 500000, getConfig().challengeBlock5kReward); - registerMilestone("challenge_block_5m", "blocked.hits", 5000000, getConfig().challengeBlock5kReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE).key("challenge_block_dmg_1k") - .title(Localizer.dLocalize("advancement.challenge_block_dmg_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_dmg_1k.description")) - .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "blocking", "challenge_block_dmg_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_CHESTPLATE) - .key("challenge_block_dmg_10k") - .title(Localizer.dLocalize("advancement.challenge_block_dmg_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_dmg_10k.description")) - .model(CustomModel.get(Material.NETHERITE_CHESTPLATE, "advancement", "blocking", "challenge_block_dmg_10k")) + .icon(Material.GOLDEN_CHESTPLATE) + .key("challenge_block_500k") + .title(Localizer.dLocalize("advancement.challenge_block_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_500k.description")) + .model(CustomModel.get(Material.GOLDEN_CHESTPLATE, "advancement", "blocking", "challenge_block_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_CHESTPLATE) + .key("challenge_block_5m") + .title(Localizer.dLocalize("advancement.challenge_block_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_block_5m.description")) + .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "blocking", "challenge_block_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_block_dmg_1k", "blocked.damage", 1000, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_dmg_10k", "blocked.damage", 10000, getConfig().challengeBlock5kReward); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_block_1k", "blocked.hits", 1000, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_5k", "blocked.hits", 5000, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_50k", "blocked.hits", 50000, getConfig().challengeBlock5kReward); + registerMilestone("challenge_block_500k", "blocked.hits", 500000, getConfig().challengeBlock5kReward); + registerMilestone("challenge_block_5m", "blocked.hits", 5000000, getConfig().challengeBlock5kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW).key("challenge_block_proj_100") - .title(Localizer.dLocalize("advancement.challenge_block_proj_100.title")) - .description(Localizer.dLocalize("advancement.challenge_block_proj_100.description")) - .model(CustomModel.get(Material.ARROW, "advancement", "blocking", "challenge_block_proj_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_block_proj_1k") - .title(Localizer.dLocalize("advancement.challenge_block_proj_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_proj_1k.description")) - .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "blocking", "challenge_block_proj_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_block_proj_100", "blocked.projectiles", 100, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_proj_1k", "blocked.projectiles", 1000, getConfig().challengeBlock5kReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE).key("challenge_block_dmg_1k") + .title(Localizer.dLocalize("advancement.challenge_block_dmg_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_dmg_1k.description")) + .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "blocking", "challenge_block_dmg_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_CHESTPLATE) + .key("challenge_block_dmg_10k") + .title(Localizer.dLocalize("advancement.challenge_block_dmg_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_dmg_10k.description")) + .model(CustomModel.get(Material.NETHERITE_CHESTPLATE, "advancement", "blocking", "challenge_block_dmg_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_block_dmg_1k", "blocked.damage", 1000, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_dmg_10k", "blocked.damage", 10000, getConfig().challengeBlock5kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD).key("challenge_block_melee_500") - .title(Localizer.dLocalize("advancement.challenge_block_melee_500.title")) - .description(Localizer.dLocalize("advancement.challenge_block_melee_500.description")) - .model(CustomModel.get(Material.IRON_SWORD, "advancement", "blocking", "challenge_block_melee_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_block_melee_5k") - .title(Localizer.dLocalize("advancement.challenge_block_melee_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_block_melee_5k.description")) - .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "blocking", "challenge_block_melee_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_block_melee_500", "blocked.melee", 500, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_melee_5k", "blocked.melee", 5000, getConfig().challengeBlock5kReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW).key("challenge_block_proj_100") + .title(Localizer.dLocalize("advancement.challenge_block_proj_100.title")) + .description(Localizer.dLocalize("advancement.challenge_block_proj_100.description")) + .model(CustomModel.get(Material.ARROW, "advancement", "blocking", "challenge_block_proj_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_block_proj_1k") + .title(Localizer.dLocalize("advancement.challenge_block_proj_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_proj_1k.description")) + .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "blocking", "challenge_block_proj_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_block_proj_100", "blocked.projectiles", 100, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_proj_1k", "blocked.projectiles", 1000, getConfig().challengeBlock5kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_CHESTPLATE).key("challenge_block_heavy_50") - .title(Localizer.dLocalize("advancement.challenge_block_heavy_50.title")) - .description(Localizer.dLocalize("advancement.challenge_block_heavy_50.description")) - .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "blocking", "challenge_block_heavy_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_CHESTPLATE) - .key("challenge_block_heavy_500") - .title(Localizer.dLocalize("advancement.challenge_block_heavy_500.title")) - .description(Localizer.dLocalize("advancement.challenge_block_heavy_500.description")) - .model(CustomModel.get(Material.NETHERITE_CHESTPLATE, "advancement", "blocking", "challenge_block_heavy_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_block_heavy_50", "blocked.heavy", 50, getConfig().challengeBlock1kReward); - registerMilestone("challenge_block_heavy_500", "blocked.heavy", 500, getConfig().challengeBlock5kReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD).key("challenge_block_melee_500") + .title(Localizer.dLocalize("advancement.challenge_block_melee_500.title")) + .description(Localizer.dLocalize("advancement.challenge_block_melee_500.description")) + .model(CustomModel.get(Material.IRON_SWORD, "advancement", "blocking", "challenge_block_melee_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_block_melee_5k") + .title(Localizer.dLocalize("advancement.challenge_block_melee_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_block_melee_5k.description")) + .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "blocking", "challenge_block_melee_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_block_melee_500", "blocked.melee", 500, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_melee_5k", "blocked.melee", 5000, getConfig().challengeBlock5kReward); - cooldowns = new HashMap<>(); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_CHESTPLATE).key("challenge_block_heavy_50") + .title(Localizer.dLocalize("advancement.challenge_block_heavy_50.title")) + .description(Localizer.dLocalize("advancement.challenge_block_heavy_50.description")) + .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "blocking", "challenge_block_heavy_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_CHESTPLATE) + .key("challenge_block_heavy_500") + .title(Localizer.dLocalize("advancement.challenge_block_heavy_500.title")) + .description(Localizer.dLocalize("advancement.challenge_block_heavy_500.description")) + .model(CustomModel.get(Material.NETHERITE_CHESTPLATE, "advancement", "blocking", "challenge_block_heavy_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_block_heavy_50", "blocked.heavy", 50, getConfig().challengeBlock1kReward); + registerMilestone("challenge_block_heavy_500", "blocked.heavy", 500, getConfig().challengeBlock5kReward); - private void handleCooldown(Player p, Runnable runnable) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - runnable.run(); - } + cooldowns = new HashMap<>(); + } + private void handleCooldown(Player p, Runnable runnable) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + runnable.run(); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getEntity() instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - shouldReturnForPlayer(p, e, () -> { - if (p.isBlocking()) { - AdaptPlayer adaptPlayer = getPlayer(p); - adaptPlayer.getData().addStat("blocked.hits", 1); - adaptPlayer.getData().addStat("blocked.damage", e.getDamage()); - if (e.getDamager() instanceof Projectile) { - adaptPlayer.getData().addStat("blocked.projectiles", 1); - } else { - adaptPlayer.getData().addStat("blocked.melee", 1); - } - if (e.getDamage() > 5) { - adaptPlayer.getData().addStat("blocked.heavy", 1); - } - handleCooldown(p, () -> { - xp(p, getConfig().xpOnBlockedAttack); - sp.play(p.getLocation(), Sound.BLOCK_IRON_DOOR_CLOSE, 0.5f, 0.77f); - sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.5f, 0.77f); - }); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getEntity() instanceof Player p) { + SoundPlayer sp = SoundPlayer.of(p); + shouldReturnForPlayer(p, e, () -> { + if (p.isBlocking()) { + AdaptPlayer adaptPlayer = getPlayer(p); + adaptPlayer.getData().addStat("blocked.hits", 1); + adaptPlayer.getData().addStat("blocked.damage", e.getDamage()); + if (e.getDamager() instanceof Projectile) { + adaptPlayer.getData().addStat("blocked.projectiles", 1); + } else { + adaptPlayer.getData().addStat("blocked.melee", 1); + } + if (e.getDamage() > 5) { + adaptPlayer.getData().addStat("blocked.heavy", 1); + } + + handleCooldown(p, () -> { + xp(p, getConfig().xpOnBlockedAttack); + sp.play(p.getLocation(), Sound.BLOCK_IRON_DOOR_CLOSE, 0.5f, 0.77f); + sp.play(p.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.5f, 0.77f); + }); } + }); } + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } + @Override + public void onTick() { + if (!this.isEnabled()) { + return; + } - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> { - checkStatTrackers(adaptPlayer); - if (getConfig().passiveXpForUsingShield > 0 && (i.getInventory().getItemInOffHand().getType().equals(Material.SHIELD) || i.getInventory().getItemInMainHand().getType().equals(Material.SHIELD))) { - xpSilent(i, getConfig().passiveXpForUsingShield, "blocking:shield-hold"); - } - }); + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> { + checkStatTrackers(adaptPlayer); + if (getConfig().passiveXpForUsingShield > 0 && (i.getInventory().getItemInOffHand().getType().equals(Material.SHIELD) || i.getInventory().getItemInMainHand().getType().equals(Material.SHIELD))) { + xpSilent(i, getConfig().passiveXpForUsingShield, "blocking:shield-hold"); } + }); } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&8"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Blocked Attack for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double xpOnBlockedAttack = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block1k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeBlock1kReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block5k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeBlock5kReward = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Xp For Using Shield for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long passiveXpForUsingShield = 0; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&8"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Blocked Attack for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpOnBlockedAttack = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block1k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeBlock1kReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Block5k Reward for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeBlock5kReward = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Xp For Using Shield for the Blocking skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long passiveXpForUsingShield = 0; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java index c6386f44e..d29ac055f 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillBrewing.java @@ -43,298 +43,325 @@ import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.inventory.BrewerInventory; import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionType; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class SkillBrewing extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillBrewing() { - super("brewing", Localizer.dLocalize("skill.brewing.icon")); - registerConfiguration(Config.class); - setColor(C.LIGHT_PURPLE); - setDescription(Localizer.dLocalize("skill.brewing.description")); - setDisplayName(Localizer.dLocalize("skill.brewing.name")); - setInterval(5851); - setIcon(Material.LINGERING_POTION); - cooldowns = new HashMap<>(); - registerAdaptation(new BrewingLingering()); // Features - registerAdaptation(new BrewingSuperHeated()); - registerAdaptation(new BrewingAbsorption()); // Brews - registerAdaptation(new BrewingBlindness()); - registerAdaptation(new BrewingDarkness()); - registerAdaptation(new BrewingDecay()); - registerAdaptation(new BrewingFatigue()); - registerAdaptation(new BrewingHaste()); - registerAdaptation(new BrewingHealthBoost()); - registerAdaptation(new BrewingHunger()); - registerAdaptation(new BrewingNausea()); - registerAdaptation(new BrewingResistance()); - registerAdaptation(new BrewingSaturation()); + public SkillBrewing() { + super("brewing", Localizer.dLocalize("skill.brewing.icon")); + registerConfiguration(Config.class); + setColor(C.LIGHT_PURPLE); + setDescription(Localizer.dLocalize("skill.brewing.description")); + setDisplayName(Localizer.dLocalize("skill.brewing.name")); + setInterval(5851); + setIcon(Material.LINGERING_POTION); + cooldowns = new HashMap<>(); + registerAdaptation(new BrewingLingering()); // Features + registerAdaptation(new BrewingSuperHeated()); + registerAdaptation(new BrewingAbsorption()); // Brews + registerAdaptation(new BrewingBlindness()); + registerAdaptation(new BrewingDarkness()); + registerAdaptation(new BrewingDecay()); + registerAdaptation(new BrewingFatigue()); + registerAdaptation(new BrewingHaste()); + registerAdaptation(new BrewingHealthBoost()); + registerAdaptation(new BrewingHunger()); + registerAdaptation(new BrewingNausea()); + registerAdaptation(new BrewingResistance()); + registerAdaptation(new BrewingSaturation()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.POTION).key("challenge_brew_1k") - .title(Localizer.dLocalize("advancement.challenge_brew_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_1k.description")) - .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_1k")) + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.POTION).key("challenge_brew_1k") + .title(Localizer.dLocalize("advancement.challenge_brew_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_1k.description")) + .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.POTION) + .key("challenge_brew_5k") + .title(Localizer.dLocalize("advancement.challenge_brew_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_5k.description")) + .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.POTION) + .key("challenge_brew_50k") + .title(Localizer.dLocalize("advancement.challenge_brew_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_50k.description")) + .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.POTION) + .key("challenge_brew_500k") + .title(Localizer.dLocalize("advancement.challenge_brew_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_500k.description")) + .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.POTION) - .key("challenge_brew_5k") - .title(Localizer.dLocalize("advancement.challenge_brew_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_5k.description")) - .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_5k")) + .key("challenge_brew_5m") + .title(Localizer.dLocalize("advancement.challenge_brew_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_5m.description")) + .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.POTION) - .key("challenge_brew_50k") - .title(Localizer.dLocalize("advancement.challenge_brew_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_50k.description")) - .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.POTION) - .key("challenge_brew_500k") - .title(Localizer.dLocalize("advancement.challenge_brew_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_500k.description")) - .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.POTION) - .key("challenge_brew_5m") - .title(Localizer.dLocalize("advancement.challenge_brew_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_5m.description")) - .model(CustomModel.get(Material.POTION, "advancement", "brewing", "challenge_brew_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) + .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_brew_1k", "brewing.consumed", 1000, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_5k", "brewing.consumed", 5000, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_50k", "brewing.consumed", 50000, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_500k", "brewing.consumed", 500000, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_5m", "brewing.consumed", 5000000, getConfig().challengeBrew1k); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_brew_1k", "brewing.consumed", 1000, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_5k", "brewing.consumed", 5000, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_50k", "brewing.consumed", 50000, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_500k", "brewing.consumed", 500000, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_5m", "brewing.consumed", 5000000, getConfig().challengeBrew1k); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPLASH_POTION).key("challenge_brewsplash_1k") - .title(Localizer.dLocalize("advancement.challenge_brewsplash_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_brewsplash_1k.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_1k")) + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPLASH_POTION).key("challenge_brewsplash_1k") + .title(Localizer.dLocalize("advancement.challenge_brewsplash_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_brewsplash_1k.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.SPLASH_POTION) + .key("challenge_brewsplash_5k") + .title(Localizer.dLocalize("advancement.challenge_brewsplash_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_brewsplash_5k.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.SPLASH_POTION) + .key("challenge_brewsplash_50k") + .title(Localizer.dLocalize("advancement.challenge_brewsplash_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_brewsplash_50k.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.SPLASH_POTION) + .key("challenge_brewsplash_500k") + .title(Localizer.dLocalize("advancement.challenge_brewsplash_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_brewsplash_500k.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.SPLASH_POTION) - .key("challenge_brewsplash_5k") - .title(Localizer.dLocalize("advancement.challenge_brewsplash_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_brewsplash_5k.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.SPLASH_POTION) - .key("challenge_brewsplash_50k") - .title(Localizer.dLocalize("advancement.challenge_brewsplash_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_brewsplash_50k.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.SPLASH_POTION) - .key("challenge_brewsplash_500k") - .title(Localizer.dLocalize("advancement.challenge_brewsplash_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_brewsplash_500k.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.SPLASH_POTION) - .key("challenge_brewsplash_5m") - .title(Localizer.dLocalize("advancement.challenge_brewsplash_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_brewsplash_5m.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_brewsplash_1k", "brewing.splashes", 1000, getConfig().challengeBrewSplash1k); - registerMilestone("challenge_brewsplash_5k", "brewing.splashes", 5000, getConfig().challengeBrewSplash1k); - registerMilestone("challenge_brewsplash_50k", "brewing.splashes", 50000, getConfig().challengeBrewSplash1k); - registerMilestone("challenge_brewsplash_500k", "brewing.splashes", 500000, getConfig().challengeBrewSplash1k); - registerMilestone("challenge_brewsplash_5m", "brewing.splashes", 5000000, getConfig().challengeBrewSplash1k); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BREWING_STAND).key("challenge_brew_stands_10") - .title(Localizer.dLocalize("advancement.challenge_brew_stands_10.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_stands_10.description")) - .model(CustomModel.get(Material.BREWING_STAND, "advancement", "brewing", "challenge_brew_stands_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BLAZE_ROD) - .key("challenge_brew_stands_50") - .title(Localizer.dLocalize("advancement.challenge_brew_stands_50.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_stands_50.description")) - .model(CustomModel.get(Material.BLAZE_ROD, "advancement", "brewing", "challenge_brew_stands_50")) + .key("challenge_brewsplash_5m") + .title(Localizer.dLocalize("advancement.challenge_brewsplash_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_brewsplash_5m.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "brewsplash_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_brew_stands_10", "brewing.stands.placed", 10, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_stands_50", "brewing.stands.placed", 50, getConfig().challengeBrew1k * 2); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_brewsplash_1k", "brewing.splashes", 1000, getConfig().challengeBrewSplash1k); + registerMilestone("challenge_brewsplash_5k", "brewing.splashes", 5000, getConfig().challengeBrewSplash1k); + registerMilestone("challenge_brewsplash_50k", "brewing.splashes", 50000, getConfig().challengeBrewSplash1k); + registerMilestone("challenge_brewsplash_500k", "brewing.splashes", 500000, getConfig().challengeBrewSplash1k); + registerMilestone("challenge_brewsplash_5m", "brewing.splashes", 5000000, getConfig().challengeBrewSplash1k); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GLOWSTONE_DUST).key("challenge_brew_strong_25") - .title(Localizer.dLocalize("advancement.challenge_brew_strong_25.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_strong_25.description")) - .model(CustomModel.get(Material.GLOWSTONE_DUST, "advancement", "brewing", "challenge_brew_strong_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DRAGON_BREATH) - .key("challenge_brew_strong_250") - .title(Localizer.dLocalize("advancement.challenge_brew_strong_250.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_strong_250.description")) - .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "brewing", "challenge_brew_strong_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_brew_strong_25", "brewing.strong", 25, getConfig().challengeBrew1k); - registerMilestone("challenge_brew_strong_250", "brewing.strong", 250, getConfig().challengeBrew1k * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BREWING_STAND).key("challenge_brew_stands_10") + .title(Localizer.dLocalize("advancement.challenge_brew_stands_10.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_stands_10.description")) + .model(CustomModel.get(Material.BREWING_STAND, "advancement", "brewing", "challenge_brew_stands_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BLAZE_ROD) + .key("challenge_brew_stands_50") + .title(Localizer.dLocalize("advancement.challenge_brew_stands_50.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_stands_50.description")) + .model(CustomModel.get(Material.BLAZE_ROD, "advancement", "brewing", "challenge_brew_stands_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_brew_stands_10", "brewing.stands.placed", 10, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_stands_50", "brewing.stands.placed", 50, getConfig().challengeBrew1k * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPLASH_POTION).key("challenge_brew_splash_hits_50") - .title(Localizer.dLocalize("advancement.challenge_brew_splash_hits_50.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_splash_hits_50.description")) - .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "challenge_brew_splash_hits_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.LINGERING_POTION) - .key("challenge_brew_splash_hits_500") - .title(Localizer.dLocalize("advancement.challenge_brew_splash_hits_500.title")) - .description(Localizer.dLocalize("advancement.challenge_brew_splash_hits_500.description")) - .model(CustomModel.get(Material.LINGERING_POTION, "advancement", "brewing", "challenge_brew_splash_hits_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_brew_splash_hits_50", "brewing.splash.hits", 50, getConfig().challengeBrewSplash1k); - registerMilestone("challenge_brew_splash_hits_500", "brewing.splash.hits", 500, getConfig().challengeBrewSplash1k * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLOWSTONE_DUST).key("challenge_brew_strong_25") + .title(Localizer.dLocalize("advancement.challenge_brew_strong_25.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_strong_25.description")) + .model(CustomModel.get(Material.GLOWSTONE_DUST, "advancement", "brewing", "challenge_brew_strong_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DRAGON_BREATH) + .key("challenge_brew_strong_250") + .title(Localizer.dLocalize("advancement.challenge_brew_strong_250.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_strong_250.description")) + .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "brewing", "challenge_brew_strong_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_brew_strong_25", "brewing.strong", 25, getConfig().challengeBrew1k); + registerMilestone("challenge_brew_strong_250", "brewing.strong", 250, getConfig().challengeBrew1k * 2); - SpatialMatter.registerSliceType(new BrewingStandOwnerMatter()); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPLASH_POTION).key("challenge_brew_splash_hits_50") + .title(Localizer.dLocalize("advancement.challenge_brew_splash_hits_50.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_splash_hits_50.description")) + .model(CustomModel.get(Material.SPLASH_POTION, "advancement", "brewing", "challenge_brew_splash_hits_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LINGERING_POTION) + .key("challenge_brew_splash_hits_500") + .title(Localizer.dLocalize("advancement.challenge_brew_splash_hits_500.title")) + .description(Localizer.dLocalize("advancement.challenge_brew_splash_hits_500.description")) + .model(CustomModel.get(Material.LINGERING_POTION, "advancement", "brewing", "challenge_brew_splash_hits_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_brew_splash_hits_50", "brewing.splash.hits", 50, getConfig().challengeBrewSplash1k); + registerMilestone("challenge_brew_splash_hits_500", "brewing.splash.hits", 500, getConfig().challengeBrewSplash1k * 2); - private void handleCooldown(Player p, Runnable runnable) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - runnable.run(); - } + SpatialMatter.registerSliceType(new BrewingStandOwnerMatter()); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerItemConsumeEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, e, () -> { - if (e.getItem().getItemMeta() instanceof PotionMeta o - && !e.getItem().toString().contains("potion-type=minecraft:water") - && !e.getItem().toString().contains("potion-type=minecraft:mundane") - && !e.getItem().toString().contains("potion-type=minecraft:thick") - && !e.getItem().toString().contains("potion-type=minecraft:awkward")) { - getPlayer(p).getData().addStat("brewing.consumed", 1); - if (o.getBasePotionData().isUpgraded()) { - getPlayer(p).getData().addStat("brewing.strong", 1); - } - handleCooldown(p, () -> xp(p, p.getLocation(), - getConfig().splashXP - + (getConfig().splashMultiplier * o.getCustomEffects().stream().mapToDouble(i -> (i.getAmplifier() + 1) * (i.getDuration() / 20D)).sum()) - + (getConfig().splashMultiplier * (o.getBasePotionData().isUpgraded() ? 50 : 25)))); - } - }); - } + private void handleCooldown(Player p, Runnable runnable) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + runnable.run(); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PotionSplashEvent e) { - if (e.getPotion().getShooter() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> { - AdaptPlayer a = getPlayer(p); - getPlayer(p).getData().addStat("brewing.splashes", 1); - getPlayer(p).getData().addStat("brewing.splash.hits", e.getAffectedEntities().size()); - xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().splashXP + (getConfig().splashMultiplier * e.getPotion().getEffects().stream().mapToDouble(i -> (i.getAmplifier() + 1) * (i.getDuration() / 20D)).sum())); - }); - } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerItemConsumeEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, e, () -> { + if (!(e.getItem().getItemMeta() instanceof PotionMeta potionMeta)) { + return; + } + PotionType baseType = potionMeta.getBasePotionType(); + if (isBasePotionExcluded(baseType)) { + return; + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockPlaceEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getBlock().getType().equals(Material.BREWING_STAND)) { - WorldData.of(e.getBlock().getWorld()).set(e.getBlock(), new BrewingStandOwner(e.getPlayer().getUniqueId())); - getPlayer(e.getPlayer()).getData().addStat("brewing.stands.placed", 1); - } - }); - } + getPlayer(p).getData().addStat("brewing.consumed", 1); + if (potionMeta.getBasePotionData().isUpgraded()) { + getPlayer(p).getData().addStat("brewing.strong", 1); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(InventoryOpenEvent e) { - if ( !(e.getPlayer() instanceof Player player) - || !(e.getInventory() instanceof BrewerInventory inv)) { - return; - } + double customEffectPower = sumPotionEffects(potionMeta.getCustomEffects()); + double upgradeBonus = potionMeta.getBasePotionData().isUpgraded() ? 50 : 25; + handleCooldown(p, () -> xp(p, p.getLocation(), + getConfig().splashXP + + (getConfig().splashMultiplier * customEffectPower) + + (getConfig().splashMultiplier * upgradeBonus))); + }); + } - var holder = inv.getHolder(); - if (holder == null) return; - var block = holder.getBlock(); - if (block.getType() != Material.BREWING_STAND) return; - - shouldReturnForPlayer(player, e, () -> { - var data = WorldData.of(block.getWorld()); - var owner = data.get(block, BrewingStandOwner.class); - if (owner != null) return; - data.set(block, new BrewingStandOwner(player.getUniqueId())); - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(PotionSplashEvent e) { + if (e.getPotion().getShooter() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> { + AdaptPlayer a = getPlayer(p); + getPlayer(p).getData().addStat("brewing.splashes", 1); + getPlayer(p).getData().addStat("brewing.splash.hits", e.getAffectedEntities().size()); + double effectPower = sumPotionEffects(e.getPotion().getEffects()); + xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().splashXP + (getConfig().splashMultiplier * effectPower)); + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { - return; - } - WorldData.of(e.getBlock().getWorld()).remove(e.getBlock(), BrewingStandOwner.class); - }); + private boolean isBasePotionExcluded(PotionType baseType) { + if (baseType == null) { + return true; } + return baseType == PotionType.WATER + || baseType == PotionType.MUNDANE + || baseType == PotionType.THICK + || baseType == PotionType.AWKWARD; + } - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); + private double sumPotionEffects(Iterable effects) { + double sum = 0D; + for (PotionEffect effect : effects) { + sum += (effect.getAmplifier() + 1) * (effect.getDuration() / 20D); } + return sum; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&d"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeBrew1k = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew Splash1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeBrewSplash1k = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash XP for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double splashXP = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 2500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash Multiplier for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double splashMultiplier = 0.4; + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockPlaceEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getBlock().getType().equals(Material.BREWING_STAND)) { + WorldData.of(e.getBlock().getWorld()).set(e.getBlock(), new BrewingStandOwner(e.getPlayer().getUniqueId())); + getPlayer(e.getPlayer()).getData().addStat("brewing.stands.placed", 1); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(InventoryOpenEvent e) { + if (!(e.getPlayer() instanceof Player player) + || !(e.getInventory() instanceof BrewerInventory inv)) { + return; } + + org.bukkit.block.BrewingStand holder = inv.getHolder(); + if (holder == null) return; + org.bukkit.block.Block block = holder.getBlock(); + if (block.getType() != Material.BREWING_STAND) return; + + shouldReturnForPlayer(player, e, () -> { + art.arcane.adapt.api.data.WorldData data = WorldData.of(block.getWorld()); + art.arcane.adapt.content.matter.BrewingStandOwner owner = data.get(block, BrewingStandOwner.class); + if (owner != null) return; + data.set(block, new BrewingStandOwner(player.getUniqueId())); + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (!e.getBlock().getType().equals(Material.BREWING_STAND)) { + return; + } + WorldData.of(e.getBlock().getWorld()).remove(e.getBlock(), BrewingStandOwner.class); + }); + } + + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&d"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeBrew1k = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Brew Splash1k for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeBrewSplash1k = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash XP for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double splashXP = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 2500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Splash Multiplier for the Brewing skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double splashMultiplier = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index f84394ac9..bf295798d 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -43,6 +43,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; @@ -50,558 +51,564 @@ import java.util.concurrent.ConcurrentHashMap; public class SkillChronos extends SimpleSkill { - private final Map lastPositions; - private final Map> positionHistory; - private final Map> recentActionTypes; - private final Map actionTypeResetTimestamps; - private final Map lastActivityTimestamps; - private final Map sleepCooldowns; - private final Map sleepEntryWorldTime; - private final Map speedPotionTrackers; - private final Map enderPearlCooldowns; - private final Map survivalStreakStart; - private final Map lastSurvivalCheck; - - public SkillChronos() { - super("chronos", Localizer.dLocalize("skill.chronos.icon")); - registerConfiguration(Config.class); - setColor(C.AQUA); - setInterval(600000); - setDescription(Localizer.dLocalize("skill.chronos.description")); - setDisplayName(Localizer.dLocalize("skill.chronos.name")); - setInterval(getConfig().setInterval); - setIcon(Material.CLOCK); - registerAdaptation(new ChronosTimeInABottle()); - registerAdaptation(new ChronosAberrantTouch()); - registerAdaptation(new ChronosInstantRecall()); - registerAdaptation(new ChronosTimeBomb()); - registerAdaptation(new ChronosTemporalEcho()); - lastPositions = new ConcurrentHashMap<>(); - positionHistory = new ConcurrentHashMap<>(); - recentActionTypes = new ConcurrentHashMap<>(); - actionTypeResetTimestamps = new ConcurrentHashMap<>(); - lastActivityTimestamps = new ConcurrentHashMap<>(); - sleepCooldowns = new ConcurrentHashMap<>(); - sleepEntryWorldTime = new ConcurrentHashMap<>(); - speedPotionTrackers = new ConcurrentHashMap<>(); - enderPearlCooldowns = new ConcurrentHashMap<>(); - survivalStreakStart = new ConcurrentHashMap<>(); - lastSurvivalCheck = new ConcurrentHashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLOCK) - .key("challenge_chronos_1h") - .title(Localizer.dLocalize("advancement.challenge_chronos_1h.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_1h.description")) - .model(CustomModel.get(Material.CLOCK, "advancement", "chronos", "challenge_chronos_1h")) + private final Map lastPositions; + private final Map> positionHistory; + private final Map> recentActionTypes; + private final Map actionTypeResetTimestamps; + private final Map lastActivityTimestamps; + private final Map sleepCooldowns; + private final Map sleepEntryWorldTime; + private final Map speedPotionTrackers; + private final Map enderPearlCooldowns; + private final Map survivalStreakStart; + private final Map lastSurvivalCheck; + + public SkillChronos() { + super("chronos", Localizer.dLocalize("skill.chronos.icon")); + registerConfiguration(Config.class); + setColor(C.AQUA); + setInterval(600000); + setDescription(Localizer.dLocalize("skill.chronos.description")); + setDisplayName(Localizer.dLocalize("skill.chronos.name")); + setInterval(getConfig().setInterval); + setIcon(Material.CLOCK); + registerAdaptation(new ChronosTimeInABottle()); + registerAdaptation(new ChronosAberrantTouch()); + registerAdaptation(new ChronosInstantRecall()); + registerAdaptation(new ChronosTimeBomb()); + registerAdaptation(new ChronosTemporalEcho()); + lastPositions = new ConcurrentHashMap<>(); + positionHistory = new ConcurrentHashMap<>(); + recentActionTypes = new ConcurrentHashMap<>(); + actionTypeResetTimestamps = new ConcurrentHashMap<>(); + lastActivityTimestamps = new ConcurrentHashMap<>(); + sleepCooldowns = new ConcurrentHashMap<>(); + sleepEntryWorldTime = new ConcurrentHashMap<>(); + speedPotionTrackers = new ConcurrentHashMap<>(); + enderPearlCooldowns = new ConcurrentHashMap<>(); + survivalStreakStart = new ConcurrentHashMap<>(); + lastSurvivalCheck = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_1h") + .title(Localizer.dLocalize("advancement.challenge_chronos_1h.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_1h.description")) + .model(CustomModel.get(Material.CLOCK, "advancement", "chronos", "challenge_chronos_1h")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.COMPASS) + .key("challenge_chronos_24h") + .title(Localizer.dLocalize("advancement.challenge_chronos_24h.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_24h.description")) + .model(CustomModel.get(Material.COMPASS, "advancement", "chronos", "challenge_chronos_24h")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_chronos_168h") + .title(Localizer.dLocalize("advancement.challenge_chronos_168h.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_168h.description")) + .model(CustomModel.get(Material.RECOVERY_COMPASS, "advancement", "chronos", "challenge_chronos_168h")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.COMPASS) - .key("challenge_chronos_24h") - .title(Localizer.dLocalize("advancement.challenge_chronos_24h.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_24h.description")) - .model(CustomModel.get(Material.COMPASS, "advancement", "chronos", "challenge_chronos_24h")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RECOVERY_COMPASS) - .key("challenge_chronos_168h") - .title(Localizer.dLocalize("advancement.challenge_chronos_168h.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_168h.description")) - .model(CustomModel.get(Material.RECOVERY_COMPASS, "advancement", "chronos", "challenge_chronos_168h")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_chronos_1h", "minutes.online", 60, getConfig().challengeChronosReward); - registerMilestone("challenge_chronos_24h", "minutes.online", 1440, getConfig().challengeChronosReward * 2); - registerMilestone("challenge_chronos_168h", "minutes.online", 10080, getConfig().challengeChronosReward * 5); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COMPASS).key("challenge_active_dist_1k") - .title(Localizer.dLocalize("advancement.challenge_active_dist_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_active_dist_1k.description")) - .model(CustomModel.get(Material.COMPASS, "advancement", "chronos", "challenge_active_dist_1k")) + .build()) + .build()) + .build()); + registerMilestone("challenge_chronos_1h", "minutes.online", 60, getConfig().challengeChronosReward); + registerMilestone("challenge_chronos_24h", "minutes.online", 1440, getConfig().challengeChronosReward * 2); + registerMilestone("challenge_chronos_168h", "minutes.online", 10080, getConfig().challengeChronosReward * 5); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COMPASS).key("challenge_active_dist_1k") + .title(Localizer.dLocalize("advancement.challenge_active_dist_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_active_dist_1k.description")) + .model(CustomModel.get(Material.COMPASS, "advancement", "chronos", "challenge_active_dist_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_active_dist_10k") + .title(Localizer.dLocalize("advancement.challenge_active_dist_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_active_dist_10k.description")) + .model(CustomModel.get(Material.RECOVERY_COMPASS, "advancement", "chronos", "challenge_active_dist_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LODESTONE) + .key("challenge_active_dist_100k") + .title(Localizer.dLocalize("advancement.challenge_active_dist_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_active_dist_100k.description")) + .model(CustomModel.get(Material.LODESTONE, "advancement", "chronos", "challenge_active_dist_100k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RECOVERY_COMPASS) - .key("challenge_active_dist_10k") - .title(Localizer.dLocalize("advancement.challenge_active_dist_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_active_dist_10k.description")) - .model(CustomModel.get(Material.RECOVERY_COMPASS, "advancement", "chronos", "challenge_active_dist_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.LODESTONE) - .key("challenge_active_dist_100k") - .title(Localizer.dLocalize("advancement.challenge_active_dist_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_active_dist_100k.description")) - .model(CustomModel.get(Material.LODESTONE, "advancement", "chronos", "challenge_active_dist_100k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_active_dist_1k", "chronos.active.distance", 1000, getConfig().challengeChronosReward); - registerMilestone("challenge_active_dist_10k", "chronos.active.distance", 10000, getConfig().challengeChronosReward * 2); - registerMilestone("challenge_active_dist_100k", "chronos.active.distance", 100000, getConfig().challengeChronosReward * 5); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHITE_BED).key("challenge_beds_10") - .title(Localizer.dLocalize("advancement.challenge_beds_10.title")) - .description(Localizer.dLocalize("advancement.challenge_beds_10.description")) - .model(CustomModel.get(Material.WHITE_BED, "advancement", "chronos", "challenge_beds_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RED_BED) - .key("challenge_beds_100") - .title(Localizer.dLocalize("advancement.challenge_beds_100.title")) - .description(Localizer.dLocalize("advancement.challenge_beds_100.description")) - .model(CustomModel.get(Material.RED_BED, "advancement", "chronos", "challenge_beds_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_beds_10", "chronos.beds.used", 10, getConfig().challengeChronosReward); - registerMilestone("challenge_beds_100", "chronos.beds.used", 100, getConfig().challengeChronosReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL).key("challenge_chronos_tp_50") - .title(Localizer.dLocalize("advancement.challenge_chronos_tp_50.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_tp_50.description")) - .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "chronos", "challenge_chronos_tp_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHORUS_FRUIT) - .key("challenge_chronos_tp_500") - .title(Localizer.dLocalize("advancement.challenge_chronos_tp_500.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_tp_500.description")) - .model(CustomModel.get(Material.CHORUS_FRUIT, "advancement", "chronos", "challenge_chronos_tp_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_chronos_tp_50", "chronos.teleports", 50, getConfig().challengeChronosReward); - registerMilestone("challenge_chronos_tp_500", "chronos.teleports", 500, getConfig().challengeChronosReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SKELETON_SKULL).key("challenge_chronos_deaths_10") - .title(Localizer.dLocalize("advancement.challenge_chronos_deaths_10.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_deaths_10.description")) - .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "chronos", "challenge_chronos_deaths_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_chronos_deaths_100") - .title(Localizer.dLocalize("advancement.challenge_chronos_deaths_100.title")) - .description(Localizer.dLocalize("advancement.challenge_chronos_deaths_100.description")) - .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "chronos", "challenge_chronos_deaths_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_chronos_deaths_10", "chronos.deaths", 10, getConfig().challengeChronosReward); - registerMilestone("challenge_chronos_deaths_100", "chronos.deaths", 100, getConfig().challengeChronosReward * 2); + .build()) + .build()) + .build()); + registerMilestone("challenge_active_dist_1k", "chronos.active.distance", 1000, getConfig().challengeChronosReward); + registerMilestone("challenge_active_dist_10k", "chronos.active.distance", 10000, getConfig().challengeChronosReward * 2); + registerMilestone("challenge_active_dist_100k", "chronos.active.distance", 100000, getConfig().challengeChronosReward * 5); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHITE_BED).key("challenge_beds_10") + .title(Localizer.dLocalize("advancement.challenge_beds_10.title")) + .description(Localizer.dLocalize("advancement.challenge_beds_10.description")) + .model(CustomModel.get(Material.WHITE_BED, "advancement", "chronos", "challenge_beds_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RED_BED) + .key("challenge_beds_100") + .title(Localizer.dLocalize("advancement.challenge_beds_100.title")) + .description(Localizer.dLocalize("advancement.challenge_beds_100.description")) + .model(CustomModel.get(Material.RED_BED, "advancement", "chronos", "challenge_beds_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_beds_10", "chronos.beds.used", 10, getConfig().challengeChronosReward); + registerMilestone("challenge_beds_100", "chronos.beds.used", 100, getConfig().challengeChronosReward * 2); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL).key("challenge_chronos_tp_50") + .title(Localizer.dLocalize("advancement.challenge_chronos_tp_50.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_tp_50.description")) + .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "chronos", "challenge_chronos_tp_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHORUS_FRUIT) + .key("challenge_chronos_tp_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_tp_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_tp_500.description")) + .model(CustomModel.get(Material.CHORUS_FRUIT, "advancement", "chronos", "challenge_chronos_tp_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_chronos_tp_50", "chronos.teleports", 50, getConfig().challengeChronosReward); + registerMilestone("challenge_chronos_tp_500", "chronos.teleports", 500, getConfig().challengeChronosReward * 2); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SKELETON_SKULL).key("challenge_chronos_deaths_10") + .title(Localizer.dLocalize("advancement.challenge_chronos_deaths_10.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_deaths_10.description")) + .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "chronos", "challenge_chronos_deaths_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_chronos_deaths_100") + .title(Localizer.dLocalize("advancement.challenge_chronos_deaths_100.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_deaths_100.description")) + .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "chronos", "challenge_chronos_deaths_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_chronos_deaths_10", "chronos.deaths", 10, getConfig().challengeChronosReward); + registerMilestone("challenge_chronos_deaths_100", "chronos.deaths", 100, getConfig().challengeChronosReward * 2); + } + + private void trackAction(UUID uuid, String actionType) { + long now = System.currentTimeMillis(); + lastActivityTimestamps.put(uuid, now); + + Long resetTime = actionTypeResetTimestamps.get(uuid); + if (resetTime == null || now - resetTime > getConfig().activityWindow) { + recentActionTypes.put(uuid, ConcurrentHashMap.newKeySet()); + actionTypeResetTimestamps.put(uuid, now); } + recentActionTypes.computeIfAbsent(uuid, k -> ConcurrentHashMap.newKeySet()).add(actionType); + } - private void trackAction(UUID uuid, String actionType) { - long now = System.currentTimeMillis(); - lastActivityTimestamps.put(uuid, now); + private boolean isAfk(UUID uuid) { + Deque history = positionHistory.get(uuid); + if (history == null || history.size() < 3) { + return false; + } - Long resetTime = actionTypeResetTimestamps.get(uuid); - if (resetTime == null || now - resetTime > getConfig().activityWindow) { - recentActionTypes.put(uuid, ConcurrentHashMap.newKeySet()); - actionTypeResetTimestamps.put(uuid, now); - } - recentActionTypes.computeIfAbsent(uuid, k -> ConcurrentHashMap.newKeySet()).add(actionType); + double avgX = 0; + double avgZ = 0; + int count = 0; + for (Location loc : history) { + avgX += loc.getX(); + avgZ += loc.getZ(); + count++; } + avgX /= count; + avgZ /= count; + + double variance = 0; + for (Location loc : history) { + double dx = loc.getX() - avgX; + double dz = loc.getZ() - avgZ; + variance += Math.sqrt(dx * dx + dz * dz); + } + variance /= count; - private boolean isAfk(UUID uuid) { - Deque history = positionHistory.get(uuid); - if (history == null || history.size() < 3) { - return false; - } + Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); + return variance < getConfig().afkVarianceThreshold && actions.size() < getConfig().afkMinActionTypes; + } - double avgX = 0; - double avgZ = 0; - int count = 0; - for (Location loc : history) { - avgX += loc.getX(); - avgZ += loc.getZ(); - count++; - } - avgX /= count; - avgZ /= count; - - double variance = 0; - for (Location loc : history) { - double dx = loc.getX() - avgX; - double dz = loc.getZ() - avgZ; - variance += Math.sqrt(dx * dx + dz * dz); - } - variance /= count; + private double getAfkMultiplier(UUID uuid) { + return isAfk(uuid) ? getConfig().afkPenaltyMultiplier : 1.0; + } - Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); - return variance < getConfig().afkVarianceThreshold && actions.size() < getConfig().afkMinActionTypes; - } + private boolean isNight(Player p) { + long time = p.getWorld().getTime(); + return time >= 12542 && time <= 23460; + } - private double getAfkMultiplier(UUID uuid) { - return isAfk(uuid) ? getConfig().afkPenaltyMultiplier : 1.0; + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + long now = System.currentTimeMillis(); - private boolean isNight(Player p) { - long time = p.getWorld().getTime(); - return time >= 12542 && time <= 23460; - } + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player p = adaptPlayer.getPlayer(); + shouldReturnForPlayer(p, () -> { + UUID uuid = p.getUniqueId(); + Location current = p.getLocation(); + Location last = lastPositions.get(uuid); + + // Update position history + Deque history = positionHistory.computeIfAbsent(uuid, k -> new ArrayDeque<>()); + history.addLast(current.clone()); + while (history.size() > getConfig().positionHistorySize) { + history.removeFirst(); + } + + double moved = (last != null && last.getWorld() != null && last.getWorld().equals(current.getWorld())) + ? last.distance(current) + : 0; - @Override - public void onTick() { - if (!this.isEnabled()) { - return; + // Track movement as an action type + if (moved >= getConfig().minimumMovementForActiveCheck) { + trackAction(uuid, "movement"); } - long now = System.currentTimeMillis(); - - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player p = adaptPlayer.getPlayer(); - shouldReturnForPlayer(p, () -> { - UUID uuid = p.getUniqueId(); - Location current = p.getLocation(); - Location last = lastPositions.get(uuid); - - // Update position history - Deque history = positionHistory.computeIfAbsent(uuid, k -> new ArrayDeque<>()); - history.addLast(current.clone()); - while (history.size() > getConfig().positionHistorySize) { - history.removeFirst(); - } - - double moved = (last != null && last.getWorld() != null && last.getWorld().equals(current.getWorld())) - ? last.distance(current) - : 0; - - // Track movement as an action type - if (moved >= getConfig().minimumMovementForActiveCheck) { - trackAction(uuid, "movement"); - } - - double afkMult = getAfkMultiplier(uuid); - - // Movement XP (existing behavior, now with AFK penalty) - if (moved >= getConfig().minimumMovementForActiveCheck) { - adaptPlayer.getData().addStat("minutes.online", 10); - adaptPlayer.getData().addStat("chronos.active.distance", moved); - double bonus = (moved / getConfig().distancePerBonusXP) * getConfig().activeMovementXP; - xpSilent(p, Math.min(getConfig().activeMovementXPCapPerTick, bonus) * afkMult, "chronos:movement"); - } - - // Passive active-play XP - Long lastActivity = lastActivityTimestamps.get(uuid); - if (lastActivity != null && now - lastActivity < getConfig().activityWindow) { - double passiveXP = getConfig().passiveActiveXP; - - // Night activity multiplier - if (isNight(p)) { - passiveXP *= getConfig().nightActivityMultiplier; - } - - // Activity variety bonus - Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); - if (actions.size() >= getConfig().activityTypesForBonus) { - passiveXP *= getConfig().activityBonusMultiplier; - } - - xpSilent(p, passiveXP * afkMult, "chronos:passive"); - } - - // Survival streak XP - survivalStreakStart.putIfAbsent(uuid, now); - Long lastCheck = lastSurvivalCheck.get(uuid); - if (lastCheck == null || now - lastCheck >= 60000) { - lastSurvivalCheck.put(uuid, now); - long aliveMs = now - survivalStreakStart.getOrDefault(uuid, now); - double aliveHours = aliveMs / 3600000.0; - double streakBonus = 1.0 + Math.min( - aliveHours * getConfig().survivalStreakBonusPerHour, - getConfig().survivalStreakHourCap * getConfig().survivalStreakBonusPerHour - ); - xpSilent(p, getConfig().survivalXPPerMinute * streakBonus * afkMult, "chronos:survival"); - } - - checkStatTrackers(adaptPlayer); - lastPositions.put(uuid, current.clone()); - }); + + double afkMult = getAfkMultiplier(uuid); + + // Movement XP (existing behavior, now with AFK penalty) + if (moved >= getConfig().minimumMovementForActiveCheck) { + adaptPlayer.getData().addStat("minutes.online", 10); + adaptPlayer.getData().addStat("chronos.active.distance", moved); + double bonus = (moved / getConfig().distancePerBonusXP) * getConfig().activeMovementXP; + xpSilent(p, Math.min(getConfig().activeMovementXPCapPerTick, bonus) * afkMult, "chronos:movement"); } - } - // --- Sleep XP --- - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerBedEnterEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, () -> { - UUID uuid = p.getUniqueId(); - long now = System.currentTimeMillis(); - - Long lastSleep = sleepCooldowns.get(uuid); - if (lastSleep != null && now - lastSleep < getConfig().sleepCooldown) { - return; - } - - trackAction(uuid, "sleep"); - long worldTime = p.getWorld().getTime(); - sleepEntryWorldTime.put(uuid, worldTime); - sleepCooldowns.put(uuid, now); - getPlayer(p).getData().addStat("chronos.beds.used", 1); - - J.runEntity(p, () -> { - if (!p.isOnline()) { - return; - } - long currentWorldTime = p.getWorld().getTime(); - boolean nightSkipped = currentWorldTime < 1000 || currentWorldTime < worldTime - 100; - if (nightSkipped) { - xp(p, p.getLocation(), getConfig().sleepSkipXP); - } else { - xp(p, p.getLocation(), getConfig().sleepAttemptXP); - } - }, 40); - }); - } + // Passive active-play XP + Long lastActivity = lastActivityTimestamps.get(uuid); + if (lastActivity != null && now - lastActivity < getConfig().activityWindow) { + double passiveXP = getConfig().passiveActiveXP; - // --- Speed Potion XP --- - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerItemConsumeEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, () -> { - ItemStack item = e.getItem(); - if (item.getType() != Material.POTION) { - return; - } - if (!(item.getItemMeta() instanceof PotionMeta meta)) { - return; - } - - boolean isSpeedPotion = false; - boolean isSpeedII = false; - - PotionType baseType = meta.getBasePotionType(); - if (baseType == PotionType.SWIFTNESS) { - isSpeedPotion = true; - } - if (baseType == PotionType.STRONG_SWIFTNESS) { - isSpeedPotion = true; - isSpeedII = true; - } - - if (!isSpeedPotion && meta.hasCustomEffects()) { - isSpeedPotion = meta.getCustomEffects().stream() - .anyMatch(effect -> effect.getType().equals(PotionEffectType.SPEED)); - isSpeedII = meta.getCustomEffects().stream() - .anyMatch(effect -> effect.getType().equals(PotionEffectType.SPEED) && effect.getAmplifier() >= 1); - } - - if (!isSpeedPotion) { - return; - } - - UUID uuid = p.getUniqueId(); - trackAction(uuid, "potion"); - long now = System.currentTimeMillis(); - - SpeedPotionTracker tracker = speedPotionTrackers.computeIfAbsent(uuid, k -> new SpeedPotionTracker()); - - if (now - tracker.lastUseTime > getConfig().speedPotionResetWindow) { - tracker.consecutiveUses = 0; - } - - double decay = getConfig().speedPotionDiminishingDecay; - double floor = getConfig().speedPotionDiminishingFloor; - double multiplier = Math.max(floor, Math.pow(1.0 - decay, tracker.consecutiveUses)); - - double xpAmount = getConfig().speedPotionBaseXP * multiplier; - if (isSpeedII) { - xpAmount *= getConfig().speedPotionLevelMultiplier; - } - - tracker.consecutiveUses++; - tracker.lastUseTime = now; - - xp(p, p.getLocation(), xpAmount); - }); - } + // Night activity multiplier + if (isNight(p)) { + passiveXP *= getConfig().nightActivityMultiplier; + } - // --- Ender Pearl XP --- + // Activity variety bonus + Set actions = recentActionTypes.getOrDefault(uuid, Set.of()); + if (actions.size() >= getConfig().activityTypesForBonus) { + passiveXP *= getConfig().activityBonusMultiplier; + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity() instanceof EnderPearl pearl)) { - return; + xpSilent(p, passiveXP * afkMult, "chronos:passive"); } - if (!(pearl.getShooter() instanceof Player p)) { - return; + + // Survival streak XP + survivalStreakStart.putIfAbsent(uuid, now); + Long lastCheck = lastSurvivalCheck.get(uuid); + if (lastCheck == null || now - lastCheck >= 60000) { + lastSurvivalCheck.put(uuid, now); + long aliveMs = now - survivalStreakStart.getOrDefault(uuid, now); + double aliveHours = aliveMs / 3600000.0; + double streakBonus = 1.0 + Math.min( + aliveHours * getConfig().survivalStreakBonusPerHour, + getConfig().survivalStreakHourCap * getConfig().survivalStreakBonusPerHour + ); + xpSilent(p, getConfig().survivalXPPerMinute * streakBonus * afkMult, "chronos:survival"); } - shouldReturnForPlayer(p, () -> { - UUID uuid = p.getUniqueId(); - long now = System.currentTimeMillis(); - - Long lastThrow = enderPearlCooldowns.get(uuid); - if (lastThrow != null && now - lastThrow < getConfig().enderPearlCooldown) { - return; - } - - trackAction(uuid, "teleport"); - enderPearlCooldowns.put(uuid, now); - xp(p, p.getLocation(), getConfig().enderPearlThrowXP); - }); - } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerTeleportEvent e) { - if (e.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { - return; + checkStatTrackers(adaptPlayer); + lastPositions.put(uuid, current.clone()); + }); + } + } + + // --- Sleep XP --- + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerBedEnterEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, () -> { + UUID uuid = p.getUniqueId(); + long now = System.currentTimeMillis(); + + Long lastSleep = sleepCooldowns.get(uuid); + if (lastSleep != null && now - lastSleep < getConfig().sleepCooldown) { + return; + } + + trackAction(uuid, "sleep"); + long worldTime = p.getWorld().getTime(); + sleepEntryWorldTime.put(uuid, worldTime); + sleepCooldowns.put(uuid, now); + getPlayer(p).getData().addStat("chronos.beds.used", 1); + + J.runEntity(p, () -> { + if (!p.isOnline()) { + return; } - Player p = e.getPlayer(); - if (ChronosInstantRecall.isRecallTeleportSuppressed(p)) { - return; + long currentWorldTime = p.getWorld().getTime(); + boolean nightSkipped = currentWorldTime < 1000 || currentWorldTime < worldTime - 100; + if (nightSkipped) { + xp(p, p.getLocation(), getConfig().sleepSkipXP); + } else { + xp(p, p.getLocation(), getConfig().sleepAttemptXP); } - shouldReturnForPlayer(p, () -> { - trackAction(p.getUniqueId(), "teleport"); - getPlayer(p).getData().addStat("chronos.teleports", 1); - xp(p, e.getTo(), getConfig().enderPearlTeleportXP); - }); - } + }, 40); + }); + } + + // --- Speed Potion XP --- + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerItemConsumeEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, () -> { + ItemStack item = e.getItem(); + if (item.getType() != Material.POTION) { + return; + } + if (!(item.getItemMeta() instanceof PotionMeta meta)) { + return; + } + + boolean isSpeedPotion = false; + boolean isSpeedII = false; + + PotionType baseType = meta.getBasePotionType(); + if (baseType == PotionType.SWIFTNESS) { + isSpeedPotion = true; + } + if (baseType == PotionType.STRONG_SWIFTNESS) { + isSpeedPotion = true; + isSpeedII = true; + } + + if (!isSpeedPotion && meta.hasCustomEffects()) { + for (PotionEffect customEffect : meta.getCustomEffects()) { + if (!customEffect.getType().equals(PotionEffectType.SPEED)) { + continue; + } + isSpeedPotion = true; + if (customEffect.getAmplifier() >= 1) { + isSpeedII = true; + break; + } + } + } - // --- Death / Survival Streak Reset --- + if (!isSpeedPotion) { + return; + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerDeathEvent e) { - Player p = e.getEntity(); - UUID uuid = p.getUniqueId(); - survivalStreakStart.put(uuid, System.currentTimeMillis()); - lastSurvivalCheck.remove(uuid); - trackAction(uuid, "combat"); - shouldReturnForPlayer(p, () -> getPlayer(p).getData().addStat("chronos.deaths", 1)); - } + UUID uuid = p.getUniqueId(); + trackAction(uuid, "potion"); + long now = System.currentTimeMillis(); - // --- Player Quit Cleanup --- - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - UUID uuid = e.getPlayer().getUniqueId(); - lastPositions.remove(uuid); - positionHistory.remove(uuid); - recentActionTypes.remove(uuid); - actionTypeResetTimestamps.remove(uuid); - lastActivityTimestamps.remove(uuid); - sleepCooldowns.remove(uuid); - sleepEntryWorldTime.remove(uuid); - speedPotionTrackers.remove(uuid); - enderPearlCooldowns.remove(uuid); - survivalStreakStart.remove(uuid); - lastSurvivalCheck.remove(uuid); - } + SpeedPotionTracker tracker = speedPotionTrackers.computeIfAbsent(uuid, k -> new SpeedPotionTracker()); - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + if (now - tracker.lastUseTime > getConfig().speedPotionResetWindow) { + tracker.consecutiveUses = 0; + } - @Override - protected void onConfigReload(Config previousConfig, Config newConfig) { - super.onConfigReload(previousConfig, newConfig); - setInterval(newConfig.setInterval); - } + double decay = getConfig().speedPotionDiminishingDecay; + double floor = getConfig().speedPotionDiminishingFloor; + double multiplier = Math.max(floor, Math.pow(1.0 - decay, tracker.consecutiveUses)); - private static class SpeedPotionTracker { - int consecutiveUses; - long lastUseTime; - } + double xpAmount = getConfig().speedPotionBaseXP * multiplier; + if (isSpeedII) { + xpAmount *= getConfig().speedPotionLevelMultiplier; + } + + tracker.consecutiveUses++; + tracker.lastUseTime = now; + + xp(p, p.getLocation(), xpAmount); + }); + } - @NoArgsConstructor - protected static class Config { - // Existing - @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval used by this logic.", impact = "Lower values run logic more often; higher values run it less often.") - long setInterval = 5050; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&b"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Movement For Active Check for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double minimumMovementForActiveCheck = 0.35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Per Bonus XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double distancePerBonusXP = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double activeMovementXP = 3.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XPCap Per Tick for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double activeMovementXPCapPerTick = 6; - - // Anti-AFK - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Position History Size for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int positionHistorySize = 12; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Variance Threshold for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double afkVarianceThreshold = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Min Action Types for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int afkMinActionTypes = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Penalty Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double afkPenaltyMultiplier = 0.03; - - // Passive active XP - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Active XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double passiveActiveXP = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long activityWindow = 15000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Types For Bonus for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int activityTypesForBonus = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Bonus Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double activityBonusMultiplier = 1.5; - - // Night bonus - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Night Activity Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double nightActivityMultiplier = 1.3; - - // Sleep - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Skip XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sleepSkipXP = 150; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Attempt XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sleepAttemptXP = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long sleepCooldown = 30000; - - // Speed potion - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Base XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedPotionBaseXP = 45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Level Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedPotionLevelMultiplier = 1.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Decay for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedPotionDiminishingDecay = 0.15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Floor for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double speedPotionDiminishingFloor = 0.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Reset Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long speedPotionResetWindow = 300000; - - // Ender pearl - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Throw XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double enderPearlThrowXP = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Teleport XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double enderPearlTeleportXP = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long enderPearlCooldown = 10000; - - // Survival streak - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival XPPer Minute for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double survivalXPPerMinute = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Bonus Per Hour for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double survivalStreakBonusPerHour = 0.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Hour Cap for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int survivalStreakHourCap = 5; - - // Challenge rewards - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chronos Reward for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeChronosReward = 500; + // --- Ender Pearl XP --- + + @EventHandler(priority = EventPriority.MONITOR) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity() instanceof EnderPearl pearl)) { + return; + } + if (!(pearl.getShooter() instanceof Player p)) { + return; + } + shouldReturnForPlayer(p, () -> { + UUID uuid = p.getUniqueId(); + long now = System.currentTimeMillis(); + + Long lastThrow = enderPearlCooldowns.get(uuid); + if (lastThrow != null && now - lastThrow < getConfig().enderPearlCooldown) { + return; + } + + trackAction(uuid, "teleport"); + enderPearlCooldowns.put(uuid, now); + xp(p, p.getLocation(), getConfig().enderPearlThrowXP); + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerTeleportEvent e) { + if (e.getCause() != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) { + return; + } + Player p = e.getPlayer(); + if (ChronosInstantRecall.isRecallTeleportSuppressed(p)) { + return; } + shouldReturnForPlayer(p, () -> { + trackAction(p.getUniqueId(), "teleport"); + getPlayer(p).getData().addStat("chronos.teleports", 1); + xp(p, e.getTo(), getConfig().enderPearlTeleportXP); + }); + } + + // --- Death / Survival Streak Reset --- + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerDeathEvent e) { + Player p = e.getEntity(); + UUID uuid = p.getUniqueId(); + survivalStreakStart.put(uuid, System.currentTimeMillis()); + lastSurvivalCheck.remove(uuid); + trackAction(uuid, "combat"); + shouldReturnForPlayer(p, () -> getPlayer(p).getData().addStat("chronos.deaths", 1)); + } + + // --- Player Quit Cleanup --- + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID uuid = e.getPlayer().getUniqueId(); + lastPositions.remove(uuid); + positionHistory.remove(uuid); + recentActionTypes.remove(uuid); + actionTypeResetTimestamps.remove(uuid); + lastActivityTimestamps.remove(uuid); + sleepCooldowns.remove(uuid); + sleepEntryWorldTime.remove(uuid); + speedPotionTrackers.remove(uuid); + enderPearlCooldowns.remove(uuid); + survivalStreakStart.remove(uuid); + lastSurvivalCheck.remove(uuid); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + protected void onConfigReload(Config previousConfig, Config newConfig) { + super.onConfigReload(previousConfig, newConfig); + setInterval(newConfig.setInterval); + } + + private static class SpeedPotionTracker { + int consecutiveUses; + long lastUseTime; + } + + @NoArgsConstructor + protected static class Config { + // Existing + @art.arcane.adapt.util.config.ConfigDoc(value = "Tick interval used by this logic.", impact = "Lower values run logic more often; higher values run it less often.") + long setInterval = 5050; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&b"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Minimum Movement For Active Check for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double minimumMovementForActiveCheck = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Distance Per Bonus XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double distancePerBonusXP = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double activeMovementXP = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Active Movement XPCap Per Tick for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double activeMovementXPCapPerTick = 6; + + // Anti-AFK + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Position History Size for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int positionHistorySize = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Variance Threshold for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double afkVarianceThreshold = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Min Action Types for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int afkMinActionTypes = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Afk Penalty Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double afkPenaltyMultiplier = 0.03; + + // Passive active XP + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Passive Active XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double passiveActiveXP = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long activityWindow = 15000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Types For Bonus for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int activityTypesForBonus = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Activity Bonus Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double activityBonusMultiplier = 1.5; + + // Night bonus + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Night Activity Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double nightActivityMultiplier = 1.3; + + // Sleep + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Skip XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sleepSkipXP = 150; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Attempt XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sleepAttemptXP = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sleep Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long sleepCooldown = 30000; + + // Speed potion + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Base XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedPotionBaseXP = 45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Level Multiplier for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedPotionLevelMultiplier = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Decay for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedPotionDiminishingDecay = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Diminishing Floor for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double speedPotionDiminishingFloor = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Speed Potion Reset Window for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long speedPotionResetWindow = 300000; + + // Ender pearl + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Throw XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double enderPearlThrowXP = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Teleport XP for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double enderPearlTeleportXP = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ender Pearl Cooldown for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long enderPearlCooldown = 10000; + + // Survival streak + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival XPPer Minute for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double survivalXPPerMinute = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Bonus Per Hour for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double survivalStreakBonusPerHour = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Survival Streak Hour Cap for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int survivalStreakHourCap = 5; + + // Challenge rewards + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Chronos Reward for the Chronos skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeChronosReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java index 2cf670cbf..e91ce8c2c 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillCrafting.java @@ -42,296 +42,296 @@ import java.util.UUID; public class SkillCrafting extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillCrafting() { - super("crafting", Localizer.dLocalize("skill.crafting.icon")); - registerConfiguration(Config.class); - setColor(C.YELLOW); - setDescription(Localizer.dLocalize("skill.crafting.description")); - setDisplayName(Localizer.dLocalize("skill.crafting.name")); - setInterval(3789); - setIcon(Material.CRAFTING_TABLE); - registerAdaptation(new CraftingDeconstruction()); - registerAdaptation(new CraftingXP()); - registerAdaptation(new CraftingLeather()); - registerAdaptation(new CraftingSkulls()); - registerAdaptation(new CraftingBackpacks()); - registerAdaptation(new CraftingStations()); - registerAdaptation(new CraftingReconstruction()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CRAFTING_TABLE).key("challenge_craft_1k") - .title(Localizer.dLocalize("advancement.challenge_craft_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_1k.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_1k")) + public SkillCrafting() { + super("crafting", Localizer.dLocalize("skill.crafting.icon")); + registerConfiguration(Config.class); + setColor(C.YELLOW); + setDescription(Localizer.dLocalize("skill.crafting.description")); + setDisplayName(Localizer.dLocalize("skill.crafting.name")); + setInterval(3789); + setIcon(Material.CRAFTING_TABLE); + registerAdaptation(new CraftingDeconstruction()); + registerAdaptation(new CraftingXP()); + registerAdaptation(new CraftingLeather()); + registerAdaptation(new CraftingSkulls()); + registerAdaptation(new CraftingBackpacks()); + registerAdaptation(new CraftingStations()); + registerAdaptation(new CraftingReconstruction()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CRAFTING_TABLE).key("challenge_craft_1k") + .title(Localizer.dLocalize("advancement.challenge_craft_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_1k.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.CRAFTING_TABLE) + .key("challenge_craft_5k") + .title(Localizer.dLocalize("advancement.challenge_craft_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_5k.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.CRAFTING_TABLE) + .key("challenge_craft_50k") + .title(Localizer.dLocalize("advancement.challenge_craft_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_50k.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.CRAFTING_TABLE) + .key("challenge_craft_500k") + .title(Localizer.dLocalize("advancement.challenge_craft_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_500k.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.CRAFTING_TABLE) - .key("challenge_craft_5k") - .title(Localizer.dLocalize("advancement.challenge_craft_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_5k.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.CRAFTING_TABLE) - .key("challenge_craft_50k") - .title(Localizer.dLocalize("advancement.challenge_craft_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_50k.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.CRAFTING_TABLE) - .key("challenge_craft_500k") - .title(Localizer.dLocalize("advancement.challenge_craft_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_500k.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.CRAFTING_TABLE) - .key("challenge_craft_5m") - .title(Localizer.dLocalize("advancement.challenge_craft_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_5m.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_craft_1k", "crafted.items", 1000, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_5k", "crafted.items", 5000, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_50k", "crafted.items", 50000, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_500k", "crafted.items", 500000, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_5m", "crafted.items", 5000000, getConfig().challengeCraft1kReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLD_INGOT).key("challenge_craft_value_10k") - .title(Localizer.dLocalize("advancement.challenge_craft_value_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_value_10k.description")) - .model(CustomModel.get(Material.GOLD_INGOT, "advancement", "crafting", "challenge_craft_value_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND) - .key("challenge_craft_value_100k") - .title(Localizer.dLocalize("advancement.challenge_craft_value_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_value_100k.description")) - .model(CustomModel.get(Material.DIAMOND, "advancement", "crafting", "challenge_craft_value_100k")) + .key("challenge_craft_5m") + .title(Localizer.dLocalize("advancement.challenge_craft_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_5m.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "crafting", "challenge_craft_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_craft_value_10k", "crafted.value", 10000, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_value_100k", "crafted.value", 100000, getConfig().challengeCraft1kReward * 2); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_craft_1k", "crafted.items", 1000, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_5k", "crafted.items", 5000, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_50k", "crafted.items", 50000, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_500k", "crafted.items", 500000, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_5m", "crafted.items", 5000000, getConfig().challengeCraft1kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE).key("challenge_craft_tools_25") - .title(Localizer.dLocalize("advancement.challenge_craft_tools_25.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_tools_25.description")) - .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "crafting", "challenge_craft_tools_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_craft_tools_250") - .title(Localizer.dLocalize("advancement.challenge_craft_tools_250.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_tools_250.description")) - .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "crafting", "challenge_craft_tools_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_craft_tools_25", "crafting.tools", 25, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_tools_250", "crafting.tools", 250, getConfig().challengeCraft1kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLD_INGOT).key("challenge_craft_value_10k") + .title(Localizer.dLocalize("advancement.challenge_craft_value_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_value_10k.description")) + .model(CustomModel.get(Material.GOLD_INGOT, "advancement", "crafting", "challenge_craft_value_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_craft_value_100k") + .title(Localizer.dLocalize("advancement.challenge_craft_value_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_value_100k.description")) + .model(CustomModel.get(Material.DIAMOND, "advancement", "crafting", "challenge_craft_value_100k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_craft_value_10k", "crafted.value", 10000, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_value_100k", "crafted.value", 100000, getConfig().challengeCraft1kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_CHESTPLATE).key("challenge_craft_armor_25") - .title(Localizer.dLocalize("advancement.challenge_craft_armor_25.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_armor_25.description")) - .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "crafting", "challenge_craft_armor_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_CHESTPLATE) - .key("challenge_craft_armor_250") - .title(Localizer.dLocalize("advancement.challenge_craft_armor_250.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_armor_250.description")) - .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "crafting", "challenge_craft_armor_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_craft_armor_25", "crafting.armor", 25, getConfig().challengeCraft1kReward); - registerMilestone("challenge_craft_armor_250", "crafting.armor", 250, getConfig().challengeCraft1kReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE).key("challenge_craft_tools_25") + .title(Localizer.dLocalize("advancement.challenge_craft_tools_25.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_tools_25.description")) + .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "crafting", "challenge_craft_tools_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_craft_tools_250") + .title(Localizer.dLocalize("advancement.challenge_craft_tools_250.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_tools_250.description")) + .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "crafting", "challenge_craft_tools_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_craft_tools_25", "crafting.tools", 25, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_tools_250", "crafting.tools", 250, getConfig().challengeCraft1kReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.HOPPER).key("challenge_craft_mass_25k") - .title(Localizer.dLocalize("advancement.challenge_craft_mass_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_mass_25k.description")) - .model(CustomModel.get(Material.HOPPER, "advancement", "crafting", "challenge_craft_mass_25k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_craft_mass_250k") - .title(Localizer.dLocalize("advancement.challenge_craft_mass_250k.title")) - .description(Localizer.dLocalize("advancement.challenge_craft_mass_250k.description")) - .model(CustomModel.get(Material.CHEST, "advancement", "crafting", "challenge_craft_mass_250k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_craft_mass_25k", "crafted.items", 25000, getConfig().challengeCraft1kReward * 2); - registerMilestone("challenge_craft_mass_250k", "crafted.items", 250000, getConfig().challengeCraft1kReward * 5); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_CHESTPLATE).key("challenge_craft_armor_25") + .title(Localizer.dLocalize("advancement.challenge_craft_armor_25.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_armor_25.description")) + .model(CustomModel.get(Material.IRON_CHESTPLATE, "advancement", "crafting", "challenge_craft_armor_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_CHESTPLATE) + .key("challenge_craft_armor_250") + .title(Localizer.dLocalize("advancement.challenge_craft_armor_250.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_armor_250.description")) + .model(CustomModel.get(Material.DIAMOND_CHESTPLATE, "advancement", "crafting", "challenge_craft_armor_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_craft_armor_25", "crafting.armor", 25, getConfig().challengeCraft1kReward); + registerMilestone("challenge_craft_armor_250", "crafting.armor", 250, getConfig().challengeCraft1kReward * 2); - cooldowns = new HashMap<>(); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.HOPPER).key("challenge_craft_mass_25k") + .title(Localizer.dLocalize("advancement.challenge_craft_mass_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_mass_25k.description")) + .model(CustomModel.get(Material.HOPPER, "advancement", "crafting", "challenge_craft_mass_25k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_craft_mass_250k") + .title(Localizer.dLocalize("advancement.challenge_craft_mass_250k.title")) + .description(Localizer.dLocalize("advancement.challenge_craft_mass_250k.description")) + .model(CustomModel.get(Material.CHEST, "advancement", "crafting", "challenge_craft_mass_250k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_craft_mass_25k", "crafted.items", 25000, getConfig().challengeCraft1kReward * 2); + registerMilestone("challenge_craft_mass_250k", "crafted.items", 250000, getConfig().challengeCraft1kReward * 5); + cooldowns = new HashMap<>(); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - shouldReturnForPlayer(p, e, () -> { - if (!isValidCraftEvent(e)) { - return; - } - int recipeAmount = calculateRecipeAmount(e); - if (recipeAmount > 0) { - double v = recipeAmount * getValue(e.getRecipe().getResult()) * getConfig().craftingValueXPMultiplier; - getPlayer(p).getData().addStat("crafted.items", recipeAmount); - getPlayer(p).getData().addStat("crafted.value", v); - Material resultType = e.getRecipe().getResult().getType(); - String typeName = resultType.name(); - if (typeName.contains("_PICKAXE") || typeName.contains("_AXE") || typeName.contains("_SHOVEL") || typeName.contains("_HOE") || typeName.contains("_SWORD")) { - getPlayer(p).getData().addStat("crafting.tools", recipeAmount); - } - if (typeName.contains("_HELMET") || typeName.contains("_CHESTPLATE") || typeName.contains("_LEGGINGS") || typeName.contains("_BOOTS")) { - getPlayer(p).getData().addStat("crafting.armor", recipeAmount); - } - xp(p, v + getConfig().baseCraftingXP); - } - }); - } - @EventHandler(priority = EventPriority.MONITOR) - public void on(FurnaceSmeltEvent e) { - if (shouldReturnForWorld(e.getBlock().getWorld(), this)) { - return; + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); + shouldReturnForPlayer(p, e, () -> { + if (!isValidCraftEvent(e)) { + return; + } + int recipeAmount = calculateRecipeAmount(e); + if (recipeAmount > 0) { + double v = recipeAmount * getValue(e.getRecipe().getResult()) * getConfig().craftingValueXPMultiplier; + getPlayer(p).getData().addStat("crafted.items", recipeAmount); + getPlayer(p).getData().addStat("crafted.value", v); + Material resultType = e.getRecipe().getResult().getType(); + String typeName = resultType.name(); + if (typeName.contains("_PICKAXE") || typeName.contains("_AXE") || typeName.contains("_SHOVEL") || typeName.contains("_HOE") || typeName.contains("_SWORD")) { + getPlayer(p).getData().addStat("crafting.tools", recipeAmount); } - xp(e.getBlock().getLocation(), getConfig().furnaceBaseXP + (getValue(e.getResult()) * getConfig().furnaceValueXPMultiplier), getConfig().furnaceXPRadius, getConfig().furnaceXPDuration); - } + if (typeName.contains("_HELMET") || typeName.contains("_CHESTPLATE") || typeName.contains("_LEGGINGS") || typeName.contains("_BOOTS")) { + getPlayer(p).getData().addStat("crafting.armor", recipeAmount); + } + xp(p, v + getConfig().baseCraftingXP); + } + }); + } - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); + @EventHandler(priority = EventPriority.MONITOR) + public void on(FurnaceSmeltEvent e) { + if (shouldReturnForWorld(e.getBlock().getWorld(), this)) { + return; } + xp(e.getBlock().getLocation(), getConfig().furnaceBaseXP + (getValue(e.getResult()) * getConfig().furnaceValueXPMultiplier), getConfig().furnaceXPRadius, getConfig().furnaceXPDuration); + } + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } - private boolean isValidCraftEvent(CraftItemEvent e) { - Player p = (Player) e.getWhoClicked(); - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return false; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + private boolean isValidCraftEvent(CraftItemEvent e) { + Player p = (Player) e.getWhoClicked(); - ItemStack result = e.getInventory().getResult(); - ItemStack cursor = e.getCursor(); + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return false; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - return result != null && result.getAmount() > 0 && (cursor == null || cursor.getAmount() < 64); - } + ItemStack result = e.getInventory().getResult(); + ItemStack cursor = e.getCursor(); + + return result != null && result.getAmount() > 0 && (cursor == null || cursor.getAmount() < 64); + } - private int calculateRecipeAmount(CraftItemEvent e) { - ItemStack test = e.getRecipe().getResult().clone(); - int recipeAmount = e.getInventory().getResult().getAmount(); - switch (e.getClick()) { - case NUMBER_KEY -> { - if (e.getWhoClicked().getInventory().getItem(e.getHotbarButton()) != null) { - recipeAmount = 0; - } - } - case DROP, CONTROL_DROP -> { - ItemStack cursor = e.getCursor(); - if (!(cursor == null || cursor.getType().isAir())) { - recipeAmount = 0; - } - } - case SHIFT_RIGHT, SHIFT_LEFT -> { - if (recipeAmount == 0) { - break; - } - int maxCraftable = getMaxCraftAmount(e.getInventory()); - int capacity = fits(test, e.getView().getBottomInventory()); - if (capacity < maxCraftable) { - maxCraftable = ((capacity + recipeAmount - 1) / recipeAmount) * recipeAmount; - } - recipeAmount = maxCraftable; - } - default -> { - } + private int calculateRecipeAmount(CraftItemEvent e) { + ItemStack test = e.getRecipe().getResult().clone(); + int recipeAmount = e.getInventory().getResult().getAmount(); + switch (e.getClick()) { + case NUMBER_KEY -> { + if (e.getWhoClicked().getInventory().getItem(e.getHotbarButton()) != null) { + recipeAmount = 0; + } + } + case DROP, CONTROL_DROP -> { + ItemStack cursor = e.getCursor(); + if (!(cursor == null || cursor.getType().isAir())) { + recipeAmount = 0; + } + } + case SHIFT_RIGHT, SHIFT_LEFT -> { + if (recipeAmount == 0) { + break; + } + int maxCraftable = getMaxCraftAmount(e.getInventory()); + int capacity = fits(test, e.getView().getBottomInventory()); + if (capacity < maxCraftable) { + maxCraftable = ((capacity + recipeAmount - 1) / recipeAmount) * recipeAmount; } - return recipeAmount; + recipeAmount = maxCraftable; + } + default -> { + } } + return recipeAmount; + } - private int fits(ItemStack stack, Inventory inv) { - ItemStack[] contents = inv.getContents(); - int result = 0; + private int fits(ItemStack stack, Inventory inv) { + ItemStack[] contents = inv.getContents(); + int result = 0; - for (ItemStack is : contents) { - if (is == null) { - result += stack.getMaxStackSize(); - } else if (is.isSimilar(stack)) { - result += Math.max(stack.getMaxStackSize() - is.getAmount(), 0); - } - } - - return result; + for (ItemStack is : contents) { + if (is == null) { + result += stack.getMaxStackSize(); + } else if (is.isSimilar(stack)) { + result += Math.max(stack.getMaxStackSize() - is.getAmount(), 0); + } } - private int getMaxCraftAmount(CraftingInventory inv) { - if (inv.getResult() == null) { - return 0; - } + return result; + } - int resultCount = inv.getResult().getAmount(); - int materialCount = Integer.MAX_VALUE; + private int getMaxCraftAmount(CraftingInventory inv) { + if (inv.getResult() == null) { + return 0; + } - for (ItemStack is : inv.getMatrix()) { - if (is != null && is.getAmount() < materialCount) { - materialCount = is.getAmount(); - } - } + int resultCount = inv.getResult().getAmount(); + int materialCount = Integer.MAX_VALUE; - return resultCount * materialCount; + for (ItemStack is : inv.getMatrix()) { + if (is != null && is.getAmount() < materialCount) { + materialCount = is.getAmount(); + } } + return resultCount * materialCount; + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&e"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Base XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double furnaceBaseXP = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double furnaceValueXPMultiplier = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPRadius for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - int furnaceXPRadius = 32; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 3000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPDuration for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long furnaceXPDuration = 10000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crafting Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double craftingValueXPMultiplier = 2.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crafting XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double baseCraftingXP = 3.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Craft1k Reward for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeCraft1kReward = 1200; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&e"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Base XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double furnaceBaseXP = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double furnaceValueXPMultiplier = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPRadius for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int furnaceXPRadius = 32; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 3000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Furnace XPDuration for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long furnaceXPDuration = 10000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Crafting Value XPMultiplier for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double craftingValueXPMultiplier = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Base Crafting XP for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double baseCraftingXP = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Craft1k Reward for the Crafting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeCraft1kReward = 1200; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java index 0cbfc0448..70320c434 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillDiscovery.java @@ -54,368 +54,368 @@ import java.util.Map; public class SkillDiscovery extends SimpleSkill { - public SkillDiscovery() { - super("discovery", Localizer.dLocalize("skill.discovery.icon")); - registerConfiguration(Config.class); - setColor(C.AQUA); - setDescription(Localizer.dLocalize("skill.discovery.description")); - setDisplayName(Localizer.dLocalize("skill.discovery.name")); - setInterval(500); - setIcon(Material.FILLED_MAP); - registerAdaptation(new DiscoveryUnity()); - registerAdaptation(new DiscoveryArmor()); - registerAdaptation(new DiscoveryXpResist()); - registerAdaptation(new DiscoveryVillagerAtt()); - registerAdaptation(new DiscoveryBetterMending()); - registerAdaptation(new DiscoveryArchaeologist()); - registerAdaptation(new DiscoveryCartographerPulse()); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ITEM_FRAME).key("challenge_discover_items_50") - .title(Localizer.dLocalize("advancement.challenge_discover_items_50.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_items_50.description")) - .model(CustomModel.get(Material.ITEM_FRAME, "advancement", "discovery", "challenge_discover_items_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHEST) - .key("challenge_discover_items_250") - .title(Localizer.dLocalize("advancement.challenge_discover_items_250.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_items_250.description")) - .model(CustomModel.get(Material.CHEST, "advancement", "discovery", "challenge_discover_items_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discover_items_50", "discovery.items", 50, 500); - registerMilestone("challenge_discover_items_250", "discovery.items", 250, 2500); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GRASS_BLOCK).key("challenge_discover_blocks_50") - .title(Localizer.dLocalize("advancement.challenge_discover_blocks_50.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_blocks_50.description")) - .model(CustomModel.get(Material.GRASS_BLOCK, "advancement", "discovery", "challenge_discover_blocks_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.STONE_BRICKS) - .key("challenge_discover_blocks_250") - .title(Localizer.dLocalize("advancement.challenge_discover_blocks_250.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_blocks_250.description")) - .model(CustomModel.get(Material.STONE_BRICKS, "advancement", "discovery", "challenge_discover_blocks_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discover_blocks_50", "discovery.blocks", 50, 500); - registerMilestone("challenge_discover_blocks_250", "discovery.blocks", 250, 2500); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EGG).key("challenge_discover_mobs_25") - .title(Localizer.dLocalize("advancement.challenge_discover_mobs_25.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_mobs_25.description")) - .model(CustomModel.get(Material.EGG, "advancement", "discovery", "challenge_discover_mobs_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPAWNER) - .key("challenge_discover_mobs_75") - .title(Localizer.dLocalize("advancement.challenge_discover_mobs_75.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_mobs_75.description")) - .model(CustomModel.get(Material.SPAWNER, "advancement", "discovery", "challenge_discover_mobs_75")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discover_mobs_25", "discovery.mobs", 25, 500); - registerMilestone("challenge_discover_mobs_75", "discovery.mobs", 75, 2500); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.MAP).key("challenge_discover_biomes_10") - .title(Localizer.dLocalize("advancement.challenge_discover_biomes_10.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_biomes_10.description")) - .model(CustomModel.get(Material.MAP, "advancement", "discovery", "challenge_discover_biomes_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FILLED_MAP) - .key("challenge_discover_biomes_40") - .title(Localizer.dLocalize("advancement.challenge_discover_biomes_40.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_biomes_40.description")) - .model(CustomModel.get(Material.FILLED_MAP, "advancement", "discovery", "challenge_discover_biomes_40")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discover_biomes_10", "discovery.biomes", 10, 500); - registerMilestone("challenge_discover_biomes_40", "discovery.biomes", 40, 2500); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.APPLE).key("challenge_discover_foods_10") - .title(Localizer.dLocalize("advancement.challenge_discover_foods_10.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_foods_10.description")) - .model(CustomModel.get(Material.APPLE, "advancement", "discovery", "challenge_discover_foods_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_discover_foods_30") - .title(Localizer.dLocalize("advancement.challenge_discover_foods_30.title")) - .description(Localizer.dLocalize("advancement.challenge_discover_foods_30.description")) - .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "discovery", "challenge_discover_foods_30")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_discover_foods_10", "discovery.foods", 10, 500); - registerMilestone("challenge_discover_foods_30", "discovery.foods", 30, 2500); + public SkillDiscovery() { + super("discovery", Localizer.dLocalize("skill.discovery.icon")); + registerConfiguration(Config.class); + setColor(C.AQUA); + setDescription(Localizer.dLocalize("skill.discovery.description")); + setDisplayName(Localizer.dLocalize("skill.discovery.name")); + setInterval(500); + setIcon(Material.FILLED_MAP); + registerAdaptation(new DiscoveryUnity()); + registerAdaptation(new DiscoveryArmor()); + registerAdaptation(new DiscoveryXpResist()); + registerAdaptation(new DiscoveryVillagerAtt()); + registerAdaptation(new DiscoveryBetterMending()); + registerAdaptation(new DiscoveryArchaeologist()); + registerAdaptation(new DiscoveryCartographerPulse()); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ITEM_FRAME).key("challenge_discover_items_50") + .title(Localizer.dLocalize("advancement.challenge_discover_items_50.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_items_50.description")) + .model(CustomModel.get(Material.ITEM_FRAME, "advancement", "discovery", "challenge_discover_items_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHEST) + .key("challenge_discover_items_250") + .title(Localizer.dLocalize("advancement.challenge_discover_items_250.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_items_250.description")) + .model(CustomModel.get(Material.CHEST, "advancement", "discovery", "challenge_discover_items_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discover_items_50", "discovery.items", 50, 500); + registerMilestone("challenge_discover_items_250", "discovery.items", 250, 2500); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GRASS_BLOCK).key("challenge_discover_blocks_50") + .title(Localizer.dLocalize("advancement.challenge_discover_blocks_50.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_blocks_50.description")) + .model(CustomModel.get(Material.GRASS_BLOCK, "advancement", "discovery", "challenge_discover_blocks_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.STONE_BRICKS) + .key("challenge_discover_blocks_250") + .title(Localizer.dLocalize("advancement.challenge_discover_blocks_250.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_blocks_250.description")) + .model(CustomModel.get(Material.STONE_BRICKS, "advancement", "discovery", "challenge_discover_blocks_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discover_blocks_50", "discovery.blocks", 50, 500); + registerMilestone("challenge_discover_blocks_250", "discovery.blocks", 250, 2500); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EGG).key("challenge_discover_mobs_25") + .title(Localizer.dLocalize("advancement.challenge_discover_mobs_25.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_mobs_25.description")) + .model(CustomModel.get(Material.EGG, "advancement", "discovery", "challenge_discover_mobs_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPAWNER) + .key("challenge_discover_mobs_75") + .title(Localizer.dLocalize("advancement.challenge_discover_mobs_75.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_mobs_75.description")) + .model(CustomModel.get(Material.SPAWNER, "advancement", "discovery", "challenge_discover_mobs_75")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discover_mobs_25", "discovery.mobs", 25, 500); + registerMilestone("challenge_discover_mobs_75", "discovery.mobs", 75, 2500); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.MAP).key("challenge_discover_biomes_10") + .title(Localizer.dLocalize("advancement.challenge_discover_biomes_10.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_biomes_10.description")) + .model(CustomModel.get(Material.MAP, "advancement", "discovery", "challenge_discover_biomes_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FILLED_MAP) + .key("challenge_discover_biomes_40") + .title(Localizer.dLocalize("advancement.challenge_discover_biomes_40.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_biomes_40.description")) + .model(CustomModel.get(Material.FILLED_MAP, "advancement", "discovery", "challenge_discover_biomes_40")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discover_biomes_10", "discovery.biomes", 10, 500); + registerMilestone("challenge_discover_biomes_40", "discovery.biomes", 40, 2500); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.APPLE).key("challenge_discover_foods_10") + .title(Localizer.dLocalize("advancement.challenge_discover_foods_10.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_foods_10.description")) + .model(CustomModel.get(Material.APPLE, "advancement", "discovery", "challenge_discover_foods_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_discover_foods_30") + .title(Localizer.dLocalize("advancement.challenge_discover_foods_30.title")) + .description(Localizer.dLocalize("advancement.challenge_discover_foods_30.description")) + .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "discovery", "challenge_discover_foods_30")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_discover_foods_10", "discovery.foods", 10, 500); + registerMilestone("challenge_discover_foods_30", "discovery.foods", 30, 2500); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerChangedWorldEvent e) { + shouldReturnForPlayer(e.getPlayer(), () -> scheduleSeeWorld(e.getPlayer())); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractAtEntityEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> seeEntity(e.getPlayer(), e.getRightClicked())); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityPickupItemEvent e) { + if (e.getEntity() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> seeItem(p, e.getItem().getItemStack())); } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerChangedWorldEvent e) { - shouldReturnForPlayer(e.getPlayer(), () -> scheduleSeeWorld(e.getPlayer())); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractAtEntityEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> seeEntity(e.getPlayer(), e.getRightClicked())); - } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityPickupItemEvent e) { - if (e.getEntity() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> seeItem(p, e.getItem().getItemStack())); + @EventHandler(priority = EventPriority.MONITOR) + public void on(CraftItemEvent e) { + if (!(e.getWhoClicked() instanceof Player p)) return; + shouldReturnForPlayer(p, e, () -> { + try { + NamespacedKey key = (NamespacedKey) Recipe.class.getDeclaredMethod("getKey()").invoke(e.getRecipe()); + if (key != null) { + seeCraftedRecipe(p, key.toString()); } - - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(CraftItemEvent e) { - if (!(e.getWhoClicked() instanceof Player p)) return; - shouldReturnForPlayer(p, e, () -> { - try { - NamespacedKey key = (NamespacedKey) Recipe.class.getDeclaredMethod("getKey()").invoke(e.getRecipe()); - if (key != null) { - seeCraftedRecipe(p, key.toString()); - } - } catch (Throwable ex) { - Adapt.verbose("No recipe key found for " + e.getRecipe().getResult().getType().name() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - }); + } catch (Throwable ex) { + Adapt.verbose("No recipe key found for " + e.getRecipe().getResult().getType().name() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerItemConsumeEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> { + seeItem(e.getPlayer(), e.getItem()); + seeFood(e.getPlayer(), e.getItem().getType()); + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getClickedBlock() != null) { + seeBlock(e.getPlayer(), e.getClickedBlock().getBlockData(), e.getClickedBlock().getLocation()); + } + }); + + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerExpChangeEvent e) { + shouldReturnForPlayer(e.getPlayer(), () -> { + if (e.getAmount() > 0 && getLevel(e.getPlayer()) > 0) { + xp(e.getPlayer(), e.getAmount()); + } + }); + } + + private void scheduleSeeWorld(Player p) { + try { + J.runEntity(p, () -> seeWorld(p, p.getWorld()), 15); + } catch (Exception e) { + Adapt.error("Failed to discover world " + p.getWorld().getName()); } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerItemConsumeEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> { - seeItem(e.getPlayer(), e.getItem()); - seeFood(e.getPlayer(), e.getItem().getType()); - }); + } + + public void seeBlock(Player p, BlockData bd, Location l) { + Discovery d = getPlayer(p).getData().getSeenBlocks(); + if (d.isNewDiscovery(bd.getAsString())) { + xp(p, getConfig().discoverBlockBaseXP + (getValue(bd) * getConfig().discoverBlockValueXPMultiplier)); + getPlayer(p).getData().addStat("discovery.blocks", 1); + if (areParticlesEnabled()) { + p.spawnParticle(Particles.TOTEM, l.clone().add(0.5, 0.5, 0.5), 9, 0, 0, 0, 0.3); + } } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getClickedBlock() != null) { - seeBlock(e.getPlayer(), e.getClickedBlock().getBlockData(), e.getClickedBlock().getLocation()); - } - }); + seeItem(p, bd.getMaterial()); + } + public void seeItem(Player p, Material bd) { + Discovery d = getPlayer(p).getData().getSeenItems(); + if (d.isNewDiscovery(bd)) { + xp(p, getConfig().discoverItemBaseXP + (getValue(bd) * getConfig().discoverItemValueXPMultiplier)); + getPlayer(p).getData().addStat("discovery.items", 1); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerExpChangeEvent e) { - shouldReturnForPlayer(e.getPlayer(), () -> { - if (e.getAmount() > 0 && getLevel(e.getPlayer()) > 0) { - xp(e.getPlayer(), e.getAmount()); - } - }); - } + public void seeItem(Player p, ItemStack bd) { + seeItem(p, bd.getType()); + Map m = bd.getEnchantments(); - private void scheduleSeeWorld(Player p) { - try { - J.runEntity(p, () -> seeWorld(p, p.getWorld()), 15); - } catch (Exception e) { - Adapt.error("Failed to discover world " + p.getWorld().getName()); - } + for (Enchantment i : m.keySet()) { + seeEnchant(p, i, m.get(i)); } + } - public void seeBlock(Player p, BlockData bd, Location l) { - Discovery d = getPlayer(p).getData().getSeenBlocks(); - if (d.isNewDiscovery(bd.getAsString())) { - xp(p, getConfig().discoverBlockBaseXP + (getValue(bd) * getConfig().discoverBlockValueXPMultiplier)); - getPlayer(p).getData().addStat("discovery.blocks", 1); - if (areParticlesEnabled()) { - p.spawnParticle(Particles.TOTEM, l.clone().add(0.5, 0.5, 0.5), 9, 0, 0, 0, 0.3); - } - } - - seeItem(p, bd.getMaterial()); + public void seeCraftedRecipe(Player p, String key) { + Discovery d = getPlayer(p).getData().getSeenRecipes(); + if (d.isNewDiscovery(key)) { + xp(p, getConfig().discoverRecipeBaseXP); } + } - public void seeItem(Player p, Material bd) { - Discovery d = getPlayer(p).getData().getSeenItems(); - if (d.isNewDiscovery(bd)) { - xp(p, getConfig().discoverItemBaseXP + (getValue(bd) * getConfig().discoverItemValueXPMultiplier)); - getPlayer(p).getData().addStat("discovery.items", 1); - } + public void seeFood(Player p, Material bd) { + Discovery d = getPlayer(p).getData().getSeenFoods(); + if (d.isNewDiscovery(bd)) { + xp(p, getConfig().discoverFoodTypeXP); + getPlayer(p).getData().addStat("discovery.foods", 1); } + } - public void seeItem(Player p, ItemStack bd) { - seeItem(p, bd.getType()); - Map m = bd.getEnchantments(); - - for (Enchantment i : m.keySet()) { - seeEnchant(p, i, m.get(i)); - } + public void seeEntity(Player p, Entity bd) { + Discovery d = getPlayer(p).getData().getSeenMobs(); + if (d.isNewDiscovery(bd.getType())) { + xp(p, getConfig().discoverEntityTypeXP); + getPlayer(p).getData().addStat("discovery.mobs", 1); } - public void seeCraftedRecipe(Player p, String key) { - Discovery d = getPlayer(p).getData().getSeenRecipes(); - if (d.isNewDiscovery(key)) { - xp(p, getConfig().discoverRecipeBaseXP); - } + if (bd instanceof Player) { + seePlayer(p, (Player) bd); } - public void seeFood(Player p, Material bd) { - Discovery d = getPlayer(p).getData().getSeenFoods(); - if (d.isNewDiscovery(bd)) { - xp(p, getConfig().discoverFoodTypeXP); - getPlayer(p).getData().addStat("discovery.foods", 1); - } + if (bd instanceof LivingEntity) { + for (PotionEffect i : ((LivingEntity) bd).getActivePotionEffects()) { + seePotionEffect(p, i); + } } + } - public void seeEntity(Player p, Entity bd) { - Discovery d = getPlayer(p).getData().getSeenMobs(); - if (d.isNewDiscovery(bd.getType())) { - xp(p, getConfig().discoverEntityTypeXP); - getPlayer(p).getData().addStat("discovery.mobs", 1); - } - - if (bd instanceof Player) { - seePlayer(p, (Player) bd); - } - - if (bd instanceof LivingEntity) { - for (PotionEffect i : ((LivingEntity) bd).getActivePotionEffects()) { - seePotionEffect(p, i); - } - } - } - - public void seePlayer(Player p, Player bd) { - Discovery d = getPlayer(p).getData().getSeenPeople(); - if (d.isNewDiscovery(bd.getUniqueId().toString())) { - xp(p, getConfig().discoverPlayerXP); - } + public void seePlayer(Player p, Player bd) { + Discovery d = getPlayer(p).getData().getSeenPeople(); + if (d.isNewDiscovery(bd.getUniqueId().toString())) { + xp(p, getConfig().discoverPlayerXP); } + } - public void seeEnchant(Player p, Enchantment bd, int level) { - Discovery d = getPlayer(p).getData().getSeenEnchants(); - if (d.isNewDiscovery(bd.getName() + " " + Form.toRoman(level))) { - xp(p, getConfig().discoverEnchantBaseXP + Math.min(getConfig().discoverEnchantMaxXP, level * getConfig().discoverEnchantLevelXPMultiplier)); - } - } - - public void seeWorld(Player p, World world) { - Discovery d = getPlayer(p).getData().getSeenWorlds(); - if (d.isNewDiscovery(world.getName() + "-" + world.getSeed())) { - xp(p, getConfig().discoverWorldXP); - } - - seeEnvironment(p, world.getEnvironment()); + public void seeEnchant(Player p, Enchantment bd, int level) { + Discovery d = getPlayer(p).getData().getSeenEnchants(); + if (d.isNewDiscovery(bd.getName() + " " + Form.toRoman(level))) { + xp(p, getConfig().discoverEnchantBaseXP + Math.min(getConfig().discoverEnchantMaxXP, level * getConfig().discoverEnchantLevelXPMultiplier)); } + } - public void seeEnvironment(Player p, World.Environment world) { - Discovery d = getPlayer(p).getData().getSeenEnvironments(); - if (d.isNewDiscovery(world)) { - xp(p, getConfig().discoverEnvironmentXP); - } + public void seeWorld(Player p, World world) { + Discovery d = getPlayer(p).getData().getSeenWorlds(); + if (d.isNewDiscovery(world.getName() + "-" + world.getSeed())) { + xp(p, getConfig().discoverWorldXP); } - public void seePotionEffect(Player p, PotionEffect e) { - Discovery d = getPlayer(p).getData().getSeenPotionEffects(); - if (d.isNewDiscovery(e.getType().getName() + " " + Form.toRoman(e.getAmplifier()).trim())) { - xp(p, getConfig().discoverPotionXP); - } - } + seeEnvironment(p, world.getEnvironment()); + } - public void seeBiome(Player p, Biome e) { - Discovery d = getPlayer(p).getData().getSeenBiomes(); - if (d.isNewDiscovery(e.getKey().toString())) { - xp(p, getConfig().discoverBiomeXP); - getPlayer(p).getData().addStat("discovery.biomes", 1); - } + public void seeEnvironment(Player p, World.Environment world) { + Discovery d = getPlayer(p).getData().getSeenEnvironments(); + if (d.isNewDiscovery(world)) { + xp(p, getConfig().discoverEnvironmentXP); } + } - @Override - public void onTick() { - if (!this.isEnabled()) return; - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> { - checkStatTrackers(adaptPlayer); - seeTargetBlock(i); - }); - } + public void seePotionEffect(Player p, PotionEffect e) { + Discovery d = getPlayer(p).getData().getSeenPotionEffects(); + if (d.isNewDiscovery(e.getType().getName() + " " + Form.toRoman(e.getAmplifier()).trim())) { + xp(p, getConfig().discoverPotionXP); } + } - private void seeTargetBlock(Player i) { - try { - Block b = i.getTargetBlockExact(5, FluidCollisionMode.NEVER); - if (b != null) { - seeBlock(i, b.getBlockData(), b.getLocation()); - seeBiome(i, b.getBiome()); - } - } catch (Throwable ex) { - Adapt.verbose("Failed to get target block for " + i.getName() + ": " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + public void seeBiome(Player p, Biome e) { + Discovery d = getPlayer(p).getData().getSeenBiomes(); + if (d.isNewDiscovery(e.getKey().toString())) { + xp(p, getConfig().discoverBiomeXP); + getPlayer(p).getData().addStat("discovery.biomes", 1); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; + } + + @Override + public void onTick() { + if (!this.isEnabled()) return; + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> { + checkStatTrackers(adaptPlayer); + seeTargetBlock(i); + }); } - - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&b"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery skill.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Biome XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverBiomeXP = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Potion XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverPotionXP = 36; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Entity Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverEntityTypeXP = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Food Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverFoodTypeXP = 75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Player XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverPlayerXP = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Environment XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverEnvironmentXP = 750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover World XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverWorldXP = 750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Max XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverEnchantMaxXP = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Level XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverEnchantLevelXPMultiplier = 52; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverEnchantBaseXP = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverItemBaseXP = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Recipe Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverRecipeBaseXP = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverItemValueXPMultiplier = 1; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverBlockBaseXP = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double discoverBlockValueXPMultiplier = 0.333; + } + + private void seeTargetBlock(Player i) { + try { + Block b = i.getTargetBlockExact(5, FluidCollisionMode.NEVER); + if (b != null) { + seeBlock(i, b.getBlockData(), b.getLocation()); + seeBiome(i, b.getBiome()); + } + } catch (Throwable ex) { + Adapt.verbose("Failed to get target block for " + i.getName() + ": " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&b"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Discovery skill.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Biome XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverBiomeXP = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Potion XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverPotionXP = 36; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Entity Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverEntityTypeXP = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Food Type XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverFoodTypeXP = 75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Player XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverPlayerXP = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Environment XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverEnvironmentXP = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover World XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverWorldXP = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Max XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverEnchantMaxXP = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Level XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverEnchantLevelXPMultiplier = 52; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Enchant Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverEnchantBaseXP = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverItemBaseXP = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Recipe Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverRecipeBaseXP = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Item Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverItemValueXPMultiplier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Base XP for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverBlockBaseXP = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Discover Block Value XPMultiplier for the Discovery skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double discoverBlockValueXPMultiplier = 0.333; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java index ebf7d459a..21a363c3e 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillEnchanting.java @@ -39,204 +39,204 @@ import java.util.UUID; public class SkillEnchanting extends SimpleSkill { - private final Map cooldowns; - - public SkillEnchanting() { - super("enchanting", Localizer.dLocalize("skill.enchanting.icon")); - registerConfiguration(Config.class); - setColor(C.LIGHT_PURPLE); - setDescription(Localizer.dLocalize("skill.enchanting.description")); - setDisplayName(Localizer.dLocalize("skill.enchanting.name")); - setInterval(3909); - setIcon(Material.KNOWLEDGE_BOOK); - cooldowns = new HashMap<>(); - registerAdaptation(new EnchantingQuickEnchant()); - registerAdaptation(new EnchantingLapisReturn()); - registerAdaptation(new EnchantingXPReturn()); // - registerAdaptation(new EnchantingAnvilSavant()); - registerAdaptation(new EnchantingOfferReroll()); - registerAdaptation(new EnchantingBookshelfAttunement()); - registerAdaptation(new EnchantingGrindstoneRecovery()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CRAFTING_TABLE).key("challenge_enchant_1k") - .title(Localizer.dLocalize("advancement.challenge_enchant_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_1k.description")) - .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "enchanting", "challenge_enchant_1k")) + private final Map cooldowns; + + public SkillEnchanting() { + super("enchanting", Localizer.dLocalize("skill.enchanting.icon")); + registerConfiguration(Config.class); + setColor(C.LIGHT_PURPLE); + setDescription(Localizer.dLocalize("skill.enchanting.description")); + setDisplayName(Localizer.dLocalize("skill.enchanting.name")); + setInterval(3909); + setIcon(Material.KNOWLEDGE_BOOK); + cooldowns = new HashMap<>(); + registerAdaptation(new EnchantingQuickEnchant()); + registerAdaptation(new EnchantingLapisReturn()); + registerAdaptation(new EnchantingXPReturn()); // + registerAdaptation(new EnchantingAnvilSavant()); + registerAdaptation(new EnchantingOfferReroll()); + registerAdaptation(new EnchantingBookshelfAttunement()); + registerAdaptation(new EnchantingGrindstoneRecovery()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CRAFTING_TABLE).key("challenge_enchant_1k") + .title(Localizer.dLocalize("advancement.challenge_enchant_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_1k.description")) + .model(CustomModel.get(Material.CRAFTING_TABLE, "advancement", "enchanting", "challenge_enchant_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.KNOWLEDGE_BOOK) + .key("challenge_enchant_5k") + .title(Localizer.dLocalize("advancement.challenge_enchant_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_5k.description")) + .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.KNOWLEDGE_BOOK) + .key("challenge_enchant_50k") + .title(Localizer.dLocalize("advancement.challenge_enchant_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_50k.description")) + .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.KNOWLEDGE_BOOK) + .key("challenge_enchant_500k") + .title(Localizer.dLocalize("advancement.challenge_enchant_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_500k.description")) + .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.KNOWLEDGE_BOOK) - .key("challenge_enchant_5k") - .title(Localizer.dLocalize("advancement.challenge_enchant_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_5k.description")) - .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.KNOWLEDGE_BOOK) - .key("challenge_enchant_50k") - .title(Localizer.dLocalize("advancement.challenge_enchant_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_50k.description")) - .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.KNOWLEDGE_BOOK) - .key("challenge_enchant_500k") - .title(Localizer.dLocalize("advancement.challenge_enchant_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_500k.description")) - .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.KNOWLEDGE_BOOK) - .key("challenge_enchant_5m") - .title(Localizer.dLocalize("advancement.challenge_enchant_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_5m.description")) - .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_enchant_1k", "enchanted.items", 1000, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_5k", "enchanted.items", 5000, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_50k", "enchanted.items", 50000, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_500k", "enchanted.items", 500000, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_5m", "enchanted.items", 5000000, getConfig().challengeEnchantReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EXPERIENCE_BOTTLE) - .key("challenge_enchant_power_100") - .title(Localizer.dLocalize("advancement.challenge_enchant_power_100.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_power_100.description")) - .model(CustomModel.get(Material.EXPERIENCE_BOTTLE, "advancement", "enchanting", "challenge_enchant_power_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTING_TABLE) - .key("challenge_enchant_power_1k") - .title(Localizer.dLocalize("advancement.challenge_enchant_power_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_power_1k.description")) - .model(CustomModel.get(Material.ENCHANTING_TABLE, "advancement", "enchanting", "challenge_enchant_power_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchant_power_100", "enchanted.power", 100, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_power_1k", "enchanted.power", 1000, getConfig().challengeEnchantReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LAPIS_LAZULI) - .key("challenge_enchant_levels_1k") - .title(Localizer.dLocalize("advancement.challenge_enchant_levels_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_levels_1k.description")) - .model(CustomModel.get(Material.LAPIS_LAZULI, "advancement", "enchanting", "challenge_enchant_levels_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.LAPIS_BLOCK) - .key("challenge_enchant_levels_10k") - .title(Localizer.dLocalize("advancement.challenge_enchant_levels_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_levels_10k.description")) - .model(CustomModel.get(Material.LAPIS_BLOCK, "advancement", "enchanting", "challenge_enchant_levels_10k")) + .key("challenge_enchant_5m") + .title(Localizer.dLocalize("advancement.challenge_enchant_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_5m.description")) + .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "enchanting", "challenge_enchant_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_enchant_levels_1k", "enchanted.levels.spent", 1000, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_levels_10k", "enchanted.levels.spent", 10000, getConfig().challengeEnchantReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BOOKSHELF) - .key("challenge_enchant_high_25") - .title(Localizer.dLocalize("advancement.challenge_enchant_high_25.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_high_25.description")) - .model(CustomModel.get(Material.BOOKSHELF, "advancement", "enchanting", "challenge_enchant_high_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTED_BOOK) - .key("challenge_enchant_high_250") - .title(Localizer.dLocalize("advancement.challenge_enchant_high_250.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_high_250.description")) - .model(CustomModel.get(Material.ENCHANTED_BOOK, "advancement", "enchanting", "challenge_enchant_high_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchant_high_25", "enchanting.high.level", 25, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_high_250", "enchanting.high.level", 250, getConfig().challengeEnchantReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.EXPERIENCE_BOTTLE) - .key("challenge_enchant_total_500") - .title(Localizer.dLocalize("advancement.challenge_enchant_total_500.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_total_500.description")) - .model(CustomModel.get(Material.EXPERIENCE_BOTTLE, "advancement", "enchanting", "challenge_enchant_total_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DRAGON_BREATH) - .key("challenge_enchant_total_5k") - .title(Localizer.dLocalize("advancement.challenge_enchant_total_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_enchant_total_5k.description")) - .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "enchanting", "challenge_enchant_total_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_enchant_total_500", "enchanting.total.levels", 500, getConfig().challengeEnchantReward); - registerMilestone("challenge_enchant_total_5k", "enchanting.total.levels", 5000, getConfig().challengeEnchantReward * 2); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(EnchantItemEvent e) { - Player p = e.getEnchanter(); - shouldReturnForPlayer(p, e, () -> { - handleEnchantItemEvent(p, e); - }); - - } - - private void handleEnchantItemEvent(Player p, EnchantItemEvent e) { - AdaptPlayer adaptPlayer = getPlayer(p); - adaptPlayer.getData().addStat("enchanted.items", 1); - adaptPlayer.getData().addStat("enchanted.power", e.getEnchantsToAdd().values().stream().mapToInt(i -> i).sum()); - adaptPlayer.getData().addStat("enchanted.levels.spent", e.getExpLevelCost()); - if (e.getExpLevelCost() >= 30) { - adaptPlayer.getData().addStat("enchanting.high.level", 1); - } - adaptPlayer.getData().addStat("enchanting.total.levels", e.getExpLevelCost()); - - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(p, getConfig().enchantPowerXPMultiplier * e.getEnchantsToAdd().values().stream().mapToInt((i) -> i).sum()); - } - - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_enchant_1k", "enchanted.items", 1000, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_5k", "enchanted.items", 5000, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_50k", "enchanted.items", 50000, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_500k", "enchanted.items", 500000, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_5m", "enchanted.items", 5000000, getConfig().challengeEnchantReward); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EXPERIENCE_BOTTLE) + .key("challenge_enchant_power_100") + .title(Localizer.dLocalize("advancement.challenge_enchant_power_100.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_power_100.description")) + .model(CustomModel.get(Material.EXPERIENCE_BOTTLE, "advancement", "enchanting", "challenge_enchant_power_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTING_TABLE) + .key("challenge_enchant_power_1k") + .title(Localizer.dLocalize("advancement.challenge_enchant_power_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_power_1k.description")) + .model(CustomModel.get(Material.ENCHANTING_TABLE, "advancement", "enchanting", "challenge_enchant_power_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchant_power_100", "enchanted.power", 100, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_power_1k", "enchanted.power", 1000, getConfig().challengeEnchantReward * 2); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LAPIS_LAZULI) + .key("challenge_enchant_levels_1k") + .title(Localizer.dLocalize("advancement.challenge_enchant_levels_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_levels_1k.description")) + .model(CustomModel.get(Material.LAPIS_LAZULI, "advancement", "enchanting", "challenge_enchant_levels_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LAPIS_BLOCK) + .key("challenge_enchant_levels_10k") + .title(Localizer.dLocalize("advancement.challenge_enchant_levels_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_levels_10k.description")) + .model(CustomModel.get(Material.LAPIS_BLOCK, "advancement", "enchanting", "challenge_enchant_levels_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchant_levels_1k", "enchanted.levels.spent", 1000, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_levels_10k", "enchanted.levels.spent", 10000, getConfig().challengeEnchantReward * 2); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BOOKSHELF) + .key("challenge_enchant_high_25") + .title(Localizer.dLocalize("advancement.challenge_enchant_high_25.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_high_25.description")) + .model(CustomModel.get(Material.BOOKSHELF, "advancement", "enchanting", "challenge_enchant_high_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTED_BOOK) + .key("challenge_enchant_high_250") + .title(Localizer.dLocalize("advancement.challenge_enchant_high_250.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_high_250.description")) + .model(CustomModel.get(Material.ENCHANTED_BOOK, "advancement", "enchanting", "challenge_enchant_high_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchant_high_25", "enchanting.high.level", 25, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_high_250", "enchanting.high.level", 250, getConfig().challengeEnchantReward * 2); + + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EXPERIENCE_BOTTLE) + .key("challenge_enchant_total_500") + .title(Localizer.dLocalize("advancement.challenge_enchant_total_500.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_total_500.description")) + .model(CustomModel.get(Material.EXPERIENCE_BOTTLE, "advancement", "enchanting", "challenge_enchant_total_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DRAGON_BREATH) + .key("challenge_enchant_total_5k") + .title(Localizer.dLocalize("advancement.challenge_enchant_total_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_enchant_total_5k.description")) + .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "enchanting", "challenge_enchant_total_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_enchant_total_500", "enchanting.total.levels", 500, getConfig().challengeEnchantReward); + registerMilestone("challenge_enchant_total_5k", "enchanting.total.levels", 5000, getConfig().challengeEnchantReward * 2); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EnchantItemEvent e) { + Player p = e.getEnchanter(); + shouldReturnForPlayer(p, e, () -> { + handleEnchantItemEvent(p, e); + }); + + } + + private void handleEnchantItemEvent(Player p, EnchantItemEvent e) { + AdaptPlayer adaptPlayer = getPlayer(p); + adaptPlayer.getData().addStat("enchanted.items", 1); + adaptPlayer.getData().addStat("enchanted.power", e.getEnchantsToAdd().values().stream().mapToInt(i -> i).sum()); + adaptPlayer.getData().addStat("enchanted.levels.spent", e.getExpLevelCost()); + if (e.getExpLevelCost() >= 30) { + adaptPlayer.getData().addStat("enchanting.high.level", 1); } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } - - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&d"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Enchant Power XPMultiplier for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double enchantPowerXPMultiplier = 45; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 5250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Enchant Reward for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeEnchantReward = 2500; + adaptPlayer.getData().addStat("enchanting.total.levels", e.getExpLevelCost()); + + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(p, getConfig().enchantPowerXPMultiplier * e.getEnchantsToAdd().values().stream().mapToInt((i) -> i).sum()); + } + + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&d"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Enchant Power XPMultiplier for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double enchantPowerXPMultiplier = 45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 5250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Enchant Reward for the Enchanting skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeEnchantReward = 2500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index 1ff2f6e39..aea9fa458 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -44,239 +44,239 @@ import java.util.UUID; public class SkillExcavation extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillExcavation() { - super("excavation", Localizer.dLocalize("skill.excavation.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.excavation.description")); - setDisplayName(Localizer.dLocalize("skill.excavation.name")); - setColor(C.YELLOW); - setInterval(5953); - setIcon(Material.DIAMOND_SHOVEL); - cooldowns = new HashMap<>(); - registerAdaptation(new ExcavationHaste()); - registerAdaptation(new ExcavationSpelunker()); - registerAdaptation(new ExcavationOmniTool()); - registerAdaptation(new ExcavationDropToInventory()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_SHOVEL).key("challenge_excavate_1k") - .title(Localizer.dLocalize("advancement.challenge_excavate_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavate_1k.description")) - .model(CustomModel.get(Material.WOODEN_SHOVEL, "advancement", "excavation", "challenge_excavate_1k")) + public SkillExcavation() { + super("excavation", Localizer.dLocalize("skill.excavation.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.excavation.description")); + setDisplayName(Localizer.dLocalize("skill.excavation.name")); + setColor(C.YELLOW); + setInterval(5953); + setIcon(Material.DIAMOND_SHOVEL); + cooldowns = new HashMap<>(); + registerAdaptation(new ExcavationHaste()); + registerAdaptation(new ExcavationSpelunker()); + registerAdaptation(new ExcavationOmniTool()); + registerAdaptation(new ExcavationDropToInventory()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_SHOVEL).key("challenge_excavate_1k") + .title(Localizer.dLocalize("advancement.challenge_excavate_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavate_1k.description")) + .model(CustomModel.get(Material.WOODEN_SHOVEL, "advancement", "excavation", "challenge_excavate_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.KNOWLEDGE_BOOK) + .key("challenge_excavate_5k") + .title(Localizer.dLocalize("advancement.challenge_excavate_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavate_5k.description")) + .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "excavation", "challenge_excavate_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() + .icon(Material.STONE_SHOVEL) + .key("challenge_excavate_50k") + .title(Localizer.dLocalize("advancement.challenge_excavate_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavate_50k.description")) + .model(CustomModel.get(Material.STONE_SHOVEL, "advancement", "excavation", "challenge_excavate_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.KNOWLEDGE_BOOK) - .key("challenge_excavate_5k") - .title(Localizer.dLocalize("advancement.challenge_excavate_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavate_5k.description")) - .model(CustomModel.get(Material.KNOWLEDGE_BOOK, "advancement", "excavation", "challenge_excavate_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.STONE_SHOVEL) - .key("challenge_excavate_50k") - .title(Localizer.dLocalize("advancement.challenge_excavate_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavate_50k.description")) - .model(CustomModel.get(Material.STONE_SHOVEL, "advancement", "excavation", "challenge_excavate_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.IRON_SHOVEL) - .key("challenge_excavate_500k") - .title(Localizer.dLocalize("advancement.challenge_excavate_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_excavate_500k.description")) - .model(CustomModel.get(Material.IRON_SHOVEL, "advancement", "excavation", "challenge_excavate_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SHOVEL) - .key("challenge_excavate_5m") - .title(Localizer.dLocalize("advancement.challenge_excavate_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_excavate_5m.description")) - .model(CustomModel.get(Material.DIAMOND_SHOVEL, "advancement", "excavation", "challenge_excavate_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) - .build()) - .build()); - registerMilestone("challenge_excavate_1k", "excavation.blocks.broken", 1000, getConfig().challengeExcavationReward); - registerMilestone("challenge_excavate_5k", "excavation.blocks.broken", 5000, getConfig().challengeExcavationReward); - registerMilestone("challenge_excavate_50k", "excavation.blocks.broken", 50000, getConfig().challengeExcavationReward); - registerMilestone("challenge_enchant_500k", "excavation.blocks.broken", 500000, getConfig().challengeExcavationReward); - registerMilestone("challenge_excavate_5m", "excavation.blocks.broken", 5000000, getConfig().challengeExcavationReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_SHOVEL).key("challenge_dig_swing_500") - .title(Localizer.dLocalize("advancement.challenge_dig_swing_500.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_swing_500.description")) - .model(CustomModel.get(Material.WOODEN_SHOVEL, "advancement", "excavation", "challenge_dig_swing_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_SHOVEL) - .key("challenge_dig_swing_5k") - .title(Localizer.dLocalize("advancement.challenge_dig_swing_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_swing_5k.description")) - .model(CustomModel.get(Material.IRON_SHOVEL, "advancement", "excavation", "challenge_dig_swing_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_dig_swing_500", "excavation.swings", 500, getConfig().challengeExcavationReward); - registerMilestone("challenge_dig_swing_5k", "excavation.swings", 5000, getConfig().challengeExcavationReward * 2); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_SHOVEL).key("challenge_dig_damage_1k") - .title(Localizer.dLocalize("advancement.challenge_dig_damage_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_damage_1k.description")) - .model(CustomModel.get(Material.GOLDEN_SHOVEL, "advancement", "excavation", "challenge_dig_damage_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() + .icon(Material.IRON_SHOVEL) + .key("challenge_excavate_500k") + .title(Localizer.dLocalize("advancement.challenge_excavate_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavate_500k.description")) + .model(CustomModel.get(Material.IRON_SHOVEL, "advancement", "excavation", "challenge_excavate_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.DIAMOND_SHOVEL) - .key("challenge_dig_damage_10k") - .title(Localizer.dLocalize("advancement.challenge_dig_damage_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_damage_10k.description")) - .model(CustomModel.get(Material.DIAMOND_SHOVEL, "advancement", "excavation", "challenge_dig_damage_10k")) + .key("challenge_excavate_5m") + .title(Localizer.dLocalize("advancement.challenge_excavate_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_excavate_5m.description")) + .model(CustomModel.get(Material.DIAMOND_SHOVEL, "advancement", "excavation", "challenge_excavate_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .build()) - .build()); - registerMilestone("challenge_dig_damage_1k", "excavation.damage", 1000, getConfig().challengeExcavationReward); - registerMilestone("challenge_dig_damage_10k", "excavation.damage", 10000, getConfig().challengeExcavationReward * 2); + .build()) + .build()) + .build()) + .build()); + registerMilestone("challenge_excavate_1k", "excavation.blocks.broken", 1000, getConfig().challengeExcavationReward); + registerMilestone("challenge_excavate_5k", "excavation.blocks.broken", 5000, getConfig().challengeExcavationReward); + registerMilestone("challenge_excavate_50k", "excavation.blocks.broken", 50000, getConfig().challengeExcavationReward); + registerMilestone("challenge_enchant_500k", "excavation.blocks.broken", 500000, getConfig().challengeExcavationReward); + registerMilestone("challenge_excavate_5m", "excavation.blocks.broken", 5000000, getConfig().challengeExcavationReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CLAY_BALL).key("challenge_dig_value_5k") - .title(Localizer.dLocalize("advancement.challenge_dig_value_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_value_5k.description")) - .model(CustomModel.get(Material.CLAY_BALL, "advancement", "excavation", "challenge_dig_value_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BRICK) - .key("challenge_dig_value_50k") - .title(Localizer.dLocalize("advancement.challenge_dig_value_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_value_50k.description")) - .model(CustomModel.get(Material.BRICK, "advancement", "excavation", "challenge_dig_value_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_dig_value_5k", "excavation.blocks.value", 5000, getConfig().challengeExcavationReward); - registerMilestone("challenge_dig_value_50k", "excavation.blocks.value", 50000, getConfig().challengeExcavationReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_SHOVEL).key("challenge_dig_swing_500") + .title(Localizer.dLocalize("advancement.challenge_dig_swing_500.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_swing_500.description")) + .model(CustomModel.get(Material.WOODEN_SHOVEL, "advancement", "excavation", "challenge_dig_swing_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_SHOVEL) + .key("challenge_dig_swing_5k") + .title(Localizer.dLocalize("advancement.challenge_dig_swing_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_swing_5k.description")) + .model(CustomModel.get(Material.IRON_SHOVEL, "advancement", "excavation", "challenge_dig_swing_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_dig_swing_500", "excavation.swings", 500, getConfig().challengeExcavationReward); + registerMilestone("challenge_dig_swing_5k", "excavation.swings", 5000, getConfig().challengeExcavationReward * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GRAVEL).key("challenge_dig_gravel_500") - .title(Localizer.dLocalize("advancement.challenge_dig_gravel_500.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_gravel_500.description")) - .model(CustomModel.get(Material.GRAVEL, "advancement", "excavation", "challenge_dig_gravel_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FLINT) - .key("challenge_dig_gravel_5k") - .title(Localizer.dLocalize("advancement.challenge_dig_gravel_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_dig_gravel_5k.description")) - .model(CustomModel.get(Material.FLINT, "advancement", "excavation", "challenge_dig_gravel_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_dig_gravel_500", "excavation.gravel", 500, getConfig().challengeExcavationReward); - registerMilestone("challenge_dig_gravel_5k", "excavation.gravel", 5000, getConfig().challengeExcavationReward * 2); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_SHOVEL).key("challenge_dig_damage_1k") + .title(Localizer.dLocalize("advancement.challenge_dig_damage_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_damage_1k.description")) + .model(CustomModel.get(Material.GOLDEN_SHOVEL, "advancement", "excavation", "challenge_dig_damage_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SHOVEL) + .key("challenge_dig_damage_10k") + .title(Localizer.dLocalize("advancement.challenge_dig_damage_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_damage_10k.description")) + .model(CustomModel.get(Material.DIAMOND_SHOVEL, "advancement", "excavation", "challenge_dig_damage_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_dig_damage_1k", "excavation.damage", 1000, getConfig().challengeExcavationReward); + registerMilestone("challenge_dig_damage_10k", "excavation.damage", 10000, getConfig().challengeExcavationReward * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CLAY_BALL).key("challenge_dig_value_5k") + .title(Localizer.dLocalize("advancement.challenge_dig_value_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_value_5k.description")) + .model(CustomModel.get(Material.CLAY_BALL, "advancement", "excavation", "challenge_dig_value_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BRICK) + .key("challenge_dig_value_50k") + .title(Localizer.dLocalize("advancement.challenge_dig_value_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_value_50k.description")) + .model(CustomModel.get(Material.BRICK, "advancement", "excavation", "challenge_dig_value_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_dig_value_5k", "excavation.blocks.value", 5000, getConfig().challengeExcavationReward); + registerMilestone("challenge_dig_value_50k", "excavation.blocks.value", 50000, getConfig().challengeExcavationReward * 2); - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { - if (!getConfig().getXpForAttackingWithTools) { - return; - } - shouldReturnForPlayer(p, e, () -> handleEntityDamageByPlayer(p, e)); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GRAVEL).key("challenge_dig_gravel_500") + .title(Localizer.dLocalize("advancement.challenge_dig_gravel_500.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_gravel_500.description")) + .model(CustomModel.get(Material.GRAVEL, "advancement", "excavation", "challenge_dig_gravel_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FLINT) + .key("challenge_dig_gravel_5k") + .title(Localizer.dLocalize("advancement.challenge_dig_gravel_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_dig_gravel_5k.description")) + .model(CustomModel.get(Material.FLINT, "advancement", "excavation", "challenge_dig_gravel_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_dig_gravel_500", "excavation.gravel", 500, getConfig().challengeExcavationReward); + registerMilestone("challenge_dig_gravel_5k", "excavation.gravel", 5000, getConfig().challengeExcavationReward * 2); + } + + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { + if (!getConfig().getXpForAttackingWithTools) { + return; + } + shouldReturnForPlayer(p, e, () -> handleEntityDamageByPlayer(p, e)); } + } - private void handleEntityDamageByPlayer(Player p, EntityDamageByEntityEvent e) { - AdaptPlayer a = getPlayer(p); - ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); - if (isShovel(hand)) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - getPlayer(p).getData().addStat("excavation.swings", 1); - getPlayer(p).getData().addStat("excavation.damage", e.getDamage()); - xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().axeDamageXPMultiplier * e.getDamage()); - } + private void handleEntityDamageByPlayer(Player p, EntityDamageByEntityEvent e) { + AdaptPlayer a = getPlayer(p); + ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); + if (isShovel(hand)) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + getPlayer(p).getData().addStat("excavation.swings", 1); + getPlayer(p).getData().addStat("excavation.damage", e.getDamage()); + xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().axeDamageXPMultiplier * e.getDamage()); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, e, () -> { - if (isShovel(p.getInventory().getItemInMainHand())) { - handleBlockBreakWithShovel(p, e); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, e, () -> { + if (isShovel(p.getInventory().getItemInMainHand())) { + handleBlockBreakWithShovel(p, e); + } + }); - } + } - private void handleBlockBreakWithShovel(Player p, BlockBreakEvent e) { - getPlayer(p).getData().addStat("excavation.blocks.broken", 1); - getPlayer(p).getData().addStat("excavation.blocks.value", getValue(e.getBlock().getBlockData())); - Material blockType = e.getBlock().getType(); - if (blockType == Material.GRAVEL || blockType == Material.SAND || blockType == Material.RED_SAND - || blockType == Material.CLAY || blockType == Material.SOUL_SAND || blockType == Material.SOUL_SOIL) { - getPlayer(p).getData().addStat("excavation.gravel", 1); - } - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - double v = getValue(e.getBlock().getType()); - xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v)); + private void handleBlockBreakWithShovel(Player p, BlockBreakEvent e) { + getPlayer(p).getData().addStat("excavation.blocks.broken", 1); + getPlayer(p).getData().addStat("excavation.blocks.value", getValue(e.getBlock().getBlockData())); + Material blockType = e.getBlock().getType(); + if (blockType == Material.GRAVEL || blockType == Material.SAND || blockType == Material.RED_SAND + || blockType == Material.CLAY || blockType == Material.SOUL_SAND || blockType == Material.SOUL_SOIL) { + getPlayer(p).getData().addStat("excavation.gravel", 1); } + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + double v = getValue(e.getBlock().getType()); + xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v)); + } - public double getValue(Material type) { - double value = super.getValue(type) * getConfig().valueXPMultiplier; - value += Math.min(getConfig().maxHardnessBonus, type.getHardness()); - value += Math.min(getConfig().maxBlastResistanceBonus, type.getBlastResistance()); - return value; - } + public double getValue(Material type) { + double value = super.getValue(type) * getConfig().valueXPMultiplier; + value += Math.min(getConfig().maxHardnessBonus, type.getHardness()); + value += Math.min(getConfig().maxBlastResistanceBonus, type.getBlastResistance()); + return value; + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&e"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Excavation skill.", impact = "True enables this behavior and false disables it.") - boolean getXpForAttackingWithTools = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHardnessBonus = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBlastResistanceBonus = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Excavation Reward for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeExcavationReward = 1200; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double valueXPMultiplier = 0.6; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double axeDamageXPMultiplier = 4.0; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&e"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Excavation skill.", impact = "True enables this behavior and false disables it.") + boolean getXpForAttackingWithTools = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHardnessBonus = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBlastResistanceBonus = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Excavation Reward for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeExcavationReward = 1200; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Value XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double valueXPMultiplier = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Axe Damage XPMultiplier for the Excavation skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double axeDamageXPMultiplier = 4.0; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index 74c6658e4..8f9decc3c 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -47,314 +47,314 @@ import java.util.UUID; public class SkillHerbalism extends SimpleSkill { - private final Map cooldown = new HashMap<>(); + private final Map cooldown = new HashMap<>(); - public SkillHerbalism() { - super("herbalism", Localizer.dLocalize("skill.herbalism.icon")); - registerConfiguration(Config.class); - setColor(C.GREEN); - setInterval(3990); - setDescription(Localizer.dLocalize("skill.herbalism.description")); - setDisplayName(Localizer.dLocalize("skill.herbalism.name")); - setIcon(Material.WHEAT); - registerAdaptation(new HerbalismGrowthAura()); - registerAdaptation(new HerbalismReplant()); - registerAdaptation(new HerbalismHungryShield()); - registerAdaptation(new HerbalismHungryHippo()); - registerAdaptation(new HerbalismDropToInventory()); - registerAdaptation(new HerbalismLuck()); - registerAdaptation(new HerbalismMyconid()); - registerAdaptation(new HerbalismTerralid()); - registerAdaptation(new HerbalismCraftableMushroomBlocks()); - registerAdaptation(new HerbalismCraftableCobweb()); - registerAdaptation(new HerbalismSeedSower()); - registerAdaptation(new HerbalismCompostCascade()); - registerAdaptation(new HerbalismRootedFooting()); - registerAdaptation(new HerbalismBeeShepherd()); - registerAdaptation(new HerbalismSporeBloom()); - registerAdvancement(AdaptAdvancement.builder() + public SkillHerbalism() { + super("herbalism", Localizer.dLocalize("skill.herbalism.icon")); + registerConfiguration(Config.class); + setColor(C.GREEN); + setInterval(3990); + setDescription(Localizer.dLocalize("skill.herbalism.description")); + setDisplayName(Localizer.dLocalize("skill.herbalism.name")); + setIcon(Material.WHEAT); + registerAdaptation(new HerbalismGrowthAura()); + registerAdaptation(new HerbalismReplant()); + registerAdaptation(new HerbalismHungryShield()); + registerAdaptation(new HerbalismHungryHippo()); + registerAdaptation(new HerbalismDropToInventory()); + registerAdaptation(new HerbalismLuck()); + registerAdaptation(new HerbalismMyconid()); + registerAdaptation(new HerbalismTerralid()); + registerAdaptation(new HerbalismCraftableMushroomBlocks()); + registerAdaptation(new HerbalismCraftableCobweb()); + registerAdaptation(new HerbalismSeedSower()); + registerAdaptation(new HerbalismCompostCascade()); + registerAdaptation(new HerbalismRootedFooting()); + registerAdaptation(new HerbalismBeeShepherd()); + registerAdaptation(new HerbalismSporeBloom()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COOKED_BEEF) + .key("challenge_eat_100") + .title(Localizer.dLocalize("advancement.challenge_eat_100.title")) + .description(Localizer.dLocalize("advancement.challenge_eat_100.description")) + .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.COOKED_BEEF) + .key("challenge_eat_1000") + .title(Localizer.dLocalize("advancement.challenge_eat_1000.title")) + .description(Localizer.dLocalize("advancement.challenge_eat_1000.description")) + .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_1000")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() .icon(Material.COOKED_BEEF) - .key("challenge_eat_100") - .title(Localizer.dLocalize("advancement.challenge_eat_100.title")) - .description(Localizer.dLocalize("advancement.challenge_eat_100.description")) - .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_100")) + .key("challenge_eat_10000") + .title(Localizer.dLocalize("advancement.challenge_eat_10000.title")) + .description(Localizer.dLocalize("advancement.challenge_eat_10000.description")) + .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_10000")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.COOKED_BEEF) - .key("challenge_eat_1000") - .title(Localizer.dLocalize("advancement.challenge_eat_1000.title")) - .description(Localizer.dLocalize("advancement.challenge_eat_1000.description")) - .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_1000")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED).child(AdaptAdvancement.builder() - .icon(Material.COOKED_BEEF) - .key("challenge_eat_10000") - .title(Localizer.dLocalize("advancement.challenge_eat_10000.title")) - .description(Localizer.dLocalize("advancement.challenge_eat_10000.description")) - .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "challenge_eat_10000")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_eat_100", "food.eaten", 100, getConfig().challengeEat100Reward); - registerMilestone("challenge_eat_1000", "food.eaten", 1000, getConfig().challengeEat1kReward); - registerMilestone("challenge_eat_10000", "food.eaten", 10000, getConfig().challengeEat1kReward); + .build()) + .build()) + .build()); + registerMilestone("challenge_eat_100", "food.eaten", 100, getConfig().challengeEat100Reward); + registerMilestone("challenge_eat_1000", "food.eaten", 1000, getConfig().challengeEat1kReward); + registerMilestone("challenge_eat_10000", "food.eaten", 10000, getConfig().challengeEat1kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COOKED_BEEF) - .key("challenge_harvest_100") - .title(Localizer.dLocalize("advancement.challenge_harvest_100.title")) - .description(Localizer.dLocalize("advancement.challenge_harvest_100.description")) - .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "harvest_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.COOKED_BEEF) - .key("challenge_harvest_1000") - .title(Localizer.dLocalize("advancement.challenge_harvest_1000.title")) - .description(Localizer.dLocalize("advancement.challenge_harvest_1000.description")) - .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "harvest_1000")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_harvest_100", "harvest.blocks", 100, getConfig().challengeHarvest100Reward); - registerMilestone("challenge_harvest_1000", "harvest.blocks", 1000, getConfig().challengeHarvest1kReward); - - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WHEAT_SEEDS) - .key("challenge_plant_100") - .title(Localizer.dLocalize("advancement.challenge_plant_100.title")) - .description(Localizer.dLocalize("advancement.challenge_plant_100.description")) - .model(CustomModel.get(Material.WHEAT_SEEDS, "advancement", "herbalism", "challenge_plant_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BEETROOT_SEEDS) - .key("challenge_plant_1k") - .title(Localizer.dLocalize("advancement.challenge_plant_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_plant_1k.description")) - .model(CustomModel.get(Material.BEETROOT_SEEDS, "advancement", "herbalism", "challenge_plant_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_CARROT) - .key("challenge_plant_5k") - .title(Localizer.dLocalize("advancement.challenge_plant_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_plant_5k.description")) - .model(CustomModel.get(Material.GOLDEN_CARROT, "advancement", "herbalism", "challenge_plant_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_plant_100", "harvest.planted", 100, getConfig().challengePlant100Reward); - registerMilestone("challenge_plant_1k", "harvest.planted", 1000, getConfig().challengePlant1kReward); - registerMilestone("challenge_plant_5k", "harvest.planted", 5000, getConfig().challengePlant5kReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COOKED_BEEF) + .key("challenge_harvest_100") + .title(Localizer.dLocalize("advancement.challenge_harvest_100.title")) + .description(Localizer.dLocalize("advancement.challenge_harvest_100.description")) + .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "harvest_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.COOKED_BEEF) + .key("challenge_harvest_1000") + .title(Localizer.dLocalize("advancement.challenge_harvest_1000.title")) + .description(Localizer.dLocalize("advancement.challenge_harvest_1000.description")) + .model(CustomModel.get(Material.COOKED_BEEF, "advancement", "herbalism", "harvest_1000")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_harvest_100", "harvest.blocks", 100, getConfig().challengeHarvest100Reward); + registerMilestone("challenge_harvest_1000", "harvest.blocks", 1000, getConfig().challengeHarvest1kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.COMPOSTER) - .key("challenge_compost_50") - .title(Localizer.dLocalize("advancement.challenge_compost_50.title")) - .description(Localizer.dLocalize("advancement.challenge_compost_50.description")) - .model(CustomModel.get(Material.COMPOSTER, "advancement", "herbalism", "challenge_compost_50")) + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WHEAT_SEEDS) + .key("challenge_plant_100") + .title(Localizer.dLocalize("advancement.challenge_plant_100.title")) + .description(Localizer.dLocalize("advancement.challenge_plant_100.description")) + .model(CustomModel.get(Material.WHEAT_SEEDS, "advancement", "herbalism", "challenge_plant_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BEETROOT_SEEDS) + .key("challenge_plant_1k") + .title(Localizer.dLocalize("advancement.challenge_plant_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_plant_1k.description")) + .model(CustomModel.get(Material.BEETROOT_SEEDS, "advancement", "herbalism", "challenge_plant_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_CARROT) + .key("challenge_plant_5k") + .title(Localizer.dLocalize("advancement.challenge_plant_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_plant_5k.description")) + .model(CustomModel.get(Material.GOLDEN_CARROT, "advancement", "herbalism", "challenge_plant_5k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BONE_MEAL) - .key("challenge_compost_500") - .title(Localizer.dLocalize("advancement.challenge_compost_500.title")) - .description(Localizer.dLocalize("advancement.challenge_compost_500.description")) - .model(CustomModel.get(Material.BONE_MEAL, "advancement", "herbalism", "challenge_compost_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_compost_50", "harvest.composted", 50, getConfig().challengeCompost50Reward); - registerMilestone("challenge_compost_500", "harvest.composted", 500, getConfig().challengeCompost500Reward); + .build()) + .build()) + .build()); + registerMilestone("challenge_plant_100", "harvest.planted", 100, getConfig().challengePlant100Reward); + registerMilestone("challenge_plant_1k", "harvest.planted", 1000, getConfig().challengePlant1kReward); + registerMilestone("challenge_plant_5k", "harvest.planted", 5000, getConfig().challengePlant5kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SHEARS) - .key("challenge_shear_50") - .title(Localizer.dLocalize("advancement.challenge_shear_50.title")) - .description(Localizer.dLocalize("advancement.challenge_shear_50.description")) - .model(CustomModel.get(Material.SHEARS, "advancement", "herbalism", "challenge_shear_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WHITE_WOOL) - .key("challenge_shear_250") - .title(Localizer.dLocalize("advancement.challenge_shear_250.title")) - .description(Localizer.dLocalize("advancement.challenge_shear_250.description")) - .model(CustomModel.get(Material.WHITE_WOOL, "advancement", "herbalism", "challenge_shear_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_shear_50", "herbalism.sheared", 50, getConfig().challengeShear50Reward); - registerMilestone("challenge_shear_250", "herbalism.sheared", 250, getConfig().challengeShear250Reward); - } + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COMPOSTER) + .key("challenge_compost_50") + .title(Localizer.dLocalize("advancement.challenge_compost_50.title")) + .description(Localizer.dLocalize("advancement.challenge_compost_50.description")) + .model(CustomModel.get(Material.COMPOSTER, "advancement", "herbalism", "challenge_compost_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BONE_MEAL) + .key("challenge_compost_500") + .title(Localizer.dLocalize("advancement.challenge_compost_500.title")) + .description(Localizer.dLocalize("advancement.challenge_compost_500.description")) + .model(CustomModel.get(Material.BONE_MEAL, "advancement", "herbalism", "challenge_compost_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_compost_50", "harvest.composted", 50, getConfig().challengeCompost50Reward); + registerMilestone("challenge_compost_500", "harvest.composted", 500, getConfig().challengeCompost500Reward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHEARS) + .key("challenge_shear_50") + .title(Localizer.dLocalize("advancement.challenge_shear_50.title")) + .description(Localizer.dLocalize("advancement.challenge_shear_50.description")) + .model(CustomModel.get(Material.SHEARS, "advancement", "herbalism", "challenge_shear_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WHITE_WOOL) + .key("challenge_shear_250") + .title(Localizer.dLocalize("advancement.challenge_shear_250.title")) + .description(Localizer.dLocalize("advancement.challenge_shear_250.description")) + .model(CustomModel.get(Material.WHITE_WOOL, "advancement", "herbalism", "challenge_shear_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_shear_50", "herbalism.sheared", 50, getConfig().challengeShear50Reward); + registerMilestone("challenge_shear_250", "herbalism.sheared", 250, getConfig().challengeShear250Reward); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - cooldown.remove(p.getUniqueId()); - } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerItemConsumeEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getItem().getItemMeta() instanceof PotionMeta o) { - return; - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + cooldown.remove(p.getUniqueId()); + } - handleHerbCooldown(p, () -> { - xp(p, getConfig().foodConsumeXP); - getPlayer(p).getData().addStat("food.eaten", 1); - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerItemConsumeEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getItem().getItemMeta() instanceof PotionMeta o) { + return; + } + handleHerbCooldown(p, () -> { + xp(p, getConfig().foodConsumeXP); + getPlayer(p).getData().addStat("food.eaten", 1); + }); - }); - } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerShearEntityEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - getPlayer(p).getData().addStat("herbalism.sheared", 1); - xp(p, e.getEntity().getLocation(), getConfig().shearXP); - }); - } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerHarvestBlockEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getHarvestedBlock(), "harvest.blocks")); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerShearEntityEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + getPlayer(p).getData().addStat("herbalism.sheared", 1); + xp(p, e.getEntity().getLocation(), getConfig().shearXP); + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockPlaceEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.planted")); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerHarvestBlockEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getHarvestedBlock(), "harvest.blocks")); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.useItemInHand().equals(Event.Result.DENY)) { - return; - } - if (e.getClickedBlock() == null) { - return; - } - if (e.getClickedBlock().getType().equals(Material.COMPOSTER)) { - handleComposterInteraction(e, p); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockPlaceEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.planted")); + } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.useItemInHand().equals(Event.Result.DENY)) { + return; + } + if (e.getClickedBlock() == null) { + return; + } + if (e.getClickedBlock().getType().equals(Material.COMPOSTER)) { + handleComposterInteraction(e, p); + } + }); - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.blocks")); - } + } - private void handleHerbCooldown(Player p, Runnable action) { - if (cooldown.containsKey(p.getUniqueId())) { - if (cooldown.get(p.getUniqueId()) + getConfig().harvestXpCooldown > System.currentTimeMillis()) { - return; - } else { - cooldown.remove(p.getUniqueId()); - } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + shouldReturnForPlayer(e.getPlayer(), e, () -> handleEvent(e, e.getPlayer(), e.getBlock(), "harvest.blocks")); + } - cooldown.put(p.getUniqueId(), System.currentTimeMillis()); - action.run(); + private void handleHerbCooldown(Player p, Runnable action) { + if (cooldown.containsKey(p.getUniqueId())) { + if (cooldown.get(p.getUniqueId()) + getConfig().harvestXpCooldown > System.currentTimeMillis()) { + return; + } else { + cooldown.remove(p.getUniqueId()); + } } - private void handleEvent(Cancellable e, Player p, Block block, String stat) { - handleHerbCooldown(p, () -> { - if (block.getBlockData() instanceof Ageable ageableBlock) { - xp(p, block.getLocation().clone().add(0.5, 0.5, 0.5), getConfig().harvestPerAgeXP * ageableBlock.getAge()); - getPlayer(p).getData().addStat(stat, 1); - } - }); - } + cooldown.put(p.getUniqueId(), System.currentTimeMillis()); + action.run(); + } - private void handleComposterInteraction(PlayerInteractEvent e, Player p) { - Block b = e.getClickedBlock(); - assert b != null; - if (!(b.getBlockData() instanceof Levelled oldData)) - return; - int ol = oldData.getLevel(); - J.runAt(b.getLocation(), () -> { - if (!(b.getBlockData() instanceof Levelled newData)) - return; - int nl = newData.getLevel(); - if (nl > ol || (ol > 0 && nl == 0)) { - xp(p, e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5), getConfig().composterBaseXP + (nl * getConfig().composterLevelXPMultiplier) + (nl == 0 ? getConfig().composterNonZeroLevelBonus : 5)); - getPlayer(p).getData().addStat("harvest.composted", 1); - } - }); - } + private void handleEvent(Cancellable e, Player p, Block block, String stat) { + handleHerbCooldown(p, () -> { + if (block.getBlockData() instanceof Ageable ageableBlock) { + xp(p, block.getLocation().clone().add(0.5, 0.5, 0.5), getConfig().harvestPerAgeXP * ageableBlock.getAge()); + getPlayer(p).getData().addStat(stat, 1); + } + }); + } + private void handleComposterInteraction(PlayerInteractEvent e, Player p) { + Block b = e.getClickedBlock(); + assert b != null; + if (!(b.getBlockData() instanceof Levelled oldData)) + return; + int ol = oldData.getLevel(); + J.runAt(b.getLocation(), () -> { + if (!(b.getBlockData() instanceof Levelled newData)) + return; + int nl = newData.getLevel(); + if (nl > ol || (ol > 0 && nl == 0)) { + xp(p, e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5), getConfig().composterBaseXP + (nl * getConfig().composterLevelXPMultiplier) + (nl == 0 ? getConfig().composterNonZeroLevelBonus : 5)); + getPlayer(p).getData().addStat("harvest.composted", 1); + } + }); + } - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); - } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } - @NoArgsConstructor - public static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - public boolean enabled = true; - String skillColor = "&a"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Xp Cooldown for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double harvestXpCooldown = 3500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Consume XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double foodConsumeXP = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shear XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double shearXP = 35; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Per Age XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double harvestPerAgeXP = 5.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Plant Crop Seeds XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double plantCropSeedsXP = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Base XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double composterBaseXP = 2.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Level XPMultiplier for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double composterLevelXPMultiplier = 1.25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Non Zero Level Bonus for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double composterNonZeroLevelBonus = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeEat100Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeEat1kReward = 6250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeHarvest100Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeHarvest1kReward = 6250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant100 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengePlant100Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengePlant1kReward = 6250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant5k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengePlant5kReward = 25000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeCompost50Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost500 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeCompost500Reward = 6250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeShear50Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear250 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double challengeShear250Reward = 6250; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + public static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + public boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Xp Cooldown for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double harvestXpCooldown = 3500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Food Consume XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double foodConsumeXP = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shear XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double shearXP = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Harvest Per Age XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double harvestPerAgeXP = 5.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Plant Crop Seeds XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double plantCropSeedsXP = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Base XP for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double composterBaseXP = 2.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Level XPMultiplier for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double composterLevelXPMultiplier = 1.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Composter Non Zero Level Bonus for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double composterNonZeroLevelBonus = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeEat100Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Eat1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeEat1kReward = 6250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest100Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeHarvest100Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Harvest1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeHarvest1kReward = 6250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant100 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengePlant100Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant1k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengePlant1kReward = 6250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Plant5k Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengePlant5kReward = 25000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeCompost50Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Compost500 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeCompost500Reward = 6250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear50 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeShear50Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Shear250 Reward for the Herbalism skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double challengeShear250Reward = 6250; + String skillColor = "&a"; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java index c65b68040..ef1680646 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java @@ -48,276 +48,276 @@ import java.util.UUID; public class SkillHunter extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillHunter() { - super("hunter", Localizer.dLocalize("skill.hunter.icon")); - registerConfiguration(Config.class); - setColor(C.RED); - setDescription(Localizer.dLocalize("skill.hunter.description")); - setDisplayName(Localizer.dLocalize("skill.hunter.name")); - setInterval(4150); - setIcon(Material.BONE); - cooldowns = new HashMap<>(); - registerAdaptation(new HunterAdrenaline()); - registerAdaptation(new HunterRegen()); - registerAdaptation(new HunterInvis()); - registerAdaptation(new HunterJumpBoost()); - registerAdaptation(new HunterLuck()); - registerAdaptation(new HunterSpeed()); - registerAdaptation(new HunterStrength()); - registerAdaptation(new HunterResistance()); - registerAdaptation(new HunterDropToInventory()); - registerAdaptation(new HunterTrophySkinner()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TURTLE_EGG) - .key("horrible_person") - .title(Localizer.dLocalize("advancement.horrible_person.title")) - .description(Localizer.dLocalize("advancement.horrible_person.description")) - .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "horrible_person")) - .frame(AdaptAdvancementFrame.GOAL) - .visibility(AdvancementVisibility.HIDDEN) - .build() - ); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TURTLE_EGG) - .key("challenge_turtle_egg_smasher") - .title(Localizer.dLocalize("advancement.challenge_turtle_egg_smasher.title")) - .description(Localizer.dLocalize("advancement.challenge_turtle_egg_smasher.description")) - .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "challenge_turtle_egg_smasher")) + public SkillHunter() { + super("hunter", Localizer.dLocalize("skill.hunter.icon")); + registerConfiguration(Config.class); + setColor(C.RED); + setDescription(Localizer.dLocalize("skill.hunter.description")); + setDisplayName(Localizer.dLocalize("skill.hunter.name")); + setInterval(4150); + setIcon(Material.BONE); + cooldowns = new HashMap<>(); + registerAdaptation(new HunterAdrenaline()); + registerAdaptation(new HunterRegen()); + registerAdaptation(new HunterInvis()); + registerAdaptation(new HunterJumpBoost()); + registerAdaptation(new HunterLuck()); + registerAdaptation(new HunterSpeed()); + registerAdaptation(new HunterStrength()); + registerAdaptation(new HunterResistance()); + registerAdaptation(new HunterDropToInventory()); + registerAdaptation(new HunterTrophySkinner()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TURTLE_EGG) + .key("horrible_person") + .title(Localizer.dLocalize("advancement.horrible_person.title")) + .description(Localizer.dLocalize("advancement.horrible_person.description")) + .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "horrible_person")) + .frame(AdaptAdvancementFrame.GOAL) + .visibility(AdvancementVisibility.HIDDEN) + .build() + ); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TURTLE_EGG) + .key("challenge_turtle_egg_smasher") + .title(Localizer.dLocalize("advancement.challenge_turtle_egg_smasher.title")) + .description(Localizer.dLocalize("advancement.challenge_turtle_egg_smasher.description")) + .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "challenge_turtle_egg_smasher")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TURTLE_EGG) + .key("challenge_turtle_egg_annihilator") + .title(Localizer.dLocalize("advancement.challenge_turtle_egg_annihilator.title")) + .description(Localizer.dLocalize("advancement.challenge_turtle_egg_annihilator.description")) + .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "challenge_turtle_egg_annihilator")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_novice_hunter") + .title(Localizer.dLocalize("advancement.challenge_novice_hunter.title")) + .description(Localizer.dLocalize("advancement.challenge_novice_hunter.description")) + .model(CustomModel.get(Material.BONE, "advancement", "hunter", "challenge_novice_hunter")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_intermediate_hunter") + .title(Localizer.dLocalize("advancement.challenge_intermediate_hunter.title")) + .description(Localizer.dLocalize("advancement.challenge_intermediate_hunter.description")) + .model(CustomModel.get(Material.IRON_SWORD, "advancement", "hunter", "challenge_intermediate_hunter")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_advanced_hunter") + .title(Localizer.dLocalize("advancement.challenge_advanced_hunter.title")) + .description(Localizer.dLocalize("advancement.challenge_advanced_hunter.description")) + .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "hunter", "challenge_advanced_hunter")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TURTLE_EGG) - .key("challenge_turtle_egg_annihilator") - .title(Localizer.dLocalize("advancement.challenge_turtle_egg_annihilator.title")) - .description(Localizer.dLocalize("advancement.challenge_turtle_egg_annihilator.description")) - .model(CustomModel.get(Material.TURTLE_EGG, "advancement", "hunter", "challenge_turtle_egg_annihilator")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_novice_hunter") - .title(Localizer.dLocalize("advancement.challenge_novice_hunter.title")) - .description(Localizer.dLocalize("advancement.challenge_novice_hunter.description")) - .model(CustomModel.get(Material.BONE, "advancement", "hunter", "challenge_novice_hunter")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_intermediate_hunter") - .title(Localizer.dLocalize("advancement.challenge_intermediate_hunter.title")) - .description(Localizer.dLocalize("advancement.challenge_intermediate_hunter.description")) - .model(CustomModel.get(Material.IRON_SWORD, "advancement", "hunter", "challenge_intermediate_hunter")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_advanced_hunter") - .title(Localizer.dLocalize("advancement.challenge_advanced_hunter.title")) - .description(Localizer.dLocalize("advancement.challenge_advanced_hunter.description")) - .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "hunter", "challenge_advanced_hunter")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CREEPER_HEAD) - .key("challenge_creeper_conqueror") - .title(Localizer.dLocalize("advancement.challenge_creeper_conqueror.title")) - .description(Localizer.dLocalize("advancement.challenge_creeper_conqueror.description")) - .model(CustomModel.get(Material.CREEPER_HEAD, "advancement", "hunter", "challenge_creeper_conqueror")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TNT) - .key("challenge_creeper_annihilator") - .title(Localizer.dLocalize("advancement.challenge_creeper_annihilator.title")) - .description(Localizer.dLocalize("advancement.challenge_creeper_annihilator.description")) - .model(CustomModel.get(Material.TNT, "advancement", "hunter", "challenge_creeper_annihilator")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); + .build()) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CREEPER_HEAD) + .key("challenge_creeper_conqueror") + .title(Localizer.dLocalize("advancement.challenge_creeper_conqueror.title")) + .description(Localizer.dLocalize("advancement.challenge_creeper_conqueror.description")) + .model(CustomModel.get(Material.CREEPER_HEAD, "advancement", "hunter", "challenge_creeper_conqueror")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TNT) + .key("challenge_creeper_annihilator") + .title(Localizer.dLocalize("advancement.challenge_creeper_annihilator.title")) + .description(Localizer.dLocalize("advancement.challenge_creeper_annihilator.description")) + .model(CustomModel.get(Material.TNT, "advancement", "hunter", "challenge_creeper_annihilator")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_kills_500") - .title(Localizer.dLocalize("advancement.challenge_kills_500.title")) - .description(Localizer.dLocalize("advancement.challenge_kills_500.description")) - .model(CustomModel.get(Material.BONE, "advancement", "hunter", "challenge_kills_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_kills_5k") - .title(Localizer.dLocalize("advancement.challenge_kills_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_kills_5k.description")) - .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "hunter", "challenge_kills_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DRAGON_HEAD) - .key("challenge_boss_1") - .title(Localizer.dLocalize("advancement.challenge_boss_1.title")) - .description(Localizer.dLocalize("advancement.challenge_boss_1.description")) - .model(CustomModel.get(Material.DRAGON_HEAD, "advancement", "hunter", "challenge_boss_1")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_boss_10") - .title(Localizer.dLocalize("advancement.challenge_boss_10.title")) - .description(Localizer.dLocalize("advancement.challenge_boss_10.description")) - .model(CustomModel.get(Material.NETHER_STAR, "advancement", "hunter", "challenge_boss_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_kills_500") + .title(Localizer.dLocalize("advancement.challenge_kills_500.title")) + .description(Localizer.dLocalize("advancement.challenge_kills_500.description")) + .model(CustomModel.get(Material.BONE, "advancement", "hunter", "challenge_kills_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_kills_5k") + .title(Localizer.dLocalize("advancement.challenge_kills_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_kills_5k.description")) + .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "hunter", "challenge_kills_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DRAGON_HEAD) + .key("challenge_boss_1") + .title(Localizer.dLocalize("advancement.challenge_boss_1.title")) + .description(Localizer.dLocalize("advancement.challenge_boss_1.description")) + .model(CustomModel.get(Material.DRAGON_HEAD, "advancement", "hunter", "challenge_boss_1")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_boss_10") + .title(Localizer.dLocalize("advancement.challenge_boss_10.title")) + .description(Localizer.dLocalize("advancement.challenge_boss_10.description")) + .model(CustomModel.get(Material.NETHER_STAR, "advancement", "hunter", "challenge_boss_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); - registerMilestone("horrible_person", "killed.turtleeggs", 1, getConfig().turtleEggKillXP); - registerMilestone("challenge_turtle_egg_smasher", "killed.turtleeggs", 100, getConfig().turtleEggKillXP*10); - registerMilestone("challenge_turtle_egg_annihilator", "killed.turtleeggs", 1000, getConfig().turtleEggKillXP*10); - registerMilestone("challenge_novice_hunter", "killed.monsters", 100, getConfig().turtleEggKillXP*3); - registerMilestone("challenge_intermediate_hunter", "killed.monsters", 1000, getConfig().turtleEggKillXP*3); - registerMilestone("challenge_advanced_hunter", "killed.monsters", 10000, getConfig().turtleEggKillXP*3); - registerMilestone("challenge_creeper_conqueror", "killed.creepers", 100, getConfig().turtleEggKillXP*3); - registerMilestone("challenge_creeper_annihilator", "killed.creepers", 1000, getConfig().turtleEggKillXP*3); - registerMilestone("challenge_kills_500", "killed.kills", 500, getConfig().killsChallengeReward); - registerMilestone("challenge_kills_5k", "killed.kills", 5000, getConfig().killsChallengeReward * 5); - registerMilestone("challenge_boss_1", "hunter.boss.kills", 1, getConfig().bossKillReward); - registerMilestone("challenge_boss_10", "hunter.boss.kills", 10, getConfig().bossKillReward * 5); - } + registerMilestone("horrible_person", "killed.turtleeggs", 1, getConfig().turtleEggKillXP); + registerMilestone("challenge_turtle_egg_smasher", "killed.turtleeggs", 100, getConfig().turtleEggKillXP * 10); + registerMilestone("challenge_turtle_egg_annihilator", "killed.turtleeggs", 1000, getConfig().turtleEggKillXP * 10); + registerMilestone("challenge_novice_hunter", "killed.monsters", 100, getConfig().turtleEggKillXP * 3); + registerMilestone("challenge_intermediate_hunter", "killed.monsters", 1000, getConfig().turtleEggKillXP * 3); + registerMilestone("challenge_advanced_hunter", "killed.monsters", 10000, getConfig().turtleEggKillXP * 3); + registerMilestone("challenge_creeper_conqueror", "killed.creepers", 100, getConfig().turtleEggKillXP * 3); + registerMilestone("challenge_creeper_annihilator", "killed.creepers", 1000, getConfig().turtleEggKillXP * 3); + registerMilestone("challenge_kills_500", "killed.kills", 500, getConfig().killsChallengeReward); + registerMilestone("challenge_kills_5k", "killed.kills", 5000, getConfig().killsChallengeReward * 5); + registerMilestone("challenge_boss_1", "hunter.boss.kills", 1, getConfig().bossKillReward); + registerMilestone("challenge_boss_10", "hunter.boss.kills", 10, getConfig().bossKillReward * 5); + } - private void handleCooldownAndXp(Player p, double xpAmount) { - handleCooldownAndXp(p, xpAmount, null); - } + private void handleCooldownAndXp(Player p, double xpAmount) { + handleCooldownAndXp(p, xpAmount, null); + } - private void handleCooldownAndXp(Player p, double xpAmount, String rewardKey) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(p, xpAmount, rewardKey); - } + private void handleCooldownAndXp(Player p, double xpAmount, String rewardKey) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(p, xpAmount, rewardKey); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getBlock().getType().equals(Material.TURTLE_EGG)) { - handleCooldownAndXp(p, getConfig().turtleEggKillXP, "hunter:turtle-egg:break"); - getPlayer(p).getData().addStat("killed.turtleeggs", 1); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getBlock().getType().equals(Material.TURTLE_EGG)) { + handleCooldownAndXp(p, getConfig().turtleEggKillXP, "hunter:turtle-egg:break"); + getPlayer(p).getData().addStat("killed.turtleeggs", 1); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) { + handleCooldownAndXp(p, getConfig().turtleEggKillXP, "hunter:turtle-egg:step"); + getPlayer(p).getData().addStat("killed.turtleeggs", 1); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() == null) { + return; } + Player p = e.getEntity().getKiller(); - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null && e.getClickedBlock().getType().equals(Material.TURTLE_EGG)) { - handleCooldownAndXp(p, getConfig().turtleEggKillXP, "hunter:turtle-egg:step"); - getPlayer(p).getData().addStat("killed.turtleeggs", 1); - } - }); + if (!getConfig().getXpForAttackingWithTools) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() == null) { - return; + shouldReturnForPlayer(p, () -> { + if (e.getEntity().getType().equals(EntityType.CREEPER)) { + double cmult = getConfig().creeperKillMultiplier; + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(e.getEntity(), Attributes.GENERIC_MAX_HEALTH); + double xpAmount = (attribute == null ? 1 : attribute.getValue()) * getConfig().killMaxHealthXPMultiplier * cmult; + if (e.getEntity().getPortalCooldown() > 0) { + xpAmount *= getConfig().spawnerMobReductionXpMultiplier; } - Player p = e.getEntity().getKiller(); + getPlayer(p).getData().addStat("killed.kills", 1); + handleCooldownAndXp(p, xpAmount, "hunter:kill:creeper"); + } else { + handleEntityKill(p, e.getEntity()); + } + EntityType type = e.getEntity().getType(); + if (type == EntityType.ENDER_DRAGON || type == EntityType.WITHER || type == EntityType.ELDER_GUARDIAN || type == EntityType.WARDEN) { + getPlayer(p).getData().addStat("hunter.boss.kills", 1); + } + }); + } - if (!getConfig().getXpForAttackingWithTools) { - return; - } - shouldReturnForPlayer(p, () -> { - if (e.getEntity().getType().equals(EntityType.CREEPER)) { - double cmult = getConfig().creeperKillMultiplier; - var attribute = Version.get().getAttribute(e.getEntity(), Attributes.GENERIC_MAX_HEALTH); - double xpAmount = (attribute == null ? 1 : attribute.getValue()) * getConfig().killMaxHealthXPMultiplier * cmult; - if (e.getEntity().getPortalCooldown() > 0) { - xpAmount *= getConfig().spawnerMobReductionXpMultiplier; - } - getPlayer(p).getData().addStat("killed.kills", 1); - handleCooldownAndXp(p, xpAmount, "hunter:kill:creeper"); - } else { - handleEntityKill(p, e.getEntity()); - } - EntityType type = e.getEntity().getType(); - if (type == EntityType.ENDER_DRAGON || type == EntityType.WITHER || type == EntityType.ELDER_GUARDIAN || type == EntityType.WARDEN) { - getPlayer(p).getData().addStat("hunter.boss.kills", 1); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(CreatureSpawnEvent e) { + if (!isEnabled()) { + return; } - - - @EventHandler(priority = EventPriority.MONITOR) - public void on(CreatureSpawnEvent e) { - if (!isEnabled()) { - return; - } - if (e.getSpawnReason().equals(CreatureSpawnEvent.SpawnReason.SPAWNER)) { - Entity ent = e.getEntity(); - ent.setPortalCooldown(630726000); - } + if (e.getSpawnReason().equals(CreatureSpawnEvent.SpawnReason.SPAWNER)) { + Entity ent = e.getEntity(); + ent.setPortalCooldown(630726000); } + } - private void handleEntityKill(Player p, Entity entity) { - if (entity instanceof LivingEntity livingEntity) { - var attribute = Version.get().getAttribute(livingEntity, Attributes.GENERIC_MAX_HEALTH); - double xpAmount = (attribute == null ? 1 : attribute.getValue()) * getConfig().killMaxHealthXPMultiplier; - if (entity.getPortalCooldown() > 0) { - xpAmount *= getConfig().spawnerMobReductionXpMultiplier; - } - getPlayer(p).getData().addStat("killed.kills", 1); - String rewardKey = "hunter:kill:" + entity.getType().name().toLowerCase(Locale.ROOT); - handleCooldownAndXp(p, xpAmount, rewardKey); - } + private void handleEntityKill(Player p, Entity entity) { + if (entity instanceof LivingEntity livingEntity) { + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(livingEntity, Attributes.GENERIC_MAX_HEALTH); + double xpAmount = (attribute == null ? 1 : attribute.getValue()) * getConfig().killMaxHealthXPMultiplier; + if (entity.getPortalCooldown() > 0) { + xpAmount *= getConfig().spawnerMobReductionXpMultiplier; + } + getPlayer(p).getData().addStat("killed.kills", 1); + String rewardKey = "hunter:kill:" + entity.getType().name().toLowerCase(Locale.ROOT); + handleCooldownAndXp(p, xpAmount, rewardKey); } + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&c"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Hunter skill.", impact = "True enables this behavior and false disables it.") - boolean getXpForAttackingWithTools = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Turtle Egg Kill XP for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double turtleEggKillXP = 100; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Creeper Kill Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double creeperKillMultiplier = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kill Max Health XPMultiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double killMaxHealthXPMultiplier = 3.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawner Mob Reduction Xp Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double spawnerMobReductionXpMultiplier = 0.3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kills Challenge Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double killsChallengeReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Boss Kill Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double bossKillReward = 1000; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&c"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Hunter skill.", impact = "True enables this behavior and false disables it.") + boolean getXpForAttackingWithTools = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Turtle Egg Kill XP for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double turtleEggKillXP = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Creeper Kill Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double creeperKillMultiplier = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kill Max Health XPMultiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double killMaxHealthXPMultiplier = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Spawner Mob Reduction Xp Multiplier for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double spawnerMobReductionXpMultiplier = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Kills Challenge Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double killsChallengeReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Boss Kill Reward for the Hunter skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bossKillReward = 1000; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java index d25b3d6f2..11603e6fc 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillNether.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillNether.java @@ -41,236 +41,236 @@ import org.bukkit.event.entity.EntityDeathEvent; public class SkillNether extends SimpleSkill { - private int witherRoseCooldown; + private int witherRoseCooldown; - public SkillNether() { - super("nether", Localizer.dLocalize("skill.nether.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.nether.description")); - setDisplayName(Localizer.dLocalize("skill.nether.name")); - setInterval(7425); - setColor(C.DARK_GRAY); - setIcon(Material.NETHER_STAR); - registerAdaptation(new NetherWitherResist()); - registerAdaptation(new NetherSkullYeet()); - registerAdaptation(new NetherFireResist()); - registerAdaptation(new NetherLavaWalker()); - registerAdaptation(new NetherGhastWard()); - registerAdaptation(new NetherBlazeLeech()); - registerAdaptation(new NetherPiglinBroker()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_nether_50") - .title(Localizer.dLocalize("advancement.challenge_nether_50.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_50.description")) - .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "nether", "challenge_nether_50")) + public SkillNether() { + super("nether", Localizer.dLocalize("skill.nether.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.nether.description")); + setDisplayName(Localizer.dLocalize("skill.nether.name")); + setInterval(7425); + setColor(C.DARK_GRAY); + setIcon(Material.NETHER_STAR); + registerAdaptation(new NetherWitherResist()); + registerAdaptation(new NetherSkullYeet()); + registerAdaptation(new NetherFireResist()); + registerAdaptation(new NetherLavaWalker()); + registerAdaptation(new NetherGhastWard()); + registerAdaptation(new NetherBlazeLeech()); + registerAdaptation(new NetherPiglinBroker()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_nether_50") + .title(Localizer.dLocalize("advancement.challenge_nether_50.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_50.description")) + .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "nether", "challenge_nether_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_nether_500") + .title(Localizer.dLocalize("advancement.challenge_nether_500.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_500.description")) + .model(CustomModel.get(Material.NETHER_STAR, "advancement", "nether", "challenge_nether_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BEACON) + .key("challenge_nether_5k") + .title(Localizer.dLocalize("advancement.challenge_nether_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_nether_5k.description")) + .model(CustomModel.get(Material.BEACON, "advancement", "nether", "challenge_nether_5k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_nether_500") - .title(Localizer.dLocalize("advancement.challenge_nether_500.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_500.description")) - .model(CustomModel.get(Material.NETHER_STAR, "advancement", "nether", "challenge_nether_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BEACON) - .key("challenge_nether_5k") - .title(Localizer.dLocalize("advancement.challenge_nether_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_nether_5k.description")) - .model(CustomModel.get(Material.BEACON, "advancement", "nether", "challenge_nether_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_nether_50", "nether.kills", 50, getConfig().getChallengeNetherReward()); - registerMilestone("challenge_nether_500", "nether.kills", 500, getConfig().getChallengeNetherReward() * 2); - registerMilestone("challenge_nether_5k", "nether.kills", 5000, getConfig().getChallengeNetherReward() * 5); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_ROSE) - .key("challenge_wither_dmg_500") - .title(Localizer.dLocalize("advancement.challenge_wither_dmg_500.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_dmg_500.description")) - .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "nether", "challenge_wither_dmg_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SOUL_LANTERN) - .key("challenge_wither_dmg_5k") - .title(Localizer.dLocalize("advancement.challenge_wither_dmg_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_dmg_5k.description")) - .model(CustomModel.get(Material.SOUL_LANTERN, "advancement", "nether", "challenge_wither_dmg_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_wither_dmg_500", "nether.wither.damage", 500, getConfig().getChallengeWitherDmgReward()); - registerMilestone("challenge_wither_dmg_5k", "nether.wither.damage", 5000, getConfig().getChallengeWitherDmgReward() * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_wither_skel_25") - .title(Localizer.dLocalize("advancement.challenge_wither_skel_25.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_skel_25.description")) - .model(CustomModel.get(Material.BONE, "advancement", "nether", "challenge_wither_skel_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_wither_skel_250") - .title(Localizer.dLocalize("advancement.challenge_wither_skel_250.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_skel_250.description")) - .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "nether", "challenge_wither_skel_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_wither_skel_25", "nether.skeleton.kills", 25, getConfig().getChallengeWitherSkelReward()); - registerMilestone("challenge_wither_skel_250", "nether.skeleton.kills", 250, getConfig().getChallengeWitherSkelReward() * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_wither_boss_1") - .title(Localizer.dLocalize("advancement.challenge_wither_boss_1.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_boss_1.description")) - .model(CustomModel.get(Material.NETHER_STAR, "advancement", "nether", "challenge_wither_boss_1")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BEACON) - .key("challenge_wither_boss_10") - .title(Localizer.dLocalize("advancement.challenge_wither_boss_10.title")) - .description(Localizer.dLocalize("advancement.challenge_wither_boss_10.description")) - .model(CustomModel.get(Material.BEACON, "advancement", "nether", "challenge_wither_boss_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_wither_boss_1", "nether.boss.kills", 1, getConfig().getChallengeWitherBossReward()); - registerMilestone("challenge_wither_boss_10", "nether.boss.kills", 10, getConfig().getChallengeWitherBossReward() * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WITHER_ROSE) - .key("challenge_roses_10") - .title(Localizer.dLocalize("advancement.challenge_roses_10.title")) - .description(Localizer.dLocalize("advancement.challenge_roses_10.description")) - .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "nether", "challenge_roses_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FLOWER_POT) - .key("challenge_roses_100") - .title(Localizer.dLocalize("advancement.challenge_roses_100.title")) - .description(Localizer.dLocalize("advancement.challenge_roses_100.description")) - .model(CustomModel.get(Material.FLOWER_POT, "advancement", "nether", "challenge_roses_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_roses_10", "nether.roses.broken", 10, getConfig().getChallengeRosesReward()); - registerMilestone("challenge_roses_100", "nether.roses.broken", 100, getConfig().getChallengeRosesReward() * 2); - } + .build()) + .build()) + .build()); + registerMilestone("challenge_nether_50", "nether.kills", 50, getConfig().getChallengeNetherReward()); + registerMilestone("challenge_nether_500", "nether.kills", 500, getConfig().getChallengeNetherReward() * 2); + registerMilestone("challenge_nether_5k", "nether.kills", 5000, getConfig().getChallengeNetherReward() * 5); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_wither_dmg_500") + .title(Localizer.dLocalize("advancement.challenge_wither_dmg_500.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_dmg_500.description")) + .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "nether", "challenge_wither_dmg_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SOUL_LANTERN) + .key("challenge_wither_dmg_5k") + .title(Localizer.dLocalize("advancement.challenge_wither_dmg_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_dmg_5k.description")) + .model(CustomModel.get(Material.SOUL_LANTERN, "advancement", "nether", "challenge_wither_dmg_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_wither_dmg_500", "nether.wither.damage", 500, getConfig().getChallengeWitherDmgReward()); + registerMilestone("challenge_wither_dmg_5k", "nether.wither.damage", 5000, getConfig().getChallengeWitherDmgReward() * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_wither_skel_25") + .title(Localizer.dLocalize("advancement.challenge_wither_skel_25.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_skel_25.description")) + .model(CustomModel.get(Material.BONE, "advancement", "nether", "challenge_wither_skel_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_wither_skel_250") + .title(Localizer.dLocalize("advancement.challenge_wither_skel_250.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_skel_250.description")) + .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "nether", "challenge_wither_skel_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_wither_skel_25", "nether.skeleton.kills", 25, getConfig().getChallengeWitherSkelReward()); + registerMilestone("challenge_wither_skel_250", "nether.skeleton.kills", 250, getConfig().getChallengeWitherSkelReward() * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_wither_boss_1") + .title(Localizer.dLocalize("advancement.challenge_wither_boss_1.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_boss_1.description")) + .model(CustomModel.get(Material.NETHER_STAR, "advancement", "nether", "challenge_wither_boss_1")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BEACON) + .key("challenge_wither_boss_10") + .title(Localizer.dLocalize("advancement.challenge_wither_boss_10.title")) + .description(Localizer.dLocalize("advancement.challenge_wither_boss_10.description")) + .model(CustomModel.get(Material.BEACON, "advancement", "nether", "challenge_wither_boss_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_wither_boss_1", "nether.boss.kills", 1, getConfig().getChallengeWitherBossReward()); + registerMilestone("challenge_wither_boss_10", "nether.boss.kills", 10, getConfig().getChallengeWitherBossReward() * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_roses_10") + .title(Localizer.dLocalize("advancement.challenge_roses_10.title")) + .description(Localizer.dLocalize("advancement.challenge_roses_10.description")) + .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "nether", "challenge_roses_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FLOWER_POT) + .key("challenge_roses_100") + .title(Localizer.dLocalize("advancement.challenge_roses_100.title")) + .description(Localizer.dLocalize("advancement.challenge_roses_100.description")) + .model(CustomModel.get(Material.FLOWER_POT, "advancement", "nether", "challenge_roses_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_roses_10", "nether.roses.broken", 10, getConfig().getChallengeRosesReward()); + registerMilestone("challenge_roses_100", "nether.roses.broken", 100, getConfig().getChallengeRosesReward() * 2); + } - private boolean isWitherDamageCause(EntityDamageEvent.DamageCause cause) { - return cause == EntityDamageEvent.DamageCause.WITHER; - } + private boolean isWitherDamageCause(EntityDamageEvent.DamageCause cause) { + return cause == EntityDamageEvent.DamageCause.WITHER; + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageEvent e) { - if (!this.isEnabled() || !(e.getEntity() instanceof Player p) || !isWitherDamageCause(e.getCause()) || e instanceof EntityDamageByBlockEvent) { - return; - } - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("nether.wither.damage", e.getDamage()); - xp(p, getConfig().getWitherDamageXp()); - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageEvent e) { + if (!this.isEnabled() || !(e.getEntity() instanceof Player p) || !isWitherDamageCause(e.getCause()) || e instanceof EntityDamageByBlockEvent) { + return; } + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("nether.wither.damage", e.getDamage()); + xp(p, getConfig().getWitherDamageXp()); + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getBlock().getType() == Material.WITHER_ROSE && witherRoseCooldown == 0) { - witherRoseCooldown = getConfig().getWitherRoseBreakCooldown(); - getPlayer(p).getData().addStat("nether.roses.broken", 1); - xp(p, e.getBlock().getLocation().add(.5D, .5D, .5D), getConfig().getWitherRoseBreakXp()); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getBlock().getType() == Material.WITHER_ROSE && witherRoseCooldown == 0) { + witherRoseCooldown = getConfig().getWitherRoseBreakCooldown(); + getPlayer(p).getData().addStat("nether.roses.broken", 1); + xp(p, e.getBlock().getLocation().add(.5D, .5D, .5D), getConfig().getWitherRoseBreakXp()); + } + }); - } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - Player p = e.getEntity().getKiller(); - if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - shouldReturnForPlayer(p, () -> { - if (e.getEntityType() == EntityType.WITHER_SKELETON) { - getPlayer(p).getData().addStat("nether.kills", 1); - getPlayer(p).getData().addStat("nether.skeleton.kills", 1); - xp(p, getConfig().getWitherSkeletonKillXp()); - } else if (e.getEntityType() == EntityType.WITHER) { - getPlayer(p).getData().addStat("nether.kills", 1); - getPlayer(p).getData().addStat("nether.boss.kills", 1); - xp(p, getConfig().getWitherKillXp()); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + Player p = e.getEntity().getKiller(); + if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { + return; } + shouldReturnForPlayer(p, () -> { + if (e.getEntityType() == EntityType.WITHER_SKELETON) { + getPlayer(p).getData().addStat("nether.kills", 1); + getPlayer(p).getData().addStat("nether.skeleton.kills", 1); + xp(p, getConfig().getWitherSkeletonKillXp()); + } else if (e.getEntityType() == EntityType.WITHER) { + getPlayer(p).getData().addStat("nether.kills", 1); + getPlayer(p).getData().addStat("nether.boss.kills", 1); + xp(p, getConfig().getWitherKillXp()); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p) || !isWitherDamageCause(e.getCause())) { - return; - } - shouldReturnForPlayer(p, e, () -> xp(p, getConfig().getWitherAttackXp())); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p) || !isWitherDamageCause(e.getCause())) { + return; } + shouldReturnForPlayer(p, e, () -> xp(p, getConfig().getWitherAttackXp())); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - if (witherRoseCooldown > 0) { - witherRoseCooldown--; - } - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> checkStatTrackers(adaptPlayer)); - } + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } - - @Override - public boolean isEnabled() { - return getConfig().isEnabled(); + if (witherRoseCooldown > 0) { + witherRoseCooldown--; } - - @Data - @NoArgsConstructor - public static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - private boolean enabled = true; - String skillColor = "&8"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Damage Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double witherDamageXp = 26.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Attack Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double witherAttackXp = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double witherSkeletonKillXp = 225; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double witherKillXp = 900; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double witherRoseBreakXp = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Cooldown for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private int witherRoseBreakCooldown = 60 * 20; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Nether Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double challengeNetherReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Damage Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double challengeWitherDmgReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Skeleton Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double challengeWitherSkelReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Boss Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double challengeWitherBossReward = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Roses Broken Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - private double challengeRosesReward = 500; + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> checkStatTrackers(adaptPlayer)); } + } + + @Override + public boolean isEnabled() { + return getConfig().isEnabled(); + } + + @Data + @NoArgsConstructor + public static class Config { + String skillColor = "&8"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + private boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Damage Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double witherDamageXp = 26.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Attack Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double witherAttackXp = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Skeleton Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double witherSkeletonKillXp = 225; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Kill Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double witherKillXp = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Xp for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double witherRoseBreakXp = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Wither Rose Break Cooldown for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private int witherRoseBreakCooldown = 60 * 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Nether Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double challengeNetherReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Damage Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double challengeWitherDmgReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Skeleton Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double challengeWitherSkelReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Wither Boss Kill Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double challengeWitherBossReward = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Roses Broken Reward for the Nether skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + private double challengeRosesReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index 02349f283..5c6acb12a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -43,297 +43,297 @@ import java.util.UUID; public class SkillPickaxes extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillPickaxes() { - super("pickaxe", Localizer.dLocalize("skill.pickaxe.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.pickaxe.description")); - setDisplayName(Localizer.dLocalize("skill.pickaxe.name")); - setColor(C.GOLD); - setInterval(2750); - setIcon(Material.NETHERITE_PICKAXE); - cooldowns = new HashMap<>(); - registerAdaptation(new PickaxeChisel()); - registerAdaptation(new PickaxeVeinminer()); - registerAdaptation(new PickaxeAutosmelt()); - registerAdaptation(new PickaxeDropToInventory()); - registerAdaptation(new PickaxeSilkSpawner()); - registerAdaptation(new PickaxeQuarrySense()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_PICKAXE) - .key("challenge_pickaxe_1k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_1k.description")) - .model(CustomModel.get(Material.WOODEN_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_1k")) + public SkillPickaxes() { + super("pickaxe", Localizer.dLocalize("skill.pickaxe.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.pickaxe.description")); + setDisplayName(Localizer.dLocalize("skill.pickaxe.name")); + setColor(C.GOLD); + setInterval(2750); + setIcon(Material.NETHERITE_PICKAXE); + cooldowns = new HashMap<>(); + registerAdaptation(new PickaxeChisel()); + registerAdaptation(new PickaxeVeinminer()); + registerAdaptation(new PickaxeAutosmelt()); + registerAdaptation(new PickaxeDropToInventory()); + registerAdaptation(new PickaxeSilkSpawner()); + registerAdaptation(new PickaxeQuarrySense()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_PICKAXE) + .key("challenge_pickaxe_1k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_1k.description")) + .model(CustomModel.get(Material.WOODEN_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.STONE_PICKAXE) + .key("challenge_pickaxe_5k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_5k.description")) + .model(CustomModel.get(Material.STONE_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_pickaxe_50k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_50k.description")) + .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_50k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) .child(AdaptAdvancement.builder() - .icon(Material.STONE_PICKAXE) - .key("challenge_pickaxe_5k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_5k.description")) - .model(CustomModel.get(Material.STONE_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_5k")) + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_pickaxe_500k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_500k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_500k.description")) + .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_500k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_PICKAXE) + .key("challenge_pickaxe_5m") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_5m.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_5m.description")) + .model(CustomModel.get(Material.NETHERITE_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_5m")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_pickaxe_50k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_50k.description")) - .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_pickaxe_500k") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_500k.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_500k.description")) - .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_500k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_PICKAXE) - .key("challenge_pickaxe_5m") - .title(Localizer.dLocalize("advancement.challenge_pickaxe_5m.title")) - .description(Localizer.dLocalize("advancement.challenge_pickaxe_5m.description")) - .model(CustomModel.get(Material.NETHERITE_PICKAXE, "advancement", "pickaxe", "challenge_pickaxe_5m")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()) .build()) - .build()); + .build()) + .build()) + .build()) + .build()); - registerMilestone("challenge_pickaxe_1k", "pickaxe.blocks.broken", 100, getConfig().emeraldBonus*2); - registerMilestone("challenge_pickaxe_5k", "pickaxe.blocks.broken", 500, getConfig().emeraldBonus*5); - registerMilestone("challenge_pickaxe_50k", "pickaxe.blocks.broken", 5000, getConfig().emeraldBonus*10); - registerMilestone("challenge_pickaxe_500k", "pickaxe.blocks.broken", 50000, getConfig().emeraldBonus*10); - registerMilestone("challenge_pickaxe_5m", "pickaxe.blocks.broken", 500000, getConfig().emeraldBonus*50); + registerMilestone("challenge_pickaxe_1k", "pickaxe.blocks.broken", 100, getConfig().emeraldBonus * 2); + registerMilestone("challenge_pickaxe_5k", "pickaxe.blocks.broken", 500, getConfig().emeraldBonus * 5); + registerMilestone("challenge_pickaxe_50k", "pickaxe.blocks.broken", 5000, getConfig().emeraldBonus * 10); + registerMilestone("challenge_pickaxe_500k", "pickaxe.blocks.broken", 50000, getConfig().emeraldBonus * 10); + registerMilestone("challenge_pickaxe_5m", "pickaxe.blocks.broken", 500000, getConfig().emeraldBonus * 50); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_PICKAXE).key("challenge_pick_swing_500") - .title(Localizer.dLocalize("advancement.challenge_pick_swing_500.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_swing_500.description")) - .model(CustomModel.get(Material.WOODEN_PICKAXE, "advancement", "pickaxe", "challenge_pick_swing_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_PICKAXE) - .key("challenge_pick_swing_5k") - .title(Localizer.dLocalize("advancement.challenge_pick_swing_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_swing_5k.description")) - .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "pickaxe", "challenge_pick_swing_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pick_swing_500", "pickaxe.swings", 500, getConfig().emeraldBonus); - registerMilestone("challenge_pick_swing_5k", "pickaxe.swings", 5000, getConfig().emeraldBonus * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_PICKAXE).key("challenge_pick_swing_500") + .title(Localizer.dLocalize("advancement.challenge_pick_swing_500.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_swing_500.description")) + .model(CustomModel.get(Material.WOODEN_PICKAXE, "advancement", "pickaxe", "challenge_pick_swing_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_pick_swing_5k") + .title(Localizer.dLocalize("advancement.challenge_pick_swing_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_swing_5k.description")) + .model(CustomModel.get(Material.IRON_PICKAXE, "advancement", "pickaxe", "challenge_pick_swing_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pick_swing_500", "pickaxe.swings", 500, getConfig().emeraldBonus); + registerMilestone("challenge_pick_swing_5k", "pickaxe.swings", 5000, getConfig().emeraldBonus * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_PICKAXE).key("challenge_pick_damage_1k") - .title(Localizer.dLocalize("advancement.challenge_pick_damage_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_damage_1k.description")) - .model(CustomModel.get(Material.GOLDEN_PICKAXE, "advancement", "pickaxe", "challenge_pick_damage_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_PICKAXE) - .key("challenge_pick_damage_10k") - .title(Localizer.dLocalize("advancement.challenge_pick_damage_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_damage_10k.description")) - .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "pickaxe", "challenge_pick_damage_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pick_damage_1k", "pickaxe.damage", 1000, getConfig().emeraldBonus); - registerMilestone("challenge_pick_damage_10k", "pickaxe.damage", 10000, getConfig().emeraldBonus * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_PICKAXE).key("challenge_pick_damage_1k") + .title(Localizer.dLocalize("advancement.challenge_pick_damage_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_damage_1k.description")) + .model(CustomModel.get(Material.GOLDEN_PICKAXE, "advancement", "pickaxe", "challenge_pick_damage_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_PICKAXE) + .key("challenge_pick_damage_10k") + .title(Localizer.dLocalize("advancement.challenge_pick_damage_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_damage_10k.description")) + .model(CustomModel.get(Material.DIAMOND_PICKAXE, "advancement", "pickaxe", "challenge_pick_damage_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pick_damage_1k", "pickaxe.damage", 1000, getConfig().emeraldBonus); + registerMilestone("challenge_pick_damage_10k", "pickaxe.damage", 10000, getConfig().emeraldBonus * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.RAW_IRON).key("challenge_pick_value_5k") - .title(Localizer.dLocalize("advancement.challenge_pick_value_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_value_5k.description")) - .model(CustomModel.get(Material.RAW_IRON, "advancement", "pickaxe", "challenge_pick_value_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.RAW_GOLD) - .key("challenge_pick_value_50k") - .title(Localizer.dLocalize("advancement.challenge_pick_value_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_value_50k.description")) - .model(CustomModel.get(Material.RAW_GOLD, "advancement", "pickaxe", "challenge_pick_value_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pick_value_5k", "pickaxe.blocks.value", 5000, getConfig().emeraldBonus); - registerMilestone("challenge_pick_value_50k", "pickaxe.blocks.value", 50000, getConfig().emeraldBonus * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.RAW_IRON).key("challenge_pick_value_5k") + .title(Localizer.dLocalize("advancement.challenge_pick_value_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_value_5k.description")) + .model(CustomModel.get(Material.RAW_IRON, "advancement", "pickaxe", "challenge_pick_value_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RAW_GOLD) + .key("challenge_pick_value_50k") + .title(Localizer.dLocalize("advancement.challenge_pick_value_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_value_50k.description")) + .model(CustomModel.get(Material.RAW_GOLD, "advancement", "pickaxe", "challenge_pick_value_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pick_value_5k", "pickaxe.blocks.value", 5000, getConfig().emeraldBonus); + registerMilestone("challenge_pick_value_50k", "pickaxe.blocks.value", 50000, getConfig().emeraldBonus * 2); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_ORE).key("challenge_pick_ores_500") - .title(Localizer.dLocalize("advancement.challenge_pick_ores_500.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_ores_500.description")) - .model(CustomModel.get(Material.IRON_ORE, "advancement", "pickaxe", "challenge_pick_ores_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_ORE) - .key("challenge_pick_ores_5k") - .title(Localizer.dLocalize("advancement.challenge_pick_ores_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_pick_ores_5k.description")) - .model(CustomModel.get(Material.DIAMOND_ORE, "advancement", "pickaxe", "challenge_pick_ores_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_pick_ores_500", "pickaxe.ores", 500, getConfig().emeraldBonus); - registerMilestone("challenge_pick_ores_5k", "pickaxe.ores", 5000, getConfig().emeraldBonus * 2); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_ORE).key("challenge_pick_ores_500") + .title(Localizer.dLocalize("advancement.challenge_pick_ores_500.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_ores_500.description")) + .model(CustomModel.get(Material.IRON_ORE, "advancement", "pickaxe", "challenge_pick_ores_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_ORE) + .key("challenge_pick_ores_5k") + .title(Localizer.dLocalize("advancement.challenge_pick_ores_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pick_ores_5k.description")) + .model(CustomModel.get(Material.DIAMOND_ORE, "advancement", "pickaxe", "challenge_pick_ores_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_pick_ores_500", "pickaxe.ores", 500, getConfig().emeraldBonus); + registerMilestone("challenge_pick_ores_5k", "pickaxe.ores", 5000, getConfig().emeraldBonus * 2); + + } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + Player p = e.getDamager() instanceof Player ? (Player) e.getDamager() : null; + if (!getConfig().getXpForAttackingWithTools || p == null) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - Player p = e.getDamager() instanceof Player ? (Player) e.getDamager() : null; - if (!getConfig().getXpForAttackingWithTools || p == null) { - return; + shouldReturnForPlayer(p, () -> { + if (checkValidEntity(e.getEntity().getType())) { + AdaptPlayer a = getPlayer(p); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (isPickaxe(hand)) { + a.getData().addStat("pickaxe.swings", 1); + a.getData().addStat("pickaxe.damage", e.getDamage()); + handleCooldown(p, () -> xp(p, e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage())); } + } + }); + } - shouldReturnForPlayer(p, () -> { - if (checkValidEntity(e.getEntity().getType())) { - AdaptPlayer a = getPlayer(p); - ItemStack hand = p.getInventory().getItemInMainHand(); - if (isPickaxe(hand)) { - a.getData().addStat("pickaxe.swings", 1); - a.getData().addStat("pickaxe.damage", e.getDamage()); - handleCooldown(p, () -> xp(p, e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage())); - } - } - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(p, () -> { + ItemStack mainHand = p.getInventory().getItemInMainHand(); - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(p, () -> { - ItemStack mainHand = p.getInventory().getItemInMainHand(); + if (isPickaxe(mainHand)) { + Material blockType = e.getBlock().getType(); + double blockValue = getValue(blockType); + AdaptPlayer adaptPlayer = getPlayer(p); - if (isPickaxe(mainHand)) { - Material blockType = e.getBlock().getType(); - double blockValue = getValue(blockType); - AdaptPlayer adaptPlayer = getPlayer(p); - - adaptPlayer.getData().addStat("pickaxe.blocks.broken", 1); - adaptPlayer.getData().addStat("pickaxe.blocks.value", blockValue); - if (blockType.name().contains("_ORE")) { - adaptPlayer.getData().addStat("pickaxe.ores", 1); - } + adaptPlayer.getData().addStat("pickaxe.blocks.broken", 1); + adaptPlayer.getData().addStat("pickaxe.blocks.value", blockValue); + if (blockType.name().contains("_ORE")) { + adaptPlayer.getData().addStat("pickaxe.ores", 1); + } - handleCooldown(p, () -> { - if (mainHand.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { - xp(p, 5); - } else { - Location blockLocation = e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5); - xp(p, blockLocation, blockXP(e.getBlock(), blockValue)); - } - }); - } + handleCooldown(p, () -> { + if (mainHand.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { + xp(p, 5); + } else { + Location blockLocation = e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5); + xp(p, blockLocation, blockXP(e.getBlock(), blockValue)); + } }); - } + } + }); + } - public double getValue(Material type) { - Config c = getConfig(); - double value = super.getValue(type) * c.blockValueMultiplier; - value += Math.min(c.maxHardnessBonus, type.getHardness()); - value += Math.min(c.maxBlastResistanceBonus, type.getBlastResistance()); + public double getValue(Material type) { + Config c = getConfig(); + double value = super.getValue(type) * c.blockValueMultiplier; + value += Math.min(c.maxHardnessBonus, type.getHardness()); + value += Math.min(c.maxBlastResistanceBonus, type.getBlastResistance()); - value += switch (type) { - case COAL_ORE -> c.coalBonus; - case COPPER_ORE -> c.copperBonus; - case IRON_ORE -> c.ironBonus; - case GOLD_ORE -> c.goldBonus; - case LAPIS_ORE -> c.lapisBonus; - case DIAMOND_ORE -> c.diamondBonus; - case EMERALD_ORE -> c.emeraldBonus; - case NETHER_GOLD_ORE -> c.netherGoldBonus; - case NETHER_QUARTZ_ORE -> c.netherQuartzBonus; - case REDSTONE_ORE -> c.redstoneBonus; - case ANCIENT_DEBRIS -> c.debrisBonus; - case DEEPSLATE_COAL_ORE -> c.coalBonus * c.deepslateMultiplier; - case DEEPSLATE_COPPER_ORE -> c.copperBonus * c.deepslateMultiplier; - case DEEPSLATE_IRON_ORE -> c.ironBonus * c.deepslateMultiplier; - case DEEPSLATE_GOLD_ORE -> c.goldBonus * c.deepslateMultiplier; - case DEEPSLATE_LAPIS_ORE -> c.lapisBonus * c.deepslateMultiplier; - case DEEPSLATE_DIAMOND_ORE -> c.diamondBonus * c.deepslateMultiplier; - case DEEPSLATE_EMERALD_ORE -> c.emeraldBonus * c.deepslateMultiplier; - case DEEPSLATE_REDSTONE_ORE -> c.redstoneBonus * c.deepslateMultiplier; - default -> 0; - }; + value += switch (type) { + case COAL_ORE -> c.coalBonus; + case COPPER_ORE -> c.copperBonus; + case IRON_ORE -> c.ironBonus; + case GOLD_ORE -> c.goldBonus; + case LAPIS_ORE -> c.lapisBonus; + case DIAMOND_ORE -> c.diamondBonus; + case EMERALD_ORE -> c.emeraldBonus; + case NETHER_GOLD_ORE -> c.netherGoldBonus; + case NETHER_QUARTZ_ORE -> c.netherQuartzBonus; + case REDSTONE_ORE -> c.redstoneBonus; + case ANCIENT_DEBRIS -> c.debrisBonus; + case DEEPSLATE_COAL_ORE -> c.coalBonus * c.deepslateMultiplier; + case DEEPSLATE_COPPER_ORE -> c.copperBonus * c.deepslateMultiplier; + case DEEPSLATE_IRON_ORE -> c.ironBonus * c.deepslateMultiplier; + case DEEPSLATE_GOLD_ORE -> c.goldBonus * c.deepslateMultiplier; + case DEEPSLATE_LAPIS_ORE -> c.lapisBonus * c.deepslateMultiplier; + case DEEPSLATE_DIAMOND_ORE -> c.diamondBonus * c.deepslateMultiplier; + case DEEPSLATE_EMERALD_ORE -> c.emeraldBonus * c.deepslateMultiplier; + case DEEPSLATE_REDSTONE_ORE -> c.redstoneBonus * c.deepslateMultiplier; + default -> 0; + }; - return value * 0.48; - } + return value * 0.48; + } - private void handleCooldown(Player p, Runnable action) { - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - action.run(); - } + private void handleCooldown(Player p, Runnable action) { + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + action.run(); + } - @Override - public void onTick() { - checkStatTrackersForOnlinePlayers(); - } + @Override + public void onTick() { + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Debris Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double debrisBonus = 210; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&6"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Pickaxes skill.", impact = "True enables this behavior and false disables it.") - boolean getXpForAttackingWithTools = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageXPMultiplier = 6.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Block Value Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double blockValueMultiplier = 0.125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxHardnessBonus = 9; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double maxBlastResistanceBonus = 10; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Coal Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double coalBonus = 18; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Iron Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double ironBonus = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redstone Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double redstoneBonus = 55; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Copper Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double copperBonus = 22; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double goldBonus = 38; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lapisBonus = 75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Diamond Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double diamondBonus = 175; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Emerald Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double emeraldBonus = 210; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double netherGoldBonus = 105; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Quartz Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double netherQuartzBonus = 125; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deepslate Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double deepslateMultiplier = 1.35; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Debris Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double debrisBonus = 210; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&6"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Get Xp For Attacking With Tools for the Pickaxes skill.", impact = "True enables this behavior and false disables it.") + boolean getXpForAttackingWithTools = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageXPMultiplier = 6.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Block Value Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double blockValueMultiplier = 0.125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Hardness Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHardnessBonus = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Blast Resistance Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBlastResistanceBonus = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Coal Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double coalBonus = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Iron Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double ironBonus = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Redstone Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double redstoneBonus = 55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Copper Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double copperBonus = 22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double goldBonus = 38; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lapis Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lapisBonus = 75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Diamond Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double diamondBonus = 175; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Emerald Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double emeraldBonus = 210; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Gold Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double netherGoldBonus = 105; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Nether Quartz Bonus for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double netherQuartzBonus = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Deepslate Multiplier for the Pickaxes skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double deepslateMultiplier = 1.35; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java index d353b4d73..441714b86 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRanged.java @@ -45,244 +45,244 @@ import java.util.UUID; public class SkillRanged extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillRanged() { - super("ranged", Localizer.dLocalize("skill.ranged.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.ranged.description")); - setDisplayName(Localizer.dLocalize("skill.ranged.name")); - setColor(C.DARK_GREEN); - setInterval(3044); - registerAdaptation(new RangedForce()); - registerAdaptation(new RangedPiercing()); - registerAdaptation(new RangedArrowRecovery()); - registerAdaptation(new RangedLungeShot()); - registerAdaptation(new RangedWebBomb()); - registerAdaptation(new RangedTrajectorySight()); - registerAdaptation(new RangedFloaters()); - registerAdaptation(new RangedPinningShot()); - registerAdaptation(new RangedRicochetBolt()); - setIcon(Material.CROSSBOW); - cooldowns = new HashMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW) - .key("challenge_ranged_100") - .title(Localizer.dLocalize("advancement.challenge_ranged_100.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_100.description")) - .model(CustomModel.get(Material.ARROW, "advancement", "ranged", "challenge_ranged_100")) + public SkillRanged() { + super("ranged", Localizer.dLocalize("skill.ranged.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.ranged.description")); + setDisplayName(Localizer.dLocalize("skill.ranged.name")); + setColor(C.DARK_GREEN); + setInterval(3044); + registerAdaptation(new RangedForce()); + registerAdaptation(new RangedPiercing()); + registerAdaptation(new RangedArrowRecovery()); + registerAdaptation(new RangedLungeShot()); + registerAdaptation(new RangedWebBomb()); + registerAdaptation(new RangedTrajectorySight()); + registerAdaptation(new RangedFloaters()); + registerAdaptation(new RangedPinningShot()); + registerAdaptation(new RangedRicochetBolt()); + setIcon(Material.CROSSBOW); + cooldowns = new HashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW) + .key("challenge_ranged_100") + .title(Localizer.dLocalize("advancement.challenge_ranged_100.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_100.description")) + .model(CustomModel.get(Material.ARROW, "advancement", "ranged", "challenge_ranged_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_1k") + .title(Localizer.dLocalize("advancement.challenge_ranged_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_1k.description")) + .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "ranged", "challenge_ranged_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CROSSBOW) + .key("challenge_ranged_10k") + .title(Localizer.dLocalize("advancement.challenge_ranged_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_10k.description")) + .model(CustomModel.get(Material.CROSSBOW, "advancement", "ranged", "challenge_ranged_10k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_1k") - .title(Localizer.dLocalize("advancement.challenge_ranged_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_1k.description")) - .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "ranged", "challenge_ranged_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CROSSBOW) - .key("challenge_ranged_10k") - .title(Localizer.dLocalize("advancement.challenge_ranged_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_10k.description")) - .model(CustomModel.get(Material.CROSSBOW, "advancement", "ranged", "challenge_ranged_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_ranged_100", "ranged.shotsfired", 100, getConfig().challengeRangedReward); - registerMilestone("challenge_ranged_1k", "ranged.shotsfired", 1000, getConfig().challengeRangedReward * 2); - registerMilestone("challenge_ranged_10k", "ranged.shotsfired", 10000, getConfig().challengeRangedReward * 5); + .build()) + .build()) + .build()); + registerMilestone("challenge_ranged_100", "ranged.shotsfired", 100, getConfig().challengeRangedReward); + registerMilestone("challenge_ranged_1k", "ranged.shotsfired", 1000, getConfig().challengeRangedReward * 2); + registerMilestone("challenge_ranged_10k", "ranged.shotsfired", 10000, getConfig().challengeRangedReward * 5); - // Chain 2 - Ranged Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BOW) - .key("challenge_ranged_dmg_1k") - .title(Localizer.dLocalize("advancement.challenge_ranged_dmg_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_dmg_1k.description")) - .model(CustomModel.get(Material.BOW, "advancement", "ranged", "challenge_ranged_dmg_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CROSSBOW) - .key("challenge_ranged_dmg_10k") - .title(Localizer.dLocalize("advancement.challenge_ranged_dmg_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_dmg_10k.description")) - .model(CustomModel.get(Material.CROSSBOW, "advancement", "ranged", "challenge_ranged_dmg_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_dmg_1k", "ranged.damage", 1000, getConfig().challengeRangedDmgReward); - registerMilestone("challenge_ranged_dmg_10k", "ranged.damage", 10000, getConfig().challengeRangedDmgReward * 3); + // Chain 2 - Ranged Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BOW) + .key("challenge_ranged_dmg_1k") + .title(Localizer.dLocalize("advancement.challenge_ranged_dmg_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_dmg_1k.description")) + .model(CustomModel.get(Material.BOW, "advancement", "ranged", "challenge_ranged_dmg_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CROSSBOW) + .key("challenge_ranged_dmg_10k") + .title(Localizer.dLocalize("advancement.challenge_ranged_dmg_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_dmg_10k.description")) + .model(CustomModel.get(Material.CROSSBOW, "advancement", "ranged", "challenge_ranged_dmg_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_dmg_1k", "ranged.damage", 1000, getConfig().challengeRangedDmgReward); + registerMilestone("challenge_ranged_dmg_10k", "ranged.damage", 10000, getConfig().challengeRangedDmgReward * 3); - // Chain 3 - Ranged Distance - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ARROW) - .key("challenge_ranged_dist_5k") - .title(Localizer.dLocalize("advancement.challenge_ranged_dist_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_dist_5k.description")) - .model(CustomModel.get(Material.ARROW, "advancement", "ranged", "challenge_ranged_dist_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SPECTRAL_ARROW) - .key("challenge_ranged_dist_50k") - .title(Localizer.dLocalize("advancement.challenge_ranged_dist_50k.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_dist_50k.description")) - .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "ranged", "challenge_ranged_dist_50k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_dist_5k", "ranged.distance", 5000, getConfig().challengeRangedDistReward); - registerMilestone("challenge_ranged_dist_50k", "ranged.distance", 50000, getConfig().challengeRangedDistReward * 3); + // Chain 3 - Ranged Distance + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ARROW) + .key("challenge_ranged_dist_5k") + .title(Localizer.dLocalize("advancement.challenge_ranged_dist_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_dist_5k.description")) + .model(CustomModel.get(Material.ARROW, "advancement", "ranged", "challenge_ranged_dist_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPECTRAL_ARROW) + .key("challenge_ranged_dist_50k") + .title(Localizer.dLocalize("advancement.challenge_ranged_dist_50k.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_dist_50k.description")) + .model(CustomModel.get(Material.SPECTRAL_ARROW, "advancement", "ranged", "challenge_ranged_dist_50k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_dist_5k", "ranged.distance", 5000, getConfig().challengeRangedDistReward); + registerMilestone("challenge_ranged_dist_50k", "ranged.distance", 50000, getConfig().challengeRangedDistReward * 3); - // Chain 4 - Ranged Kills - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TIPPED_ARROW) - .key("challenge_ranged_kills_50") - .title(Localizer.dLocalize("advancement.challenge_ranged_kills_50.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_kills_50.description")) - .model(CustomModel.get(Material.TIPPED_ARROW, "advancement", "ranged", "challenge_ranged_kills_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TARGET) - .key("challenge_ranged_kills_500") - .title(Localizer.dLocalize("advancement.challenge_ranged_kills_500.title")) - .description(Localizer.dLocalize("advancement.challenge_ranged_kills_500.description")) - .model(CustomModel.get(Material.TARGET, "advancement", "ranged", "challenge_ranged_kills_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_ranged_kills_50", "ranged.kills", 50, getConfig().challengeRangedKillsReward); - registerMilestone("challenge_ranged_kills_500", "ranged.kills", 500, getConfig().challengeRangedKillsReward * 3); + // Chain 4 - Ranged Kills + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TIPPED_ARROW) + .key("challenge_ranged_kills_50") + .title(Localizer.dLocalize("advancement.challenge_ranged_kills_50.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_kills_50.description")) + .model(CustomModel.get(Material.TIPPED_ARROW, "advancement", "ranged", "challenge_ranged_kills_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TARGET) + .key("challenge_ranged_kills_500") + .title(Localizer.dLocalize("advancement.challenge_ranged_kills_500.title")) + .description(Localizer.dLocalize("advancement.challenge_ranged_kills_500.description")) + .model(CustomModel.get(Material.TARGET, "advancement", "ranged", "challenge_ranged_kills_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_ranged_kills_50", "ranged.kills", 50, getConfig().challengeRangedKillsReward); + registerMilestone("challenge_ranged_kills_500", "ranged.kills", 500, getConfig().challengeRangedKillsReward * 3); - // Chain 5 - Longshots - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SPYGLASS) - .key("challenge_longshot_25") - .title(Localizer.dLocalize("advancement.challenge_longshot_25.title")) - .description(Localizer.dLocalize("advancement.challenge_longshot_25.description")) - .model(CustomModel.get(Material.SPYGLASS, "advancement", "ranged", "challenge_longshot_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_longshot_250") - .title(Localizer.dLocalize("advancement.challenge_longshot_250.title")) - .description(Localizer.dLocalize("advancement.challenge_longshot_250.description")) - .model(CustomModel.get(Material.ENDER_EYE, "advancement", "ranged", "challenge_longshot_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_longshot_25", "ranged.longshots", 25, getConfig().challengeRangedLongshotReward); - registerMilestone("challenge_longshot_250", "ranged.longshots", 250, getConfig().challengeRangedLongshotReward * 3); + // Chain 5 - Longshots + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPYGLASS) + .key("challenge_longshot_25") + .title(Localizer.dLocalize("advancement.challenge_longshot_25.title")) + .description(Localizer.dLocalize("advancement.challenge_longshot_25.description")) + .model(CustomModel.get(Material.SPYGLASS, "advancement", "ranged", "challenge_longshot_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_longshot_250") + .title(Localizer.dLocalize("advancement.challenge_longshot_250.title")) + .description(Localizer.dLocalize("advancement.challenge_longshot_250.description")) + .model(CustomModel.get(Material.ENDER_EYE, "advancement", "ranged", "challenge_longshot_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_longshot_25", "ranged.longshots", 25, getConfig().challengeRangedLongshotReward); + registerMilestone("challenge_longshot_250", "ranged.longshots", 250, getConfig().challengeRangedLongshotReward * 3); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity().getShooter() instanceof Player p)) { + return; } + shouldReturnForPlayer(p, e, () -> { + if (e.getEntity() instanceof Snowball || e.getEntity().getType().name().toLowerCase(Locale.ROOT).contains("hook")) { + return; // Ignore snowballs and fishing hooks + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity().getShooter() instanceof Player p)) { - return; - } - shouldReturnForPlayer(p, e, () -> { - if (e.getEntity() instanceof Snowball || e.getEntity().getType().name().toLowerCase(Locale.ROOT).contains("hook")) { - return; // Ignore snowballs and fishing hooks - } + getPlayer(p).getData().addStat("ranged.shotsfired", 1); + getPlayer(p).getData().addStat("ranged.shotsfired." + e.getEntity().getType().name().toLowerCase(Locale.ROOT), 1); + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(p, getConfig().shootXP); + }); + } - getPlayer(p).getData().addStat("ranged.shotsfired", 1); - getPlayer(p).getData().addStat("ranged.shotsfired." + e.getEntity().getType().name().toLowerCase(Locale.ROOT), 1); - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(p, getConfig().shootXP); - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Projectile) || !(((Projectile) e.getDamager()).getShooter() instanceof Player p) || !checkValidEntity(e.getEntity().getType())) { + return; } - - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Projectile) || !(((Projectile) e.getDamager()).getShooter() instanceof Player p) || !checkValidEntity(e.getEntity().getType())) { - return; + shouldReturnForPlayer(p, e, () -> { + if (e.getEntity() instanceof Snowball || e.getEntity() instanceof FishHook) { + return; // Ignore snowballs and fishing hooks + } + if (e.getEntity().getLocation().getWorld().equals(p.getLocation().getWorld())) { + double distance = e.getEntity().getLocation().distance(p.getLocation()); + getPlayer(p).getData().addStat("ranged.distance", distance); + getPlayer(p).getData().addStat("ranged.distance." + e.getDamager().getType().name().toLowerCase(Locale.ROOT), distance); + if (distance > 30) { + getPlayer(p).getData().addStat("ranged.longshots", 1); } - shouldReturnForPlayer(p, e, () -> { - if (e.getEntity() instanceof Snowball || e.getEntity() instanceof FishHook) { - return; // Ignore snowballs and fishing hooks - } - if (e.getEntity().getLocation().getWorld().equals(p.getLocation().getWorld())) { - double distance = e.getEntity().getLocation().distance(p.getLocation()); - getPlayer(p).getData().addStat("ranged.distance", distance); - getPlayer(p).getData().addStat("ranged.distance." + e.getDamager().getType().name().toLowerCase(Locale.ROOT), distance); - if (distance > 30) { - getPlayer(p).getData().addStat("ranged.longshots", 1); - } - } - getPlayer(p).getData().addStat("ranged.damage", e.getDamage()); - getPlayer(p).getData().addStat("ranged.damage." + e.getDamager().getType().name().toLowerCase(Locale.ROOT), e.getDamage()); - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(p, e.getEntity().getLocation(), (getConfig().hitDamageXPMultiplier * e.getDamage()) + (e.getEntity().getLocation().distance(p.getLocation()) * getConfig().hitDistanceXPMultiplier)); + } + getPlayer(p).getData().addStat("ranged.damage", e.getDamage()); + getPlayer(p).getData().addStat("ranged.damage." + e.getDamager().getType().name().toLowerCase(Locale.ROOT), e.getDamage()); + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(p, e.getEntity().getLocation(), (getConfig().hitDamageXPMultiplier * e.getDamage()) + (e.getEntity().getLocation().distance(p.getLocation()) * getConfig().hitDistanceXPMultiplier)); - }); - } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - Player p = e.getEntity().getKiller(); - if (p == null) { - return; - } - shouldReturnForPlayer(p, () -> { - ItemStack hand = p.getInventory().getItemInMainHand(); - if (hand.getType() == Material.BOW || hand.getType() == Material.CROSSBOW) { - getPlayer(p).getData().addStat("ranged.kills", 1); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + Player p = e.getEntity().getKiller(); + if (p == null) { + return; } + shouldReturnForPlayer(p, () -> { + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand.getType() == Material.BOW || hand.getType() == Material.CROSSBOW) { + getPlayer(p).getData().addStat("ranged.kills", 1); + } + }); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&2"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shoot XP for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double shootXP = 5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Damage XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hitDamageXPMultiplier = 1.75; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Distance XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double hitDistanceXPMultiplier = 1.2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRangedReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Damage Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRangedDmgReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Distance Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRangedDistReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Kills Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRangedKillsReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Longshot Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRangedLongshotReward = 500; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&2"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Shoot XP for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double shootXP = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Damage XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hitDamageXPMultiplier = 1.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Hit Distance XPMultiplier for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double hitDistanceXPMultiplier = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRangedReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Damage Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRangedDmgReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Distance Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRangedDistReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Kills Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRangedKillsReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Ranged Longshot Reward for the Ranged skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRangedLongshotReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java index a42e82f63..ffcbd9904 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillRift.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillRift.java @@ -47,282 +47,282 @@ import java.util.UUID; public class SkillRift extends SimpleSkill { - private final KMap lasttp; + private final KMap lasttp; - public SkillRift() { - super("rift", Localizer.dLocalize("skill.rift.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.rift.description")); - setDisplayName(Localizer.dLocalize("skill.rift.name")); - setColor(C.DARK_PURPLE); - setInterval(1154); - setIcon(Material.ENDER_EYE); - registerAdaptation(new RiftResist()); - registerAdaptation(new RiftAccess()); - registerAdaptation(new RiftEnderchest()); - registerAdaptation(new RiftGate()); - registerAdaptation(new RiftBlink()); - registerAdaptation(new RiftDescent()); - registerAdaptation(new RiftVisage()); - registerAdaptation(new RiftEnderTaglock()); - registerAdaptation(new RiftInflatedPocketDimension()); - registerAdaptation(new RiftVoidMagnet()); - lasttp = new KMap<>(); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_50") - .title(Localizer.dLocalize("advancement.challenge_rift_50.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_50.description")) - .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_50")) + public SkillRift() { + super("rift", Localizer.dLocalize("skill.rift.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.rift.description")); + setDisplayName(Localizer.dLocalize("skill.rift.name")); + setColor(C.DARK_PURPLE); + setInterval(1154); + setIcon(Material.ENDER_EYE); + registerAdaptation(new RiftResist()); + registerAdaptation(new RiftAccess()); + registerAdaptation(new RiftEnderchest()); + registerAdaptation(new RiftGate()); + registerAdaptation(new RiftBlink()); + registerAdaptation(new RiftDescent()); + registerAdaptation(new RiftVisage()); + registerAdaptation(new RiftEnderTaglock()); + registerAdaptation(new RiftInflatedPocketDimension()); + registerAdaptation(new RiftVoidMagnet()); + lasttp = new KMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_50") + .title(Localizer.dLocalize("advancement.challenge_rift_50.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_50.description")) + .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_500") + .title(Localizer.dLocalize("advancement.challenge_rift_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_500.description")) + .model(CustomModel.get(Material.ENDER_EYE, "advancement", "rift", "challenge_rift_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.END_CRYSTAL) + .key("challenge_rift_5k") + .title(Localizer.dLocalize("advancement.challenge_rift_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_5k.description")) + .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "rift", "challenge_rift_5k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_500") - .title(Localizer.dLocalize("advancement.challenge_rift_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_500.description")) - .model(CustomModel.get(Material.ENDER_EYE, "advancement", "rift", "challenge_rift_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.END_CRYSTAL) - .key("challenge_rift_5k") - .title(Localizer.dLocalize("advancement.challenge_rift_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_5k.description")) - .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "rift", "challenge_rift_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_rift_50", "rift.teleports", 50, getConfig().challengeRiftReward); - registerMilestone("challenge_rift_500", "rift.teleports", 500, getConfig().challengeRiftReward * 2); - registerMilestone("challenge_rift_5k", "rift.teleports", 5000, getConfig().challengeRiftReward * 5); + .build()) + .build()) + .build()); + registerMilestone("challenge_rift_50", "rift.teleports", 50, getConfig().challengeRiftReward); + registerMilestone("challenge_rift_500", "rift.teleports", 500, getConfig().challengeRiftReward * 2); + registerMilestone("challenge_rift_5k", "rift.teleports", 5000, getConfig().challengeRiftReward * 5); - // Chain 2 - Ender Pearl Throws - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_pearls_50") - .title(Localizer.dLocalize("advancement.challenge_rift_pearls_50.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_pearls_50.description")) - .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_pearls_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENDER_EYE) - .key("challenge_rift_pearls_500") - .title(Localizer.dLocalize("advancement.challenge_rift_pearls_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_pearls_500.description")) - .model(CustomModel.get(Material.ENDER_EYE, "advancement", "rift", "challenge_rift_pearls_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_pearls_50", "rift.ender.pearls", 50, getConfig().challengeRiftReward); - registerMilestone("challenge_rift_pearls_500", "rift.ender.pearls", 500, getConfig().challengeRiftReward * 2); + // Chain 2 - Ender Pearl Throws + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_pearls_50") + .title(Localizer.dLocalize("advancement.challenge_rift_pearls_50.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_pearls_50.description")) + .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_pearls_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_rift_pearls_500") + .title(Localizer.dLocalize("advancement.challenge_rift_pearls_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_pearls_500.description")) + .model(CustomModel.get(Material.ENDER_EYE, "advancement", "rift", "challenge_rift_pearls_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_pearls_50", "rift.ender.pearls", 50, getConfig().challengeRiftReward); + registerMilestone("challenge_rift_pearls_500", "rift.ender.pearls", 500, getConfig().challengeRiftReward * 2); - // Chain 3 - Enderman Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ENDER_PEARL) - .key("challenge_rift_enderman_50") - .title(Localizer.dLocalize("advancement.challenge_rift_enderman_50.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_enderman_50.description")) - .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_enderman_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.END_STONE) - .key("challenge_rift_enderman_500") - .title(Localizer.dLocalize("advancement.challenge_rift_enderman_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_enderman_500.description")) - .model(CustomModel.get(Material.END_STONE, "advancement", "rift", "challenge_rift_enderman_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_enderman_50", "rift.enderman.kills", 50, getConfig().challengeRiftReward); - registerMilestone("challenge_rift_enderman_500", "rift.enderman.kills", 500, getConfig().challengeRiftReward * 2); + // Chain 3 - Enderman Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_PEARL) + .key("challenge_rift_enderman_50") + .title(Localizer.dLocalize("advancement.challenge_rift_enderman_50.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_enderman_50.description")) + .model(CustomModel.get(Material.ENDER_PEARL, "advancement", "rift", "challenge_rift_enderman_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.END_STONE) + .key("challenge_rift_enderman_500") + .title(Localizer.dLocalize("advancement.challenge_rift_enderman_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_enderman_500.description")) + .model(CustomModel.get(Material.END_STONE, "advancement", "rift", "challenge_rift_enderman_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_enderman_50", "rift.enderman.kills", 50, getConfig().challengeRiftReward); + registerMilestone("challenge_rift_enderman_500", "rift.enderman.kills", 500, getConfig().challengeRiftReward * 2); - // Chain 4 - Dragon Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DRAGON_BREATH) - .key("challenge_rift_dragon_500") - .title(Localizer.dLocalize("advancement.challenge_rift_dragon_500.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_dragon_500.description")) - .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "rift", "challenge_rift_dragon_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DRAGON_HEAD) - .key("challenge_rift_dragon_5k") - .title(Localizer.dLocalize("advancement.challenge_rift_dragon_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_dragon_5k.description")) - .model(CustomModel.get(Material.DRAGON_HEAD, "advancement", "rift", "challenge_rift_dragon_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_dragon_500", "rift.dragon.damage", 500, getConfig().challengeRiftReward); - registerMilestone("challenge_rift_dragon_5k", "rift.dragon.damage", 5000, getConfig().challengeRiftReward * 2); + // Chain 4 - Dragon Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DRAGON_BREATH) + .key("challenge_rift_dragon_500") + .title(Localizer.dLocalize("advancement.challenge_rift_dragon_500.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_dragon_500.description")) + .model(CustomModel.get(Material.DRAGON_BREATH, "advancement", "rift", "challenge_rift_dragon_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DRAGON_HEAD) + .key("challenge_rift_dragon_5k") + .title(Localizer.dLocalize("advancement.challenge_rift_dragon_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_dragon_5k.description")) + .model(CustomModel.get(Material.DRAGON_HEAD, "advancement", "rift", "challenge_rift_dragon_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_dragon_500", "rift.dragon.damage", 500, getConfig().challengeRiftReward); + registerMilestone("challenge_rift_dragon_5k", "rift.dragon.damage", 5000, getConfig().challengeRiftReward * 2); - // Chain 5 - End Crystal Destruction - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.END_CRYSTAL) - .key("challenge_rift_crystal_10") - .title(Localizer.dLocalize("advancement.challenge_rift_crystal_10.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_crystal_10.description")) - .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "rift", "challenge_rift_crystal_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BEACON) - .key("challenge_rift_crystal_100") - .title(Localizer.dLocalize("advancement.challenge_rift_crystal_100.title")) - .description(Localizer.dLocalize("advancement.challenge_rift_crystal_100.description")) - .model(CustomModel.get(Material.BEACON, "advancement", "rift", "challenge_rift_crystal_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_rift_crystal_10", "rift.crystals.destroyed", 10, getConfig().challengeRiftReward); - registerMilestone("challenge_rift_crystal_100", "rift.crystals.destroyed", 100, getConfig().challengeRiftReward * 2); + // Chain 5 - End Crystal Destruction + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.END_CRYSTAL) + .key("challenge_rift_crystal_10") + .title(Localizer.dLocalize("advancement.challenge_rift_crystal_10.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_crystal_10.description")) + .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "rift", "challenge_rift_crystal_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BEACON) + .key("challenge_rift_crystal_100") + .title(Localizer.dLocalize("advancement.challenge_rift_crystal_100.title")) + .description(Localizer.dLocalize("advancement.challenge_rift_crystal_100.description")) + .model(CustomModel.get(Material.BEACON, "advancement", "rift", "challenge_rift_crystal_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_rift_crystal_10", "rift.crystals.destroyed", 10, getConfig().challengeRiftReward); + registerMilestone("challenge_rift_crystal_100", "rift.crystals.destroyed", 100, getConfig().challengeRiftReward * 2); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerTeleportEvent e) { + Player p = e.getPlayer(); + if (ChronosInstantRecall.isRecallTeleportSuppressed(p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerTeleportEvent e) { - Player p = e.getPlayer(); - if (ChronosInstantRecall.isRecallTeleportSuppressed(p)) { - return; - } + shouldReturnForPlayer(e.getPlayer(), e, () -> { + getPlayer(p).getData().addStat("rift.teleports", 1); + if (!lasttp.containsKey(p.getUniqueId())) { + xpSilent(p, getConfig().teleportXP, "rift:teleport"); + lasttp.put(p.getUniqueId(), M.ms()); + } + }); + } - shouldReturnForPlayer(e.getPlayer(), e, () -> { - getPlayer(p).getData().addStat("rift.teleports", 1); - if (!lasttp.containsKey(p.getUniqueId())) { - xpSilent(p, getConfig().teleportXP, "rift:teleport"); - lasttp.put(p.getUniqueId(), M.ms()); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity().getShooter() instanceof Player p)) { + return; } + shouldReturnForPlayer(p, e, () -> { + if (e.getEntity() instanceof EnderPearl) { + xp(p, getConfig().throwEnderpearlXP, "rift:throw:ender-pearl"); + getPlayer(p).getData().addStat("rift.ender.pearls", 1); + } else if (e.getEntity() instanceof EnderSignal) { + xp(p, getConfig().throwEnderEyeXP, "rift:throw:ender-eye"); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity().getShooter() instanceof Player p)) { - return; - } - shouldReturnForPlayer(p, e, () -> { - if (e.getEntity() instanceof EnderPearl) { - xp(p, getConfig().throwEnderpearlXP, "rift:throw:ender-pearl"); - getPlayer(p).getData().addStat("rift.ender.pearls", 1); - } else if (e.getEntity() instanceof EnderSignal) { - xp(p, getConfig().throwEnderEyeXP, "rift:throw:ender-eye"); - } - }); + private void handleEntityDamageByEntity(Entity entity, Player p, double damage) { + if (entity instanceof LivingEntity living) { + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(living, Attributes.GENERIC_MAX_HEALTH); + double baseHealth = attribute == null ? 1 : attribute.getBaseValue(); + double multiplier = switch (entity.getType()) { + case ENDERMAN -> getConfig().damageEndermanXPMultiplier; + case ENDERMITE -> getConfig().damageEndermiteXPMultiplier; + case ENDER_DRAGON -> getConfig().damageEnderdragonXPMultiplier; + default -> 0; + }; + double xp = multiplier * Math.min(damage, baseHealth); + String rewardKey = switch (entity.getType()) { + case ENDERMAN -> "rift:damage:enderman"; + case ENDERMITE -> "rift:damage:endermite"; + case ENDER_DRAGON -> "rift:damage:ender-dragon"; + default -> "rift:damage:other"; + }; + if (xp > 0) xp(p, xp, rewardKey); + if (entity.getType() == EntityType.ENDERMAN) { + getPlayer(p).getData().addStat("rift.enderman.kills", 1); + } else if (entity.getType() == EntityType.ENDER_DRAGON) { + getPlayer(p).getData().addStat("rift.dragon.damage", damage); + } + } else if (entity.getType() == EntityTypes.ENDER_CRYSTAL) { + xp(p, getConfig().damageEndCrystalXP, "rift:damage:end-crystal"); } + } - private void handleEntityDamageByEntity(Entity entity, Player p, double damage) { - if (entity instanceof LivingEntity living) { - var attribute = Version.get().getAttribute(living, Attributes.GENERIC_MAX_HEALTH); - double baseHealth = attribute == null ? 1 : attribute.getBaseValue(); - double multiplier = switch (entity.getType()) { - case ENDERMAN -> getConfig().damageEndermanXPMultiplier; - case ENDERMITE -> getConfig().damageEndermiteXPMultiplier; - case ENDER_DRAGON -> getConfig().damageEnderdragonXPMultiplier; - default -> 0; - }; - double xp = multiplier * Math.min(damage, baseHealth); - String rewardKey = switch (entity.getType()) { - case ENDERMAN -> "rift:damage:enderman"; - case ENDERMITE -> "rift:damage:endermite"; - case ENDER_DRAGON -> "rift:damage:ender-dragon"; - default -> "rift:damage:other"; - }; - if (xp > 0) xp(p, xp, rewardKey); - if (entity.getType() == EntityType.ENDERMAN) { - getPlayer(p).getData().addStat("rift.enderman.kills", 1); - } else if (entity.getType() == EntityType.ENDER_DRAGON) { - getPlayer(p).getData().addStat("rift.dragon.damage", damage); - } - } else if (entity.getType() == EntityTypes.ENDER_CRYSTAL) { - xp(p, getConfig().damageEndCrystalXP, "rift:damage:end-crystal"); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> handleEntityDamageByEntity(e.getEntity(), p, e.getDamage())); + } else if (e.getDamager() instanceof Projectile j && j.getShooter() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> handleEntityDamageByEntity(e.getEntity(), p, e.getDamage())); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> handleEntityDamageByEntity(e.getEntity(), p, e.getDamage())); - } else if (e.getDamager() instanceof Projectile j && j.getShooter() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> handleEntityDamageByEntity(e.getEntity(), p, e.getDamage())); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity() instanceof EnderCrystal && e.getEntity().getKiller() != null) { + Player p = e.getEntity().getKiller(); + shouldReturnForPlayer(p, () -> { + xp(e.getEntity().getKiller(), getConfig().destroyEndCrystalXP, "rift:kill:end-crystal"); + getPlayer(p).getData().addStat("rift.crystals.destroyed", 1); + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity() instanceof EnderCrystal && e.getEntity().getKiller() != null) { - Player p = e.getEntity().getKiller(); - shouldReturnForPlayer(p, () -> { - xp(e.getEntity().getKiller(), getConfig().destroyEndCrystalXP, "rift:kill:end-crystal"); - getPlayer(p).getData().addStat("rift.crystals.destroyed", 1); - }); - } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + Player p = e.getPlayer(); + lasttp.remove(p.getUniqueId()); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerQuitEvent e) { - Player p = e.getPlayer(); - lasttp.remove(p.getUniqueId()); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + for (UUID playerId : lasttp.k()) { + Player player = Bukkit.getPlayer(playerId); + if (player == null || !player.isOnline()) { + lasttp.remove(playerId); + continue; + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - for (UUID playerId : lasttp.k()) { - Player player = Bukkit.getPlayer(playerId); - if (player == null || !player.isOnline()) { - lasttp.remove(playerId); - continue; - } - - shouldReturnForPlayer(player, () -> { - if (M.ms() - lasttp.get(playerId) > getConfig().teleportXPCooldown) { - lasttp.remove(playerId); - } - }); + shouldReturnForPlayer(player, () -> { + if (M.ms() - lasttp.get(playerId) > getConfig().teleportXPCooldown) { + lasttp.remove(playerId); } - checkStatTrackersForOnlinePlayers(); + }); } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&5"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Destroy End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double destroyEndCrystalXP = 250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageEndCrystalXP = 110; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderman XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageEndermanXPMultiplier = 4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Endermite XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageEndermiteXPMultiplier = 2; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderdragon XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageEnderdragonXPMultiplier = 8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Enderpearl XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double throwEnderpearlXP = 65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Ender Eye XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double throwEnderEyeXP = 30; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double teleportXP = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XPCooldown for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double teleportXPCooldown = 60000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Rift Reward for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeRiftReward = 500; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&5"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Destroy End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double destroyEndCrystalXP = 250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage End Crystal XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageEndCrystalXP = 110; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderman XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageEndermanXPMultiplier = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Endermite XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageEndermiteXPMultiplier = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Enderdragon XPMultiplier for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageEnderdragonXPMultiplier = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Enderpearl XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double throwEnderpearlXP = 65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Throw Ender Eye XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double throwEnderEyeXP = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XP for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double teleportXP = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Teleport XPCooldown for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double teleportXPCooldown = 60000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Rift Reward for the Rift skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeRiftReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java index 501f483e8..84bb6339b 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSeaborne.java @@ -48,263 +48,263 @@ import java.util.UUID; public class SkillSeaborne extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillSeaborne() { - super("seaborne", Localizer.dLocalize("skill.seaborne.icon")); - registerConfiguration(Config.class); - setColor(C.BLUE); - setDescription(Localizer.dLocalize("skill.seaborne.description")); - setDisplayName(Localizer.dLocalize("skill.seaborne.name")); - setInterval(2120); - setIcon(Material.TRIDENT); - registerAdaptation(new SeaborneOxygen()); - registerAdaptation(new SeaborneSpeed()); - registerAdaptation(new SeaborneFishersFantasy()); - registerAdaptation(new SeaborneTurtlesVision()); - registerAdaptation(new SeaborneTurtlesMiningSpeed()); - registerAdaptation(new SeaborneTidecaller()); - registerAdaptation(new SeabornePressureDiver()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TURTLE_HELMET) - .key("challenge_swim_1nm") - .title(Localizer.dLocalize("advancement.challenge_swim_1nm.title")) - .description(Localizer.dLocalize("advancement.challenge_swim_1nm.description")) - .model(CustomModel.get(Material.TURTLE_HELMET, "advancement", "seaborne", "challenge_swim_1nm")) + public SkillSeaborne() { + super("seaborne", Localizer.dLocalize("skill.seaborne.icon")); + registerConfiguration(Config.class); + setColor(C.BLUE); + setDescription(Localizer.dLocalize("skill.seaborne.description")); + setDisplayName(Localizer.dLocalize("skill.seaborne.name")); + setInterval(2120); + setIcon(Material.TRIDENT); + registerAdaptation(new SeaborneOxygen()); + registerAdaptation(new SeaborneSpeed()); + registerAdaptation(new SeaborneFishersFantasy()); + registerAdaptation(new SeaborneTurtlesVision()); + registerAdaptation(new SeaborneTurtlesMiningSpeed()); + registerAdaptation(new SeaborneTidecaller()); + registerAdaptation(new SeabornePressureDiver()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TURTLE_HELMET) + .key("challenge_swim_1nm") + .title(Localizer.dLocalize("advancement.challenge_swim_1nm.title")) + .description(Localizer.dLocalize("advancement.challenge_swim_1nm.description")) + .model(CustomModel.get(Material.TURTLE_HELMET, "advancement", "seaborne", "challenge_swim_1nm")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HEART_OF_THE_SEA) + .key("challenge_swim_5k") + .title(Localizer.dLocalize("advancement.challenge_swim_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_swim_5k.description")) + .model(CustomModel.get(Material.HEART_OF_THE_SEA, "advancement", "seaborne", "challenge_swim_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TRIDENT) + .key("challenge_swim_20k") + .title(Localizer.dLocalize("advancement.challenge_swim_20k.title")) + .description(Localizer.dLocalize("advancement.challenge_swim_20k.description")) + .model(CustomModel.get(Material.TRIDENT, "advancement", "seaborne", "challenge_swim_20k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HEART_OF_THE_SEA) - .key("challenge_swim_5k") - .title(Localizer.dLocalize("advancement.challenge_swim_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_swim_5k.description")) - .model(CustomModel.get(Material.HEART_OF_THE_SEA, "advancement", "seaborne", "challenge_swim_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TRIDENT) - .key("challenge_swim_20k") - .title(Localizer.dLocalize("advancement.challenge_swim_20k.title")) - .description(Localizer.dLocalize("advancement.challenge_swim_20k.description")) - .model(CustomModel.get(Material.TRIDENT, "advancement", "seaborne", "challenge_swim_20k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_swim_1nm", "move.swim", 1852, getConfig().challengeSwim1nmReward); - registerMilestone("challenge_swim_5k", "move.swim", 5000, getConfig().challengeSwim5kReward); - registerMilestone("challenge_swim_20k", "move.swim", 20000, getConfig().challengeSwim20kReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FISHING_ROD) - .key("challenge_fish_25") - .title(Localizer.dLocalize("advancement.challenge_fish_25.title")) - .description(Localizer.dLocalize("advancement.challenge_fish_25.description")) - .model(CustomModel.get(Material.FISHING_ROD, "advancement", "seaborne", "challenge_fish_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TROPICAL_FISH) - .key("challenge_fish_250") - .title(Localizer.dLocalize("advancement.challenge_fish_250.title")) - .description(Localizer.dLocalize("advancement.challenge_fish_250.description")) - .model(CustomModel.get(Material.TROPICAL_FISH, "advancement", "seaborne", "challenge_fish_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_fish_25", "seaborne.fish.caught", 25, getConfig().challengeSwim1nmReward); - registerMilestone("challenge_fish_250", "seaborne.fish.caught", 250, getConfig().challengeSwim1nmReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ROTTEN_FLESH) - .key("challenge_drowned_25") - .title(Localizer.dLocalize("advancement.challenge_drowned_25.title")) - .description(Localizer.dLocalize("advancement.challenge_drowned_25.description")) - .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "seaborne", "challenge_drowned_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.TRIDENT) - .key("challenge_drowned_250") - .title(Localizer.dLocalize("advancement.challenge_drowned_250.title")) - .description(Localizer.dLocalize("advancement.challenge_drowned_250.description")) - .model(CustomModel.get(Material.TRIDENT, "advancement", "seaborne", "challenge_drowned_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_drowned_25", "seaborne.drowned.kills", 25, getConfig().challengeSwim1nmReward); - registerMilestone("challenge_drowned_250", "seaborne.drowned.kills", 250, getConfig().challengeSwim1nmReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.PRISMARINE_SHARD) - .key("challenge_guardian_10") - .title(Localizer.dLocalize("advancement.challenge_guardian_10.title")) - .description(Localizer.dLocalize("advancement.challenge_guardian_10.description")) - .model(CustomModel.get(Material.PRISMARINE_SHARD, "advancement", "seaborne", "challenge_guardian_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.SEA_LANTERN) - .key("challenge_guardian_100") - .title(Localizer.dLocalize("advancement.challenge_guardian_100.title")) - .description(Localizer.dLocalize("advancement.challenge_guardian_100.description")) - .model(CustomModel.get(Material.SEA_LANTERN, "advancement", "seaborne", "challenge_guardian_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_guardian_10", "seaborne.guardian.kills", 10, getConfig().challengeSwim1nmReward); - registerMilestone("challenge_guardian_100", "seaborne.guardian.kills", 100, getConfig().challengeSwim1nmReward); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.PRISMARINE) - .key("challenge_underwater_blocks_100") - .title(Localizer.dLocalize("advancement.challenge_underwater_blocks_100.title")) - .description(Localizer.dLocalize("advancement.challenge_underwater_blocks_100.description")) - .model(CustomModel.get(Material.PRISMARINE, "advancement", "seaborne", "challenge_underwater_blocks_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CONDUIT) - .key("challenge_underwater_blocks_1k") - .title(Localizer.dLocalize("advancement.challenge_underwater_blocks_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_underwater_blocks_1k.description")) - .model(CustomModel.get(Material.CONDUIT, "advancement", "seaborne", "challenge_underwater_blocks_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_underwater_blocks_100", "seaborne.underwater.blocks", 100, getConfig().challengeSwim1nmReward); - registerMilestone("challenge_underwater_blocks_1k", "seaborne.underwater.blocks", 1000, getConfig().challengeSwim1nmReward); - cooldowns = new HashMap<>(); - } + .build()) + .build()) + .build()); + registerMilestone("challenge_swim_1nm", "move.swim", 1852, getConfig().challengeSwim1nmReward); + registerMilestone("challenge_swim_5k", "move.swim", 5000, getConfig().challengeSwim5kReward); + registerMilestone("challenge_swim_20k", "move.swim", 20000, getConfig().challengeSwim20kReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FISHING_ROD) + .key("challenge_fish_25") + .title(Localizer.dLocalize("advancement.challenge_fish_25.title")) + .description(Localizer.dLocalize("advancement.challenge_fish_25.description")) + .model(CustomModel.get(Material.FISHING_ROD, "advancement", "seaborne", "challenge_fish_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TROPICAL_FISH) + .key("challenge_fish_250") + .title(Localizer.dLocalize("advancement.challenge_fish_250.title")) + .description(Localizer.dLocalize("advancement.challenge_fish_250.description")) + .model(CustomModel.get(Material.TROPICAL_FISH, "advancement", "seaborne", "challenge_fish_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_fish_25", "seaborne.fish.caught", 25, getConfig().challengeSwim1nmReward); + registerMilestone("challenge_fish_250", "seaborne.fish.caught", 250, getConfig().challengeSwim1nmReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ROTTEN_FLESH) + .key("challenge_drowned_25") + .title(Localizer.dLocalize("advancement.challenge_drowned_25.title")) + .description(Localizer.dLocalize("advancement.challenge_drowned_25.description")) + .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "seaborne", "challenge_drowned_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TRIDENT) + .key("challenge_drowned_250") + .title(Localizer.dLocalize("advancement.challenge_drowned_250.title")) + .description(Localizer.dLocalize("advancement.challenge_drowned_250.description")) + .model(CustomModel.get(Material.TRIDENT, "advancement", "seaborne", "challenge_drowned_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_drowned_25", "seaborne.drowned.kills", 25, getConfig().challengeSwim1nmReward); + registerMilestone("challenge_drowned_250", "seaborne.drowned.kills", 250, getConfig().challengeSwim1nmReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.PRISMARINE_SHARD) + .key("challenge_guardian_10") + .title(Localizer.dLocalize("advancement.challenge_guardian_10.title")) + .description(Localizer.dLocalize("advancement.challenge_guardian_10.description")) + .model(CustomModel.get(Material.PRISMARINE_SHARD, "advancement", "seaborne", "challenge_guardian_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SEA_LANTERN) + .key("challenge_guardian_100") + .title(Localizer.dLocalize("advancement.challenge_guardian_100.title")) + .description(Localizer.dLocalize("advancement.challenge_guardian_100.description")) + .model(CustomModel.get(Material.SEA_LANTERN, "advancement", "seaborne", "challenge_guardian_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_guardian_10", "seaborne.guardian.kills", 10, getConfig().challengeSwim1nmReward); + registerMilestone("challenge_guardian_100", "seaborne.guardian.kills", 100, getConfig().challengeSwim1nmReward); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.PRISMARINE) + .key("challenge_underwater_blocks_100") + .title(Localizer.dLocalize("advancement.challenge_underwater_blocks_100.title")) + .description(Localizer.dLocalize("advancement.challenge_underwater_blocks_100.description")) + .model(CustomModel.get(Material.PRISMARINE, "advancement", "seaborne", "challenge_underwater_blocks_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CONDUIT) + .key("challenge_underwater_blocks_1k") + .title(Localizer.dLocalize("advancement.challenge_underwater_blocks_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_underwater_blocks_1k.description")) + .model(CustomModel.get(Material.CONDUIT, "advancement", "seaborne", "challenge_underwater_blocks_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_underwater_blocks_100", "seaborne.underwater.blocks", 100, getConfig().challengeSwim1nmReward); + registerMilestone("challenge_underwater_blocks_1k", "seaborne.underwater.blocks", 1000, getConfig().challengeSwim1nmReward); + cooldowns = new HashMap<>(); + } - private boolean isOnCooldown(Player p, long cooldown) { - Long lastCooldown = cooldowns.get(p.getUniqueId()); - return lastCooldown != null && lastCooldown + cooldown > System.currentTimeMillis(); - } + private boolean isOnCooldown(Player p, long cooldown) { + Long lastCooldown = cooldowns.get(p.getUniqueId()); + return lastCooldown != null && lastCooldown + cooldown > System.currentTimeMillis(); + } - private void setCooldown(Player p) { - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - } + private void setCooldown(Player p) { + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; + @Override + public void onTick() { + if (!this.isEnabled()) { + return; + } + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> { + if ((i.isInWater() || i.isSwimming()) && i.getRemainingAir() < i.getMaximumAir()) { + Adapt.verbose("seaborne Tick"); + checkStatTrackers(adaptPlayer); + xpSilent(i, getConfig().swimXP, "seaborne:swim"); } - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> { - if ((i.isInWater() || i.isSwimming()) && i.getRemainingAir() < i.getMaximumAir()) { - Adapt.verbose("seaborne Tick"); - checkStatTrackers(adaptPlayer); - xpSilent(i, getConfig().swimXP, "seaborne:swim"); - } - }); + }); - } } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerFishEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (e.getState().equals(PlayerFishEvent.State.CAUGHT_FISH)) { - getPlayer(p).getData().addStat("seaborne.fish.caught", 1); - xp(p, 250); - } else if (e.getState().equals(PlayerFishEvent.State.CAUGHT_ENTITY)) { - xp(p, 10); - } - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerFishEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (e.getState().equals(PlayerFishEvent.State.CAUGHT_FISH)) { + getPlayer(p).getData().addStat("seaborne.fish.caught", 1); + xp(p, 250); + } else if (e.getState().equals(PlayerFishEvent.State.CAUGHT_ENTITY)) { + xp(p, 10); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(BlockBreakEvent e) { - Player p = e.getPlayer(); - shouldReturnForPlayer(e.getPlayer(), e, () -> { - if (isOnCooldown(p, getConfig().seaPickleCooldown)) { - return; - } - setCooldown(p); - if (p.isSwimming() || p.isInWater()) { - getPlayer(p).getData().addStat("seaborne.underwater.blocks", 1); - } - if (e.getBlock().getType().equals(Material.SEA_PICKLE) && p.isSwimming() && p.getRemainingAir() < p.getMaximumAir()) { // BECAUSE I LIKE PICKLES - xpSilent(p, 10, "seaborne:sea-pickle"); - } else { - xpSilent(p, 3, "seaborne:underwater-block"); - } - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + shouldReturnForPlayer(e.getPlayer(), e, () -> { + if (isOnCooldown(p, getConfig().seaPickleCooldown)) { + return; + } + setCooldown(p); + if (p.isSwimming() || p.isInWater()) { + getPlayer(p).getData().addStat("seaborne.underwater.blocks", 1); + } + if (e.getBlock().getType().equals(Material.SEA_PICKLE) && p.isSwimming() && p.getRemainingAir() < p.getMaximumAir()) { // BECAUSE I LIKE PICKLES + xpSilent(p, 10, "seaborne:sea-pickle"); + } else { + xpSilent(p, 3, "seaborne:underwater-block"); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - Player p = e.getEntity().getKiller(); - if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { - return; - } - shouldReturnForPlayer(p, () -> { - if (e.getEntityType() == EntityType.DROWNED) { - getPlayer(p).getData().addStat("seaborne.drowned.kills", 1); - } else if (e.getEntityType() == EntityType.GUARDIAN || e.getEntityType() == EntityType.ELDER_GUARDIAN) { - getPlayer(p).getData().addStat("seaborne.guardian.kills", 1); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + Player p = e.getEntity().getKiller(); + if (p == null || !p.getClass().getSimpleName().equals("CraftPlayer")) { + return; } + shouldReturnForPlayer(p, () -> { + if (e.getEntityType() == EntityType.DROWNED) { + getPlayer(p).getData().addStat("seaborne.drowned.kills", 1); + } else if (e.getEntityType() == EntityType.GUARDIAN || e.getEntityType() == EntityType.ELDER_GUARDIAN) { + getPlayer(p).getData().addStat("seaborne.guardian.kills", 1); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getEntity() instanceof LivingEntity entity)) - return; + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof LivingEntity entity)) + return; - if (e.getEntity().getType() == EntityType.DROWNED && e.getDamager() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> { - if (isOnCooldown(p, getConfig().seaPickleCooldown)) { - return; - } - setCooldown(p); - xp(p, getConfig().damagedrownxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity))); - }); - } else if (e.getDamager().getType() == EntityType.TRIDENT) { - var shooter = ((Trident) e.getDamager()).getShooter(); - if (shooter instanceof Player p) { - shouldReturnForPlayer(p, e, () -> xp(p, getConfig().tridentxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity)))); - } - } else if (e.getDamager() instanceof Player p) { - if (p.getInventory().getItemInMainHand().getType().equals(Material.TRIDENT)) { - shouldReturnForPlayer(p, e, () -> xp(p, getConfig().tridentxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity)))); - } + if (e.getEntity().getType() == EntityType.DROWNED && e.getDamager() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> { + if (isOnCooldown(p, getConfig().seaPickleCooldown)) { + return; } + setCooldown(p); + xp(p, getConfig().damagedrownxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity))); + }); + } else if (e.getDamager().getType() == EntityType.TRIDENT) { + org.bukkit.projectiles.ProjectileSource shooter = ((Trident) e.getDamager()).getShooter(); + if (shooter instanceof Player p) { + shouldReturnForPlayer(p, e, () -> xp(p, getConfig().tridentxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity)))); + } + } else if (e.getDamager() instanceof Player p) { + if (p.getInventory().getItemInMainHand().getType().equals(Material.TRIDENT)) { + shouldReturnForPlayer(p, e, () -> xp(p, getConfig().tridentxpmultiplier * Math.min(e.getDamage(), getBaseHealth(entity)))); + } } + } - private double getBaseHealth(LivingEntity entity) { - var attribute = Version.get().getAttribute(entity, Attributes.GENERIC_MAX_HEALTH); - return attribute == null ? 0 : attribute.getBaseValue(); - } + private double getBaseHealth(LivingEntity entity) { + art.arcane.adapt.api.version.IAttribute attribute = Version.get().getAttribute(entity, Attributes.GENERIC_MAX_HEALTH); + return attribute == null ? 0 : attribute.getBaseValue(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sea Pickle Cooldown for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public long seaPickleCooldown = 60000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tridentxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double tridentxpmultiplier = 4.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damagedrownxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damagedrownxpmultiplier = 3; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&9"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim1nm Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwim1nmReward = 750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim5k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwim5kReward = 1500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim20k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwim20kReward = 3750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim XP for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double swimXP = 0.4; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sea Pickle Cooldown for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public long seaPickleCooldown = 60000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tridentxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double tridentxpmultiplier = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damagedrownxpmultiplier for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damagedrownxpmultiplier = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&9"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim1nm Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwim1nmReward = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim5k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwim5kReward = 1500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Swim20k Reward for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwim20kReward = 3750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Swim XP for the Seaborne skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double swimXP = 0.4; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java index 08d5f0d00..640c80219 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java @@ -42,235 +42,235 @@ import java.util.UUID; public class SkillStealth extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillStealth() { - super("stealth", Localizer.dLocalize("skill.stealth.icon")); - registerConfiguration(Config.class); - setColor(C.DARK_GRAY); - setInterval(1412); - setIcon(Material.WITHER_ROSE); - cooldowns = new HashMap<>(); - setDescription(Localizer.dLocalize("skill.stealth.description")); - setDisplayName(Localizer.dLocalize("skill.stealth.name")); - registerAdaptation(new StealthSpeed()); - registerAdaptation(new StealthSnatch()); - registerAdaptation(new StealthGhostArmor()); - registerAdaptation(new StealthSight()); - registerAdaptation(new StealthEnderVeil()); - registerAdaptation(new StealthSilentStep()); - registerAdaptation(new StealthShadowDecoy()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_LEGGINGS) - .key("challenge_sneak_1k") - .title(Localizer.dLocalize("advancement.challenge_sneak_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_sneak_1k.description")) - .model(CustomModel.get(Material.LEATHER_LEGGINGS, "advancement", "stealth", "challenge_sneak_1k")) + public SkillStealth() { + super("stealth", Localizer.dLocalize("skill.stealth.icon")); + registerConfiguration(Config.class); + setColor(C.DARK_GRAY); + setInterval(1412); + setIcon(Material.WITHER_ROSE); + cooldowns = new HashMap<>(); + setDescription(Localizer.dLocalize("skill.stealth.description")); + setDisplayName(Localizer.dLocalize("skill.stealth.name")); + registerAdaptation(new StealthSpeed()); + registerAdaptation(new StealthSnatch()); + registerAdaptation(new StealthGhostArmor()); + registerAdaptation(new StealthSight()); + registerAdaptation(new StealthEnderVeil()); + registerAdaptation(new StealthSilentStep()); + registerAdaptation(new StealthShadowDecoy()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_LEGGINGS) + .key("challenge_sneak_1k") + .title(Localizer.dLocalize("advancement.challenge_sneak_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_sneak_1k.description")) + .model(CustomModel.get(Material.LEATHER_LEGGINGS, "advancement", "stealth", "challenge_sneak_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHAINMAIL_LEGGINGS) + .key("challenge_sneak_5k") + .title(Localizer.dLocalize("advancement.challenge_sneak_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_sneak_5k.description")) + .model(CustomModel.get(Material.CHAINMAIL_LEGGINGS, "advancement", "stealth", "challenge_sneak_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_LEGGINGS) + .key("challenge_sneak_20k") + .title(Localizer.dLocalize("advancement.challenge_sneak_20k.title")) + .description(Localizer.dLocalize("advancement.challenge_sneak_20k.description")) + .model(CustomModel.get(Material.NETHERITE_LEGGINGS, "advancement", "stealth", "challenge_sneak_20k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHAINMAIL_LEGGINGS) - .key("challenge_sneak_5k") - .title(Localizer.dLocalize("advancement.challenge_sneak_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_sneak_5k.description")) - .model(CustomModel.get(Material.CHAINMAIL_LEGGINGS, "advancement", "stealth", "challenge_sneak_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_LEGGINGS) - .key("challenge_sneak_20k") - .title(Localizer.dLocalize("advancement.challenge_sneak_20k.title")) - .description(Localizer.dLocalize("advancement.challenge_sneak_20k.description")) - .model(CustomModel.get(Material.NETHERITE_LEGGINGS, "advancement", "stealth", "challenge_sneak_20k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_sneak_1k", "move.sneak", 1000, getConfig().challengeSneak1kReward); - registerMilestone("challenge_sneak_5k", "move.sneak", 5000, getConfig().challengeSneak5kReward); - registerMilestone("challenge_sneak_20k", "move.sneak", 20000, getConfig().challengeSneak20kReward); + .build()) + .build()) + .build()); + registerMilestone("challenge_sneak_1k", "move.sneak", 1000, getConfig().challengeSneak1kReward); + registerMilestone("challenge_sneak_5k", "move.sneak", 5000, getConfig().challengeSneak5kReward); + registerMilestone("challenge_sneak_20k", "move.sneak", 20000, getConfig().challengeSneak20kReward); - // Chain 2 - Stealth Damage While Sneaking - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.STONE_SWORD) - .key("challenge_stealth_dmg_500") - .title(Localizer.dLocalize("advancement.challenge_stealth_dmg_500.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_dmg_500.description")) - .model(CustomModel.get(Material.STONE_SWORD, "advancement", "stealth", "challenge_stealth_dmg_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_stealth_dmg_5k") - .title(Localizer.dLocalize("advancement.challenge_stealth_dmg_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_dmg_5k.description")) - .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "stealth", "challenge_stealth_dmg_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_dmg_500", "stealth.damage.sneaking", 500, getConfig().challengeStealthDmg500Reward); - registerMilestone("challenge_stealth_dmg_5k", "stealth.damage.sneaking", 5000, getConfig().challengeStealthDmg5kReward); + // Chain 2 - Stealth Damage While Sneaking + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.STONE_SWORD) + .key("challenge_stealth_dmg_500") + .title(Localizer.dLocalize("advancement.challenge_stealth_dmg_500.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_dmg_500.description")) + .model(CustomModel.get(Material.STONE_SWORD, "advancement", "stealth", "challenge_stealth_dmg_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_stealth_dmg_5k") + .title(Localizer.dLocalize("advancement.challenge_stealth_dmg_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_dmg_5k.description")) + .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "stealth", "challenge_stealth_dmg_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_dmg_500", "stealth.damage.sneaking", 500, getConfig().challengeStealthDmg500Reward); + registerMilestone("challenge_stealth_dmg_5k", "stealth.damage.sneaking", 5000, getConfig().challengeStealthDmg5kReward); - // Chain 3 - Stealth Kills While Sneaking - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SKELETON_SKULL) - .key("challenge_stealth_kills_10") - .title(Localizer.dLocalize("advancement.challenge_stealth_kills_10.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_kills_10.description")) - .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "stealth", "challenge_stealth_kills_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WITHER_ROSE) - .key("challenge_stealth_kills_100") - .title(Localizer.dLocalize("advancement.challenge_stealth_kills_100.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_kills_100.description")) - .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "stealth", "challenge_stealth_kills_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_kills_10", "stealth.kills.sneaking", 10, getConfig().challengeStealthKills10Reward); - registerMilestone("challenge_stealth_kills_100", "stealth.kills.sneaking", 100, getConfig().challengeStealthKills100Reward); + // Chain 3 - Stealth Kills While Sneaking + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SKELETON_SKULL) + .key("challenge_stealth_kills_10") + .title(Localizer.dLocalize("advancement.challenge_stealth_kills_10.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_kills_10.description")) + .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "stealth", "challenge_stealth_kills_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_stealth_kills_100") + .title(Localizer.dLocalize("advancement.challenge_stealth_kills_100.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_kills_100.description")) + .model(CustomModel.get(Material.WITHER_ROSE, "advancement", "stealth", "challenge_stealth_kills_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_kills_10", "stealth.kills.sneaking", 10, getConfig().challengeStealthKills10Reward); + registerMilestone("challenge_stealth_kills_100", "stealth.kills.sneaking", 100, getConfig().challengeStealthKills100Reward); - // Chain 4 - Stealth Time Spent Sneaking - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEATHER_BOOTS) - .key("challenge_stealth_time_1h") - .title(Localizer.dLocalize("advancement.challenge_stealth_time_1h.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_time_1h.description")) - .model(CustomModel.get(Material.LEATHER_BOOTS, "advancement", "stealth", "challenge_stealth_time_1h")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CHAINMAIL_BOOTS) - .key("challenge_stealth_time_10h") - .title(Localizer.dLocalize("advancement.challenge_stealth_time_10h.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_time_10h.description")) - .model(CustomModel.get(Material.CHAINMAIL_BOOTS, "advancement", "stealth", "challenge_stealth_time_10h")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_time_1h", "stealth.time", 3600, getConfig().challengeStealthTime1hReward); - registerMilestone("challenge_stealth_time_10h", "stealth.time", 36000, getConfig().challengeStealthTime10hReward); + // Chain 4 - Stealth Time Spent Sneaking + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEATHER_BOOTS) + .key("challenge_stealth_time_1h") + .title(Localizer.dLocalize("advancement.challenge_stealth_time_1h.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_time_1h.description")) + .model(CustomModel.get(Material.LEATHER_BOOTS, "advancement", "stealth", "challenge_stealth_time_1h")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CHAINMAIL_BOOTS) + .key("challenge_stealth_time_10h") + .title(Localizer.dLocalize("advancement.challenge_stealth_time_10h.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_time_10h.description")) + .model(CustomModel.get(Material.CHAINMAIL_BOOTS, "advancement", "stealth", "challenge_stealth_time_10h")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_time_1h", "stealth.time", 3600, getConfig().challengeStealthTime1hReward); + registerMilestone("challenge_stealth_time_10h", "stealth.time", 36000, getConfig().challengeStealthTime10hReward); - // Chain 5 - Stealth Arrows Fired While Sneaking - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BOW) - .key("challenge_stealth_arrows_50") - .title(Localizer.dLocalize("advancement.challenge_stealth_arrows_50.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_arrows_50.description")) - .model(CustomModel.get(Material.BOW, "advancement", "stealth", "challenge_stealth_arrows_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CROSSBOW) - .key("challenge_stealth_arrows_500") - .title(Localizer.dLocalize("advancement.challenge_stealth_arrows_500.title")) - .description(Localizer.dLocalize("advancement.challenge_stealth_arrows_500.description")) - .model(CustomModel.get(Material.CROSSBOW, "advancement", "stealth", "challenge_stealth_arrows_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_stealth_arrows_50", "stealth.arrows.sneaking", 50, getConfig().challengeStealthArrows50Reward); - registerMilestone("challenge_stealth_arrows_500", "stealth.arrows.sneaking", 500, getConfig().challengeStealthArrows500Reward); - } + // Chain 5 - Stealth Arrows Fired While Sneaking + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BOW) + .key("challenge_stealth_arrows_50") + .title(Localizer.dLocalize("advancement.challenge_stealth_arrows_50.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_arrows_50.description")) + .model(CustomModel.get(Material.BOW, "advancement", "stealth", "challenge_stealth_arrows_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CROSSBOW) + .key("challenge_stealth_arrows_500") + .title(Localizer.dLocalize("advancement.challenge_stealth_arrows_500.title")) + .description(Localizer.dLocalize("advancement.challenge_stealth_arrows_500.description")) + .model(CustomModel.get(Material.CROSSBOW, "advancement", "stealth", "challenge_stealth_arrows_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_stealth_arrows_50", "stealth.arrows.sneaking", 50, getConfig().challengeStealthArrows50Reward); + registerMilestone("challenge_stealth_arrows_500", "stealth.arrows.sneaking", 500, getConfig().challengeStealthArrows500Reward); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && p.isSneaking()) { - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("stealth.damage.sneaking", e.getDamage()); - xp(p, e.getEntity().getLocation(), e.getDamage() * getConfig().sneakCombatXPMultiplier); - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && p.isSneaking()) { + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("stealth.damage.sneaking", e.getDamage()); + xp(p, e.getEntity().getLocation(), e.getDamage() * getConfig().sneakCombatXPMultiplier); + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() == null) { - return; - } - Player p = e.getEntity().getKiller(); - if (p.isSneaking()) { - shouldReturnForPlayer(p, () -> { - getPlayer(p).getData().addStat("stealth.kills.sneaking", 1); - xp(p, e.getEntity().getLocation(), getConfig().sneakKillXP); - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() == null) { + return; + } + Player p = e.getEntity().getKiller(); + if (p.isSneaking()) { + shouldReturnForPlayer(p, () -> { + getPlayer(p).getData().addStat("stealth.kills.sneaking", 1); + xp(p, e.getEntity().getLocation(), getConfig().sneakKillXP); + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(ProjectileLaunchEvent e) { - if (!(e.getEntity().getShooter() instanceof Player p)) { - return; - } - if (p.isSneaking()) { - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("stealth.arrows.sneaking", 1); - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(ProjectileLaunchEvent e) { + if (!(e.getEntity().getShooter() instanceof Player p)) { + return; } + if (p.isSneaking()) { + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("stealth.arrows.sneaking", 1); + }); + } + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; + @Override + public void onTick() { + if (!this.isEnabled()) { + return; + } + for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + Player i = adaptPlayer.getPlayer(); + shouldReturnForPlayer(i, () -> { + checkStatTrackers(adaptPlayer); + if (i.isSneaking() && !i.isSwimming() && !i.isSprinting() && !i.isFlying() && !i.isGliding() && (i.getGameMode().equals(GameMode.SURVIVAL) || i.getGameMode().equals(GameMode.ADVENTURE))) { + xpSilent(i, getConfig().sneakXP, "stealth:sneak"); + adaptPlayer.getData().addStat("stealth.time", 1); } - for (AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { - Player i = adaptPlayer.getPlayer(); - shouldReturnForPlayer(i, () -> { - checkStatTrackers(adaptPlayer); - if (i.isSneaking() && !i.isSwimming() && !i.isSprinting() && !i.isFlying() && !i.isGliding() && (i.getGameMode().equals(GameMode.SURVIVAL) || i.getGameMode().equals(GameMode.ADVENTURE))) { - xpSilent(i, getConfig().sneakXP, "stealth:sneak"); - adaptPlayer.getData().addStat("stealth.time", 1); - } - }); + }); - } } + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&8"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak1k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSneak1kReward = 1750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSneak5kReward = 3500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak20k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSneak20kReward = 8750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak XP for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sneakXP = 0.4; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP multiplier for dealing damage while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sneakCombatXPMultiplier = 3.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for killing while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double sneakKillXP = 15; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthDmg500Reward = 1500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthDmg5kReward = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 10 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthKills10Reward = 1000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 100 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthKills100Reward = 5000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 1h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthTime1hReward = 2000; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 10h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthTime10hReward = 7500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 50 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthArrows50Reward = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeStealthArrows500Reward = 5000; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&8"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak1k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSneak1kReward = 1750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSneak5kReward = 3500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sneak20k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSneak20kReward = 8750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Sneak XP for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sneakXP = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP multiplier for dealing damage while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sneakCombatXPMultiplier = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for killing while sneaking.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double sneakKillXP = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthDmg500Reward = 1500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Dmg 5k Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthDmg5kReward = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 10 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthKills10Reward = 1000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Kills 100 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthKills100Reward = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 1h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthTime1hReward = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Time 10h Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthTime10hReward = 7500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 50 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthArrows50Reward = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Stealth Arrows 500 Reward for the Stealth skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeStealthArrows500Reward = 5000; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java index 318eadf8e..84cf9feee 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillSwords.java @@ -41,223 +41,223 @@ import java.util.UUID; public class SkillSwords extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillSwords() { - super("swords", Localizer.dLocalize("skill.swords.icon")); - registerConfiguration(Config.class); - setColor(C.YELLOW); - setDescription(Localizer.dLocalize("skill.swords.description")); - setDisplayName(Localizer.dLocalize("skill.swords.name")); - setInterval(2150); - setIcon(Material.DIAMOND_SWORD); - cooldowns = new HashMap<>(); - registerAdaptation(new SwordsMachete()); - registerAdaptation(new SwordsPoisonedBlade()); - registerAdaptation(new SwordsBloodyBlade()); - registerAdaptation(new SwordsDualWield()); - registerAdaptation(new SwordsExecutionersEdge()); - registerAdaptation(new SwordsRiposteWindow()); - registerAdaptation(new SwordsCrimsonCyclone()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.WOODEN_SWORD) - .key("challenge_sword_100") - .title(Localizer.dLocalize("advancement.challenge_sword_100.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_100.description")) - .model(CustomModel.get(Material.WOODEN_SWORD, "advancement", "swords", "challenge_sword_100")) + public SkillSwords() { + super("swords", Localizer.dLocalize("skill.swords.icon")); + registerConfiguration(Config.class); + setColor(C.YELLOW); + setDescription(Localizer.dLocalize("skill.swords.description")); + setDisplayName(Localizer.dLocalize("skill.swords.name")); + setInterval(2150); + setIcon(Material.DIAMOND_SWORD); + cooldowns = new HashMap<>(); + registerAdaptation(new SwordsMachete()); + registerAdaptation(new SwordsPoisonedBlade()); + registerAdaptation(new SwordsBloodyBlade()); + registerAdaptation(new SwordsDualWield()); + registerAdaptation(new SwordsExecutionersEdge()); + registerAdaptation(new SwordsRiposteWindow()); + registerAdaptation(new SwordsCrimsonCyclone()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WOODEN_SWORD) + .key("challenge_sword_100") + .title(Localizer.dLocalize("advancement.challenge_sword_100.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_100.description")) + .model(CustomModel.get(Material.WOODEN_SWORD, "advancement", "swords", "challenge_sword_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_sword_1k") + .title(Localizer.dLocalize("advancement.challenge_sword_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_1k.description")) + .model(CustomModel.get(Material.IRON_SWORD, "advancement", "swords", "challenge_sword_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_sword_10k") + .title(Localizer.dLocalize("advancement.challenge_sword_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_10k.description")) + .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_10k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_sword_1k") - .title(Localizer.dLocalize("advancement.challenge_sword_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_1k.description")) - .model(CustomModel.get(Material.IRON_SWORD, "advancement", "swords", "challenge_sword_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_sword_10k") - .title(Localizer.dLocalize("advancement.challenge_sword_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_10k.description")) - .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_sword_100", "sword.hits", 100, getConfig().challengeSwordReward); - registerMilestone("challenge_sword_1k", "sword.hits", 1000, getConfig().challengeSwordReward * 2); - registerMilestone("challenge_sword_10k", "sword.hits", 10000, getConfig().challengeSwordReward * 5); + .build()) + .build()) + .build()); + registerMilestone("challenge_sword_100", "sword.hits", 100, getConfig().challengeSwordReward); + registerMilestone("challenge_sword_1k", "sword.hits", 1000, getConfig().challengeSwordReward * 2); + registerMilestone("challenge_sword_10k", "sword.hits", 10000, getConfig().challengeSwordReward * 5); - // Chain 2 - sword.damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_SWORD) - .key("challenge_sword_dmg_1k") - .title(Localizer.dLocalize("advancement.challenge_sword_dmg_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_dmg_1k.description")) - .model(CustomModel.get(Material.GOLDEN_SWORD, "advancement", "swords", "challenge_sword_dmg_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_sword_dmg_10k") - .title(Localizer.dLocalize("advancement.challenge_sword_dmg_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_dmg_10k.description")) - .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_dmg_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_sword_dmg_1k", "sword.damage", 1000, getConfig().challengeSwordDmgReward); - registerMilestone("challenge_sword_dmg_10k", "sword.damage", 10000, getConfig().challengeSwordDmgReward * 3); + // Chain 2 - sword.damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_SWORD) + .key("challenge_sword_dmg_1k") + .title(Localizer.dLocalize("advancement.challenge_sword_dmg_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_dmg_1k.description")) + .model(CustomModel.get(Material.GOLDEN_SWORD, "advancement", "swords", "challenge_sword_dmg_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_sword_dmg_10k") + .title(Localizer.dLocalize("advancement.challenge_sword_dmg_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_dmg_10k.description")) + .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_dmg_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_sword_dmg_1k", "sword.damage", 1000, getConfig().challengeSwordDmgReward); + registerMilestone("challenge_sword_dmg_10k", "sword.damage", 10000, getConfig().challengeSwordDmgReward * 3); - // Chain 3 - sword.kills - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.IRON_SWORD) - .key("challenge_sword_kills_50") - .title(Localizer.dLocalize("advancement.challenge_sword_kills_50.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_kills_50.description")) - .model(CustomModel.get(Material.IRON_SWORD, "advancement", "swords", "challenge_sword_kills_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_sword_kills_500") - .title(Localizer.dLocalize("advancement.challenge_sword_kills_500.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_kills_500.description")) - .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_kills_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_sword_kills_50", "sword.kills", 50, getConfig().challengeSwordKillsReward); - registerMilestone("challenge_sword_kills_500", "sword.kills", 500, getConfig().challengeSwordKillsReward * 3); + // Chain 3 - sword.kills + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SWORD) + .key("challenge_sword_kills_50") + .title(Localizer.dLocalize("advancement.challenge_sword_kills_50.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_kills_50.description")) + .model(CustomModel.get(Material.IRON_SWORD, "advancement", "swords", "challenge_sword_kills_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_sword_kills_500") + .title(Localizer.dLocalize("advancement.challenge_sword_kills_500.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_kills_500.description")) + .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_kills_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_sword_kills_50", "sword.kills", 50, getConfig().challengeSwordKillsReward); + registerMilestone("challenge_sword_kills_500", "sword.kills", 500, getConfig().challengeSwordKillsReward * 3); - // Chain 4 - sword.critical - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_SWORD) - .key("challenge_sword_crit_50") - .title(Localizer.dLocalize("advancement.challenge_sword_crit_50.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_crit_50.description")) - .model(CustomModel.get(Material.GOLDEN_SWORD, "advancement", "swords", "challenge_sword_crit_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_sword_crit_500") - .title(Localizer.dLocalize("advancement.challenge_sword_crit_500.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_crit_500.description")) - .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_crit_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_sword_crit_50", "sword.critical", 50, getConfig().challengeSwordCritReward); - registerMilestone("challenge_sword_crit_500", "sword.critical", 500, getConfig().challengeSwordCritReward * 3); + // Chain 4 - sword.critical + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_SWORD) + .key("challenge_sword_crit_50") + .title(Localizer.dLocalize("advancement.challenge_sword_crit_50.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_crit_50.description")) + .model(CustomModel.get(Material.GOLDEN_SWORD, "advancement", "swords", "challenge_sword_crit_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_sword_crit_500") + .title(Localizer.dLocalize("advancement.challenge_sword_crit_500.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_crit_500.description")) + .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_crit_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_sword_crit_50", "sword.critical", 50, getConfig().challengeSwordCritReward); + registerMilestone("challenge_sword_crit_500", "sword.critical", 500, getConfig().challengeSwordCritReward * 3); - // Chain 5 - sword.heavy.hits - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_sword_heavy_25") - .title(Localizer.dLocalize("advancement.challenge_sword_heavy_25.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_heavy_25.description")) - .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_heavy_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHERITE_SWORD) - .key("challenge_sword_heavy_250") - .title(Localizer.dLocalize("advancement.challenge_sword_heavy_250.title")) - .description(Localizer.dLocalize("advancement.challenge_sword_heavy_250.description")) - .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_heavy_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_sword_heavy_25", "sword.heavy.hits", 25, getConfig().challengeSwordHeavyReward); - registerMilestone("challenge_sword_heavy_250", "sword.heavy.hits", 250, getConfig().challengeSwordHeavyReward * 3); - } + // Chain 5 - sword.heavy.hits + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_sword_heavy_25") + .title(Localizer.dLocalize("advancement.challenge_sword_heavy_25.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_heavy_25.description")) + .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "swords", "challenge_sword_heavy_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERITE_SWORD) + .key("challenge_sword_heavy_250") + .title(Localizer.dLocalize("advancement.challenge_sword_heavy_250.title")) + .description(Localizer.dLocalize("advancement.challenge_sword_heavy_250.description")) + .model(CustomModel.get(Material.NETHERITE_SWORD, "advancement", "swords", "challenge_sword_heavy_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_sword_heavy_25", "sword.heavy.hits", 25, getConfig().challengeSwordHeavyReward); + registerMilestone("challenge_sword_heavy_250", "sword.heavy.hits", 250, getConfig().challengeSwordHeavyReward * 3); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { - shouldReturnForPlayer(p, e, () -> { - AdaptPlayer a = getPlayer(p); - ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); - if (isSword(hand)) { - getPlayer(p).getData().addStat("sword.hits", 1); - getPlayer(p).getData().addStat("sword.damage", e.getDamage()); - if (p.getFallDistance() > 0 && !p.isOnGround()) { - getPlayer(p).getData().addStat("sword.critical", 1); - } - if (e.getDamage() > 8) { - getPlayer(p).getData().addStat("sword.heavy.hits", 1); - } - if (!isOnCooldown(p)) { - setCooldown(p); - xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage()); - } - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player p && checkValidEntity(e.getEntity().getType())) { + shouldReturnForPlayer(p, e, () -> { + AdaptPlayer a = getPlayer(p); + ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); + if (isSword(hand)) { + getPlayer(p).getData().addStat("sword.hits", 1); + getPlayer(p).getData().addStat("sword.damage", e.getDamage()); + if (p.getFallDistance() > 0 && !p.isOnGround()) { + getPlayer(p).getData().addStat("sword.critical", 1); + } + if (e.getDamage() > 8) { + getPlayer(p).getData().addStat("sword.heavy.hits", 1); + } + if (!isOnCooldown(p)) { + setCooldown(p); + xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage()); + } } + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() == null) { - return; - } - Player p = e.getEntity().getKiller(); - shouldReturnForPlayer(p, () -> { - ItemStack hand = p.getInventory().getItemInMainHand(); - if (isSword(hand)) { - getPlayer(p).getData().addStat("sword.kills", 1); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() == null) { + return; } + Player p = e.getEntity().getKiller(); + shouldReturnForPlayer(p, () -> { + ItemStack hand = p.getInventory().getItemInMainHand(); + if (isSword(hand)) { + getPlayer(p).getData().addStat("sword.kills", 1); + } + }); + } - private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p.getUniqueId()); - return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); - } + private boolean isOnCooldown(Player p) { + Long cooldown = cooldowns.get(p.getUniqueId()); + return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); + } - private void setCooldown(Player p) { - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - } + private void setCooldown(Player p) { + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&e"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageXPMultiplier = 4.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwordReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Damage Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwordDmgReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Kills Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwordKillsReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Critical Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwordCritReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Heavy Hits Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeSwordHeavyReward = 500; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&e"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageXPMultiplier = 4.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwordReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Damage Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwordDmgReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Kills Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwordKillsReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Critical Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwordCritReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Sword Heavy Hits Reward for the Swords skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeSwordHeavyReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java index 9d6387fd6..e10aaae51 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java @@ -43,235 +43,235 @@ import java.util.UUID; public class SkillTaming extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillTaming() { - super("taming", Localizer.dLocalize("skill.taming.icon")); - registerConfiguration(Config.class); - setDescription(Localizer.dLocalize("skill.taming.description")); - setDisplayName(Localizer.dLocalize("skill.taming.name")); - setColor(C.GOLD); - setInterval(3480); - setIcon(Material.LEAD); - cooldowns = new HashMap<>(); - registerAdaptation(new TamingHealthBoost()); - registerAdaptation(new TamingDamage()); - registerAdaptation(new TamingHealthRegeneration()); - registerAdaptation(new TamingPackLeaderAura()); - registerAdaptation(new TamingBeastRecall()); - registerAdaptation(new TamingSharedPain()); - registerAdaptation(new TamingMountedTactics()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEAD) - .key("challenge_taming_10") - .title(Localizer.dLocalize("advancement.challenge_taming_10.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_10.description")) - .model(CustomModel.get(Material.LEAD, "advancement", "taming", "challenge_taming_10")) + public SkillTaming() { + super("taming", Localizer.dLocalize("skill.taming.icon")); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("skill.taming.description")); + setDisplayName(Localizer.dLocalize("skill.taming.name")); + setColor(C.GOLD); + setInterval(3480); + setIcon(Material.LEAD); + cooldowns = new HashMap<>(); + registerAdaptation(new TamingHealthBoost()); + registerAdaptation(new TamingDamage()); + registerAdaptation(new TamingHealthRegeneration()); + registerAdaptation(new TamingPackLeaderAura()); + registerAdaptation(new TamingBeastRecall()); + registerAdaptation(new TamingSharedPain()); + registerAdaptation(new TamingMountedTactics()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEAD) + .key("challenge_taming_10") + .title(Localizer.dLocalize("advancement.challenge_taming_10.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_10.description")) + .model(CustomModel.get(Material.LEAD, "advancement", "taming", "challenge_taming_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NAME_TAG) + .key("challenge_taming_50") + .title(Localizer.dLocalize("advancement.challenge_taming_50.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_50.description")) + .model(CustomModel.get(Material.NAME_TAG, "advancement", "taming", "challenge_taming_50")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_taming_500") + .title(Localizer.dLocalize("advancement.challenge_taming_500.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_500.description")) + .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "taming", "challenge_taming_500")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NAME_TAG) - .key("challenge_taming_50") - .title(Localizer.dLocalize("advancement.challenge_taming_50.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_50.description")) - .model(CustomModel.get(Material.NAME_TAG, "advancement", "taming", "challenge_taming_50")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_taming_500") - .title(Localizer.dLocalize("advancement.challenge_taming_500.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_500.description")) - .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "taming", "challenge_taming_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_pet_dmg_500") - .title(Localizer.dLocalize("advancement.challenge_pet_dmg_500.title")) - .description(Localizer.dLocalize("advancement.challenge_pet_dmg_500.description")) - .model(CustomModel.get(Material.BONE, "advancement", "taming", "challenge_pet_dmg_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.DIAMOND_SWORD) - .key("challenge_pet_dmg_5k") - .title(Localizer.dLocalize("advancement.challenge_pet_dmg_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_pet_dmg_5k.description")) - .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "taming", "challenge_pet_dmg_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.LEAD) - .key("challenge_tamed_10") - .title(Localizer.dLocalize("advancement.challenge_tamed_10.title")) - .description(Localizer.dLocalize("advancement.challenge_tamed_10.description")) - .model(CustomModel.get(Material.LEAD, "advancement", "taming", "challenge_tamed_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NAME_TAG) - .key("challenge_tamed_100") - .title(Localizer.dLocalize("advancement.challenge_tamed_100.title")) - .description(Localizer.dLocalize("advancement.challenge_tamed_100.description")) - .model(CustomModel.get(Material.NAME_TAG, "advancement", "taming", "challenge_tamed_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_pet_kills_25") - .title(Localizer.dLocalize("advancement.challenge_pet_kills_25.title")) - .description(Localizer.dLocalize("advancement.challenge_pet_kills_25.description")) - .model(CustomModel.get(Material.BONE, "advancement", "taming", "challenge_pet_kills_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.GOLDEN_APPLE) - .key("challenge_pet_kills_250") - .title(Localizer.dLocalize("advancement.challenge_pet_kills_250.title")) - .description(Localizer.dLocalize("advancement.challenge_pet_kills_250.description")) - .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "taming", "challenge_pet_kills_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.GOLDEN_CARROT) - .key("challenge_taming_2500") - .title(Localizer.dLocalize("advancement.challenge_taming_2500.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_2500.description")) - .model(CustomModel.get(Material.GOLDEN_CARROT, "advancement", "taming", "challenge_taming_2500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ENCHANTED_GOLDEN_APPLE) - .key("challenge_taming_25k") - .title(Localizer.dLocalize("advancement.challenge_taming_25k.title")) - .description(Localizer.dLocalize("advancement.challenge_taming_25k.description")) - .model(CustomModel.get(Material.ENCHANTED_GOLDEN_APPLE, "advancement", "taming", "challenge_taming_25k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_taming_10", "taming.bred", 10, getConfig().challengeTamingReward); - registerMilestone("challenge_taming_50", "taming.bred", 50, getConfig().challengeTamingReward * 2); - registerMilestone("challenge_taming_500", "taming.bred", 500, getConfig().challengeTamingReward * 5); - registerMilestone("challenge_pet_dmg_500", "taming.pet.damage", 500, getConfig().challengePetDmgReward); - registerMilestone("challenge_pet_dmg_5k", "taming.pet.damage", 5000, getConfig().challengePetDmgReward * 5); - registerMilestone("challenge_tamed_10", "taming.tamed", 10, getConfig().challengeTamedReward); - registerMilestone("challenge_tamed_100", "taming.tamed", 100, getConfig().challengeTamedReward * 5); - registerMilestone("challenge_pet_kills_25", "taming.pet.kills", 25, getConfig().challengePetKillsReward); - registerMilestone("challenge_pet_kills_250", "taming.pet.kills", 250, getConfig().challengePetKillsReward * 5); - registerMilestone("challenge_taming_2500", "taming.bred", 2500, getConfig().challengeTamingReward * 10); - registerMilestone("challenge_taming_25k", "taming.bred", 25000, getConfig().challengeTamingReward * 25); - } + .build()) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_pet_dmg_500") + .title(Localizer.dLocalize("advancement.challenge_pet_dmg_500.title")) + .description(Localizer.dLocalize("advancement.challenge_pet_dmg_500.description")) + .model(CustomModel.get(Material.BONE, "advancement", "taming", "challenge_pet_dmg_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND_SWORD) + .key("challenge_pet_dmg_5k") + .title(Localizer.dLocalize("advancement.challenge_pet_dmg_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pet_dmg_5k.description")) + .model(CustomModel.get(Material.DIAMOND_SWORD, "advancement", "taming", "challenge_pet_dmg_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LEAD) + .key("challenge_tamed_10") + .title(Localizer.dLocalize("advancement.challenge_tamed_10.title")) + .description(Localizer.dLocalize("advancement.challenge_tamed_10.description")) + .model(CustomModel.get(Material.LEAD, "advancement", "taming", "challenge_tamed_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NAME_TAG) + .key("challenge_tamed_100") + .title(Localizer.dLocalize("advancement.challenge_tamed_100.title")) + .description(Localizer.dLocalize("advancement.challenge_tamed_100.description")) + .model(CustomModel.get(Material.NAME_TAG, "advancement", "taming", "challenge_tamed_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_pet_kills_25") + .title(Localizer.dLocalize("advancement.challenge_pet_kills_25.title")) + .description(Localizer.dLocalize("advancement.challenge_pet_kills_25.description")) + .model(CustomModel.get(Material.BONE, "advancement", "taming", "challenge_pet_kills_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.GOLDEN_APPLE) + .key("challenge_pet_kills_250") + .title(Localizer.dLocalize("advancement.challenge_pet_kills_250.title")) + .description(Localizer.dLocalize("advancement.challenge_pet_kills_250.description")) + .model(CustomModel.get(Material.GOLDEN_APPLE, "advancement", "taming", "challenge_pet_kills_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GOLDEN_CARROT) + .key("challenge_taming_2500") + .title(Localizer.dLocalize("advancement.challenge_taming_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_2500.description")) + .model(CustomModel.get(Material.GOLDEN_CARROT, "advancement", "taming", "challenge_taming_2500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ENCHANTED_GOLDEN_APPLE) + .key("challenge_taming_25k") + .title(Localizer.dLocalize("advancement.challenge_taming_25k.title")) + .description(Localizer.dLocalize("advancement.challenge_taming_25k.description")) + .model(CustomModel.get(Material.ENCHANTED_GOLDEN_APPLE, "advancement", "taming", "challenge_taming_25k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_taming_10", "taming.bred", 10, getConfig().challengeTamingReward); + registerMilestone("challenge_taming_50", "taming.bred", 50, getConfig().challengeTamingReward * 2); + registerMilestone("challenge_taming_500", "taming.bred", 500, getConfig().challengeTamingReward * 5); + registerMilestone("challenge_pet_dmg_500", "taming.pet.damage", 500, getConfig().challengePetDmgReward); + registerMilestone("challenge_pet_dmg_5k", "taming.pet.damage", 5000, getConfig().challengePetDmgReward * 5); + registerMilestone("challenge_tamed_10", "taming.tamed", 10, getConfig().challengeTamedReward); + registerMilestone("challenge_tamed_100", "taming.tamed", 100, getConfig().challengeTamedReward * 5); + registerMilestone("challenge_pet_kills_25", "taming.pet.kills", 25, getConfig().challengePetKillsReward); + registerMilestone("challenge_pet_kills_250", "taming.pet.kills", 250, getConfig().challengePetKillsReward * 5); + registerMilestone("challenge_taming_2500", "taming.bred", 2500, getConfig().challengeTamingReward * 10); + registerMilestone("challenge_taming_25k", "taming.bred", 25000, getConfig().challengeTamingReward * 25); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityBreedEvent e) { - for (Entity nearby : e.getEntity().getNearbyEntities(15, 15, 15)) { - if (!(nearby instanceof Player p)) { - continue; - } - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("taming.bred", 1); - if (!isOnCooldown(p)) { - setCooldown(p); - xp(p, getConfig().tameXpBase); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityBreedEvent e) { + for (Entity nearby : e.getEntity().getNearbyEntities(15, 15, 15)) { + if (!(nearby instanceof Player p)) { + continue; + } + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("taming.bred", 1); + if (!isOnCooldown(p)) { + setCooldown(p); + xp(p, getConfig().tameXpBase); } + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (e.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("taming.pet.damage", e.getDamage()); - if (!isOnCooldown(p)) { - setCooldown(p); - xp(p, e.getEntity().getLocation(), e.getDamage() * getConfig().tameDamageXPMultiplier); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("taming.pet.damage", e.getDamage()); + if (!isOnCooldown(p)) { + setCooldown(p); + xp(p, e.getEntity().getLocation(), e.getDamage() * getConfig().tameDamageXPMultiplier); } + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityTameEvent e) { - if (e.getOwner() instanceof Player p) { - shouldReturnForPlayer(p, e, () -> { - getPlayer(p).getData().addStat("taming.tamed", 1); - xp(p, e.getEntity().getLocation(), getConfig().tameSuccessXP); - }); - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityTameEvent e) { + if (e.getOwner() instanceof Player p) { + shouldReturnForPlayer(p, e, () -> { + getPlayer(p).getData().addStat("taming.tamed", 1); + xp(p, e.getEntity().getLocation(), getConfig().tameSuccessXP); + }); } + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() != null) { - return; - } - if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent damageEvent) { - if (damageEvent.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { - shouldReturnForPlayer(p, () -> { - getPlayer(p).getData().addStat("taming.pet.kills", 1); - xp(p, e.getEntity().getLocation(), getConfig().petKillXP); - }); - } - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() != null) { + return; } - - private boolean isOnCooldown(Player p) { - Long cooldown = cooldowns.get(p.getUniqueId()); - return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent damageEvent) { + if (damageEvent.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { + shouldReturnForPlayer(p, () -> { + getPlayer(p).getData().addStat("taming.pet.kills", 1); + xp(p, e.getEntity().getLocation(), getConfig().petKillXP); + }); + } } + } - private void setCooldown(Player p) { - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - } + private boolean isOnCooldown(Player p) { + Long cooldown = cooldowns.get(p.getUniqueId()); + return cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis(); + } + private void setCooldown(Player p) { + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); - } - @Override - public boolean isEnabled() { - return getConfig().enabled; + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&6"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Xp Base for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double tameXpBase = 65; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Damage XPMultiplier for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double tameDamageXPMultiplier = 8.0; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for successfully taming an animal.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double tameSuccessXP = 150; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded when a pet kills a mob.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double petKillXP = 25; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Taming Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeTamingReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Damage Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengePetDmgReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Tamed Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeTamedReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Kills Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengePetKillsReward = 500; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&6"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Xp Base for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double tameXpBase = 65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Tame Damage XPMultiplier for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double tameDamageXPMultiplier = 8.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded for successfully taming an animal.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double tameSuccessXP = 150; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP awarded when a pet kills a mob.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double petKillXP = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Taming Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeTamingReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Damage Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengePetDmgReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Tamed Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeTamedReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Pet Kills Reward for the Taming skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengePetKillsReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 61b067658..3ba9c62aa 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -50,269 +50,269 @@ import java.util.UUID; public class SkillTragOul extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillTragOul() { - super("tragoul", Localizer.dLocalize("skill.tragoul.icon")); - registerConfiguration(Config.class); - setColor(C.AQUA); - setDescription(Localizer.dLocalize("skill.tragoul.description")); - setDisplayName(Localizer.dLocalize("skill.tragoul.name")); - setInterval(2755); - setIcon(Material.CRIMSON_ROOTS); - cooldowns = new HashMap<>(); - registerAdaptation(new TragoulThorns()); - registerAdaptation(new TragoulGlobe()); - registerAdaptation(new TragoulHealing()); - registerAdaptation(new TragoulLance()); - registerAdaptation(new TragoulBloodPact()); - registerAdaptation(new TragoulBoneHarvest()); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.CRIMSON_ROOTS) - .key("challenge_trag_1k") - .title(Localizer.dLocalize("advancement.challenge_trag_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_1k.description")) - .model(CustomModel.get(Material.CRIMSON_ROOTS, "advancement", "tragoul", "challenge_trag_1k")) + public SkillTragOul() { + super("tragoul", Localizer.dLocalize("skill.tragoul.icon")); + registerConfiguration(Config.class); + setColor(C.AQUA); + setDescription(Localizer.dLocalize("skill.tragoul.description")); + setDisplayName(Localizer.dLocalize("skill.tragoul.name")); + setInterval(2755); + setIcon(Material.CRIMSON_ROOTS); + cooldowns = new HashMap<>(); + registerAdaptation(new TragoulThorns()); + registerAdaptation(new TragoulGlobe()); + registerAdaptation(new TragoulHealing()); + registerAdaptation(new TragoulLance()); + registerAdaptation(new TragoulBloodPact()); + registerAdaptation(new TragoulBoneHarvest()); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.CRIMSON_ROOTS) + .key("challenge_trag_1k") + .title(Localizer.dLocalize("advancement.challenge_trag_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_1k.description")) + .model(CustomModel.get(Material.CRIMSON_ROOTS, "advancement", "tragoul", "challenge_trag_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CRIMSON_STEM) + .key("challenge_trag_10k") + .title(Localizer.dLocalize("advancement.challenge_trag_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_10k.description")) + .model(CustomModel.get(Material.CRIMSON_STEM, "advancement", "tragoul", "challenge_trag_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_trag_100k") + .title(Localizer.dLocalize("advancement.challenge_trag_100k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_100k.description")) + .model(CustomModel.get(Material.NETHER_STAR, "advancement", "tragoul", "challenge_trag_100k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.CRIMSON_STEM) - .key("challenge_trag_10k") - .title(Localizer.dLocalize("advancement.challenge_trag_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_10k.description")) - .model(CustomModel.get(Material.CRIMSON_STEM, "advancement", "tragoul", "challenge_trag_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_trag_100k") - .title(Localizer.dLocalize("advancement.challenge_trag_100k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_100k.description")) - .model(CustomModel.get(Material.NETHER_STAR, "advancement", "tragoul", "challenge_trag_100k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_trag_1k", "trag.damage", 1000, getConfig().challengeTragReward); - registerMilestone("challenge_trag_10k", "trag.damage", 10000, getConfig().challengeTragReward * 2); - registerMilestone("challenge_trag_100k", "trag.damage", 100000, getConfig().challengeTragReward * 5); + .build()) + .build()) + .build()); + registerMilestone("challenge_trag_1k", "trag.damage", 1000, getConfig().challengeTragReward); + registerMilestone("challenge_trag_10k", "trag.damage", 10000, getConfig().challengeTragReward * 2); + registerMilestone("challenge_trag_100k", "trag.damage", 100000, getConfig().challengeTragReward * 5); - // Chain 2 - Hits Received - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ROTTEN_FLESH) - .key("challenge_trag_hits_500") - .title(Localizer.dLocalize("advancement.challenge_trag_hits_500.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_hits_500.description")) - .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "tragoul", "challenge_trag_hits_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BONE) - .key("challenge_trag_hits_5k") - .title(Localizer.dLocalize("advancement.challenge_trag_hits_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_hits_5k.description")) - .model(CustomModel.get(Material.BONE, "advancement", "tragoul", "challenge_trag_hits_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_trag_hits_500", "trag.hitsrecieved", 500, getConfig().challengeTragReward); - registerMilestone("challenge_trag_hits_5k", "trag.hitsrecieved", 5000, getConfig().challengeTragReward); + // Chain 2 - Hits Received + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ROTTEN_FLESH) + .key("challenge_trag_hits_500") + .title(Localizer.dLocalize("advancement.challenge_trag_hits_500.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_hits_500.description")) + .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "tragoul", "challenge_trag_hits_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_trag_hits_5k") + .title(Localizer.dLocalize("advancement.challenge_trag_hits_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_hits_5k.description")) + .model(CustomModel.get(Material.BONE, "advancement", "tragoul", "challenge_trag_hits_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_trag_hits_500", "trag.hitsrecieved", 500, getConfig().challengeTragReward); + registerMilestone("challenge_trag_hits_5k", "trag.hitsrecieved", 5000, getConfig().challengeTragReward); - // Chain 3 - Deaths - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SKELETON_SKULL) - .key("challenge_trag_deaths_10") - .title(Localizer.dLocalize("advancement.challenge_trag_deaths_10.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_deaths_10.description")) - .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "tragoul", "challenge_trag_deaths_10")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.WITHER_SKELETON_SKULL) - .key("challenge_trag_deaths_100") - .title(Localizer.dLocalize("advancement.challenge_trag_deaths_100.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_deaths_100.description")) - .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "tragoul", "challenge_trag_deaths_100")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_trag_deaths_10", "trag.deaths", 10, getConfig().challengeTragReward); - registerMilestone("challenge_trag_deaths_100", "trag.deaths", 100, getConfig().challengeTragReward); + // Chain 3 - Deaths + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SKELETON_SKULL) + .key("challenge_trag_deaths_10") + .title(Localizer.dLocalize("advancement.challenge_trag_deaths_10.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_deaths_10.description")) + .model(CustomModel.get(Material.SKELETON_SKULL, "advancement", "tragoul", "challenge_trag_deaths_10")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_trag_deaths_100") + .title(Localizer.dLocalize("advancement.challenge_trag_deaths_100.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_deaths_100.description")) + .model(CustomModel.get(Material.WITHER_SKELETON_SKULL, "advancement", "tragoul", "challenge_trag_deaths_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_trag_deaths_10", "trag.deaths", 10, getConfig().challengeTragReward); + registerMilestone("challenge_trag_deaths_100", "trag.deaths", 100, getConfig().challengeTragReward); - // Chain 4 - Fire Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_trag_fire_500") - .title(Localizer.dLocalize("advancement.challenge_trag_fire_500.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_fire_500.description")) - .model(CustomModel.get(Material.BLAZE_POWDER, "advancement", "tragoul", "challenge_trag_fire_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.MAGMA_CREAM) - .key("challenge_trag_fire_5k") - .title(Localizer.dLocalize("advancement.challenge_trag_fire_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_fire_5k.description")) - .model(CustomModel.get(Material.MAGMA_CREAM, "advancement", "tragoul", "challenge_trag_fire_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_trag_fire_500", "trag.fire.damage", 500, getConfig().challengeTragReward); - registerMilestone("challenge_trag_fire_5k", "trag.fire.damage", 5000, getConfig().challengeTragReward); + // Chain 4 - Fire Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_trag_fire_500") + .title(Localizer.dLocalize("advancement.challenge_trag_fire_500.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_fire_500.description")) + .model(CustomModel.get(Material.BLAZE_POWDER, "advancement", "tragoul", "challenge_trag_fire_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.MAGMA_CREAM) + .key("challenge_trag_fire_5k") + .title(Localizer.dLocalize("advancement.challenge_trag_fire_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_fire_5k.description")) + .model(CustomModel.get(Material.MAGMA_CREAM, "advancement", "tragoul", "challenge_trag_fire_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_trag_fire_500", "trag.fire.damage", 500, getConfig().challengeTragReward); + registerMilestone("challenge_trag_fire_5k", "trag.fire.damage", 5000, getConfig().challengeTragReward); - // Chain 5 - Fall Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FEATHER) - .key("challenge_trag_fall_500") - .title(Localizer.dLocalize("advancement.challenge_trag_fall_500.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_fall_500.description")) - .model(CustomModel.get(Material.FEATHER, "advancement", "tragoul", "challenge_trag_fall_500")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.HAY_BLOCK) - .key("challenge_trag_fall_5k") - .title(Localizer.dLocalize("advancement.challenge_trag_fall_5k.title")) - .description(Localizer.dLocalize("advancement.challenge_trag_fall_5k.description")) - .model(CustomModel.get(Material.HAY_BLOCK, "advancement", "tragoul", "challenge_trag_fall_5k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_trag_fall_500", "trag.fall.damage", 500, getConfig().challengeTragReward); - registerMilestone("challenge_trag_fall_5k", "trag.fall.damage", 5000, getConfig().challengeTragReward); - } + // Chain 5 - Fall Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FEATHER) + .key("challenge_trag_fall_500") + .title(Localizer.dLocalize("advancement.challenge_trag_fall_500.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_fall_500.description")) + .model(CustomModel.get(Material.FEATHER, "advancement", "tragoul", "challenge_trag_fall_500")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.HAY_BLOCK) + .key("challenge_trag_fall_5k") + .title(Localizer.dLocalize("advancement.challenge_trag_fall_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_trag_fall_5k.description")) + .model(CustomModel.get(Material.HAY_BLOCK, "advancement", "tragoul", "challenge_trag_fall_5k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_trag_fall_500", "trag.fall.damage", 500, getConfig().challengeTragReward); + registerMilestone("challenge_trag_fall_5k", "trag.fall.damage", 5000, getConfig().challengeTragReward); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; - } - shouldReturnForPlayer(p, e, () -> { - if (e.getEntity().isDead() - || e.getEntity().isInvulnerable() - || p.isInvulnerable() - || p.isBlocking() - || !checkValidEntity(e.getEntity().getType())) { - return; - } - AdaptPlayer a = getPlayer(p); - a.getData().addStat("trag.hitsrecieved", 1); - a.getData().addStat("trag.damage", e.getDamage()); - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(a.getPlayer(), getConfig().damageReceivedXpMultiplier * e.getDamage()); - if (p.getHealth() - e.getFinalDamage() > 0 && p.getHealth() - e.getFinalDamage() <= 8) { - xp(a.getPlayer(), getConfig().lowHealthSurvivalXP); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } + shouldReturnForPlayer(p, e, () -> { + if (e.getEntity().isDead() + || e.getEntity().isInvulnerable() + || p.isInvulnerable() + || p.isBlocking() + || !checkValidEntity(e.getEntity().getType())) { + return; + } + AdaptPlayer a = getPlayer(p); + a.getData().addStat("trag.hitsrecieved", 1); + a.getData().addStat("trag.damage", e.getDamage()); + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(a.getPlayer(), getConfig().damageReceivedXpMultiplier * e.getDamage()); + if (p.getHealth() - e.getFinalDamage() > 0 && p.getHealth() - e.getFinalDamage() <= 8) { + xp(a.getPlayer(), getConfig().lowHealthSurvivalXP); + } + }); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(PlayerDeathEvent e) { - Player p = e.getEntity(); - shouldReturnForPlayer(p, () -> { - AdaptPlayer a = getPlayer(p); - a.getData().addStat("trag.deaths", 1); - if (AdaptConfig.get().isHardcoreResetOnPlayerDeath()) { - Adapt.info("Resetting " + p.getName() + "'s skills due to death"); - a.delete(p.getUniqueId()); - return; - } - if (getConfig().takeAwaySkillsOnDeath) { - if (areParticlesEnabled()) { - CloudEffect ce = new CloudEffect(Adapt.instance.adaptEffectManager); - ce.mainParticle = Particle.ASH; - ce.cloudParticle = Particles.REDSTONE; - ce.duration = 10000; - ce.iterations = 1000; - ce.setEntity(p); - ce.start(); - } - - if (this.hasBlacklistPermission(p, this)) { - return; - } + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerDeathEvent e) { + Player p = e.getEntity(); + shouldReturnForPlayer(p, () -> { + AdaptPlayer a = getPlayer(p); + a.getData().addStat("trag.deaths", 1); + if (AdaptConfig.get().isHardcoreResetOnPlayerDeath()) { + Adapt.info("Resetting " + p.getName() + "'s skills due to death"); + a.delete(p.getUniqueId()); + return; + } + if (getConfig().takeAwaySkillsOnDeath) { + if (areParticlesEnabled()) { + CloudEffect ce = new CloudEffect(Adapt.instance.adaptEffectManager); + ce.mainParticle = Particle.ASH; + ce.cloudParticle = Particles.REDSTONE; + ce.duration = 10000; + ce.iterations = 1000; + ce.setEntity(p); + ce.start(); + } - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.ENTITY_BLAZE_DEATH, 1f, 1f); + if (this.hasBlacklistPermission(p, this)) { + return; + } - PlayerSkillLine tragoul = a.getData().getSkillLineNullable("tragoul"); - if (tragoul != null) { - double xp = tragoul.getXp(); - if (xp > getConfig().deathXpLoss) { - xp(p, getConfig().deathXpLoss); - } else { - tragoul.setXp(0); - } - tragoul.setLastXP(xp); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.ENTITY_BLAZE_DEATH, 1f, 1f); - for (PlayerAdaptation adapt : tragoul.getAdaptations().values()) { - adapt.setLevel(Math.max(adapt.getLevel() - 1, 0)); - } + PlayerSkillLine tragoul = a.getData().getSkillLineNullable("tragoul"); + if (tragoul != null) { + double xp = tragoul.getXp(); + if (xp > getConfig().deathXpLoss) { + xp(p, getConfig().deathXpLoss); + } else { + tragoul.setXp(0); + } + tragoul.setLastXP(xp); - recalcTotalExp(p); - a.getData().pruneAdaptationsForPowerBudget(); - } - } - }); - } + for (PlayerAdaptation adapt : tragoul.getAdaptations().values()) { + adapt.setLevel(Math.max(adapt.getLevel() - 1, 0)); + } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageEvent e) { - if (!(e.getEntity() instanceof Player p)) { - return; + recalcTotalExp(p); + a.getData().pruneAdaptationsForPowerBudget(); } - shouldReturnForPlayer(p, e, () -> { - EntityDamageEvent.DamageCause cause = e.getCause(); - if (cause == EntityDamageEvent.DamageCause.FALL) { - getPlayer(p).getData().addStat("trag.fall.damage", e.getDamage()); - } else if (cause == EntityDamageEvent.DamageCause.FIRE - || cause == EntityDamageEvent.DamageCause.FIRE_TICK - || cause == EntityDamageEvent.DamageCause.LAVA) { - getPlayer(p).getData().addStat("trag.fire.damage", e.getDamage()); - } - }); + } + }); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; } + shouldReturnForPlayer(p, e, () -> { + EntityDamageEvent.DamageCause cause = e.getCause(); + if (cause == EntityDamageEvent.DamageCause.FALL) { + getPlayer(p).getData().addStat("trag.fall.damage", e.getDamage()); + } else if (cause == EntityDamageEvent.DamageCause.FIRE + || cause == EntityDamageEvent.DamageCause.FIRE_TICK + || cause == EntityDamageEvent.DamageCause.LAVA) { + getPlayer(p).getData().addStat("trag.fire.damage", e.getDamage()); + } + }); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Death Xp Loss for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - public double deathXpLoss = -250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Take Away Skills On Death for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") - boolean takeAwaySkillsOnDeath = false; - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&b"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") - boolean showParticles = true; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 450; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Received Xp Multiplier for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageReceivedXpMultiplier = 4.8; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP bonus for surviving a hit below 4 hearts.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double lowHealthSurvivalXP = 28; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Trag Reward for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeTragReward = 500; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Death Xp Loss for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + public double deathXpLoss = -250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Take Away Skills On Death for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") + boolean takeAwaySkillsOnDeath = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&b"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Trag Oul skill.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 450; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage Received Xp Multiplier for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageReceivedXpMultiplier = 4.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls XP bonus for surviving a hit below 4 hearts.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lowHealthSurvivalXP = 28; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Trag Reward for the Trag Oul skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeTragReward = 500; + } } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java index 207746a5b..27b1f528f 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java @@ -41,228 +41,228 @@ import java.util.UUID; public class SkillUnarmed extends SimpleSkill { - private final Map cooldowns; + private final Map cooldowns; - public SkillUnarmed() { - super("unarmed", Localizer.dLocalize("skill.unarmed.icon")); - registerConfiguration(Config.class); - cooldowns = new HashMap<>(); - setColor(C.YELLOW); - setDescription(Localizer.dLocalize("skill.unarmed.description")); - setDisplayName(Localizer.dLocalize("skill.unarmed.name")); - setInterval(2579); - registerAdaptation(new UnarmedSuckerPunch()); - registerAdaptation(new UnarmedPower()); - registerAdaptation(new UnarmedGlassCannon()); - registerAdaptation(new UnarmedBatteringCharge()); - registerAdaptation(new UnarmedComboChain()); - setIcon(Material.FIRE_CHARGE); - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FIRE_CHARGE) - .key("challenge_unarmed_100") - .title(Localizer.dLocalize("advancement.challenge_unarmed_100.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_100.description")) - .model(CustomModel.get(Material.FIRE_CHARGE, "advancement", "unarmed", "challenge_unarmed_100")) + public SkillUnarmed() { + super("unarmed", Localizer.dLocalize("skill.unarmed.icon")); + registerConfiguration(Config.class); + cooldowns = new HashMap<>(); + setColor(C.YELLOW); + setDescription(Localizer.dLocalize("skill.unarmed.description")); + setDisplayName(Localizer.dLocalize("skill.unarmed.name")); + setInterval(2579); + registerAdaptation(new UnarmedSuckerPunch()); + registerAdaptation(new UnarmedPower()); + registerAdaptation(new UnarmedGlassCannon()); + registerAdaptation(new UnarmedBatteringCharge()); + registerAdaptation(new UnarmedComboChain()); + setIcon(Material.FIRE_CHARGE); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FIRE_CHARGE) + .key("challenge_unarmed_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_100.description")) + .model(CustomModel.get(Material.FIRE_CHARGE, "advancement", "unarmed", "challenge_unarmed_100")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BLAZE_POWDER) + .key("challenge_unarmed_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_1k.description")) + .model(CustomModel.get(Material.BLAZE_POWDER, "advancement", "unarmed", "challenge_unarmed_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHER_STAR) + .key("challenge_unarmed_10k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_10k.description")) + .model(CustomModel.get(Material.NETHER_STAR, "advancement", "unarmed", "challenge_unarmed_10k")) .frame(AdaptAdvancementFrame.CHALLENGE) .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BLAZE_POWDER) - .key("challenge_unarmed_1k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_1k.description")) - .model(CustomModel.get(Material.BLAZE_POWDER, "advancement", "unarmed", "challenge_unarmed_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.NETHER_STAR) - .key("challenge_unarmed_10k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_10k.description")) - .model(CustomModel.get(Material.NETHER_STAR, "advancement", "unarmed", "challenge_unarmed_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()) - .build()); - registerMilestone("challenge_unarmed_100", "unarmed.hits", 100, getConfig().challengeUnarmedReward); - registerMilestone("challenge_unarmed_1k", "unarmed.hits", 1000, getConfig().challengeUnarmedReward * 2); - registerMilestone("challenge_unarmed_10k", "unarmed.hits", 10000, getConfig().challengeUnarmedReward * 5); + .build()) + .build()) + .build()); + registerMilestone("challenge_unarmed_100", "unarmed.hits", 100, getConfig().challengeUnarmedReward); + registerMilestone("challenge_unarmed_1k", "unarmed.hits", 1000, getConfig().challengeUnarmedReward * 2); + registerMilestone("challenge_unarmed_10k", "unarmed.hits", 10000, getConfig().challengeUnarmedReward * 5); - // Chain 2 - Unarmed Damage - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.FIRE_CHARGE) - .key("challenge_unarmed_dmg_1k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_dmg_1k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_dmg_1k.description")) - .model(CustomModel.get(Material.FIRE_CHARGE, "advancement", "unarmed", "challenge_unarmed_dmg_1k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.BLAZE_ROD) - .key("challenge_unarmed_dmg_10k") - .title(Localizer.dLocalize("advancement.challenge_unarmed_dmg_10k.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_dmg_10k.description")) - .model(CustomModel.get(Material.BLAZE_ROD, "advancement", "unarmed", "challenge_unarmed_dmg_10k")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_dmg_1k", "unarmed.damage", 1000, getConfig().challengeUnarmedDmgReward); - registerMilestone("challenge_unarmed_dmg_10k", "unarmed.damage", 10000, getConfig().challengeUnarmedDmgReward * 3); + // Chain 2 - Unarmed Damage + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FIRE_CHARGE) + .key("challenge_unarmed_dmg_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_dmg_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_dmg_1k.description")) + .model(CustomModel.get(Material.FIRE_CHARGE, "advancement", "unarmed", "challenge_unarmed_dmg_1k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BLAZE_ROD) + .key("challenge_unarmed_dmg_10k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_dmg_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_dmg_10k.description")) + .model(CustomModel.get(Material.BLAZE_ROD, "advancement", "unarmed", "challenge_unarmed_dmg_10k")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_dmg_1k", "unarmed.damage", 1000, getConfig().challengeUnarmedDmgReward); + registerMilestone("challenge_unarmed_dmg_10k", "unarmed.damage", 10000, getConfig().challengeUnarmedDmgReward * 3); - // Chain 3 - Unarmed Kills - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.ROTTEN_FLESH) - .key("challenge_unarmed_kills_25") - .title(Localizer.dLocalize("advancement.challenge_unarmed_kills_25.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_kills_25.description")) - .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "unarmed", "challenge_unarmed_kills_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.ZOMBIE_HEAD) - .key("challenge_unarmed_kills_250") - .title(Localizer.dLocalize("advancement.challenge_unarmed_kills_250.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_kills_250.description")) - .model(CustomModel.get(Material.ZOMBIE_HEAD, "advancement", "unarmed", "challenge_unarmed_kills_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_kills_25", "unarmed.kills", 25, getConfig().challengeUnarmedKillsReward); - registerMilestone("challenge_unarmed_kills_250", "unarmed.kills", 250, getConfig().challengeUnarmedKillsReward * 3); + // Chain 3 - Unarmed Kills + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ROTTEN_FLESH) + .key("challenge_unarmed_kills_25") + .title(Localizer.dLocalize("advancement.challenge_unarmed_kills_25.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_kills_25.description")) + .model(CustomModel.get(Material.ROTTEN_FLESH, "advancement", "unarmed", "challenge_unarmed_kills_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.ZOMBIE_HEAD) + .key("challenge_unarmed_kills_250") + .title(Localizer.dLocalize("advancement.challenge_unarmed_kills_250.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_kills_250.description")) + .model(CustomModel.get(Material.ZOMBIE_HEAD, "advancement", "unarmed", "challenge_unarmed_kills_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_kills_25", "unarmed.kills", 25, getConfig().challengeUnarmedKillsReward); + registerMilestone("challenge_unarmed_kills_250", "unarmed.kills", 250, getConfig().challengeUnarmedKillsReward * 3); - // Chain 4 - Unarmed Criticals - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.SUGAR) - .key("challenge_unarmed_crit_25") - .title(Localizer.dLocalize("advancement.challenge_unarmed_crit_25.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_crit_25.description")) - .model(CustomModel.get(Material.SUGAR, "advancement", "unarmed", "challenge_unarmed_crit_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.FERMENTED_SPIDER_EYE) - .key("challenge_unarmed_crit_250") - .title(Localizer.dLocalize("advancement.challenge_unarmed_crit_250.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_crit_250.description")) - .model(CustomModel.get(Material.FERMENTED_SPIDER_EYE, "advancement", "unarmed", "challenge_unarmed_crit_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_crit_25", "unarmed.critical", 25, getConfig().challengeUnarmedCritReward); - registerMilestone("challenge_unarmed_crit_250", "unarmed.critical", 250, getConfig().challengeUnarmedCritReward * 3); + // Chain 4 - Unarmed Criticals + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SUGAR) + .key("challenge_unarmed_crit_25") + .title(Localizer.dLocalize("advancement.challenge_unarmed_crit_25.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_crit_25.description")) + .model(CustomModel.get(Material.SUGAR, "advancement", "unarmed", "challenge_unarmed_crit_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.FERMENTED_SPIDER_EYE) + .key("challenge_unarmed_crit_250") + .title(Localizer.dLocalize("advancement.challenge_unarmed_crit_250.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_crit_250.description")) + .model(CustomModel.get(Material.FERMENTED_SPIDER_EYE, "advancement", "unarmed", "challenge_unarmed_crit_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_crit_25", "unarmed.critical", 25, getConfig().challengeUnarmedCritReward); + registerMilestone("challenge_unarmed_crit_250", "unarmed.critical", 250, getConfig().challengeUnarmedCritReward * 3); - // Chain 5 - Unarmed Heavy Hits - registerAdvancement(AdaptAdvancement.builder() - .icon(Material.TNT) - .key("challenge_unarmed_heavy_25") - .title(Localizer.dLocalize("advancement.challenge_unarmed_heavy_25.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_heavy_25.description")) - .model(CustomModel.get(Material.TNT, "advancement", "unarmed", "challenge_unarmed_heavy_25")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .child(AdaptAdvancement.builder() - .icon(Material.END_CRYSTAL) - .key("challenge_unarmed_heavy_250") - .title(Localizer.dLocalize("advancement.challenge_unarmed_heavy_250.title")) - .description(Localizer.dLocalize("advancement.challenge_unarmed_heavy_250.description")) - .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "unarmed", "challenge_unarmed_heavy_250")) - .frame(AdaptAdvancementFrame.CHALLENGE) - .visibility(AdvancementVisibility.PARENT_GRANTED) - .build()) - .build()); - registerMilestone("challenge_unarmed_heavy_25", "unarmed.heavy", 25, getConfig().challengeUnarmedHeavyReward); - registerMilestone("challenge_unarmed_heavy_250", "unarmed.heavy", 250, getConfig().challengeUnarmedHeavyReward * 3); + // Chain 5 - Unarmed Heavy Hits + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TNT) + .key("challenge_unarmed_heavy_25") + .title(Localizer.dLocalize("advancement.challenge_unarmed_heavy_25.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_heavy_25.description")) + .model(CustomModel.get(Material.TNT, "advancement", "unarmed", "challenge_unarmed_heavy_25")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.END_CRYSTAL) + .key("challenge_unarmed_heavy_250") + .title(Localizer.dLocalize("advancement.challenge_unarmed_heavy_250.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_heavy_250.description")) + .model(CustomModel.get(Material.END_CRYSTAL, "advancement", "unarmed", "challenge_unarmed_heavy_250")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_heavy_25", "unarmed.heavy", 25, getConfig().challengeUnarmedHeavyReward); + registerMilestone("challenge_unarmed_heavy_250", "unarmed.heavy", 250, getConfig().challengeUnarmedHeavyReward * 3); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p)) { + return; } - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDamageByEntityEvent e) { - if (!(e.getDamager() instanceof Player p)) { - return; - } + shouldReturnForPlayer(p, e, () -> { + if (e.getEntity().isDead() + || e.getEntity().isInvulnerable() + || p.isInvulnerable()) { + return; + } - shouldReturnForPlayer(p, e, () -> { - if (e.getEntity().isDead() - || e.getEntity().isInvulnerable() - || p.isInvulnerable()) { - return; - } + if (!checkValidEntity(e.getEntity().getType())) { + return; + } - if (!checkValidEntity(e.getEntity().getType())) { - return; - } + AdaptPlayer a = getPlayer(p); + ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); - AdaptPlayer a = getPlayer(p); - ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); + if (!isMelee(hand)) { + a.getData().addStat("unarmed.hits", 1); + a.getData().addStat("unarmed.damage", e.getDamage()); + if (p.getFallDistance() > 0 && !p.isOnGround()) { + a.getData().addStat("unarmed.critical", 1); + } + if (e.getDamage() > 6) { + a.getData().addStat("unarmed.heavy", 1); + } + Long cooldown = cooldowns.get(p.getUniqueId()); + if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) + return; + cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage()); + } + }); + } - if (!isMelee(hand)) { - a.getData().addStat("unarmed.hits", 1); - a.getData().addStat("unarmed.damage", e.getDamage()); - if (p.getFallDistance() > 0 && !p.isOnGround()) { - a.getData().addStat("unarmed.critical", 1); - } - if (e.getDamage() > 6) { - a.getData().addStat("unarmed.heavy", 1); - } - Long cooldown = cooldowns.get(p.getUniqueId()); - if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) - return; - cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); - xp(a.getPlayer(), e.getEntity().getLocation(), getConfig().damageXPMultiplier * e.getDamage()); - } - }); + @EventHandler(priority = EventPriority.MONITOR) + public void on(EntityDeathEvent e) { + if (e.getEntity().getKiller() == null) { + return; } + Player p = e.getEntity().getKiller(); - @EventHandler(priority = EventPriority.MONITOR) - public void on(EntityDeathEvent e) { - if (e.getEntity().getKiller() == null) { - return; - } - Player p = e.getEntity().getKiller(); + shouldReturnForPlayer(p, () -> { + AdaptPlayer a = getPlayer(p); + ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); - shouldReturnForPlayer(p, () -> { - AdaptPlayer a = getPlayer(p); - ItemStack hand = a.getPlayer().getInventory().getItemInMainHand(); + if (!isMelee(hand)) { + a.getData().addStat("unarmed.kills", 1); + } + }); + } - if (!isMelee(hand)) { - a.getData().addStat("unarmed.kills", 1); - } - }); + @Override + public void onTick() { + if (!this.isEnabled()) { + return; } + checkStatTrackersForOnlinePlayers(); + } - @Override - public void onTick() { - if (!this.isEnabled()) { - return; - } - checkStatTrackersForOnlinePlayers(); - } - - @Override - public boolean isEnabled() { - return getConfig().enabled; - } + @Override + public boolean isEnabled() { + return getConfig().enabled; + } - @NoArgsConstructor - protected static class Config { - @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") - boolean enabled = true; - String skillColor = "&e"; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double damageXPMultiplier = 4.5; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - long cooldownDelay = 1250; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeUnarmedReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Damage Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeUnarmedDmgReward = 500; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Kills Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeUnarmedKillsReward = 750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Critical Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeUnarmedCritReward = 750; - @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Heavy Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") - double challengeUnarmedHeavyReward = 750; - } + @NoArgsConstructor + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + String skillColor = "&e"; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Damage XPMultiplier for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double damageXPMultiplier = 4.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Delay for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + long cooldownDelay = 1250; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeUnarmedReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Damage Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeUnarmedDmgReward = 500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Kills Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeUnarmedKillsReward = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Critical Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeUnarmedCritReward = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Challenge Unarmed Heavy Reward for the Unarmed skill.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double challengeUnarmedHeavyReward = 750; + } } diff --git a/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java b/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java index 2906dd87b..533499141 100644 --- a/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java +++ b/src/main/java/art/arcane/adapt/core/nms/container/BlockProperty.java @@ -8,158 +8,158 @@ import java.util.function.Function; public class BlockProperty { - private static final Set> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class); - private final String name; - private final Class type; - - private final Object defaultValue; - private final Set values; - private final Function nameFunction; - private final Function jsonFunction; - - public > BlockProperty( - String name, - Class type, - T defaultValue, - Collection values, - Function nameFunction - ) { - this.name = name; - this.type = type; - this.defaultValue = defaultValue; - this.values = Collections.unmodifiableSet(new TreeSet<>(values)); - this.nameFunction = (Function) (Object) nameFunction; - jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply; - } - - public static > BlockProperty ofEnum(Class type, String name, T defaultValue) { - return new BlockProperty( - name, - type, - defaultValue, - Arrays.asList(type.getEnumConstants()), - val -> val == null ? "null" : val.name() - ); - } - - public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) { - return new BoundedDouble( - name, - defaultValue, - min, - max, - exclusiveMin, - exclusiveMax, - (f) -> String.format("%.2f", f) - ); - } - - public static BlockProperty ofBoolean(String name, boolean defaultValue) { - return new BlockProperty( - name, - Boolean.class, - defaultValue, - List.of(true, false), - (b) -> b ? "true" : "false" - ); - } - - @Override - public @NotNull String toString() { - return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]"; - } - - public String name() { - return name; + private static final Set> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class); + private final String name; + private final Class type; + + private final Object defaultValue; + private final Set values; + private final Function nameFunction; + private final Function jsonFunction; + + public > BlockProperty( + String name, + Class type, + T defaultValue, + Collection values, + Function nameFunction + ) { + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + this.values = Collections.unmodifiableSet(new TreeSet<>(values)); + this.nameFunction = (Function) (Object) nameFunction; + jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply; + } + + public static > BlockProperty ofEnum(Class type, String name, T defaultValue) { + return new BlockProperty( + name, + type, + defaultValue, + Arrays.asList(type.getEnumConstants()), + val -> val == null ? "null" : val.name() + ); + } + + public static BlockProperty ofFloat(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) { + return new BoundedDouble( + name, + defaultValue, + min, + max, + exclusiveMin, + exclusiveMax, + (f) -> String.format("%.2f", f) + ); + } + + public static BlockProperty ofBoolean(String name, boolean defaultValue) { + return new BlockProperty( + name, + Boolean.class, + defaultValue, + List.of(true, false), + (b) -> b ? "true" : "false" + ); + } + + @Override + public @NotNull String toString() { + return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]"; + } + + public String name() { + return name; + } + + public String defaultValue() { + return nameFunction.apply(defaultValue); + } + + public List names() { + return values.stream().map(nameFunction).toList(); + } + + public Object defaultValueAsJson() { + return jsonFunction.apply(defaultValue); + } + + public JSONArray valuesAsJson() { + return new JSONArray(values.stream().map(jsonFunction).toList()); + } + + public JSONObject buildJson() { + art.arcane.volmlib.util.json.JSONObject json = new JSONObject(); + json.put("type", jsonType()); + json.put("default", defaultValueAsJson()); + if (!values.isEmpty()) { + json.put("enum", valuesAsJson()); } + return json; + } - public String defaultValue() { - return nameFunction.apply(defaultValue); + public String jsonType() { + if (type == Boolean.class) { + return "boolean"; } - - public List names() { - return values.stream().map(nameFunction).toList(); - } - - public Object defaultValueAsJson() { - return jsonFunction.apply(defaultValue); + if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class) { + return "integer"; } - - public JSONArray valuesAsJson() { - return new JSONArray(values.stream().map(jsonFunction).toList()); + if (type == Float.class || type == Double.class) { + return "number"; } + return "string"; + } - public JSONObject buildJson() { - var json = new JSONObject(); - json.put("type", jsonType()); - json.put("default", defaultValueAsJson()); - if (!values.isEmpty()) { - json.put("enum", valuesAsJson()); - } - return json; + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; } - - public String jsonType() { - if (type == Boolean.class) { - return "boolean"; - } - if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class) { - return "integer"; - } - if (type == Float.class || type == Double.class) { - return "number"; - } - return "string"; + if (obj == null || obj.getClass() != this.getClass()) { + return false; } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj == null || obj.getClass() != this.getClass()) { - return false; - } - var that = (BlockProperty) obj; - return Objects.equals(this.name, that.name) && - Objects.equals(this.values, that.values) && - Objects.equals(this.type, that.type); + art.arcane.adapt.core.nms.container.BlockProperty that = (BlockProperty) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.values, that.values) && + Objects.equals(this.type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, values, type); + } + + private static class BoundedDouble extends BlockProperty { + private final double min; + private final double max; + private final boolean exclusiveMin; + private final boolean exclusiveMax; + + public BoundedDouble( + String name, + double defaultValue, + double min, + double max, + boolean exclusiveMin, + boolean exclusiveMax, + Function nameFunction + ) { + super(name, Double.class, defaultValue, List.of(), nameFunction); + this.min = min; + this.max = max; + this.exclusiveMin = exclusiveMin; + this.exclusiveMax = exclusiveMax; } @Override - public int hashCode() { - return Objects.hash(name, values, type); - } - - private static class BoundedDouble extends BlockProperty { - private final double min; - private final double max; - private final boolean exclusiveMin; - private final boolean exclusiveMax; - - public BoundedDouble( - String name, - double defaultValue, - double min, - double max, - boolean exclusiveMin, - boolean exclusiveMax, - Function nameFunction - ) { - super(name, Double.class, defaultValue, List.of(), nameFunction); - this.min = min; - this.max = max; - this.exclusiveMin = exclusiveMin; - this.exclusiveMax = exclusiveMax; - } - - @Override - public JSONObject buildJson() { - return super.buildJson() - .put("minimum", min) - .put("maximum", max) - .put("exclusiveMinimum", exclusiveMin) - .put("exclusiveMaximum", exclusiveMax); - } + public JSONObject buildJson() { + return super.buildJson() + .put("minimum", min) + .put("maximum", max) + .put("exclusiveMinimum", exclusiveMin) + .put("exclusiveMaximum", exclusiveMax); } + } } diff --git a/src/main/java/art/arcane/adapt/nms/NMS.java b/src/main/java/art/arcane/adapt/nms/NMS.java index ecb10c47c..b1f92109d 100644 --- a/src/main/java/art/arcane/adapt/nms/NMS.java +++ b/src/main/java/art/arcane/adapt/nms/NMS.java @@ -11,22 +11,22 @@ public class NMS { - public static String serializeStack(ItemStack is) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (BukkitObjectOutputStream oos = new BukkitObjectOutputStream(out)){ - oos.writeObject(is); - return Base64.getUrlEncoder().encodeToString(out.toByteArray()); - } catch (IOException e) { - throw new RuntimeException(e); - } + public static String serializeStack(ItemStack is) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (BukkitObjectOutputStream oos = new BukkitObjectOutputStream(out)) { + oos.writeObject(is); + return Base64.getUrlEncoder().encodeToString(out.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); } + } - public static ItemStack deserializeStack(String s) { - ByteArrayInputStream in = new ByteArrayInputStream(Base64.getUrlDecoder().decode(s)); - try (BukkitObjectInputStream ois = new BukkitObjectInputStream(in)) { - return (ItemStack) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } + public static ItemStack deserializeStack(String s) { + ByteArrayInputStream in = new ByteArrayInputStream(Base64.getUrlDecoder().decode(s)); + try (BukkitObjectInputStream ois = new BukkitObjectInputStream(in)) { + return (ItemStack) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); } + } } diff --git a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java index fb24104b4..fd0bb8f7d 100644 --- a/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java +++ b/src/main/java/art/arcane/adapt/service/AdaptIntegrationService.java @@ -14,161 +14,173 @@ import java.util.Set; public class AdaptIntegrationService implements AdaptService, IntegrationServiceContract { - private static final Set SUPPORTED_PROTOCOLS = Set.of( - new IntegrationProtocolVersion(1, 0), - new IntegrationProtocolVersion(1, 1) - ); - private static final Set CAPABILITIES = Set.of( - "handshake", - "heartbeat", - "metrics", - "adapt-runtime-metrics" - ); - - private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1); - - @Override - public void onEnable() { - Bukkit.getServicesManager().register(IntegrationServiceContract.class, this, Adapt.instance, ServicePriority.Normal); - Adapt.verbose("Integration provider registered for Adapt"); + private static final Set SUPPORTED_PROTOCOLS = Set.of( + new IntegrationProtocolVersion(1, 0), + new IntegrationProtocolVersion(1, 1) + ); + private static final Set CAPABILITIES = Set.of( + "handshake", + "heartbeat", + "metrics", + "adapt-runtime-metrics" + ); + + private volatile IntegrationProtocolVersion negotiatedProtocol = new IntegrationProtocolVersion(1, 1); + + @Override + public void onEnable() { + Bukkit.getServicesManager().register(IntegrationServiceContract.class, this, Adapt.instance, ServicePriority.Normal); + Adapt.verbose("Integration provider registered for Adapt"); + } + + @Override + public void onDisable() { + Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this); + AbilityCheckTelemetry.clear(); + WorldPolicyLatencyTelemetry.clear(); + } + + @Override + public String pluginId() { + return "adapt"; + } + + @Override + public String pluginVersion() { + return Adapt.instance.getDescription().getVersion(); + } + + @Override + public Set supportedProtocols() { + return SUPPORTED_PROTOCOLS; + } + + @Override + public Set capabilities() { + return CAPABILITIES; + } + + @Override + public Set metricDescriptors() { + return IntegrationMetricSchema.descriptors().stream() + .filter(descriptor -> descriptor.key().startsWith("adapt.")) + .collect(java.util.stream.Collectors.toSet()); + } + + @Override + public IntegrationHandshakeResponse handshake(IntegrationHandshakeRequest request) { + long now = System.currentTimeMillis(); + if (request == null) { + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + false, + null, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "missing request", + now + ); } - @Override - public void onDisable() { - Bukkit.getServicesManager().unregister(IntegrationServiceContract.class, this); - AbilityCheckTelemetry.clear(); - WorldPolicyLatencyTelemetry.clear(); - } - - @Override - public String pluginId() { - return "adapt"; - } - - @Override - public String pluginVersion() { - return Adapt.instance.getDescription().getVersion(); - } - - @Override - public Set supportedProtocols() { - return SUPPORTED_PROTOCOLS; - } - - @Override - public Set capabilities() { - return CAPABILITIES; - } - - @Override - public Set metricDescriptors() { - return IntegrationMetricSchema.descriptors().stream() - .filter(descriptor -> descriptor.key().startsWith("adapt.")) - .collect(java.util.stream.Collectors.toSet()); - } - - @Override - public IntegrationHandshakeResponse handshake(IntegrationHandshakeRequest request) { - long now = System.currentTimeMillis(); - if (request == null) { - return new IntegrationHandshakeResponse( - pluginId(), - pluginVersion(), - false, - null, - SUPPORTED_PROTOCOLS, - CAPABILITIES, - "missing request", - now - ); - } - - Optional negotiated = IntegrationProtocolNegotiator.negotiate( - SUPPORTED_PROTOCOLS, - request.supportedProtocols() - ); - if (negotiated.isEmpty()) { - return new IntegrationHandshakeResponse( - pluginId(), - pluginVersion(), - false, - null, - SUPPORTED_PROTOCOLS, - CAPABILITIES, - "no-common-protocol", - now - ); - } - - negotiatedProtocol = negotiated.get(); - return new IntegrationHandshakeResponse( - pluginId(), - pluginVersion(), - true, - negotiatedProtocol, - SUPPORTED_PROTOCOLS, - CAPABILITIES, - "ok", - now - ); - } - - @Override - public IntegrationHeartbeat heartbeat() { - long now = System.currentTimeMillis(); - return new IntegrationHeartbeat(negotiatedProtocol, true, now, "ok"); - } - - @Override - public Map sampleMetrics(Set metricKeys) { - Set requested = metricKeys == null || metricKeys.isEmpty() - ? IntegrationMetricSchema.adaptKeys() - : metricKeys; - long now = System.currentTimeMillis(); - Map out = new HashMap<>(); - - for (String key : requested) { - switch (key) { - case IntegrationMetricSchema.ADAPT_SESSION_LOAD -> out.put(key, sampleSessionLoad(now)); - case IntegrationMetricSchema.ADAPT_ABILITY_OPS -> out.put(key, sampleAbilityOps(now)); - case IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS -> out.put(key, sampleAbilityCheckOps(now)); - case IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY -> out.put(key, sampleWorldPolicyLatency(now)); - default -> out.put(key, IntegrationMetricSample.unavailable( - IntegrationMetricSchema.descriptor(key), - "unsupported-key", - now - )); - } - } - - return out; + Optional negotiated = IntegrationProtocolNegotiator.negotiate( + SUPPORTED_PROTOCOLS, + request.supportedProtocols() + ); + if (negotiated.isEmpty()) { + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + false, + null, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "no-common-protocol", + now + ); } - private IntegrationMetricSample sampleSessionLoad(long now) { - IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_SESSION_LOAD); - if (Adapt.instance.getTicker() == null) { - return IntegrationMetricSample.unavailable(descriptor, "ticker-not-ready", now); - } - - double load = Adapt.instance.getTicker().getWindowLoadPercent(); - return IntegrationMetricSample.available(descriptor, load, now); + negotiatedProtocol = negotiated.get(); + return new IntegrationHandshakeResponse( + pluginId(), + pluginVersion(), + true, + negotiatedProtocol, + SUPPORTED_PROTOCOLS, + CAPABILITIES, + "ok", + now + ); + } + + @Override + public IntegrationHeartbeat heartbeat() { + long now = System.currentTimeMillis(); + return new IntegrationHeartbeat(negotiatedProtocol, true, now, "ok"); + } + + @Override + public Map sampleMetrics(Set metricKeys) { + Set requested = metricKeys == null || metricKeys.isEmpty() + ? IntegrationMetricSchema.adaptKeys() + : metricKeys; + long now = System.currentTimeMillis(); + Map out = new HashMap<>(); + + for (String key : requested) { + switch (key) { + case IntegrationMetricSchema.ADAPT_SESSION_LOAD -> + out.put(key, sampleSessionLoad(now)); + case IntegrationMetricSchema.ADAPT_ABILITY_OPS -> + out.put(key, sampleAbilityOps(now)); + case IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS -> + out.put(key, sampleAbilityCheckOps(now)); + case IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS_TICK -> + out.put(key, sampleAbilityCheckOpsTick(now)); + case IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY -> + out.put(key, sampleWorldPolicyLatency(now)); + default -> out.put(key, IntegrationMetricSample.unavailable( + IntegrationMetricSchema.descriptor(key), + "unsupported-key", + now + )); + } } - private IntegrationMetricSample sampleAbilityOps(long now) { - IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_OPS); - long count = AbilityCheckTelemetry.successfulChecksPerMinute(now); - return IntegrationMetricSample.available(descriptor, count, now); - } + return out; + } - private IntegrationMetricSample sampleAbilityCheckOps(long now) { - IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS); - long count = AbilityCheckTelemetry.checksPerMinute(now); - return IntegrationMetricSample.available(descriptor, count, now); + private IntegrationMetricSample sampleSessionLoad(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_SESSION_LOAD); + if (Adapt.instance.getTicker() == null) { + return IntegrationMetricSample.unavailable(descriptor, "ticker-not-ready", now); } - private IntegrationMetricSample sampleWorldPolicyLatency(long now) { - IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY); - double averageMs = WorldPolicyLatencyTelemetry.averageMillis(now); - return IntegrationMetricSample.available(descriptor, averageMs, now); - } + double load = Adapt.instance.getTicker().getWindowLoadPercent(); + return IntegrationMetricSample.available(descriptor, load, now); + } + + private IntegrationMetricSample sampleAbilityOps(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_OPS); + long count = AbilityCheckTelemetry.successfulChecksPerMinute(now); + return IntegrationMetricSample.available(descriptor, count, now); + } + + private IntegrationMetricSample sampleAbilityCheckOps(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS); + long count = AbilityCheckTelemetry.checksPerMinute(now); + return IntegrationMetricSample.available(descriptor, count, now); + } + + private IntegrationMetricSample sampleAbilityCheckOpsTick(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_ABILITY_CHECK_OPS_TICK); + double count = AbilityCheckTelemetry.checksPerTick(now); + return IntegrationMetricSample.available(descriptor, count, now); + } + + private IntegrationMetricSample sampleWorldPolicyLatency(long now) { + IntegrationMetricDescriptor descriptor = IntegrationMetricSchema.descriptor(IntegrationMetricSchema.ADAPT_WORLD_POLICY_LATENCY); + double averageMs = WorldPolicyLatencyTelemetry.averageMillis(now); + return IntegrationMetricSample.available(descriptor, averageMs, now); + } } diff --git a/src/main/java/art/arcane/adapt/service/CommandSVC.java b/src/main/java/art/arcane/adapt/service/CommandSVC.java index 77e1b67fe..dedc57e5a 100644 --- a/src/main/java/art/arcane/adapt/service/CommandSVC.java +++ b/src/main/java/art/arcane/adapt/service/CommandSVC.java @@ -45,192 +45,193 @@ import java.util.Optional; public class CommandSVC implements AdaptService, CommandExecutor, TabCompleter, DirectorInvocationHook { - private static final String ROOT_COMMAND = "adapt"; - private static final String ROOT_PERMISSION = "adapt.main"; + private static final String ROOT_COMMAND = "adapt"; + private static final String ROOT_PERMISSION = "adapt.main"; - private final transient AtomicCache directorCache = new AtomicCache<>(); - private final transient AtomicCache helpCache = new AtomicCache<>(); + private final transient AtomicCache directorCache = new AtomicCache<>(); + private final transient AtomicCache helpCache = new AtomicCache<>(); - @Override - public void onEnable() { - Adapt.verbose("Initializing Commands..."); - PluginCommand command = Adapt.instance.getCommand(ROOT_COMMAND); - if (command == null) { - Adapt.warn("Failed to find command '" + ROOT_COMMAND + "'"); - return; - } - - command.setExecutor(this); - command.setTabCompleter(this); - J.a(this::getDirector); + @Override + public void onEnable() { + Adapt.verbose("Initializing Commands..."); + PluginCommand command = Adapt.instance.getCommand(ROOT_COMMAND); + if (command == null) { + Adapt.warn("Failed to find command '" + ROOT_COMMAND + "'"); + return; } - @Override - public void onDisable() { + command.setExecutor(this); + command.setTabCompleter(this); + J.a(this::getDirector); + } - } + @Override + public void onDisable() { - public DirectorRuntimeEngine getDirector() { - return directorCache.aquireNastyPrint(() -> DirectorEngineFactory.create( - new CommandAdapt(), - null, - buildDirectorContexts(), - this::dispatchDirector, - this, - DirectorSystem.handlers - )); - } + } - private DirectorContextRegistry buildDirectorContexts() { - DirectorContextRegistry contexts = new DirectorContextRegistry(); - contexts.register(World.class, (invocation, map) -> { - if (invocation.getSender() instanceof BukkitDirectorSender sender && sender.sender() instanceof Player player) { - return player.getWorld(); - } + public DirectorRuntimeEngine getDirector() { + return directorCache.aquireNastyPrint(() -> DirectorEngineFactory.create( + new CommandAdapt(), + null, + buildDirectorContexts(), + this::dispatchDirector, + this, + DirectorSystem.handlers + )); + } - return null; - }); + private DirectorContextRegistry buildDirectorContexts() { + DirectorContextRegistry contexts = new DirectorContextRegistry(); + contexts.register(World.class, (invocation, map) -> { + if (invocation.getSender() instanceof BukkitDirectorSender sender && sender.sender() instanceof Player player) { + return player.getWorld(); + } - return contexts; - } + return null; + }); - private void dispatchDirector(DirectorExecutionMode mode, Runnable runnable) { - if (mode == DirectorExecutionMode.SYNC) { - J.s(runnable); - } else { - runnable.run(); - } - } + return contexts; + } - @Override - public void beforeInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { - if (invocation.getSender() instanceof BukkitDirectorSender sender) { - BukkitDirectorContext.touch(sender.sender()); - } + private void dispatchDirector(DirectorExecutionMode mode, Runnable runnable) { + if (mode == DirectorExecutionMode.SYNC) { + J.s(runnable); + } else { + runnable.run(); } + } - @Override - public void afterInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { - BukkitDirectorContext.remove(); + @Override + public void beforeInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { + if (invocation.getSender() instanceof BukkitDirectorSender sender) { + BukkitDirectorContext.touch(sender.sender()); } + } - @Nullable - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { - return List.of(); - } + @Override + public void afterInvoke(DirectorInvocation invocation, DirectorRuntimeNode node) { + BukkitDirectorContext.remove(); + } - List v = runDirectorTab(sender, alias, args); + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { + return List.of(); + } - if (sender instanceof Player p) { - SoundPlayer sp = SoundPlayer.of(p); - sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); - } + List v = runDirectorTab(sender, alias, args); - return v; + if (sender instanceof Player p) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.25f, RNG.r.f(0.125f, 1.95f)); } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { - return false; - } + return v; + } - Adapt.verbose("Received Command from %s: /%s".formatted(sender.getName(), label + String.join(" ", args))); - if (!sender.hasPermission(ROOT_PERMISSION)) { - sender.sendMessage("You lack the Permission '" + ROOT_PERMISSION + "'"); - return true; - } + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!command.getName().equalsIgnoreCase(ROOT_COMMAND)) { + return false; + } - executeCommand(sender, label, args); - return true; + Adapt.verbose("Received Command from %s: /%s".formatted(sender.getName(), label + String.join(" ", args))); + if (!sender.hasPermission(ROOT_PERMISSION)) { + sender.sendMessage("You lack the Permission '" + ROOT_PERMISSION + "'"); + return true; } - private void executeCommand(CommandSender sender, String label, String[] args) { - if (sendHelpIfRequested(sender, args)) { - playSuccessSound(sender); - return; - } + executeCommand(sender, label, args); + return true; + } - DirectorExecutionResult result = runDirector(sender, label, args); + private void executeCommand(CommandSender sender, String label, String[] args) { + if (sendHelpIfRequested(sender, args)) { + playSuccessSound(sender); + return; + } - if (result.isSuccess()) { - playSuccessSound(sender); - return; - } + DirectorExecutionResult result = runDirector(sender, label, args); - playFailureSound(sender); - if (result.getMessage() == null || result.getMessage().trim().isEmpty()) { - sender.sendMessage(C.RED + "Unknown Adapt Command"); - } + if (result.isSuccess()) { + playSuccessSound(sender); + return; } - private boolean sendHelpIfRequested(CommandSender sender, String[] args) { - Optional request = DirectorVisualCommand.resolveHelp(getHelpRoot(), Arrays.asList(args)); - if (request.isEmpty()) { - return false; - } - - VolmitSender volmitSender = new VolmitSender(sender); - volmitSender.sendDirectorHelp(request.get().command(), request.get().page()); - return true; + playFailureSound(sender); + if (result.getMessage() == null || result.getMessage().trim().isEmpty()) { + sender.sendMessage(C.RED + "Unknown Adapt Command"); } + } - private DirectorVisualCommand getHelpRoot() { - return helpCache.aquireNastyPrint(() -> DirectorVisualCommand.createRoot(getDirector())); + private boolean sendHelpIfRequested(CommandSender sender, String[] args) { + Optional request = DirectorVisualCommand.resolveHelp(getHelpRoot(), Arrays.asList(args)); + if (request.isEmpty()) { + return false; } - private DirectorExecutionResult runDirector(CommandSender sender, String label, String[] args) { - try { - return getDirector().execute(new DirectorInvocation(new BukkitDirectorSender(sender), label, Arrays.asList(args))); - } catch (Throwable e) { - Adapt.warn("Director command execution failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); - return DirectorExecutionResult.notHandled(); - } + VolmitSender volmitSender = new VolmitSender(sender); + volmitSender.sendDirectorHelp(request.get().command(), request.get().page()); + return true; + } + + private DirectorVisualCommand getHelpRoot() { + return helpCache.aquireNastyPrint(() -> DirectorVisualCommand.createRoot(getDirector())); + } + + private DirectorExecutionResult runDirector(CommandSender sender, String label, String[] args) { + try { + return getDirector().execute(new DirectorInvocation(new BukkitDirectorSender(sender), label, Arrays.asList(args))); + } catch (Throwable e) { + Adapt.warn("Director command execution failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); + return DirectorExecutionResult.notHandled(); } + } - private List runDirectorTab(CommandSender sender, String alias, String[] args) { - try { - return getDirector().tabComplete(new DirectorInvocation(new BukkitDirectorSender(sender), alias, Arrays.asList(args))); - } catch (Throwable e) { - Adapt.warn("Director tab completion failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); - return List.of(); - } + private List runDirectorTab(CommandSender sender, String alias, String[] args) { + try { + return getDirector().tabComplete(new DirectorInvocation(new BukkitDirectorSender(sender), alias, Arrays.asList(args))); + } catch (Throwable e) { + Adapt.warn("Director tab completion failed: " + e.getClass().getSimpleName() + " " + e.getMessage()); + return List.of(); } + } - private void playFailureSound(CommandSender sender) { - if (sender instanceof Player player) { - SoundPlayer sp = SoundPlayer.of(player); - sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, 0.77f, 0.25f); - sp.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.2f, 0.45f); - } + private void playFailureSound(CommandSender sender) { + if (sender instanceof Player player) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, 0.77f, 0.25f); + sp.play(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 0.2f, 0.45f); } + } - private void playSuccessSound(CommandSender sender) { - if (sender instanceof Player player) { - SoundPlayer sp = SoundPlayer.of(player); - sp.play(player.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_BREAK, 0.77f, 1.65f); - sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.125f, 2.99f); - } + private void playSuccessSound(CommandSender sender) { + if (sender instanceof Player player) { + SoundPlayer sp = SoundPlayer.of(player); + sp.play(player.getLocation(), Sound.BLOCK_AMETHYST_CLUSTER_BREAK, 0.77f, 1.65f); + sp.play(player.getLocation(), Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 0.125f, 2.99f); } + } - private record BukkitDirectorSender(CommandSender sender) implements DirectorSender { - @Override - public String getName() { - return sender.getName(); - } + private record BukkitDirectorSender( + CommandSender sender) implements DirectorSender { + @Override + public String getName() { + return sender.getName(); + } - @Override - public boolean isPlayer() { - return sender instanceof Player; - } + @Override + public boolean isPlayer() { + return sender instanceof Player; + } - @Override - public void sendMessage(String message) { - if (message != null && !message.trim().isEmpty()) { - sender.sendMessage(message); - } - } + @Override + public void sendMessage(String message) { + if (message != null && !message.trim().isEmpty()) { + sender.sendMessage(message); + } } + } } diff --git a/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java index 9f79ad980..da5dbfc80 100644 --- a/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java +++ b/src/main/java/art/arcane/adapt/service/ConfigInputSVC.java @@ -16,115 +16,115 @@ import java.util.concurrent.ConcurrentHashMap; public class ConfigInputSVC implements AdaptService { - private static final long SESSION_TIMEOUT_MS = 45_000L; + private static final long SESSION_TIMEOUT_MS = 45_000L; - private final Map sessions = new ConcurrentHashMap<>(); - private int cleanupTaskId = -1; + private final Map sessions = new ConcurrentHashMap<>(); + private int cleanupTaskId = -1; - @Override - public void onEnable() { - cleanupTaskId = J.sr(this::cleanupExpiredSessions, 20); + @Override + public void onEnable() { + cleanupTaskId = J.sr(this::cleanupExpiredSessions, 20); + } + + @Override + public void onDisable() { + if (cleanupTaskId != -1) { + J.csr(cleanupTaskId); + cleanupTaskId = -1; } + sessions.clear(); + } - @Override - public void onDisable() { - if (cleanupTaskId != -1) { - J.csr(cleanupTaskId); - cleanupTaskId = -1; - } - sessions.clear(); + public void beginSession(Player player, String valuePath, String returnSectionPath, int returnPage, Class targetType, String label) { + if (player == null) { + return; } - public void beginSession(Player player, String valuePath, String returnSectionPath, int returnPage, Class targetType, String label) { - if (player == null) { - return; - } - - PendingInput pending = new PendingInput( - player.getUniqueId(), - valuePath, - returnSectionPath == null ? "" : returnSectionPath, - Math.max(0, returnPage), - targetType, - label == null ? valuePath : label, - System.currentTimeMillis() + SESSION_TIMEOUT_MS - ); - sessions.put(player.getUniqueId(), pending); - - ConfigGui.suppressClose(player); - player.closeInventory(); - Adapt.messagePlayer(player, C.AQUA + "Enter value for " + C.WHITE + pending.label()); - Adapt.messagePlayer(player, C.AQUA + "Path: " + C.WHITE + pending.valuePath()); - Adapt.messagePlayer(player, C.AQUA + "Expected type: " + C.WHITE + ConfigGui.typeName(targetType)); - Adapt.messagePlayer(player, C.GRAY + "Type " + C.WHITE + "cancel" + C.GRAY + " to abort."); + PendingInput pending = new PendingInput( + player.getUniqueId(), + valuePath, + returnSectionPath == null ? "" : returnSectionPath, + Math.max(0, returnPage), + targetType, + label == null ? valuePath : label, + System.currentTimeMillis() + SESSION_TIMEOUT_MS + ); + sessions.put(player.getUniqueId(), pending); + + ConfigGui.suppressClose(player); + player.closeInventory(); + Adapt.messagePlayer(player, C.AQUA + "Enter value for " + C.WHITE + pending.label()); + Adapt.messagePlayer(player, C.AQUA + "Path: " + C.WHITE + pending.valuePath()); + Adapt.messagePlayer(player, C.AQUA + "Expected type: " + C.WHITE + ConfigGui.typeName(targetType)); + Adapt.messagePlayer(player, C.GRAY + "Type " + C.WHITE + "cancel" + C.GRAY + " to abort."); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onAsyncChat(AsyncPlayerChatEvent event) { + Player player = event.getPlayer(); + PendingInput pending = sessions.get(player.getUniqueId()); + if (pending == null) { + return; } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onAsyncChat(AsyncPlayerChatEvent event) { - Player player = event.getPlayer(); - PendingInput pending = sessions.get(player.getUniqueId()); - if (pending == null) { - return; - } - - event.setCancelled(true); - - if (pending.isExpired()) { - sessions.remove(player.getUniqueId()); - J.s(() -> { - Adapt.messagePlayer(player, C.RED + "Config input timed out."); - ConfigGui.open(player, pending.returnSectionPath(), pending.returnPage()); - }); - return; - } - - String message = event.getMessage() == null ? "" : event.getMessage(); - if (message.equalsIgnoreCase("cancel")) { - sessions.remove(player.getUniqueId()); - J.s(() -> { - Adapt.messagePlayer(player, C.YELLOW + "Config edit cancelled."); - ConfigGui.open(player, pending.returnSectionPath(), pending.returnPage()); - }); - return; - } - - ConfigGui.ParseResult parsed = ConfigGui.parseInputValue(pending.targetType(), message); - if (!parsed.success()) { - J.s(() -> { - Adapt.messagePlayer(player, C.RED + parsed.error()); - Adapt.messagePlayer(player, C.GRAY + "Try again or type " + C.WHITE + "cancel"); - }); - return; - } - - sessions.remove(player.getUniqueId()); - Object value = parsed.value(); - J.s(() -> { - ConfigGui.confirmAndApply(player, pending.returnSectionPath(), pending.returnPage(), pending.valuePath(), value); - }); + event.setCancelled(true); + + if (pending.isExpired()) { + sessions.remove(player.getUniqueId()); + J.s(() -> { + Adapt.messagePlayer(player, C.RED + "Config input timed out."); + ConfigGui.open(player, pending.returnSectionPath(), pending.returnPage()); + }); + return; } - @EventHandler - public void onQuit(PlayerQuitEvent event) { - sessions.remove(event.getPlayer().getUniqueId()); + String message = event.getMessage() == null ? "" : event.getMessage(); + if (message.equalsIgnoreCase("cancel")) { + sessions.remove(player.getUniqueId()); + J.s(() -> { + Adapt.messagePlayer(player, C.YELLOW + "Config edit cancelled."); + ConfigGui.open(player, pending.returnSectionPath(), pending.returnPage()); + }); + return; } - private void cleanupExpiredSessions() { - long now = System.currentTimeMillis(); - sessions.entrySet().removeIf(e -> e.getValue().expiresAt() <= now); + ConfigGui.ParseResult parsed = ConfigGui.parseInputValue(pending.targetType(), message); + if (!parsed.success()) { + J.s(() -> { + Adapt.messagePlayer(player, C.RED + parsed.error()); + Adapt.messagePlayer(player, C.GRAY + "Try again or type " + C.WHITE + "cancel"); + }); + return; } - private record PendingInput( - UUID playerId, - String valuePath, - String returnSectionPath, - int returnPage, - Class targetType, - String label, - long expiresAt - ) { - private boolean isExpired() { - return System.currentTimeMillis() >= expiresAt; - } + sessions.remove(player.getUniqueId()); + Object value = parsed.value(); + J.s(() -> { + ConfigGui.confirmAndApply(player, pending.returnSectionPath(), pending.returnPage(), pending.valuePath(), value); + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + sessions.remove(event.getPlayer().getUniqueId()); + } + + private void cleanupExpiredSessions() { + long now = System.currentTimeMillis(); + sessions.entrySet().removeIf(e -> e.getValue().expiresAt() <= now); + } + + private record PendingInput( + UUID playerId, + String valuePath, + String returnSectionPath, + int returnPage, + Class targetType, + String label, + long expiresAt + ) { + private boolean isExpired() { + return System.currentTimeMillis() >= expiresAt; } + } } diff --git a/src/main/java/art/arcane/adapt/service/HotloadSVC.java b/src/main/java/art/arcane/adapt/service/HotloadSVC.java index 81252625b..bbf0ec391 100644 --- a/src/main/java/art/arcane/adapt/service/HotloadSVC.java +++ b/src/main/java/art/arcane/adapt/service/HotloadSVC.java @@ -11,7 +11,6 @@ import art.arcane.adapt.content.gui.SkillsGui; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.adapt.util.common.io.Json; import art.arcane.adapt.util.common.misc.CustomModel; import art.arcane.adapt.util.common.plugin.AdaptService; @@ -19,6 +18,7 @@ import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.adapt.util.project.config.ConfigRewriteReporter; import art.arcane.volmlib.util.hotload.ConfigHotloadEngine; +import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.volmlib.util.io.IO; import com.google.gson.JsonElement; import org.bukkit.Bukkit; @@ -32,471 +32,471 @@ import static art.arcane.adapt.util.director.context.AdaptationListingHandler.initializeAdaptationListings; public class HotloadSVC implements AdaptService { - private static final long WATCHER_POLL_MS = 500; - private static final int MAX_DIFF_MESSAGES_PER_FILE = 12; - - private TickedObject configTicker; - private File adaptConfigFile; - private File adaptConfigLegacyFile; - private File modelsFile; - private File modelsLegacyFile; - private File skillsFolder; - private File adaptationsFolder; - private final ConfigHotloadEngine hotloadEngine = new ConfigHotloadEngine( - this::isManagedConfigFile, - this::listKnownConfigFiles, - this::readFileContent, - this::normalizeContent + private static final long WATCHER_POLL_MS = 500; + private static final int MAX_DIFF_MESSAGES_PER_FILE = 12; + + private TickedObject configTicker; + private File adaptConfigFile; + private File adaptConfigLegacyFile; + private File modelsFile; + private File modelsLegacyFile; + private File skillsFolder; + private File adaptationsFolder; + private final ConfigHotloadEngine hotloadEngine = new ConfigHotloadEngine( + this::isManagedConfigFile, + this::listKnownConfigFiles, + this::readFileContent, + this::normalizeContent + ); + + @Override + public void onEnable() { + adaptConfigFile = Adapt.instance.getDataFile("adapt", "adapt.toml"); + adaptConfigLegacyFile = Adapt.instance.getDataFile("adapt", "adapt.json"); + modelsFile = Adapt.instance.getDataFile("adapt", "models.toml"); + modelsLegacyFile = Adapt.instance.getDataFile("adapt", "models.json"); + skillsFolder = Adapt.instance.getDataFolder("adapt", "skills"); + adaptationsFolder = Adapt.instance.getDataFolder("adapt", "adaptations"); + hotloadEngine.configure( + WATCHER_POLL_MS, + List.of(adaptConfigFile, adaptConfigLegacyFile, modelsFile, modelsLegacyFile), + List.of(skillsFolder, adaptationsFolder) ); - - @Override - public void onEnable() { - adaptConfigFile = Adapt.instance.getDataFile("adapt", "adapt.toml"); - adaptConfigLegacyFile = Adapt.instance.getDataFile("adapt", "adapt.json"); - modelsFile = Adapt.instance.getDataFile("adapt", "models.toml"); - modelsLegacyFile = Adapt.instance.getDataFile("adapt", "models.json"); - skillsFolder = Adapt.instance.getDataFolder("adapt", "skills"); - adaptationsFolder = Adapt.instance.getDataFolder("adapt", "adaptations"); - hotloadEngine.configure( - WATCHER_POLL_MS, - List.of(adaptConfigFile, adaptConfigLegacyFile, modelsFile, modelsLegacyFile), - List.of(skillsFolder, adaptationsFolder) - ); - Adapt.info("Config hotload watcher enabled for all /adapt/*.json and /adapt/*.toml files."); - - configTicker = new TickedObject("config", "config-hotload-service", WATCHER_POLL_MS) { - @Override - public void onTick() { - pollConfigChanges(); - } - }; - } - - @Override - public void onDisable() { - if (configTicker != null) { - configTicker.unregister(); - configTicker = null; - } - hotloadEngine.clear(); + Adapt.info("Config hotload watcher enabled for all /adapt/*.json and /adapt/*.toml files."); + + configTicker = new TickedObject("config", "config-hotload-service", WATCHER_POLL_MS) { + @Override + public void onTick() { + pollConfigChanges(); + } + }; + } + + @Override + public void onDisable() { + if (configTicker != null) { + configTicker.unregister(); + configTicker = null; } + hotloadEngine.clear(); + } - private void pollConfigChanges() { - var touched = hotloadEngine.pollTouchedFiles(); - if (touched.isEmpty()) { - return; - } - - boolean refreshedSomething = false; - for (File file : touched) { - refreshedSomething = processConfigChange(file) || refreshedSomething; - } - - if (refreshedSomething) { - refreshOpenAdaptGuis(); - } + private void pollConfigChanges() { + java.util.Set touched = hotloadEngine.pollTouchedFiles(); + if (touched.isEmpty()) { + return; } - private boolean processConfigChange(File file) { - return hotloadEngine.processFileChange(file, this::applyConfigChange, delta -> { - if (isModelsConfigFile(file)) { - return; - } - - notifyOps(file, delta.before(), delta.after()); - }); + boolean refreshedSomething = false; + for (File file : touched) { + refreshedSomething = processConfigChange(file) || refreshedSomething; } - private boolean applyConfigChange(File file) { - try { - if (isShadowedLegacyJson(file)) { - if (!isModelsConfigFile(file)) { - Adapt.verbose("Ignoring legacy json hotload because canonical toml exists: " + file.getPath()); - } - return false; - } - - if (isAdaptConfigFile(file)) { - boolean ok = AdaptConfig.reload(); - if (ok) { - refreshGlobalRuntimeSettings(); - } else { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config."); - } - return ok; - } - - if (isSkillConfigFile(file)) { - return reloadSkillConfig(file); - } - - if (isAdaptationConfigFile(file)) { - return reloadAdaptationConfig(file); - } - - if (isModelsConfigFile(file)) { - return reloadModelsConfig(file); - } - - return validateAndCanonicalizeConfig(file); - } catch (Throwable e) { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); - return false; - } + if (refreshedSomething) { + refreshOpenAdaptGuis(); } + } + + private boolean processConfigChange(File file) { + return hotloadEngine.processFileChange(file, this::applyConfigChange, delta -> { + if (isModelsConfigFile(file)) { + return; + } + + notifyOps(file, delta.before(), delta.after()); + }); + } - private boolean reloadSkillConfig(File file) { - String skillName = toConfigName(file.getName()); - if (skillName == null) { - return false; + private boolean applyConfigChange(File file) { + try { + if (isShadowedLegacyJson(file)) { + if (!isModelsConfigFile(file)) { + Adapt.verbose("Ignoring legacy json hotload because canonical toml exists: " + file.getPath()); } + return false; + } - SkillRegistry registry = Adapt.instance.getAdaptServer().getSkillRegistry(); - boolean ok = registry.hotReloadSkillConfig(skillName); + if (isAdaptConfigFile(file)) { + boolean ok = AdaptConfig.reload(); if (ok) { - initializeAdaptationListings(); + refreshGlobalRuntimeSettings(); } else { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid skill config."); + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config."); } return ok; - } + } - private boolean reloadAdaptationConfig(File file) { - String adaptationName = toConfigName(file.getName()); - if (adaptationName == null) { - return false; - } + if (isSkillConfigFile(file)) { + return reloadSkillConfig(file); + } - for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()) { - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.getName().equalsIgnoreCase(adaptationName)) { - continue; - } - - if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { - return false; - } - - boolean ok = simpleAdaptation.reloadConfigFromDisk(false); - if (ok) { - Adapt.instance.getAdaptServer().getSkillRegistry().refreshRecipes(skill); - initializeAdaptationListings(); - } else { - Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid adaptation config."); - } - return ok; - } - } + if (isAdaptationConfigFile(file)) { + return reloadAdaptationConfig(file); + } + + if (isModelsConfigFile(file)) { + return reloadModelsConfig(file); + } - return validateAndCanonicalizeConfig(file); + return validateAndCanonicalizeConfig(file); + } catch (Throwable e) { + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid config: " + e.getMessage()); + return false; } + } - private boolean reloadModelsConfig(File file) { - return CustomModel.reloadFromDisk(true); + private boolean reloadSkillConfig(File file) { + String skillName = toConfigName(file.getName()); + if (skillName == null) { + return false; } - private void refreshGlobalRuntimeSettings() { - Adapt.wordKey.clear(); - if (AdaptConfig.get().isAutoUpdateLanguage()) { - Localizer.updateLanguageFile(); - } + SkillRegistry registry = Adapt.instance.getAdaptServer().getSkillRegistry(); + boolean ok = registry.hotReloadSkillConfig(skillName); + if (ok) { + initializeAdaptationListings(); + } else { + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid skill config."); + } + return ok; + } - if (AdaptConfig.get().isCustomModels()) { - CustomModel.reloadFromDisk(true); - } else { - CustomModel.clear(); - } + private boolean reloadAdaptationConfig(File file) { + String adaptationName = toConfigName(file.getName()); + if (adaptationName == null) { + return false; } - private boolean validateAndCanonicalizeConfig(File file) { - if (file == null || !file.exists() || !file.isFile()) { - return true; + for (Skill skill : Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()) { + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.getName().equalsIgnoreCase(adaptationName)) { + continue; } - try { - String raw = readFileContent(file); - JsonElement parsed = parseStructured(raw, file); - if (parsed == null) { - return false; - } - - if (ConfigFileSupport.isTomlFile(file)) { - return true; - } - - String canonical = Json.toJson(parsed, true); - if (!normalizeContent(raw).equals(normalizeContent(canonical))) { - ConfigRewriteReporter.reportRewrite(file, "hotload", raw, canonical); - IO.writeAll(file, canonical); - } - return true; - } catch (Throwable e) { - return false; + if (!(adaptation instanceof SimpleAdaptation simpleAdaptation)) { + return false; } - } - private boolean isAdaptConfigFile(File file) { - return sameFile(file, adaptConfigFile) || sameFile(file, adaptConfigLegacyFile); + boolean ok = simpleAdaptation.reloadConfigFromDisk(false); + if (ok) { + Adapt.instance.getAdaptServer().getSkillRegistry().refreshRecipes(skill); + initializeAdaptationListings(); + } else { + Adapt.warn("Skipped hotload for " + file.getPath() + " due to invalid adaptation config."); + } + return ok; + } } - private boolean isModelsConfigFile(File file) { - return sameFile(file, modelsFile) || sameFile(file, modelsLegacyFile); - } + return validateAndCanonicalizeConfig(file); + } - private boolean isSkillConfigFile(File file) { - return isDirectChild(skillsFolder, file) && ConfigFileSupport.isSupportedConfigFile(file); - } + private boolean reloadModelsConfig(File file) { + return CustomModel.reloadFromDisk(true); + } - private boolean isAdaptationConfigFile(File file) { - return isDirectChild(adaptationsFolder, file) && ConfigFileSupport.isSupportedConfigFile(file); + private void refreshGlobalRuntimeSettings() { + Adapt.wordKey.clear(); + if (AdaptConfig.get().isAutoUpdateLanguage()) { + Localizer.updateLanguageFile(); } - private boolean isManagedConfigFile(File file) { - return isAdaptConfigFile(file) - || isModelsConfigFile(file) - || isSkillConfigFile(file) - || isAdaptationConfigFile(file); + if (AdaptConfig.get().isCustomModels()) { + CustomModel.reloadFromDisk(true); + } else { + CustomModel.clear(); } + } - private boolean isDirectChild(File parent, File child) { - if (parent == null || child == null) { - return false; - } - - File childParent = child.getParentFile(); - return childParent != null && sameFile(parent, childParent); + private boolean validateAndCanonicalizeConfig(File file) { + if (file == null || !file.exists() || !file.isFile()) { + return true; } - private boolean sameFile(File a, File b) { - return a != null && b != null && a.getAbsoluteFile().equals(b.getAbsoluteFile()); + try { + String raw = readFileContent(file); + JsonElement parsed = parseStructured(raw, file); + if (parsed == null) { + return false; + } + + if (ConfigFileSupport.isTomlFile(file)) { + return true; + } + + String canonical = Json.toJson(parsed, true); + if (!normalizeContent(raw).equals(normalizeContent(canonical))) { + ConfigRewriteReporter.reportRewrite(file, "hotload", raw, canonical); + IO.writeAll(file, canonical); + } + return true; + } catch (Throwable e) { + return false; + } + } + + private boolean isAdaptConfigFile(File file) { + return sameFile(file, adaptConfigFile) || sameFile(file, adaptConfigLegacyFile); + } + + private boolean isModelsConfigFile(File file) { + return sameFile(file, modelsFile) || sameFile(file, modelsLegacyFile); + } + + private boolean isSkillConfigFile(File file) { + return isDirectChild(skillsFolder, file) && ConfigFileSupport.isSupportedConfigFile(file); + } + + private boolean isAdaptationConfigFile(File file) { + return isDirectChild(adaptationsFolder, file) && ConfigFileSupport.isSupportedConfigFile(file); + } + + private boolean isManagedConfigFile(File file) { + return isAdaptConfigFile(file) + || isModelsConfigFile(file) + || isSkillConfigFile(file) + || isAdaptationConfigFile(file); + } + + private boolean isDirectChild(File parent, File child) { + if (parent == null || child == null) { + return false; } - private boolean isShadowedLegacyJson(File file) { - if (file == null || !file.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { - return false; - } + File childParent = child.getParentFile(); + return childParent != null && sameFile(parent, childParent); + } - if (sameFile(file, adaptConfigLegacyFile) && adaptConfigFile != null && adaptConfigFile.exists()) { - return true; - } - if (sameFile(file, modelsLegacyFile) && modelsFile != null && modelsFile.exists()) { - return true; - } - if ((isSkillConfigFile(file) || isAdaptationConfigFile(file)) && ConfigFileSupport.toTomlFile(file).exists()) { - return true; - } + private boolean sameFile(File a, File b) { + return a != null && b != null && a.getAbsoluteFile().equals(b.getAbsoluteFile()); + } - return false; + private boolean isShadowedLegacyJson(File file) { + if (file == null || !file.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { + return false; } - private String toConfigName(String fileName) { - return ConfigFileSupport.configNameFromFileName(fileName); + if (sameFile(file, adaptConfigLegacyFile) && adaptConfigFile != null && adaptConfigFile.exists()) { + return true; + } + if (sameFile(file, modelsLegacyFile) && modelsFile != null && modelsFile.exists()) { + return true; + } + if ((isSkillConfigFile(file) || isAdaptationConfigFile(file)) && ConfigFileSupport.toTomlFile(file).exists()) { + return true; } - private List listKnownConfigFiles() { - List files = new ArrayList<>(); - Map added = new HashMap<>(); + return false; + } - addIfManaged(files, added, adaptConfigFile); - addIfManaged(files, added, adaptConfigLegacyFile); - addIfManaged(files, added, modelsFile); - addIfManaged(files, added, modelsLegacyFile); + private String toConfigName(String fileName) { + return ConfigFileSupport.configNameFromFileName(fileName); + } - addDirectChildren(skillsFolder, files, added); - addDirectChildren(adaptationsFolder, files, added); + private List listKnownConfigFiles() { + List files = new ArrayList<>(); + Map added = new HashMap<>(); - return files; - } + addIfManaged(files, added, adaptConfigFile); + addIfManaged(files, added, adaptConfigLegacyFile); + addIfManaged(files, added, modelsFile); + addIfManaged(files, added, modelsLegacyFile); - private void addDirectChildren(File folder, List out, Map added) { - if (folder == null || !folder.exists() || !folder.isDirectory()) { - return; - } + addDirectChildren(skillsFolder, files, added); + addDirectChildren(adaptationsFolder, files, added); - File[] children = folder.listFiles(); - if (children == null || children.length == 0) { - return; - } + return files; + } - for (File child : children) { - if (child == null || !child.isFile()) { - continue; - } - addIfManaged(out, added, child); - } + private void addDirectChildren(File folder, List out, Map added) { + if (folder == null || !folder.exists() || !folder.isDirectory()) { + return; } - private void addIfManaged(List out, Map added, File file) { - if (file == null || !isManagedConfigFile(file)) { - return; - } - - String path = file.getAbsolutePath(); - if (added.putIfAbsent(path, file) != null) { - return; - } - - out.add(file); + File[] children = folder.listFiles(); + if (children == null || children.length == 0) { + return; } - private String readFileContent(File file) { - if (file == null || !file.exists() || !file.isFile()) { - return null; - } + for (File child : children) { + if (child == null || !child.isFile()) { + continue; + } + addIfManaged(out, added, child); + } + } - try { - return Files.readString(file.toPath()); - } catch (Throwable e) { - return null; - } + private void addIfManaged(List out, Map added, File file) { + if (file == null || !isManagedConfigFile(file)) { + return; } - private String normalizeContent(String text) { - if (text == null) { - return null; - } - return ConfigFileSupport.normalize(text); + String path = file.getAbsolutePath(); + if (added.putIfAbsent(path, file) != null) { + return; } - private JsonElement parseStructured(String raw, File file) { - if (raw == null || raw.isBlank()) { - return null; - } + out.add(file); + } - return ConfigFileSupport.parseToJsonElement(raw, file); + private String readFileContent(File file) { + if (file == null || !file.exists() || !file.isFile()) { + return null; } - private void notifyOps(File file, String before, String after) { - List diffs = ConfigHotloadEngine.computeStructuredDiff( - before, - after, - raw -> parseStructured(raw, null) - ); - if (diffs.isEmpty()) { - return; - } + try { + return Files.readString(file.toPath()); + } catch (Throwable e) { + return null; + } + } - String relative = relativizeToDataFolder(file); - List messages = new ArrayList<>(); - int shown = Math.min(MAX_DIFF_MESSAGES_PER_FILE, diffs.size()); - for (int i = 0; i < shown; i++) { - ConfigHotloadEngine.DiffEntry diff = diffs.get(i); - messages.add(formatHotloadMessage(relative, diff.key(), diff.oldValue(), diff.newValue())); - } + private String normalizeContent(String text) { + if (text == null) { + return null; + } + return ConfigFileSupport.normalize(text); + } - if (diffs.size() > shown) { - int remaining = diffs.size() - shown; - messages.add(formatHotloadMessage(relative, "...", "+" + remaining + " more", "truncated")); - } + private JsonElement parseStructured(String raw, File file) { + if (raw == null || raw.isBlank()) { + return null; + } - J.s(() -> { - for (Player player : Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()) { - if (!player.isOp()) { - continue; - } + return ConfigFileSupport.parseToJsonElement(raw, file); + } - player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.8f, 1.6f); - messages.forEach(player::sendMessage); - } - }); + private void notifyOps(File file, String before, String after) { + List diffs = ConfigHotloadEngine.computeStructuredDiff( + before, + after, + raw -> parseStructured(raw, null) + ); + if (diffs.isEmpty()) { + return; } - private String formatHotloadMessage(String file, String key, String oldValue, String newValue) { - return C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " - + C.GREEN + "Adapt Hotloaded: " - + C.WHITE + "[" + file + "] " - + C.AQUA + "[" + key + "] " - + C.GRAY + "[" + formatValue(oldValue) + " -> " + formatValue(newValue) + "]"; + String relative = relativizeToDataFolder(file); + List messages = new ArrayList<>(); + int shown = Math.min(MAX_DIFF_MESSAGES_PER_FILE, diffs.size()); + for (int i = 0; i < shown; i++) { + ConfigHotloadEngine.DiffEntry diff = diffs.get(i); + messages.add(formatHotloadMessage(relative, diff.key(), diff.oldValue(), diff.newValue())); } - private String formatValue(String value) { - return ConfigHotloadEngine.compactValue(value, 120); + if (diffs.size() > shown) { + int remaining = diffs.size() - shown; + messages.add(formatHotloadMessage(relative, "...", "+" + remaining + " more", "truncated")); } - private String relativizeToDataFolder(File file) { - try { - return Adapt.instance.getDataFolder().toPath().relativize(file.toPath()).toString(); - } catch (Throwable e) { - return file.getName(); - } + J.s(() -> { + for (Player player : Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()) { + if (!player.isOp()) { + continue; + } + + player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.8f, 1.6f); + messages.forEach(player::sendMessage); + } + }); + } + + private String formatHotloadMessage(String file, String key, String oldValue, String newValue) { + return C.GRAY + "[" + C.DARK_RED + "Adapt" + C.GRAY + "]: " + + C.GREEN + "Adapt Hotloaded: " + + C.WHITE + "[" + file + "] " + + C.AQUA + "[" + key + "] " + + C.GRAY + "[" + formatValue(oldValue) + " -> " + formatValue(newValue) + "]"; + } + + private String formatValue(String value) { + return ConfigHotloadEngine.compactValue(value, 120); + } + + private String relativizeToDataFolder(File file) { + try { + return Adapt.instance.getDataFolder().toPath().relativize(file.toPath()).toString(); + } catch (Throwable e) { + return file.getName(); } + } - private void refreshOpenAdaptGuis() { - J.s(() -> { - Map open = new HashMap<>(Adapt.instance.getGuiLeftovers()); - for (Map.Entry entry : open.entrySet()) { - String playerKey = entry.getKey(); - UIWindow window = entry.getValue(); - if (window == null) { - continue; - } - - UUID uuid; - try { - uuid = UUID.fromString(playerKey); - } catch (Throwable ignored) { - continue; - } - - Player player = Bukkit.getPlayer(uuid); - if (player == null || !player.isOnline()) { - Adapt.instance.getGuiLeftovers().remove(playerKey); - continue; - } - - reopenFromTag(player, window.getTag()); - } - }); - } - - private void reopenFromTag(Player player, String tag) { - if (tag == null || tag.isBlank() || "/".equals(tag)) { - SkillsGui.open(player); - return; + private void refreshOpenAdaptGuis() { + J.s(() -> { + Map open = new HashMap<>(Adapt.instance.getGuiLeftovers()); + for (Map.Entry entry : open.entrySet()) { + String playerKey = entry.getKey(); + UIWindow window = entry.getValue(); + if (window == null) { + continue; } - if (tag.startsWith("config/")) { - ConfigGui.reopenFromTag(player, tag); - return; + UUID uuid; + try { + uuid = UUID.fromString(playerKey); + } catch (Throwable ignored) { + continue; } - if (!tag.startsWith("skill/")) { - SkillsGui.open(player); - return; + Player player = Bukkit.getPlayer(uuid); + if (player == null || !player.isOnline()) { + Adapt.instance.getGuiLeftovers().remove(playerKey); + continue; } - String[] parts = tag.split("/"); - if (parts.length < 2) { - SkillsGui.open(player); - return; - } + reopenFromTag(player, window.getTag()); + } + }); + } - Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(parts[1]); - if (skill == null || !skill.isEnabled()) { - SkillsGui.open(player); - return; - } + private void reopenFromTag(Player player, String tag) { + if (tag == null || tag.isBlank() || "/".equals(tag)) { + SkillsGui.open(player); + return; + } - if (parts.length == 2) { - skill.openGui(player); - return; - } + if (tag.startsWith("config/")) { + ConfigGui.reopenFromTag(player, tag); + return; + } - String adaptationName = parts[2]; - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.getName().equalsIgnoreCase(adaptationName)) { - continue; - } - - if (adaptation.isEnabled()) { - adaptation.openGui(player); - } else { - skill.openGui(player); - } - return; - } + if (!tag.startsWith("skill/")) { + SkillsGui.open(player); + return; + } + + String[] parts = tag.split("/"); + if (parts.length < 2) { + SkillsGui.open(player); + return; + } + + Skill skill = Adapt.instance.getAdaptServer().getSkillRegistry().getSkill(parts[1]); + if (skill == null || !skill.isEnabled()) { + SkillsGui.open(player); + return; + } + + if (parts.length == 2) { + skill.openGui(player); + return; + } + String adaptationName = parts[2]; + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.getName().equalsIgnoreCase(adaptationName)) { + continue; + } + + if (adaptation.isEnabled()) { + adaptation.openGui(player); + } else { skill.openGui(player); + } + return; } + skill.openGui(player); + } + } diff --git a/src/main/java/art/arcane/adapt/util/cache/AtomicCache.java b/src/main/java/art/arcane/adapt/util/cache/AtomicCache.java index 26f4f802b..9b102052e 100644 --- a/src/main/java/art/arcane/adapt/util/cache/AtomicCache.java +++ b/src/main/java/art/arcane/adapt/util/cache/AtomicCache.java @@ -21,6 +21,7 @@ //import art.arcane.react.React; + import art.arcane.adapt.Adapt; import art.arcane.volmlib.util.function.NastySupplier; @@ -30,81 +31,81 @@ import java.util.function.Supplier; public class AtomicCache { - private transient final AtomicReference t; - private transient final AtomicBoolean set; - private transient final ReentrantLock lock; - private transient final boolean nullSupport; - - public AtomicCache() { - this(false); + private transient final AtomicReference t; + private transient final AtomicBoolean set; + private transient final ReentrantLock lock; + private transient final boolean nullSupport; + + public AtomicCache() { + this(false); + } + + public AtomicCache(boolean nullSupport) { + set = nullSupport ? new AtomicBoolean() : null; + t = new AtomicReference<>(); + lock = new ReentrantLock(); + this.nullSupport = nullSupport; + } + + public void reset() { + t.set(null); + + if (nullSupport) { + set.set(false); } - - public AtomicCache(boolean nullSupport) { - set = nullSupport ? new AtomicBoolean() : null; - t = new AtomicReference<>(); - lock = new ReentrantLock(); - this.nullSupport = nullSupport; + } + + public T aquireNasty(NastySupplier t) { + return aquire(() -> { + try { + return t.get(); + } catch (Throwable e) { + return null; + } + }); + } + + public T aquireNastyPrint(NastySupplier t) { + return aquire(() -> { + try { + return t.get(); + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + }); + } + + public T aquire(Supplier t) { + if (this.t.get() != null) { + return this.t.get(); + } else if (nullSupport && set.get()) { + return null; } - public void reset() { - t.set(null); + lock.lock(); - if (nullSupport) { - set.set(false); - } + if (this.t.get() != null) { + lock.unlock(); + return this.t.get(); + } else if (nullSupport && set.get()) { + lock.unlock(); + return null; } - public T aquireNasty(NastySupplier t) { - return aquire(() -> { - try { - return t.get(); - } catch (Throwable e) { - return null; - } - }); - } + try { + this.t.set(t.get()); - public T aquireNastyPrint(NastySupplier t) { - return aquire(() -> { - try { - return t.get(); - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - }); + if (nullSupport) { + set.set(true); + } + } catch (Throwable e) { + Adapt.error("Atomic cache failure!"); + e.printStackTrace(); } - public T aquire(Supplier t) { - if (this.t.get() != null) { - return this.t.get(); - } else if (nullSupport && set.get()) { - return null; - } - - lock.lock(); - - if (this.t.get() != null) { - lock.unlock(); - return this.t.get(); - } else if (nullSupport && set.get()) { - lock.unlock(); - return null; - } - - try { - this.t.set(t.get()); - - if (nullSupport) { - set.set(true); - } - } catch (Throwable e) { - Adapt.error("Atomic cache failure!"); - e.printStackTrace(); - } - - lock.unlock(); - - return this.t.get(); - } + lock.unlock(); + + return this.t.get(); + } } diff --git a/src/main/java/art/arcane/adapt/util/cache/Cache.java b/src/main/java/art/arcane/adapt/util/cache/Cache.java index 36a9f26e7..4b1e193a3 100644 --- a/src/main/java/art/arcane/adapt/util/cache/Cache.java +++ b/src/main/java/art/arcane/adapt/util/cache/Cache.java @@ -23,31 +23,31 @@ import org.bukkit.Chunk; public interface Cache { - static long key(Chunk chunk) { - return CacheKey.key(chunk); - } + static long key(Chunk chunk) { + return CacheKey.key(chunk); + } - static long key(int x, int z) { - return CacheKey.key(x, z); - } + static long key(int x, int z) { + return CacheKey.key(x, z); + } - static int keyX(long key) { - return CacheKey.keyX(key); - } + static int keyX(long key) { + return CacheKey.keyX(key); + } - static int keyZ(long key) { - return CacheKey.keyZ(key); - } + static int keyZ(long key) { + return CacheKey.keyZ(key); + } - static int to1D(int x, int y, int z, int w, int h) { - return CacheKey.to1D(x, y, z, w, h); - } + static int to1D(int x, int y, int z, int w, int h) { + return CacheKey.to1D(x, y, z, w, h); + } - static int[] to3D(int idx, int w, int h) { - return CacheKey.to3D(idx, w, h); - } + static int[] to3D(int idx, int w, int h) { + return CacheKey.to3D(idx, w, h); + } - int getId(); + int getId(); - V get(int x, int z); + V get(int x, int z); } diff --git a/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java index 6cd1d832a..d252242e1 100644 --- a/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/Dictionary.java @@ -23,390 +23,390 @@ import java.util.regex.Pattern; class Dictionary { - //!\"#$%&[] - private static final Pattern wordsPattern = Pattern.compile("[A-Z][A-Z][A-Z][A-Z]*"); - private static final Pattern longCodesPattern = Pattern.compile("<[N-Z0-9!\\\\\"#$%\\[\\]& _][A-Z0-9!\\\\\"#$%\\[\\]& _]"); - private static final Pattern shortCodesPattern = Pattern.compile("<[A-M]"); + //!\"#$%&[] + private static final Pattern wordsPattern = Pattern.compile("[A-Z][A-Z][A-Z][A-Z]*"); + private static final Pattern longCodesPattern = Pattern.compile("<[N-Z0-9!\\\\\"#$%\\[\\]& _][A-Z0-9!\\\\\"#$%\\[\\]& _]"); + private static final Pattern shortCodesPattern = Pattern.compile("<[A-M]"); - private static final Comparator byLength = new Comparator() { - @Override - public int compare(String a, String b) { - return b.length() - a.length(); - } - }; + private static final Comparator byLength = new Comparator() { + @Override + public int compare(String a, String b) { + return b.length() - a.length(); + } + }; - private static final String[] wordsAndCodes = { - "A", "THE", "B", "AND", "C", "FOR", "D", "YOU", "E", "NOT", "F", "ARE", - "G", "ALL", "H", "NEW", "I", "WAS", "J", "CAN", "K", "HAS", "L", "BUT", - "M", "OUR", - "NA", "THAT", "NB", "THIS", "NC", "WITH", "ND", "FROM", "NE", "YOUR", - "NF", "HAVE", "NG", "MORE", "NH", "WILL", "NI", "HOME", "NJ", "ABOUT", - "NK", "PAGE", "NL", "SEARCH", "NM", "FREE", "NN", "OTHER", "NO", "INFORMATION", - "NP", "TIME", "NQ", "THEY", "NR", "SITE", "NS", "WHAT", "NT", "WHICH", - "NU", "THEIR", "NV", "NEWS", "NW", "THERE", "NX", "ONLY", "NY", "WHEN", - "NZ", "CONTACT", "N0", "HERE", "N1", "BUSINESS", "N2", "ALSO", "N3", "HELP", - "N4", "VIEW", "N5", "ONLINE", "N6", "FIRST", "N7", "BEEN", "N8", "WOULD", - "N9", "WERE", "N!", "SERVICES", "N\"", "SOME", "N#", "THESE", "N$", "CLICK", - "N%", "LIKE", "N&", "SERVICE", "N[", "THAN", "N]", "FIND", "N ", "PRICE", - "N_", "DATE", "OA", "BACK", "OB", "PEOPLE", "OC", "LIST", "OD", "NAME", - "OE", "JUST", "OF", "OVER", "OG", "STATE", "OH", "YEAR", "OI", "INTO", - "OJ", "EMAIL", "OK", "HEALTH", "OL", "WORLD", "OM", "NEXT", "ON", "USED", - "OO", "WORK", "OP", "LAST", "OQ", "MOST", "OR", "PRODUCTS", "OS", "MUSIC", - "OT", "DATA", "OU", "MAKE", "OV", "THEM", "OW", "SHOULD", "OX", "PRODUCT", - "OY", "SYSTEM", "OZ", "POST", "O0", "CITY", "O1", "POLICY", "O2", "NUMBER", - "O3", "SUCH", "O4", "PLEASE", "O5", "AVAILABLE", "O6", "COPYRIGHT", "O7", "SUPPORT", - "O8", "MESSAGE", "O9", "AFTER", "O!", "BEST", "O\"", "SOFTWARE", "O#", "THEN", - "O$", "GOOD", "O%", "VIDEO", "O&", "WELL", "O[", "WHERE", "O]", "INFO", - "O ", "RIGHTS", "O_", "PUBLIC", "PA", "BOOKS", "PB", "HIGH", "PC", "SCHOOL", - "PD", "THROUGH", "PE", "EACH", "PF", "LINKS", "PG", "REVIEW", "PH", "YEARS", - "PI", "ORDER", "PJ", "VERY", "PK", "PRIVACY", "PL", "BOOK", "PM", "ITEMS", - "PN", "COMPANY", "PO", "READ", "PP", "GROUP", "PQ", "NEED", "PR", "MANY", - "PS", "USER", "PT", "SAID", "PU", "DOES", "PV", "UNDER", "PW", "GENERAL", - "PX", "RESEARCH", "PY", "UNIVERSITY", "PZ", "JANUARY", "P0", "MAIL", "P1", "FULL", - "P2", "REVIEWS", "P3", "PROGRAM", "P4", "LIFE", "P5", "KNOW", "P6", "GAMES", - "P7", "DAYS", "P8", "MANAGEMENT", "P9", "PART", "P!", "COULD", "P\"", "GREAT", - "P#", "UNITED", "P$", "HOTEL", "P%", "REAL", "P&", "ITEM", "P[", "INTERNATIONAL", - "P]", "CENTER", "P ", "EBAY", "P_", "MUST", "QA", "STORE", "QB", "TRAVEL", - "QC", "COMMENTS", "QD", "MADE", "QE", "DEVELOPMENT", "QF", "REPORT", "QG", "MEMBER", - "QH", "DETAILS", "QI", "LINE", "QJ", "TERMS", "QK", "BEFORE", "QL", "HOTELS", - "QM", "SEND", "QN", "RIGHT", "QO", "TYPE", "QP", "BECAUSE", "QQ", "LOCAL", - "QR", "THOSE", "QS", "USING", "QT", "RESULTS", "QU", "OFFICE", "QV", "EDUCATION", - "QW", "NATIONAL", "QX", "DESIGN", "QY", "TAKE", "QZ", "POSTED", "Q0", "INTERNET", - "Q1", "ADDRESS", "Q2", "COMMUNITY", "Q3", "WITHIN", "Q4", "STATES", "Q5", "AREA", - "Q6", "WANT", "Q7", "PHONE", "Q8", "SHIPPING", "Q9", "RESERVED", "Q!", "SUBJECT", - "Q\"", "BETWEEN", "Q#", "FORUM", "Q$", "FAMILY", "Q%", "LONG", "Q&", "BASED", - "Q[", "CODE", "Q]", "SHOW", "Q ", "EVEN", "Q_", "BLACK", "RA", "CHECK", - "RB", "SPECIAL", "RC", "PRICES", "RD", "WEBSITE", "RE", "INDEX", "RF", "BEING", - "RG", "WOMEN", "RH", "MUCH", "RI", "SIGN", "RJ", "FILE", "RK", "LINK", - "RL", "OPEN", "RM", "TODAY", "RN", "TECHNOLOGY", "RO", "SOUTH", "RP", "CASE", - "RQ", "PROJECT", "RR", "SAME", "RS", "PAGES", "RT", "VERSION", "RU", "SECTION", - "RV", "FOUND", "RW", "SPORTS", "RX", "HOUSE", "RY", "RELATED", "RZ", "SECURITY", - "R0", "BOTH", "R1", "COUNTY", "R2", "AMERICAN", "R3", "PHOTO", "R4", "GAME", - "R5", "MEMBERS", "R6", "POWER", "R7", "WHILE", "R8", "CARE", "R9", "NETWORK", - "R!", "DOWN", "R\"", "COMPUTER", "R#", "SYSTEMS", "R$", "THREE", "R%", "TOTAL", - "R&", "PLACE", "R[", "FOLLOWING", "R]", "DOWNLOAD", "R ", "WITHOUT", "R_", "ACCESS", - "SA", "THINK", "SB", "NORTH", "SC", "RESOURCES", "SD", "CURRENT", "SE", "POSTS", - "SF", "MEDIA", "SG", "CONTROL", "SH", "WATER", "SI", "HISTORY", "SJ", "PICTURES", - "SK", "SIZE", "SL", "PERSONAL", "SM", "SINCE", "SN", "INCLUDING", "SO", "GUIDE", - "SP", "SHOP", "SQ", "DIRECTORY", "SR", "BOARD", "SS", "LOCATION", "ST", "CHANGE", - "SU", "WHITE", "SV", "TEXT", "SW", "SMALL", "SX", "RATING", "SY", "RATE", - "SZ", "GOVERNMENT", "S0", "CHILDREN", "S1", "DURING", "S2", "RETURN", "S3", "STUDENTS", - "S4", "SHOPPING", "S5", "ACCOUNT", "S6", "TIMES", "S7", "SITES", "S8", "LEVEL", - "S9", "DIGITAL", "S!", "PROFILE", "S\"", "PREVIOUS", "S#", "FORM", "S$", "EVENTS", - "S%", "LOVE", "S&", "JOHN", "S[", "MAIN", "S]", "CALL", "S ", "HOURS", - "S_", "IMAGE", "TA", "DEPARTMENT", "TB", "TITLE", "TC", "DESCRIPTION", "TD", "INSURANCE", - "TE", "ANOTHER", "TF", "SHALL", "TG", "PROPERTY", "TH", "CLASS", "TI", "STILL", - "TJ", "MONEY", "TK", "QUALITY", "TL", "EVERY", "TM", "LISTING", "TN", "CONTENT", - "TO", "COUNTRY", "TP", "PRIVATE", "TQ", "LITTLE", "TR", "VISIT", "TS", "SAVE", - "TT", "TOOLS", "TU", "REPLY", "TV", "CUSTOMER", "TW", "DECEMBER", "TX", "COMPARE", - "TY", "MOVIES", "TZ", "INCLUDE", "T0", "COLLEGE", "T1", "VALUE", "T2", "ARTICLE", - "T3", "YORK", "T4", "CARD", "T5", "JOBS", "T6", "PROVIDE", "T7", "FOOD", - "T8", "SOURCE", "T9", "AUTHOR", "T!", "DIFFERENT", "T\"", "PRESS", "T#", "LEARN", - "T$", "SALE", "T%", "AROUND", "T&", "PRINT", "T[", "COURSE", "T]", "CANADA", - "T ", "PROCESS", "T_", "TEEN", "UA", "ROOM", "UB", "STOCK", "UC", "TRAINING", - "UD", "CREDIT", "UE", "POINT", "UF", "JOIN", "UG", "SCIENCE", "UH", "CATEGORIES", - "UI", "ADVANCED", "UJ", "WEST", "UK", "SALES", "UL", "LOOK", "UM", "ENGLISH", - "UN", "LEFT", "UO", "TEAM", "UP", "ESTATE", "UQ", "CONDITIONS", "UR", "SELECT", - "US", "WINDOWS", "UT", "PHOTOS", "UU", "THREAD", "UV", "WEEK", "UW", "CATEGORY", - "UX", "NOTE", "UY", "LIVE", "UZ", "LARGE", "U0", "GALLERY", "U1", "TABLE", - "U2", "REGISTER", "U3", "HOWEVER", "U4", "JUNE", "U5", "OCTOBER", "U6", "NOVEMBER", - "U7", "MARKET", "U8", "LIBRARY", "U9", "REALLY", "U!", "ACTION", "U\"", "START", - "U#", "SERIES", "U$", "MODEL", "U%", "FEATURES", "U&", "INDUSTRY", "U[", "PLAN", - "U]", "HUMAN", "U ", "PROVIDED", "U_", "REQUIRED", "VA", "SECOND", "VB", "ACCESSORIES", - "VC", "COST", "VD", "MOVIE", "VE", "FORUMS", "VF", "MARCH", "VG", "SEPTEMBER", - "VH", "BETTER", "VI", "QUESTIONS", "VJ", "JULY", "VK", "YAHOO", "VL", "GOING", - "VM", "MEDICAL", "VN", "TEST", "VO", "FRIEND", "VP", "COME", "VQ", "SERVER", - "VR", "STUDY", "VS", "APPLICATION", "VT", "CART", "VU", "STAFF", "VV", "ARTICLES", - "VW", "FEEDBACK", "VX", "AGAIN", "VY", "PLAY", "VZ", "LOOKING", "V0", "ISSUES", - "V1", "APRIL", "V2", "NEVER", "V3", "USERS", "V4", "COMPLETE", "V5", "STREET", - "V6", "TOPIC", "V7", "COMMENT", "V8", "FINANCIAL", "V9", "THINGS", "V!", "WORKING", - "V\"", "AGAINST", "V#", "STANDARD", "V$", "PERSON", "V%", "BELOW", "V&", "MOBILE", - "V[", "LESS", "V]", "BLOG", "V ", "PARTY", "V_", "PAYMENT", "WA", "EQUIPMENT", - "WB", "LOGIN", "WC", "STUDENT", "WD", "PROGRAMS", "WE", "OFFERS", "WF", "LEGAL", - "WG", "ABOVE", "WH", "RECENT", "WI", "PARK", "WJ", "STORES", "WK", "SIDE", - "WL", "PROBLEM", "WM", "GIVE", "WN", "MEMORY", "WO", "PERFORMANCE", "WP", "SOCIAL", - "WQ", "AUGUST", "WR", "QUOTE", "WS", "LANGUAGE", "WT", "STORY", "WU", "SELL", - "WV", "OPTIONS", "WW", "EXPERIENCE", "WX", "RATES", "WY", "CREATE", "WZ", "BODY", - "W0", "YOUNG", "W1", "AMERICA", "W2", "IMPORTANT", "W3", "FIELD", "W4", "EAST", - "W5", "PAPER", "W6", "SINGLE", "W7", "ACTIVITIES", "W8", "CLUB", "W9", "EXAMPLE", - "W!", "GIRLS", "W\"", "ADDITIONAL", "W#", "PASSWORD", "W$", "LATEST", "W%", "SOMETHING", - "W&", "ROAD", "W[", "GIFT", "W]", "QUESTION", "W ", "CHANGES", "W_", "NIGHT", - "XA", "HARD", "XB", "TEXAS", "XC", "FOUR", "XD", "POKER", "XE", "STATUS", - "XF", "BROWSE", "XG", "ISSUE", "XH", "RANGE", "XI", "BUILDING", "XJ", "SELLER", - "XK", "COURT", "XL", "FEBRUARY", "XM", "ALWAYS", "XN", "RESULT", "XO", "AUDIO", - "XP", "LIGHT", "XQ", "WRITE", "XR", "OFFER", "XS", "BLUE", "XT", "GROUPS", - "XU", "EASY", "XV", "GIVEN", "XW", "FILES", "XX", "EVENT", "XY", "RELEASE", - "XZ", "ANALYSIS", "X0", "REQUEST", "X1", "CHINA", "X2", "MAKING", "X3", "PICTURE", - "X4", "NEEDS", "X5", "POSSIBLE", "X6", "MIGHT", "X7", "PROFESSIONAL", "X8", "MONTH", - "X9", "MAJOR", "X!", "STAR", "X\"", "AREAS", "X#", "FUTURE", "X$", "SPACE", - "X%", "COMMITTEE", "X&", "HAND", "X[", "CARDS", "X]", "PROBLEMS", "X ", "LONDON", - "X_", "WASHINGTON", "YA", "MEETING", "YB", "BECOME", "YC", "INTEREST", "YD", "CHILD", - "YE", "KEEP", "YF", "ENTER", "YG", "CALIFORNIA", "YH", "PORN", "YI", "SHARE", - "YJ", "SIMILAR", "YK", "GARDEN", "YL", "SCHOOLS", "YM", "MILLION", "YN", "ADDED", - "YO", "REFERENCE", "YP", "COMPANIES", "YQ", "LISTED", "YR", "BABY", "YS", "LEARNING", - "YT", "ENERGY", "YU", "DELIVERY", "YV", "POPULAR", "YW", "TERM", "YX", "FILM", - "YY", "STORIES", "YZ", "COMPUTERS", "Y0", "JOURNAL", "Y1", "REPORTS", "Y2", "WELCOME", - "Y3", "CENTRAL", "Y4", "IMAGES", "Y5", "PRESIDENT", "Y6", "NOTICE", "Y7", "ORIGINAL", - "Y8", "HEAD", "Y9", "RADIO", "Y!", "UNTIL", "Y\"", "CELL", "Y#", "COLOR", - "Y$", "SELF", "Y%", "COUNCIL", "Y&", "AWAY", "Y[", "INCLUDES", "Y]", "TRACK", - "Y ", "AUSTRALIA", "Y_", "DISCUSSION", "ZA", "ARCHIVE", "ZB", "ONCE", "ZC", "OTHERS", - "ZD", "ENTERTAINMENT", "ZE", "AGREEMENT", "ZF", "FORMAT", "ZG", "LEAST", "ZH", "SOCIETY", - "ZI", "MONTHS", "ZJ", "SAFETY", "ZK", "FRIENDS", "ZL", "SURE", "ZM", "TRADE", - "ZN", "EDITION", "ZO", "CARS", "ZP", "MESSAGES", "ZQ", "MARKETING", "ZR", "TELL", - "ZS", "FURTHER", "ZT", "UPDATED", "ZU", "ASSOCIATION", "ZV", "ABLE", "ZW", "HAVING", - "ZX", "PROVIDES", "ZY", "DAVID", "ZZ", "ALREADY", "Z0", "GREEN", "Z1", "STUDIES", - "Z2", "CLOSE", "Z3", "COMMON", "Z4", "DRIVE", "Z5", "SPECIFIC", "Z6", "SEVERAL", - "Z7", "GOLD", "Z8", "LIVING", "Z9", "COLLECTION", "Z!", "CALLED", "Z\"", "SHORT", - "Z#", "ARTS", "Z$", "DISPLAY", "Z%", "LIMITED", "Z&", "POWERED", "Z[", "SOLUTIONS", - "Z]", "MEANS", "Z ", "DIRECTOR", "Z_", "DAILY", "0A", "BEACH", "0B", "PAST", - "0C", "NATURAL", "0D", "WHETHER", "0E", "ELECTRONICS", "0F", "FIVE", "0G", "UPON", - "0H", "PERIOD", "0I", "PLANNING", "0J", "DATABASE", "0K", "SAYS", "0L", "OFFICIAL", - "0M", "WEATHER", "0N", "LAND", "0O", "AVERAGE", "0P", "DONE", "0Q", "TECHNICAL", - "0R", "WINDOW", "0S", "FRANCE", "0T", "REGION", "0U", "ISLAND", "0V", "RECORD", - "0W", "DIRECT", "0X", "MICROSOFT", "0Y", "CONFERENCE", "0Z", "ENVIRONMENT", "00", "RECORDS", - "01", "DISTRICT", "02", "CALENDAR", "03", "COSTS", "04", "STYLE", "05", "FRONT", - "06", "STATEMENT", "07", "UPDATE", "08", "PARTS", "09", "EVER", "0!", "DOWNLOADS", - "0\"", "EARLY", "0#", "MILES", "0$", "SOUND", "0%", "RESOURCE", "0&", "PRESENT", - "0[", "APPLICATIONS", "0]", "EITHER", "0 ", "DOCUMENT", "0_", "WORD", "1A", "WORKS", - "1B", "MATERIAL", "1C", "BILL", "1D", "WRITTEN", "1E", "TALK", "1F", "FEDERAL", - "1G", "HOSTING", "1H", "RULES", "1I", "FINAL", "1J", "ADULT", "1K", "TICKETS", - "1L", "THING", "1M", "CENTRE", "1N", "REQUIREMENTS", "1O", "CHEAP", "1P", "NUDE", - "1Q", "KIDS", "1R", "FINANCE", "1S", "TRUE", "1T", "MINUTES", "1U", "ELSE", - "1V", "MARK", "1W", "THIRD", "1X", "ROCK", "1Y", "GIFTS", "1Z", "EUROPE", - "10", "READING", "11", "TOPICS", "12", "INDIVIDUAL", "13", "TIPS", "14", "PLUS", - "15", "AUTO", "16", "COVER", "17", "USUALLY", "18", "EDIT", "19", "TOGETHER", - "1!", "VIDEOS", "1\"", "PERCENT", "1#", "FAST", "1$", "FUNCTION", "1%", "FACT", - "1&", "UNIT", "1[", "GETTING", "1]", "GLOBAL", "1 ", "TECH", "1_", "MEET", - "2A", "ECONOMIC", "2B", "PLAYER", "2C", "PROJECTS", "2D", "LYRICS", "2E", "OFTEN", - "2F", "SUBSCRIBE", "2G", "SUBMIT", "2H", "GERMANY", "2I", "AMOUNT", "2J", "WATCH", - "2K", "INCLUDED", "2L", "FEEL", "2M", "THOUGH", "2N", "BANK", "2O", "RISK", - "2P", "THANKS", "2Q", "EVERYTHING", "2R", "DEALS", "2S", "VARIOUS", "2T", "WORDS", - "2U", "LINUX", "2V", "PRODUCTION", "2W", "COMMERCIAL", "2X", "JAMES", "2Y", "WEIGHT", - "2Z", "TOWN", "20", "HEART", "21", "ADVERTISING", "22", "RECEIVED", "23", "CHOOSE", - "24", "TREATMENT", "25", "NEWSLETTER", "26", "ARCHIVES", "27", "POINTS", "28", "KNOWLEDGE", - "29", "MAGAZINE", "2!", "ERROR", "2\"", "CAMERA", "2#", "GIRL", "2$", "CURRENTLY", - "2%", "CONSTRUCTION", "2&", "TOYS", "2[", "REGISTERED", "2]", "CLEAR", "2 ", "GOLF", - "2_", "RECEIVE", "3A", "DOMAIN", "3B", "METHODS", "3C", "CHAPTER", "3D", "MAKES", - "3E", "PROTECTION", "3F", "POLICIES", "3G", "LOAN", "3H", "WIDE", "3I", "BEAUTY", - "3J", "MANAGER", "3K", "INDIA", "3L", "POSITION", "3M", "TAKEN", "3N", "SORT", - "3O", "LISTINGS", "3P", "MODELS", "3Q", "MICHAEL", "3R", "KNOWN", "3S", "HALF", - "3T", "CASES", "3U", "STEP", "3V", "ENGINEERING", "3W", "FLORIDA", "3X", "SIMPLE", - "3Y", "QUICK", "3Z", "NONE", "30", "WIRELESS", "31", "LICENSE", "32", "PAUL", - "33", "FRIDAY", "34", "LAKE", "35", "WHOLE", "36", "ANNUAL", "37", "PUBLISHED", - "38", "LATER", "39", "BASIC", "3!", "SONY", "3\"", "SHOWS", "3#", "CORPORATE", - "3$", "GOOGLE", "3%", "CHURCH", "3&", "METHOD", "3[", "PURCHASE", "3]", "CUSTOMERS", - "3 ", "ACTIVE", "3_", "RESPONSE", "4A", "PRACTICE", "4B", "HARDWARE", "4C", "FIGURE", - "4D", "MATERIALS", "4E", "FIRE", "4F", "HOLIDAY", "4G", "CHAT", "4H", "ENOUGH", - "4I", "DESIGNED", "4J", "ALONG", "4K", "AMONG", "4L", "DEATH", "4M", "WRITING", - "4N", "SPEED", "4O", "HTML", "4P", "COUNTRIES", "4Q", "LOSS", "4R", "FACE", - "4S", "BRAND", "4T", "DISCOUNT", "4U", "HIGHER", "4V", "EFFECTS", "4W", "CREATED", - "4X", "REMEMBER", "4Y", "STANDARDS", "4Z", "YELLOW", "40", "POLITICAL", "41", "INCREASE", - "42", "ADVERTISE", "43", "KINGDOM", "44", "BASE", "45", "NEAR", "46", "ENVIRONMENTAL", - "47", "THOUGHT", "48", "STUFF", "49", "FRENCH", "4!", "STORAGE", "4\"", "JAPAN", - "4#", "DOING", "4$", "LOANS", "4%", "SHOES", "4&", "ENTRY", "4[", "STAY", - "4]", "NATURE", "4 ", "ORDERS", "4_", "AVAILABILITY", "5A", "AFRICA", "5B", "SUMMARY", - "5C", "TURN", "5D", "MEAN", "5E", "GROWTH", "5F", "NOTES", "5G", "AGENCY", - "5H", "KING", "5I", "MONDAY", "5J", "EUROPEAN", "5K", "ACTIVITY", "5L", "COPY", - "5M", "ALTHOUGH", "5N", "DRUG", "5O", "PICS", "5P", "WESTERN", "5Q", "INCOME", - "5R", "FORCE", "5S", "CASH", "5T", "EMPLOYMENT", "5U", "OVERALL", "5V", "RIVER", - "5W", "COMMISSION", "5X", "PACKAGE", "5Y", "CONTENTS", "5Z", "SEEN", "50", "PLAYERS", - "51", "ENGINE", "52", "PORT", "53", "ALBUM", "54", "REGIONAL", "55", "STOP", - "56", "SUPPLIES", "57", "STARTED", "58", "ADMINISTRATION", "59", "INSTITUTE", "5!", "VIEWS", - "5\"", "PLANS", "5#", "DOUBLE", "5$", "BUILD", "5%", "SCREEN", "5&", "EXCHANGE", - "5[", "TYPES", "5]", "SOON", "5 ", "SPONSORED", "5_", "LINES", "6A", "ELECTRONIC", - "6B", "CONTINUE", "6C", "ACROSS", "6D", "BENEFITS", "6E", "NEEDED", "6F", "SEASON", - "6G", "APPLY", "6H", "SOMEONE", "6I", "HELD", "6J", "ANYTHING", "6K", "PRINTER", - "6L", "CONDITION", "6M", "EFFECTIVE", "6N", "BELIEVE", "6O", "ORGANIZATION", "6P", "EFFECT", - "6Q", "ASKED", "6R", "MIND", "6S", "SUNDAY", "6T", "SELECTION", "6U", "CASINO", - "6V", "LOST", "6W", "TOUR", "6X", "MENU", "6Y", "VOLUME", "6Z", "CROSS", - "60", "ANYONE", "61", "MORTGAGE", "62", "HOPE", "63", "SILVER", "64", "CORPORATION", - "65", "WISH", "66", "INSIDE", "67", "SOLUTION", "68", "MATURE", "69", "ROLE", - "6!", "RATHER", "6\"", "WEEKS", "6#", "ADDITION", "6$", "CAME", "6%", "SUPPLY", - "6&", "NOTHING", "6[", "CERTAIN", "6]", "EXECUTIVE", "6 ", "RUNNING", "6_", "LOWER", - "7A", "NECESSARY", "7B", "UNION", "7C", "JEWELRY", "7D", "ACCORDING", "7E", "CLOTHING", - "7F", "PARTICULAR", "7G", "FINE", "7H", "NAMES", "7I", "ROBERT", "7J", "HOMEPAGE", - "7K", "HOUR", "7L", "SKILLS", "7M", "BUSH", "7N", "ISLANDS", "7O", "ADVICE", - "7P", "CAREER", "7Q", "MILITARY", "7R", "RENTAL", "7S", "DECISION", "7T", "LEAVE", - "7U", "BRITISH", "7V", "TEENS", "7W", "HUGE", "7X", "WOMAN", "7Y", "FACILITIES", - "7Z", "KIND", "70", "SELLERS", "71", "MIDDLE", "72", "MOVE", "73", "CABLE", - "74", "OPPORTUNITIES", "75", "TAKING", "76", "VALUES", "77", "DIVISION", "78", "COMING", - "79", "TUESDAY", "7!", "OBJECT", "7\"", "LESBIAN", "7#", "APPROPRIATE", "7$", "MACHINE", - "7%", "LOGO", "7&", "LENGTH", "7[", "ACTUALLY", "7]", "NICE", "7 ", "SCORE", - "7_", "STATISTICS", "8A", "CLIENT", "8B", "RETURNS", "8C", "CAPITAL", "8D", "FOLLOW", - "8E", "SAMPLE", "8F", "INVESTMENT", "8G", "SENT", "8H", "SHOWN", "8I", "SATURDAY", - "8J", "CHRISTMAS", "8K", "ENGLAND", "8L", "CULTURE", "8M", "BAND", "8N", "FLASH", - "8O", "LEAD", "8P", "GEORGE", "8Q", "CHOICE", "8R", "WENT", "8S", "STARTING", - "8T", "REGISTRATION", "8U", "THURSDAY", "8V", "COURSES", "8W", "CONSUMER", "8X", "AIRPORT", - "8Y", "FOREIGN", "8Z", "ARTIST", "80", "OUTSIDE", "81", "FURNITURE", "82", "LEVELS", - "83", "CHANNEL", "84", "LETTER", "85", "MODE", "86", "PHONES", "87", "IDEAS", - "88", "WEDNESDAY", "89", "STRUCTURE", "8!", "FUND", "8\"", "SUMMER", "8#", "ALLOW", - "8$", "DEGREE", "8%", "CONTRACT", "8&", "BUTTON", "8[", "RELEASES", "8]", "HOMES", - "8 ", "SUPER", "8_", "MALE", "9A", "MATTER", "9B", "CUSTOM", "9C", "VIRGINIA", - "9D", "ALMOST", "9E", "TOOK", "9F", "LOCATED", "9G", "MULTIPLE", "9H", "ASIAN", - "9I", "DISTRIBUTION", "9J", "EDITOR", "9K", "INDUSTRIAL", "9L", "CAUSE", "9M", "POTENTIAL", - "9N", "SONG", "9O", "CNET", "9P", "FOCUS", "9Q", "LATE", "9R", "FALL", - "9S", "FEATURED", "9T", "IDEA", "9U", "ROOMS", "9V", "FEMALE", "9W", "RESPONSIBLE", - "9X", "COMMUNICATIONS", "9Y", "ASSOCIATED", "9Z", "THOMAS", "90", "PRIMARY", "91", "CANCER", - "92", "NUMBERS", "93", "REASON", "94", "TOOL", "95", "BROWSER", "96", "SPRING", - "97", "FOUNDATION", "98", "ANSWER", "99", "VOICE", "9!", "FRIENDLY", "9\"", "SCHEDULE", - "9#", "DOCUMENTS", "9$", "COMMUNICATION", "9%", "PURPOSE", "9&", "FEATURE", "9[", "COMES", - "9]", "POLICE", "9 ", "EVERYONE", "9_", "INDEPENDENT", "!A", "APPROACH", "!B", "CAMERAS", - "!C", "BROWN", "!D", "PHYSICAL", "!E", "OPERATING", "!F", "HILL", "!G", "MAPS", - "!H", "MEDICINE", "!I", "DEAL", "!J", "HOLD", "!K", "RATINGS", "!L", "CHICAGO", - "!M", "FORMS", "!N", "GLASS", "!O", "HAPPY", "!P", "SMITH", "!Q", "WANTED", - "!R", "DEVELOPED", "!S", "THANK", "!T", "SAFE", "!U", "UNIQUE", "!V", "SURVEY", - "!W", "PRIOR", "!X", "TELEPHONE", "!Y", "SPORT", "!Z", "READY", "!0", "FEED", - "!1", "ANIMAL", "!2", "SOURCES", "!3", "MEXICO", "!4", "POPULATION", "!5", "REGULAR", - "!6", "SECURE", "!7", "NAVIGATION", "!8", "OPERATIONS", "!9", "THEREFORE", "!!", "SIMPLY", - "!\"", "EVIDENCE", "!#", "STATION", "!$", "CHRISTIAN", "!%", "ROUND", "!&", "PAYPAL", - "![", "FAVORITE", "!]", "UNDERSTAND", "! ", "OPTION", "!_", "MASTER", "\"A", "VALLEY", - "\"B", "RECENTLY", "\"C", "PROBABLY", "\"D", "RENTALS", "\"E", "BUILT", "\"F", "PUBLICATIONS", - "\"G", "BLOOD", "\"H", "WORLDWIDE", "\"I", "IMPROVE", "\"J", "CONNECTION", "\"K", "PUBLISHER", - "\"L", "HALL", "\"M", "LARGER", "\"N", "ANTI", "\"O", "NETWORKS", "\"P", "EARTH", - "\"Q", "PARENTS", "\"R", "NOKIA", "\"S", "IMPACT", "\"T", "TRANSFER", "\"U", "INTRODUCTION", - "\"V", "KITCHEN", "\"W", "STRONG", "\"X", "CAROLINA", "\"Y", "WEDDING", "\"Z", "PROPERTIES", - "\"0", "HOSPITAL", "\"1", "GROUND", "\"2", "OVERVIEW", "\"3", "SHIP", "\"4", "ACCOMMODATION", - "\"5", "OWNERS", "\"6", "DISEASE", "\"7", "EXCELLENT", "\"8", "PAID", "\"9", "ITALY", - "\"!", "PERFECT", "\"\"", "HAIR", "\"#", "OPPORTUNITY", "\"$", "CLASSIC", "\"%", "BASIS", - "\"&", "COMMAND", "\"[", "CITIES", "\"]", "WILLIAM", "\" ", "EXPRESS", "\"_", "ANAL", - "#A", "AWARD", "#B", "DISTANCE", "#C", "TREE", "#D", "PETER", "#E", "ASSESSMENT", - "#F", "ENSURE", "#G", "THUS", "#H", "WALL", "#I", "INVOLVED", "#J", "EXTRA", - "#K", "ESPECIALLY", "#L", "INTERFACE", "#M", "PUSSY", "#N", "PARTNERS", "#O", "BUDGET", - "#P", "RATED", "#Q", "GUIDES", "#R", "SUCCESS", "#S", "MAXIMUM", "#T", "OPERATION", - "#U", "EXISTING", "#V", "QUITE", "#W", "SELECTED", "#X", "AMAZON", "#Y", "PATIENTS", - "#Z", "RESTAURANTS", "#0", "BEAUTIFUL", "#1", "WARNING", "#2", "WINE", "#3", "LOCATIONS", - "#4", "HORSE", "#5", "VOTE", "#6", "FORWARD", "#7", "FLOWERS", "#8", "STARS", - "#9", "SIGNIFICANT", "#!", "LISTS", "#\"", "TECHNOLOGIES", "##", "OWNER", "#$", "RETAIL", - "#%", "ANIMALS", "#&", "USEFUL", "#[", "DIRECTLY", "#]", "MANUFACTURER", "# ", "WAYS", - "#_", "PROVIDING", "$A", "RULE", "$B", "HOUSING", "$C", "TAKES", "$D", "BRING", - "$E", "CATALOG", "$F", "SEARCHES", "$G", "TRYING", "$H", "MOTHER", "$I", "AUTHORITY", - "$J", "CONSIDERED", "$K", "TOLD", "$L", "TRAFFIC", "$M", "PROGRAMME", "$N", "JOINED", - "$O", "INPUT", "$P", "STRATEGY", "$Q", "FEET", "$R", "AGENT", "$S", "VALID", - "$T", "MODERN", "$U", "SENIOR", "$V", "IRELAND", "$W", "SEXY", "$X", "TEACHING", - "$Y", "DOOR", "$Z", "GRAND", "$0", "TESTING", "$1", "TRIAL", "$2", "CHARGE", - "$3", "UNITS", "$4", "INSTEAD", "$5", "CANADIAN", "$6", "COOL", "$7", "NORMAL", - "$8", "WROTE", "$9", "ENTERPRISE", "$!", "SHIPS", "$\"", "ENTIRE", "$#", "EDUCATIONAL", - "$$", "LEADING", "$%", "METAL", "$&", "POSITIVE", "$[", "FITNESS", "$]", "CHINESE", - "$ ", "OPINION", "$_", "ASIA", "%A", "FOOTBALL", "%B", "ABSTRACT", "%C", "USES", - "%D", "OUTPUT", "%E", "FUNDS", "%F", "GREATER", "%G", "LIKELY", "%H", "DEVELOP", - "%I", "EMPLOYEES", "%J", "ARTISTS", "%K", "ALTERNATIVE", "%L", "PROCESSING", "%M", "RESPONSIBILITY", - "%N", "RESOLUTION", "%O", "JAVA", "%P", "GUEST", "%Q", "SEEMS", "%R", "PUBLICATION", - "%S", "PASS", "%T", "RELATIONS", "%U", "TRUST", "%V", "CONTAINS", "%W", "SESSION", - "%X", "MULTI", "%Y", "PHOTOGRAPHY", "%Z", "REPUBLIC", "%0", "FEES", "%1", "COMPONENTS", - "%2", "VACATION", "%3", "CENTURY", "%4", "ACADEMIC", "%5", "ASSISTANCE", "%6", "COMPLETED", - "%7", "SKIN", "%8", "GRAPHICS", "%9", "INDIAN", "%!", "PREV", "%\"", "MARY", - "%#", "EXPECTED", "%$", "RING", "%%", "GRADE", "%&", "DATING", "%[", "PACIFIC", - "%]", "MOUNTAIN", "% ", "ORGANIZATIONS", "%_", "FILTER", "&A", "MAILING", "&B", "VEHICLE", - "&C", "LONGER", "&D", "CONSIDER", "&E", "NORTHERN", "&F", "BEHIND", "&G", "PANEL", - "&H", "FLOOR", "&I", "GERMAN", "&J", "BUYING", "&K", "MATCH", "&L", "PROPOSED", - "&M", "DEFAULT", "&N", "REQUIRE", "&O", "IRAQ", "&P", "BOYS", "&Q", "OUTDOOR", - "&R", "DEEP", "&S", "MORNING", "&T", "OTHERWISE", "&U", "ALLOWS", "&V", "REST", - "&W", "PROTEIN", "&X", "PLANT", "&Y", "REPORTED", "&Z", "TRANSPORTATION", "&0", "POOL", - "&1", "MINI", "&2", "POLITICS", "&3", "PARTNER", "&4", "DISCLAIMER", "&5", "AUTHORS", - "&6", "BOARDS", "&7", "FACULTY", "&8", "PARTIES", "&9", "FISH", "&!", "MEMBERSHIP", - "&\"", "MISSION", "&#", "STRING", "&$", "SENSE", "&%", "MODIFIED", "&&", "PACK", - "&[", "RELEASED", "&]", "STAGE", "& ", "INTERNAL", "&_", "GOODS", "[A", "RECOMMENDED", - "[B", "BORN", "[C", "UNLESS", "[D", "RICHARD", "[E", "DETAILED", "[F", "JAPANESE", - "[G", "RACE", "[H", "APPROVED", "[I", "BACKGROUND", "[J", "TARGET", "[K", "EXCEPT", - "[L", "CHARACTER", "[M", "MAINTENANCE", "[N", "ABILITY", "[O", "MAYBE", "[P", "FUNCTIONS", - "[Q", "MOVING", "[R", "BRANDS", "[S", "PLACES", "[T", "PRETTY", "[U", "TRADEMARKS", - "[V", "PHENTERMINE", "[W", "SPAIN", "[X", "SOUTHERN", "[Y", "YOURSELF", "[Z", "WINTER", - "[0", "RAPE", "[1", "BATTERY", "[2", "YOUTH", "[3", "PRESSURE", "[4", "SUBMITTED", - "[5", "BOSTON", "[6", "INCEST", "[7", "DEBT", "[8", "KEYWORDS", "[9", "MEDIUM", - "[!", "TELEVISION", "[\"", "INTERESTED", "[#", "CORE", "[$", "BREAK", "[%", "PURPOSES", - "[&", "THROUGHOUT", "[[", "SETS", "[]", "DANCE", "[ ", "WOOD", "[_", "ITSELF", - "]A", "DEFINED", "]B", "PAPERS", "]C", "PLAYING", "]D", "AWARDS", "]E", "STUDIO", - "]F", "READER", "]G", "VIRTUAL", "]H", "DEVICE", "]I", "ESTABLISHED", "]J", "ANSWERS", - "]K", "RENT", "]L", "REMOTE", "]M", "DARK", "]N", "PROGRAMMING", "]O", "EXTERNAL", - "]P", "APPLE", "]Q", "REGARDING", "]R", "INSTRUCTIONS", "]S", "OFFERED", "]T", "THEORY", - "]U", "ENJOY", "]V", "REMOVE", "]W", "SURFACE", "]X", "MINIMUM", "]Y", "VISUAL", - "]Z", "HOST", "]0", "VARIETY", "]1", "TEACHERS", "]2", "ISBN", "]3", "MARTIN", - "]4", "MANUAL", "]5", "BLOCK", "]6", "SUBJECTS", "]7", "AGENTS", "]8", "INCREASED", - "]9", "REPAIR", "]!", "FAIR", "]\"", "CIVIL", "]#", "STEEL", "]$", "UNDERSTANDING", - "]%", "SONGS", "]&", "FIXED", "][", "WRONG", "]]", "BEGINNING", "] ", "HANDS", - "]_", "ASSOCIATES", " A", "FINALLY", " B", "UPDATES", " C", "DESKTOP", " D", "CLASSES", - " E", "PARIS", " F", "OHIO", " G", "GETS", " H", "SECTOR", " I", "CAPACITY", - " J", "REQUIRES", " K", "JERSEY", " L", "FULLY", " M", "FATHER", " N", "ELECTRIC", - " O", "INSTRUMENTS", " P", "QUOTES", " Q", "OFFICER", " R", "DRIVER", " S", "BUSINESSES", - " T", "DEAD", " U", "RESPECT", " V", "UNKNOWN", " W", "SPECIFIED", " X", "RESTAURANT", - " Y", "MIKE", " Z", "TRIP", " 0", "WORTH", " 1", "PROCEDURES", " 2", "POOR", - " 3", "TEACHER", " 4", "EYES", " 5", "RELATIONSHIP", " 6", "WORKERS", " 7", "FARM", - " 8", "HTTP://", " 9", "GEORGIA", " !", "PEACE", " \"", "TRADITIONAL", " #", "CAMPUS", - " $", "SHOWING", " %", "CREATIVE", " &", "COAST", " [", "BENEFIT", " ]", "PROGRESS", - " ", "FUNDING", " _", "DEVICES", "_A", "LORD", "_B", "GRANT", "_C", "AGREE", - "_D", "FICTION", "_E", "HEAR", "_F", "SOMETIMES", "_G", "WATCHES", "_H", "CAREERS", - "_I", "BEYOND", "_J", "GOES", "_K", "FAMILIES", "_L", "MUSEUM", "_M", "THEMSELVES", - "_N", "TRANSPORT", "_O", "INTERESTING", "_P", "BLOGS", "_Q", "WIFE", "_R", "EVALUATION", - "_S", "ACCEPTED", "_T", "FORMER", "_U", "IMPLEMENTATION", "_V", "HITS", "_W", "ZONE", - "_X", "COMPLEX", "_Y", "GALLERIES", "_Z", "REFERENCES", "_0", "PRESENTED", "_1", "JACK", - "_2", "FLAT", "_3", "FLOW", "_4", "AGENCIES", "_5", "LITERATURE", "_6", "RESPECTIVE", - "_7", "PARENT", "_8", "SPANISH", "_9", "MICHIGAN", "_!", "COLUMBIA", "_\"", "SETTING", - "_#", "SCALE", "_$", "STAND", "_%", "ECONOMY", "_&", "HIGHEST", "_[", "HELPFUL", - "_]", "MONTHLY", "_ ", "CRITICAL", "__", "FRAME" - }; - private static final Map wordsToCodes = new HashMap(); - private static final Map codesToWords = new HashMap(); + private static final String[] wordsAndCodes = { + "A", "THE", "B", "AND", "C", "FOR", "D", "YOU", "E", "NOT", "F", "ARE", + "G", "ALL", "H", "NEW", "I", "WAS", "J", "CAN", "K", "HAS", "L", "BUT", + "M", "OUR", + "NA", "THAT", "NB", "THIS", "NC", "WITH", "ND", "FROM", "NE", "YOUR", + "NF", "HAVE", "NG", "MORE", "NH", "WILL", "NI", "HOME", "NJ", "ABOUT", + "NK", "PAGE", "NL", "SEARCH", "NM", "FREE", "NN", "OTHER", "NO", "INFORMATION", + "NP", "TIME", "NQ", "THEY", "NR", "SITE", "NS", "WHAT", "NT", "WHICH", + "NU", "THEIR", "NV", "NEWS", "NW", "THERE", "NX", "ONLY", "NY", "WHEN", + "NZ", "CONTACT", "N0", "HERE", "N1", "BUSINESS", "N2", "ALSO", "N3", "HELP", + "N4", "VIEW", "N5", "ONLINE", "N6", "FIRST", "N7", "BEEN", "N8", "WOULD", + "N9", "WERE", "N!", "SERVICES", "N\"", "SOME", "N#", "THESE", "N$", "CLICK", + "N%", "LIKE", "N&", "SERVICE", "N[", "THAN", "N]", "FIND", "N ", "PRICE", + "N_", "DATE", "OA", "BACK", "OB", "PEOPLE", "OC", "LIST", "OD", "NAME", + "OE", "JUST", "OF", "OVER", "OG", "STATE", "OH", "YEAR", "OI", "INTO", + "OJ", "EMAIL", "OK", "HEALTH", "OL", "WORLD", "OM", "NEXT", "ON", "USED", + "OO", "WORK", "OP", "LAST", "OQ", "MOST", "OR", "PRODUCTS", "OS", "MUSIC", + "OT", "DATA", "OU", "MAKE", "OV", "THEM", "OW", "SHOULD", "OX", "PRODUCT", + "OY", "SYSTEM", "OZ", "POST", "O0", "CITY", "O1", "POLICY", "O2", "NUMBER", + "O3", "SUCH", "O4", "PLEASE", "O5", "AVAILABLE", "O6", "COPYRIGHT", "O7", "SUPPORT", + "O8", "MESSAGE", "O9", "AFTER", "O!", "BEST", "O\"", "SOFTWARE", "O#", "THEN", + "O$", "GOOD", "O%", "VIDEO", "O&", "WELL", "O[", "WHERE", "O]", "INFO", + "O ", "RIGHTS", "O_", "PUBLIC", "PA", "BOOKS", "PB", "HIGH", "PC", "SCHOOL", + "PD", "THROUGH", "PE", "EACH", "PF", "LINKS", "PG", "REVIEW", "PH", "YEARS", + "PI", "ORDER", "PJ", "VERY", "PK", "PRIVACY", "PL", "BOOK", "PM", "ITEMS", + "PN", "COMPANY", "PO", "READ", "PP", "GROUP", "PQ", "NEED", "PR", "MANY", + "PS", "USER", "PT", "SAID", "PU", "DOES", "PV", "UNDER", "PW", "GENERAL", + "PX", "RESEARCH", "PY", "UNIVERSITY", "PZ", "JANUARY", "P0", "MAIL", "P1", "FULL", + "P2", "REVIEWS", "P3", "PROGRAM", "P4", "LIFE", "P5", "KNOW", "P6", "GAMES", + "P7", "DAYS", "P8", "MANAGEMENT", "P9", "PART", "P!", "COULD", "P\"", "GREAT", + "P#", "UNITED", "P$", "HOTEL", "P%", "REAL", "P&", "ITEM", "P[", "INTERNATIONAL", + "P]", "CENTER", "P ", "EBAY", "P_", "MUST", "QA", "STORE", "QB", "TRAVEL", + "QC", "COMMENTS", "QD", "MADE", "QE", "DEVELOPMENT", "QF", "REPORT", "QG", "MEMBER", + "QH", "DETAILS", "QI", "LINE", "QJ", "TERMS", "QK", "BEFORE", "QL", "HOTELS", + "QM", "SEND", "QN", "RIGHT", "QO", "TYPE", "QP", "BECAUSE", "QQ", "LOCAL", + "QR", "THOSE", "QS", "USING", "QT", "RESULTS", "QU", "OFFICE", "QV", "EDUCATION", + "QW", "NATIONAL", "QX", "DESIGN", "QY", "TAKE", "QZ", "POSTED", "Q0", "INTERNET", + "Q1", "ADDRESS", "Q2", "COMMUNITY", "Q3", "WITHIN", "Q4", "STATES", "Q5", "AREA", + "Q6", "WANT", "Q7", "PHONE", "Q8", "SHIPPING", "Q9", "RESERVED", "Q!", "SUBJECT", + "Q\"", "BETWEEN", "Q#", "FORUM", "Q$", "FAMILY", "Q%", "LONG", "Q&", "BASED", + "Q[", "CODE", "Q]", "SHOW", "Q ", "EVEN", "Q_", "BLACK", "RA", "CHECK", + "RB", "SPECIAL", "RC", "PRICES", "RD", "WEBSITE", "RE", "INDEX", "RF", "BEING", + "RG", "WOMEN", "RH", "MUCH", "RI", "SIGN", "RJ", "FILE", "RK", "LINK", + "RL", "OPEN", "RM", "TODAY", "RN", "TECHNOLOGY", "RO", "SOUTH", "RP", "CASE", + "RQ", "PROJECT", "RR", "SAME", "RS", "PAGES", "RT", "VERSION", "RU", "SECTION", + "RV", "FOUND", "RW", "SPORTS", "RX", "HOUSE", "RY", "RELATED", "RZ", "SECURITY", + "R0", "BOTH", "R1", "COUNTY", "R2", "AMERICAN", "R3", "PHOTO", "R4", "GAME", + "R5", "MEMBERS", "R6", "POWER", "R7", "WHILE", "R8", "CARE", "R9", "NETWORK", + "R!", "DOWN", "R\"", "COMPUTER", "R#", "SYSTEMS", "R$", "THREE", "R%", "TOTAL", + "R&", "PLACE", "R[", "FOLLOWING", "R]", "DOWNLOAD", "R ", "WITHOUT", "R_", "ACCESS", + "SA", "THINK", "SB", "NORTH", "SC", "RESOURCES", "SD", "CURRENT", "SE", "POSTS", + "SF", "MEDIA", "SG", "CONTROL", "SH", "WATER", "SI", "HISTORY", "SJ", "PICTURES", + "SK", "SIZE", "SL", "PERSONAL", "SM", "SINCE", "SN", "INCLUDING", "SO", "GUIDE", + "SP", "SHOP", "SQ", "DIRECTORY", "SR", "BOARD", "SS", "LOCATION", "ST", "CHANGE", + "SU", "WHITE", "SV", "TEXT", "SW", "SMALL", "SX", "RATING", "SY", "RATE", + "SZ", "GOVERNMENT", "S0", "CHILDREN", "S1", "DURING", "S2", "RETURN", "S3", "STUDENTS", + "S4", "SHOPPING", "S5", "ACCOUNT", "S6", "TIMES", "S7", "SITES", "S8", "LEVEL", + "S9", "DIGITAL", "S!", "PROFILE", "S\"", "PREVIOUS", "S#", "FORM", "S$", "EVENTS", + "S%", "LOVE", "S&", "JOHN", "S[", "MAIN", "S]", "CALL", "S ", "HOURS", + "S_", "IMAGE", "TA", "DEPARTMENT", "TB", "TITLE", "TC", "DESCRIPTION", "TD", "INSURANCE", + "TE", "ANOTHER", "TF", "SHALL", "TG", "PROPERTY", "TH", "CLASS", "TI", "STILL", + "TJ", "MONEY", "TK", "QUALITY", "TL", "EVERY", "TM", "LISTING", "TN", "CONTENT", + "TO", "COUNTRY", "TP", "PRIVATE", "TQ", "LITTLE", "TR", "VISIT", "TS", "SAVE", + "TT", "TOOLS", "TU", "REPLY", "TV", "CUSTOMER", "TW", "DECEMBER", "TX", "COMPARE", + "TY", "MOVIES", "TZ", "INCLUDE", "T0", "COLLEGE", "T1", "VALUE", "T2", "ARTICLE", + "T3", "YORK", "T4", "CARD", "T5", "JOBS", "T6", "PROVIDE", "T7", "FOOD", + "T8", "SOURCE", "T9", "AUTHOR", "T!", "DIFFERENT", "T\"", "PRESS", "T#", "LEARN", + "T$", "SALE", "T%", "AROUND", "T&", "PRINT", "T[", "COURSE", "T]", "CANADA", + "T ", "PROCESS", "T_", "TEEN", "UA", "ROOM", "UB", "STOCK", "UC", "TRAINING", + "UD", "CREDIT", "UE", "POINT", "UF", "JOIN", "UG", "SCIENCE", "UH", "CATEGORIES", + "UI", "ADVANCED", "UJ", "WEST", "UK", "SALES", "UL", "LOOK", "UM", "ENGLISH", + "UN", "LEFT", "UO", "TEAM", "UP", "ESTATE", "UQ", "CONDITIONS", "UR", "SELECT", + "US", "WINDOWS", "UT", "PHOTOS", "UU", "THREAD", "UV", "WEEK", "UW", "CATEGORY", + "UX", "NOTE", "UY", "LIVE", "UZ", "LARGE", "U0", "GALLERY", "U1", "TABLE", + "U2", "REGISTER", "U3", "HOWEVER", "U4", "JUNE", "U5", "OCTOBER", "U6", "NOVEMBER", + "U7", "MARKET", "U8", "LIBRARY", "U9", "REALLY", "U!", "ACTION", "U\"", "START", + "U#", "SERIES", "U$", "MODEL", "U%", "FEATURES", "U&", "INDUSTRY", "U[", "PLAN", + "U]", "HUMAN", "U ", "PROVIDED", "U_", "REQUIRED", "VA", "SECOND", "VB", "ACCESSORIES", + "VC", "COST", "VD", "MOVIE", "VE", "FORUMS", "VF", "MARCH", "VG", "SEPTEMBER", + "VH", "BETTER", "VI", "QUESTIONS", "VJ", "JULY", "VK", "YAHOO", "VL", "GOING", + "VM", "MEDICAL", "VN", "TEST", "VO", "FRIEND", "VP", "COME", "VQ", "SERVER", + "VR", "STUDY", "VS", "APPLICATION", "VT", "CART", "VU", "STAFF", "VV", "ARTICLES", + "VW", "FEEDBACK", "VX", "AGAIN", "VY", "PLAY", "VZ", "LOOKING", "V0", "ISSUES", + "V1", "APRIL", "V2", "NEVER", "V3", "USERS", "V4", "COMPLETE", "V5", "STREET", + "V6", "TOPIC", "V7", "COMMENT", "V8", "FINANCIAL", "V9", "THINGS", "V!", "WORKING", + "V\"", "AGAINST", "V#", "STANDARD", "V$", "PERSON", "V%", "BELOW", "V&", "MOBILE", + "V[", "LESS", "V]", "BLOG", "V ", "PARTY", "V_", "PAYMENT", "WA", "EQUIPMENT", + "WB", "LOGIN", "WC", "STUDENT", "WD", "PROGRAMS", "WE", "OFFERS", "WF", "LEGAL", + "WG", "ABOVE", "WH", "RECENT", "WI", "PARK", "WJ", "STORES", "WK", "SIDE", + "WL", "PROBLEM", "WM", "GIVE", "WN", "MEMORY", "WO", "PERFORMANCE", "WP", "SOCIAL", + "WQ", "AUGUST", "WR", "QUOTE", "WS", "LANGUAGE", "WT", "STORY", "WU", "SELL", + "WV", "OPTIONS", "WW", "EXPERIENCE", "WX", "RATES", "WY", "CREATE", "WZ", "BODY", + "W0", "YOUNG", "W1", "AMERICA", "W2", "IMPORTANT", "W3", "FIELD", "W4", "EAST", + "W5", "PAPER", "W6", "SINGLE", "W7", "ACTIVITIES", "W8", "CLUB", "W9", "EXAMPLE", + "W!", "GIRLS", "W\"", "ADDITIONAL", "W#", "PASSWORD", "W$", "LATEST", "W%", "SOMETHING", + "W&", "ROAD", "W[", "GIFT", "W]", "QUESTION", "W ", "CHANGES", "W_", "NIGHT", + "XA", "HARD", "XB", "TEXAS", "XC", "FOUR", "XD", "POKER", "XE", "STATUS", + "XF", "BROWSE", "XG", "ISSUE", "XH", "RANGE", "XI", "BUILDING", "XJ", "SELLER", + "XK", "COURT", "XL", "FEBRUARY", "XM", "ALWAYS", "XN", "RESULT", "XO", "AUDIO", + "XP", "LIGHT", "XQ", "WRITE", "XR", "OFFER", "XS", "BLUE", "XT", "GROUPS", + "XU", "EASY", "XV", "GIVEN", "XW", "FILES", "XX", "EVENT", "XY", "RELEASE", + "XZ", "ANALYSIS", "X0", "REQUEST", "X1", "CHINA", "X2", "MAKING", "X3", "PICTURE", + "X4", "NEEDS", "X5", "POSSIBLE", "X6", "MIGHT", "X7", "PROFESSIONAL", "X8", "MONTH", + "X9", "MAJOR", "X!", "STAR", "X\"", "AREAS", "X#", "FUTURE", "X$", "SPACE", + "X%", "COMMITTEE", "X&", "HAND", "X[", "CARDS", "X]", "PROBLEMS", "X ", "LONDON", + "X_", "WASHINGTON", "YA", "MEETING", "YB", "BECOME", "YC", "INTEREST", "YD", "CHILD", + "YE", "KEEP", "YF", "ENTER", "YG", "CALIFORNIA", "YH", "PORN", "YI", "SHARE", + "YJ", "SIMILAR", "YK", "GARDEN", "YL", "SCHOOLS", "YM", "MILLION", "YN", "ADDED", + "YO", "REFERENCE", "YP", "COMPANIES", "YQ", "LISTED", "YR", "BABY", "YS", "LEARNING", + "YT", "ENERGY", "YU", "DELIVERY", "YV", "POPULAR", "YW", "TERM", "YX", "FILM", + "YY", "STORIES", "YZ", "COMPUTERS", "Y0", "JOURNAL", "Y1", "REPORTS", "Y2", "WELCOME", + "Y3", "CENTRAL", "Y4", "IMAGES", "Y5", "PRESIDENT", "Y6", "NOTICE", "Y7", "ORIGINAL", + "Y8", "HEAD", "Y9", "RADIO", "Y!", "UNTIL", "Y\"", "CELL", "Y#", "COLOR", + "Y$", "SELF", "Y%", "COUNCIL", "Y&", "AWAY", "Y[", "INCLUDES", "Y]", "TRACK", + "Y ", "AUSTRALIA", "Y_", "DISCUSSION", "ZA", "ARCHIVE", "ZB", "ONCE", "ZC", "OTHERS", + "ZD", "ENTERTAINMENT", "ZE", "AGREEMENT", "ZF", "FORMAT", "ZG", "LEAST", "ZH", "SOCIETY", + "ZI", "MONTHS", "ZJ", "SAFETY", "ZK", "FRIENDS", "ZL", "SURE", "ZM", "TRADE", + "ZN", "EDITION", "ZO", "CARS", "ZP", "MESSAGES", "ZQ", "MARKETING", "ZR", "TELL", + "ZS", "FURTHER", "ZT", "UPDATED", "ZU", "ASSOCIATION", "ZV", "ABLE", "ZW", "HAVING", + "ZX", "PROVIDES", "ZY", "DAVID", "ZZ", "ALREADY", "Z0", "GREEN", "Z1", "STUDIES", + "Z2", "CLOSE", "Z3", "COMMON", "Z4", "DRIVE", "Z5", "SPECIFIC", "Z6", "SEVERAL", + "Z7", "GOLD", "Z8", "LIVING", "Z9", "COLLECTION", "Z!", "CALLED", "Z\"", "SHORT", + "Z#", "ARTS", "Z$", "DISPLAY", "Z%", "LIMITED", "Z&", "POWERED", "Z[", "SOLUTIONS", + "Z]", "MEANS", "Z ", "DIRECTOR", "Z_", "DAILY", "0A", "BEACH", "0B", "PAST", + "0C", "NATURAL", "0D", "WHETHER", "0E", "ELECTRONICS", "0F", "FIVE", "0G", "UPON", + "0H", "PERIOD", "0I", "PLANNING", "0J", "DATABASE", "0K", "SAYS", "0L", "OFFICIAL", + "0M", "WEATHER", "0N", "LAND", "0O", "AVERAGE", "0P", "DONE", "0Q", "TECHNICAL", + "0R", "WINDOW", "0S", "FRANCE", "0T", "REGION", "0U", "ISLAND", "0V", "RECORD", + "0W", "DIRECT", "0X", "MICROSOFT", "0Y", "CONFERENCE", "0Z", "ENVIRONMENT", "00", "RECORDS", + "01", "DISTRICT", "02", "CALENDAR", "03", "COSTS", "04", "STYLE", "05", "FRONT", + "06", "STATEMENT", "07", "UPDATE", "08", "PARTS", "09", "EVER", "0!", "DOWNLOADS", + "0\"", "EARLY", "0#", "MILES", "0$", "SOUND", "0%", "RESOURCE", "0&", "PRESENT", + "0[", "APPLICATIONS", "0]", "EITHER", "0 ", "DOCUMENT", "0_", "WORD", "1A", "WORKS", + "1B", "MATERIAL", "1C", "BILL", "1D", "WRITTEN", "1E", "TALK", "1F", "FEDERAL", + "1G", "HOSTING", "1H", "RULES", "1I", "FINAL", "1J", "ADULT", "1K", "TICKETS", + "1L", "THING", "1M", "CENTRE", "1N", "REQUIREMENTS", "1O", "CHEAP", "1P", "NUDE", + "1Q", "KIDS", "1R", "FINANCE", "1S", "TRUE", "1T", "MINUTES", "1U", "ELSE", + "1V", "MARK", "1W", "THIRD", "1X", "ROCK", "1Y", "GIFTS", "1Z", "EUROPE", + "10", "READING", "11", "TOPICS", "12", "INDIVIDUAL", "13", "TIPS", "14", "PLUS", + "15", "AUTO", "16", "COVER", "17", "USUALLY", "18", "EDIT", "19", "TOGETHER", + "1!", "VIDEOS", "1\"", "PERCENT", "1#", "FAST", "1$", "FUNCTION", "1%", "FACT", + "1&", "UNIT", "1[", "GETTING", "1]", "GLOBAL", "1 ", "TECH", "1_", "MEET", + "2A", "ECONOMIC", "2B", "PLAYER", "2C", "PROJECTS", "2D", "LYRICS", "2E", "OFTEN", + "2F", "SUBSCRIBE", "2G", "SUBMIT", "2H", "GERMANY", "2I", "AMOUNT", "2J", "WATCH", + "2K", "INCLUDED", "2L", "FEEL", "2M", "THOUGH", "2N", "BANK", "2O", "RISK", + "2P", "THANKS", "2Q", "EVERYTHING", "2R", "DEALS", "2S", "VARIOUS", "2T", "WORDS", + "2U", "LINUX", "2V", "PRODUCTION", "2W", "COMMERCIAL", "2X", "JAMES", "2Y", "WEIGHT", + "2Z", "TOWN", "20", "HEART", "21", "ADVERTISING", "22", "RECEIVED", "23", "CHOOSE", + "24", "TREATMENT", "25", "NEWSLETTER", "26", "ARCHIVES", "27", "POINTS", "28", "KNOWLEDGE", + "29", "MAGAZINE", "2!", "ERROR", "2\"", "CAMERA", "2#", "GIRL", "2$", "CURRENTLY", + "2%", "CONSTRUCTION", "2&", "TOYS", "2[", "REGISTERED", "2]", "CLEAR", "2 ", "GOLF", + "2_", "RECEIVE", "3A", "DOMAIN", "3B", "METHODS", "3C", "CHAPTER", "3D", "MAKES", + "3E", "PROTECTION", "3F", "POLICIES", "3G", "LOAN", "3H", "WIDE", "3I", "BEAUTY", + "3J", "MANAGER", "3K", "INDIA", "3L", "POSITION", "3M", "TAKEN", "3N", "SORT", + "3O", "LISTINGS", "3P", "MODELS", "3Q", "MICHAEL", "3R", "KNOWN", "3S", "HALF", + "3T", "CASES", "3U", "STEP", "3V", "ENGINEERING", "3W", "FLORIDA", "3X", "SIMPLE", + "3Y", "QUICK", "3Z", "NONE", "30", "WIRELESS", "31", "LICENSE", "32", "PAUL", + "33", "FRIDAY", "34", "LAKE", "35", "WHOLE", "36", "ANNUAL", "37", "PUBLISHED", + "38", "LATER", "39", "BASIC", "3!", "SONY", "3\"", "SHOWS", "3#", "CORPORATE", + "3$", "GOOGLE", "3%", "CHURCH", "3&", "METHOD", "3[", "PURCHASE", "3]", "CUSTOMERS", + "3 ", "ACTIVE", "3_", "RESPONSE", "4A", "PRACTICE", "4B", "HARDWARE", "4C", "FIGURE", + "4D", "MATERIALS", "4E", "FIRE", "4F", "HOLIDAY", "4G", "CHAT", "4H", "ENOUGH", + "4I", "DESIGNED", "4J", "ALONG", "4K", "AMONG", "4L", "DEATH", "4M", "WRITING", + "4N", "SPEED", "4O", "HTML", "4P", "COUNTRIES", "4Q", "LOSS", "4R", "FACE", + "4S", "BRAND", "4T", "DISCOUNT", "4U", "HIGHER", "4V", "EFFECTS", "4W", "CREATED", + "4X", "REMEMBER", "4Y", "STANDARDS", "4Z", "YELLOW", "40", "POLITICAL", "41", "INCREASE", + "42", "ADVERTISE", "43", "KINGDOM", "44", "BASE", "45", "NEAR", "46", "ENVIRONMENTAL", + "47", "THOUGHT", "48", "STUFF", "49", "FRENCH", "4!", "STORAGE", "4\"", "JAPAN", + "4#", "DOING", "4$", "LOANS", "4%", "SHOES", "4&", "ENTRY", "4[", "STAY", + "4]", "NATURE", "4 ", "ORDERS", "4_", "AVAILABILITY", "5A", "AFRICA", "5B", "SUMMARY", + "5C", "TURN", "5D", "MEAN", "5E", "GROWTH", "5F", "NOTES", "5G", "AGENCY", + "5H", "KING", "5I", "MONDAY", "5J", "EUROPEAN", "5K", "ACTIVITY", "5L", "COPY", + "5M", "ALTHOUGH", "5N", "DRUG", "5O", "PICS", "5P", "WESTERN", "5Q", "INCOME", + "5R", "FORCE", "5S", "CASH", "5T", "EMPLOYMENT", "5U", "OVERALL", "5V", "RIVER", + "5W", "COMMISSION", "5X", "PACKAGE", "5Y", "CONTENTS", "5Z", "SEEN", "50", "PLAYERS", + "51", "ENGINE", "52", "PORT", "53", "ALBUM", "54", "REGIONAL", "55", "STOP", + "56", "SUPPLIES", "57", "STARTED", "58", "ADMINISTRATION", "59", "INSTITUTE", "5!", "VIEWS", + "5\"", "PLANS", "5#", "DOUBLE", "5$", "BUILD", "5%", "SCREEN", "5&", "EXCHANGE", + "5[", "TYPES", "5]", "SOON", "5 ", "SPONSORED", "5_", "LINES", "6A", "ELECTRONIC", + "6B", "CONTINUE", "6C", "ACROSS", "6D", "BENEFITS", "6E", "NEEDED", "6F", "SEASON", + "6G", "APPLY", "6H", "SOMEONE", "6I", "HELD", "6J", "ANYTHING", "6K", "PRINTER", + "6L", "CONDITION", "6M", "EFFECTIVE", "6N", "BELIEVE", "6O", "ORGANIZATION", "6P", "EFFECT", + "6Q", "ASKED", "6R", "MIND", "6S", "SUNDAY", "6T", "SELECTION", "6U", "CASINO", + "6V", "LOST", "6W", "TOUR", "6X", "MENU", "6Y", "VOLUME", "6Z", "CROSS", + "60", "ANYONE", "61", "MORTGAGE", "62", "HOPE", "63", "SILVER", "64", "CORPORATION", + "65", "WISH", "66", "INSIDE", "67", "SOLUTION", "68", "MATURE", "69", "ROLE", + "6!", "RATHER", "6\"", "WEEKS", "6#", "ADDITION", "6$", "CAME", "6%", "SUPPLY", + "6&", "NOTHING", "6[", "CERTAIN", "6]", "EXECUTIVE", "6 ", "RUNNING", "6_", "LOWER", + "7A", "NECESSARY", "7B", "UNION", "7C", "JEWELRY", "7D", "ACCORDING", "7E", "CLOTHING", + "7F", "PARTICULAR", "7G", "FINE", "7H", "NAMES", "7I", "ROBERT", "7J", "HOMEPAGE", + "7K", "HOUR", "7L", "SKILLS", "7M", "BUSH", "7N", "ISLANDS", "7O", "ADVICE", + "7P", "CAREER", "7Q", "MILITARY", "7R", "RENTAL", "7S", "DECISION", "7T", "LEAVE", + "7U", "BRITISH", "7V", "TEENS", "7W", "HUGE", "7X", "WOMAN", "7Y", "FACILITIES", + "7Z", "KIND", "70", "SELLERS", "71", "MIDDLE", "72", "MOVE", "73", "CABLE", + "74", "OPPORTUNITIES", "75", "TAKING", "76", "VALUES", "77", "DIVISION", "78", "COMING", + "79", "TUESDAY", "7!", "OBJECT", "7\"", "LESBIAN", "7#", "APPROPRIATE", "7$", "MACHINE", + "7%", "LOGO", "7&", "LENGTH", "7[", "ACTUALLY", "7]", "NICE", "7 ", "SCORE", + "7_", "STATISTICS", "8A", "CLIENT", "8B", "RETURNS", "8C", "CAPITAL", "8D", "FOLLOW", + "8E", "SAMPLE", "8F", "INVESTMENT", "8G", "SENT", "8H", "SHOWN", "8I", "SATURDAY", + "8J", "CHRISTMAS", "8K", "ENGLAND", "8L", "CULTURE", "8M", "BAND", "8N", "FLASH", + "8O", "LEAD", "8P", "GEORGE", "8Q", "CHOICE", "8R", "WENT", "8S", "STARTING", + "8T", "REGISTRATION", "8U", "THURSDAY", "8V", "COURSES", "8W", "CONSUMER", "8X", "AIRPORT", + "8Y", "FOREIGN", "8Z", "ARTIST", "80", "OUTSIDE", "81", "FURNITURE", "82", "LEVELS", + "83", "CHANNEL", "84", "LETTER", "85", "MODE", "86", "PHONES", "87", "IDEAS", + "88", "WEDNESDAY", "89", "STRUCTURE", "8!", "FUND", "8\"", "SUMMER", "8#", "ALLOW", + "8$", "DEGREE", "8%", "CONTRACT", "8&", "BUTTON", "8[", "RELEASES", "8]", "HOMES", + "8 ", "SUPER", "8_", "MALE", "9A", "MATTER", "9B", "CUSTOM", "9C", "VIRGINIA", + "9D", "ALMOST", "9E", "TOOK", "9F", "LOCATED", "9G", "MULTIPLE", "9H", "ASIAN", + "9I", "DISTRIBUTION", "9J", "EDITOR", "9K", "INDUSTRIAL", "9L", "CAUSE", "9M", "POTENTIAL", + "9N", "SONG", "9O", "CNET", "9P", "FOCUS", "9Q", "LATE", "9R", "FALL", + "9S", "FEATURED", "9T", "IDEA", "9U", "ROOMS", "9V", "FEMALE", "9W", "RESPONSIBLE", + "9X", "COMMUNICATIONS", "9Y", "ASSOCIATED", "9Z", "THOMAS", "90", "PRIMARY", "91", "CANCER", + "92", "NUMBERS", "93", "REASON", "94", "TOOL", "95", "BROWSER", "96", "SPRING", + "97", "FOUNDATION", "98", "ANSWER", "99", "VOICE", "9!", "FRIENDLY", "9\"", "SCHEDULE", + "9#", "DOCUMENTS", "9$", "COMMUNICATION", "9%", "PURPOSE", "9&", "FEATURE", "9[", "COMES", + "9]", "POLICE", "9 ", "EVERYONE", "9_", "INDEPENDENT", "!A", "APPROACH", "!B", "CAMERAS", + "!C", "BROWN", "!D", "PHYSICAL", "!E", "OPERATING", "!F", "HILL", "!G", "MAPS", + "!H", "MEDICINE", "!I", "DEAL", "!J", "HOLD", "!K", "RATINGS", "!L", "CHICAGO", + "!M", "FORMS", "!N", "GLASS", "!O", "HAPPY", "!P", "SMITH", "!Q", "WANTED", + "!R", "DEVELOPED", "!S", "THANK", "!T", "SAFE", "!U", "UNIQUE", "!V", "SURVEY", + "!W", "PRIOR", "!X", "TELEPHONE", "!Y", "SPORT", "!Z", "READY", "!0", "FEED", + "!1", "ANIMAL", "!2", "SOURCES", "!3", "MEXICO", "!4", "POPULATION", "!5", "REGULAR", + "!6", "SECURE", "!7", "NAVIGATION", "!8", "OPERATIONS", "!9", "THEREFORE", "!!", "SIMPLY", + "!\"", "EVIDENCE", "!#", "STATION", "!$", "CHRISTIAN", "!%", "ROUND", "!&", "PAYPAL", + "![", "FAVORITE", "!]", "UNDERSTAND", "! ", "OPTION", "!_", "MASTER", "\"A", "VALLEY", + "\"B", "RECENTLY", "\"C", "PROBABLY", "\"D", "RENTALS", "\"E", "BUILT", "\"F", "PUBLICATIONS", + "\"G", "BLOOD", "\"H", "WORLDWIDE", "\"I", "IMPROVE", "\"J", "CONNECTION", "\"K", "PUBLISHER", + "\"L", "HALL", "\"M", "LARGER", "\"N", "ANTI", "\"O", "NETWORKS", "\"P", "EARTH", + "\"Q", "PARENTS", "\"R", "NOKIA", "\"S", "IMPACT", "\"T", "TRANSFER", "\"U", "INTRODUCTION", + "\"V", "KITCHEN", "\"W", "STRONG", "\"X", "CAROLINA", "\"Y", "WEDDING", "\"Z", "PROPERTIES", + "\"0", "HOSPITAL", "\"1", "GROUND", "\"2", "OVERVIEW", "\"3", "SHIP", "\"4", "ACCOMMODATION", + "\"5", "OWNERS", "\"6", "DISEASE", "\"7", "EXCELLENT", "\"8", "PAID", "\"9", "ITALY", + "\"!", "PERFECT", "\"\"", "HAIR", "\"#", "OPPORTUNITY", "\"$", "CLASSIC", "\"%", "BASIS", + "\"&", "COMMAND", "\"[", "CITIES", "\"]", "WILLIAM", "\" ", "EXPRESS", "\"_", "ANAL", + "#A", "AWARD", "#B", "DISTANCE", "#C", "TREE", "#D", "PETER", "#E", "ASSESSMENT", + "#F", "ENSURE", "#G", "THUS", "#H", "WALL", "#I", "INVOLVED", "#J", "EXTRA", + "#K", "ESPECIALLY", "#L", "INTERFACE", "#M", "PUSSY", "#N", "PARTNERS", "#O", "BUDGET", + "#P", "RATED", "#Q", "GUIDES", "#R", "SUCCESS", "#S", "MAXIMUM", "#T", "OPERATION", + "#U", "EXISTING", "#V", "QUITE", "#W", "SELECTED", "#X", "AMAZON", "#Y", "PATIENTS", + "#Z", "RESTAURANTS", "#0", "BEAUTIFUL", "#1", "WARNING", "#2", "WINE", "#3", "LOCATIONS", + "#4", "HORSE", "#5", "VOTE", "#6", "FORWARD", "#7", "FLOWERS", "#8", "STARS", + "#9", "SIGNIFICANT", "#!", "LISTS", "#\"", "TECHNOLOGIES", "##", "OWNER", "#$", "RETAIL", + "#%", "ANIMALS", "#&", "USEFUL", "#[", "DIRECTLY", "#]", "MANUFACTURER", "# ", "WAYS", + "#_", "PROVIDING", "$A", "RULE", "$B", "HOUSING", "$C", "TAKES", "$D", "BRING", + "$E", "CATALOG", "$F", "SEARCHES", "$G", "TRYING", "$H", "MOTHER", "$I", "AUTHORITY", + "$J", "CONSIDERED", "$K", "TOLD", "$L", "TRAFFIC", "$M", "PROGRAMME", "$N", "JOINED", + "$O", "INPUT", "$P", "STRATEGY", "$Q", "FEET", "$R", "AGENT", "$S", "VALID", + "$T", "MODERN", "$U", "SENIOR", "$V", "IRELAND", "$W", "SEXY", "$X", "TEACHING", + "$Y", "DOOR", "$Z", "GRAND", "$0", "TESTING", "$1", "TRIAL", "$2", "CHARGE", + "$3", "UNITS", "$4", "INSTEAD", "$5", "CANADIAN", "$6", "COOL", "$7", "NORMAL", + "$8", "WROTE", "$9", "ENTERPRISE", "$!", "SHIPS", "$\"", "ENTIRE", "$#", "EDUCATIONAL", + "$$", "LEADING", "$%", "METAL", "$&", "POSITIVE", "$[", "FITNESS", "$]", "CHINESE", + "$ ", "OPINION", "$_", "ASIA", "%A", "FOOTBALL", "%B", "ABSTRACT", "%C", "USES", + "%D", "OUTPUT", "%E", "FUNDS", "%F", "GREATER", "%G", "LIKELY", "%H", "DEVELOP", + "%I", "EMPLOYEES", "%J", "ARTISTS", "%K", "ALTERNATIVE", "%L", "PROCESSING", "%M", "RESPONSIBILITY", + "%N", "RESOLUTION", "%O", "JAVA", "%P", "GUEST", "%Q", "SEEMS", "%R", "PUBLICATION", + "%S", "PASS", "%T", "RELATIONS", "%U", "TRUST", "%V", "CONTAINS", "%W", "SESSION", + "%X", "MULTI", "%Y", "PHOTOGRAPHY", "%Z", "REPUBLIC", "%0", "FEES", "%1", "COMPONENTS", + "%2", "VACATION", "%3", "CENTURY", "%4", "ACADEMIC", "%5", "ASSISTANCE", "%6", "COMPLETED", + "%7", "SKIN", "%8", "GRAPHICS", "%9", "INDIAN", "%!", "PREV", "%\"", "MARY", + "%#", "EXPECTED", "%$", "RING", "%%", "GRADE", "%&", "DATING", "%[", "PACIFIC", + "%]", "MOUNTAIN", "% ", "ORGANIZATIONS", "%_", "FILTER", "&A", "MAILING", "&B", "VEHICLE", + "&C", "LONGER", "&D", "CONSIDER", "&E", "NORTHERN", "&F", "BEHIND", "&G", "PANEL", + "&H", "FLOOR", "&I", "GERMAN", "&J", "BUYING", "&K", "MATCH", "&L", "PROPOSED", + "&M", "DEFAULT", "&N", "REQUIRE", "&O", "IRAQ", "&P", "BOYS", "&Q", "OUTDOOR", + "&R", "DEEP", "&S", "MORNING", "&T", "OTHERWISE", "&U", "ALLOWS", "&V", "REST", + "&W", "PROTEIN", "&X", "PLANT", "&Y", "REPORTED", "&Z", "TRANSPORTATION", "&0", "POOL", + "&1", "MINI", "&2", "POLITICS", "&3", "PARTNER", "&4", "DISCLAIMER", "&5", "AUTHORS", + "&6", "BOARDS", "&7", "FACULTY", "&8", "PARTIES", "&9", "FISH", "&!", "MEMBERSHIP", + "&\"", "MISSION", "&#", "STRING", "&$", "SENSE", "&%", "MODIFIED", "&&", "PACK", + "&[", "RELEASED", "&]", "STAGE", "& ", "INTERNAL", "&_", "GOODS", "[A", "RECOMMENDED", + "[B", "BORN", "[C", "UNLESS", "[D", "RICHARD", "[E", "DETAILED", "[F", "JAPANESE", + "[G", "RACE", "[H", "APPROVED", "[I", "BACKGROUND", "[J", "TARGET", "[K", "EXCEPT", + "[L", "CHARACTER", "[M", "MAINTENANCE", "[N", "ABILITY", "[O", "MAYBE", "[P", "FUNCTIONS", + "[Q", "MOVING", "[R", "BRANDS", "[S", "PLACES", "[T", "PRETTY", "[U", "TRADEMARKS", + "[V", "PHENTERMINE", "[W", "SPAIN", "[X", "SOUTHERN", "[Y", "YOURSELF", "[Z", "WINTER", + "[0", "RAPE", "[1", "BATTERY", "[2", "YOUTH", "[3", "PRESSURE", "[4", "SUBMITTED", + "[5", "BOSTON", "[6", "INCEST", "[7", "DEBT", "[8", "KEYWORDS", "[9", "MEDIUM", + "[!", "TELEVISION", "[\"", "INTERESTED", "[#", "CORE", "[$", "BREAK", "[%", "PURPOSES", + "[&", "THROUGHOUT", "[[", "SETS", "[]", "DANCE", "[ ", "WOOD", "[_", "ITSELF", + "]A", "DEFINED", "]B", "PAPERS", "]C", "PLAYING", "]D", "AWARDS", "]E", "STUDIO", + "]F", "READER", "]G", "VIRTUAL", "]H", "DEVICE", "]I", "ESTABLISHED", "]J", "ANSWERS", + "]K", "RENT", "]L", "REMOTE", "]M", "DARK", "]N", "PROGRAMMING", "]O", "EXTERNAL", + "]P", "APPLE", "]Q", "REGARDING", "]R", "INSTRUCTIONS", "]S", "OFFERED", "]T", "THEORY", + "]U", "ENJOY", "]V", "REMOVE", "]W", "SURFACE", "]X", "MINIMUM", "]Y", "VISUAL", + "]Z", "HOST", "]0", "VARIETY", "]1", "TEACHERS", "]2", "ISBN", "]3", "MARTIN", + "]4", "MANUAL", "]5", "BLOCK", "]6", "SUBJECTS", "]7", "AGENTS", "]8", "INCREASED", + "]9", "REPAIR", "]!", "FAIR", "]\"", "CIVIL", "]#", "STEEL", "]$", "UNDERSTANDING", + "]%", "SONGS", "]&", "FIXED", "][", "WRONG", "]]", "BEGINNING", "] ", "HANDS", + "]_", "ASSOCIATES", " A", "FINALLY", " B", "UPDATES", " C", "DESKTOP", " D", "CLASSES", + " E", "PARIS", " F", "OHIO", " G", "GETS", " H", "SECTOR", " I", "CAPACITY", + " J", "REQUIRES", " K", "JERSEY", " L", "FULLY", " M", "FATHER", " N", "ELECTRIC", + " O", "INSTRUMENTS", " P", "QUOTES", " Q", "OFFICER", " R", "DRIVER", " S", "BUSINESSES", + " T", "DEAD", " U", "RESPECT", " V", "UNKNOWN", " W", "SPECIFIED", " X", "RESTAURANT", + " Y", "MIKE", " Z", "TRIP", " 0", "WORTH", " 1", "PROCEDURES", " 2", "POOR", + " 3", "TEACHER", " 4", "EYES", " 5", "RELATIONSHIP", " 6", "WORKERS", " 7", "FARM", + " 8", "HTTP://", " 9", "GEORGIA", " !", "PEACE", " \"", "TRADITIONAL", " #", "CAMPUS", + " $", "SHOWING", " %", "CREATIVE", " &", "COAST", " [", "BENEFIT", " ]", "PROGRESS", + " ", "FUNDING", " _", "DEVICES", "_A", "LORD", "_B", "GRANT", "_C", "AGREE", + "_D", "FICTION", "_E", "HEAR", "_F", "SOMETIMES", "_G", "WATCHES", "_H", "CAREERS", + "_I", "BEYOND", "_J", "GOES", "_K", "FAMILIES", "_L", "MUSEUM", "_M", "THEMSELVES", + "_N", "TRANSPORT", "_O", "INTERESTING", "_P", "BLOGS", "_Q", "WIFE", "_R", "EVALUATION", + "_S", "ACCEPTED", "_T", "FORMER", "_U", "IMPLEMENTATION", "_V", "HITS", "_W", "ZONE", + "_X", "COMPLEX", "_Y", "GALLERIES", "_Z", "REFERENCES", "_0", "PRESENTED", "_1", "JACK", + "_2", "FLAT", "_3", "FLOW", "_4", "AGENCIES", "_5", "LITERATURE", "_6", "RESPECTIVE", + "_7", "PARENT", "_8", "SPANISH", "_9", "MICHIGAN", "_!", "COLUMBIA", "_\"", "SETTING", + "_#", "SCALE", "_$", "STAND", "_%", "ECONOMY", "_&", "HIGHEST", "_[", "HELPFUL", + "_]", "MONTHLY", "_ ", "CRITICAL", "__", "FRAME" + }; + private static final Map wordsToCodes = new HashMap(); + private static final Map codesToWords = new HashMap(); - static { - for (int i = 0; i < wordsAndCodes.length; i = i + 2) { - String code = wordsAndCodes[i]; - String word = wordsAndCodes[i + 1]; - wordsToCodes.put(word, code); - codesToWords.put(code, word); - } + static { + for (int i = 0; i < wordsAndCodes.length; i = i + 2) { + String code = wordsAndCodes[i]; + String word = wordsAndCodes[i + 1]; + wordsToCodes.put(word, code); + codesToWords.put(code, word); } + } - public static String encode(String word) { - if (wordsToCodes.containsKey(word)) { - return "<" + wordsToCodes.get(word); - } - return word; + public static String encode(String word) { + if (wordsToCodes.containsKey(word)) { + return "<" + wordsToCodes.get(word); } + return word; + } - public static String decode(String code1) { - if (!code1.startsWith("<")) { - return code1; - } - String code = code1.substring(1); - if (codesToWords.containsKey(code)) { - return codesToWords.get(code); - } - return code1; + public static String decode(String code1) { + if (!code1.startsWith("<")) { + return code1; } - - public static String lengthen(String result2) { - List shortCodesInString = wordsForPattern(shortCodesPattern, result2); - for (String w : shortCodesInString) { - String theWord = Dictionary.decode(w); - result2 = result2.replace(w, theWord); - } - List longCodesInString = wordsForPattern(longCodesPattern, result2); - for (String w : longCodesInString) { - String theWord = Dictionary.decode(w); - result2 = result2.replace(w, theWord); - } - return result2; + String code = code1.substring(1); + if (codesToWords.containsKey(code)) { + return codesToWords.get(code); } + return code1; + } - public static String shorten(String str) { - List words = wordsForPattern(wordsPattern, str); - String result = str; - for (String w : words) { - String code = Dictionary.encode(w); - result = result.replace(w, code); - } - return result; + public static String lengthen(String result2) { + List shortCodesInString = wordsForPattern(shortCodesPattern, result2); + for (String w : shortCodesInString) { + String theWord = Dictionary.decode(w); + result2 = result2.replace(w, theWord); + } + List longCodesInString = wordsForPattern(longCodesPattern, result2); + for (String w : longCodesInString) { + String theWord = Dictionary.decode(w); + result2 = result2.replace(w, theWord); } + return result2; + } - public static List wordsForPattern(Pattern p, String str) { - Matcher m = p.matcher(str); - List words = new ArrayList(); - while (m.find()) { - words.add(m.group(0)); - } + public static String shorten(String str) { + List words = wordsForPattern(wordsPattern, str); + String result = str; + for (String w : words) { + String code = Dictionary.encode(w); + result = result.replace(w, code); + } + return result; + } - Collections.sort(words, byLength); - return words; + public static List wordsForPattern(Pattern p, String str) { + Matcher m = p.matcher(str); + List words = new ArrayList(); + while (m.find()) { + words.add(m.group(0)); } + + Collections.sort(words, byLength); + return words; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java b/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java index 9cf0a2f8c..d37866759 100644 --- a/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java +++ b/src/main/java/art/arcane/adapt/util/common/collection/KeyPair.java @@ -27,33 +27,33 @@ */ @SuppressWarnings("hiding") public class KeyPair { - private K k; - private V v; + private K k; + private V v; - /** - * Create a keypair - * - * @param k the key - * @param v the value - */ - public KeyPair(K k, V v) { - this.k = k; - this.v = v; - } + /** + * Create a keypair + * + * @param k the key + * @param v the value + */ + public KeyPair(K k, V v) { + this.k = k; + this.v = v; + } - public K getK() { - return k; - } + public K getK() { + return k; + } - public void setK(K k) { - this.k = k; - } + public void setK(K k) { + this.k = k; + } - public V getV() { - return v; - } + public V getV() { + return v; + } - public void setV(V v) { - this.v = v; - } + public void setV(V v) { + this.v = v; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java b/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java index 0ecb09c2b..f51924751 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java +++ b/src/main/java/art/arcane/adapt/util/common/format/AdventureCompat.java @@ -27,80 +27,80 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class AdventureCompat { - private static final AtomicBoolean FALLBACK_NOTIFIED = new AtomicBoolean(false); - private static volatile boolean miniMessageCompatible = true; - - private AdventureCompat() { - } - - public static Component deserialize(String message) { - String source = normalize(message); - if (miniMessageCompatible) { - try { - return MiniMessage.miniMessage().deserialize(source); - } catch (Throwable e) { - markIncompatible(e); - } - } - - return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); - } - - public static Component deserializeNoProcessing(String message) { - String source = normalize(message); - if (miniMessageCompatible) { - try { - return MiniMessage.builder().postProcessor(c -> c).build().deserialize(source); - } catch (Throwable e) { - markIncompatible(e); - } - } - - return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); - } - - public static String stripTags(String message) { - String source = normalize(message); - if (miniMessageCompatible) { - try { - return MiniMessage.miniMessage().stripTags(source); - } catch (Throwable e) { - markIncompatible(e); - } - } - - return stripTagsFallback(source); + private static final AtomicBoolean FALLBACK_NOTIFIED = new AtomicBoolean(false); + private static volatile boolean miniMessageCompatible = true; + + private AdventureCompat() { + } + + public static Component deserialize(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.miniMessage().deserialize(source); + } catch (Throwable e) { + markIncompatible(e); + } } - public static String toLegacySection(String message) { - String source = normalize(message); - if (miniMessageCompatible) { - try { - return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(source)); - } catch (Throwable e) { - markIncompatible(e); - } - } - - return C.translateAlternateColorCodes('&', stripTagsFallback(source)); + return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); + } + + public static Component deserializeNoProcessing(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.builder().postProcessor(c -> c).build().deserialize(source); + } catch (Throwable e) { + markIncompatible(e); + } } - private static String normalize(String message) { - return message == null ? "" : message; + return LegacyComponentSerializer.legacySection().deserialize(C.translateAlternateColorCodes('&', stripTagsFallback(source))); + } + + public static String stripTags(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return MiniMessage.miniMessage().stripTags(source); + } catch (Throwable e) { + markIncompatible(e); + } } - private static String stripTagsFallback(String message) { - return message.replaceAll("<[^>]+>", ""); + return stripTagsFallback(source); + } + + public static String toLegacySection(String message) { + String source = normalize(message); + if (miniMessageCompatible) { + try { + return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(source)); + } catch (Throwable e) { + markIncompatible(e); + } } - private static void markIncompatible(Throwable e) { - miniMessageCompatible = false; - if (FALLBACK_NOTIFIED.compareAndSet(false, true)) { - String reason = e == null ? "unknown" : e.getClass().getSimpleName(); - Adapt.warn("MiniMessage compatibility fallback enabled (" + reason + ")."); - if (e != null) { - Adapt.verbose("MiniMessage fallback reason: " + e.toString().toLowerCase(Locale.ROOT)); - } - } + return C.translateAlternateColorCodes('&', stripTagsFallback(source)); + } + + private static String normalize(String message) { + return message == null ? "" : message; + } + + private static String stripTagsFallback(String message) { + return message.replaceAll("<[^>]+>", ""); + } + + private static void markIncompatible(Throwable e) { + miniMessageCompatible = false; + if (FALLBACK_NOTIFIED.compareAndSet(false, true)) { + String reason = e == null ? "unknown" : e.getClass().getSimpleName(); + Adapt.warn("MiniMessage compatibility fallback enabled (" + reason + ")."); + if (e != null) { + Adapt.verbose("MiniMessage fallback reason: " + e.toString().toLowerCase(Locale.ROOT)); + } } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/format/C.java b/src/main/java/art/arcane/adapt/util/common/format/C.java index 7178e6691..67a48e810 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/C.java +++ b/src/main/java/art/arcane/adapt/util/common/format/C.java @@ -37,664 +37,665 @@ * @author cyberpwn */ public enum C { - /** - * Represents black - */ - BLACK('0', 0x00) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.BLACK; - } - }, - /** - * Represents dark blue - */ - DARK_BLUE('1', 0x1) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_BLUE; - } - }, - /** - * Represents dark green - */ - DARK_GREEN('2', 0x2) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_GREEN; - } - }, - /** - * Represents dark blue (aqua) - */ - DARK_AQUA('3', 0x3) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_AQUA; - } - }, - /** - * Represents dark red - */ - DARK_RED('4', 0x4) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_RED; - } - }, - /** - * Represents dark red - */ - ADAPT('4', 0x4) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_RED; - } - }, - /** - * Represents dark purple - */ - DARK_PURPLE('5', 0x5) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_PURPLE; - } - }, - /** - * Represents gold - */ - GOLD('6', 0x6) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.GOLD; - } - }, - /** - * Represents gray - */ - GRAY('7', 0x7) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.GRAY; - } - }, - /** - * Represents dark gray - */ - DARK_GRAY('8', 0x8) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.DARK_GRAY; - } - }, - /** - * Represents blue - */ - BLUE('9', 0x9) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.BLUE; - } - }, - /** - * Represents green - */ - GREEN('a', 0xA) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.GREEN; - } - }, - /** - * Represents aqua - */ - AQUA('b', 0xB) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.AQUA; - } - }, - /** - * Represents red - */ - RED('c', 0xC) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.RED; - } - }, - /** - * Represents light purple - */ - LIGHT_PURPLE('d', 0xD) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.LIGHT_PURPLE; - } - }, - /** - * Represents yellow - */ - YELLOW('e', 0xE) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.YELLOW; - } - }, - /** - * Represents white - */ - WHITE('f', 0xF) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.WHITE; - } - }, - /** - * Represents magical characters that change around randomly - */ - MAGIC("", 'k', 0x10, true) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.MAGIC; - } - }, - /** - * Makes the text bold. - */ - BOLD('l', 0x11, true) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.BOLD; - } - }, - /** - * Makes a line appear through the text. - */ - STRIKETHROUGH('m', 0x12, true) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.STRIKETHROUGH; - } - }, - /** - * Makes the text appear underlined. - */ - UNDERLINE("", 'n', 0x13, true) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.UNDERLINE; - } - }, - /** - * Makes the text italic. - */ - ITALIC('o', 0x14, true) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.ITALIC; - } - }, - - /** - * Resets all previous chat colors or formats. - */ - RESET('r', 0x15) { - @Override - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.RESET; - } - }, - - - ; - /** - * The special character which prefixes all chat colour codes. Use this if you - * need to dynamically convert colour codes from your custom format. - */ - public static final char COLOR_CHAR = '\u00A7'; - public final static C[] COLORCYCLE = new C[]{C.GOLD, C.YELLOW, C.GREEN, C.AQUA, C.LIGHT_PURPLE, C.AQUA, C.GREEN, C.YELLOW, C.GOLD, C.RED}; - private final static C[] COLORS = new C[]{C.BLACK, C.DARK_BLUE, C.DARK_GREEN, C.DARK_AQUA, C.DARK_RED, C.DARK_PURPLE, C.GOLD, C.GRAY, C.DARK_GRAY, C.BLUE, C.GREEN, C.AQUA, C.RED, C.LIGHT_PURPLE, C.YELLOW, C.WHITE}; - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - private final static Map BY_ID = new HashMap<>(); - private final static Map BY_CHAR = new HashMap<>(); - private final static Map dyeChatMap = new HashMap<>(); - private final static Map chatHexMap = new HashMap<>(); - private final static Map dyeHexMap = new HashMap<>(); - - static { - chatHexMap.put(C.BLACK, "#000000"); - chatHexMap.put(C.DARK_BLUE, "#0000AA"); - chatHexMap.put(C.DARK_GREEN, "#00AA00"); - chatHexMap.put(C.DARK_AQUA, "#00AAAA"); - chatHexMap.put(C.DARK_RED, "#AA0000"); - chatHexMap.put(C.ADAPT, "#AA0000"); - chatHexMap.put(C.DARK_PURPLE, "#AA00AA"); - chatHexMap.put(C.GOLD, "#FFAA00"); - chatHexMap.put(C.GRAY, "#AAAAAA"); - chatHexMap.put(C.DARK_GRAY, "#555555"); - chatHexMap.put(C.BLUE, "#5555FF"); - chatHexMap.put(C.GREEN, "#55FF55"); - chatHexMap.put(C.AQUA, "#55FFFF"); - chatHexMap.put(C.RED, "#FF5555"); - chatHexMap.put(C.LIGHT_PURPLE, "#FF55FF"); - chatHexMap.put(C.YELLOW, "#FFFF55"); - chatHexMap.put(C.WHITE, "#FFFFFF"); - dyeChatMap.put(DyeColor.BLACK, C.DARK_GRAY); - dyeChatMap.put(DyeColor.BLUE, C.DARK_BLUE); - dyeChatMap.put(DyeColor.BROWN, C.GOLD); - dyeChatMap.put(DyeColor.CYAN, C.AQUA); - dyeChatMap.put(DyeColor.GRAY, C.GRAY); - dyeChatMap.put(DyeColor.GREEN, C.DARK_GREEN); - dyeChatMap.put(DyeColor.LIGHT_BLUE, C.BLUE); - dyeChatMap.put(DyeColor.LIME, C.GREEN); - dyeChatMap.put(DyeColor.MAGENTA, C.LIGHT_PURPLE); - dyeChatMap.put(DyeColor.ORANGE, C.GOLD); - dyeChatMap.put(DyeColor.PINK, C.LIGHT_PURPLE); - dyeChatMap.put(DyeColor.PURPLE, C.DARK_PURPLE); - dyeChatMap.put(DyeColor.RED, C.RED); - dyeChatMap.put(DyeColor.LIGHT_GRAY, C.GRAY); - dyeChatMap.put(DyeColor.WHITE, C.WHITE); - dyeChatMap.put(DyeColor.YELLOW, C.YELLOW); - dyeHexMap.put(DyeColor.BLACK, "#181414"); - dyeHexMap.put(DyeColor.BLUE, "#253193"); - dyeHexMap.put(DyeColor.BROWN, "#56331c"); - dyeHexMap.put(DyeColor.CYAN, "#267191"); - dyeHexMap.put(DyeColor.GRAY, "#414141"); - dyeHexMap.put(DyeColor.GREEN, "#364b18"); - dyeHexMap.put(DyeColor.LIGHT_BLUE, "#6387d2"); - dyeHexMap.put(DyeColor.LIME, "#39ba2e"); - dyeHexMap.put(DyeColor.MAGENTA, "#be49c9"); - dyeHexMap.put(DyeColor.ORANGE, "#ea7e35"); - dyeHexMap.put(DyeColor.PINK, "#d98199"); - dyeHexMap.put(DyeColor.PURPLE, "#7e34bf"); - dyeHexMap.put(DyeColor.RED, "#9e2b27"); - dyeHexMap.put(DyeColor.LIGHT_GRAY, "#a0a7a7"); - dyeHexMap.put(DyeColor.WHITE, "#a4a4a4"); - dyeHexMap.put(DyeColor.YELLOW, "#c2b51c"); - } - - static { - for (C color : values()) { - BY_ID.put(color.intCode, color); - BY_CHAR.put(color.code, color); - } + /** + * Represents black + */ + BLACK('0', 0x00) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.BLACK; } - - private final int intCode; - private final char code; - private final String token; - private final boolean isFormat; - private final String toString; - - C(char code, int intCode) { - this("^", code, intCode, false); + }, + /** + * Represents dark blue + */ + DARK_BLUE('1', 0x1) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_BLUE; } - - C(String token, char code, int intCode) { - this(token, code, intCode, false); + }, + /** + * Represents dark green + */ + DARK_GREEN('2', 0x2) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_GREEN; } - - C(char code, int intCode, boolean isFormat) { - this("^", code, intCode, isFormat); + }, + /** + * Represents dark blue (aqua) + */ + DARK_AQUA('3', 0x3) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_AQUA; } - - C(String token, char code, int intCode, boolean isFormat) { - this.code = code; - this.token = token.equalsIgnoreCase("^") ? "<" + name().toLowerCase(Locale.ROOT) + ">" : token; - this.intCode = intCode; - this.isFormat = isFormat; - this.toString = new String(new char[]{COLOR_CHAR, code}); + }, + /** + * Represents dark red + */ + DARK_RED('4', 0x4) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_RED; } - - public static float[] spin(float[] c, int shift) { - return new float[]{spin(c[0], shift), spinc(c[1], shift), spinc(c[2], shift)}; + }, + /** + * Represents dark red + */ + ADAPT('4', 0x4) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_RED; } - - public static float[] spin(float[] c, int a, int b, int d) { - return new float[]{spin(c[0], a), spinc(c[1], b), spinc(c[2], d)}; + }, + /** + * Represents dark purple + */ + DARK_PURPLE('5', 0x5) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_PURPLE; } - - public static float spin(float c, int shift) { - float g = ((((int) Math.floor(c * 360)) + shift) % 360) / 360F; - return g < 0 ? 1f - g : g; + }, + /** + * Represents gold + */ + GOLD('6', 0x6) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.GOLD; } - - public static float spinc(float c, int shift) { - float g = ((((int) Math.floor(c * 255)) + shift)) / 255F; - return Math.max(0f, Math.min(g, 1f)); + }, + /** + * Represents gray + */ + GRAY('7', 0x7) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.GRAY; } - - public static java.awt.Color spin(java.awt.Color c, int h, int s, int b) { - float[] hsb = java.awt.Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null); - hsb = spin(hsb, h, s, b); - return java.awt.Color.getHSBColor(hsb[0], hsb[1], hsb[2]); + }, + /** + * Represents dark gray + */ + DARK_GRAY('8', 0x8) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.DARK_GRAY; } - - public static String spinToHex(C color, int h, int s, int b) { - return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2); + }, + /** + * Represents blue + */ + BLUE('9', 0x9) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.BLUE; } - - public static String aura(String s, int hrad, int srad, int vrad) { - return aura(s, hrad, srad, vrad, 0.3D); + }, + /** + * Represents green + */ + GREEN('a', 0xA) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.GREEN; } - - public static String aura(String s, int hrad, int srad, int vrad, double pulse) { - StringBuilder b = new StringBuilder(); - boolean c = false; - - for (char i : s.toCharArray()) { - if (c) { - c = false; - - C o = C.getByChar(i); - - if (o.isColor() && (hrad != 0 || srad != 0 || vrad != 0)) { - if (pulse > 0) { - b.append(VolmitSender.pulse(spinToHex(o, hrad, srad, vrad), spinToHex(o, -hrad, -srad, -vrad), pulse)); - } else { - b.append(""); - } - } else { - b.append(C.getByChar(i).token); - } - - continue; - } - - if (i == C.COLOR_CHAR) { - c = true; - continue; - } - - b.append(i); - } - - return b.toString(); + }, + /** + * Represents aqua + */ + AQUA('b', 0xB) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.AQUA; } - - public static String compress(String c) { - return BaseComponent.toLegacyText(TextComponent.fromLegacyText(c)); + }, + /** + * Represents red + */ + RED('c', 0xC) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.RED; } - - /** - * Gets the color represented by the specified color code - * - * @param code Code to check - * @return Associative {@link org.bukkit.ChatColor} with the given code, or null - * if it doesn't exist - */ - public static C getByChar(char code) { - try { - C c = BY_CHAR.get(code); - return c == null ? C.WHITE : c; - } catch (Exception e) { - return C.WHITE; - } + }, + /** + * Represents light purple + */ + LIGHT_PURPLE('d', 0xD) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.LIGHT_PURPLE; } - - /** - * Gets the color represented by the specified color code - * - * @param code Code to check - * @return Associative {@link org.bukkit.ChatColor} with the given code, or null - * if it doesn't exist - */ - public static C getByChar(String code) { - try { - Validate.notNull(code, "Code cannot be null"); - Validate.isTrue(code.length() > 0, "Code must have at least one char"); - - return BY_CHAR.get(code.charAt(0)); - } catch (Exception e) { - return C.WHITE; - } + }, + /** + * Represents yellow + */ + YELLOW('e', 0xE) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.YELLOW; } + }, + /** + * Represents white + */ + WHITE('f', 0xF) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.WHITE; + } + }, + /** + * Represents magical characters that change around randomly + */ + MAGIC("", 'k', 0x10, true) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.MAGIC; + } + }, + /** + * Makes the text bold. + */ + BOLD('l', 0x11, true) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.BOLD; + } + }, + /** + * Makes a line appear through the text. + */ + STRIKETHROUGH('m', 0x12, true) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.STRIKETHROUGH; + } + }, + /** + * Makes the text appear underlined. + */ + UNDERLINE("", 'n', 0x13, true) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.UNDERLINE; + } + }, + /** + * Makes the text italic. + */ + ITALIC('o', 0x14, true) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.ITALIC; + } + }, - /** - * Strips the given message of all color codes - * - * @param input String to strip of color - * @return A copy of the input string, without any coloring - */ - public static String stripColor(final String input) { - return ColorFormatter.stripColor(input); - } - - /** - * DyeColor to ChatColor - * - * @param dclr the dye color - * @return the color - */ - public static C dyeToChat(DyeColor dclr) { - if (dyeChatMap.containsKey(dclr)) { - return dyeChatMap.get(dclr); - } - - return C.MAGIC; + /** + * Resets all previous chat colors or formats. + */ + RESET('r', 0x15) { + @Override + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.RESET; + } + }, + + + ; + /** + * The special character which prefixes all chat colour codes. Use this if you + * need to dynamically convert colour codes from your custom format. + */ + public static final char COLOR_CHAR = '\u00A7'; + public final static C[] COLORCYCLE = new C[]{C.GOLD, C.YELLOW, C.GREEN, C.AQUA, C.LIGHT_PURPLE, C.AQUA, C.GREEN, C.YELLOW, C.GOLD, C.RED}; + private final static C[] COLORS = new C[]{C.BLACK, C.DARK_BLUE, C.DARK_GREEN, C.DARK_AQUA, C.DARK_RED, C.DARK_PURPLE, C.GOLD, C.GRAY, C.DARK_GRAY, C.BLUE, C.GREEN, C.AQUA, C.RED, C.LIGHT_PURPLE, C.YELLOW, C.WHITE}; + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private final static Map BY_ID = new HashMap<>(); + private final static Map BY_CHAR = new HashMap<>(); + private final static Map dyeChatMap = new HashMap<>(); + private final static Map chatHexMap = new HashMap<>(); + private final static Map dyeHexMap = new HashMap<>(); + + static { + chatHexMap.put(C.BLACK, "#000000"); + chatHexMap.put(C.DARK_BLUE, "#0000AA"); + chatHexMap.put(C.DARK_GREEN, "#00AA00"); + chatHexMap.put(C.DARK_AQUA, "#00AAAA"); + chatHexMap.put(C.DARK_RED, "#AA0000"); + chatHexMap.put(C.ADAPT, "#AA0000"); + chatHexMap.put(C.DARK_PURPLE, "#AA00AA"); + chatHexMap.put(C.GOLD, "#FFAA00"); + chatHexMap.put(C.GRAY, "#AAAAAA"); + chatHexMap.put(C.DARK_GRAY, "#555555"); + chatHexMap.put(C.BLUE, "#5555FF"); + chatHexMap.put(C.GREEN, "#55FF55"); + chatHexMap.put(C.AQUA, "#55FFFF"); + chatHexMap.put(C.RED, "#FF5555"); + chatHexMap.put(C.LIGHT_PURPLE, "#FF55FF"); + chatHexMap.put(C.YELLOW, "#FFFF55"); + chatHexMap.put(C.WHITE, "#FFFFFF"); + dyeChatMap.put(DyeColor.BLACK, C.DARK_GRAY); + dyeChatMap.put(DyeColor.BLUE, C.DARK_BLUE); + dyeChatMap.put(DyeColor.BROWN, C.GOLD); + dyeChatMap.put(DyeColor.CYAN, C.AQUA); + dyeChatMap.put(DyeColor.GRAY, C.GRAY); + dyeChatMap.put(DyeColor.GREEN, C.DARK_GREEN); + dyeChatMap.put(DyeColor.LIGHT_BLUE, C.BLUE); + dyeChatMap.put(DyeColor.LIME, C.GREEN); + dyeChatMap.put(DyeColor.MAGENTA, C.LIGHT_PURPLE); + dyeChatMap.put(DyeColor.ORANGE, C.GOLD); + dyeChatMap.put(DyeColor.PINK, C.LIGHT_PURPLE); + dyeChatMap.put(DyeColor.PURPLE, C.DARK_PURPLE); + dyeChatMap.put(DyeColor.RED, C.RED); + dyeChatMap.put(DyeColor.LIGHT_GRAY, C.GRAY); + dyeChatMap.put(DyeColor.WHITE, C.WHITE); + dyeChatMap.put(DyeColor.YELLOW, C.YELLOW); + dyeHexMap.put(DyeColor.BLACK, "#181414"); + dyeHexMap.put(DyeColor.BLUE, "#253193"); + dyeHexMap.put(DyeColor.BROWN, "#56331c"); + dyeHexMap.put(DyeColor.CYAN, "#267191"); + dyeHexMap.put(DyeColor.GRAY, "#414141"); + dyeHexMap.put(DyeColor.GREEN, "#364b18"); + dyeHexMap.put(DyeColor.LIGHT_BLUE, "#6387d2"); + dyeHexMap.put(DyeColor.LIME, "#39ba2e"); + dyeHexMap.put(DyeColor.MAGENTA, "#be49c9"); + dyeHexMap.put(DyeColor.ORANGE, "#ea7e35"); + dyeHexMap.put(DyeColor.PINK, "#d98199"); + dyeHexMap.put(DyeColor.PURPLE, "#7e34bf"); + dyeHexMap.put(DyeColor.RED, "#9e2b27"); + dyeHexMap.put(DyeColor.LIGHT_GRAY, "#a0a7a7"); + dyeHexMap.put(DyeColor.WHITE, "#a4a4a4"); + dyeHexMap.put(DyeColor.YELLOW, "#c2b51c"); + } + + static { + for (C color : values()) { + BY_ID.put(color.intCode, color); + BY_CHAR.put(color.code, color); + } + } + + private final int intCode; + private final char code; + private final String token; + private final boolean isFormat; + private final String toString; + + C(char code, int intCode) { + this("^", code, intCode, false); + } + + C(String token, char code, int intCode) { + this(token, code, intCode, false); + } + + C(char code, int intCode, boolean isFormat) { + this("^", code, intCode, isFormat); + } + + C(String token, char code, int intCode, boolean isFormat) { + this.code = code; + this.token = token.equalsIgnoreCase("^") ? "<" + name().toLowerCase(Locale.ROOT) + ">" : token; + this.intCode = intCode; + this.isFormat = isFormat; + this.toString = new String(new char[]{COLOR_CHAR, code}); + } + + public static float[] spin(float[] c, int shift) { + return new float[]{spin(c[0], shift), spinc(c[1], shift), spinc(c[2], shift)}; + } + + public static float[] spin(float[] c, int a, int b, int d) { + return new float[]{spin(c[0], a), spinc(c[1], b), spinc(c[2], d)}; + } + + public static float spin(float c, int shift) { + float g = ((((int) Math.floor(c * 360)) + shift) % 360) / 360F; + return g < 0 ? 1f - g : g; + } + + public static float spinc(float c, int shift) { + float g = ((((int) Math.floor(c * 255)) + shift)) / 255F; + return Math.max(0f, Math.min(g, 1f)); + } + + public static java.awt.Color spin(java.awt.Color c, int h, int s, int b) { + float[] hsb = java.awt.Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null); + hsb = spin(hsb, h, s, b); + return java.awt.Color.getHSBColor(hsb[0], hsb[1], hsb[2]); + } + + public static String spinToHex(C color, int h, int s, int b) { + return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2); + } + + public static String aura(String s, int hrad, int srad, int vrad) { + return aura(s, hrad, srad, vrad, 0.3D); + } + + public static String aura(String s, int hrad, int srad, int vrad, double pulse) { + StringBuilder b = new StringBuilder(); + boolean c = false; + + for (char i : s.toCharArray()) { + if (c) { + c = false; + + C o = C.getByChar(i); + + if (o.isColor() && (hrad != 0 || srad != 0 || vrad != 0)) { + if (pulse > 0) { + b.append(VolmitSender.pulse(spinToHex(o, hrad, srad, vrad), spinToHex(o, -hrad, -srad, -vrad), pulse)); + } else { + b.append(""); + } + } else { + b.append(C.getByChar(i).token); + } + + continue; + } + + if (i == C.COLOR_CHAR) { + c = true; + continue; + } + + b.append(i); + } + + return b.toString(); + } + + public static String compress(String c) { + return BaseComponent.toLegacyText(TextComponent.fromLegacyText(c)); + } + + /** + * Gets the color represented by the specified color code + * + * @param code Code to check + * @return Associative {@link org.bukkit.ChatColor} with the given code, or + * null if it doesn't exist + */ + public static C getByChar(char code) { + try { + C c = BY_CHAR.get(code); + return c == null ? C.WHITE : c; + } catch (Exception e) { + return C.WHITE; } + } - public static DyeColor chatToDye(ChatColor color) { - for (Map.Entry entry : dyeChatMap.entrySet()) { - if (entry.getValue().toString().equals(color.toString())) { - return entry.getKey(); - } - } + /** + * Gets the color represented by the specified color code + * + * @param code Code to check + * @return Associative {@link org.bukkit.ChatColor} with the given code, or + * null if it doesn't exist + */ + public static C getByChar(String code) { + try { + Validate.notNull(code, "Code cannot be null"); + Validate.isTrue(code.length() > 0, "Code must have at least one char"); - return DyeColor.BLACK; + return BY_CHAR.get(code.charAt(0)); + } catch (Exception e) { + return C.WHITE; } + } - @SuppressWarnings("unlikely-arg-type") - public static String chatToHex(C clr) { - if (chatHexMap.containsKey(clr)) { - return chatHexMap.get(clr); - } + /** + * Strips the given message of all color codes + * + * @param input String to strip of color + * @return A copy of the input string, without any coloring + */ + public static String stripColor(final String input) { + return ColorFormatter.stripColor(input); + } - return "#000000"; + /** + * DyeColor to ChatColor + * + * @param dclr the dye color + * @return the color + */ + public static C dyeToChat(DyeColor dclr) { + if (dyeChatMap.containsKey(dclr)) { + return dyeChatMap.get(dclr); } - public static String dyeToHex(DyeColor clr) { - if (dyeHexMap.containsKey(clr)) { - return dyeHexMap.get(clr); - } + return C.MAGIC; + } - return "#000000"; + public static DyeColor chatToDye(ChatColor color) { + for (Map.Entry entry : dyeChatMap.entrySet()) { + if (entry.getValue().toString().equals(color.toString())) { + return entry.getKey(); + } } - public static Color hexToColor(String hex) { - if (hex.startsWith("#")) { - hex = hex.substring(1); - } - - if (hex.contains("x")) { - hex = hex.substring(hex.indexOf("x")); - } + return DyeColor.BLACK; + } - if (hex.length() != 6 && hex.length() != 3) { - return null; - } - int sz = hex.length() / 3, mult = 1 << ((2 - sz) * 4), x = 0; + @SuppressWarnings("unlikely-arg-type") + public static String chatToHex(C clr) { + if (chatHexMap.containsKey(clr)) { + return chatHexMap.get(clr); + } - for (int i = 0, z = 0; z < hex.length(); ++i, z += sz) { - x |= (mult * Integer.parseInt(hex.substring(z, z + sz), 16)) << (i * 8); - } + return "#000000"; + } - return Color.fromBGR(x & 0xffffff); + public static String dyeToHex(DyeColor clr) { + if (dyeHexMap.containsKey(clr)) { + return dyeHexMap.get(clr); } - public static Color rgbToColor(String rgb) { - String[] parts = rgb.split("[^0-9]+"); - if (parts.length < 3) { - return null; - } - - int x = 0, i; + return "#000000"; + } - for (i = 0; i < 3; ++i) { - x |= Integer.parseInt(parts[i]) << (i * 8); - } + public static Color hexToColor(String hex) { + if (hex.startsWith("#")) { + hex = hex.substring(1); + } - return Color.fromBGR(x & 0xffffff); + if (hex.contains("x")) { + hex = hex.substring(hex.indexOf("x")); } - public static String generateColorTable() { - StringBuilder str = new StringBuilder(); + if (hex.length() != 6 && hex.length() != 3) { + return null; + } + int sz = hex.length() / 3, mult = 1 << ((2 - sz) * 4), x = 0; - str.append(""); + for (int i = 0, z = 0; z < hex.length(); ++i, z += sz) { + x |= (mult * Integer.parseInt(hex.substring(z, z + sz), 16)) << (i * 8); + } - for (Map.Entry e : chatHexMap.entrySet()) { - str.append(String.format("" + "", e.getKey().name(), e.getValue())); - } + return Color.fromBGR(x & 0xffffff); + } - str.append("
Chat ColorColor
%1$sTest String
"); - str.append(""); - for (Map.Entry e : dyeHexMap.entrySet()) { - str.append(String.format("" + "", e.getKey().name(), e.getValue())); - } + public static Color rgbToColor(String rgb) { + String[] parts = rgb.split("[^0-9]+"); + if (parts.length < 3) { + return null; + } - str.append("
Dye ColorColor
%1$sTest String
"); + int x = 0, i; - return str.toString(); + for (i = 0; i < 3; ++i) { + x |= Integer.parseInt(parts[i]) << (i * 8); } - /** - * Translates a string using an alternate color code character into a string - * that uses the internal ChatColor.COLOR_CODE color code character. The - * alternate color code character will only be replaced if it is immediately - * followed by 0-9, A-F, a-f, K-O, k-o, R or r. - * - * @param altColorChar The alternate color code character to replace. Ex: {@literal &} - * @param textToTranslate Text containing the alternate color code character. - * @return Text containing the ChatColor.COLOR_CODE color code character. - */ - public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { - return ColorFormatter.translateAlternateColorCodes(altColorChar, textToTranslate); - } + return Color.fromBGR(x & 0xffffff); + } - public static C fromItemMeta(byte c) { - for (C i : C.values()) { - if (i.getItemMeta() == c) { - return i; - } - } + public static String generateColorTable() { + StringBuilder str = new StringBuilder(); - return null; - } + str.append(""); - public static C randomColor() { - return COLORS[(int) (Math.random() * (COLORS.length - 1))]; + for (Map.Entry e : chatHexMap.entrySet()) { + str.append(String.format("" + "", e.getKey().name(), e.getValue())); } - /** - * Gets the ChatColors used at the end of the given input string. - * - * @param input Input string to retrieve the colors from. - * @return Any remaining ChatColors to pass onto the next line. - */ - public static String getLastColors(String input) { - return ColorFormatter.getLastColors(input); + str.append("
Chat ColorColor
%1$sTest String
"); + str.append(""); + for (Map.Entry e : dyeHexMap.entrySet()) { + str.append(String.format("" + "", e.getKey().name(), e.getValue())); } - public net.md_5.bungee.api.ChatColor asBungee() { - return net.md_5.bungee.api.ChatColor.RESET; - } + str.append("
Dye ColorColor
%1$sTest String
"); - /** - * Gets the char value associated with this color - * - * @return A char value of this color code - */ - public char getChar() { - return code; - } + return str.toString(); + } - @Override - public String toString() { - return intCode == -1 ? token : toString; - } - - /** - * get the dye color for the chatcolor - */ - public DyeColor dye() { - return chatToDye(chatColor()); - } - - public String hex() { - return chatToHex(this); - } - - public java.awt.Color awtColor() { - return java.awt.Color.decode(hex()); - } - - /** - * Checks if this code is a format code as opposed to a color code. - * - * @return whether this ChatColor is a format code - */ - public boolean isFormat() { - return isFormat; - } - - /** - * Checks if this code is a color code as opposed to a format code. - * - * @return whether this ChatColor is a color code - */ - public boolean isColor() { - return !isFormat && this != RESET; - } - - /** - * Get the ChatColor enum instance instead of C - */ - public ChatColor chatColor() { - return ChatColor.getByChar(code); - } - - public byte getMeta() { - return switch (this) { - case AQUA -> (byte) 11; - case BLACK -> (byte) 0; - case BLUE, DARK_AQUA -> (byte) 9; - case BOLD, UNDERLINE, STRIKETHROUGH, RESET, MAGIC, ITALIC -> (byte) -1; - case DARK_BLUE -> (byte) 1; - case DARK_GRAY -> (byte) 8; - case DARK_GREEN -> (byte) 2; - case DARK_PURPLE -> (byte) 5; - case DARK_RED -> (byte) 4; - case GOLD -> (byte) 6; - case GRAY -> (byte) 7; - case GREEN -> (byte) 10; - case LIGHT_PURPLE -> (byte) 13; - case RED -> (byte) 12; - case YELLOW -> (byte) 14; - default -> (byte) 15; - }; - } - - public byte getItemMeta() { - return switch (this) { - case AQUA, DARK_AQUA -> (byte) 9; - case BLUE -> (byte) 3; - case BOLD, UNDERLINE, RESET, STRIKETHROUGH, MAGIC, ITALIC -> (byte) -1; - case DARK_BLUE -> (byte) 11; - case DARK_GRAY -> (byte) 7; - case DARK_GREEN -> (byte) 13; - case DARK_PURPLE -> (byte) 10; - case DARK_RED, RED -> (byte) 14; - case GOLD, YELLOW -> (byte) 4; - case GRAY -> (byte) 8; - case GREEN -> (byte) 5; - case LIGHT_PURPLE -> (byte) 2; - case WHITE -> (byte) 0; - default -> (byte) 15; - }; - } + /** + * Translates a string using an alternate color code character into a string + * that uses the internal ChatColor.COLOR_CODE color code character. The + * alternate color code character will only be replaced if it is immediately + * followed by 0-9, A-F, a-f, K-O, k-o, R or r. + * + * @param altColorChar The alternate color code character to replace. Ex: + * {@literal &} + * @param textToTranslate Text containing the alternate color code character. + * @return Text containing the ChatColor.COLOR_CODE color code character. + */ + public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { + return ColorFormatter.translateAlternateColorCodes(altColorChar, textToTranslate); + } + + public static C fromItemMeta(byte c) { + for (C i : C.values()) { + if (i.getItemMeta() == c) { + return i; + } + } + + return null; + } + + public static C randomColor() { + return COLORS[(int) (Math.random() * (COLORS.length - 1))]; + } + + /** + * Gets the ChatColors used at the end of the given input string. + * + * @param input Input string to retrieve the colors from. + * @return Any remaining ChatColors to pass onto the next line. + */ + public static String getLastColors(String input) { + return ColorFormatter.getLastColors(input); + } + + public net.md_5.bungee.api.ChatColor asBungee() { + return net.md_5.bungee.api.ChatColor.RESET; + } + + /** + * Gets the char value associated with this color + * + * @return A char value of this color code + */ + public char getChar() { + return code; + } + + @Override + public String toString() { + return intCode == -1 ? token : toString; + } + + /** + * get the dye color for the chatcolor + */ + public DyeColor dye() { + return chatToDye(chatColor()); + } + + public String hex() { + return chatToHex(this); + } + + public java.awt.Color awtColor() { + return java.awt.Color.decode(hex()); + } + + /** + * Checks if this code is a format code as opposed to a color code. + * + * @return whether this ChatColor is a format code + */ + public boolean isFormat() { + return isFormat; + } + + /** + * Checks if this code is a color code as opposed to a format code. + * + * @return whether this ChatColor is a color code + */ + public boolean isColor() { + return !isFormat && this != RESET; + } + + /** + * Get the ChatColor enum instance instead of C + */ + public ChatColor chatColor() { + return ChatColor.getByChar(code); + } + + public byte getMeta() { + return switch (this) { + case AQUA -> (byte) 11; + case BLACK -> (byte) 0; + case BLUE, DARK_AQUA -> (byte) 9; + case BOLD, UNDERLINE, STRIKETHROUGH, RESET, MAGIC, ITALIC -> (byte) -1; + case DARK_BLUE -> (byte) 1; + case DARK_GRAY -> (byte) 8; + case DARK_GREEN -> (byte) 2; + case DARK_PURPLE -> (byte) 5; + case DARK_RED -> (byte) 4; + case GOLD -> (byte) 6; + case GRAY -> (byte) 7; + case GREEN -> (byte) 10; + case LIGHT_PURPLE -> (byte) 13; + case RED -> (byte) 12; + case YELLOW -> (byte) 14; + default -> (byte) 15; + }; + } + + public byte getItemMeta() { + return switch (this) { + case AQUA, DARK_AQUA -> (byte) 9; + case BLUE -> (byte) 3; + case BOLD, UNDERLINE, RESET, STRIKETHROUGH, MAGIC, ITALIC -> (byte) -1; + case DARK_BLUE -> (byte) 11; + case DARK_GRAY -> (byte) 7; + case DARK_GREEN -> (byte) 13; + case DARK_PURPLE -> (byte) 10; + case DARK_RED, RED -> (byte) 14; + case GOLD, YELLOW -> (byte) 4; + case GRAY -> (byte) 8; + case GREEN -> (byte) 5; + case LIGHT_PURPLE -> (byte) 2; + case WHITE -> (byte) 0; + default -> (byte) 15; + }; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java b/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java index 4376499b1..24191f961 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/format/HiddenStringUtils.java @@ -24,122 +24,122 @@ public class HiddenStringUtils { - // String constants. TODO Change them to something unique to avoid conflict with other plugins! - private static final String SEQUENCE_HEADER = "" + ChatColor.RESET + ChatColor.UNDERLINE + ChatColor.RESET; - private static final String SEQUENCE_FOOTER = "" + ChatColor.RESET + ChatColor.ITALIC + ChatColor.RESET; + // String constants. TODO Change them to something unique to avoid conflict with other plugins! + private static final String SEQUENCE_HEADER = "" + ChatColor.RESET + ChatColor.UNDERLINE + ChatColor.RESET; + private static final String SEQUENCE_FOOTER = "" + ChatColor.RESET + ChatColor.ITALIC + ChatColor.RESET; - public static String encodeString(String hiddenString) { - return quote(stringToColors(hiddenString)); - } + public static String encodeString(String hiddenString) { + return quote(stringToColors(hiddenString)); + } - public static boolean hasHiddenString(String input) { - if (input == null) return false; + public static boolean hasHiddenString(String input) { + if (input == null) return false; - return input.indexOf(SEQUENCE_HEADER) > -1 && input.indexOf(SEQUENCE_FOOTER) > -1; - } + return input.indexOf(SEQUENCE_HEADER) > -1 && input.indexOf(SEQUENCE_FOOTER) > -1; + } - public static String extractHiddenString(String input) { - return colorsToString(extract(input)); - } + public static String extractHiddenString(String input) { + return colorsToString(extract(input)); + } - public static String replaceHiddenString(String input, String hiddenString) { - if (input == null) return null; + public static String replaceHiddenString(String input, String hiddenString) { + if (input == null) return null; - int start = input.indexOf(SEQUENCE_HEADER); - int end = input.indexOf(SEQUENCE_FOOTER); + int start = input.indexOf(SEQUENCE_HEADER); + int end = input.indexOf(SEQUENCE_FOOTER); - if (start < 0 || end < 0) { - return null; - } - - return input.substring(0, start + SEQUENCE_HEADER.length()) + stringToColors(hiddenString) + input.substring(end); + if (start < 0 || end < 0) { + return null; } - /** - * Internal stuff. - */ - private static String quote(String input) { - if (input == null) return null; - return SEQUENCE_HEADER + input + SEQUENCE_FOOTER; - } + return input.substring(0, start + SEQUENCE_HEADER.length()) + stringToColors(hiddenString) + input.substring(end); + } - private static String extract(String input) { - if (input == null) return null; + /** + * Internal stuff. + */ + private static String quote(String input) { + if (input == null) return null; + return SEQUENCE_HEADER + input + SEQUENCE_FOOTER; + } - int start = input.indexOf(SEQUENCE_HEADER); - int end = input.indexOf(SEQUENCE_FOOTER); + private static String extract(String input) { + if (input == null) return null; - if (start < 0 || end < 0) { - return null; - } + int start = input.indexOf(SEQUENCE_HEADER); + int end = input.indexOf(SEQUENCE_FOOTER); - return input.substring(start + SEQUENCE_HEADER.length(), end); + if (start < 0 || end < 0) { + return null; } - private static String stringToColors(String normal) { - if (normal == null) return null; + return input.substring(start + SEQUENCE_HEADER.length(), end); + } - byte[] bytes = normal.getBytes(StandardCharsets.UTF_8); - char[] chars = new char[bytes.length * 4]; + private static String stringToColors(String normal) { + if (normal == null) return null; - for (int i = 0; i < bytes.length; i++) { - char[] hex = byteToHex(bytes[i]); - chars[i * 4] = ChatColor.COLOR_CHAR; - chars[i * 4 + 1] = hex[0]; - chars[i * 4 + 2] = ChatColor.COLOR_CHAR; - chars[i * 4 + 3] = hex[1]; - } + byte[] bytes = normal.getBytes(StandardCharsets.UTF_8); + char[] chars = new char[bytes.length * 4]; - return new String(chars); + for (int i = 0; i < bytes.length; i++) { + char[] hex = byteToHex(bytes[i]); + chars[i * 4] = ChatColor.COLOR_CHAR; + chars[i * 4 + 1] = hex[0]; + chars[i * 4 + 2] = ChatColor.COLOR_CHAR; + chars[i * 4 + 3] = hex[1]; } - private static String colorsToString(String colors) { - if (colors == null) return null; + return new String(chars); + } - colors = colors.toLowerCase().replace("" + ChatColor.COLOR_CHAR, ""); + private static String colorsToString(String colors) { + if (colors == null) return null; - if (colors.length() % 2 != 0) { - colors = colors.substring(0, (colors.length() / 2) * 2); - } + colors = colors.toLowerCase().replace("" + ChatColor.COLOR_CHAR, ""); - char[] chars = colors.toCharArray(); - byte[] bytes = new byte[chars.length / 2]; + if (colors.length() % 2 != 0) { + colors = colors.substring(0, (colors.length() / 2) * 2); + } - for (int i = 0; i < chars.length; i += 2) { - bytes[i / 2] = hexToByte(chars[i], chars[i + 1]); - } + char[] chars = colors.toCharArray(); + byte[] bytes = new byte[chars.length / 2]; - return new String(bytes, StandardCharsets.UTF_8); + for (int i = 0; i < chars.length; i += 2) { + bytes[i / 2] = hexToByte(chars[i], chars[i + 1]); } - private static int hexToUnsignedInt(char c) { - if (c >= '0' && c <= '9') { - return c - 48; - } else if (c >= 'a' && c <= 'f') { - return c - 87; - } else { - throw new IllegalArgumentException("Invalid hex char: out of range"); - } - } + return new String(bytes, StandardCharsets.UTF_8); + } - private static char unsignedIntToHex(int i) { - if (i >= 0 && i <= 9) { - return (char) (i + 48); - } else if (i >= 10 && i <= 15) { - return (char) (i + 87); - } else { - throw new IllegalArgumentException("Invalid hex int: out of range"); - } + private static int hexToUnsignedInt(char c) { + if (c >= '0' && c <= '9') { + return c - 48; + } else if (c >= 'a' && c <= 'f') { + return c - 87; + } else { + throw new IllegalArgumentException("Invalid hex char: out of range"); } - - private static byte hexToByte(char hex1, char hex0) { - return (byte) (((hexToUnsignedInt(hex1) << 4) | hexToUnsignedInt(hex0)) + Byte.MIN_VALUE); + } + + private static char unsignedIntToHex(int i) { + if (i >= 0 && i <= 9) { + return (char) (i + 48); + } else if (i >= 10 && i <= 15) { + return (char) (i + 87); + } else { + throw new IllegalArgumentException("Invalid hex int: out of range"); } + } - private static char[] byteToHex(byte b) { - int unsignedByte = (int) b - Byte.MIN_VALUE; - return new char[]{unsignedIntToHex((unsignedByte >> 4) & 0xf), unsignedIntToHex(unsignedByte & 0xf)}; - } + private static byte hexToByte(char hex1, char hex0) { + return (byte) (((hexToUnsignedInt(hex1) << 4) | hexToUnsignedInt(hex0)) + Byte.MIN_VALUE); + } + + private static char[] byteToHex(byte b) { + int unsignedByte = (int) b - Byte.MIN_VALUE; + return new char[]{unsignedIntToHex((unsignedByte >> 4) & 0xf), unsignedIntToHex(unsignedByte & 0xf)}; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/format/ING.java b/src/main/java/art/arcane/adapt/util/common/format/ING.java index 7b99902cb..e6c27e9d6 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/ING.java +++ b/src/main/java/art/arcane/adapt/util/common/format/ING.java @@ -17,10 +17,11 @@ -----------------------------------------------------------------------------*/ package art.arcane.adapt.util.common.format; + import art.arcane.volmlib.util.math.RNG; public class ING { - public ING(RNG rng) { + public ING(RNG rng) { - } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java index d9ff488fb..124f223a0 100644 --- a/src/main/java/art/arcane/adapt/util/common/format/Localizer.java +++ b/src/main/java/art/arcane/adapt/util/common/format/Localizer.java @@ -32,312 +32,312 @@ import java.util.Objects; public class Localizer { - private static final Object LANGUAGE_CACHE_LOCK = new Object(); - private static String cachedPrimaryLanguage; - private static JsonObject cachedPrimaryLanguageRoot; - private static String cachedFallbackLanguage; - private static JsonObject cachedFallbackLanguageRoot; - private static final String LANGUAGE_COLOR_NOTE = String.join("\n", - "# Color Codes:", - "# - Legacy format: &0..&f, &k..&o, &r (example: \"&7Gray\")", - "# - Hex format: &#RRGGBB or &x&R&R&G&G&B&B (example: \"7FFAAMint\")", - "# - MiniMessage also works (example: \"<#55FFAA>Mint\")", - "" - ); - - @SneakyThrows - public static void updateLanguageFile() { - if (AdaptConfig.get().isAutoUpdateLanguage()) { - Adapt.verbose("Attempting to update Language File"); - File langFolder = new File(Adapt.instance.getDataFolder() + "/languages"); - if (!langFolder.exists()) { - langFolder.mkdirs(); - } - - Adapt.verbose("Updating Primary Language File: " + AdaptConfig.get().getLanguage()); - syncLanguageResource(langFolder, AdaptConfig.get().getLanguage()); - Adapt.verbose("Loaded Primary Language: " + AdaptConfig.get().getLanguage()); - - if (!Objects.equals(AdaptConfig.get().getLanguage(), AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing())) { - Adapt.verbose("Updating Fallback Language File: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); - syncLanguageResource(langFolder, AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); - Adapt.verbose("Loaded Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); - } - - migrateExistingLanguageFilesToToml(); - } else { - Adapt.error("Auto Update Language is disabled, Expect Errors."); - Adapt.error("Do not disable this unless you know what you are doing, and dont expect support."); - migrateExistingLanguageFilesToToml(); - } + private static final Object LANGUAGE_CACHE_LOCK = new Object(); + private static final String LANGUAGE_COLOR_NOTE = String.join("\n", + "# Color Codes:", + "# - Legacy format: &0..&f, &k..&o, &r (example: \"&7Gray\")", + "# - Hex format: &#RRGGBB or &x&R&R&G&G&B&B (example: \"7FFAAMint\")", + "# - MiniMessage also works (example: \"<#55FFAA>Mint\")", + "" + ); + private static String cachedPrimaryLanguage; + private static JsonObject cachedPrimaryLanguageRoot; + private static String cachedFallbackLanguage; + private static JsonObject cachedFallbackLanguageRoot; + + @SneakyThrows + public static void updateLanguageFile() { + if (AdaptConfig.get().isAutoUpdateLanguage()) { + Adapt.verbose("Attempting to update Language File"); + File langFolder = new File(Adapt.instance.getDataFolder() + "/languages"); + if (!langFolder.exists()) { + langFolder.mkdirs(); + } + + Adapt.verbose("Updating Primary Language File: " + AdaptConfig.get().getLanguage()); + syncLanguageResource(langFolder, AdaptConfig.get().getLanguage()); + Adapt.verbose("Loaded Primary Language: " + AdaptConfig.get().getLanguage()); + + if (!Objects.equals(AdaptConfig.get().getLanguage(), AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing())) { + Adapt.verbose("Updating Fallback Language File: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); + syncLanguageResource(langFolder, AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); + Adapt.verbose("Loaded Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); + } + + migrateExistingLanguageFilesToToml(); + } else { + Adapt.error("Auto Update Language is disabled, Expect Errors."); + Adapt.error("Do not disable this unless you know what you are doing, and dont expect support."); + migrateExistingLanguageFilesToToml(); + } - invalidateLanguageCache(); + invalidateLanguageCache(); + } + + + @SneakyThrows + public static String dLocalize(String s1, String s2, String s3) { + return dLocalize(s1 + "." + s2 + "." + s3); + } + + @SneakyThrows + public static String dLocalize(String key, Object... params) { + String cacheKey = key; + if (!Adapt.wordKey.containsKey(cacheKey)) { + File langFolder = new File(Adapt.instance.getDataFolder(), "languages"); + String resolved = resolveLocalizedFromRoot(getPrimaryLanguageRoot(langFolder), key); + + if (resolved == null) { + updateLanguageFile(); + resolved = resolveLocalizedFromRoot(getPrimaryLanguageRoot(langFolder), key); + } + + if (resolved == null) { + Adapt.verbose("Your Language File is missing the following key: " + key); + Adapt.verbose("Loading English Language File FallBack"); + + resolved = resolveLocalizedFromRoot(getFallbackLanguageRoot(langFolder), key); + } + + if (resolved == null) { + Adapt.wordKey.put(cacheKey, key); + Adapt.error("Your Fallback Language File is missing the following key: " + key); + Adapt.verbose("New Assignement: " + key); + Adapt.error("Please report this to the developer!"); + } else { + Adapt.wordKey.put(cacheKey, resolved); + Adapt.verbose("Loaded Localization: " + resolved + " for key: " + key); + } + } + String s = applyParameters(Adapt.wordKey.get(cacheKey), params); + s = C.translateAlternateColorCodes('&', s); + if (AdaptConfig.get().isAutomaticGradients()) { + s = C.aura(s, -20, 7, 8, 0.36); } + return AdventureCompat.toLegacySection(s); + } - @SneakyThrows - public static String dLocalize(String s1, String s2, String s3) { - return dLocalize(s1 + "." + s2 + "." + s3); + private static void syncLanguageResource(File langFolder, String languageCode) throws Exception { + if (languageCode == null || languageCode.isBlank()) { + return; } - @SneakyThrows - public static String dLocalize(String key, Object... params) { - String cacheKey = key; - if (!Adapt.wordKey.containsKey(cacheKey)) { - File langFolder = new File(Adapt.instance.getDataFolder(), "languages"); - String resolved = resolveLocalizedFromRoot(getPrimaryLanguageRoot(langFolder), key); - - if (resolved == null) { - updateLanguageFile(); - resolved = resolveLocalizedFromRoot(getPrimaryLanguageRoot(langFolder), key); - } - - if (resolved == null) { - Adapt.verbose("Your Language File is missing the following key: " + key); - Adapt.verbose("Loading English Language File FallBack"); - - resolved = resolveLocalizedFromRoot(getFallbackLanguageRoot(langFolder), key); - } - - if (resolved == null) { - Adapt.wordKey.put(cacheKey, key); - Adapt.error("Your Fallback Language File is missing the following key: " + key); - Adapt.verbose("New Assignement: " + key); - Adapt.error("Please report this to the developer!"); - } else { - Adapt.wordKey.put(cacheKey, resolved); - Adapt.verbose("Loaded Localization: " + resolved + " for key: " + key); - } - } - String s = applyParameters(Adapt.wordKey.get(cacheKey), params); - s = C.translateAlternateColorCodes('&', s); - if (AdaptConfig.get().isAutomaticGradients()) { - s = C.aura(s, -20, 7, 8, 0.36); - } + String tomlResourcePath = languageCode + ".toml"; + String jsonResourcePath = languageCode + ".json"; - return AdventureCompat.toLegacySection(s); + String resourcePath = tomlResourcePath; + InputStream in = Adapt.instance.getResource(tomlResourcePath); + if (in == null) { + resourcePath = jsonResourcePath; + in = Adapt.instance.getResource(jsonResourcePath); } - private static void syncLanguageResource(File langFolder, String languageCode) throws Exception { - if (languageCode == null || languageCode.isBlank()) { - return; - } + if (in == null) { + Adapt.warn("Missing bundled language resource: " + tomlResourcePath + " (and fallback " + jsonResourcePath + ")"); + return; + } - String tomlResourcePath = languageCode + ".toml"; - String jsonResourcePath = languageCode + ".json"; + try (InputStream stream = in) { + String raw = IO.readAll(stream); + JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, new File(resourcePath)); + if (parsed == null) { + Adapt.warn("Failed to parse bundled language resource: " + resourcePath); + return; + } + + File tomlTarget = new File(langFolder, languageCode + ".toml"); + Files.deleteIfExists(tomlTarget.toPath()); + String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); + Files.writeString(tomlTarget.toPath(), applyLanguageHeader(toml)); + + File legacyJsonTarget = new File(langFolder, jsonResourcePath); + Files.deleteIfExists(legacyJsonTarget.toPath()); + } + } - String resourcePath = tomlResourcePath; - InputStream in = Adapt.instance.getResource(tomlResourcePath); - if (in == null) { - resourcePath = jsonResourcePath; - in = Adapt.instance.getResource(jsonResourcePath); - } + private static File resolveLanguageFile(File languageFolder, String languageCode) { + File toml = new File(languageFolder, languageCode + ".toml"); + if (toml.exists()) { + return toml; + } - if (in == null) { - Adapt.warn("Missing bundled language resource: " + tomlResourcePath + " (and fallback " + jsonResourcePath + ")"); - return; - } + return new File(languageFolder, languageCode + ".json"); + } - try (InputStream stream = in) { - String raw = IO.readAll(stream); - JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, new File(resourcePath)); - if (parsed == null) { - Adapt.warn("Failed to parse bundled language resource: " + resourcePath); - return; - } - - File tomlTarget = new File(langFolder, languageCode + ".toml"); - Files.deleteIfExists(tomlTarget.toPath()); - String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); - Files.writeString(tomlTarget.toPath(), applyLanguageHeader(toml)); - - File legacyJsonTarget = new File(langFolder, jsonResourcePath); - Files.deleteIfExists(legacyJsonTarget.toPath()); - } - } + private static JsonObject loadLanguageRoot(File file) { + try { + if (file == null || !file.exists() || !file.isFile()) { + return null; + } - private static File resolveLanguageFile(File languageFolder, String languageCode) { - File toml = new File(languageFolder, languageCode + ".toml"); - if (toml.exists()) { - return toml; - } + String raw = Files.readString(file.toPath()); + JsonElement root = ConfigFileSupport.parseToJsonElement(raw, file); + if (root == null || !root.isJsonObject()) { + return null; + } - return new File(languageFolder, languageCode + ".json"); + return root.getAsJsonObject(); + } catch (Throwable ignored) { + return null; } + } + + private static JsonObject getPrimaryLanguageRoot(File languageFolder) { + synchronized (LANGUAGE_CACHE_LOCK) { + String language = AdaptConfig.get().getLanguage(); + if (Objects.equals(language, cachedPrimaryLanguage) && cachedPrimaryLanguageRoot != null) { + return cachedPrimaryLanguageRoot; + } + + cachedPrimaryLanguage = language; + cachedPrimaryLanguageRoot = loadLanguageRoot(resolveLanguageFile(languageFolder, language)); + return cachedPrimaryLanguageRoot; + } + } + + private static JsonObject getFallbackLanguageRoot(File languageFolder) { + synchronized (LANGUAGE_CACHE_LOCK) { + String fallbackLanguage = AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing(); + if (Objects.equals(fallbackLanguage, cachedFallbackLanguage) && cachedFallbackLanguageRoot != null) { + return cachedFallbackLanguageRoot; + } + + cachedFallbackLanguage = fallbackLanguage; + cachedFallbackLanguageRoot = loadLanguageRoot(resolveLanguageFile(languageFolder, fallbackLanguage)); + return cachedFallbackLanguageRoot; + } + } - private static JsonObject loadLanguageRoot(File file) { - try { - if (file == null || !file.exists() || !file.isFile()) { - return null; - } - - String raw = Files.readString(file.toPath()); - JsonElement root = ConfigFileSupport.parseToJsonElement(raw, file); - if (root == null || !root.isJsonObject()) { - return null; - } - - return root.getAsJsonObject(); - } catch (Throwable ignored) { - return null; - } + private static String resolveLocalizedFromRoot(JsonObject root, String key) { + if (root == null) { + return null; } + return resolveLocalizedElementValue(resolveLocalizedElement(root, key)); + } + + private static void invalidateLanguageCache() { + synchronized (LANGUAGE_CACHE_LOCK) { + cachedPrimaryLanguage = null; + cachedPrimaryLanguageRoot = null; + cachedFallbackLanguage = null; + cachedFallbackLanguageRoot = null; + } + Adapt.wordKey.clear(); + } - private static JsonObject getPrimaryLanguageRoot(File languageFolder) { - synchronized (LANGUAGE_CACHE_LOCK) { - String language = AdaptConfig.get().getLanguage(); - if (Objects.equals(language, cachedPrimaryLanguage) && cachedPrimaryLanguageRoot != null) { - return cachedPrimaryLanguageRoot; - } + private static JsonElement resolveLocalizedElement(JsonObject root, String key) { + JsonObject current = root; + JsonElement element = null; - cachedPrimaryLanguage = language; - cachedPrimaryLanguageRoot = loadLanguageRoot(resolveLanguageFile(languageFolder, language)); - return cachedPrimaryLanguageRoot; - } - } + for (String path : key.split("\\.")) { + if (current == null || !current.has(path)) { + return null; + } - private static JsonObject getFallbackLanguageRoot(File languageFolder) { - synchronized (LANGUAGE_CACHE_LOCK) { - String fallbackLanguage = AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing(); - if (Objects.equals(fallbackLanguage, cachedFallbackLanguage) && cachedFallbackLanguageRoot != null) { - return cachedFallbackLanguageRoot; - } + element = current.get(path); + if (element == null || element.isJsonNull()) { + return null; + } - cachedFallbackLanguage = fallbackLanguage; - cachedFallbackLanguageRoot = loadLanguageRoot(resolveLanguageFile(languageFolder, fallbackLanguage)); - return cachedFallbackLanguageRoot; - } + if (element.isJsonObject()) { + current = element.getAsJsonObject(); + } else { + current = null; + } } - private static String resolveLocalizedFromRoot(JsonObject root, String key) { - if (root == null) { - return null; - } - return resolveLocalizedElementValue(resolveLocalizedElement(root, key)); - } + return element; + } - private static void invalidateLanguageCache() { - synchronized (LANGUAGE_CACHE_LOCK) { - cachedPrimaryLanguage = null; - cachedPrimaryLanguageRoot = null; - cachedFallbackLanguage = null; - cachedFallbackLanguageRoot = null; - } - Adapt.wordKey.clear(); + private static String resolveLocalizedElementValue(JsonElement element) { + if (element == null || element.isJsonNull()) { + return null; } - private static JsonElement resolveLocalizedElement(JsonObject root, String key) { - JsonObject current = root; - JsonElement element = null; - - for (String path : key.split("\\.")) { - if (current == null || !current.has(path)) { - return null; - } - - element = current.get(path); - if (element == null || element.isJsonNull()) { - return null; - } - - if (element.isJsonObject()) { - current = element.getAsJsonObject(); - } else { - current = null; - } - } - - return element; + if (element.isJsonPrimitive()) { + return element.getAsString(); } - private static String resolveLocalizedElementValue(JsonElement element) { - if (element == null || element.isJsonNull()) { - return null; + if (element.isJsonArray()) { + StringBuilder result = new StringBuilder(); + for (JsonElement value : element.getAsJsonArray()) { + if (!value.isJsonPrimitive()) { + continue; } - if (element.isJsonPrimitive()) { - return element.getAsString(); + if (result.length() > 0) { + result.append('\n'); } - if (element.isJsonArray()) { - StringBuilder result = new StringBuilder(); - for (JsonElement value : element.getAsJsonArray()) { - if (!value.isJsonPrimitive()) { - continue; - } + result.append(value.getAsString()); + } - if (result.length() > 0) { - result.append('\n'); - } + return result.toString(); + } - result.append(value.getAsString()); - } + return null; + } - return result.toString(); - } + private static String applyParameters(String value, Object... params) { + if (value == null || params == null || params.length == 0) { + return value; + } - return null; + String result = value; + for (int i = 0; i < params.length; i++) { + result = result.replace("{" + i + "}", String.valueOf(params[i])); } - private static String applyParameters(String value, Object... params) { - if (value == null || params == null || params.length == 0) { - return value; - } + return result; + } + + private static void migrateExistingLanguageFilesToToml() { + try { + File languageFolder = new File(Adapt.instance.getDataFolder(), "languages"); + if (!languageFolder.exists() || !languageFolder.isDirectory()) { + return; + } - String result = value; - for (int i = 0; i < params.length; i++) { - result = result.replace("{" + i + "}", String.valueOf(params[i])); + File[] files = languageFolder.listFiles((dir, name) -> name.toLowerCase().endsWith(".json")); + if (files == null || files.length == 0) { + return; + } + + for (File jsonFile : files) { + if (jsonFile == null || !jsonFile.exists() || !jsonFile.isFile()) { + continue; } - return result; - } + File tomlFile = ConfigFileSupport.toTomlFile(jsonFile); + if (tomlFile.exists() && tomlFile.isFile()) { + Files.deleteIfExists(jsonFile.toPath()); + continue; + } - private static void migrateExistingLanguageFilesToToml() { - try { - File languageFolder = new File(Adapt.instance.getDataFolder(), "languages"); - if (!languageFolder.exists() || !languageFolder.isDirectory()) { - return; - } - - File[] files = languageFolder.listFiles((dir, name) -> name.toLowerCase().endsWith(".json")); - if (files == null || files.length == 0) { - return; - } - - for (File jsonFile : files) { - if (jsonFile == null || !jsonFile.exists() || !jsonFile.isFile()) { - continue; - } - - File tomlFile = ConfigFileSupport.toTomlFile(jsonFile); - if (tomlFile.exists() && tomlFile.isFile()) { - Files.deleteIfExists(jsonFile.toPath()); - continue; - } - - String raw = Files.readString(jsonFile.toPath()); - JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, jsonFile); - if (parsed == null) { - continue; - } - - String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); - Files.writeString(tomlFile.toPath(), applyLanguageHeader(toml)); - Adapt.info("Migrated legacy language file [" + jsonFile.getName() + "] -> [" + tomlFile.getName() + "]."); - Files.deleteIfExists(jsonFile.toPath()); - } - invalidateLanguageCache(); - } catch (Throwable e) { - Adapt.warn("Failed to migrate legacy language json files: " + e.getMessage()); + String raw = Files.readString(jsonFile.toPath()); + JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, jsonFile); + if (parsed == null) { + continue; } + + String toml = ConfigFileSupport.serializeJsonElementToToml(parsed); + Files.writeString(tomlFile.toPath(), applyLanguageHeader(toml)); + Adapt.info("Migrated legacy language file [" + jsonFile.getName() + "] -> [" + tomlFile.getName() + "]."); + Files.deleteIfExists(jsonFile.toPath()); + } + invalidateLanguageCache(); + } catch (Throwable e) { + Adapt.warn("Failed to migrate legacy language json files: " + e.getMessage()); } + } - private static String applyLanguageHeader(String body) { - String source = body == null ? "" : body; - String trimmed = source.stripLeading(); - if (trimmed.startsWith("# Color Codes:")) { - return source; - } - return LANGUAGE_COLOR_NOTE + source; + private static String applyLanguageHeader(String body) { + String source = body == null ? "" : body; + String trimmed = source.stripLeading(); + if (trimmed.startsWith("# Color Codes:")) { + return source; } + return LANGUAGE_COLOR_NOTE + source; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java b/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java index 037964667..65afb8661 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java +++ b/src/main/java/art/arcane/adapt/util/common/function/CallbackCV.java @@ -19,5 +19,5 @@ package art.arcane.adapt.util.common.function; public interface CallbackCV { - void run(T t); + void run(T t); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java index f660c3786..549c9f8d1 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer2.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer2 { - void accept(A a, B b); + void accept(A a, B b); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java index 946b75730..4d89668a8 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer4.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer4 { - void accept(A a, B b, C c, D d); + void accept(A a, B b, C c, D d); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java index 15560ced3..b137cb0a7 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer5.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer5 { - void accept(A a, B b, C c, D d, E e); + void accept(A a, B b, C c, D d, E e); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java index 4887e929b..f56bd9cb5 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer6.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer6 { - void accept(A a, B b, C c, D d, E e, F f); + void accept(A a, B b, C c, D d, E e, F f); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java index d57eb0414..5efce4f97 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer7.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer7 { - void accept(A a, B b, C c, D d, E e, F f, G g); + void accept(A a, B b, C c, D d, E e, F f, G g); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java index 71527d8f7..c9a11cf72 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Consumer8.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Consumer8 { - void accept(A a, B b, C c, D d, E e, F f, G g, H h); + void accept(A a, B b, C c, D d, E e, F f, G g, H h); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function2.java b/src/main/java/art/arcane/adapt/util/common/function/Function2.java index 3dd9f501a..790cde9c7 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Function2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function2.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Function2 { - R apply(A a, B b); + R apply(A a, B b); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Function4.java b/src/main/java/art/arcane/adapt/util/common/function/Function4.java index dd0e3f0ce..f5d932621 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Function4.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Function4.java @@ -22,5 +22,5 @@ @SuppressWarnings("hiding") @FunctionalInterface public interface Function4 { - R apply(A a, B b, C c, D d); + R apply(A a, B b, C c, D d); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java b/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java index d3201ec61..6aa96c4d0 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NoiseInjector.java @@ -20,5 +20,5 @@ @FunctionalInterface public interface NoiseInjector { - double[] combine(double src, double value); + double[] combine(double src, double value); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java b/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java index fee748568..e1bcb7beb 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java +++ b/src/main/java/art/arcane/adapt/util/common/function/NoiseProvider.java @@ -20,5 +20,5 @@ @FunctionalInterface public interface NoiseProvider { - double noise(double x, double z); + double noise(double x, double z); } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java b/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java index 7dfcc4048..2ff132dff 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Supplier2.java @@ -19,5 +19,5 @@ package art.arcane.adapt.util.common.function; public interface Supplier2 { - void get(T t, TT tt); + void get(T t, TT tt); } diff --git a/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java b/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java index b5051c6ef..c25ad1084 100644 --- a/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java +++ b/src/main/java/art/arcane/adapt/util/common/function/Supplier3.java @@ -19,5 +19,5 @@ package art.arcane.adapt.util.common.function; public interface Supplier3 { - void get(T t, TT tt, TTT ttt); + void get(T t, TT tt, TTT ttt); } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index edac0d026..b0a4ac5dd 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -6,62 +6,61 @@ import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.volmlib.util.inventorygui.UIElement; import art.arcane.volmlib.util.inventorygui.UIWindow; -import art.arcane.volmlib.util.inventorygui.Window; import org.bukkit.Material; import org.bukkit.entity.Player; public final class GuiConfirm { - private GuiConfirm() { - } + private GuiConfirm() { + } - public static void open( - Player player, - String title, - String message, - Runnable onConfirm, - Runnable onCancel - ) { - if (player == null) { - return; - } + public static void open( + Player player, + String title, + String message, + Runnable onConfirm, + Runnable onCancel + ) { + if (player == null) { + return; + } - if (!J.isPrimaryThread()) { - J.s(() -> open(player, title, message, onConfirm, onCancel)); - return; - } + if (!J.isPrimaryThread()) { + J.s(() -> open(player, title, message, onConfirm, onCancel)); + return; + } - UIWindow w = new UIWindow(Adapt.instance, player); - GuiTheme.apply(w, "confirm"); - w.setViewportHeight(3); + UIWindow w = new UIWindow(Adapt.instance, player); + GuiTheme.apply(w, "confirm"); + w.setViewportHeight(3); - w.setElement(0, 0, new UIElement("confirm-msg") - .setMaterial(new MaterialBlock(Material.PAPER)) - .setName(C.WHITE + (title == null ? "Confirm" : title)) - .addLore(C.GRAY + (message == null ? "Apply this change?" : message))); + w.setElement(0, 0, new UIElement("confirm-msg") + .setMaterial(new MaterialBlock(Material.PAPER)) + .setName(C.WHITE + (title == null ? "Confirm" : title)) + .addLore(C.GRAY + (message == null ? "Apply this change?" : message))); - w.setElement(-2, 1, new UIElement("confirm-yes") - .setMaterial(new MaterialBlock(Material.LIME_STAINED_GLASS_PANE)) - .setName(C.GREEN + "Confirm") - .onLeftClick((e) -> { - w.close(); - if (onConfirm != null) { - onConfirm.run(); - } - })); + w.setElement(-2, 1, new UIElement("confirm-yes") + .setMaterial(new MaterialBlock(Material.LIME_STAINED_GLASS_PANE)) + .setName(C.GREEN + "Confirm") + .onLeftClick((e) -> { + w.close(); + if (onConfirm != null) { + onConfirm.run(); + } + })); - w.setElement(2, 1, new UIElement("confirm-no") - .setMaterial(new MaterialBlock(Material.RED_STAINED_GLASS_PANE)) - .setName(C.RED + "Cancel") - .onLeftClick((e) -> { - w.close(); - if (onCancel != null) { - onCancel.run(); - } - })); + w.setElement(2, 1, new UIElement("confirm-no") + .setMaterial(new MaterialBlock(Material.RED_STAINED_GLASS_PANE)) + .setName(C.RED + "Cancel") + .onLeftClick((e) -> { + w.close(); + if (onCancel != null) { + onCancel.run(); + } + })); - w.setTitle(C.GRAY + "Confirm"); - w.onClosed((window) -> Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString())); - w.open(); - Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); - } + w.setTitle(C.GRAY + "Confirm"); + w.onClosed((window) -> Adapt.instance.getGuiLeftovers().remove(player.getUniqueId().toString())); + w.open(); + Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java index e869614ed..6cfee1917 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiEffects.java @@ -6,22 +6,22 @@ import java.util.List; public final class GuiEffects { - private GuiEffects() { - } - - public static void applyReveal(Window window, List placements) { - if (window == null || placements == null || placements.isEmpty()) { - return; - } + private GuiEffects() { + } - for (Placement placement : placements) { - if (placement == null || placement.element() == null) { - continue; - } - window.setElement(placement.position(), placement.row(), placement.element()); - } + public static void applyReveal(Window window, List placements) { + if (window == null || placements == null || placements.isEmpty()) { + return; } - public record Placement(int position, int row, Element element) { + for (Placement placement : placements) { + if (placement == null || placement.element() == null) { + continue; + } + window.setElement(placement.position(), placement.row(), placement.element()); } + } + + public record Placement(int position, int row, Element element) { + } } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java index 7ea1716da..f89b54689 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiLayout.java @@ -1,66 +1,66 @@ package art.arcane.adapt.util.common.inventorygui; public final class GuiLayout { - public static final int WIDTH = 9; - public static final int MAX_ROWS = 6; + public static final int WIDTH = 9; + public static final int MAX_ROWS = 6; - private GuiLayout() { - } + private GuiLayout() { + } - public static PagePlan plan(int totalItems, boolean reserveNavigationRow) { - int items = Math.max(0, totalItems); + public static PagePlan plan(int totalItems, boolean reserveNavigationRow) { + int items = Math.max(0, totalItems); - boolean navigation = reserveNavigationRow; - int maxContentRows = MAX_ROWS - (navigation ? 1 : 0); - if (maxContentRows < 1) { - maxContentRows = 1; - } + boolean navigation = reserveNavigationRow; + int maxContentRows = MAX_ROWS - (navigation ? 1 : 0); + if (maxContentRows < 1) { + maxContentRows = 1; + } - if (items > maxContentRows * WIDTH) { - navigation = true; - maxContentRows = MAX_ROWS - 1; - } + if (items > maxContentRows * WIDTH) { + navigation = true; + maxContentRows = MAX_ROWS - 1; + } - int contentRows; - if (items <= 0) { - contentRows = 1; - } else if (items > maxContentRows * WIDTH) { - contentRows = maxContentRows; - } else { - contentRows = (int) Math.ceil(items / (double) WIDTH); - } + int contentRows; + if (items <= 0) { + contentRows = 1; + } else if (items > maxContentRows * WIDTH) { + contentRows = maxContentRows; + } else { + contentRows = (int) Math.ceil(items / (double) WIDTH); + } - contentRows = Math.max(1, Math.min(maxContentRows, contentRows)); - int rows = contentRows + (navigation ? 1 : 0); - rows = Math.max(1, Math.min(MAX_ROWS, rows)); + contentRows = Math.max(1, Math.min(maxContentRows, contentRows)); + int rows = contentRows + (navigation ? 1 : 0); + rows = Math.max(1, Math.min(MAX_ROWS, rows)); - int itemsPerPage = contentRows * WIDTH; - itemsPerPage = Math.max(WIDTH, itemsPerPage); - int pages = Math.max(1, (int) Math.ceil(items / (double) itemsPerPage)); + int itemsPerPage = contentRows * WIDTH; + itemsPerPage = Math.max(WIDTH, itemsPerPage); + int pages = Math.max(1, (int) Math.ceil(items / (double) itemsPerPage)); - return new PagePlan(rows, contentRows, navigation, itemsPerPage, pages); - } + return new PagePlan(rows, contentRows, navigation, itemsPerPage, pages); + } - public static int clampPage(int page, int pageCount) { - if (pageCount <= 0) { - return 0; - } - return Math.max(0, Math.min(pageCount - 1, page)); + public static int clampPage(int page, int pageCount) { + if (pageCount <= 0) { + return 0; } + return Math.max(0, Math.min(pageCount - 1, page)); + } - public static int centeredPosition(int indexInRow, int rowCount) { - int count = Math.max(1, Math.min(WIDTH, rowCount)); - int index = Math.max(0, Math.min(count - 1, indexInRow)); - int start = -(count / 2); - return start + index; - } + public static int centeredPosition(int indexInRow, int rowCount) { + int count = Math.max(1, Math.min(WIDTH, rowCount)); + int index = Math.max(0, Math.min(count - 1, indexInRow)); + int start = -(count / 2); + return start + index; + } - public record PagePlan( - int rows, - int contentRows, - boolean hasNavigationRow, - int itemsPerPage, - int pageCount - ) { - } + public record PagePlan( + int rows, + int contentRows, + boolean hasNavigationRow, + int itemsPerPage, + int pageCount + ) { + } } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java index 01336e2f7..1a50fa465 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiTheme.java @@ -2,40 +2,39 @@ import art.arcane.volmlib.util.data.MaterialBlock; import art.arcane.volmlib.util.inventorygui.UIElement; -import art.arcane.volmlib.util.inventorygui.UIWindow; import art.arcane.volmlib.util.inventorygui.Window; import art.arcane.volmlib.util.inventorygui.WindowResolution; import org.bukkit.Material; public final class GuiTheme { - private GuiTheme() { - } - - public static void apply(Window window, String tag) { - if (window == null) { - return; - } + private GuiTheme() { + } - window.setResolution(WindowResolution.W9_H6); - window.setViewportHeight(WindowResolution.W9_H6.getMaxHeight()); - if (tag != null) { - window.setTag(tag); - } + public static void apply(Window window, String tag) { + if (window == null) { + return; + } - window.setDecorator((w, position, row) -> new UIElement("bg-" + row + "-" + position) - .setName(" ") - .setMaterial(new MaterialBlock(background(row, position)))); + window.setResolution(WindowResolution.W9_H6); + window.setViewportHeight(WindowResolution.W9_H6.getMaxHeight()); + if (tag != null) { + window.setTag(tag); } - public static Material background(int row, int position) { - if (row == 0) { - return position % 2 == 0 ? Material.GRAY_STAINED_GLASS_PANE : Material.LIGHT_GRAY_STAINED_GLASS_PANE; - } + window.setDecorator((w, position, row) -> new UIElement("bg-" + row + "-" + position) + .setName(" ") + .setMaterial(new MaterialBlock(background(row, position)))); + } - if (row == 1) { - return Material.BLACK_STAINED_GLASS_PANE; - } + public static Material background(int row, int position) { + if (row == 0) { + return position % 2 == 0 ? Material.GRAY_STAINED_GLASS_PANE : Material.LIGHT_GRAY_STAINED_GLASS_PANE; + } - return position % 2 == 0 ? Material.BLACK_STAINED_GLASS_PANE : Material.GRAY_STAINED_GLASS_PANE; + if (row == 1) { + return Material.BLACK_STAINED_GLASS_PANE; } + + return position % 2 == 0 ? Material.BLACK_STAINED_GLASS_PANE : Material.GRAY_STAINED_GLASS_PANE; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java index 524ed426b..caf46093a 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Inventories.java @@ -31,76 +31,76 @@ * @author cyberpwn */ public class Inventories { - /** - * Does the inventory have any space (empty slots) - * - * @param i the inventory - * @return true if it has at least one slot empty - */ - public static boolean hasSpace(Inventory i) { - return new PhantomInventory(i).hasSpace(); - } - - /** - * Does the inventory have a given amount of empty space (or more) - * - * @param i the inventory - * @param slots the slots needed empty - * @return true if it has more than or enough empty slots - */ - public static boolean hasSpace(Inventory i, int slots) { - int ex = 0; + /** + * Does the inventory have any space (empty slots) + * + * @param i the inventory + * @return true if it has at least one slot empty + */ + public static boolean hasSpace(Inventory i) { + return new PhantomInventory(i).hasSpace(); + } - ItemStack[] vv = i.getContents(); + /** + * Does the inventory have a given amount of empty space (or more) + * + * @param i the inventory + * @param slots the slots needed empty + * @return true if it has more than or enough empty slots + */ + public static boolean hasSpace(Inventory i, int slots) { + int ex = 0; - for (ItemStack itemStack : vv) { - if (itemStack == null || itemStack.getType().equals(Material.AIR)) { - ex++; - } - } + ItemStack[] vv = i.getContents(); - return ex >= slots; + for (ItemStack itemStack : vv) { + if (itemStack == null || itemStack.getType().equals(Material.AIR)) { + ex++; + } } - /** - * Get the ACTUAL contents in this inventory. Meaning no elements in the - * list are null, or just plain air - * - * @param i the inventory - * @return the ACTUAL contents - */ - public static List getActualContents(Inventory i) { - List actualItems = new ArrayList<>(); + return ex >= slots; + } - for (ItemStack j : i.getContents()) { - if (Items.is(j)) { - actualItems.add(j); - } - } + /** + * Get the ACTUAL contents in this inventory. Meaning no elements in the list + * are null, or just plain air + * + * @param i the inventory + * @return the ACTUAL contents + */ + public static List getActualContents(Inventory i) { + List actualItems = new ArrayList<>(); - return actualItems; + for (ItemStack j : i.getContents()) { + if (Items.is(j)) { + actualItems.add(j); + } } - /** - * Does the inventory have space for the given item - * - * @param i the inventory - * @param item the item - * @return returns true if either there is enough empty slots to fill it - * (amt / maxStackSize) OR the item can be merged with an existing - * item, else false. - */ - public static boolean hasSpace(Inventory i, ItemStack item) { - if (hasSpace(i, item.getAmount() / item.getMaxStackSize())) { - return true; - } else { - for (ItemStack j : getActualContents(i)) { - if (Items.isMergable(j, item)) { - return true; - } - } - } + return actualItems; + } - return false; + /** + * Does the inventory have space for the given item + * + * @param i the inventory + * @param item the item + * @return returns true if either there is enough empty slots to fill it (amt + * / maxStackSize) OR the item can be merged with an existing item, else + * false. + */ + public static boolean hasSpace(Inventory i, ItemStack item) { + if (hasSpace(i, item.getAmount() / item.getMaxStackSize())) { + return true; + } else { + for (ItemStack j : getActualContents(i)) { + if (Items.isMergable(j, item)) { + return true; + } + } } + + return false; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java index d17b1a2a6..f107fc49c 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/Items.java @@ -31,328 +31,328 @@ * @author cyberpwn */ public class Items { - /** - * Is the item an item (not null or air) - * - * @param is the item - * @return true if it is - */ - public static boolean is(ItemStack is) { - return is != null && !is.getType().equals(Material.AIR); + /** + * Is the item an item (not null or air) + * + * @param is the item + * @return true if it is + */ + public static boolean is(ItemStack is) { + return is != null && !is.getType().equals(Material.AIR); + } + + /** + * Is the item a certain material + * + * @param is the item + * @param material the material + * @return true if it is + */ + public static boolean is(ItemStack is, Material material) { + return is(is) && is.getType().equals(material); + } + + /** + * Is the item a certain material and metadata + * + * @param is the item + * @param mb the materialblock + * @return true if it is + */ + @SuppressWarnings("deprecation") + public static boolean is(ItemStack is, MaterialBlock mb) { + return is(is, mb.getMaterial()) && is.getData().getData() == mb.getData(); + } + + /** + * Is the item a given material and data + * + * @param is the item + * @param material the material + * @param data the data + * @return true if it is + */ + public static boolean is(ItemStack is, Material material, byte data) { + return is(is, new MaterialBlock(material, data)); + } + + /** + * Is the item a given material and data + * + * @param is the item + * @param material the material + * @param data the data + * @return true if it is + */ + public static boolean is(ItemStack is, Material material, int data) { + return is(is, material, (byte) data); + } + + /** + * Does the item have meta + * + * @param is the item + * @return true if it does + */ + public static boolean hasMeta(ItemStack is) { + return is(is) && is.hasItemMeta(); + } + + /** + * Does the item have a custom name + * + * @param is the item + * @return true if it has a name + */ + public static boolean hasName(ItemStack is) { + return hasMeta(is) && is.getItemMeta().hasDisplayName(); + } + + /** + * Does the item have any lore? + * + * @param is the item + * @return true if it does + */ + public static boolean hasLore(ItemStack is) { + return hasMeta(is) && is.getItemMeta().hasLore(); + } + + /** + * Does the item have the given name (color matters) + * + * @param is the item + * @param name the name + * @return true if it has the name + */ + public static boolean hasName(ItemStack is, String name) { + return hasName(is) && is.getItemMeta().getDisplayName().equals(name); + } + + + /** + * Does the item have the given enchantment + * + * @param is the item + * @param e the enchantment + * @return true if it does + */ + public static boolean hasEnchantment(ItemStack is, Enchantment e) { + return is(is) && is.getEnchantments().containsKey(e); + } + + /** + * Does the item have the enchantment at the given level + * + * @param is the item + * @param e the enchantment + * @param level the level + * @return true if it does + */ + public static boolean hasEnchantment(ItemStack is, Enchantment e, int level) { + if (!is(is)) { + return false; } - /** - * Is the item a certain material - * - * @param is the item - * @param material the material - * @return true if it is - */ - public static boolean is(ItemStack is, Material material) { - return is(is) && is.getType().equals(material); + return hasEnchantment(is, e) && is.getEnchantmentLevel(e) == level; + } + + /** + * Does the item have any enchantments + * + * @param is the item + * @return true if it does + */ + public static boolean hasEnchantments(ItemStack is) { + if (!is(is)) { + return false; } - /** - * Is the item a certain material and metadata - * - * @param is the item - * @param mb the materialblock - * @return true if it is - */ - @SuppressWarnings("deprecation") - public static boolean is(ItemStack is, MaterialBlock mb) { - return is(is, mb.getMaterial()) && is.getData().getData() == mb.getData(); + return !is.getEnchantments().isEmpty(); + } + + /** + * Get a materialblock representation of this item + * + * @param is the item + * @return the materialblock or null if the item is null + */ + @SuppressWarnings("deprecation") + public static MaterialBlock toMaterialBlock(ItemStack is) { + if (is != null) { + return new MaterialBlock(is.getType(), is.getData().getData()); } - /** - * Is the item a given material and data - * - * @param is the item - * @param material the material - * @param data the data - * @return true if it is - */ - public static boolean is(ItemStack is, Material material, byte data) { - return is(is, new MaterialBlock(material, data)); + return null; + } + + /** + * Should the itemstack be broken? + * + * @param is the itemStack + * @return true if it should be broken + */ + public static boolean isBroken(ItemStack is) { + return is(is) && getMaxDurability(is) == getDurability(is) && hasDurability(is); + } + + /** + * Does this item have durability + * + * @param is the item + * @return true if it does + */ + public static boolean hasDurability(ItemStack is) { + return is(is) && getMaxDurability(is) > 0; + } + + /** + * Get the durability percent + * + * @param is the itemstack + * @return the percent + */ + public static double getDurabilityPercent(ItemStack is) { + if (!is(is)) { + return 0.0; } - /** - * Is the item a given material and data - * - * @param is the item - * @param material the material - * @param data the data - * @return true if it is - */ - public static boolean is(ItemStack is, Material material, int data) { - return is(is, material, (byte) data); + if (getMaxDurability(is) == 0) { + return 1.0; } - /** - * Does the item have meta - * - * @param is the item - * @return true if it does - */ - public static boolean hasMeta(ItemStack is) { - return is(is) && is.hasItemMeta(); - } - - /** - * Does the item have a custom name - * - * @param is the item - * @return true if it has a name - */ - public static boolean hasName(ItemStack is) { - return hasMeta(is) && is.getItemMeta().hasDisplayName(); - } - - /** - * Does the item have any lore? - * - * @param is the item - * @return true if it does - */ - public static boolean hasLore(ItemStack is) { - return hasMeta(is) && is.getItemMeta().hasLore(); - } - - /** - * Does the item have the given name (color matters) - * - * @param is the item - * @param name the name - * @return true if it has the name - */ - public static boolean hasName(ItemStack is, String name) { - return hasName(is) && is.getItemMeta().getDisplayName().equals(name); + return 1.0 - ((double) getDurability(is) / (double) getMaxDurability(is)); + } + + /** + * Set the durability percent + * + * @param is the itemStack + * @param pc the percent + */ + public static void setDurabilityPercent(ItemStack is, double pc) { + if (!is(is)) { + return; } + pc = (pc > 1.0 ? 1.0 : (pc < 0.0 ? 0.0 : pc)); - /** - * Does the item have the given enchantment - * - * @param is the item - * @param e the enchantment - * @return true if it does - */ - public static boolean hasEnchantment(ItemStack is, Enchantment e) { - return is(is) && is.getEnchantments().containsKey(e); + if (getDurability(is) == 0) { + return; } - /** - * Does the item have the enchantment at the given level - * - * @param is the item - * @param e the enchantment - * @param level the level - * @return true if it does - */ - public static boolean hasEnchantment(ItemStack is, Enchantment e, int level) { - if (!is(is)) { - return false; - } - - return hasEnchantment(is, e) && is.getEnchantmentLevel(e) == level; - } - - /** - * Does the item have any enchantments - * - * @param is the item - * @return true if it does - */ - public static boolean hasEnchantments(ItemStack is) { - if (!is(is)) { - return false; - } - - return !is.getEnchantments().isEmpty(); + setDurability(is, (int) ((double) getMaxDurability(is) * (1.0 - pc))); + } + + /** + * Get the max durability + * + * @param is the item + * @return the item type's max durability + */ + public static short getMaxDurability(ItemStack is) { + if (!is(is)) { + return 0; } - /** - * Get a materialblock representation of this item - * - * @param is the item - * @return the materialblock or null if the item is null - */ - @SuppressWarnings("deprecation") - public static MaterialBlock toMaterialBlock(ItemStack is) { - if (is != null) { - return new MaterialBlock(is.getType(), is.getData().getData()); - } - - return null; + return is.getType().getMaxDurability(); + } + + /** + * Get the durability + * + * @param is the item + * @return the item durability + */ + public static short getDurability(ItemStack is) { + if (!is(is)) { + return 0; } - /** - * Should the itemstack be broken? - * - * @param is the itemStack - * @return true if it should be broken - */ - public static boolean isBroken(ItemStack is) { - return is(is) && getMaxDurability(is) == getDurability(is) && hasDurability(is); + return is.getDurability(); + } + + /** + * Set the durability (if higher than max, it will be set to the max) + * + * @param is the item + * @param dmg the durability + */ + public static void setDurability(ItemStack is, short dmg) { + if (!is(is)) { + return; } - /** - * Does this item have durability - * - * @param is the item - * @return true if it does - */ - public static boolean hasDurability(ItemStack is) { - return is(is) && getMaxDurability(is) > 0; + is.setDurability(dmg > getMaxDurability(is) ? getMaxDurability(is) : dmg); + } + + /** + * Set the durability (if higher than max, it will be set to the max) + * + * @param is the item + * @param dmg the durability + */ + public static void setDurability(ItemStack is, int dmg) { + if (!is(is)) { + return; } - /** - * Get the durability percent - * - * @param is the itemstack - * @return the percent - */ - public static double getDurabilityPercent(ItemStack is) { - if (!is(is)) { - return 0.0; - } - - if (getMaxDurability(is) == 0) { - return 1.0; - } - - return 1.0 - ((double) getDurability(is) / (double) getMaxDurability(is)); + setDurability(is, (short) dmg); + } + + /** + * Damage an itemstack + * + * @param is the item + * @param amt the amount to damage + */ + public static void damage(ItemStack is, int amt) { + if (!is(is)) { + return; } - /** - * Set the durability percent - * - * @param is the itemStack - * @param pc the percent - */ - public static void setDurabilityPercent(ItemStack is, double pc) { - if (!is(is)) { - return; - } - - pc = (pc > 1.0 ? 1.0 : (pc < 0.0 ? 0.0 : pc)); - - if (getDurability(is) == 0) { - return; - } - - setDurability(is, (int) ((double) getMaxDurability(is) * (1.0 - pc))); - } - - /** - * Get the max durability - * - * @param is the item - * @return the item type's max durability - */ - public static short getMaxDurability(ItemStack is) { - if (!is(is)) { - return 0; - } - - return is.getType().getMaxDurability(); - } - - /** - * Get the durability - * - * @param is the item - * @return the item durability - */ - public static short getDurability(ItemStack is) { - if (!is(is)) { - return 0; - } + setDurability(is, getDurability(is) + amt); + } + + /** + * Can the item a be stacked onto the item b (following max stack size) + * + * @param a the item a + * @param b the item b + * @return true if they can be merged + */ + @SuppressWarnings("deprecation") + public static boolean isMergable(ItemStack a, ItemStack b) { + if (is(a) && is(b)) { + if (!a.getType().equals(b.getType())) { + return false; + } - return is.getDurability(); - } + if (a.getData().getData() != b.getData().getData()) { + return false; + } - /** - * Set the durability (if higher than max, it will be set to the max) - * - * @param is the item - * @param dmg the durability - */ - public static void setDurability(ItemStack is, short dmg) { - if (!is(is)) { - return; - } + if (a.hasItemMeta() != b.hasItemMeta()) { + return false; + } - is.setDurability(dmg > getMaxDurability(is) ? getMaxDurability(is) : dmg); - } + if (a.getDurability() != b.getDurability()) { + return false; + } - /** - * Set the durability (if higher than max, it will be set to the max) - * - * @param is the item - * @param dmg the durability - */ - public static void setDurability(ItemStack is, int dmg) { - if (!is(is)) { - return; + if (a.hasItemMeta()) { + if (!a.getItemMeta().getDisplayName().equals(b.getItemMeta().getDisplayName())) { + return false; } - setDurability(is, (short) dmg); - } - - /** - * Damage an itemstack - * - * @param is the item - * @param amt the amount to damage - */ - public static void damage(ItemStack is, int amt) { - if (!is(is)) { - return; + if (!new ArrayList(a.getItemMeta().getLore()).equals(new ArrayList<>(b.getItemMeta().getLore()))) { + return false; } + } - setDurability(is, getDurability(is) + amt); + return a.getMaxStackSize() >= a.getAmount() + b.getAmount(); } - /** - * Can the item a be stacked onto the item b (following max stack size) - * - * @param a the item a - * @param b the item b - * @return true if they can be merged - */ - @SuppressWarnings("deprecation") - public static boolean isMergable(ItemStack a, ItemStack b) { - if (is(a) && is(b)) { - if (!a.getType().equals(b.getType())) { - return false; - } - - if (a.getData().getData() != b.getData().getData()) { - return false; - } - - if (a.hasItemMeta() != b.hasItemMeta()) { - return false; - } - - if (a.getDurability() != b.getDurability()) { - return false; - } - - if (a.hasItemMeta()) { - if (!a.getItemMeta().getDisplayName().equals(b.getItemMeta().getDisplayName())) { - return false; - } - - if (!new ArrayList(a.getItemMeta().getLore()).equals(new ArrayList<>(b.getItemMeta().getLore()))) { - return false; - } - } - - return a.getMaxStackSize() >= a.getAmount() + b.getAmount(); - } - - return false; - } + return false; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java index ee35cc93e..d0e8d870d 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java @@ -31,187 +31,187 @@ import java.util.ListIterator; public class PhantomInventory implements PhantomInventoryWrapper { - protected Inventory i; - - public PhantomInventory(Inventory i) { - this.i = i; - } - - @Override - public HashMap addItem(ItemStack... arg0) throws IllegalArgumentException { - return i.addItem(arg0); - } - - @Override - public HashMap all(Material arg0) throws IllegalArgumentException { - return i.all(arg0); - } - - @Override - public HashMap all(ItemStack arg0) { - return i.all(arg0); - } - - @Override - public void clear() { - i.clear(); - } - - @Override - public void clear(int arg0) { - i.clear(arg0); - } - - @Override - public boolean contains(Material arg0) throws IllegalArgumentException { - return i.contains(arg0); - } - - @Override - public boolean contains(ItemStack arg0) { - return i.contains(arg0); - } - - @Override - public boolean contains(Material arg0, int arg1) throws IllegalArgumentException { - return i.contains(arg0, arg1); - } - - @Override - public boolean contains(ItemStack arg0, int arg1) { - return i.contains(arg0, arg1); - } - - @Override - public boolean containsAtLeast(ItemStack arg0, int arg1) { - return i.containsAtLeast(arg0, arg1); - } - - @Override - public int first(Material arg0) throws IllegalArgumentException { - return i.first(arg0); - } - - @Override - public int first(ItemStack arg0) { - return i.first(arg0); - } - - @Override - public int firstEmpty() { - return i.firstEmpty(); - } - - @Override - public boolean isEmpty() { - return i.isEmpty(); - } - - @Override - public ItemStack[] getContents() { - return i.getContents(); - } - - @Override - public void setContents(ItemStack[] arg0) throws IllegalArgumentException { - i.setContents(arg0); - } - - @Override - public InventoryHolder getHolder() { - return i.getHolder(); - } - - @Override - public ItemStack getItem(int arg0) { - return i.getItem(arg0); - } - - @Override - public int getMaxStackSize() { - return i.getMaxStackSize(); - } - - @Override - public void setMaxStackSize(int arg0) { - i.setMaxStackSize(arg0); - } - - @Override - public int getSize() { - return i.getSize(); - } - - @Override - public InventoryType getType() { - return i.getType(); - } - - @Override - public List getViewers() { - return i.getViewers(); - } - - @Override - public ListIterator iterator() { - return i.iterator(); - } - - @Override - public ListIterator iterator(int arg0) { - return i.iterator(arg0); - } - - @Override - public void remove(Material arg0) throws IllegalArgumentException { - i.remove(arg0); - } - - @Override - public void remove(ItemStack arg0) { - i.remove(arg0); - } - - @Override - public HashMap removeItem(ItemStack... arg0) throws IllegalArgumentException { - return i.removeItem(arg0); - } - - @Override - public void setItem(int arg0, ItemStack arg1) { - i.setItem(arg0, arg1); - } - - @Override - public boolean hasSpace() { - return firstEmpty() != -1; - } - - @Override - public int getSlotsLeft() { - int x = 0; - - for (ItemStack i : getContents()) { - if (i == null || i.getType().equals(Material.AIR)) { - x++; - } - } - - return x; - } - - @Override - public Location getLocation() { - return i.getLocation(); - } - - @Override - public ItemStack[] getStorageContents() { - return i.getStorageContents(); - } - - @Override - public void setStorageContents(ItemStack[] arg0) throws IllegalArgumentException { - i.setStorageContents(arg0); - } + protected Inventory i; + + public PhantomInventory(Inventory i) { + this.i = i; + } + + @Override + public HashMap addItem(ItemStack... arg0) throws IllegalArgumentException { + return i.addItem(arg0); + } + + @Override + public HashMap all(Material arg0) throws IllegalArgumentException { + return i.all(arg0); + } + + @Override + public HashMap all(ItemStack arg0) { + return i.all(arg0); + } + + @Override + public void clear() { + i.clear(); + } + + @Override + public void clear(int arg0) { + i.clear(arg0); + } + + @Override + public boolean contains(Material arg0) throws IllegalArgumentException { + return i.contains(arg0); + } + + @Override + public boolean contains(ItemStack arg0) { + return i.contains(arg0); + } + + @Override + public boolean contains(Material arg0, int arg1) throws IllegalArgumentException { + return i.contains(arg0, arg1); + } + + @Override + public boolean contains(ItemStack arg0, int arg1) { + return i.contains(arg0, arg1); + } + + @Override + public boolean containsAtLeast(ItemStack arg0, int arg1) { + return i.containsAtLeast(arg0, arg1); + } + + @Override + public int first(Material arg0) throws IllegalArgumentException { + return i.first(arg0); + } + + @Override + public int first(ItemStack arg0) { + return i.first(arg0); + } + + @Override + public int firstEmpty() { + return i.firstEmpty(); + } + + @Override + public boolean isEmpty() { + return i.isEmpty(); + } + + @Override + public ItemStack[] getContents() { + return i.getContents(); + } + + @Override + public void setContents(ItemStack[] arg0) throws IllegalArgumentException { + i.setContents(arg0); + } + + @Override + public InventoryHolder getHolder() { + return i.getHolder(); + } + + @Override + public ItemStack getItem(int arg0) { + return i.getItem(arg0); + } + + @Override + public int getMaxStackSize() { + return i.getMaxStackSize(); + } + + @Override + public void setMaxStackSize(int arg0) { + i.setMaxStackSize(arg0); + } + + @Override + public int getSize() { + return i.getSize(); + } + + @Override + public InventoryType getType() { + return i.getType(); + } + + @Override + public List getViewers() { + return i.getViewers(); + } + + @Override + public ListIterator iterator() { + return i.iterator(); + } + + @Override + public ListIterator iterator(int arg0) { + return i.iterator(arg0); + } + + @Override + public void remove(Material arg0) throws IllegalArgumentException { + i.remove(arg0); + } + + @Override + public void remove(ItemStack arg0) { + i.remove(arg0); + } + + @Override + public HashMap removeItem(ItemStack... arg0) throws IllegalArgumentException { + return i.removeItem(arg0); + } + + @Override + public void setItem(int arg0, ItemStack arg1) { + i.setItem(arg0, arg1); + } + + @Override + public boolean hasSpace() { + return firstEmpty() != -1; + } + + @Override + public int getSlotsLeft() { + int x = 0; + + for (ItemStack i : getContents()) { + if (i == null || i.getType().equals(Material.AIR)) { + x++; + } + } + + return x; + } + + @Override + public Location getLocation() { + return i.getLocation(); + } + + @Override + public ItemStack[] getStorageContents() { + return i.getStorageContents(); + } + + @Override + public void setStorageContents(ItemStack[] arg0) throws IllegalArgumentException { + i.setStorageContents(arg0); + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java index d4130da14..928e82ceb 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventoryWrapper.java @@ -26,17 +26,17 @@ * @author cyberpwn */ public interface PhantomInventoryWrapper extends Inventory { - /** - * Does the given inventory have any space? - * - * @return true if it has space for at least one more slot. - */ - boolean hasSpace(); + /** + * Does the given inventory have any space? + * + * @return true if it has space for at least one more slot. + */ + boolean hasSpace(); - /** - * Get how many air slots exist (empty slots) - * - * @return the number of slots empty (0 if full) - */ - int getSlotsLeft(); + /** + * Get how many air slots exist (empty slots) + * + * @return the number of slots empty (0 if full) + */ + int getSlotsLeft(); } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java index 7e9aec40e..18e6252a1 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java +++ b/src/main/java/art/arcane/adapt/util/common/io/BukkitGson.java @@ -29,74 +29,74 @@ import org.bukkit.util.Vector; public class BukkitGson { - public static final Gson gson = new GsonBuilder() - .registerTypeAdapter(World.class, (JsonSerializer) (world, type, s) -> s.serialize(world.getName())) - .registerTypeAdapter(World.class, (JsonDeserializer) (j, type, d) -> Bukkit.getWorld(j.getAsString())) - .registerTypeAdapter(BlockData.class, (JsonSerializer) (data, type, s) -> new JsonPrimitive(data.getAsString(true))) - .registerTypeAdapter(BlockData.class, (JsonDeserializer) (j, type, d) -> Bukkit.createBlockData(j.getAsString())) - .registerTypeAdapter(Location.class, (JsonSerializer) (data, type, s) -> { - JsonArray a = new JsonArray(); - a.add(data.getWorld().getName()); - a.add(truncate(data.getX(), 1)); - a.add(truncate(data.getY(), 1)); - a.add(truncate(data.getZ(), 1)); - a.add((int) data.getYaw()); - a.add((int) data.getPitch()); - return a; - }) - .registerTypeAdapter(Location.class, (JsonDeserializer) (j, type, d) -> { - JsonArray a = j.getAsJsonArray(); - return new Location(Bukkit.getWorld(a.get(0).getAsString()), a.get(1).getAsDouble(), a.get(2).getAsDouble(), a.get(3).getAsDouble(), a.get(4).getAsFloat(), a.get(5).getAsFloat()); - }) - .registerTypeAdapter(Block.class, (JsonSerializer) (data, type, s) -> { - JsonArray a = new JsonArray(); - a.add(data.getWorld().getName()); - a.add(data.getX()); - a.add(data.getY()); - a.add(data.getZ()); - return a; - }) - .registerTypeAdapter(Block.class, (JsonDeserializer) (j, type, d) -> { - JsonArray a = j.getAsJsonArray(); - return new Location(Bukkit.getWorld(a.get(0).getAsString()), a.get(1).getAsInt(), a.get(2).getAsInt(), a.get(3).getAsInt()).getBlock(); - }) - .registerTypeAdapter(BlockVector.class, (JsonSerializer) (data, type, s) -> { - JsonArray a = new JsonArray(); - a.add(data.getBlockX()); - a.add(data.getBlockY()); - a.add(data.getBlockZ()); - return a; - }) - .registerTypeAdapter(BlockVector.class, (JsonDeserializer) (j, type, d) -> { - JsonArray a = j.getAsJsonArray(); - return new BlockVector(a.get(0).getAsInt(), a.get(1).getAsInt(), a.get(2).getAsInt()); - }) - .registerTypeAdapter(Vector.class, (JsonSerializer) (data, type, s) -> { - JsonArray a = new JsonArray(); - a.add(truncate(data.getX(), 1)); - a.add(truncate(data.getY(), 1)); - a.add(truncate(data.getZ(), 1)); - return a; - }) - .registerTypeAdapter(Vector.class, (JsonDeserializer) (j, type, d) -> { - JsonArray a = j.getAsJsonArray(); - return new BlockVector(a.get(0).getAsDouble(), a.get(1).getAsDouble(), a.get(2).getAsDouble()); - }) - .create(); + public static final Gson gson = new GsonBuilder() + .registerTypeAdapter(World.class, (JsonSerializer) (world, type, s) -> s.serialize(world.getName())) + .registerTypeAdapter(World.class, (JsonDeserializer) (j, type, d) -> Bukkit.getWorld(j.getAsString())) + .registerTypeAdapter(BlockData.class, (JsonSerializer) (data, type, s) -> new JsonPrimitive(data.getAsString(true))) + .registerTypeAdapter(BlockData.class, (JsonDeserializer) (j, type, d) -> Bukkit.createBlockData(j.getAsString())) + .registerTypeAdapter(Location.class, (JsonSerializer) (data, type, s) -> { + JsonArray a = new JsonArray(); + a.add(data.getWorld().getName()); + a.add(truncate(data.getX(), 1)); + a.add(truncate(data.getY(), 1)); + a.add(truncate(data.getZ(), 1)); + a.add((int) data.getYaw()); + a.add((int) data.getPitch()); + return a; + }) + .registerTypeAdapter(Location.class, (JsonDeserializer) (j, type, d) -> { + JsonArray a = j.getAsJsonArray(); + return new Location(Bukkit.getWorld(a.get(0).getAsString()), a.get(1).getAsDouble(), a.get(2).getAsDouble(), a.get(3).getAsDouble(), a.get(4).getAsFloat(), a.get(5).getAsFloat()); + }) + .registerTypeAdapter(Block.class, (JsonSerializer) (data, type, s) -> { + JsonArray a = new JsonArray(); + a.add(data.getWorld().getName()); + a.add(data.getX()); + a.add(data.getY()); + a.add(data.getZ()); + return a; + }) + .registerTypeAdapter(Block.class, (JsonDeserializer) (j, type, d) -> { + JsonArray a = j.getAsJsonArray(); + return new Location(Bukkit.getWorld(a.get(0).getAsString()), a.get(1).getAsInt(), a.get(2).getAsInt(), a.get(3).getAsInt()).getBlock(); + }) + .registerTypeAdapter(BlockVector.class, (JsonSerializer) (data, type, s) -> { + JsonArray a = new JsonArray(); + a.add(data.getBlockX()); + a.add(data.getBlockY()); + a.add(data.getBlockZ()); + return a; + }) + .registerTypeAdapter(BlockVector.class, (JsonDeserializer) (j, type, d) -> { + JsonArray a = j.getAsJsonArray(); + return new BlockVector(a.get(0).getAsInt(), a.get(1).getAsInt(), a.get(2).getAsInt()); + }) + .registerTypeAdapter(Vector.class, (JsonSerializer) (data, type, s) -> { + JsonArray a = new JsonArray(); + a.add(truncate(data.getX(), 1)); + a.add(truncate(data.getY(), 1)); + a.add(truncate(data.getZ(), 1)); + return a; + }) + .registerTypeAdapter(Vector.class, (JsonDeserializer) (j, type, d) -> { + JsonArray a = j.getAsJsonArray(); + return new BlockVector(a.get(0).getAsDouble(), a.get(1).getAsDouble(), a.get(2).getAsDouble()); + }) + .create(); - private static double truncate(double d, int p) { - if ((int) d == d) { - return d; - } - - return Double.parseDouble(Form.f(d, p)); + private static double truncate(double d, int p) { + if ((int) d == d) { + return d; } - private static double truncate(float d, int p) { - if ((int) d == d) { - return d; - } + return Double.parseDouble(Form.f(d, p)); + } - return Float.parseFloat(Form.f(d, p)); + private static double truncate(float d, int p) { + if ((int) d == d) { + return d; } + + return Float.parseFloat(Form.f(d, p)); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/io/Json.java b/src/main/java/art/arcane/adapt/util/common/io/Json.java index 6a9c84f08..247238109 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/Json.java +++ b/src/main/java/art/arcane/adapt/util/common/io/Json.java @@ -7,32 +7,32 @@ import java.lang.reflect.Type; public class Json { - public static final Gson NORMAL = create(false); - public static final Gson PRETTY = create(true); - - @NonNull - public static String toJson(@NonNull Object src, boolean pretty) { - return toJson(src, src.getClass(), pretty); - } - - @NonNull - public static String toJson(@NonNull Object src, @NonNull Type type, boolean pretty) { - return (pretty ? PRETTY : NORMAL).toJson(src, type); - } - - public static T fromJson(@NonNull String json, @NonNull Class type) { - return NORMAL.fromJson(json, type); - } - - public static T fromJson(@NonNull String json, @NonNull Type type) { - return NORMAL.fromJson(json, type); - } - - private static Gson create(boolean pretty) { - var builder = new GsonBuilder() - .setLenient() - .disableHtmlEscaping(); - if (pretty) builder.setPrettyPrinting(); - return builder.create(); - } + public static final Gson NORMAL = create(false); + public static final Gson PRETTY = create(true); + + @NonNull + public static String toJson(@NonNull Object src, boolean pretty) { + return toJson(src, src.getClass(), pretty); + } + + @NonNull + public static String toJson(@NonNull Object src, @NonNull Type type, boolean pretty) { + return (pretty ? PRETTY : NORMAL).toJson(src, type); + } + + public static T fromJson(@NonNull String json, @NonNull Class type) { + return NORMAL.fromJson(json, type); + } + + public static T fromJson(@NonNull String json, @NonNull Type type) { + return NORMAL.fromJson(json, type); + } + + private static Gson create(boolean pretty) { + com.google.gson.GsonBuilder builder = new GsonBuilder() + .setLenient() + .disableHtmlEscaping(); + if (pretty) builder.setPrettyPrinting(); + return builder.create(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/io/LZString.java b/src/main/java/art/arcane/adapt/util/common/io/LZString.java index fd4406ac6..a565a4078 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/LZString.java +++ b/src/main/java/art/arcane/adapt/util/common/io/LZString.java @@ -22,535 +22,535 @@ public class LZString { - private static final char[] keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(); - private static final char[] keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$".toCharArray(); - private static final Map> baseReverseDic = new HashMap>(); - - private static char getBaseValue(char[] alphabet, Character character) { - Map map = baseReverseDic.get(alphabet); - if (map == null) { - map = new HashMap(); - baseReverseDic.put(alphabet, map); - for (int i = 0; i < alphabet.length; i++) { - map.put(alphabet[i], i); - } - } - return (char) map.get(character).intValue(); + private static final char[] keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(); + private static final char[] keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$".toCharArray(); + private static final Map> baseReverseDic = new HashMap>(); + + private static char getBaseValue(char[] alphabet, Character character) { + Map map = baseReverseDic.get(alphabet); + if (map == null) { + map = new HashMap(); + baseReverseDic.put(alphabet, map); + for (int i = 0; i < alphabet.length; i++) { + map.put(alphabet[i], i); + } } - - public static String compressToBase64(String input) { - if (input == null) - return ""; - String res = LZString._compress(input, 6, new CompressFunctionWrapper() { - @Override - public char doFunc(int a) { - return keyStrBase64[a]; - } - }); - switch (res.length() % 4) { // To produce valid Base64 - default: // When could this happen ? - case 0: - return res; - case 1: - return res + "==="; - case 2: - return res + "=="; - case 3: - return res + "="; - } + return (char) map.get(character).intValue(); + } + + public static String compressToBase64(String input) { + if (input == null) + return ""; + String res = LZString._compress(input, 6, new CompressFunctionWrapper() { + @Override + public char doFunc(int a) { + return keyStrBase64[a]; + } + }); + switch (res.length() % 4) { // To produce valid Base64 + default: // When could this happen ? + case 0: + return res; + case 1: + return res + "==="; + case 2: + return res + "=="; + case 3: + return res + "="; } - - public static String decompressFromBase64(final String inputStr) { - if (inputStr == null) - return ""; - if (inputStr.equals("")) - return null; - return LZString._decompress(inputStr.length(), 32, new DecompressFunctionWrapper() { - @Override - public char doFunc(int index) { - return getBaseValue(keyStrBase64, inputStr.charAt(index)); + } + + public static String decompressFromBase64(final String inputStr) { + if (inputStr == null) + return ""; + if (inputStr.equals("")) + return null; + return LZString._decompress(inputStr.length(), 32, new DecompressFunctionWrapper() { + @Override + public char doFunc(int index) { + return getBaseValue(keyStrBase64, inputStr.charAt(index)); + } + }); + } + + public static String compressToUTF16(String input) { + if (input == null) + return ""; + return LZString._compress(input, 15, new CompressFunctionWrapper() { + @Override + public char doFunc(int a) { + return fc(a + 32); + } + }) + " "; + } + + public static String decompressFromUTF16(final String compressedStr) { + if (compressedStr == null) + return ""; + if (compressedStr.isEmpty()) + return null; + return LZString._decompress(compressedStr.length(), 16384, new DecompressFunctionWrapper() { + @Override + public char doFunc(int index) { + return (char) (compressedStr.charAt(index) - 32); + } + }); + } + + //TODO: java has no Uint8Array type, what can we do? + + public static String compressToEncodedURIComponent(String input) { + if (input == null) + return ""; + return LZString._compress(input, 6, new CompressFunctionWrapper() { + @Override + public char doFunc(int a) { + return keyStrUriSafe[a]; + } + }); + } + + public static String decompressFromEncodedURIComponent(String inputStr) { + if (inputStr == null) return ""; + if (inputStr.isEmpty()) return null; + final String urlEncodedInputStr = inputStr.replace(' ', '+'); + return LZString._decompress(urlEncodedInputStr.length(), 32, new DecompressFunctionWrapper() { + @Override + public char doFunc(int index) { + return getBaseValue(keyStrUriSafe, urlEncodedInputStr.charAt(index)); + } + }); + } + + public static String compress(String uncompressed) { + return LZString._compress(uncompressed, 16, new CompressFunctionWrapper() { + @Override + public char doFunc(int a) { + return fc(a); + } + }); + } + + private static String _compress(String uncompressedStr, int bitsPerChar, CompressFunctionWrapper getCharFromInt) { + if (uncompressedStr == null) return ""; + int i, value; + Map context_dictionary = new HashMap(); + Set context_dictionaryToCreate = new HashSet(); + String context_c = ""; + String context_wc = ""; + String context_w = ""; + int context_enlargeIn = 2; // Compensate for the first entry which should not count + int context_dictSize = 3; + int context_numBits = 2; + StringBuilder context_data = new StringBuilder(uncompressedStr.length() / 3); + int context_data_val = 0; + int context_data_position = 0; + int ii; + + for (ii = 0; ii < uncompressedStr.length(); ii += 1) { + context_c = String.valueOf(uncompressedStr.charAt(ii)); + if (!context_dictionary.containsKey(context_c)) { + context_dictionary.put(context_c, context_dictSize++); + context_dictionaryToCreate.add(context_c); + } + + context_wc = context_w + context_c; + if (context_dictionary.containsKey(context_wc)) { + context_w = context_wc; + } else { + if (context_dictionaryToCreate.contains(context_w)) { + if (context_w.charAt(0) < 256) { + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } } - }); - } - - public static String compressToUTF16(String input) { - if (input == null) - return ""; - return LZString._compress(input, 15, new CompressFunctionWrapper() { - @Override - public char doFunc(int a) { - return fc(a + 32); + value = context_w.charAt(0); + for (i = 0; i < 8; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } + value = value >> 1; } - }) + " "; - } - - public static String decompressFromUTF16(final String compressedStr) { - if (compressedStr == null) - return ""; - if (compressedStr.isEmpty()) - return null; - return LZString._decompress(compressedStr.length(), 16384, new DecompressFunctionWrapper() { - @Override - public char doFunc(int index) { - return (char) (compressedStr.charAt(index) - 32); + } else { + value = 1; + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1) | value; + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } + value = 0; } - }); - } - - //TODO: java has no Uint8Array type, what can we do? - - public static String compressToEncodedURIComponent(String input) { - if (input == null) - return ""; - return LZString._compress(input, 6, new CompressFunctionWrapper() { - @Override - public char doFunc(int a) { - return keyStrUriSafe[a]; + value = context_w.charAt(0); + for (i = 0; i < 16; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } + value = value >> 1; } - }); - } - - public static String decompressFromEncodedURIComponent(String inputStr) { - if (inputStr == null) return ""; - if (inputStr.isEmpty()) return null; - final String urlEncodedInputStr = inputStr.replace(' ', '+'); - return LZString._decompress(urlEncodedInputStr.length(), 32, new DecompressFunctionWrapper() { - @Override - public char doFunc(int index) { - return getBaseValue(keyStrUriSafe, urlEncodedInputStr.charAt(index)); + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = powerOf2(context_numBits); + context_numBits++; + } + context_dictionaryToCreate.remove(context_w); + } else { + value = context_dictionary.get(context_w); + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; } - }); - } + value = value >> 1; + } - public static String compress(String uncompressed) { - return LZString._compress(uncompressed, 16, new CompressFunctionWrapper() { - @Override - public char doFunc(int a) { - return fc(a); - } - }); + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = powerOf2(context_numBits); + context_numBits++; + } + // Add wc to the dictionary. + context_dictionary.put(context_wc, context_dictSize++); + context_w = context_c; + } } - private static String _compress(String uncompressedStr, int bitsPerChar, CompressFunctionWrapper getCharFromInt) { - if (uncompressedStr == null) return ""; - int i, value; - Map context_dictionary = new HashMap(); - Set context_dictionaryToCreate = new HashSet(); - String context_c = ""; - String context_wc = ""; - String context_w = ""; - int context_enlargeIn = 2; // Compensate for the first entry which should not count - int context_dictSize = 3; - int context_numBits = 2; - StringBuilder context_data = new StringBuilder(uncompressedStr.length() / 3); - int context_data_val = 0; - int context_data_position = 0; - int ii; - - for (ii = 0; ii < uncompressedStr.length(); ii += 1) { - context_c = String.valueOf(uncompressedStr.charAt(ii)); - if (!context_dictionary.containsKey(context_c)) { - context_dictionary.put(context_c, context_dictSize++); - context_dictionaryToCreate.add(context_c); - } - - context_wc = context_w + context_c; - if (context_dictionary.containsKey(context_wc)) { - context_w = context_wc; + // Output the code for w. + if (!context_w.isEmpty()) { + if (context_dictionaryToCreate.contains(context_w)) { + if (context_w.charAt(0) < 256) { + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; } else { - if (context_dictionaryToCreate.contains(context_w)) { - if (context_w.charAt(0) < 256) { - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - } - value = context_w.charAt(0); - for (i = 0; i < 8; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } else { - value = 1; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | value; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = 0; - } - value = context_w.charAt(0); - for (i = 0; i < 16; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = powerOf2(context_numBits); - context_numBits++; - } - context_dictionaryToCreate.remove(context_w); - } else { - value = context_dictionary.get(context_w); - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = powerOf2(context_numBits); - context_numBits++; - } - // Add wc to the dictionary. - context_dictionary.put(context_wc, context_dictSize++); - context_w = context_c; + context_data_position++; } - } - - // Output the code for w. - if (!context_w.isEmpty()) { - if (context_dictionaryToCreate.contains(context_w)) { - if (context_w.charAt(0) < 256) { - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - } - value = context_w.charAt(0); - for (i = 0; i < 8; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } else { - value = 1; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | value; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = 0; - } - value = context_w.charAt(0); - for (i = 0; i < 16; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = powerOf2(context_numBits); - context_numBits++; - } - context_dictionaryToCreate.remove(context_w); + } + value = context_w.charAt(0); + for (i = 0; i < 8; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; } else { - value = context_dictionary.get(context_w); - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - + context_data_position++; } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = powerOf2(context_numBits); - context_numBits++; + value = value >> 1; + } + } else { + value = 1; + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1) | value; + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; } - } - - // Mark the end of the stream - value = 2; - for (i = 0; i < context_numBits; i++) { + value = 0; + } + value = context_w.charAt(0); + for (i = 0; i < 16; i++) { context_data_val = (context_data_val << 1) | (value & 1); if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.append(getCharFromInt.doFunc(context_data_val)); - context_data_val = 0; + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; } else { - context_data_position++; + context_data_position++; } value = value >> 1; + } } - - // Flush the last char - while (true) { - context_data_val = (context_data_val << 1); - if (context_data_position == bitsPerChar - 1) { - context_data.append(getCharFromInt.doFunc(context_data_val)); - break; - } else - context_data_position++; + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = powerOf2(context_numBits); + context_numBits++; + } + context_dictionaryToCreate.remove(context_w); + } else { + value = context_dictionary.get(context_w); + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } + value = value >> 1; } - return context_data.toString(); - } - public static String f(int i) { - return String.valueOf((char) i); + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = powerOf2(context_numBits); + context_numBits++; + } } - public static char fc(int i) { - return (char) i; + // Mark the end of the stream + value = 2; + for (i = 0; i < context_numBits; i++) { + context_data_val = (context_data_val << 1) | (value & 1); + if (context_data_position == bitsPerChar - 1) { + context_data_position = 0; + context_data.append(getCharFromInt.doFunc(context_data_val)); + context_data_val = 0; + } else { + context_data_position++; + } + value = value >> 1; } - public static String decompress(final String compressed) { - if (compressed == null) - return ""; - if (compressed.isEmpty()) - return null; - return LZString._decompress(compressed.length(), 32768, new DecompressFunctionWrapper() { - @Override - public char doFunc(int i) { - return compressed.charAt(i); - } - }); + // Flush the last char + while (true) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar - 1) { + context_data.append(getCharFromInt.doFunc(context_data_val)); + break; + } else + context_data_position++; + } + return context_data.toString(); + } + + public static String f(int i) { + return String.valueOf((char) i); + } + + public static char fc(int i) { + return (char) i; + } + + public static String decompress(final String compressed) { + if (compressed == null) + return ""; + if (compressed.isEmpty()) + return null; + return LZString._decompress(compressed.length(), 32768, new DecompressFunctionWrapper() { + @Override + public char doFunc(int i) { + return compressed.charAt(i); + } + }); + } + + private static String _decompress(int length, int resetValue, DecompressFunctionWrapper getNextValue) { + List dictionary = new ArrayList(); + // TODO: is next an unused variable in original lz-string? + @SuppressWarnings("unused") + int next; + int enlargeIn = 4; + int dictSize = 4; + int numBits = 3; + String entry = ""; + StringBuilder result = new StringBuilder(); + String w; + int bits, resb; + int maxpower, power; + String c = null; + DecData data = new DecData(); + data.val = getNextValue.doFunc(0); + data.position = resetValue; + data.index = 1; + + for (int i = 0; i < 3; i += 1) { + dictionary.add(i, f(i)); } - private static String _decompress(int length, int resetValue, DecompressFunctionWrapper getNextValue) { - List dictionary = new ArrayList(); - // TODO: is next an unused variable in original lz-string? - @SuppressWarnings("unused") - int next; - int enlargeIn = 4; - int dictSize = 4; - int numBits = 3; - String entry = ""; - StringBuilder result = new StringBuilder(); - String w; - int bits, resb; - int maxpower, power; - String c = null; - DecData data = new DecData(); - data.val = getNextValue.doFunc(0); + bits = 0; + maxpower = powerOf2(2); + power = 1; + while (power != maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { data.position = resetValue; - data.index = 1; + data.val = getNextValue.doFunc(data.index++); + } + bits |= (resb > 0 ? 1 : 0) * power; + power <<= 1; + } - for (int i = 0; i < 3; i += 1) { - dictionary.add(i, f(i)); + switch (next = bits) { + case 0: + bits = 0; + maxpower = powerOf2(8); + power = 1; + while (power != maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue.doFunc(data.index++); + } + bits |= (resb > 0 ? 1 : 0) * power; + power <<= 1; } - + c = f(bits); + break; + case 1: bits = 0; - maxpower = powerOf2(2); + maxpower = powerOf2(16); power = 1; while (power != maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue.doFunc(data.index++); + } + bits |= (resb > 0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 2: + return ""; + } + dictionary.add(3, c); + w = c; + result.append(w); + while (true) { + if (data.index > length) { + return ""; + } + + bits = 0; + maxpower = powerOf2(numBits); + power = 1; + while (power != maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue.doFunc(data.index++); + } + bits |= (resb > 0 ? 1 : 0) * power; + power <<= 1; + } + // TODO: very strange here, c above is as char/string, here further is a int, rename "c" in the switch as "cc" + int cc; + switch (cc = bits) { + case 0: + bits = 0; + maxpower = powerOf2(8); + power = 1; + while (power != maxpower) { resb = data.val & data.position; data.position >>= 1; if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); + data.position = resetValue; + data.val = getNextValue.doFunc(data.index++); } bits |= (resb > 0 ? 1 : 0) * power; power <<= 1; - } - - switch (next = bits) { - case 0: - bits = 0; - maxpower = powerOf2(8); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - c = f(bits); - break; - case 1: - bits = 0; - maxpower = powerOf2(16); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - c = f(bits); - break; - case 2: - return ""; - } - dictionary.add(3, c); - w = c; - result.append(w); - while (true) { - if (data.index > length) { - return ""; - } - - bits = 0; - maxpower = powerOf2(numBits); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - // TODO: very strange here, c above is as char/string, here further is a int, rename "c" in the switch as "cc" - int cc; - switch (cc = bits) { - case 0: - bits = 0; - maxpower = powerOf2(8); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - - dictionary.add(dictSize++, f(bits)); - cc = dictSize - 1; - enlargeIn--; - break; - case 1: - bits = 0; - maxpower = powerOf2(16); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue.doFunc(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - dictionary.add(dictSize++, f(bits)); - cc = dictSize - 1; - enlargeIn--; - break; - case 2: - return result.toString(); - } - - if (enlargeIn == 0) { - enlargeIn = powerOf2(numBits); - numBits++; - } - - if (cc < dictionary.size() && dictionary.get(cc) != null) { - entry = dictionary.get(cc); - } else { - if (cc == dictSize) { - entry = w + w.charAt(0); - } else { - return null; - } + } + + dictionary.add(dictSize++, f(bits)); + cc = dictSize - 1; + enlargeIn--; + break; + case 1: + bits = 0; + maxpower = powerOf2(16); + power = 1; + while (power != maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue.doFunc(data.index++); } - result.append(entry); - - // Add w+entry[0] to the dictionary. - dictionary.add(dictSize++, w + entry.charAt(0)); - enlargeIn--; + bits |= (resb > 0 ? 1 : 0) * power; + power <<= 1; + } + dictionary.add(dictSize++, f(bits)); + cc = dictSize - 1; + enlargeIn--; + break; + case 2: + return result.toString(); + } + + if (enlargeIn == 0) { + enlargeIn = powerOf2(numBits); + numBits++; + } + + if (cc < dictionary.size() && dictionary.get(cc) != null) { + entry = dictionary.get(cc); + } else { + if (cc == dictSize) { + entry = w + w.charAt(0); + } else { + return null; + } + } + result.append(entry); - w = entry; + // Add w+entry[0] to the dictionary. + dictionary.add(dictSize++, w + entry.charAt(0)); + enlargeIn--; - if (enlargeIn == 0) { - enlargeIn = powerOf2(numBits); - numBits++; - } + w = entry; - } + if (enlargeIn == 0) { + enlargeIn = powerOf2(numBits); + numBits++; + } } - private static int powerOf2(int power) { - return 1 << power; - } + } - private static abstract class CompressFunctionWrapper { - public abstract char doFunc(int i); - } + private static int powerOf2(int power) { + return 1 << power; + } - private static abstract class DecompressFunctionWrapper { - public abstract char doFunc(int i); - } + private static abstract class CompressFunctionWrapper { + public abstract char doFunc(int i); + } - protected static class DecData { - public char val; - public int position; - public int index; - } + private static abstract class DecompressFunctionWrapper { + public abstract char doFunc(int i); + } + + protected static class DecData { + public char val; + public int position; + public int index; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java b/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java index 9553245d1..d1cc8663a 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java +++ b/src/main/java/art/arcane/adapt/util/common/io/PersistentJson.java @@ -25,17 +25,17 @@ public class PersistentJson { - public static void write(PersistentDataContainer c, String key, Object data) { - c.set(new NamespacedKey(Adapt.instance, key), PersistentDataType.STRING, Json.toJson(data, false)); - } - - private static T fromJSON(PersistentDataContainer c, String key, Class type) { - String s = c.get(new NamespacedKey(Adapt.instance, key), PersistentDataType.STRING); + public static void write(PersistentDataContainer c, String key, Object data) { + c.set(new NamespacedKey(Adapt.instance, key), PersistentDataType.STRING, Json.toJson(data, false)); + } - if (s == null) { - return Json.fromJson(s, type); - } + private static T fromJSON(PersistentDataContainer c, String key, Class type) { + String s = c.get(new NamespacedKey(Adapt.instance, key), PersistentDataType.STRING); - return null; + if (s == null) { + return Json.fromJson(s, type); } + + return null; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java index be4fb2c03..4bb16fc13 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ReactiveFolder.java @@ -25,55 +25,55 @@ import java.util.List; public class ReactiveFolder { - private final File folder; - private final Consumer3, List, List> hotload; - private FolderWatcher fw; + private final File folder; + private final Consumer3, List, List> hotload; + private FolderWatcher fw; - public ReactiveFolder(File folder, Consumer3, List, List> hotload) { - this.folder = folder; - this.hotload = hotload; - this.fw = new FolderWatcher(folder); - fw.checkModified(); - } - - public void checkIgnore() { - fw = new FolderWatcher(folder); - } + public ReactiveFolder(File folder, Consumer3, List, List> hotload) { + this.folder = folder; + this.hotload = hotload; + this.fw = new FolderWatcher(folder); + fw.checkModified(); + } - public void check() { - boolean modified = false; + public void checkIgnore() { + fw = new FolderWatcher(folder); + } - if (fw.checkModified()) { - for (File i : fw.getCreated()) { - if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { - modified = true; - break; - } - } + public void check() { + boolean modified = false; - if (!modified) { - for (File i : fw.getChanged()) { - if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { - modified = true; - break; - } - } - } + if (fw.checkModified()) { + for (File i : fw.getCreated()) { + if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { + modified = true; + break; + } + } - if (!modified) { - for (File i : fw.getDeleted()) { - if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { - modified = true; - break; - } - } - } + if (!modified) { + for (File i : fw.getChanged()) { + if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { + modified = true; + break; + } } + } - if (modified) { - hotload.accept(fw.getCreated(), fw.getChanged(), fw.getDeleted()); + if (!modified) { + for (File i : fw.getDeleted()) { + if (i.getName().endsWith(".iob") || i.getName().endsWith(".json")) { + modified = true; + break; + } } + } + } - fw.checkModified(); + if (modified) { + hotload.accept(fw.getCreated(), fw.getChanged(), fw.getDeleted()); } + + fw.checkModified(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java index fe8341b74..f791ef0c8 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java +++ b/src/main/java/art/arcane/adapt/util/common/io/SQLManager.java @@ -8,129 +8,129 @@ public class SQLManager { - private static final String TABLE_NAME = "ADAPT_DATA"; - private static final String CREATE_TABLE_QUERY = "CREATE TABLE " + TABLE_NAME + " (UUID char(36) NOT NULL UNIQUE, DATA MEDIUMTEXT NOT NULL)"; - private static final String UPDATE_QUERY = "INSERT INTO " + TABLE_NAME + " (UUID, DATA) VALUES(?, ?) ON DUPLICATE KEY UPDATE DATA=?"; - private static final String FETCH_QUERY = "SELECT DATA FROM " + TABLE_NAME + " WHERE UUID=?"; - private static final String DELETE_QUERY = "DELETE FROM " + TABLE_NAME + " WHERE UUID=?"; + private static final String TABLE_NAME = "ADAPT_DATA"; + private static final String CREATE_TABLE_QUERY = "CREATE TABLE " + TABLE_NAME + " (UUID char(36) NOT NULL UNIQUE, DATA MEDIUMTEXT NOT NULL)"; + private static final String UPDATE_QUERY = "INSERT INTO " + TABLE_NAME + " (UUID, DATA) VALUES(?, ?) ON DUPLICATE KEY UPDATE DATA=?"; + private static final String FETCH_QUERY = "SELECT DATA FROM " + TABLE_NAME + " WHERE UUID=?"; + private static final String DELETE_QUERY = "DELETE FROM " + TABLE_NAME + " WHERE UUID=?"; - private Connection connection; + private Connection connection; - public synchronized void establishConnection() { - if (connection != null) { - closeConnection(); - } - - AdaptConfig config = AdaptConfig.get(); - try { - connection = DriverManager.getConnection(assembleUrl(config), config.getSql().getUsername(), config.getSql().getPassword()); - int verifySeconds = Math.max(1, Math.min(10, config.getSqlSecondsCheckverify())); - if (!connection.isValid(verifySeconds)) { - throw new SQLException("Connection timed out"); - } else { - setupDatabase(); - } - } catch (SQLException e) { - handleSQLException("Failed to establish a connection to the SQL server!", e); - connection = null; - } + public synchronized void establishConnection() { + if (connection != null) { + closeConnection(); } - private void setupDatabase() throws SQLException { - if (!connection.getMetaData().getTables(null, null, TABLE_NAME, new String[]{"TABLE"}).next()) { - try (Statement statement = connection.createStatement()) { - statement.executeUpdate(CREATE_TABLE_QUERY); - } - } + AdaptConfig config = AdaptConfig.get(); + try { + connection = DriverManager.getConnection(assembleUrl(config), config.getSql().getUsername(), config.getSql().getPassword()); + int verifySeconds = Math.max(1, Math.min(10, config.getSqlSecondsCheckverify())); + if (!connection.isValid(verifySeconds)) { + throw new SQLException("Connection timed out"); + } else { + setupDatabase(); + } + } catch (SQLException e) { + handleSQLException("Failed to establish a connection to the SQL server!", e); + connection = null; } + } - public synchronized void closeConnection() { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - handleSQLException("Failed to close the connection to the SQL server!", e); - } - connection = null; - } + private void setupDatabase() throws SQLException { + if (!connection.getMetaData().getTables(null, null, TABLE_NAME, new String[]{"TABLE"}).next()) { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate(CREATE_TABLE_QUERY); + } } - - public synchronized void updateData(UUID uuid, String data) { - executeWithRetry(conn -> { - try (PreparedStatement statement = conn.prepareStatement(UPDATE_QUERY)) { - statement.setString(1, uuid.toString()); - statement.setString(2, data); - statement.setString(3, data); - statement.executeUpdate(); - } - }, "Failed to write data to the SQL server!"); - } - - public synchronized void delete(UUID uuid) { - executeWithRetry(conn -> { - try (PreparedStatement statement = conn.prepareStatement(DELETE_QUERY)) { - statement.setString(1, uuid.toString()); - statement.executeUpdate(); - } - }, "Failed to delete data from the SQL server!"); + } + + public synchronized void closeConnection() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + handleSQLException("Failed to close the connection to the SQL server!", e); + } + connection = null; } - - public synchronized String fetchData(UUID uuid) { - try { - checkAndReestablishConnection(); - try (PreparedStatement statement = connection.prepareStatement(FETCH_QUERY)) { - statement.setString(1, uuid.toString()); - try (ResultSet set = statement.executeQuery()) { - if (!set.next()) { - return null; - } - return set.getString("DATA"); - } - } - } catch (SQLException e) { - handleSQLException("Failed to read data from the SQL server!", e); + } + + public synchronized void updateData(UUID uuid, String data) { + executeWithRetry(conn -> { + try (PreparedStatement statement = conn.prepareStatement(UPDATE_QUERY)) { + statement.setString(1, uuid.toString()); + statement.setString(2, data); + statement.setString(3, data); + statement.executeUpdate(); + } + }, "Failed to write data to the SQL server!"); + } + + public synchronized void delete(UUID uuid) { + executeWithRetry(conn -> { + try (PreparedStatement statement = conn.prepareStatement(DELETE_QUERY)) { + statement.setString(1, uuid.toString()); + statement.executeUpdate(); + } + }, "Failed to delete data from the SQL server!"); + } + + public synchronized String fetchData(UUID uuid) { + try { + checkAndReestablishConnection(); + try (PreparedStatement statement = connection.prepareStatement(FETCH_QUERY)) { + statement.setString(1, uuid.toString()); + try (ResultSet set = statement.executeQuery()) { + if (!set.next()) { return null; + } + return set.getString("DATA"); } + } + } catch (SQLException e) { + handleSQLException("Failed to read data from the SQL server!", e); + return null; } - - private void executeWithRetry(SqlAction action, String errorMessage) { - try { - checkAndReestablishConnection(); - action.run(connection); - } catch (SQLException e) { - handleSQLException(errorMessage, e); - } + } + + private void executeWithRetry(SqlAction action, String errorMessage) { + try { + checkAndReestablishConnection(); + action.run(connection); + } catch (SQLException e) { + handleSQLException(errorMessage, e); } + } - private void checkAndReestablishConnection() throws SQLException { - if (connection == null || !connection.isValid(AdaptConfig.get().getSqlSecondsCheckverify())) { // 30 sec by default - establishConnection(); - } - if (connection == null) { - throw new SQLException("No active SQL connection"); - } + private void checkAndReestablishConnection() throws SQLException { + if (connection == null || !connection.isValid(AdaptConfig.get().getSqlSecondsCheckverify())) { // 30 sec by default + establishConnection(); } - - private void handleSQLException(String message, SQLException e) { - Adapt.error(message); - Adapt.error("\t" + e.getClass().getSimpleName() + (e.getMessage() != null ? ": " + e.getMessage() : "")); - } - - private String assembleUrl(AdaptConfig config) { - long connectTimeout = Math.max(1000L, config.getSql().getConnectionTimeout()); - long socketTimeout = Math.max(connectTimeout, connectTimeout * 2L); - return String.format( - "jdbc:mysql://%s:%d/%s?connectTimeout=%d&socketTimeout=%d", - config.getSql().getHost(), - config.getSql().getPort(), - config.getSql().getDatabase(), - connectTimeout, - socketTimeout - ); - } - - @FunctionalInterface - interface SqlAction { - void run(Connection connection) throws SQLException; + if (connection == null) { + throw new SQLException("No active SQL connection"); } + } + + private void handleSQLException(String message, SQLException e) { + Adapt.error(message); + Adapt.error("\t" + e.getClass().getSimpleName() + (e.getMessage() != null ? ": " + e.getMessage() : "")); + } + + private String assembleUrl(AdaptConfig config) { + long connectTimeout = Math.max(1000L, config.getSql().getConnectionTimeout()); + long socketTimeout = Math.max(connectTimeout, connectTimeout * 2L); + return String.format( + "jdbc:mysql://%s:%d/%s?connectTimeout=%d&socketTimeout=%d", + config.getSql().getHost(), + config.getSql().getPort(), + config.getSql().getDatabase(), + connectTimeout, + socketTimeout + ); + } + + @FunctionalInterface + interface SqlAction { + void run(Connection connection) throws SQLException; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java b/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java index 522b1478f..2eb3e192b 100644 --- a/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java +++ b/src/main/java/art/arcane/adapt/util/common/io/ShittyGsonDataClass.java @@ -25,18 +25,18 @@ @Setter public class ShittyGsonDataClass { + @Getter + @Setter + public class Snippets { + @Getter @Setter - public class Snippets { - - @Getter - @Setter - public class SkillsGUI { - private String Level; - private String Knowledge; - private String PowerUsed; - } + public class SkillsGUI { + private String Level; + private String Knowledge; + private String PowerUsed; } + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java b/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java index fb1faffcc..7b542d96e 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java +++ b/src/main/java/art/arcane/adapt/util/common/math/ArrayType.java @@ -27,7 +27,7 @@ @Retention(RUNTIME) @Target({PARAMETER, TYPE, FIELD}) public @interface ArrayType { - Class type(); + Class type(); - int min() default 0; + int min() default 0; } diff --git a/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java b/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java index d6ec0eea9..fc1c1ff1f 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java +++ b/src/main/java/art/arcane/adapt/util/common/math/CarveResult.java @@ -22,10 +22,10 @@ @Value public class CarveResult { - private final int surface; - private final int ceiling; + private final int surface; + private final int ceiling; - public int getHeight() { - return ceiling - surface; - } + public int getHeight() { + return ceiling - surface; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java b/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java index fb85180de..c54aa811c 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java +++ b/src/main/java/art/arcane/adapt/util/common/math/CaveResult.java @@ -22,15 +22,15 @@ @Data public class CaveResult { - private int floor; - private int ceiling; + private int floor; + private int ceiling; - public CaveResult(int floor, int ceiling) { - this.floor = floor; - this.ceiling = ceiling; - } + public CaveResult(int floor, int ceiling) { + this.floor = floor; + this.ceiling = ceiling; + } - public boolean isWithin(int v) { - return v > floor || v < ceiling; - } + public boolean isWithin(int v) { + return v > floor || v < ceiling; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java b/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java index 448f4b798..576525e03 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java +++ b/src/main/java/art/arcane/adapt/util/common/math/ChunkPosition.java @@ -19,48 +19,48 @@ package art.arcane.adapt.util.common.math; public class ChunkPosition { - private int x; - private int z; + private int x; + private int z; - public ChunkPosition(int x, int z) { - this.x = x; - this.z = z; - } + public ChunkPosition(int x, int z) { + this.x = x; + this.z = z; + } - public int getX() { - return x; - } + public int getX() { + return x; + } - public void setX(int x) { - this.x = x; - } + public void setX(int x) { + this.x = x; + } - public int getZ() { - return z; - } + public int getZ() { + return z; + } - public void setZ(int z) { - this.z = z; - } + public void setZ(int z) { + this.z = z; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + x; - result = prime * result + z; - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + x; + result = prime * result + z; + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ChunkPosition)) { - return false; - } - ChunkPosition other = (ChunkPosition) obj; - return x == other.x && z == other.z; + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ChunkPosition)) { + return false; } + ChunkPosition other = (ChunkPosition) obj; + return x == other.x && z == other.z; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java index b27f8e20c..699631b24 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DataPalette.java @@ -27,100 +27,100 @@ import java.util.List; public abstract class DataPalette implements Writable { - private static final int DEFAULT_BITS_PER_BLOCK = 4; - private static final int CAPACITY = 4096; - private int bpb; - private NibbleArray data; - private List palette; - - public DataPalette(T defaultValue) { - palette = new ArrayList<>(); - bpb = DEFAULT_BITS_PER_BLOCK; - data = new NibbleArray(bpb, CAPACITY); - data.setAll(Byte.MIN_VALUE); - getPaletteId(defaultValue); + private static final int DEFAULT_BITS_PER_BLOCK = 4; + private static final int CAPACITY = 4096; + private int bpb; + private NibbleArray data; + private List palette; + + public DataPalette(T defaultValue) { + palette = new ArrayList<>(); + bpb = DEFAULT_BITS_PER_BLOCK; + data = new NibbleArray(bpb, CAPACITY); + data.setAll(Byte.MIN_VALUE); + getPaletteId(defaultValue); + } + + public abstract T readType(DataInputStream i) throws IOException; + + public abstract void writeType(T t, DataOutputStream o) throws IOException; + + @Override + public void write(DataOutputStream o) throws IOException { + o.writeByte(bpb + Byte.MIN_VALUE); + o.writeByte(palette.size() + Byte.MIN_VALUE); + + for (T i : palette) { + writeType(i, o); } - public abstract T readType(DataInputStream i) throws IOException; + data.write(o); + } - public abstract void writeType(T t, DataOutputStream o) throws IOException; + @Override + public void read(DataInputStream i) throws IOException { + bpb = i.readByte() - Byte.MIN_VALUE; + palette = new ArrayList<>(); + int v = i.readByte() - Byte.MIN_VALUE; - @Override - public void write(DataOutputStream o) throws IOException { - o.writeByte(bpb + Byte.MIN_VALUE); - o.writeByte(palette.size() + Byte.MIN_VALUE); - - for (T i : palette) { - writeType(i, o); - } - - data.write(o); + for (int j = 0; j < v; j++) { + palette.add(readType(i)); } - @Override - public void read(DataInputStream i) throws IOException { - bpb = i.readByte() - Byte.MIN_VALUE; - palette = new ArrayList<>(); - int v = i.readByte() - Byte.MIN_VALUE; - - for (int j = 0; j < v; j++) { - palette.add(readType(i)); - } + data = new NibbleArray(CAPACITY, i); + } - data = new NibbleArray(CAPACITY, i); + private final void expand() { + if (bpb < 8) { + changeBitsPerBlock(bpb + 1); + } else { + throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!"); } + } - private final void expand() { - if (bpb < 8) { - changeBitsPerBlock(bpb + 1); - } else { - throw new IndexOutOfBoundsException("The Data Palette can only handle at most 256 block types per 16x16x16 region. We cannot use more than 8 bits per block!"); - } - } - - public final void optimize() { - int targetBits = bpb; - int needed = palette.size(); - - for (int i = 1; i < bpb; i++) { - if (Math.pow(2, i) > needed) { - targetBits = i; - break; - } - } + public final void optimize() { + int targetBits = bpb; + int needed = palette.size(); - changeBitsPerBlock(targetBits); + for (int i = 1; i < bpb; i++) { + if (Math.pow(2, i) > needed) { + targetBits = i; + break; + } } - private final void changeBitsPerBlock(int bits) { - bpb = bits; - data = new NibbleArray(bpb, CAPACITY, data); - } + changeBitsPerBlock(targetBits); + } - public final void set(int x, int y, int z, T d) { - data.set(getCoordinateIndex(x, y, z), getPaletteId(d)); - } + private final void changeBitsPerBlock(int bits) { + bpb = bits; + data = new NibbleArray(bpb, CAPACITY, data); + } - public final T get(int x, int y, int z) { - return palette.get(data.get(getCoordinateIndex(x, y, z))); - } + public final void set(int x, int y, int z, T d) { + data.set(getCoordinateIndex(x, y, z), getPaletteId(d)); + } - private final int getPaletteId(T d) { - int index = palette.indexOf(d); + public final T get(int x, int y, int z) { + return palette.get(data.get(getCoordinateIndex(x, y, z))); + } - if (index == -1) { - index = palette.size(); - palette.add(d); + private final int getPaletteId(T d) { + int index = palette.indexOf(d); - if (palette.size() > Math.pow(2, bpb)) { - expand(); - } - } + if (index == -1) { + index = palette.size(); + palette.add(d); - return index + Byte.MIN_VALUE; + if (palette.size() > Math.pow(2, bpb)) { + expand(); + } } - private final int getCoordinateIndex(int x, int y, int z) { - return y << 8 | z << 4 | x; - } + return index + Byte.MIN_VALUE; + } + + private final int getCoordinateIndex(int x, int y, int z) { + return y << 8 | z << 4 | x; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/Dimension.java b/src/main/java/art/arcane/adapt/util/common/math/Dimension.java index 57123e48d..6ad1182c3 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Dimension.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Dimension.java @@ -24,66 +24,66 @@ * @author cyberpwn */ public class Dimension { - private final int width; - private final int height; - private final int depth; + private final int width; + private final int height; + private final int depth; - /** - * Make a dimension - * - * @param width width of this (X) - * @param height the height (Y) - * @param depth the depth (Z) - */ - public Dimension(int width, int height, int depth) { - this.width = width; - this.height = height; - this.depth = depth; - } - - /** - * Make a dimension - * - * @param width width of this (X) - * @param height the height (Y) - */ - public Dimension(int width, int height) { - this.width = width; - this.height = height; - this.depth = 0; - } - - /** - * Get the direction of the flat part of this dimension (null if no thin - * face) - * - * @return the direction of the flat pane or null - */ - public DimensionFace getPane() { - if (width == 1) { - return DimensionFace.X; - } - - if (height == 1) { - return DimensionFace.Y; - } + /** + * Make a dimension + * + * @param width width of this (X) + * @param height the height (Y) + * @param depth the depth (Z) + */ + public Dimension(int width, int height, int depth) { + this.width = width; + this.height = height; + this.depth = depth; + } - if (depth == 1) { - return DimensionFace.Z; - } + /** + * Make a dimension + * + * @param width width of this (X) + * @param height the height (Y) + */ + public Dimension(int width, int height) { + this.width = width; + this.height = height; + this.depth = 0; + } - return null; + /** + * Get the direction of the flat part of this dimension (null if no thin + * face) + * + * @return the direction of the flat pane or null + */ + public DimensionFace getPane() { + if (width == 1) { + return DimensionFace.X; } - public int getWidth() { - return width; + if (height == 1) { + return DimensionFace.Y; } - public int getHeight() { - return height; + if (depth == 1) { + return DimensionFace.Z; } - public int getDepth() { - return depth; - } + return null; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getDepth() { + return depth; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java b/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java index 270aaa8f7..5dea09935 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DimensionFace.java @@ -24,18 +24,18 @@ * @author cyberpwn */ public enum DimensionFace { - /** - * The X dimension (width) - */ - X, + /** + * The X dimension (width) + */ + X, - /** - * The Y dimension (height) - */ - Y, + /** + * The Y dimension (height) + */ + Y, - /** - * The Z dimension (depth) - */ - Z + /** + * The Z dimension (depth) + */ + Z } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/math/Direction.java b/src/main/java/art/arcane/adapt/util/common/math/Direction.java index 6de617093..e72805447 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Direction.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Direction.java @@ -18,8 +18,8 @@ package art.arcane.adapt.util.common.math; -import art.arcane.volmlib.util.data.Cuboid.CuboidDirection; import art.arcane.volmlib.util.collection.GBiset; +import art.arcane.volmlib.util.data.Cuboid.CuboidDirection; import art.arcane.volmlib.util.math.DOP; import org.bukkit.Axis; import org.bukkit.block.BlockFace; @@ -36,405 +36,405 @@ * @author cyberpwn */ public enum Direction { - U(0, 1, 0, CuboidDirection.Up), - D(0, -1, 0, CuboidDirection.Down), - N(0, 0, -1, CuboidDirection.North), - S(0, 0, 1, CuboidDirection.South), - E(1, 0, 0, CuboidDirection.East), - W(-1, 0, 0, CuboidDirection.West); - - private static Map, DOP> permute = null; - - private final int x; - private final int y; - private final int z; - private final CuboidDirection f; - - Direction(int x, int y, int z, CuboidDirection f) { - this.x = x; - this.y = y; - this.z = z; - this.f = f; - } - - public static Direction getDirection(BlockFace f) { - switch (f) { - case DOWN: - return D; - case EAST: - return E; - case EAST_NORTH_EAST: - return E; - case EAST_SOUTH_EAST: - return E; - case NORTH: - return N; - case NORTH_EAST: - return N; - case NORTH_NORTH_EAST: - return N; - case NORTH_NORTH_WEST: - return N; - case NORTH_WEST: - return N; - case SELF: - return U; - case SOUTH: - return S; - case SOUTH_EAST: - return S; - case SOUTH_SOUTH_EAST: - return S; - case SOUTH_SOUTH_WEST: - return S; - case SOUTH_WEST: - return S; - case UP: - return U; - case WEST: - return W; - case WEST_NORTH_WEST: - return W; - case WEST_SOUTH_WEST: - return W; - } - + U(0, 1, 0, CuboidDirection.Up), + D(0, -1, 0, CuboidDirection.Down), + N(0, 0, -1, CuboidDirection.North), + S(0, 0, 1, CuboidDirection.South), + E(1, 0, 0, CuboidDirection.East), + W(-1, 0, 0, CuboidDirection.West); + + private static Map, DOP> permute = null; + + private final int x; + private final int y; + private final int z; + private final CuboidDirection f; + + Direction(int x, int y, int z, CuboidDirection f) { + this.x = x; + this.y = y; + this.z = z; + this.f = f; + } + + public static Direction getDirection(BlockFace f) { + switch (f) { + case DOWN: return D; + case EAST: + return E; + case EAST_NORTH_EAST: + return E; + case EAST_SOUTH_EAST: + return E; + case NORTH: + return N; + case NORTH_EAST: + return N; + case NORTH_NORTH_EAST: + return N; + case NORTH_NORTH_WEST: + return N; + case NORTH_WEST: + return N; + case SELF: + return U; + case SOUTH: + return S; + case SOUTH_EAST: + return S; + case SOUTH_SOUTH_EAST: + return S; + case SOUTH_SOUTH_WEST: + return S; + case SOUTH_WEST: + return S; + case UP: + return U; + case WEST: + return W; + case WEST_NORTH_WEST: + return W; + case WEST_SOUTH_WEST: + return W; } - public static Direction closest(Vector v) { - double m = Double.MAX_VALUE; - Direction s = null; + return D; + } - for (Direction i : values()) { - Vector x = i.toVector(); - double g = x.dot(v); + public static Direction closest(Vector v) { + double m = Double.MAX_VALUE; + Direction s = null; - if (g < m) { - m = g; - s = i; - } - } + for (Direction i : values()) { + Vector x = i.toVector(); + double g = x.dot(v); - return s; + if (g < m) { + m = g; + s = i; + } } - public static Direction closest(Vector v, Direction... d) { - double m = Double.MAX_VALUE; - Direction s = null; + return s; + } - for (Direction i : d) { - Vector x = i.toVector(); - double g = x.distance(v); + public static Direction closest(Vector v, Direction... d) { + double m = Double.MAX_VALUE; + Direction s = null; - if (g < m) { - m = g; - s = i; - } - } + for (Direction i : d) { + Vector x = i.toVector(); + double g = x.distance(v); - return s; + if (g < m) { + m = g; + s = i; + } } - public static Direction closest(Vector v, List d) { - double m = Double.MAX_VALUE; - Direction s = null; + return s; + } - for (Direction i : d) { - Vector x = i.toVector(); - double g = x.distance(v); + public static Direction closest(Vector v, List d) { + double m = Double.MAX_VALUE; + Direction s = null; - if (g < m) { - m = g; - s = i; - } - } + for (Direction i : d) { + Vector x = i.toVector(); + double g = x.distance(v); - return s; + if (g < m) { + m = g; + s = i; + } } - public static List news() { - return Arrays.asList(N, E, W, S); - } + return s; + } - public static Direction getDirection(Vector v) { - Vector k = VectorMath.triNormalize(v.clone().normalize()); + public static List news() { + return Arrays.asList(N, E, W, S); + } - for (Direction i : udnews()) { - if (i.x == k.getBlockX() && i.y == k.getBlockY() && i.z == k.getBlockZ()) { - return i; - } - } + public static Direction getDirection(Vector v) { + Vector k = VectorMath.triNormalize(v.clone().normalize()); - return Direction.N; + for (Direction i : udnews()) { + if (i.x == k.getBlockX() && i.y == k.getBlockY() && i.z == k.getBlockZ()) { + return i; + } } - public static List udnews() { - return Arrays.asList(U, D, N, E, W, S); + return Direction.N; + } + + public static List udnews() { + return Arrays.asList(U, D, N, E, W, S); + } + + /** + * Get the directional value from the given byte from common directional + * blocks (MUST BE BETWEEN 0 and 5 INCLUSIVE) + * + * @param b the byte + * @return the direction or null if the byte is outside of the inclusive range + * 0-5 + */ + public static Direction fromByte(byte b) { + if (b > 5 || b < 0) { + return null; } - /** - * Get the directional value from the given byte from common directional blocks - * (MUST BE BETWEEN 0 and 5 INCLUSIVE) - * - * @param b the byte - * @return the direction or null if the byte is outside of the inclusive range - * 0-5 - */ - public static Direction fromByte(byte b) { - if (b > 5 || b < 0) { - return null; - } - - if (b == 0) { - return D; - } else if (b == 1) { - return U; - } else if (b == 2) { - return N; - } else if (b == 3) { - return S; - } else if (b == 4) { - return W; - } else { - return E; - } + if (b == 0) { + return D; + } else if (b == 1) { + return U; + } else if (b == 2) { + return N; + } else if (b == 3) { + return S; + } else if (b == 4) { + return W; + } else { + return E; } + } - public static void calculatePermutations() { - if (permute != null) { - return; - } - - permute = new HashMap<>(); - - for (Direction i : udnews()) { - for (Direction j : udnews()) { - GBiset b = new GBiset(i, j); - - if (i.equals(j)) { - permute.put(b, new DOP("DIRECT") { - @Override - public Vector op(Vector v) { - return v; - } - }); - } else if (i.reverse().equals(j)) { - if (i.isVertical()) { - permute.put(b, new DOP("R180CCZ") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CCZ(VectorMath.rotate90CCZ(v)); - } - }); - } else { - permute.put(b, new DOP("R180CCY") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CCY(VectorMath.rotate90CCY(v)); - } - }); - } - } else if (getDirection(VectorMath.rotate90CX(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CX") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CX(v); - } - }); - } else if (getDirection(VectorMath.rotate90CCX(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CCX") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CCX(v); - } - }); - } else if (getDirection(VectorMath.rotate90CY(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CY") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CY(v); - } - }); - } else if (getDirection(VectorMath.rotate90CCY(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CCY") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CCY(v); - } - }); - } else if (getDirection(VectorMath.rotate90CZ(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CZ") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CZ(v); - } - }); - } else if (getDirection(VectorMath.rotate90CCZ(i.toVector())).equals(j)) { - permute.put(b, new DOP("R90CCZ") { - @Override - public Vector op(Vector v) { - return VectorMath.rotate90CCZ(v); - } - }); - } else { - permute.put(b, new DOP("FAIL") { - @Override - public Vector op(Vector v) { - return v; - } - }); - } - } - } + public static void calculatePermutations() { + if (permute != null) { + return; } - @Override - public String toString() { - switch (this) { - case D: - return "Down"; - case E: - return "East"; - case N: - return "North"; - case S: - return "South"; - case U: - return "Up"; - case W: - return "West"; - } + permute = new HashMap<>(); - return "?"; - } + for (Direction i : udnews()) { + for (Direction j : udnews()) { + GBiset b = new GBiset(i, j); - public boolean isVertical() { - return equals(D) || equals(U); + if (i.equals(j)) { + permute.put(b, new DOP("DIRECT") { + @Override + public Vector op(Vector v) { + return v; + } + }); + } else if (i.reverse().equals(j)) { + if (i.isVertical()) { + permute.put(b, new DOP("R180CCZ") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CCZ(VectorMath.rotate90CCZ(v)); + } + }); + } else { + permute.put(b, new DOP("R180CCY") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CCY(VectorMath.rotate90CCY(v)); + } + }); + } + } else if (getDirection(VectorMath.rotate90CX(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CX") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CX(v); + } + }); + } else if (getDirection(VectorMath.rotate90CCX(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CCX") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CCX(v); + } + }); + } else if (getDirection(VectorMath.rotate90CY(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CY") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CY(v); + } + }); + } else if (getDirection(VectorMath.rotate90CCY(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CCY") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CCY(v); + } + }); + } else if (getDirection(VectorMath.rotate90CZ(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CZ") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CZ(v); + } + }); + } else if (getDirection(VectorMath.rotate90CCZ(i.toVector())).equals(j)) { + permute.put(b, new DOP("R90CCZ") { + @Override + public Vector op(Vector v) { + return VectorMath.rotate90CCZ(v); + } + }); + } else { + permute.put(b, new DOP("FAIL") { + @Override + public Vector op(Vector v) { + return v; + } + }); + } + } } - - public Vector toVector() { - return new Vector(x, y, z); + } + + @Override + public String toString() { + switch (this) { + case D: + return "Down"; + case E: + return "East"; + case N: + return "North"; + case S: + return "South"; + case U: + return "Up"; + case W: + return "West"; } - public boolean isCrooked(Direction to) { - if (equals(to.reverse())) { - return false; - } - - return !equals(to); - } + return "?"; + } - public Vector angle(Vector initial, Direction d) { - calculatePermutations(); + public boolean isVertical() { + return equals(D) || equals(U); + } - for (GBiset i : permute.keySet()) { - if (i.getA().equals(this) && i.getB().equals(d)) { - return permute.get(i).op(initial); - } - } + public Vector toVector() { + return new Vector(x, y, z); + } - return initial; + public boolean isCrooked(Direction to) { + if (equals(to.reverse())) { + return false; } - public Direction reverse() { - switch (this) { - case D: - return U; - case E: - return W; - case N: - return S; - case S: - return N; - case U: - return D; - case W: - return E; - default: - break; - } + return !equals(to); + } - return null; - } + public Vector angle(Vector initial, Direction d) { + calculatePermutations(); - public int x() { - return x; + for (GBiset i : permute.keySet()) { + if (i.getA().equals(this) && i.getB().equals(d)) { + return permute.get(i).op(initial); + } } - public int y() { - return y; + return initial; + } + + public Direction reverse() { + switch (this) { + case D: + return U; + case E: + return W; + case N: + return S; + case S: + return N; + case U: + return D; + case W: + return E; + default: + break; } - public int z() { - return z; + return null; + } + + public int x() { + return x; + } + + public int y() { + return y; + } + + public int z() { + return z; + } + + public CuboidDirection f() { + return f; + } + + /** + * Get the byte value represented in some directional blocks + * + * @return the byte value + */ + public byte byteValue() { + switch (this) { + case D: + return 0; + case E: + return 5; + case N: + return 2; + case S: + return 3; + case U: + return 1; + case W: + return 4; + default: + break; } - public CuboidDirection f() { - return f; + return -1; + } + + public BlockFace getFace() { + switch (this) { + case D: + return BlockFace.DOWN; + case E: + return BlockFace.EAST; + case N: + return BlockFace.NORTH; + case S: + return BlockFace.SOUTH; + case U: + return BlockFace.UP; + case W: + return BlockFace.WEST; } - /** - * Get the byte value represented in some directional blocks - * - * @return the byte value - */ - public byte byteValue() { - switch (this) { - case D: - return 0; - case E: - return 5; - case N: - return 2; - case S: - return 3; - case U: - return 1; - case W: - return 4; - default: - break; - } - - return -1; + return null; + } + + public Axis getAxis() { + switch (this) { + case D: + return Axis.Y; + case E: + return Axis.X; + case N: + return Axis.Z; + case S: + return Axis.Z; + case U: + return Axis.Y; + case W: + return Axis.X; } - public BlockFace getFace() { - switch (this) { - case D: - return BlockFace.DOWN; - case E: - return BlockFace.EAST; - case N: - return BlockFace.NORTH; - case S: - return BlockFace.SOUTH; - case U: - return BlockFace.UP; - case W: - return BlockFace.WEST; - } - - return null; - } - - public Axis getAxis() { - switch (this) { - case D: - return Axis.Y; - case E: - return Axis.X; - case N: - return Axis.Z; - case S: - return Axis.Z; - case U: - return Axis.Y; - case W: - return Axis.X; - } - - return null; - } + return null; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java b/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java index 12b8250b9..1c45cc14a 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/math/DoubleArrayUtils.java @@ -24,27 +24,27 @@ import java.util.Arrays; public class DoubleArrayUtils { - public static void shiftRight(double[] values, double push) { - for (int index = values.length - 2; index >= 0; index--) { - values[index + 1] = values[index]; - } - - values[0] = push; + public static void shiftRight(double[] values, double push) { + for (int index = values.length - 2; index >= 0; index--) { + values[index + 1] = values[index]; } - public static void wrapRight(double[] values) { - double last = values[values.length - 1]; - shiftRight(values, last); - } + values[0] = push; + } - public static void fill(double[] values, double value) { - Arrays.fill(values, value); - } + public static void wrapRight(double[] values) { + double last = values[values.length - 1]; + shiftRight(values, last); + } + + public static void fill(double[] values, double value) { + Arrays.fill(values, value); + } - public static void fill(AtomicDoubleArray values, double value) { - for (int i = 0; i < values.length(); i++) { - values.set(i, value); - } + public static void fill(AtomicDoubleArray values, double value) { + for (int i = 0; i < values.length(); i++) { + values.set(i, value); } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java b/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java index 3eec79aa9..8cb2d9482 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java +++ b/src/main/java/art/arcane/adapt/util/common/math/HeightMap.java @@ -21,18 +21,18 @@ import java.util.Arrays; public class HeightMap { - private final byte[] height; + private final byte[] height; - public HeightMap() { - height = new byte[256]; - Arrays.fill(height, Byte.MIN_VALUE); - } + public HeightMap() { + height = new byte[256]; + Arrays.fill(height, Byte.MIN_VALUE); + } - public void setHeight(int x, int z, int h) { - height[x * 16 + z] = (byte) (h + Byte.MIN_VALUE); - } + public void setHeight(int x, int z, int h) { + height[x * 16 + z] = (byte) (h + Byte.MIN_VALUE); + } - public int getHeight(int x, int z) { - return height[x * 16 + z] - Byte.MIN_VALUE; - } + public int getHeight(int x, int z) { + return height[x * 16 + z] - Byte.MIN_VALUE; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java b/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java index eb806dc24..ce0d7c65b 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IObjectPlacer.java @@ -21,21 +21,21 @@ import org.bukkit.block.data.BlockData; public interface IObjectPlacer { - int getHighest(int x, int z); + int getHighest(int x, int z); - int getHighest(int x, int z, boolean ignoreFluid); + int getHighest(int x, int z, boolean ignoreFluid); - void set(int x, int y, int z, BlockData d); + void set(int x, int y, int z, BlockData d); - BlockData get(int x, int y, int z); + BlockData get(int x, int y, int z); - boolean isPreventingDecay(); + boolean isPreventingDecay(); - boolean isSolid(int x, int y, int z); + boolean isSolid(int x, int y, int z); - boolean isUnderwater(int x, int z); + boolean isUnderwater(int x, int z); - int getFluidHeight(); + int getFluidHeight(); - boolean isDebugSmartBore(); + boolean isDebugSmartBore(); } diff --git a/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java b/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java index 2263a7a8e..cbced2c3f 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IPostBlockAccess.java @@ -24,15 +24,15 @@ import java.util.List; public interface IPostBlockAccess { - BlockData getPostBlock(int x, int y, int z, int currentPostX, int currentPostZ, ChunkData currentData); + BlockData getPostBlock(int x, int y, int z, int currentPostX, int currentPostZ, ChunkData currentData); - void setPostBlock(int x, int y, int z, BlockData d, int currentPostX, int currentPostZ, ChunkData currentData); + void setPostBlock(int x, int y, int z, BlockData d, int currentPostX, int currentPostZ, ChunkData currentData); - int highestTerrainOrFluidBlock(int x, int z); + int highestTerrainOrFluidBlock(int x, int z); - int highestTerrainBlock(int x, int z); + int highestTerrainBlock(int x, int z); - void updateHeight(int x, int z, int h); + void updateHeight(int x, int z, int h); - List caveFloors(int x, int z); + List caveFloors(int x, int z); } diff --git a/src/main/java/art/arcane/adapt/util/common/math/IRare.java b/src/main/java/art/arcane/adapt/util/common/math/IRare.java index 8a15c1c66..5a7f373f6 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/IRare.java +++ b/src/main/java/art/arcane/adapt/util/common/math/IRare.java @@ -19,9 +19,9 @@ package art.arcane.adapt.util.common.math; public interface IRare { - static int get(Object v) { - return v instanceof IRare ? ((IRare) v).getRarity() : 1; - } + static int get(Object v) { + return v instanceof IRare ? ((IRare) v).getRarity() : 1; + } - int getRarity(); + int getRarity(); } diff --git a/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java b/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java index 30a612ec2..5899caf0b 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java +++ b/src/main/java/art/arcane/adapt/util/common/math/InterpolationType.java @@ -19,9 +19,9 @@ package art.arcane.adapt.util.common.math; public enum InterpolationType { - LINEAR, - PARAMETRIC_2, - PARAMETRIC_4, - BEZIER, - NONE + LINEAR, + PARAMETRIC_2, + PARAMETRIC_4, + BEZIER, + NONE } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java b/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java index dfd6e9776..ec876e2fa 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java +++ b/src/main/java/art/arcane/adapt/util/common/math/MaterialBlock.java @@ -30,97 +30,97 @@ */ @SuppressWarnings("deprecation") public class MaterialBlock { - private Material material; - private Byte data; - - /** - * Create a materialblock - * - * @param material the material - * @param data the data - */ - public MaterialBlock(Material material, Byte data) { - this.material = material; - this.data = data; + private Material material; + private Byte data; + + /** + * Create a materialblock + * + * @param material the material + * @param data the data + */ + public MaterialBlock(Material material, Byte data) { + this.material = material; + this.data = data; + } + + public MaterialBlock(Material material) { + this.material = material; + data = 0; + } + + public MaterialBlock(Location location) { + this(location.getBlock()); + } + + public MaterialBlock(BlockState state) { + material = state.getType(); + data = state.getData().getData(); + } + + public MaterialBlock(Block block) { + material = block.getType(); + data = block.getData(); + } + + public MaterialBlock() { + material = Material.AIR; + data = 0; + } + + public Material getMaterial() { + return material; + } + + public void setMaterial(Material material) { + this.material = material; + } + + public Byte getData() { + return data; + } + + public void setData(Byte data) { + this.data = data; + } + + @Override + public String toString() { + if (getData() == 0) { + return getMaterial().toString(); } - public MaterialBlock(Material material) { - this.material = material; - data = 0; + return getMaterial().toString() + ":" + getData(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((data == null) ? 0 : data.hashCode()); + result = prime * result + ((material == null) ? 0 : material.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - public MaterialBlock(Location location) { - this(location.getBlock()); - } - - public MaterialBlock(BlockState state) { - material = state.getType(); - data = state.getData().getData(); - } - - public MaterialBlock(Block block) { - material = block.getType(); - data = block.getData(); - } - - public MaterialBlock() { - material = Material.AIR; - data = 0; - } - - public Material getMaterial() { - return material; - } - - public void setMaterial(Material material) { - this.material = material; - } - - public Byte getData() { - return data; - } - - public void setData(Byte data) { - this.data = data; + if (obj == null) { + return false; } - - @Override - public String toString() { - if (getData() == 0) { - return getMaterial().toString(); - } - - return getMaterial().toString() + ":" + getData(); + if (getClass() != obj.getClass()) { + return false; } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((data == null) ? 0 : data.hashCode()); - result = prime * result + ((material == null) ? 0 : material.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - MaterialBlock other = (MaterialBlock) obj; - if (data == null) { - if (other.data != null) { - return false; - } - } else if (!data.equals(other.data)) { - return false; - } - return material == other.material; + MaterialBlock other = (MaterialBlock) obj; + if (data == null) { + if (other.data != null) { + return false; + } + } else if (!data.equals(other.data)) { + return false; } + return material == other.material; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java index 107086c73..a6a251b4b 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java +++ b/src/main/java/art/arcane/adapt/util/common/math/MathHelper.java @@ -26,468 +26,468 @@ import java.util.function.IntPredicate; public class MathHelper { - public static final float a = MathHelper.c(2.0f); - private static final float[] b = (float[]) a((Object) new float[65536], var0 -> - { - for (int var1 = 0; var1 < ((float[]) var0).length; ++var1) { - ((float[]) var0)[var1] = (float) Math.sin((double) var1 * 3.141592653589793 * 2.0 / 65536.0); - } - }); - private static final Random c = new Random(); - private static final int[] d = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; - private static final double e = Double.longBitsToDouble(4805340802404319232L); - private static final double[] f = new double[257]; - private static final double[] g = new double[257]; + public static final float a = MathHelper.c(2.0f); + private static final float[] b = (float[]) a((Object) new float[65536], var0 -> + { + for (int var1 = 0; var1 < ((float[]) var0).length; ++var1) { + ((float[]) var0)[var1] = (float) Math.sin((double) var1 * 3.141592653589793 * 2.0 / 65536.0); + } + }); + private static final Random c = new Random(); + private static final int[] d = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; + private static final double e = Double.longBitsToDouble(4805340802404319232L); + private static final double[] f = new double[257]; + private static final double[] g = new double[257]; + + static { + for (int var02 = 0; var02 < 257; ++var02) { + double var1 = (double) var02 / 256.0; + double var3 = Math.asin(var1); + MathHelper.g[var02] = Math.cos(var3); + MathHelper.f[var02] = var3; + } + } + + public static T a(T var0, Consumer var1) { + var1.accept(var0); + return var0; + } - static { - for (int var02 = 0; var02 < 257; ++var02) { - double var1 = (double) var02 / 256.0; - double var3 = Math.asin(var1); - MathHelper.g[var02] = Math.cos(var3); - MathHelper.f[var02] = var3; - } - } + public static float sin(float var0) { + return b[(int) (var0 * 10430.378f) & 65535]; + } - public static T a(T var0, Consumer var1) { - var1.accept(var0); - return var0; - } + public static float cos(float var0) { + return b[(int) (var0 * 10430.378f + 16384.0f) & 65535]; + } - public static float sin(float var0) { - return b[(int) (var0 * 10430.378f) & 65535]; - } + public static float c(float var0) { + return (float) Math.sqrt(var0); + } - public static float cos(float var0) { - return b[(int) (var0 * 10430.378f + 16384.0f) & 65535]; - } + public static float sqrt(double var0) { + return (float) Math.sqrt(var0); + } - public static float c(float var0) { - return (float) Math.sqrt(var0); - } + public static int d(float var0) { + int var1 = (int) var0; + return var0 < (float) var1 ? var1 - 1 : var1; + } - public static float sqrt(double var0) { - return (float) Math.sqrt(var0); - } + public static int floor(double var0) { + int var2 = (int) var0; + return var0 < (double) var2 ? var2 - 1 : var2; + } - public static int d(float var0) { - int var1 = (int) var0; - return var0 < (float) var1 ? var1 - 1 : var1; - } + public static long d(double var0) { + long var2 = (long) var0; + return var0 < (double) var2 ? var2 - 1L : var2; + } - public static int floor(double var0) { - int var2 = (int) var0; - return var0 < (double) var2 ? var2 - 1 : var2; - } + public static float e(float var0) { + return Math.abs(var0); + } - public static long d(double var0) { - long var2 = (long) var0; - return var0 < (double) var2 ? var2 - 1L : var2; - } + public static int a(int var0) { + return Math.abs(var0); + } - public static float e(float var0) { - return Math.abs(var0); - } + public static int f(float var0) { + int var1 = (int) var0; + return var0 > (float) var1 ? var1 + 1 : var1; + } - public static int a(int var0) { - return Math.abs(var0); - } + public static int f(double var0) { + int var2 = (int) var0; + return var0 > (double) var2 ? var2 + 1 : var2; + } - public static int f(float var0) { - int var1 = (int) var0; - return var0 > (float) var1 ? var1 + 1 : var1; + public static int clamp(int var0, int var1, int var2) { + if (var0 < var1) { + return var1; } - - public static int f(double var0) { - int var2 = (int) var0; - return var0 > (double) var2 ? var2 + 1 : var2; + if (var0 > var2) { + return var2; } + return var0; + } - public static int clamp(int var0, int var1, int var2) { - if (var0 < var1) { - return var1; - } - if (var0 > var2) { - return var2; - } - return var0; + public static float a(float var0, float var1, float var2) { + if (var0 < var1) { + return var1; } - - public static float a(float var0, float var1, float var2) { - if (var0 < var1) { - return var1; - } - if (var0 > var2) { - return var2; - } - return var0; + if (var0 > var2) { + return var2; } + return var0; + } - public static double a(double var0, double var2, double var4) { - if (var0 < var2) { - return var2; - } - if (var0 > var4) { - return var4; - } - return var0; + public static double a(double var0, double var2, double var4) { + if (var0 < var2) { + return var2; } - - public static double b(double var0, double var2, double var4) { - if (var4 < 0.0) { - return var0; - } - if (var4 > 1.0) { - return var2; - } - return MathHelper.d(var4, var0, var2); + if (var0 > var4) { + return var4; } + return var0; + } - public static double a(double var0, double var2) { - if (var0 < 0.0) { - var0 = -var0; - } - if (var2 < 0.0) { - var2 = -var2; - } - return var0 > var2 ? var0 : var2; + public static double b(double var0, double var2, double var4) { + if (var4 < 0.0) { + return var0; } - - public static int a(int var0, int var1) { - return Math.floorDiv(var0, var1); + if (var4 > 1.0) { + return var2; } + return MathHelper.d(var4, var0, var2); + } - public static int nextInt(Random var0, int var1, int var2) { - if (var1 >= var2) { - return var1; - } - return var0.nextInt(var2 - var1 + 1) + var1; + public static double a(double var0, double var2) { + if (var0 < 0.0) { + var0 = -var0; } - - public static float a(Random var0, float var1, float var2) { - if (var1 >= var2) { - return var1; - } - return var0.nextFloat() * (var2 - var1) + var1; - } - - public static double a(Random var0, double var1, double var3) { - if (var1 >= var3) { - return var1; - } - return var0.nextDouble() * (var3 - var1) + var1; - } - - public static double a(long[] var0) { - long var1 = 0L; - for (long var6 : var0) { - var1 += var6; - } - return (double) var1 / (double) var0.length; - } - - public static boolean b(double var0, double var2) { - return Math.abs(var2 - var0) < 9.999999747378752E-6; + if (var2 < 0.0) { + var2 = -var2; } + return var0 > var2 ? var0 : var2; + } - public static int b(int var0, int var1) { - return Math.floorMod(var0, var1); - } + public static int a(int var0, int var1) { + return Math.floorDiv(var0, var1); + } - public static float g(float var0) { - float var1 = var0 % 360.0f; - if (var1 >= 180.0f) { - var1 -= 360.0f; - } - if (var1 < -180.0f) { - var1 += 360.0f; - } - return var1; + public static int nextInt(Random var0, int var1, int var2) { + if (var1 >= var2) { + return var1; } + return var0.nextInt(var2 - var1 + 1) + var1; + } - public static double g(double var0) { - double var2 = var0 % 360.0; - if (var2 >= 180.0) { - var2 -= 360.0; - } - if (var2 < -180.0) { - var2 += 360.0; - } - return var2; + public static float a(Random var0, float var1, float var2) { + if (var1 >= var2) { + return var1; } + return var0.nextFloat() * (var2 - var1) + var1; + } - public static float c(float var0, float var1) { - return MathHelper.g(var1 - var0); + public static double a(Random var0, double var1, double var3) { + if (var1 >= var3) { + return var1; } + return var0.nextDouble() * (var3 - var1) + var1; + } - public static float d(float var0, float var1) { - return MathHelper.e(MathHelper.c(var0, var1)); + public static double a(long[] var0) { + long var1 = 0L; + for (long var6 : var0) { + var1 += var6; } + return (double) var1 / (double) var0.length; + } - public static float b(float var0, float var1, float var2) { - float var3 = MathHelper.c(var0, var1); - float var4 = MathHelper.a(var3, -var2, var2); - return var1 - var4; - } + public static boolean b(double var0, double var2) { + return Math.abs(var2 - var0) < 9.999999747378752E-6; + } - public static float c(float var0, float var1, float var2) { - var2 = MathHelper.e(var2); - if (var0 < var1) { - return MathHelper.a(var0 + var2, var0, var1); - } - return MathHelper.a(var0 - var2, var1, var0); - } + public static int b(int var0, int var1) { + return Math.floorMod(var0, var1); + } - public static float d(float var0, float var1, float var2) { - float var3 = MathHelper.c(var0, var1); - return MathHelper.c(var0, var0 + var3, var2); + public static float g(float var0) { + float var1 = var0 % 360.0f; + if (var1 >= 180.0f) { + var1 -= 360.0f; } - - public static int c(int var0) { - int var1 = var0 - 1; - var1 |= var1 >> 1; - var1 |= var1 >> 2; - var1 |= var1 >> 4; - var1 |= var1 >> 8; - var1 |= var1 >> 16; - return var1 + 1; + if (var1 < -180.0f) { + var1 += 360.0f; } + return var1; + } - public static boolean d(int var0) { - return var0 != 0 && (var0 & var0 - 1) == 0; + public static double g(double var0) { + double var2 = var0 % 360.0; + if (var2 >= 180.0) { + var2 -= 360.0; } - - public static int e(int var0) { - var0 = MathHelper.d(var0) ? var0 : MathHelper.c(var0); - return d[(int) ((long) var0 * 125613361L >> 27) & 31]; + if (var2 < -180.0) { + var2 += 360.0; } + return var2; + } - public static int f(int var0) { - return MathHelper.e(var0) - (MathHelper.d(var0) ? 0 : 1); - } + public static float c(float var0, float var1) { + return MathHelper.g(var1 - var0); + } - public static int c(int var0, int var1) { - int var2; - if (var1 == 0) { - return 0; - } - if (var0 == 0) { - return var1; - } - if (var0 < 0) { - var1 *= -1; - } - if ((var2 = var0 % var1) == 0) { - return var0; - } - return var0 + var1 - var2; - } - - public static float h(float var0) { - return var0 - (float) MathHelper.d(var0); - } - - public static double h(double var0) { - return var0 - (double) MathHelper.d(var0); - } - - public static long a(BlockPosition var0) { - return c(var0.getX(), var0.getY(), var0.getZ()); - } - - public static long c(int var0, int var1, int var2) { - long var3 = (long) (var0 * 3129871) ^ (long) var2 * 116129781L ^ (long) var1; - var3 = var3 * var3 * 42317861L + var3 * 11L; - return var3 >> 16; - } - - public static UUID a(Random var0) { - long var1 = var0.nextLong() & -61441L | 16384L; - long var3 = var0.nextLong() & 0x3FFFFFFFFFFFFFFFL | Long.MIN_VALUE; - return new UUID(var1, var3); - } - - public static UUID a() { - return MathHelper.a(c); - } - - public static double c(double var0, double var2, double var4) { - return (var0 - var2) / (var4 - var2); - } - - public static double d(double var0, double var2) { - double var9; - boolean var6; - boolean var7; - boolean var8; - double var4 = var2 * var2 + var0 * var0; - if (Double.isNaN(var4)) { - return Double.NaN; - } - @SuppressWarnings("unused") - boolean bl = var6 = var0 < 0.0; - if (var6) { - var0 = -var0; - } - @SuppressWarnings("unused") - boolean bl2 = var7 = var2 < 0.0; - if (var7) { - var2 = -var2; - } - @SuppressWarnings("unused") - boolean bl3 = var8 = var0 > var2; - if (var8) { - var9 = var2; - var2 = var0; - var0 = var9; - } - var9 = MathHelper.i(var4); - double var11 = e + (var0 *= var9); - int var13 = (int) Double.doubleToRawLongBits(var11); - double var14 = f[var13]; - double var16 = g[var13]; - double var18 = var11 - e; - double var20 = var0 * var16 - (var2 *= var9) * var18; - double var22 = (6.0 + var20 * var20) * var20 * 0.16666666666666666; - double var24 = var14 + var22; - if (var8) { - var24 = 1.5707963267948966 - var24; - } - if (var7) { - var24 = 3.141592653589793 - var24; - } - if (var6) { - var24 = -var24; - } - return var24; - } - - public static double i(double var0) { - double var2 = 0.5 * var0; - long var4 = Double.doubleToRawLongBits(var0); - var4 = 6910469410427058090L - (var4 >> 1); - var0 = Double.longBitsToDouble(var4); - var0 *= 1.5 - var2 * var0 * var0; - return var0; - } - - public static int f(float var0, float var1, float var2) { - float var9; - float var8; - float var10; - int var3 = (int) (var0 * 6.0f) % 6; - float var4 = var0 * 6.0f - (float) var3; - float var5 = var2 * (1.0f - var1); - float var6 = var2 * (1.0f - var4 * var1); - float var7 = var2 * (1.0f - (1.0f - var4) * var1); - switch (var3) { - case 0: { - var8 = var2; - var9 = var7; - var10 = var5; - break; - } - case 1: { - var8 = var6; - var9 = var2; - var10 = var5; - break; - } - case 2: { - var8 = var5; - var9 = var2; - var10 = var7; - break; - } - case 3: { - var8 = var5; - var9 = var6; - var10 = var2; - break; - } - case 4: { - var8 = var7; - var9 = var5; - var10 = var2; - break; - } - case 5: { - var8 = var2; - var9 = var5; - var10 = var6; - break; - } - default: { - throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + var0 + ", " + var1 + ", " + var2); - } - } - int var11 = MathHelper.clamp((int) (var8 * 255.0f), 0, 255); - int var12 = MathHelper.clamp((int) (var9 * 255.0f), 0, 255); - int var13 = MathHelper.clamp((int) (var10 * 255.0f), 0, 255); - return var11 << 16 | var12 << 8 | var13; - } - - public static int g(int var0) { - var0 ^= var0 >>> 16; - var0 *= -2048144789; - var0 ^= var0 >>> 13; - var0 *= -1028477387; - var0 ^= var0 >>> 16; - return var0; - } - - public static int a(int var0, int var1, IntPredicate var2) { - int var3 = var1 - var0; - while (var3 > 0) { - int var4 = var3 / 2; - int var5 = var0 + var4; - if (var2.test(var5)) { - var3 = var4; - continue; - } - var0 = var5 + 1; - var3 -= var4 + 1; - } - return var0; - } - - public static float g(float var0, float var1, float var2) { - return var1 + var0 * (var2 - var1); - } - - public static double d(double var0, double var2, double var4) { - return var2 + var0 * (var4 - var2); - } - - public static double a(double var0, double var2, double var4, double var6, double var8, double var10) { - return MathHelper.d(var2, MathHelper.d(var0, var4, var6), MathHelper.d(var0, var8, var10)); - } - - public static double a(double var0, double var2, double var4, double var6, double var8, double var10, double var12, double var14, double var16, double var18, double var20) { - return MathHelper.d(var4, MathHelper.a(var0, var2, var6, var8, var10, var12), MathHelper.a(var0, var2, var14, var16, var18, var20)); - } - - public static double j(double var0) { - return var0 * var0 * var0 * (var0 * (var0 * 6.0 - 15.0) + 10.0); - } - - public static int k(double var0) { - if (var0 == 0.0) { - return 0; - } - return var0 > 0.0 ? 1 : -1; - } - - public static float j(float var0, float var1, float var2) { - float var3; - for (var3 = var1 - var0; var3 < -180.0f; var3 += 360.0f) { - } - while (var3 >= 180.0f) { - var3 -= 360.0f; - } - return var0 + var2 * var3; - } - - public static float k(float var0) { - return var0 * var0; - } + public static float d(float var0, float var1) { + return MathHelper.e(MathHelper.c(var0, var1)); + } + + public static float b(float var0, float var1, float var2) { + float var3 = MathHelper.c(var0, var1); + float var4 = MathHelper.a(var3, -var2, var2); + return var1 - var4; + } + + public static float c(float var0, float var1, float var2) { + var2 = MathHelper.e(var2); + if (var0 < var1) { + return MathHelper.a(var0 + var2, var0, var1); + } + return MathHelper.a(var0 - var2, var1, var0); + } + + public static float d(float var0, float var1, float var2) { + float var3 = MathHelper.c(var0, var1); + return MathHelper.c(var0, var0 + var3, var2); + } + + public static int c(int var0) { + int var1 = var0 - 1; + var1 |= var1 >> 1; + var1 |= var1 >> 2; + var1 |= var1 >> 4; + var1 |= var1 >> 8; + var1 |= var1 >> 16; + return var1 + 1; + } + + public static boolean d(int var0) { + return var0 != 0 && (var0 & var0 - 1) == 0; + } + + public static int e(int var0) { + var0 = MathHelper.d(var0) ? var0 : MathHelper.c(var0); + return d[(int) ((long) var0 * 125613361L >> 27) & 31]; + } + + public static int f(int var0) { + return MathHelper.e(var0) - (MathHelper.d(var0) ? 0 : 1); + } + + public static int c(int var0, int var1) { + int var2; + if (var1 == 0) { + return 0; + } + if (var0 == 0) { + return var1; + } + if (var0 < 0) { + var1 *= -1; + } + if ((var2 = var0 % var1) == 0) { + return var0; + } + return var0 + var1 - var2; + } + + public static float h(float var0) { + return var0 - (float) MathHelper.d(var0); + } + + public static double h(double var0) { + return var0 - (double) MathHelper.d(var0); + } + + public static long a(BlockPosition var0) { + return c(var0.getX(), var0.getY(), var0.getZ()); + } + + public static long c(int var0, int var1, int var2) { + long var3 = (long) (var0 * 3129871) ^ (long) var2 * 116129781L ^ (long) var1; + var3 = var3 * var3 * 42317861L + var3 * 11L; + return var3 >> 16; + } + + public static UUID a(Random var0) { + long var1 = var0.nextLong() & -61441L | 16384L; + long var3 = var0.nextLong() & 0x3FFFFFFFFFFFFFFFL | Long.MIN_VALUE; + return new UUID(var1, var3); + } + + public static UUID a() { + return MathHelper.a(c); + } + + public static double c(double var0, double var2, double var4) { + return (var0 - var2) / (var4 - var2); + } + + public static double d(double var0, double var2) { + double var9; + boolean var6; + boolean var7; + boolean var8; + double var4 = var2 * var2 + var0 * var0; + if (Double.isNaN(var4)) { + return Double.NaN; + } + @SuppressWarnings("unused") + boolean bl = var6 = var0 < 0.0; + if (var6) { + var0 = -var0; + } + @SuppressWarnings("unused") + boolean bl2 = var7 = var2 < 0.0; + if (var7) { + var2 = -var2; + } + @SuppressWarnings("unused") + boolean bl3 = var8 = var0 > var2; + if (var8) { + var9 = var2; + var2 = var0; + var0 = var9; + } + var9 = MathHelper.i(var4); + double var11 = e + (var0 *= var9); + int var13 = (int) Double.doubleToRawLongBits(var11); + double var14 = f[var13]; + double var16 = g[var13]; + double var18 = var11 - e; + double var20 = var0 * var16 - (var2 *= var9) * var18; + double var22 = (6.0 + var20 * var20) * var20 * 0.16666666666666666; + double var24 = var14 + var22; + if (var8) { + var24 = 1.5707963267948966 - var24; + } + if (var7) { + var24 = 3.141592653589793 - var24; + } + if (var6) { + var24 = -var24; + } + return var24; + } + + public static double i(double var0) { + double var2 = 0.5 * var0; + long var4 = Double.doubleToRawLongBits(var0); + var4 = 6910469410427058090L - (var4 >> 1); + var0 = Double.longBitsToDouble(var4); + var0 *= 1.5 - var2 * var0 * var0; + return var0; + } + + public static int f(float var0, float var1, float var2) { + float var9; + float var8; + float var10; + int var3 = (int) (var0 * 6.0f) % 6; + float var4 = var0 * 6.0f - (float) var3; + float var5 = var2 * (1.0f - var1); + float var6 = var2 * (1.0f - var4 * var1); + float var7 = var2 * (1.0f - (1.0f - var4) * var1); + switch (var3) { + case 0: { + var8 = var2; + var9 = var7; + var10 = var5; + break; + } + case 1: { + var8 = var6; + var9 = var2; + var10 = var5; + break; + } + case 2: { + var8 = var5; + var9 = var2; + var10 = var7; + break; + } + case 3: { + var8 = var5; + var9 = var6; + var10 = var2; + break; + } + case 4: { + var8 = var7; + var9 = var5; + var10 = var2; + break; + } + case 5: { + var8 = var2; + var9 = var5; + var10 = var6; + break; + } + default: { + throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + var0 + ", " + var1 + ", " + var2); + } + } + int var11 = MathHelper.clamp((int) (var8 * 255.0f), 0, 255); + int var12 = MathHelper.clamp((int) (var9 * 255.0f), 0, 255); + int var13 = MathHelper.clamp((int) (var10 * 255.0f), 0, 255); + return var11 << 16 | var12 << 8 | var13; + } + + public static int g(int var0) { + var0 ^= var0 >>> 16; + var0 *= -2048144789; + var0 ^= var0 >>> 13; + var0 *= -1028477387; + var0 ^= var0 >>> 16; + return var0; + } + + public static int a(int var0, int var1, IntPredicate var2) { + int var3 = var1 - var0; + while (var3 > 0) { + int var4 = var3 / 2; + int var5 = var0 + var4; + if (var2.test(var5)) { + var3 = var4; + continue; + } + var0 = var5 + 1; + var3 -= var4 + 1; + } + return var0; + } + + public static float g(float var0, float var1, float var2) { + return var1 + var0 * (var2 - var1); + } + + public static double d(double var0, double var2, double var4) { + return var2 + var0 * (var4 - var2); + } + + public static double a(double var0, double var2, double var4, double var6, double var8, double var10) { + return MathHelper.d(var2, MathHelper.d(var0, var4, var6), MathHelper.d(var0, var8, var10)); + } + + public static double a(double var0, double var2, double var4, double var6, double var8, double var10, double var12, double var14, double var16, double var18, double var20) { + return MathHelper.d(var4, MathHelper.a(var0, var2, var6, var8, var10, var12), MathHelper.a(var0, var2, var14, var16, var18, var20)); + } + + public static double j(double var0) { + return var0 * var0 * var0 * (var0 * (var0 * 6.0 - 15.0) + 10.0); + } + + public static int k(double var0) { + if (var0 == 0.0) { + return 0; + } + return var0 > 0.0 ? 1 : -1; + } + + public static float j(float var0, float var1, float var2) { + float var3; + for (var3 = var1 - var0; var3 < -180.0f; var3 += 360.0f) { + } + while (var3 >= 180.0f) { + var3 -= 360.0f; + } + return var0 + var2 * var3; + } + + public static float k(float var0) { + return var0 * var0; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/Sphere.java b/src/main/java/art/arcane/adapt/util/common/math/Sphere.java index 105652cba..e761b1b5c 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Sphere.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Sphere.java @@ -6,45 +6,45 @@ import java.util.Iterator; public class Sphere implements Iterator, Cloneable { - private final KList blocks; - private int i = 0; - - public Sphere(int radius) { - int dist = radius * radius * radius; - - blocks = new KList<>(); - for (int x = -radius; x <= radius; x++) { - for (int z = -radius; z <= radius; z++) { - for (int y = -radius; y <= radius; y++) { - if (x * x + z * z + y * y > dist) - continue; - - blocks.add(new BlockPosition(x, y, z)); - } - } - } - } + private final KList blocks; + private int i = 0; - private Sphere(KList blocks) { - this.blocks = blocks.copy(); - } + public Sphere(int radius) { + int dist = radius * radius * radius; - public void reset() { - i = 0; - } + blocks = new KList<>(); + for (int x = -radius; x <= radius; x++) { + for (int z = -radius; z <= radius; z++) { + for (int y = -radius; y <= radius; y++) { + if (x * x + z * z + y * y > dist) + continue; - @Override - public boolean hasNext() { - return i < blocks.size(); - } - - @Override - public BlockPosition next() { - return blocks.get(i++); - } - - @Override - public Sphere clone() { - return new Sphere(blocks); + blocks.add(new BlockPosition(x, y, z)); + } + } } + } + + private Sphere(KList blocks) { + this.blocks = blocks.copy(); + } + + public void reset() { + i = 0; + } + + @Override + public boolean hasNext() { + return i < blocks.size(); + } + + @Override + public BlockPosition next() { + return blocks.get(i++); + } + + @Override + public Sphere clone() { + return new Sphere(blocks); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java index 08fa2a136..4845de80c 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VectorMath.java @@ -18,8 +18,8 @@ package art.arcane.adapt.util.common.math; -import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.collection.GListAdapter; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.CDou; import org.bukkit.Axis; import org.bukkit.Bukkit; @@ -37,587 +37,587 @@ * @author cyberpwn */ public class VectorMath { - public static Vector scaleStatic(Axis x, Vector v, double amt) { - switch (x) { - case X: - return scaleX(v, amt); - case Y: - return scaleY(v, amt); - case Z: - return scaleZ(v, amt); + public static Vector scaleStatic(Axis x, Vector v, double amt) { + switch (x) { + case X: + return scaleX(v, amt); + case Y: + return scaleY(v, amt); + case Z: + return scaleZ(v, amt); + } + + return null; + } + + public static Vector scaleX(Vector v, double amt) { + double x = v.getX(); + double y = v.getY(); + double z = v.getZ(); + double rx = x == 0 ? 1 : amt / x; + + return new Vector(x * rx, y * rx, z * rx); + } + + public static Vector scaleY(Vector v, double amt) { + double x = v.getX(); + double y = v.getY(); + double z = v.getZ(); + double rx = y == 0 ? 1 : amt / y; + + return new Vector(x * rx, y * rx, z * rx); + } + + public static Vector scaleZ(Vector v, double amt) { + double x = v.getX(); + double y = v.getY(); + double z = v.getZ(); + double rx = z == 0 ? 1 : amt / z; + + return new Vector(x * rx, y * rx, z * rx); + } + + public static Vector reverseXZ(Vector v) { + v.setX(-v.getX()); + v.setZ(-v.getZ()); + return v; + } + + public static boolean isLookingNear(Location a, Location b, double maxOff) { + Vector perfect = VectorMath.direction(a, b); + Vector actual = a.getDirection(); + + return perfect.distance(actual) <= maxOff; + } + + public static Vector rotate(Direction current, Direction to, Vector v) { + if (current.equals(to)) { + return v; + } else if (current.equals(to.reverse())) { + if (current.isVertical()) { + return new Vector(v.getX(), -v.getY(), v.getZ()); + } else { + return new Vector(-v.getX(), v.getY(), -v.getZ()); + } + } else { + Vector c = current.toVector().clone().add(to.toVector()); + + if (c.getX() == 0) { + if (c.getY() != c.getZ()) { + return rotate90CX(v); } - return null; - } - - public static Vector scaleX(Vector v, double amt) { - double x = v.getX(); - double y = v.getY(); - double z = v.getZ(); - double rx = x == 0 ? 1 : amt / x; - - return new Vector(x * rx, y * rx, z * rx); - } - - public static Vector scaleY(Vector v, double amt) { - double x = v.getX(); - double y = v.getY(); - double z = v.getZ(); - double rx = y == 0 ? 1 : amt / y; - - return new Vector(x * rx, y * rx, z * rx); - } - - public static Vector scaleZ(Vector v, double amt) { - double x = v.getX(); - double y = v.getY(); - double z = v.getZ(); - double rx = z == 0 ? 1 : amt / z; - - return new Vector(x * rx, y * rx, z * rx); - } - - public static Vector reverseXZ(Vector v) { - v.setX(-v.getX()); - v.setZ(-v.getZ()); - return v; - } - - public static boolean isLookingNear(Location a, Location b, double maxOff) { - Vector perfect = VectorMath.direction(a, b); - Vector actual = a.getDirection(); - - return perfect.distance(actual) <= maxOff; - } - - public static Vector rotate(Direction current, Direction to, Vector v) { - if (current.equals(to)) { - return v; - } else if (current.equals(to.reverse())) { - if (current.isVertical()) { - return new Vector(v.getX(), -v.getY(), v.getZ()); - } else { - return new Vector(-v.getX(), v.getY(), -v.getZ()); - } - } else { - Vector c = current.toVector().clone().add(to.toVector()); - - if (c.getX() == 0) { - if (c.getY() != c.getZ()) { - return rotate90CX(v); - } - - return rotate90CCX(v); - } else if (c.getY() == 0) { - if (c.getX() != c.getZ()) { - return rotate90CY(v); - } - - return rotate90CCY(v); - } else if (c.getZ() == 0) { - if (c.getX() != c.getY()) { - return rotate90CZ(v); - } - - return rotate90CCZ(v); - } + return rotate90CCX(v); + } else if (c.getY() == 0) { + if (c.getX() != c.getZ()) { + return rotate90CY(v); } - return v; - } - - // Y X 0 0 - // X Z 0 0 - - // 0 X Y 0 - // 0 Z X 0 - - public static Vector rotate(Direction current, Direction to, Vector v, int w, int h, int d) { - if (current.equals(to)) { - return v; - } else if (current.equals(to.reverse())) { - if (current.isVertical()) { - return new Vector(v.getX(), -v.getY() + h, v.getZ()); - } else { - return new Vector(-v.getX() + w, v.getY(), -v.getZ() + d); - } - } else { - Vector c = current.toVector().clone().add(to.toVector()); - - if (c.getX() == 0) { - if (c.getY() != c.getZ()) { - return rotate90CX(v, d); - } - - return rotate90CCX(v, h); - } else if (c.getY() == 0) { - if (c.getX() != c.getZ()) { - return rotate90CY(v, d); - } - - return rotate90CCY(v, w); - } else if (c.getZ() == 0) { - if (c.getX() != c.getY()) { - return rotate90CZ(v, w); - } - - return rotate90CCZ(v, h); - } + return rotate90CCY(v); + } else if (c.getZ() == 0) { + if (c.getX() != c.getY()) { + return rotate90CZ(v); } - return v; + return rotate90CCZ(v); + } } - public static Vector rotate90CX(Vector v) { - return new Vector(v.getX(), -v.getZ(), v.getY()); - } - - public static Vector rotate90CCX(Vector v) { - return new Vector(v.getX(), v.getZ(), -v.getY()); - } + return v; + } - public static Vector rotate90CY(Vector v) { - return new Vector(-v.getZ(), v.getY(), v.getX()); - } + // Y X 0 0 + // X Z 0 0 - public static Vector rotate90CCY(Vector v) { - return new Vector(v.getZ(), v.getY(), -v.getX()); - } + // 0 X Y 0 + // 0 Z X 0 - public static Vector rotate90CZ(Vector v) { - return new Vector(v.getY(), -v.getX(), v.getZ()); - } + public static Vector rotate(Direction current, Direction to, Vector v, int w, int h, int d) { + if (current.equals(to)) { + return v; + } else if (current.equals(to.reverse())) { + if (current.isVertical()) { + return new Vector(v.getX(), -v.getY() + h, v.getZ()); + } else { + return new Vector(-v.getX() + w, v.getY(), -v.getZ() + d); + } + } else { + Vector c = current.toVector().clone().add(to.toVector()); - public static Vector rotate90CCZ(Vector v) { - return new Vector(-v.getY(), v.getX(), v.getZ()); - } - - public static Vector rotate90CX(Vector v, int s) { - return new Vector(v.getX(), -v.getZ() + s, v.getY()); - } - - public static Vector rotate90CCX(Vector v, int s) { - return new Vector(v.getX(), v.getZ(), -v.getY() + s); - } - - public static Vector rotate90CY(Vector v, int s) { - return new Vector(-v.getZ() + s, v.getY(), v.getX()); - } - - public static Vector rotate90CCY(Vector v, int s) { - return new Vector(v.getZ(), v.getY(), -v.getX() + s); - } - - public static Vector rotate90CZ(Vector v, int s) { - return new Vector(v.getY(), -v.getX() + s, v.getZ()); - } - - public static Vector rotate90CCZ(Vector v, int s) { - return new Vector(-v.getY() + s, v.getX(), v.getZ()); - } - - public static Vector getAxis(Direction current, Direction to) { - if (current.equals(Direction.U) || current.equals(Direction.D)) { - if (to.equals(Direction.U) || to.equals(Direction.D)) { - return new Vector(1, 0, 0); - } else { - if (current.equals(Direction.N) || current.equals(Direction.S)) { - return Direction.E.toVector(); - } else { - return Direction.S.toVector(); - } - } + if (c.getX() == 0) { + if (c.getY() != c.getZ()) { + return rotate90CX(v, d); } - return new Vector(0, 1, 0); - } - - private static double round(double value, int precision) { - return Double.valueOf(Form.f(value, precision)); - } - - public static Vector clip(Vector v, int decimals) { - v.setX(round(v.getX(), decimals)); - v.setY(round(v.getY(), decimals)); - v.setZ(round(v.getZ(), decimals)); - return v; - } - - public static Vector rotateVectorCC(Vector vec, Vector axis, double deg) { - double theta = Math.toRadians(deg); - double x, y, z; - double u, v, w; - x = vec.getX(); - y = vec.getY(); - z = vec.getZ(); - u = axis.getX(); - v = axis.getY(); - w = axis.getZ(); - double xPrime = u * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + x * Math.cos(theta) + (-w * y + v * z) * Math.sin(theta); - double yPrime = v * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + y * Math.cos(theta) + (w * x - u * z) * Math.sin(theta); - double zPrime = w * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + z * Math.cos(theta) + (-v * x + u * y) * Math.sin(theta); - - return clip(new Vector(xPrime, yPrime, zPrime), 4); - } - - /** - * Get all SIMPLE block faces from a more specific block face (SOUTH_EAST) = - * (south, east) - * - * @param f the block face - * @return multiple faces, or one if the face is already simple - */ - public static List split(BlockFace f) { - List faces = new ArrayList<>(); - - switch (f) { - case DOWN: - faces.add(BlockFace.DOWN); - break; - case EAST: - faces.add(BlockFace.EAST); - break; - case EAST_NORTH_EAST: - faces.add(BlockFace.EAST); - faces.add(BlockFace.EAST); - faces.add(BlockFace.NORTH); - break; - case EAST_SOUTH_EAST: - faces.add(BlockFace.EAST); - faces.add(BlockFace.EAST); - faces.add(BlockFace.SOUTH); - break; - case NORTH: - faces.add(BlockFace.NORTH); - break; - case NORTH_EAST: - faces.add(BlockFace.NORTH); - faces.add(BlockFace.EAST); - break; - case NORTH_NORTH_EAST: - faces.add(BlockFace.NORTH); - faces.add(BlockFace.NORTH); - faces.add(BlockFace.EAST); - break; - case NORTH_NORTH_WEST: - faces.add(BlockFace.NORTH); - faces.add(BlockFace.NORTH); - faces.add(BlockFace.WEST); - break; - case NORTH_WEST: - faces.add(BlockFace.NORTH); - faces.add(BlockFace.WEST); - break; - case SELF: - faces.add(BlockFace.SELF); - break; - case SOUTH: - faces.add(BlockFace.SOUTH); - break; - case SOUTH_EAST: - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.EAST); - break; - case SOUTH_SOUTH_EAST: - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.EAST); - break; - case SOUTH_SOUTH_WEST: - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.WEST); - break; - case SOUTH_WEST: - faces.add(BlockFace.SOUTH); - faces.add(BlockFace.WEST); - break; - case UP: - faces.add(BlockFace.UP); - break; - case WEST: - faces.add(BlockFace.WEST); - break; - case WEST_NORTH_WEST: - faces.add(BlockFace.WEST); - faces.add(BlockFace.WEST); - faces.add(BlockFace.NORTH); - break; - case WEST_SOUTH_WEST: - faces.add(BlockFace.WEST); - faces.add(BlockFace.WEST); - faces.add(BlockFace.SOUTH); - break; - default: - break; - } - - return faces; - } - - /** - * Get a normalized vector going from a location to another - * - * @param from from here - * @param to to here - * @return the normalized vector direction - */ - public static Vector direction(Location from, Location to) { - return to.clone().subtract(from.clone()).toVector().normalize(); - } - - public static Vector directionNoNormal(Location from, Location to) { - return to.clone().subtract(from.clone()).toVector(); - } - - /** - * Get the vector direction from the yaw and pitch - * - * @param yaw the yaw - * @param pitch the pitch - * @return the vector - */ - public static Vector toVector(float yaw, float pitch) { - return new Vector(Math.cos(pitch) * Math.cos(yaw), Math.sin(pitch), Math.cos(pitch) * Math.sin(-yaw)); - } - - /** - * Add an impulse (force) to an entity - * - * @param e the entity - * @param v the vector - */ - public static void impulse(Entity e, Vector v) { - impulse(e, v, 1.0); - } - - /** - * Add an impulse (force) on an entity - * - * @param e the entity - * @param v the vector - * @param effectiveness the effectiveness - */ - public static void impulse(Entity e, Vector v, double effectiveness) { - Vector vx = e.getVelocity(); - vx.add(v.clone().multiply(effectiveness)); - e.setVelocity(vx); - } - - /** - * Reverse a direction - * - * @param v the direction - * @return the reversed direction - */ - public static Vector reverse(Vector v) { - if (v.getX() != 0) { - v.setX(-v.getX()); + return rotate90CCX(v, h); + } else if (c.getY() == 0) { + if (c.getX() != c.getZ()) { + return rotate90CY(v, d); } - if (v.getY() != 0) { - v.setY(-v.getY()); + return rotate90CCY(v, w); + } else if (c.getZ() == 0) { + if (c.getX() != c.getY()) { + return rotate90CZ(v, w); } - if (v.getZ() != 0) { - v.setZ(-v.getZ()); - } - - return v; + return rotate90CCZ(v, h); + } } - /** - * Get a speed value from a vector (velocity) - * - * @param v the vector - * @return the speed - */ - public static double getSpeed(Vector v) { - Vector vi = new Vector(0, 0, 0); - Vector vt = new Vector(0, 0, 0).add(v); - - return vi.distance(vt); - } + return v; + } - /** - * Shift all vectors based on the given vector - * - * @param vector the vector direction to shift the vectors - * @param vectors the vectors to be shifted - * @return the shifted vectors - */ - public static List shift(Vector vector, List vectors) { - return new ArrayList<>(new GListAdapter() { - @Override - public Vector onAdapt(Vector from) { - return from.add(vector); - } - }.adapt(vectors)); - } + public static Vector rotate90CX(Vector v) { + return new Vector(v.getX(), -v.getZ(), v.getY()); + } - /** - * Attempt to get the blockFace for the vector (will be tri-normalized) - * - * @param v the vector - * @return the block face or null - */ - public static BlockFace getBlockFace(Vector v) { - Vector p = triNormalize(v); - - for (BlockFace i : BlockFace.values()) { - if (p.getX() == i.getModX() && p.getY() == i.getModY() && p.getZ() == i.getModZ()) { - return i; - } - } + public static Vector rotate90CCX(Vector v) { + return new Vector(v.getX(), v.getZ(), -v.getY()); + } - for (BlockFace i : BlockFace.values()) { - if (p.getX() == i.getModX() && p.getZ() == i.getModZ()) { - return i; - } - } + public static Vector rotate90CY(Vector v) { + return new Vector(-v.getZ(), v.getY(), v.getX()); + } - for (BlockFace i : BlockFace.values()) { - if (p.getY() == i.getModY() && p.getZ() == i.getModZ()) { - return i; - } - } + public static Vector rotate90CCY(Vector v) { + return new Vector(v.getZ(), v.getY(), -v.getX()); + } - for (BlockFace i : BlockFace.values()) { - if (p.getX() == i.getModX() || p.getY() == i.getModY()) { - return i; - } - } + public static Vector rotate90CZ(Vector v) { + return new Vector(v.getY(), -v.getX(), v.getZ()); + } - for (BlockFace i : BlockFace.values()) { - if (p.getX() == i.getModX() || p.getY() == i.getModY() || p.getZ() == i.getModZ()) { - return i; - } - } + public static Vector rotate90CCZ(Vector v) { + return new Vector(-v.getY(), v.getX(), v.getZ()); + } - return null; - } + public static Vector rotate90CX(Vector v, int s) { + return new Vector(v.getX(), -v.getZ() + s, v.getY()); + } - /** - * Angle the vector in a self relative direction - * - * @param v the initial direction - * @param amt the amount to shift in the direction - * @return the shifted direction - */ - public static Vector angleLeft(Vector v, float amt) { - Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); - l.setDirection(v); - float y = l.getYaw(); - float p = l.getPitch(); - CDou cy = new CDou(360); - CDou cp = new CDou(180); - cy.set(y); - cp.set(p); - cy.sub(amt); - l.setYaw((float) cy.get()); - l.setPitch((float) cp.get()); - - return l.getDirection(); - } + public static Vector rotate90CCX(Vector v, int s) { + return new Vector(v.getX(), v.getZ(), -v.getY() + s); + } - /** - * Angle the vector in a self relative direction - * - * @param v the initial direction - * @param amt the amount to shift in the direction - * @return the shifted direction - */ - public static Vector angleRight(Vector v, float amt) { - Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); - l.setDirection(v); - float y = l.getYaw(); - float p = l.getPitch(); - CDou cy = new CDou(360); - CDou cp = new CDou(180); - cy.set(y); - cp.set(p); - cy.add(amt); - l.setYaw((float) cy.get()); - l.setPitch((float) cp.get()); - - return l.getDirection(); - } + public static Vector rotate90CY(Vector v, int s) { + return new Vector(-v.getZ() + s, v.getY(), v.getX()); + } - /** - * Angle the vector in a self relative direction - * - * @param v the initial direction - * @param amt the amount to shift in the direction - * @return the shifted direction - */ - public static Vector angleUp(Vector v, float amt) { - Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); - l.setDirection(v); - float y = l.getYaw(); - float p = l.getPitch(); - CDou cy = new CDou(360); - cy.set(y); - l.setYaw((float) cy.get()); - l.setPitch(Math.max(-90, p - amt)); - - return l.getDirection(); - } + public static Vector rotate90CCY(Vector v, int s) { + return new Vector(v.getZ(), v.getY(), -v.getX() + s); + } - /** - * Angle the vector in a self relative direction - * - * @param v the initial direction - * @param amt the amount to shift in the direction - * @return the shifted direction - */ - public static Vector angleDown(Vector v, float amt) { - Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); - l.setDirection(v); - float y = l.getYaw(); - float p = l.getPitch(); - CDou cy = new CDou(360); - cy.set(y); - l.setYaw((float) cy.get()); - l.setPitch(Math.min(90, p + amt)); - - return l.getDirection(); - } - - /** - * (clone) Force normalize the vector into three points, 1, 0, or -1. If the - * value is > 0.333 (1) if the value is less than -0.333 (-1) else 0 - * - * @param direction the direction - * @return the vector - */ - public static Vector triNormalize(Vector direction) { - Vector v = direction.clone(); - v.normalize(); - - if (v.getX() > 0.333) { - v.setX(1); - } else if (v.getX() < -0.333) { - v.setX(-1); - } else { - v.setX(0); - } + public static Vector rotate90CZ(Vector v, int s) { + return new Vector(v.getY(), -v.getX() + s, v.getZ()); + } - if (v.getY() > 0.333) { - v.setY(1); - } else if (v.getY() < -0.333) { - v.setY(-1); - } else { - v.setY(0); - } + public static Vector rotate90CCZ(Vector v, int s) { + return new Vector(-v.getY() + s, v.getX(), v.getZ()); + } - if (v.getZ() > 0.333) { - v.setZ(1); - } else if (v.getZ() < -0.333) { - v.setZ(-1); + public static Vector getAxis(Direction current, Direction to) { + if (current.equals(Direction.U) || current.equals(Direction.D)) { + if (to.equals(Direction.U) || to.equals(Direction.D)) { + return new Vector(1, 0, 0); + } else { + if (current.equals(Direction.N) || current.equals(Direction.S)) { + return Direction.E.toVector(); } else { - v.setZ(0); + return Direction.S.toVector(); } - - return v; - } + } + } + + return new Vector(0, 1, 0); + } + + private static double round(double value, int precision) { + return Double.valueOf(Form.f(value, precision)); + } + + public static Vector clip(Vector v, int decimals) { + v.setX(round(v.getX(), decimals)); + v.setY(round(v.getY(), decimals)); + v.setZ(round(v.getZ(), decimals)); + return v; + } + + public static Vector rotateVectorCC(Vector vec, Vector axis, double deg) { + double theta = Math.toRadians(deg); + double x, y, z; + double u, v, w; + x = vec.getX(); + y = vec.getY(); + z = vec.getZ(); + u = axis.getX(); + v = axis.getY(); + w = axis.getZ(); + double xPrime = u * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + x * Math.cos(theta) + (-w * y + v * z) * Math.sin(theta); + double yPrime = v * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + y * Math.cos(theta) + (w * x - u * z) * Math.sin(theta); + double zPrime = w * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + z * Math.cos(theta) + (-v * x + u * y) * Math.sin(theta); + + return clip(new Vector(xPrime, yPrime, zPrime), 4); + } + + /** + * Get all SIMPLE block faces from a more specific block face (SOUTH_EAST) = + * (south, east) + * + * @param f the block face + * @return multiple faces, or one if the face is already simple + */ + public static List split(BlockFace f) { + List faces = new ArrayList<>(); + + switch (f) { + case DOWN: + faces.add(BlockFace.DOWN); + break; + case EAST: + faces.add(BlockFace.EAST); + break; + case EAST_NORTH_EAST: + faces.add(BlockFace.EAST); + faces.add(BlockFace.EAST); + faces.add(BlockFace.NORTH); + break; + case EAST_SOUTH_EAST: + faces.add(BlockFace.EAST); + faces.add(BlockFace.EAST); + faces.add(BlockFace.SOUTH); + break; + case NORTH: + faces.add(BlockFace.NORTH); + break; + case NORTH_EAST: + faces.add(BlockFace.NORTH); + faces.add(BlockFace.EAST); + break; + case NORTH_NORTH_EAST: + faces.add(BlockFace.NORTH); + faces.add(BlockFace.NORTH); + faces.add(BlockFace.EAST); + break; + case NORTH_NORTH_WEST: + faces.add(BlockFace.NORTH); + faces.add(BlockFace.NORTH); + faces.add(BlockFace.WEST); + break; + case NORTH_WEST: + faces.add(BlockFace.NORTH); + faces.add(BlockFace.WEST); + break; + case SELF: + faces.add(BlockFace.SELF); + break; + case SOUTH: + faces.add(BlockFace.SOUTH); + break; + case SOUTH_EAST: + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.EAST); + break; + case SOUTH_SOUTH_EAST: + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.EAST); + break; + case SOUTH_SOUTH_WEST: + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.WEST); + break; + case SOUTH_WEST: + faces.add(BlockFace.SOUTH); + faces.add(BlockFace.WEST); + break; + case UP: + faces.add(BlockFace.UP); + break; + case WEST: + faces.add(BlockFace.WEST); + break; + case WEST_NORTH_WEST: + faces.add(BlockFace.WEST); + faces.add(BlockFace.WEST); + faces.add(BlockFace.NORTH); + break; + case WEST_SOUTH_WEST: + faces.add(BlockFace.WEST); + faces.add(BlockFace.WEST); + faces.add(BlockFace.SOUTH); + break; + default: + break; + } + + return faces; + } + + /** + * Get a normalized vector going from a location to another + * + * @param from from here + * @param to to here + * @return the normalized vector direction + */ + public static Vector direction(Location from, Location to) { + return to.clone().subtract(from.clone()).toVector().normalize(); + } + + public static Vector directionNoNormal(Location from, Location to) { + return to.clone().subtract(from.clone()).toVector(); + } + + /** + * Get the vector direction from the yaw and pitch + * + * @param yaw the yaw + * @param pitch the pitch + * @return the vector + */ + public static Vector toVector(float yaw, float pitch) { + return new Vector(Math.cos(pitch) * Math.cos(yaw), Math.sin(pitch), Math.cos(pitch) * Math.sin(-yaw)); + } + + /** + * Add an impulse (force) to an entity + * + * @param e the entity + * @param v the vector + */ + public static void impulse(Entity e, Vector v) { + impulse(e, v, 1.0); + } + + /** + * Add an impulse (force) on an entity + * + * @param e the entity + * @param v the vector + * @param effectiveness the effectiveness + */ + public static void impulse(Entity e, Vector v, double effectiveness) { + Vector vx = e.getVelocity(); + vx.add(v.clone().multiply(effectiveness)); + e.setVelocity(vx); + } + + /** + * Reverse a direction + * + * @param v the direction + * @return the reversed direction + */ + public static Vector reverse(Vector v) { + if (v.getX() != 0) { + v.setX(-v.getX()); + } + + if (v.getY() != 0) { + v.setY(-v.getY()); + } + + if (v.getZ() != 0) { + v.setZ(-v.getZ()); + } + + return v; + } + + /** + * Get a speed value from a vector (velocity) + * + * @param v the vector + * @return the speed + */ + public static double getSpeed(Vector v) { + Vector vi = new Vector(0, 0, 0); + Vector vt = new Vector(0, 0, 0).add(v); + + return vi.distance(vt); + } + + /** + * Shift all vectors based on the given vector + * + * @param vector the vector direction to shift the vectors + * @param vectors the vectors to be shifted + * @return the shifted vectors + */ + public static List shift(Vector vector, List vectors) { + return new ArrayList<>(new GListAdapter() { + @Override + public Vector onAdapt(Vector from) { + return from.add(vector); + } + }.adapt(vectors)); + } + + /** + * Attempt to get the blockFace for the vector (will be tri-normalized) + * + * @param v the vector + * @return the block face or null + */ + public static BlockFace getBlockFace(Vector v) { + Vector p = triNormalize(v); + + for (BlockFace i : BlockFace.values()) { + if (p.getX() == i.getModX() && p.getY() == i.getModY() && p.getZ() == i.getModZ()) { + return i; + } + } + + for (BlockFace i : BlockFace.values()) { + if (p.getX() == i.getModX() && p.getZ() == i.getModZ()) { + return i; + } + } + + for (BlockFace i : BlockFace.values()) { + if (p.getY() == i.getModY() && p.getZ() == i.getModZ()) { + return i; + } + } + + for (BlockFace i : BlockFace.values()) { + if (p.getX() == i.getModX() || p.getY() == i.getModY()) { + return i; + } + } + + for (BlockFace i : BlockFace.values()) { + if (p.getX() == i.getModX() || p.getY() == i.getModY() || p.getZ() == i.getModZ()) { + return i; + } + } + + return null; + } + + /** + * Angle the vector in a self relative direction + * + * @param v the initial direction + * @param amt the amount to shift in the direction + * @return the shifted direction + */ + public static Vector angleLeft(Vector v, float amt) { + Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + l.setDirection(v); + float y = l.getYaw(); + float p = l.getPitch(); + CDou cy = new CDou(360); + CDou cp = new CDou(180); + cy.set(y); + cp.set(p); + cy.sub(amt); + l.setYaw((float) cy.get()); + l.setPitch((float) cp.get()); + + return l.getDirection(); + } + + /** + * Angle the vector in a self relative direction + * + * @param v the initial direction + * @param amt the amount to shift in the direction + * @return the shifted direction + */ + public static Vector angleRight(Vector v, float amt) { + Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + l.setDirection(v); + float y = l.getYaw(); + float p = l.getPitch(); + CDou cy = new CDou(360); + CDou cp = new CDou(180); + cy.set(y); + cp.set(p); + cy.add(amt); + l.setYaw((float) cy.get()); + l.setPitch((float) cp.get()); + + return l.getDirection(); + } + + /** + * Angle the vector in a self relative direction + * + * @param v the initial direction + * @param amt the amount to shift in the direction + * @return the shifted direction + */ + public static Vector angleUp(Vector v, float amt) { + Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + l.setDirection(v); + float y = l.getYaw(); + float p = l.getPitch(); + CDou cy = new CDou(360); + cy.set(y); + l.setYaw((float) cy.get()); + l.setPitch(Math.max(-90, p - amt)); + + return l.getDirection(); + } + + /** + * Angle the vector in a self relative direction + * + * @param v the initial direction + * @param amt the amount to shift in the direction + * @return the shifted direction + */ + public static Vector angleDown(Vector v, float amt) { + Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0); + l.setDirection(v); + float y = l.getYaw(); + float p = l.getPitch(); + CDou cy = new CDou(360); + cy.set(y); + l.setYaw((float) cy.get()); + l.setPitch(Math.min(90, p + amt)); + + return l.getDirection(); + } + + /** + * (clone) Force normalize the vector into three points, 1, 0, or -1. If the + * value is > 0.333 (1) if the value is less than -0.333 (-1) else 0 + * + * @param direction the direction + * @return the vector + */ + public static Vector triNormalize(Vector direction) { + Vector v = direction.clone(); + v.normalize(); + + if (v.getX() > 0.333) { + v.setX(1); + } else if (v.getX() < -0.333) { + v.setX(-1); + } else { + v.setX(0); + } + + if (v.getY() > 0.333) { + v.setY(1); + } else if (v.getY() < -0.333) { + v.setY(-1); + } else { + v.setY(0); + } + + if (v.getZ() > 0.333) { + v.setZ(1); + } else if (v.getZ() < -0.333) { + v.setZ(-1); + } else { + v.setZ(0); + } + + return v; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java index 3ec18c2b5..25817e64c 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java +++ b/src/main/java/art/arcane/adapt/util/common/math/VelocitySpeed.java @@ -5,125 +5,126 @@ import org.bukkit.util.Vector; public final class VelocitySpeed { - public static final double EPSILON = 1.0E-6; - - private VelocitySpeed() { + public static final double EPSILON = 1.0E-6; + + private VelocitySpeed() { + } + + public static InputSnapshot readInput(Player p, double fallbackThresholdSquared) { + try { + Input input = p.getCurrentInput(); + if (input != null) { + InputSnapshot snapshot = new InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); + if (snapshot.hasHorizontal()) { + return snapshot; + } + } + } catch (NoSuchMethodError ignored) { + // Fallback path for runtimes without Player#getCurrentInput. } - public static InputSnapshot readInput(Player p, double fallbackThresholdSquared) { - try { - Input input = p.getCurrentInput(); - if (input != null) { - InputSnapshot snapshot = new InputSnapshot(input.isForward(), input.isBackward(), input.isLeft(), input.isRight()); - if (snapshot.hasHorizontal()) { - return snapshot; - } - } - } catch (NoSuchMethodError ignored) { - // Fallback path for runtimes without Player#getCurrentInput. - } + Vector horizontal = horizontalOnly(p.getVelocity()); + if (horizontal.lengthSquared() <= Math.max(0, fallbackThresholdSquared)) { + return InputSnapshot.NONE; + } - Vector horizontal = horizontalOnly(p.getVelocity()); - if (horizontal.lengthSquared() <= Math.max(0, fallbackThresholdSquared)) { - return InputSnapshot.NONE; - } + Vector movementDirection = horizontal.normalize(); + Vector look = p.getLocation().getDirection().setY(0); + if (look.lengthSquared() <= EPSILON) { + return InputSnapshot.NONE; + } - Vector movementDirection = horizontal.normalize(); - Vector look = p.getLocation().getDirection().setY(0); - if (look.lengthSquared() <= EPSILON) { - return InputSnapshot.NONE; - } + look.normalize(); + Vector right = new Vector(-look.getZ(), 0, look.getX()); + double forwardDot = movementDirection.dot(look); + double sideDot = movementDirection.dot(right); + return new InputSnapshot(forwardDot > 0.2, forwardDot < -0.2, sideDot < -0.2, sideDot > 0.2); + } + + public static Vector resolveHorizontalDirection(Player p, InputSnapshot input) { + Vector look = p.getLocation().getDirection().setY(0); + if (look.lengthSquared() <= EPSILON) { + return new Vector(); + } - look.normalize(); - Vector right = new Vector(-look.getZ(), 0, look.getX()); - double forwardDot = movementDirection.dot(look); - double sideDot = movementDirection.dot(right); - return new InputSnapshot(forwardDot > 0.2, forwardDot < -0.2, sideDot < -0.2, sideDot > 0.2); + look.normalize(); + Vector right = new Vector(-look.getZ(), 0, look.getX()); + Vector direction = new Vector(); + if (input.forward()) { + direction.add(look); + } + if (input.backward()) { + direction.subtract(look); + } + if (input.right()) { + direction.add(right); + } + if (input.left()) { + direction.subtract(right); } - public static Vector resolveHorizontalDirection(Player p, InputSnapshot input) { - Vector look = p.getLocation().getDirection().setY(0); - if (look.lengthSquared() <= EPSILON) { - return new Vector(); - } + if (direction.lengthSquared() <= EPSILON) { + return direction; + } - look.normalize(); - Vector right = new Vector(-look.getZ(), 0, look.getX()); - Vector direction = new Vector(); - if (input.forward()) { - direction.add(look); - } - if (input.backward()) { - direction.subtract(look); - } - if (input.right()) { - direction.add(right); - } - if (input.left()) { - direction.subtract(right); - } + return direction.normalize(); + } - if (direction.lengthSquared() <= EPSILON) { - return direction; - } + public static Vector horizontalOnly(Vector velocity) { + return new Vector(velocity.getX(), 0, velocity.getZ()); + } - return direction.normalize(); + public static Vector moveTowards(Vector current, Vector target, double maxDelta) { + if (maxDelta <= 0) { + return current.clone(); } - public static Vector horizontalOnly(Vector velocity) { - return new Vector(velocity.getX(), 0, velocity.getZ()); + Vector delta = target.clone().subtract(current); + double distance = delta.length(); + if (distance <= EPSILON || distance <= maxDelta) { + return target.clone(); } - public static Vector moveTowards(Vector current, Vector target, double maxDelta) { - if (maxDelta <= 0) { - return current.clone(); - } + return current.clone().add(delta.multiply(maxDelta / distance)); + } - Vector delta = target.clone().subtract(current); - double distance = delta.length(); - if (distance <= EPSILON || distance <= maxDelta) { - return target.clone(); - } + public static Vector clampHorizontal(Vector horizontal, double maxSpeed) { + double clamped = Math.max(0, maxSpeed); + if (clamped <= 0) { + return new Vector(); + } - return current.clone().add(delta.multiply(maxDelta / distance)); + if (horizontal.lengthSquared() <= clamped * clamped) { + return horizontal; } - public static Vector clampHorizontal(Vector horizontal, double maxSpeed) { - double clamped = Math.max(0, maxSpeed); - if (clamped <= 0) { - return new Vector(); - } + return horizontal.normalize().multiply(clamped); + } - if (horizontal.lengthSquared() <= clamped * clamped) { - return horizontal; - } + public static void setHorizontalVelocity(Player p, Vector horizontal) { + Vector velocity = p.getVelocity(); + p.setVelocity(new Vector(horizontal.getX(), velocity.getY(), horizontal.getZ())); + } - return horizontal.normalize().multiply(clamped); - } + public static void hardStopHorizontal(Player p) { + Vector velocity = p.getVelocity(); + p.setVelocity(new Vector(0, velocity.getY(), 0)); + } - public static void setHorizontalVelocity(Player p, Vector horizontal) { - Vector velocity = p.getVelocity(); - p.setVelocity(new Vector(horizontal.getX(), velocity.getY(), horizontal.getZ())); - } + public static double speedAmplifierScalar(int amplifier) { + return 1.0 + (Math.max(0, amplifier) + 1) * 0.2; + } - public static void hardStopHorizontal(Player p) { - Vector velocity = p.getVelocity(); - p.setVelocity(new Vector(0, velocity.getY(), 0)); - } + public record InputSnapshot(boolean forward, boolean backward, boolean left, + boolean right) { + public static final InputSnapshot NONE = new InputSnapshot(false, false, false, false); - public static double speedAmplifierScalar(int amplifier) { - return 1.0 + (Math.max(0, amplifier) + 1) * 0.2; + public boolean hasHorizontal() { + return forward || backward || left || right; } - public record InputSnapshot(boolean forward, boolean backward, boolean left, boolean right) { - public static final InputSnapshot NONE = new InputSnapshot(false, false, false, false); - - public boolean hasHorizontal() { - return forward || backward || left || right; - } - - public boolean isForwardOnly() { - return forward && !backward && !left && !right; - } + public boolean isForwardOnly() { + return forward && !backward && !left && !right; } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/math/Writable.java b/src/main/java/art/arcane/adapt/util/common/math/Writable.java index babb7cc97..92dbd4f8b 100644 --- a/src/main/java/art/arcane/adapt/util/common/math/Writable.java +++ b/src/main/java/art/arcane/adapt/util/common/math/Writable.java @@ -23,7 +23,7 @@ import java.io.IOException; public interface Writable { - void write(DataOutputStream o) throws IOException; + void write(DataOutputStream o) throws IOException; - void read(DataInputStream i) throws IOException; + void read(DataInputStream i) throws IOException; } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java index 6c09cecf2..56fe8013a 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/AdvancementUtils.java @@ -20,65 +20,66 @@ public class AdvancementUtils { - private static volatile boolean unavailable; - private static volatile boolean warned; + private static volatile boolean unavailable; + private static volatile boolean warned; - /** - * Displays a custom toast to a player. - * - * @param player A player to show the toast. - * @param icon The displayed item of the toast. - * @param title The displayed title of the toast. - * @param frame The frame type of the toast. - * @see UltimateAdvancementAPI#displayCustomToast(Player, ItemStack, String, com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementFrameType) - */ - public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull String description, @NotNull AdaptAdvancementFrame frame) { - if (unavailable) { - return; - } + /** + * Displays a custom toast to a player. + * + * @param player A player to show the toast. + * @param icon The displayed item of the toast. + * @param title The displayed title of the toast. + * @param frame The frame type of the toast. + * @see UltimateAdvancementAPI#displayCustomToast(Player, ItemStack, String, + * com.fren_gor.ultimateAdvancementAPI.advancement.display.AdvancementFrameType) + */ + public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull String description, @NotNull AdaptAdvancementFrame frame) { + if (unavailable) { + return; + } - Preconditions.checkNotNull(player, "Player is null."); - Preconditions.checkNotNull(icon, "Icon is null."); - Preconditions.checkNotNull(title, "Title is null."); - Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); - Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(icon, "Icon is null."); + Preconditions.checkNotNull(title, "Title is null."); + Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); + Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); - try { - MinecraftKeyWrapper rootKey = MinecraftKeyWrapper.craft("com.fren_gor", "root"); - MinecraftKeyWrapper notificationKey = MinecraftKeyWrapper.craft("com.fren_gor", "notification"); - AdvancementDisplayWrapper rootDisplay = AdvancementDisplayWrapper.craft( - new ItemStack(Material.GRASS_BLOCK), - "§f§lNotifications§1§2§3§4§5§6§7§8§9§0", - "§7Notification page.\n§7Close and reopen advancements to hide.", - AdvancementFrameTypeWrapper.TASK, - 0, - 0, - "textures/block/stone.png" - ); - AdvancementWrapper root = AdvancementWrapper.craftRootAdvancement(rootKey, rootDisplay, 1); - AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, description, frame.toUaaFrame().getNMSWrapper(), 1, 0, true, false, false); - AdvancementWrapper notification = AdvancementWrapper.craftBaseAdvancement(notificationKey, root, display, 1); - PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of( - root, 1, - notification, 1 - )).sendTo(player); - PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(rootKey, notificationKey)).sendTo(player); - } catch (Throwable e) { - unavailable = true; - warnOnce(e); - } + try { + MinecraftKeyWrapper rootKey = MinecraftKeyWrapper.craft("com.fren_gor", "root"); + MinecraftKeyWrapper notificationKey = MinecraftKeyWrapper.craft("com.fren_gor", "notification"); + AdvancementDisplayWrapper rootDisplay = AdvancementDisplayWrapper.craft( + new ItemStack(Material.GRASS_BLOCK), + "§f§lNotifications§1§2§3§4§5§6§7§8§9§0", + "§7Notification page.\n§7Close and reopen advancements to hide.", + AdvancementFrameTypeWrapper.TASK, + 0, + 0, + "textures/block/stone.png" + ); + AdvancementWrapper root = AdvancementWrapper.craftRootAdvancement(rootKey, rootDisplay, 1); + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, description, frame.toUaaFrame().getNMSWrapper(), 1, 0, true, false, false); + AdvancementWrapper notification = AdvancementWrapper.craftBaseAdvancement(notificationKey, root, display, 1); + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of( + root, 1, + notification, 1 + )).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(rootKey, notificationKey)).sendTo(player); + } catch (Throwable e) { + unavailable = true; + warnOnce(e); } + } - private static void warnOnce(Throwable throwable) { - if (warned) { - return; - } + private static void warnOnce(Throwable throwable) { + if (warned) { + return; + } - warned = true; - Throwable root = throwable; - while (root.getCause() != null && root.getCause() != root) { - root = root.getCause(); - } - Adapt.warn("Advancement notifications are unavailable: " + Objects.toString(root.getMessage(), root.getClass().getSimpleName())); + warned = true; + Throwable root = throwable; + while (root.getCause() != null && root.getCause() != root) { + root = root.getCause(); } + Adapt.warn("Advancement notifications are unavailable: " + Objects.toString(root.getMessage(), root.getClass().getSimpleName())); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Area.java b/src/main/java/art/arcane/adapt/util/common/misc/Area.java index c6a25a4ff..5d516acd9 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Area.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Area.java @@ -37,217 +37,219 @@ * @author cyberpwn */ public class Area { - private Location location; - private Double radius; - - /** - * Used to instantiate a new "area" in which you can check if entities are - * within this area. - * - * @param location The center location of the area - * @param radius The radius used as a double. - */ - public Area(Location location, Double radius) { - this.location = location; - this.radius = radius; - } - - /** - * Used to instantiate a new "area" in which you can check if entities are - * within this area. - * - * @param location The center location of the area - * @param radius The radius used as an int. - */ - public Area(Location location, Integer radius) { - this.location = location; - this.radius = (double) radius; - } - - public static boolean within(Location center, Location target, double rad) { - return new Area(center, rad).isWithin(target); - } - - public Cuboid toCuboid() { - return new Cuboid(location.clone().add(radius, radius, radius), location.clone().subtract(radius, radius, radius)); - } - - /** - * Calculate the ESTIMATED distance from the center of this - * area, to the given location WARNING: This uses newton's method, - * be careful on how accurate you need this. As it is meant for FAST - * calculations with minimal load. - * - * @param location The given location to calculate a distance from the center. - * @return Returns the distance of location from the center. - */ - public Double distance(Location location) { - double c = this.location.distanceSquared(location); - double t = c; - - for (int i = 0; i < 3; i++) { - t = (c / t + t) / 2.0; - } - - return t; - } - - /** - * Calculate the EXACT distance from the center of this - * area, to the given location WARNING: This uses the sqrt function, - * be careful on how heavy you call this. - * - * @param location The given location to calculate a distance from the center. - * @return Returns the distance of location from the center. - */ - public Double slowDistance(Location location) { - return this.location.distance(location); - } - - /** - * Check to see weather a location is within the area - * - * @param location The location to measure from the center. - * @return Returns True if within; False if not. - */ - public boolean isWithin(Location location) { - return this.location.distance(location) <= (radius * radius); - } - - /** - * But does it have any entities? - */ - public boolean hasEntities() { - return getNearbyEntities().length > 0; - } - - /** - * Get all nearby entities matching the given entity type - * - * @param type the entity type - * @return the nearby entities matching the given type - */ - public Entity[] getNearbyEntities(EntityType type) { - KList e = new KList<>(getNearbyEntities()); - - for (Entity i : e.copy()) { - if (!i.getType().equals(type)) { - e.remove(i); - } - } - - return e.toArray(new Entity[e.size()]); - } - - /** - * Get nearby entities which match the following class - * - * @param entityClass the entity class - * @return the nearby entities assignable from the given class - */ - public Entity[] getNearbyEntities(Class entityClass) { - KList e = new KList<>(getNearbyEntities()); - - for (Entity i : e.copy()) { - if (!i.getClass().isAssignableFrom(entityClass)) { - e.remove(i); + private Location location; + private Double radius; + + /** + * Used to instantiate a new "area" in which you can check if entities are + * within this area. + * + * @param location The center location of the area + * @param radius The radius used as a double. + */ + public Area(Location location, Double radius) { + this.location = location; + this.radius = radius; + } + + /** + * Used to instantiate a new "area" in which you can check if entities are + * within this area. + * + * @param location The center location of the area + * @param radius The radius used as an int. + */ + public Area(Location location, Integer radius) { + this.location = location; + this.radius = (double) radius; + } + + public static boolean within(Location center, Location target, double rad) { + return new Area(center, rad).isWithin(target); + } + + public Cuboid toCuboid() { + return new Cuboid(location.clone().add(radius, radius, radius), location.clone().subtract(radius, radius, radius)); + } + + /** + * Calculate the ESTIMATED distance from the center of this + * area, to the given location WARNING: This uses newton's method, be + * careful on how accurate you need this. As it is meant for FAST calculations + * with minimal load. + * + * @param location The given location to calculate a distance from the + * center. + * @return Returns the distance of location from the center. + */ + public Double distance(Location location) { + double c = this.location.distanceSquared(location); + double t = c; + + for (int i = 0; i < 3; i++) { + t = (c / t + t) / 2.0; + } + + return t; + } + + /** + * Calculate the EXACT distance from the center of this area, + * to the given location WARNING: This uses the sqrt function, be + * careful on how heavy you call this. + * + * @param location The given location to calculate a distance from the + * center. + * @return Returns the distance of location from the center. + */ + public Double slowDistance(Location location) { + return this.location.distance(location); + } + + /** + * Check to see weather a location is within the area + * + * @param location The location to measure from the center. + * @return Returns True if within; False if not. + */ + public boolean isWithin(Location location) { + return this.location.distance(location) <= (radius * radius); + } + + /** + * But does it have any entities? + */ + public boolean hasEntities() { + return getNearbyEntities().length > 0; + } + + /** + * Get all nearby entities matching the given entity type + * + * @param type the entity type + * @return the nearby entities matching the given type + */ + public Entity[] getNearbyEntities(EntityType type) { + KList e = new KList<>(getNearbyEntities()); + + for (Entity i : e.copy()) { + if (!i.getType().equals(type)) { + e.remove(i); + } + } + + return e.toArray(new Entity[e.size()]); + } + + /** + * Get nearby entities which match the following class + * + * @param entityClass the entity class + * @return the nearby entities assignable from the given class + */ + public Entity[] getNearbyEntities(Class entityClass) { + KList e = new KList<>(getNearbyEntities()); + + for (Entity i : e.copy()) { + if (!i.getClass().isAssignableFrom(entityClass)) { + e.remove(i); + } + } + + return e.toArray(new Entity[0]); + } + + /** + * Get ALL entities within the area. NOTE: This is EVERY entity, not + * just LivingEntities. Drops, Particles, Mobs, Players, Everything + * + * @return Returns an Entity[] array of all entities within the given area. + */ + public Entity[] getNearbyEntities() { + try { + int chunkRadius = (int) (radius < 16 ? 1 : (radius - (radius % 16)) / 16); + HashSet radiusEntities = new HashSet(); + + for (int chX = 0 - chunkRadius; chX <= chunkRadius; chX++) { + for (int chZ = 0 - chunkRadius; chZ <= chunkRadius; chZ++) { + int x = (int) location.getX(), y = (int) location.getY(), z = (int) location.getZ(); + + for (Entity e : new Location(location.getWorld(), x + (chX * 16), y, z + (chZ * 16)).getChunk().getEntities()) { + if (e.getLocation().distanceSquared(location) <= radius * radius && e.getLocation().getBlock() != location.getBlock()) { + radiusEntities.add(e); } + } } - - return e.toArray(new Entity[0]); - } - - /** - * Get ALL entities within the area. NOTE: This is EVERY entity, not - * just LivingEntities. Drops, Particles, Mobs, Players, Everything - * - * @return Returns an Entity[] array of all entities within the given area. - */ - public Entity[] getNearbyEntities() { - try { - int chunkRadius = (int) (radius < 16 ? 1 : (radius - (radius % 16)) / 16); - HashSet radiusEntities = new HashSet(); - - for (int chX = 0 - chunkRadius; chX <= chunkRadius; chX++) { - for (int chZ = 0 - chunkRadius; chZ <= chunkRadius; chZ++) { - int x = (int) location.getX(), y = (int) location.getY(), z = (int) location.getZ(); - - for (Entity e : new Location(location.getWorld(), x + (chX * 16), y, z + (chZ * 16)).getChunk().getEntities()) { - if (e.getLocation().distanceSquared(location) <= radius * radius && e.getLocation().getBlock() != location.getBlock()) { - radiusEntities.add(e); - } - } - } - } - - return radiusEntities.toArray(new Entity[radiusEntities.size()]); - } catch (Exception e) { - return new ArrayList().toArray(new Entity[0]); - } - } - - /** - * Get all players within the area. - * - * @return Returns an Player[] array of all players within the given area. - */ - public Player[] getNearbyPlayers() { - List px = new ArrayList<>(); - - for (Entity i : getNearbyEntities()) { - if (i.getType().equals(EntityType.PLAYER)) { - px.add((Player) i); - } - } - - return px.toArray(new Player[0]); - } - - /** - * Get the defined center location - * - * @return Returns the center location of the area - */ - public Location getLocation() { - return location; - } - - /** - * Set the defined center location - * - * @param location The new location to be set - */ - public void setLocation(Location location) { - this.location = location; - } - - /** - * Gets the area's radius - * - * @return Returns the area's radius - */ - public Double getRadius() { - return radius; - } - - /** - * Set the area's radius - * - * @param radius The new radius to be set - */ - public void setRadius(Double radius) { - this.radius = radius; - } - - /** - * Pick a random location in this radius - */ - public Location random() { - Random r = new Random(); - double x = radius * ((r.nextDouble() - 0.5) * 2); - double y = radius * ((r.nextDouble() - 0.5) * 2); - double z = radius * ((r.nextDouble() - 0.5) * 2); - - return location.clone().add(x, y, z); - } + } + + return radiusEntities.toArray(new Entity[radiusEntities.size()]); + } catch (Exception e) { + return new ArrayList().toArray(new Entity[0]); + } + } + + /** + * Get all players within the area. + * + * @return Returns an Player[] array of all players within the given area. + */ + public Player[] getNearbyPlayers() { + List px = new ArrayList<>(); + + for (Entity i : getNearbyEntities()) { + if (i.getType().equals(EntityType.PLAYER)) { + px.add((Player) i); + } + } + + return px.toArray(new Player[0]); + } + + /** + * Get the defined center location + * + * @return Returns the center location of the area + */ + public Location getLocation() { + return location; + } + + /** + * Set the defined center location + * + * @param location The new location to be set + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Gets the area's radius + * + * @return Returns the area's radius + */ + public Double getRadius() { + return radius; + } + + /** + * Set the area's radius + * + * @param radius The new radius to be set + */ + public void setRadius(Double radius) { + this.radius = radius; + } + + /** + * Pick a random location in this radius + */ + public Location random() { + Random r = new Random(); + double x = radius * ((r.nextDouble() - 0.5) * 2); + double y = radius * ((r.nextDouble() - 0.5) * 2); + double z = radius * ((r.nextDouble() - 0.5) * 2); + + return location.clone().add(x, y, z); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java index e651e8c34..e3939d8d8 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Chunker.java @@ -30,46 +30,46 @@ import java.util.function.Consumer; public class Chunker { - private final List q; - private ExecutorService executor; - private int threads; - private int workload; + private final List q; + private ExecutorService executor; + private int threads; + private int workload; - public Chunker(List q) { - this.q = q; - } + public Chunker(List q) { + this.q = q; + } - public Chunker threads(int threads) { - this.threads = threads; - return this; - } + public Chunker threads(int threads) { + this.threads = threads; + return this; + } - public Chunker workload(int workload) { - this.workload = workload; - return this; - } + public Chunker workload(int workload) { + this.workload = workload; + return this; + } - public void execute(Consumer consumer, Callback progress, int progressInterval) { - ChronoLatch cl = new ChronoLatch(progressInterval); - Contained consumed = new Contained(0); - executor = Executors.newFixedThreadPool(threads); - int length = q.size(); - int remaining = length; + public void execute(Consumer consumer, Callback progress, int progressInterval) { + ChronoLatch cl = new ChronoLatch(progressInterval); + Contained consumed = new Contained(0); + executor = Executors.newFixedThreadPool(threads); + int length = q.size(); + int remaining = length; - while (remaining > 0) { - int at = remaining; - remaining -= (remaining > workload ? workload : remaining); - int to = remaining; + while (remaining > 0) { + int at = remaining; + remaining -= (remaining > workload ? workload : remaining); + int to = remaining; - executor.submit(() -> - { - J.dofor(at, (i) -> i >= to, -1, (i) -> J.attempt(() -> consumer.accept(q.get(i)))); - consumed.mod((c) -> c += workload); - J.doif(() -> progress != null && cl.flip(), () -> progress.run((double) consumed.get() / (double) length)); - }); - } - - executor.shutdown(); - J.attempt(() -> executor.awaitTermination(100, TimeUnit.HOURS)); + executor.submit(() -> + { + J.dofor(at, (i) -> i >= to, -1, (i) -> J.attempt(() -> consumer.accept(q.get(i)))); + consumed.mod((c) -> c += workload); + J.doif(() -> progress != null && cl.flip(), () -> progress.run((double) consumed.get() / (double) length)); + }); } + + executor.shutdown(); + J.attempt(() -> executor.awaitTermination(100, TimeUnit.HOURS)); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java index 876af362f..957cd15da 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java @@ -18,177 +18,178 @@ import static art.arcane.adapt.Adapt.instance; -public record CustomModel(Material material, int model, NamespacedKey modelKey) { - public static final NamespacedKey EMPTY_KEY = NamespacedKey.minecraft("empty"); - private static UpdateChecker updateChecker = null; - - public ItemStack toItemStack() { - return toItemStack(new ItemStack(material)); - } - - public ItemStack toItemStack(ItemStack itemStack) { - var meta = itemStack.getItemMeta(); - if (meta == null || model == 0) - return itemStack; - - Version.get().applyModel(this, meta); - itemStack.setItemMeta(meta); - return itemStack; +public record CustomModel(Material material, int model, + NamespacedKey modelKey) { + public static final NamespacedKey EMPTY_KEY = NamespacedKey.minecraft("empty"); + private static UpdateChecker updateChecker = null; + + public static CustomModel get(Material fallback, String... path) { + if (!AdaptConfig.get().isCustomModels()) + return new CustomModel(fallback, 0, null); + + if (updateChecker == null) + updateChecker = new UpdateChecker(); + return updateChecker.get(fallback, path); + } + + public static void clear() { + if (updateChecker == null) + return; + updateChecker = null; + } + + public static boolean reloadFromDisk() { + return reloadFromDisk(false); + } + + public static boolean reloadFromDisk(boolean quiet) { + if (updateChecker == null) + updateChecker = new UpdateChecker(); + + return updateChecker.reloadFromDisk(quiet); + } + + public ItemStack toItemStack() { + return toItemStack(new ItemStack(material)); + } + + public ItemStack toItemStack(ItemStack itemStack) { + org.bukkit.inventory.meta.ItemMeta meta = itemStack.getItemMeta(); + if (meta == null || model == 0) + return itemStack; + + Version.get().applyModel(this, meta); + itemStack.setItemMeta(meta); + return itemStack; + } + + private static class UpdateChecker { + private final Object lock = new Object(); + private final File modelsFile; + private final File legacyModelsFile; + private final KMap cache = new KMap<>(); + private JsonObject json = new JsonObject(); + + public UpdateChecker() { + modelsFile = instance.getDataFile("adapt", "models.toml"); + legacyModelsFile = instance.getDataFile("adapt", "models.json"); + + try { + readFile(); + } catch (IOException e) { + Adapt.error("Failed to read models.toml"); + e.printStackTrace(); + } } - public static CustomModel get(Material fallback, String... path) { - if (!AdaptConfig.get().isCustomModels()) - return new CustomModel(fallback, 0, null); - - if (updateChecker == null) - updateChecker = new UpdateChecker(); - return updateChecker.get(fallback, path); + public boolean reloadFromDisk(boolean quiet) { + synchronized (lock) { + try { + readFile(); + cache.clear(); + return true; + } catch (IOException e) { + if (!quiet) { + Adapt.error("Failed to read models.toml"); + e.printStackTrace(); + } + return false; + } + } } - public static void clear() { - if (updateChecker == null) - return; - updateChecker = null; - } + public CustomModel get(Material fallback, String... path) { + return cache.computeIfAbsent(String.join("", path), k -> { + com.google.gson.JsonObject json = this.json; + for (java.lang.String s : path) { + if (!json.has(s)) + return set(new CustomModel(fallback, 0, EMPTY_KEY), path); + com.google.gson.JsonElement v = json.get(s); + if (!v.isJsonObject()) { + Adapt.warn("Invalid json at path: " + String.join(".", path)); + return new CustomModel(fallback, 0, EMPTY_KEY); + } + json = v.getAsJsonObject(); + } - public static boolean reloadFromDisk() { - return reloadFromDisk(false); + return new CustomModel( + json.has("material") ? Material.valueOf(json.get("material").getAsString()) : fallback, + json.has("model") ? json.get("model").getAsInt() : 0, + json.has("modelKey") ? NamespacedKey.fromString(json.get("modelKey").getAsString()) : EMPTY_KEY + ); + }); } - public static boolean reloadFromDisk(boolean quiet) { - if (updateChecker == null) - updateChecker = new UpdateChecker(); + public CustomModel set(CustomModel data, String... path) { + com.google.gson.JsonObject json = this.json; + for (java.lang.String s : path) { + if (!json.has(s)) + json.add(s, new JsonObject()); - return updateChecker.reloadFromDisk(quiet); + com.google.gson.JsonElement v = json.get(s); + if (!v.isJsonObject()) { + v = new JsonObject(); + json.add(s, v); + } + json = v.getAsJsonObject(); + } + + json.addProperty("material", data.material.name()); + json.addProperty("model", data.model); + json.addProperty("modelKey", (data.modelKey == null ? EMPTY_KEY : data.modelKey).toString()); + + try { + writeFile(); + } catch (IOException e) { + Adapt.error("Failed to write models.toml"); + e.printStackTrace(); + } + return data; } - private static class UpdateChecker { - private final Object lock = new Object(); - private final File modelsFile; - private final File legacyModelsFile; - private final KMap cache = new KMap<>(); - private JsonObject json = new JsonObject(); - - public UpdateChecker() { - modelsFile = instance.getDataFile("adapt", "models.toml"); - legacyModelsFile = instance.getDataFile("adapt", "models.json"); - - try { - readFile(); - } catch (IOException e) { - Adapt.error("Failed to read models.toml"); - e.printStackTrace(); - } - } + public void readFile() throws IOException { + synchronized (lock) { + if (modelsFile.exists()) { + String raw = IO.readAll(modelsFile); + if (raw == null || raw.isBlank()) { + json = new JsonObject(); + ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); + return; + } - public boolean reloadFromDisk(boolean quiet) { - synchronized (lock) { - try { - readFile(); - cache.clear(); - return true; - } catch (IOException e) { - if (!quiet) { - Adapt.error("Failed to read models.toml"); - e.printStackTrace(); - } - return false; - } - } - } + JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, modelsFile); + if (parsed == null || !parsed.isJsonObject()) { + throw new IOException("Invalid models.toml"); + } - public CustomModel get(Material fallback, String... path) { - return cache.computeIfAbsent(String.join("", path), k -> { - var json = this.json; - for (var s : path) { - if (!json.has(s)) - return set(new CustomModel(fallback, 0, EMPTY_KEY), path); - var v = json.get(s); - if (!v.isJsonObject()) { - Adapt.warn("Invalid json at path: " + String.join(".", path)); - return new CustomModel(fallback, 0, EMPTY_KEY); - } - json = v.getAsJsonObject(); - } - - return new CustomModel( - json.has("material") ? Material.valueOf(json.get("material").getAsString()) : fallback, - json.has("model") ? json.get("model").getAsInt() : 0, - json.has("modelKey") ? NamespacedKey.fromString(json.get("modelKey").getAsString()) : EMPTY_KEY - ); - }); + json = parsed.getAsJsonObject(); + ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); + return; } - public CustomModel set(CustomModel data, String... path) { - var json = this.json; - for (var s : path) { - if (!json.has(s)) - json.add(s, new JsonObject()); - - var v = json.get(s); - if (!v.isJsonObject()) { - v = new JsonObject(); - json.add(s, v); - } - json = v.getAsJsonObject(); - } - - json.addProperty("material", data.material.name()); - json.addProperty("model", data.model); - json.addProperty("modelKey", (data.modelKey == null ? EMPTY_KEY : data.modelKey).toString()); - - try { - writeFile(); - } catch (IOException e) { - Adapt.error("Failed to write models.toml"); - e.printStackTrace(); - } - return data; + if (legacyModelsFile.exists()) { + String legacyRaw = IO.readAll(legacyModelsFile); + JsonObject legacy = Json.fromJson(legacyRaw, JsonObject.class); + if (legacy == null) { + throw new IOException("Invalid models.json"); + } + json = legacy; + IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(legacy)); + Adapt.info("Migrated legacy config [adapt/models.json] -> [adapt/models.toml]."); + ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); + return; } - public void readFile() throws IOException { - synchronized (lock) { - if (modelsFile.exists()) { - String raw = IO.readAll(modelsFile); - if (raw == null || raw.isBlank()) { - json = new JsonObject(); - ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); - return; - } - - JsonElement parsed = ConfigFileSupport.parseToJsonElement(raw, modelsFile); - if (parsed == null || !parsed.isJsonObject()) { - throw new IOException("Invalid models.toml"); - } - - json = parsed.getAsJsonObject(); - ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); - return; - } - - if (legacyModelsFile.exists()) { - String legacyRaw = IO.readAll(legacyModelsFile); - JsonObject legacy = Json.fromJson(legacyRaw, JsonObject.class); - if (legacy == null) { - throw new IOException("Invalid models.json"); - } - json = legacy; - IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(legacy)); - Adapt.info("Migrated legacy config [adapt/models.json] -> [adapt/models.toml]."); - ConfigFileSupport.deleteLegacyFileIfMigrated(modelsFile, legacyModelsFile, "models-config"); - return; - } - - json = new JsonObject(); - IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(json)); - ConfigFileSupport.recordMissingConfigCreated(); - } - } + json = new JsonObject(); + IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(json)); + ConfigFileSupport.recordMissingConfigCreated(); + } + } - public void writeFile() throws IOException { - synchronized (lock) { - IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(json)); - } - } + public void writeFile() throws IOException { + synchronized (lock) { + IO.writeAll(modelsFile, ConfigFileSupport.serializeJsonElementToToml(json)); + } } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java b/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java index 2f5f45a45..6d1250d4a 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DependsOn.java @@ -27,5 +27,5 @@ @Retention(RUNTIME) @Target({FIELD}) public @interface DependsOn { - String[] value(); + String[] value(); } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java index 04d163275..902d46dda 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/DirtyString.java @@ -25,27 +25,27 @@ public class DirtyString { - public static String write(Object data) { - return write(Json.toJson(data, false)); - } + public static String write(Object data) { + return write(Json.toJson(data, false)); + } - public static T fromJson(String data, Class t) { - return Json.fromJson(read(data), t); - } + public static T fromJson(String data, Class t) { + return Json.fromJson(read(data), t); + } - public static boolean has(String data) { - if (!HiddenStringUtils.hasHiddenString(data)) { - Adapt.info("Not has in " + data.replaceAll("\\Q" + ChatColor.COLOR_CHAR + "\\E", "&")); - } - return HiddenStringUtils.hasHiddenString(data); + public static boolean has(String data) { + if (!HiddenStringUtils.hasHiddenString(data)) { + Adapt.info("Not has in " + data.replaceAll("\\Q" + ChatColor.COLOR_CHAR + "\\E", "&")); } + return HiddenStringUtils.hasHiddenString(data); + } - public static String write(String data) { - return HiddenStringUtils.encodeString(data); - } + public static String write(String data) { + return HiddenStringUtils.encodeString(data); + } - public static String read(String data) { - return HiddenStringUtils.extractHiddenString(data); - } + public static String read(String data) { + return HiddenStringUtils.extractHiddenString(data); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java index 296a0d9f9..62c198f69 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java @@ -28,83 +28,83 @@ import java.util.List; public class Impulse { - private final List ignore; - private double radius; - private double forceMax; - private double forceMin; - private double damageMin; - private double damageMax; - - public Impulse(double radius) { - ignore = new ArrayList<>(); - this.radius = radius; - this.forceMax = 1; - this.forceMin = 0; - this.damageMax = 1; - this.damageMin = 0; - } - - public Impulse radius(double radius) { - this.radius = radius; - return this; - } - - public Impulse force(double force) { - this.forceMax = force; - return this; - } - - public Impulse force(double forceMax, double forceMin) { - this.forceMax = forceMax; - this.forceMin = forceMin; - return this; - } - - public Impulse damage(double damage) { - this.damageMax = damage; - return this; - } - - public Impulse damage(double damageMax, double damageMin) { - this.damageMax = damageMax; - this.damageMin = damageMin; - return this; - } - - public void punch(Location at) { - Area a = new Area(at, radius); - - for (Entity i : a.getNearbyEntities()) { - if (ignore.contains(i)) { - continue; - } - - Vector force = VectorMath.direction(at, i.getLocation()); - double damage = 0; - double distance = i.getLocation().distance(at); - - if (forceMin < forceMax) { - force.clone().multiply(((1D - (distance / radius)) * (forceMax - forceMin)) + forceMin); - } - - if (damageMin < damageMax) { - damage = ((1D - (distance / radius)) * (damageMax - damageMin)) + damageMin; - } - - try { - if (i instanceof LivingEntity && damage > 0) { - ((LivingEntity) i).damage(damage); - } + private final List ignore; + private double radius; + private double forceMax; + private double forceMin; + private double damageMin; + private double damageMax; + + public Impulse(double radius) { + ignore = new ArrayList<>(); + this.radius = radius; + this.forceMax = 1; + this.forceMin = 0; + this.damageMax = 1; + this.damageMin = 0; + } + + public Impulse radius(double radius) { + this.radius = radius; + return this; + } + + public Impulse force(double force) { + this.forceMax = force; + return this; + } + + public Impulse force(double forceMax, double forceMin) { + this.forceMax = forceMax; + this.forceMin = forceMin; + return this; + } + + public Impulse damage(double damage) { + this.damageMax = damage; + return this; + } + + public Impulse damage(double damageMax, double damageMin) { + this.damageMax = damageMax; + this.damageMin = damageMin; + return this; + } + + public void punch(Location at) { + Area a = new Area(at, radius); + + for (Entity i : a.getNearbyEntities()) { + if (ignore.contains(i)) { + continue; + } + + Vector force = VectorMath.direction(at, i.getLocation()); + double damage = 0; + double distance = i.getLocation().distance(at); + + if (forceMin < forceMax) { + force.clone().multiply(((1D - (distance / radius)) * (forceMax - forceMin)) + forceMin); + } + + if (damageMin < damageMax) { + damage = ((1D - (distance / radius)) * (damageMax - damageMin)) + damageMin; + } + + try { + if (i instanceof LivingEntity && damage > 0) { + ((LivingEntity) i).damage(damage); + } - i.setVelocity(i.getVelocity().add(force)); - } catch (Exception e) { + i.setVelocity(i.getVelocity().add(force)); + } catch (Exception e) { - } - } + } } + } - public Impulse ignore(Entity player) { - ignore.add(player); - return this; - } + public Impulse ignore(Entity player) { + ignore.add(player); + return this; + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Info.java b/src/main/java/art/arcane/adapt/util/common/misc/Info.java index c121cb6e4..7c3e5e862 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Info.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Info.java @@ -21,7 +21,7 @@ import org.bukkit.Bukkit; public class Info { - public static String getPortIP() { - return Bukkit.getPort() + Bukkit.getIp(); - } + public static String getPortIP() { + return Bukkit.getPort() + Bukkit.getIp(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java b/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java index 620b1e679..63deed4a8 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/MaxNumber.java @@ -27,5 +27,5 @@ @Retention(RUNTIME) @Target({FIELD}) public @interface MaxNumber { - double value(); + double value(); } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java b/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java index d91da5206..6a116651b 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/MinNumber.java @@ -27,5 +27,5 @@ @Retention(RUNTIME) @Target({FIELD}) public @interface MinNumber { - double value(); + double value(); } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java b/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java index 8a0d89650..d9f40017f 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/NMSVersion.java @@ -22,117 +22,117 @@ import java.util.List; public enum NMSVersion { - R1_16, - R1_15, - R1_14, - R1_13, - R1_13_1, - R1_12, - R1_11, - R1_10, - R1_9_4, - R1_9_2, - R1_8; - - public static NMSVersion getMinimum() { - return values()[values().length - 1]; + R1_16, + R1_15, + R1_14, + R1_13, + R1_13_1, + R1_12, + R1_11, + R1_10, + R1_9_4, + R1_9_2, + R1_8; + + public static NMSVersion getMinimum() { + return values()[values().length - 1]; + } + + public static NMSVersion getMaximum() { + return values()[0]; + } + + public static NMSVersion current() { + if (tryVersion("1_8_R3")) { + return R1_8; } - public static NMSVersion getMaximum() { - return values()[0]; + if (tryVersion("1_9_R1")) { + return R1_9_2; } - public static NMSVersion current() { - if (tryVersion("1_8_R3")) { - return R1_8; - } - - if (tryVersion("1_9_R1")) { - return R1_9_2; - } - - if (tryVersion("1_9_R2")) { - return R1_9_4; - } - - if (tryVersion("1_10_R1")) { - return R1_10; - } + if (tryVersion("1_9_R2")) { + return R1_9_4; + } - if (tryVersion("1_11_R1")) { - return R1_11; - } + if (tryVersion("1_10_R1")) { + return R1_10; + } - if (tryVersion("1_12_R1")) { - return R1_12; - } + if (tryVersion("1_11_R1")) { + return R1_11; + } - if (tryVersion("1_13_R1")) { - return R1_13; - } + if (tryVersion("1_12_R1")) { + return R1_12; + } - if (tryVersion("1_13_R2")) { - return R1_13_1; - } + if (tryVersion("1_13_R1")) { + return R1_13; + } - if (tryVersion("1_14_R1")) { - return R1_14; - } + if (tryVersion("1_13_R2")) { + return R1_13_1; + } - if (tryVersion("1_15_R1")) { - return R1_15; - } + if (tryVersion("1_14_R1")) { + return R1_14; + } - if (tryVersion("1_16_R1")) { - return R1_16; - } - return null; + if (tryVersion("1_15_R1")) { + return R1_15; } - private static boolean tryVersion(String v) { - try { - Class.forName("org.bukkit.craftbukkit.v" + v + ".CraftWorld"); - return true; - } catch (Throwable e) { + if (tryVersion("1_16_R1")) { + return R1_16; + } + return null; + } - } + private static boolean tryVersion(String v) { + try { + Class.forName("org.bukkit.craftbukkit.v" + v + ".CraftWorld"); + return true; + } catch (Throwable e) { - return false; } - public List getAboveInclusive() { - List n = new ArrayList<>(); + return false; + } - for (NMSVersion i : values()) { - if (i.ordinal() >= ordinal()) { - n.add(i); - } - } + public List getAboveInclusive() { + List n = new ArrayList<>(); - return n; + for (NMSVersion i : values()) { + if (i.ordinal() >= ordinal()) { + n.add(i); + } } - public List betweenInclusive(NMSVersion other) { - List n = new ArrayList<>(); + return n; + } - for (NMSVersion i : values()) { - if (i.ordinal() <= Math.max(other.ordinal(), ordinal()) && i.ordinal() >= Math.min(ordinal(), other.ordinal())) { - n.add(i); - } - } + public List betweenInclusive(NMSVersion other) { + List n = new ArrayList<>(); - return n; + for (NMSVersion i : values()) { + if (i.ordinal() <= Math.max(other.ordinal(), ordinal()) && i.ordinal() >= Math.min(ordinal(), other.ordinal())) { + n.add(i); + } } - public List getBelowInclusive() { - List n = new ArrayList<>(); + return n; + } - for (NMSVersion i : values()) { - if (i.ordinal() <= ordinal()) { - n.add(i); - } - } + public List getBelowInclusive() { + List n = new ArrayList<>(); - return n; + for (NMSVersion i : values()) { + if (i.ordinal() <= ordinal()) { + n.add(i); + } } + + return n; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java b/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java index a57f9d556..40e70a622 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Shrinkwrap.java @@ -19,21 +19,21 @@ package art.arcane.adapt.util.common.misc; public class Shrinkwrap { - private T t; + private T t; - public Shrinkwrap(T t) { - set(t); - } + public Shrinkwrap(T t) { + set(t); + } - public Shrinkwrap() { - this(null); - } + public Shrinkwrap() { + this(null); + } - public T get() { - return t; - } + public T get() { + return t; + } - public void set(T t) { - this.t = t; - } + public void set(T t) { + this.t = t; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java index 600a54ea0..ccd36272c 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/SoundPlayer.java @@ -17,47 +17,47 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class SoundPlayer { - private final Collection players; + private final Collection players; - public static SoundPlayer of(Collection players) { - return new SoundPlayer(players); - } + public static SoundPlayer of(Collection players) { + return new SoundPlayer(players); + } - public static SoundPlayer of(Player player) { - return new SoundPlayer(List.of(player)); - } + public static SoundPlayer of(Player player) { + return new SoundPlayer(List.of(player)); + } - public static SoundPlayer of(World world) { - return new SoundPlayer(world.getPlayers()); - } + public static SoundPlayer of(World world) { + return new SoundPlayer(world.getPlayers()); + } - public void play(@NotNull Location location, @NotNull Sound sound) { - play(location, sound, 1.0f, 1.0f); - } + public void play(@NotNull Location location, @NotNull Sound sound) { + play(location, sound, 1.0f, 1.0f); + } - public void play(@NotNull Entity entity, @NotNull Sound sound, float volume, float pitch) { - play(entity.getLocation(), sound, volume, pitch); - } + public void play(@NotNull Entity entity, @NotNull Sound sound, float volume, float pitch) { + play(entity.getLocation(), sound, volume, pitch); + } - public void play(@NotNull Location location, @NotNull Sound sound, float volume, float pitch) { - if (!areSoundsEnabled()) { - return; - } - players.forEach(player -> player.playSound(location, sound, volume, pitch)); - //J.s(() -> Objects.requireNonNull(location.getWorld()).playSound(location, sound, volume, pitch)); + public void play(@NotNull Location location, @NotNull Sound sound, float volume, float pitch) { + if (!areSoundsEnabled()) { + return; } + players.forEach(player -> player.playSound(location, sound, volume, pitch)); + //J.s(() -> Objects.requireNonNull(location.getWorld()).playSound(location, sound, volume, pitch)); + } - public void play(@NotNull Location location, @NotNull Sound sound, SoundCategory category, float volume, float pitch) { - if (!areSoundsEnabled()) { - return; - } - players.forEach(player -> player.playSound(location, sound, category, volume, pitch)); - //J.s(() -> Objects.requireNonNull(location.getWorld()).playSound(location, sound, volume, pitch)); + public void play(@NotNull Location location, @NotNull Sound sound, SoundCategory category, float volume, float pitch) { + if (!areSoundsEnabled()) { + return; } + players.forEach(player -> player.playSound(location, sound, category, volume, pitch)); + //J.s(() -> Objects.requireNonNull(location.getWorld()).playSound(location, sound, volume, pitch)); + } - private boolean areSoundsEnabled() { - AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); - return effects == null || effects.isSoundsEnabled(); - } + private boolean areSoundsEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isSoundsEnabled(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java index a928d7fe1..e629c40c2 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ByteArrayTag.java @@ -25,43 +25,43 @@ */ public final class ByteArrayTag extends Tag { - /** - * The value. - */ - private final byte[] value; + /** + * The value. + */ + private final byte[] value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public ByteArrayTag(String name, byte[] value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ByteArrayTag(String name, byte[] value) { + super(name); + this.value = value; + } - @Override - public byte[] getValue() { - return value; - } + @Override + public byte[] getValue() { + return value; + } - @Override - public String toString() { - StringBuilder hex = new StringBuilder(); - for (byte b : value) { - String hexDigits = Integer.toHexString(b).toUpperCase(); - if (hexDigits.length() == 1) { - hex.append("0"); - } - hex.append(hexDigits).append(" "); - } - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Byte_Array" + append + ": " + hex; + @Override + public String toString() { + StringBuilder hex = new StringBuilder(); + for (byte b : value) { + String hexDigits = Integer.toHexString(b).toUpperCase(); + if (hexDigits.length() == 1) { + hex.append("0"); + } + hex.append(hexDigits).append(" "); + } + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Byte_Array" + append + ": " + hex; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java index a75c6887c..1f63051bd 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ByteTag.java @@ -25,35 +25,35 @@ */ public final class ByteTag extends Tag { - /** - * The value. - */ - private final byte value; + /** + * The value. + */ + private final byte value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public ByteTag(String name, byte value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ByteTag(String name, byte value) { + super(name); + this.value = value; + } - @Override - public Byte getValue() { - return value; - } + @Override + public Byte getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Byte" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Byte" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java index e74f5ea2e..b4d0bd1e9 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/CompoundTag.java @@ -27,41 +27,41 @@ */ public final class CompoundTag extends Tag { - /** - * The value. - */ - private final Map value; + /** + * The value. + */ + private final Map value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public CompoundTag(String name, Map value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public CompoundTag(String name, Map value) { + super(name); + this.value = value; + } - @Override - public Map getValue() { - return value; - } + @Override + public Map getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - StringBuilder bldr = new StringBuilder(); - bldr.append("TAG_Compound" + append + ": " + value.size() + " entries\r\n{\r\n"); - for (Map.Entry entry : value.entrySet()) { - bldr.append(" " + entry.getValue().toString().replaceAll("\r\n", "\r\n ") + "\r\n"); - } - bldr.append("}"); - return bldr.toString(); + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_Compound" + append + ": " + value.size() + " entries\r\n{\r\n"); + for (Map.Entry entry : value.entrySet()) { + bldr.append(" " + entry.getValue().toString().replaceAll("\r\n", "\r\n ") + "\r\n"); } + bldr.append("}"); + return bldr.toString(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java index 2aa7bcc36..689891a13 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/DoubleTag.java @@ -25,35 +25,35 @@ */ public final class DoubleTag extends Tag { - /** - * The value. - */ - private final double value; + /** + * The value. + */ + private final double value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public DoubleTag(String name, double value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public DoubleTag(String name, double value) { + super(name); + this.value = value; + } - @Override - public Double getValue() { - return value; - } + @Override + public Double getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Double" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Double" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java index 32a50b18a..62e498ec3 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/EndTag.java @@ -25,21 +25,21 @@ */ public final class EndTag extends Tag { - /** - * Creates the tag. - */ - public EndTag() { - super(""); - } + /** + * Creates the tag. + */ + public EndTag() { + super(""); + } - @Override - public Object getValue() { - return null; - } + @Override + public Object getValue() { + return null; + } - @Override - public String toString() { - return "TAG_End"; - } + @Override + public String toString() { + return "TAG_End"; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java index d46d19db5..62dae6cd1 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/FloatTag.java @@ -25,35 +25,35 @@ */ public final class FloatTag extends Tag { - /** - * The value. - */ - private final float value; + /** + * The value. + */ + private final float value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public FloatTag(String name, float value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public FloatTag(String name, float value) { + super(name); + this.value = value; + } - @Override - public Float getValue() { - return value; - } + @Override + public Float getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Float" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Float" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java index d271a96e8..681ddbefc 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/IntArrayTag.java @@ -27,35 +27,35 @@ */ public final class IntArrayTag extends Tag { - /** - * The value. - */ - private final int[] value; - - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public IntArrayTag(String name, int[] value) { - super(name); - this.value = value; - } - - @Override - public int[] getValue() { - return value; - } - - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Int_Array" + append + ": " + Arrays.toString(value); + /** + * The value. + */ + private final int[] value; + + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public IntArrayTag(String name, int[] value) { + super(name); + this.value = value; + } + + @Override + public int[] getValue() { + return value; + } + + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Int_Array" + append + ": " + Arrays.toString(value); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java index 92cf04d23..4d4539a74 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/IntTag.java @@ -25,35 +25,35 @@ */ public final class IntTag extends Tag { - /** - * The value. - */ - private final int value; + /** + * The value. + */ + private final int value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public IntTag(String name, int value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public IntTag(String name, int value) { + super(name); + this.value = value; + } - @Override - public Integer getValue() { - return value; - } + @Override + public Integer getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Int" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Int" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java index a06f6ea14..8d5aff06d 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ListTag.java @@ -28,57 +28,57 @@ */ public final class ListTag extends Tag { - /** - * The type. - */ - private final Class type; + /** + * The type. + */ + private final Class type; - /** - * The value. - */ - private final List value; + /** + * The value. + */ + private final List value; - /** - * Creates the tag. - * - * @param name The name. - * @param type The type of item in the list. - * @param value The value. - */ - public ListTag(String name, Class type, List value) { - super(name); - this.type = type; - this.value = Collections.unmodifiableList(value); - } + /** + * Creates the tag. + * + * @param name The name. + * @param type The type of item in the list. + * @param value The value. + */ + public ListTag(String name, Class type, List value) { + super(name); + this.type = type; + this.value = Collections.unmodifiableList(value); + } - /** - * Gets the type of item in this list. - * - * @return The type of item in this list. - */ - public Class getType() { - return type; - } + /** + * Gets the type of item in this list. + * + * @return The type of item in this list. + */ + public Class getType() { + return type; + } - @Override - public List getValue() { - return value; - } + @Override + public List getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - StringBuilder bldr = new StringBuilder(); - bldr.append("TAG_List" + append + ": " + value.size() + " entries of type " + NBTUtils.getTypeName(type) + "\r\n{\r\n"); - for (Tag t : value) { - bldr.append(" " + t.toString().replaceAll("\r\n", "\r\n ") + "\r\n"); - } - bldr.append("}"); - return bldr.toString(); + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; + } + StringBuilder bldr = new StringBuilder(); + bldr.append("TAG_List" + append + ": " + value.size() + " entries of type " + NBTUtils.getTypeName(type) + "\r\n{\r\n"); + for (Tag t : value) { + bldr.append(" " + t.toString().replaceAll("\r\n", "\r\n ") + "\r\n"); } + bldr.append("}"); + return bldr.toString(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java index b3220c1ed..dc17c2a43 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/LongTag.java @@ -25,35 +25,35 @@ */ public final class LongTag extends Tag { - /** - * The value. - */ - private final long value; + /** + * The value. + */ + private final long value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public LongTag(String name, long value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public LongTag(String name, long value) { + super(name); + this.value = value; + } - @Override - public Long getValue() { - return value; - } + @Override + public Long getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Long" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Long" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java index da7623f93..7d2526586 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTConstants.java @@ -32,32 +32,32 @@ */ public final class NBTConstants { - /** - * The character set used by NBT (UTF-8). - */ - public static final Charset CHARSET = StandardCharsets.UTF_8; - - /** - * Tag type constants. - */ - public static final int TYPE_END = 0, - TYPE_BYTE = 1, - TYPE_SHORT = 2, - TYPE_INT = 3, - TYPE_LONG = 4, - TYPE_FLOAT = 5, - TYPE_DOUBLE = 6, - TYPE_BYTE_ARRAY = 7, - TYPE_STRING = 8, - TYPE_LIST = 9, - TYPE_COMPOUND = 10, - TYPE_INT_ARRAY = 11; - - /** - * Default private constructor. - */ - private NBTConstants() { - - } + /** + * The character set used by NBT (UTF-8). + */ + public static final Charset CHARSET = StandardCharsets.UTF_8; + + /** + * Tag type constants. + */ + public static final int TYPE_END = 0, + TYPE_BYTE = 1, + TYPE_SHORT = 2, + TYPE_INT = 3, + TYPE_LONG = 4, + TYPE_FLOAT = 5, + TYPE_DOUBLE = 6, + TYPE_BYTE_ARRAY = 7, + TYPE_STRING = 8, + TYPE_LIST = 9, + TYPE_COMPOUND = 10, + TYPE_INT_ARRAY = 11; + + /** + * Default private constructor. + */ + private NBTConstants() { + + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java index 4deb6fd56..d79d63408 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTInputStream.java @@ -29,8 +29,7 @@ import java.util.zip.GZIPInputStream; /** - * Changes : - * Neil Wightman - Support 19133 Tag_Int_Array tag + * Changes : Neil Wightman - Support 19133 Tag_Int_Array tag */ /** @@ -48,144 +47,144 @@ */ public final class NBTInputStream implements Closeable { - /** - * The data input stream. - */ - private final DataInputStream is; - - /** - * Create a new NBTInputStream, which will source its data from the specified input stream. - * - * @param is The output stream - */ - public NBTInputStream(DataInputStream is) { - this.is = is; + /** + * The data input stream. + */ + private final DataInputStream is; + + /** + * Create a new NBTInputStream, which will source its data from the specified input stream. + * + * @param is The output stream + */ + public NBTInputStream(DataInputStream is) { + this.is = is; + } + + /** + * Creates a new NBTInputStream, which will source its data from the specified input stream. + * The stream will be decompressed using GZIP. + * + * @param is The input stream. + * @throws IOException if an I/O error occurs. + */ + public NBTInputStream(InputStream is) throws IOException { + this.is = new DataInputStream(new GZIPInputStream(is)); + } + + /** + * Reads an NBT tag from the stream. + * + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + public Tag readTag() throws IOException { + return readTag(0); + } + + /** + * Reads an NBT from the stream. + * + * @param depth The depth of this tag. + * @return The tag that was read. + * @throws IOException if an I/O error occurs. + */ + private Tag readTag(int depth) throws IOException { + int type = is.readByte() & 0xFF; + + String name; + if (type != NBTConstants.TYPE_END) { + int nameLength = is.readShort() & 0xFFFF; + byte[] nameBytes = new byte[nameLength]; + is.readFully(nameBytes); + name = new String(nameBytes, NBTConstants.CHARSET); + } else { + name = ""; } - /** - * Creates a new NBTInputStream, which will source its data from the specified input stream. - * The stream will be decompressed using GZIP. - * - * @param is The input stream. - * @throws IOException if an I/O error occurs. - */ - public NBTInputStream(InputStream is) throws IOException { - this.is = new DataInputStream(new GZIPInputStream(is)); - } - - /** - * Reads an NBT tag from the stream. - * - * @return The tag that was read. - * @throws IOException if an I/O error occurs. - */ - public Tag readTag() throws IOException { - return readTag(0); - } - - /** - * Reads an NBT from the stream. - * - * @param depth The depth of this tag. - * @return The tag that was read. - * @throws IOException if an I/O error occurs. - */ - private Tag readTag(int depth) throws IOException { - int type = is.readByte() & 0xFF; - - String name; - if (type != NBTConstants.TYPE_END) { - int nameLength = is.readShort() & 0xFFFF; - byte[] nameBytes = new byte[nameLength]; - is.readFully(nameBytes); - name = new String(nameBytes, NBTConstants.CHARSET); + return readTagPayload(type, name, depth); + } + + /** + * Reads the payload of a tag, given the name and type. + * + * @param type The type. + * @param name The name. + * @param depth The depth. + * @return The tag. + * @throws IOException if an I/O error occurs. + */ + private Tag readTagPayload(int type, String name, int depth) throws IOException { + switch (type) { + case NBTConstants.TYPE_END: + if (depth == 0) { + throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it."); } else { - name = ""; + return new EndTag(); + } + case NBTConstants.TYPE_BYTE: + return new ByteTag(name, is.readByte()); + case NBTConstants.TYPE_SHORT: + return new ShortTag(name, is.readShort()); + case NBTConstants.TYPE_INT: + return new IntTag(name, is.readInt()); + case NBTConstants.TYPE_LONG: + return new LongTag(name, is.readLong()); + case NBTConstants.TYPE_FLOAT: + return new FloatTag(name, is.readFloat()); + case NBTConstants.TYPE_DOUBLE: + return new DoubleTag(name, is.readDouble()); + case NBTConstants.TYPE_BYTE_ARRAY: + int length = is.readInt(); + byte[] bytes = new byte[length]; + is.readFully(bytes); + return new ByteArrayTag(name, bytes); + case NBTConstants.TYPE_STRING: + length = is.readShort(); + bytes = new byte[length]; + is.readFully(bytes); + return new StringTag(name, new String(bytes, NBTConstants.CHARSET)); + case NBTConstants.TYPE_LIST: + int childType = is.readByte(); + length = is.readInt(); + + List tagList = new ArrayList(); + for (int i = 0; i < length; i++) { + Tag tag = readTagPayload(childType, "", depth + 1); + if (tag instanceof EndTag) { + throw new IOException("TAG_End not permitted in a list."); + } + tagList.add(tag); } - return readTagPayload(type, name, depth); - } + return new ListTag(name, NBTUtils.getTypeClass(childType), tagList); + case NBTConstants.TYPE_COMPOUND: + Map tagMap = new HashMap(); + while (true) { + Tag tag = readTag(depth + 1); + if (tag instanceof EndTag) { + break; + } else { + tagMap.put(tag.getName(), tag); + } + } - /** - * Reads the payload of a tag, given the name and type. - * - * @param type The type. - * @param name The name. - * @param depth The depth. - * @return The tag. - * @throws IOException if an I/O error occurs. - */ - private Tag readTagPayload(int type, String name, int depth) throws IOException { - switch (type) { - case NBTConstants.TYPE_END: - if (depth == 0) { - throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it."); - } else { - return new EndTag(); - } - case NBTConstants.TYPE_BYTE: - return new ByteTag(name, is.readByte()); - case NBTConstants.TYPE_SHORT: - return new ShortTag(name, is.readShort()); - case NBTConstants.TYPE_INT: - return new IntTag(name, is.readInt()); - case NBTConstants.TYPE_LONG: - return new LongTag(name, is.readLong()); - case NBTConstants.TYPE_FLOAT: - return new FloatTag(name, is.readFloat()); - case NBTConstants.TYPE_DOUBLE: - return new DoubleTag(name, is.readDouble()); - case NBTConstants.TYPE_BYTE_ARRAY: - int length = is.readInt(); - byte[] bytes = new byte[length]; - is.readFully(bytes); - return new ByteArrayTag(name, bytes); - case NBTConstants.TYPE_STRING: - length = is.readShort(); - bytes = new byte[length]; - is.readFully(bytes); - return new StringTag(name, new String(bytes, NBTConstants.CHARSET)); - case NBTConstants.TYPE_LIST: - int childType = is.readByte(); - length = is.readInt(); - - List tagList = new ArrayList(); - for (int i = 0; i < length; i++) { - Tag tag = readTagPayload(childType, "", depth + 1); - if (tag instanceof EndTag) { - throw new IOException("TAG_End not permitted in a list."); - } - tagList.add(tag); - } - - return new ListTag(name, NBTUtils.getTypeClass(childType), tagList); - case NBTConstants.TYPE_COMPOUND: - Map tagMap = new HashMap(); - while (true) { - Tag tag = readTag(depth + 1); - if (tag instanceof EndTag) { - break; - } else { - tagMap.put(tag.getName(), tag); - } - } - - return new CompoundTag(name, tagMap); - case NBTConstants.TYPE_INT_ARRAY: - length = is.readInt(); - int[] value = new int[length]; - for (int i = 0; i < length; i++) { - value[i] = is.readInt(); - } - return new IntArrayTag(name, value); - default: - throw new IOException("Invalid tag type: " + type + "."); + return new CompoundTag(name, tagMap); + case NBTConstants.TYPE_INT_ARRAY: + length = is.readInt(); + int[] value = new int[length]; + for (int i = 0; i < length; i++) { + value[i] = is.readInt(); } + return new IntArrayTag(name, value); + default: + throw new IOException("Invalid tag type: " + type + "."); } + } - @Override - public void close() throws IOException { - is.close(); - } + @Override + public void close() throws IOException { + is.close(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java index 228898691..9cd19e745 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTOutputStream.java @@ -31,257 +31,260 @@ /** *

* This class writes NBT, or - * Named Binary Tag Tag objects to an underlying OutputStream.

+ * Named Binary Tag Tag objects to an underlying + * OutputStream.

* *

- * The NBT format was created by Markus Persson, and the specification may be found at + * The NBT format was created by Markus Persson, and the specification may be + * found at * http://www.minecraft.net/docs/NBT.txt.

* * @author Graham Edgecombe */ public final class NBTOutputStream implements Closeable { - /** - * The output stream. - */ - private final DataOutputStream os; + /** + * The output stream. + */ + private final DataOutputStream os; - /** - * Create a new NBTOutputStream, which will write data to the specified underlying output stream. - * - * @param os The output stream - */ - public NBTOutputStream(DataOutputStream os) { - this.os = os; - } - - /** - * Creates a new NBTOutputStream, which will write data to the specified underlying output stream. - * the stream will be compressed using GZIP. - * - * @param os The output stream. - * @throws IOException if an I/O error occurs. - */ - public NBTOutputStream(OutputStream os) throws IOException { - this.os = new DataOutputStream(new GZIPOutputStream(os)); - } + /** + * Create a new NBTOutputStream, which will write data to the + * specified underlying output stream. + * + * @param os The output stream + */ + public NBTOutputStream(DataOutputStream os) { + this.os = os; + } - /** - * Writes a tag. - * - * @param tag The tag to write. - * @throws IOException if an I/O error occurs. - */ - public void writeTag(Tag tag) throws IOException { - int type = NBTUtils.getTypeCode(tag.getClass()); - String name = tag.getName(); - byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + /** + * Creates a new NBTOutputStream, which will write data to the + * specified underlying output stream. the stream will be compressed using + * GZIP. + * + * @param os The output stream. + * @throws IOException if an I/O error occurs. + */ + public NBTOutputStream(OutputStream os) throws IOException { + this.os = new DataOutputStream(new GZIPOutputStream(os)); + } - os.writeByte(type); - os.writeShort(nameBytes.length); - os.write(nameBytes); + /** + * Writes a tag. + * + * @param tag The tag to write. + * @throws IOException if an I/O error occurs. + */ + public void writeTag(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + String name = tag.getName(); + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); - if (type == NBTConstants.TYPE_END) { - throw new IOException("Named TAG_End not permitted."); - } + os.writeByte(type); + os.writeShort(nameBytes.length); + os.write(nameBytes); - writeTagPayload(tag); + if (type == NBTConstants.TYPE_END) { + throw new IOException("Named TAG_End not permitted."); } - /** - * Writes tag payload. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeTagPayload(Tag tag) throws IOException { - int type = NBTUtils.getTypeCode(tag.getClass()); - switch (type) { - case NBTConstants.TYPE_END: - writeEndTagPayload((EndTag) tag); - break; - case NBTConstants.TYPE_BYTE: - writeByteTagPayload((ByteTag) tag); - break; - case NBTConstants.TYPE_SHORT: - writeShortTagPayload((ShortTag) tag); - break; - case NBTConstants.TYPE_INT: - writeIntTagPayload((IntTag) tag); - break; - case NBTConstants.TYPE_LONG: - writeLongTagPayload((LongTag) tag); - break; - case NBTConstants.TYPE_FLOAT: - writeFloatTagPayload((FloatTag) tag); - break; - case NBTConstants.TYPE_DOUBLE: - writeDoubleTagPayload((DoubleTag) tag); - break; - case NBTConstants.TYPE_BYTE_ARRAY: - writeByteArrayTagPayload((ByteArrayTag) tag); - break; - case NBTConstants.TYPE_STRING: - writeStringTagPayload((StringTag) tag); - break; - case NBTConstants.TYPE_LIST: - writeListTagPayload((ListTag) tag); - break; - case NBTConstants.TYPE_COMPOUND: - writeCompoundTagPayload((CompoundTag) tag); - break; - case NBTConstants.TYPE_INT_ARRAY: - writeIntArrayTagPayload((IntArrayTag) tag); - break; - default: - throw new IOException("Invalid tag type: " + type + "."); - } - } + writeTagPayload(tag); + } - /** - * Writes a TAG_Byte tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeByteTagPayload(ByteTag tag) throws IOException { - os.writeByte(tag.getValue()); + /** + * Writes tag payload. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeTagPayload(Tag tag) throws IOException { + int type = NBTUtils.getTypeCode(tag.getClass()); + switch (type) { + case NBTConstants.TYPE_END: + writeEndTagPayload((EndTag) tag); + break; + case NBTConstants.TYPE_BYTE: + writeByteTagPayload((ByteTag) tag); + break; + case NBTConstants.TYPE_SHORT: + writeShortTagPayload((ShortTag) tag); + break; + case NBTConstants.TYPE_INT: + writeIntTagPayload((IntTag) tag); + break; + case NBTConstants.TYPE_LONG: + writeLongTagPayload((LongTag) tag); + break; + case NBTConstants.TYPE_FLOAT: + writeFloatTagPayload((FloatTag) tag); + break; + case NBTConstants.TYPE_DOUBLE: + writeDoubleTagPayload((DoubleTag) tag); + break; + case NBTConstants.TYPE_BYTE_ARRAY: + writeByteArrayTagPayload((ByteArrayTag) tag); + break; + case NBTConstants.TYPE_STRING: + writeStringTagPayload((StringTag) tag); + break; + case NBTConstants.TYPE_LIST: + writeListTagPayload((ListTag) tag); + break; + case NBTConstants.TYPE_COMPOUND: + writeCompoundTagPayload((CompoundTag) tag); + break; + case NBTConstants.TYPE_INT_ARRAY: + writeIntArrayTagPayload((IntArrayTag) tag); + break; + default: + throw new IOException("Invalid tag type: " + type + "."); } + } - /** - * Writes a TAG_Byte_Array tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException { - byte[] bytes = tag.getValue(); - os.writeInt(bytes.length); - os.write(bytes); - } + /** + * Writes a TAG_Byte tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteTagPayload(ByteTag tag) throws IOException { + os.writeByte(tag.getValue()); + } + + /** + * Writes a TAG_Byte_Array tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeByteArrayTagPayload(ByteArrayTag tag) throws IOException { + byte[] bytes = tag.getValue(); + os.writeInt(bytes.length); + os.write(bytes); + } - /** - * Writes a TAG_Compound tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeCompoundTagPayload(CompoundTag tag) throws IOException { - for (Tag childTag : tag.getValue().values()) { - writeTag(childTag); - } - os.writeByte((byte) 0); // end tag - better way? + /** + * Writes a TAG_Compound tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeCompoundTagPayload(CompoundTag tag) throws IOException { + for (Tag childTag : tag.getValue().values()) { + writeTag(childTag); } + os.writeByte((byte) 0); // end tag - better way? + } - /** - * Writes a TAG_List tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeListTagPayload(ListTag tag) throws IOException { - Class clazz = tag.getType(); - List tags = tag.getValue(); - int size = tags.size(); + /** + * Writes a TAG_List tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeListTagPayload(ListTag tag) throws IOException { + Class clazz = tag.getType(); + List tags = tag.getValue(); + int size = tags.size(); - os.writeByte(NBTUtils.getTypeCode(clazz)); - os.writeInt(size); - for (int i = 0; i < size; i++) { - writeTagPayload(tags.get(i)); - } + os.writeByte(NBTUtils.getTypeCode(clazz)); + os.writeInt(size); + for (int i = 0; i < size; i++) { + writeTagPayload(tags.get(i)); } + } - /** - * Writes a TAG_String tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeStringTagPayload(StringTag tag) throws IOException { - byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET); - os.writeShort(bytes.length); - os.write(bytes); - } + /** + * Writes a TAG_String tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeStringTagPayload(StringTag tag) throws IOException { + byte[] bytes = tag.getValue().getBytes(NBTConstants.CHARSET); + os.writeShort(bytes.length); + os.write(bytes); + } - /** - * Writes a TAG_Double tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeDoubleTagPayload(DoubleTag tag) throws IOException { - os.writeDouble(tag.getValue()); - } + /** + * Writes a TAG_Double tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeDoubleTagPayload(DoubleTag tag) throws IOException { + os.writeDouble(tag.getValue()); + } - /** - * Writes a TAG_Float tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeFloatTagPayload(FloatTag tag) throws IOException { - os.writeFloat(tag.getValue()); - } + /** + * Writes a TAG_Float tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeFloatTagPayload(FloatTag tag) throws IOException { + os.writeFloat(tag.getValue()); + } - /** - * Writes a TAG_Long tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeLongTagPayload(LongTag tag) throws IOException { - os.writeLong(tag.getValue()); - } + /** + * Writes a TAG_Long tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeLongTagPayload(LongTag tag) throws IOException { + os.writeLong(tag.getValue()); + } - /** - * Writes a TAG_Int tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeIntTagPayload(IntTag tag) throws IOException { - os.writeInt(tag.getValue()); - } + /** + * Writes a TAG_Int tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeIntTagPayload(IntTag tag) throws IOException { + os.writeInt(tag.getValue()); + } - /** - * Writes a TAG_Short tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeShortTagPayload(ShortTag tag) throws IOException { - os.writeShort(tag.getValue()); - } + /** + * Writes a TAG_Short tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeShortTagPayload(ShortTag tag) throws IOException { + os.writeShort(tag.getValue()); + } - /** - * Writes a TAG_Empty tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeEndTagPayload(EndTag tag) { - /* empty */ - } + /** + * Writes a TAG_Empty tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeEndTagPayload(EndTag tag) { + /* empty */ + } - /** - * Writes a TAG_Int_Array tag. - * - * @param tag The tag. - * @throws IOException if an I/O error occurs. - */ - private void writeIntArrayTagPayload(IntArrayTag tag) throws IOException { - final int[] values = tag.getValue(); - os.writeInt(values.length); - for (final int value : values) { - os.writeInt(value); - } + /** + * Writes a TAG_Int_Array tag. + * + * @param tag The tag. + * @throws IOException if an I/O error occurs. + */ + private void writeIntArrayTagPayload(IntArrayTag tag) throws IOException { + final int[] values = tag.getValue(); + os.writeInt(values.length); + for (final int value : values) { + os.writeInt(value); } + } - @Override - public void close() throws IOException { - os.close(); - } + @Override + public void close() throws IOException { + os.close(); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java b/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java index e3afc41d7..edb346b95 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NBTUtils.java @@ -23,129 +23,129 @@ */ /** - * A class which contains NBT-related utility methods. This currently supports reading 19133 but only writing - * 19132. + * A class which contains NBT-related utility methods. This currently supports + * reading 19133 but only writing 19132. * * @author Graham Edgecombe */ public final class NBTUtils { - /** - * Default private constructor. - */ - private NBTUtils() { + /** + * Default private constructor. + */ + private NBTUtils() { - } + } - /** - * Gets the type name of a tag. - * - * @param clazz The tag class. - * @return The type name. - */ - public static String getTypeName(Class clazz) { - if (clazz.equals(ByteArrayTag.class)) { - return "TAG_Byte_Array"; - } else if (clazz.equals(ByteTag.class)) { - return "TAG_Byte"; - } else if (clazz.equals(CompoundTag.class)) { - return "TAG_Compound"; - } else if (clazz.equals(DoubleTag.class)) { - return "TAG_Double"; - } else if (clazz.equals(EndTag.class)) { - return "TAG_End"; - } else if (clazz.equals(FloatTag.class)) { - return "TAG_Float"; - } else if (clazz.equals(IntTag.class)) { - return "TAG_Int"; - } else if (clazz.equals(ListTag.class)) { - return "TAG_List"; - } else if (clazz.equals(LongTag.class)) { - return "TAG_Long"; - } else if (clazz.equals(ShortTag.class)) { - return "TAG_Short"; - } else if (clazz.equals(StringTag.class)) { - return "TAG_String"; - } else if (clazz.equals(IntArrayTag.class)) { - return "TAG_Int_Array"; - } else { - throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); - } + /** + * Gets the type name of a tag. + * + * @param clazz The tag class. + * @return The type name. + */ + public static String getTypeName(Class clazz) { + if (clazz.equals(ByteArrayTag.class)) { + return "TAG_Byte_Array"; + } else if (clazz.equals(ByteTag.class)) { + return "TAG_Byte"; + } else if (clazz.equals(CompoundTag.class)) { + return "TAG_Compound"; + } else if (clazz.equals(DoubleTag.class)) { + return "TAG_Double"; + } else if (clazz.equals(EndTag.class)) { + return "TAG_End"; + } else if (clazz.equals(FloatTag.class)) { + return "TAG_Float"; + } else if (clazz.equals(IntTag.class)) { + return "TAG_Int"; + } else if (clazz.equals(ListTag.class)) { + return "TAG_List"; + } else if (clazz.equals(LongTag.class)) { + return "TAG_Long"; + } else if (clazz.equals(ShortTag.class)) { + return "TAG_Short"; + } else if (clazz.equals(StringTag.class)) { + return "TAG_String"; + } else if (clazz.equals(IntArrayTag.class)) { + return "TAG_Int_Array"; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); } + } - /** - * Gets the type code of a tag class. - * - * @param clazz The tag class. - * @return The type code. - * @throws IllegalArgumentException if the tag class is invalid. - */ - public static int getTypeCode(Class clazz) { - if (clazz.equals(ByteArrayTag.class)) { - return NBTConstants.TYPE_BYTE_ARRAY; - } else if (clazz.equals(ByteTag.class)) { - return NBTConstants.TYPE_BYTE; - } else if (clazz.equals(CompoundTag.class)) { - return NBTConstants.TYPE_COMPOUND; - } else if (clazz.equals(DoubleTag.class)) { - return NBTConstants.TYPE_DOUBLE; - } else if (clazz.equals(EndTag.class)) { - return NBTConstants.TYPE_END; - } else if (clazz.equals(FloatTag.class)) { - return NBTConstants.TYPE_FLOAT; - } else if (clazz.equals(IntTag.class)) { - return NBTConstants.TYPE_INT; - } else if (clazz.equals(ListTag.class)) { - return NBTConstants.TYPE_LIST; - } else if (clazz.equals(LongTag.class)) { - return NBTConstants.TYPE_LONG; - } else if (clazz.equals(ShortTag.class)) { - return NBTConstants.TYPE_SHORT; - } else if (clazz.equals(StringTag.class)) { - return NBTConstants.TYPE_STRING; - } else if (clazz.equals(IntArrayTag.class)) { - return NBTConstants.TYPE_INT_ARRAY; - } else { - throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); - } + /** + * Gets the type code of a tag class. + * + * @param clazz The tag class. + * @return The type code. + * @throws IllegalArgumentException if the tag class is invalid. + */ + public static int getTypeCode(Class clazz) { + if (clazz.equals(ByteArrayTag.class)) { + return NBTConstants.TYPE_BYTE_ARRAY; + } else if (clazz.equals(ByteTag.class)) { + return NBTConstants.TYPE_BYTE; + } else if (clazz.equals(CompoundTag.class)) { + return NBTConstants.TYPE_COMPOUND; + } else if (clazz.equals(DoubleTag.class)) { + return NBTConstants.TYPE_DOUBLE; + } else if (clazz.equals(EndTag.class)) { + return NBTConstants.TYPE_END; + } else if (clazz.equals(FloatTag.class)) { + return NBTConstants.TYPE_FLOAT; + } else if (clazz.equals(IntTag.class)) { + return NBTConstants.TYPE_INT; + } else if (clazz.equals(ListTag.class)) { + return NBTConstants.TYPE_LIST; + } else if (clazz.equals(LongTag.class)) { + return NBTConstants.TYPE_LONG; + } else if (clazz.equals(ShortTag.class)) { + return NBTConstants.TYPE_SHORT; + } else if (clazz.equals(StringTag.class)) { + return NBTConstants.TYPE_STRING; + } else if (clazz.equals(IntArrayTag.class)) { + return NBTConstants.TYPE_INT_ARRAY; + } else { + throw new IllegalArgumentException("Invalid tag classs (" + clazz.getName() + ")."); } + } - /** - * Gets the class of a type of tag. - * - * @param type The type. - * @return The class. - * @throws IllegalArgumentException if the tag type is invalid. - */ - public static Class getTypeClass(int type) { - switch (type) { - case NBTConstants.TYPE_END: - return EndTag.class; - case NBTConstants.TYPE_BYTE: - return ByteTag.class; - case NBTConstants.TYPE_SHORT: - return ShortTag.class; - case NBTConstants.TYPE_INT: - return IntTag.class; - case NBTConstants.TYPE_LONG: - return LongTag.class; - case NBTConstants.TYPE_FLOAT: - return FloatTag.class; - case NBTConstants.TYPE_DOUBLE: - return DoubleTag.class; - case NBTConstants.TYPE_BYTE_ARRAY: - return ByteArrayTag.class; - case NBTConstants.TYPE_STRING: - return StringTag.class; - case NBTConstants.TYPE_LIST: - return ListTag.class; - case NBTConstants.TYPE_COMPOUND: - return CompoundTag.class; - case NBTConstants.TYPE_INT_ARRAY: - return IntArrayTag.class; - default: - throw new IllegalArgumentException("Invalid tag type : " + type + "."); - } + /** + * Gets the class of a type of tag. + * + * @param type The type. + * @return The class. + * @throws IllegalArgumentException if the tag type is invalid. + */ + public static Class getTypeClass(int type) { + switch (type) { + case NBTConstants.TYPE_END: + return EndTag.class; + case NBTConstants.TYPE_BYTE: + return ByteTag.class; + case NBTConstants.TYPE_SHORT: + return ShortTag.class; + case NBTConstants.TYPE_INT: + return IntTag.class; + case NBTConstants.TYPE_LONG: + return LongTag.class; + case NBTConstants.TYPE_FLOAT: + return FloatTag.class; + case NBTConstants.TYPE_DOUBLE: + return DoubleTag.class; + case NBTConstants.TYPE_BYTE_ARRAY: + return ByteArrayTag.class; + case NBTConstants.TYPE_STRING: + return StringTag.class; + case NBTConstants.TYPE_LIST: + return ListTag.class; + case NBTConstants.TYPE_COMPOUND: + return CompoundTag.class; + case NBTConstants.TYPE_INT_ARRAY: + return IntArrayTag.class; + default: + throw new IllegalArgumentException("Invalid tag type : " + type + "."); } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java index aa0b9de3f..14579c303 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/NibbleArray.java @@ -28,170 +28,170 @@ import java.util.StringJoiner; public class NibbleArray implements Writable { - private static final int[] MASKS = new int[8]; + private static final int[] MASKS = new int[8]; - static { - for (int i = 0; i < MASKS.length; i++) { - MASKS[i] = maskFor(i); - } + static { + for (int i = 0; i < MASKS.length; i++) { + MASKS[i] = maskFor(i); } + } - private final int size; - private final Object lock = new Object(); - private byte[] data; - private int depth; - private byte mask; - private transient int bitIndex, byteIndex, bitInByte; + private final int size; + private final Object lock = new Object(); + private byte[] data; + private int depth; + private byte mask; + private transient int bitIndex, byteIndex, bitInByte; - public NibbleArray(int capacity, DataInputStream in) throws IOException { - size = capacity; - read(in); - } - - public NibbleArray(int nibbleDepth, int capacity) { - if (nibbleDepth > 8 || nibbleDepth < 1) { - throw new IllegalArgumentException(); - } + public NibbleArray(int capacity, DataInputStream in) throws IOException { + size = capacity; + read(in); + } - int neededBits = nibbleDepth * capacity; - - size = capacity; - depth = nibbleDepth; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(nibbleDepth); + public NibbleArray(int nibbleDepth, int capacity) { + if (nibbleDepth > 8 || nibbleDepth < 1) { + throw new IllegalArgumentException(); } - public NibbleArray(int nibbleDepth, int capacity, NibbleArray existing) { - if (nibbleDepth > 8 || nibbleDepth < 1) { - throw new IllegalArgumentException(); - } + int neededBits = nibbleDepth * capacity; - int neededBits = nibbleDepth * capacity; - size = capacity; - depth = nibbleDepth; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(nibbleDepth); + size = capacity; + depth = nibbleDepth; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(nibbleDepth); + } - for (int i = 0; i < Math.min(size, existing.size()); i++) { - set(i, existing.get(i)); - } + public NibbleArray(int nibbleDepth, int capacity, NibbleArray existing) { + if (nibbleDepth > 8 || nibbleDepth < 1) { + throw new IllegalArgumentException(); } - public static int maskFor(int amountOfBits) { - return powerOfTwo(amountOfBits) - 1; + int neededBits = nibbleDepth * capacity; + size = capacity; + depth = nibbleDepth; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(nibbleDepth); + + for (int i = 0; i < Math.min(size, existing.size()); i++) { + set(i, existing.get(i)); } + } - public static int powerOfTwo(int power) { - int result = 1; + public static int maskFor(int amountOfBits) { + return powerOfTwo(amountOfBits) - 1; + } - for (int i = 0; i < power; i++) { - result *= 2; - } + public static int powerOfTwo(int power) { + int result = 1; - return result; + for (int i = 0; i < power; i++) { + result *= 2; } - public static String binaryString(byte b, ByteOrder byteOrder) { - String str = String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(' ', '0'); + return result; + } - return byteOrder.equals(ByteOrder.BIG_ENDIAN) ? str : reverse(str); - } + public static String binaryString(byte b, ByteOrder byteOrder) { + String str = String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(' ', '0'); - public static String reverse(String str) { - return new StringBuilder(str).reverse().toString(); - } + return byteOrder.equals(ByteOrder.BIG_ENDIAN) ? str : reverse(str); + } - @Override - public void write(DataOutputStream o) throws IOException { - o.writeByte(depth + Byte.MIN_VALUE); - o.write(data); - } + public static String reverse(String str) { + return new StringBuilder(str).reverse().toString(); + } - @Override - public void read(DataInputStream i) throws IOException { - depth = i.readByte() - Byte.MIN_VALUE; - int neededBits = depth * size; - data = new byte[(neededBits + neededBits % 8) / 8]; - mask = (byte) maskFor(depth); - i.read(data); - } + @Override + public void write(DataOutputStream o) throws IOException { + o.writeByte(depth + Byte.MIN_VALUE); + o.write(data); + } - public int size() { - return size; - } + @Override + public void read(DataInputStream i) throws IOException { + depth = i.readByte() - Byte.MIN_VALUE; + int neededBits = depth * size; + data = new byte[(neededBits + neededBits % 8) / 8]; + mask = (byte) maskFor(depth); + i.read(data); + } - public byte get(int index) { - synchronized (lock) { - bitIndex = index * depth; - byteIndex = bitIndex >> 3; - bitInByte = bitIndex & 7; - int value = data[byteIndex] >> bitInByte; + public int size() { + return size; + } - if (bitInByte + depth > 8) { - value |= data[byteIndex + 1] << bitInByte; - } + public byte get(int index) { + synchronized (lock) { + bitIndex = index * depth; + byteIndex = bitIndex >> 3; + bitInByte = bitIndex & 7; + int value = data[byteIndex] >> bitInByte; - return (byte) (value & mask); - } - } + if (bitInByte + depth > 8) { + value |= data[byteIndex + 1] << bitInByte; + } - public byte getAsync(int index) { - int bitIndex = index * depth; - int byteIndex = bitIndex >> 3; - int bitInByte = bitIndex & 7; - int value = data[byteIndex] >> bitInByte; + return (byte) (value & mask); + } + } - if (bitInByte + depth > 8) { - value |= data[byteIndex + 1] << bitInByte; - } + public byte getAsync(int index) { + int bitIndex = index * depth; + int byteIndex = bitIndex >> 3; + int bitInByte = bitIndex & 7; + int value = data[byteIndex] >> bitInByte; - return (byte) (value & mask); + if (bitInByte + depth > 8) { + value |= data[byteIndex + 1] << bitInByte; } - public void set(int index, int nibble) { - set(index, (byte) nibble); - } + return (byte) (value & mask); + } - public void set(int index, byte nybble) { - synchronized (lock) { - bitIndex = index * depth; - byteIndex = bitIndex >> 3; - bitInByte = bitIndex & 7; - data[byteIndex] = (byte) (((~(data[byteIndex] & (mask << bitInByte)) & data[byteIndex]) | ((nybble & mask) << bitInByte)) & 0xff); - - if (bitInByte + depth > 8) { - data[byteIndex + 1] = (byte) (((~(data[byteIndex + 1] & MASKS[bitInByte + depth - 8]) & data[byteIndex + 1]) | ((nybble & mask) >> (8 - bitInByte))) & 0xff); - } - } - } + public void set(int index, int nibble) { + set(index, (byte) nibble); + } + + public void set(int index, byte nybble) { + synchronized (lock) { + bitIndex = index * depth; + byteIndex = bitIndex >> 3; + bitInByte = bitIndex & 7; + data[byteIndex] = (byte) (((~(data[byteIndex] & (mask << bitInByte)) & data[byteIndex]) | ((nybble & mask) << bitInByte)) & 0xff); - public String toBitsString() { - return toBitsString(ByteOrder.BIG_ENDIAN); + if (bitInByte + depth > 8) { + data[byteIndex + 1] = (byte) (((~(data[byteIndex + 1] & MASKS[bitInByte + depth - 8]) & data[byteIndex + 1]) | ((nybble & mask) >> (8 - bitInByte))) & 0xff); + } } + } - public String toBitsString(ByteOrder byteOrder) { - StringJoiner joiner = new StringJoiner(" "); + public String toBitsString() { + return toBitsString(ByteOrder.BIG_ENDIAN); + } - for (int i = 0; i < data.length; i++) { - joiner.add(binaryString(data[i], byteOrder)); - } + public String toBitsString(ByteOrder byteOrder) { + StringJoiner joiner = new StringJoiner(" "); - return joiner.toString(); + for (int i = 0; i < data.length; i++) { + joiner.add(binaryString(data[i], byteOrder)); } - public void clear() { - Arrays.fill(data, (byte) 0); - } + return joiner.toString(); + } + + public void clear() { + Arrays.fill(data, (byte) 0); + } - public void setAll(byte nibble) { - for (int i = 0; i < size; i++) { - set(i, nibble); - } + public void setAll(byte nibble) { + for (int i = 0; i < size; i++) { + set(i, nibble); } + } - public void setAll(int nibble) { - for (int i = 0; i < size; i++) { - set(i, (byte) nibble); - } + public void setAll(int nibble) { + for (int i = 0; i < size; i++) { + set(i, (byte) nibble); } + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java index c73827b47..e1a428be2 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/ShortTag.java @@ -25,35 +25,35 @@ */ public final class ShortTag extends Tag { - /** - * The value. - */ - private final short value; + /** + * The value. + */ + private final short value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public ShortTag(String name, short value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public ShortTag(String name, short value) { + super(name); + this.value = value; + } - @Override - public Short getValue() { - return value; - } + @Override + public Short getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_Short" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_Short" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java b/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java index c25dfb8e3..4bb1cc73f 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/StringTag.java @@ -25,35 +25,35 @@ */ public final class StringTag extends Tag { - /** - * The value. - */ - private final String value; + /** + * The value. + */ + private final String value; - /** - * Creates the tag. - * - * @param name The name. - * @param value The value. - */ - public StringTag(String name, String value) { - super(name); - this.value = value; - } + /** + * Creates the tag. + * + * @param name The name. + * @param value The value. + */ + public StringTag(String name, String value) { + super(name); + this.value = value; + } - @Override - public String getValue() { - return value; - } + @Override + public String getValue() { + return value; + } - @Override - public String toString() { - String name = getName(); - String append = ""; - if (name != null && !name.equals("")) { - append = "(\"" + this.getName() + "\")"; - } - return "TAG_String" + append + ": " + value; + @Override + public String toString() { + String name = getName(); + String append = ""; + if (name != null && !name.equals("")) { + append = "(\"" + this.getName() + "\")"; } + return "TAG_String" + append + ": " + value; + } } diff --git a/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java b/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java index 267eaa66c..6a6e3d278 100644 --- a/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java +++ b/src/main/java/art/arcane/adapt/util/common/nbt/Tag.java @@ -25,34 +25,34 @@ */ public abstract class Tag { - /** - * The name of this tag. - */ - private final String name; + /** + * The name of this tag. + */ + private final String name; - /** - * Creates the tag with the specified name. - * - * @param name The name. - */ - public Tag(String name) { - this.name = name; - } + /** + * Creates the tag with the specified name. + * + * @param name The name. + */ + public Tag(String name) { + this.name = name; + } - /** - * Gets the name of this tag. - * - * @return The name of this tag. - */ - public final String getName() { - return name; - } + /** + * Gets the name of this tag. + * + * @return The name of this tag. + */ + public final String getName() { + return name; + } - /** - * Gets the value of this tag. - * - * @return The value of this tag. - */ - public abstract Object getValue(); + /** + * Gets the value of this tag. + * + * @return The value of this tag. + */ + public abstract Object getValue(); } diff --git a/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java b/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java index 4592279ce..f9aab0f77 100644 --- a/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java +++ b/src/main/java/art/arcane/adapt/util/common/parallel/BurstExecutor.java @@ -3,7 +3,7 @@ import java.util.concurrent.ExecutorService; public class BurstExecutor extends art.arcane.volmlib.util.parallel.BurstExecutorSupport { - public BurstExecutor(ExecutorService executor, int burstSizeEstimate) { - super(executor, burstSizeEstimate, Throwable::printStackTrace); - } + public BurstExecutor(ExecutorService executor, int burstSizeEstimate) { + super(executor, burstSizeEstimate, Throwable::printStackTrace); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java index 20b5b8823..c11f9ea17 100644 --- a/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java +++ b/src/main/java/art/arcane/adapt/util/common/parallel/MultiBurst.java @@ -6,35 +6,35 @@ import java.util.function.IntSupplier; public class MultiBurst extends art.arcane.volmlib.util.parallel.MultiBurstSupport { - private static final long TIMEOUT = Long.getLong("adapt.burst.timeout", 15000); - public static MultiBurst burst = new MultiBurst(Runtime.getRuntime().availableProcessors()); - - public MultiBurst(int tc) { - this(() -> tc); - } - - public MultiBurst(IntSupplier parallelism) { - super("Adapt Workgroup", Thread.MAX_PRIORITY, parallelism, i -> Math.max(i, 1), System::currentTimeMillis, - e -> { - Adapt.info("Exception encountered in burst thread"); - e.printStackTrace(); - }, - Adapt::info, - Adapt::warn, - TIMEOUT); - } - - public ExecutorService getService() { - return service(); - } - - @Override - public BurstExecutor burst(int estimate) { - return new BurstExecutor(service(), estimate); - } - - @Override - public BurstExecutor burst() { - return burst(16); - } + private static final long TIMEOUT = Long.getLong("adapt.burst.timeout", 15000); + public static MultiBurst burst = new MultiBurst(Runtime.getRuntime().availableProcessors()); + + public MultiBurst(int tc) { + this(() -> tc); + } + + public MultiBurst(IntSupplier parallelism) { + super("Adapt Workgroup", Thread.MAX_PRIORITY, parallelism, i -> Math.max(i, 1), System::currentTimeMillis, + e -> { + Adapt.info("Exception encountered in burst thread"); + e.printStackTrace(); + }, + Adapt::info, + Adapt::warn, + TIMEOUT); + } + + public ExecutorService getService() { + return service(); + } + + @Override + public BurstExecutor burst(int estimate) { + return new BurstExecutor(service(), estimate); + } + + @Override + public BurstExecutor burst() { + return burst(16); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java b/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java index 740b8f710..59eccd8bb 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/AdaptService.java @@ -22,11 +22,11 @@ import org.bukkit.event.Listener; public interface AdaptService extends Listener { - void onEnable(); + void onEnable(); - void onDisable(); + void onDisable(); - default void postShutdown(Runnable r) { - Adapt.instance.postShutdown(r); - } + default void postShutdown(Runnable r) { + Adapt.instance.postShutdown(r); + } } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java index 29eed1130..f0bc127c9 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/Metrics.java @@ -45,821 +45,839 @@ public class Metrics { - private final Plugin plugin; + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my + * plugin id? + */ + public Metrics(JavaPlugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ex) { + Adapt.verbose("Failed to save bStats config file: " + + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> J.s(submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } - private final MetricsBase metricsBase; + public static class MetricsBase { /** - * Creates a new Metrics instance. + * The version of the Metrics class. + */ + public static final String METRICS_VERSION = "2.2.1"; + + private static final ScheduledExecutorService scheduler = + Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. * - * @param plugin Your plugin instance. - * @param serviceId The id of the service. It can be found at What is my plugin id? + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is + * enabled. + * @param appendPlatformDataConsumer A consumer that receives a + * {@code JsonObjectBuilder} and appends + * all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a + * {@code JsonObjectBuilder} and appends + * all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with + * the submit task. This can be used to + * delegate the data collection to a + * another thread to prevent errors + * caused by concurrency. Can be + * {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is + * still enabled. + * @param errorLogger A consumer that accepts log message + * and an error. + * @param infoLogger A consumer that accepts info log + * messages. + * @param logErrors Whether or not errors should be + * logged. + * @param logSentData Whether or not the sent data should be + * logged. + * @param logResponseStatusText Whether or not the response status + * text should be logged. */ - public Metrics(JavaPlugin plugin, int serviceId) { - this.plugin = plugin; - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - if (!config.isSet("serverUuid")) { - config.addDefault("enabled", true); - config.addDefault("serverUuid", UUID.randomUUID().toString()); - config.addDefault("logFailedRequests", false); - config.addDefault("logSentData", false); - config.addDefault("logResponseStatusText", false); - // Inform the server owners about bStats - config - .options() - .header( - "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" - + "many people use their plugin and their total player count. It's recommended to keep bStats\n" - + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" - + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" - + "anonymous.") - .copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ex) { - Adapt.verbose("Failed to save bStats config file: " - + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } - } - // Load the data - boolean enabled = config.getBoolean("enabled", true); - String serverUUID = config.getString("serverUuid"); - boolean logErrors = config.getBoolean("logFailedRequests", false); - boolean logSentData = config.getBoolean("logSentData", false); - boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); - metricsBase = - new MetricsBase( - "bukkit", - serverUUID, - serviceId, - enabled, - this::appendPlatformData, - this::appendServiceData, - submitDataTask -> J.s(submitDataTask), - plugin::isEnabled, - (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), - (message) -> this.plugin.getLogger().log(Level.INFO, message), - logErrors, - logSentData, - logResponseStatusText); + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + startSubmitting(); + } } /** - * Adds a custom chart. + * Gzips the given string. * - * @param chart The chart to add. + * @param str The string to gzip. + * @return The gzipped string. */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + public void addCustomChart(CustomChart chart) { - metricsBase.addCustomChart(chart); - } - - private void appendPlatformData(JsonObjectBuilder builder) { - builder.appendField("playerAmount", getPlayerAmount()); - builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); - builder.appendField("bukkitVersion", Bukkit.getVersion()); - builder.appendField("bukkitName", Bukkit.getName()); - builder.appendField("javaVersion", System.getProperty("java.version")); - builder.appendField("osName", System.getProperty("os.name")); - builder.appendField("osArch", System.getProperty("os.arch")); - builder.appendField("osVersion", System.getProperty("os.version")); - builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); - } - - private void appendServiceData(JsonObjectBuilder builder) { - builder.appendField("pluginVersion", plugin.getDescription().getVersion()); - } - - private int getPlayerAmount() { - try { - // Around MC 1.8 the return type was changed from an array to a collection, - // This fixes java.lang.NoSuchMethodError: - // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - return onlinePlayersMethod.getReturnType().equals(Collection.class) - ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() - : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - // Just use the new method if the reflection failed - return Bukkit.getOnlinePlayers().size(); - } + this.customCharts.add(chart); } - public static class MetricsBase { - - /** - * The version of the Metrics class. - */ - public static final String METRICS_VERSION = "2.2.1"; - - private static final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); - - private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; - - private final String platform; - - private final String serverUuid; - - private final int serviceId; - - private final Consumer appendPlatformDataConsumer; - - private final Consumer appendServiceDataConsumer; - - private final Consumer submitTaskConsumer; - - private final Supplier checkServiceEnabledSupplier; - - private final BiConsumer errorLogger; - - private final Consumer infoLogger; - - private final boolean logErrors; - - private final boolean logSentData; - - private final boolean logResponseStatusText; - - private final Set customCharts = new HashSet<>(); - - private final boolean enabled; - - /** - * Creates a new MetricsBase class instance. - * - * @param platform The platform of the service. - * @param serviceId The id of the service. - * @param serverUuid The server uuid. - * @param enabled Whether or not data sending is enabled. - * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and - * appends all platform-specific data. - * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and - * appends all service-specific data. - * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be - * used to delegate the data collection to a another thread to prevent errors caused by - * concurrency. Can be {@code null}. - * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. - * @param errorLogger A consumer that accepts log message and an error. - * @param infoLogger A consumer that accepts info log messages. - * @param logErrors Whether or not errors should be logged. - * @param logSentData Whether or not the sent data should be logged. - * @param logResponseStatusText Whether or not the response status text should be logged. - */ - public MetricsBase( - String platform, - String serverUuid, - int serviceId, - boolean enabled, - Consumer appendPlatformDataConsumer, - Consumer appendServiceDataConsumer, - Consumer submitTaskConsumer, - Supplier checkServiceEnabledSupplier, - BiConsumer errorLogger, - Consumer infoLogger, - boolean logErrors, - boolean logSentData, - boolean logResponseStatusText) { - this.platform = platform; - this.serverUuid = serverUuid; - this.serviceId = serviceId; - this.enabled = enabled; - this.appendPlatformDataConsumer = appendPlatformDataConsumer; - this.appendServiceDataConsumer = appendServiceDataConsumer; - this.submitTaskConsumer = submitTaskConsumer; - this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; - this.errorLogger = errorLogger; - this.infoLogger = infoLogger; - this.logErrors = logErrors; - this.logSentData = logSentData; - this.logResponseStatusText = logResponseStatusText; - checkRelocation(); - if (enabled) { - startSubmitting(); - } - } + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution + // of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into the initial + // and second delay. + // WARNING: You must not modify and part of this Metrics class, including the submit delay or + // frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } - /** - * Gzips the given string. - * - * @param str The string to gzip. - * @return The gzipped string. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - } - return outputStream.toByteArray(); - } + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } - public void addCustomChart(CustomChart chart) { - this.customCharts.add(chart); - } + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } - private void startSubmitting() { - final Runnable submitTask = - () -> { - if (!enabled || !checkServiceEnabledSupplier.get()) { - // Submitting data or service is disabled - scheduler.shutdown(); - return; - } - if (submitTaskConsumer != null) { - submitTaskConsumer.accept(this::submitData); - } else { - this.submitData(); - } - }; - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution - // of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial - // and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or - // frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! - long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); - long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); - scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); - scheduler.scheduleAtFixedRate( - submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); - } + /** + * Checks that the class was properly relocated. + */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little + // "trick" ... :D + final String defaultPackage = + new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong package + // names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + } - private void submitData() { - final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); - appendPlatformDataConsumer.accept(baseJsonBuilder); - final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); - appendServiceDataConsumer.accept(serviceJsonBuilder); - JsonObjectBuilder.JsonObject[] chartData = - customCharts.stream() - .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) - .filter(Objects::nonNull) - .toArray(JsonObjectBuilder.JsonObject[]::new); - serviceJsonBuilder.appendField("id", serviceId); - serviceJsonBuilder.appendField("customCharts", chartData); - baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); - baseJsonBuilder.appendField("serverUUID", serverUuid); - baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); - JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); - scheduler.execute( - () -> { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logErrors) { - errorLogger.accept("Could not submit bStats metrics data", e); - } - } - }); - } + public static class AdvancedBarChart extends CustomChart { - private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { - if (logSentData) { - infoLogger.accept("Sent bStats metrics data: " + data.toString()); - } - String url = String.format(REPORT_URL, platform); - HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("User-Agent", "Metrics-Service/1"); - connection.setDoOutput(true); - try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { - outputStream.write(compressedData); - } - StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferedReader = - new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - builder.append(line); - } - } - if (logResponseStatusText) { - infoLogger.accept("Sent data to bStats and received response: " + builder); - } - } + private final Callable> callable; - /** - * Checks that the class was properly relocated. - */ - private void checkRelocation() { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null - || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little - // "trick" ... :D - final String defaultPackage = - new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); - final String examplePackage = - new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure no one just copy & pastes the example and uses the wrong package - // names - if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) - || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; } - public static class AdvancedBarChart extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - private final Callable> callable; + public static class SimpleBarChart extends CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + private final Callable> callable; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; } - public static class SimpleBarChart extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - private final Callable> callable; + public static class MultiLineChart extends CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimpleBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + private final Callable> callable; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - for (Map.Entry entry : map.entrySet()) { - valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()}); - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; } - public static class MultiLineChart extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - private final Callable> callable; + public static class AdvancedPie extends CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public MultiLineChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + private final Callable> callable; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; } - public static class AdvancedPie extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - private final Callable> callable; + public abstract static class CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } + private final String chartId; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; } - public abstract static class CustomChart { + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } - private final String chartId; + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } - protected CustomChart(String chartId) { - if (chartId == null) { - throw new IllegalArgumentException("chartId must not be null"); - } - this.chartId = chartId; - } + public static class SingleLineChart extends CustomChart { - public JsonObjectBuilder.JsonObject getRequestJsonObject( - BiConsumer errorLogger, boolean logErrors) { - JsonObjectBuilder builder = new JsonObjectBuilder(); - builder.appendField("chartId", chartId); - try { - JsonObjectBuilder.JsonObject data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - builder.appendField("data", data); - } catch (Throwable t) { - if (logErrors) { - errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return builder.build(); - } + private final Callable callable; - protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; } - public static class SingleLineChart extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } - private final Callable callable; + public static class SimplePie extends CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } + private final Callable callable; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; } - public static class SimplePie extends CustomChart { + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } - private final Callable callable; + public static class DrilldownPie extends CustomChart { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } + private final Callable>> callable; - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; } - public static class DrilldownPie extends CustomChart { + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } - private final Callable>> callable; + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, + * it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } + private StringBuilder builder = new StringBuilder(); - @Override - public JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); } /** - * An extremely simple JSON builder. + * Escapes the given string like stated in + * https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and + * '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not + * as "\n"). * - *

While this class is neither feature-rich nor the most performant one, it's sufficient enough - * for its use-case. + * @param value The value to escape. + * @return The escaped value. */ - public static class JsonObjectBuilder { - - private StringBuilder builder = new StringBuilder(); - - private boolean hasAtLeastOneField = false; - - public JsonObjectBuilder() { - builder.append("{"); - } - - /** - * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. - * - *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. - * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). - * - * @param value The value to escape. - * @return The escaped value. - */ - private static String escape(String value) { - final StringBuilder builder = new StringBuilder(); - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '"') { - builder.append("\\\""); - } else if (c == '\\') { - builder.append("\\\\"); - } else if (c <= '\u000F') { - builder.append("\\u000").append(Integer.toHexString(c)); - } else if (c <= '\u001F') { - builder.append("\\u00").append(Integer.toHexString(c)); - } else { - builder.append(c); - } - } - return builder.toString(); - } + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } - /** - * Appends a null field to the JSON. - * - * @param key The key of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendNull(String key) { - appendFieldUnescaped(key, "null"); - return this; - } + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } - /** - * Appends a string field to the JSON. - * - * @param key The key of the field. - * @param value The value of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, String value) { - if (value == null) { - throw new IllegalArgumentException("JSON value must not be null"); - } - appendFieldUnescaped(key, "\"" + escape(value) + "\""); - return this; - } + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } - /** - * Appends an integer field to the JSON. - * - * @param key The key of the field. - * @param value The value of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, int value) { - appendFieldUnescaped(key, String.valueOf(value)); - return this; - } + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } - /** - * Appends an object to the JSON. - * - * @param key The key of the field. - * @param object The object. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, JsonObject object) { - if (object == null) { - throw new IllegalArgumentException("JSON object must not be null"); - } - appendFieldUnescaped(key, object.toString()); - return this; - } + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } - /** - * Appends a string array to the JSON. - * - * @param key The key of the field. - * @param values The string array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, String[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = - Arrays.stream(values) - .map(value -> "\"" + escape(value) + "\"") - .collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } - /** - * Appends an integer array to the JSON. - * - * @param key The key of the field. - * @param values The integer array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, int[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = - Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } - /** - * Appends an object array to the JSON. - * - * @param key The key of the field. - * @param values The integer array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, JsonObject[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = - Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } - /** - * Appends a field to the object. - * - * @param key The key of the field. - * @param escapedValue The escaped value of the field. - */ - private void appendFieldUnescaped(String key, String escapedValue) { - if (builder == null) { - throw new IllegalStateException("JSON has already been built"); - } - if (key == null) { - throw new IllegalArgumentException("JSON key must not be null"); - } - if (hasAtLeastOneField) { - builder.append(","); - } - builder.append("\"").append(escape(key)).append("\":").append(escapedValue); - hasAtLeastOneField = true; - } + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } - /** - * Builds the JSON string and invalidates this builder. - * - * @return The built JSON string. - */ - public JsonObject build() { - if (builder == null) { - throw new IllegalStateException("JSON has already been built"); - } - JsonObject object = new JsonObject(builder.append("}").toString()); - builder = null; - return object; - } + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } - /** - * A super simple representation of a JSON object. - * - *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not - * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, - * JsonObject)}. - */ - public static class JsonObject { + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the + * {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like + * {@link JsonObjectBuilder#appendField(String, JsonObject)}. + */ + public static class JsonObject { - private final String value; + private final String value; - private JsonObject(String value) { - this.value = value; - } + private JsonObject(String value) { + this.value = value; + } - @Override - public String toString() { - return value; - } - } + @Override + public String toString() { + return value; + } } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java index e9b42fc8c..94bf83e81 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitPlugin.java @@ -43,269 +43,270 @@ import java.util.List; public abstract class VolmitPlugin extends JavaPlugin implements Listener { - public static boolean bad = false; - private int controllerTickerTaskId = -1; - private KMap controllers; - private List cachedControllers; - private KMap, IController> cachedClassControllers; - - public void l(Object l) { - Adapt.info("[" + getName() + "]: " + l); + public static boolean bad = false; + private int controllerTickerTaskId = -1; + private KMap controllers; + private List cachedControllers; + private KMap, IController> cachedClassControllers; + + public void l(Object l) { + Adapt.info("[" + getName() + "]: " + l); + } + + public void w(Object l) { + Adapt.warn("[" + getName() + "]: " + l); + } + + public void f(Object l) { + Adapt.error("[" + getName() + "]: " + l); + } + + public void v(Object l) { + Adapt.verbose("[" + getName() + "]: " + l); + } + + public void onEnable() { + registerInstance(); + registerControllers(); + controllerTickerTaskId = J.sr(this::tickControllers, 1); + J.a(this::outputInfo); + registerListener(this); + start(); + } + + public void unregisterAll() { + try { + stopControllers(); + unregisterListeners(); + unregisterInstance(); + } catch (Exception e) { + Adapt.error("Adapt: Failed to unregister all, You have a plugin that is not unloading properly. This is a bug in that plugin. Please report it to the developer. This is on shutdown however, so it's not a big deal."); + Adapt.error("Adapt: This is not a bug in Adapt. This is a bug in another plugin. Adapt is unloading ALL Command Nodes with Adapt ID's, If another plugin is unloading all or some of these nodes, it will cause this error."); } - - public void w(Object l) { - Adapt.warn("[" + getName() + "]: " + l); + } + + private void outputInfo() { + try { + IO.delete(getDataFolder("info")); + getDataFolder("info").mkdirs(); + outputPluginInfo(); + } catch (Throwable ex) { + Adapt.verbose("Failed to output plugin info: " + ex.getClass().getSimpleName() + + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); } + } - public void f(Object l) { - Adapt.error("[" + getName() + "]: " + l); - } - public void v(Object l) { - Adapt.verbose("[" + getName() + "]: " + l); - } + private void outputPluginInfo() throws IOException { + FileConfiguration fc = new YamlConfiguration(); + fc.set("version", getDescription().getVersion()); + fc.set("name", getDescription().getName()); + fc.save(getDataFile("info", "plugin.yml")); + } - public void onEnable() { - registerInstance(); - registerControllers(); - controllerTickerTaskId = J.sr(this::tickControllers, 1); - J.a(this::outputInfo); - registerListener(this); - start(); - } - public void unregisterAll() { - try { - stopControllers(); - unregisterListeners(); - unregisterInstance(); - } catch (Exception e) { - Adapt.error("Adapt: Failed to unregister all, You have a plugin that is not unloading properly. This is a bug in that plugin. Please report it to the developer. This is on shutdown however, so it's not a big deal."); - Adapt.error("Adapt: This is not a bug in Adapt. This is a bug in another plugin. Adapt is unloading ALL Command Nodes with Adapt ID's, If another plugin is unloading all or some of these nodes, it will cause this error."); - } + @Override + public void onDisable() { + stop(); + if (controllerTickerTaskId != -1) { + J.csr(controllerTickerTaskId); + controllerTickerTaskId = -1; } - - private void outputInfo() { - try { - IO.delete(getDataFolder("info")); - getDataFolder("info").mkdirs(); - outputPluginInfo(); - } catch (Throwable ex) { - Adapt.verbose("Failed to output plugin info: " + ex.getClass().getSimpleName() - + (ex.getMessage() == null ? "" : " - " + ex.getMessage())); - } + J.cancelPluginTasks(); + unregisterListener(this); + unregisterAll(); + } + + private void tickControllers() { + if (bad) { + return; } - - - - private void outputPluginInfo() throws IOException { - FileConfiguration fc = new YamlConfiguration(); - fc.set("version", getDescription().getVersion()); - fc.set("name", getDescription().getName()); - fc.save(getDataFile("info", "plugin.yml")); + for (IController i : getControllers()) { + tickController(i); } + } - - - @Override - public void onDisable() { - stop(); - if (controllerTickerTaskId != -1) { - J.csr(controllerTickerTaskId); - controllerTickerTaskId = -1; - } - J.cancelPluginTasks(); - unregisterListener(this); - unregisterAll(); + private void tickController(IController i) { + if (bad) { + return; } - private void tickControllers() { - if (bad) { - return; - } + if (i.getTickInterval() < 0) { + return; + } - for (IController i : getControllers()) { - tickController(i); - } + M.tick++; + if (M.interval(i.getTickInterval())) { + try { + i.tick(); + } catch (Throwable e) { + w("Failed to tick controller " + i.getName()); + e.printStackTrace(); + } } + } - private void tickController(IController i) { - if (bad) { - return; - } + public List getControllers() { + return cachedControllers; + } - if (i.getTickInterval() < 0) { - return; - } + private void registerControllers() { + if (bad) { + return; + } + controllers = new KMap<>(); + cachedClassControllers = new KMap<>(); - M.tick++; - if (M.interval(i.getTickInterval())) { - try { - i.tick(); - } catch (Throwable e) { - w("Failed to tick controller " + i.getName()); - e.printStackTrace(); - } + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Control.class)) { + try { + i.setAccessible(true); + IController pc = (IController) i.getType().getConstructor().newInstance(); + registerController(pc); + i.set(this, pc); + v("Registered " + pc.getName() + " (" + i.getName() + ")"); + } catch (IllegalArgumentException | IllegalAccessException | + InstantiationException | + InvocationTargetException | NoSuchMethodException | + SecurityException e) { + w("Failed to register controller (field " + i.getName() + ")"); + e.printStackTrace(); } + } } - public List getControllers() { - return cachedControllers; - } + cachedControllers = controllers.v(); + } - private void registerControllers() { - if (bad) { - return; - } - controllers = new KMap<>(); - cachedClassControllers = new KMap<>(); - - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Control.class)) { - try { - i.setAccessible(true); - IController pc = (IController) i.getType().getConstructor().newInstance(); - registerController(pc); - i.set(this, pc); - v("Registered " + pc.getName() + " (" + i.getName() + ")"); - } catch (IllegalArgumentException | IllegalAccessException | InstantiationException | - InvocationTargetException | NoSuchMethodException | SecurityException e) { - w("Failed to register controller (field " + i.getName() + ")"); - e.printStackTrace(); - } - } - } + public IController getController(Class c) { + return cachedClassControllers.get(c); + } - cachedControllers = controllers.v(); + private void registerController(IController pc) { + if (bad) { + return; } - - public IController getController(Class c) { - return cachedClassControllers.get(c); + controllers.put(pc.getName(), pc); + cachedClassControllers.put(pc.getClass(), pc); + registerListener(pc); + + try { + pc.start(); + v("Started " + pc.getName()); + } catch (Throwable e) { + w("Failed to start controller " + pc.getName()); + e.printStackTrace(); } + } - private void registerController(IController pc) { - if (bad) { - return; - } - controllers.put(pc.getName(), pc); - cachedClassControllers.put(pc.getClass(), pc); - registerListener(pc); - + private void registerInstance() { + if (bad) { + return; + } + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Instance.class)) { try { - pc.start(); - v("Started " + pc.getName()); - } catch (Throwable e) { - w("Failed to start controller " + pc.getName()); - e.printStackTrace(); + i.setAccessible(true); + i.set(Modifier.isStatic(i.getModifiers()) ? null : this, this); + v("Registered Instance " + i.getName()); + } catch (IllegalArgumentException | IllegalAccessException | + SecurityException e) { + w("Failed to register instance (field " + i.getName() + ")"); + e.printStackTrace(); } + } } + } - private void registerInstance() { - if (bad) { - return; - } - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Instance.class)) { - try { - i.setAccessible(true); - i.set(Modifier.isStatic(i.getModifiers()) ? null : this, this); - v("Registered Instance " + i.getName()); - } catch (IllegalArgumentException | IllegalAccessException | SecurityException e) { - w("Failed to register instance (field " + i.getName() + ")"); - e.printStackTrace(); - } - } - } + private void unregisterInstance() { + if (bad) { + return; } - - private void unregisterInstance() { - if (bad) { - return; - } - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Instance.class)) { - try { - i.setAccessible(true); - i.set(Modifier.isStatic(i.getModifiers()) ? null : this, null); - v("Unregistered Instance " + i.getName()); - } catch (IllegalArgumentException | IllegalAccessException | SecurityException e) { - w("Failed to unregister instance (field " + i.getName() + ")"); - e.printStackTrace(); - } - } + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Instance.class)) { + try { + i.setAccessible(true); + i.set(Modifier.isStatic(i.getModifiers()) ? null : this, null); + v("Unregistered Instance " + i.getName()); + } catch (IllegalArgumentException | IllegalAccessException | + SecurityException e) { + w("Failed to unregister instance (field " + i.getName() + ")"); + e.printStackTrace(); } + } } + } - public String getTag() { - if (bad) { - return ""; - } - return getTag(""); + public String getTag() { + if (bad) { + return ""; } + return getTag(""); + } - public void registerListener(Listener l) { - if (bad) { - return; - } - if (!SkillEventRegistrar.register(this, l) && !AdaptationEventRegistrar.register(this, l)) { - Bukkit.getPluginManager().registerEvents(l, this); - } - ReflectiveEvents.register(l); + public void registerListener(Listener l) { + if (bad) { + return; + } + if (!SkillEventRegistrar.register(this, l) && !AdaptationEventRegistrar.register(this, l)) { + Bukkit.getPluginManager().registerEvents(l, this); } + ReflectiveEvents.register(l); + } - public void unregisterListener(Listener l) { - if (bad) { - return; - } - HandlerList.unregisterAll(l); + public void unregisterListener(Listener l) { + if (bad) { + return; } + HandlerList.unregisterAll(l); + } - public void unregisterListeners() { - if (bad) { - return; - } - HandlerList.unregisterAll((Listener) this); + public void unregisterListeners() { + if (bad) { + return; } + HandlerList.unregisterAll((Listener) this); + } - private void stopControllers() { - if (bad) { - return; - } - for (IController i : controllers.v()) { - try { - unregisterListener(i); - i.stop(); - v("Stopped " + i.getName()); - } catch (Throwable e) { - w("Failed to stop controller " + i.getName()); - e.printStackTrace(); - } - } + private void stopControllers() { + if (bad) { + return; } - - public File getDataFile(String... strings) { - File f = new File(getDataFolder(), String.join(File.separator, strings)); - f.getParentFile().mkdirs(); - return f; + for (IController i : controllers.v()) { + try { + unregisterListener(i); + i.stop(); + v("Stopped " + i.getName()); + } catch (Throwable e) { + w("Failed to stop controller " + i.getName()); + e.printStackTrace(); + } } + } - public File getDataFolder(String... strings) { - if (strings.length == 0) { - return super.getDataFolder(); - } - - File f = new File(getDataFolder(), String.join(File.separator, strings)); - f.mkdirs(); + public File getDataFile(String... strings) { + File f = new File(getDataFolder(), String.join(File.separator, strings)); + f.getParentFile().mkdirs(); + return f; + } - return f; + public File getDataFolder(String... strings) { + if (strings.length == 0) { + return super.getDataFolder(); } - public abstract void start(); + File f = new File(getDataFolder(), String.join(File.separator, strings)); + f.mkdirs(); + + return f; + } + + public abstract void start(); - public abstract void stop(); + public abstract void stop(); - public abstract String getTag(String subTag); + public abstract String getTag(String subTag); } diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index b492b3ac1..9d0f8d51c 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -60,568 +60,568 @@ * @author cyberpwn */ public class VolmitSender implements CommandSender { - @Getter - private static final KMap helpCache = new KMap<>(); - private final CommandSender s; - public boolean useConsoleCustomColors = true; - public boolean useCustomColorsIngame = true; - public int spinh = -20; - public int spins = 7; - public int spinb = 8; - private String tag; - @Getter - @Setter - private String command; - - /** - * Wrap a command sender - * - * @param s the command sender - */ - public VolmitSender(CommandSender s) { - tag = ""; - this.s = s; - } - - public VolmitSender(CommandSender s, String tag) { - this.tag = tag; - this.s = s; - } - - public static long getTick() { - return art.arcane.volmlib.util.math.M.ms() / 16; - } - - public static String pulse(String colorA, String colorB, double speed) { - return ""; - } - - public static String pulse(double speed) { - return Form.f(invertSpread((((getTick() * 15D * speed) % 1000D) / 1000D)), 3).replaceAll("\\Q,\\E", ".").replaceAll("\\Q?\\E", "-"); - } - - public static double invertSpread(double v) { - return ((1D - v) * 2D) - 1D; - } - - public static List paginate(List all, int linesPerPage, int page, AtomicBoolean hasNext) { - int totalPages = (int) Math.ceil((double) all.size() / linesPerPage); - page = page < 0 ? 0 : page >= totalPages ? totalPages - 1 : page; - hasNext.set(page < totalPages - 1); - List d = new ArrayList<>(); - - for (int i = linesPerPage * page; i < Math.min(all.size(), linesPerPage * (page + 1)); i++) { - d.add(all.get(i)); - } - - return d; - } - - /** - * Get the command tag - * - * @return the command tag - */ - public String getTag() { - return tag; - } - - /** - * Set a command tag (prefix for sendMessage) - * - * @param tag the tag - */ - public void setTag(String tag) { - this.tag = tag; - } - - /** - * Is this sender a player? - * - * @return true if it is - */ - public boolean isPlayer() { - return getS() instanceof Player; - } - - /** - * Is this sender a console? - * - * @return true if it is - */ - public boolean isConsole() { - return getS() instanceof ConsoleCommandSender; - } - - /** - * Force cast to player (be sure to check first) - * - * @return a casted player - */ - public Player player() { - return (Player) getS(); - } - - /** - * Get the origin sender this object is wrapping - * - * @return the command sender - */ - public CommandSender getS() { - return s; - } - - @Override - public boolean isPermissionSet(String name) { - return s.isPermissionSet(name); - } - - @Override - public boolean isPermissionSet(Permission perm) { - return s.isPermissionSet(perm); - } - - @Override - public boolean hasPermission(String name) { - return s.hasPermission(name); - } - - @Override - public boolean hasPermission(Permission perm) { - return s.hasPermission(perm); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { - return s.addAttachment(plugin, name, value); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin) { - return s.addAttachment(plugin); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { - return s.addAttachment(plugin, name, value, ticks); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, int ticks) { - return s.addAttachment(plugin, ticks); - } - - @Override - public void removeAttachment(PermissionAttachment attachment) { - s.removeAttachment(attachment); - } - - @Override - public void recalculatePermissions() { - s.recalculatePermissions(); - } - - @Override - public Set getEffectivePermissions() { - return s.getEffectivePermissions(); - } - - @Override - public boolean isOp() { - return s.isOp(); - } - - @Override - public void setOp(boolean value) { - s.setOp(value); - } - - public void hr() { - s.sendMessage("========================================================"); - } - - public void sendTitle(String title, String subtitle, int i, int s, int o) { - Adapt.audiences.player(player()).showTitle(Title.title( - createComponent(title), - createComponent(subtitle), - Title.Times.times(Duration.ofMillis(i), Duration.ofMillis(s), Duration.ofMillis(o)))); - } - - public void sendProgress(double percent, String thing) { - //noinspection IfStatementWithIdenticalBranches - if (percent < 0) { - int l = 44; - int g = (int) (1D * l); - sendTitle(C.ADAPT + thing + " ", 0, 500, 250); - sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + @Getter + private static final KMap helpCache = new KMap<>(); + private final CommandSender s; + public boolean useConsoleCustomColors = true; + public boolean useCustomColorsIngame = true; + public int spinh = -20; + public int spins = 7; + public int spinb = 8; + private String tag; + @Getter + @Setter + private String command; + + /** + * Wrap a command sender + * + * @param s the command sender + */ + public VolmitSender(CommandSender s) { + tag = ""; + this.s = s; + } + + public VolmitSender(CommandSender s, String tag) { + this.tag = tag; + this.s = s; + } + + public static long getTick() { + return art.arcane.volmlib.util.math.M.ms() / 16; + } + + public static String pulse(String colorA, String colorB, double speed) { + return ""; + } + + public static String pulse(double speed) { + return Form.f(invertSpread((((getTick() * 15D * speed) % 1000D) / 1000D)), 3).replaceAll("\\Q,\\E", ".").replaceAll("\\Q?\\E", "-"); + } + + public static double invertSpread(double v) { + return ((1D - v) * 2D) - 1D; + } + + public static List paginate(List all, int linesPerPage, int page, AtomicBoolean hasNext) { + int totalPages = (int) Math.ceil((double) all.size() / linesPerPage); + page = page < 0 ? 0 : page >= totalPages ? totalPages - 1 : page; + hasNext.set(page < totalPages - 1); + List d = new ArrayList<>(); + + for (int i = linesPerPage * page; i < Math.min(all.size(), linesPerPage * (page + 1)); i++) { + d.add(all.get(i)); + } + + return d; + } + + /** + * Get the command tag + * + * @return the command tag + */ + public String getTag() { + return tag; + } + + /** + * Set a command tag (prefix for sendMessage) + * + * @param tag the tag + */ + public void setTag(String tag) { + this.tag = tag; + } + + /** + * Is this sender a player? + * + * @return true if it is + */ + public boolean isPlayer() { + return getS() instanceof Player; + } + + /** + * Is this sender a console? + * + * @return true if it is + */ + public boolean isConsole() { + return getS() instanceof ConsoleCommandSender; + } + + /** + * Force cast to player (be sure to check first) + * + * @return a casted player + */ + public Player player() { + return (Player) getS(); + } + + /** + * Get the origin sender this object is wrapping + * + * @return the command sender + */ + public CommandSender getS() { + return s; + } + + @Override + public boolean isPermissionSet(String name) { + return s.isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return s.isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return s.hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return s.hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return s.addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return s.addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return s.addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return s.addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + s.removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + s.recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return s.getEffectivePermissions(); + } + + @Override + public boolean isOp() { + return s.isOp(); + } + + @Override + public void setOp(boolean value) { + s.setOp(value); + } + + public void hr() { + s.sendMessage("========================================================"); + } + + public void sendTitle(String title, String subtitle, int i, int s, int o) { + Adapt.audiences.player(player()).showTitle(Title.title( + createComponent(title), + createComponent(subtitle), + Title.Times.times(Duration.ofMillis(i), Duration.ofMillis(s), Duration.ofMillis(o)))); + } + + public void sendProgress(double percent, String thing) { + //noinspection IfStatementWithIdenticalBranches + if (percent < 0) { + int l = 44; + int g = (int) (1D * l); + sendTitle(C.ADAPT + thing + " ", 0, 500, 250); + sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + } else { + int l = 44; + int g = (int) (percent * l); + sendTitle(C.ADAPT + thing + " " + C.BLUE + "" + Form.pc(percent, 0), 0, 500, 250); + sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + } + } + + public void sendAction(String action) { + Adapt.audiences.player(player()).sendActionBar(createNoPrefixComponent(action)); + } + + public void sendActionNoProcessing(String action) { + Adapt.audiences.player(player()).sendActionBar(createNoPrefixComponentNoProcessing(action)); + } + + public void sendTitle(String subtitle, int i, int s, int o) { + Adapt.audiences.player(player()).showTitle(Title.title( + createNoPrefixComponent(" "), + createNoPrefixComponent(subtitle), + Title.Times.times(Duration.ofMillis(i), Duration.ofMillis(s), Duration.ofMillis(o)))); + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean canUseCustomColors(VolmitSender volmitSender) { + return volmitSender.isPlayer() ? useCustomColorsIngame : useConsoleCustomColors; + } + + private Component createNoPrefixComponent(String message) { + if (!canUseCustomColors(this)) { + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(message)); + return AdventureCompat.deserialize(t); + } + + String t = C.translateAlternateColorCodes('&', message); + String a = C.aura(t, spinh, spins, spinb, 0.36); + return AdventureCompat.deserialize(a); + } + + private Component createNoPrefixComponentNoProcessing(String message) { + return AdventureCompat.deserializeNoProcessing(message); + } + + private Component createComponent(String message) { + if (!canUseCustomColors(this)) { + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); + return AdventureCompat.deserialize(t); + } + + String t = C.translateAlternateColorCodes('&', getTag() + message); + String a = C.aura(t, spinh, spins, spinb); + return AdventureCompat.deserialize(a); + } + + private Component createComponentRaw(String message) { + if (!canUseCustomColors(this)) { + String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); + return AdventureCompat.deserialize(t); + } + + String t = C.translateAlternateColorCodes('&', getTag() + message); + return AdventureCompat.deserialize(t); + } + + public void showWaiting(String passive, CompletableFuture f) { + AtomicInteger v = new AtomicInteger(); + v.set(J.sr(() -> { + if (f.isDone()) { + J.csr(v.get()); + sendAction(" "); + return; + } + + sendProgress(-1, passive); + }, 1)); + J.a(() -> { + try { + f.get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + }); + + } + + @Override + public void sendMessage(String message) { + if (s instanceof CommandDummy) { + return; + } + + if ((!useCustomColorsIngame && s instanceof Player) || !useConsoleCustomColors) { + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + return; + } + + if (message.contains("")) { + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message.replaceAll("\\Q\\E", ""))); + return; + } + + try { + Adapt.audiences.sender(s).sendMessage(createComponent(message)); + } catch (Throwable e) { + System.out.println("============================================="); + e.printStackTrace(); + if (e.getCause() != null) { + e.getCause().printStackTrace(); + } + System.out.println("============================================="); + String t = C.translateAlternateColorCodes('&', getTag() + message); + String a = C.aura(t, spinh, spins, spinb); + + Adapt.debug("Failure to parse " + a); + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + } + } + + public void sendMessageBasic(String message) { + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + } + + public void sendMessageRaw(String message) { + if (s instanceof CommandDummy) { + return; + } + + if ((!useCustomColorsIngame && s instanceof Player) || !useConsoleCustomColors) { + s.sendMessage(C.translateAlternateColorCodes('&', message)); + return; + } + + if (message.contains("")) { + s.sendMessage(message.replaceAll("\\Q\\E", "")); + return; + } + + try { + Adapt.audiences.sender(s).sendMessage(createComponentRaw(message)); + } catch (Throwable e) { + String t = C.translateAlternateColorCodes('&', getTag() + message); + String a = C.aura(t, spinh, spins, spinb); + + System.out.println("============================================="); + e.printStackTrace(); + if (e.getCause() != null) { + e.getCause().printStackTrace(); + } + System.out.println("============================================="); + Adapt.debug("Failure to parse " + a); + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + } + } + + @Override + public void sendMessage(String[] messages) { + for (String str : messages) + sendMessage(str); + } + + @Override + public void sendMessage(UUID uuid, String message) { + sendMessage(message); + } + + @Override + public void sendMessage(UUID uuid, String[] messages) { + sendMessage(messages); + } + + @Override + public Server getServer() { + return s.getServer(); + } + + @Override + public String getName() { + return s.getName(); + } + + @Override + public Spigot spigot() { + return s.spigot(); + } + + private String pickRandoms(int max, DirectorVisualCommand i) { + KList m = new KList<>(); + for (int ix = 0; ix < max; ix++) { + m.add((i.isNode() + ? (i.getNode().getParameters().isNotEmpty()) + ? "<#ffd1d1>✦ <#ff6b6b>" + + i.getParentPath() + + " <#ff8a8a>" + + i.getName() + " " + + i.getNode().getParameters().shuffleCopy(RNG.r).kConvert((f) + -> (f.isRequired() || RNG.r.b(0.5) + ? "<#f2e15e>" + f.getNames().getRandom() + "=" + + "<#ff9ea0>" + f.example() + : "")) + .toString(" ") + : "" + : "")); + } + + return m.removeDuplicates().kConvert((iff) -> iff.replaceAll("\\Q \\E", " ")).toString("\n"); + } + + public void sendHeader(String name, int overrideLength) { + int len = overrideLength; + int h = name.length() + 2; + String s = Form.repeat(" ", len - h - 4); + String si = Form.repeat("(", 3); + String so = Form.repeat(")", 3); + String sf = "["; + String se = "]"; + + if (name.trim().isEmpty()) { + sendMessageRaw("" + sf + s + "" + s + se); + } else { + sendMessageRaw("" + sf + s + si + " " + name + " " + so + s + se); + } + } + + public void sendHeader(String name) { + sendHeader(name, 40); + } + + public void sendDirectorHelp(DirectorVisualCommand v) { + sendDirectorHelp(v, 0); + } + + public void sendDirectorHelp(DirectorVisualCommand v, int page) { + if (!isPlayer()) { + for (DirectorVisualCommand i : v.getNodes()) { + sendDirectorHelpNode(i); + } + + return; + } + + sendMessageRaw("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + + if (v.getNodes().isNotEmpty()) { + sendHeader(v.getPath() + (page > 0 ? (" {" + (page + 1) + "}") : "")); + if (isPlayer() && v.getParent() != null) { + sendMessageRaw("Click to go back to <#ff8a8a>" + Form.capitalize(v.getParent().getName()) + " Help" + "'><#ff6b6b>〈 Back"); + } + + AtomicBoolean next = new AtomicBoolean(false); + for (DirectorVisualCommand i : paginate(v.getNodes(), 17, page, next)) { + sendDirectorHelpNode(i); + } + + String s = ""; + int l = 75 - (page > 0 ? 10 : 0) - (next.get() ? 10 : 0); + + if (page > 0) { + s += "Click to go back to page " + page + "'>〈 Page " + page + " "; + } + + s += "" + Form.repeat(" ", l) + ""; + + if (next.get()) { + s += " Click to go to back to page " + (page + 2) + "'>Page " + (page + 2) + " ❭"; + } + + sendMessageRaw(s); + } else { + sendMessage(C.RED + "There are no subcommands in this group! Contact support, this is a command design issue!"); + } + } + + public void sendDirectorHelpNode(DirectorVisualCommand i) { + if (isPlayer() || s instanceof CommandDummy) { + sendMessageRaw(helpCache.computeIfAbsent(i.getPath(), (k) -> { + String newline = "\n"; + + String realText = i.getPath() + " >" + "<#ffe6e6>⇀ " + i.getName(); + String hoverTitle = i.getNames().copy().reverse().kConvert((f) -> "<#ff5c5c>" + f).toString(", "); + String description = "<#ff9ea0>✎ <#ffd1d1>" + i.getDescription(); + String usage = "<#FF0000>✒ <#8b1a1a>"; + + String onClick; + if (i.isNode()) { + if (i.getNode().getParameters().isEmpty()) { + usage += "There are no parameters. Click to type command."; + onClick = "suggest_command"; + } else { + usage += "Hover over all of the parameters to learn more."; + onClick = "suggest_command"; + } } else { - int l = 44; - int g = (int) (percent * l); - sendTitle(C.ADAPT + thing + " " + C.BLUE + "" + Form.pc(percent, 0), 0, 500, 250); - sendActionNoProcessing("" + "" + pulse("#ff5c5c", "#4d0000", 1D) + " " + Form.repeat(" ", g) + "" + Form.repeat(" ", l - g)); + usage += "This is a command category. Click to run."; + onClick = "run_command"; } - } - - public void sendAction(String action) { - Adapt.audiences.player(player()).sendActionBar(createNoPrefixComponent(action)); - } - - public void sendActionNoProcessing(String action) { - Adapt.audiences.player(player()).sendActionBar(createNoPrefixComponentNoProcessing(action)); - } - - public void sendTitle(String subtitle, int i, int s, int o) { - Adapt.audiences.player(player()).showTitle(Title.title( - createNoPrefixComponent(" "), - createNoPrefixComponent(subtitle), - Title.Times.times(Duration.ofMillis(i), Duration.ofMillis(s), Duration.ofMillis(o)))); - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean canUseCustomColors(VolmitSender volmitSender) { - return volmitSender.isPlayer() ? useCustomColorsIngame : useConsoleCustomColors; - } - - private Component createNoPrefixComponent(String message) { - if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(message)); - return AdventureCompat.deserialize(t); - } - - String t = C.translateAlternateColorCodes('&', message); - String a = C.aura(t, spinh, spins, spinb, 0.36); - return AdventureCompat.deserialize(a); - } - - private Component createNoPrefixComponentNoProcessing(String message) { - return AdventureCompat.deserializeNoProcessing(message); - } - - private Component createComponent(String message) { - if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); - return AdventureCompat.deserialize(t); - } - - String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - return AdventureCompat.deserialize(a); - } - private Component createComponentRaw(String message) { - if (!canUseCustomColors(this)) { - String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); - return AdventureCompat.deserialize(t); + String suggestion = ""; + String suggestions = ""; + if (i.isNode() && i.getNode().getParameters().isNotEmpty()) { + suggestion += newline + "<#ffd1d1>✦ <#ff6b6b>" + i.getParentPath() + " <#ff8a8a>" + i.getName() + " " + + i.getNode().getParameters().kConvert((f) -> "<#ff9ea0>" + f.example()).toString(" "); + suggestions += newline + "" + pickRandoms(Math.min(i.getNode().getParameters().size() + 1, 5), i); } - String t = C.translateAlternateColorCodes('&', getTag() + message); - return AdventureCompat.deserialize(t); - } - - public void showWaiting(String passive, CompletableFuture f) { - AtomicInteger v = new AtomicInteger(); - v.set(J.sr(() -> { - if (f.isDone()) { - J.csr(v.get()); - sendAction(" "); - return; - } - - sendProgress(-1, passive); - }, 1)); - J.a(() -> { - try { - f.get(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); - } - }); - - } - - @Override - public void sendMessage(String message) { - if (s instanceof CommandDummy) { - return; - } - - if ((!useCustomColorsIngame && s instanceof Player) || !useConsoleCustomColors) { - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); - return; - } - - if (message.contains("")) { - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message.replaceAll("\\Q\\E", ""))); - return; - } - - try { - Adapt.audiences.sender(s).sendMessage(createComponent(message)); - } catch (Throwable e) { - System.out.println("============================================="); - e.printStackTrace(); - if (e.getCause() != null) { - e.getCause().printStackTrace(); + StringBuilder nodes = new StringBuilder(); + if (i.isNode()) { + for (DirectorVisualParameter p : i.getNode().getParameters()) { + String nTitle = "" + p.getName(); + String nHoverTitle = p.getNames().kConvert((ff) -> "<#ff9900>" + ff).toString(", "); + String nDescription = "<#ff9ea0>✎ <#ffd1d1>" + p.getDescription(); + String nUsage; + String fullTitle; + Adapt.debug("Contextual: " + p.isContextual() + " / player: " + isPlayer()); + if (p.isContextual() && (isPlayer() || s instanceof CommandDummy)) { + fullTitle = "<#ffcc66>[" + nTitle + "<#ffcc66>] "; + nUsage = "<#ff9900>➱ <#ffcc66>The value may be derived from environment context."; + } else if (p.isRequired()) { + fullTitle = "[" + nTitle + "] "; + nUsage = "<#ff6b6b>⚠ <#ffd1d1>This parameter is required."; + } else if (p.hasDefault()) { + fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; + nUsage = "<#ff8a8a>✔ <#ffd1d1>Defaults to \"" + p.getParam().defaultValue() + "\" if undefined."; + } else { + fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; + nUsage = "<#ff6b6b>✔ <#ffd1d1>This parameter is optional."; } - System.out.println("============================================="); - String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - - Adapt.debug("Failure to parse " + a); - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); - } - } - - public void sendMessageBasic(String message) { - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); - } - - public void sendMessageRaw(String message) { - if (s instanceof CommandDummy) { - return; - } - - if ((!useCustomColorsIngame && s instanceof Player) || !useConsoleCustomColors) { - s.sendMessage(C.translateAlternateColorCodes('&', message)); - return; - } - - if (message.contains("")) { - s.sendMessage(message.replaceAll("\\Q\\E", "")); - return; - } - - try { - Adapt.audiences.sender(s).sendMessage(createComponentRaw(message)); - } catch (Throwable e) { - String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - - System.out.println("============================================="); - e.printStackTrace(); - if (e.getCause() != null) { - e.getCause().printStackTrace(); - } - System.out.println("============================================="); - Adapt.debug("Failure to parse " + a); - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); - } - } - - @Override - public void sendMessage(String[] messages) { - for (String str : messages) - sendMessage(str); - } - - @Override - public void sendMessage(UUID uuid, String message) { - sendMessage(message); - } - - @Override - public void sendMessage(UUID uuid, String[] messages) { - sendMessage(messages); - } - - @Override - public Server getServer() { - return s.getServer(); - } - - @Override - public String getName() { - return s.getName(); - } - - @Override - public Spigot spigot() { - return s.spigot(); - } - - private String pickRandoms(int max, DirectorVisualCommand i) { - KList m = new KList<>(); - for (int ix = 0; ix < max; ix++) { - m.add((i.isNode() - ? (i.getNode().getParameters().isNotEmpty()) - ? "<#ffd1d1>✦ <#ff6b6b>" - + i.getParentPath() - + " <#ff8a8a>" - + i.getName() + " " - + i.getNode().getParameters().shuffleCopy(RNG.r).kConvert((f) - -> (f.isRequired() || RNG.r.b(0.5) - ? "<#f2e15e>" + f.getNames().getRandom() + "=" - + "<#ff9ea0>" + f.example() - : "")) - .toString(" ") - : "" - : "")); - } - - return m.removeDuplicates().kConvert((iff) -> iff.replaceAll("\\Q \\E", " ")).toString("\n"); - } - - public void sendHeader(String name, int overrideLength) { - int len = overrideLength; - int h = name.length() + 2; - String s = Form.repeat(" ", len - h - 4); - String si = Form.repeat("(", 3); - String so = Form.repeat(")", 3); - String sf = "["; - String se = "]"; - - if (name.trim().isEmpty()) { - sendMessageRaw("" + sf + s + "" + s + se); - } else { - sendMessageRaw("" + sf + s + si + " " + name + " " + so + s + se); - } - } - - public void sendHeader(String name) { - sendHeader(name, 40); - } - - public void sendDirectorHelp(DirectorVisualCommand v) { - sendDirectorHelp(v, 0); - } - - public void sendDirectorHelp(DirectorVisualCommand v, int page) { - if (!isPlayer()) { - for (DirectorVisualCommand i : v.getNodes()) { - sendDirectorHelpNode(i); - } - - return; - } - - sendMessageRaw("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - - if (v.getNodes().isNotEmpty()) { - sendHeader(v.getPath() + (page > 0 ? (" {" + (page + 1) + "}") : "")); - if (isPlayer() && v.getParent() != null) { - sendMessageRaw("Click to go back to <#ff8a8a>" + Form.capitalize(v.getParent().getName()) + " Help" + "'><#ff6b6b>〈 Back"); - } - - AtomicBoolean next = new AtomicBoolean(false); - for (DirectorVisualCommand i : paginate(v.getNodes(), 17, page, next)) { - sendDirectorHelpNode(i); - } - - String s = ""; - int l = 75 - (page > 0 ? 10 : 0) - (next.get() ? 10 : 0); - - if (page > 0) { - s += "Click to go back to page " + page + "'>〈 Page " + page + " "; - } - - s += "" + Form.repeat(" ", l) + ""; - - if (next.get()) { - s += " Click to go to back to page " + (page + 2) + "'>Page " + (page + 2) + " ❭"; - } - - sendMessageRaw(s); + String type = "<#ff6b6b>✢ <#ff9ea0>This parameter is of type " + p.getType().getSimpleName() + "."; + + nodes + .append("") + .append(fullTitle) + .append(""); + } } else { - sendMessage(C.RED + "There are no subcommands in this group! Contact support, this is a command design issue!"); + nodes = new StringBuilder(" - Category of Commands"); } - } - public void sendDirectorHelpNode(DirectorVisualCommand i) { - if (isPlayer() || s instanceof CommandDummy) { - sendMessageRaw(helpCache.computeIfAbsent(i.getPath(), (k) -> { - String newline = "\n"; - - String realText = i.getPath() + " >" + "<#ffe6e6>⇀ " + i.getName(); - String hoverTitle = i.getNames().copy().reverse().kConvert((f) -> "<#ff5c5c>" + f).toString(", "); - String description = "<#ff9ea0>✎ <#ffd1d1>" + i.getDescription(); - String usage = "<#FF0000>✒ <#8b1a1a>"; - - String onClick; - if (i.isNode()) { - if (i.getNode().getParameters().isEmpty()) { - usage += "There are no parameters. Click to type command."; - onClick = "suggest_command"; - } else { - usage += "Hover over all of the parameters to learn more."; - onClick = "suggest_command"; - } - } else { - usage += "This is a command category. Click to run."; - onClick = "run_command"; - } - - String suggestion = ""; - String suggestions = ""; - if (i.isNode() && i.getNode().getParameters().isNotEmpty()) { - suggestion += newline + "<#ffd1d1>✦ <#ff6b6b>" + i.getParentPath() + " <#ff8a8a>" + i.getName() + " " - + i.getNode().getParameters().kConvert((f) -> "<#ff9ea0>" + f.example()).toString(" "); - suggestions += newline + "" + pickRandoms(Math.min(i.getNode().getParameters().size() + 1, 5), i); - } - - StringBuilder nodes = new StringBuilder(); - if (i.isNode()) { - for (DirectorVisualParameter p : i.getNode().getParameters()) { - String nTitle = "" + p.getName(); - String nHoverTitle = p.getNames().kConvert((ff) -> "<#ff9900>" + ff).toString(", "); - String nDescription = "<#ff9ea0>✎ <#ffd1d1>" + p.getDescription(); - String nUsage; - String fullTitle; - Adapt.debug("Contextual: " + p.isContextual() + " / player: " + isPlayer()); - if (p.isContextual() && (isPlayer() || s instanceof CommandDummy)) { - fullTitle = "<#ffcc66>[" + nTitle + "<#ffcc66>] "; - nUsage = "<#ff9900>➱ <#ffcc66>The value may be derived from environment context."; - } else if (p.isRequired()) { - fullTitle = "[" + nTitle + "] "; - nUsage = "<#ff6b6b>⚠ <#ffd1d1>This parameter is required."; - } else if (p.hasDefault()) { - fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; - nUsage = "<#ff8a8a>✔ <#ffd1d1>Defaults to \"" + p.getParam().defaultValue() + "\" if undefined."; - } else { - fullTitle = "<#F7F7F7>⊰" + nTitle + "<#F7F7F7>⊱"; - nUsage = "<#ff6b6b>✔ <#ffd1d1>This parameter is optional."; - } - String type = "<#ff6b6b>✢ <#ff9ea0>This parameter is of type " + p.getType().getSimpleName() + "."; - - nodes - .append("") - .append(fullTitle) - .append(""); - } - } else { - nodes = new StringBuilder(" - Category of Commands"); - } - - return "" + - "" + - "" + - " " + - nodes; - })); - } else { - sendMessage(i.getPath()); - } - } - - public void playSound(Sound sound, float volume, float pitch) { - if (isPlayer()) { - player().playSound(player().getLocation(), sound, volume, pitch); - } - } + return "" + + "" + + "" + + " " + + nodes; + })); + } else { + sendMessage(i.getPath()); + } + } + + public void playSound(Sound sound, float volume, float pitch) { + if (isPlayer()) { + player().playSound(player().getLocation(), sound, volume, pitch); + } + } } diff --git a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java index daa2e51d7..0df13d297 100644 --- a/src/main/java/art/arcane/adapt/util/common/scheduling/J.java +++ b/src/main/java/art/arcane/adapt/util/common/scheduling/J.java @@ -36,183 +36,183 @@ import java.util.function.Supplier; public class J { - private static final SchedulerRuntime RUNTIME = new SchedulerRuntime( - () -> Adapt.instance, - J::a, - Adapt::verbose, - Adapt::warn, - Throwable::printStackTrace - ); - - static { - SchedulerBridge.setSyncScheduler(J::s); - SchedulerBridge.setDelayedSyncScheduler(J::s); - SchedulerBridge.setAsyncScheduler(J::a); - SchedulerBridge.setDelayedAsyncScheduler(J::a); - SchedulerBridge.setSyncRepeatingScheduler(J::sr); - SchedulerBridge.setAsyncRepeatingScheduler(J::ar); - SchedulerBridge.setCancelScheduler(J::car); - SchedulerBridge.setErrorHandler(Throwable::printStackTrace); - SchedulerBridge.setInfoLogger(Adapt::info); - } - - public static void dofor(int a, Function c, int ch, Consumer d) { - JSupport.dofor(a, c, ch, d); - } - - public static boolean doif(Supplier c, Runnable g) { - return JSupport.doif(c, g, null); - } - - public static void a(Runnable a) { - MultiBurst.burst.lazy(a); - } - - public static Future a(Callable a) { - return MultiBurst.burst.getService().submit(a); - } - - public static void attemptAsync(NastyRunnable r) { - JSupport.attemptAsync(r::run, J::a); - } - - public static R attemptResult(NastyFuture r, R onError) { - return JSupport.attemptResult(r::run, onError, Throwable::printStackTrace); - } - - public static R attemptFunction(NastyFunction r, T param, R onError) { - return JSupport.attemptFunction(r::run, param, onError, e -> Adapt.verbose("Failed to run function: " + e.getMessage())); - } - - public static boolean sleep(long ms) { - return JSupport.sleep(ms); - } - - public static boolean attempt(NastyRunnable r) { - return JSupport.attempt(r::run); - } - - public static Throwable attemptCatch(NastyRunnable r) { - return JSupport.attemptCatch(r::run); - } - - public static T attempt(Supplier t, T i) { - return JSupport.attempt(t::get, i, null); - } - - /** - * Dont call this unless you know what you are doing! - */ - public static void executeAfterStartupQueue() { - RUNTIME.executeAfterStartupQueue(J::s); - } - - public static void ass(Runnable r) { - RUNTIME.enqueueAfterStartupSync(r, J::s); - } - - public static void asa(Runnable r) { - RUNTIME.enqueueAfterStartupAsync(r); - } - - public static boolean isPrimaryThread() { - return FoliaScheduler.isPrimaryThread(); - } - - public static boolean isFoliaThreading() { - return RUNTIME.isFoliaThreading(); - } - - public static boolean isOwnedByCurrentRegion(Entity entity) { - return RUNTIME.isOwnedByCurrentRegion(entity); - } - - public static boolean runEntity(Entity entity, Runnable runnable) { - return RUNTIME.runEntity(entity, runnable); - } - - public static boolean runEntity(Entity entity, Runnable runnable, int delayTicks) { - return RUNTIME.runEntity(entity, runnable, delayTicks); - } - - public static boolean teleport(Entity entity, Location location) { - return teleport(entity, location, null); - } - - public static boolean teleport(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { - return RUNTIME.teleport(entity, location, cause); - } - - public static boolean runAt(Location location, Runnable runnable) { - return RUNTIME.runAt(location, runnable); - } - - public static boolean runAt(Location location, Runnable runnable, int delayTicks) { - return RUNTIME.runAt(location, runnable, delayTicks); - } - - public static void cancelPluginTasks() { - RUNTIME.cancelPluginTasks(); - } - - public static void s(Runnable r) { - RUNTIME.s(r); - } - - public static void s(Runnable r, int delay) { - RUNTIME.s(r, delay); - } - - public static void csr(int id) { - RUNTIME.csr(id); - } - - public static int sr(Runnable r, int interval) { - return RUNTIME.sr(r, interval); - } - - public static void sr(Runnable r, int interval, int intervals) { - FinalInteger fi = new FinalInteger(0); - - new SR(interval) { - @Override - public void run() { - fi.add(1); - r.run(); - - if (fi.get() >= intervals) { - cancel(); - } - } - }; - } - - public static void a(Runnable r, int delay) { - RUNTIME.a(r, delay); - } - - public static void car(int id) { - RUNTIME.car(id); - } - - public static int ar(Runnable r, int interval) { - return RUNTIME.ar(r, interval); - } - - public static void ar(Runnable r, int interval, int intervals) { - FinalInteger fi = new FinalInteger(0); - - new AR(interval) { - @Override - public void run() { - fi.add(1); - r.run(); - - if (fi.get() >= intervals) { - cancel(); - } - } - }; - } + private static final SchedulerRuntime RUNTIME = new SchedulerRuntime( + () -> Adapt.instance, + J::a, + Adapt::verbose, + Adapt::warn, + Throwable::printStackTrace + ); + + static { + SchedulerBridge.setSyncScheduler(J::s); + SchedulerBridge.setDelayedSyncScheduler(J::s); + SchedulerBridge.setAsyncScheduler(J::a); + SchedulerBridge.setDelayedAsyncScheduler(J::a); + SchedulerBridge.setSyncRepeatingScheduler(J::sr); + SchedulerBridge.setAsyncRepeatingScheduler(J::ar); + SchedulerBridge.setCancelScheduler(J::car); + SchedulerBridge.setErrorHandler(Throwable::printStackTrace); + SchedulerBridge.setInfoLogger(Adapt::info); + } + + public static void dofor(int a, Function c, int ch, Consumer d) { + JSupport.dofor(a, c, ch, d); + } + + public static boolean doif(Supplier c, Runnable g) { + return JSupport.doif(c, g, null); + } + + public static void a(Runnable a) { + MultiBurst.burst.lazy(a); + } + + public static Future a(Callable a) { + return MultiBurst.burst.getService().submit(a); + } + + public static void attemptAsync(NastyRunnable r) { + JSupport.attemptAsync(r::run, J::a); + } + + public static R attemptResult(NastyFuture r, R onError) { + return JSupport.attemptResult(r::run, onError, Throwable::printStackTrace); + } + + public static R attemptFunction(NastyFunction r, T param, R onError) { + return JSupport.attemptFunction(r::run, param, onError, e -> Adapt.verbose("Failed to run function: " + e.getMessage())); + } + + public static boolean sleep(long ms) { + return JSupport.sleep(ms); + } + + public static boolean attempt(NastyRunnable r) { + return JSupport.attempt(r::run); + } + + public static Throwable attemptCatch(NastyRunnable r) { + return JSupport.attemptCatch(r::run); + } + + public static T attempt(Supplier t, T i) { + return JSupport.attempt(t::get, i, null); + } + + /** + * Dont call this unless you know what you are doing! + */ + public static void executeAfterStartupQueue() { + RUNTIME.executeAfterStartupQueue(J::s); + } + + public static void ass(Runnable r) { + RUNTIME.enqueueAfterStartupSync(r, J::s); + } + + public static void asa(Runnable r) { + RUNTIME.enqueueAfterStartupAsync(r); + } + + public static boolean isPrimaryThread() { + return FoliaScheduler.isPrimaryThread(); + } + + public static boolean isFoliaThreading() { + return RUNTIME.isFoliaThreading(); + } + + public static boolean isOwnedByCurrentRegion(Entity entity) { + return RUNTIME.isOwnedByCurrentRegion(entity); + } + + public static boolean runEntity(Entity entity, Runnable runnable) { + return RUNTIME.runEntity(entity, runnable); + } + + public static boolean runEntity(Entity entity, Runnable runnable, int delayTicks) { + return RUNTIME.runEntity(entity, runnable, delayTicks); + } + + public static boolean teleport(Entity entity, Location location) { + return teleport(entity, location, null); + } + + public static boolean teleport(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { + return RUNTIME.teleport(entity, location, cause); + } + + public static boolean runAt(Location location, Runnable runnable) { + return RUNTIME.runAt(location, runnable); + } + + public static boolean runAt(Location location, Runnable runnable, int delayTicks) { + return RUNTIME.runAt(location, runnable, delayTicks); + } + + public static void cancelPluginTasks() { + RUNTIME.cancelPluginTasks(); + } + + public static void s(Runnable r) { + RUNTIME.s(r); + } + + public static void s(Runnable r, int delay) { + RUNTIME.s(r, delay); + } + + public static void csr(int id) { + RUNTIME.csr(id); + } + + public static int sr(Runnable r, int interval) { + return RUNTIME.sr(r, interval); + } + + public static void sr(Runnable r, int interval, int intervals) { + FinalInteger fi = new FinalInteger(0); + + new SR(interval) { + @Override + public void run() { + fi.add(1); + r.run(); + + if (fi.get() >= intervals) { + cancel(); + } + } + }; + } + + public static void a(Runnable r, int delay) { + RUNTIME.a(r, delay); + } + + public static void car(int id) { + RUNTIME.car(id); + } + + public static int ar(Runnable r, int interval) { + return RUNTIME.ar(r, interval); + } + + public static void ar(Runnable r, int interval, int intervals) { + FinalInteger fi = new FinalInteger(0); + + new AR(interval) { + @Override + public void run() { + fi.add(1); + r.run(); + + if (fi.get() >= intervals) { + cancel(); + } + } + }; + } } diff --git a/src/main/java/art/arcane/adapt/util/data/Metadata.java b/src/main/java/art/arcane/adapt/util/data/Metadata.java index 14baf11a4..8c7b2e6a6 100644 --- a/src/main/java/art/arcane/adapt/util/data/Metadata.java +++ b/src/main/java/art/arcane/adapt/util/data/Metadata.java @@ -7,35 +7,35 @@ import static java.util.UUID.randomUUID; public class Metadata { - private static final String UUID = randomUUID().toString(); - public static final Metadata VEIN_MINED = new Metadata("vein-mined", false); - - private final String key; - private final boolean defaultValue; - - private Metadata(String prefix, boolean defaultValue) { - this.key = UUID + prefix; - this.defaultValue = defaultValue; - } - - public boolean has(Metadatable metadatable) { - return metadatable.hasMetadata(key); - } - - public boolean get(Metadatable metadatable) { - return metadatable.hasMetadata(key) ? metadatable.getMetadata(key).get(0).asBoolean() : defaultValue; - } - - public void set(Metadatable metadatable, boolean value) { - if (value != defaultValue) metadatable.setMetadata(key, new FixedMetadataValue(Adapt.instance, value)); - else metadatable.removeMetadata(key, Adapt.instance); - } - - public void add(Metadatable metadatable) { - set(metadatable, !defaultValue); - } - - public void remove(Metadatable metadatable) { - set(metadatable, defaultValue); - } + public static final Metadata VEIN_MINED = new Metadata("vein-mined", false); + private static final String UUID = randomUUID().toString(); + private final String key; + private final boolean defaultValue; + + private Metadata(String prefix, boolean defaultValue) { + this.key = UUID + prefix; + this.defaultValue = defaultValue; + } + + public boolean has(Metadatable metadatable) { + return metadatable.hasMetadata(key); + } + + public boolean get(Metadatable metadatable) { + return metadatable.hasMetadata(key) ? metadatable.getMetadata(key).get(0).asBoolean() : defaultValue; + } + + public void set(Metadatable metadatable, boolean value) { + if (value != defaultValue) + metadatable.setMetadata(key, new FixedMetadataValue(Adapt.instance, value)); + else metadatable.removeMetadata(key, Adapt.instance); + } + + public void add(Metadatable metadatable) { + set(metadatable, !defaultValue); + } + + public void remove(Metadatable metadatable) { + set(metadatable, defaultValue); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java b/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java index 1a0fbe1d4..71efbb622 100644 --- a/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java +++ b/src/main/java/art/arcane/adapt/util/director/DirectorSystem.java @@ -23,25 +23,26 @@ import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.DirectorSystemSupport; -public final class DirectorSystem { - public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.director.handlers", null).convert((i) -> (DirectorParameterHandler) i); - private DirectorSystem() { - } +public final class DirectorSystem { + public static final KList> handlers = Adapt.initialize("art.arcane.adapt.util.director.handlers", null).convert((i) -> (DirectorParameterHandler) i); - /** - * Get the handler for the specified type - * - * @param type The type to handle - * @return The corresponding {@link DirectorParameterHandler}, or null - */ - public static DirectorParameterHandler getHandler(Class type) { - DirectorParameterHandler handler = DirectorSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); - if (handler != null) { - return handler; - } + private DirectorSystem() { + } - Adapt.error("Unhandled type in Director Parameter: " + type.getName() + ". This is bad!"); - return null; + /** + * Get the handler for the specified type + * + * @param type The type to handle + * @return The corresponding {@link DirectorParameterHandler}, or null + */ + public static DirectorParameterHandler getHandler(Class type) { + DirectorParameterHandler handler = DirectorSystemSupport.getHandler(handlers, type, (h, t) -> h.supports(t)); + if (handler != null) { + return handler; } + + Adapt.error("Unhandled type in Director Parameter: " + type.getName() + ". This is bad!"); + return null; + } } diff --git a/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java b/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java index b190ca677..409b334b7 100644 --- a/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/context/AdaptationListingHandler.java @@ -8,143 +8,143 @@ public class AdaptationListingHandler { - private static KList adaptationLists; - private static KList adaptationSkillLists; - private static KList adaptationProviders; - private static KList skillProviders; - - - public static void initializeAdaptationListings() { - adaptationLists = new KList<>(); - adaptationSkillLists = new KList<>(); - adaptationProviders = new KList<>(); - skillProviders = new KList<>(); - - getAdaptionListings(); - getAdaptionSkillListings(); - getAdaptationProviders(); - getSkillProvider(); - } - - public static KList getAdaptionListings() { - if (adaptationLists.isNotEmpty()) return adaptationLists; - - AdaptationList main = new AdaptationList("[Main]"); - adaptationLists.add(main); - - for (Skill skill : SkillRegistry.skills.sortV()) { - if (!skill.isEnabled()) { - continue; - } - AdaptationList skillList = new AdaptationList("[Skill]-" + skill.getName()); - adaptationLists.add(skillList); - - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.isEnabled()) { - continue; - } - AdaptationList adaptationList = new AdaptationList("[Adaptation]-" + adaptation.getName()); - adaptationLists.add(adaptationList); - } + private static KList adaptationLists; + private static KList adaptationSkillLists; + private static KList adaptationProviders; + private static KList skillProviders; + + + public static void initializeAdaptationListings() { + adaptationLists = new KList<>(); + adaptationSkillLists = new KList<>(); + adaptationProviders = new KList<>(); + skillProviders = new KList<>(); + + getAdaptionListings(); + getAdaptionSkillListings(); + getAdaptationProviders(); + getSkillProvider(); + } + + public static KList getAdaptionListings() { + if (adaptationLists.isNotEmpty()) return adaptationLists; + + AdaptationList main = new AdaptationList("[Main]"); + adaptationLists.add(main); + + for (Skill skill : SkillRegistry.skills.sortV()) { + if (!skill.isEnabled()) { + continue; + } + AdaptationList skillList = new AdaptationList("[Skill]-" + skill.getName()); + adaptationLists.add(skillList); + + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.isEnabled()) { + continue; } - return adaptationLists; + AdaptationList adaptationList = new AdaptationList("[Adaptation]-" + adaptation.getName()); + adaptationLists.add(adaptationList); + } } + return adaptationLists; + } - public static KList getAdaptionSkillListings() { - if (adaptationSkillLists.isNotEmpty()) return adaptationSkillLists; + public static KList getAdaptionSkillListings() { + if (adaptationSkillLists.isNotEmpty()) return adaptationSkillLists; - AdaptationSkillList t1 = new AdaptationSkillList("[all]"); - adaptationSkillLists.add(t1); - AdaptationSkillList t2 = new AdaptationSkillList("[random]"); - adaptationSkillLists.add(t2); + AdaptationSkillList t1 = new AdaptationSkillList("[all]"); + adaptationSkillLists.add(t1); + AdaptationSkillList t2 = new AdaptationSkillList("[random]"); + adaptationSkillLists.add(t2); - for (Skill skill : allSkills()) { - AdaptationSkillList t3 = new AdaptationSkillList(skill.getName()); - adaptationSkillLists.add(t3); - } - - adaptationSkillLists.removeDuplicates(); - return adaptationSkillLists; + for (Skill skill : allSkills()) { + AdaptationSkillList t3 = new AdaptationSkillList(skill.getName()); + adaptationSkillLists.add(t3); } - private static KList> allSkills() { - if (Adapt.instance != null - && Adapt.instance.getAdaptServer() != null - && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { - return new KList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); - } + adaptationSkillLists.removeDuplicates(); + return adaptationSkillLists; + } - return SkillRegistry.skills.sortV(); + private static KList> allSkills() { + if (Adapt.instance != null + && Adapt.instance.getAdaptServer() != null + && Adapt.instance.getAdaptServer().getSkillRegistry() != null) { + return new KList<>(Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills()); } - public static KList getAdaptationProviders() { - if (adaptationProviders.isNotEmpty()) return adaptationProviders; - - for (Skill skill : SkillRegistry.skills.sortV()) { - if (!skill.isEnabled()) { - continue; - } - for (Adaptation adaptation : skill.getAdaptations()) { - if (!adaptation.isEnabled()) { - continue; - } - AdaptationProvider suggestion = new AdaptationProvider(skill.getName() + ":" +adaptation.getName()); - adaptationProviders.add(suggestion); - } - } - return adaptationProviders; - } + return SkillRegistry.skills.sortV(); + } - public static KList getSkillProvider() { - if (skillProviders.isNotEmpty()) return skillProviders; + public static KList getAdaptationProviders() { + if (adaptationProviders.isNotEmpty()) return adaptationProviders; - for (Skill skill : SkillRegistry.skills.sortV()) { - if (!skill.isEnabled()) { - continue; - } - SkillProvider t1 = new SkillProvider(skill.getName()); - skillProviders.add(t1); + for (Skill skill : SkillRegistry.skills.sortV()) { + if (!skill.isEnabled()) { + continue; + } + for (Adaptation adaptation : skill.getAdaptations()) { + if (!adaptation.isEnabled()) { + continue; } - return skillProviders; + AdaptationProvider suggestion = new AdaptationProvider(skill.getName() + ":" + adaptation.getName()); + adaptationProviders.add(suggestion); + } + } + return adaptationProviders; + } + + public static KList getSkillProvider() { + if (skillProviders.isNotEmpty()) return skillProviders; + + for (Skill skill : SkillRegistry.skills.sortV()) { + if (!skill.isEnabled()) { + continue; + } + SkillProvider t1 = new SkillProvider(skill.getName()); + skillProviders.add(t1); } + return skillProviders; + } - public record AdaptationList(String name) { - public boolean startsWith(String prefix) { - return name.startsWith(prefix); - } + public record AdaptationList(String name) { + public boolean startsWith(String prefix) { + return name.startsWith(prefix); + } - public boolean equals(String prefix) { - return name.equalsIgnoreCase(prefix); - } + public boolean equals(String prefix) { + return name.equalsIgnoreCase(prefix); } + } - public record AdaptationSkillList(String name) { - public boolean startsWith(String prefix) { - return name.startsWith(prefix); - } + public record AdaptationSkillList(String name) { + public boolean startsWith(String prefix) { + return name.startsWith(prefix); + } - public boolean equals(String prefix) { - return name.equalsIgnoreCase(prefix); - } + public boolean equals(String prefix) { + return name.equalsIgnoreCase(prefix); } + } - public record AdaptationProvider(String name) { - public boolean startsWith(String prefix) { - return name.startsWith(prefix); - } + public record AdaptationProvider(String name) { + public boolean startsWith(String prefix) { + return name.startsWith(prefix); + } - public boolean equals(String prefix) { - return name.equalsIgnoreCase(prefix); - } + public boolean equals(String prefix) { + return name.equalsIgnoreCase(prefix); } + } - public record SkillProvider(String name) { - public boolean startsWith(String prefix) { - return name.startsWith(prefix); - } + public record SkillProvider(String name) { + public boolean startsWith(String prefix) { + return name.startsWith(prefix); + } - public boolean equals(String prefix) { - return name.equalsIgnoreCase(prefix); - } + public boolean equals(String prefix) { + return name.equalsIgnoreCase(prefix); } + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java index cd26fb281..4eeca495e 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationListHandler.java @@ -1,28 +1,28 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; public class AdaptationListHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return AdaptationListingHandler.getAdaptionListings(); - } + @Override + public KList getPossibilities() { + return AdaptationListingHandler.getAdaptionListings(); + } - @Override - public String toString(AdaptationListingHandler.AdaptationList adaptationList) { - return adaptationList.name(); - } + @Override + public String toString(AdaptationListingHandler.AdaptationList adaptationList) { + return adaptationList.name(); + } - @Override - public AdaptationListingHandler.AdaptationList parse(String in, boolean force) throws DirectorParsingException { - return new AdaptationListingHandler.AdaptationList(in); - } + @Override + public AdaptationListingHandler.AdaptationList parse(String in, boolean force) throws DirectorParsingException { + return new AdaptationListingHandler.AdaptationList(in); + } - @Override - public boolean supports(Class type) { - return type.equals(AdaptationListingHandler.AdaptationList.class); - } + @Override + public boolean supports(Class type) { + return type.equals(AdaptationListingHandler.AdaptationList.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java index ff1a3a021..417267748 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationProviderHandler.java @@ -1,29 +1,29 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; public class AdaptationProviderHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return AdaptationListingHandler.getAdaptationProviders(); - } + @Override + public KList getPossibilities() { + return AdaptationListingHandler.getAdaptationProviders(); + } - @Override - public String toString(AdaptationListingHandler.AdaptationProvider adaptationProvider) { - return adaptationProvider.name(); - } + @Override + public String toString(AdaptationListingHandler.AdaptationProvider adaptationProvider) { + return adaptationProvider.name(); + } - @Override - public AdaptationListingHandler.AdaptationProvider parse(String in, boolean force) throws DirectorParsingException { - return new AdaptationListingHandler.AdaptationProvider(in); - } + @Override + public AdaptationListingHandler.AdaptationProvider parse(String in, boolean force) throws DirectorParsingException { + return new AdaptationListingHandler.AdaptationProvider(in); + } - @Override - public boolean supports(Class type) { - return type.equals(AdaptationListingHandler.AdaptationProvider.class); - } + @Override + public boolean supports(Class type) { + return type.equals(AdaptationListingHandler.AdaptationProvider.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java index 94217cd52..11014cf34 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/AdaptationSkillListHandler.java @@ -1,28 +1,28 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; public class AdaptationSkillListHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return AdaptationListingHandler.getAdaptionSkillListings(); - } + @Override + public KList getPossibilities() { + return AdaptationListingHandler.getAdaptionSkillListings(); + } - @Override - public String toString(AdaptationListingHandler.AdaptationSkillList adaptationSkillList) { - return adaptationSkillList.name(); - } + @Override + public String toString(AdaptationListingHandler.AdaptationSkillList adaptationSkillList) { + return adaptationSkillList.name(); + } - @Override - public AdaptationListingHandler.AdaptationSkillList parse(String in, boolean force) throws DirectorParsingException { - return new AdaptationListingHandler.AdaptationSkillList(in); - } + @Override + public AdaptationListingHandler.AdaptationSkillList parse(String in, boolean force) throws DirectorParsingException { + return new AdaptationListingHandler.AdaptationSkillList(in); + } - @Override - public boolean supports(Class type) { - return type.equals(AdaptationListingHandler.AdaptationSkillList.class); - } + @Override + public boolean supports(Class type) { + return type.equals(AdaptationListingHandler.AdaptationSkillList.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java index 1947cc58a..ea1a2e501 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/BlockVectorHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.DirectorSystem; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.volmlib.util.director.handlers.base.BlockVectorHandlerBase; import art.arcane.volmlib.util.format.Form; import org.bukkit.FluidCollisionMode; @@ -12,28 +12,28 @@ import java.util.List; public class BlockVectorHandler extends BlockVectorHandlerBase implements DirectorParameterHandler { - @Override - protected boolean isSenderPlayer() { - return BukkitDirectorContext.isPlayer(); - } + @Override + protected boolean isSenderPlayer() { + return BukkitDirectorContext.isPlayer(); + } - @Override - protected BlockVector getSenderBlockVector() { - return BukkitDirectorContext.player().getLocation().toVector().toBlockVector(); - } + @Override + protected BlockVector getSenderBlockVector() { + return BukkitDirectorContext.player().getLocation().toVector().toBlockVector(); + } - @Override - protected BlockVector getLookBlockVector() { - return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); - } + @Override + protected BlockVector getLookBlockVector() { + return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector().toBlockVector(); + } - @Override - protected List playerPossibilities(String query) { - return DirectorSystem.getHandler(Player.class).getPossibilities(query); - } + @Override + protected List playerPossibilities(String query) { + return DirectorSystem.getHandler(Player.class).getPossibilities(query); + } - @Override - protected String format(double value) { - return Form.f(value, 2); - } + @Override + protected String format(double value) { + return Form.f(value, 2); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java index 60e83faef..85c3f5cb2 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/OptionalWorldHandler.java @@ -23,8 +23,8 @@ import art.arcane.volmlib.util.director.handlers.base.OptionalWorldHandlerBase; public class OptionalWorldHandler extends OptionalWorldHandlerBase implements DirectorParameterHandler { - @Override - protected String excludedPrefix() { - return "adapt/"; - } + @Override + protected String excludedPrefix() { + return "adapt/"; + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java index 8d3cde315..1394b80f4 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/ParticleHandler.java @@ -1,32 +1,32 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.bukkit.Particle; public class ParticleHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return new KList<>(Particle.values()); - } + @Override + public KList getPossibilities() { + return new KList<>(Particle.values()); + } - @Override - public String toString(Particle particle) { - return particle.name(); - } + @Override + public String toString(Particle particle) { + return particle.name(); + } - @Override - public Particle parse(String in, boolean force) throws DirectorParsingException { - try { - return Particle.valueOf(in); - } catch (IllegalArgumentException e) { - throw new DirectorParsingException("Invalid particle: " + in); - } + @Override + public Particle parse(String in, boolean force) throws DirectorParsingException { + try { + return Particle.valueOf(in); + } catch (IllegalArgumentException e) { + throw new DirectorParsingException("Invalid particle: " + in); } + } - @Override - public boolean supports(Class type) { - return type.equals(Particle.class); - } + @Override + public boolean supports(Class type) { + return type.equals(Particle.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java index 83075e8ba..cf8f37a24 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/PlayerHandler.java @@ -29,11 +29,11 @@ import java.util.List; public class PlayerHandler extends PlayerHandlerBase implements DirectorParameterHandler { - @Override - protected List playerOptions() { - if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { - return new ArrayList<>(Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()); - } - return new ArrayList<>(Bukkit.getOnlinePlayers()); + @Override + protected List playerOptions() { + if (Adapt.instance != null && Adapt.instance.getAdaptServer() != null) { + return new ArrayList<>(Adapt.instance.getAdaptServer().getOnlinePlayerSnapshot()); } + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java index a10717a50..724a15766 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/SkillProviderHandler.java @@ -1,28 +1,28 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.context.AdaptationListingHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; public class SkillProviderHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return AdaptationListingHandler.getSkillProvider(); - } + @Override + public KList getPossibilities() { + return AdaptationListingHandler.getSkillProvider(); + } - @Override - public String toString(AdaptationListingHandler.SkillProvider skillProvider) { - return skillProvider.name(); - } + @Override + public String toString(AdaptationListingHandler.SkillProvider skillProvider) { + return skillProvider.name(); + } - @Override - public AdaptationListingHandler.SkillProvider parse(String in, boolean force) throws DirectorParsingException { - return new AdaptationListingHandler.SkillProvider(in); - } + @Override + public AdaptationListingHandler.SkillProvider parse(String in, boolean force) throws DirectorParsingException { + return new AdaptationListingHandler.SkillProvider(in); + } - @Override - public boolean supports(Class type) { - return type.equals(AdaptationListingHandler.SkillProvider.class); - } + @Override + public boolean supports(Class type) { + return type.equals(AdaptationListingHandler.SkillProvider.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java index b7177cf79..cc5a47816 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/SoundHandler.java @@ -1,32 +1,32 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.bukkit.Sound; public class SoundHandler implements DirectorParameterHandler { - @Override - public KList getPossibilities() { - return new KList<>(Sound.values()); - } + @Override + public KList getPossibilities() { + return new KList<>(Sound.values()); + } - @Override - public String toString(Sound sound) { - return sound.name(); - } + @Override + public String toString(Sound sound) { + return sound.name(); + } - @Override - public Sound parse(String in, boolean force) throws DirectorParsingException { - try { - return Sound.valueOf(in); - } catch (IllegalArgumentException e) { - throw new DirectorParsingException("Invalid sound: " + in); - } + @Override + public Sound parse(String in, boolean force) throws DirectorParsingException { + try { + return Sound.valueOf(in); + } catch (IllegalArgumentException e) { + throw new DirectorParsingException("Invalid sound: " + in); } + } - @Override - public boolean supports(Class type) { - return type.equals(Sound.class); - } + @Override + public boolean supports(Class type) { + return type.equals(Sound.class); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java index 43cd72dde..acc1859f5 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/VectorHandler.java @@ -1,8 +1,8 @@ package art.arcane.adapt.util.director.handlers; -import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.DirectorSystem; +import art.arcane.volmlib.util.director.DirectorParameterHandler; +import art.arcane.volmlib.util.director.compat.BukkitDirectorContext; import art.arcane.volmlib.util.director.handlers.base.VectorHandlerBase; import art.arcane.volmlib.util.format.Form; import org.bukkit.FluidCollisionMode; @@ -12,28 +12,28 @@ import java.util.List; public class VectorHandler extends VectorHandlerBase implements DirectorParameterHandler { - @Override - protected boolean isSenderPlayer() { - return BukkitDirectorContext.isPlayer(); - } + @Override + protected boolean isSenderPlayer() { + return BukkitDirectorContext.isPlayer(); + } - @Override - protected Vector getSenderVector() { - return BukkitDirectorContext.player().getLocation().toVector(); - } + @Override + protected Vector getSenderVector() { + return BukkitDirectorContext.player().getLocation().toVector(); + } - @Override - protected Vector getLookVector() { - return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); - } + @Override + protected Vector getLookVector() { + return BukkitDirectorContext.player().getTargetBlockExact(256, FluidCollisionMode.NEVER).getLocation().toVector(); + } - @Override - protected List playerPossibilities(String query) { - return DirectorSystem.getHandler(Player.class).getPossibilities(query); - } + @Override + protected List playerPossibilities(String query) { + return DirectorSystem.getHandler(Player.class).getPossibilities(query); + } - @Override - protected String format(double value) { - return Form.f(value, 2); - } + @Override + protected String format(double value) { + return Form.f(value, 2); + } } diff --git a/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java b/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java index 88f6ddb16..777a07cd0 100644 --- a/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/handlers/WorldHandler.java @@ -5,8 +5,8 @@ import org.bukkit.World; public class WorldHandler extends WorldHandlerBase implements DirectorParameterHandler { - @Override - protected String excludedPrefix() { - return "adapt/"; - } + @Override + protected String excludedPrefix() { + return "adapt/"; + } } diff --git a/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java b/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java index d417dd51c..d29a3c667 100644 --- a/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java +++ b/src/main/java/art/arcane/adapt/util/director/specialhandlers/NullablePlayerHandler.java @@ -1,22 +1,22 @@ package art.arcane.adapt.util.director.specialhandlers; -import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.adapt.util.director.handlers.PlayerHandler; +import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.bukkit.entity.Player; public class NullablePlayerHandler extends PlayerHandler implements DirectorParameterHandler { - @Override - public Player parse(String in, boolean force) throws DirectorParsingException { - if (in == null) { - return null; - } - - String value = in.trim(); - if (value.isEmpty() || value.equals("---") || value.equalsIgnoreCase("null")) { - return null; - } + @Override + public Player parse(String in, boolean force) throws DirectorParsingException { + if (in == null) { + return null; + } - return super.parse(value, force); + String value = in.trim(); + if (value.isEmpty() || value.equals("---") || value.equalsIgnoreCase("null")) { + return null; } + + return super.parse(value, force); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/Command.java b/src/main/java/art/arcane/adapt/util/project/command/Command.java index bda71e596..67baa14eb 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/Command.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Command.java @@ -27,5 +27,5 @@ @Retention(RUNTIME) @Target(FIELD) public @interface Command { - String value() default ""; + String value() default ""; } diff --git a/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java index e1839d859..0dd5e7281 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java +++ b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java @@ -32,111 +32,111 @@ import java.util.UUID; public class CommandDummy implements CommandSender { - @Override - public void sendMessage(@NotNull String message) { + @Override + public void sendMessage(@NotNull String message) { - } - - @Override - public void sendMessage(@NotNull String... messages) { + } + + @Override + public void sendMessage(@NotNull String... messages) { - } + } - @Override - public void sendMessage(@Nullable UUID sender, @NotNull String message) { + @Override + public void sendMessage(@Nullable UUID sender, @NotNull String message) { - } + } - @Override - public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { + @Override + public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { - } + } - @NotNull - @Override - public Server getServer() { - return null; - } + @NotNull + @Override + public Server getServer() { + return null; + } - @NotNull - @Override - public String getName() { - return null; - } + @NotNull + @Override + public String getName() { + return null; + } - @NotNull - @Override - public Spigot spigot() { - return null; - } + @NotNull + @Override + public Spigot spigot() { + return null; + } - @Override - public boolean isPermissionSet(@NotNull String name) { - return false; - } + @Override + public boolean isPermissionSet(@NotNull String name) { + return false; + } - @Override - public boolean isPermissionSet(@NotNull Permission perm) { - return false; - } + @Override + public boolean isPermissionSet(@NotNull Permission perm) { + return false; + } - @Override - public boolean hasPermission(@NotNull String name) { - return false; - } + @Override + public boolean hasPermission(@NotNull String name) { + return false; + } - @Override - public boolean hasPermission(@NotNull Permission perm) { - return false; - } + @Override + public boolean hasPermission(@NotNull Permission perm) { + return false; + } - @NotNull - @Override - public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) { - return null; - } + @NotNull + @Override + public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) { + return null; + } - @NotNull - @Override - public PermissionAttachment addAttachment(@NotNull Plugin plugin) { - return null; - } + @NotNull + @Override + public PermissionAttachment addAttachment(@NotNull Plugin plugin) { + return null; + } - @Nullable - @Override - public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) { - return null; - } + @Nullable + @Override + public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) { + return null; + } - @Nullable - @Override - public PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) { - return null; - } + @Nullable + @Override + public PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) { + return null; + } - @Override - public void removeAttachment(@NotNull PermissionAttachment attachment) { + @Override + public void removeAttachment(@NotNull PermissionAttachment attachment) { - } + } - @Override - public void recalculatePermissions() { + @Override + public void recalculatePermissions() { - } + } - @NotNull - @Override - public Set getEffectivePermissions() { - return null; - } + @NotNull + @Override + public Set getEffectivePermissions() { + return null; + } - @Override - public boolean isOp() { - return false; - } + @Override + public boolean isOp() { + return false; + } - @Override - public void setOp(boolean value) { - - } + @Override + public void setOp(boolean value) { + + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/FConst.java b/src/main/java/art/arcane/adapt/util/project/command/FConst.java index 17d10e3ac..f3bf32d71 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/FConst.java +++ b/src/main/java/art/arcane/adapt/util/project/command/FConst.java @@ -8,61 +8,61 @@ import java.awt.*; public class FConst { - public static final Color COLOR_ERROR = new Color(255, 0, 0); - public static final Color COLOR_SUCCESS = new Color(0, 255, 0); - public static final Color COLOR_WARNING = new Color(255, 255, 0); - public static final Color COLOR_INFO = new Color(255, 255, 255); - public static final Feedback TELEPORT = Feedback.builder() - .sound(SoundFeedback.builder() - .sound(Sound.ENTITY_ENDER_EYE_LAUNCH) - .volume(0.7f) - .pitch(1.25f) - .build()) - .build(); + public static final Color COLOR_ERROR = new Color(255, 0, 0); + public static final Color COLOR_SUCCESS = new Color(0, 255, 0); + public static final Color COLOR_WARNING = new Color(255, 255, 0); + public static final Color COLOR_INFO = new Color(255, 255, 255); + public static final Feedback TELEPORT = Feedback.builder() + .sound(SoundFeedback.builder() + .sound(Sound.ENTITY_ENDER_EYE_LAUNCH) + .volume(0.7f) + .pitch(1.25f) + .build()) + .build(); - public static Feedback error(String message, Object... args) { - return Feedback.builder() - .message(errorText(message, args)) - .sound(SoundFeedback.builder().sound(Sound.BLOCK_DEEPSLATE_BREAK).pitch(0.5f).volume(1f).build()) - .build(); - } + public static Feedback error(String message, Object... args) { + return Feedback.builder() + .message(errorText(message, args)) + .sound(SoundFeedback.builder().sound(Sound.BLOCK_DEEPSLATE_BREAK).pitch(0.5f).volume(1f).build()) + .build(); + } - public static Feedback success(String message, Object... args) { - return Feedback.builder() - .message(successText(message, args)) - .sound(SoundFeedback.builder().sound(Sound.BLOCK_AMETHYST_BLOCK_PLACE).pitch(1.5f).volume(1f).build()) - .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_ELYTRA).pitch(1.1f).volume(1f).build()) - .build(); - } + public static Feedback success(String message, Object... args) { + return Feedback.builder() + .message(successText(message, args)) + .sound(SoundFeedback.builder().sound(Sound.BLOCK_AMETHYST_BLOCK_PLACE).pitch(1.5f).volume(1f).build()) + .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_ELYTRA).pitch(1.1f).volume(1f).build()) + .build(); + } - public static Feedback warning(String message, Object... args) { - return Feedback.builder() - .message(warningText(message, args)) - .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_CHAIN).pitch(0.6f).volume(1f).build()) - .build(); - } + public static Feedback warning(String message, Object... args) { + return Feedback.builder() + .message(warningText(message, args)) + .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_CHAIN).pitch(0.6f).volume(1f).build()) + .build(); + } - public static Feedback info(String message, Object... args) { - return Feedback.builder() - .message(infoText(message, args)) - .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_LEATHER).pitch(1.1f).volume(1f).build()) - .build(); - } + public static Feedback info(String message, Object... args) { + return Feedback.builder() + .message(infoText(message, args)) + .sound(SoundFeedback.builder().sound(Sound.ITEM_ARMOR_EQUIP_LEATHER).pitch(1.1f).volume(1f).build()) + .build(); + } - public static TextComponent errorText(String message, Object... args) { - return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_ERROR.getRGB())); - } + public static TextComponent errorText(String message, Object... args) { + return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_ERROR.getRGB())); + } - public static TextComponent successText(String message, Object... args) { - return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_SUCCESS.getRGB())); - } + public static TextComponent successText(String message, Object... args) { + return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_SUCCESS.getRGB())); + } - public static TextComponent warningText(String message, Object... args) { - return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_WARNING.getRGB())); - } + public static TextComponent warningText(String message, Object... args) { + return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_WARNING.getRGB())); + } - public static TextComponent infoText(String message, Object... args) { - return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_INFO.getRGB())); - } + public static TextComponent infoText(String message, Object... args) { + return Component.text(message.formatted(args)).color(TextColor.color(FConst.COLOR_INFO.getRGB())); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/FService.java b/src/main/java/art/arcane/adapt/util/project/command/FService.java index d8278abe4..6d91ec17a 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/FService.java +++ b/src/main/java/art/arcane/adapt/util/project/command/FService.java @@ -1,7 +1,7 @@ package art.arcane.adapt.util.command; public interface FService { - void start(); + void start(); - void stop(); + void stop(); } diff --git a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java index 8dc446705..487e7c5a6 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java @@ -18,30 +18,30 @@ @Data @Accessors(chain = true, fluent = true) public class Feedback { - @Singular - private List sounds; - @Singular - private List messages; + @Singular + private List sounds; + @Singular + private List messages; - public void send(CommandSender serverOrPlayer) { - if (serverOrPlayer instanceof Player p) { - for (SoundFeedback i : sounds) { - i.play(p); - } - } - - Component prefix = Component.text("[").color(NamedTextColor.GRAY) - .append(Component.text("Adapt").color(NamedTextColor.DARK_RED)) - .append(Component.text("] ")); - for (TextComponent i : messages) { - Adapt.audiences.sender(serverOrPlayer).sendMessage(Component.text() - .append(prefix) - .append(i) - .build()); - } + public void send(CommandSender serverOrPlayer) { + if (serverOrPlayer instanceof Player p) { + for (SoundFeedback i : sounds) { + i.play(p); + } } - public void send(VolmitSender sender) { - send(sender.getS()); + Component prefix = Component.text("[").color(NamedTextColor.GRAY) + .append(Component.text("Adapt").color(NamedTextColor.DARK_RED)) + .append(Component.text("] ")); + for (TextComponent i : messages) { + Adapt.audiences.sender(serverOrPlayer).sendMessage(Component.text() + .append(prefix) + .append(i) + .build()); } + } + + public void send(VolmitSender sender) { + send(sender.getS()); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/ICommand.java b/src/main/java/art/arcane/adapt/util/project/command/ICommand.java index 8307e88f7..27bdf6d58 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/ICommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/ICommand.java @@ -26,45 +26,45 @@ * @author cyberpwn */ public interface ICommand { - List getRequiredPermissions(); + List getRequiredPermissions(); - /** - * Get the name of this command (node) - * - * @return the node - */ - String getNode(); + /** + * Get the name of this command (node) + * + * @return the node + */ + String getNode(); - /** - * Get all (realized) nodes of this command - * - * @return the nodes - */ - List getNodes(); + /** + * Get all (realized) nodes of this command + * + * @return the nodes + */ + List getNodes(); - /** - * Get all (every) node in this command - * - * @return all nodes - */ - List getAllNodes(); + /** + * Get all (every) node in this command + * + * @return all nodes + */ + List getAllNodes(); - /** - * Add a node to this command - * - * @param node the node - */ - void addNode(String node); + /** + * Add a node to this command + * + * @param node the node + */ + void addNode(String node); - /** - * Handle a command. If this is a subcommand, parameters after the subcommand - * will be adapted in args for you - * - * @param sender the volume sender (pre-tagged) - * @param args the arguments after this command node - * @return return true to mark it as handled - */ - boolean handle(MortarSender sender, String[] args); + /** + * Handle a command. If this is a subcommand, parameters after the subcommand + * will be adapted in args for you + * + * @param sender the volume sender (pre-tagged) + * @param args the arguments after this command node + * @return return true to mark it as handled + */ + boolean handle(MortarSender sender, String[] args); - List handleTab(MortarSender sender, String[] args); + List handleTab(MortarSender sender, String[] args); } diff --git a/src/main/java/art/arcane/adapt/util/project/command/IController.java b/src/main/java/art/arcane/adapt/util/project/command/IController.java index 00b05239c..4ffc74fd3 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/IController.java +++ b/src/main/java/art/arcane/adapt/util/project/command/IController.java @@ -21,21 +21,21 @@ import org.bukkit.event.Listener; public interface IController extends Listener { - String getName(); + String getName(); - void start(); + void start(); - void stop(); + void stop(); - void tick(); + void tick(); - int getTickInterval(); + int getTickInterval(); - void l(Object l); + void l(Object l); - void w(Object l); + void w(Object l); - void f(Object l); + void f(Object l); - void v(Object l); + void v(Object l); } diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java index 5b3381aa5..32ab35a37 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarCommand.java @@ -34,187 +34,189 @@ * @author cyberpwn */ public abstract class MortarCommand implements ICommand { - private final KList children; - private final KList nodes; - private final KList requiredPermissions; - private final String node; - private String category; - private String description; - - /** - * Override this with a super constructor as most commands shouldn't change - * these parameters - * - * @param node the node (primary node) i.e. volume - * @param nodes the aliases. i.e. v, vol, bile - */ - public MortarCommand(String node, String... nodes) { - category = ""; - this.node = node; - this.nodes = new KList<>(); - this.nodes.add(nodes); - requiredPermissions = new KList<>(); - children = buildChildren(); - description = "No Description"; - } - - @Override - public List handleTab(MortarSender sender, String[] args) { - List v = new ArrayList<>(); - if (args.length == 0) { - for (MortarCommand i : getChildren()) { - v.add(i.getNode()); - } - } - - addTabOptions(sender, args, v); - - if (v.isEmpty()) { - return null; + private final KList children; + private final KList nodes; + private final KList requiredPermissions; + private final String node; + private String category; + private String description; + + /** + * Override this with a super constructor as most commands shouldn't change + * these parameters + * + * @param node the node (primary node) i.e. volume + * @param nodes the aliases. i.e. v, vol, bile + */ + public MortarCommand(String node, String... nodes) { + category = ""; + this.node = node; + this.nodes = new KList<>(); + this.nodes.add(nodes); + requiredPermissions = new KList<>(); + children = buildChildren(); + description = "No Description"; + } + + @Override + public List handleTab(MortarSender sender, String[] args) { + List v = new ArrayList<>(); + if (args.length == 0) { + for (MortarCommand i : getChildren()) { + v.add(i.getNode()); + } + } + + addTabOptions(sender, args, v); + + if (v.isEmpty()) { + return null; + } + + if (sender.isPlayer()) { + SoundPlayer spw = SoundPlayer.of(sender.player().getWorld()); + spw.play(sender.player().getLocation(), Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.25f, 1.7f); + } + + return v; + } + + public abstract void addTabOptions(MortarSender sender, String[] args, List list); + + public void printHelp(MortarSender sender) { + boolean b = false; + + for (MortarCommand i : getChildren()) { + for (String j : i.getRequiredPermissions()) { + if (!sender.hasPermission(j)) { + continue; } + } - if (sender.isPlayer()) { - SoundPlayer spw = SoundPlayer.of(sender.player().getWorld()); - spw.play(sender.player().getLocation(), Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.25f, 1.7f); - } + b = true; - return v; + sender.sendMessage(C.GREEN + i.getNode() + " " + C.WHITE + i.getArgsUsage() + C.GRAY + " - " + i.getDescription()); } - public abstract void addTabOptions(MortarSender sender, String[] args, List list); - - public void printHelp(MortarSender sender) { - boolean b = false; - - for (MortarCommand i : getChildren()) { - for (String j : i.getRequiredPermissions()) { - if (!sender.hasPermission(j)) { - continue; - } - } - - b = true; - - sender.sendMessage(C.GREEN + i.getNode() + " " + C.WHITE + i.getArgsUsage() + C.GRAY + " - " + i.getDescription()); - } - - if (!b) { - sender.sendMessage("There are either no sub-commands or you do not have permission to use them."); - } - - if (sender.isPlayer()) { - SoundPlayer spw = SoundPlayer.of(sender.player().getWorld()); - spw.play(sender.player().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.28f, 1.4f); - spw.play(sender.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.7f); - } + if (!b) { + sender.sendMessage("There are either no sub-commands or you do not have permission to use them."); } - protected abstract String getArgsUsage(); - - public String getDescription() { - return description; + if (sender.isPlayer()) { + SoundPlayer spw = SoundPlayer.of(sender.player().getWorld()); + spw.play(sender.player().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.28f, 1.4f); + spw.play(sender.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.7f); } + } - protected void setDescription(String description) { - this.description = description; - } + protected abstract String getArgsUsage(); - protected void requiresPermission(MortarPermission node) { - if (node == null) { - return; - } + public String getDescription() { + return description; + } - requiresPermission(node.toString()); - } + protected void setDescription(String description) { + this.description = description; + } - protected void requiresPermission(String node) { - if (node == null) { - return; - } - - requiredPermissions.add(node); + protected void requiresPermission(MortarPermission node) { + if (node == null) { + return; } - public void rejectAny(int past, MortarSender sender, String[] a) { - if (a.length > past) { - int p = past; - - String m = ""; - - for (String i : a) { - p--; - if (p < 0) { - m += i + ", "; - } - } - - if (!m.trim().isEmpty()) { - sender.sendMessage("Parameters Ignored: " + m); - } - } - } - - @Override - public String getNode() { - return node; - } + requiresPermission(node.toString()); + } - @Override - public KList getNodes() { - return nodes; + protected void requiresPermission(String node) { + if (node == null) { + return; } - @Override - public KList getAllNodes() { - return getNodes().copy().qadd(getNode()); - } + requiredPermissions.add(node); + } - @Override - public void addNode(String node) { - getNodes().add(node); - } + public void rejectAny(int past, MortarSender sender, String[] a) { + if (a.length > past) { + int p = past; - public List getChildren() { - return children; - } + String m = ""; - private KList buildChildren() { - KList p = new KList<>(); - - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Command.class)) { - try { - i.setAccessible(true); - MortarCommand pc = (MortarCommand) i.getType().getConstructor().newInstance(); - Command c = i.getAnnotation(Command.class); - - if (!c.value().trim().isEmpty()) { - pc.setCategory(c.value().trim()); - } else { - pc.setCategory(getCategory()); - } - - p.add(pc); - } catch (IllegalArgumentException | IllegalAccessException | InstantiationException | - InvocationTargetException | NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - } + for (String i : a) { + p--; + if (p < 0) { + m += i + ", "; } - - return p; + } + + if (!m.trim().isEmpty()) { + sender.sendMessage("Parameters Ignored: " + m); + } + } + } + + @Override + public String getNode() { + return node; + } + + @Override + public KList getNodes() { + return nodes; + } + + @Override + public KList getAllNodes() { + return getNodes().copy().qadd(getNode()); + } + + @Override + public void addNode(String node) { + getNodes().add(node); + } + + public List getChildren() { + return children; + } + + private KList buildChildren() { + KList p = new KList<>(); + + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Command.class)) { + try { + i.setAccessible(true); + MortarCommand pc = (MortarCommand) i.getType().getConstructor().newInstance(); + Command c = i.getAnnotation(Command.class); + + if (!c.value().trim().isEmpty()) { + pc.setCategory(c.value().trim()); + } else { + pc.setCategory(getCategory()); + } + + p.add(pc); + } catch (IllegalArgumentException | IllegalAccessException | + InstantiationException | + InvocationTargetException | NoSuchMethodException | + SecurityException e) { + e.printStackTrace(); + } + } } - @Override - public List getRequiredPermissions() { - return requiredPermissions; - } + return p; + } - public String getCategory() { - return category; - } + @Override + public List getRequiredPermissions() { + return requiredPermissions; + } - public void setCategory(String category) { - this.category = category; - } + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java index b582e4267..791f796fb 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarPermission.java @@ -28,71 +28,74 @@ import java.util.List; public abstract class MortarPermission { - private MortarPermission parent; - - public MortarPermission() { - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Permission.class)) { - try { - MortarPermission px = (MortarPermission) i.getType().getConstructor().newInstance(); - px.setParent(this); - i.set(Modifier.isStatic(i.getModifiers()) ? null : this, px); - } catch (IllegalArgumentException | IllegalAccessException | InstantiationException | - InvocationTargetException | NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - } + private MortarPermission parent; + + public MortarPermission() { + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Permission.class)) { + try { + MortarPermission px = (MortarPermission) i.getType().getConstructor().newInstance(); + px.setParent(this); + i.set(Modifier.isStatic(i.getModifiers()) ? null : this, px); + } catch (IllegalArgumentException | IllegalAccessException | + InstantiationException | + InvocationTargetException | NoSuchMethodException | + SecurityException e) { + e.printStackTrace(); } + } } - - public List getChildren() { - List p = new ArrayList<>(); - - for (Field i : getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Permission.class)) { - try { - p.add((MortarPermission) i.get(Modifier.isStatic(i.getModifiers()) ? null : this)); - } catch (IllegalArgumentException | IllegalAccessException | SecurityException e) { - e.printStackTrace(); - } - } + } + + public List getChildren() { + List p = new ArrayList<>(); + + for (Field i : getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Permission.class)) { + try { + p.add((MortarPermission) i.get(Modifier.isStatic(i.getModifiers()) ? null : this)); + } catch (IllegalArgumentException | IllegalAccessException | + SecurityException e) { + e.printStackTrace(); } - - return p; + } } - public String getFullNode() { - if (hasParent()) { - return getParent().getFullNode() + "." + getNode(); - } + return p; + } - return getNode(); + public String getFullNode() { + if (hasParent()) { + return getParent().getFullNode() + "." + getNode(); } - protected abstract String getNode(); + return getNode(); + } - public abstract String getDescription(); + protected abstract String getNode(); - public abstract boolean isDefault(); + public abstract String getDescription(); - @Override - public String toString() { - return getFullNode(); - } + public abstract boolean isDefault(); - public boolean hasParent() { - return getParent() != null; - } + @Override + public String toString() { + return getFullNode(); + } - public MortarPermission getParent() { - return parent; - } + public boolean hasParent() { + return getParent() != null; + } - public void setParent(MortarPermission parent) { - this.parent = parent; - } + public MortarPermission getParent() { + return parent; + } - public boolean has(CommandSender sender) { - return sender.hasPermission(getFullNode()); - } + public void setParent(MortarPermission parent) { + this.parent = parent; + } + + public boolean has(CommandSender sender) { + return sender.hasPermission(getFullNode()); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java index c8b88f9fa..a819224dd 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java @@ -40,175 +40,175 @@ * @author cyberpwn */ public class MortarSender implements CommandSender { - private final CommandSender s; - private String tag; - - @Getter - @Setter - private String command; - - /** - * Wrap a command sender - * - * @param s the command sender - */ - public MortarSender(CommandSender s) { - tag = ""; - this.s = s; - } - - public MortarSender(CommandSender s, String tag) { - this.tag = tag; - this.s = s; - } - - /** - * Get the command tag - * - * @return the command tag - */ - public String getTag() { - return tag; - } - - /** - * Set a command tag (prefix for sendMessage) - * - * @param tag the tag - */ - public void setTag(String tag) { - this.tag = tag; - } - - /** - * Is this sender a player? - * - * @return true if it is - */ - public boolean isPlayer() { - return getS() instanceof Player; - } - - /** - * Force cast to player (be sure to check first) - * - * @return a casted player - */ - public Player player() { - return (Player) getS(); - } - - /** - * Get the origin sender this object is wrapping - * - * @return the command sender - */ - public CommandSender getS() { - return s; - } - - @Override - public boolean isPermissionSet(String name) { - return s.isPermissionSet(name); - } - - @Override - public boolean isPermissionSet(Permission perm) { - return s.isPermissionSet(perm); - } - - @Override - public boolean hasPermission(String name) { - return s.hasPermission(name); - } - - @Override - public boolean hasPermission(Permission perm) { - return s.hasPermission(perm); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { - return s.addAttachment(plugin, name, value); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin) { - return s.addAttachment(plugin); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { - return s.addAttachment(plugin, name, value, ticks); - } - - @Override - public PermissionAttachment addAttachment(Plugin plugin, int ticks) { - return s.addAttachment(plugin, ticks); - } - - @Override - public void removeAttachment(PermissionAttachment attachment) { - s.removeAttachment(attachment); - } - - @Override - public void recalculatePermissions() { - s.recalculatePermissions(); - } - - @Override - public Set getEffectivePermissions() { - return s.getEffectivePermissions(); - } - - @Override - public boolean isOp() { - return s.isOp(); - } - - @Override - public void setOp(boolean value) { - s.setOp(value); - } - - public void hr() { - s.sendMessage("========================================================"); - } - - @Override - public void sendMessage(String message) { - s.sendMessage(C.translateAlternateColorCodes('&', getTag()) + message); - } - - @Override - public void sendMessage(String[] messages) { - for (String str : messages) - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + str)); - } - - @Override - public void sendMessage(@Nullable UUID sender, @NotNull String message) { - s.sendMessage(sender, message); - } - - @Override - public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { - s.sendMessage(sender, messages); - } - - @Override - public Server getServer() { - return s.getServer(); - } - - @Override - public String getName() { - return s.getName(); - } - - @Override - public Spigot spigot() { - return s.spigot(); - } + private final CommandSender s; + private String tag; + + @Getter + @Setter + private String command; + + /** + * Wrap a command sender + * + * @param s the command sender + */ + public MortarSender(CommandSender s) { + tag = ""; + this.s = s; + } + + public MortarSender(CommandSender s, String tag) { + this.tag = tag; + this.s = s; + } + + /** + * Get the command tag + * + * @return the command tag + */ + public String getTag() { + return tag; + } + + /** + * Set a command tag (prefix for sendMessage) + * + * @param tag the tag + */ + public void setTag(String tag) { + this.tag = tag; + } + + /** + * Is this sender a player? + * + * @return true if it is + */ + public boolean isPlayer() { + return getS() instanceof Player; + } + + /** + * Force cast to player (be sure to check first) + * + * @return a casted player + */ + public Player player() { + return (Player) getS(); + } + + /** + * Get the origin sender this object is wrapping + * + * @return the command sender + */ + public CommandSender getS() { + return s; + } + + @Override + public boolean isPermissionSet(String name) { + return s.isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return s.isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return s.hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return s.hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return s.addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return s.addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return s.addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return s.addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + s.removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + s.recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return s.getEffectivePermissions(); + } + + @Override + public boolean isOp() { + return s.isOp(); + } + + @Override + public void setOp(boolean value) { + s.setOp(value); + } + + public void hr() { + s.sendMessage("========================================================"); + } + + @Override + public void sendMessage(String message) { + s.sendMessage(C.translateAlternateColorCodes('&', getTag()) + message); + } + + @Override + public void sendMessage(String[] messages) { + for (String str : messages) + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + str)); + } + + @Override + public void sendMessage(@Nullable UUID sender, @NotNull String message) { + s.sendMessage(sender, message); + } + + @Override + public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { + s.sendMessage(sender, messages); + } + + @Override + public Server getServer() { + return s.getServer(); + } + + @Override + public String getName() { + return s.getName(); + } + + @Override + public Spigot spigot() { + return s.spigot(); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java b/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java index f74788058..38a78a542 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/RouterCommand.java @@ -28,35 +28,35 @@ * @author cyberpwn */ public class RouterCommand extends org.bukkit.command.Command { - private final CommandExecutor ex; - private String usage; - - /** - * The router command routes commands to bukkit executors - * - * @param realCommand the real command - * @param ex the executor - */ - public RouterCommand(ICommand realCommand, CommandExecutor ex) { - super(realCommand.getNode().toLowerCase()); - setAliases(realCommand.getNodes()); - - this.ex = ex; - } - - @Override - public Command setUsage(String u) { - this.usage = u; - return this; - } - - @Override - public String getUsage() { - return usage; - } - - @Override - public boolean execute(CommandSender sender, String commandLabel, String[] args) { - return ex.onCommand(sender, this, commandLabel, args); - } + private final CommandExecutor ex; + private String usage; + + /** + * The router command routes commands to bukkit executors + * + * @param realCommand the real command + * @param ex the executor + */ + public RouterCommand(ICommand realCommand, CommandExecutor ex) { + super(realCommand.getNode().toLowerCase()); + setAliases(realCommand.getNodes()); + + this.ex = ex; + } + + @Override + public Command setUsage(String u) { + this.usage = u; + return this; + } + + @Override + public String getUsage() { + return usage; + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + return ex.onCommand(sender, this, commandLabel, args); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java b/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java index 859dc62dc..86418762e 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/SoundFeedback.java @@ -10,13 +10,13 @@ @Data @Accessors(chain = true, fluent = true) public class SoundFeedback { - private Sound sound; - @Builder.Default - private float volume = 1f; - @Builder.Default - private float pitch = 1f; + private Sound sound; + @Builder.Default + private float volume = 1f; + @Builder.Default + private float pitch = 1f; - public void play(Player p) { - p.playSound(p.getLocation(), sound, volume, pitch); - } + public void play(Player p) { + p.playSound(p.getLocation(), sound, volume, pitch); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java index 8a5962982..2134646ee 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java @@ -39,148 +39,148 @@ * @author cyberpwn */ public class VirtualCommand { - private final ICommand command; - private final String tag; - - private final KMap, VirtualCommand> children; - - public VirtualCommand(ICommand command) { - this(command, ""); - } - - public VirtualCommand(ICommand command, String tag) { - this.command = command; - children = new KMap<>(); - this.tag = tag; - - for (Field i : command.getClass().getDeclaredFields()) { - if (i.isAnnotationPresent(Command.class)) { - try { - Command cc = i.getAnnotation(Command.class); - ICommand cmd = (ICommand) i.getType().getConstructor().newInstance(); - new V(command, true, true).set(i.getName(), cmd); - children.put(cmd.getAllNodes(), new VirtualCommand(cmd, cc.value().trim().isEmpty() ? tag : cc.value().trim())); - } catch (Exception e) { - e.printStackTrace(); - } - } + private final ICommand command; + private final String tag; + + private final KMap, VirtualCommand> children; + + public VirtualCommand(ICommand command) { + this(command, ""); + } + + public VirtualCommand(ICommand command, String tag) { + this.command = command; + children = new KMap<>(); + this.tag = tag; + + for (Field i : command.getClass().getDeclaredFields()) { + if (i.isAnnotationPresent(Command.class)) { + try { + Command cc = i.getAnnotation(Command.class); + ICommand cmd = (ICommand) i.getType().getConstructor().newInstance(); + new V(command, true, true).set(i.getName(), cmd); + children.put(cmd.getAllNodes(), new VirtualCommand(cmd, cc.value().trim().isEmpty() ? tag : cc.value().trim())); + } catch (Exception e) { + e.printStackTrace(); } + } } + } - public String getTag() { - return tag; - } + public String getTag() { + return tag; + } - public ICommand getCommand() { - return command; - } + public ICommand getCommand() { + return command; + } - public Map, VirtualCommand> getChildren() { - return children; - } + public Map, VirtualCommand> getChildren() { + return children; + } - public boolean hit(CommandSender sender, KList chain) { - return hit(sender, chain, null); - } + public boolean hit(CommandSender sender, KList chain) { + return hit(sender, chain, null); + } - public boolean hit(CommandSender sender, KList chain, String label) { - MortarSender vs = new MortarSender(sender); - vs.setTag(tag); + public boolean hit(CommandSender sender, KList chain, String label) { + MortarSender vs = new MortarSender(sender); + vs.setTag(tag); - if (label != null) { - vs.setCommand(label); - } + if (label != null) { + vs.setCommand(label); + } - if (chain.isEmpty()) { - if (!checkPermissions(sender, command)) { - return true; - } + if (chain.isEmpty()) { + if (!checkPermissions(sender, command)) { + return true; + } - return command.handle(vs, new String[0]); - } + return command.handle(vs, new String[0]); + } - String nl = chain.get(0); - - for (List i : children.k()) { - for (String j : i) { - if (j.equalsIgnoreCase(nl)) { - vs.setCommand(chain.get(0)); - VirtualCommand cmd = children.get(i); - KList c = chain.copy(); - c.remove(0); - if (cmd.hit(sender, c, vs.getCommand())) { - if (vs.isPlayer()) { - SoundPlayer spw = SoundPlayer.of(vs.player().getWorld()); - spw.play(vs.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.8f); - } - - return true; - } - } + String nl = chain.get(0); + + for (List i : children.k()) { + for (String j : i) { + if (j.equalsIgnoreCase(nl)) { + vs.setCommand(chain.get(0)); + VirtualCommand cmd = children.get(i); + KList c = chain.copy(); + c.remove(0); + if (cmd.hit(sender, c, vs.getCommand())) { + if (vs.isPlayer()) { + SoundPlayer spw = SoundPlayer.of(vs.player().getWorld()); + spw.play(vs.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.8f); } - } - if (!checkPermissions(sender, command)) { return true; + } } + } + } - return command.handle(vs, chain.toArray(new String[chain.size()])); + if (!checkPermissions(sender, command)) { + return true; } - public List hitTab(CommandSender sender, KList chain, String label) { - MortarSender vs = new MortarSender(sender); - vs.setTag(tag); + return command.handle(vs, chain.toArray(new String[chain.size()])); + } - if (label != null) - vs.setCommand(label); + public List hitTab(CommandSender sender, KList chain, String label) { + MortarSender vs = new MortarSender(sender); + vs.setTag(tag); - if (chain.isEmpty()) { - if (!checkPermissions(sender, command)) { - return null; - } + if (label != null) + vs.setCommand(label); - return command.handleTab(vs, new String[0]); - } + if (chain.isEmpty()) { + if (!checkPermissions(sender, command)) { + return null; + } - String nl = chain.get(0); - - for (List i : children.k()) { - for (String j : i) { - if (j.equalsIgnoreCase(nl)) { - vs.setCommand(chain.get(0)); - VirtualCommand cmd = children.get(i); - KList c = chain.copy(); - c.remove(0); - List v = cmd.hitTab(sender, c, vs.getCommand()); - if (v != null) { - return v; - } - } - } - } + return command.handleTab(vs, new String[0]); + } - if (!checkPermissions(sender, command)) { - return null; + String nl = chain.get(0); + + for (List i : children.k()) { + for (String j : i) { + if (j.equalsIgnoreCase(nl)) { + vs.setCommand(chain.get(0)); + VirtualCommand cmd = children.get(i); + KList c = chain.copy(); + c.remove(0); + List v = cmd.hitTab(sender, c, vs.getCommand()); + if (v != null) { + return v; + } } + } + } - return command.handleTab(vs, chain.toArray(new String[chain.size()])); + if (!checkPermissions(sender, command)) { + return null; } - private boolean checkPermissions(CommandSender sender, ICommand command2) { - boolean failed = false; + return command.handleTab(vs, chain.toArray(new String[chain.size()])); + } - for (String i : command.getRequiredPermissions()) { - if (!sender.hasPermission(i)) { - failed = true; - J.s(() -> Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "- " + C.WHITE + i)); - } - } + private boolean checkPermissions(CommandSender sender, ICommand command2) { + boolean failed = false; - if (failed) { - Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "Insufficient Permissions"); - return false; - } + for (String i : command.getRequiredPermissions()) { + if (!sender.hasPermission(i)) { + failed = true; + J.s(() -> Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "- " + C.WHITE + i)); + } + } - return true; + if (failed) { + Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "Insufficient Permissions"); + return false; } + + return true; + } } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java index e68214984..226063549 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDescription.java @@ -9,5 +9,5 @@ @Retention(RUNTIME) @Target(TYPE) public @interface ConfigDescription { - String value(); + String value(); } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java index 08da6d7e4..4f04f360d 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDoc.java @@ -9,7 +9,7 @@ @Retention(RUNTIME) @Target(FIELD) public @interface ConfigDoc { - String value(); + String value(); - String impact() default ""; + String impact() default ""; } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java index af0591cb7..9d470fa63 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigDocumentation.java @@ -4,305 +4,305 @@ import java.util.*; public final class ConfigDocumentation { - private static final Map SUMMARY_BY_KEY = Map.ofEntries( - Map.entry("enabled", "Enables or disables this feature."), - Map.entry("permanent", "Keeps this adaptation permanently active once learned."), - Map.entry("baseCost", "Base knowledge cost used when learning this adaptation."), - Map.entry("initialCost", "Knowledge cost required to purchase level 1."), - Map.entry("costFactor", "Scaling factor applied to higher adaptation levels."), - Map.entry("maxLevel", "Maximum level a player can reach for this adaptation."), - Map.entry("setInterval", "Tick interval used by this logic."), - Map.entry("minXp", "Minimum xp threshold required for this skill logic."), - Map.entry("language", "Primary language file used for localizations."), - Map.entry("fallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing", "Fallback language used when a localization key is missing."), - Map.entry("autoUpdateLanguage", "When enabled, language files are refreshed from plugin resources."), - Map.entry("autoUpdateCheck", "Checks for plugin updates during startup."), - Map.entry("metrics", "Sends anonymous bStats usage metrics."), - Map.entry("xpInCreative", "Allows skill xp gain while players are in creative or spectator."), - Map.entry("allowAdaptationsInCreative", "Allows using adaptations in creative mode."), - Map.entry("blacklistedWorlds", "World folder names where Adapt logic is disabled."), - Map.entry("experienceMaxLevel", "Global maximum level cap for skill progression."), - Map.entry("adaptActivatorBlock", "Block type players right-click to open the skills UI."), - Map.entry("adaptActivatorBlockName", "Display name used in UI text for the activator block."), - Map.entry("customModels", "Enables custom model lookups from the models config."), - Map.entry("advancements", "Enables Adapt advancement registration and grant flow."), - Map.entry("loginBonus", "Grants the configured login bonus message/rewards."), - Map.entry("welcomeMessage", "Shows the Adapt welcome message when players join."), - Map.entry("useSql", "Uses SQL as the player data backend."), - Map.entry("useRedis", "Enables Redis synchronization when SQL is active.") - ); + private static final Map SUMMARY_BY_KEY = Map.ofEntries( + Map.entry("enabled", "Enables or disables this feature."), + Map.entry("permanent", "Keeps this adaptation permanently active once learned."), + Map.entry("baseCost", "Base knowledge cost used when learning this adaptation."), + Map.entry("initialCost", "Knowledge cost required to purchase level 1."), + Map.entry("costFactor", "Scaling factor applied to higher adaptation levels."), + Map.entry("maxLevel", "Maximum level a player can reach for this adaptation."), + Map.entry("setInterval", "Tick interval used by this logic."), + Map.entry("minXp", "Minimum xp threshold required for this skill logic."), + Map.entry("language", "Primary language file used for localizations."), + Map.entry("fallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing", "Fallback language used when a localization key is missing."), + Map.entry("autoUpdateLanguage", "When enabled, language files are refreshed from plugin resources."), + Map.entry("autoUpdateCheck", "Checks for plugin updates during startup."), + Map.entry("metrics", "Sends anonymous bStats usage metrics."), + Map.entry("xpInCreative", "Allows skill xp gain while players are in creative or spectator."), + Map.entry("allowAdaptationsInCreative", "Allows using adaptations in creative mode."), + Map.entry("blacklistedWorlds", "World folder names where Adapt logic is disabled."), + Map.entry("experienceMaxLevel", "Global maximum level cap for skill progression."), + Map.entry("adaptActivatorBlock", "Block type players right-click to open the skills UI."), + Map.entry("adaptActivatorBlockName", "Display name used in UI text for the activator block."), + Map.entry("customModels", "Enables custom model lookups from the models config."), + Map.entry("advancements", "Enables Adapt advancement registration and grant flow."), + Map.entry("loginBonus", "Grants the configured login bonus message/rewards."), + Map.entry("welcomeMessage", "Shows the Adapt welcome message when players join."), + Map.entry("useSql", "Uses SQL as the player data backend."), + Map.entry("useRedis", "Enables Redis synchronization when SQL is active.") + ); - private static final Map IMPACT_BY_KEY = Map.ofEntries( - Map.entry("enabled", "Set to false to disable behavior without uninstalling files."), - Map.entry("permanent", "True removes the normal learn/unlearn flow and treats it as always learned."), - Map.entry("baseCost", "Higher values make each level cost more knowledge."), - Map.entry("initialCost", "Higher values make unlocking the first level more expensive."), - Map.entry("costFactor", "Higher values increase level-to-level cost growth."), - Map.entry("maxLevel", "Higher values allow more levels; lower values cap progression sooner."), - Map.entry("setInterval", "Lower values run logic more often; higher values run it less often."), - Map.entry("minXp", "Higher values delay when this skill starts applying."), - Map.entry("metrics", "Set to false to opt out of bStats telemetry."), - Map.entry("xpInCreative", "Set to true if you want creative/spectator players to gain xp."), - Map.entry("allowAdaptationsInCreative", "Set to true to let creative players trigger adaptations."), - Map.entry("experienceMaxLevel", "Higher values raise the hard cap for skill leveling."), - Map.entry("customModels", "Set to false to disable all custom model assignments."), - Map.entry("advancements", "Set to false to disable advancement creation and toast notifications."), - Map.entry("useSql", "Switching this changes where player data is loaded/saved."), - Map.entry("useRedis", "Requires SQL support and Redis credentials to synchronize across servers.") - ); - private static final Set ALWAYS_VISIBLE_KEYS = Set.of( - "enabled", - "permanent", - "baseCost", - "initialCost", - "costFactor", - "maxLevel", - "minXp", - "showParticles", - "showSounds" - ); + private static final Map IMPACT_BY_KEY = Map.ofEntries( + Map.entry("enabled", "Set to false to disable behavior without uninstalling files."), + Map.entry("permanent", "True removes the normal learn/unlearn flow and treats it as always learned."), + Map.entry("baseCost", "Higher values make each level cost more knowledge."), + Map.entry("initialCost", "Higher values make unlocking the first level more expensive."), + Map.entry("costFactor", "Higher values increase level-to-level cost growth."), + Map.entry("maxLevel", "Higher values allow more levels; lower values cap progression sooner."), + Map.entry("setInterval", "Lower values run logic more often; higher values run it less often."), + Map.entry("minXp", "Higher values delay when this skill starts applying."), + Map.entry("metrics", "Set to false to opt out of bStats telemetry."), + Map.entry("xpInCreative", "Set to true if you want creative/spectator players to gain xp."), + Map.entry("allowAdaptationsInCreative", "Set to true to let creative players trigger adaptations."), + Map.entry("experienceMaxLevel", "Higher values raise the hard cap for skill leveling."), + Map.entry("customModels", "Set to false to disable all custom model assignments."), + Map.entry("advancements", "Set to false to disable advancement creation and toast notifications."), + Map.entry("useSql", "Switching this changes where player data is loaded/saved."), + Map.entry("useRedis", "Requires SQL support and Redis credentials to synchronize across servers.") + ); + private static final Set ALWAYS_VISIBLE_KEYS = Set.of( + "enabled", + "permanent", + "baseCost", + "initialCost", + "costFactor", + "maxLevel", + "minXp", + "showParticles", + "showSounds" + ); - private ConfigDocumentation() { - } - - public static List buildFieldComments(String sourceTag, String path, Field field, Object value) { - List lines = new ArrayList<>(); - ConfigDoc annotation = field.getAnnotation(ConfigDoc.class); - String key = field.getName(); - String summary; - String impact; + private ConfigDocumentation() { + } - if (annotation != null) { - summary = annotation.value().strip(); - impact = annotation.impact().strip(); - if (isGenericSummary(summary)) { - summary = defaultSummary(sourceTag, path, field); - } - if (impact.isBlank() || isGenericImpact(impact)) { - impact = defaultImpact(field, value); - } - } else { - summary = SUMMARY_BY_KEY.getOrDefault(key, defaultSummary(sourceTag, path, field)); - impact = IMPACT_BY_KEY.getOrDefault(key, defaultImpact(field, value)); - } + public static List buildFieldComments(String sourceTag, String path, Field field, Object value) { + List lines = new ArrayList<>(); + ConfigDoc annotation = field.getAnnotation(ConfigDoc.class); + String key = field.getName(); + String summary; + String impact; - if (summary != null && !summary.isBlank()) { - lines.add(summary); - } - if (!impact.isBlank()) { - lines.add("Effect: " + impact); - } - return lines; + if (annotation != null) { + summary = annotation.value().strip(); + impact = annotation.impact().strip(); + if (isGenericSummary(summary)) { + summary = defaultSummary(sourceTag, path, field); + } + if (impact.isBlank() || isGenericImpact(impact)) { + impact = defaultImpact(field, value); + } + } else { + summary = SUMMARY_BY_KEY.getOrDefault(key, defaultSummary(sourceTag, path, field)); + impact = IMPACT_BY_KEY.getOrDefault(key, defaultImpact(field, value)); } - public static boolean shouldExposeField(String sourceTag, String path, Field field, Object value) { - if (field == null) { - return false; - } - if (field.getAnnotation(ConfigAdvanced.class) != null) { - return false; - } + if (summary != null && !summary.isBlank()) { + lines.add(summary); + } + if (!impact.isBlank()) { + lines.add("Effect: " + impact); + } + return lines; + } - String key = field.getName(); - if (ALWAYS_VISIBLE_KEYS.contains(key)) { - return true; - } + public static boolean shouldExposeField(String sourceTag, String path, Field field, Object value) { + if (field == null) { + return false; + } + if (field.getAnnotation(ConfigAdvanced.class) != null) { + return false; + } - String lowered = key.toLowerCase(Locale.ROOT); - Class type = field.getType(); - boolean isBoolean = type == boolean.class || type == Boolean.class; + String key = field.getName(); + if (ALWAYS_VISIBLE_KEYS.contains(key)) { + return true; + } - // Hide challenge reward tuning; these are rarely gameplay-critical knobs. - if (lowered.startsWith("challenge") && lowered.contains("reward")) { - return false; - } + String lowered = key.toLowerCase(Locale.ROOT); + Class type = field.getType(); + boolean isBoolean = type == boolean.class || type == Boolean.class; - // Internal update cadence knobs are advanced and should stay out of default configs. - if (lowered.equals("setinterval") || lowered.equals("statintervalms")) { - return false; - } + // Hide challenge reward tuning; these are rarely gameplay-critical knobs. + if (lowered.startsWith("challenge") && lowered.contains("reward")) { + return false; + } - // Hide over-granular audiovisual tuning by default. - if (lowered.contains("pitch") || lowered.contains("volume")) { - return false; - } - if (lowered.contains("sound") && !isBoolean) { - return false; - } - if (lowered.contains("particlesize") || lowered.contains("particlecount") || lowered.contains("particleevery")) { - return false; - } - if (lowered.contains("xoffset") || lowered.contains("yoffset") || lowered.contains("zoffset")) { - return false; - } + // Internal update cadence knobs are advanced and should stay out of default configs. + if (lowered.equals("setinterval") || lowered.equals("statintervalms")) { + return false; + } - // Hide fallback/anti-edge tuning that is mostly diagnostic. - if (lowered.contains("fallback") || lowered.contains("variance") || lowered.contains("curveexponent")) { - return false; - } + // Hide over-granular audiovisual tuning by default. + if (lowered.contains("pitch") || lowered.contains("volume")) { + return false; + } + if (lowered.contains("sound") && !isBoolean) { + return false; + } + if (lowered.contains("particlesize") || lowered.contains("particlecount") || lowered.contains("particleevery")) { + return false; + } + if (lowered.contains("xoffset") || lowered.contains("yoffset") || lowered.contains("zoffset")) { + return false; + } - return true; + // Hide fallback/anti-edge tuning that is mostly diagnostic. + if (lowered.contains("fallback") || lowered.contains("variance") || lowered.contains("curveexponent")) { + return false; } - public static List buildSectionComments(String sourceTag, String path) { - if (path == null || path.isBlank()) { - return List.of(); - } + return true; + } - String leaf = path; - int idx = leaf.lastIndexOf('.'); - if (idx >= 0 && idx + 1 < leaf.length()) { - leaf = leaf.substring(idx + 1); - } + public static List buildSectionComments(String sourceTag, String path) { + if (path == null || path.isBlank()) { + return List.of(); + } - String humanLeaf = humanize(leaf); - if (sourceTag != null && sourceTag.startsWith("skill:")) { - return List.of("Settings for the " + sourceTag.substring("skill:".length()) + " skill " + humanLeaf + " section."); - } - if (sourceTag != null && sourceTag.startsWith("adaptation:")) { - return List.of("Settings for the " + sourceTag.substring("adaptation:".length()) + " adaptation " + humanLeaf + " section."); - } + String leaf = path; + int idx = leaf.lastIndexOf('.'); + if (idx >= 0 && idx + 1 < leaf.length()) { + leaf = leaf.substring(idx + 1); + } - return List.of("Settings for " + humanLeaf + "."); + String humanLeaf = humanize(leaf); + if (sourceTag != null && sourceTag.startsWith("skill:")) { + return List.of("Settings for the " + sourceTag.substring("skill:".length()) + " skill " + humanLeaf + " section."); + } + if (sourceTag != null && sourceTag.startsWith("adaptation:")) { + return List.of("Settings for the " + sourceTag.substring("adaptation:".length()) + " adaptation " + humanLeaf + " section."); } - private static String defaultSummary(String sourceTag, String path, Field field) { - String key = field.getName(); - String lower = key.toLowerCase(Locale.ROOT); - String subject = subject(sourceTag, path); - if (lower.contains("cooldown")) { - return "Cooldown between " + subject + " activations."; - } - if (lower.contains("chance")) { - return "Chance for " + subject + " to trigger."; - } - if (lower.contains("xp")) { - return "XP gain tuning for " + subject + "."; - } - if (lower.contains("multiplier") || lower.contains("factor") || lower.contains("scalar")) { - return "Scaling applied to " + subject + "."; - } - if (lower.contains("duration") || lower.contains("ticks") || lower.contains("millis") || lower.endsWith("ms")) { - return "Duration or timing used by " + subject + "."; - } - if (lower.contains("radius") || lower.contains("range") || lower.contains("distance")) { - return "Distance/area limit used by " + subject + "."; - } - if (lower.startsWith("min") || lower.contains("threshold")) { - return "Minimum threshold required for " + subject + "."; - } - if (lower.startsWith("max") || lower.contains("cap")) { - return "Maximum cap applied to " + subject + "."; - } + return List.of("Settings for " + humanLeaf + "."); + } - String label = humanize(field.getName()); - if (sourceTag != null && sourceTag.startsWith("skill:")) { - return "Controls " + label + " for the " + sourceTag.substring("skill:".length()) + " skill."; - } - if (sourceTag != null && sourceTag.startsWith("adaptation:")) { - return "Controls " + label + " for the " + sourceTag.substring("adaptation:".length()) + " adaptation."; - } - if (path != null && !path.isBlank()) { - return "Controls " + label + " in the " + path + " section."; - } - return "Controls " + label + "."; + private static String defaultSummary(String sourceTag, String path, Field field) { + String key = field.getName(); + String lower = key.toLowerCase(Locale.ROOT); + String subject = subject(sourceTag, path); + if (lower.contains("cooldown")) { + return "Cooldown between " + subject + " activations."; + } + if (lower.contains("chance")) { + return "Chance for " + subject + " to trigger."; + } + if (lower.contains("xp")) { + return "XP gain tuning for " + subject + "."; + } + if (lower.contains("multiplier") || lower.contains("factor") || lower.contains("scalar")) { + return "Scaling applied to " + subject + "."; + } + if (lower.contains("duration") || lower.contains("ticks") || lower.contains("millis") || lower.endsWith("ms")) { + return "Duration or timing used by " + subject + "."; + } + if (lower.contains("radius") || lower.contains("range") || lower.contains("distance")) { + return "Distance/area limit used by " + subject + "."; + } + if (lower.startsWith("min") || lower.contains("threshold")) { + return "Minimum threshold required for " + subject + "."; + } + if (lower.startsWith("max") || lower.contains("cap")) { + return "Maximum cap applied to " + subject + "."; } - private static String defaultImpact(Field field, Object value) { - Class type = field.getType(); - String lower = field.getName().toLowerCase(Locale.ROOT); - if (type == boolean.class || type == Boolean.class) { - return "True enables this behavior and false disables it."; - } - if (lower.contains("chance")) { - return "Use values near 0.0-1.0; higher values trigger more often."; - } - if (lower.contains("cooldown")) { - return "Higher values increase time between activations; lower values allow more frequent triggers."; - } - if (lower.contains("xp")) { - return "Higher values grant more progression; lower values slow progression."; - } - if (lower.contains("multiplier") || lower.contains("factor") || lower.contains("scalar")) { - return "Higher values scale the effect more strongly; lower values scale it down."; - } - if (lower.contains("duration") || lower.contains("ticks") || lower.contains("millis") || lower.endsWith("ms")) { - return "Higher values make the effect last longer; lower values shorten it."; - } - if (lower.contains("radius") || lower.contains("range") || lower.contains("distance")) { - return "Higher values affect a wider area; lower values keep the effect tighter."; - } - if (lower.startsWith("min") || lower.contains("threshold")) { - return "Higher values make activation stricter; lower values make it easier to trigger."; - } - if (lower.startsWith("max") || lower.contains("cap")) { - return "Higher values raise the upper limit; lower values clamp the effect sooner."; - } - if (Number.class.isAssignableFrom(type) || type.isPrimitive() && type != boolean.class && type != char.class) { - return "Higher values increase intensity or limits; lower values reduce them."; - } - if (type.isEnum()) { - return "Changing this selects a different operating mode."; - } - if (type == String.class || type == char.class || type == Character.class) { - return "Changing this alters the identifier or text used by the feature."; - } - if (value instanceof List) { - return "Add or remove entries to control which values are included."; - } - if (value instanceof Map) { - return "Edit entries to control per-key overrides for this feature."; - } - return ""; + String label = humanize(field.getName()); + if (sourceTag != null && sourceTag.startsWith("skill:")) { + return "Controls " + label + " for the " + sourceTag.substring("skill:".length()) + " skill."; } + if (sourceTag != null && sourceTag.startsWith("adaptation:")) { + return "Controls " + label + " for the " + sourceTag.substring("adaptation:".length()) + " adaptation."; + } + if (path != null && !path.isBlank()) { + return "Controls " + label + " in the " + path + " section."; + } + return "Controls " + label + "."; + } - private static boolean isGenericSummary(String summary) { - if (summary == null || summary.isBlank()) { - return true; - } + private static String defaultImpact(Field field, Object value) { + Class type = field.getType(); + String lower = field.getName().toLowerCase(Locale.ROOT); + if (type == boolean.class || type == Boolean.class) { + return "True enables this behavior and false disables it."; + } + if (lower.contains("chance")) { + return "Use values near 0.0-1.0; higher values trigger more often."; + } + if (lower.contains("cooldown")) { + return "Higher values increase time between activations; lower values allow more frequent triggers."; + } + if (lower.contains("xp")) { + return "Higher values grant more progression; lower values slow progression."; + } + if (lower.contains("multiplier") || lower.contains("factor") || lower.contains("scalar")) { + return "Higher values scale the effect more strongly; lower values scale it down."; + } + if (lower.contains("duration") || lower.contains("ticks") || lower.contains("millis") || lower.endsWith("ms")) { + return "Higher values make the effect last longer; lower values shorten it."; + } + if (lower.contains("radius") || lower.contains("range") || lower.contains("distance")) { + return "Higher values affect a wider area; lower values keep the effect tighter."; + } + if (lower.startsWith("min") || lower.contains("threshold")) { + return "Higher values make activation stricter; lower values make it easier to trigger."; + } + if (lower.startsWith("max") || lower.contains("cap")) { + return "Higher values raise the upper limit; lower values clamp the effect sooner."; + } + if (Number.class.isAssignableFrom(type) || type.isPrimitive() && type != boolean.class && type != char.class) { + return "Higher values increase intensity or limits; lower values reduce them."; + } + if (type.isEnum()) { + return "Changing this selects a different operating mode."; + } + if (type == String.class || type == char.class || type == Character.class) { + return "Changing this alters the identifier or text used by the feature."; + } + if (value instanceof List) { + return "Add or remove entries to control which values are included."; + } + if (value instanceof Map) { + return "Edit entries to control per-key overrides for this feature."; + } + return ""; + } - String lower = summary.toLowerCase(Locale.ROOT).trim(); - return lower.startsWith("controls ") || lower.equals("no description provided"); + private static boolean isGenericSummary(String summary) { + if (summary == null || summary.isBlank()) { + return true; } - private static boolean isGenericImpact(String impact) { - if (impact == null || impact.isBlank()) { - return true; - } + String lower = summary.toLowerCase(Locale.ROOT).trim(); + return lower.startsWith("controls ") || lower.equals("no description provided"); + } - String lower = impact.toLowerCase(Locale.ROOT); - return lower.contains("higher values usually increase intensity, limits, or frequency; lower values reduce it.") - || lower.contains("true enables this behavior and false disables it."); + private static boolean isGenericImpact(String impact) { + if (impact == null || impact.isBlank()) { + return true; } - private static String subject(String sourceTag, String path) { - if (sourceTag != null && sourceTag.startsWith("skill:")) { - return "the " + sourceTag.substring("skill:".length()) + " skill"; - } - if (sourceTag != null && sourceTag.startsWith("adaptation:")) { - return "the " + sourceTag.substring("adaptation:".length()) + " adaptation"; - } - if (path != null && !path.isBlank()) { - return "the " + path + " section"; - } - return "this feature"; - } + String lower = impact.toLowerCase(Locale.ROOT); + return lower.contains("higher values usually increase intensity, limits, or frequency; lower values reduce it.") + || lower.contains("true enables this behavior and false disables it."); + } - private static String humanize(String key) { - if (key == null || key.isBlank()) { - return "this setting"; - } + private static String subject(String sourceTag, String path) { + if (sourceTag != null && sourceTag.startsWith("skill:")) { + return "the " + sourceTag.substring("skill:".length()) + " skill"; + } + if (sourceTag != null && sourceTag.startsWith("adaptation:")) { + return "the " + sourceTag.substring("adaptation:".length()) + " adaptation"; + } + if (path != null && !path.isBlank()) { + return "the " + path + " section"; + } + return "this feature"; + } - String spaced = key - .replace('_', ' ') - .replace('-', ' ') - .replaceAll("([a-z])([A-Z])", "$1 $2") - .trim(); - if (spaced.isBlank()) { - return key; - } + private static String humanize(String key) { + if (key == null || key.isBlank()) { + return "this setting"; + } - String lower = spaced.toLowerCase(Locale.ROOT); - return Character.toUpperCase(lower.charAt(0)) + lower.substring(1); + String spaced = key + .replace('_', ' ') + .replace('-', ' ') + .replaceAll("([a-z])([A-Z])", "$1 $2") + .trim(); + if (spaced.isBlank()) { + return key; } + + String lower = spaced.toLowerCase(Locale.ROOT); + return Character.toUpperCase(lower.charAt(0)) + lower.substring(1); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java index d996e4cb2..b6ec19ea2 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigFileSupport.java @@ -13,269 +13,269 @@ import java.util.concurrent.atomic.AtomicInteger; public final class ConfigFileSupport { - private static final long MAX_CONFIG_BYTES_DEFAULT = 2L * 1024L * 1024L; - private static final long MAX_CONFIG_BYTES_SKILL_OR_ADAPTATION = 256L * 1024L; - private static final AtomicInteger CREATED_MISSING_CONFIGS = new AtomicInteger(); + private static final long MAX_CONFIG_BYTES_DEFAULT = 2L * 1024L * 1024L; + private static final long MAX_CONFIG_BYTES_SKILL_OR_ADAPTATION = 256L * 1024L; + private static final AtomicInteger CREATED_MISSING_CONFIGS = new AtomicInteger(); + + private ConfigFileSupport() { + } + + public static T load( + File canonicalFile, + File legacyFile, + Class type, + T fallback, + boolean overwriteOnReadFailure, + String sourceTag, + String createdMessage + ) throws IOException { + long maxConfigBytes = maxConfigBytesForSourceTag(sourceTag); + boolean canonicalizeExisting = shouldCanonicalizeExisting(sourceTag, overwriteOnReadFailure); + if (canonicalFile != null && canonicalFile.exists()) { + try { + if (canonicalFile.length() > maxConfigBytes) { + throw new IOException("Config file is too large (" + canonicalFile.length() + " bytes)"); + } + String raw = IO.readAll(canonicalFile); + T loaded = deserialize(raw, canonicalFile, type); + if (loaded == null) { + throw new IOException("Config parser returned null."); + } + + if (canonicalizeExisting) { + String canonical = serialize(loaded, canonicalFile, sourceTag); + if (!normalize(canonical).equals(normalize(raw))) { + ConfigRewriteReporter.reportRewrite(canonicalFile, sourceTag, raw, canonical); + IO.writeAll(canonicalFile, canonical); + } + } + deleteLegacyFileIfMigrated(canonicalFile, legacyFile, sourceTag); + return loaded; + } catch (Throwable e) { + if (overwriteOnReadFailure) { + ConfigRewriteReporter.reportFallbackRewrite(canonicalFile, sourceTag, reason("invalid config", e)); + IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); + return fallback; + } - private ConfigFileSupport() { + throw new IOException("Invalid config", e); + } } - public static T load( - File canonicalFile, - File legacyFile, - Class type, - T fallback, - boolean overwriteOnReadFailure, - String sourceTag, - String createdMessage - ) throws IOException { - long maxConfigBytes = maxConfigBytesForSourceTag(sourceTag); - boolean canonicalizeExisting = shouldCanonicalizeExisting(sourceTag, overwriteOnReadFailure); - if (canonicalFile != null && canonicalFile.exists()) { - try { - if (canonicalFile.length() > maxConfigBytes) { - throw new IOException("Config file is too large (" + canonicalFile.length() + " bytes)"); - } - String raw = IO.readAll(canonicalFile); - T loaded = deserialize(raw, canonicalFile, type); - if (loaded == null) { - throw new IOException("Config parser returned null."); - } - - if (canonicalizeExisting) { - String canonical = serialize(loaded, canonicalFile, sourceTag); - if (!normalize(canonical).equals(normalize(raw))) { - ConfigRewriteReporter.reportRewrite(canonicalFile, sourceTag, raw, canonical); - IO.writeAll(canonicalFile, canonical); - } - } - deleteLegacyFileIfMigrated(canonicalFile, legacyFile, sourceTag); - return loaded; - } catch (Throwable e) { - if (overwriteOnReadFailure) { - ConfigRewriteReporter.reportFallbackRewrite(canonicalFile, sourceTag, reason("invalid config", e)); - IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); - return fallback; - } - - throw new IOException("Invalid config", e); - } + if (legacyFile != null && legacyFile.exists()) { + try { + if (legacyFile.length() > maxConfigBytes) { + throw new IOException("Legacy config file is too large (" + legacyFile.length() + " bytes)"); + } + String raw = IO.readAll(legacyFile); + T loaded = deserialize(raw, legacyFile, type); + if (loaded == null) { + throw new IOException("Config parser returned null."); } - if (legacyFile != null && legacyFile.exists()) { - try { - if (legacyFile.length() > maxConfigBytes) { - throw new IOException("Legacy config file is too large (" + legacyFile.length() + " bytes)"); - } - String raw = IO.readAll(legacyFile); - T loaded = deserialize(raw, legacyFile, type); - if (loaded == null) { - throw new IOException("Config parser returned null."); - } - - IO.writeAll(canonicalFile, serialize(loaded, canonicalFile, sourceTag)); - Adapt.info("Migrated legacy config [" + legacyPath(legacyFile) + "] -> [" + legacyPath(canonicalFile) + "]."); - deleteLegacyFileIfMigrated(canonicalFile, legacyFile, sourceTag); - return loaded; - } catch (Throwable e) { - if (overwriteOnReadFailure) { - ConfigRewriteReporter.reportFallbackRewrite(canonicalFile, sourceTag, reason("invalid legacy config", e)); - IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); - return fallback; - } - - throw new IOException("Invalid legacy config", e); - } + IO.writeAll(canonicalFile, serialize(loaded, canonicalFile, sourceTag)); + Adapt.info("Migrated legacy config [" + legacyPath(legacyFile) + "] -> [" + legacyPath(canonicalFile) + "]."); + deleteLegacyFileIfMigrated(canonicalFile, legacyFile, sourceTag); + return loaded; + } catch (Throwable e) { + if (overwriteOnReadFailure) { + ConfigRewriteReporter.reportFallbackRewrite(canonicalFile, sourceTag, reason("invalid legacy config", e)); + IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); + return fallback; } - IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); - recordMissingConfigCreated(); - return fallback; + throw new IOException("Invalid legacy config", e); + } } - public static void recordMissingConfigCreated() { - CREATED_MISSING_CONFIGS.incrementAndGet(); - } + IO.writeAll(canonicalFile, serialize(fallback, canonicalFile, sourceTag)); + recordMissingConfigCreated(); + return fallback; + } - public static void flushCreatedConfigSummary() { - int created = CREATED_MISSING_CONFIGS.getAndSet(0); - if (created <= 0) { - return; - } + public static void recordMissingConfigCreated() { + CREATED_MISSING_CONFIGS.incrementAndGet(); + } - Adapt.info("Created " + created + " missing config " + (created == 1 ? "entry" : "entries") + " from defaults."); + public static void flushCreatedConfigSummary() { + int created = CREATED_MISSING_CONFIGS.getAndSet(0); + if (created <= 0) { + return; } - public static String normalize(String text) { - if (text == null) { - return ""; - } - return text.replace("\r\n", "\n").stripTrailing(); - } + Adapt.info("Created " + created + " missing config " + (created == 1 ? "entry" : "entries") + " from defaults."); + } - public static File toTomlFile(File file) { - if (file == null) { - return null; - } - return replaceExtension(file, ".toml"); + public static String normalize(String text) { + if (text == null) { + return ""; } + return text.replace("\r\n", "\n").stripTrailing(); + } - public static File toJsonFile(File file) { - if (file == null) { - return null; - } - return replaceExtension(file, ".json"); + public static File toTomlFile(File file) { + if (file == null) { + return null; } + return replaceExtension(file, ".toml"); + } - public static File replaceExtension(File file, String extension) { - String name = file.getName(); - int idx = name.lastIndexOf('.'); - String base = idx >= 0 ? name.substring(0, idx) : name; - return new File(file.getParentFile(), base + extension); + public static File toJsonFile(File file) { + if (file == null) { + return null; } - - public static boolean isTomlFile(File file) { - return file != null && file.getName().toLowerCase(Locale.ROOT).endsWith(".toml"); + return replaceExtension(file, ".json"); + } + + public static File replaceExtension(File file, String extension) { + String name = file.getName(); + int idx = name.lastIndexOf('.'); + String base = idx >= 0 ? name.substring(0, idx) : name; + return new File(file.getParentFile(), base + extension); + } + + public static boolean isTomlFile(File file) { + return file != null && file.getName().toLowerCase(Locale.ROOT).endsWith(".toml"); + } + + public static boolean isJsonFile(File file) { + if (file == null) { + return false; } + String name = file.getName().toLowerCase(Locale.ROOT); + return name.endsWith(".json") || name.endsWith(".yml") || name.endsWith(".yaml"); + } - public static boolean isJsonFile(File file) { - if (file == null) { - return false; - } - String name = file.getName().toLowerCase(Locale.ROOT); - return name.endsWith(".json") || name.endsWith(".yml") || name.endsWith(".yaml"); - } + public static boolean isSupportedConfigFile(File file) { + return isTomlFile(file) || isJsonFile(file); + } - public static boolean isSupportedConfigFile(File file) { - return isTomlFile(file) || isJsonFile(file); + public static String configNameFromFileName(String fileName) { + if (fileName == null) { + return null; } - - public static String configNameFromFileName(String fileName) { - if (fileName == null) { - return null; - } - String lower = fileName.toLowerCase(Locale.ROOT); - if (lower.endsWith(".toml")) { - return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); - } - if (lower.endsWith(".json")) { - return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); - } - if (lower.endsWith(".yml")) { - return fileName.substring(0, fileName.length() - 4).toLowerCase(Locale.ROOT); - } - if (lower.endsWith(".yaml")) { - return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); - } - return null; + String lower = fileName.toLowerCase(Locale.ROOT); + if (lower.endsWith(".toml")) { + return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); } + if (lower.endsWith(".json")) { + return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); + } + if (lower.endsWith(".yml")) { + return fileName.substring(0, fileName.length() - 4).toLowerCase(Locale.ROOT); + } + if (lower.endsWith(".yaml")) { + return fileName.substring(0, fileName.length() - 5).toLowerCase(Locale.ROOT); + } + return null; + } - public static JsonElement parseToJsonElement(String raw, File file) { - if (raw == null || raw.isBlank()) { - return null; - } - - try { - if (isTomlFile(file)) { - return TomlCodec.toJsonElement(raw); - } + public static JsonElement parseToJsonElement(String raw, File file) { + if (raw == null || raw.isBlank()) { + return null; + } - return Json.fromJson(raw, JsonElement.class); - } catch (Throwable ignored) { - } + try { + if (isTomlFile(file)) { + return TomlCodec.toJsonElement(raw); + } - try { - return TomlCodec.toJsonElement(raw); - } catch (Throwable ignored) { - return null; - } + return Json.fromJson(raw, JsonElement.class); + } catch (Throwable ignored) { } - public static String serializeJsonElementToToml(JsonElement element) { - return TomlCodec.toToml(element); + try { + return TomlCodec.toJsonElement(raw); + } catch (Throwable ignored) { + return null; } + } - public static boolean deleteLegacyFileIfMigrated(File canonicalFile, File legacyFile, String sourceTag) { - if (canonicalFile == null || legacyFile == null) { - return false; - } - if (!canonicalFile.exists() || !canonicalFile.isFile() || !legacyFile.exists() || !legacyFile.isFile()) { - return false; - } - if (canonicalFile.getAbsoluteFile().equals(legacyFile.getAbsoluteFile())) { - return false; - } + public static String serializeJsonElementToToml(JsonElement element) { + return TomlCodec.toToml(element); + } - try { - boolean deleted = Files.deleteIfExists(legacyFile.toPath()); - if (deleted) { - Adapt.verbose("Deleted migrated legacy config [" + legacyPath(legacyFile) + "] for [" + (sourceTag == null ? "config" : sourceTag) + "]."); - } - return deleted; - } catch (Throwable e) { - Adapt.warn("Failed to delete migrated legacy config [" + legacyPath(legacyFile) + "]: " + e.getMessage()); - return false; - } + public static boolean deleteLegacyFileIfMigrated(File canonicalFile, File legacyFile, String sourceTag) { + if (canonicalFile == null || legacyFile == null) { + return false; } - - private static T deserialize(String raw, File sourceFile, Class type) throws IOException { - try { - if (isTomlFile(sourceFile)) { - return TomlCodec.fromToml(raw, type); - } - return Json.fromJson(raw, type); - } catch (Throwable e) { - throw new IOException("Failed to parse " + sourceFile.getName(), e); - } + if (!canonicalFile.exists() || !canonicalFile.isFile() || !legacyFile.exists() || !legacyFile.isFile()) { + return false; + } + if (canonicalFile.getAbsoluteFile().equals(legacyFile.getAbsoluteFile())) { + return false; } - private static String serialize(Object loaded, File targetFile, String sourceTag) { - if (isTomlFile(targetFile)) { - return TomlCodec.toToml(loaded, sourceTag); - } - return Json.toJson(loaded, true); + try { + boolean deleted = Files.deleteIfExists(legacyFile.toPath()); + if (deleted) { + Adapt.verbose("Deleted migrated legacy config [" + legacyPath(legacyFile) + "] for [" + (sourceTag == null ? "config" : sourceTag) + "]."); + } + return deleted; + } catch (Throwable e) { + Adapt.warn("Failed to delete migrated legacy config [" + legacyPath(legacyFile) + "]: " + e.getMessage()); + return false; + } + } + + private static T deserialize(String raw, File sourceFile, Class type) throws IOException { + try { + if (isTomlFile(sourceFile)) { + return TomlCodec.fromToml(raw, type); + } + return Json.fromJson(raw, type); + } catch (Throwable e) { + throw new IOException("Failed to parse " + sourceFile.getName(), e); } + } - private static long maxConfigBytesForSourceTag(String sourceTag) { - if (sourceTag != null && (sourceTag.startsWith("skill:") || sourceTag.startsWith("adaptation:"))) { - return MAX_CONFIG_BYTES_SKILL_OR_ADAPTATION; - } - return MAX_CONFIG_BYTES_DEFAULT; + private static String serialize(Object loaded, File targetFile, String sourceTag) { + if (isTomlFile(targetFile)) { + return TomlCodec.toToml(loaded, sourceTag); } + return Json.toJson(loaded, true); + } - private static boolean shouldCanonicalizeExisting(String sourceTag, boolean overwriteOnReadFailure) { - if (sourceTag == null) { - return true; - } + private static long maxConfigBytesForSourceTag(String sourceTag) { + if (sourceTag != null && (sourceTag.startsWith("skill:") || sourceTag.startsWith("adaptation:"))) { + return MAX_CONFIG_BYTES_SKILL_OR_ADAPTATION; + } + return MAX_CONFIG_BYTES_DEFAULT; + } - // During initial startup of skill/adaptation content we prioritize fast parse/load. - // Canonical rewrites still occur via explicit canonicalization/hotload paths. - if (overwriteOnReadFailure && (sourceTag.startsWith("skill:") || sourceTag.startsWith("adaptation:"))) { - return false; - } + private static boolean shouldCanonicalizeExisting(String sourceTag, boolean overwriteOnReadFailure) { + if (sourceTag == null) { + return true; + } - return true; + // During initial startup of skill/adaptation content we prioritize fast parse/load. + // Canonical rewrites still occur via explicit canonicalization/hotload paths. + if (overwriteOnReadFailure && (sourceTag.startsWith("skill:") || sourceTag.startsWith("adaptation:"))) { + return false; } - private static String reason(String prefix, Throwable error) { - if (error == null || error.getMessage() == null || error.getMessage().isBlank()) { - return prefix; - } - return prefix + ": " + error.getMessage(); + return true; + } + + private static String reason(String prefix, Throwable error) { + if (error == null || error.getMessage() == null || error.getMessage().isBlank()) { + return prefix; } + return prefix + ": " + error.getMessage(); + } - private static String legacyPath(File file) { - if (file == null) { - return ""; - } - try { - File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); - if (dataFolder == null) { - return file.getPath(); - } - return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); - } catch (Throwable ignored) { - return file.getPath(); - } + private static String legacyPath(File file) { + if (file == null) { + return ""; + } + try { + File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); + if (dataFolder == null) { + return file.getPath(); + } + return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); + } catch (Throwable ignored) { + return file.getPath(); } + } } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java index 23865b89a..2c58d36d2 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigMigrationManager.java @@ -18,199 +18,199 @@ import java.util.zip.ZipOutputStream; public final class ConfigMigrationManager { - private static final Object LOCK = new Object(); - private static final DateTimeFormatter TS = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"); - private static volatile boolean backupAttempted = false; - - private ConfigMigrationManager() { + private static final Object LOCK = new Object(); + private static final DateTimeFormatter TS = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"); + private static volatile boolean backupAttempted = false; + + private ConfigMigrationManager() { + } + + public static void backupLegacyJsonConfigsOnce() { + synchronized (LOCK) { + if (backupAttempted) { + return; + } + backupAttempted = true; + + File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); + if (dataFolder == null || !dataFolder.exists()) { + return; + } + + File marker = Adapt.instance.getDataFile("adapt", "migrations", ".legacy-json-backed-up"); + if (marker.exists()) { + return; + } + + List legacyJson = collectLegacyJsonFiles(dataFolder); + if (legacyJson.isEmpty()) { + return; + } + + boolean migrationNeeded = legacyJson.stream() + .map(ConfigFileSupport::toTomlFile) + .anyMatch(file -> file != null && !file.exists()); + if (!migrationNeeded) { + return; + } + + File backupDir = Adapt.instance.getDataFolder("adapt", "migrations", "backups"); + String timestamp = LocalDateTime.now().format(TS); + File zip = new File(backupDir, timestamp + "-pre-toml-migration.zip"); + + try { + zipLegacyFiles(dataFolder, legacyJson, zip); + IO.writeAll(marker, "backup=" + zip.getName() + "\ncreated=" + timestamp + "\n"); + Adapt.warn("Created legacy config backup before TOML migration: " + zip.getPath()); + } catch (Throwable e) { + Adapt.warn("Failed to create legacy config backup zip: " + e.getMessage()); + } } - - public static void backupLegacyJsonConfigsOnce() { - synchronized (LOCK) { - if (backupAttempted) { - return; - } - backupAttempted = true; - - File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); - if (dataFolder == null || !dataFolder.exists()) { - return; - } - - File marker = Adapt.instance.getDataFile("adapt", "migrations", ".legacy-json-backed-up"); - if (marker.exists()) { - return; - } - - List legacyJson = collectLegacyJsonFiles(dataFolder); - if (legacyJson.isEmpty()) { - return; - } - - boolean migrationNeeded = legacyJson.stream() - .map(ConfigFileSupport::toTomlFile) - .anyMatch(file -> file != null && !file.exists()); - if (!migrationNeeded) { - return; - } - - File backupDir = Adapt.instance.getDataFolder("adapt", "migrations", "backups"); - String timestamp = LocalDateTime.now().format(TS); - File zip = new File(backupDir, timestamp + "-pre-toml-migration.zip"); - - try { - zipLegacyFiles(dataFolder, legacyJson, zip); - IO.writeAll(marker, "backup=" + zip.getName() + "\ncreated=" + timestamp + "\n"); - Adapt.warn("Created legacy config backup before TOML migration: " + zip.getPath()); - } catch (Throwable e) { - Adapt.warn("Failed to create legacy config backup zip: " + e.getMessage()); - } + } + + public static int deleteMigratedLegacyJsonFiles() { + synchronized (LOCK) { + File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); + if (dataFolder == null || !dataFolder.exists()) { + return 0; + } + + int deleted = 0; + for (File legacyJson : collectLegacyJsonFiles(dataFolder)) { + if (legacyJson == null || !legacyJson.exists() || !legacyJson.isFile()) { + continue; } - } - public static int deleteMigratedLegacyJsonFiles() { - synchronized (LOCK) { - File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); - if (dataFolder == null || !dataFolder.exists()) { - return 0; - } - - int deleted = 0; - for (File legacyJson : collectLegacyJsonFiles(dataFolder)) { - if (legacyJson == null || !legacyJson.exists() || !legacyJson.isFile()) { - continue; - } - - File canonicalToml = ConfigFileSupport.toTomlFile(legacyJson); - if (canonicalToml == null || !canonicalToml.exists() || !canonicalToml.isFile()) { - continue; - } - - try { - if (Files.deleteIfExists(legacyJson.toPath())) { - deleted++; - } - } catch (Throwable e) { - Adapt.warn("Failed to delete migrated legacy config [" + legacyPath(dataFolder, legacyJson) + "]: " + e.getMessage()); - } - } - - return deleted; + File canonicalToml = ConfigFileSupport.toTomlFile(legacyJson); + if (canonicalToml == null || !canonicalToml.exists() || !canonicalToml.isFile()) { + continue; } - } - - public static boolean hasLegacySkillOrAdaptationJsonFiles() { - synchronized (LOCK) { - File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); - if (dataFolder == null || !dataFolder.exists()) { - return false; - } - File adaptRoot = new File(dataFolder, "adapt"); - return hasAnyJson(new File(adaptRoot, "skills")) || hasAnyJson(new File(adaptRoot, "adaptations")); + try { + if (Files.deleteIfExists(legacyJson.toPath())) { + deleted++; + } + } catch (Throwable e) { + Adapt.warn("Failed to delete migrated legacy config [" + legacyPath(dataFolder, legacyJson) + "]: " + e.getMessage()); } - } + } - private static List collectLegacyJsonFiles(File dataFolder) { - List files = new ArrayList<>(); - addScopedJsonFiles(new File(dataFolder, "adapt"), files); - addScopedJsonFiles(new File(dataFolder, "languages"), files); - return files; + return deleted; } + } - private static void addScopedJsonFiles(File root, List out) { - if (root == null || !root.exists() || !root.isDirectory()) { - return; - } + public static boolean hasLegacySkillOrAdaptationJsonFiles() { + synchronized (LOCK) { + File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); + if (dataFolder == null || !dataFolder.exists()) { + return false; + } - ArrayDeque queue = new ArrayDeque<>(); - queue.add(root); - while (!queue.isEmpty()) { - File next = queue.removeFirst(); - File[] children = next.listFiles(); - if (children == null || children.length == 0) { - continue; - } - - for (File child : children) { - if (child == null) { - continue; - } - if (child.isDirectory()) { - queue.add(child); - continue; - } - - if (child.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { - out.add(child); - } - } - } + File adaptRoot = new File(dataFolder, "adapt"); + return hasAnyJson(new File(adaptRoot, "skills")) || hasAnyJson(new File(adaptRoot, "adaptations")); + } + } + + private static List collectLegacyJsonFiles(File dataFolder) { + List files = new ArrayList<>(); + addScopedJsonFiles(new File(dataFolder, "adapt"), files); + addScopedJsonFiles(new File(dataFolder, "languages"), files); + return files; + } + + private static void addScopedJsonFiles(File root, List out) { + if (root == null || !root.exists() || !root.isDirectory()) { + return; } - private static boolean hasAnyJson(File root) { - if (root == null || !root.exists() || !root.isDirectory()) { - return false; + ArrayDeque queue = new ArrayDeque<>(); + queue.add(root); + while (!queue.isEmpty()) { + File next = queue.removeFirst(); + File[] children = next.listFiles(); + if (children == null || children.length == 0) { + continue; + } + + for (File child : children) { + if (child == null) { + continue; + } + if (child.isDirectory()) { + queue.add(child); + continue; } - ArrayDeque queue = new ArrayDeque<>(); - queue.add(root); - while (!queue.isEmpty()) { - File next = queue.removeFirst(); - File[] children = next.listFiles(); - if (children == null || children.length == 0) { - continue; - } - - for (File child : children) { - if (child == null) { - continue; - } - if (child.isDirectory()) { - queue.add(child); - continue; - } - if (child.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { - return true; - } - } + if (child.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { + out.add(child); } + } + } + } - return false; + private static boolean hasAnyJson(File root) { + if (root == null || !root.exists() || !root.isDirectory()) { + return false; } - private static void zipLegacyFiles(File dataFolder, List files, File zipFile) throws Exception { - zipFile.getParentFile().mkdirs(); - try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile), StandardCharsets.UTF_8)) { - byte[] buffer = new byte[8192]; - for (File file : files) { - if (file == null || !file.exists() || !file.isFile()) { - continue; - } - - String relative = dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); - out.putNextEntry(new ZipEntry(relative)); - try (FileInputStream in = new FileInputStream(file)) { - int read; - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - } - } - out.closeEntry(); - } + ArrayDeque queue = new ArrayDeque<>(); + queue.add(root); + while (!queue.isEmpty()) { + File next = queue.removeFirst(); + File[] children = next.listFiles(); + if (children == null || children.length == 0) { + continue; + } + + for (File child : children) { + if (child == null) { + continue; } + if (child.isDirectory()) { + queue.add(child); + continue; + } + if (child.getName().toLowerCase(Locale.ROOT).endsWith(".json")) { + return true; + } + } } - private static String legacyPath(File dataFolder, File file) { - if (file == null) { - return ""; + return false; + } + + private static void zipLegacyFiles(File dataFolder, List files, File zipFile) throws Exception { + zipFile.getParentFile().mkdirs(); + try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile), StandardCharsets.UTF_8)) { + byte[] buffer = new byte[8192]; + for (File file : files) { + if (file == null || !file.exists() || !file.isFile()) { + continue; } - try { - if (dataFolder != null && dataFolder.exists()) { - return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); - } - } catch (Throwable ignored) { + + String relative = dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); + out.putNextEntry(new ZipEntry(relative)); + try (FileInputStream in = new FileInputStream(file)) { + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } } - return file.getPath(); + out.closeEntry(); + } + } + } + + private static String legacyPath(File dataFolder, File file) { + if (file == null) { + return ""; + } + try { + if (dataFolder != null && dataFolder.exists()) { + return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); + } + } catch (Throwable ignored) { } + return file.getPath(); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java index e651b9172..83401a32d 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java +++ b/src/main/java/art/arcane/adapt/util/project/config/ConfigRewriteReporter.java @@ -9,212 +9,212 @@ import java.util.*; public class ConfigRewriteReporter { - private static final int MAX_KEYS_PER_CATEGORY = 8; - private static final String MISSING = ""; - private static final String REMOVED = ""; - - public static void reportRewrite(File file, String source, String beforeRaw, String afterRaw) { - String before = normalize(beforeRaw); - String after = normalize(afterRaw); - if (Objects.equals(before, after)) { - return; - } - - List changes = computeDiff(before, after); - String path = relativize(file); - String sourceTag = source == null || source.isBlank() ? "config" : source; - - if (changes.isEmpty()) { - Adapt.info("Canonicalized " + sourceTag + " [" + path + "] (format/order only)."); - return; - } - - int removed = 0; - int added = 0; - int changed = 0; - List removedKeys = new ArrayList<>(); - List addedKeys = new ArrayList<>(); - List changedKeys = new ArrayList<>(); - for (Change changeEntry : changes) { - if (changeEntry.type == ChangeType.REMOVED) { - removed++; - removedKeys.add(changeEntry.key); - } else if (changeEntry.type == ChangeType.ADDED) { - added++; - addedKeys.add(changeEntry.key); - } else { - changed++; - changedKeys.add(changeEntry.key); - } - } - - Adapt.warn("Canonicalized " + sourceTag + " [" + path + "] with schema changes (removed=" + removed + ", added=" + added + ", changed=" + changed + ")."); - if (!removedKeys.isEmpty()) { - Adapt.warn(" - removed keys: " + summarizeKeys(removedKeys)); - } - if (!addedKeys.isEmpty()) { - Adapt.info(" - added keys: " + summarizeKeys(addedKeys)); - } - if (!changedKeys.isEmpty()) { - Adapt.info(" - changed keys: " + summarizeKeys(changedKeys)); - } - } - - public static void reportFallbackRewrite(File file, String source, String reason) { - String path = relativize(file); - String sourceTag = source == null || source.isBlank() ? "config" : source; - String reasonText = reason == null || reason.isBlank() ? "invalid/unsupported content" : reason; - Adapt.warn("Rewrote " + sourceTag + " [" + path + "] using fallback defaults (" + reasonText + ")."); - } - - private static List computeDiff(String before, String after) { - Map left = flattenForDiff(before); - Map right = flattenForDiff(after); - Set keys = new HashSet<>(left.keySet()); - keys.addAll(right.keySet()); - - List ordered = new ArrayList<>(keys); - ordered.sort(String::compareTo); - - List out = new ArrayList<>(); - for (String key : ordered) { - boolean inLeft = left.containsKey(key); - boolean inRight = right.containsKey(key); - String oldValue = inLeft ? left.get(key) : MISSING; - String newValue = inRight ? right.get(key) : REMOVED; - if (Objects.equals(oldValue, newValue)) { - continue; - } - - ChangeType type; - if (inLeft && !inRight) { - type = ChangeType.REMOVED; - } else if (!inLeft && inRight) { - type = ChangeType.ADDED; - } else { - type = ChangeType.CHANGED; - } - - out.add(new Change(type, key)); - } - - return out; - } - - private static Map flattenForDiff(String raw) { - JsonElement element = parseStructured(raw); - if (element == null) { - Map fallback = new HashMap<>(); - if (raw != null && !raw.isBlank()) { - fallback.put("$", normalize(raw)); - } - return fallback; - } - - Map out = new HashMap<>(); - flattenJson("$", element, out); - return out; - } - - private static void flattenJson(String path, JsonElement element, Map out) { - if (element == null || element.isJsonNull()) { - out.put(path, "null"); - return; - } - - if (element.isJsonPrimitive()) { - out.put(path, element.toString()); - return; - } - - if (element.isJsonArray()) { - if (element.getAsJsonArray().size() == 0) { - out.put(path, "[]"); - return; - } - - for (int i = 0; i < element.getAsJsonArray().size(); i++) { - flattenJson(path + "[" + i + "]", element.getAsJsonArray().get(i), out); - } - return; - } - - JsonObject object = element.getAsJsonObject(); - if (object.entrySet().isEmpty()) { - out.put(path, "{}"); - return; - } - - for (Map.Entry entry : object.entrySet()) { - flattenJson(path + "." + entry.getKey(), entry.getValue(), out); - } - } - - private static JsonElement parseStructured(String raw) { - if (raw == null || raw.isBlank()) { - return null; - } - - return ConfigFileSupport.parseToJsonElement(raw, null); - } - - private static String summarizeKeys(List keys) { - if (keys == null || keys.isEmpty()) { - return "(none)"; - } - - int shown = Math.min(MAX_KEYS_PER_CATEGORY, keys.size()); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < shown; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append(keys.get(i)); - } - - if (keys.size() > shown) { - sb.append(" (+").append(keys.size() - shown).append(" more)"); - } - - return sb.toString(); - } - - private static String normalize(String json) { - if (json == null) { - return null; - } - return ConfigFileSupport.normalize(json); - } - - private static String relativize(File file) { - if (file == null) { - return ""; - } + private static final int MAX_KEYS_PER_CATEGORY = 8; + private static final String MISSING = ""; + private static final String REMOVED = ""; + + public static void reportRewrite(File file, String source, String beforeRaw, String afterRaw) { + String before = normalize(beforeRaw); + String after = normalize(afterRaw); + if (Objects.equals(before, after)) { + return; + } + + List changes = computeDiff(before, after); + String path = relativize(file); + String sourceTag = source == null || source.isBlank() ? "config" : source; + + if (changes.isEmpty()) { + Adapt.info("Canonicalized " + sourceTag + " [" + path + "] (format/order only)."); + return; + } + + int removed = 0; + int added = 0; + int changed = 0; + List removedKeys = new ArrayList<>(); + List addedKeys = new ArrayList<>(); + List changedKeys = new ArrayList<>(); + for (Change changeEntry : changes) { + if (changeEntry.type == ChangeType.REMOVED) { + removed++; + removedKeys.add(changeEntry.key); + } else if (changeEntry.type == ChangeType.ADDED) { + added++; + addedKeys.add(changeEntry.key); + } else { + changed++; + changedKeys.add(changeEntry.key); + } + } + + Adapt.warn("Canonicalized " + sourceTag + " [" + path + "] with schema changes (removed=" + removed + ", added=" + added + ", changed=" + changed + ")."); + if (!removedKeys.isEmpty()) { + Adapt.warn(" - removed keys: " + summarizeKeys(removedKeys)); + } + if (!addedKeys.isEmpty()) { + Adapt.info(" - added keys: " + summarizeKeys(addedKeys)); + } + if (!changedKeys.isEmpty()) { + Adapt.info(" - changed keys: " + summarizeKeys(changedKeys)); + } + } + + public static void reportFallbackRewrite(File file, String source, String reason) { + String path = relativize(file); + String sourceTag = source == null || source.isBlank() ? "config" : source; + String reasonText = reason == null || reason.isBlank() ? "invalid/unsupported content" : reason; + Adapt.warn("Rewrote " + sourceTag + " [" + path + "] using fallback defaults (" + reasonText + ")."); + } + + private static List computeDiff(String before, String after) { + Map left = flattenForDiff(before); + Map right = flattenForDiff(after); + Set keys = new HashSet<>(left.keySet()); + keys.addAll(right.keySet()); + + List ordered = new ArrayList<>(keys); + ordered.sort(String::compareTo); + + List out = new ArrayList<>(); + for (String key : ordered) { + boolean inLeft = left.containsKey(key); + boolean inRight = right.containsKey(key); + String oldValue = inLeft ? left.get(key) : MISSING; + String newValue = inRight ? right.get(key) : REMOVED; + if (Objects.equals(oldValue, newValue)) { + continue; + } + + ChangeType type; + if (inLeft && !inRight) { + type = ChangeType.REMOVED; + } else if (!inLeft && inRight) { + type = ChangeType.ADDED; + } else { + type = ChangeType.CHANGED; + } + + out.add(new Change(type, key)); + } + + return out; + } + + private static Map flattenForDiff(String raw) { + JsonElement element = parseStructured(raw); + if (element == null) { + Map fallback = new HashMap<>(); + if (raw != null && !raw.isBlank()) { + fallback.put("$", normalize(raw)); + } + return fallback; + } + + Map out = new HashMap<>(); + flattenJson("$", element, out); + return out; + } + + private static void flattenJson(String path, JsonElement element, Map out) { + if (element == null || element.isJsonNull()) { + out.put(path, "null"); + return; + } + + if (element.isJsonPrimitive()) { + out.put(path, element.toString()); + return; + } + + if (element.isJsonArray()) { + if (element.getAsJsonArray().size() == 0) { + out.put(path, "[]"); + return; + } + + for (int i = 0; i < element.getAsJsonArray().size(); i++) { + flattenJson(path + "[" + i + "]", element.getAsJsonArray().get(i), out); + } + return; + } + + JsonObject object = element.getAsJsonObject(); + if (object.entrySet().isEmpty()) { + out.put(path, "{}"); + return; + } + + for (Map.Entry entry : object.entrySet()) { + flattenJson(path + "." + entry.getKey(), entry.getValue(), out); + } + } + + private static JsonElement parseStructured(String raw) { + if (raw == null || raw.isBlank()) { + return null; + } + + return ConfigFileSupport.parseToJsonElement(raw, null); + } + + private static String summarizeKeys(List keys) { + if (keys == null || keys.isEmpty()) { + return "(none)"; + } - try { - File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); - if (dataFolder == null) { - return file.getPath(); - } - return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); - } catch (Throwable ignored) { - return file.getPath(); - } - } - - private enum ChangeType { - REMOVED, - ADDED, - CHANGED + int shown = Math.min(MAX_KEYS_PER_CATEGORY, keys.size()); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < shown; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(keys.get(i)); } - private static class Change { - private final ChangeType type; - private final String key; + if (keys.size() > shown) { + sb.append(" (+").append(keys.size() - shown).append(" more)"); + } + + return sb.toString(); + } + + private static String normalize(String json) { + if (json == null) { + return null; + } + return ConfigFileSupport.normalize(json); + } + + private static String relativize(File file) { + if (file == null) { + return ""; + } + + try { + File dataFolder = Adapt.instance == null ? null : Adapt.instance.getDataFolder(); + if (dataFolder == null) { + return file.getPath(); + } + return dataFolder.toPath().relativize(file.toPath()).toString().replace(File.separatorChar, '/'); + } catch (Throwable ignored) { + return file.getPath(); + } + } + + private enum ChangeType { + REMOVED, + ADDED, + CHANGED + } + + private static class Change { + private final ChangeType type; + private final String key; - private Change(ChangeType type, String key) { - this.type = type; - this.key = key; - } + private Change(ChangeType type, String key) { + this.type = type; + this.key = key; } + } } diff --git a/src/main/java/art/arcane/adapt/util/project/config/Desc.java b/src/main/java/art/arcane/adapt/util/project/config/Desc.java index 436baa234..946cde605 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/Desc.java +++ b/src/main/java/art/arcane/adapt/util/project/config/Desc.java @@ -27,5 +27,5 @@ @Retention(RUNTIME) @Target({PARAMETER, TYPE, FIELD}) public @interface Desc { - String value(); + String value(); } diff --git a/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java index 906b57a33..f455ec2d5 100644 --- a/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java +++ b/src/main/java/art/arcane/adapt/util/project/config/TomlCodec.java @@ -11,458 +11,458 @@ import java.util.*; public final class TomlCodec { - private TomlCodec() { + private TomlCodec() { + } + + public static T fromToml(String raw, Class type) throws IOException { + try { + Object parsed = parseToml(raw); + String json = Json.toJson(parsed, false); + return Json.fromJson(json, type); + } catch (Throwable e) { + throw new IOException("Invalid toml", e); } - - public static T fromToml(String raw, Class type) throws IOException { - try { - Object parsed = parseToml(raw); - String json = Json.toJson(parsed, false); - return Json.fromJson(json, type); - } catch (Throwable e) { - throw new IOException("Invalid toml", e); - } + } + + public static JsonElement toJsonElement(String raw) throws IOException { + try { + Object parsed = parseToml(raw); + String json = Json.toJson(parsed, false); + return Json.fromJson(json, JsonElement.class); + } catch (Throwable e) { + throw new IOException("Invalid toml", e); } - - public static JsonElement toJsonElement(String raw) throws IOException { - try { - Object parsed = parseToml(raw); - String json = Json.toJson(parsed, false); - return Json.fromJson(json, JsonElement.class); - } catch (Throwable e) { - throw new IOException("Invalid toml", e); - } + } + + public static String toToml(Object object, String sourceTag) { + return new ReflectiveTomlWriter(sourceTag).write(object); + } + + public static String toToml(JsonElement element) { + Object data = Json.NORMAL.fromJson(element, Object.class); + return new GenericTomlWriter().write(data); + } + + private static Object parseToml(String raw) { + Toml toml = new Toml().read(raw == null ? "" : raw); + Map map = toml.toMap(); + if (map == null) { + return new LinkedHashMap(); } - - public static String toToml(Object object, String sourceTag) { - return new ReflectiveTomlWriter(sourceTag).write(object); + return map; + } + + private static List getSerializableFields(Class type) { + List out = new ArrayList<>(); + collectFields(type, out); + return out; + } + + private static void collectFields(Class type, List out) { + if (type == null || type == Object.class) { + return; } - public static String toToml(JsonElement element) { - Object data = Json.NORMAL.fromJson(element, Object.class); - return new GenericTomlWriter().write(data); + collectFields(type.getSuperclass(), out); + for (Field field : type.getDeclaredFields()) { + if (field.isSynthetic()) { + continue; + } + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { + continue; + } + field.setAccessible(true); + out.add(field); } + } - private static Object parseToml(String raw) { - Toml toml = new Toml().read(raw == null ? "" : raw); - Map map = toml.toMap(); - if (map == null) { - return new LinkedHashMap(); - } - return map; + private static Object getFieldValue(Field field, Object object) { + try { + return field.get(object); + } catch (Throwable ignored) { + return null; } + } - private static final class ReflectiveTomlWriter { - private final StringBuilder out = new StringBuilder(); - private final String sourceTag; - - private ReflectiveTomlWriter(String sourceTag) { - this.sourceTag = sourceTag == null ? "config" : sourceTag; - } - - private String write(Object root) { - if (root == null) { - return ""; - } - - out.append("# Adapt configuration - ").append(sourceTag).append('\n'); - out.append("# This file is canonicalized on load; comments and new keys may update automatically.\n"); - ConfigDescription desc = root.getClass().getAnnotation(ConfigDescription.class); - if (desc != null && !desc.value().isBlank()) { - out.append("#\n"); - out.append("# ").append(desc.value().strip()).append('\n'); - } - out.append('\n'); - writePojoSection("", root); - return normalize(out.toString()); - } + private static boolean isInlineValue(Object value) { + if (value == null) { + return true; + } - private void writePojoSection(String path, Object sectionObject) { - if (sectionObject == null) { - return; - } - - List fields = getSerializableFields(sectionObject.getClass()); - List deferred = new ArrayList<>(); - - for (Field field : fields) { - Object value = getFieldValue(field, sectionObject); - if (value == null) { - continue; - } - if (!ConfigDocumentation.shouldExposeField(sourceTag, path, field, value)) { - continue; - } - - if (isInlineValue(value)) { - writeFieldComments(path, field, value); - out.append(formatKey(field.getName())).append(" = ").append(formatInlineValue(value)).append('\n'); - } else { - deferred.add(field); - } - } - - for (Field field : deferred) { - Object value = getFieldValue(field, sectionObject); - if (value == null) { - continue; - } - if (!ConfigDocumentation.shouldExposeField(sourceTag, path, field, value)) { - continue; - } - - String childPath = joinPath(path, field.getName()); - if (value instanceof Map map) { - writeMapSection(childPath, map, field); - continue; - } - - writeSectionHeader(childPath, ConfigDocumentation.buildSectionComments(sourceTag, childPath)); - writePojoSection(childPath, value); - } - } + if (value instanceof String + || value instanceof Number + || value instanceof Boolean + || value instanceof Character + || value instanceof Enum) { + return true; + } - private void writeMapSection(String sectionPath, Map map, Field sourceField) { - writeSectionHeader(sectionPath, ConfigDocumentation.buildFieldComments(sourceTag, sectionPath, sourceField, map)); - if (map.isEmpty()) { - return; - } - - List> deferred = new ArrayList<>(); - for (Map.Entry entry : map.entrySet()) { - if (entry == null || entry.getKey() == null) { - continue; - } - - Object value = entry.getValue(); - if (value == null) { - continue; - } - - if (isInlineValue(value)) { - out.append(formatKey(String.valueOf(entry.getKey()))) - .append(" = ") - .append(formatInlineValue(value)) - .append('\n'); - } else { - deferred.add(entry); - } - } - - for (Map.Entry entry : deferred) { - Object value = entry.getValue(); - if (value == null || entry.getKey() == null) { - continue; - } - - String childPath = joinPath(sectionPath, String.valueOf(entry.getKey())); - if (value instanceof Map nested) { - writeSectionHeader(childPath, List.of()); - writeMapBody(childPath, nested); - } else { - writeSectionHeader(childPath, List.of()); - writePojoSection(childPath, value); - } - } + if (value.getClass().isArray()) { + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + Object v = Array.get(value, i); + if (!isInlineValue(v) || v instanceof Map || v instanceof Collection) { + return false; } + } + return true; + } - private void writeMapBody(String sectionPath, Map map) { - List> deferred = new ArrayList<>(); - for (Map.Entry entry : map.entrySet()) { - if (entry == null || entry.getKey() == null || entry.getValue() == null) { - continue; - } - if (isInlineValue(entry.getValue())) { - out.append(formatKey(String.valueOf(entry.getKey()))) - .append(" = ") - .append(formatInlineValue(entry.getValue())) - .append('\n'); - } else { - deferred.add(entry); - } - } - - for (Map.Entry entry : deferred) { - String childPath = joinPath(sectionPath, String.valueOf(entry.getKey())); - Object value = entry.getValue(); - if (value instanceof Map nested) { - writeSectionHeader(childPath, List.of()); - writeMapBody(childPath, nested); - } else { - writeSectionHeader(childPath, List.of()); - writePojoSection(childPath, value); - } - } + if (value instanceof Collection collection) { + for (Object item : collection) { + if (!isInlineValue(item) || item instanceof Map || item instanceof Collection) { + return false; } + } + return true; + } - private void writeFieldComments(String path, Field field, Object value) { - List comments = ConfigDocumentation.buildFieldComments(sourceTag, path, field, value); - for (String comment : comments) { - if (comment == null || comment.isBlank()) { - continue; - } - out.append("# ").append(comment.strip()).append('\n'); - } - } + return false; + } - private void writeSectionHeader(String path, List comments) { - if (path == null || path.isBlank()) { - return; - } - - if (!out.isEmpty() && out.charAt(out.length() - 1) != '\n') { - out.append('\n'); - } - if (!out.isEmpty()) { - out.append('\n'); - } - - for (String comment : comments) { - if (comment == null || comment.isBlank()) { - continue; - } - out.append("# ").append(comment.strip()).append('\n'); - } - out.append('[').append(renderPath(path)).append(']').append('\n'); - } + private static String formatInlineValue(Object value) { + if (value == null) { + return "\"\""; } - private static final class GenericTomlWriter { - private final StringBuilder out = new StringBuilder(); - private String lastTopLevelSection; + if (value instanceof String string) { + return '"' + escape(string) + '"'; + } + if (value instanceof Character c) { + return '"' + escape(String.valueOf(c)) + '"'; + } + if (value instanceof Enum enumValue) { + return '"' + escape(enumValue.name()) + '"'; + } + if (value instanceof Number || value instanceof Boolean) { + return String.valueOf(value); + } + if (value.getClass().isArray()) { + int length = Array.getLength(value); + List parts = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + parts.add(formatInlineValue(Array.get(value, i))); + } + return "[" + String.join(", ", parts) + "]"; + } + if (value instanceof Collection collection) { + List parts = new ArrayList<>(collection.size()); + for (Object item : collection) { + parts.add(formatInlineValue(item)); + } + return "[" + String.join(", ", parts) + "]"; + } - private String write(Object root) { - if (root instanceof Map map) { - writeMapSection("", map, 0); - return normalize(out.toString()); - } + return '"' + escape(String.valueOf(value)) + '"'; + } - out.append("value = ").append(formatInlineValue(root)).append('\n'); - return normalize(out.toString()); - } + private static String renderPath(String path) { + if (path == null || path.isBlank()) { + return ""; + } - private void writeMapSection(String path, Map map, int depth) { - if (!path.isBlank()) { - writeSectionHeader(path, depth); - } - - List> deferred = new ArrayList<>(); - String valueIndent = " ".repeat(Math.max(0, depth)); - for (Map.Entry entry : map.entrySet()) { - if (entry == null || entry.getKey() == null || entry.getValue() == null) { - continue; - } - - Object value = entry.getValue(); - if (isInlineValue(value)) { - out.append(valueIndent) - .append(formatKey(String.valueOf(entry.getKey()))) - .append(" = ") - .append(formatInlineValue(value)) - .append('\n'); - } else { - deferred.add(entry); - } - } - - for (Map.Entry entry : deferred) { - String childPath = joinPath(path, String.valueOf(entry.getKey())); - Object value = entry.getValue(); - if (value instanceof Map nested) { - writeMapSection(childPath, nested, depth + 1); - } else { - Map wrapper = new LinkedHashMap<>(); - wrapper.put("value", value); - writeMapSection(childPath, wrapper, depth + 1); - } - } - } + String[] parts = path.split("\\."); + List rendered = new ArrayList<>(parts.length); + for (String part : parts) { + rendered.add(formatKey(part)); + } + return String.join(".", rendered); + } - private void writeSectionHeader(String path, int depth) { - String topLevel = topLevelSegment(path); - if (depth == 1 && (lastTopLevelSection == null || !lastTopLevelSection.equals(topLevel))) { - if (!out.isEmpty()) { - out.append('\n'); - } - out.append("# ").append(topLevel).append('\n'); - lastTopLevelSection = topLevel; - } else if (!out.isEmpty()) { - out.append('\n'); - } - - out.append('[').append(renderPath(path)).append(']').append('\n'); - } + private static String formatKey(String key) { + if (key == null || key.isBlank()) { + return "\"\""; + } - private String topLevelSegment(String path) { - if (path == null || path.isBlank()) { - return ""; - } + if (key.matches("[A-Za-z0-9_-]+")) { + return key; + } + return '"' + escape(key) + '"'; + } - int dot = path.indexOf('.'); - if (dot == -1) { - return path; - } - return path.substring(0, dot); - } + private static String joinPath(String a, String b) { + if (a == null || a.isBlank()) { + return b; + } + if (b == null || b.isBlank()) { + return a; + } + return a + "." + b; + } + + private static String escape(String input) { + return input + .replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + private static String normalize(String raw) { + if (raw == null) { + return ""; } - private static List getSerializableFields(Class type) { - List out = new ArrayList<>(); - collectFields(type, out); - return out; + String normalized = raw.replace("\r\n", "\n").stripTrailing(); + if (!normalized.isEmpty()) { + normalized += "\n"; } + return normalized; + } - private static void collectFields(Class type, List out) { - if (type == null || type == Object.class) { - return; - } + private static final class ReflectiveTomlWriter { + private final StringBuilder out = new StringBuilder(); + private final String sourceTag; - collectFields(type.getSuperclass(), out); - for (Field field : type.getDeclaredFields()) { - if (field.isSynthetic()) { - continue; - } - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { - continue; - } - field.setAccessible(true); - out.add(field); - } + private ReflectiveTomlWriter(String sourceTag) { + this.sourceTag = sourceTag == null ? "config" : sourceTag; } - private static Object getFieldValue(Field field, Object object) { - try { - return field.get(object); - } catch (Throwable ignored) { - return null; - } + private String write(Object root) { + if (root == null) { + return ""; + } + + out.append("# Adapt configuration - ").append(sourceTag).append('\n'); + out.append("# This file is canonicalized on load; comments and new keys may update automatically.\n"); + ConfigDescription desc = root.getClass().getAnnotation(ConfigDescription.class); + if (desc != null && !desc.value().isBlank()) { + out.append("#\n"); + out.append("# ").append(desc.value().strip()).append('\n'); + } + out.append('\n'); + writePojoSection("", root); + return normalize(out.toString()); } - private static boolean isInlineValue(Object value) { + private void writePojoSection(String path, Object sectionObject) { + if (sectionObject == null) { + return; + } + + List fields = getSerializableFields(sectionObject.getClass()); + List deferred = new ArrayList<>(); + + for (Field field : fields) { + Object value = getFieldValue(field, sectionObject); if (value == null) { - return true; + continue; + } + if (!ConfigDocumentation.shouldExposeField(sourceTag, path, field, value)) { + continue; } - if (value instanceof String - || value instanceof Number - || value instanceof Boolean - || value instanceof Character - || value instanceof Enum) { - return true; + if (isInlineValue(value)) { + writeFieldComments(path, field, value); + out.append(formatKey(field.getName())).append(" = ").append(formatInlineValue(value)).append('\n'); + } else { + deferred.add(field); } + } - if (value.getClass().isArray()) { - int length = Array.getLength(value); - for (int i = 0; i < length; i++) { - Object v = Array.get(value, i); - if (!isInlineValue(v) || v instanceof Map || v instanceof Collection) { - return false; - } - } - return true; + for (Field field : deferred) { + Object value = getFieldValue(field, sectionObject); + if (value == null) { + continue; + } + if (!ConfigDocumentation.shouldExposeField(sourceTag, path, field, value)) { + continue; } - if (value instanceof Collection collection) { - for (Object item : collection) { - if (!isInlineValue(item) || item instanceof Map || item instanceof Collection) { - return false; - } - } - return true; + String childPath = joinPath(path, field.getName()); + if (value instanceof Map map) { + writeMapSection(childPath, map, field); + continue; } - return false; + writeSectionHeader(childPath, ConfigDocumentation.buildSectionComments(sourceTag, childPath)); + writePojoSection(childPath, value); + } } - private static String formatInlineValue(Object value) { - if (value == null) { - return "\"\""; - } + private void writeMapSection(String sectionPath, Map map, Field sourceField) { + writeSectionHeader(sectionPath, ConfigDocumentation.buildFieldComments(sourceTag, sectionPath, sourceField, map)); + if (map.isEmpty()) { + return; + } - if (value instanceof String string) { - return '"' + escape(string) + '"'; + List> deferred = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + if (entry == null || entry.getKey() == null) { + continue; } - if (value instanceof Character c) { - return '"' + escape(String.valueOf(c)) + '"'; - } - if (value instanceof Enum enumValue) { - return '"' + escape(enumValue.name()) + '"'; + + Object value = entry.getValue(); + if (value == null) { + continue; } - if (value instanceof Number || value instanceof Boolean) { - return String.valueOf(value); + + if (isInlineValue(value)) { + out.append(formatKey(String.valueOf(entry.getKey()))) + .append(" = ") + .append(formatInlineValue(value)) + .append('\n'); + } else { + deferred.add(entry); } - if (value.getClass().isArray()) { - int length = Array.getLength(value); - List parts = new ArrayList<>(length); - for (int i = 0; i < length; i++) { - parts.add(formatInlineValue(Array.get(value, i))); - } - return "[" + String.join(", ", parts) + "]"; + } + + for (Map.Entry entry : deferred) { + Object value = entry.getValue(); + if (value == null || entry.getKey() == null) { + continue; } - if (value instanceof Collection collection) { - List parts = new ArrayList<>(collection.size()); - for (Object item : collection) { - parts.add(formatInlineValue(item)); - } - return "[" + String.join(", ", parts) + "]"; + + String childPath = joinPath(sectionPath, String.valueOf(entry.getKey())); + if (value instanceof Map nested) { + writeSectionHeader(childPath, List.of()); + writeMapBody(childPath, nested); + } else { + writeSectionHeader(childPath, List.of()); + writePojoSection(childPath, value); } + } + } - return '"' + escape(String.valueOf(value)) + '"'; + private void writeMapBody(String sectionPath, Map map) { + List> deferred = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + if (entry == null || entry.getKey() == null || entry.getValue() == null) { + continue; + } + if (isInlineValue(entry.getValue())) { + out.append(formatKey(String.valueOf(entry.getKey()))) + .append(" = ") + .append(formatInlineValue(entry.getValue())) + .append('\n'); + } else { + deferred.add(entry); + } + } + + for (Map.Entry entry : deferred) { + String childPath = joinPath(sectionPath, String.valueOf(entry.getKey())); + Object value = entry.getValue(); + if (value instanceof Map nested) { + writeSectionHeader(childPath, List.of()); + writeMapBody(childPath, nested); + } else { + writeSectionHeader(childPath, List.of()); + writePojoSection(childPath, value); + } + } } - private static String renderPath(String path) { - if (path == null || path.isBlank()) { - return ""; + private void writeFieldComments(String path, Field field, Object value) { + List comments = ConfigDocumentation.buildFieldComments(sourceTag, path, field, value); + for (String comment : comments) { + if (comment == null || comment.isBlank()) { + continue; } + out.append("# ").append(comment.strip()).append('\n'); + } + } - String[] parts = path.split("\\."); - List rendered = new ArrayList<>(parts.length); - for (String part : parts) { - rendered.add(formatKey(part)); - } - return String.join(".", rendered); + private void writeSectionHeader(String path, List comments) { + if (path == null || path.isBlank()) { + return; + } + + if (!out.isEmpty() && out.charAt(out.length() - 1) != '\n') { + out.append('\n'); + } + if (!out.isEmpty()) { + out.append('\n'); + } + + for (String comment : comments) { + if (comment == null || comment.isBlank()) { + continue; + } + out.append("# ").append(comment.strip()).append('\n'); + } + out.append('[').append(renderPath(path)).append(']').append('\n'); } + } - private static String formatKey(String key) { - if (key == null || key.isBlank()) { - return "\"\""; - } + private static final class GenericTomlWriter { + private final StringBuilder out = new StringBuilder(); + private String lastTopLevelSection; - if (key.matches("[A-Za-z0-9_-]+")) { - return key; - } - return '"' + escape(key) + '"'; - } + private String write(Object root) { + if (root instanceof Map map) { + writeMapSection("", map, 0); + return normalize(out.toString()); + } - private static String joinPath(String a, String b) { - if (a == null || a.isBlank()) { - return b; - } - if (b == null || b.isBlank()) { - return a; - } - return a + "." + b; + out.append("value = ").append(formatInlineValue(root)).append('\n'); + return normalize(out.toString()); } - private static String escape(String input) { - return input - .replace("\\", "\\\\") - .replace("\"", "\\\"") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("\t", "\\t"); + private void writeMapSection(String path, Map map, int depth) { + if (!path.isBlank()) { + writeSectionHeader(path, depth); + } + + List> deferred = new ArrayList<>(); + String valueIndent = " ".repeat(Math.max(0, depth)); + for (Map.Entry entry : map.entrySet()) { + if (entry == null || entry.getKey() == null || entry.getValue() == null) { + continue; + } + + Object value = entry.getValue(); + if (isInlineValue(value)) { + out.append(valueIndent) + .append(formatKey(String.valueOf(entry.getKey()))) + .append(" = ") + .append(formatInlineValue(value)) + .append('\n'); + } else { + deferred.add(entry); + } + } + + for (Map.Entry entry : deferred) { + String childPath = joinPath(path, String.valueOf(entry.getKey())); + Object value = entry.getValue(); + if (value instanceof Map nested) { + writeMapSection(childPath, nested, depth + 1); + } else { + Map wrapper = new LinkedHashMap<>(); + wrapper.put("value", value); + writeMapSection(childPath, wrapper, depth + 1); + } + } } - private static String normalize(String raw) { - if (raw == null) { - return ""; + private void writeSectionHeader(String path, int depth) { + String topLevel = topLevelSegment(path); + if (depth == 1 && (lastTopLevelSection == null || !lastTopLevelSection.equals(topLevel))) { + if (!out.isEmpty()) { + out.append('\n'); } + out.append("# ").append(topLevel).append('\n'); + lastTopLevelSection = topLevel; + } else if (!out.isEmpty()) { + out.append('\n'); + } - String normalized = raw.replace("\r\n", "\n").stripTrailing(); - if (!normalized.isEmpty()) { - normalized += "\n"; - } - return normalized; + out.append('[').append(renderPath(path)).append(']').append('\n'); + } + + private String topLevelSegment(String path) { + if (path == null || path.isBlank()) { + return ""; + } + + int dot = path.indexOf('.'); + if (dot == -1) { + return path; + } + return path.substring(0, dot); } + } } diff --git a/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java index 30d888255..241abb35d 100644 --- a/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java +++ b/src/main/java/art/arcane/adapt/util/project/redis/RedisSync.java @@ -23,57 +23,57 @@ @Log public class RedisSync implements AutoCloseable { - private final RedisClient redisClient; - private final RedisPubSubReactiveCommands pubSub; - private final Cache dataCache = Caffeine.newBuilder() - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(); + private final RedisClient redisClient; + private final RedisPubSubReactiveCommands pubSub; + private final Cache dataCache = Caffeine.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(); - public RedisSync() { - if (!AdaptConfig.get().isUseRedis() || !AdaptConfig.get().isUseSql()) { - this.redisClient = null; - this.pubSub = null; - return; - } - - this.redisClient = AdaptConfig.get().getRedis().createClient(); - this.pubSub = redisClient.connectPubSub(Codec.INSTANCE).reactive(); - pubSub.subscribe("Adapt:data").subscribe(); - pubSub.observeChannels().doOnNext(this::update).subscribe(); + public RedisSync() { + if (!AdaptConfig.get().isUseRedis() || !AdaptConfig.get().isUseSql()) { + this.redisClient = null; + this.pubSub = null; + return; } - private void update(@NotNull ChannelMessage<@NotNull String, @Nullable Message> channelMessage) { - if (!channelMessage.getChannel().equals("Adapt:data")) return; - Message raw = channelMessage.getMessage(); - if (raw instanceof DataMessage message) { - Adapt.verbose("Received player data for " + message.uuid()); - dataCache.put(message.uuid(), message.json()); - } else if (raw instanceof DataRequest message) { - Adapt.instance.getAdaptServer() - .getPlayerData(message.uuid()) - .map(data -> data.toJson(false)) - .ifPresent(data -> publish(message.uuid(), data)); - } - } + this.redisClient = AdaptConfig.get().getRedis().createClient(); + this.pubSub = redisClient.connectPubSub(Codec.INSTANCE).reactive(); + pubSub.subscribe("Adapt:data").subscribe(); + pubSub.observeChannels().doOnNext(this::update).subscribe(); + } - public void publish(@NonNull UUID uuid, @NonNull String playerData) { - if (pubSub == null) return; - Adapt.verbose("Publishing player data for " + uuid); - pubSub.publish("Adapt:data", new DataMessage(uuid, playerData)) - .subscribe() - .dispose(); + private void update(@NotNull ChannelMessage<@NotNull String, @Nullable Message> channelMessage) { + if (!channelMessage.getChannel().equals("Adapt:data")) return; + Message raw = channelMessage.getMessage(); + if (raw instanceof DataMessage message) { + Adapt.verbose("Received player data for " + message.uuid()); + dataCache.put(message.uuid(), message.json()); + } else if (raw instanceof DataRequest message) { + Adapt.instance.getAdaptServer() + .getPlayerData(message.uuid()) + .map(data -> data.toJson(false)) + .ifPresent(data -> publish(message.uuid(), data)); } + } - @NonNull - public Optional cachedData(@NonNull UUID uuid) { - if (pubSub == null) return Optional.empty(); - return Optional.ofNullable(dataCache.getIfPresent(uuid)) - .map(PlayerData::fromJson); - } + public void publish(@NonNull UUID uuid, @NonNull String playerData) { + if (pubSub == null) return; + Adapt.verbose("Publishing player data for " + uuid); + pubSub.publish("Adapt:data", new DataMessage(uuid, playerData)) + .subscribe() + .dispose(); + } - @Override - public void close() throws Exception { - if (redisClient != null) - redisClient.close(); - } + @NonNull + public Optional cachedData(@NonNull UUID uuid) { + if (pubSub == null) return Optional.empty(); + return Optional.ofNullable(dataCache.getIfPresent(uuid)) + .map(PlayerData::fromJson); + } + + @Override + public void close() throws Exception { + if (redisClient != null) + redisClient.close(); + } } diff --git a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java index c033c6224..4bf25513e 100644 --- a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java +++ b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java @@ -28,61 +28,61 @@ public class SecretSplash { - @Getter - public static KList secretSplash = new KList<>( - "\n" + C.BLUE + " ⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝ \n" + - C.BLUE + " ⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇ \n" + - C.BLUE + " ⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀ " + C.DARK_RED + "Adapt\n" + - C.BLUE + " ⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + - C.BLUE + " ⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.BLUE + " ⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + - C.BLUE + " ⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀ ⠀\n" + - C.BLUE + " ⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀ ⠀⠀⠀\n" + - C.BLUE + " ⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀\n" + - C.BLUE + " ⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀\n" + - C.BLUE + " ⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀No Splash Screen?\n" + - C.BLUE + " ⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀\n" + - C.BLUE + " ⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀", + @Getter + public static KList secretSplash = new KList<>( + "\n" + C.BLUE + " ⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝ \n" + + C.BLUE + " ⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇ \n" + + C.BLUE + " ⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀ " + C.DARK_RED + "Adapt\n" + + C.BLUE + " ⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + + C.BLUE + " ⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.BLUE + " ⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + + C.BLUE + " ⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀ ⠀\n" + + C.BLUE + " ⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀ ⠀⠀⠀\n" + + C.BLUE + " ⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀\n" + + C.BLUE + " ⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀\n" + + C.BLUE + " ⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀No Splash Screen?\n" + + C.BLUE + " ⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀\n" + + C.BLUE + " ⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀", - "\n :::. :::::::-. :::. ::::::::::. :::::::::::: \n" + - " ;;`;; ;;, `';, ;;`;; `;;;```.;;;;;;;;;;;'''' " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + - " ,[[ '[[, `[[ [[ ,[[ '[[, `]]nnn]]' [[ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - " $$$$$$$$ $$, $$ $$$$$$$$ $$$\"\" $$ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + - " 888 888,888_,o8P' 888 888,888o 88, \n" + - " YMM \"\"` MMMMP\"` YMM \"\"` YMMMb MMM \n", + "\n :::. :::::::-. :::. ::::::::::. :::::::::::: \n" + + " ;;`;; ;;, `';, ;;`;; `;;;```.;;;;;;;;;;;'''' " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + + " ,[[ '[[, `[[ [[ ,[[ '[[, `]]nnn]]' [[ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + " $$$$$$$$ $$, $$ $$$$$$$$ $$$\"\" $$ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + + " 888 888,888_,o8P' 888 888,888o 88, \n" + + " YMM \"\"` MMMMP\"` YMM \"\"` YMMMb MMM \n", - C.GRAY + "\n ██░ ██ ▓█████ ██▓ ██▓███ ███▄ ▄███▓▓█████ \n" + - C.GRAY + "▓██░ ██▒▓█ ▀ ▓██▒ ▓██░ ██▒ ▓██▒▀█▀ ██▒▓█ ▀ " + C.DARK_RED + "Adapt \n" + - C.GRAY + "▒██▀▀██░▒███ ▒██░ ▓██░ ██▓▒ ▓██ ▓██░▒███ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + - C.GRAY + "░▓█ ░██ ▒▓█ ▄ ▒██░ ▒██▄█▓▒ ▒ ▒██ ▒██ ▒▓█ ▄ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.GRAY + "░▓█▒░██▓░▒████▒░██████▒▒██▒ ░ ░ ▒██▒ ░██▒░▒████▒ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + - C.GRAY + " ▒ ░░▒░▒░░ ▒░ ░░ ▒░▓ ░▒▓▒░ ░ ░ ░ ▒░ ░ ░░░ ▒░ ░ ", + C.GRAY + "\n ██░ ██ ▓█████ ██▓ ██▓███ ███▄ ▄███▓▓█████ \n" + + C.GRAY + "▓██░ ██▒▓█ ▀ ▓██▒ ▓██░ ██▒ ▓██▒▀█▀ ██▒▓█ ▀ " + C.DARK_RED + "Adapt \n" + + C.GRAY + "▒██▀▀██░▒███ ▒██░ ▓██░ ██▓▒ ▓██ ▓██░▒███ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + + C.GRAY + "░▓█ ░██ ▒▓█ ▄ ▒██░ ▒██▄█▓▒ ▒ ▒██ ▒██ ▒▓█ ▄ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.GRAY + "░▓█▒░██▓░▒████▒░██████▒▒██▒ ░ ░ ▒██▒ ░██▒░▒████▒ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + + C.GRAY + " ▒ ░░▒░▒░░ ▒░ ░░ ▒░▓ ░▒▓▒░ ░ ░ ░ ▒░ ░ ░░░ ▒░ ░ ", - C.GRAY + "⠀⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣶⣿⣿⣷⣶⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣾⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀ ⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡟⠁⣰⣿⣿⣿⡿⠿⠻⠿⣿⣿⣿⣿⣧⠀ ⠀⠀⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⣴⣿⣿⣿⠉⠀⠀⠀⠀⠀⠈⢻⣿⣿⣇⠀⠀⠀ \n" + - C.GRAY + "⠀⠀⠀⠀⢀⣠⣼⣿⣿⡏⠀⢠⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⡀⠀ ⠀" + C.DARK_RED + "Adapt \n" + - C.GRAY + "⠀⠀⠀⣰⣿⣿⣿⣿⣿⡇⠀⢸⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀ ⠀" + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + - C.GRAY + "⠀⠀⢰⣿⣿⡿⣿⣿⣿⡇⠀⠘⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⢀⣸⣿⣿⣿⠁⠀ ⠀" + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.GRAY + "⠀⠀⣿⣿⣿⠁⣿⣿⣿⡇⠀⠀⠻⣿⣿⣿⣷⣶⣶⣶⣶⣶⣿⣿⣿⣿⠃⠀ ⠀⠀" + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + - C.GRAY + "⠀⢰⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀ ⠀\n" + - C.GRAY + "⠀⢸⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠉⢉⣿⣿⠀⠀⠀⠀⠀ ⠀\n" + - C.GRAY + "⠀⢸⣿⣿⣇⠀⣿⣿⣿⠀⠀⠀⠀⠀⢀⣤⣤⣤⡀⠀⠀⢸⣿⣿⣿⣷⣦⠀ ⠀⠀\n" + - C.GRAY + "⠀⠀⢻⣿⣿⣶⣿⣿⣿⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣦⡀⠀⠉⠉⠻⣿⣿⡇⠀ ⠀ \n" + - C.GRAY + "⠀⠀⠀⠛⠿⣿⣿⣿⣿⣷⣤⡀⠀⠀⠀⠀⠈⠹⣿⣿⣇⣀⠀⣠⣾⣿⣿⡇⠀⠀ ⠀⠀" + C.DARK_RED + "sus\n " + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣦⣤⣤⣤⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀ ⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⢿⣿⣿⣿⣿⣿⣿⠿⠋⠉⠛⠋⠉⠉⠁⠀⠀⠀ ⠀\n" + - C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁" + C.GRAY + "⠀⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣶⣿⣿⣷⣶⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣾⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀ ⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⡟⠁⣰⣿⣿⣿⡿⠿⠻⠿⣿⣿⣿⣿⣧⠀ ⠀⠀⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⣴⣿⣿⣿⠉⠀⠀⠀⠀⠀⠈⢻⣿⣿⣇⠀⠀⠀ \n" + + C.GRAY + "⠀⠀⠀⠀⢀⣠⣼⣿⣿⡏⠀⢠⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⡀⠀ ⠀" + C.DARK_RED + "Adapt \n" + + C.GRAY + "⠀⠀⠀⣰⣿⣿⣿⣿⣿⡇⠀⢸⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀ ⠀" + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + + C.GRAY + "⠀⠀⢰⣿⣿⡿⣿⣿⣿⡇⠀⠘⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⢀⣸⣿⣿⣿⠁⠀ ⠀" + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.GRAY + "⠀⠀⣿⣿⣿⠁⣿⣿⣿⡇⠀⠀⠻⣿⣿⣿⣷⣶⣶⣶⣶⣶⣿⣿⣿⣿⠃⠀ ⠀⠀" + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + + C.GRAY + "⠀⢰⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀ ⠀\n" + + C.GRAY + "⠀⢸⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠉⢉⣿⣿⠀⠀⠀⠀⠀ ⠀\n" + + C.GRAY + "⠀⢸⣿⣿⣇⠀⣿⣿⣿⠀⠀⠀⠀⠀⢀⣤⣤⣤⡀⠀⠀⢸⣿⣿⣿⣷⣦⠀ ⠀⠀\n" + + C.GRAY + "⠀⠀⢻⣿⣿⣶⣿⣿⣿⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣦⡀⠀⠉⠉⠻⣿⣿⡇⠀ ⠀ \n" + + C.GRAY + "⠀⠀⠀⠛⠿⣿⣿⣿⣿⣷⣤⡀⠀⠀⠀⠀⠈⠹⣿⣿⣇⣀⠀⣠⣾⣿⣿⡇⠀⠀ ⠀⠀" + C.DARK_RED + "sus\n " + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣦⣤⣤⣤⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀ ⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⢿⣿⣿⣿⣿⣿⣿⠿⠋⠉⠛⠋⠉⠉⠁⠀⠀⠀ ⠀\n" + + C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠁" - ); + ); - public static String randomString7() { - byte[] array = new byte[7]; // length is bounded by 7 - new Random().nextBytes(array); - String generatedString = new String(array, StandardCharsets.UTF_8); + public static String randomString7() { + byte[] array = new byte[7]; // length is bounded by 7 + new Random().nextBytes(array); + String generatedString = new String(array, StandardCharsets.UTF_8); - return generatedString; - } + return generatedString; + } } diff --git a/src/main/java/art/arcane/adapt/util/reflect/Reflect.java b/src/main/java/art/arcane/adapt/util/reflect/Reflect.java index 7e17405e9..b8a33aaa0 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/Reflect.java +++ b/src/main/java/art/arcane/adapt/util/reflect/Reflect.java @@ -9,63 +9,65 @@ public class Reflect { - @NotNull - public static Optional> getClass(@NotNull String className) { - try { - return Optional.of(Class.forName(className)); - } catch (ClassNotFoundException e) { - return Optional.empty(); - } + @NotNull + public static Optional> getClass(@NotNull String className) { + try { + return Optional.of(Class.forName(className)); + } catch (ClassNotFoundException e) { + return Optional.empty(); } + } - @NotNull - public static > Optional getEnum(@NotNull Class enumClass, @NotNull String enumName) { - try { - return Optional.of(Enum.valueOf(enumClass, enumName)); - } catch (IllegalArgumentException e) { - return Optional.empty(); - } + @NotNull + public static > Optional getEnum(@NotNull Class enumClass, @NotNull String enumName) { + try { + return Optional.of(Enum.valueOf(enumClass, enumName)); + } catch (IllegalArgumentException e) { + return Optional.empty(); } + } - @NotNull - public static > E getEnum(@NotNull Class enumClass, @NotNull String... enumNames) { - if (enumNames.length == 0) throw new IllegalArgumentException("Need at least one enum name"); - for (String enumName : enumNames) { - Optional optionalEnum = getEnum(enumClass, enumName); - if (optionalEnum.isPresent()) return optionalEnum.get(); - } - throw new IllegalArgumentException("No Enum found for names " + Arrays.toString(enumNames)); + @NotNull + public static > E getEnum(@NotNull Class enumClass, @NotNull String... enumNames) { + if (enumNames.length == 0) + throw new IllegalArgumentException("Need at least one enum name"); + for (String enumName : enumNames) { + Optional optionalEnum = getEnum(enumClass, enumName); + if (optionalEnum.isPresent()) return optionalEnum.get(); } + throw new IllegalArgumentException("No Enum found for names " + Arrays.toString(enumNames)); + } - @NotNull - public static Optional getMethod(@NotNull Class clazz, @NotNull String methodName, @NotNull Class... parameterTypes) { - try { - return Optional.of(clazz.getDeclaredMethod(methodName, parameterTypes)); - } catch (NoSuchMethodException e) { - return Optional.empty(); - } + @NotNull + public static Optional getMethod(@NotNull Class clazz, @NotNull String methodName, @NotNull Class... parameterTypes) { + try { + return Optional.of(clazz.getDeclaredMethod(methodName, parameterTypes)); + } catch (NoSuchMethodException e) { + return Optional.empty(); } + } - public static E getField(Class clazz, @NotNull String... fieldNames) { - if (fieldNames.length == 0) throw new IllegalArgumentException("Need at least one field name"); - for (String fieldName : fieldNames) { - Optional optionalField = getField(clazz, fieldName); - if (optionalField.isPresent()) { - try { - return (E) optionalField.get().get(null); - } catch (IllegalAccessException ignored) { - } - } + public static E getField(Class clazz, @NotNull String... fieldNames) { + if (fieldNames.length == 0) + throw new IllegalArgumentException("Need at least one field name"); + for (String fieldName : fieldNames) { + Optional optionalField = getField(clazz, fieldName); + if (optionalField.isPresent()) { + try { + return (E) optionalField.get().get(null); + } catch (IllegalAccessException ignored) { } - throw new IllegalArgumentException("No Field found for names " + Arrays.toString(fieldNames)); + } } + throw new IllegalArgumentException("No Field found for names " + Arrays.toString(fieldNames)); + } - @NotNull - public static Optional getField(@NotNull Class clazz, @NotNull String fieldName) { - try { - return Optional.of(clazz.getDeclaredField(fieldName)); - } catch (NoSuchFieldException e) { - return Optional.empty(); - } + @NotNull + public static Optional getField(@NotNull Class clazz, @NotNull String fieldName) { + try { + return Optional.of(clazz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException e) { + return Optional.empty(); } + } } diff --git a/src/main/java/art/arcane/adapt/util/reflect/WrappedField.java b/src/main/java/art/arcane/adapt/util/reflect/WrappedField.java index 83fbd1a65..8b7273d74 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/WrappedField.java +++ b/src/main/java/art/arcane/adapt/util/reflect/WrappedField.java @@ -6,37 +6,37 @@ public class WrappedField { - private final Field field; - - public WrappedField(Class origin, String methodName) { - Field f = null; - try { - f = origin.getDeclaredField(methodName); - f.setAccessible(true); - } catch (NoSuchFieldException e) { - Adapt.error("Failed to created WrappedField %s#%s: %s%s".formatted(origin.getSimpleName(), methodName, e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); - } - this.field = f; + private final Field field; + + public WrappedField(Class origin, String methodName) { + Field f = null; + try { + f = origin.getDeclaredField(methodName); + f.setAccessible(true); + } catch (NoSuchFieldException e) { + Adapt.error("Failed to created WrappedField %s#%s: %s%s".formatted(origin.getSimpleName(), methodName, e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); } + this.field = f; + } - public T get() { - return get(null); - } + public T get() { + return get(null); + } - public T get(C instance) { - if (field == null) { - return null; - } - - try { - return (T) field.get(instance); - } catch (IllegalAccessException e) { - Adapt.error("Failed to get WrappedField %s#%s: %s%s".formatted(field.getDeclaringClass().getSimpleName(), field.getName(), e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); - return null; - } + public T get(C instance) { + if (field == null) { + return null; } - public boolean hasFailed() { - return field == null; + try { + return (T) field.get(instance); + } catch (IllegalAccessException e) { + Adapt.error("Failed to get WrappedField %s#%s: %s%s".formatted(field.getDeclaringClass().getSimpleName(), field.getName(), e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); + return null; } + } + + public boolean hasFailed() { + return field == null; + } } diff --git a/src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java b/src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java index 669432670..64850db82 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java +++ b/src/main/java/art/arcane/adapt/util/reflect/WrappedReturningMethod.java @@ -8,33 +8,33 @@ public final class WrappedReturningMethod { - private final Method method; - - public WrappedReturningMethod(Class origin, String methodName, Class... paramTypes) { - Method m = null; - try { - m = origin.getDeclaredMethod(methodName, paramTypes); - m.setAccessible(true); - } catch (NoSuchMethodException e) { - Adapt.error("Failed to created WrappedMethod %s#%s: %s%s".formatted(origin.getSimpleName(), methodName, e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); - } - this.method = m; + private final Method method; + + public WrappedReturningMethod(Class origin, String methodName, Class... paramTypes) { + Method m = null; + try { + m = origin.getDeclaredMethod(methodName, paramTypes); + m.setAccessible(true); + } catch (NoSuchMethodException e) { + Adapt.error("Failed to created WrappedMethod %s#%s: %s%s".formatted(origin.getSimpleName(), methodName, e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); } + this.method = m; + } - public R invoke(Object... args) { - return invoke(null, args); + public R invoke(Object... args) { + return invoke(null, args); + } + + public R invoke(C instance, Object... args) { + if (method == null) { + return null; } - public R invoke(C instance, Object... args) { - if (method == null) { - return null; - } - - try { - return (R) method.invoke(instance, args); - } catch (InvocationTargetException | IllegalAccessException e) { - Adapt.error("Failed to invoke WrappedMethod %s#%s: %s%s".formatted(method.getDeclaringClass().getSimpleName(), method.getName(), e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); - return null; - } + try { + return (R) method.invoke(instance, args); + } catch (InvocationTargetException | IllegalAccessException e) { + Adapt.error("Failed to invoke WrappedMethod %s#%s: %s%s".formatted(method.getDeclaringClass().getSimpleName(), method.getName(), e.getClass().getSimpleName(), e.getMessage().equals("") ? "" : " | " + e.getMessage())); + return null; } + } } \ No newline at end of file diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java b/src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java index 36af33d8a..33f65b84e 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/ReflectiveEvents.java @@ -25,98 +25,98 @@ import java.util.stream.Collectors; public class ReflectiveEvents { - private static final KMap, Class> EVENTS = new KMap<>(); - private static final KMap, HandlerList> HANDLERS = new KMap<>(); - - static { - register(EntityMountEvent.class, "org.bukkit.event.entity.EntityMountEvent", "org.spigotmc.event.entity.EntityMountEvent"); - register(EntityDismountEvent.class, "org.bukkit.event.entity.EntityDismountEvent", "org.spigotmc.event.entity.EntityDismountEvent"); - register(EndermanAttackPlayerEvent.class, "com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent"); - } - - public static void register(@NonNull Listener listener) { - if (Adapt.bad) return; - - Arrays.stream(listener.getClass().getDeclaredMethods()) - .filter(method -> method.isAnnotationPresent(ReflectiveHandler.class)) - .filter(method -> !Modifier.isStatic(method.getModifiers())) - .filter(method -> method.getParameterCount() == 1) - .filter(method -> Event.class.isAssignableFrom(method.getParameterTypes()[0])) - .collect(Collectors.toMap(method -> HANDLERS.get(method.getParameterTypes()[0]), - method -> { - try { - if (!Modifier.isPublic(method.getModifiers())) { - method.setAccessible(true); - } - } catch (Throwable e) { - return new KList(); - } - - - Class eventClass = method.getParameterTypes()[0]; - Class bukkitClass = EVENTS.get(eventClass); - ReflectiveHandler handler = method.getAnnotation(ReflectiveHandler.class); - - EventExecutor executor = (obj, event) -> { - if (!bukkitClass.isAssignableFrom(event.getClass())) - return; - - try { - method.invoke(obj, newProxy(obj, eventClass)); - } catch (InvocationTargetException e) { - throw new EventException(e.getCause()); - } catch (Throwable e) { - throw new EventException(e); - } - }; - - return new KList<>(new RegisteredListener(listener, executor, handler.priority(), Adapt.instance, handler.ignoreCancelled())); - }, KList::add)) - .forEach((handlerList, registeredListeners) -> { - if (handlerList == null) return; - handlerList.registerAll(registeredListeners); - }); - } - - public static void register(Class eventInterface, String... classes) { - for (String clazz : classes) { - Optional> opt = Reflect.getClass(clazz); - if (opt.isEmpty()) - continue; - - var handlerList = getHandlerList(opt.get()); - if (handlerList == null) { - Adapt.warn("Event class does not contain HandlerList: " + clazz); - continue; - } - - EVENTS.put(eventInterface, opt.get()); - HANDLERS.put(opt.get(), handlerList); - return; - } + private static final KMap, Class> EVENTS = new KMap<>(); + private static final KMap, HandlerList> HANDLERS = new KMap<>(); + + static { + register(EntityMountEvent.class, "org.bukkit.event.entity.EntityMountEvent", "org.spigotmc.event.entity.EntityMountEvent"); + register(EntityDismountEvent.class, "org.bukkit.event.entity.EntityDismountEvent", "org.spigotmc.event.entity.EntityDismountEvent"); + register(EndermanAttackPlayerEvent.class, "com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent"); + } + + public static void register(@NonNull Listener listener) { + if (Adapt.bad) return; + + Arrays.stream(listener.getClass().getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(ReflectiveHandler.class)) + .filter(method -> !Modifier.isStatic(method.getModifiers())) + .filter(method -> method.getParameterCount() == 1) + .filter(method -> Event.class.isAssignableFrom(method.getParameterTypes()[0])) + .collect(Collectors.toMap(method -> HANDLERS.get(method.getParameterTypes()[0]), + method -> { + try { + if (!Modifier.isPublic(method.getModifiers())) { + method.setAccessible(true); + } + } catch (Throwable e) { + return new KList(); + } + + + Class eventClass = method.getParameterTypes()[0]; + Class bukkitClass = EVENTS.get(eventClass); + ReflectiveHandler handler = method.getAnnotation(ReflectiveHandler.class); + + EventExecutor executor = (obj, event) -> { + if (!bukkitClass.isAssignableFrom(event.getClass())) + return; + + try { + method.invoke(obj, newProxy(obj, eventClass)); + } catch (InvocationTargetException e) { + throw new EventException(e.getCause()); + } catch (Throwable e) { + throw new EventException(e); + } + }; + + return new KList<>(new RegisteredListener(listener, executor, handler.priority(), Adapt.instance, handler.ignoreCancelled())); + }, KList::add)) + .forEach((handlerList, registeredListeners) -> { + if (handlerList == null) return; + handlerList.registerAll(registeredListeners); + }); + } + + public static void register(Class eventInterface, String... classes) { + for (String clazz : classes) { + Optional> opt = Reflect.getClass(clazz); + if (opt.isEmpty()) + continue; + + org.bukkit.event.HandlerList handlerList = getHandlerList(opt.get()); + if (handlerList == null) { + Adapt.warn("Event class does not contain HandlerList: " + clazz); + continue; + } + + EVENTS.put(eventInterface, opt.get()); + HANDLERS.put(opt.get(), handlerList); + return; } + } - public static boolean exists(@NonNull Class eventInterface) { - return EVENTS.containsKey(eventInterface); - } + public static boolean exists(@NonNull Class eventInterface) { + return EVENTS.containsKey(eventInterface); + } - @Nullable - private static HandlerList getHandlerList(Class parent) { - while (parent != null) { - if (!org.bukkit.event.Event.class.isAssignableFrom(parent)) - return null; - - try { - var method = parent.getDeclaredMethod("getHandlerList"); - return (HandlerList) method.invoke(null); - } catch (Throwable e) { - parent = parent.getSuperclass(); - } - } + @Nullable + private static HandlerList getHandlerList(Class parent) { + while (parent != null) { + if (!org.bukkit.event.Event.class.isAssignableFrom(parent)) return null; - } - private static Object newProxy(Object o, Class... interfaces) { - return Proxy.newProxyInstance(Event.class.getClassLoader(), interfaces, (proxy, method, args) -> method.invoke(o, args)); + try { + java.lang.reflect.Method method = parent.getDeclaredMethod("getHandlerList"); + return (HandlerList) method.invoke(null); + } catch (Throwable e) { + parent = parent.getSuperclass(); + } } + return null; + } + + private static Object newProxy(Object o, Class... interfaces) { + return Proxy.newProxyInstance(Event.class.getClassLoader(), interfaces, (proxy, method, args) -> method.invoke(o, args)); + } } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java index 46e4e742f..45fac8cf4 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/Event.java @@ -1,5 +1,5 @@ package art.arcane.adapt.util.reflect.events.api; public interface Event { - boolean isAsynchronous(); + boolean isAsynchronous(); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java index ff5b2bc20..dc2fe5e8c 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/ReflectiveHandler.java @@ -10,7 +10,7 @@ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ReflectiveHandler { - EventPriority priority() default EventPriority.NORMAL; + EventPriority priority() default EventPriority.NORMAL; - boolean ignoreCancelled() default false; + boolean ignoreCancelled() default false; } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java index 5b3506d60..25af31096 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EndermanAttackPlayerEvent.java @@ -6,8 +6,9 @@ import org.jetbrains.annotations.NotNull; public interface EndermanAttackPlayerEvent extends EntityEvent, Cancellable { - @NotNull - Enderman getEntity(); - @NotNull - Player getPlayer(); + @NotNull + Enderman getEntity(); + + @NotNull + Player getPlayer(); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java index 28b423749..e8831017a 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityDismountEvent.java @@ -5,6 +5,6 @@ import org.jetbrains.annotations.NotNull; public interface EntityDismountEvent extends EntityEvent, Cancellable { - @NotNull - Entity getDismounted(); + @NotNull + Entity getDismounted(); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java index cc2eee00b..27e89455e 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityEvent.java @@ -5,6 +5,7 @@ import org.bukkit.entity.EntityType; public interface EntityEvent extends Event { - Entity getEntity(); - EntityType getType(); + Entity getEntity(); + + EntityType getType(); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java index add1551fb..a784bfb9d 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java +++ b/src/main/java/art/arcane/adapt/util/reflect/events/api/entity/EntityMountEvent.java @@ -5,6 +5,6 @@ import org.jetbrains.annotations.NotNull; public interface EntityMountEvent extends EntityEvent, Cancellable { - @NotNull - Entity getMount(); + @NotNull + Entity getMount(); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java index 369d98a02..8b7d0428f 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/Attributes.java @@ -3,8 +3,8 @@ import org.bukkit.attribute.Attribute; public class Attributes { - public static final Attribute GENERIC_ARMOR = RegistryUtil.find(Attribute.class, "generic_armor", "armor"); - public static final Attribute GENERIC_ATTACK_DAMAGE = RegistryUtil.find(Attribute.class, "generic_attack_damage", "attack_damage"); - public static final Attribute GENERIC_MAX_HEALTH = RegistryUtil.find(Attribute.class, "generic_max_health", "max_health"); - public static final Attribute GENERIC_MOVEMENT_SPEED = RegistryUtil.find(Attribute.class, "generic_movement_speed", "movement_speed"); + public static final Attribute GENERIC_ARMOR = RegistryUtil.find(Attribute.class, "generic_armor", "armor"); + public static final Attribute GENERIC_ATTACK_DAMAGE = RegistryUtil.find(Attribute.class, "generic_attack_damage", "attack_damage"); + public static final Attribute GENERIC_MAX_HEALTH = RegistryUtil.find(Attribute.class, "generic_max_health", "max_health"); + public static final Attribute GENERIC_MOVEMENT_SPEED = RegistryUtil.find(Attribute.class, "generic_movement_speed", "movement_speed"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java index 7fc4af2d2..598c3d4d0 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/Enchantments.java @@ -3,7 +3,7 @@ import org.bukkit.enchantments.Enchantment; public class Enchantments { - public static final Enchantment DURABILITY = RegistryUtil.find(Enchantment.class, "unbreaking", "durability"); - public static final Enchantment ARROW_INFINITE = RegistryUtil.find(Enchantment.class, "infinity", "arrow_infinite"); - public static final Enchantment LOOT_BONUS_BLOCKS = RegistryUtil.find(Enchantment.class, "fortune", "loot_bonus_blocks"); + public static final Enchantment DURABILITY = RegistryUtil.find(Enchantment.class, "unbreaking", "durability"); + public static final Enchantment ARROW_INFINITE = RegistryUtil.find(Enchantment.class, "infinity", "arrow_infinite"); + public static final Enchantment LOOT_BONUS_BLOCKS = RegistryUtil.find(Enchantment.class, "fortune", "loot_bonus_blocks"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java index 0dae179fc..6af6540ed 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/EntityTypes.java @@ -3,5 +3,5 @@ import org.bukkit.entity.EntityType; public class EntityTypes { - public static final EntityType ENDER_CRYSTAL = RegistryUtil.find(EntityType.class, "ender_crystal", "end_crystal"); + public static final EntityType ENDER_CRYSTAL = RegistryUtil.find(EntityType.class, "ender_crystal", "end_crystal"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java b/src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java index 0b366b6f7..6144eede9 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/ItemFlags.java @@ -3,5 +3,5 @@ import org.bukkit.inventory.ItemFlag; public class ItemFlags { - public static final ItemFlag HIDE_POTION_EFFECTS = RegistryUtil.find(ItemFlag.class, "hide_potion_effects", "hide_additional_tooltip"); + public static final ItemFlag HIDE_POTION_EFFECTS = RegistryUtil.find(ItemFlag.class, "hide_potion_effects", "hide_additional_tooltip"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java index 554647aff..962415311 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/Materials.java @@ -3,90 +3,90 @@ import org.bukkit.Material; public class Materials { - public static final Material GRASS = RegistryUtil.find(Material.class, "grass", "short_grass"); + public static final Material GRASS = RegistryUtil.find(Material.class, "grass", "short_grass"); - /* Added Materials */ - public static final Material MACE = RegistryUtil.findNullable(Material.class, "mace"); - //1.20+ - public static final Material OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "oak_hanging_sign"); - public static final Material OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "oak_wall_hanging_sign"); - public static final Material SPRUCE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "spruce_hanging_sign"); - public static final Material SPRUCE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "spruce_wall_hanging_sign"); - public static final Material BIRCH_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "birch_hanging_sign"); - public static final Material BIRCH_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "birch_wall_hanging_sign"); - public static final Material JUNGLE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "jungle_hanging_sign"); - public static final Material JUNGLE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "jungle_wall_hanging_sign"); - public static final Material ACACIA_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "acacia_hanging_sign"); - public static final Material ACACIA_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "acacia_wall_hanging_sign"); - public static final Material DARK_OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "dark_oak_hanging_sign"); - public static final Material DARK_OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "dark_oak_wall_hanging_sign"); - public static final Material MANGROVE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "mangrove_hanging_sign"); - public static final Material MANGROVE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "mangrove_wall_hanging_sign"); - public static final Material CRIMSON_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "crimson_hanging_sign"); - public static final Material CRIMSON_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "crimson_wall_hanging_sign"); - public static final Material WARPED_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "warped_hanging_sign"); - public static final Material WARPED_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "warped_wall_hanging_sign"); + /* Added Materials */ + public static final Material MACE = RegistryUtil.findNullable(Material.class, "mace"); + //1.20+ + public static final Material OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "oak_hanging_sign"); + public static final Material OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "oak_wall_hanging_sign"); + public static final Material SPRUCE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "spruce_hanging_sign"); + public static final Material SPRUCE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "spruce_wall_hanging_sign"); + public static final Material BIRCH_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "birch_hanging_sign"); + public static final Material BIRCH_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "birch_wall_hanging_sign"); + public static final Material JUNGLE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "jungle_hanging_sign"); + public static final Material JUNGLE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "jungle_wall_hanging_sign"); + public static final Material ACACIA_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "acacia_hanging_sign"); + public static final Material ACACIA_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "acacia_wall_hanging_sign"); + public static final Material DARK_OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "dark_oak_hanging_sign"); + public static final Material DARK_OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "dark_oak_wall_hanging_sign"); + public static final Material MANGROVE_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "mangrove_hanging_sign"); + public static final Material MANGROVE_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "mangrove_wall_hanging_sign"); + public static final Material CRIMSON_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "crimson_hanging_sign"); + public static final Material CRIMSON_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "crimson_wall_hanging_sign"); + public static final Material WARPED_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "warped_hanging_sign"); + public static final Material WARPED_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "warped_wall_hanging_sign"); - public static final Material CHERRY_LOG = RegistryUtil.findNullable(Material.class, "cherry_log"); - public static final Material CHERRY_WOOD = RegistryUtil.findNullable(Material.class, "cherry_wood"); - public static final Material STRIPPED_CHERRY_LOG = RegistryUtil.findNullable(Material.class, "stripped_cherry_log"); - public static final Material STRIPPED_CHERRY_WOOD = RegistryUtil.findNullable(Material.class, "stripped_cherry_wood"); - public static final Material CHERRY_PLANKS = RegistryUtil.findNullable(Material.class, "cherry_planks"); - public static final Material CHERRY_STAIRS = RegistryUtil.findNullable(Material.class, "cherry_stairs"); - public static final Material CHERRY_SLAB = RegistryUtil.findNullable(Material.class, "cherry_slab"); - public static final Material CHERRY_FENCE = RegistryUtil.findNullable(Material.class, "cherry_fence"); - public static final Material CHERRY_FENCE_GATE = RegistryUtil.findNullable(Material.class, "cherry_fence_gate"); - public static final Material CHERRY_DOOR = RegistryUtil.findNullable(Material.class, "cherry_door"); - public static final Material CHERRY_TRAPDOOR = RegistryUtil.findNullable(Material.class, "cherry_trapdoor"); - public static final Material CHERRY_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "cherry_pressure_plate"); - public static final Material CHERRY_BUTTON = RegistryUtil.findNullable(Material.class, "cherry_button"); - public static final Material CHERRY_SIGN = RegistryUtil.findNullable(Material.class, "cherry_sign"); - public static final Material CHERRY_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "cherry_hanging_sign"); - public static final Material CHERRY_WALL_SIGN = RegistryUtil.findNullable(Material.class, "cherry_wall_sign"); - public static final Material CHERRY_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "cherry_wall_hanging_sign"); - public static final Material CHERRY_LEAVES = RegistryUtil.findNullable(Material.class, "cherry_leaves"); - public static final Material CHERRY_SAPLING = RegistryUtil.findNullable(Material.class, "cherry_sapling"); + public static final Material CHERRY_LOG = RegistryUtil.findNullable(Material.class, "cherry_log"); + public static final Material CHERRY_WOOD = RegistryUtil.findNullable(Material.class, "cherry_wood"); + public static final Material STRIPPED_CHERRY_LOG = RegistryUtil.findNullable(Material.class, "stripped_cherry_log"); + public static final Material STRIPPED_CHERRY_WOOD = RegistryUtil.findNullable(Material.class, "stripped_cherry_wood"); + public static final Material CHERRY_PLANKS = RegistryUtil.findNullable(Material.class, "cherry_planks"); + public static final Material CHERRY_STAIRS = RegistryUtil.findNullable(Material.class, "cherry_stairs"); + public static final Material CHERRY_SLAB = RegistryUtil.findNullable(Material.class, "cherry_slab"); + public static final Material CHERRY_FENCE = RegistryUtil.findNullable(Material.class, "cherry_fence"); + public static final Material CHERRY_FENCE_GATE = RegistryUtil.findNullable(Material.class, "cherry_fence_gate"); + public static final Material CHERRY_DOOR = RegistryUtil.findNullable(Material.class, "cherry_door"); + public static final Material CHERRY_TRAPDOOR = RegistryUtil.findNullable(Material.class, "cherry_trapdoor"); + public static final Material CHERRY_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "cherry_pressure_plate"); + public static final Material CHERRY_BUTTON = RegistryUtil.findNullable(Material.class, "cherry_button"); + public static final Material CHERRY_SIGN = RegistryUtil.findNullable(Material.class, "cherry_sign"); + public static final Material CHERRY_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "cherry_hanging_sign"); + public static final Material CHERRY_WALL_SIGN = RegistryUtil.findNullable(Material.class, "cherry_wall_sign"); + public static final Material CHERRY_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "cherry_wall_hanging_sign"); + public static final Material CHERRY_LEAVES = RegistryUtil.findNullable(Material.class, "cherry_leaves"); + public static final Material CHERRY_SAPLING = RegistryUtil.findNullable(Material.class, "cherry_sapling"); - public static final Material BAMBOO_BLOCK = RegistryUtil.findNullable(Material.class, "bamboo_block"); - public static final Material STRIPPED_BAMBOO_BLOCK = RegistryUtil.findNullable(Material.class, "stripped_bamboo_block"); - public static final Material BAMBOO_PLANKS = RegistryUtil.findNullable(Material.class, "bamboo_planks"); - public static final Material BAMBOO_MOSAIC = RegistryUtil.findNullable(Material.class, "bamboo_mosaic"); - public static final Material BAMBOO_STAIRS = RegistryUtil.findNullable(Material.class, "bamboo_stairs"); - public static final Material BAMBOO_MOSAIC_STAIRS = RegistryUtil.findNullable(Material.class, "bamboo_mosaic_stairs"); - public static final Material BAMBOO_SLAB = RegistryUtil.findNullable(Material.class, "bamboo_slab"); - public static final Material BAMBOO_MOSAIC_SLAB = RegistryUtil.findNullable(Material.class, "bamboo_mosaic_slab"); - public static final Material BAMBOO_FENCE = RegistryUtil.findNullable(Material.class, "bamboo_fence"); - public static final Material BAMBOO_FENCE_GATE = RegistryUtil.findNullable(Material.class, "bamboo_fence_gate"); - public static final Material BAMBOO_DOOR = RegistryUtil.findNullable(Material.class, "bamboo_door"); - public static final Material BAMBOO_TRAPDOOR = RegistryUtil.findNullable(Material.class, "bamboo_trapdoor"); - public static final Material BAMBOO_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "bamboo_pressure_plate"); - public static final Material BAMBOO_BUTTON = RegistryUtil.findNullable(Material.class, "bamboo_button"); - public static final Material BAMBOO_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_sign"); - public static final Material BAMBOO_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_hanging_sign"); - public static final Material BAMBOO_WALL_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_wall_sign"); - public static final Material BAMBOO_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_wall_hanging_sign"); + public static final Material BAMBOO_BLOCK = RegistryUtil.findNullable(Material.class, "bamboo_block"); + public static final Material STRIPPED_BAMBOO_BLOCK = RegistryUtil.findNullable(Material.class, "stripped_bamboo_block"); + public static final Material BAMBOO_PLANKS = RegistryUtil.findNullable(Material.class, "bamboo_planks"); + public static final Material BAMBOO_MOSAIC = RegistryUtil.findNullable(Material.class, "bamboo_mosaic"); + public static final Material BAMBOO_STAIRS = RegistryUtil.findNullable(Material.class, "bamboo_stairs"); + public static final Material BAMBOO_MOSAIC_STAIRS = RegistryUtil.findNullable(Material.class, "bamboo_mosaic_stairs"); + public static final Material BAMBOO_SLAB = RegistryUtil.findNullable(Material.class, "bamboo_slab"); + public static final Material BAMBOO_MOSAIC_SLAB = RegistryUtil.findNullable(Material.class, "bamboo_mosaic_slab"); + public static final Material BAMBOO_FENCE = RegistryUtil.findNullable(Material.class, "bamboo_fence"); + public static final Material BAMBOO_FENCE_GATE = RegistryUtil.findNullable(Material.class, "bamboo_fence_gate"); + public static final Material BAMBOO_DOOR = RegistryUtil.findNullable(Material.class, "bamboo_door"); + public static final Material BAMBOO_TRAPDOOR = RegistryUtil.findNullable(Material.class, "bamboo_trapdoor"); + public static final Material BAMBOO_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "bamboo_pressure_plate"); + public static final Material BAMBOO_BUTTON = RegistryUtil.findNullable(Material.class, "bamboo_button"); + public static final Material BAMBOO_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_sign"); + public static final Material BAMBOO_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_hanging_sign"); + public static final Material BAMBOO_WALL_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_wall_sign"); + public static final Material BAMBOO_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "bamboo_wall_hanging_sign"); - //1.21.4+ - public static final Material PALE_OAK_LOG = RegistryUtil.findNullable(Material.class, "pale_oak_log"); - public static final Material PALE_OAK_WOOD = RegistryUtil.findNullable(Material.class, "pale_oak_wood"); - public static final Material STRIPPED_PALE_OAK_LOG = RegistryUtil.findNullable(Material.class, "stripped_pale_oak_log"); - public static final Material STRIPPED_PALE_OAK_WOOD = RegistryUtil.findNullable(Material.class, "stripped_pale_oak_wood"); + //1.21.4+ + public static final Material PALE_OAK_LOG = RegistryUtil.findNullable(Material.class, "pale_oak_log"); + public static final Material PALE_OAK_WOOD = RegistryUtil.findNullable(Material.class, "pale_oak_wood"); + public static final Material STRIPPED_PALE_OAK_LOG = RegistryUtil.findNullable(Material.class, "stripped_pale_oak_log"); + public static final Material STRIPPED_PALE_OAK_WOOD = RegistryUtil.findNullable(Material.class, "stripped_pale_oak_wood"); - public static final Material PALE_OAK_PLANKS = RegistryUtil.findNullable(Material.class, "pale_oak_planks"); - public static final Material PALE_OAK_STAIRS = RegistryUtil.findNullable(Material.class, "pale_oak_stairs"); - public static final Material PALE_OAK_SLAB = RegistryUtil.findNullable(Material.class, "pale_oak_slab"); - public static final Material PALE_OAK_FENCE = RegistryUtil.findNullable(Material.class, "pale_oak_fence"); - public static final Material PALE_OAK_FENCE_GATE = RegistryUtil.findNullable(Material.class, "pale_oak_fence_gate"); - public static final Material PALE_OAK_DOOR = RegistryUtil.findNullable(Material.class, "pale_oak_door"); - public static final Material PALE_OAK_TRAPDOOR = RegistryUtil.findNullable(Material.class, "pale_oak_trapdoor"); - public static final Material PALE_OAK_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "pale_oak_pressure_plate"); - public static final Material PALE_OAK_BUTTON = RegistryUtil.findNullable(Material.class, "pale_oak_button"); + public static final Material PALE_OAK_PLANKS = RegistryUtil.findNullable(Material.class, "pale_oak_planks"); + public static final Material PALE_OAK_STAIRS = RegistryUtil.findNullable(Material.class, "pale_oak_stairs"); + public static final Material PALE_OAK_SLAB = RegistryUtil.findNullable(Material.class, "pale_oak_slab"); + public static final Material PALE_OAK_FENCE = RegistryUtil.findNullable(Material.class, "pale_oak_fence"); + public static final Material PALE_OAK_FENCE_GATE = RegistryUtil.findNullable(Material.class, "pale_oak_fence_gate"); + public static final Material PALE_OAK_DOOR = RegistryUtil.findNullable(Material.class, "pale_oak_door"); + public static final Material PALE_OAK_TRAPDOOR = RegistryUtil.findNullable(Material.class, "pale_oak_trapdoor"); + public static final Material PALE_OAK_PRESSURE_PLATE = RegistryUtil.findNullable(Material.class, "pale_oak_pressure_plate"); + public static final Material PALE_OAK_BUTTON = RegistryUtil.findNullable(Material.class, "pale_oak_button"); - public static final Material PALE_OAK_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_sign"); - public static final Material PALE_OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_hanging_sign"); - public static final Material PALE_OAK_WALL_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_wall_sign"); - public static final Material PALE_OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_wall_hanging_sign"); + public static final Material PALE_OAK_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_sign"); + public static final Material PALE_OAK_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_hanging_sign"); + public static final Material PALE_OAK_WALL_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_wall_sign"); + public static final Material PALE_OAK_WALL_HANGING_SIGN = RegistryUtil.findNullable(Material.class, "pale_oak_wall_hanging_sign"); - public static final Material PALE_OAK_LEAVES = RegistryUtil.findNullable(Material.class, "pale_oak_leaves"); - public static final Material PALE_OAK_SAPLING = RegistryUtil.findNullable(Material.class, "pale_oak_sapling"); + public static final Material PALE_OAK_LEAVES = RegistryUtil.findNullable(Material.class, "pale_oak_leaves"); + public static final Material PALE_OAK_SAPLING = RegistryUtil.findNullable(Material.class, "pale_oak_sapling"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java b/src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java index 2d6219588..c9205f759 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/Particles.java @@ -3,11 +3,11 @@ import org.bukkit.Particle; public class Particles { - public static final Particle REDSTONE = RegistryUtil.find(Particle.class, "redstone", "dust"); - public static final Particle ENCHANTMENT_TABLE = RegistryUtil.find(Particle.class, "enchantment_table", "enchant"); - public static final Particle CRIT_MAGIC = RegistryUtil.find(Particle.class, "crit_magic", "crit"); - public static final Particle TOTEM = RegistryUtil.find(Particle.class, "totem", "totem_of_undying"); - public static final Particle BLOCK_CRACK = RegistryUtil.find(Particle.class, "block_crack", "block"); - public static final Particle VILLAGER_HAPPY = RegistryUtil.find(Particle.class, "villager_happy", "happy_villager"); - public static final Particle ITEM_CRACK = RegistryUtil.find(Particle.class, "item_crack", "item"); + public static final Particle REDSTONE = RegistryUtil.find(Particle.class, "redstone", "dust"); + public static final Particle ENCHANTMENT_TABLE = RegistryUtil.find(Particle.class, "enchantment_table", "enchant"); + public static final Particle CRIT_MAGIC = RegistryUtil.find(Particle.class, "crit_magic", "crit"); + public static final Particle TOTEM = RegistryUtil.find(Particle.class, "totem", "totem_of_undying"); + public static final Particle BLOCK_CRACK = RegistryUtil.find(Particle.class, "block_crack", "block"); + public static final Particle VILLAGER_HAPPY = RegistryUtil.find(Particle.class, "villager_happy", "happy_villager"); + public static final Particle ITEM_CRACK = RegistryUtil.find(Particle.class, "item_crack", "item"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java index b97c7a27c..b18fe8693 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionEffectTypes.java @@ -3,10 +3,10 @@ import org.bukkit.potion.PotionEffectType; public class PotionEffectTypes { - public static final PotionEffectType FAST_DIGGING = RegistryUtil.find(PotionEffectType.class, "fast_digging", "haste"); - public static final PotionEffectType DAMAGE_RESISTANCE = RegistryUtil.find(PotionEffectType.class, "damage_resistance", "resistance"); - public static final PotionEffectType JUMP = RegistryUtil.find(PotionEffectType.class, "jump", "jump_boost"); - public static final PotionEffectType SLOW_DIGGING = RegistryUtil.find(PotionEffectType.class, "slow_digging", "mining_fatigue"); - public static final PotionEffectType CONFUSION = RegistryUtil.find(PotionEffectType.class, "confusion", "nausea"); - public static final PotionEffectType INCREASE_DAMAGE = RegistryUtil.find(PotionEffectType.class, "increase_damage", "strength"); + public static final PotionEffectType FAST_DIGGING = RegistryUtil.find(PotionEffectType.class, "fast_digging", "haste"); + public static final PotionEffectType DAMAGE_RESISTANCE = RegistryUtil.find(PotionEffectType.class, "damage_resistance", "resistance"); + public static final PotionEffectType JUMP = RegistryUtil.find(PotionEffectType.class, "jump", "jump_boost"); + public static final PotionEffectType SLOW_DIGGING = RegistryUtil.find(PotionEffectType.class, "slow_digging", "mining_fatigue"); + public static final PotionEffectType CONFUSION = RegistryUtil.find(PotionEffectType.class, "confusion", "nausea"); + public static final PotionEffectType INCREASE_DAMAGE = RegistryUtil.find(PotionEffectType.class, "increase_damage", "strength"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java index caba20d74..63accded1 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/PotionTypes.java @@ -3,10 +3,10 @@ import org.bukkit.potion.PotionType; public class PotionTypes { - public static final PotionType UNCRAFTABLE = RegistryUtil.findNullable(PotionType.class, "uncraftable", "empty"); + public static final PotionType UNCRAFTABLE = RegistryUtil.findNullable(PotionType.class, "uncraftable", "empty"); - public static final PotionType INSTANT_HEAL = RegistryUtil.find(PotionType.class, "instant_heal", "healing"); - public static final PotionType SPEED = RegistryUtil.find(PotionType.class, "speed", "swiftness"); - public static final PotionType REGEN = RegistryUtil.find(PotionType.class, "regen", "regeneration"); - public static final PotionType JUMP = RegistryUtil.find(PotionType.class, "jump", "leaping"); + public static final PotionType INSTANT_HEAL = RegistryUtil.find(PotionType.class, "instant_heal", "healing"); + public static final PotionType SPEED = RegistryUtil.find(PotionType.class, "speed", "swiftness"); + public static final PotionType REGEN = RegistryUtil.find(PotionType.class, "regen", "regeneration"); + public static final PotionType JUMP = RegistryUtil.find(PotionType.class, "jump", "leaping"); } diff --git a/src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java b/src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java index 6dffde1ae..8321e4b32 100644 --- a/src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java +++ b/src/main/java/art/arcane/adapt/util/reflect/registries/RegistryUtil.java @@ -20,224 +20,226 @@ @SuppressWarnings("unchecked") public class RegistryUtil { - private static final AtomicCache registryLookup = new AtomicCache<>(); - private static final Map, Map> KEYED_REGISTRY = new HashMap<>(); - private static final Map, Map> ENUM_REGISTRY = new HashMap<>(); - private static final Map, Registry> REGISTRY = new HashMap<>(); - - @NonNull - public static T find(@NonNull Class typeClass, @NonNull String... keys) { - return findOptional(typeClass, keys).orElseThrow(notFound(typeClass, keys)); - } - - @NonNull - public static Optional findOptional(@NonNull Class typeClass, @NonNull String... keys) { - return findOptional(typeClass, defaultLookup(), keys); - } - - @Nullable - public static T findNullable(@NonNull Class typeClass, @NonNull String... keys) { - return findOptional(typeClass, defaultLookup(), keys).orElse(null); - } - - @NonNull - public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull String... keys) { - return findOptional(typeClass, lookup, keys).orElseThrow(notFound(typeClass, keys)); - } - - @NonNull - public static Optional findOptional(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull String... keys) { - return findOptional(typeClass, lookup, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new)); - } - - @NonNull - public static T find(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { - return findOptional(typeClass, keys).orElseThrow(notFound(typeClass, keys)); - } - - @NonNull - public static Optional findOptional(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { - return findOptional(typeClass, defaultLookup(), keys); - } - - @NonNull - public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull NamespacedKey... keys) { - return findOptional(typeClass, lookup, keys).orElseThrow(notFound(typeClass, keys)); - } - - @NonNull - public static Optional findOptional(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull NamespacedKey... keys) { - if (keys.length == 0) throw new IllegalArgumentException("Need at least one key"); - Registry registry = null; - if (Keyed.class.isAssignableFrom(typeClass)) { - registry = getRegistry(typeClass.asSubclass(Keyed.class)); - } - if (registry == null) { - registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields()) - .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) - .filter(field -> Registry.class.isAssignableFrom(field.getType())) - .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(t)) - .map(field -> { - try { - return (Registry) field.get(null); - } catch (IllegalAccessException e) { - return null; - } - }) - .filter(Objects::nonNull) - .findFirst() - .orElse(null)); - } - - if (registry != null) { - for (NamespacedKey key : keys) { - Keyed value = registry.get(key); - if (value != null) - return Optional.of((T) value); + private static final AtomicCache registryLookup = new AtomicCache<>(); + private static final Map, Map> KEYED_REGISTRY = new HashMap<>(); + private static final Map, Map> ENUM_REGISTRY = new HashMap<>(); + private static final Map, Registry> REGISTRY = new HashMap<>(); + + @NonNull + public static T find(@NonNull Class typeClass, @NonNull String... keys) { + return findOptional(typeClass, keys).orElseThrow(notFound(typeClass, keys)); + } + + @NonNull + public static Optional findOptional(@NonNull Class typeClass, @NonNull String... keys) { + return findOptional(typeClass, defaultLookup(), keys); + } + + @Nullable + public static T findNullable(@NonNull Class typeClass, @NonNull String... keys) { + return findOptional(typeClass, defaultLookup(), keys).orElse(null); + } + + @NonNull + public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull String... keys) { + return findOptional(typeClass, lookup, keys).orElseThrow(notFound(typeClass, keys)); + } + + @NonNull + public static Optional findOptional(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull String... keys) { + return findOptional(typeClass, lookup, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new)); + } + + @NonNull + public static T find(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { + return findOptional(typeClass, keys).orElseThrow(notFound(typeClass, keys)); + } + + @NonNull + public static Optional findOptional(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { + return findOptional(typeClass, defaultLookup(), keys); + } + + @NonNull + public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull NamespacedKey... keys) { + return findOptional(typeClass, lookup, keys).orElseThrow(notFound(typeClass, keys)); + } + + @NonNull + public static Optional findOptional(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull NamespacedKey... keys) { + if (keys.length == 0) + throw new IllegalArgumentException("Need at least one key"); + Registry registry = null; + if (Keyed.class.isAssignableFrom(typeClass)) { + registry = getRegistry(typeClass.asSubclass(Keyed.class)); + } + if (registry == null) { + registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields()) + .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) + .filter(field -> Registry.class.isAssignableFrom(field.getType())) + .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(t)) + .map(field -> { + try { + return (Registry) field.get(null); + } catch (IllegalAccessException e) { + return null; } + }) + .filter(Objects::nonNull) + .findFirst() + .orElse(null)); + } + + if (registry != null) { + for (NamespacedKey key : keys) { + Keyed value = registry.get(key); + if (value != null) + return Optional.of((T) value); + } + } + + if (lookup != null) + return lookup.find(typeClass, keys); + return Optional.empty(); + } + + @NonNull + public static Optional findByField(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { + java.util.Map values = KEYED_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getKeyedValues); + for (NamespacedKey key : keys) { + org.bukkit.Keyed value = values.get(key); + if (value != null) + return Optional.of((T) value); + } + return Optional.empty(); + } + + @NonNull + public static Optional findByEnum(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { + java.util.Map values = ENUM_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getEnumValues); + for (NamespacedKey key : keys) { + java.lang.Object value = values.get(key); + if (value != null) + return Optional.of((T) value); + } + return Optional.empty(); + } + + @NonNull + public static Lookup defaultLookup() { + return Lookup.combine(RegistryUtil::findByField, RegistryUtil::findByEnum); + } + + private static Map getKeyedValues(@NonNull Class typeClass) { + return Arrays.stream(typeClass.getDeclaredFields()) + .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) + .filter(field -> Keyed.class.isAssignableFrom(field.getType())) + .map(field -> { + try { + return (Keyed) field.get(null); + } catch (Throwable e) { + return null; + } + }) + .map(keyed -> { + if (keyed == null) return null; + try { + return Pair.make(keyed.getKey(), keyed); + } catch (Throwable e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + } + + private static Map getEnumValues(@NonNull Class typeClass) { + return Arrays.stream(typeClass.getDeclaredFields()) + .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) + .filter(field -> typeClass.isAssignableFrom(field.getType())) + .map(field -> { + try { + return Map.entry(NamespacedKey.minecraft(field.getName().toLowerCase()), field.get(null)); + } catch (Throwable e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + } + + @Nullable + private static Registry getRegistry(@NotNull Class type) { + RegistryLookup lookup = registryLookup.aquire(() -> { + RegistryLookup bukkit; + try { + bukkit = Bukkit::getRegistry; + } catch (Throwable ignored) { + bukkit = null; + } + return new DefaultRegistryLookup(bukkit); + }); + return lookup.find(type); + } + + public static Supplier notFound(Class type, T... keys) { + return () -> new IllegalArgumentException("No " + type.getSimpleName() + " found for keys: " + Arrays.deepToString(keys)); + } + + @FunctionalInterface + public interface Lookup { + static Lookup combine(@NonNull Lookup... lookups) { + if (lookups.length == 0) + throw new IllegalArgumentException("Need at least one lookup"); + return (typeClass, keys) -> { + Optional opt = Optional.empty(); + for (Lookup lookup : lookups) { + opt = opt.or(() -> lookup.find(typeClass, keys)); } - - if (lookup != null) - return lookup.find(typeClass, keys); - return Optional.empty(); - } - - @NonNull - public static Optional findByField(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { - var values = KEYED_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getKeyedValues); - for (NamespacedKey key : keys) { - var value = values.get(key); - if (value != null) - return Optional.of((T) value); - } - return Optional.empty(); + return opt; + }; } @NonNull - public static Optional findByEnum(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { - var values = ENUM_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getEnumValues); - for (NamespacedKey key : keys) { - var value = values.get(key); - if (value != null) - return Optional.of((T) value); - } - return Optional.empty(); - } - - @NonNull - public static Lookup defaultLookup() { - return Lookup.combine(RegistryUtil::findByField, RegistryUtil::findByEnum); - } - - private static Map getKeyedValues(@NonNull Class typeClass) { - return Arrays.stream(typeClass.getDeclaredFields()) - .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) - .filter(field -> Keyed.class.isAssignableFrom(field.getType())) - .map(field -> { - try { - return (Keyed) field.get(null); - } catch (Throwable e) { - return null; - } - }) - .map(keyed -> { - if (keyed == null) return null; - try { - return Pair.make(keyed.getKey(), keyed); - } catch (Throwable e) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); - } - - private static Map getEnumValues(@NonNull Class typeClass) { - return Arrays.stream(typeClass.getDeclaredFields()) - .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) - .filter(field -> typeClass.isAssignableFrom(field.getType())) - .map(field -> { - try { - return Map.entry(NamespacedKey.minecraft(field.getName().toLowerCase()), field.get(null)); - } catch (Throwable e) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - } - - @FunctionalInterface - public interface Lookup { - @NonNull - Optional find(@NonNull Class typeClass, @NonNull NamespacedKey... keys); - - static Lookup combine(@NonNull Lookup... lookups) { - if (lookups.length == 0) throw new IllegalArgumentException("Need at least one lookup"); - return (typeClass, keys) -> { - Optional opt = Optional.empty(); - for (Lookup lookup : lookups) { - opt = opt.or(() -> lookup.find(typeClass, keys)); - } - return opt; - }; - } - } + Optional find(@NonNull Class typeClass, @NonNull NamespacedKey... keys); + } + private interface RegistryLookup { @Nullable - private static Registry getRegistry(@NotNull Class type) { - RegistryLookup lookup = registryLookup.aquire(() -> { - RegistryLookup bukkit; - try { - bukkit = Bukkit::getRegistry; - } catch (Throwable ignored) { - bukkit = null; - } - return new DefaultRegistryLookup(bukkit); - }); - return lookup.find(type); - } - - private interface RegistryLookup { - @Nullable - Registry find(@NonNull Class type); - } - - private static class DefaultRegistryLookup implements RegistryLookup { - private final RegistryLookup bukkit; - private final Map registries; - - private DefaultRegistryLookup(RegistryLookup bukkit) { - this.bukkit = bukkit; - registries = Arrays.stream(Registry.class.getDeclaredFields()) - .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) - .filter(field -> Registry.class.isAssignableFrom(field.getType())) - .map(field -> { - var type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; - try { - return Map.entry(type, field.get(null)); - } catch (Throwable e) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a)); - } - - @Nullable - @Override - public Registry find(@NonNull Class type) { - if (bukkit == null) return (Registry) registries.get(type); + Registry find(@NonNull Class type); + } + + private static class DefaultRegistryLookup implements RegistryLookup { + private final RegistryLookup bukkit; + private final Map registries; + + private DefaultRegistryLookup(RegistryLookup bukkit) { + this.bukkit = bukkit; + registries = Arrays.stream(Registry.class.getDeclaredFields()) + .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) + .filter(field -> Registry.class.isAssignableFrom(field.getType())) + .map(field -> { + java.lang.reflect.Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; try { - return bukkit.find(type); + return Map.entry(type, field.get(null)); } catch (Throwable e) { - return (Registry) registries.get(type); + return null; } - } + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a)); } - public static Supplier notFound(Class type, T... keys) { - return () -> new IllegalArgumentException("No " + type.getSimpleName() + " found for keys: " + Arrays.deepToString(keys)); - } + @Nullable + @Override + public Registry find(@NonNull Class type) { + if (bukkit == null) return (Registry) registries.get(type); + try { + return bukkit.find(type); + } catch (Throwable e) { + return (Registry) registries.get(type); + } + } + } } diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java index eb69d0cc4..4dae9d225 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java @@ -42,54 +42,30 @@ public class AdvancementUtils { - /** - * The {@code show_advancement_messages} game rule (previously called {@code announceAdvancements}). - */ - public static final GameRule SHOW_ADVANCEMENT_MESSAGES_GAMERULE = getShowAdvancementMessagesGamerule(); - - public static final MinecraftKeyWrapper ROOT_KEY, NOTIFICATION_KEY; - private static final String ADV_DESCRIPTION = "\n§7A notification."; - private static final AdvancementWrapper ROOT; - - static { - try { - ROOT_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "root"); - NOTIFICATION_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "notification"); - AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(new ItemStack(Material.GRASS_BLOCK), "§f§lNotifications§1§2§3§4§5§6§7§8§9§0", "§7Notification page.\n§7Close and reopen advancements to hide.", AdvancementFrameTypeWrapper.TASK, 0, 0, "textures/block/stone.png"); - ROOT = AdvancementWrapper.craftRootAdvancement(ROOT_KEY, display, 1); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - /** - * Displays a custom toast to a player. - * - * @param player A player to show the toast. - * @param icon The displayed item of the toast. - * @param title The displayed title of the toast. - * @param frame The {@link AdvancementFrameType} of the toast. - * @see UltimateAdvancementAPI#displayCustomToast(Player, ItemStack, String, AdvancementFrameType) - */ - public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull AdvancementFrameType frame) { - Preconditions.checkNotNull(player, "Player is null."); - Preconditions.checkNotNull(icon, "Icon is null."); - Preconditions.checkNotNull(title, "Title is null."); - Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); - Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); - - try { - AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, ADV_DESCRIPTION, frame.getNMSWrapper(), 1, 0, true, false, false); - AdvancementWrapper notification = AdvancementWrapper.craftBaseAdvancement(NOTIFICATION_KEY, ROOT, display, 1); - PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of( - ROOT, 1, - notification, 1 - )).sendTo(player); - PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(ROOT_KEY, NOTIFICATION_KEY)).sendTo(player); - } catch (ReflectiveOperationException e) { - e.printStackTrace(); - } - } + /** + * The {@code show_advancement_messages} game rule (previously called + * {@code announceAdvancements}). + */ + public static final GameRule SHOW_ADVANCEMENT_MESSAGES_GAMERULE = getShowAdvancementMessagesGamerule(); + + public static final MinecraftKeyWrapper ROOT_KEY, NOTIFICATION_KEY; + private static final String ADV_DESCRIPTION = "\n§7A notification."; + private static final AdvancementWrapper ROOT; + + static { + try { + ROOT_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "root"); + NOTIFICATION_KEY = MinecraftKeyWrapper.craft("com.fren_gor", "notification"); + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(new ItemStack(Material.GRASS_BLOCK), "§f§lNotifications§1§2§3§4§5§6§7§8§9§0", "§7Notification page.\n§7Close and reopen advancements to hide.", AdvancementFrameTypeWrapper.TASK, 0, 0, "textures/block/stone.png"); + ROOT = AdvancementWrapper.craftRootAdvancement(ROOT_KEY, display, 1); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private AdvancementUtils() { + throw new UnsupportedOperationException("Utility class."); + } /*public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull AdvancementFrameType frame, @NotNull Advancement base) { Preconditions.checkNotNull(player, "Player is null."); @@ -115,308 +91,321 @@ public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, } }*/ - public static void displayToastDuringUpdate(@NotNull Player player, @NotNull Advancement advancement) { - Preconditions.checkNotNull(player, "Player is null."); - Preconditions.checkNotNull(advancement, "Advancement is null."); - Preconditions.checkArgument(advancement.isValid(), "Advancement isn't valid."); - - final AdvancementDisplay display = advancement.getDisplay(); - final MinecraftKeyWrapper keyWrapper = getUniqueKey(advancement.getAdvancementTab()).getNMSWrapper(); - - try { - AdvancementDisplayWrapper displayWrapper = AdvancementDisplayWrapper.craft(display.getIcon(), display.getTitle(), ADV_DESCRIPTION, display.getFrame().getNMSWrapper(), 0, 0, true, false, false); - AdvancementWrapper advWrapper = AdvancementWrapper.craftBaseAdvancement(keyWrapper, advancement.getNMSWrapper(), displayWrapper, 1); - - PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of(advWrapper, 1)).sendTo(player); - PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(keyWrapper)).sendTo(player); - } catch (ReflectiveOperationException e) { - e.printStackTrace(); - } - } - - @NotNull - private static AdvancementKey getUniqueKey(@NotNull AdvancementTab tab) { - final String namespace = tab.getNamespace(); - StringBuilder builder = new StringBuilder("i"); - AdvancementKey key; - while (tab.getAdvancement(key = new AdvancementKey(namespace, builder.toString())) != null) { - builder.append('i'); - } - return key; + /** + * Displays a custom toast to a player. + * + * @param player A player to show the toast. + * @param icon The displayed item of the toast. + * @param title The displayed title of the toast. + * @param frame The {@link AdvancementFrameType} of the toast. + * @see UltimateAdvancementAPI#displayCustomToast(Player, ItemStack, String, + * AdvancementFrameType) + */ + public static void displayToast(@NotNull Player player, @NotNull ItemStack icon, @NotNull String title, @NotNull AdvancementFrameType frame) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(icon, "Icon is null."); + Preconditions.checkNotNull(title, "Title is null."); + Preconditions.checkNotNull(frame, "AdvancementFrameType is null."); + Preconditions.checkArgument(icon.getType() != Material.AIR, "ItemStack is air."); + + try { + AdvancementDisplayWrapper display = AdvancementDisplayWrapper.craft(icon, title, ADV_DESCRIPTION, frame.getNMSWrapper(), 1, 0, true, false, false); + AdvancementWrapper notification = AdvancementWrapper.craftBaseAdvancement(NOTIFICATION_KEY, ROOT, display, 1); + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of( + ROOT, 1, + notification, 1 + )).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(ROOT_KEY, NOTIFICATION_KEY)).sendTo(player); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + public static void displayToastDuringUpdate(@NotNull Player player, @NotNull Advancement advancement) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(advancement, "Advancement is null."); + Preconditions.checkArgument(advancement.isValid(), "Advancement isn't valid."); + + final AdvancementDisplay display = advancement.getDisplay(); + final MinecraftKeyWrapper keyWrapper = getUniqueKey(advancement.getAdvancementTab()).getNMSWrapper(); + + try { + AdvancementDisplayWrapper displayWrapper = AdvancementDisplayWrapper.craft(display.getIcon(), display.getTitle(), ADV_DESCRIPTION, display.getFrame().getNMSWrapper(), 0, 0, true, false, false); + AdvancementWrapper advWrapper = AdvancementWrapper.craftBaseAdvancement(keyWrapper, advancement.getNMSWrapper(), displayWrapper, 1); + + PacketPlayOutAdvancementsWrapper.craftSendPacket(Map.of(advWrapper, 1)).sendTo(player); + PacketPlayOutAdvancementsWrapper.craftRemovePacket(Set.of(keyWrapper)).sendTo(player); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + + @NotNull + private static AdvancementKey getUniqueKey(@NotNull AdvancementTab tab) { + final String namespace = tab.getNamespace(); + StringBuilder builder = new StringBuilder("i"); + AdvancementKey key; + while (tab.getAdvancement(key = new AdvancementKey(namespace, builder.toString())) != null) { + builder.append('i'); + } + return key; + } + + /** + * Disables vanilla advancements. + * + * @throws Exception If disabling fails. + * @see UltimateAdvancementAPI#disableVanillaAdvancements() + */ + public static void disableVanillaAdvancements() throws Exception { + VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(true, false); + } + + /** + * Disables vanilla recipe advancements (i.e. the advancements which unlock + * recipes). + * + * @throws Exception If disabling fails. + * @see UltimateAdvancementAPI#disableVanillaRecipeAdvancements() + */ + public static void disableVanillaRecipeAdvancements() throws Exception { + VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(false, true); + } + + @NotNull + public static BaseComponent[] fromStringList(@NotNull List list) { + return fromStringList(null, list); + } + + @NotNull + public static BaseComponent[] fromStringList(@Nullable String title, @NotNull List list) { + Preconditions.checkNotNull(list); + ComponentBuilder builder = new ComponentBuilder(); + if (title != null) { + builder.append(TextComponent.fromLegacyText(title), FormatRetention.NONE); + if (list.isEmpty()) { + return builder.create(); + } + builder.append("\n", FormatRetention.NONE); + } else if (list.isEmpty()) { + return builder.create(); } - - /** - * Disables vanilla advancements. - * - * @throws Exception If disabling fails. - * @see UltimateAdvancementAPI#disableVanillaAdvancements() - */ - public static void disableVanillaAdvancements() throws Exception { - VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(true, false); + int i = 0; + for (String s : list) { + builder.append(TextComponent.fromLegacyText(s), FormatRetention.NONE); + if (++i < list.size()) // Don't append \n at the end + builder.append("\n", FormatRetention.NONE); } + return builder.create(); + } - /** - * Disables vanilla recipe advancements (i.e. the advancements which unlock recipes). - * - * @throws Exception If disabling fails. - * @see UltimateAdvancementAPI#disableVanillaRecipeAdvancements() - */ - public static void disableVanillaRecipeAdvancements() throws Exception { - VanillaAdvancementDisablerWrapper.disableVanillaAdvancements(false, true); + public static boolean startsWithEmptyLine(@NotNull String text) { + Preconditions.checkNotNull(text); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '§') { + i++; // Skip next character since it is a color code + } else { + return c == '\n'; + } } + return false; + } - @NotNull - public static BaseComponent[] fromStringList(@NotNull List list) { - return fromStringList(null, list); + @Contract("_ -> param1") + public static int validateProgressionValue(int progression) { + if (progression < 0) { + throw new IllegalArgumentException("Progression value cannot be < 0"); } + return progression; + } - @NotNull - public static BaseComponent[] fromStringList(@Nullable String title, @NotNull List list) { - Preconditions.checkNotNull(list); - ComponentBuilder builder = new ComponentBuilder(); - if (title != null) { - builder.append(TextComponent.fromLegacyText(title), FormatRetention.NONE); - if (list.isEmpty()) { - return builder.create(); - } - builder.append("\n", FormatRetention.NONE); - } else if (list.isEmpty()) { - return builder.create(); - } - int i = 0; - for (String s : list) { - builder.append(TextComponent.fromLegacyText(s), FormatRetention.NONE); - if (++i < list.size()) // Don't append \n at the end - builder.append("\n", FormatRetention.NONE); - } - return builder.create(); + public static void validateProgressionValueStrict(int progression, int maxProgression) { + validateProgressionValue(progression); + if (progression > maxProgression) { + throw new IllegalArgumentException("Progression value cannot be greater than the maximum progression (" + maxProgression + ')'); } + } - public static boolean startsWithEmptyLine(@NotNull String text) { - Preconditions.checkNotNull(text); - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - if (c == '§') { - i++; // Skip next character since it is a color code - } else { - return c == '\n'; - } - } - return false; + public static void validateIncrement(int increment) { + if (increment <= 0) { + throw new IllegalArgumentException("Increment cannot be zero or less."); } + } - @Contract("_ -> param1") - public static int validateProgressionValue(int progression) { - if (progression < 0) { - throw new IllegalArgumentException("Progression value cannot be < 0"); - } - return progression; - } + @Contract("null -> fail; !null -> param1") + public static TeamProgression validateTeamProgression(TeamProgression pro) { + Preconditions.checkNotNull(pro, "TeamProgression is null."); + Preconditions.checkArgument(pro.isValid(), "Invalid TeamProgression."); + return pro; + } - public static void validateProgressionValueStrict(int progression, int maxProgression) { - validateProgressionValue(progression); - if (progression > maxProgression) { - throw new IllegalArgumentException("Progression value cannot be greater than the maximum progression (" + maxProgression + ')'); - } + public static void checkTeamProgressionNotNull(TeamProgression progression) { + if (progression == null) { + throw new UserNotLoadedException(); } + } - public static void validateIncrement(int increment) { - if (increment <= 0) { - throw new IllegalArgumentException("Increment cannot be zero or less."); - } + public static void checkTeamProgressionNotNull(TeamProgression progression, UUID uuid) { + if (progression == null) { + throw new UserNotLoadedException(uuid); } + } - @Contract("null -> fail; !null -> param1") - public static TeamProgression validateTeamProgression(TeamProgression pro) { - Preconditions.checkNotNull(pro, "TeamProgression is null."); - Preconditions.checkArgument(pro.isValid(), "Invalid TeamProgression."); - return pro; + public static void checkSync() { + if (J.isFoliaThreading() || hasFoliaScheduler()) { + return; } - public static void checkTeamProgressionNotNull(TeamProgression progression) { - if (progression == null) { - throw new UserNotLoadedException(); - } - } + if (!Bukkit.isPrimaryThread()) + throw new AsyncExecutionException("Illegal async method call. This method can be called only from the main thread."); + } - public static void checkTeamProgressionNotNull(TeamProgression progression, UUID uuid) { - if (progression == null) { - throw new UserNotLoadedException(uuid); - } - } + public static void runSync(@NotNull AdvancementMain main, @NotNull Runnable runnable) { + runSync(main.getOwningPlugin(), runnable); + } - public static void checkSync() { - if (J.isFoliaThreading() || hasFoliaScheduler()) { - return; - } + public static void runSync(@NotNull Plugin plugin, @NotNull Runnable runnable) { + runSync(plugin, 1, runnable); + } - if (!Bukkit.isPrimaryThread()) - throw new AsyncExecutionException("Illegal async method call. This method can be called only from the main thread."); - } + public static void runSync(@NotNull AdvancementMain main, long delay, @NotNull Runnable runnable) { + runSync(main.getOwningPlugin(), delay, runnable); + } - public static void runSync(@NotNull AdvancementMain main, @NotNull Runnable runnable) { - runSync(main.getOwningPlugin(), runnable); + public static void runSync(@NotNull Plugin plugin, long delay, @NotNull Runnable runnable) { + Preconditions.checkNotNull(plugin, "Plugin is null."); + Preconditions.checkNotNull(runnable, "Runnable is null."); + int safeDelay = sanitizeDelay(delay); + if (scheduleFoliaSync(plugin, runnable, safeDelay)) { + return; } - public static void runSync(@NotNull Plugin plugin, @NotNull Runnable runnable) { - runSync(plugin, 1, runnable); + if (safeDelay <= 0) { + J.s(runnable); + } else { + J.s(runnable, safeDelay); } + } - public static void runSync(@NotNull AdvancementMain main, long delay, @NotNull Runnable runnable) { - runSync(main.getOwningPlugin(), delay, runnable); + private static int sanitizeDelay(long delay) { + if (delay <= 0) { + return 0; } - public static void runSync(@NotNull Plugin plugin, long delay, @NotNull Runnable runnable) { - Preconditions.checkNotNull(plugin, "Plugin is null."); - Preconditions.checkNotNull(runnable, "Runnable is null."); - int safeDelay = sanitizeDelay(delay); - if (hasFoliaScheduler()) { - if (scheduleFoliaSync(plugin, runnable, safeDelay)) { - return; - } - - if (safeDelay <= 0) { - J.s(runnable); - } else { - J.s(runnable, safeDelay); - } - return; - } - - try { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable, safeDelay); - } catch (UnsupportedOperationException ex) { - // Folia rejects BukkitScheduler sync calls; retry with Adapt's Folia-aware scheduler path. - if (safeDelay <= 0) { - J.s(runnable); - } else { - J.s(runnable, safeDelay); - } - } + if (delay >= Integer.MAX_VALUE) { + return Integer.MAX_VALUE; } - private static int sanitizeDelay(long delay) { - if (delay <= 0) { - return 0; - } + return (int) delay; + } - if (delay >= Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - - return (int) delay; + private static boolean scheduleFoliaSync(@NotNull Plugin plugin, @NotNull Runnable runnable, int safeDelay) { + Player player = extractPlayer(runnable); + if (player != null && player.isOnline() && FoliaScheduler.runEntity(plugin, player, runnable, safeDelay)) { + return true; } - private static boolean scheduleFoliaSync(@NotNull Plugin plugin, @NotNull Runnable runnable, int safeDelay) { - Player player = extractPlayer(runnable); - if (player != null && player.isOnline() && FoliaScheduler.runEntity(plugin, player, runnable, safeDelay)) { - return true; - } + return FoliaScheduler.runGlobal(plugin, runnable, safeDelay); + } - return FoliaScheduler.runGlobal(plugin, runnable, safeDelay); - } - - private static boolean hasFoliaScheduler() { - return FoliaScheduler.isFolia(Bukkit.getServer()); - } + private static boolean hasFoliaScheduler() { + return FoliaScheduler.isFolia(Bukkit.getServer()); + } - @Nullable - private static Player extractPlayer(@NotNull Runnable runnable) { - Class current = runnable.getClass(); - while (current != null && current != Object.class) { - for (Field field : current.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) { - continue; - } - - try { - field.setAccessible(true); - Object value = field.get(runnable); - Player player = asPlayer(value); - if (player != null) { - return player; - } - } catch (Throwable ex) { - // Ignore inaccessible synthetic fields while walking runnable captures. - } - } - - current = current.getSuperclass(); + @Nullable + private static Player extractPlayer(@NotNull Runnable runnable) { + Class current = runnable.getClass(); + while (current != null && current != Object.class) { + for (Field field : current.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; } - return null; - } - - @Nullable - private static Player asPlayer(@Nullable Object value) { - if (value instanceof Player player) { + try { + field.setAccessible(true); + Object value = field.get(runnable); + Player player = asPlayer(value); + if (player != null) { return player; + } + } catch (Throwable ex) { + // Ignore inaccessible synthetic fields while walking runnable captures. } + } - if (value instanceof OfflinePlayer offlinePlayer) { - return offlinePlayer.getPlayer(); - } + current = current.getSuperclass(); + } - if (value instanceof UUID uuid) { - return Bukkit.getPlayer(uuid); - } + return null; + } - return null; + @Nullable + private static Player asPlayer(@Nullable Object value) { + if (value instanceof Player player) { + return player; } - @NotNull - public static UUID uuidFromPlayer(@NotNull Player player) { - Preconditions.checkNotNull(player, "Player is null."); - return player.getUniqueId(); + if (value instanceof OfflinePlayer offlinePlayer) { + return offlinePlayer.getPlayer(); } - @NotNull - public static UUID uuidFromPlayer(@NotNull OfflinePlayer player) { - Preconditions.checkNotNull(player, "OfflinePlayer is null."); - return player.getUniqueId(); + if (value instanceof UUID uuid) { + return Bukkit.getPlayer(uuid); } - @NotNull - public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull Advancement advancement) { - return progressionFromPlayer(player, advancement.getAdvancementTab()); - } + return null; + } - @NotNull - public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull Advancement advancement) { - return progressionFromUUID(uuid, advancement.getAdvancementTab()); - } + @NotNull + public static UUID uuidFromPlayer(@NotNull Player player) { + Preconditions.checkNotNull(player, "Player is null."); + return player.getUniqueId(); + } - @NotNull - public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull AdvancementTab tab) { - return progressionFromUUID(uuidFromPlayer(player), tab); - } + @NotNull + public static UUID uuidFromPlayer(@NotNull OfflinePlayer player) { + Preconditions.checkNotNull(player, "OfflinePlayer is null."); + return player.getUniqueId(); + } - @NotNull - public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull AdvancementTab tab) { - Preconditions.checkNotNull(uuid, "UUID is null."); - return tab.getDatabaseManager().getTeamProgression(uuid); - } + @NotNull + public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull Advancement advancement) { + return progressionFromPlayer(player, advancement.getAdvancementTab()); + } - @SuppressWarnings("unchecked") - private static GameRule getShowAdvancementMessagesGamerule() { - try { - // Spigot 1.21.11+ - return (GameRule) GameRule.class.getDeclaredField("SHOW_ADVANCEMENT_MESSAGES").get(null); - } catch (NoSuchFieldException e) { - // Spigot <= 1.21.10, Paper all versions - try { - return (GameRule) GameRule.class.getDeclaredField("ANNOUNCE_ADVANCEMENTS").get(null); - } catch (NoSuchFieldException ex) { - return null; - } catch (ReflectiveOperationException inner) { - throw new RuntimeException(inner); - } - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } + @NotNull + public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull Advancement advancement) { + return progressionFromUUID(uuid, advancement.getAdvancementTab()); + } + + @NotNull + public static TeamProgression progressionFromPlayer(@NotNull Player player, @NotNull AdvancementTab tab) { + return progressionFromUUID(uuidFromPlayer(player), tab); + } - private AdvancementUtils() { - throw new UnsupportedOperationException("Utility class."); + @NotNull + public static TeamProgression progressionFromUUID(@NotNull UUID uuid, @NotNull AdvancementTab tab) { + Preconditions.checkNotNull(uuid, "UUID is null."); + return tab.getDatabaseManager().getTeamProgression(uuid); + } + + @SuppressWarnings("unchecked") + private static GameRule getShowAdvancementMessagesGamerule() { + try { + // Spigot 1.21.11+ + return (GameRule) GameRule.class.getDeclaredField("SHOW_ADVANCEMENT_MESSAGES").get(null); + } catch (NoSuchFieldException e) { + // Spigot <= 1.21.10, Paper all versions + try { + return (GameRule) GameRule.class.getDeclaredField("ANNOUNCE_ADVANCEMENTS").get(null); + } catch (NoSuchFieldException ex) { + return null; + } catch (ReflectiveOperationException inner) { + throw new RuntimeException(inner); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); } + } } diff --git a/src/main/resources/de_DE.toml b/src/main/resources/de_DE.toml index 6d02e2adb..9fa4e4633 100644 --- a/src/main/resources/de_DE.toml +++ b/src/main/resources/de_DE.toml @@ -2,2060 +2,2060 @@ [advancement] [advancement.challenge_move_1k] - title = "Muss mich bewegen!" - description = "Laufe über 1 Kilometer (1.000 Blöcke)" +title = "Muss mich bewegen!" +description = "Laufe über 1 Kilometer (1.000 Blöcke)" [advancement.challenge_sprint_5k] - title = "Sprinte 5K!" - description = "Laufe über 5 Kilometer (5.000 Blöcke)" +title = "Sprinte 5K!" +description = "Laufe über 5 Kilometer (5.000 Blöcke)" [advancement.challenge_sprint_50k] - title = "Düse 50K!" - description = "Laufe über 50 Kilometer (50.000 Blöcke)" +title = "Düse 50K!" +description = "Laufe über 50 Kilometer (50.000 Blöcke)" [advancement.challenge_sprint_500k] - title = "Durchquere das Universum!!" - description = "Laufe über 500 Kilometer (500.000 Blöcke)" +title = "Durchquere das Universum!!" +description = "Laufe über 500 Kilometer (500.000 Blöcke)" [advancement.challenge_sprint_marathon] - title = "Sprinte einen (buchstäblichen) Marathon!" - description = "Sprinte über 42.195 Blöcke!" +title = "Sprinte einen (buchstäblichen) Marathon!" +description = "Sprinte über 42.195 Blöcke!" [advancement.challenge_place_1k] - title = "Bauanfänger!" - description = "Platziere 1.000 Blöcke" +title = "Bauanfänger!" +description = "Platziere 1.000 Blöcke" [advancement.challenge_place_5k] - title = "Fortgeschrittener Bauarbeiter!" - description = "Platziere 5.000 Blöcke" +title = "Fortgeschrittener Bauarbeiter!" +description = "Platziere 5.000 Blöcke" [advancement.challenge_place_50k] - title = "Erfahrener Baumeister!" - description = "Platziere 50.000 Blöcke" +title = "Erfahrener Baumeister!" +description = "Platziere 50.000 Blöcke" [advancement.challenge_place_500k] - title = "Meisterbaumeister!" - description = "Platziere 500.000 Blöcke" +title = "Meisterbaumeister!" +description = "Platziere 500.000 Blöcke" [advancement.challenge_place_5m] - title = "Akolyth der Symmetrie!" - description = "REALITÄT IST DEIN SPIELPLATZ! (5 Millionen Blöcke)" +title = "Akolyth der Symmetrie!" +description = "REALITÄT IST DEIN SPIELPLATZ! (5 Millionen Blöcke)" [advancement.challenge_chop_1k] - title = "Holzfäller-Anfänger!" - description = "Fälle 1.000 Blöcke" +title = "Holzfäller-Anfänger!" +description = "Fälle 1.000 Blöcke" [advancement.challenge_chop_5k] - title = "Fortgeschrittener Holzfäller!" - description = "Fälle 5.000 Blöcke" +title = "Fortgeschrittener Holzfäller!" +description = "Fälle 5.000 Blöcke" [advancement.challenge_chop_50k] - title = "Erfahrener Holzfäller!" - description = "Fälle 50.000 Blöcke" +title = "Erfahrener Holzfäller!" +description = "Fälle 50.000 Blöcke" [advancement.challenge_chop_500k] - title = "Meister-Holzfäller!" - description = "Fälle 500.000 Blöcke" +title = "Meister-Holzfäller!" +description = "Fälle 500.000 Blöcke" [advancement.challenge_chop_5m] - title = "Jackson the Dog" - description = "Der allerbeste brave Junge! (5 Millionen Blöcke)" +title = "Jackson the Dog" +description = "Der allerbeste brave Junge! (5 Millionen Blöcke)" [advancement.challenge_block_1k] - title = "Kaum geblockt!" - description = "Blockiere 1000 Treffer" +title = "Kaum geblockt!" +description = "Blockiere 1000 Treffer" [advancement.challenge_block_5k] - title = "Blocken macht Spaß!" - description = "Blockiere 5000 Treffer" +title = "Blocken macht Spaß!" +description = "Blockiere 5000 Treffer" [advancement.challenge_block_50k] - title = "Blocken ist mein Leben!" - description = "Blockiere 50.000 Treffer" +title = "Blocken ist mein Leben!" +description = "Blockiere 50.000 Treffer" [advancement.challenge_block_500k] - title = "Blocken ist meine Bestimmung!" - description = "Blockiere 500.000 Treffer" +title = "Blocken ist meine Bestimmung!" +description = "Blockiere 500.000 Treffer" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Blockiere 5.000.000 Treffer" +title = "Die Hand Die Verletzt" +description = "Blockiere 5.000.000 Treffer" [advancement.challenge_brew_1k] - title = "Alchemisten-Anfänger!" - description = "Konsumiere 1000 Tränke" +title = "Alchemisten-Anfänger!" +description = "Konsumiere 1000 Tränke" [advancement.challenge_brew_5k] - title = "Fortgeschrittener Alchemist!" - description = "Konsumiere 5000 Tränke" +title = "Fortgeschrittener Alchemist!" +description = "Konsumiere 5000 Tränke" [advancement.challenge_brew_50k] - title = "Erfahrener Alchemist!" - description = "Konsumiere 50.000 Tränke" +title = "Erfahrener Alchemist!" +description = "Konsumiere 50.000 Tränke" [advancement.challenge_brew_500k] - title = "Meister-Alchemist!" - description = "Konsumiere 500.000 Tränke" +title = "Meister-Alchemist!" +description = "Konsumiere 500.000 Tränke" [advancement.challenge_brew_5m] - title = "Der Alchemist" - description = "Konsumiere 5.000.000 Tränke" +title = "Der Alchemist" +description = "Konsumiere 5.000.000 Tränke" [advancement.challenge_brewsplash_1k] - title = "Wurftrank-Anfänger!" - description = "Wirf 1000 Tränke" +title = "Wurftrank-Anfänger!" +description = "Wirf 1000 Tränke" [advancement.challenge_brewsplash_5k] - title = "Fortgeschrittener Trankwerfer!" - description = "Wirf 5000 Tränke" +title = "Fortgeschrittener Trankwerfer!" +description = "Wirf 5000 Tränke" [advancement.challenge_brewsplash_50k] - title = "Erfahrener Trankwerfer!" - description = "Wirf 50.000 Tränke" +title = "Erfahrener Trankwerfer!" +description = "Wirf 50.000 Tränke" [advancement.challenge_brewsplash_500k] - title = "Meister-Trankwerfer!" - description = "Wirf 500.000 Tränke" +title = "Meister-Trankwerfer!" +description = "Wirf 500.000 Tränke" [advancement.challenge_brewsplash_5m] - title = "Der Splash-Meister" - description = "Wirf 5.000.000 Tränke" +title = "Der Splash-Meister" +description = "Wirf 5.000.000 Tränke" [advancement.challenge_craft_1k] - title = "Schlauer Handwerker!" - description = "Stelle 1000 Gegenstände her" +title = "Schlauer Handwerker!" +description = "Stelle 1000 Gegenstände her" [advancement.challenge_craft_5k] - title = "Mürrischer Handwerker!" - description = "Stelle 5000 Gegenstände her" +title = "Mürrischer Handwerker!" +description = "Stelle 5000 Gegenstände her" [advancement.challenge_craft_50k] - title = "Unterwürfiger Handwerker!" - description = "Stelle 50.000 Gegenstände her" +title = "Unterwürfiger Handwerker!" +description = "Stelle 50.000 Gegenstände her" [advancement.challenge_craft_500k] - title = "Kakophonischer Handwerker!" - description = "Stelle 500.000 Gegenstände her" +title = "Kakophonischer Handwerker!" +description = "Stelle 500.000 Gegenstände her" [advancement.challenge_craft_5m] - title = "Verhängnisvolles McCraftface" - description = "Stelle 5.000.000 Gegenstände her" +title = "Verhängnisvolles McCraftface" +description = "Stelle 5.000.000 Gegenstände her" [advancement.challenge_enchant_1k] - title = "Verzauberer-Anfänger!" - description = "Verzaubere 1000 Gegenstände" +title = "Verzauberer-Anfänger!" +description = "Verzaubere 1000 Gegenstände" [advancement.challenge_enchant_5k] - title = "Fortgeschrittener Verzauberer!" - description = "Verzaubere 5000 Gegenstände" +title = "Fortgeschrittener Verzauberer!" +description = "Verzaubere 5000 Gegenstände" [advancement.challenge_enchant_50k] - title = "Erfahrener Verzauberer!" - description = "Verzaubere 50.000 Gegenstände" +title = "Erfahrener Verzauberer!" +description = "Verzaubere 50.000 Gegenstände" [advancement.challenge_enchant_500k] - title = "Meister-Verzauberer!" - description = "Verzaubere 500.000 Gegenstände" +title = "Meister-Verzauberer!" +description = "Verzaubere 500.000 Gegenstände" [advancement.challenge_enchant_5m] - title = "Rätselhafter Verzauberer" - description = "Verzaubere 5.000.000 Gegenstände" +title = "Rätselhafter Verzauberer" +description = "Verzaubere 5.000.000 Gegenstände" [advancement.challenge_excavate_1k] - title = "Begeisterter Ausgräber!" - description = "Grabe 1000 Blöcke aus" +title = "Begeisterter Ausgräber!" +description = "Grabe 1000 Blöcke aus" [advancement.challenge_excavate_5k] - title = "Fortgeschrittener Ausgräber!" - description = "Grabe 5000 Blöcke aus" +title = "Fortgeschrittener Ausgräber!" +description = "Grabe 5000 Blöcke aus" [advancement.challenge_excavate_50k] - title = "Erfahrener Ausgräber!" - description = "Grabe 50.000 Blöcke aus" +title = "Erfahrener Ausgräber!" +description = "Grabe 50.000 Blöcke aus" [advancement.challenge_excavate_500k] - title = "Meister-Ausgräber!" - description = "Grabe 500.000 Blöcke aus" +title = "Meister-Ausgräber!" +description = "Grabe 500.000 Blöcke aus" [advancement.challenge_excavate_5m] - title = "Rätselhafter Ausgräber" - description = "Grabe 5.000.000 Blöcke aus" +title = "Rätselhafter Ausgräber" +description = "Grabe 5.000.000 Blöcke aus" [advancement.horrible_person] - title = "Du bist eine schreckliche Person" - description = "Unvorstellbar, wirklich" +title = "Du bist eine schreckliche Person" +description = "Unvorstellbar, wirklich" [advancement.challenge_turtle_egg_smasher] - title = "Schildkrötenei-Zertrümmerer!" - description = "Zerbrich 100 Schildkröteneier" +title = "Schildkrötenei-Zertrümmerer!" +description = "Zerbrich 100 Schildkröteneier" [advancement.challenge_turtle_egg_annihilator] - title = "Schildkrötenei-Vernichter!" - description = "Zerbrich 500 Schildkröteneier" +title = "Schildkrötenei-Vernichter!" +description = "Zerbrich 500 Schildkröteneier" [advancement.challenge_novice_hunter] - title = "Jäger-Anfänger!" - description = "Töte 100 Wesen" +title = "Jäger-Anfänger!" +description = "Töte 100 Wesen" [advancement.challenge_intermediate_hunter] - title = "Fortgeschrittener Jäger!" - description = "Töte 500 Wesen" +title = "Fortgeschrittener Jäger!" +description = "Töte 500 Wesen" [advancement.challenge_advanced_hunter] - title = "Erfahrener Jäger!" - description = "Töte 5000 Wesen" +title = "Erfahrener Jäger!" +description = "Töte 5000 Wesen" [advancement.challenge_creeper_conqueror] - title = "Creeper-Eroberer!" - description = "Töte 50 Creeper" +title = "Creeper-Eroberer!" +description = "Töte 50 Creeper" [advancement.challenge_creeper_annihilator] - title = "Creeper-Vernichter!" - description = "Töte 200 Creeper" +title = "Creeper-Vernichter!" +description = "Töte 200 Creeper" [advancement.challenge_pickaxe_1k] - title = "Bergmann-Anfänger" - description = "Zerbrich 1000 Blöcke" +title = "Bergmann-Anfänger" +description = "Zerbrich 1000 Blöcke" [advancement.challenge_pickaxe_5k] - title = "Geschickter Bergmann" - description = "Zerbrich 5000 Blöcke" +title = "Geschickter Bergmann" +description = "Zerbrich 5000 Blöcke" [advancement.challenge_pickaxe_50k] - title = "Erfahrener Bergmann" - description = "Zerbrich 50.000 Blöcke" +title = "Erfahrener Bergmann" +description = "Zerbrich 50.000 Blöcke" [advancement.challenge_pickaxe_500k] - title = "Meister-Bergmann" - description = "Zerbrich 500.000 Blöcke" +title = "Meister-Bergmann" +description = "Zerbrich 500.000 Blöcke" [advancement.challenge_pickaxe_5m] - title = "Legendärer Bergmann" - description = "Zerbrich 5.000.000 Blöcke" +title = "Legendärer Bergmann" +description = "Zerbrich 5.000.000 Blöcke" [advancement.challenge_eat_100] - title = "So viel zu essen!" - description = "Iss über 100 Gegenstände!" +title = "So viel zu essen!" +description = "Iss über 100 Gegenstände!" [advancement.challenge_eat_1000] - title = "Unstillbarer Hunger!" - description = "Iss über 1.000 Gegenstände!" +title = "Unstillbarer Hunger!" +description = "Iss über 1.000 Gegenstände!" [advancement.challenge_eat_10000] - title = "EWIGER HUNGER!" - description = "Iss über 10.000 Gegenstände!" +title = "EWIGER HUNGER!" +description = "Iss über 10.000 Gegenstände!" [advancement.challenge_harvest_100] - title = "Volle Ernte" - description = "Ernte über 100 Feldfrüchte!" +title = "Volle Ernte" +description = "Ernte über 100 Feldfrüchte!" [advancement.challenge_harvest_1000] - title = "Große Ernte" - description = "Ernte über 1.000 Feldfrüchte!" +title = "Große Ernte" +description = "Ernte über 1.000 Feldfrüchte!" [advancement.challenge_swim_1nm] - title = "Menschliches U-Boot!" - description = "Schwimme 1 Seemeile (1.852 Blöcke)" +title = "Menschliches U-Boot!" +description = "Schwimme 1 Seemeile (1.852 Blöcke)" [advancement.challenge_sneak_1k] - title = "Knieschmerzen" - description = "Schleiche über einen Kilometer (1.000 Blöcke)" +title = "Knieschmerzen" +description = "Schleiche über einen Kilometer (1.000 Blöcke)" [advancement.challenge_sneak_5k] - title = "Schattenwandler" - description = "Schleiche über 5.000 Blöcke" +title = "Schattenwandler" +description = "Schleiche über 5.000 Blöcke" [advancement.challenge_sneak_20k] - title = "Geist" - description = "Schleiche über 20.000 Blöcke" +title = "Geist" +description = "Schleiche über 20.000 Blöcke" [advancement.challenge_swim_5k] - title = "Tieftaucher" - description = "Schwimme über 5.000 Blöcke" +title = "Tieftaucher" +description = "Schwimme über 5.000 Blöcke" [advancement.challenge_swim_20k] - title = "Poseidons Auserwählter" - description = "Schwimme über 20.000 Blöcke" +title = "Poseidons Auserwählter" +description = "Schwimme über 20.000 Blöcke" [advancement.challenge_sword_100] - title = "Erstes Blut" - description = "Lande 100 Treffer mit einem Schwert" +title = "Erstes Blut" +description = "Lande 100 Treffer mit einem Schwert" [advancement.challenge_sword_1k] - title = "Klingentänzer" - description = "Lande 1.000 Treffer mit einem Schwert" +title = "Klingentänzer" +description = "Lande 1.000 Treffer mit einem Schwert" [advancement.challenge_sword_10k] - title = "Tausend Schnitte" - description = "Lande 10.000 Treffer mit einem Schwert" +title = "Tausend Schnitte" +description = "Lande 10.000 Treffer mit einem Schwert" [advancement.challenge_unarmed_100] - title = "Kneipenschläger" - description = "Lande 100 unbewaffnete Treffer" +title = "Kneipenschläger" +description = "Lande 100 unbewaffnete Treffer" [advancement.challenge_unarmed_1k] - title = "Eiserne Fäuste" - description = "Lande 1.000 unbewaffnete Treffer" +title = "Eiserne Fäuste" +description = "Lande 1.000 unbewaffnete Treffer" [advancement.challenge_unarmed_10k] - title = "One Punch" - description = "Lande 10.000 unbewaffnete Treffer" +title = "One Punch" +description = "Lande 10.000 unbewaffnete Treffer" [advancement.challenge_trag_1k] - title = "Blutpreis" - description = "Erleide 1.000 Schaden" +title = "Blutpreis" +description = "Erleide 1.000 Schaden" [advancement.challenge_trag_10k] - title = "Blutstrom" - description = "Erleide 10.000 Schaden" +title = "Blutstrom" +description = "Erleide 10.000 Schaden" [advancement.challenge_trag_100k] - title = "Avatar des Leidens" - description = "Erleide 100.000 Schaden" +title = "Avatar des Leidens" +description = "Erleide 100.000 Schaden" [advancement.challenge_ranged_100] - title = "Zielübung" - description = "Feuere 100 Projektile ab" +title = "Zielübung" +description = "Feuere 100 Projektile ab" [advancement.challenge_ranged_1k] - title = "Falkenauge" - description = "Feuere 1.000 Projektile ab" +title = "Falkenauge" +description = "Feuere 1.000 Projektile ab" [advancement.challenge_ranged_10k] - title = "Pfeilsturm" - description = "Feuere 10.000 Projektile ab" +title = "Pfeilsturm" +description = "Feuere 10.000 Projektile ab" [advancement.challenge_chronos_1h] - title = "Tick Tack" - description = "Verbringe 1 Stunde online" +title = "Tick Tack" +description = "Verbringe 1 Stunde online" [advancement.challenge_chronos_24h] - title = "Sand der Zeit" - description = "Verbringe 24 Stunden online" +title = "Sand der Zeit" +description = "Verbringe 24 Stunden online" [advancement.challenge_chronos_168h] - title = "Zeitlos" - description = "Verbringe 168 Stunden (1 Woche) online" +title = "Zeitlos" +description = "Verbringe 168 Stunden (1 Woche) online" [advancement.challenge_nether_50] - title = "Höllenpförtner" - description = "Erschlage 50 Netherwesen" +title = "Höllenpförtner" +description = "Erschlage 50 Netherwesen" [advancement.challenge_nether_500] - title = "Abgrundwächter" - description = "Erschlage 500 Netherwesen" +title = "Abgrundwächter" +description = "Erschlage 500 Netherwesen" [advancement.challenge_nether_5k] - title = "Herr des Nethers" - description = "Erschlage 5.000 Netherwesen" +title = "Herr des Nethers" +description = "Erschlage 5.000 Netherwesen" [advancement.challenge_rift_50] - title = "Raumanomalie" - description = "Teleportiere dich 50 Mal" +title = "Raumanomalie" +description = "Teleportiere dich 50 Mal" [advancement.challenge_rift_500] - title = "Leerenwandler" - description = "Teleportiere dich 500 Mal" +title = "Leerenwandler" +description = "Teleportiere dich 500 Mal" [advancement.challenge_rift_5k] - title = "Zwischen den Welten" - description = "Teleportiere dich 5.000 Mal" +title = "Zwischen den Welten" +description = "Teleportiere dich 5.000 Mal" [advancement.challenge_taming_10] - title = "Tierflüsterer" - description = "Züchte 10 Tiere" +title = "Tierflüsterer" +description = "Züchte 10 Tiere" [advancement.challenge_taming_50] - title = "Rudelführer" - description = "Züchte 50 Tiere" +title = "Rudelführer" +description = "Züchte 50 Tiere" [advancement.challenge_taming_500] - title = "Bestienmeister" - description = "Züchte 500 Tiere" +title = "Bestienmeister" +description = "Züchte 500 Tiere" # Agility [advancement.challenge_sprint_dist_5k] - title = "Geschwindigkeitsdaemon" - description = "Sprinte ueber 5 Kilometer (5.000 Bloecke)" +title = "Geschwindigkeitsdaemon" +description = "Sprinte ueber 5 Kilometer (5.000 Bloecke)" [advancement.challenge_sprint_dist_50k] - title = "Blitzbeine" - description = "Sprinte ueber 50 Kilometer (50.000 Bloecke)" +title = "Blitzbeine" +description = "Sprinte ueber 50 Kilometer (50.000 Bloecke)" [advancement.challenge_agility_swim_1k] - title = "Wasserlaeufer" - description = "Schwimme ueber 1 Kilometer (1.000 Bloecke)" +title = "Wasserlaeufer" +description = "Schwimme ueber 1 Kilometer (1.000 Bloecke)" [advancement.challenge_agility_swim_10k] - title = "Ozeanreisender" - description = "Schwimme ueber 10 Kilometer (10.000 Bloecke)" +title = "Ozeanreisender" +description = "Schwimme ueber 10 Kilometer (10.000 Bloecke)" [advancement.challenge_fly_1k] - title = "Himmelstaenzer" - description = "Fliege ueber 1 Kilometer (1.000 Bloecke)" +title = "Himmelstaenzer" +description = "Fliege ueber 1 Kilometer (1.000 Bloecke)" [advancement.challenge_fly_10k] - title = "Windreiter" - description = "Fliege ueber 10 Kilometer (10.000 Bloecke)" +title = "Windreiter" +description = "Fliege ueber 10 Kilometer (10.000 Bloecke)" [advancement.challenge_agility_sneak_500] - title = "Leise Schritte" - description = "Schleiche ueber 500 Bloecke" +title = "Leise Schritte" +description = "Schleiche ueber 500 Bloecke" [advancement.challenge_agility_sneak_5k] - title = "Phantomschritte" - description = "Schleiche ueber 5 Kilometer (5.000 Bloecke)" +title = "Phantomschritte" +description = "Schleiche ueber 5 Kilometer (5.000 Bloecke)" # Architect [advancement.challenge_demolish_500] - title = "Abrisstruppp" - description = "Baue 500 Bloecke ab" +title = "Abrisstruppp" +description = "Baue 500 Bloecke ab" [advancement.challenge_demolish_5k] - title = "Abrissbirne" - description = "Baue 5.000 Bloecke ab" +title = "Abrissbirne" +description = "Baue 5.000 Bloecke ab" [advancement.challenge_value_placed_10k] - title = "Wertvoller Baumeister" - description = "Platziere Bloecke im Wert von 10.000" +title = "Wertvoller Baumeister" +description = "Platziere Bloecke im Wert von 10.000" [advancement.challenge_value_placed_100k] - title = "Meisterarchitekt" - description = "Platziere Bloecke im Wert von 100.000" +title = "Meisterarchitekt" +description = "Platziere Bloecke im Wert von 100.000" [advancement.challenge_demolish_val_5k] - title = "Bergungsexperte" - description = "Berge 5.000 Blockwert durch Abriss" +title = "Bergungsexperte" +description = "Berge 5.000 Blockwert durch Abriss" [advancement.challenge_demolish_val_50k] - title = "Totaler Rueckbau" - description = "Berge 50.000 Blockwert durch Abriss" +title = "Totaler Rueckbau" +description = "Berge 50.000 Blockwert durch Abriss" [advancement.challenge_high_build_100] - title = "Himmelsbauer" - description = "Platziere 100 Bloecke ueber Y=128" +title = "Himmelsbauer" +description = "Platziere 100 Bloecke ueber Y=128" [advancement.challenge_high_build_1k] - title = "Wolkenarchitekt" - description = "Platziere 1.000 Bloecke ueber Y=128" +title = "Wolkenarchitekt" +description = "Platziere 1.000 Bloecke ueber Y=128" # Axes [advancement.challenge_axe_swing_500] - title = "Axtschwiniger" - description = "Schwinge deine Axt 500 Mal" +title = "Axtschwiniger" +description = "Schwinge deine Axt 500 Mal" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Schwinge deine Axt 5.000 Mal" +title = "Berserker" +description = "Schwinge deine Axt 5.000 Mal" [advancement.challenge_axe_damage_1k] - title = "Spalter" - description = "Verursache 1.000 Schaden mit Aexten" +title = "Spalter" +description = "Verursache 1.000 Schaden mit Aexten" [advancement.challenge_axe_damage_10k] - title = "Henkersbeil" - description = "Verursache 10.000 Schaden mit Aexten" +title = "Henkersbeil" +description = "Verursache 10.000 Schaden mit Aexten" [advancement.challenge_axe_value_5k] - title = "Holzhaendler" - description = "Ernte Holz im Wert von 5.000" +title = "Holzhaendler" +description = "Ernte Holz im Wert von 5.000" [advancement.challenge_axe_value_50k] - title = "Holzbaron" - description = "Ernte Holz im Wert von 50.000" +title = "Holzbaron" +description = "Ernte Holz im Wert von 50.000" [advancement.challenge_leaves_500] - title = "Laubblaeser" - description = "Entferne 500 Laubbloecke mit einer Axt" +title = "Laubblaeser" +description = "Entferne 500 Laubbloecke mit einer Axt" [advancement.challenge_leaves_5k] - title = "Entlauber" - description = "Entferne 5.000 Laubbloecke mit einer Axt" +title = "Entlauber" +description = "Entferne 5.000 Laubbloecke mit einer Axt" # Blocking [advancement.challenge_block_dmg_1k] - title = "Schadensabsorber" - description = "Blocke 1.000 Schaden mit einem Schild" +title = "Schadensabsorber" +description = "Blocke 1.000 Schaden mit einem Schild" [advancement.challenge_block_dmg_10k] - title = "Menschliches Schutzschild" - description = "Blocke 10.000 Schaden mit einem Schild" +title = "Menschliches Schutzschild" +description = "Blocke 10.000 Schaden mit einem Schild" [advancement.challenge_block_proj_100] - title = "Pfeilabwehrer" - description = "Blocke 100 Projektile mit einem Schild" +title = "Pfeilabwehrer" +description = "Blocke 100 Projektile mit einem Schild" [advancement.challenge_block_proj_1k] - title = "Projektilschild" - description = "Blocke 1.000 Projektile mit einem Schild" +title = "Projektilschild" +description = "Blocke 1.000 Projektile mit einem Schild" [advancement.challenge_block_melee_500] - title = "Pariermeister" - description = "Blocke 500 Nahkampfangriffe mit einem Schild" +title = "Pariermeister" +description = "Blocke 500 Nahkampfangriffe mit einem Schild" [advancement.challenge_block_melee_5k] - title = "Eiserne Festung" - description = "Blocke 5.000 Nahkampfangriffe mit einem Schild" +title = "Eiserne Festung" +description = "Blocke 5.000 Nahkampfangriffe mit einem Schild" [advancement.challenge_block_heavy_50] - title = "Panzer" - description = "Blocke 50 schwere Angriffe (ueber 5 Schaden)" +title = "Panzer" +description = "Blocke 50 schwere Angriffe (ueber 5 Schaden)" [advancement.challenge_block_heavy_500] - title = "Unbewegliches Objekt" - description = "Blocke 500 schwere Angriffe (ueber 5 Schaden)" +title = "Unbewegliches Objekt" +description = "Blocke 500 schwere Angriffe (ueber 5 Schaden)" # Brewing [advancement.challenge_brew_stands_10] - title = "Brauerei-Einrichtung" - description = "Platziere 10 Braustaende" +title = "Brauerei-Einrichtung" +description = "Platziere 10 Braustaende" [advancement.challenge_brew_stands_50] - title = "Trankfabrik" - description = "Platziere 50 Braustaende" +title = "Trankfabrik" +description = "Platziere 50 Braustaende" [advancement.challenge_brew_strong_25] - title = "Starkes Gebraeu" - description = "Trinke 25 verstaerkte Traenke" +title = "Starkes Gebraeu" +description = "Trinke 25 verstaerkte Traenke" [advancement.challenge_brew_strong_250] - title = "Maximale Wirkung" - description = "Trinke 250 verstaerkte Traenke" +title = "Maximale Wirkung" +description = "Trinke 250 verstaerkte Traenke" [advancement.challenge_brew_splash_hits_50] - title = "Spritzzone" - description = "Triff 50 Wesen mit Wurf-Traenken" +title = "Spritzzone" +description = "Triff 50 Wesen mit Wurf-Traenken" [advancement.challenge_brew_splash_hits_500] - title = "Pestdoktor" - description = "Triff 500 Wesen mit Wurf-Traenken" +title = "Pestdoktor" +description = "Triff 500 Wesen mit Wurf-Traenken" # Chronos [advancement.challenge_active_dist_1k] - title = "Rastlos" - description = "Reise 1 Kilometer waehrend du aktiv bist" +title = "Rastlos" +description = "Reise 1 Kilometer waehrend du aktiv bist" [advancement.challenge_active_dist_10k] - title = "Pfadfinder" - description = "Reise 10 Kilometer waehrend du aktiv bist" +title = "Pfadfinder" +description = "Reise 10 Kilometer waehrend du aktiv bist" [advancement.challenge_active_dist_100k] - title = "Ewige Bewegung" - description = "Reise 100 Kilometer waehrend du aktiv bist" +title = "Ewige Bewegung" +description = "Reise 100 Kilometer waehrend du aktiv bist" [advancement.challenge_beds_10] - title = "Fruehaufsteher" - description = "Schlafe 10 Mal in einem Bett" +title = "Fruehaufsteher" +description = "Schlafe 10 Mal in einem Bett" [advancement.challenge_beds_100] - title = "Zeitueberbruecker" - description = "Schlafe 100 Mal in einem Bett" +title = "Zeitueberbruecker" +description = "Schlafe 100 Mal in einem Bett" [advancement.challenge_chronos_tp_50] - title = "Zeitverschiebung" - description = "Teleportiere dich 50 Mal" +title = "Zeitverschiebung" +description = "Teleportiere dich 50 Mal" [advancement.challenge_chronos_tp_500] - title = "Zeitsprung" - description = "Teleportiere dich 500 Mal" +title = "Zeitsprung" +description = "Teleportiere dich 500 Mal" [advancement.challenge_chronos_deaths_10] - title = "Sterblich" - description = "Stirb 10 Mal" +title = "Sterblich" +description = "Stirb 10 Mal" [advancement.challenge_chronos_deaths_100] - title = "Todestrotzer" - description = "Stirb 100 Mal" +title = "Todestrotzer" +description = "Stirb 100 Mal" # Crafting [advancement.challenge_craft_value_10k] - title = "Handwerkswert" - description = "Stelle Gegenstaende im Wert von 10.000 her" +title = "Handwerkswert" +description = "Stelle Gegenstaende im Wert von 10.000 her" [advancement.challenge_craft_value_100k] - title = "Kunsthandwerker" - description = "Stelle Gegenstaende im Wert von 100.000 her" +title = "Kunsthandwerker" +description = "Stelle Gegenstaende im Wert von 100.000 her" [advancement.challenge_craft_tools_25] - title = "Werkzeugschmied" - description = "Stelle 25 Werkzeuge her" +title = "Werkzeugschmied" +description = "Stelle 25 Werkzeuge her" [advancement.challenge_craft_tools_250] - title = "Meisterschmied" - description = "Stelle 250 Werkzeuge her" +title = "Meisterschmied" +description = "Stelle 250 Werkzeuge her" [advancement.challenge_craft_armor_25] - title = "Ruestungsschmied" - description = "Stelle 25 Ruestungsteile her" +title = "Ruestungsschmied" +description = "Stelle 25 Ruestungsteile her" [advancement.challenge_craft_armor_250] - title = "Ruestungsmeister" - description = "Stelle 250 Ruestungsteile her" +title = "Ruestungsmeister" +description = "Stelle 250 Ruestungsteile her" [advancement.challenge_craft_mass_25k] - title = "Massenproduzent" - description = "Stelle 25.000 Gegenstaende her" +title = "Massenproduzent" +description = "Stelle 25.000 Gegenstaende her" [advancement.challenge_craft_mass_250k] - title = "Industrielle Revolution" - description = "Stelle 250.000 Gegenstaende her" +title = "Industrielle Revolution" +description = "Stelle 250.000 Gegenstaende her" # Discovery [advancement.challenge_discover_items_50] - title = "Sammler" - description = "Entdecke 50 einzigartige Gegenstaende" +title = "Sammler" +description = "Entdecke 50 einzigartige Gegenstaende" [advancement.challenge_discover_items_250] - title = "Katalogisierer" - description = "Entdecke 250 einzigartige Gegenstaende" +title = "Katalogisierer" +description = "Entdecke 250 einzigartige Gegenstaende" [advancement.challenge_discover_blocks_50] - title = "Vermesser" - description = "Entdecke 50 einzigartige Bloecke" +title = "Vermesser" +description = "Entdecke 50 einzigartige Bloecke" [advancement.challenge_discover_blocks_250] - title = "Geologe" - description = "Entdecke 250 einzigartige Bloecke" +title = "Geologe" +description = "Entdecke 250 einzigartige Bloecke" [advancement.challenge_discover_mobs_25] - title = "Beobachter" - description = "Entdecke 25 einzigartige Kreaturen" +title = "Beobachter" +description = "Entdecke 25 einzigartige Kreaturen" [advancement.challenge_discover_mobs_75] - title = "Naturforscher" - description = "Entdecke 75 einzigartige Kreaturen" +title = "Naturforscher" +description = "Entdecke 75 einzigartige Kreaturen" [advancement.challenge_discover_biomes_10] - title = "Wanderer" - description = "Entdecke 10 einzigartige Biome" +title = "Wanderer" +description = "Entdecke 10 einzigartige Biome" [advancement.challenge_discover_biomes_40] - title = "Weltreisender" - description = "Entdecke 40 einzigartige Biome" +title = "Weltreisender" +description = "Entdecke 40 einzigartige Biome" [advancement.challenge_discover_foods_10] - title = "Feinschmecker" - description = "Entdecke 10 einzigartige Nahrungsmittel" +title = "Feinschmecker" +description = "Entdecke 10 einzigartige Nahrungsmittel" [advancement.challenge_discover_foods_30] - title = "Kuechenmeister" - description = "Entdecke 30 einzigartige Nahrungsmittel" +title = "Kuechenmeister" +description = "Entdecke 30 einzigartige Nahrungsmittel" # Enchanting [advancement.challenge_enchant_power_100] - title = "Kraftweber" - description = "Sammle 100 Verzauberungskraft" +title = "Kraftweber" +description = "Sammle 100 Verzauberungskraft" [advancement.challenge_enchant_power_1k] - title = "Arkaner Meister" - description = "Sammle 1.000 Verzauberungskraft" +title = "Arkaner Meister" +description = "Sammle 1.000 Verzauberungskraft" [advancement.challenge_enchant_levels_1k] - title = "Stufenverschwender" - description = "Gib 1.000 Erfahrungsstufen fuer Verzauberungen aus" +title = "Stufenverschwender" +description = "Gib 1.000 Erfahrungsstufen fuer Verzauberungen aus" [advancement.challenge_enchant_levels_10k] - title = "EP-Verschlinger" - description = "Gib 10.000 Erfahrungsstufen fuer Verzauberungen aus" +title = "EP-Verschlinger" +description = "Gib 10.000 Erfahrungsstufen fuer Verzauberungen aus" [advancement.challenge_enchant_high_25] - title = "Hochstapler" - description = "Fuehre 25 Verzauberungen auf hoechster Stufe durch" +title = "Hochstapler" +description = "Fuehre 25 Verzauberungen auf hoechster Stufe durch" [advancement.challenge_enchant_high_250] - title = "Legendaerer Verzauberer" - description = "Fuehre 250 Verzauberungen auf hoechster Stufe durch" +title = "Legendaerer Verzauberer" +description = "Fuehre 250 Verzauberungen auf hoechster Stufe durch" [advancement.challenge_enchant_total_500] - title = "Stufenbrenner" - description = "Gib insgesamt 500 Stufen fuer Verzauberungen aus" +title = "Stufenbrenner" +description = "Gib insgesamt 500 Stufen fuer Verzauberungen aus" [advancement.challenge_enchant_total_5k] - title = "Arkane Investition" - description = "Gib insgesamt 5.000 Stufen fuer Verzauberungen aus" +title = "Arkane Investition" +description = "Gib insgesamt 5.000 Stufen fuer Verzauberungen aus" # Excavation [advancement.challenge_dig_swing_500] - title = "Graeber" - description = "Schwinge deine Schaufel 500 Mal" +title = "Graeber" +description = "Schwinge deine Schaufel 500 Mal" [advancement.challenge_dig_swing_5k] - title = "Baggerfahrer" - description = "Schwinge deine Schaufel 5.000 Mal" +title = "Baggerfahrer" +description = "Schwinge deine Schaufel 5.000 Mal" [advancement.challenge_dig_damage_1k] - title = "Schaufelritter" - description = "Verursache 1.000 Schaden mit einer Schaufel" +title = "Schaufelritter" +description = "Verursache 1.000 Schaden mit einer Schaufel" [advancement.challenge_dig_damage_10k] - title = "Schaufelmeister" - description = "Verursache 10.000 Schaden mit einer Schaufel" +title = "Schaufelmeister" +description = "Verursache 10.000 Schaden mit einer Schaufel" [advancement.challenge_dig_value_5k] - title = "Erdhaendler" - description = "Grabe Bloecke im Wert von 5.000 aus" +title = "Erdhaendler" +description = "Grabe Bloecke im Wert von 5.000 aus" [advancement.challenge_dig_value_50k] - title = "Erdbaron" - description = "Grabe Bloecke im Wert von 50.000 aus" +title = "Erdbaron" +description = "Grabe Bloecke im Wert von 50.000 aus" [advancement.challenge_dig_gravel_500] - title = "Kiesmalmer" - description = "Grabe 500 Kies-, Sand- oder Lehmbloecke" +title = "Kiesmalmer" +description = "Grabe 500 Kies-, Sand- oder Lehmbloecke" [advancement.challenge_dig_gravel_5k] - title = "Sandsieber" - description = "Grabe 5.000 Kies-, Sand- oder Lehmbloecke" +title = "Sandsieber" +description = "Grabe 5.000 Kies-, Sand- oder Lehmbloecke" # Herbalism [advancement.challenge_plant_100] - title = "Saemann" - description = "Pflanze 100 Feldfrueche" +title = "Saemann" +description = "Pflanze 100 Feldfrueche" [advancement.challenge_plant_1k] - title = "Gruener Daumen" - description = "Pflanze 1.000 Feldfrueche" +title = "Gruener Daumen" +description = "Pflanze 1.000 Feldfrueche" [advancement.challenge_plant_5k] - title = "Ackerbaron" - description = "Pflanze 5.000 Feldfrueche" +title = "Ackerbaron" +description = "Pflanze 5.000 Feldfrueche" [advancement.challenge_compost_50] - title = "Recycler" - description = "Kompostiere 50 Gegenstaende" +title = "Recycler" +description = "Kompostiere 50 Gegenstaende" [advancement.challenge_compost_500] - title = "Bodenveredler" - description = "Kompostiere 500 Gegenstaende" +title = "Bodenveredler" +description = "Kompostiere 500 Gegenstaende" [advancement.challenge_shear_50] - title = "Scherer" - description = "Schere 50 Wesen" +title = "Scherer" +description = "Schere 50 Wesen" [advancement.challenge_shear_250] - title = "Herdenmeister" - description = "Schere 250 Wesen" +title = "Herdenmeister" +description = "Schere 250 Wesen" # Hunter [advancement.challenge_kills_500] - title = "Toeter" - description = "Erschlage 500 Kreaturen" +title = "Toeter" +description = "Erschlage 500 Kreaturen" [advancement.challenge_kills_5k] - title = "Henker" - description = "Erschlage 5.000 Kreaturen" +title = "Henker" +description = "Erschlage 5.000 Kreaturen" [advancement.challenge_boss_1] - title = "Bossherausforderer" - description = "Besiege einen Boss" +title = "Bossherausforderer" +description = "Besiege einen Boss" [advancement.challenge_boss_10] - title = "Legendentoeter" - description = "Besiege 10 Bosse" +title = "Legendentoeter" +description = "Besiege 10 Bosse" # Nether [advancement.challenge_wither_dmg_500] - title = "Verwelkt" - description = "Ertrage 500 Wither-Schaden" +title = "Verwelkt" +description = "Ertrage 500 Wither-Schaden" [advancement.challenge_wither_dmg_5k] - title = "Seuchenueberlebender" - description = "Ertrage 5.000 Wither-Schaden" +title = "Seuchenueberlebender" +description = "Ertrage 5.000 Wither-Schaden" [advancement.challenge_wither_skel_25] - title = "Knochensammler" - description = "Erschlage 25 Witherskelette" +title = "Knochensammler" +description = "Erschlage 25 Witherskelette" [advancement.challenge_wither_skel_250] - title = "Skelettfluch" - description = "Erschlage 250 Witherskelette" +title = "Skelettfluch" +description = "Erschlage 250 Witherskelette" [advancement.challenge_wither_boss_1] - title = "Withertoeter" - description = "Besiege den Wither" +title = "Withertoeter" +description = "Besiege den Wither" [advancement.challenge_wither_boss_10] - title = "Netherbeherrscher" - description = "Besiege den Wither 10 Mal" +title = "Netherbeherrscher" +description = "Besiege den Wither 10 Mal" [advancement.challenge_roses_10] - title = "Todesqaertner" - description = "Zerstoere 10 Witherrosen" +title = "Todesqaertner" +description = "Zerstoere 10 Witherrosen" [advancement.challenge_roses_100] - title = "Seuchensammler" - description = "Zerstoere 100 Witherrosen" +title = "Seuchensammler" +description = "Zerstoere 100 Witherrosen" # Pickaxes [advancement.challenge_pick_swing_500] - title = "Bergmannsarm" - description = "Schwinge deine Spitzhacke 500 Mal" +title = "Bergmannsarm" +description = "Schwinge deine Spitzhacke 500 Mal" [advancement.challenge_pick_swing_5k] - title = "Tunnelgraber" - description = "Schwinge deine Spitzhacke 5.000 Mal" +title = "Tunnelgraber" +description = "Schwinge deine Spitzhacke 5.000 Mal" [advancement.challenge_pick_damage_1k] - title = "Hackenkaempfer" - description = "Verursache 1.000 Schaden mit einer Spitzhacke" +title = "Hackenkaempfer" +description = "Verursache 1.000 Schaden mit einer Spitzhacke" [advancement.challenge_pick_damage_10k] - title = "Kriegshacke" - description = "Verursache 10.000 Schaden mit einer Spitzhacke" +title = "Kriegshacke" +description = "Verursache 10.000 Schaden mit einer Spitzhacke" [advancement.challenge_pick_value_5k] - title = "Edelsteinfinder" - description = "Baue Bloecke im Wert von 5.000 ab" +title = "Edelsteinfinder" +description = "Baue Bloecke im Wert von 5.000 ab" [advancement.challenge_pick_value_50k] - title = "Erzbaron" - description = "Baue Bloecke im Wert von 50.000 ab" +title = "Erzbaron" +description = "Baue Bloecke im Wert von 50.000 ab" [advancement.challenge_pick_ores_500] - title = "Schuerfer" - description = "Baue 500 Erzbloecke ab" +title = "Schuerfer" +description = "Baue 500 Erzbloecke ab" [advancement.challenge_pick_ores_5k] - title = "Meisterbergmann" - description = "Baue 5.000 Erzbloecke ab" +title = "Meisterbergmann" +description = "Baue 5.000 Erzbloecke ab" # Ranged [advancement.challenge_ranged_dmg_1k] - title = "Scharfschuetze" - description = "Verursache 1.000 Fernkampfschaden" +title = "Scharfschuetze" +description = "Verursache 1.000 Fernkampfschaden" [advancement.challenge_ranged_dmg_10k] - title = "Toedlicher Schuetze" - description = "Verursache 10.000 Fernkampfschaden" +title = "Toedlicher Schuetze" +description = "Verursache 10.000 Fernkampfschaden" [advancement.challenge_ranged_dist_5k] - title = "Grosse Reichweite" - description = "Feuere Projektile ueber insgesamt 5.000 Bloecke Entfernung" +title = "Grosse Reichweite" +description = "Feuere Projektile ueber insgesamt 5.000 Bloecke Entfernung" [advancement.challenge_ranged_dist_50k] - title = "Meilenschuetze" - description = "Feuere Projektile ueber insgesamt 50.000 Bloecke Entfernung" +title = "Meilenschuetze" +description = "Feuere Projektile ueber insgesamt 50.000 Bloecke Entfernung" [advancement.challenge_ranged_kills_50] - title = "Bogenschuetze" - description = "Toete 50 Kreaturen mit Fernkampfwaffen" +title = "Bogenschuetze" +description = "Toete 50 Kreaturen mit Fernkampfwaffen" [advancement.challenge_ranged_kills_500] - title = "Meisterbogenschuetze" - description = "Toete 500 Kreaturen mit Fernkampfwaffen" +title = "Meisterbogenschuetze" +description = "Toete 500 Kreaturen mit Fernkampfwaffen" [advancement.challenge_longshot_25] - title = "Heckenschuetze" - description = "Lande 25 Weitschusstreffer (ueber 30 Bloecke)" +title = "Heckenschuetze" +description = "Lande 25 Weitschusstreffer (ueber 30 Bloecke)" [advancement.challenge_longshot_250] - title = "Adlerauge" - description = "Lande 250 Weitschusstreffer (ueber 30 Bloecke)" +title = "Adlerauge" +description = "Lande 250 Weitschusstreffer (ueber 30 Bloecke)" # Rift [advancement.challenge_rift_pearls_50] - title = "Perlenwerfer" - description = "Wirf 50 Enderperlen" +title = "Perlenwerfer" +description = "Wirf 50 Enderperlen" [advancement.challenge_rift_pearls_500] - title = "Teleportsuechtig" - description = "Wirf 500 Enderperlen" +title = "Teleportsuechtig" +description = "Wirf 500 Enderperlen" [advancement.challenge_rift_enderman_50] - title = "Enderman-Jaeger" - description = "Erschlage 50 Endermen" +title = "Enderman-Jaeger" +description = "Erschlage 50 Endermen" [advancement.challenge_rift_enderman_500] - title = "Leerenpirscher" - description = "Erschlage 500 Endermen" +title = "Leerenpirscher" +description = "Erschlage 500 Endermen" [advancement.challenge_rift_dragon_500] - title = "Drachenkaempfer" - description = "Verursache 500 Schaden am Enderdrachen" +title = "Drachenkaempfer" +description = "Verursache 500 Schaden am Enderdrachen" [advancement.challenge_rift_dragon_5k] - title = "Drachentoeter" - description = "Verursache 5.000 Schaden am Enderdrachen" +title = "Drachentoeter" +description = "Verursache 5.000 Schaden am Enderdrachen" [advancement.challenge_rift_crystal_10] - title = "Kristallbrecher" - description = "Zerstoere 10 Endkristalle" +title = "Kristallbrecher" +description = "Zerstoere 10 Endkristalle" [advancement.challenge_rift_crystal_100] - title = "Endzerstoerer" - description = "Zerstoere 100 Endkristalle" +title = "Endzerstoerer" +description = "Zerstoere 100 Endkristalle" # Seaborne [advancement.challenge_fish_25] - title = "Angler" - description = "Fange 25 Fische" +title = "Angler" +description = "Fange 25 Fische" [advancement.challenge_fish_250] - title = "Meisterfischer" - description = "Fange 250 Fische" +title = "Meisterfischer" +description = "Fange 250 Fische" [advancement.challenge_drowned_25] - title = "Ertrunkenen-Jaeger" - description = "Erschlage 25 Ertrunkene" +title = "Ertrunkenen-Jaeger" +description = "Erschlage 25 Ertrunkene" [advancement.challenge_drowned_250] - title = "Ozeanreiniger" - description = "Erschlage 250 Ertrunkene" +title = "Ozeanreiniger" +description = "Erschlage 250 Ertrunkene" [advancement.challenge_guardian_10] - title = "Waechtertoeter" - description = "Erschlage 10 Waechter" +title = "Waechtertoeter" +description = "Erschlage 10 Waechter" [advancement.challenge_guardian_100] - title = "Tempelraeuber" - description = "Erschlage 100 Waechter" +title = "Tempelraeuber" +description = "Erschlage 100 Waechter" [advancement.challenge_underwater_blocks_100] - title = "Unterwasserbergmann" - description = "Baue 100 Bloecke unter Wasser ab" +title = "Unterwasserbergmann" +description = "Baue 100 Bloecke unter Wasser ab" [advancement.challenge_underwater_blocks_1k] - title = "Unterwasseringenieur" - description = "Baue 1.000 Bloecke unter Wasser ab" +title = "Unterwasseringenieur" +description = "Baue 1.000 Bloecke unter Wasser ab" # Stealth [advancement.challenge_stealth_dmg_500] - title = "Meuchler" - description = "Verursache 500 Schaden waehrend du schleichst" +title = "Meuchler" +description = "Verursache 500 Schaden waehrend du schleichst" [advancement.challenge_stealth_dmg_5k] - title = "Lautloser Toeter" - description = "Verursache 5.000 Schaden waehrend du schleichst" +title = "Lautloser Toeter" +description = "Verursache 5.000 Schaden waehrend du schleichst" [advancement.challenge_stealth_kills_10] - title = "Assassine" - description = "Toete 10 Kreaturen waehrend du schleichst" +title = "Assassine" +description = "Toete 10 Kreaturen waehrend du schleichst" [advancement.challenge_stealth_kills_100] - title = "Schattensensenmann" - description = "Toete 100 Kreaturen waehrend du schleichst" +title = "Schattensensenmann" +description = "Toete 100 Kreaturen waehrend du schleichst" [advancement.challenge_stealth_time_1h] - title = "Geduldig" - description = "Verbringe 1 Stunde schleichend (3.600 Sekunden)" +title = "Geduldig" +description = "Verbringe 1 Stunde schleichend (3.600 Sekunden)" [advancement.challenge_stealth_time_10h] - title = "Meister der Schatten" - description = "Verbringe 10 Stunden schleichend (36.000 Sekunden)" +title = "Meister der Schatten" +description = "Verbringe 10 Stunden schleichend (36.000 Sekunden)" [advancement.challenge_stealth_arrows_50] - title = "Stiller Bogenschuetze" - description = "Feuere 50 Pfeile waehrend du schleichst" +title = "Stiller Bogenschuetze" +description = "Feuere 50 Pfeile waehrend du schleichst" [advancement.challenge_stealth_arrows_500] - title = "Phantombogenschuetze" - description = "Feuere 500 Pfeile waehrend du schleichst" +title = "Phantombogenschuetze" +description = "Feuere 500 Pfeile waehrend du schleichst" # Swords [advancement.challenge_sword_dmg_1k] - title = "Klingenlehrling" - description = "Verursache 1.000 Schaden mit Schwertern" +title = "Klingenlehrling" +description = "Verursache 1.000 Schaden mit Schwertern" [advancement.challenge_sword_dmg_10k] - title = "Schwertkaempfer" - description = "Verursache 10.000 Schaden mit Schwertern" +title = "Schwertkaempfer" +description = "Verursache 10.000 Schaden mit Schwertern" [advancement.challenge_sword_kills_50] - title = "Duellant" - description = "Toete 50 Kreaturen mit Schwertern" +title = "Duellant" +description = "Toete 50 Kreaturen mit Schwertern" [advancement.challenge_sword_kills_500] - title = "Gladiator" - description = "Toete 500 Kreaturen mit Schwertern" +title = "Gladiator" +description = "Toete 500 Kreaturen mit Schwertern" [advancement.challenge_sword_crit_50] - title = "Kritischer Treffer" - description = "Lande 50 kritische Treffer mit Schwertern" +title = "Kritischer Treffer" +description = "Lande 50 kritische Treffer mit Schwertern" [advancement.challenge_sword_crit_500] - title = "Praezisionsmeister" - description = "Lande 500 kritische Treffer mit Schwertern" +title = "Praezisionsmeister" +description = "Lande 500 kritische Treffer mit Schwertern" [advancement.challenge_sword_heavy_25] - title = "Schwerer Schlag" - description = "Lande 25 schwere Treffer mit Schwertern (ueber 8 Schaden)" +title = "Schwerer Schlag" +description = "Lande 25 schwere Treffer mit Schwertern (ueber 8 Schaden)" [advancement.challenge_sword_heavy_250] - title = "Verheerender Hieb" - description = "Lande 250 schwere Treffer mit Schwertern (ueber 8 Schaden)" +title = "Verheerender Hieb" +description = "Lande 250 schwere Treffer mit Schwertern (ueber 8 Schaden)" # Taming [advancement.challenge_pet_dmg_500] - title = "Tiertrainer" - description = "Deine Haustiere verursachen insgesamt 500 Schaden" +title = "Tiertrainer" +description = "Deine Haustiere verursachen insgesamt 500 Schaden" [advancement.challenge_pet_dmg_5k] - title = "Kriegsmeister" - description = "Deine Haustiere verursachen insgesamt 5.000 Schaden" +title = "Kriegsmeister" +description = "Deine Haustiere verursachen insgesamt 5.000 Schaden" [advancement.challenge_tamed_10] - title = "Tierfreund" - description = "Zaehme 10 Tiere" +title = "Tierfreund" +description = "Zaehme 10 Tiere" [advancement.challenge_tamed_100] - title = "Zoowaerter" - description = "Zaehme 100 Tiere" +title = "Zoowaerter" +description = "Zaehme 100 Tiere" [advancement.challenge_pet_kills_25] - title = "Rudeltaktik" - description = "Deine Haustiere erschlagen 25 Kreaturen" +title = "Rudeltaktik" +description = "Deine Haustiere erschlagen 25 Kreaturen" [advancement.challenge_pet_kills_250] - title = "Alphakommandant" - description = "Deine Haustiere erschlagen 250 Kreaturen" +title = "Alphakommandant" +description = "Deine Haustiere erschlagen 250 Kreaturen" [advancement.challenge_taming_2500] - title = "Zuchtexperte" - description = "Zuechte 2.500 Tiere" +title = "Zuchtexperte" +description = "Zuechte 2.500 Tiere" [advancement.challenge_taming_25k] - title = "Genetikmeister" - description = "Zuechte 25.000 Tiere" +title = "Genetikmeister" +description = "Zuechte 25.000 Tiere" # TragOul [advancement.challenge_trag_hits_500] - title = "Sandsack" - description = "Erhalte 500 Treffer" +title = "Sandsack" +description = "Erhalte 500 Treffer" [advancement.challenge_trag_hits_5k] - title = "Glutton fuer Strafe" - description = "Erhalte 5.000 Treffer" +title = "Glutton fuer Strafe" +description = "Erhalte 5.000 Treffer" [advancement.challenge_trag_deaths_10] - title = "Neun Leben" - description = "Stirb 10 Mal" +title = "Neun Leben" +description = "Stirb 10 Mal" [advancement.challenge_trag_deaths_100] - title = "Wiederkehrender Albtraum" - description = "Stirb 100 Mal" +title = "Wiederkehrender Albtraum" +description = "Stirb 100 Mal" [advancement.challenge_trag_fire_500] - title = "Brandopfer" - description = "Ertrage 500 Feuerschaden" +title = "Brandopfer" +description = "Ertrage 500 Feuerschaden" [advancement.challenge_trag_fire_5k] - title = "Phoeinx" - description = "Ertrage 5.000 Feuerschaden" +title = "Phoeinx" +description = "Ertrage 5.000 Feuerschaden" [advancement.challenge_trag_fall_500] - title = "Schwerkrafttest" - description = "Ertrage 500 Fallschaden" +title = "Schwerkrafttest" +description = "Ertrage 500 Fallschaden" [advancement.challenge_trag_fall_5k] - title = "Endgeschwindigkeit" - description = "Ertrage 5.000 Fallschaden" +title = "Endgeschwindigkeit" +description = "Ertrage 5.000 Fallschaden" # Unarmed [advancement.challenge_unarmed_dmg_1k] - title = "Raufbold" - description = "Verursache 1.000 Schaden mit blossen Faeusten" +title = "Raufbold" +description = "Verursache 1.000 Schaden mit blossen Faeusten" [advancement.challenge_unarmed_dmg_10k] - title = "Kampfkuenstler" - description = "Verursache 10.000 Schaden mit blossen Faeusten" +title = "Kampfkuenstler" +description = "Verursache 10.000 Schaden mit blossen Faeusten" [advancement.challenge_unarmed_kills_25] - title = "Faustkampf" - description = "Toete 25 Kreaturen mit blossen Faeusten" +title = "Faustkampf" +description = "Toete 25 Kreaturen mit blossen Faeusten" [advancement.challenge_unarmed_kills_250] - title = "Faust der Legende" - description = "Toete 250 Kreaturen mit blossen Faeusten" +title = "Faust der Legende" +description = "Toete 250 Kreaturen mit blossen Faeusten" [advancement.challenge_unarmed_crit_25] - title = "Kritischer Schlag" - description = "Lande 25 kritische Treffer mit blossen Faeusten" +title = "Kritischer Schlag" +description = "Lande 25 kritische Treffer mit blossen Faeusten" [advancement.challenge_unarmed_crit_250] - title = "Praezisionsfaust" - description = "Lande 250 kritische Treffer mit blossen Faeusten" +title = "Praezisionsfaust" +description = "Lande 250 kritische Treffer mit blossen Faeusten" [advancement.challenge_unarmed_heavy_25] - title = "Kraftschlag" - description = "Lande 25 schwere Treffer mit blossen Faeusten (ueber 6 Schaden)" +title = "Kraftschlag" +description = "Lande 25 schwere Treffer mit blossen Faeusten (ueber 6 Schaden)" [advancement.challenge_unarmed_heavy_250] - title = "Knockout-Koenig" - description = "Lande 250 schwere Treffer mit blossen Faeusten (ueber 6 Schaden)" +title = "Knockout-Koenig" +description = "Lande 250 schwere Treffer mit blossen Faeusten (ueber 6 Schaden)" # items [items] [items.bound_ender_peral] - name = "Reliquien-Portschlüssel" - usage1 = "Shift + Linksklick zum Binden" - usage2 = "Rechtsklick, um auf das gebundene Inventar zuzugreifen" +name = "Reliquien-Portschlüssel" +usage1 = "Shift + Linksklick zum Binden" +usage2 = "Rechtsklick, um auf das gebundene Inventar zuzugreifen" [items.bound_eye_of_ender] - name = "Okularanker" - usage1 = "Rechtsklick zum Verbrauchen und Teleportieren zum gebundenen Ort" - usage2 = "Shift + Linksklick, um an einen Block zu binden" +name = "Okularanker" +usage1 = "Rechtsklick zum Verbrauchen und Teleportieren zum gebundenen Ort" +usage2 = "Shift + Linksklick, um an einen Block zu binden" [items.bound_redstone_torch] - name = "Redstone-Fernsteuerung" - usage1 = "Rechtsklick, um einen 1-Tick-Redstone-Impuls zu erzeugen" - usage2 = "Shift + Linksklick auf einen 'Ziel'-Block zum Binden" +name = "Redstone-Fernsteuerung" +usage1 = "Rechtsklick, um einen 1-Tick-Redstone-Impuls zu erzeugen" +usage2 = "Shift + Linksklick auf einen 'Ziel'-Block zum Binden" [items.bound_snowball] - name = "Netzfalle!" - usage1 = "Werfen, um eine vorübergehende Spinnennetzfalle am Auftreffpunkt zu erstellen" +name = "Netzfalle!" +usage1 = "Werfen, um eine vorübergehende Spinnennetzfalle am Auftreffpunkt zu erstellen" [items.chrono_time_bottle] - name = "Zeit in einer Flasche" - usage1 = "Speichert passiv Zeit, solange sie sich in deinem Inventar befindet" - usage2 = "Rechtsklick auf zeitbasierte Blöcke oder Tierbabys, um gespeicherte Zeit zu verbrauchen" - stored = "Gespeicherte Zeit" +name = "Zeit in einer Flasche" +usage1 = "Speichert passiv Zeit, solange sie sich in deinem Inventar befindet" +usage2 = "Rechtsklick auf zeitbasierte Blöcke oder Tierbabys, um gespeicherte Zeit zu verbrauchen" +stored = "Gespeicherte Zeit" [items.chrono_time_bomb] - name = "Zeitbombe" - usage1 = "Rechtsklick, um einen Chronobolzen zu werfen, der ein Zeitfeld erzeugt" +name = "Zeitbombe" +usage1 = "Rechtsklick, um einen Chronobolzen zu werfen, der ein Zeitfeld erzeugt" [items.elevator_block] - name = "Aufzugblock" - usage1 = "Springen, um nach oben zu teleportieren" - usage2 = "Schleichen, um nach unten zu teleportieren" - usage3 = "Mindestens 2 Luftblöcke zwischen den Aufzügen" +name = "Aufzugblock" +usage1 = "Springen, um nach oben zu teleportieren" +usage2 = "Schleichen, um nach unten zu teleportieren" +usage3 = "Mindestens 2 Luftblöcke zwischen den Aufzügen" # snippets [snippets] [snippets.gui] - level = "Stufe" - knowledge = "Wissen" - power_used = "Verbrauchte Energie" - not_learned = "Noch nicht gelernt" - xp = "XP bis" - welcome = "Willkommen!" - welcome_back = "Willkommen zurück!" - xp_bonus_for_time = "XP für" - max_ability_power = "Maximale Fähigkeitsenergie" - unlock_this_by_clicking = "Entsperre dies per Rechtsklick: " - back = "Zurück" - unlearn_all = "Alles verlernen" - unlearned_all = "Alles verlernt" +level = "Stufe" +knowledge = "Wissen" +power_used = "Verbrauchte Energie" +not_learned = "Noch nicht gelernt" +xp = "XP bis" +welcome = "Willkommen!" +welcome_back = "Willkommen zurück!" +xp_bonus_for_time = "XP für" +max_ability_power = "Maximale Fähigkeitsenergie" +unlock_this_by_clicking = "Entsperre dies per Rechtsklick: " +back = "Zurück" +unlearn_all = "Alles verlernen" +unlearned_all = "Alles verlernt" [snippets.adapt_menu] - may_not_unlearn = "KANN NICHT VERLERNT WERDEN" - may_unlearn = "KANN GELERNT/VERLERNT WERDEN" - knowledge_cost = "Wissenskosten" - knowledge_available = "Verfügbares Wissen" - already_learned = "Bereits gelernt" - unlearn_refund = "Klicken zum Verlernen & Erstatten" - no_refunds = "HARDCORE, ERSTATTUNGEN DEAKTIVIERT" - knowledge = "Wissen" - click_learn = "Klicken zum Erlernen" - no_knowledge = "(Du hast kein Wissen)" - you_only_have = "Du hast nur" - how_to_level_up = "Verbessere deine Fähigkeiten, um deine maximale Energie zu erhöhen." - not_enough_power = "Nicht genug Energie! Jede Fähigkeitsstufe kostet 1 Energie." - power = "Energie" - power_drain = "Energieverbrauch" - learned = "Gelernt " - unlearned = "Verlernt " - activator_block = "Bücherregal" +may_not_unlearn = "KANN NICHT VERLERNT WERDEN" +may_unlearn = "KANN GELERNT/VERLERNT WERDEN" +knowledge_cost = "Wissenskosten" +knowledge_available = "Verfügbares Wissen" +already_learned = "Bereits gelernt" +unlearn_refund = "Klicken zum Verlernen & Erstatten" +no_refunds = "HARDCORE, ERSTATTUNGEN DEAKTIVIERT" +knowledge = "Wissen" +click_learn = "Klicken zum Erlernen" +no_knowledge = "(Du hast kein Wissen)" +you_only_have = "Du hast nur" +how_to_level_up = "Verbessere deine Fähigkeiten, um deine maximale Energie zu erhöhen." +not_enough_power = "Nicht genug Energie! Jede Fähigkeitsstufe kostet 1 Energie." +power = "Energie" +power_drain = "Energieverbrauch" +learned = "Gelernt " +unlearned = "Verlernt " +activator_block = "Bücherregal" [snippets.knowledge_orb] - contains = "enthält" - knowledge = "Wissen" - rightclick = "Rechtsklick" - togainknowledge = "um dieses Wissen zu erlangen" - knowledge_orb = "Wissenskugel" +contains = "enthält" +knowledge = "Wissen" +rightclick = "Rechtsklick" +togainknowledge = "um dieses Wissen zu erlangen" +knowledge_orb = "Wissenskugel" [snippets.experience_orb] - contains = "enthält" - xp = "Erfahrung" - rightclick = "Rechtsklick" - togainxp = "um diese Erfahrung zu erlangen" - xporb = "Erfahrungskugel" +contains = "enthält" +xp = "Erfahrung" +rightclick = "Rechtsklick" +togainxp = "um diese Erfahrung zu erlangen" +xporb = "Erfahrungskugel" # skill [skill] [skill.agility] - name = "Agilität" - icon = "⇉" - description = "Agilität ist die Fähigkeit, sich schnell und geschmeidig angesichts von Hindernissen zu bewegen." +name = "Agilität" +icon = "⇉" +description = "Agilität ist die Fähigkeit, sich schnell und geschmeidig angesichts von Hindernissen zu bewegen." [skill.architect] - name = "Architekt" - icon = "⬧" - description = "Strukturen sind die Bausteine der Welt. Die Realität liegt in deinen Händen, du bestimmst sie." +name = "Architekt" +icon = "⬧" +description = "Strukturen sind die Bausteine der Welt. Die Realität liegt in deinen Händen, du bestimmst sie." [skill.axes] - name = "Äxte" - icon = "🪓" - description1 = "Warum Bäume fällen, wenn man stattdessen " - description2 = "Dinge" - description3 = "fällen kann - gleiches Ergebnis!" +name = "Äxte" +icon = "🪓" +description1 = "Warum Bäume fällen, wenn man stattdessen " +description2 = "Dinge" +description3 = "fällen kann - gleiches Ergebnis!" [skill.brewing] - name = "Brauen" - icon = "❦" - description = "Doppelte Blase, dreifache Blase, vierfache Blase - Ich kann diesen Trank immer noch nicht in einen Kessel füllen" +name = "Brauen" +icon = "❦" +description = "Doppelte Blase, dreifache Blase, vierfache Blase - Ich kann diesen Trank immer noch nicht in einen Kessel füllen" [skill.blocking] - name = "Blocken" - icon = "🛡" - description = "Stöcke und Steine brechen dir nicht die Knochen, aber ein Schild schon." +name = "Blocken" +icon = "🛡" +description = "Stöcke und Steine brechen dir nicht die Knochen, aber ein Schild schon." [skill.crafting] - name = "Handwerk" - icon = "⌂" - description = "Wenn keine Teile mehr zu platzieren sind, warum nicht einfach neue herstellen?" +name = "Handwerk" +icon = "⌂" +description = "Wenn keine Teile mehr zu platzieren sind, warum nicht einfach neue herstellen?" [skill.discovery] - name = "Entdeckung" - icon = "⚛" - description = "Wenn sich deine Wahrnehmung erweitert, entfaltet sich dein Geist und entdeckt das, was dir verborgen blieb." +name = "Entdeckung" +icon = "⚛" +description = "Wenn sich deine Wahrnehmung erweitert, entfaltet sich dein Geist und entdeckt das, was dir verborgen blieb." [skill.enchanting] - name = "Verzauberung" - icon = "♰" - description = "Wovon redest du eigentlich? Prophezeiungen, Visionen, abergläubisches Gefasel?" +name = "Verzauberung" +icon = "♰" +description = "Wovon redest du eigentlich? Prophezeiungen, Visionen, abergläubisches Gefasel?" [skill.excavation] - name = "Ausgrabung" - icon = "ᛳ" - description = "Diggy Diggy Hole..." +name = "Ausgrabung" +icon = "ᛳ" +description = "Diggy Diggy Hole..." [skill.herbalism] - name = "Kräuterkunde" - icon = "⚘" - description = "Ich kann keine Pflanzen finden, aber ein paar Samen und - ist das... Unkraut?" +name = "Kräuterkunde" +icon = "⚘" +description = "Ich kann keine Pflanzen finden, aber ein paar Samen und - ist das... Unkraut?" [skill.hunter] - name = "Jäger" - icon = "☠" - description = "Bei der Jagd geht es um den Weg, nicht um das Ergebnis." +name = "Jäger" +icon = "☠" +description = "Bei der Jagd geht es um den Weg, nicht um das Ergebnis." [skill.nether] - name = "Nether" - icon = "₪" - description = "Aus den Tiefen des Nethers selbst." +name = "Nether" +icon = "₪" +description = "Aus den Tiefen des Nethers selbst." [skill.pickaxe] - name = "Spitzhacke" - icon = "⛏" - description = "Zwerge sind die Bergleute, aber ich habe in meiner Zeit auch einiges gelernt. ICH BIN SCHWEDE!" +name = "Spitzhacke" +icon = "⛏" +description = "Zwerge sind die Bergleute, aber ich habe in meiner Zeit auch einiges gelernt. ICH BIN SCHWEDE!" [skill.ranged] - name = "Fernkampf" - icon = "🏹" - description = "Entfernung ist der Schlüssel zum Sieg und der Schlüssel zum Überleben." +name = "Fernkampf" +icon = "🏹" +description = "Entfernung ist der Schlüssel zum Sieg und der Schlüssel zum Überleben." [skill.rift] - name = "Riss" - icon = "❍" - description = "Der Riss ist ein ätzendes Geschirr, aber du hast das Geschirr gemeistert." +name = "Riss" +icon = "❍" +description = "Der Riss ist ein ätzendes Geschirr, aber du hast das Geschirr gemeistert." [skill.seaborne] - name = "Seefahrt" - icon = "🎣" - description = "Mit dieser Fähigkeit kannst du die Wunder des Wassers beherrschen." +name = "Seefahrt" +icon = "🎣" +description = "Mit dieser Fähigkeit kannst du die Wunder des Wassers beherrschen." [skill.stealth] - name = "Heimlichkeit" - icon = "☯" - description = "Die Kunst des Unsichtbaren. Wandle in den Schatten." +name = "Heimlichkeit" +icon = "☯" +description = "Die Kunst des Unsichtbaren. Wandle in den Schatten." [skill.swords] - name = "Schwerter" - icon = "⚔" - description = "Bei der Macht von GreyStone!" +name = "Schwerter" +icon = "⚔" +description = "Bei der Macht von GreyStone!" [skill.taming] - name = "Zähmung" - icon = "♥" - description = "Die Papageien und die Bienen... und du?" +name = "Zähmung" +icon = "♥" +description = "Die Papageien und die Bienen... und du?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Blut fließt durch die Adern des Universums. Zusammengepresst von deinen Händen." +name = "TragOul" +icon = "🗡" +description = "Blut fließt durch die Adern des Universums. Zusammengepresst von deinen Händen." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Ziehe die Uhr des Universums auf, erlebe den Fluss. Zerbrich die Uhr, werde sie." +name = "Chronos" +icon = "🕒" +description = "Ziehe die Uhr des Universums auf, erlebe den Fluss. Zerbrich die Uhr, werde sie." [skill.unarmed] - name = "Unbewaffnet" - icon = "»" - description = "Ohne Waffe heißt nicht ohne Stärke." +name = "Unbewaffnet" +icon = "»" +description = "Ohne Waffe heißt nicht ohne Stärke." # agility [agility] [agility.armor_up] - name = "Aufrüstung" - description = "Erhalte mehr Rüstung, je länger du sprintest!" - lore1 = "Maximale Rüstung" - lore2 = "Aufrüstungszeit" - lore = ["Maximale Rüstung", "Aufrüstungszeit"] +name = "Aufrüstung" +description = "Erhalte mehr Rüstung, je länger du sprintest!" +lore1 = "Maximale Rüstung" +lore2 = "Aufrüstungszeit" +lore = ["Maximale Rüstung", "Aufrüstungszeit"] [agility.ladder_slide] - name = "Leiterrutsche" - description = "Klettere und rutsche Leitern in beide Richtungen viel schneller." - lore1 = "Leitergeschwindigkeits-Multiplikator" - lore2 = "Schnelle Abstiegsgeschwindigkeit" - lore = ["Leitergeschwindigkeits-Multiplikator", "Schnelle Abstiegsgeschwindigkeit"] +name = "Leiterrutsche" +description = "Klettere und rutsche Leitern in beide Richtungen viel schneller." +lore1 = "Leitergeschwindigkeits-Multiplikator" +lore2 = "Schnelle Abstiegsgeschwindigkeit" +lore = ["Leitergeschwindigkeits-Multiplikator", "Schnelle Abstiegsgeschwindigkeit"] [agility.super_jump] - name = "Supersprung" - description = "Außergewöhnlicher Höhenvorteil." - lore1 = "Maximale Sprunghöhe" - lore2 = "Schleichen + Springen für Supersprung!" - lore = ["Maximale Sprunghöhe", "Schleichen + Springen für Supersprung!"] +name = "Supersprung" +description = "Außergewöhnlicher Höhenvorteil." +lore1 = "Maximale Sprunghöhe" +lore2 = "Schleichen + Springen für Supersprung!" +lore = ["Maximale Sprunghöhe", "Schleichen + Springen für Supersprung!"] [agility.wall_jump] - name = "Wandsprung" - description = "Halte Shift in der Luft an einer Wand, um dich festzuhalten und zu springen!" - lore1 = "Maximale Sprünge" - lore2 = "Sprunghöhe" - lore = ["Maximale Sprünge", "Sprunghöhe"] +name = "Wandsprung" +description = "Halte Shift in der Luft an einer Wand, um dich festzuhalten und zu springen!" +lore1 = "Maximale Sprünge" +lore2 = "Sprunghöhe" +lore = ["Maximale Sprünge", "Sprunghöhe"] [agility.wind_up] - name = "Anlauf" - description = "Werde schneller, je länger du sprintest!" - lore1 = "Maximale Geschwindigkeit" - lore2 = "Anlaufzeit" - lore = ["Maximale Geschwindigkeit", "Anlaufzeit"] +name = "Anlauf" +description = "Werde schneller, je länger du sprintest!" +lore1 = "Maximale Geschwindigkeit" +lore2 = "Anlaufzeit" +lore = ["Maximale Geschwindigkeit", "Anlaufzeit"] # architect [architect] [architect.elevator] - name = "Aufzug" - description = "Ermöglicht dir den Bau eines Aufzugs für schnelle vertikale Teleportation!" - lore1 = "Schaltet Aufzugrezept frei: X=WOLLE, Y=ENDERPERLE" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Schaltet Aufzugrezept frei: X=WOLLE, Y=ENDERPERLE", "XXX", "XYX", "XXX"] +name = "Aufzug" +description = "Ermöglicht dir den Bau eines Aufzugs für schnelle vertikale Teleportation!" +lore1 = "Schaltet Aufzugrezept frei: X=WOLLE, Y=ENDERPERLE" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Schaltet Aufzugrezept frei: X=WOLLE, Y=ENDERPERLE", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Magisches Fundament" - description = "Ermöglicht dir, beim Schleichen ein vorübergehendes Fundament unter dir zu erschaffen!" - lore1 = "Magisch erschaffen: " - lore2 = "Blöcke unter dir!" - lore = ["Magisch erschaffen: ", "Blöcke unter dir!"] +name = "Magisches Fundament" +description = "Ermöglicht dir, beim Schleichen ein vorübergehendes Fundament unter dir zu erschaffen!" +lore1 = "Magisch erschaffen: " +lore2 = "Blöcke unter dir!" +lore = ["Magisch erschaffen: ", "Blöcke unter dir!"] [architect.glass] - name = "Behutsamkeits-Glas" - description = "Verhindert den Verlust von Glasblöcken, wenn du sie mit leeren Händen zerbrichst!" - lore1 = "Deine Hände erhalten Behutsamkeit für Glas" - lore = ["Deine Hände erhalten Behutsamkeit für Glas"] +name = "Behutsamkeits-Glas" +description = "Verhindert den Verlust von Glasblöcken, wenn du sie mit leeren Händen zerbrichst!" +lore1 = "Deine Hände erhalten Behutsamkeit für Glas" +lore = ["Deine Hände erhalten Behutsamkeit für Glas"] [architect.wireless_redstone] - name = "Redstone-Fernsteuerung" - description = "Ermöglicht dir, eine Redstone-Fackel zu verwenden, um Redstone aus der Ferne zu schalten!" - lore1 = "Ziel + Redstone-Fackel + Enderperle = 1 Redstone-Fernsteuerung" - lore = ["Ziel + Redstone-Fackel + Enderperle = 1 Redstone-Fernsteuerung"] +name = "Redstone-Fernsteuerung" +description = "Ermöglicht dir, eine Redstone-Fackel zu verwenden, um Redstone aus der Ferne zu schalten!" +lore1 = "Ziel + Redstone-Fackel + Enderperle = 1 Redstone-Fernsteuerung" +lore = ["Ziel + Redstone-Fackel + Enderperle = 1 Redstone-Fernsteuerung"] [architect.placement] - name = "Baustab" - description = "Ermöglicht dir, mehrere Blöcke auf einmal zu platzieren. Zum Aktivieren schleiche und halte einen Block, der dem anvisierten Block entspricht, und platziere! Beachte, dass du dich etwas bewegen musst, um die Begrenzung auszulösen!" - lore1 = "Du brauchst" - lore2 = "Blöcke in deiner Hand, um dies zu platzieren" - lore3 = "Ein Material-Baustab" - lore = ["Du brauchst", "Blöcke in deiner Hand, um dies zu platzieren", "Ein Material-Baustab"] +name = "Baustab" +description = "Ermöglicht dir, mehrere Blöcke auf einmal zu platzieren. Zum Aktivieren schleiche und halte einen Block, der dem anvisierten Block entspricht, und platziere! Beachte, dass du dich etwas bewegen musst, um die Begrenzung auszulösen!" +lore1 = "Du brauchst" +lore2 = "Blöcke in deiner Hand, um dies zu platzieren" +lore3 = "Ein Material-Baustab" +lore = ["Du brauchst", "Blöcke in deiner Hand, um dies zu platzieren", "Ein Material-Baustab"] # axe [axe] [axe.chop] - name = "Axtschlag" - description = "Fälle Bäume per Rechtsklick auf den Stamm!" - lore1 = "Blöcke pro Hieb" - lore2 = "Hieb-Abklingzeit" - lore3 = "Werkzeugverschleiß" - lore = ["Blöcke pro Hieb", "Hieb-Abklingzeit", "Werkzeugverschleiß"] +name = "Axtschlag" +description = "Fälle Bäume per Rechtsklick auf den Stamm!" +lore1 = "Blöcke pro Hieb" +lore2 = "Hieb-Abklingzeit" +lore3 = "Werkzeugverschleiß" +lore = ["Blöcke pro Hieb", "Hieb-Abklingzeit", "Werkzeugverschleiß"] [axe.log_swap] - name = "Lucys Stammtauscher" - description = "Ändere die Art von Holzstämmen in einer Werkbank!" - lore1 = "8 Stämme beliebiger Art + 1 Setzling = 8 Stämme der Art des Setzlings" - lore = ["8 Stämme beliebiger Art + 1 Setzling = 8 Stämme der Art des Setzlings"] +name = "Lucys Stammtauscher" +description = "Ändere die Art von Holzstämmen in einer Werkbank!" +lore1 = "8 Stämme beliebiger Art + 1 Setzling = 8 Stämme der Art des Setzlings" +lore = ["8 Stämme beliebiger Art + 1 Setzling = 8 Stämme der Art des Setzlings"] [axe.drop_to_inventory] - name = "Axt-Drop-ins-Inventar" +name = "Axt-Drop-ins-Inventar" [axe.ground_smash] - name = "Axt-Bodenschlag" - description = "Springe, dann schleiche und zerschmettere alle Feinde in der Nähe." - lore1 = "Schaden" - lore2 = "Block-Radius" - lore3 = "Wucht" - lore4 = "Schlag-Abklingzeit" - lore = ["Schaden", "Block-Radius", "Wucht", "Schlag-Abklingzeit"] +name = "Axt-Bodenschlag" +description = "Springe, dann schleiche und zerschmettere alle Feinde in der Nähe." +lore1 = "Schaden" +lore2 = "Block-Radius" +lore3 = "Wucht" +lore4 = "Schlag-Abklingzeit" +lore = ["Schaden", "Block-Radius", "Wucht", "Schlag-Abklingzeit"] [axe.leaf_miner] - name = "Blattfresser" - description = "Ermöglicht dir, große Mengen Blätter auf einmal abzubauen!" - lore1 = "Schleiche und baue BLÄTTER ab" - lore2 = "Reichweite des Blattabbaus" - lore3 = "Du erhältst keine Drops von den Blättern (Exploit-Schutz)" - lore = ["Schleiche und baue BLÄTTER ab", "Reichweite des Blattabbaus", "Du erhältst keine Drops von den Blättern (Exploit-Schutz)"] +name = "Blattfresser" +description = "Ermöglicht dir, große Mengen Blätter auf einmal abzubauen!" +lore1 = "Schleiche und baue BLÄTTER ab" +lore2 = "Reichweite des Blattabbaus" +lore3 = "Du erhältst keine Drops von den Blättern (Exploit-Schutz)" +lore = ["Schleiche und baue BLÄTTER ab", "Reichweite des Blattabbaus", "Du erhältst keine Drops von den Blättern (Exploit-Schutz)"] [axe.wood_miner] - name = "Holzfäller" - description = "Ermöglicht dir, große Mengen Holz auf einmal abzubauen!" - lore1 = "Schleiche und fälle HOLZ/STÄMME (keine Bretter)" - lore2 = "Reichweite des Holzabbaus" - lore3 = "Funktioniert mit Drop-ins-Inventar" - lore = ["Schleiche und fälle HOLZ/STÄMME (keine Bretter)", "Reichweite des Holzabbaus", "Funktioniert mit Drop-ins-Inventar"] +name = "Holzfäller" +description = "Ermöglicht dir, große Mengen Holz auf einmal abzubauen!" +lore1 = "Schleiche und fälle HOLZ/STÄMME (keine Bretter)" +lore2 = "Reichweite des Holzabbaus" +lore3 = "Funktioniert mit Drop-ins-Inventar" +lore = ["Schleiche und fälle HOLZ/STÄMME (keine Bretter)", "Reichweite des Holzabbaus", "Funktioniert mit Drop-ins-Inventar"] # brewing [brewing] [brewing.lingering] - name = "Verweilendes Gebräu" - description = "Gebraute Tränke wirken länger!" - lore1 = "Dauer" - lore2 = "Dauer" - lore = ["Dauer", "Dauer"] +name = "Verweilendes Gebräu" +description = "Gebraute Tränke wirken länger!" +lore1 = "Dauer" +lore2 = "Dauer" +lore = ["Dauer", "Dauer"] [brewing.super_heated] - name = "Superheißes Gebräu" - description = "Braustände arbeiten schneller, je heißer sie sind." - lore1 = "Pro berührendem Feuerblock" - lore2 = "Pro berührendem Lavablock" - lore = ["Pro berührendem Feuerblock", "Pro berührendem Lavablock"] +name = "Superheißes Gebräu" +description = "Braustände arbeiten schneller, je heißer sie sind." +lore1 = "Pro berührendem Feuerblock" +lore2 = "Pro berührendem Lavablock" +lore = ["Pro berührendem Feuerblock", "Pro berührendem Lavablock"] [brewing.darkness] - name = "Abgefüllte Dunkelheit" - description = "Nicht sicher, warum du das brauchst, aber bitte schön!" - lore1 = "Nachtsichttrank + Schwarzer Beton = Trank der Dunkelheit (30 Sekunden)" - lore2 = "Achtung: Dies hindert den Benutzer am Sprinten!" - lore = ["Nachtsichttrank + Schwarzer Beton = Trank der Dunkelheit (30 Sekunden)", "Achtung: Dies hindert den Benutzer am Sprinten!"] +name = "Abgefüllte Dunkelheit" +description = "Nicht sicher, warum du das brauchst, aber bitte schön!" +lore1 = "Nachtsichttrank + Schwarzer Beton = Trank der Dunkelheit (30 Sekunden)" +lore2 = "Achtung: Dies hindert den Benutzer am Sprinten!" +lore = ["Nachtsichttrank + Schwarzer Beton = Trank der Dunkelheit (30 Sekunden)", "Achtung: Dies hindert den Benutzer am Sprinten!"] [brewing.haste] - name = "Abgefüllte Eile" - description = "Wenn Effizienz nicht ausreicht" - lore1 = "Schnelligkeitstrank + Amethystsplitter = Trank der Eile (60 Sekunden)" - lore2 = "Schnelligkeitstrank + Amethystblock = Trank der Eile-2 (30 Sekunden)" - lore = ["Schnelligkeitstrank + Amethystsplitter = Trank der Eile (60 Sekunden)", "Schnelligkeitstrank + Amethystblock = Trank der Eile-2 (30 Sekunden)"] +name = "Abgefüllte Eile" +description = "Wenn Effizienz nicht ausreicht" +lore1 = "Schnelligkeitstrank + Amethystsplitter = Trank der Eile (60 Sekunden)" +lore2 = "Schnelligkeitstrank + Amethystblock = Trank der Eile-2 (30 Sekunden)" +lore = ["Schnelligkeitstrank + Amethystsplitter = Trank der Eile (60 Sekunden)", "Schnelligkeitstrank + Amethystblock = Trank der Eile-2 (30 Sekunden)"] [brewing.absorption] - name = "Abgefüllte Absorption" - description = "Härte den Körper!" - lore1 = "Sofortige Heilung + Quarz = Trank der Absorption (60 Sekunden)" - lore2 = "Sofortige Heilung + Quarzblock = Trank der Absorption-2 (30 Sekunden)" - lore = ["Sofortige Heilung + Quarz = Trank der Absorption (60 Sekunden)", "Sofortige Heilung + Quarzblock = Trank der Absorption-2 (30 Sekunden)"] +name = "Abgefüllte Absorption" +description = "Härte den Körper!" +lore1 = "Sofortige Heilung + Quarz = Trank der Absorption (60 Sekunden)" +lore2 = "Sofortige Heilung + Quarzblock = Trank der Absorption-2 (30 Sekunden)" +lore = ["Sofortige Heilung + Quarz = Trank der Absorption (60 Sekunden)", "Sofortige Heilung + Quarzblock = Trank der Absorption-2 (30 Sekunden)"] [brewing.fatigue] - name = "Abgefüllte Müdigkeit" - description = "Schwäche den Körper!" - lore1 = "Schwächetrank + Schleimball = Trank der Müdigkeit (30 Sekunden)" - lore2 = "Schwächetrank + Schleimblock = Trank der Müdigkeit-2 (15 Sekunden)" - lore = ["Schwächetrank + Schleimball = Trank der Müdigkeit (30 Sekunden)", "Schwächetrank + Schleimblock = Trank der Müdigkeit-2 (15 Sekunden)"] +name = "Abgefüllte Müdigkeit" +description = "Schwäche den Körper!" +lore1 = "Schwächetrank + Schleimball = Trank der Müdigkeit (30 Sekunden)" +lore2 = "Schwächetrank + Schleimblock = Trank der Müdigkeit-2 (15 Sekunden)" +lore = ["Schwächetrank + Schleimball = Trank der Müdigkeit (30 Sekunden)", "Schwächetrank + Schleimblock = Trank der Müdigkeit-2 (15 Sekunden)"] [brewing.hunger] - name = "Abgefüllter Hunger" - description = "Nähre die Unersättlichen!" - lore1 = "Seltsamer Trank + Verrottetes Fleisch = Trank des Hungers (30 Sekunden)" - lore2 = "Schwächetrank + Verrottetes Fleisch = Trank des Hungers-3 (15 Sekunden)" - lore = ["Seltsamer Trank + Verrottetes Fleisch = Trank des Hungers (30 Sekunden)", "Schwächetrank + Verrottetes Fleisch = Trank des Hungers-3 (15 Sekunden)"] +name = "Abgefüllter Hunger" +description = "Nähre die Unersättlichen!" +lore1 = "Seltsamer Trank + Verrottetes Fleisch = Trank des Hungers (30 Sekunden)" +lore2 = "Schwächetrank + Verrottetes Fleisch = Trank des Hungers-3 (15 Sekunden)" +lore = ["Seltsamer Trank + Verrottetes Fleisch = Trank des Hungers (30 Sekunden)", "Schwächetrank + Verrottetes Fleisch = Trank des Hungers-3 (15 Sekunden)"] [brewing.nausea] - name = "Abgefüllte Übelkeit" - description = "Du machst mich krank!" - lore1 = "Seltsamer Trank + Brauner Pilz = Trank der Übelkeit (16 Sekunden)" - lore2 = "Seltsamer Trank + Karmesinpilz = Trank der Übelkeit-2 (8 Sekunden)" - lore = ["Seltsamer Trank + Brauner Pilz = Trank der Übelkeit (16 Sekunden)", "Seltsamer Trank + Karmesinpilz = Trank der Übelkeit-2 (8 Sekunden)"] +name = "Abgefüllte Übelkeit" +description = "Du machst mich krank!" +lore1 = "Seltsamer Trank + Brauner Pilz = Trank der Übelkeit (16 Sekunden)" +lore2 = "Seltsamer Trank + Karmesinpilz = Trank der Übelkeit-2 (8 Sekunden)" +lore = ["Seltsamer Trank + Brauner Pilz = Trank der Übelkeit (16 Sekunden)", "Seltsamer Trank + Karmesinpilz = Trank der Übelkeit-2 (8 Sekunden)"] [brewing.blindness] - name = "Abgefüllte Blindheit" - description = "Du bist ein schrecklicher Mensch..." - lore1 = "Seltsamer Trank + Tintenbeutel = Trank der Blindheit (30 Sekunden)" - lore2 = "Seltsamer Trank + Leuchtender Tintenbeutel = Trank der Blindheit-2 (15 Sekunden)" - lore = ["Seltsamer Trank + Tintenbeutel = Trank der Blindheit (30 Sekunden)", "Seltsamer Trank + Leuchtender Tintenbeutel = Trank der Blindheit-2 (15 Sekunden)"] +name = "Abgefüllte Blindheit" +description = "Du bist ein schrecklicher Mensch..." +lore1 = "Seltsamer Trank + Tintenbeutel = Trank der Blindheit (30 Sekunden)" +lore2 = "Seltsamer Trank + Leuchtender Tintenbeutel = Trank der Blindheit-2 (15 Sekunden)" +lore = ["Seltsamer Trank + Tintenbeutel = Trank der Blindheit (30 Sekunden)", "Seltsamer Trank + Leuchtender Tintenbeutel = Trank der Blindheit-2 (15 Sekunden)"] [brewing.resistance] - name = "Abgefüllter Widerstand" - description = "Befestigung in Vollendung!" - lore1 = "Seltsamer Trank + Eisenbarren = Trank des Widerstands (60 Sekunden)" - lore2 = "Seltsamer Trank + Eisenblock = Trank des Widerstands-2 (30 Sekunden)" - lore = ["Seltsamer Trank + Eisenbarren = Trank des Widerstands (60 Sekunden)", "Seltsamer Trank + Eisenblock = Trank des Widerstands-2 (30 Sekunden)"] +name = "Abgefüllter Widerstand" +description = "Befestigung in Vollendung!" +lore1 = "Seltsamer Trank + Eisenbarren = Trank des Widerstands (60 Sekunden)" +lore2 = "Seltsamer Trank + Eisenblock = Trank des Widerstands-2 (30 Sekunden)" +lore = ["Seltsamer Trank + Eisenbarren = Trank des Widerstands (60 Sekunden)", "Seltsamer Trank + Eisenblock = Trank des Widerstands-2 (30 Sekunden)"] [brewing.health_boost] - name = "Abgefülltes Leben" - description = "Wenn maximale Gesundheit nicht ausreicht..." - lore1 = "Trank der Heilung + Goldener Apfel = Trank der Gesundheitssteigerung (120 Sekunden)" - lore2 = "Trank der Heilung + Verzauberter Goldener Apfel = Trank der Gesundheitssteigerung-2 (120 Sekunden)" - lore = ["Trank der Heilung + Goldener Apfel = Trank der Gesundheitssteigerung (120 Sekunden)", "Trank der Heilung + Verzauberter Goldener Apfel = Trank der Gesundheitssteigerung-2 (120 Sekunden)"] +name = "Abgefülltes Leben" +description = "Wenn maximale Gesundheit nicht ausreicht..." +lore1 = "Trank der Heilung + Goldener Apfel = Trank der Gesundheitssteigerung (120 Sekunden)" +lore2 = "Trank der Heilung + Verzauberter Goldener Apfel = Trank der Gesundheitssteigerung-2 (120 Sekunden)" +lore = ["Trank der Heilung + Goldener Apfel = Trank der Gesundheitssteigerung (120 Sekunden)", "Trank der Heilung + Verzauberter Goldener Apfel = Trank der Gesundheitssteigerung-2 (120 Sekunden)"] [brewing.decay] - name = "Abgefüllter Verfall" - description = "Wer hätte gedacht, dass Detritus so nützlich sein würde?" - lore1 = "Schwächetrank + Giftige Kartoffel = Trank des Wither (16 Sekunden)" - lore2 = "Schwächetrank + Karmesinwurzeln = Trank des Wither-2 (8 Sekunden)" - lore = ["Schwächetrank + Giftige Kartoffel = Trank des Wither (16 Sekunden)", "Schwächetrank + Karmesinwurzeln = Trank des Wither-2 (8 Sekunden)"] +name = "Abgefüllter Verfall" +description = "Wer hätte gedacht, dass Detritus so nützlich sein würde?" +lore1 = "Schwächetrank + Giftige Kartoffel = Trank des Wither (16 Sekunden)" +lore2 = "Schwächetrank + Karmesinwurzeln = Trank des Wither-2 (8 Sekunden)" +lore = ["Schwächetrank + Giftige Kartoffel = Trank des Wither (16 Sekunden)", "Schwächetrank + Karmesinwurzeln = Trank des Wither-2 (8 Sekunden)"] [brewing.saturation] - name = "Abgefüllte Sättigung" - description = "Weißt du was... Ich bin nicht mal hungrig..." - lore1 = "Regenerationstrank + Gebackene Kartoffel = Trank der Sättigung" - lore2 = "Regenerationstrank + Heuballen = Trank der Sättigung-2" - lore = ["Regenerationstrank + Gebackene Kartoffel = Trank der Sättigung", "Regenerationstrank + Heuballen = Trank der Sättigung-2"] +name = "Abgefüllte Sättigung" +description = "Weißt du was... Ich bin nicht mal hungrig..." +lore1 = "Regenerationstrank + Gebackene Kartoffel = Trank der Sättigung" +lore2 = "Regenerationstrank + Heuballen = Trank der Sättigung-2" +lore = ["Regenerationstrank + Gebackene Kartoffel = Trank der Sättigung", "Regenerationstrank + Heuballen = Trank der Sättigung-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Ketten des Mephistopheles" - description = "Ermöglicht dir die Herstellung von Kettenrüstung" - lore1 = "Das Rezept ist dasselbe wie bei jeder anderen Rüstung, aber mit Eisennuggets" - lore = ["Das Rezept ist dasselbe wie bei jeder anderen Rüstung, aber mit Eisennuggets"] +name = "Ketten des Mephistopheles" +description = "Ermöglicht dir die Herstellung von Kettenrüstung" +lore1 = "Das Rezept ist dasselbe wie bei jeder anderen Rüstung, aber mit Eisennuggets" +lore = ["Das Rezept ist dasselbe wie bei jeder anderen Rüstung, aber mit Eisennuggets"] [blocking.horse_armorer] - name = "Herstellbare Pferderüstung" - description = "Ermöglicht dir die Herstellung von Pferderüstung" - lore1 = "Umgib einen Sattel mit dem gewünschten Material, um die Rüstung herzustellen" - lore = ["Umgib einen Sattel mit dem gewünschten Material, um die Rüstung herzustellen"] +name = "Herstellbare Pferderüstung" +description = "Ermöglicht dir die Herstellung von Pferderüstung" +lore1 = "Umgib einen Sattel mit dem gewünschten Material, um die Rüstung herzustellen" +lore = ["Umgib einen Sattel mit dem gewünschten Material, um die Rüstung herzustellen"] [blocking.saddle_crafter] - name = "Herstellbarer Sattel" - description = "Stelle einen Sattel aus Leder her" - lore1 = "Rezept: 5 Leder:" - lore = ["Rezept: 5 Leder:"] +name = "Herstellbarer Sattel" +description = "Stelle einen Sattel aus Leder her" +lore1 = "Rezept: 5 Leder:" +lore = ["Rezept: 5 Leder:"] [blocking.multi_armor] - name = "Multi-Rüstung" - description = "Binde Elytren an Rüstung" - lore1 = "Eine erstaunliche Fähigkeit zum Reisen." - lore2 = "Dynamisches Zusammenführen und Wechseln von Rüstung/Elytra im Flug!" - lore3 = "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar." - lore4 = "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen." - lore5 = "Wenn deine Multi-Rüstung zerstört wird, verlierst du alle darin enthaltenen Gegenstände." - lore6 = "Ich brauche keine Rüstung, sie enttäuscht mich..." - lore = ["Eine erstaunliche Fähigkeit zum Reisen.", "Dynamisches Zusammenführen und Wechseln von Rüstung/Elytra im Flug!", "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar.", "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen.", "Wenn deine Multi-Rüstung zerstört wird, verlierst du alle darin enthaltenen Gegenstände.", "Ich brauche keine Rüstung, sie enttäuscht mich..."] +name = "Multi-Rüstung" +description = "Binde Elytren an Rüstung" +lore1 = "Eine erstaunliche Fähigkeit zum Reisen." +lore2 = "Dynamisches Zusammenführen und Wechseln von Rüstung/Elytra im Flug!" +lore3 = "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar." +lore4 = "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen." +lore5 = "Wenn deine Multi-Rüstung zerstört wird, verlierst du alle darin enthaltenen Gegenstände." +lore6 = "Ich brauche keine Rüstung, sie enttäuscht mich..." +lore = ["Eine erstaunliche Fähigkeit zum Reisen.", "Dynamisches Zusammenführen und Wechseln von Rüstung/Elytra im Flug!", "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar.", "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen.", "Wenn deine Multi-Rüstung zerstört wird, verlierst du alle darin enthaltenen Gegenstände.", "Ich brauche keine Rüstung, sie enttäuscht mich..."] # crafting [crafting] [crafting.deconstruction] - name = "Dekonstruktion" - description = "Zerlege Blöcke & Gegenstände in wiederverwertbare Grundkomponenten!" - lore1 = "Wirf einen beliebigen Gegenstand auf den Boden." - lore2 = "Dann schleiche und Rechtsklick mit einer Schere" - lore = ["Wirf einen beliebigen Gegenstand auf den Boden.", "Dann schleiche und Rechtsklick mit einer Schere"] +name = "Dekonstruktion" +description = "Zerlege Blöcke & Gegenstände in wiederverwertbare Grundkomponenten!" +lore1 = "Wirf einen beliebigen Gegenstand auf den Boden." +lore2 = "Dann schleiche und Rechtsklick mit einer Schere" +lore = ["Wirf einen beliebigen Gegenstand auf den Boden.", "Dann schleiche und Rechtsklick mit einer Schere"] [crafting.xp] - name = "Handwerkserfahrung" - description = "Erhalte passiv EP beim Herstellen" - lore1 = "Erhalte EP beim Herstellen" - lore = ["Erhalte EP beim Herstellen"] +name = "Handwerkserfahrung" +description = "Erhalte passiv EP beim Herstellen" +lore1 = "Erhalte EP beim Herstellen" +lore = ["Erhalte EP beim Herstellen"] [crafting.reconstruction] - name = "Erzrekonstruktion" - description = "Stelle Erze aus ihren Grundkomponenten wieder her!" - lore1 = "8 der Drops und 1 Wirtsblock = 1 Erz (formlos)" - lore2 = "Drops müssen geschmolzen werden (falls zutreffend)" - lore3 = "Ausgenommen: Schrott, Quarz und Smaragde usw..." - lore4 = "Wirtsblock = Ummantelung. z.B.: Stein, Netherrack, Tiefenschiefer" - lore = ["8 der Drops und 1 Wirtsblock = 1 Erz (formlos)", "Drops müssen geschmolzen werden (falls zutreffend)", "Ausgenommen: Schrott, Quarz und Smaragde usw...", "Wirtsblock = Ummantelung. z.B.: Stein, Netherrack, Tiefenschiefer"] +name = "Erzrekonstruktion" +description = "Stelle Erze aus ihren Grundkomponenten wieder her!" +lore1 = "8 der Drops und 1 Wirtsblock = 1 Erz (formlos)" +lore2 = "Drops müssen geschmolzen werden (falls zutreffend)" +lore3 = "Ausgenommen: Schrott, Quarz und Smaragde usw..." +lore4 = "Wirtsblock = Ummantelung. z.B.: Stein, Netherrack, Tiefenschiefer" +lore = ["8 der Drops und 1 Wirtsblock = 1 Erz (formlos)", "Drops müssen geschmolzen werden (falls zutreffend)", "Ausgenommen: Schrott, Quarz und Smaragde usw...", "Wirtsblock = Ummantelung. z.B.: Stein, Netherrack, Tiefenschiefer"] [crafting.leather] - name = "Herstellbares Leder" - description = "Stelle Leder aus verrottetem Fleisch her" - lore1 = "Wirf es (verrottetes Fleisch) einfach aufs Lagerfeuer!" - lore = ["Wirf es (verrottetes Fleisch) einfach aufs Lagerfeuer!"] +name = "Herstellbares Leder" +description = "Stelle Leder aus verrottetem Fleisch her" +lore1 = "Wirf es (verrottetes Fleisch) einfach aufs Lagerfeuer!" +lore = ["Wirf es (verrottetes Fleisch) einfach aufs Lagerfeuer!"] [crafting.backpacks] - name = "Boutiliers Rucksäcke!" - description = "Das bringt das Mojang-Bündel ins Spiel!" - lore1 = "Du musst im Überlebensmodus sein, um dies zu nutzen" - lore2 = "XLX : Leder, Leine, Leder" - lore3 = "XSX : Leder, Fass, Leder" - lore4 = "XCX : Leder, Truhe, Leder" - lore = ["Du musst im Überlebensmodus sein, um dies zu nutzen", "XLX : Leder, Leine, Leder", "XSX : Leder, Fass, Leder", "XCX : Leder, Truhe, Leder"] +name = "Boutiliers Rucksäcke!" +description = "Das bringt das Mojang-Bündel ins Spiel!" +lore1 = "Du musst im Überlebensmodus sein, um dies zu nutzen" +lore2 = "XLX : Leder, Leine, Leder" +lore3 = "XSX : Leder, Fass, Leder" +lore4 = "XCX : Leder, Truhe, Leder" +lore = ["Du musst im Überlebensmodus sein, um dies zu nutzen", "XLX : Leder, Leine, Leder", "XSX : Leder, Fass, Leder", "XCX : Leder, Truhe, Leder"] [crafting.stations] - name = "Tragbare Tische!" - description = "Benutze einen Tisch direkt aus deiner Hand!" - lore2 = "ALLE GEGENSTÄNDE, DIE DU IM TISCH BEIM SCHLIEßEN VERGISST, SIND FÜR IMMER VERLOREN!" - lore3 = "Gültige Tische: Amboss, Werkbank, Schleifstein, Kartentisch, Steinsäge, Webstuhl" - lore = ["ALLE GEGENSTÄNDE, DIE DU IM TISCH BEIM SCHLIEßEN VERGISST, SIND FÜR IMMER VERLOREN!", "Gültige Tische: Amboss, Werkbank, Schleifstein, Kartentisch, Steinsäge, Webstuhl"] +name = "Tragbare Tische!" +description = "Benutze einen Tisch direkt aus deiner Hand!" +lore2 = "ALLE GEGENSTÄNDE, DIE DU IM TISCH BEIM SCHLIEßEN VERGISST, SIND FÜR IMMER VERLOREN!" +lore3 = "Gültige Tische: Amboss, Werkbank, Schleifstein, Kartentisch, Steinsäge, Webstuhl" +lore = ["ALLE GEGENSTÄNDE, DIE DU IM TISCH BEIM SCHLIEßEN VERGISST, SIND FÜR IMMER VERLOREN!", "Gültige Tische: Amboss, Werkbank, Schleifstein, Kartentisch, Steinsäge, Webstuhl"] [crafting.skulls] - name = "Herstellbare Schädel!" - description = "Mit Materialien kannst du Mob-Schädel herstellen!" - lore1 = "Umgib einen Knochenblock mit Folgendem, um einen Schädel zu erhalten:" - lore2 = "Zombie: Verrottetes Fleisch" - lore3 = "Skelett: Knochen" - lore4 = "Creeper: Schwarzpulver" - lore5 = "Wither: Netherziegel" - lore6 = "Drache: Drachenatem" - lore = ["Umgib einen Knochenblock mit Folgendem, um einen Schädel zu erhalten:", "Zombie: Verrottetes Fleisch", "Skelett: Knochen", "Creeper: Schwarzpulver", "Wither: Netherziegel", "Drache: Drachenatem"] +name = "Herstellbare Schädel!" +description = "Mit Materialien kannst du Mob-Schädel herstellen!" +lore1 = "Umgib einen Knochenblock mit Folgendem, um einen Schädel zu erhalten:" +lore2 = "Zombie: Verrottetes Fleisch" +lore3 = "Skelett: Knochen" +lore4 = "Creeper: Schwarzpulver" +lore5 = "Wither: Netherziegel" +lore6 = "Drache: Drachenatem" +lore = ["Umgib einen Knochenblock mit Folgendem, um einen Schädel zu erhalten:", "Zombie: Verrottetes Fleisch", "Skelett: Knochen", "Creeper: Schwarzpulver", "Wither: Netherziegel", "Drache: Drachenatem"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Zeit in einer Flasche" - description = "Trage eine Zeitflasche, die Zeit speichert, und verbrauche sie, um zeitbasierte Blöcke, Gewächse und altersabhängige Wesen wie Tierbabys zu beschleunigen. Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche." - lore1 = "Gespeicherte Sekunden pro Tick aufgeladen" - lore2 = "Zeitbeschleunigung pro gespeicherter Sekunde" - lore3 = "Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche" - lore = ["Gespeicherte Sekunden pro Tick aufgeladen", "Zeitbeschleunigung pro gespeicherter Sekunde", "Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche"] +name = "Zeit in einer Flasche" +description = "Trage eine Zeitflasche, die Zeit speichert, und verbrauche sie, um zeitbasierte Blöcke, Gewächse und altersabhängige Wesen wie Tierbabys zu beschleunigen. Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche." +lore1 = "Gespeicherte Sekunden pro Tick aufgeladen" +lore2 = "Zeitbeschleunigung pro gespeicherter Sekunde" +lore3 = "Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche" +lore = ["Gespeicherte Sekunden pro Tick aufgeladen", "Zeitbeschleunigung pro gespeicherter Sekunde", "Rezept (Formlos): Schnelligkeitstrank + Uhr + Glasflasche"] [chronos.aberrant_touch] - name = "Abweichende Berührung" - description = "Nahkampfangriffe verursachen stapelbare Langsamkeit auf Kosten von Hunger, mit strikten PvP-Grenzen, und verankern Ziele bei 5 Stapeln." - lore1 = "Nahkampfangriffe verursachen stapelbare Langsamkeit" - lore2 = "PvE-Langsamkeitsdauer-Obergrenze" - lore3 = "PvP-Langsamkeitsverstärker-Obergrenze" - lore = ["Nahkampfangriffe verursachen stapelbare Langsamkeit", "PvE-Langsamkeitsdauer-Obergrenze", "PvP-Langsamkeitsverstärker-Obergrenze"] +name = "Abweichende Berührung" +description = "Nahkampfangriffe verursachen stapelbare Langsamkeit auf Kosten von Hunger, mit strikten PvP-Grenzen, und verankern Ziele bei 5 Stapeln." +lore1 = "Nahkampfangriffe verursachen stapelbare Langsamkeit" +lore2 = "PvE-Langsamkeitsdauer-Obergrenze" +lore3 = "PvP-Langsamkeitsverstärker-Obergrenze" +lore = ["Nahkampfangriffe verursachen stapelbare Langsamkeit", "PvE-Langsamkeitsdauer-Obergrenze", "PvP-Langsamkeitsverstärker-Obergrenze"] [chronos.instant_recall] - name = "Sofortige Rückkehr" - description = "Links- oder Rechtsklick mit einer Uhr in der Hand, um zu einem kürzlichen Zeitpunkt zurückzuspulen, mit wiederhergestellter Gesundheit und Sättigung." - lore1 = "Rückspuldauer" - lore2 = "Abklingzeit" - lore3 = "Kein Inventar-Rollback" - lore = ["Rückspuldauer", "Abklingzeit", "Kein Inventar-Rollback"] +name = "Sofortige Rückkehr" +description = "Links- oder Rechtsklick mit einer Uhr in der Hand, um zu einem kürzlichen Zeitpunkt zurückzuspulen, mit wiederhergestellter Gesundheit und Sättigung." +lore1 = "Rückspuldauer" +lore2 = "Abklingzeit" +lore3 = "Kein Inventar-Rollback" +lore = ["Rückspuldauer", "Abklingzeit", "Kein Inventar-Rollback"] [chronos.time_bomb] - name = "Zeitbombe" - description = "Wirf eine hergestellte Chronobombe, die ein Zeitfeld erzeugt, Wesen verlangsamt und Geschosse einfriert." - lore1 = "Zeitfeld-Radius" - lore2 = "Zeitfeld-Dauer" - lore3 = "Bomben-Abklingzeit" - lore4 = "Rezept (Formlos): Uhr + Schneeball + Diamant + Sand" - lore = ["Zeitfeld-Radius", "Zeitfeld-Dauer", "Bomben-Abklingzeit", "Rezept (Formlos): Uhr + Schneeball + Diamant + Sand"] +name = "Zeitbombe" +description = "Wirf eine hergestellte Chronobombe, die ein Zeitfeld erzeugt, Wesen verlangsamt und Geschosse einfriert." +lore1 = "Zeitfeld-Radius" +lore2 = "Zeitfeld-Dauer" +lore3 = "Bomben-Abklingzeit" +lore4 = "Rezept (Formlos): Uhr + Schneeball + Diamant + Sand" +lore = ["Zeitfeld-Radius", "Zeitfeld-Dauer", "Bomben-Abklingzeit", "Rezept (Formlos): Uhr + Schneeball + Diamant + Sand"] # discovery [discovery] [discovery.armor] - name = "Weltrüstung" - description = "Passive Rüstung abhängig von der Härte nahegelegener Blöcke." - lore1 = "Passive Rüstung" - lore2 = "Basiert auf der Härte nahegelegener Blöcke" - lore3 = "Rüstungsstärke:" - lore = ["Passive Rüstung", "Basiert auf der Härte nahegelegener Blöcke", "Rüstungsstärke:"] +name = "Weltrüstung" +description = "Passive Rüstung abhängig von der Härte nahegelegener Blöcke." +lore1 = "Passive Rüstung" +lore2 = "Basiert auf der Härte nahegelegener Blöcke" +lore3 = "Rüstungsstärke:" +lore = ["Passive Rüstung", "Basiert auf der Härte nahegelegener Blöcke", "Rüstungsstärke:"] [discovery.unity] - name = "Experimentelle Einheit" - description = "Erfahrungskugeln sammeln fügt zufälligen Fähigkeiten EP hinzu." - lore1 = "EP " - lore2 = "Pro Kugel" - lore = ["EP ", "Pro Kugel"] +name = "Experimentelle Einheit" +description = "Erfahrungskugeln sammeln fügt zufälligen Fähigkeiten EP hinzu." +lore1 = "EP " +lore2 = "Pro Kugel" +lore = ["EP ", "Pro Kugel"] [discovery.resist] - name = "Experimenteller Widerstand" - description = "Verbrauche Erfahrung, um Schaden abzudämpfen, nur wenn ein Treffer dich unter 5 Herzen fallen lassen oder töten würde." - lore0 = "Löst nur bei kritischer Gesundheit (<= 5 Herzen) einmal alle 15 Sekunden aus" - lore1 = " Reduzierter Schaden" - lore2 = "Erfahrung verbraucht" - lore = ["Löst nur bei kritischer Gesundheit (<= 5 Herzen) einmal alle 15 Sekunden aus", " Reduzierter Schaden", "Erfahrung verbraucht"] +name = "Experimenteller Widerstand" +description = "Verbrauche Erfahrung, um Schaden abzudämpfen, nur wenn ein Treffer dich unter 5 Herzen fallen lassen oder töten würde." +lore0 = "Löst nur bei kritischer Gesundheit (<= 5 Herzen) einmal alle 15 Sekunden aus" +lore1 = " Reduzierter Schaden" +lore2 = "Erfahrung verbraucht" +lore = ["Löst nur bei kritischer Gesundheit (<= 5 Herzen) einmal alle 15 Sekunden aus", " Reduzierter Schaden", "Erfahrung verbraucht"] [discovery.villager] - name = "Dorfbewohner-Anziehung" - description = "Ermöglicht dir bessere Handelsangebote bei Dorfbewohnern!" - lore1 = "Verbraucht EP pro Interaktion mit Dorfbewohnern" - lore2 = "Chance pro Interaktion, EP zu verbrauchen und Handelsangebote zu verbessern" - lore3 = "Erforderlicher EP-Verbrauch pro Interaktion" - lore = ["Verbraucht EP pro Interaktion mit Dorfbewohnern", "Chance pro Interaktion, EP zu verbrauchen und Handelsangebote zu verbessern", "Erforderlicher EP-Verbrauch pro Interaktion"] +name = "Dorfbewohner-Anziehung" +description = "Ermöglicht dir bessere Handelsangebote bei Dorfbewohnern!" +lore1 = "Verbraucht EP pro Interaktion mit Dorfbewohnern" +lore2 = "Chance pro Interaktion, EP zu verbrauchen und Handelsangebote zu verbessern" +lore3 = "Erforderlicher EP-Verbrauch pro Interaktion" +lore = ["Verbraucht EP pro Interaktion mit Dorfbewohnern", "Chance pro Interaktion, EP zu verbrauchen und Handelsangebote zu verbessern", "Erforderlicher EP-Verbrauch pro Interaktion"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapislazuli-Rückgabe" - description = "Auf Kosten von 1 Stufe mehr EP, mit einer Chance, kostenlos Lapislazuli zurückzubekommen" - lore1 = "Für jede Stufe erhöhen sich die Verzauberungskosten um 1, können aber bis zu 3 Lapislazuli zurückgeben" - lore = ["Für jede Stufe erhöhen sich die Verzauberungskosten um 1, können aber bis zu 3 Lapislazuli zurückgeben"] +name = "Lapislazuli-Rückgabe" +description = "Auf Kosten von 1 Stufe mehr EP, mit einer Chance, kostenlos Lapislazuli zurückzubekommen" +lore1 = "Für jede Stufe erhöhen sich die Verzauberungskosten um 1, können aber bis zu 3 Lapislazuli zurückgeben" +lore = ["Für jede Stufe erhöhen sich die Verzauberungskosten um 1, können aber bis zu 3 Lapislazuli zurückgeben"] [enchanting.quick_enchant] - name = "Schnellklick-Verzauberung" - description = "Verzaubere Gegenstände, indem du Verzauberungsbücher direkt darauf klickst." - lore1 = "Maximale kombinierte Stufen" - lore2 = "Kann einen Gegenstand nicht mit mehr als " - lore3 = "Energie verzaubern" - lore = ["Maximale kombinierte Stufen", "Kann einen Gegenstand nicht mit mehr als ", "Energie verzaubern"] +name = "Schnellklick-Verzauberung" +description = "Verzaubere Gegenstände, indem du Verzauberungsbücher direkt darauf klickst." +lore1 = "Maximale kombinierte Stufen" +lore2 = "Kann einen Gegenstand nicht mit mehr als " +lore3 = "Energie verzaubern" +lore = ["Maximale kombinierte Stufen", "Kann einen Gegenstand nicht mit mehr als ", "Energie verzaubern"] [enchanting.return] - name = "EP-Rückgabe" - description = "Verzauberungs-EP werden dir beim Verzaubern eines Gegenstands zurückgegeben." - lore1 = "Ausgegebene Erfahrung hat eine Chance, beim Verzaubern erstattet zu werden" - lore2 = "Erfahrung pro Verzauberung" - lore = ["Ausgegebene Erfahrung hat eine Chance, beim Verzaubern erstattet zu werden", "Erfahrung pro Verzauberung"] +name = "EP-Rückgabe" +description = "Verzauberungs-EP werden dir beim Verzaubern eines Gegenstands zurückgegeben." +lore1 = "Ausgegebene Erfahrung hat eine Chance, beim Verzaubern erstattet zu werden" +lore2 = "Erfahrung pro Verzauberung" +lore = ["Ausgegebene Erfahrung hat eine Chance, beim Verzaubern erstattet zu werden", "Erfahrung pro Verzauberung"] # excavation [excavation] [excavation.haste] - name = "Eiliger Ausgräber" - description = "Beschleunigt den Grabungsprozess mit EILE!" - lore1 = "Erhalte Eile beim Graben" - lore2 = "x Stufen Eile, wenn du anfängst, IRGENDWELCHE Blöcke abzubauen." - lore = ["Erhalte Eile beim Graben", "x Stufen Eile, wenn du anfängst, IRGENDWELCHE Blöcke abzubauen."] +name = "Eiliger Ausgräber" +description = "Beschleunigt den Grabungsprozess mit EILE!" +lore1 = "Erhalte Eile beim Graben" +lore2 = "x Stufen Eile, wenn du anfängst, IRGENDWELCHE Blöcke abzubauen." +lore = ["Erhalte Eile beim Graben", "x Stufen Eile, wenn du anfängst, IRGENDWELCHE Blöcke abzubauen."] [excavation.spelunker] - name = "Supersehender Höhlenforscher!" - description = "Sieh Erze mit deinen Augen, aber durch den Boden!" - lore1 = "Erz in der Nebenhand, Leuchtbeeren in der Haupthand, und Schleichen!" - lore2 = "Blockreichweite: " - lore3 = "Verbraucht eine Leuchtbeere bei Benutzung" - lore = ["Erz in der Nebenhand, Leuchtbeeren in der Haupthand, und Schleichen!", "Blockreichweite: ", "Verbraucht eine Leuchtbeere bei Benutzung"] +name = "Supersehender Höhlenforscher!" +description = "Sieh Erze mit deinen Augen, aber durch den Boden!" +lore1 = "Erz in der Nebenhand, Leuchtbeeren in der Haupthand, und Schleichen!" +lore2 = "Blockreichweite: " +lore3 = "Verbraucht eine Leuchtbeere bei Benutzung" +lore = ["Erz in der Nebenhand, Leuchtbeeren in der Haupthand, und Schleichen!", "Blockreichweite: ", "Verbraucht eine Leuchtbeere bei Benutzung"] [excavation.drop_to_inventory] - name = "Schaufel-Drop-ins-Inventar" +name = "Schaufel-Drop-ins-Inventar" [excavation.omni_tool] - name = "OMNI - W.E.R.K." - description = "Tackles überdesigntes opulentes Taschenmesser" - lore1 = "Wahrscheinlich das Mächtigste von vielen: ermöglicht es dir," - lore2 = "Werkzeuge dynamisch zusammenzuführen und nach Bedarf zu wechseln." - lore3 = "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar." - lore4 = "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen." - lore5 = "Werkzeuge in diesem Taschenmesser können nicht zerbrechen, aber zerbrochene können nicht benutzt werden" - lore6 = "Zusammenführbare Gegenstände insgesamt." - lore7 = "Warum fünf oder sechs Werkzeuge, wenn auch eins reicht!" - lore = ["Wahrscheinlich das Mächtigste von vielen: ermöglicht es dir,", "Werkzeuge dynamisch zusammenzuführen und nach Bedarf zu wechseln.", "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar.", "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen.", "Werkzeuge in diesem Taschenmesser können nicht zerbrechen, aber zerbrochene können nicht benutzt werden", "Zusammenführbare Gegenstände insgesamt.", "Warum fünf oder sechs Werkzeuge, wenn auch eins reicht!"] +name = "OMNI - W.E.R.K." +description = "Tackles überdesigntes opulentes Taschenmesser" +lore1 = "Wahrscheinlich das Mächtigste von vielen: ermöglicht es dir," +lore2 = "Werkzeuge dynamisch zusammenzuführen und nach Bedarf zu wechseln." +lore3 = "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar." +lore4 = "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen." +lore5 = "Werkzeuge in diesem Taschenmesser können nicht zerbrechen, aber zerbrochene können nicht benutzt werden" +lore6 = "Zusammenführbare Gegenstände insgesamt." +lore7 = "Warum fünf oder sechs Werkzeuge, wenn auch eins reicht!" +lore = ["Wahrscheinlich das Mächtigste von vielen: ermöglicht es dir,", "Werkzeuge dynamisch zusammenzuführen und nach Bedarf zu wechseln.", "Zum Zusammenführen: Shift-Klick auf einen Gegenstand über einem anderen im Inventar.", "Zum Trennen: Schleiche und wirf den Gegenstand, um ihn zu zerlegen.", "Werkzeuge in diesem Taschenmesser können nicht zerbrechen, aber zerbrochene können nicht benutzt werden", "Zusammenführbare Gegenstände insgesamt.", "Warum fünf oder sechs Werkzeuge, wenn auch eins reicht!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Wachstumsaura" - description = "Lasse die Natur um dich herum in einer Aura wachsen" - lore1 = "Block-Radius" - lore2 = "Stärke der Wachstumsaura" - lore3 = "Nahrungskosten" - lore = ["Block-Radius", "Stärke der Wachstumsaura", "Nahrungskosten"] +name = "Wachstumsaura" +description = "Lasse die Natur um dich herum in einer Aura wachsen" +lore1 = "Block-Radius" +lore2 = "Stärke der Wachstumsaura" +lore3 = "Nahrungskosten" +lore = ["Block-Radius", "Stärke der Wachstumsaura", "Nahrungskosten"] [herbalism.hippo] - name = "Raupe Nimmersatt" - description = "Nahrungsaufnahme gibt dir mehr Sättigung" - lore1 = "Nahrung) zusätzliche Sättigungspunkte beim Verzehr" - lore = ["Nahrung) zusätzliche Sättigungspunkte beim Verzehr"] +name = "Raupe Nimmersatt" +description = "Nahrungsaufnahme gibt dir mehr Sättigung" +lore1 = "Nahrung) zusätzliche Sättigungspunkte beim Verzehr" +lore = ["Nahrung) zusätzliche Sättigungspunkte beim Verzehr"] [herbalism.myconid] - name = "Myzel des Kräuterkundlers" - description = "Verleiht dir die Fähigkeit, Myzel herzustellen" - lore1 = "Jede Erde, ein brauner und ein roter Pilz ergeben Myzel." - lore = ["Jede Erde, ein brauner und ein roter Pilz ergeben Myzel."] +name = "Myzel des Kräuterkundlers" +description = "Verleiht dir die Fähigkeit, Myzel herzustellen" +lore1 = "Jede Erde, ein brauner und ein roter Pilz ergeben Myzel." +lore = ["Jede Erde, ein brauner und ein roter Pilz ergeben Myzel."] [herbalism.terralid] - name = "Terrain des Kräuterkundlers" - description = "Verleiht dir die Fähigkeit, Grasblöcke herzustellen" - lore1 = "Drei Samen über 3 Erde ergeben 3 Grasblöcke." - lore = ["Drei Samen über 3 Erde ergeben 3 Grasblöcke."] +name = "Terrain des Kräuterkundlers" +description = "Verleiht dir die Fähigkeit, Grasblöcke herzustellen" +lore1 = "Drei Samen über 3 Erde ergeben 3 Grasblöcke." +lore = ["Drei Samen über 3 Erde ergeben 3 Grasblöcke."] [herbalism.cobweb] - name = "Spinnennetzweber" - description = "Verleiht dir die Fähigkeit, Spinnweben in einer Werkbank herzustellen" - lore1 = "Neun Fäden ergeben ein Spinnennetz." - lore = ["Neun Fäden ergeben ein Spinnennetz."] +name = "Spinnennetzweber" +description = "Verleiht dir die Fähigkeit, Spinnweben in einer Werkbank herzustellen" +lore1 = "Neun Fäden ergeben ein Spinnennetz." +lore = ["Neun Fäden ergeben ein Spinnennetz."] [herbalism.mushroom_blocks] - name = "Pilzblockmacher" - description = "Verleiht dir die Fähigkeit, Pilzblöcke in einer Werkbank herzustellen" - lore1 = "Vier Pilze für einen Block, oder ein Block für einen Stiel." - lore = ["Vier Pilze für einen Block, oder ein Block für einen Stiel."] +name = "Pilzblockmacher" +description = "Verleiht dir die Fähigkeit, Pilzblöcke in einer Werkbank herzustellen" +lore1 = "Vier Pilze für einen Block, oder ein Block für einen Stiel." +lore = ["Vier Pilze für einen Block, oder ein Block für einen Stiel."] [herbalism.drop_to_inventory] - name = "Hacke-Drop-ins-Inventar" +name = "Hacke-Drop-ins-Inventar" [herbalism.hungry_shield] - name = "Hungriges Schild" - description = "Nimm Schaden an deinem Hunger vor deiner Gesundheit." - lore1 = "Durch Hunger abgewehrt" - lore = ["Durch Hunger abgewehrt"] +name = "Hungriges Schild" +description = "Nimm Schaden an deinem Hunger vor deiner Gesundheit." +lore1 = "Durch Hunger abgewehrt" +lore = ["Durch Hunger abgewehrt"] [herbalism.luck] - name = "Glück des Kräuterkundlers" - description = "Wenn du Gras/Blumen zerbrichst, hast du eine Chance, einen zufälligen Gegenstand zu erhalten" - lore0 = "Blumen = Nahrung, und Gras = Samen" - lore1 = "Chance, einen Gegenstand aus zerbrochenen Blumen zu erhalten" - lore2 = "Chance, einen Gegenstand aus zerbrochenem Gras zu erhalten" - lore = ["Blumen = Nahrung, und Gras = Samen", "Chance, einen Gegenstand aus zerbrochenen Blumen zu erhalten", "Chance, einen Gegenstand aus zerbrochenem Gras zu erhalten"] +name = "Glück des Kräuterkundlers" +description = "Wenn du Gras/Blumen zerbrichst, hast du eine Chance, einen zufälligen Gegenstand zu erhalten" +lore0 = "Blumen = Nahrung, und Gras = Samen" +lore1 = "Chance, einen Gegenstand aus zerbrochenen Blumen zu erhalten" +lore2 = "Chance, einen Gegenstand aus zerbrochenem Gras zu erhalten" +lore = ["Blumen = Nahrung, und Gras = Samen", "Chance, einen Gegenstand aus zerbrochenen Blumen zu erhalten", "Chance, einen Gegenstand aus zerbrochenem Gras zu erhalten"] [herbalism.replant] - name = "Ernten & Neupflanzen" - description = "Rechtsklick auf eine Pflanze mit einer Hacke, um sie zu ernten und neu zu pflanzen." - lore1 = "Blöcke Neupflanz-Radius" - lore = ["Blöcke Neupflanz-Radius"] +name = "Ernten & Neupflanzen" +description = "Rechtsklick auf eine Pflanze mit einer Hacke, um sie zu ernten und neu zu pflanzen." +lore1 = "Blöcke Neupflanz-Radius" +lore = ["Blöcke Neupflanz-Radius"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalin" - description = "Verursache mehr Schaden, je weniger Gesundheit du hast (Nahkampf)" - lore1 = "Maximaler Schaden" - lore = ["Maximaler Schaden"] +name = "Adrenalin" +description = "Verursache mehr Schaden, je weniger Gesundheit du hast (Nahkampf)" +lore1 = "Maximaler Schaden" +lore = ["Maximaler Schaden"] [hunter.penalty] - name = "" - description = "" - lore1 = "Du erhältst Giftstapel, wenn dir der Hunger ausgeht" - lore = ["Du erhältst Giftstapel, wenn dir der Hunger ausgeht"] +name = "" +description = "" +lore1 = "Du erhältst Giftstapel, wenn dir der Hunger ausgeht" +lore = ["Du erhältst Giftstapel, wenn dir der Hunger ausgeht"] [hunter.drop_to_inventory] - name = "Gegenstände-Drop-ins-Inventar" - description = "Wenn du etwas tötest / einen Block mit einem Schwert zerstörst, werden die Drops in dein Inventar teleportiert" - lore1 = "Immer wenn ein Gegenstand von einem Mob/Block droppt, den du zerstörst, landet er in deinem Inventar." - lore = ["Immer wenn ein Gegenstand von einem Mob/Block droppt, den du zerstörst, landet er in deinem Inventar."] +name = "Gegenstände-Drop-ins-Inventar" +description = "Wenn du etwas tötest / einen Block mit einem Schwert zerstörst, werden die Drops in dein Inventar teleportiert" +lore1 = "Immer wenn ein Gegenstand von einem Mob/Block droppt, den du zerstörst, landet er in deinem Inventar." +lore = ["Immer wenn ein Gegenstand von einem Mob/Block droppt, den du zerstörst, landet er in deinem Inventar."] [hunter.invisibility] - name = "Verschwindender Schritt" - description = "Wenn du getroffen wirst, erhältst du Unsichtbarkeit auf Kosten von Hunger" - lore1 = "Erhalte passive Unsichtbarkeit bei Treffern" - lore2 = "x Unsichtbarkeitsstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Unsichtbarkeitsdauer" - lore = ["Erhalte passive Unsichtbarkeit bei Treffern", "x Unsichtbarkeitsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Unsichtbarkeitsdauer"] +name = "Verschwindender Schritt" +description = "Wenn du getroffen wirst, erhältst du Unsichtbarkeit auf Kosten von Hunger" +lore1 = "Erhalte passive Unsichtbarkeit bei Treffern" +lore2 = "x Unsichtbarkeitsstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Unsichtbarkeitsdauer" +lore = ["Erhalte passive Unsichtbarkeit bei Treffern", "x Unsichtbarkeitsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Unsichtbarkeitsdauer"] [hunter.jump_boost] - name = "Jägers Höhen" - description = "Wenn du getroffen wirst, erhältst du Sprungverstärkung auf Kosten von Hunger" - lore1 = "Erhalte passiven Sprungboost bei Treffern" - lore2 = "x Sprungverstärkungsstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Sprungverstärkung-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passiven Sprungboost bei Treffern", "x Sprungverstärkungsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Sprungverstärkung-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Höhen" +description = "Wenn du getroffen wirst, erhältst du Sprungverstärkung auf Kosten von Hunger" +lore1 = "Erhalte passiven Sprungboost bei Treffern" +lore2 = "x Sprungverstärkungsstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Sprungverstärkung-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passiven Sprungboost bei Treffern", "x Sprungverstärkungsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Sprungverstärkung-Stapelmultiplikator, nicht Dauer."] [hunter.luck] - name = "Jägers Glück" - description = "Wenn du getroffen wirst, erhältst du Glück auf Kosten von Hunger" - lore1 = "Erhalte passives Glück bei Treffern" - lore2 = "x Glücksstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Glück-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passives Glück bei Treffern", "x Glücksstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Glück-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Glück" +description = "Wenn du getroffen wirst, erhältst du Glück auf Kosten von Hunger" +lore1 = "Erhalte passives Glück bei Treffern" +lore2 = "x Glücksstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Glück-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passives Glück bei Treffern", "x Glücksstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Glück-Stapelmultiplikator, nicht Dauer."] [hunter.regen] - name = "Jägers Regeneration" - description = "Wenn du getroffen wirst, erhältst du Regeneration auf Kosten von Hunger" - lore1 = "Erhalte passive Regeneration bei Treffern" - lore2 = "x Regenerationsstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Regeneration-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passive Regeneration bei Treffern", "x Regenerationsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Regeneration-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Regeneration" +description = "Wenn du getroffen wirst, erhältst du Regeneration auf Kosten von Hunger" +lore1 = "Erhalte passive Regeneration bei Treffern" +lore2 = "x Regenerationsstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Regeneration-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passive Regeneration bei Treffern", "x Regenerationsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Regeneration-Stapelmultiplikator, nicht Dauer."] [hunter.resistance] - name = "Jägers Widerstand" - description = "Wenn du getroffen wirst, erhältst du Widerstand auf Kosten von Hunger" - lore1 = "Erhalte passiven Widerstand bei Treffern" - lore2 = "x Widerstandsstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Widerstands-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passiven Widerstand bei Treffern", "x Widerstandsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Widerstands-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Widerstand" +description = "Wenn du getroffen wirst, erhältst du Widerstand auf Kosten von Hunger" +lore1 = "Erhalte passiven Widerstand bei Treffern" +lore2 = "x Widerstandsstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Widerstands-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passiven Widerstand bei Treffern", "x Widerstandsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Widerstands-Stapelmultiplikator, nicht Dauer."] [hunter.speed] - name = "Jägers Geschwindigkeit" - description = "Wenn du getroffen wirst, erhältst du Geschwindigkeit auf Kosten von Hunger" - lore1 = "Erhalte passive Geschwindigkeit bei Treffern" - lore2 = "x Geschwindigkeitsstapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Geschwindigkeit-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passive Geschwindigkeit bei Treffern", "x Geschwindigkeitsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Geschwindigkeit-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Geschwindigkeit" +description = "Wenn du getroffen wirst, erhältst du Geschwindigkeit auf Kosten von Hunger" +lore1 = "Erhalte passive Geschwindigkeit bei Treffern" +lore2 = "x Geschwindigkeitsstapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Geschwindigkeit-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passive Geschwindigkeit bei Treffern", "x Geschwindigkeitsstapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Geschwindigkeit-Stapelmultiplikator, nicht Dauer."] [hunter.strength] - name = "Jägers Stärke" - description = "Wenn du getroffen wirst, erhältst du Stärke auf Kosten von Hunger" - lore1 = "Erhalte passive Stärke bei Treffern" - lore2 = "x Stärkestapel für 3 Sekunden bei Treffer" - lore3 = "x Stapelbarer Hunger" - lore4 = "Hungerstapel Dauer und Multiplikator." - lore5 = "Stärke-Stapelmultiplikator, nicht Dauer." - lore = ["Erhalte passive Stärke bei Treffern", "x Stärkestapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Stärke-Stapelmultiplikator, nicht Dauer."] +name = "Jägers Stärke" +description = "Wenn du getroffen wirst, erhältst du Stärke auf Kosten von Hunger" +lore1 = "Erhalte passive Stärke bei Treffern" +lore2 = "x Stärkestapel für 3 Sekunden bei Treffer" +lore3 = "x Stapelbarer Hunger" +lore4 = "Hungerstapel Dauer und Multiplikator." +lore5 = "Stärke-Stapelmultiplikator, nicht Dauer." +lore = ["Erhalte passive Stärke bei Treffern", "x Stärkestapel für 3 Sekunden bei Treffer", "x Stapelbarer Hunger", "Hungerstapel Dauer und Multiplikator.", "Stärke-Stapelmultiplikator, nicht Dauer."] # nether [nether] [nether.skull_toss] - name = "Witherschädel-Wurf" - description1 = "Entfessle deinen inneren Wither, indem du" - description2 = "jemandes" - description3 = "Kopf benutzt." - lore1 = "Sekunden Abklingzeit zwischen Schädelwürfen." - lore2 = "Witherschädel verwenden: Wirf einen " - lore3 = "Witherschädel" - lore4 = "der beim Aufprall explodiert." - lore = ["Sekunden Abklingzeit zwischen Schädelwürfen.", "Witherschädel verwenden: Wirf einen ", "Witherschädel", "der beim Aufprall explodiert."] +name = "Witherschädel-Wurf" +description1 = "Entfessle deinen inneren Wither, indem du" +description2 = "jemandes" +description3 = "Kopf benutzt." +lore1 = "Sekunden Abklingzeit zwischen Schädelwürfen." +lore2 = "Witherschädel verwenden: Wirf einen " +lore3 = "Witherschädel" +lore4 = "der beim Aufprall explodiert." +lore = ["Sekunden Abklingzeit zwischen Schädelwürfen.", "Witherschädel verwenden: Wirf einen ", "Witherschädel", "der beim Aufprall explodiert."] [nether.wither_resist] - name = "Wither-Resistenz" - description = "Widersteht dem Wither durch die Kraft des Netherit." - lore1 = "Chance, Wither zu negieren (pro Rüstungsteil)." - lore2 = "Passiv: Netherit-Rüstung hat eine Chance, das " - lore3 = "Withern zu negieren." - lore = ["Chance, Wither zu negieren (pro Rüstungsteil).", "Passiv: Netherit-Rüstung hat eine Chance, das ", "Withern zu negieren."] +name = "Wither-Resistenz" +description = "Widersteht dem Wither durch die Kraft des Netherit." +lore1 = "Chance, Wither zu negieren (pro Rüstungsteil)." +lore2 = "Passiv: Netherit-Rüstung hat eine Chance, das " +lore3 = "Withern zu negieren." +lore = ["Chance, Wither zu negieren (pro Rüstungsteil).", "Passiv: Netherit-Rüstung hat eine Chance, das ", "Withern zu negieren."] [nether.fire_resist] - name = "Feuerresistenz" - description = "Widersteht Feuer durch Härtung deiner Haut." - lore1 = "Chance, den Brenneffekt zu negieren!" - lore = ["Chance, den Brenneffekt zu negieren!"] +name = "Feuerresistenz" +description = "Widersteht Feuer durch Härtung deiner Haut." +lore1 = "Chance, den Brenneffekt zu negieren!" +lore = ["Chance, den Brenneffekt zu negieren!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Automatisches Schmelzen" - description = "Ermöglicht das Schmelzen von abgebauten Vanilla-Erzen" - lore1 = "Erze, die geschmolzen werden können, werden automatisch geschmolzen" - lore2 = "% Chance auf ein Extra" - lore = ["Erze, die geschmolzen werden können, werden automatisch geschmolzen", "% Chance auf ein Extra"] +name = "Automatisches Schmelzen" +description = "Ermöglicht das Schmelzen von abgebauten Vanilla-Erzen" +lore1 = "Erze, die geschmolzen werden können, werden automatisch geschmolzen" +lore2 = "% Chance auf ein Extra" +lore = ["Erze, die geschmolzen werden können, werden automatisch geschmolzen", "% Chance auf ein Extra"] [pickaxe.chisel] - name = "Erzmeißel" - description = "Rechtsklick auf Erze, um mehr Erz herauszumeißeln, auf Kosten starker Haltbarkeitsabnutzung." - lore1 = "Chance auf Drop" - lore2 = "Werkzeugverschleiß" - lore = ["Chance auf Drop", "Werkzeugverschleiß"] +name = "Erzmeißel" +description = "Rechtsklick auf Erze, um mehr Erz herauszumeißeln, auf Kosten starker Haltbarkeitsabnutzung." +lore1 = "Chance auf Drop" +lore2 = "Werkzeugverschleiß" +lore = ["Chance auf Drop", "Werkzeugverschleiß"] [pickaxe.drop_to_inventory] - name = "Spitzhacke-Drop-ins-Inventar" - description = "Wenn du einen Block zerbrichst, wird der Gegenstand in dein Inventar teleportiert" - lore1 = "Immer wenn ein Gegenstand von einem zerbrochenen Block droppt, landet er in deinem Inventar." - lore = ["Immer wenn ein Gegenstand von einem zerbrochenen Block droppt, landet er in deinem Inventar."] +name = "Spitzhacke-Drop-ins-Inventar" +description = "Wenn du einen Block zerbrichst, wird der Gegenstand in dein Inventar teleportiert" +lore1 = "Immer wenn ein Gegenstand von einem zerbrochenen Block droppt, landet er in deinem Inventar." +lore = ["Immer wenn ein Gegenstand von einem zerbrochenen Block droppt, landet er in deinem Inventar."] [pickaxe.silk_spawner] - name = "Spitzhacke Behutsamkeits-Spawner" - description = "Spawner droppen beim Abbauen" - lore1 = "Spawner können mit Behutsamkeit abgebaut werden." - lore2 = "Spawner können im Schleichen abgebaut werden." - lore = ["Spawner können mit Behutsamkeit abgebaut werden.", "Spawner können im Schleichen abgebaut werden."] +name = "Spitzhacke Behutsamkeits-Spawner" +description = "Spawner droppen beim Abbauen" +lore1 = "Spawner können mit Behutsamkeit abgebaut werden." +lore2 = "Spawner können im Schleichen abgebaut werden." +lore = ["Spawner können mit Behutsamkeit abgebaut werden.", "Spawner können im Schleichen abgebaut werden."] [pickaxe.vein_miner] - name = "Aderabbau" - description = "Ermöglicht das Abbauen von Blöcken in einer Ader/einem Cluster von Vanilla-Erzen" - lore1 = "Schleiche und baue ERZE ab" - lore2 = "Reichweite des Aderabbaus" - lore3 = "Diese Fähigkeit fasst NICHT alle Drops zusammen!" - lore = ["Schleiche und baue ERZE ab", "Reichweite des Aderabbaus", "Diese Fähigkeit fasst NICHT alle Drops zusammen!"] +name = "Aderabbau" +description = "Ermöglicht das Abbauen von Blöcken in einer Ader/einem Cluster von Vanilla-Erzen" +lore1 = "Schleiche und baue ERZE ab" +lore2 = "Reichweite des Aderabbaus" +lore3 = "Diese Fähigkeit fasst NICHT alle Drops zusammen!" +lore = ["Schleiche und baue ERZE ab", "Reichweite des Aderabbaus", "Diese Fähigkeit fasst NICHT alle Drops zusammen!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Pfeilbergung" - description = "Stelle Pfeile wieder her, nachdem du einen Feind getötet hast." - lore1 = "Chance, Pfeile bei Treffer/Tötung zurückzubekommen" - lore2 = "Chance: " - lore = ["Chance, Pfeile bei Treffer/Tötung zurückzubekommen", "Chance: "] +name = "Pfeilbergung" +description = "Stelle Pfeile wieder her, nachdem du einen Feind getötet hast." +lore1 = "Chance, Pfeile bei Treffer/Tötung zurückzubekommen" +lore2 = "Chance: " +lore = ["Chance, Pfeile bei Treffer/Tötung zurückzubekommen", "Chance: "] [ranged.web_shot] - name = "Netzfalle" - description = "Umhülle dein Ziel mit Spinnweben, wenn du es triffst!" - lore1 = "8 Spinnweben um einen Schneeball, und werfen!" - lore2 = "Sekunden eines Käfigs, ungefähr." - lore = ["8 Spinnweben um einen Schneeball, und werfen!", "Sekunden eines Käfigs, ungefähr."] +name = "Netzfalle" +description = "Umhülle dein Ziel mit Spinnweben, wenn du es triffst!" +lore1 = "8 Spinnweben um einen Schneeball, und werfen!" +lore2 = "Sekunden eines Käfigs, ungefähr." +lore = ["8 Spinnweben um einen Schneeball, und werfen!", "Sekunden eines Käfigs, ungefähr."] [ranged.force_shot] - name = "Kraftschuss" - description = "Schieße Geschosse weiter und schneller!" - advancementname = "Weitschuss" - advancementlore = "Lande einen Schuss aus über 30 Blöcken Entfernung!" - lore1 = "Geschossgeschwindigkeit" - lore = ["Geschossgeschwindigkeit"] +name = "Kraftschuss" +description = "Schieße Geschosse weiter und schneller!" +advancementname = "Weitschuss" +advancementlore = "Lande einen Schuss aus über 30 Blöcken Entfernung!" +lore1 = "Geschossgeschwindigkeit" +lore = ["Geschossgeschwindigkeit"] [ranged.lunge_shot] - name = "Ausfallschuss" - description = "Während du fällst, schleudern dich deine Pfeile in eine zufällige Richtung" - lore1 = "Zufällige Schubgeschwindigkeit" - lore = ["Zufällige Schubgeschwindigkeit"] +name = "Ausfallschuss" +description = "Während du fällst, schleudern dich deine Pfeile in eine zufällige Richtung" +lore1 = "Zufällige Schubgeschwindigkeit" +lore = ["Zufällige Schubgeschwindigkeit"] [ranged.arrow_piercing] - name = "Pfeildurchbohrung" - description = "Fügt Geschossen Durchschlagskraft hinzu! Schieße durch Dinge hindurch!" - lore1 = "Ziele durchbohren" - lore = ["Ziele durchbohren"] +name = "Pfeildurchbohrung" +description = "Fügt Geschossen Durchschlagskraft hinzu! Schieße durch Dinge hindurch!" +lore1 = "Ziele durchbohren" +lore = ["Ziele durchbohren"] # rift [rift] [rift.remote_access] - name = "Fernzugriff" - description = "Greife in die Leere und gelange zu einem markierten Behälter." - lore1 = "Enderperle + Kompass = Reliquien-Portschlüssel" - lore2 = "Dieser Gegenstand ermöglicht dir den Fernzugriff auf Behälter" - lore3 = "Nach der Herstellung den Gegenstand ansehen für die Nutzung" - notcontainer = "Das ist kein Behälter" - lore = ["Enderperle + Kompass = Reliquien-Portschlüssel", "Dieser Gegenstand ermöglicht dir den Fernzugriff auf Behälter", "Nach der Herstellung den Gegenstand ansehen für die Nutzung"] +name = "Fernzugriff" +description = "Greife in die Leere und gelange zu einem markierten Behälter." +lore1 = "Enderperle + Kompass = Reliquien-Portschlüssel" +lore2 = "Dieser Gegenstand ermöglicht dir den Fernzugriff auf Behälter" +lore3 = "Nach der Herstellung den Gegenstand ansehen für die Nutzung" +notcontainer = "Das ist kein Behälter" +lore = ["Enderperle + Kompass = Reliquien-Portschlüssel", "Dieser Gegenstand ermöglicht dir den Fernzugriff auf Behälter", "Nach der Herstellung den Gegenstand ansehen für die Nutzung"] [rift.blink] - name = "Riss-Blinzeln" - description = "Kurze sofortige Teleportation, nur ein Wimpernschlag entfernt!" - lore1 = "Blöcke beim Blinzeln (2x vertikal)" - lore2 = "Beim Sprinten: Doppeltippe Springen, um zu " - lore3 = "Blinzeln" - lore = ["Blöcke beim Blinzeln (2x vertikal)", "Beim Sprinten: Doppeltippe Springen, um zu ", "Blinzeln"] +name = "Riss-Blinzeln" +description = "Kurze sofortige Teleportation, nur ein Wimpernschlag entfernt!" +lore1 = "Blöcke beim Blinzeln (2x vertikal)" +lore2 = "Beim Sprinten: Doppeltippe Springen, um zu " +lore3 = "Blinzeln" +lore = ["Blöcke beim Blinzeln (2x vertikal)", "Beim Sprinten: Doppeltippe Springen, um zu ", "Blinzeln"] [rift.chest] - name = "Einfache Endertruhe" - description = "Öffne eine Endertruhe per Linksklick in deiner Hand." - lore1 = "Klicke auf eine Endertruhe in deiner Hand, um sie zu öffnen (nicht platzieren)" - lore = ["Klicke auf eine Endertruhe in deiner Hand, um sie zu öffnen (nicht platzieren)"] +name = "Einfache Endertruhe" +description = "Öffne eine Endertruhe per Linksklick in deiner Hand." +lore1 = "Klicke auf eine Endertruhe in deiner Hand, um sie zu öffnen (nicht platzieren)" +lore = ["Klicke auf eine Endertruhe in deiner Hand, um sie zu öffnen (nicht platzieren)"] [rift.descent] - name = "Anti-Levitation" - description = "Bist du es leid, in der Luft festzusitzen? Das ist die Fähigkeit für dich!" - lore1 = "Einfach schleichen zum Absteigen, und du fällst langsamer als normal!" - lore2 = "Abklingzeit:" - lore = ["Einfach schleichen zum Absteigen, und du fällst langsamer als normal!", "Abklingzeit:"] +name = "Anti-Levitation" +description = "Bist du es leid, in der Luft festzusitzen? Das ist die Fähigkeit für dich!" +lore1 = "Einfach schleichen zum Absteigen, und du fällst langsamer als normal!" +lore2 = "Abklingzeit:" +lore = ["Einfach schleichen zum Absteigen, und du fällst langsamer als normal!", "Abklingzeit:"] [rift.gate] - name = "Risstor" - description = "Teleportiere dich zu einem markierten Ort." - lore1 = "HERSTELLUNG: Smaragd + Amethystsplitter + Enderperle" - lore2 = "Vor Gebrauch lesen!" - lore3 = "5s Verzögerung, " - lore4 = "du kannst während dieser Animation sterben" - lore = ["HERSTELLUNG: Smaragd + Amethystsplitter + Enderperle", "Vor Gebrauch lesen!", "5s Verzögerung, ", "du kannst während dieser Animation sterben"] +name = "Risstor" +description = "Teleportiere dich zu einem markierten Ort." +lore1 = "HERSTELLUNG: Smaragd + Amethystsplitter + Enderperle" +lore2 = "Vor Gebrauch lesen!" +lore3 = "5s Verzögerung, " +lore4 = "du kannst während dieser Animation sterben" +lore = ["HERSTELLUNG: Smaragd + Amethystsplitter + Enderperle", "Vor Gebrauch lesen!", "5s Verzögerung, ", "du kannst während dieser Animation sterben"] [rift.resist] - name = "Riss-Widerstand" - description = "Erhalte Resistenz bei der Verwendung von Ender-Gegenständen & Fähigkeiten" - lore1 = "+ Passiv: Bietet Widerstand, wenn du Riss-Fähigkeiten oder Ender-Gegenstände verwendest" - lore2 = "NICHT die tragbare Endertruhe, nur Dinge, die du verbrauchen kannst" - lore = ["+ Passiv: Bietet Widerstand, wenn du Riss-Fähigkeiten oder Ender-Gegenstände verwendest", "NICHT die tragbare Endertruhe, nur Dinge, die du verbrauchen kannst"] +name = "Riss-Widerstand" +description = "Erhalte Resistenz bei der Verwendung von Ender-Gegenständen & Fähigkeiten" +lore1 = "+ Passiv: Bietet Widerstand, wenn du Riss-Fähigkeiten oder Ender-Gegenstände verwendest" +lore2 = "NICHT die tragbare Endertruhe, nur Dinge, die du verbrauchen kannst" +lore = ["+ Passiv: Bietet Widerstand, wenn du Riss-Fähigkeiten oder Ender-Gegenstände verwendest", "NICHT die tragbare Endertruhe, nur Dinge, die du verbrauchen kannst"] [rift.visage] - name = "Riss-Antlitz" - description = "Verhindert, dass Endermen aggressiv werden, wenn du Enderperlen im Inventar hast." - lore1 = "Endermen werden nicht aggressiv, wenn du Enderperlen in deinem Inventar hast." - lore = ["Endermen werden nicht aggressiv, wenn du Enderperlen in deinem Inventar hast."] +name = "Riss-Antlitz" +description = "Verhindert, dass Endermen aggressiv werden, wenn du Enderperlen im Inventar hast." +lore1 = "Endermen werden nicht aggressiv, wenn du Enderperlen in deinem Inventar hast." +lore = ["Endermen werden nicht aggressiv, wenn du Enderperlen in deinem Inventar hast."] # seaborn [seaborn] [seaborn.oxygen] - name = "Organischer Sauerstofftank" - description = "Halte mehr Sauerstoff in deinen winzigen Lungen!" - lore1 = "Erhöhung der Sauerstoffkapazität" - lore = ["Erhöhung der Sauerstoffkapazität"] +name = "Organischer Sauerstofftank" +description = "Halte mehr Sauerstoff in deinen winzigen Lungen!" +lore1 = "Erhöhung der Sauerstoffkapazität" +lore = ["Erhöhung der Sauerstoffkapazität"] [seaborn.fishers_fantasy] - name = "Fischers Fantasie" - description = "Verdiene mehr EP beim Fischen und fange mehr Fische!" - lore1 = "Für jede Stufe gibt es eine Chance, mehr EP und Fisch zu erhalten!" - lore = ["Für jede Stufe gibt es eine Chance, mehr EP und Fisch zu erhalten!"] +name = "Fischers Fantasie" +description = "Verdiene mehr EP beim Fischen und fange mehr Fische!" +lore1 = "Für jede Stufe gibt es eine Chance, mehr EP und Fisch zu erhalten!" +lore = ["Für jede Stufe gibt es eine Chance, mehr EP und Fisch zu erhalten!"] [seaborn.haste] - name = "Schildkröten-Bergbau" - description = "Erhalte Eile beim Abbauen unter Wasser!" - lore1 = "Eile 3 wird unter Wasser beim Abbauen angewendet (stapelt sich mit Wasseraffinität), nachdem der Wasseratmungseffekt nachlässt!" - lore = ["Eile 3 wird unter Wasser beim Abbauen angewendet (stapelt sich mit Wasseraffinität), nachdem der Wasseratmungseffekt nachlässt!"] +name = "Schildkröten-Bergbau" +description = "Erhalte Eile beim Abbauen unter Wasser!" +lore1 = "Eile 3 wird unter Wasser beim Abbauen angewendet (stapelt sich mit Wasseraffinität), nachdem der Wasseratmungseffekt nachlässt!" +lore = ["Eile 3 wird unter Wasser beim Abbauen angewendet (stapelt sich mit Wasseraffinität), nachdem der Wasseratmungseffekt nachlässt!"] [seaborn.night_vision] - name = "Schildkröten-Sicht" - description = "Erhalte Nachtsicht, während du unter Wasser bist" - lore1 = "Erhalte einfach Nachtsicht unter Wasser, nachdem dein Wasseratmungseffekt nachlässt!" - lore = ["Erhalte einfach Nachtsicht unter Wasser, nachdem dein Wasseratmungseffekt nachlässt!"] +name = "Schildkröten-Sicht" +description = "Erhalte Nachtsicht, während du unter Wasser bist" +lore1 = "Erhalte einfach Nachtsicht unter Wasser, nachdem dein Wasseratmungseffekt nachlässt!" +lore = ["Erhalte einfach Nachtsicht unter Wasser, nachdem dein Wasseratmungseffekt nachlässt!"] [seaborn.dolphin_grace] - name = "Gunst des Delfins" - description = "Schwimme wie ein Delfin, ohne die Delfine" - lore1 = "+ Passiv: erhalte " - lore2 = "x Geschwindigkeit (Gunst des Delfins)" - lore3 = "Präzise deutsche Ingenieurskunst - Moment, das stimmt nicht... Nicht kompatibel mit Tiefenschreiter" - lore = ["+ Passiv: erhalte ", "x Geschwindigkeit (Gunst des Delfins)", "Präzise deutsche Ingenieurskunst - Moment, das stimmt nicht... Nicht kompatibel mit Tiefenschreiter"] +name = "Gunst des Delfins" +description = "Schwimme wie ein Delfin, ohne die Delfine" +lore1 = "+ Passiv: erhalte " +lore2 = "x Geschwindigkeit (Gunst des Delfins)" +lore3 = "Präzise deutsche Ingenieurskunst - Moment, das stimmt nicht... Nicht kompatibel mit Tiefenschreiter" +lore = ["+ Passiv: erhalte ", "x Geschwindigkeit (Gunst des Delfins)", "Präzise deutsche Ingenieurskunst - Moment, das stimmt nicht... Nicht kompatibel mit Tiefenschreiter"] # stealth [stealth] [stealth.ghost_armor] - name = "Geisterrüstung" - description = "Langsam aufbauende Rüstung, wenn kein Schaden erlitten wird, hält 1 Treffer" - lore1 = "Maximale Rüstung" - lore2 = "Geschwindigkeit" - lore = ["Maximale Rüstung", "Geschwindigkeit"] +name = "Geisterrüstung" +description = "Langsam aufbauende Rüstung, wenn kein Schaden erlitten wird, hält 1 Treffer" +lore1 = "Maximale Rüstung" +lore2 = "Geschwindigkeit" +lore = ["Maximale Rüstung", "Geschwindigkeit"] [stealth.night_vision] - name = "Heimliche Sicht" - description = "Erhalte Nachtsicht beim Schleichen" - lore1 = "Erhalte einen Schub von " - lore2 = "Nachtsicht" - lore3 = "beim Schleichen" - lore = ["Erhalte einen Schub von ", "Nachtsicht", "beim Schleichen"] +name = "Heimliche Sicht" +description = "Erhalte Nachtsicht beim Schleichen" +lore1 = "Erhalte einen Schub von " +lore2 = "Nachtsicht" +lore3 = "beim Schleichen" +lore = ["Erhalte einen Schub von ", "Nachtsicht", "beim Schleichen"] [stealth.snatch] - name = "Gegenstand-Schnappen" - description = "Schnappe dir fallengelassene Gegenstände sofort beim Schleichen!" - lore1 = "Schnapp-Radius" - lore = ["Schnapp-Radius"] +name = "Gegenstand-Schnappen" +description = "Schnappe dir fallengelassene Gegenstände sofort beim Schleichen!" +lore1 = "Schnapp-Radius" +lore = ["Schnapp-Radius"] [stealth.speed] - name = "Schleichgeschwindigkeit" - description = "Erhalte Geschwindigkeit beim Schleichen" - lore1 = "Schleichgeschwindigkeit" - lore = ["Schleichgeschwindigkeit"] +name = "Schleichgeschwindigkeit" +description = "Erhalte Geschwindigkeit beim Schleichen" +lore1 = "Schleichgeschwindigkeit" +lore = ["Schleichgeschwindigkeit"] [stealth.ender_veil] - name = "Enderschleier" - description = "Keine Kürbisse mehr nötig, um Enderman-Angriffe zu verhindern" - lore1 = "Verhindere Enderman-Angriffe beim Schleichen" - lore2 = "Verhindere alle Enderman-Angriffe" - lore = ["Verhindere Enderman-Angriffe beim Schleichen", "Verhindere alle Enderman-Angriffe"] +name = "Enderschleier" +description = "Keine Kürbisse mehr nötig, um Enderman-Angriffe zu verhindern" +lore1 = "Verhindere Enderman-Angriffe beim Schleichen" +lore2 = "Verhindere alle Enderman-Angriffe" +lore = ["Verhindere Enderman-Angriffe beim Schleichen", "Verhindere alle Enderman-Angriffe"] # sword [sword] [sword.machete] - name = "Machete" - description = "Schneide mit Leichtigkeit durchs Laub!" - lore1 = "Hieb-Radius" - lore2 = "Hieb-Abklingzeit" - lore3 = "Werkzeugverschleiß" - lore = ["Hieb-Radius", "Hieb-Abklingzeit", "Werkzeugverschleiß"] +name = "Machete" +description = "Schneide mit Leichtigkeit durchs Laub!" +lore1 = "Hieb-Radius" +lore2 = "Hieb-Abklingzeit" +lore3 = "Werkzeugverschleiß" +lore = ["Hieb-Radius", "Hieb-Abklingzeit", "Werkzeugverschleiß"] [sword.bloody_blade] - name = "Blutige Klinge" - description = "Schwertschläge verursachen Blutung!" - lore1 = "Das Treffen eines Lebewesens mit deinem Schwert verursacht Blutung" - lore2 = "Blutungsdauer" - lore3 = "Blutungs-Abklingzeit" - lore = ["Das Treffen eines Lebewesens mit deinem Schwert verursacht Blutung", "Blutungsdauer", "Blutungs-Abklingzeit"] +name = "Blutige Klinge" +description = "Schwertschläge verursachen Blutung!" +lore1 = "Das Treffen eines Lebewesens mit deinem Schwert verursacht Blutung" +lore2 = "Blutungsdauer" +lore3 = "Blutungs-Abklingzeit" +lore = ["Das Treffen eines Lebewesens mit deinem Schwert verursacht Blutung", "Blutungsdauer", "Blutungs-Abklingzeit"] [sword.poisoned_blade] - name = "Vergiftete Klinge" - description = "Schwertschläge verursachen Vergiftung!" - lore1 = "Das Treffen eines Lebewesens mit deinem Schwert verursacht Vergiftung" - lore2 = "Vergiftungsdauer" - lore3 = "Vergiftungs-Abklingzeit" - lore = ["Das Treffen eines Lebewesens mit deinem Schwert verursacht Vergiftung", "Vergiftungsdauer", "Vergiftungs-Abklingzeit"] +name = "Vergiftete Klinge" +description = "Schwertschläge verursachen Vergiftung!" +lore1 = "Das Treffen eines Lebewesens mit deinem Schwert verursacht Vergiftung" +lore2 = "Vergiftungsdauer" +lore3 = "Vergiftungs-Abklingzeit" +lore = ["Das Treffen eines Lebewesens mit deinem Schwert verursacht Vergiftung", "Vergiftungsdauer", "Vergiftungs-Abklingzeit"] # taming [taming] [taming.damage] - name = "Zähm-Schaden" - description = "Erhöhe den Schaden deiner gezähmten Tiere." - lore1 = "Erhöhter Schaden" - lore = ["Erhöhter Schaden"] +name = "Zähm-Schaden" +description = "Erhöhe den Schaden deiner gezähmten Tiere." +lore1 = "Erhöhter Schaden" +lore = ["Erhöhter Schaden"] [taming.health] - name = "Zähm-Gesundheit" - description = "Erhöhe die Gesundheit deiner gezähmten Tiere." - lore1 = "Erhöhte Gesundheit" - lore = ["Erhöhte Gesundheit"] +name = "Zähm-Gesundheit" +description = "Erhöhe die Gesundheit deiner gezähmten Tiere." +lore1 = "Erhöhte Gesundheit" +lore = ["Erhöhte Gesundheit"] [taming.regeneration] - name = "Zähm-Regeneration" - description = "Erhöhe die Regeneration deiner gezähmten Tiere." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Zähm-Regeneration" +description = "Erhöhe die Regeneration deiner gezähmten Tiere." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Dornen" - description = "Reflektiere Schaden zurück an deinen Angreifer!" - lore1 = "Vergoltener Schaden bei Treffern" - lore = ["Vergoltener Schaden bei Treffern"] +name = "Dornen" +description = "Reflektiere Schaden zurück an deinen Angreifer!" +lore1 = "Vergoltener Schaden bei Treffern" +lore = ["Vergoltener Schaden bei Treffern"] [tragoul.globe] - name = "Globus des Schmerzes" - description = "Verteile den verursachten Schaden basierend auf der Anzahl der Feinde um dich herum!" - lore1 = "Je mehr Feinde um dich herum, desto weniger Schaden fügst du jedem einzelnen zu" - lore2 = "Reichweite: " - lore3 = "Zusätzlicher Schaden an alle Wesen: " - lore = ["Je mehr Feinde um dich herum, desto weniger Schaden fügst du jedem einzelnen zu", "Reichweite: ", "Zusätzlicher Schaden an alle Wesen: "] +name = "Globus des Schmerzes" +description = "Verteile den verursachten Schaden basierend auf der Anzahl der Feinde um dich herum!" +lore1 = "Je mehr Feinde um dich herum, desto weniger Schaden fügst du jedem einzelnen zu" +lore2 = "Reichweite: " +lore3 = "Zusätzlicher Schaden an alle Wesen: " +lore = ["Je mehr Feinde um dich herum, desto weniger Schaden fügst du jedem einzelnen zu", "Reichweite: ", "Zusätzlicher Schaden an alle Wesen: "] [tragoul.healing] - name = "Wille des Schmerzes" - description = "Gewinne Gesundheit basierend auf dem verursachten Schaden!" - lore1 = "Dinge zu verletzen hat sich noch nie so gut angefühlt! Heile durch verursachten Schaden" - lore2 = "Es gibt ein 3-Sekunden-Schadensfenster für Heilung und 1 Sekunde Abklingzeit " - lore3 = "Heilung pro Schadensprozentsatz: " - lore = ["Dinge zu verletzen hat sich noch nie so gut angefühlt! Heile durch verursachten Schaden", "Es gibt ein 3-Sekunden-Schadensfenster für Heilung und 1 Sekunde Abklingzeit ", "Heilung pro Schadensprozentsatz: "] +name = "Wille des Schmerzes" +description = "Gewinne Gesundheit basierend auf dem verursachten Schaden!" +lore1 = "Dinge zu verletzen hat sich noch nie so gut angefühlt! Heile durch verursachten Schaden" +lore2 = "Es gibt ein 3-Sekunden-Schadensfenster für Heilung und 1 Sekunde Abklingzeit " +lore3 = "Heilung pro Schadensprozentsatz: " +lore = ["Dinge zu verletzen hat sich noch nie so gut angefühlt! Heile durch verursachten Schaden", "Es gibt ein 3-Sekunden-Schadensfenster für Heilung und 1 Sekunde Abklingzeit ", "Heilung pro Schadensprozentsatz: "] [tragoul.lance] - name = "Leichenlanzen" - description = "Einen Feind zu töten oder eine Fähigkeit, die einen Feind tötet, erzeugt eine Lanze, die einem nahegelegenen Feind Schaden zufügt!" - lore1 = "Lanzen zielen auf alles, was du tötest, UND wenn diese Fähigkeit einen Feind tötet." - lore2 = "Opfere einen Teil deines Lebens, um die Lanzen zu erschaffen (das kann dich töten)" - lore3 = "Max. Lanzen: 1 + " - lore = ["Lanzen zielen auf alles, was du tötest, UND wenn diese Fähigkeit einen Feind tötet.", "Opfere einen Teil deines Lebens, um die Lanzen zu erschaffen (das kann dich töten)", "Max. Lanzen: 1 + "] +name = "Leichenlanzen" +description = "Einen Feind zu töten oder eine Fähigkeit, die einen Feind tötet, erzeugt eine Lanze, die einem nahegelegenen Feind Schaden zufügt!" +lore1 = "Lanzen zielen auf alles, was du tötest, UND wenn diese Fähigkeit einen Feind tötet." +lore2 = "Opfere einen Teil deines Lebens, um die Lanzen zu erschaffen (das kann dich töten)" +lore3 = "Max. Lanzen: 1 + " +lore = ["Lanzen zielen auf alles, was du tötest, UND wenn diese Fähigkeit einen Feind tötet.", "Opfere einen Teil deines Lebens, um die Lanzen zu erschaffen (das kann dich töten)", "Max. Lanzen: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Glaskanone" - description = "Bonus auf unbewaffneten Schaden, je niedriger dein Rüstungswert" - lore1 = "x Schaden bei 0 Rüstung" - lore2 = "Bonus-Schaden pro Stufe" - lore = ["x Schaden bei 0 Rüstung", "Bonus-Schaden pro Stufe"] +name = "Glaskanone" +description = "Bonus auf unbewaffneten Schaden, je niedriger dein Rüstungswert" +lore1 = "x Schaden bei 0 Rüstung" +lore2 = "Bonus-Schaden pro Stufe" +lore = ["x Schaden bei 0 Rüstung", "Bonus-Schaden pro Stufe"] [unarmed.power] - name = "Unbewaffnete Stärke" - description = "Verbesserter unbewaffneter Schaden" - lore1 = "Schaden" - lore = ["Schaden"] +name = "Unbewaffnete Stärke" +description = "Verbesserter unbewaffneter Schaden" +lore1 = "Schaden" +lore = ["Schaden"] [unarmed.sucker_punch] - name = "Fausthieb" - description = "Sprint-Schläge, aber tödlicher." - lore1 = "Schaden" - lore2 = "Schaden erhöht sich mit deiner Geschwindigkeit beim Schlagen" - lore = ["Schaden", "Schaden erhöht sich mit deiner Geschwindigkeit beim Schlagen"] +name = "Fausthieb" +description = "Sprint-Schläge, aber tödlicher." +lore1 = "Schaden" +lore2 = "Schaden erhöht sich mit deiner Geschwindigkeit beim Schlagen" +lore = ["Schaden", "Schaden erhöht sich mit deiner Geschwindigkeit beim Schlagen"] diff --git a/src/main/resources/en_US.toml b/src/main/resources/en_US.toml index 282b44a63..49a731263 100644 --- a/src/main/resources/en_US.toml +++ b/src/main/resources/en_US.toml @@ -7,2603 +7,2603 @@ [advancement] [advancement.challenge_move_1k] - title = "Gotta Move!" - description = "Walk over 1 Kilometer (1,000 blocks)" +title = "Gotta Move!" +description = "Walk over 1 Kilometer (1,000 blocks)" [advancement.challenge_sprint_5k] - title = "Sprint a 5K!" - description = "Walk over 5 Kilometers (5,000 blocks)" +title = "Sprint a 5K!" +description = "Walk over 5 Kilometers (5,000 blocks)" [advancement.challenge_sprint_50k] - title = "Zoom a 50K!" - description = "Walk over 50 Kilometers (50,000 blocks)" +title = "Zoom a 50K!" +description = "Walk over 50 Kilometers (50,000 blocks)" [advancement.challenge_sprint_500k] - title = "Traverse the Universe!!" - description = "Walk over 500 Kilometers (500,000 blocks)" +title = "Traverse the Universe!!" +description = "Walk over 500 Kilometers (500,000 blocks)" [advancement.challenge_sprint_marathon] - title = "Sprint a (literal) Marathon!" - description = "Sprint over 42,195 Blocks!" +title = "Sprint a (literal) Marathon!" +description = "Sprint over 42,195 Blocks!" [advancement.challenge_place_1k] - title = "Foundation Layer" - description = "Place 1,000 blocks" +title = "Foundation Layer" +description = "Place 1,000 blocks" [advancement.challenge_place_5k] - title = "Rising Walls" - description = "Place 5,000 blocks" +title = "Rising Walls" +description = "Place 5,000 blocks" [advancement.challenge_place_50k] - title = "City Planner" - description = "Place 50,000 blocks" +title = "City Planner" +description = "Place 50,000 blocks" [advancement.challenge_place_500k] - title = "World Shaper" - description = "Place 500,000 blocks" +title = "World Shaper" +description = "Place 500,000 blocks" [advancement.challenge_place_5m] - title = "Symmetry's Acolyte" - description = "Place 5,000,000 blocks" +title = "Symmetry's Acolyte" +description = "Place 5,000,000 blocks" [advancement.challenge_chop_1k] - title = "Splinter Collector" - description = "Chop 1,000 blocks" +title = "Splinter Collector" +description = "Chop 1,000 blocks" [advancement.challenge_chop_5k] - title = "Timber!" - description = "Chop 5,000 blocks" +title = "Timber!" +description = "Chop 5,000 blocks" [advancement.challenge_chop_50k] - title = "Deforester" - description = "Chop 50,000 blocks" +title = "Deforester" +description = "Chop 50,000 blocks" [advancement.challenge_chop_500k] - title = "Paul Bunyan" - description = "Chop 500,000 blocks" +title = "Paul Bunyan" +description = "Chop 500,000 blocks" [advancement.challenge_chop_5m] - title = "Jackson the Dog" - description = "The bestest good Boy! (5 Million blocks)" +title = "Jackson the Dog" +description = "The bestest good Boy! (5 Million blocks)" [advancement.challenge_block_1k] - title = "Shield Bearer" - description = "Block 1,000 hits" +title = "Shield Bearer" +description = "Block 1,000 hits" [advancement.challenge_block_5k] - title = "Iron Wall" - description = "Block 5,000 hits" +title = "Iron Wall" +description = "Block 5,000 hits" [advancement.challenge_block_50k] - title = "Immovable Object" - description = "Block 50,000 hits" +title = "Immovable Object" +description = "Block 50,000 hits" [advancement.challenge_block_500k] - title = "Living Fortress" - description = "Block 500,000 hits" +title = "Living Fortress" +description = "Block 500,000 hits" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Block 5,000,000 hits" +title = "Die Hand Die Verletzt" +description = "Block 5,000,000 hits" [advancement.challenge_brew_1k] - title = "Bottoms Up" - description = "Consume 1,000 potions" +title = "Bottoms Up" +description = "Consume 1,000 potions" [advancement.challenge_brew_5k] - title = "Potion Addict" - description = "Consume 5,000 potions" +title = "Potion Addict" +description = "Consume 5,000 potions" [advancement.challenge_brew_50k] - title = "Elixir Connoisseur" - description = "Consume 50,000 potions" +title = "Elixir Connoisseur" +description = "Consume 50,000 potions" [advancement.challenge_brew_500k] - title = "Liquid Courage" - description = "Consume 500,000 potions" +title = "Liquid Courage" +description = "Consume 500,000 potions" [advancement.challenge_brew_5m] - title = "The Alchemist" - description = "Consume 5,000,000 potions" +title = "The Alchemist" +description = "Consume 5,000,000 potions" [advancement.challenge_brewsplash_1k] - title = "Glass Cannon" - description = "Splash 1,000 potions" +title = "Glass Cannon" +description = "Splash 1,000 potions" [advancement.challenge_brewsplash_5k] - title = "Area Denial" - description = "Splash 5,000 potions" +title = "Area Denial" +description = "Splash 5,000 potions" [advancement.challenge_brewsplash_50k] - title = "Chemical Warfare" - description = "Splash 50,000 potions" +title = "Chemical Warfare" +description = "Splash 50,000 potions" [advancement.challenge_brewsplash_500k] - title = "Rain of Reagents" - description = "Splash 500,000 potions" +title = "Rain of Reagents" +description = "Splash 500,000 potions" [advancement.challenge_brewsplash_5m] - title = "The Splash Meister" - description = "Splash 5,000,000 potions" +title = "The Splash Meister" +description = "Splash 5,000,000 potions" [advancement.challenge_craft_1k] - title = "Crafty Crafter!" - description = "Craft 1000 Items" +title = "Crafty Crafter!" +description = "Craft 1000 Items" [advancement.challenge_craft_5k] - title = "Cantankerous Crafter!" - description = "Craft 5000 Items" +title = "Cantankerous Crafter!" +description = "Craft 5000 Items" [advancement.challenge_craft_50k] - title = "Servile Crafter!" - description = "Craft 50,000 Items" +title = "Servile Crafter!" +description = "Craft 50,000 Items" [advancement.challenge_craft_500k] - title = "Cacophonous Crafter!" - description = "Craft 500,000 Items" +title = "Cacophonous Crafter!" +description = "Craft 500,000 Items" [advancement.challenge_craft_5m] - title = "Calamitous McCraftface" - description = "Craft 5,000,000 Items" +title = "Calamitous McCraftface" +description = "Craft 5,000,000 Items" [advancement.challenge_enchant_1k] - title = "Arcane Dabbler" - description = "Enchant 1,000 items" +title = "Arcane Dabbler" +description = "Enchant 1,000 items" [advancement.challenge_enchant_5k] - title = "Runeweaver" - description = "Enchant 5,000 items" +title = "Runeweaver" +description = "Enchant 5,000 items" [advancement.challenge_enchant_50k] - title = "Mystic Artisan" - description = "Enchant 50,000 items" +title = "Mystic Artisan" +description = "Enchant 50,000 items" [advancement.challenge_enchant_500k] - title = "Archmage" - description = "Enchant 500,000 items" +title = "Archmage" +description = "Enchant 500,000 items" [advancement.challenge_enchant_5m] - title = "Enigmatic Enchanter" - description = "Enchant 5,000,000 items" +title = "Enigmatic Enchanter" +description = "Enchant 5,000,000 items" [advancement.challenge_excavate_1k] - title = "Dirt Mover" - description = "Excavate 1,000 blocks" +title = "Dirt Mover" +description = "Excavate 1,000 blocks" [advancement.challenge_excavate_5k] - title = "Trench Digger" - description = "Excavate 5,000 blocks" +title = "Trench Digger" +description = "Excavate 5,000 blocks" [advancement.challenge_excavate_50k] - title = "Canyon Carver" - description = "Excavate 50,000 blocks" +title = "Canyon Carver" +description = "Excavate 50,000 blocks" [advancement.challenge_excavate_500k] - title = "Terrain Sculptor" - description = "Excavate 500,000 blocks" +title = "Terrain Sculptor" +description = "Excavate 500,000 blocks" [advancement.challenge_excavate_5m] - title = "Geological Anomaly" - description = "Excavate 5,000,000 blocks" +title = "Geological Anomaly" +description = "Excavate 5,000,000 blocks" [advancement.horrible_person] - title = "You're a Horrible Person" - description = "Unfathomable, really" +title = "You're a Horrible Person" +description = "Unfathomable, really" [advancement.challenge_turtle_egg_smasher] - title = "Turtle Egg Smasher!" - description = "Break 100 turtle eggs" +title = "Turtle Egg Smasher!" +description = "Break 100 turtle eggs" [advancement.challenge_turtle_egg_annihilator] - title = "Turtle Egg Annihilator!" - description = "Break 500 turtle eggs" +title = "Turtle Egg Annihilator!" +description = "Break 500 turtle eggs" [advancement.challenge_novice_hunter] - title = "Predator's Instinct" - description = "Kill 100 entities" +title = "Predator's Instinct" +description = "Kill 100 entities" [advancement.challenge_intermediate_hunter] - title = "Apex Predator" - description = "Kill 500 entities" +title = "Apex Predator" +description = "Kill 500 entities" [advancement.challenge_advanced_hunter] - title = "Extinction Event" - description = "Kill 5,000 entities" +title = "Extinction Event" +description = "Kill 5,000 entities" [advancement.challenge_creeper_conqueror] - title = "Creeper Conqueror!" - description = "Kill 50 creepers" +title = "Creeper Conqueror!" +description = "Kill 50 creepers" [advancement.challenge_creeper_annihilator] - title = "Creeper Annihilator!" - description = "Kill 200 creepers" +title = "Creeper Annihilator!" +description = "Kill 200 creepers" [advancement.challenge_pickaxe_1k] - title = "Rock Breaker" - description = "Break 1,000 blocks with a pickaxe" +title = "Rock Breaker" +description = "Break 1,000 blocks with a pickaxe" [advancement.challenge_pickaxe_5k] - title = "Tunnel Rat" - description = "Break 5,000 blocks with a pickaxe" +title = "Tunnel Rat" +description = "Break 5,000 blocks with a pickaxe" [advancement.challenge_pickaxe_50k] - title = "Dwarven Heritage" - description = "Break 50,000 blocks with a pickaxe" +title = "Dwarven Heritage" +description = "Break 50,000 blocks with a pickaxe" [advancement.challenge_pickaxe_500k] - title = "Core Breacher" - description = "Break 500,000 blocks with a pickaxe" +title = "Core Breacher" +description = "Break 500,000 blocks with a pickaxe" [advancement.challenge_pickaxe_5m] - title = "World Eater" - description = "Break 5,000,000 blocks with a pickaxe" +title = "World Eater" +description = "Break 5,000,000 blocks with a pickaxe" [advancement.challenge_eat_100] - title = "So much to eat!" - description = "Eat over 100 Items!" +title = "So much to eat!" +description = "Eat over 100 Items!" [advancement.challenge_eat_1000] - title = "Unquenchable Hunger!" - description = "Eat over 1,000 Items!" +title = "Unquenchable Hunger!" +description = "Eat over 1,000 Items!" [advancement.challenge_eat_10000] - title = "EVERLASTING HUNGER!" - description = "Eat over 10,000 Items!" +title = "EVERLASTING HUNGER!" +description = "Eat over 10,000 Items!" [advancement.challenge_harvest_100] - title = "Full Harvest" - description = "Harvest over 100 crops!" +title = "Full Harvest" +description = "Harvest over 100 crops!" [advancement.challenge_harvest_1000] - title = "Grand Harvest" - description = "Harvest over 1,000 crops!" +title = "Grand Harvest" +description = "Harvest over 1,000 crops!" [advancement.challenge_swim_1nm] - title = "Human Submarine" - description = "Swim 1 Nautical Mile (1,852 blocks)" +title = "Human Submarine" +description = "Swim 1 Nautical Mile (1,852 blocks)" [advancement.challenge_swim_5k] - title = "Deep Diver" - description = "Swim over 5,000 blocks" +title = "Deep Diver" +description = "Swim over 5,000 blocks" [advancement.challenge_swim_20k] - title = "Poseidon's Chosen" - description = "Swim over 20,000 blocks" +title = "Poseidon's Chosen" +description = "Swim over 20,000 blocks" [advancement.challenge_sneak_1k] - title = "Knee Pain" - description = "Sneak over a kilometer (1,000 blocks)" +title = "Knee Pain" +description = "Sneak over a kilometer (1,000 blocks)" [advancement.challenge_sneak_5k] - title = "Shadow Walker" - description = "Sneak over 5,000 blocks" +title = "Shadow Walker" +description = "Sneak over 5,000 blocks" [advancement.challenge_sneak_20k] - title = "Ghost" - description = "Sneak over 20,000 blocks" +title = "Ghost" +description = "Sneak over 20,000 blocks" [advancement.challenge_sword_100] - title = "First Blood" - description = "Land 100 hits with a sword" +title = "First Blood" +description = "Land 100 hits with a sword" [advancement.challenge_sword_1k] - title = "Blade Dancer" - description = "Land 1,000 hits with a sword" +title = "Blade Dancer" +description = "Land 1,000 hits with a sword" [advancement.challenge_sword_10k] - title = "Thousand Cuts" - description = "Land 10,000 hits with a sword" +title = "Thousand Cuts" +description = "Land 10,000 hits with a sword" [advancement.challenge_unarmed_100] - title = "Bar Brawler" - description = "Land 100 unarmed hits" +title = "Bar Brawler" +description = "Land 100 unarmed hits" [advancement.challenge_unarmed_1k] - title = "Iron Fists" - description = "Land 1,000 unarmed hits" +title = "Iron Fists" +description = "Land 1,000 unarmed hits" [advancement.challenge_unarmed_10k] - title = "One Punch" - description = "Land 10,000 unarmed hits" +title = "One Punch" +description = "Land 10,000 unarmed hits" [advancement.challenge_trag_1k] - title = "Blood Price" - description = "Receive 1,000 damage" +title = "Blood Price" +description = "Receive 1,000 damage" [advancement.challenge_trag_10k] - title = "Crimson Tide" - description = "Receive 10,000 damage" +title = "Crimson Tide" +description = "Receive 10,000 damage" [advancement.challenge_trag_100k] - title = "Avatar of Suffering" - description = "Receive 100,000 damage" +title = "Avatar of Suffering" +description = "Receive 100,000 damage" [advancement.challenge_ranged_100] - title = "Target Practice" - description = "Fire 100 projectiles" +title = "Target Practice" +description = "Fire 100 projectiles" [advancement.challenge_ranged_1k] - title = "Hawkeye" - description = "Fire 1,000 projectiles" +title = "Hawkeye" +description = "Fire 1,000 projectiles" [advancement.challenge_ranged_10k] - title = "Storm of Arrows" - description = "Fire 10,000 projectiles" +title = "Storm of Arrows" +description = "Fire 10,000 projectiles" [advancement.challenge_chronos_1h] - title = "Tick Tock" - description = "Spend 1 hour online" +title = "Tick Tock" +description = "Spend 1 hour online" [advancement.challenge_chronos_24h] - title = "Sands of Time" - description = "Spend 24 hours online" +title = "Sands of Time" +description = "Spend 24 hours online" [advancement.challenge_chronos_168h] - title = "Timeless" - description = "Spend 168 hours (1 week) online" +title = "Timeless" +description = "Spend 168 hours (1 week) online" [advancement.challenge_nether_50] - title = "Hell's Gatekeeper" - description = "Slay 50 nether creatures" +title = "Hell's Gatekeeper" +description = "Slay 50 nether creatures" [advancement.challenge_nether_500] - title = "Abyssal Warden" - description = "Slay 500 nether creatures" +title = "Abyssal Warden" +description = "Slay 500 nether creatures" [advancement.challenge_nether_5k] - title = "Lord of the Nether" - description = "Slay 5,000 nether creatures" +title = "Lord of the Nether" +description = "Slay 5,000 nether creatures" [advancement.challenge_rift_50] - title = "Spatial Anomaly" - description = "Teleport 50 times" +title = "Spatial Anomaly" +description = "Teleport 50 times" [advancement.challenge_rift_500] - title = "Void Walker" - description = "Teleport 500 times" +title = "Void Walker" +description = "Teleport 500 times" [advancement.challenge_rift_5k] - title = "Between Worlds" - description = "Teleport 5,000 times" +title = "Between Worlds" +description = "Teleport 5,000 times" [advancement.challenge_taming_10] - title = "Animal Whisperer" - description = "Breed 10 animals" +title = "Animal Whisperer" +description = "Breed 10 animals" [advancement.challenge_taming_50] - title = "Pack Leader" - description = "Breed 50 animals" +title = "Pack Leader" +description = "Breed 50 animals" [advancement.challenge_taming_500] - title = "Beastmaster" - description = "Breed 500 animals" +title = "Beastmaster" +description = "Breed 500 animals" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Speed Demon" - description = "Sprint over 5 Kilometers (5,000 blocks)" +title = "Speed Demon" +description = "Sprint over 5 Kilometers (5,000 blocks)" [advancement.challenge_sprint_dist_50k] - title = "Lightning Legs" - description = "Sprint over 50 Kilometers (50,000 blocks)" +title = "Lightning Legs" +description = "Sprint over 50 Kilometers (50,000 blocks)" [advancement.challenge_agility_swim_1k] - title = "Water Strider" - description = "Swim over 1 Kilometer (1,000 blocks)" +title = "Water Strider" +description = "Swim over 1 Kilometer (1,000 blocks)" [advancement.challenge_agility_swim_10k] - title = "Aquatic Voyager" - description = "Swim over 10 Kilometers (10,000 blocks)" +title = "Aquatic Voyager" +description = "Swim over 10 Kilometers (10,000 blocks)" [advancement.challenge_fly_1k] - title = "Sky Dancer" - description = "Fly over 1 Kilometer (1,000 blocks)" +title = "Sky Dancer" +description = "Fly over 1 Kilometer (1,000 blocks)" [advancement.challenge_fly_10k] - title = "Wind Rider" - description = "Fly over 10 Kilometers (10,000 blocks)" +title = "Wind Rider" +description = "Fly over 10 Kilometers (10,000 blocks)" [advancement.challenge_agility_sneak_500] - title = "Quiet Steps" - description = "Sneak over 500 blocks" +title = "Quiet Steps" +description = "Sneak over 500 blocks" [advancement.challenge_agility_sneak_5k] - title = "Phantom Steps" - description = "Sneak over 5 Kilometers (5,000 blocks)" +title = "Phantom Steps" +description = "Sneak over 5 Kilometers (5,000 blocks)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Demolition Crew" - description = "Break 500 blocks" +title = "Demolition Crew" +description = "Break 500 blocks" [advancement.challenge_demolish_5k] - title = "Wrecking Ball" - description = "Break 5,000 blocks" +title = "Wrecking Ball" +description = "Break 5,000 blocks" [advancement.challenge_value_placed_10k] - title = "Valuable Builder" - description = "Place blocks worth 10,000 value" +title = "Valuable Builder" +description = "Place blocks worth 10,000 value" [advancement.challenge_value_placed_100k] - title = "Master Architect" - description = "Place blocks worth 100,000 value" +title = "Master Architect" +description = "Place blocks worth 100,000 value" [advancement.challenge_demolish_val_5k] - title = "Salvage Expert" - description = "Salvage 5,000 block value from demolition" +title = "Salvage Expert" +description = "Salvage 5,000 block value from demolition" [advancement.challenge_demolish_val_50k] - title = "Total Deconstruction" - description = "Salvage 50,000 block value from demolition" +title = "Total Deconstruction" +description = "Salvage 50,000 block value from demolition" [advancement.challenge_high_build_100] - title = "Sky Builder" - description = "Place 100 blocks above Y=128" +title = "Sky Builder" +description = "Place 100 blocks above Y=128" [advancement.challenge_high_build_1k] - title = "Cloud Architect" - description = "Place 1,000 blocks above Y=128" +title = "Cloud Architect" +description = "Place 1,000 blocks above Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Axe Swinger" - description = "Swing your axe 500 times" +title = "Axe Swinger" +description = "Swing your axe 500 times" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Swing your axe 5,000 times" +title = "Berserker" +description = "Swing your axe 5,000 times" [advancement.challenge_axe_damage_1k] - title = "Cleaver" - description = "Deal 1,000 damage with axes" +title = "Cleaver" +description = "Deal 1,000 damage with axes" [advancement.challenge_axe_damage_10k] - title = "Executioner's Axe" - description = "Deal 10,000 damage with axes" +title = "Executioner's Axe" +description = "Deal 10,000 damage with axes" [advancement.challenge_axe_value_5k] - title = "Timber Merchant" - description = "Harvest 5,000 value worth of wood" +title = "Timber Merchant" +description = "Harvest 5,000 value worth of wood" [advancement.challenge_axe_value_50k] - title = "Logging Baron" - description = "Harvest 50,000 value worth of wood" +title = "Logging Baron" +description = "Harvest 50,000 value worth of wood" [advancement.challenge_leaves_500] - title = "Leaf Blower" - description = "Clear 500 leaf blocks with an axe" +title = "Leaf Blower" +description = "Clear 500 leaf blocks with an axe" [advancement.challenge_leaves_5k] - title = "Defoliator" - description = "Clear 5,000 leaf blocks with an axe" +title = "Defoliator" +description = "Clear 5,000 leaf blocks with an axe" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Damage Absorber" - description = "Block 1,000 damage with a shield" +title = "Damage Absorber" +description = "Block 1,000 damage with a shield" [advancement.challenge_block_dmg_10k] - title = "Human Shield" - description = "Block 10,000 damage with a shield" +title = "Human Shield" +description = "Block 10,000 damage with a shield" [advancement.challenge_block_proj_100] - title = "Arrow Deflector" - description = "Block 100 projectiles with a shield" +title = "Arrow Deflector" +description = "Block 100 projectiles with a shield" [advancement.challenge_block_proj_1k] - title = "Projectile Shield" - description = "Block 1,000 projectiles with a shield" +title = "Projectile Shield" +description = "Block 1,000 projectiles with a shield" [advancement.challenge_block_melee_500] - title = "Parry Master" - description = "Block 500 melee attacks with a shield" +title = "Parry Master" +description = "Block 500 melee attacks with a shield" [advancement.challenge_block_melee_5k] - title = "Iron Fortress" - description = "Block 5,000 melee attacks with a shield" +title = "Iron Fortress" +description = "Block 5,000 melee attacks with a shield" [advancement.challenge_block_heavy_50] - title = "Tank" - description = "Block 50 heavy attacks (over 5 damage)" +title = "Tank" +description = "Block 50 heavy attacks (over 5 damage)" [advancement.challenge_block_heavy_500] - title = "Immovable Object" - description = "Block 500 heavy attacks (over 5 damage)" +title = "Immovable Object" +description = "Block 500 heavy attacks (over 5 damage)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Brewer's Setup" - description = "Place 10 brewing stands" +title = "Brewer's Setup" +description = "Place 10 brewing stands" [advancement.challenge_brew_stands_50] - title = "Potion Factory" - description = "Place 50 brewing stands" +title = "Potion Factory" +description = "Place 50 brewing stands" [advancement.challenge_brew_strong_25] - title = "Strong Brew" - description = "Consume 25 upgraded potions" +title = "Strong Brew" +description = "Consume 25 upgraded potions" [advancement.challenge_brew_strong_250] - title = "Maximum Potency" - description = "Consume 250 upgraded potions" +title = "Maximum Potency" +description = "Consume 250 upgraded potions" [advancement.challenge_brew_splash_hits_50] - title = "Splash Zone" - description = "Hit 50 entities with splash potions" +title = "Splash Zone" +description = "Hit 50 entities with splash potions" [advancement.challenge_brew_splash_hits_500] - title = "Plague Doctor" - description = "Hit 500 entities with splash potions" +title = "Plague Doctor" +description = "Hit 500 entities with splash potions" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Restless" - description = "Travel 1 Kilometer while active" +title = "Restless" +description = "Travel 1 Kilometer while active" [advancement.challenge_active_dist_10k] - title = "Pathfinder" - description = "Travel 10 Kilometers while active" +title = "Pathfinder" +description = "Travel 10 Kilometers while active" [advancement.challenge_active_dist_100k] - title = "Perpetual Motion" - description = "Travel 100 Kilometers while active" +title = "Perpetual Motion" +description = "Travel 100 Kilometers while active" [advancement.challenge_beds_10] - title = "Early Riser" - description = "Sleep in a bed 10 times" +title = "Early Riser" +description = "Sleep in a bed 10 times" [advancement.challenge_beds_100] - title = "Time Skipper" - description = "Sleep in a bed 100 times" +title = "Time Skipper" +description = "Sleep in a bed 100 times" [advancement.challenge_chronos_tp_50] - title = "Temporal Shift" - description = "Teleport 50 times" +title = "Temporal Shift" +description = "Teleport 50 times" [advancement.challenge_chronos_tp_500] - title = "Time Warp" - description = "Teleport 500 times" +title = "Time Warp" +description = "Teleport 500 times" [advancement.challenge_chronos_deaths_10] - title = "Mortal" - description = "Die 10 times" +title = "Mortal" +description = "Die 10 times" [advancement.challenge_chronos_deaths_100] - title = "Death Defier" - description = "Die 100 times" +title = "Death Defier" +description = "Die 100 times" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Craft Worth" - description = "Craft items worth 10,000 total value" +title = "Craft Worth" +description = "Craft items worth 10,000 total value" [advancement.challenge_craft_value_100k] - title = "Artisan" - description = "Craft items worth 100,000 total value" +title = "Artisan" +description = "Craft items worth 100,000 total value" [advancement.challenge_craft_tools_25] - title = "Toolsmith" - description = "Craft 25 tools" +title = "Toolsmith" +description = "Craft 25 tools" [advancement.challenge_craft_tools_250] - title = "Master Forger" - description = "Craft 250 tools" +title = "Master Forger" +description = "Craft 250 tools" [advancement.challenge_craft_armor_25] - title = "Armorsmith" - description = "Craft 25 pieces of armor" +title = "Armorsmith" +description = "Craft 25 pieces of armor" [advancement.challenge_craft_armor_250] - title = "Master Armorer" - description = "Craft 250 pieces of armor" +title = "Master Armorer" +description = "Craft 250 pieces of armor" [advancement.challenge_craft_mass_25k] - title = "Mass Producer" - description = "Craft 25,000 items" +title = "Mass Producer" +description = "Craft 25,000 items" [advancement.challenge_craft_mass_250k] - title = "Industrial Revolution" - description = "Craft 250,000 items" +title = "Industrial Revolution" +description = "Craft 250,000 items" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Collector" - description = "Discover 50 unique items" +title = "Collector" +description = "Discover 50 unique items" [advancement.challenge_discover_items_250] - title = "Cataloger" - description = "Discover 250 unique items" +title = "Cataloger" +description = "Discover 250 unique items" [advancement.challenge_discover_blocks_50] - title = "Surveyor" - description = "Discover 50 unique blocks" +title = "Surveyor" +description = "Discover 50 unique blocks" [advancement.challenge_discover_blocks_250] - title = "Geologist" - description = "Discover 250 unique blocks" +title = "Geologist" +description = "Discover 250 unique blocks" [advancement.challenge_discover_mobs_25] - title = "Observer" - description = "Discover 25 unique mobs" +title = "Observer" +description = "Discover 25 unique mobs" [advancement.challenge_discover_mobs_75] - title = "Naturalist" - description = "Discover 75 unique mobs" +title = "Naturalist" +description = "Discover 75 unique mobs" [advancement.challenge_discover_biomes_10] - title = "Wanderer" - description = "Discover 10 unique biomes" +title = "Wanderer" +description = "Discover 10 unique biomes" [advancement.challenge_discover_biomes_40] - title = "World Traveler" - description = "Discover 40 unique biomes" +title = "World Traveler" +description = "Discover 40 unique biomes" [advancement.challenge_discover_foods_10] - title = "Foodie" - description = "Discover 10 unique foods" +title = "Foodie" +description = "Discover 10 unique foods" [advancement.challenge_discover_foods_30] - title = "Culinary Master" - description = "Discover 30 unique foods" +title = "Culinary Master" +description = "Discover 30 unique foods" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Power Weaver" - description = "Accumulate 100 enchantment power" +title = "Power Weaver" +description = "Accumulate 100 enchantment power" [advancement.challenge_enchant_power_1k] - title = "Arcane Master" - description = "Accumulate 1,000 enchantment power" +title = "Arcane Master" +description = "Accumulate 1,000 enchantment power" [advancement.challenge_enchant_levels_1k] - title = "Level Spender" - description = "Spend 1,000 experience levels on enchanting" +title = "Level Spender" +description = "Spend 1,000 experience levels on enchanting" [advancement.challenge_enchant_levels_10k] - title = "XP Sink" - description = "Spend 10,000 experience levels on enchanting" +title = "XP Sink" +description = "Spend 10,000 experience levels on enchanting" [advancement.challenge_enchant_high_25] - title = "High Roller" - description = "Perform 25 max-level enchantments" +title = "High Roller" +description = "Perform 25 max-level enchantments" [advancement.challenge_enchant_high_250] - title = "Legendary Enchanter" - description = "Perform 250 max-level enchantments" +title = "Legendary Enchanter" +description = "Perform 250 max-level enchantments" [advancement.challenge_enchant_total_500] - title = "Level Burner" - description = "Spend 500 total levels across all enchantments" +title = "Level Burner" +description = "Spend 500 total levels across all enchantments" [advancement.challenge_enchant_total_5k] - title = "Arcane Investment" - description = "Spend 5,000 total levels across all enchantments" +title = "Arcane Investment" +description = "Spend 5,000 total levels across all enchantments" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Digger" - description = "Swing your shovel 500 times" +title = "Digger" +description = "Swing your shovel 500 times" [advancement.challenge_dig_swing_5k] - title = "Excavator" - description = "Swing your shovel 5,000 times" +title = "Excavator" +description = "Swing your shovel 5,000 times" [advancement.challenge_dig_damage_1k] - title = "Shovel Knight" - description = "Deal 1,000 damage with a shovel" +title = "Shovel Knight" +description = "Deal 1,000 damage with a shovel" [advancement.challenge_dig_damage_10k] - title = "Shovel Master" - description = "Deal 10,000 damage with a shovel" +title = "Shovel Master" +description = "Deal 10,000 damage with a shovel" [advancement.challenge_dig_value_5k] - title = "Dirt Merchant" - description = "Excavate 5,000 value worth of blocks" +title = "Dirt Merchant" +description = "Excavate 5,000 value worth of blocks" [advancement.challenge_dig_value_50k] - title = "Earth Baron" - description = "Excavate 50,000 value worth of blocks" +title = "Earth Baron" +description = "Excavate 50,000 value worth of blocks" [advancement.challenge_dig_gravel_500] - title = "Gravel Grinder" - description = "Dig 500 gravel, sand, or clay blocks" +title = "Gravel Grinder" +description = "Dig 500 gravel, sand, or clay blocks" [advancement.challenge_dig_gravel_5k] - title = "Sand Sifter" - description = "Dig 5,000 gravel, sand, or clay blocks" +title = "Sand Sifter" +description = "Dig 5,000 gravel, sand, or clay blocks" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Seed Sower" - description = "Plant 100 crops" +title = "Seed Sower" +description = "Plant 100 crops" [advancement.challenge_plant_1k] - title = "Green Thumb" - description = "Plant 1,000 crops" +title = "Green Thumb" +description = "Plant 1,000 crops" [advancement.challenge_plant_5k] - title = "Agricultural Baron" - description = "Plant 5,000 crops" +title = "Agricultural Baron" +description = "Plant 5,000 crops" [advancement.challenge_compost_50] - title = "Recycler" - description = "Compost 50 items" +title = "Recycler" +description = "Compost 50 items" [advancement.challenge_compost_500] - title = "Soil Enricher" - description = "Compost 500 items" +title = "Soil Enricher" +description = "Compost 500 items" [advancement.challenge_shear_50] - title = "Shearer" - description = "Shear 50 entities" +title = "Shearer" +description = "Shear 50 entities" [advancement.challenge_shear_250] - title = "Flock Master" - description = "Shear 250 entities" +title = "Flock Master" +description = "Shear 250 entities" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Slayer" - description = "Slay 500 creatures" +title = "Slayer" +description = "Slay 500 creatures" [advancement.challenge_kills_5k] - title = "Executioner" - description = "Slay 5,000 creatures" +title = "Executioner" +description = "Slay 5,000 creatures" [advancement.challenge_boss_1] - title = "Boss Challenger" - description = "Slay a boss mob" +title = "Boss Challenger" +description = "Slay a boss mob" [advancement.challenge_boss_10] - title = "Legend Killer" - description = "Slay 10 boss mobs" +title = "Legend Killer" +description = "Slay 10 boss mobs" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Withered" - description = "Endure 500 wither damage" +title = "Withered" +description = "Endure 500 wither damage" [advancement.challenge_wither_dmg_5k] - title = "Blight Survivor" - description = "Endure 5,000 wither damage" +title = "Blight Survivor" +description = "Endure 5,000 wither damage" [advancement.challenge_wither_skel_25] - title = "Bone Collector" - description = "Slay 25 wither skeletons" +title = "Bone Collector" +description = "Slay 25 wither skeletons" [advancement.challenge_wither_skel_250] - title = "Skeleton Bane" - description = "Slay 250 wither skeletons" +title = "Skeleton Bane" +description = "Slay 250 wither skeletons" [advancement.challenge_wither_boss_1] - title = "Wither Slayer" - description = "Defeat the Wither" +title = "Wither Slayer" +description = "Defeat the Wither" [advancement.challenge_wither_boss_10] - title = "Nether Dominator" - description = "Defeat the Wither 10 times" +title = "Nether Dominator" +description = "Defeat the Wither 10 times" [advancement.challenge_roses_10] - title = "Death Gardener" - description = "Break 10 wither roses" +title = "Death Gardener" +description = "Break 10 wither roses" [advancement.challenge_roses_100] - title = "Blight Collector" - description = "Break 100 wither roses" +title = "Blight Collector" +description = "Break 100 wither roses" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Miner's Arm" - description = "Swing your pickaxe 500 times" +title = "Miner's Arm" +description = "Swing your pickaxe 500 times" [advancement.challenge_pick_swing_5k] - title = "Tunnel Maker" - description = "Swing your pickaxe 5,000 times" +title = "Tunnel Maker" +description = "Swing your pickaxe 5,000 times" [advancement.challenge_pick_damage_1k] - title = "Pick Fighter" - description = "Deal 1,000 damage with a pickaxe" +title = "Pick Fighter" +description = "Deal 1,000 damage with a pickaxe" [advancement.challenge_pick_damage_10k] - title = "War Pick" - description = "Deal 10,000 damage with a pickaxe" +title = "War Pick" +description = "Deal 10,000 damage with a pickaxe" [advancement.challenge_pick_value_5k] - title = "Gem Finder" - description = "Mine 5,000 value worth of blocks" +title = "Gem Finder" +description = "Mine 5,000 value worth of blocks" [advancement.challenge_pick_value_50k] - title = "Ore Baron" - description = "Mine 50,000 value worth of blocks" +title = "Ore Baron" +description = "Mine 50,000 value worth of blocks" [advancement.challenge_pick_ores_500] - title = "Prospector" - description = "Mine 500 ore blocks" +title = "Prospector" +description = "Mine 500 ore blocks" [advancement.challenge_pick_ores_5k] - title = "Master Miner" - description = "Mine 5,000 ore blocks" +title = "Master Miner" +description = "Mine 5,000 ore blocks" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Sharp Shooter" - description = "Deal 1,000 ranged damage" +title = "Sharp Shooter" +description = "Deal 1,000 ranged damage" [advancement.challenge_ranged_dmg_10k] - title = "Lethal Archer" - description = "Deal 10,000 ranged damage" +title = "Lethal Archer" +description = "Deal 10,000 ranged damage" [advancement.challenge_ranged_dist_5k] - title = "Long Range" - description = "Fire projectiles covering 5,000 blocks total distance" +title = "Long Range" +description = "Fire projectiles covering 5,000 blocks total distance" [advancement.challenge_ranged_dist_50k] - title = "Mile Shooter" - description = "Fire projectiles covering 50,000 blocks total distance" +title = "Mile Shooter" +description = "Fire projectiles covering 50,000 blocks total distance" [advancement.challenge_ranged_kills_50] - title = "Bowman" - description = "Kill 50 mobs with ranged weapons" +title = "Bowman" +description = "Kill 50 mobs with ranged weapons" [advancement.challenge_ranged_kills_500] - title = "Master Archer" - description = "Kill 500 mobs with ranged weapons" +title = "Master Archer" +description = "Kill 500 mobs with ranged weapons" [advancement.challenge_longshot_25] - title = "Sniper" - description = "Land 25 long-range shots (over 30 blocks)" +title = "Sniper" +description = "Land 25 long-range shots (over 30 blocks)" [advancement.challenge_longshot_250] - title = "Eagle Eye" - description = "Land 250 long-range shots (over 30 blocks)" +title = "Eagle Eye" +description = "Land 250 long-range shots (over 30 blocks)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Pearl Tosser" - description = "Throw 50 ender pearls" +title = "Pearl Tosser" +description = "Throw 50 ender pearls" [advancement.challenge_rift_pearls_500] - title = "Teleport Junkie" - description = "Throw 500 ender pearls" +title = "Teleport Junkie" +description = "Throw 500 ender pearls" [advancement.challenge_rift_enderman_50] - title = "Enderman Hunter" - description = "Slay 50 endermen" +title = "Enderman Hunter" +description = "Slay 50 endermen" [advancement.challenge_rift_enderman_500] - title = "Void Stalker" - description = "Slay 500 endermen" +title = "Void Stalker" +description = "Slay 500 endermen" [advancement.challenge_rift_dragon_500] - title = "Dragon Fighter" - description = "Deal 500 damage to the Ender Dragon" +title = "Dragon Fighter" +description = "Deal 500 damage to the Ender Dragon" [advancement.challenge_rift_dragon_5k] - title = "Dragonbane" - description = "Deal 5,000 damage to the Ender Dragon" +title = "Dragonbane" +description = "Deal 5,000 damage to the Ender Dragon" [advancement.challenge_rift_crystal_10] - title = "Crystal Breaker" - description = "Destroy 10 end crystals" +title = "Crystal Breaker" +description = "Destroy 10 end crystals" [advancement.challenge_rift_crystal_100] - title = "End Demolisher" - description = "Destroy 100 end crystals" +title = "End Demolisher" +description = "Destroy 100 end crystals" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Angler" - description = "Catch 25 fish" +title = "Angler" +description = "Catch 25 fish" [advancement.challenge_fish_250] - title = "Master Fisher" - description = "Catch 250 fish" +title = "Master Fisher" +description = "Catch 250 fish" [advancement.challenge_drowned_25] - title = "Drowned Hunter" - description = "Slay 25 drowned" +title = "Drowned Hunter" +description = "Slay 25 drowned" [advancement.challenge_drowned_250] - title = "Ocean Cleaner" - description = "Slay 250 drowned" +title = "Ocean Cleaner" +description = "Slay 250 drowned" [advancement.challenge_guardian_10] - title = "Guardian Slayer" - description = "Slay 10 guardians" +title = "Guardian Slayer" +description = "Slay 10 guardians" [advancement.challenge_guardian_100] - title = "Temple Raider" - description = "Slay 100 guardians" +title = "Temple Raider" +description = "Slay 100 guardians" [advancement.challenge_underwater_blocks_100] - title = "Underwater Miner" - description = "Break 100 blocks while underwater" +title = "Underwater Miner" +description = "Break 100 blocks while underwater" [advancement.challenge_underwater_blocks_1k] - title = "Aquatic Engineer" - description = "Break 1,000 blocks while underwater" +title = "Aquatic Engineer" +description = "Break 1,000 blocks while underwater" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Backstabber" - description = "Deal 500 damage while sneaking" +title = "Backstabber" +description = "Deal 500 damage while sneaking" [advancement.challenge_stealth_dmg_5k] - title = "Silent Killer" - description = "Deal 5,000 damage while sneaking" +title = "Silent Killer" +description = "Deal 5,000 damage while sneaking" [advancement.challenge_stealth_kills_10] - title = "Assassin" - description = "Kill 10 mobs while sneaking" +title = "Assassin" +description = "Kill 10 mobs while sneaking" [advancement.challenge_stealth_kills_100] - title = "Shadow Reaper" - description = "Kill 100 mobs while sneaking" +title = "Shadow Reaper" +description = "Kill 100 mobs while sneaking" [advancement.challenge_stealth_time_1h] - title = "Patient" - description = "Spend 1 hour sneaking (3,600 seconds)" +title = "Patient" +description = "Spend 1 hour sneaking (3,600 seconds)" [advancement.challenge_stealth_time_10h] - title = "Master of Shadows" - description = "Spend 10 hours sneaking (36,000 seconds)" +title = "Master of Shadows" +description = "Spend 10 hours sneaking (36,000 seconds)" [advancement.challenge_stealth_arrows_50] - title = "Silent Archer" - description = "Fire 50 arrows while sneaking" +title = "Silent Archer" +description = "Fire 50 arrows while sneaking" [advancement.challenge_stealth_arrows_500] - title = "Phantom Bowman" - description = "Fire 500 arrows while sneaking" +title = "Phantom Bowman" +description = "Fire 500 arrows while sneaking" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Blade Apprentice" - description = "Deal 1,000 damage with swords" +title = "Blade Apprentice" +description = "Deal 1,000 damage with swords" [advancement.challenge_sword_dmg_10k] - title = "Swordsman" - description = "Deal 10,000 damage with swords" +title = "Swordsman" +description = "Deal 10,000 damage with swords" [advancement.challenge_sword_kills_50] - title = "Duelist" - description = "Kill 50 mobs with swords" +title = "Duelist" +description = "Kill 50 mobs with swords" [advancement.challenge_sword_kills_500] - title = "Gladiator" - description = "Kill 500 mobs with swords" +title = "Gladiator" +description = "Kill 500 mobs with swords" [advancement.challenge_sword_crit_50] - title = "Critical Striker" - description = "Land 50 critical hits with swords" +title = "Critical Striker" +description = "Land 50 critical hits with swords" [advancement.challenge_sword_crit_500] - title = "Precision Master" - description = "Land 500 critical hits with swords" +title = "Precision Master" +description = "Land 500 critical hits with swords" [advancement.challenge_sword_heavy_25] - title = "Heavy Hitter" - description = "Land 25 heavy hits with swords (over 8 damage)" +title = "Heavy Hitter" +description = "Land 25 heavy hits with swords (over 8 damage)" [advancement.challenge_sword_heavy_250] - title = "Devastating Blow" - description = "Land 250 heavy hits with swords (over 8 damage)" +title = "Devastating Blow" +description = "Land 250 heavy hits with swords (over 8 damage)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Beast Trainer" - description = "Your pets deal 500 total damage" +title = "Beast Trainer" +description = "Your pets deal 500 total damage" [advancement.challenge_pet_dmg_5k] - title = "War Master" - description = "Your pets deal 5,000 total damage" +title = "War Master" +description = "Your pets deal 5,000 total damage" [advancement.challenge_tamed_10] - title = "Animal Friend" - description = "Tame 10 animals" +title = "Animal Friend" +description = "Tame 10 animals" [advancement.challenge_tamed_100] - title = "Zookeeper" - description = "Tame 100 animals" +title = "Zookeeper" +description = "Tame 100 animals" [advancement.challenge_pet_kills_25] - title = "Pack Tactics" - description = "Your pets slay 25 creatures" +title = "Pack Tactics" +description = "Your pets slay 25 creatures" [advancement.challenge_pet_kills_250] - title = "Alpha Commander" - description = "Your pets slay 250 creatures" +title = "Alpha Commander" +description = "Your pets slay 250 creatures" [advancement.challenge_taming_2500] - title = "Breeding Expert" - description = "Breed 2,500 animals" +title = "Breeding Expert" +description = "Breed 2,500 animals" [advancement.challenge_taming_25k] - title = "Genetics Master" - description = "Breed 25,000 animals" +title = "Genetics Master" +description = "Breed 25,000 animals" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Punching Bag" - description = "Receive 500 hits" +title = "Punching Bag" +description = "Receive 500 hits" [advancement.challenge_trag_hits_5k] - title = "Glutton for Punishment" - description = "Receive 5,000 hits" +title = "Glutton for Punishment" +description = "Receive 5,000 hits" [advancement.challenge_trag_deaths_10] - title = "Nine Lives" - description = "Die 10 times" +title = "Nine Lives" +description = "Die 10 times" [advancement.challenge_trag_deaths_100] - title = "Recurring Nightmare" - description = "Die 100 times" +title = "Recurring Nightmare" +description = "Die 100 times" [advancement.challenge_trag_fire_500] - title = "Burn Victim" - description = "Endure 500 fire damage" +title = "Burn Victim" +description = "Endure 500 fire damage" [advancement.challenge_trag_fire_5k] - title = "Phoenix" - description = "Endure 5,000 fire damage" +title = "Phoenix" +description = "Endure 5,000 fire damage" [advancement.challenge_trag_fall_500] - title = "Gravity Check" - description = "Endure 500 fall damage" +title = "Gravity Check" +description = "Endure 500 fall damage" [advancement.challenge_trag_fall_5k] - title = "Terminal Velocity" - description = "Endure 5,000 fall damage" +title = "Terminal Velocity" +description = "Endure 5,000 fall damage" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Brawler" - description = "Deal 1,000 damage with bare fists" +title = "Brawler" +description = "Deal 1,000 damage with bare fists" [advancement.challenge_unarmed_dmg_10k] - title = "Martial Artist" - description = "Deal 10,000 damage with bare fists" +title = "Martial Artist" +description = "Deal 10,000 damage with bare fists" [advancement.challenge_unarmed_kills_25] - title = "Bare Knuckle" - description = "Kill 25 mobs with bare fists" +title = "Bare Knuckle" +description = "Kill 25 mobs with bare fists" [advancement.challenge_unarmed_kills_250] - title = "Fist of Legend" - description = "Kill 250 mobs with bare fists" +title = "Fist of Legend" +description = "Kill 250 mobs with bare fists" [advancement.challenge_unarmed_crit_25] - title = "Critical Punch" - description = "Land 25 critical hits with bare fists" +title = "Critical Punch" +description = "Land 25 critical hits with bare fists" [advancement.challenge_unarmed_crit_250] - title = "Precision Fist" - description = "Land 250 critical hits with bare fists" +title = "Precision Fist" +description = "Land 250 critical hits with bare fists" [advancement.challenge_unarmed_heavy_25] - title = "Power Punch" - description = "Land 25 heavy hits with bare fists (over 6 damage)" +title = "Power Punch" +description = "Land 25 heavy hits with bare fists (over 6 damage)" [advancement.challenge_unarmed_heavy_250] - title = "Knockout King" - description = "Land 250 heavy hits with bare fists (over 6 damage)" +title = "Knockout King" +description = "Land 250 heavy hits with bare fists (over 6 damage)" # items [items] [items.bound_ender_peral] - name = "Reliquary Portkey" - usage1 = "Shift + Left Click to bind" - usage2 = "Right Click to access the bound Inventory" +name = "Reliquary Portkey" +usage1 = "Shift + Left Click to bind" +usage2 = "Right Click to access the bound Inventory" [items.bound_eye_of_ender] - name = "Ocular Anchor" - usage1 = "Right Click to consume and teleport to the bound location" - usage2 = "Shift + Left Click to bind to a block" +name = "Ocular Anchor" +usage1 = "Right Click to consume and teleport to the bound location" +usage2 = "Shift + Left Click to bind to a block" [items.bound_redstone_torch] - name = "Redstone Remote" - usage1 = "Right Click to create a 1-Tick Redstone pulse" - usage2 = "Shift + Left Click on a 'Target' Block to bind" +name = "Redstone Remote" +usage1 = "Right Click to create a 1-Tick Redstone pulse" +usage2 = "Shift + Left Click on a 'Target' Block to bind" [items.bound_snowball] - name = "Web Snare!" - usage1 = "Throw to create a temporary web trap at the location" +name = "Web Snare!" +usage1 = "Throw to create a temporary web trap at the location" [items.chrono_time_bottle] - name = "Time In A Bottle" - usage1 = "Passively stores time while in your inventory" - usage2 = "Right-click timed blocks or baby animals to spend stored time" - stored = "Stored Time" +name = "Time In A Bottle" +usage1 = "Passively stores time while in your inventory" +usage2 = "Right-click timed blocks or baby animals to spend stored time" +stored = "Stored Time" [items.chrono_time_bomb] - name = "Time Bomb" - usage1 = "Right-click to launch a chrono bolt that creates a temporal field" +name = "Time Bomb" +usage1 = "Right-click to launch a chrono bolt that creates a temporal field" [items.elevator_block] - name = "Elevator Block" - usage1 = "Jump to teleport up" - usage2 = "Shift to teleport down" - usage3 = "Minimum of 2 air blocks between the elevators" +name = "Elevator Block" +usage1 = "Jump to teleport up" +usage2 = "Shift to teleport down" +usage3 = "Minimum of 2 air blocks between the elevators" # snippets [snippets] [snippets.gui] - level = "Level" - knowledge = "knowledge" - power_used = "Power Used" - not_learned = "Not Learned" - xp = "XP to" - welcome = "Welcome!" - welcome_back = "Welcome back!" - xp_bonus_for_time = "XP for" - max_ability_power = "Maximum Ability Power" - unlock_this_by_clicking = "Unlock this by Right-Clicking: " - back = "Back" - unlearn_all = "Unlearn all" - unlearned_all = "Unlearned all" +level = "Level" +knowledge = "knowledge" +power_used = "Power Used" +not_learned = "Not Learned" +xp = "XP to" +welcome = "Welcome!" +welcome_back = "Welcome back!" +xp_bonus_for_time = "XP for" +max_ability_power = "Maximum Ability Power" +unlock_this_by_clicking = "Unlock this by Right-Clicking: " +back = "Back" +unlearn_all = "Unlearn all" +unlearned_all = "Unlearned all" [snippets.adapt_menu] - may_not_unlearn = "YOU MAY NOT UNLEARN" - may_unlearn = "YOU MAY LEARN/UNLEARN" - knowledge_cost = "Knowledge Cost" - knowledge_available = "Knowledge Available" - already_learned = "Already Learned" - unlearn_refund = "Click to Unlearn & Refund" - no_refunds = "HARDCORE, REFUNDS DISABLED" - knowledge = "knowledge" - click_learn = "Click to Learn" - no_knowledge = "(You don't have any Knowledge)" - you_only_have = "You only have" - how_to_level_up = "Level up skills to increase your max power." - not_enough_power = "Not enough power! Each Ability Level costs 1 power." - power = "power" - power_drain = "Power Drain" - learned = "Learned " - unlearned = "Unlearned " - activator_block = "Bookshelf" +may_not_unlearn = "YOU MAY NOT UNLEARN" +may_unlearn = "YOU MAY LEARN/UNLEARN" +knowledge_cost = "Knowledge Cost" +knowledge_available = "Knowledge Available" +already_learned = "Already Learned" +unlearn_refund = "Click to Unlearn & Refund" +no_refunds = "HARDCORE, REFUNDS DISABLED" +knowledge = "knowledge" +click_learn = "Click to Learn" +no_knowledge = "(You don't have any Knowledge)" +you_only_have = "You only have" +how_to_level_up = "Level up skills to increase your max power." +not_enough_power = "Not enough power! Each Ability Level costs 1 power." +power = "power" +power_drain = "Power Drain" +learned = "Learned " +unlearned = "Unlearned " +activator_block = "Bookshelf" [snippets.knowledge_orb] - contains = "contains" - knowledge = "knowledge" - rightclick = "Right-Click" - togainknowledge = "to gain this knowledge" - knowledge_orb = "Knowledge Orb" +contains = "contains" +knowledge = "knowledge" +rightclick = "Right-Click" +togainknowledge = "to gain this knowledge" +knowledge_orb = "Knowledge Orb" [snippets.experience_orb] - contains = "contains" - xp = "Experience" - rightclick = "Right-Click" - togainxp = "to gain this experience" - xporb = "Experience Orb" +contains = "contains" +xp = "Experience" +rightclick = "Right-Click" +togainxp = "to gain this experience" +xporb = "Experience Orb" # skill [skill] [skill.agility] - name = "Agility" - icon = "⇉" - description = "Agility is the ability to move quick and fluidly in the face of obstacles." +name = "Agility" +icon = "⇉" +description = "Agility is the ability to move quick and fluidly in the face of obstacles." [skill.architect] - name = "Architect" - icon = "⬧" - description = "Structures are the building blocks of the world. Reality is in your hands, yours to control." +name = "Architect" +icon = "⬧" +description = "Structures are the building blocks of the world. Reality is in your hands, yours to control." [skill.axes] - name = "Axes" - icon = "🪓" - description1 = "Why chop down trees, when you could chop " - description2 = "things" - description3 = "instead, same end result!" +name = "Axes" +icon = "🪓" +description1 = "Why chop down trees, when you could chop " +description2 = "things" +description3 = "instead, same end result!" [skill.brewing] - name = "Brewing" - icon = "❦" - description = "Double Bubble, Triple Bubble, Quadruple Bubble- I still cant put this potion into a cauldron" +name = "Brewing" +icon = "❦" +description = "Double Bubble, Triple Bubble, Quadruple Bubble- I still cant put this potion into a cauldron" [skill.blocking] - name = "Blocking" - icon = "🛡" - description = "Sticks and stones Won't break your bones, But a shield will." +name = "Blocking" +icon = "🛡" +description = "Sticks and stones Won't break your bones, But a shield will." [skill.crafting] - name = "Crafting" - icon = "⌂" - description = "With no more pieces left to place, why not make another?" +name = "Crafting" +icon = "⌂" +description = "With no more pieces left to place, why not make another?" [skill.discovery] - name = "Discovery" - icon = "⚛" - description = "As your perception expands, your mind unravels to discover that which you did not." +name = "Discovery" +icon = "⚛" +description = "As your perception expands, your mind unravels to discover that which you did not." [skill.enchanting] - name = "Enchanting" - icon = "♰" - description = "What are you going on about? Prophecies, visions, superstitious jibber-jabber?" +name = "Enchanting" +icon = "♰" +description = "What are you going on about? Prophecies, visions, superstitious jibber-jabber?" [skill.excavation] - name = "Excavation" - icon = "ᛳ" - description = "Diggy Diggy Hole..." +name = "Excavation" +icon = "ᛳ" +description = "Diggy Diggy Hole..." [skill.herbalism] - name = "Herbalism" - icon = "⚘" - description = "I can't find any plants, but I can find some seeds and- is that... Weed?" +name = "Herbalism" +icon = "⚘" +description = "I can't find any plants, but I can find some seeds and- is that... Weed?" [skill.hunter] - name = "Hunter" - icon = "☠" - description = "Hunting is about the journey not the outcome." +name = "Hunter" +icon = "☠" +description = "Hunting is about the journey not the outcome." [skill.nether] - name = "Nether" - icon = "₪" - description = "From the depths of the Nether itself." +name = "Nether" +icon = "₪" +description = "From the depths of the Nether itself." [skill.pickaxe] - name = "Pickaxe" - icon = "⛏" - description = "Dwarves are the miners, but ive learned a thing or two in my time. IM SWEDISH" +name = "Pickaxe" +icon = "⛏" +description = "Dwarves are the miners, but ive learned a thing or two in my time. IM SWEDISH" [skill.ranged] - name = "Ranged" - icon = "🏹" - description = "Distance is the key to victory, and the key to survival." +name = "Ranged" +icon = "🏹" +description = "Distance is the key to victory, and the key to survival." [skill.rift] - name = "Rift" - icon = "❍" - description = "The Rift is a caustic harness, but you have harnessed the harness." +name = "Rift" +icon = "❍" +description = "The Rift is a caustic harness, but you have harnessed the harness." [skill.seaborne] - name = "Seaborne" - icon = "🎣" - description = "With this skill, you may will the wonders of the water." +name = "Seaborne" +icon = "🎣" +description = "With this skill, you may will the wonders of the water." [skill.stealth] - name = "Stealth" - icon = "☯" - description = "The art of the unseen. Walk in the shadows." +name = "Stealth" +icon = "☯" +description = "The art of the unseen. Walk in the shadows." [skill.swords] - name = "Swords" - icon = "⚔" - description = "By the power of GreyStone!" +name = "Swords" +icon = "⚔" +description = "By the power of GreyStone!" [skill.taming] - name = "Taming" - icon = "♥" - description = "The parrots and the bees... and you?" +name = "Taming" +icon = "♥" +description = "The parrots and the bees... and you?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Blood flows through the veins of the universe. Constricted by your hands." +name = "TragOul" +icon = "🗡" +description = "Blood flows through the veins of the universe. Constricted by your hands." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Wind the Clock of the universe, experience the flow. Break the clock, become it." +name = "Chronos" +icon = "🕒" +description = "Wind the Clock of the universe, experience the flow. Break the clock, become it." [skill.unarmed] - name = "Unarmed" - icon = "»" - description = "Without a weapon is not without strength." +name = "Unarmed" +icon = "»" +description = "Without a weapon is not without strength." # agility [agility] [agility.armor_up] - name = "Armor-Up" - description = "Get more armor the longer you sprint!" - lore1 = "Max Armor" - lore2 = "Armor-Up Time" - lore = ["Max Armor", "Armor-Up Time"] +name = "Armor-Up" +description = "Get more armor the longer you sprint!" +lore1 = "Max Armor" +lore2 = "Armor-Up Time" +lore = ["Max Armor", "Armor-Up Time"] [agility.ladder_slide] - name = "Ladder Slide" - description = "Climb and slide ladders much faster in both directions." - lore1 = "Ladder speed multiplier" - lore2 = "Fast descent speed" - lore = ["Ladder speed multiplier", "Fast descent speed"] +name = "Ladder Slide" +description = "Climb and slide ladders much faster in both directions." +lore1 = "Ladder speed multiplier" +lore2 = "Fast descent speed" +lore = ["Ladder speed multiplier", "Fast descent speed"] [agility.super_jump] - name = "Super Jump" - description = "Exceptional Height Advantage." - lore1 = "Max Jump Height" - lore2 = "Sneak + Jump to Super Jump!" - lore = ["Max Jump Height", "Sneak + Jump to Super Jump!"] +name = "Super Jump" +description = "Exceptional Height Advantage." +lore1 = "Max Jump Height" +lore2 = "Sneak + Jump to Super Jump!" +lore = ["Max Jump Height", "Sneak + Jump to Super Jump!"] [agility.wall_jump] - name = "Wall Jump" - description = "Hold shift while mid-air against a wall to wall latch & jump!" - lore1 = "Max Jumps" - lore2 = "Jump Height" - lore = ["Max Jumps", "Jump Height"] +name = "Wall Jump" +description = "Hold shift while mid-air against a wall to wall latch & jump!" +lore1 = "Max Jumps" +lore2 = "Jump Height" +lore = ["Max Jumps", "Jump Height"] [agility.wind_up] - name = "Wind Up" - description = "Get faster the longer you sprint!" - lore1 = "Max Speed" - lore2 = "Windup Time" - lore = ["Max Speed", "Windup Time"] +name = "Wind Up" +description = "Get faster the longer you sprint!" +lore1 = "Max Speed" +lore2 = "Windup Time" +lore = ["Max Speed", "Windup Time"] [agility.parkour_momentum] - name = "Parkour Momentum" - description = "Chain sprint-jumps and land on ledges to build momentum for mobility boosts." - lore1 = "Max Momentum Stacks" - lore2 = "Max Speed Amplifier" - lore3 = "Max Jump Amplifier" - lore = ["Max Momentum Stacks", "Max Speed Amplifier", "Max Jump Amplifier"] +name = "Parkour Momentum" +description = "Chain sprint-jumps and land on ledges to build momentum for mobility boosts." +lore1 = "Max Momentum Stacks" +lore2 = "Max Speed Amplifier" +lore3 = "Max Jump Amplifier" +lore = ["Max Momentum Stacks", "Max Speed Amplifier", "Max Jump Amplifier"] [agility.roll_landing] - name = "Roll Landing" - description = "Timed crouch before landing converts part of fall damage into hunger cost." - lore1 = "Fall Damage Conversion" - lore2 = "Input Timing Window" - lore3 = "Roll Cooldown" - lore = ["Fall Damage Conversion", "Input Timing Window", "Roll Cooldown"] +name = "Roll Landing" +description = "Timed crouch before landing converts part of fall damage into hunger cost." +lore1 = "Fall Damage Conversion" +lore2 = "Input Timing Window" +lore3 = "Roll Cooldown" +lore = ["Fall Damage Conversion", "Input Timing Window", "Roll Cooldown"] # architect [architect] [architect.elevator] - name = "Elevator" - description = "This allows for you to build an elevator to teleport vertically fast!" - lore1 = "Unlocks elevator recipe: X=WOOL, Y=ENDER PEARL" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Unlocks elevator recipe: X=WOOL, Y=ENDER PEARL", "XXX", "XYX", "XXX"] +name = "Elevator" +description = "This allows for you to build an elevator to teleport vertically fast!" +lore1 = "Unlocks elevator recipe: X=WOOL, Y=ENDER PEARL" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Unlocks elevator recipe: X=WOOL, Y=ENDER PEARL", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Magic Foundation" - description = "This allows for you to sneak and place a temporary foundation beneath you!" - lore1 = "Magically create: " - lore2 = "Blocks beneath you!" - lore = ["Magically create: ", "Blocks beneath you!"] +name = "Magic Foundation" +description = "This allows for you to sneak and place a temporary foundation beneath you!" +lore1 = "Magically create: " +lore2 = "Blocks beneath you!" +lore = ["Magically create: ", "Blocks beneath you!"] [architect.glass] - name = "Silk-Touch Glass" - description = "This allows for you to essentially prevent the loss of glass blocks when you break them with an empty hand!" - lore1 = "Your hands gain silk touch for Glass" - lore = ["Your hands gain silk touch for Glass"] +name = "Silk-Touch Glass" +description = "This allows for you to essentially prevent the loss of glass blocks when you break them with an empty hand!" +lore1 = "Your hands gain silk touch for Glass" +lore = ["Your hands gain silk touch for Glass"] [architect.wireless_redstone] - name = "Redstone Remote" - description = "This allows for you to use a redstone torch to toggle redstone, remotely!" - lore1 = "Target + Redstone Torch + Enderpearl = 1 Redstone Remote" - lore = ["Target + Redstone Torch + Enderpearl = 1 Redstone Remote"] +name = "Redstone Remote" +description = "This allows for you to use a redstone torch to toggle redstone, remotely!" +lore1 = "Target + Redstone Torch + Enderpearl = 1 Redstone Remote" +lore = ["Target + Redstone Torch + Enderpearl = 1 Redstone Remote"] [architect.placement] - name = "Builders Wand" - description = "Allows for you to place multiple blocks at once to activate Sneak, and hold a block that matches your looking block and place! Keep in mind, you may need to move a tad to trigger bounding the boxes!" - lore1 = "You need" - lore2 = "blocks in your hand to place this" - lore3 = "A Material Builders Wand" - lore = ["You need", "blocks in your hand to place this", "A Material Builders Wand"] +name = "Builders Wand" +description = "Allows for you to place multiple blocks at once to activate Sneak, and hold a block that matches your looking block and place! Keep in mind, you may need to move a tad to trigger bounding the boxes!" +lore1 = "You need" +lore2 = "blocks in your hand to place this" +lore3 = "A Material Builders Wand" +lore = ["You need", "blocks in your hand to place this", "A Material Builders Wand"] [architect.smart_shape] - name = "Smart Shape" - description = "Sneak-punch blocks with an empty hand to rotate orientation." - lore1 = "Rotates directional and axis block states" - lore2 = "Requires empty main hand" - lore = ["Rotates directional and axis block states", "Requires empty main hand"] +name = "Smart Shape" +description = "Sneak-punch blocks with an empty hand to rotate orientation." +lore1 = "Rotates directional and axis block states" +lore2 = "Requires empty main hand" +lore = ["Rotates directional and axis block states", "Requires empty main hand"] # axe [axe] [axe.chop] - name = "Axe Chop" - description = "Chop down trees by right clicking the base log!" - lore1 = "Blocks Per Chop" - lore2 = "Chop Cooldown" - lore3 = "Tool Wear" - lore = ["Blocks Per Chop", "Chop Cooldown", "Tool Wear"] +name = "Axe Chop" +description = "Chop down trees by right clicking the base log!" +lore1 = "Blocks Per Chop" +lore2 = "Chop Cooldown" +lore3 = "Tool Wear" +lore = ["Blocks Per Chop", "Chop Cooldown", "Tool Wear"] [axe.log_swap] - name = "Lucy's Log-Swapper" - description = "Change the flavor of logs in a Crafting Table!" - lore1 = "8 Log of any kind + 1 sapling = 8 log of the sapling's type" - lore = ["8 Log of any kind + 1 sapling = 8 log of the sapling's type"] +name = "Lucy's Log-Swapper" +description = "Change the flavor of logs in a Crafting Table!" +lore1 = "8 Log of any kind + 1 sapling = 8 log of the sapling's type" +lore = ["8 Log of any kind + 1 sapling = 8 log of the sapling's type"] [axe.drop_to_inventory] - name = "Axe Drop-To-Inventory" +name = "Axe Drop-To-Inventory" [axe.ground_smash] - name = "Axe Ground Smash" - description = "Jump, then crouch and smash all nearby enemies." - lore1 = "Damage" - lore2 = "Block Radius" - lore3 = "Force" - lore4 = "Smash Cooldown" - lore = ["Damage", "Block Radius", "Force", "Smash Cooldown"] +name = "Axe Ground Smash" +description = "Jump, then crouch and smash all nearby enemies." +lore1 = "Damage" +lore2 = "Block Radius" +lore3 = "Force" +lore4 = "Smash Cooldown" +lore = ["Damage", "Block Radius", "Force", "Smash Cooldown"] [axe.leaf_miner] - name = "Leaf-miner" - description = "Allows you to break bulk leaves at once!" - lore1 = "Sneak, and mine LEAVES" - lore2 = "range of Leaf-mining" - lore3 = "You will not get the drops from the leaves (Exploit Prevention)" - lore = ["Sneak, and mine LEAVES", "range of Leaf-mining", "You will not get the drops from the leaves (Exploit Prevention)"] +name = "Leaf-miner" +description = "Allows you to break bulk leaves at once!" +lore1 = "Sneak, and mine LEAVES" +lore2 = "range of Leaf-mining" +lore3 = "You will not get the drops from the leaves (Exploit Prevention)" +lore = ["Sneak, and mine LEAVES", "range of Leaf-mining", "You will not get the drops from the leaves (Exploit Prevention)"] [axe.wood_miner] - name = "Wood-miner" - description = "Allows you to break bulk wood at once!" - lore1 = "Sneak, and mine WOOD/LOGS ( Not Planks )" - lore2 = "range of Wood-mining" - lore3 = "Works with Drop to inventory" - lore = ["Sneak, and mine WOOD/LOGS ( Not Planks )", "range of Wood-mining", "Works with Drop to inventory"] +name = "Wood-miner" +description = "Allows you to break bulk wood at once!" +lore1 = "Sneak, and mine WOOD/LOGS ( Not Planks )" +lore2 = "range of Wood-mining" +lore3 = "Works with Drop to inventory" +lore = ["Sneak, and mine WOOD/LOGS ( Not Planks )", "range of Wood-mining", "Works with Drop to inventory"] [axe.timber_mark] - name = "Timber Mark" - description = "Sneak-right-click a log with an axe to mark it, then break that log to fell connected wood." - lore1 = "Max Logs Broken" - lore2 = "Mark Duration" - lore = ["Max Logs Broken", "Mark Duration"] +name = "Timber Mark" +description = "Sneak-right-click a log with an axe to mark it, then break that log to fell connected wood." +lore1 = "Max Logs Broken" +lore2 = "Mark Duration" +lore = ["Max Logs Broken", "Mark Duration"] # brewing [brewing] [brewing.lingering] - name = "Lingering Brew" - description = "Brewed potions last longer!" - lore1 = "Duration" - lore2 = "Duration" - lore = ["Duration", "Duration"] +name = "Lingering Brew" +description = "Brewed potions last longer!" +lore1 = "Duration" +lore2 = "Duration" +lore = ["Duration", "Duration"] [brewing.super_heated] - name = "Super Heated Brew" - description = "Brewing stands work faster the hotter they are." - lore1 = "Per Touching Fire Block" - lore2 = "Per Touching Lava Block" - lore = ["Per Touching Fire Block", "Per Touching Lava Block"] +name = "Super Heated Brew" +description = "Brewing stands work faster the hotter they are." +lore1 = "Per Touching Fire Block" +lore2 = "Per Touching Lava Block" +lore = ["Per Touching Fire Block", "Per Touching Lava Block"] [brewing.darkness] - name = "Bottled Darkness" - description = "Not sure why you need this, but here you go!" - lore1 = "NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)" - lore2 = "It Should be noted that This prevents the user from Sprinting!" - lore = ["NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)", "It Should be noted that This prevents the user from Sprinting!"] +name = "Bottled Darkness" +description = "Not sure why you need this, but here you go!" +lore1 = "NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)" +lore2 = "It Should be noted that This prevents the user from Sprinting!" +lore = ["NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)", "It Should be noted that This prevents the user from Sprinting!"] [brewing.haste] - name = "Bottled Haste" - description = "When Efficiency is not enough" - lore1 = "Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)" - lore2 = "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)" - lore = ["Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)", "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)"] +name = "Bottled Haste" +description = "When Efficiency is not enough" +lore1 = "Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)" +lore2 = "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)" +lore = ["Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)", "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)"] [brewing.absorption] - name = "Bottled Absorption" - description = "Harden the body!" - lore1 = "Instant Heal + Quartz = Potion of Absorption (60 seconds)" - lore2 = "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)" - lore = ["Instant Heal + Quartz = Potion of Absorption (60 seconds)", "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)"] +name = "Bottled Absorption" +description = "Harden the body!" +lore1 = "Instant Heal + Quartz = Potion of Absorption (60 seconds)" +lore2 = "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)" +lore = ["Instant Heal + Quartz = Potion of Absorption (60 seconds)", "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)"] [brewing.fatigue] - name = "Bottled Fatigue" - description = "Weaken the body!" - lore1 = "Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)" - lore2 = "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)" - lore = ["Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)", "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)"] +name = "Bottled Fatigue" +description = "Weaken the body!" +lore1 = "Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)" +lore2 = "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)" +lore = ["Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)", "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)"] [brewing.hunger] - name = "Bottled Hunger" - description = "Feed the Insatiable!" - lore1 = "Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)" - lore2 = "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)" - lore = ["Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)", "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)"] +name = "Bottled Hunger" +description = "Feed the Insatiable!" +lore1 = "Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)" +lore2 = "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)" +lore = ["Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)", "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)"] [brewing.nausea] - name = "Bottled Nausea" - description = "You make me sick!" - lore1 = "Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)" - lore2 = "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)" - lore = ["Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)", "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)"] +name = "Bottled Nausea" +description = "You make me sick!" +lore1 = "Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)" +lore2 = "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)" +lore = ["Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)", "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)"] [brewing.blindness] - name = "Bottled Blindness" - description = "You're a horrible person..." - lore1 = "Awkward Potion + Ink sack = Potion of Blindness (30 seconds)" - lore2 = "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)" - lore = ["Awkward Potion + Ink sack = Potion of Blindness (30 seconds)", "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)"] +name = "Bottled Blindness" +description = "You're a horrible person..." +lore1 = "Awkward Potion + Ink sack = Potion of Blindness (30 seconds)" +lore2 = "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)" +lore = ["Awkward Potion + Ink sack = Potion of Blindness (30 seconds)", "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)"] [brewing.resistance] - name = "Bottled Resistance" - description = "Fortification at its finest!" - lore1 = "Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)" - lore2 = "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)" - lore = ["Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)", "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)"] +name = "Bottled Resistance" +description = "Fortification at its finest!" +lore1 = "Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)" +lore2 = "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)" +lore = ["Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)", "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)"] [brewing.health_boost] - name = "Bottled Life" - description = "When Maximum health is not enough..." - lore1 = "Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)" - lore2 = "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)" - lore = ["Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)", "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)"] +name = "Bottled Life" +description = "When Maximum health is not enough..." +lore1 = "Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)" +lore2 = "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)" +lore = ["Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)", "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)"] [brewing.decay] - name = "Bottled Decay" - description = "Who knew Detritus would be so useful?" - lore1 = "Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)" - lore2 = "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)" - lore = ["Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)", "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)"] +name = "Bottled Decay" +description = "Who knew Detritus would be so useful?" +lore1 = "Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)" +lore2 = "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)" +lore = ["Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)", "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)"] [brewing.saturation] - name = "Bottled Saturation" - description = "Ya know... Im not even Hungry..." - lore1 = "Regen Potion + Baked Potato = Potion of Saturation" - lore2 = "Regen Potion + Hay Bale = Potion of Saturation-2" - lore = ["Regen Potion + Baked Potato = Potion of Saturation", "Regen Potion + Hay Bale = Potion of Saturation-2"] +name = "Bottled Saturation" +description = "Ya know... Im not even Hungry..." +lore1 = "Regen Potion + Baked Potato = Potion of Saturation" +lore2 = "Regen Potion + Hay Bale = Potion of Saturation-2" +lore = ["Regen Potion + Baked Potato = Potion of Saturation", "Regen Potion + Hay Bale = Potion of Saturation-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Chains of Mephistopheles" - description = "Allows you to craft Chainmail Armor" - lore1 = "The Crafting recipe is the same as any other, but with iron nuggets instead" - lore = ["The Crafting recipe is the same as any other, but with iron nuggets instead"] +name = "Chains of Mephistopheles" +description = "Allows you to craft Chainmail Armor" +lore1 = "The Crafting recipe is the same as any other, but with iron nuggets instead" +lore = ["The Crafting recipe is the same as any other, but with iron nuggets instead"] [blocking.horse_armorer] - name = "Craftable Horse Armor" - description = "Allows you to craft Horse Armor" - lore1 = "Surround a saddle with the material you want to use to craft the armor" - lore = ["Surround a saddle with the material you want to use to craft the armor"] +name = "Craftable Horse Armor" +description = "Allows you to craft Horse Armor" +lore1 = "Surround a saddle with the material you want to use to craft the armor" +lore = ["Surround a saddle with the material you want to use to craft the armor"] [blocking.saddle_crafter] - name = "Craftable Saddle" - description = "Craft a Saddle with Leather" - lore1 = "Recipe: 5 Leather:" - lore = ["Recipe: 5 Leather:"] +name = "Craftable Saddle" +description = "Craft a Saddle with Leather" +lore1 = "Recipe: 5 Leather:" +lore = ["Recipe: 5 Leather:"] [blocking.multi_armor] - name = "Multi-Armor" - description = "Bind Elytras to Armor" - lore1 = "This Is an amazing skill for traveling." - lore2 = "Dynamically merge and change Armor/Elytra on the Fly!" - lore3 = "To merge, shift click an item over another in your inventory." - lore4 = "To unbind Armor, Sneak-Drop the item, and it will disassemble." - lore5 = "If your MultiArmor is destroyed, you will lose all items in it." - lore6 = "I Don't need armor, It disappoints me..." - lore = ["This Is an amazing skill for traveling.", "Dynamically merge and change Armor/Elytra on the Fly!", "To merge, shift click an item over another in your inventory.", "To unbind Armor, Sneak-Drop the item, and it will disassemble.", "If your MultiArmor is destroyed, you will lose all items in it.", "I Don't need armor, It disappoints me..."] +name = "Multi-Armor" +description = "Bind Elytras to Armor" +lore1 = "This Is an amazing skill for traveling." +lore2 = "Dynamically merge and change Armor/Elytra on the Fly!" +lore3 = "To merge, shift click an item over another in your inventory." +lore4 = "To unbind Armor, Sneak-Drop the item, and it will disassemble." +lore5 = "If your MultiArmor is destroyed, you will lose all items in it." +lore6 = "I Don't need armor, It disappoints me..." +lore = ["This Is an amazing skill for traveling.", "Dynamically merge and change Armor/Elytra on the Fly!", "To merge, shift click an item over another in your inventory.", "To unbind Armor, Sneak-Drop the item, and it will disassemble.", "If your MultiArmor is destroyed, you will lose all items in it.", "I Don't need armor, It disappoints me..."] [blocking.counter_guard] - name = "Counter Guard" - description = "Each blocked hit builds shield stacks. Your next proc consumes stacks to reflect damage to the attacker." - lore1 = "Max Stored Counter Stacks" - lore2 = "Reflect Proc Chance" - lore3 = "Base Reflect Damage" - lore = ["Max Stored Counter Stacks", "Reflect Proc Chance", "Base Reflect Damage"] +name = "Counter Guard" +description = "Each blocked hit builds shield stacks. Your next proc consumes stacks to reflect damage to the attacker." +lore1 = "Max Stored Counter Stacks" +lore2 = "Reflect Proc Chance" +lore3 = "Base Reflect Damage" +lore = ["Max Stored Counter Stacks", "Reflect Proc Chance", "Base Reflect Damage"] [blocking.bastion_stance] - name = "Bastion Stance" - description = "While sneaking and actively blocking with a shield, reduce knockback and incoming projectile pressure." - lore1 = "Knockback Resistance" - lore2 = "Projectile Damage Reduction" - lore3 = "Projectile Full-Block Chance" - lore = ["Knockback Resistance", "Projectile Damage Reduction", "Projectile Full-Block Chance"] +name = "Bastion Stance" +description = "While sneaking and actively blocking with a shield, reduce knockback and incoming projectile pressure." +lore1 = "Knockback Resistance" +lore2 = "Projectile Damage Reduction" +lore3 = "Projectile Full-Block Chance" +lore = ["Knockback Resistance", "Projectile Damage Reduction", "Projectile Full-Block Chance"] [blocking.mirror_block] - name = "Mirror Block" - description = "Blocking with a shield can reflect incoming projectiles with reduced follow-up force." - lore1 = "Projectile Reflect Chance" - lore2 = "Reflected Damage Factor" - lore3 = "Reflect Cooldown" - lore = ["Projectile Reflect Chance", "Reflected Damage Factor", "Reflect Cooldown"] +name = "Mirror Block" +description = "Blocking with a shield can reflect incoming projectiles with reduced follow-up force." +lore1 = "Projectile Reflect Chance" +lore2 = "Reflected Damage Factor" +lore3 = "Reflect Cooldown" +lore = ["Projectile Reflect Chance", "Reflected Damage Factor", "Reflect Cooldown"] [blocking.bulwark_bash] - name = "Bulwark Bash" - description = "Sprint-jump and land a shielded crit to trigger a bash shockwave." - lore1 = "Bash Range" - lore2 = "Bash Damage" - lore3 = "Bash Cooldown" - lore = ["Bash Range", "Bash Damage", "Bash Cooldown"] +name = "Bulwark Bash" +description = "Sprint-jump and land a shielded crit to trigger a bash shockwave." +lore1 = "Bash Range" +lore2 = "Bash Damage" +lore3 = "Bash Cooldown" +lore = ["Bash Range", "Bash Damage", "Bash Cooldown"] # crafting [crafting] [crafting.deconstruction] - name = "Deconstruction" - description = "Deconstruct blocks & items into salvageable base components!" - lore1 = "Drop any item on the ground." - lore2 = "Then, Sneak and Right-Click with Shears" - lore = ["Drop any item on the ground.", "Then, Sneak and Right-Click with Shears"] +name = "Deconstruction" +description = "Deconstruct blocks & items into salvageable base components!" +lore1 = "Drop any item on the ground." +lore2 = "Then, Sneak and Right-Click with Shears" +lore = ["Drop any item on the ground.", "Then, Sneak and Right-Click with Shears"] [crafting.xp] - name = "Crafting XP" - description = "Gain passive XP when crafting" - lore1 = "Gain XP when crafting" - lore = ["Gain XP when crafting"] +name = "Crafting XP" +description = "Gain passive XP when crafting" +lore1 = "Gain XP when crafting" +lore = ["Gain XP when crafting"] [crafting.reconstruction] - name = "Ore Reconstruction" - description = "Recraft ores from their base components!" - lore1 = "8 of the Drops and 1 Host = 1 Ore (shapeless)" - lore2 = "Drops must be smelted (if applicable)" - lore3 = "Not including: Scraps, Quarts, and Emeralds etc..." - lore4 = "Host = Encasement. ie: Stone, Netherack, Deepslate" - lore = ["8 of the Drops and 1 Host = 1 Ore (shapeless)", "Drops must be smelted (if applicable)", "Not including: Scraps, Quarts, and Emeralds etc...", "Host = Encasement. ie: Stone, Netherack, Deepslate"] +name = "Ore Reconstruction" +description = "Recraft ores from their base components!" +lore1 = "8 of the Drops and 1 Host = 1 Ore (shapeless)" +lore2 = "Drops must be smelted (if applicable)" +lore3 = "Not including: Scraps, Quarts, and Emeralds etc..." +lore4 = "Host = Encasement. ie: Stone, Netherack, Deepslate" +lore = ["8 of the Drops and 1 Host = 1 Ore (shapeless)", "Drops must be smelted (if applicable)", "Not including: Scraps, Quarts, and Emeralds etc...", "Host = Encasement. ie: Stone, Netherack, Deepslate"] [crafting.leather] - name = "Craftable Leather" - description = "Craft Leather from Rotten Flesh" - lore1 = "Just toss it(rotten flesh) on the campfire!" - lore = ["Just toss it(rotten flesh) on the campfire!"] +name = "Craftable Leather" +description = "Craft Leather from Rotten Flesh" +lore1 = "Just toss it(rotten flesh) on the campfire!" +lore = ["Just toss it(rotten flesh) on the campfire!"] [crafting.backpacks] - name = "A Boutilier's Backpacks!" - description = "This just Brings the Mojang Bundle into the game!" - lore1 = "You need to be in Survival to use this" - lore2 = "XLX : Leather, Lead, Leather" - lore3 = "XSX : Leather, Barrel Box, Leather" - lore4 = "XCX : Leather, Chest, Leather" - lore = ["You need to be in Survival to use this", "XLX : Leather, Lead, Leather", "XSX : Leather, Barrel Box, Leather", "XCX : Leather, Chest, Leather"] +name = "A Boutilier's Backpacks!" +description = "This just Brings the Mojang Bundle into the game!" +lore1 = "You need to be in Survival to use this" +lore2 = "XLX : Leather, Lead, Leather" +lore3 = "XSX : Leather, Barrel Box, Leather" +lore4 = "XCX : Leather, Chest, Leather" +lore = ["You need to be in Survival to use this", "XLX : Leather, Lead, Leather", "XSX : Leather, Barrel Box, Leather", "XCX : Leather, Chest, Leather"] [crafting.stations] - name = "Portable Tables!" - description = "Use a table in the palm of your hand!" - lore2 = "ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!" - lore3 = "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom" - lore = ["ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!", "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom"] +name = "Portable Tables!" +description = "Use a table in the palm of your hand!" +lore2 = "ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!" +lore3 = "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom" +lore = ["ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!", "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom"] [crafting.skulls] - name = "Craftable skulls!" - description = "Using Materials you can Craft Mob Skulls!" - lore1 = "Surround a Bone Block with the following to get a skull:" - lore2 = "Zombie: Rotting Flesh" - lore3 = "Skeleton: Bone" - lore4 = "Creeper: Gunpowder" - lore5 = "Wither: Nether Brick" - lore6 = "Dragon: Dragons Breath" - lore = ["Surround a Bone Block with the following to get a skull:", "Zombie: Rotting Flesh", "Skeleton: Bone", "Creeper: Gunpowder", "Wither: Nether Brick", "Dragon: Dragons Breath"] +name = "Craftable skulls!" +description = "Using Materials you can Craft Mob Skulls!" +lore1 = "Surround a Bone Block with the following to get a skull:" +lore2 = "Zombie: Rotting Flesh" +lore3 = "Skeleton: Bone" +lore4 = "Creeper: Gunpowder" +lore5 = "Wither: Nether Brick" +lore6 = "Dragon: Dragons Breath" +lore = ["Surround a Bone Block with the following to get a skull:", "Zombie: Rotting Flesh", "Skeleton: Bone", "Creeper: Gunpowder", "Wither: Nether Brick", "Dragon: Dragons Breath"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Time In A Bottle" - description = "Carry a temporal bottle that stores time and spend it to accelerate timed blocks, growables, and Ageable entities such as baby animals. Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle." - lore1 = "Stored seconds charged each tick" - lore2 = "Time acceleration per stored second" - lore3 = "Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle" - lore = ["Stored seconds charged each tick", "Time acceleration per stored second", "Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle"] +name = "Time In A Bottle" +description = "Carry a temporal bottle that stores time and spend it to accelerate timed blocks, growables, and Ageable entities such as baby animals. Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle." +lore1 = "Stored seconds charged each tick" +lore2 = "Time acceleration per stored second" +lore3 = "Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle" +lore = ["Stored seconds charged each tick", "Time acceleration per stored second", "Recipe (Shapeless): Swiftness Potion + Clock + Glass Bottle"] [chronos.aberrant_touch] - name = "Aberrant Touch" - description = "Melee attacks apply stacking slowness at the cost of hunger, with strict PvP caps, and root targets at 5 stacks." - lore1 = "Melee attacks apply stacking slowness" - lore2 = "PvE slowness duration cap" - lore3 = "PvP slowness amplifier cap" - lore = ["Melee attacks apply stacking slowness", "PvE slowness duration cap", "PvP slowness amplifier cap"] +name = "Aberrant Touch" +description = "Melee attacks apply stacking slowness at the cost of hunger, with strict PvP caps, and root targets at 5 stacks." +lore1 = "Melee attacks apply stacking slowness" +lore2 = "PvE slowness duration cap" +lore3 = "PvP slowness amplifier cap" +lore = ["Melee attacks apply stacking slowness", "PvE slowness duration cap", "PvP slowness amplifier cap"] [chronos.instant_recall] - name = "Instant Recall" - description = "Left or right click with a clock in hand to rewind to a recent snapshot with health and hunger restored." - lore1 = "Rewind duration" - lore2 = "Cooldown" - lore3 = "No inventory rollback" - lore = ["Rewind duration", "Cooldown", "No inventory rollback"] +name = "Instant Recall" +description = "Left or right click with a clock in hand to rewind to a recent snapshot with health and hunger restored." +lore1 = "Rewind duration" +lore2 = "Cooldown" +lore3 = "No inventory rollback" +lore = ["Rewind duration", "Cooldown", "No inventory rollback"] [chronos.time_bomb] - name = "Time Bomb" - description = "Throw a crafted chrono bomb that creates a temporal field, slows entities, and freezes projectiles." - lore1 = "Temporal field radius" - lore2 = "Temporal field duration" - lore3 = "Bomb cooldown" - lore4 = "Recipe (Shapeless): Clock + Snowball + Diamond + Sand" - lore = ["Temporal field radius", "Temporal field duration", "Bomb cooldown", "Recipe (Shapeless): Clock + Snowball + Diamond + Sand"] +name = "Time Bomb" +description = "Throw a crafted chrono bomb that creates a temporal field, slows entities, and freezes projectiles." +lore1 = "Temporal field radius" +lore2 = "Temporal field duration" +lore3 = "Bomb cooldown" +lore4 = "Recipe (Shapeless): Clock + Snowball + Diamond + Sand" +lore = ["Temporal field radius", "Temporal field duration", "Bomb cooldown", "Recipe (Shapeless): Clock + Snowball + Diamond + Sand"] [chronos.temporal_echo] - name = "Temporal Echo" - description = "Projectile actions can replay once after a short delay at reduced strength." - lore1 = "Echo Delay" - lore2 = "Echo Velocity Factor" - lore3 = "Echo Cooldown" - lore = ["Echo Delay", "Echo Velocity Factor", "Echo Cooldown"] +name = "Temporal Echo" +description = "Projectile actions can replay once after a short delay at reduced strength." +lore1 = "Echo Delay" +lore2 = "Echo Velocity Factor" +lore3 = "Echo Cooldown" +lore = ["Echo Delay", "Echo Velocity Factor", "Echo Cooldown"] # discovery [discovery] [discovery.armor] - name = "World Armor" - description = "Passive armor depending on nearby block hardness." - lore1 = "Passive Armor" - lore2 = "Based on nearby block hardness" - lore3 = "Armor Strength:" - lore = ["Passive Armor", "Based on nearby block hardness", "Armor Strength:"] +name = "World Armor" +description = "Passive armor depending on nearby block hardness." +lore1 = "Passive Armor" +lore2 = "Based on nearby block hardness" +lore3 = "Armor Strength:" +lore = ["Passive Armor", "Based on nearby block hardness", "Armor Strength:"] [discovery.unity] - name = "Experimental Unity" - description = "Collecting Experience Orbs adds XP to random skills." - lore1 = "XP " - lore2 = "Per Orb" - lore = ["XP ", "Per Orb"] +name = "Experimental Unity" +description = "Collecting Experience Orbs adds XP to random skills." +lore1 = "XP " +lore2 = "Per Orb" +lore = ["XP ", "Per Orb"] [discovery.resist] - name = "Experimental Resistance" - description = "Consume experience to mitigate damage only when a hit would drop you below 5 hearts or kill you." - lore0 = "Triggers only at critical health (<= 5 hearts) once per 15 seconds" - lore1 = " Reduced Damage" - lore2 = "experience drained" - lore = ["Triggers only at critical health (<= 5 hearts) once per 15 seconds", " Reduced Damage", "experience drained"] +name = "Experimental Resistance" +description = "Consume experience to mitigate damage only when a hit would drop you below 5 hearts or kill you." +lore0 = "Triggers only at critical health (<= 5 hearts) once per 15 seconds" +lore1 = " Reduced Damage" +lore2 = "experience drained" +lore = ["Triggers only at critical health (<= 5 hearts) once per 15 seconds", " Reduced Damage", "experience drained"] [discovery.villager] - name = "Villager Attraction" - description = "Allows for you to get Better trades with villagers!" - lore1 = "This consumes XP per interaction with Villagers" - lore2 = "Chance Per interaction to consume XP, and enhance trades" - lore3 = "required XP drain per Interaction" - lore = ["This consumes XP per interaction with Villagers", "Chance Per interaction to consume XP, and enhance trades", "required XP drain per Interaction"] +name = "Villager Attraction" +description = "Allows for you to get Better trades with villagers!" +lore1 = "This consumes XP per interaction with Villagers" +lore2 = "Chance Per interaction to consume XP, and enhance trades" +lore3 = "required XP drain per Interaction" +lore = ["This consumes XP per interaction with Villagers", "Chance Per interaction to consume XP, and enhance trades", "required XP drain per Interaction"] [discovery.better_mending] - name = "Better Mending" - description = "Sneak-left-click to spend your stored XP and directly mend the Mending item in your hand." - lore1 = "Durability Repaired per XP" - lore2 = "Max XP Spend per Click" - lore3 = "Mending Cooldown" - lore = ["Durability Repaired per XP", "Max XP Spend per Click", "Mending Cooldown"] +name = "Better Mending" +description = "Sneak-left-click to spend your stored XP and directly mend the Mending item in your hand." +lore1 = "Durability Repaired per XP" +lore2 = "Max XP Spend per Click" +lore3 = "Mending Cooldown" +lore = ["Durability Repaired per XP", "Max XP Spend per Click", "Mending Cooldown"] [discovery.archaeologist] - name = "Archaeologist" - description = "Brushing suspicious blocks can yield bonus archaeology rewards." - lore1 = "Bonus Reward Chance" - lore2 = "Rare Reward Chance" - lore3 = "Reward Cooldown" - lore = ["Bonus Reward Chance", "Rare Reward Chance", "Reward Cooldown"] +name = "Archaeologist" +description = "Brushing suspicious blocks can yield bonus archaeology rewards." +lore1 = "Bonus Reward Chance" +lore2 = "Rare Reward Chance" +lore3 = "Reward Cooldown" +lore = ["Bonus Reward Chance", "Rare Reward Chance", "Reward Cooldown"] [discovery.cartographer_pulse] - name = "Cartographer Pulse" - description = "Sneak-right-click with a compass to lock your compass toward a nearby structure." - lore1 = "Structure Search Range" - lore2 = "Pulse Cooldown" - lore = ["Structure Search Range", "Pulse Cooldown"] +name = "Cartographer Pulse" +description = "Sneak-right-click with a compass to lock your compass toward a nearby structure." +lore1 = "Structure Search Range" +lore2 = "Pulse Cooldown" +lore = ["Structure Search Range", "Pulse Cooldown"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapis Return" - description = "At the cost of 1 more level of XP, and has a chance to give you free lapis in return" - lore1 = "For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis" - lore = ["For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis"] +name = "Lapis Return" +description = "At the cost of 1 more level of XP, and has a chance to give you free lapis in return" +lore1 = "For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis" +lore = ["For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis"] [enchanting.quick_enchant] - name = "Quick-Click Enchant" - description = "Enchant items by clicking enchant books directly on them." - lore1 = "Max Combined Levels" - lore2 = "Cannot Enchant an item with more than " - lore3 = "power" - lore = ["Max Combined Levels", "Cannot Enchant an item with more than ", "power"] +name = "Quick-Click Enchant" +description = "Enchant items by clicking enchant books directly on them." +lore1 = "Max Combined Levels" +lore2 = "Cannot Enchant an item with more than " +lore3 = "power" +lore = ["Max Combined Levels", "Cannot Enchant an item with more than ", "power"] [enchanting.return] - name = "XP Return" - description = "Enchanting XP is returned to you when you enchant an item." - lore1 = "Experience spent has a chance to be refunded when you enchant an item" - lore2 = "Experience per Enchant" - lore = ["Experience spent has a chance to be refunded when you enchant an item", "Experience per Enchant"] +name = "XP Return" +description = "Enchanting XP is returned to you when you enchant an item." +lore1 = "Experience spent has a chance to be refunded when you enchant an item" +lore2 = "Experience per Enchant" +lore = ["Experience spent has a chance to be refunded when you enchant an item", "Experience per Enchant"] [enchanting.anvil_savant] - name = "Anvil Savant" - description = "Reduce anvil XP cost when combining, repairing, and renaming." - lore1 = "Anvil Cost Reduction" - lore = ["Anvil Cost Reduction"] +name = "Anvil Savant" +description = "Reduce anvil XP cost when combining, repairing, and renaming." +lore1 = "Anvil Cost Reduction" +lore = ["Anvil Cost Reduction"] [enchanting.offer_reroll] - name = "Offer Reroll" - description = "Sneak-right-click an enchanting table to reroll offers for lapis and XP." - lore1 = "Reroll Cooldown" - lore2 = "Lapis Cost" - lore = ["Reroll Cooldown", "Lapis Cost"] +name = "Offer Reroll" +description = "Sneak-right-click an enchanting table to reroll offers for lapis and XP." +lore1 = "Reroll Cooldown" +lore2 = "Lapis Cost" +lore = ["Reroll Cooldown", "Lapis Cost"] [enchanting.bookshelf_attunement] - name = "Bookshelf Attunement" - description = "Gain virtual bookshelf power to improve enchanting table offer quality." - lore1 = "Virtual Bookshelf Power" - lore = ["Virtual Bookshelf Power"] +name = "Bookshelf Attunement" +description = "Gain virtual bookshelf power to improve enchanting table offer quality." +lore1 = "Virtual Bookshelf Power" +lore = ["Virtual Bookshelf Power"] [enchanting.grindstone_recovery] - name = "Grindstone Recovery" - description = "Disenchanting can recover one removed enchantment onto a book with bonus XP." - lore1 = "Recovery Chance" - lore2 = "Bonus XP" - lore3 = "Recovery Cooldown" - lore = ["Recovery Chance", "Bonus XP", "Recovery Cooldown"] +name = "Grindstone Recovery" +description = "Disenchanting can recover one removed enchantment onto a book with bonus XP." +lore1 = "Recovery Chance" +lore2 = "Bonus XP" +lore3 = "Recovery Cooldown" +lore = ["Recovery Chance", "Bonus XP", "Recovery Cooldown"] # excavation [excavation] [excavation.haste] - name = "Hasty Excavator" - description = "This will speed up the excavation process, with HASTE!" - lore1 = "Gain Haste while excavating" - lore2 = "x Levels of haste when you start mining ANY block." - lore = ["Gain Haste while excavating", "x Levels of haste when you start mining ANY block."] +name = "Hasty Excavator" +description = "This will speed up the excavation process, with HASTE!" +lore1 = "Gain Haste while excavating" +lore2 = "x Levels of haste when you start mining ANY block." +lore = ["Gain Haste while excavating", "x Levels of haste when you start mining ANY block."] [excavation.spelunker] - name = "Super-Seeing Spelunker!" - description = "See Ores with your eyes, but through the ground!" - lore1 = "Ore in your offhand, Glowberries in your main hand, and Sneak!" - lore2 = "Block Range: " - lore3 = "Consumes Glowberry on use" - lore = ["Ore in your offhand, Glowberries in your main hand, and Sneak!", "Block Range: ", "Consumes Glowberry on use"] +name = "Super-Seeing Spelunker!" +description = "See Ores with your eyes, but through the ground!" +lore1 = "Ore in your offhand, Glowberries in your main hand, and Sneak!" +lore2 = "Block Range: " +lore3 = "Consumes Glowberry on use" +lore = ["Ore in your offhand, Glowberries in your main hand, and Sneak!", "Block Range: ", "Consumes Glowberry on use"] [excavation.seismic_ping] - name = "Seismic Ping" - description = "Mining can emit directional pings that point toward nearby ore." - lore1 = "Scan Range" - lore2 = "Ping Chance" - lore3 = "Ping Cooldown" - lore = ["Scan Range", "Ping Chance", "Ping Cooldown"] +name = "Seismic Ping" +description = "Mining can emit directional pings that point toward nearby ore." +lore1 = "Scan Range" +lore2 = "Ping Chance" +lore3 = "Ping Cooldown" +lore = ["Scan Range", "Ping Chance", "Ping Cooldown"] [excavation.drop_to_inventory] - name = "Shovel Drop-To-Inventory" +name = "Shovel Drop-To-Inventory" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Tackle's overdesigned opulent Leatherman" - lore1 = "Probably the most powerful of many allows you to" - lore2 = "dynamically merge and change tools on the fly, based on your needs." - lore3 = "To merge, shift click an item over another in your inventory." - lore4 = "To unbind tools, Sneak-Drop the item, and it will disassemble." - lore5 = "You can't break tools in this leatherman but you can't use broken tools" - lore6 = "total merge-able items." - lore7 = "You could use five or six tools, or just one!" - lore = ["Probably the most powerful of many allows you to", "dynamically merge and change tools on the fly, based on your needs.", "To merge, shift click an item over another in your inventory.", "To unbind tools, Sneak-Drop the item, and it will disassemble.", "You can't break tools in this leatherman but you can't use broken tools", "total merge-able items.", "You could use five or six tools, or just one!"] +name = "OMNI - T.O.O.L." +description = "Tackle's overdesigned opulent Leatherman" +lore1 = "Probably the most powerful of many allows you to" +lore2 = "dynamically merge and change tools on the fly, based on your needs." +lore3 = "To merge, shift click an item over another in your inventory." +lore4 = "To unbind tools, Sneak-Drop the item, and it will disassemble." +lore5 = "You can't break tools in this leatherman but you can't use broken tools" +lore6 = "total merge-able items." +lore7 = "You could use five or six tools, or just one!" +lore = ["Probably the most powerful of many allows you to", "dynamically merge and change tools on the fly, based on your needs.", "To merge, shift click an item over another in your inventory.", "To unbind tools, Sneak-Drop the item, and it will disassemble.", "You can't break tools in this leatherman but you can't use broken tools", "total merge-able items.", "You could use five or six tools, or just one!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Growth Aura" - description = "Grow nature around you in an aura" - lore1 = "Block Radius" - lore2 = "Growth Aura Strength" - lore3 = "Food Cost" - lore = ["Block Radius", "Growth Aura Strength", "Food Cost"] +name = "Growth Aura" +description = "Grow nature around you in an aura" +lore1 = "Block Radius" +lore2 = "Growth Aura Strength" +lore3 = "Food Cost" +lore = ["Block Radius", "Growth Aura Strength", "Food Cost"] [herbalism.hippo] - name = "Herbalist's Hippo" - description = "Consuming food, gives you more saturation" - lore1 = "Food) additional saturation points on consumption" - lore = ["Food) additional saturation points on consumption"] +name = "Herbalist's Hippo" +description = "Consuming food, gives you more saturation" +lore1 = "Food) additional saturation points on consumption" +lore = ["Food) additional saturation points on consumption"] [herbalism.myconid] - name = "Herbalist's Myconid" - description = "Gives you the ability to craft Mycelium" - lore1 = "Any Dirt, and a Brown & Red Mushroom will craft Mycelium." - lore = ["Any Dirt, and a Brown & Red Mushroom will craft Mycelium."] +name = "Herbalist's Myconid" +description = "Gives you the ability to craft Mycelium" +lore1 = "Any Dirt, and a Brown & Red Mushroom will craft Mycelium." +lore = ["Any Dirt, and a Brown & Red Mushroom will craft Mycelium."] [herbalism.terralid] - name = "Herbalist's Terralid" - description = "Gives you the ability to craft Grass Blocks" - lore1 = "Three Seeds, over 3 Dirt, will craft 3 Grass Blocks." - lore = ["Three Seeds, over 3 Dirt, will craft 3 Grass Blocks."] +name = "Herbalist's Terralid" +description = "Gives you the ability to craft Grass Blocks" +lore1 = "Three Seeds, over 3 Dirt, will craft 3 Grass Blocks." +lore = ["Three Seeds, over 3 Dirt, will craft 3 Grass Blocks."] [herbalism.cobweb] - name = "Webby Creator" - description = "Gives you the ability to craft Cobwebs in a Crafting Table" - lore1 = "Nine String, will craft a Cobweb." - lore = ["Nine String, will craft a Cobweb."] +name = "Webby Creator" +description = "Gives you the ability to craft Cobwebs in a Crafting Table" +lore1 = "Nine String, will craft a Cobweb." +lore = ["Nine String, will craft a Cobweb."] [herbalism.mushroom_blocks] - name = "Mushroom Maker" - description = "Gives you the ability to craft Mushroom Blocks in a Crafting Table" - lore1 = "Four Mushrooms to make a block, or a block to make a stem." - lore = ["Four Mushrooms to make a block, or a block to make a stem."] +name = "Mushroom Maker" +description = "Gives you the ability to craft Mushroom Blocks in a Crafting Table" +lore1 = "Four Mushrooms to make a block, or a block to make a stem." +lore = ["Four Mushrooms to make a block, or a block to make a stem."] [herbalism.drop_to_inventory] - name = "Hoe Drop-To-Inventory" +name = "Hoe Drop-To-Inventory" [herbalism.hungry_shield] - name = "Hungry Shield" - description = "Take damage to your hunger before your health." - lore1 = "Resisted by Hunger" - lore = ["Resisted by Hunger"] +name = "Hungry Shield" +description = "Take damage to your hunger before your health." +lore1 = "Resisted by Hunger" +lore = ["Resisted by Hunger"] [herbalism.luck] - name = "Herbalist's Luck" - description = "When you break Grass/Flowers, you have a chance to get a random item" - lore0 = "Flowers = Food, and Grass = Seeds" - lore1 = "Chance to get an item from breaking Flowers" - lore2 = "Chance to get an item from breaking Grass" - lore = ["Flowers = Food, and Grass = Seeds", "Chance to get an item from breaking Flowers", "Chance to get an item from breaking Grass"] +name = "Herbalist's Luck" +description = "When you break Grass/Flowers, you have a chance to get a random item" +lore0 = "Flowers = Food, and Grass = Seeds" +lore1 = "Chance to get an item from breaking Flowers" +lore2 = "Chance to get an item from breaking Grass" +lore = ["Flowers = Food, and Grass = Seeds", "Chance to get an item from breaking Flowers", "Chance to get an item from breaking Grass"] [herbalism.replant] - name = "Harvest & Replant" - description = "Right click a crop with a hoe to harvest & replant it." - lore1 = "Blocks Replant Radius" - lore = ["Blocks Replant Radius"] +name = "Harvest & Replant" +description = "Right click a crop with a hoe to harvest & replant it." +lore1 = "Blocks Replant Radius" +lore = ["Blocks Replant Radius"] [herbalism.seed_sower] - name = "Seed Sower" - description = "Sneak-right-click with seeds to plant nearby farmland and soul-sand plots." - lore1 = "Plant Radius" - lore2 = "Max Crops Per Use" - lore3 = "Sowing Cooldown" - lore = ["Plant Radius", "Max Crops Per Use", "Sowing Cooldown"] +name = "Seed Sower" +description = "Sneak-right-click with seeds to plant nearby farmland and soul-sand plots." +lore1 = "Plant Radius" +lore2 = "Max Crops Per Use" +lore3 = "Sowing Cooldown" +lore = ["Plant Radius", "Max Crops Per Use", "Sowing Cooldown"] [herbalism.compost_cascade] - name = "Compost Cascade" - description = "Sneak-right-click a composter to consume nearby drops, mature crop harvests, leaves, and your inventory compostables." - lore1 = "Cascade Radius" - lore2 = "Max Items Processed" - lore3 = "Compost Fill Chance" - lore4 = "Cascade Cooldown" - lore = ["Cascade Radius", "Max Items Processed", "Compost Fill Chance", "Cascade Cooldown"] +name = "Compost Cascade" +description = "Sneak-right-click a composter to consume nearby drops, mature crop harvests, leaves, and your inventory compostables." +lore1 = "Cascade Radius" +lore2 = "Max Items Processed" +lore3 = "Compost Fill Chance" +lore4 = "Cascade Cooldown" +lore = ["Cascade Radius", "Max Items Processed", "Compost Fill Chance", "Cascade Cooldown"] [herbalism.rooted_footing] - name = "Rooted Footing" - description = "Permanent passive: protect farmland and convert part of fall damage into hunger while on natural ground." - lore1 = "Fall Damage Converted" - lore2 = "Food Per Damage" - lore3 = "Prevents Farmland Trample" - lore = ["Fall Damage Converted", "Food Per Damage", "Prevents Farmland Trample"] +name = "Rooted Footing" +description = "Permanent passive: protect farmland and convert part of fall damage into hunger while on natural ground." +lore1 = "Fall Damage Converted" +lore2 = "Food Per Damage" +lore3 = "Prevents Farmland Trample" +lore = ["Fall Damage Converted", "Food Per Damage", "Prevents Farmland Trample"] [herbalism.bee_shepherd] - name = "Bee Shepherd" - description = "Hold flowers near crops to pulse growth and draw nearby bees toward you." - lore1 = "Pulse Radius" - lore2 = "Growth Attempts" - lore3 = "Pulse Cooldown" - lore = ["Pulse Radius", "Growth Attempts", "Pulse Cooldown"] +name = "Bee Shepherd" +description = "Hold flowers near crops to pulse growth and draw nearby bees toward you." +lore1 = "Pulse Radius" +lore2 = "Growth Attempts" +lore3 = "Pulse Cooldown" +lore = ["Pulse Radius", "Growth Attempts", "Pulse Cooldown"] [herbalism.spore_bloom] - name = "Spore Bloom" - description = "Sneak-right-click mycelium with mushrooms to spread controlled bloom patches." - lore1 = "Bloom Attempts" - lore2 = "Bloom Radius" - lore3 = "Bloom Cooldown" - lore = ["Bloom Attempts", "Bloom Radius", "Bloom Cooldown"] +name = "Spore Bloom" +description = "Sneak-right-click mycelium with mushrooms to spread controlled bloom patches." +lore1 = "Bloom Attempts" +lore2 = "Bloom Radius" +lore3 = "Bloom Cooldown" +lore = ["Bloom Attempts", "Bloom Radius", "Bloom Cooldown"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenaline" - description = "Deal more damage the lower health you are (Melee)" - lore1 = "Max Damage" - lore = ["Max Damage"] +name = "Adrenaline" +description = "Deal more damage the lower health you are (Melee)" +lore1 = "Max Damage" +lore = ["Max Damage"] [hunter.penalty] - name = "" - description = "" - lore1 = "You will Gain Poison stacks if you run out of hunger" - lore = ["You will Gain Poison stacks if you run out of hunger"] +name = "" +description = "" +lore1 = "You will Gain Poison stacks if you run out of hunger" +lore = ["You will Gain Poison stacks if you run out of hunger"] [hunter.drop_to_inventory] - name = "Items Drop-To-Inventory" - description = "When you Kill something / Break a block With a sword it teleports the drops into your inventory" - lore1 = "Whenever an item is dropped from a mob/block you break it goes into your inventory if it can." - lore = ["Whenever an item is dropped from a mob/block you break it goes into your inventory if it can."] +name = "Items Drop-To-Inventory" +description = "When you Kill something / Break a block With a sword it teleports the drops into your inventory" +lore1 = "Whenever an item is dropped from a mob/block you break it goes into your inventory if it can." +lore = ["Whenever an item is dropped from a mob/block you break it goes into your inventory if it can."] [hunter.trophy_skinner] - name = "Trophy Skinner" - description = "Precision kills can yield bonus trophy drops and occasional mob heads." - lore1 = "Bonus Trophy Chance" - lore2 = "Head Drop Chance" - lore3 = "Minimum Ranged Precision Distance" - lore = ["Bonus Trophy Chance", "Head Drop Chance", "Minimum Ranged Precision Distance"] +name = "Trophy Skinner" +description = "Precision kills can yield bonus trophy drops and occasional mob heads." +lore1 = "Bonus Trophy Chance" +lore2 = "Head Drop Chance" +lore3 = "Minimum Ranged Precision Distance" +lore = ["Bonus Trophy Chance", "Head Drop Chance", "Minimum Ranged Precision Distance"] [hunter.invisibility] - name = "Vanishing Step" - description = "When you are struck you gain invisibility, at the cost of hunger" - lore1 = "Gain passive invisibility when struck" - lore2 = "x Invisibility stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Invisibility duration" - lore = ["Gain passive invisibility when struck", "x Invisibility stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Invisibility duration"] +name = "Vanishing Step" +description = "When you are struck you gain invisibility, at the cost of hunger" +lore1 = "Gain passive invisibility when struck" +lore2 = "x Invisibility stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Invisibility duration" +lore = ["Gain passive invisibility when struck", "x Invisibility stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Invisibility duration"] [hunter.jump_boost] - name = "Hunter's Heights" - description = "When you are struck you gain jump-boost, at the cost of hunger" - lore1 = "Gain passive jump-boost when struck" - lore2 = "x Jump-Boost stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Jump-Boost stacks multiplier, not duration." - lore = ["Gain passive jump-boost when struck", "x Jump-Boost stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Jump-Boost stacks multiplier, not duration."] +name = "Hunter's Heights" +description = "When you are struck you gain jump-boost, at the cost of hunger" +lore1 = "Gain passive jump-boost when struck" +lore2 = "x Jump-Boost stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Jump-Boost stacks multiplier, not duration." +lore = ["Gain passive jump-boost when struck", "x Jump-Boost stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Jump-Boost stacks multiplier, not duration."] [hunter.luck] - name = "Hunter's Luck" - description = "When you are struck you gain luck, at the cost of hunger" - lore1 = "Gain passive luck when struck" - lore2 = "x Luck stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Luck stacks multiplier, not duration." - lore = ["Gain passive luck when struck", "x Luck stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Luck stacks multiplier, not duration."] +name = "Hunter's Luck" +description = "When you are struck you gain luck, at the cost of hunger" +lore1 = "Gain passive luck when struck" +lore2 = "x Luck stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Luck stacks multiplier, not duration." +lore = ["Gain passive luck when struck", "x Luck stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Luck stacks multiplier, not duration."] [hunter.regen] - name = "Hunter's Regen" - description = "When you are struck you gain regeneration, at the cost of hunger" - lore1 = "Gain passive regeneration when struck" - lore2 = "x Regeneration stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Regeneration stacks multiplier, not duration." - lore = ["Gain passive regeneration when struck", "x Regeneration stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Regeneration stacks multiplier, not duration."] +name = "Hunter's Regen" +description = "When you are struck you gain regeneration, at the cost of hunger" +lore1 = "Gain passive regeneration when struck" +lore2 = "x Regeneration stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Regeneration stacks multiplier, not duration." +lore = ["Gain passive regeneration when struck", "x Regeneration stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Regeneration stacks multiplier, not duration."] [hunter.resistance] - name = "Hunter's Resistance" - description = "When you are struck you gain resistance, at the cost of hunger" - lore1 = "Gain passive resistance when struck" - lore2 = "x Resistance stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Resistance stacks multiplier, not duration." - lore = ["Gain passive resistance when struck", "x Resistance stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Resistance stacks multiplier, not duration."] +name = "Hunter's Resistance" +description = "When you are struck you gain resistance, at the cost of hunger" +lore1 = "Gain passive resistance when struck" +lore2 = "x Resistance stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Resistance stacks multiplier, not duration." +lore = ["Gain passive resistance when struck", "x Resistance stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Resistance stacks multiplier, not duration."] [hunter.speed] - name = "Hunter's Speed" - description = "When you are struck you gain speed, at the cost of hunger" - lore1 = "Gain passive speed when struck" - lore2 = "x Speed stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Speed stacks multiplier, not duration." - lore = ["Gain passive speed when struck", "x Speed stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Speed stacks multiplier, not duration."] +name = "Hunter's Speed" +description = "When you are struck you gain speed, at the cost of hunger" +lore1 = "Gain passive speed when struck" +lore2 = "x Speed stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Speed stacks multiplier, not duration." +lore = ["Gain passive speed when struck", "x Speed stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Speed stacks multiplier, not duration."] [hunter.strength] - name = "Hunter's Strength" - description = "When you are struck you gain strength, at the cost of hunger" - lore1 = "Gain passive strength when struck" - lore2 = "x Strength stacks for a 3 seconds on hit" - lore3 = "x Stacking hunger" - lore4 = "Hunger stacks duration and multiplier." - lore5 = "Strength stacks multiplier, not duration." - lore = ["Gain passive strength when struck", "x Strength stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Strength stacks multiplier, not duration."] +name = "Hunter's Strength" +description = "When you are struck you gain strength, at the cost of hunger" +lore1 = "Gain passive strength when struck" +lore2 = "x Strength stacks for a 3 seconds on hit" +lore3 = "x Stacking hunger" +lore4 = "Hunger stacks duration and multiplier." +lore5 = "Strength stacks multiplier, not duration." +lore = ["Gain passive strength when struck", "x Strength stacks for a 3 seconds on hit", "x Stacking hunger", "Hunger stacks duration and multiplier.", "Strength stacks multiplier, not duration."] # nether [nether] [nether.skull_toss] - name = "Wither Skull Throw" - description1 = "Unleash your inner Wither by using" - description2 = "someones" - description3 = "head." - lore1 = "Seconds of cooldown between skull tosses." - lore2 = "Using Wither Skull: Toss a " - lore3 = "Wither Skull" - lore4 = "exploding on impact." - lore = ["Seconds of cooldown between skull tosses.", "Using Wither Skull: Toss a ", "Wither Skull", "exploding on impact."] +name = "Wither Skull Throw" +description1 = "Unleash your inner Wither by using" +description2 = "someones" +description3 = "head." +lore1 = "Seconds of cooldown between skull tosses." +lore2 = "Using Wither Skull: Toss a " +lore3 = "Wither Skull" +lore4 = "exploding on impact." +lore = ["Seconds of cooldown between skull tosses.", "Using Wither Skull: Toss a ", "Wither Skull", "exploding on impact."] [nether.wither_resist] - name = "Wither Resistance" - description = "Resists withering through the power of Netherite." - lore1 = "chance to negate withering (per piece)." - lore2 = "Passive: Wearing Netherite Armor has a chance to negate " - lore3 = "withering." - lore = ["chance to negate withering (per piece).", "Passive: Wearing Netherite Armor has a chance to negate ", "withering."] +name = "Wither Resistance" +description = "Resists withering through the power of Netherite." +lore1 = "chance to negate withering (per piece)." +lore2 = "Passive: Wearing Netherite Armor has a chance to negate " +lore3 = "withering." +lore = ["chance to negate withering (per piece).", "Passive: Wearing Netherite Armor has a chance to negate ", "withering."] [nether.fire_resist] - name = "Fire Resistance" - description = "Resists fire by hardening your skin." - lore1 = "chance to negate the burning effect!" - lore = ["chance to negate the burning effect!"] +name = "Fire Resistance" +description = "Resists fire by hardening your skin." +lore1 = "chance to negate the burning effect!" +lore = ["chance to negate the burning effect!"] [nether.lava_walker] - name = "Lava Walker" - description = "Stride over lava in the Nether at the cost of hunger." - lore1 = "Lava Stride Speed" - lore2 = "Hunger Cost" - lore = ["Lava Stride Speed", "Hunger Cost"] +name = "Lava Walker" +description = "Stride over lava in the Nether at the cost of hunger." +lore1 = "Lava Stride Speed" +lore2 = "Hunger Cost" +lore = ["Lava Stride Speed", "Hunger Cost"] [nether.ghast_ward] - name = "Ghast Ward" - description = "Harden against ghast blasts and wither-skeleton ranged pressure in the Nether." - lore1 = "Ghast Projectile Reduction" - lore2 = "Explosion Reduction" - lore3 = "Wither Skeleton Projectile Reduction" - lore = ["Ghast Projectile Reduction", "Explosion Reduction", "Wither Skeleton Projectile Reduction"] +name = "Ghast Ward" +description = "Harden against ghast blasts and wither-skeleton ranged pressure in the Nether." +lore1 = "Ghast Projectile Reduction" +lore2 = "Explosion Reduction" +lore3 = "Wither Skeleton Projectile Reduction" +lore = ["Ghast Projectile Reduction", "Explosion Reduction", "Wither Skeleton Projectile Reduction"] [nether.blaze_leech] - name = "Blaze Leech" - description = "Fire interactions can trigger brief hunger and regeneration gains." - lore1 = "Leech Trigger Chance" - lore2 = "Regen Burst Duration" - lore3 = "Food Restored per Proc" - lore = ["Leech Trigger Chance", "Regen Burst Duration", "Food Restored per Proc"] +name = "Blaze Leech" +description = "Fire interactions can trigger brief hunger and regeneration gains." +lore1 = "Leech Trigger Chance" +lore2 = "Regen Burst Duration" +lore3 = "Food Restored per Proc" +lore = ["Leech Trigger Chance", "Regen Burst Duration", "Food Restored per Proc"] [nether.piglin_broker] - name = "Piglin Broker" - description = "Nearby barters can grant extra rolls and occasional premium bonus items." - lore1 = "Extra Roll Chance" - lore2 = "Rare Bonus Chance" - lore = ["Extra Roll Chance", "Rare Bonus Chance"] +name = "Piglin Broker" +description = "Nearby barters can grant extra rolls and occasional premium bonus items." +lore1 = "Extra Roll Chance" +lore2 = "Rare Bonus Chance" +lore = ["Extra Roll Chance", "Rare Bonus Chance"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Autosmelt" - description = "Allows you to smelt mined Vanilla ores" - lore1 = "Ores that can be smelted are smelted automatically" - lore2 = "% chance for an extra" - lore = ["Ores that can be smelted are smelted automatically", "% chance for an extra"] +name = "Autosmelt" +description = "Allows you to smelt mined Vanilla ores" +lore1 = "Ores that can be smelted are smelted automatically" +lore2 = "% chance for an extra" +lore = ["Ores that can be smelted are smelted automatically", "% chance for an extra"] [pickaxe.chisel] - name = "Ore Chisel" - description = "Right Click Ores to Chisel more ore out of them, at a severe durability cost." - lore1 = "Chance to Drop" - lore2 = "Tool Wear" - lore = ["Chance to Drop", "Tool Wear"] +name = "Ore Chisel" +description = "Right Click Ores to Chisel more ore out of them, at a severe durability cost." +lore1 = "Chance to Drop" +lore2 = "Tool Wear" +lore = ["Chance to Drop", "Tool Wear"] [pickaxe.drop_to_inventory] - name = "Pickaxe Drop-To-Inventory" - description = "When you break a block it teleports the item into your inventory" - lore1 = "Whenever an item is dropped from a block you break it goes into your inventory if it can." - lore = ["Whenever an item is dropped from a block you break it goes into your inventory if it can."] +name = "Pickaxe Drop-To-Inventory" +description = "When you break a block it teleports the item into your inventory" +lore1 = "Whenever an item is dropped from a block you break it goes into your inventory if it can." +lore = ["Whenever an item is dropped from a block you break it goes into your inventory if it can."] [pickaxe.silk_spawner] - name = "Pickaxe Silk-Spawner" - description = "Makes Spawners drop when broken" - lore1 = "Makes Spawners breakable with silk touch." - lore2 = "Makes Spawners breakable while sneaking." - lore = ["Makes Spawners breakable with silk touch.", "Makes Spawners breakable while sneaking."] +name = "Pickaxe Silk-Spawner" +description = "Makes Spawners drop when broken" +lore1 = "Makes Spawners breakable with silk touch." +lore2 = "Makes Spawners breakable while sneaking." +lore = ["Makes Spawners breakable with silk touch.", "Makes Spawners breakable while sneaking."] [pickaxe.vein_miner] - name = "Veinminer" - description = "Allows you to break blocks in a Vein/Cluster of Vanilla ores" - lore1 = "Sneak, and mine ORES" - lore2 = "range of vein-mining" - lore3 = "This skill does NOT group all drops together!" - lore = ["Sneak, and mine ORES", "range of vein-mining", "This skill does NOT group all drops together!"] +name = "Veinminer" +description = "Allows you to break blocks in a Vein/Cluster of Vanilla ores" +lore1 = "Sneak, and mine ORES" +lore2 = "range of vein-mining" +lore3 = "This skill does NOT group all drops together!" +lore = ["Sneak, and mine ORES", "range of vein-mining", "This skill does NOT group all drops together!"] [pickaxe.quarry_sense] - name = "Quarry Sense" - description = "Sneak-right-click a block with an iron+ pickaxe to reveal nearby ores using temporary glowing markers." - lore1 = "Ore Scan Radius" - lore2 = "Durability Cost (% of Max Durability)" - lore3 = "Sense Cooldown" - lore = ["Ore Scan Radius", "Durability Cost (% of Max Durability)", "Sense Cooldown"] +name = "Quarry Sense" +description = "Sneak-right-click a block with an iron+ pickaxe to reveal nearby ores using temporary glowing markers." +lore1 = "Ore Scan Radius" +lore2 = "Durability Cost (% of Max Durability)" +lore3 = "Sense Cooldown" +lore = ["Ore Scan Radius", "Durability Cost (% of Max Durability)", "Sense Cooldown"] # ranged [ranged] [ranged.arrow_recovery] - name = "Arrow Recovery" - description = "Recover Arrows after you have killed an enemy." - lore1 = "Chance to Recover Arrows on Hit/Kill" - lore2 = "Chance: " - lore = ["Chance to Recover Arrows on Hit/Kill", "Chance: "] +name = "Arrow Recovery" +description = "Recover Arrows after you have killed an enemy." +lore1 = "Chance to Recover Arrows on Hit/Kill" +lore2 = "Chance: " +lore = ["Chance to Recover Arrows on Hit/Kill", "Chance: "] [ranged.web_shot] - name = "Web Snare" - description = "Surround cobwebs around your target when you hit them!" - lore1 = "8 Cobwebs around a Snowball, and throw!" - lore2 = "seconds of a cage, roughly." - lore = ["8 Cobwebs around a Snowball, and throw!", "seconds of a cage, roughly."] +name = "Web Snare" +description = "Surround cobwebs around your target when you hit them!" +lore1 = "8 Cobwebs around a Snowball, and throw!" +lore2 = "seconds of a cage, roughly." +lore = ["8 Cobwebs around a Snowball, and throw!", "seconds of a cage, roughly."] [ranged.force_shot] - name = "Force Shot" - description = "Shoot projectiles further, faster!" - advancementname = "Long Shot" - advancementlore = "Land a shot from over 30 blocks away!" - lore1 = "Projectile Speed" - lore = ["Projectile Speed"] +name = "Force Shot" +description = "Shoot projectiles further, faster!" +advancementname = "Long Shot" +advancementlore = "Land a shot from over 30 blocks away!" +lore1 = "Projectile Speed" +lore = ["Projectile Speed"] [ranged.trajectory_sight] - name = "Trajectory Sight" - description = "Sneak or draw a ranged weapon to preview projectile flight." - lore1 = "Prediction Range" - lore2 = "Prediction Detail" - lore = ["Prediction Range", "Prediction Detail"] +name = "Trajectory Sight" +description = "Sneak or draw a ranged weapon to preview projectile flight." +lore1 = "Prediction Range" +lore2 = "Prediction Detail" +lore = ["Prediction Range", "Prediction Detail"] [ranged.lunge_shot] - name = "Lunge Shot" - description = "While falling your arrows toss you in a random direction" - lore1 = "Random Burst Speed" - lore = ["Random Burst Speed"] +name = "Lunge Shot" +description = "While falling your arrows toss you in a random direction" +lore1 = "Random Burst Speed" +lore = ["Random Burst Speed"] [ranged.arrow_piercing] - name = "Arrow Piercing" - description = "Adds Piercing to projectiles! Shoot through things!" - lore1 = "Pierce Targets" - lore = ["Pierce Targets"] +name = "Arrow Piercing" +description = "Adds Piercing to projectiles! Shoot through things!" +lore1 = "Pierce Targets" +lore = ["Pierce Targets"] [ranged.floaters] - name = "Floaters" - description = "Projectiles have a chance to apply levitation and hold targets in the air." - lore1 = "Levitation Chance" - lore2 = "Levitation Duration" - lore3 = "Levitation Strength" - lore = ["Levitation Chance", "Levitation Duration", "Levitation Strength"] +name = "Floaters" +description = "Projectiles have a chance to apply levitation and hold targets in the air." +lore1 = "Levitation Chance" +lore2 = "Levitation Duration" +lore3 = "Levitation Strength" +lore = ["Levitation Chance", "Levitation Duration", "Levitation Strength"] [ranged.pinning_shot] - name = "Pinning Shot" - description = "Any player-fired projectile can pin targets with heavy slowness." - lore1 = "Pin Chance" - lore2 = "Pin Duration" - lore3 = "Reapply Cooldown" - lore = ["Pin Chance", "Pin Duration", "Reapply Cooldown"] +name = "Pinning Shot" +description = "Any player-fired projectile can pin targets with heavy slowness." +lore1 = "Pin Chance" +lore2 = "Pin Duration" +lore3 = "Reapply Cooldown" +lore = ["Pin Chance", "Pin Duration", "Reapply Cooldown"] [ranged.ricochet_bolt] - name = "Ricochet Bolt" - description = "Projectiles ricochet off solid blocks with chained bounces." - lore1 = "Max Ricochets" - lore2 = "Speed Bonus Per Ricochet" - lore3 = "Bonus Damage Per Ricochet" - lore = ["Max Ricochets", "Speed Bonus Per Ricochet", "Bonus Damage Per Ricochet"] +name = "Ricochet Bolt" +description = "Projectiles ricochet off solid blocks with chained bounces." +lore1 = "Max Ricochets" +lore2 = "Speed Bonus Per Ricochet" +lore3 = "Bonus Damage Per Ricochet" +lore = ["Max Ricochets", "Speed Bonus Per Ricochet", "Bonus Damage Per Ricochet"] # rift [rift] [rift.remote_access] - name = "Remote Access" - description = "Pull from the void, and get into a marked container." - lore1 = "Enderpearl + Compass = Reliquary Portkey" - lore2 = "This item allows you to access containers remotely" - lore3 = "Once crafted look at item to see usage" - notcontainer = "That's not a container" - lore = ["Enderpearl + Compass = Reliquary Portkey", "This item allows you to access containers remotely", "Once crafted look at item to see usage"] +name = "Remote Access" +description = "Pull from the void, and get into a marked container." +lore1 = "Enderpearl + Compass = Reliquary Portkey" +lore2 = "This item allows you to access containers remotely" +lore3 = "Once crafted look at item to see usage" +notcontainer = "That's not a container" +lore = ["Enderpearl + Compass = Reliquary Portkey", "This item allows you to access containers remotely", "Once crafted look at item to see usage"] [rift.blink] - name = "Rift Blink" - description = "Short ranged instant teleportation, Just a blink away!" - lore1 = "Blocks on blink (2x Vertical)" - lore2 = "While Sprinting: Double tap Jump to " - lore3 = "Blink" - lore = ["Blocks on blink (2x Vertical)", "While Sprinting: Double tap Jump to ", "Blink"] +name = "Rift Blink" +description = "Short ranged instant teleportation, Just a blink away!" +lore1 = "Blocks on blink (2x Vertical)" +lore2 = "While Sprinting: Double tap Jump to " +lore3 = "Blink" +lore = ["Blocks on blink (2x Vertical)", "While Sprinting: Double tap Jump to ", "Blink"] [rift.chest] - name = "Easy Enderchest" - description = "Open an enderchest by Left-clicking it in your hand." - lore1 = "Click an Enderchest in your hand to open (Just dont place it)" - lore = ["Click an Enderchest in your hand to open (Just dont place it)"] +name = "Easy Enderchest" +description = "Open an enderchest by Left-clicking it in your hand." +lore1 = "Click an Enderchest in your hand to open (Just dont place it)" +lore = ["Click an Enderchest in your hand to open (Just dont place it)"] [rift.descent] - name = "Anti-Levitation" - description = "Are you tired of being stuck in the air? This is the skill for you!" - lore1 = "Just Sneak to descend, and you will fall at a less than normal rate!" - lore2 = "Cooldown:" - lore = ["Just Sneak to descend, and you will fall at a less than normal rate!", "Cooldown:"] +name = "Anti-Levitation" +description = "Are you tired of being stuck in the air? This is the skill for you!" +lore1 = "Just Sneak to descend, and you will fall at a less than normal rate!" +lore2 = "Cooldown:" +lore = ["Just Sneak to descend, and you will fall at a less than normal rate!", "Cooldown:"] [rift.gate] - name = "Rift Gate" - description = "Teleport to a marked location." - lore1 = "CRAFTING: Emerald + Amethyst shard + Ender Pearl" - lore2 = "Read before using!" - lore3 = "5s delay, " - lore4 = "you can die while you are in this animation" - lore = ["CRAFTING: Emerald + Amethyst shard + Ender Pearl", "Read before using!", "5s delay, ", "you can die while you are in this animation"] +name = "Rift Gate" +description = "Teleport to a marked location." +lore1 = "CRAFTING: Emerald + Amethyst shard + Ender Pearl" +lore2 = "Read before using!" +lore3 = "5s delay, " +lore4 = "you can die while you are in this animation" +lore = ["CRAFTING: Emerald + Amethyst shard + Ender Pearl", "Read before using!", "5s delay, ", "you can die while you are in this animation"] [rift.resist] - name = "Rift Resistance" - description = "Gain Resistance when using Ender Items & Abilities" - lore1 = "+ Passive: Provides resistance when you use rift abilities, or Ender Items" - lore2 = "NOT Including Portable Enderchest, only things you can Consume" - lore = ["+ Passive: Provides resistance when you use rift abilities, or Ender Items", "NOT Including Portable Enderchest, only things you can Consume"] +name = "Rift Resistance" +description = "Gain Resistance when using Ender Items & Abilities" +lore1 = "+ Passive: Provides resistance when you use rift abilities, or Ender Items" +lore2 = "NOT Including Portable Enderchest, only things you can Consume" +lore = ["+ Passive: Provides resistance when you use rift abilities, or Ender Items", "NOT Including Portable Enderchest, only things you can Consume"] [rift.visage] - name = "Rift Visage" - description = "Prevents Endermen from becoming aggressive if you have Enderpearls in your inventory." - lore1 = "Endermen will not become aggressive if you have Enderpearls in your inventory." - lore = ["Endermen will not become aggressive if you have Enderpearls in your inventory."] +name = "Rift Visage" +description = "Prevents Endermen from becoming aggressive if you have Enderpearls in your inventory." +lore1 = "Endermen will not become aggressive if you have Enderpearls in your inventory." +lore = ["Endermen will not become aggressive if you have Enderpearls in your inventory."] [rift.void_magnet] - name = "Void Magnet" - description = "Sneak to pull nearby item drops into your ender chest first, then inventory overflow." - lore1 = "Magnet Radius" - lore2 = "Max Items Per Pulse" - lore3 = "Pulse Delay" - lore = ["Magnet Radius", "Max Items Per Pulse", "Pulse Delay"] +name = "Void Magnet" +description = "Sneak to pull nearby item drops into your ender chest first, then inventory overflow." +lore1 = "Magnet Radius" +lore2 = "Max Items Per Pulse" +lore3 = "Pulse Delay" +lore = ["Magnet Radius", "Max Items Per Pulse", "Pulse Delay"] [rift.inflated_pocket_dimension] - name = "Inflated Pocket Dimension" - description = "Empty-hand right-click a block to pull matching stacks from your ender chest; sneak-drop to store items into it." - lore1 = "Right-click block to pull stack" - lore2 = "Building auto-refill from ender chest" - lore3 = "Sneak-drop stores item in ender chest" - lore = ["Right-click block to pull stack", "Building auto-refill from ender chest", "Sneak-drop stores item in ender chest"] +name = "Inflated Pocket Dimension" +description = "Empty-hand right-click a block to pull matching stacks from your ender chest; sneak-drop to store items into it." +lore1 = "Right-click block to pull stack" +lore2 = "Building auto-refill from ender chest" +lore3 = "Sneak-drop stores item in ender chest" +lore = ["Right-click block to pull stack", "Building auto-refill from ender chest", "Sneak-drop stores item in ender chest"] [rift.ender_taglock] - name = "Ender Taglock" - description = "Sneak-left-click entities with an ender pearl to bind them, then throw the tagged pearl to relocate only that target. The thrower is never teleported." - lore1 = "Level 1: Passive and hostile mobs" - lore2 = "Level 2: Villagers and large targets" - lore3 = "Level 3: Any target, including players" - lore4 = "Tagged Pearl Throw Cooldown" - lore = ["Level 1: Passive and hostile mobs", "Level 2: Villagers and large targets", "Level 3: Any target, including players", "Tagged Pearl Throw Cooldown"] +name = "Ender Taglock" +description = "Sneak-left-click entities with an ender pearl to bind them, then throw the tagged pearl to relocate only that target. The thrower is never teleported." +lore1 = "Level 1: Passive and hostile mobs" +lore2 = "Level 2: Villagers and large targets" +lore3 = "Level 3: Any target, including players" +lore4 = "Tagged Pearl Throw Cooldown" +lore = ["Level 1: Passive and hostile mobs", "Level 2: Villagers and large targets", "Level 3: Any target, including players", "Tagged Pearl Throw Cooldown"] # seaborn [seaborn] [seaborn.oxygen] - name = "Organic Oxygen Tank" - description = "Hold more oxygen in your tiny lungs!" - lore1 = "Oxygen Capacity Increase" - lore = ["Oxygen Capacity Increase"] +name = "Organic Oxygen Tank" +description = "Hold more oxygen in your tiny lungs!" +lore1 = "Oxygen Capacity Increase" +lore = ["Oxygen Capacity Increase"] [seaborn.fishers_fantasy] - name = "Fisher's Fantasy" - description = "Earn more XP from fishing, and get more fish!" - lore1 = "For each level there is a chance to get more XP and Fish!" - lore = ["For each level there is a chance to get more XP and Fish!"] +name = "Fisher's Fantasy" +description = "Earn more XP from fishing, and get more fish!" +lore1 = "For each level there is a chance to get more XP and Fish!" +lore = ["For each level there is a chance to get more XP and Fish!"] [seaborn.haste] - name = "Turtle Miner" - description = "While mining underwater you gain haste!" - lore1 = "Haste 3 is applied underwater while mining (stacks with AquaAffinity) after your water breathing effect wears off!" - lore = ["Haste 3 is applied underwater while mining (stacks with AquaAffinity) after your water breathing effect wears off!"] +name = "Turtle Miner" +description = "While mining underwater you gain haste!" +lore1 = "Haste 3 is applied underwater while mining (stacks with AquaAffinity) after your water breathing effect wears off!" +lore = ["Haste 3 is applied underwater while mining (stacks with AquaAffinity) after your water breathing effect wears off!"] [seaborn.night_vision] - name = "Turtle's Vision" - description = "While underwater, you gain Night Vision" - lore1 = "Simply gain Night Vision while underwater after your water breathing effect wears off!" - lore = ["Simply gain Night Vision while underwater after your water breathing effect wears off!"] +name = "Turtle's Vision" +description = "While underwater, you gain Night Vision" +lore1 = "Simply gain Night Vision while underwater after your water breathing effect wears off!" +lore = ["Simply gain Night Vision while underwater after your water breathing effect wears off!"] [seaborn.dolphin_grace] - name = "Dolphin's Grace" - description = "Swim like a dolphin, without the dolphins" - lore1 = "+ Passive: gain " - lore2 = "x speed (dolphins grace)" - lore3 = "precision german engineeeri- wait that's not right... Not Compatible with Depth Strider" - lore = ["+ Passive: gain ", "x speed (dolphins grace)", "precision german engineeeri- wait that's not right... Not Compatible with Depth Strider"] +name = "Dolphin's Grace" +description = "Swim like a dolphin, without the dolphins" +lore1 = "+ Passive: gain " +lore2 = "x speed (dolphins grace)" +lore3 = "precision german engineeeri- wait that's not right... Not Compatible with Depth Strider" +lore = ["+ Passive: gain ", "x speed (dolphins grace)", "precision german engineeeri- wait that's not right... Not Compatible with Depth Strider"] [seaborn.tidecaller] - name = "Tidecaller" - description = "Sneak while it is raining on you to surge forward in a water-themed blink." - lore1 = "Rain Blink Distance" - lore2 = "Rain Blink Cooldown" - lore = ["Rain Blink Distance", "Rain Blink Cooldown"] +name = "Tidecaller" +description = "Sneak while it is raining on you to surge forward in a water-themed blink." +lore1 = "Rain Blink Distance" +lore2 = "Rain Blink Cooldown" +lore = ["Rain Blink Distance", "Rain Blink Cooldown"] [seaborn.pressure_diver] - name = "Pressure Diver" - description = "Gain depth-based protection underwater and partially suppress mining fatigue pressure." - lore1 = "Minimum Depth Requirement" - lore2 = "Depth Damage Reduction" - lore3 = "Mining Fatigue Reduction Chance" - lore = ["Minimum Depth Requirement", "Depth Damage Reduction", "Mining Fatigue Reduction Chance"] +name = "Pressure Diver" +description = "Gain depth-based protection underwater and partially suppress mining fatigue pressure." +lore1 = "Minimum Depth Requirement" +lore2 = "Depth Damage Reduction" +lore3 = "Mining Fatigue Reduction Chance" +lore = ["Minimum Depth Requirement", "Depth Damage Reduction", "Mining Fatigue Reduction Chance"] # stealth [stealth] [stealth.ghost_armor] - name = "Ghost's Armor" - description = "Slow building armor when not taking damage, Lasts for 1 hit" - lore1 = "Max Armor" - lore2 = "Speed" - lore = ["Max Armor", "Speed"] +name = "Ghost's Armor" +description = "Slow building armor when not taking damage, Lasts for 1 hit" +lore1 = "Max Armor" +lore2 = "Speed" +lore = ["Max Armor", "Speed"] [stealth.night_vision] - name = "Stealth Vision" - description = "Gain night vision while sneaking" - lore1 = "Gain a burst of " - lore2 = "night vision" - lore3 = "while sneaking" - lore = ["Gain a burst of ", "night vision", "while sneaking"] +name = "Stealth Vision" +description = "Gain night vision while sneaking" +lore1 = "Gain a burst of " +lore2 = "night vision" +lore3 = "while sneaking" +lore = ["Gain a burst of ", "night vision", "while sneaking"] [stealth.snatch] - name = "Item Snatch" - description = "Snatch Dropped items instantly while sneaking!" - lore1 = "Snatch Radius" - lore = ["Snatch Radius"] +name = "Item Snatch" +description = "Snatch Dropped items instantly while sneaking!" +lore1 = "Snatch Radius" +lore = ["Snatch Radius"] [stealth.speed] - name = "Sneak Speed" - description = "Gain speed while sneaking" - lore1 = "Sneaking Speed" - lore = ["Sneaking Speed"] +name = "Sneak Speed" +description = "Gain speed while sneaking" +lore1 = "Sneaking Speed" +lore = ["Sneaking Speed"] [stealth.ender_veil] - name = "Enderveil" - description = "No more Pumpkins to prevent Enderman attacks" - lore1 = "Prevent enderman attacks while sneaking" - lore2 = "Prevent all enderman attacks" - lore = ["Prevent enderman attacks while sneaking", "Prevent all enderman attacks"] +name = "Enderveil" +description = "No more Pumpkins to prevent Enderman attacks" +lore1 = "Prevent enderman attacks while sneaking" +lore2 = "Prevent all enderman attacks" +lore = ["Prevent enderman attacks while sneaking", "Prevent all enderman attacks"] [stealth.silent_step] - name = "Silent Step" - description = "Sneaking suppresses detection, dims your vision while hidden, and grants backstab damage on unseen hits." - lore1 = "Mob Detection Suppression Radius" - lore2 = "Mob Backstab Damage Bonus" - lore3 = "Player Backstab Damage Bonus" - lore = ["Mob Detection Suppression Radius", "Mob Backstab Damage Bonus", "Player Backstab Damage Bonus"] +name = "Silent Step" +description = "Sneaking suppresses detection, dims your vision while hidden, and grants backstab damage on unseen hits." +lore1 = "Mob Detection Suppression Radius" +lore2 = "Mob Backstab Damage Bonus" +lore3 = "Player Backstab Damage Bonus" +lore = ["Mob Detection Suppression Radius", "Mob Backstab Damage Bonus", "Player Backstab Damage Bonus"] [stealth.shadow_decoy] - name = "Shadow Decoy" - description = "Stop sneaking to spawn a decoy that pulls nearby mob aggression." - lore1 = "Decoy Duration" - lore2 = "Decoy Attraction Radius" - lore3 = "Decoy Cooldown" - lore = ["Decoy Duration", "Decoy Attraction Radius", "Decoy Cooldown"] +name = "Shadow Decoy" +description = "Stop sneaking to spawn a decoy that pulls nearby mob aggression." +lore1 = "Decoy Duration" +lore2 = "Decoy Attraction Radius" +lore3 = "Decoy Cooldown" +lore = ["Decoy Duration", "Decoy Attraction Radius", "Decoy Cooldown"] # sword [sword] [sword.machete] - name = "Machete" - description = "Cut through foliage with ease!" - lore1 = "Slash Radius" - lore2 = "Chop Cooldown" - lore3 = "Tool Wear" - lore = ["Slash Radius", "Chop Cooldown", "Tool Wear"] +name = "Machete" +description = "Cut through foliage with ease!" +lore1 = "Slash Radius" +lore2 = "Chop Cooldown" +lore3 = "Tool Wear" +lore = ["Slash Radius", "Chop Cooldown", "Tool Wear"] [sword.bloody_blade] - name = "Bloody Blade" - description = "Strikes with your sword, cause Bleeding!" - lore1 = "Striking a Living entity with your Sword causes Bleeding" - lore2 = "Bleed Duration" - lore3 = "Bleed Cooldown" - lore = ["Striking a Living entity with your Sword causes Bleeding", "Bleed Duration", "Bleed Cooldown"] +name = "Bloody Blade" +description = "Strikes with your sword, cause Bleeding!" +lore1 = "Striking a Living entity with your Sword causes Bleeding" +lore2 = "Bleed Duration" +lore3 = "Bleed Cooldown" +lore = ["Striking a Living entity with your Sword causes Bleeding", "Bleed Duration", "Bleed Cooldown"] [sword.poisoned_blade] - name = "Poisoned Blade" - description = "Strikes with your sword, cause Poison!" - lore1 = "Striking a Living entity with your Sword causes Poison" - lore2 = "Poison Duration" - lore3 = "Poison Cooldown" - lore = ["Striking a Living entity with your Sword causes Poison", "Poison Duration", "Poison Cooldown"] +name = "Poisoned Blade" +description = "Strikes with your sword, cause Poison!" +lore1 = "Striking a Living entity with your Sword causes Poison" +lore2 = "Poison Duration" +lore3 = "Poison Cooldown" +lore = ["Striking a Living entity with your Sword causes Poison", "Poison Duration", "Poison Cooldown"] [sword.dual_wield] - name = "Dual Wield Stance" - description = "Holding items in both hands grants bonus melee damage. Matching items grant the higher bonus." - lore1 = "Matching Item Bonus" - lore2 = "Mixed Item Bonus" - lore = ["Matching Item Bonus", "Mixed Item Bonus"] +name = "Dual Wield Stance" +description = "Holding items in both hands grants bonus melee damage. Matching items grant the higher bonus." +lore1 = "Matching Item Bonus" +lore2 = "Mixed Item Bonus" +lore = ["Matching Item Bonus", "Mixed Item Bonus"] [sword.executioners_edge] - name = "Executioner's Edge" - description = "Sword strikes deal extra damage to low-health targets." - lore1 = "Bonus Damage" - lore2 = "Health Threshold" - lore = ["Bonus Damage", "Health Threshold"] +name = "Executioner's Edge" +description = "Sword strikes deal extra damage to low-health targets." +lore1 = "Bonus Damage" +lore2 = "Health Threshold" +lore = ["Bonus Damage", "Health Threshold"] [sword.riposte_window] - name = "Riposte Window" - description = "Blocking with a shield arms a short riposte for your next strike." - lore1 = "Riposte Window" - lore2 = "Riposte Damage Bonus" - lore = ["Riposte Window", "Riposte Damage Bonus"] +name = "Riposte Window" +description = "Blocking with a shield arms a short riposte for your next strike." +lore1 = "Riposte Window" +lore2 = "Riposte Damage Bonus" +lore = ["Riposte Window", "Riposte Damage Bonus"] [sword.crimson_cyclone] - name = "Crimson Cyclone" - description = "Land a sword crit to unleash a bleeding area slash around your target." - lore1 = "Cyclone Radius" - lore2 = "Cyclone Damage" - lore3 = "Cyclone Cooldown" - lore = ["Cyclone Radius", "Cyclone Damage", "Cyclone Cooldown"] +name = "Crimson Cyclone" +description = "Land a sword crit to unleash a bleeding area slash around your target." +lore1 = "Cyclone Radius" +lore2 = "Cyclone Damage" +lore3 = "Cyclone Cooldown" +lore = ["Cyclone Radius", "Cyclone Damage", "Cyclone Cooldown"] # taming [taming] [taming.damage] - name = "Tame Damage" - description = "Increase your tamed animal damage dealt." - lore1 = "Increased Damage" - lore = ["Increased Damage"] +name = "Tame Damage" +description = "Increase your tamed animal damage dealt." +lore1 = "Increased Damage" +lore = ["Increased Damage"] [taming.health] - name = "Tame Health" - description = "Increase your tamed animal health." - lore1 = "Increased Health" - lore = ["Increased Health"] +name = "Tame Health" +description = "Increase your tamed animal health." +lore1 = "Increased Health" +lore = ["Increased Health"] [taming.regeneration] - name = "Tame Regeneration" - description = "Increase your tamed animal regeneration." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Tame Regeneration" +description = "Increase your tamed animal regeneration." +lore1 = "HP/s" +lore = ["HP/s"] [taming.pack_leader_aura] - name = "Pack Leader Aura" - description = "Nearby tamed companions gain speed and regeneration near their owner." - lore1 = "Aura Radius" - lore2 = "Aura Strength" - lore = ["Aura Radius", "Aura Strength"] +name = "Pack Leader Aura" +description = "Nearby tamed companions gain speed and regeneration near their owner." +lore1 = "Aura Radius" +lore2 = "Aura Strength" +lore = ["Aura Radius", "Aura Strength"] [taming.beast_recall] - name = "Beast Recall" - description = "Sneak-right-click with a lead to recall your nearest tamed companion to a safe nearby spot." - lore1 = "Recall Radius" - lore2 = "Recall Cooldown" - lore = ["Recall Radius", "Recall Cooldown"] +name = "Beast Recall" +description = "Sneak-right-click with a lead to recall your nearest tamed companion to a safe nearby spot." +lore1 = "Recall Radius" +lore2 = "Recall Cooldown" +lore = ["Recall Radius", "Recall Cooldown"] [taming.shared_pain] - name = "Shared Pain" - description = "Redirect a portion of your tamed companion's damage to yourself." - lore1 = "Redirected Damage" - lore2 = "Owner Health Floor" - lore = ["Redirected Damage", "Owner Health Floor"] +name = "Shared Pain" +description = "Redirect a portion of your tamed companion's damage to yourself." +lore1 = "Redirected Damage" +lore2 = "Owner Health Floor" +lore = ["Redirected Damage", "Owner Health Floor"] [taming.mounted_tactics] - name = "Mounted Tactics" - description = "Gain mount-specific combat and handling bonuses while riding." - lore1 = "Mounted Damage Bonus" - lore2 = "Mounted Damage Reduction" - lore = ["Mounted Damage Bonus", "Mounted Damage Reduction"] +name = "Mounted Tactics" +description = "Gain mount-specific combat and handling bonuses while riding." +lore1 = "Mounted Damage Bonus" +lore2 = "Mounted Damage Reduction" +lore = ["Mounted Damage Bonus", "Mounted Damage Reduction"] # tragoul [tragoul] [tragoul.thorns] - name = "Thorns" - description = "Reflect damage back to your attacker!" - lore1 = "Damage retaliated when struck" - lore = ["Damage retaliated when struck"] +name = "Thorns" +description = "Reflect damage back to your attacker!" +lore1 = "Damage retaliated when struck" +lore = ["Damage retaliated when struck"] [tragoul.globe] - name = "Globe of Pain" - description = "Divide the Damage you deal based on the number of enemies around you!" - lore1 = "The more enemies around you, the less damage you deal to each of them" - lore2 = "Range: " - lore3 = "Added Damage to all Entities: " - lore = ["The more enemies around you, the less damage you deal to each of them", "Range: ", "Added Damage to all Entities: "] +name = "Globe of Pain" +description = "Divide the Damage you deal based on the number of enemies around you!" +lore1 = "The more enemies around you, the less damage you deal to each of them" +lore2 = "Range: " +lore3 = "Added Damage to all Entities: " +lore = ["The more enemies around you, the less damage you deal to each of them", "Range: ", "Added Damage to all Entities: "] [tragoul.healing] - name = "Will of Pain" - description = "Regain health based on the damage you deal!" - lore1 = "Harming things has never felt so good! Heal from Damage Dealt" - lore2 = "There is a 3 Second damage window, for healing and a 1 Second cooldown " - lore3 = "Healing Per Damage Percent: " - lore = ["Harming things has never felt so good! Heal from Damage Dealt", "There is a 3 Second damage window, for healing and a 1 Second cooldown ", "Healing Per Damage Percent: "] +name = "Will of Pain" +description = "Regain health based on the damage you deal!" +lore1 = "Harming things has never felt so good! Heal from Damage Dealt" +lore2 = "There is a 3 Second damage window, for healing and a 1 Second cooldown " +lore3 = "Healing Per Damage Percent: " +lore = ["Harming things has never felt so good! Heal from Damage Dealt", "There is a 3 Second damage window, for healing and a 1 Second cooldown ", "Healing Per Damage Percent: "] [tragoul.lance] - name = "Corpse Lances" - description = "Killing an enemy or having an ability kill an enemy, spawns a lance that deals damage to a nearby enemy!" - lore1 = "Lances will Seek out from anything you kill, AND if This ability kills an enemy." - lore2 = "Sacrifice a portion of your life in order to create the lances (this can kill you)" - lore3 = "Max Lances: 1 + " - lore = ["Lances will Seek out from anything you kill, AND if This ability kills an enemy.", "Sacrifice a portion of your life in order to create the lances (this can kill you)", "Max Lances: 1 + "] +name = "Corpse Lances" +description = "Killing an enemy or having an ability kill an enemy, spawns a lance that deals damage to a nearby enemy!" +lore1 = "Lances will Seek out from anything you kill, AND if This ability kills an enemy." +lore2 = "Sacrifice a portion of your life in order to create the lances (this can kill you)" +lore3 = "Max Lances: 1 + " +lore = ["Lances will Seek out from anything you kill, AND if This ability kills an enemy.", "Sacrifice a portion of your life in order to create the lances (this can kill you)", "Max Lances: 1 + "] [tragoul.blood_pact] - name = "Blood Pact" - description = "Taking at least 2 hearts of damage can trigger random beneficial effects." - lore1 = "Proc Chance" - lore2 = "Buff Duration" - lore3 = "Proc Cooldown" - lore = ["Proc Chance", "Buff Duration", "Proc Cooldown"] +name = "Blood Pact" +description = "Taking at least 2 hearts of damage can trigger random beneficial effects." +lore1 = "Proc Chance" +lore2 = "Buff Duration" +lore3 = "Proc Cooldown" +lore = ["Proc Chance", "Buff Duration", "Proc Cooldown"] [tragoul.bone_harvest] - name = "Bone Harvest" - description = "Kills can spawn blood globes or bone snowball globes that grant buffs when picked up." - lore1 = "Globe Spawn Chance" - lore2 = "Globe Lifetime" - lore = ["Globe Spawn Chance", "Globe Lifetime"] +name = "Bone Harvest" +description = "Kills can spawn blood globes or bone snowball globes that grant buffs when picked up." +lore1 = "Globe Spawn Chance" +lore2 = "Globe Lifetime" +lore = ["Globe Spawn Chance", "Globe Lifetime"] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Glass Cannon" - description = "Bonus Unarmed Damage the lower your armor value is" - lore1 = "x Damage at 0 armor" - lore2 = "PerLevel Bonus Damage" - lore = ["x Damage at 0 armor", "PerLevel Bonus Damage"] +name = "Glass Cannon" +description = "Bonus Unarmed Damage the lower your armor value is" +lore1 = "x Damage at 0 armor" +lore2 = "PerLevel Bonus Damage" +lore = ["x Damage at 0 armor", "PerLevel Bonus Damage"] [unarmed.power] - name = "Unarmed Power" - description = "Improved Unarmed Damage" - lore1 = "Damage" - lore = ["Damage"] +name = "Unarmed Power" +description = "Improved Unarmed Damage" +lore1 = "Damage" +lore = ["Damage"] [unarmed.sucker_punch] - name = "Sucker Punch" - description = "Sprint punches, but more deadly." - lore1 = "Damage" - lore2 = "Damage increases by with your speed while punching" - lore = ["Damage", "Damage increases by with your speed while punching"] +name = "Sucker Punch" +description = "Sprint punches, but more deadly." +lore1 = "Damage" +lore2 = "Damage increases by with your speed while punching" +lore = ["Damage", "Damage increases by with your speed while punching"] [unarmed.battering_charge] - name = "Battering Charge" - description = "Sprint into enemies with fists or a shield to deal impact damage." - lore1 = "Impact Damage Bonus" - lore2 = "Impact Knockback" - lore3 = "Charge Cooldown" - lore = ["Impact Damage Bonus", "Impact Knockback", "Charge Cooldown"] +name = "Battering Charge" +description = "Sprint into enemies with fists or a shield to deal impact damage." +lore1 = "Impact Damage Bonus" +lore2 = "Impact Knockback" +lore3 = "Charge Cooldown" +lore = ["Impact Damage Bonus", "Impact Knockback", "Charge Cooldown"] [unarmed.combo_chain] - name = "Combo Chain" - description = "Consecutive unarmed hits build combo stacks that increase punch damage." - lore1 = "Max Combo Stacks" - lore2 = "Damage Per Stack" - lore3 = "Combo Window" - lore = ["Max Combo Stacks", "Damage Per Stack", "Combo Window"] +name = "Combo Chain" +description = "Consecutive unarmed hits build combo stacks that increase punch damage." +lore1 = "Max Combo Stacks" +lore2 = "Damage Per Stack" +lore3 = "Combo Window" +lore = ["Max Combo Stacks", "Damage Per Stack", "Combo Window"] # ============================================================ # Adaptation-Level Achievements @@ -2612,1179 +2612,1179 @@ # --- AGILITY --- [advancement.challenge_agility_armor_up_30min] - title = "Iron Jogger" - description = "Sprint with armor bonus for 30 cumulative minutes" +title = "Iron Jogger" +description = "Sprint with armor bonus for 30 cumulative minutes" [advancement.challenge_agility_armor_up_5hr] - title = "Marathon Knight" - description = "Sprint with armor bonus for 5 cumulative hours" +title = "Marathon Knight" +description = "Sprint with armor bonus for 5 cumulative hours" [advancement.challenge_agility_ladder_500] - title = "Elevator Operator" - description = "Climb 500 ladder blocks" +title = "Elevator Operator" +description = "Climb 500 ladder blocks" [advancement.challenge_agility_ladder_10k] - title = "Vertical Expressway" - description = "Climb 10,000 ladder blocks" +title = "Vertical Expressway" +description = "Climb 10,000 ladder blocks" [advancement.challenge_agility_super_jump_100] - title = "Leap of Faith" - description = "Perform 100 super jumps" +title = "Leap of Faith" +description = "Perform 100 super jumps" [advancement.challenge_agility_super_jump_5k] - title = "Frog God" - description = "Perform 5,000 super jumps" +title = "Frog God" +description = "Perform 5,000 super jumps" [advancement.challenge_agility_wall_jump_500] - title = "Spider-Player" - description = "Perform 500 wall jumps" +title = "Spider-Player" +description = "Perform 500 wall jumps" [advancement.challenge_agility_parkour_master] - title = "Parkour Master" - description = "Chain 5+ wall jumps without touching the ground" +title = "Parkour Master" +description = "Chain 5+ wall jumps without touching the ground" [advancement.challenge_agility_wind_up_10min] - title = "Second Wind" - description = "Spend 10 cumulative minutes at max sprint speed" +title = "Second Wind" +description = "Spend 10 cumulative minutes at max sprint speed" [advancement.challenge_agility_wind_up_2hr] - title = "Sonic Boom" - description = "Spend 2 cumulative hours at max sprint speed" +title = "Sonic Boom" +description = "Spend 2 cumulative hours at max sprint speed" [advancement.challenge_agility_parkour_500] - title = "Freerunner" - description = "Land 500 ledge jumps" +title = "Freerunner" +description = "Land 500 ledge jumps" [advancement.challenge_agility_roll_100] - title = "Tuck and Roll" - description = "Prevent 100 hearts of fall damage" +title = "Tuck and Roll" +description = "Prevent 100 hearts of fall damage" [advancement.challenge_agility_roll_1000] - title = "Stuntman" - description = "Prevent 1,000 hearts of fall damage" +title = "Stuntman" +description = "Prevent 1,000 hearts of fall damage" [advancement.challenge_agility_fearless] - title = "Fearless" - description = "Roll-land from a 30+ block fall" +title = "Fearless" +description = "Roll-land from a 30+ block fall" # --- ARCHITECT --- [advancement.challenge_architect_elevator_100] - title = "Going Up" - description = "Use elevators 100 times" +title = "Going Up" +description = "Use elevators 100 times" [advancement.challenge_architect_elevator_penthouse] - title = "Penthouse Express" - description = "Take a single elevator trip of 50+ blocks" +title = "Penthouse Express" +description = "Take a single elevator trip of 50+ blocks" [advancement.challenge_architect_foundation_1k] - title = "Scaffold Master" - description = "Place 1,000 foundation blocks" +title = "Scaffold Master" +description = "Place 1,000 foundation blocks" [advancement.challenge_architect_foundation_10k] - title = "Walking on Air" - description = "Place 10,000 foundation blocks" +title = "Walking on Air" +description = "Place 10,000 foundation blocks" [advancement.challenge_architect_glass_200] - title = "Glazier" - description = "Recover 200 glass blocks" +title = "Glazier" +description = "Recover 200 glass blocks" [advancement.challenge_architect_glass_5k] - title = "Crystal Clear" - description = "Recover 5,000 glass blocks" +title = "Crystal Clear" +description = "Recover 5,000 glass blocks" [advancement.challenge_architect_wireless_100] - title = "Remote Control" - description = "Send 100 wireless redstone pulses" +title = "Remote Control" +description = "Send 100 wireless redstone pulses" [advancement.challenge_architect_wireless_5k] - title = "Nikola" - description = "Send 5,000 wireless redstone pulses" +title = "Nikola" +description = "Send 5,000 wireless redstone pulses" [advancement.challenge_architect_placement_1k] - title = "Speed Builder" - description = "Place 1,000 blocks via multi-placement" +title = "Speed Builder" +description = "Place 1,000 blocks via multi-placement" [advancement.challenge_architect_placement_25k] - title = "Architect's Touch" - description = "Place 25,000 blocks via multi-placement" +title = "Architect's Touch" +description = "Place 25,000 blocks via multi-placement" [advancement.challenge_architect_smart_shape_200] - title = "Fine Tuning" - description = "Rotate 200 blocks" +title = "Fine Tuning" +description = "Rotate 200 blocks" [advancement.challenge_architect_smart_shape_5k] - title = "Perfectionist" - description = "Rotate 5,000 blocks" +title = "Perfectionist" +description = "Rotate 5,000 blocks" # --- AXE --- [advancement.challenge_axe_chop_100] - title = "Lumberjack" - description = "Fell 100 trees" +title = "Lumberjack" +description = "Fell 100 trees" [advancement.challenge_axe_chop_2500] - title = "Deforestation" - description = "Fell 2,500 trees" +title = "Deforestation" +description = "Fell 2,500 trees" [advancement.challenge_axe_chop_one_swing] - title = "One Swing Wonder" - description = "Fell a tree yielding 30+ logs in one chop" +title = "One Swing Wonder" +description = "Fell a tree yielding 30+ logs in one chop" [advancement.challenge_axe_log_swap_500] - title = "Log Alchemist" - description = "Convert 500 logs" +title = "Log Alchemist" +description = "Convert 500 logs" [advancement.challenge_axe_dti_5k] - title = "Clean Logger" - description = "Catch 5,000 items to inventory" +title = "Clean Logger" +description = "Catch 5,000 items to inventory" [advancement.challenge_axe_ground_smash_500] - title = "Quake" - description = "Hit 500 enemies with ground smash" +title = "Quake" +description = "Hit 500 enemies with ground smash" [advancement.challenge_axe_ground_smash_5] - title = "Seismic Slam" - description = "Hit 5+ enemies in a single ground smash" +title = "Seismic Slam" +description = "Hit 5+ enemies in a single ground smash" [advancement.challenge_axe_leaf_5k] - title = "Autumn Breeze" - description = "Veinmine 5,000 leaves" +title = "Autumn Breeze" +description = "Veinmine 5,000 leaves" [advancement.challenge_axe_wood_vein_2500] - title = "Chain Reaction" - description = "Veinmine 2,500 logs" +title = "Chain Reaction" +description = "Veinmine 2,500 logs" [advancement.challenge_axe_wood_vein_cascade] - title = "Cascade Logger" - description = "Veinmine 15+ logs in a single activation" +title = "Cascade Logger" +description = "Veinmine 15+ logs in a single activation" [advancement.challenge_axe_timber_200] - title = "Mark of the Woodsman" - description = "Fell 200 marked trees" +title = "Mark of the Woodsman" +description = "Fell 200 marked trees" [advancement.challenge_axe_timber_40] - title = "Marked for Removal" - description = "Fell a marked tree yielding 40+ logs" +title = "Marked for Removal" +description = "Fell a marked tree yielding 40+ logs" # --- BLOCKING --- [advancement.challenge_blocking_chain_25] - title = "Medieval Smith" - description = "Craft 25 chainmail pieces" +title = "Medieval Smith" +description = "Craft 25 chainmail pieces" [advancement.challenge_blocking_horse_armor_10] - title = "Equine Outfitter" - description = "Craft 10 horse armors" +title = "Equine Outfitter" +description = "Craft 10 horse armors" [advancement.challenge_blocking_saddle_25] - title = "Saddler" - description = "Craft 25 saddles" +title = "Saddler" +description = "Craft 25 saddles" [advancement.challenge_blocking_multi_200] - title = "Hybrid Warrior" - description = "Perform 200 elytra-armor auto-swaps" +title = "Hybrid Warrior" +description = "Perform 200 elytra-armor auto-swaps" [advancement.challenge_blocking_multi_5k] - title = "Ironbird" - description = "Perform 5,000 elytra-armor auto-swaps" +title = "Ironbird" +description = "Perform 5,000 elytra-armor auto-swaps" [advancement.challenge_blocking_counter_500] - title = "Payback" - description = "Reflect 500 total damage via counter guard" +title = "Payback" +description = "Reflect 500 total damage via counter guard" [advancement.challenge_blocking_counter_max] - title = "Full Counter" - description = "Reach maximum counter stacks and release" +title = "Full Counter" +description = "Reach maximum counter stacks and release" [advancement.challenge_blocking_bastion_500] - title = "Immovable Object" - description = "Soften 500 projectiles while bracing" +title = "Immovable Object" +description = "Soften 500 projectiles while bracing" [advancement.challenge_blocking_bastion_10] - title = "Arrow Sponge" - description = "Soften 10 projectiles in a single bracing session" +title = "Arrow Sponge" +description = "Soften 10 projectiles in a single bracing session" [advancement.challenge_blocking_mirror_100] - title = "Return to Sender" - description = "Reflect 100 projectiles" +title = "Return to Sender" +description = "Reflect 100 projectiles" [advancement.challenge_blocking_mirror_3in5] - title = "Bullet Time" - description = "Reflect 3 projectiles within 5 seconds" +title = "Bullet Time" +description = "Reflect 3 projectiles within 5 seconds" [advancement.challenge_blocking_bulwark_500] - title = "Shield Charge" - description = "Bash 500 enemies" +title = "Shield Charge" +description = "Bash 500 enemies" [advancement.challenge_blocking_bulwark_4] - title = "Bowling Strike" - description = "Hit 4+ enemies in a single bash" +title = "Bowling Strike" +description = "Hit 4+ enemies in a single bash" # --- BREWING --- [advancement.challenge_brewing_lingering_200] - title = "Slow Sipper" - description = "Benefit from 200 extended potions" +title = "Slow Sipper" +description = "Benefit from 200 extended potions" [advancement.challenge_brewing_lingering_5k] - title = "Everlasting Elixir" - description = "Benefit from 5,000 extended potions" +title = "Everlasting Elixir" +description = "Benefit from 5,000 extended potions" [advancement.challenge_brewing_super_heated_100] - title = "Volcanic Brewer" - description = "Complete 100 superheated brews" +title = "Volcanic Brewer" +description = "Complete 100 superheated brews" [advancement.challenge_brewing_super_heated_2500] - title = "Hell's Kitchen" - description = "Complete 2,500 superheated brews" +title = "Hell's Kitchen" +description = "Complete 2,500 superheated brews" [advancement.challenge_brewing_darkness_25] - title = "Lights Out" - description = "Brew 25 darkness potions" +title = "Lights Out" +description = "Brew 25 darkness potions" [advancement.challenge_brewing_haste_25] - title = "Hyperactive" - description = "Brew 25 haste potions" +title = "Hyperactive" +description = "Brew 25 haste potions" [advancement.challenge_brewing_absorption_25] - title = "Extra Padding" - description = "Brew 25 absorption potions" +title = "Extra Padding" +description = "Brew 25 absorption potions" [advancement.challenge_brewing_fatigue_25] - title = "Exhaustion Expert" - description = "Brew 25 fatigue potions" +title = "Exhaustion Expert" +description = "Brew 25 fatigue potions" [advancement.challenge_brewing_hunger_25] - title = "Famished" - description = "Brew 25 hunger potions" +title = "Famished" +description = "Brew 25 hunger potions" [advancement.challenge_brewing_nausea_25] - title = "Dizzy Concoction" - description = "Brew 25 nausea potions" +title = "Dizzy Concoction" +description = "Brew 25 nausea potions" [advancement.challenge_brewing_blindness_25] - title = "Lights Off" - description = "Brew 25 blindness potions" +title = "Lights Off" +description = "Brew 25 blindness potions" [advancement.challenge_brewing_resistance_25] - title = "Ironbrew" - description = "Brew 25 resistance potions" +title = "Ironbrew" +description = "Brew 25 resistance potions" [advancement.challenge_brewing_health_boost_25] - title = "Vitality Vial" - description = "Brew 25 health boost potions" +title = "Vitality Vial" +description = "Brew 25 health boost potions" [advancement.challenge_brewing_decay_25] - title = "Necrotoxin" - description = "Brew 25 decay potions" +title = "Necrotoxin" +description = "Brew 25 decay potions" [advancement.challenge_brewing_saturation_25] - title = "Feast in a Bottle" - description = "Brew 25 saturation potions" +title = "Feast in a Bottle" +description = "Brew 25 saturation potions" # --- CHRONOS --- [advancement.challenge_chronos_bottle_1k] - title = "Chronomancer" - description = "Spend 1,000 time charges" +title = "Chronomancer" +description = "Spend 1,000 time charges" [advancement.challenge_chronos_bottle_25k] - title = "Time Lord" - description = "Spend 25,000 time charges" +title = "Time Lord" +description = "Spend 25,000 time charges" [advancement.challenge_chronos_aberrant_500] - title = "Temporal Drain" - description = "Apply 500 slowness stacks" +title = "Temporal Drain" +description = "Apply 500 slowness stacks" [advancement.challenge_chronos_aberrant_frozen] - title = "Frozen in Time" - description = "Apply 5+ slowness stacks to a single target" +title = "Frozen in Time" +description = "Apply 5+ slowness stacks to a single target" [advancement.challenge_chronos_recall_50] - title = "Second Chance" - description = "Recall 50 times" +title = "Second Chance" +description = "Recall 50 times" [advancement.challenge_chronos_recall_1k] - title = "Groundhog Day" - description = "Recall 1,000 times" +title = "Groundhog Day" +description = "Recall 1,000 times" [advancement.challenge_chronos_recall_cheat_death] - title = "Cheating Death" - description = "Recall when below 2 hearts, restoring to above 8" +title = "Cheating Death" +description = "Recall when below 2 hearts, restoring to above 8" [advancement.challenge_chronos_bomb_freeze_50] - title = "Time Stop" - description = "Freeze 50 projectiles" +title = "Time Stop" +description = "Freeze 50 projectiles" [advancement.challenge_chronos_bomb_crowd_8] - title = "Crowd Control" - description = "Slow 8+ entities with one time bomb" +title = "Crowd Control" +description = "Slow 8+ entities with one time bomb" [advancement.challenge_chronos_echo_200] - title = "Double Tap" - description = "Land 200 echo hits" +title = "Double Tap" +description = "Land 200 echo hits" # --- CRAFTING --- [advancement.challenge_crafting_decon_200] - title = "Salvager" - description = "Deconstruct 200 items" +title = "Salvager" +description = "Deconstruct 200 items" [advancement.challenge_crafting_decon_5k] - title = "Recycling Plant" - description = "Deconstruct 5,000 items" +title = "Recycling Plant" +description = "Deconstruct 5,000 items" [advancement.challenge_crafting_xp_1k] - title = "Artisan" - description = "Craft 1,000 items" +title = "Artisan" +description = "Craft 1,000 items" [advancement.challenge_crafting_xp_25k] - title = "Master Crafter" - description = "Craft 25,000 items" +title = "Master Crafter" +description = "Craft 25,000 items" [advancement.challenge_crafting_recon_100] - title = "Reverse Engineering" - description = "Reconstruct 100 ores" +title = "Reverse Engineering" +description = "Reconstruct 100 ores" [advancement.challenge_crafting_leather_100] - title = "Tanner" - description = "Craft 100 leather from rotten flesh" +title = "Tanner" +description = "Craft 100 leather from rotten flesh" [advancement.challenge_crafting_backpack_25] - title = "Pack Rat" - description = "Craft 25 bundles" +title = "Pack Rat" +description = "Craft 25 bundles" [advancement.challenge_crafting_stations_200] - title = "On the Go" - description = "Use portable stations 200 times" +title = "On the Go" +description = "Use portable stations 200 times" [advancement.challenge_crafting_stations_5k] - title = "Mobile Workshop" - description = "Use portable stations 5,000 times" +title = "Mobile Workshop" +description = "Use portable stations 5,000 times" [advancement.challenge_crafting_skulls_10] - title = "Skull Collector" - description = "Craft 10 mob skulls" +title = "Skull Collector" +description = "Craft 10 mob skulls" [advancement.challenge_crafting_skulls_100] - title = "Headhunter" - description = "Craft 100 mob skulls" +title = "Headhunter" +description = "Craft 100 mob skulls" # --- DISCOVERY --- [advancement.challenge_discovery_unity_5k] - title = "Renaissance Player" - description = "Distribute 5,000 XP orbs to random skills" +title = "Renaissance Player" +description = "Distribute 5,000 XP orbs to random skills" [advancement.challenge_discovery_unity_50k] - title = "Jack of All Trades" - description = "Distribute 50,000 XP orbs" +title = "Jack of All Trades" +description = "Distribute 50,000 XP orbs" [advancement.challenge_discovery_xp_resist_25] - title = "Emergency Reserves" - description = "Mitigate lethal damage 25 times using XP" +title = "Emergency Reserves" +description = "Mitigate lethal damage 25 times using XP" [advancement.challenge_discovery_xp_resist_250] - title = "XP Shield" - description = "Mitigate lethal damage 250 times" +title = "XP Shield" +description = "Mitigate lethal damage 250 times" [advancement.challenge_discovery_xp_resist_clutch] - title = "Clutch" - description = "Survive a hit that would have dealt 15+ hearts" +title = "Clutch" +description = "Survive a hit that would have dealt 15+ hearts" [advancement.challenge_discovery_villager_100] - title = "Silver Tongue" - description = "Complete 100 improved villager trades" +title = "Silver Tongue" +description = "Complete 100 improved villager trades" [advancement.challenge_discovery_villager_2500] - title = "Master Negotiator" - description = "Complete 2,500 improved trades" +title = "Master Negotiator" +description = "Complete 2,500 improved trades" [advancement.challenge_discovery_armor_1hr] - title = "One with the Earth" - description = "Have environmental armor for 1 cumulative hour" +title = "One with the Earth" +description = "Have environmental armor for 1 cumulative hour" [advancement.challenge_discovery_armor_24hr] - title = "Stone Skin" - description = "Have environmental armor for 24 cumulative hours" +title = "Stone Skin" +description = "Have environmental armor for 24 cumulative hours" [advancement.challenge_discovery_mending_10k] - title = "Master Mender" - description = "Restore 10,000 durability via direct mending" +title = "Master Mender" +description = "Restore 10,000 durability via direct mending" [advancement.challenge_discovery_mending_100k] - title = "Good as New" - description = "Restore 100,000 durability" +title = "Good as New" +description = "Restore 100,000 durability" [advancement.challenge_discovery_archaeologist_50] - title = "Relic Hunter" - description = "Find 50 bonus archaeology items" +title = "Relic Hunter" +description = "Find 50 bonus archaeology items" [advancement.challenge_discovery_archaeologist_500] - title = "Indiana Jones" - description = "Find 500 bonus items" +title = "Indiana Jones" +description = "Find 500 bonus items" [advancement.challenge_discovery_cartographer_100] - title = "Wayfinder" - description = "Pulse toward 100 structures" +title = "Wayfinder" +description = "Pulse toward 100 structures" [advancement.challenge_discovery_cartographer_1k] - title = "Explorer Extraordinaire" - description = "Pulse toward 1,000 structures" +title = "Explorer Extraordinaire" +description = "Pulse toward 1,000 structures" # --- ENCHANTING --- [advancement.challenge_enchanting_lapis_100] - title = "Penny Pincher" - description = "Save 100 lapis from enchanting" +title = "Penny Pincher" +description = "Save 100 lapis from enchanting" [advancement.challenge_enchanting_lapis_2500] - title = "Blue Miser" - description = "Save 2,500 lapis" +title = "Blue Miser" +description = "Save 2,500 lapis" [advancement.challenge_enchanting_quick_100] - title = "Bookworm" - description = "Apply 100 books directly" +title = "Bookworm" +description = "Apply 100 books directly" [advancement.challenge_enchanting_quick_1k] - title = "Walking Enchant Table" - description = "Apply 1,000 books directly" +title = "Walking Enchant Table" +description = "Apply 1,000 books directly" [advancement.challenge_enchanting_xp_100] - title = "Thrifty Enchanter" - description = "Save 100 XP levels from enchanting" +title = "Thrifty Enchanter" +description = "Save 100 XP levels from enchanting" [advancement.challenge_enchanting_anvil_200] - title = "Anvil Whisperer" - description = "Save 200 levels at anvil" +title = "Anvil Whisperer" +description = "Save 200 levels at anvil" [advancement.challenge_enchanting_anvil_5k] - title = "Blacksmith Prodigy" - description = "Save 5,000 levels at anvil" +title = "Blacksmith Prodigy" +description = "Save 5,000 levels at anvil" [advancement.challenge_enchanting_reroll_100] - title = "Picky Enchanter" - description = "Reroll enchantment offers 100 times" +title = "Picky Enchanter" +description = "Reroll enchantment offers 100 times" [advancement.challenge_enchanting_reroll_1k] - title = "Never Satisfied" - description = "Reroll enchantment offers 1,000 times" +title = "Never Satisfied" +description = "Reroll enchantment offers 1,000 times" [advancement.challenge_enchanting_bookshelf_100] - title = "Library Card" - description = "Enchant 100 items with virtual bookshelf bonus" +title = "Library Card" +description = "Enchant 100 items with virtual bookshelf bonus" [advancement.challenge_enchanting_grindstone_50] - title = "Salvage Specialist" - description = "Recover 50 enchantments at the grindstone" +title = "Salvage Specialist" +description = "Recover 50 enchantments at the grindstone" [advancement.challenge_enchanting_grindstone_500] - title = "Nothing Wasted" - description = "Recover 500 enchantments" +title = "Nothing Wasted" +description = "Recover 500 enchantments" # --- EXCAVATION --- [advancement.challenge_excavation_haste_5k] - title = "Dig Faster" - description = "Break 5,000 blocks while hasted" +title = "Dig Faster" +description = "Break 5,000 blocks while hasted" [advancement.challenge_excavation_haste_50k] - title = "Tunneling Machine" - description = "Break 50,000 blocks while hasted" +title = "Tunneling Machine" +description = "Break 50,000 blocks while hasted" [advancement.challenge_excavation_spelunker_1k] - title = "X-Ray Vision" - description = "Reveal 1,000 ores" +title = "X-Ray Vision" +description = "Reveal 1,000 ores" [advancement.challenge_excavation_spelunker_25k] - title = "All-Seeing Eye" - description = "Reveal 25,000 ores" +title = "All-Seeing Eye" +description = "Reveal 25,000 ores" [advancement.challenge_excavation_dti_10k] - title = "Clean Digger" - description = "Catch 10,000 items to inventory" +title = "Clean Digger" +description = "Catch 10,000 items to inventory" [advancement.challenge_excavation_omni_1k] - title = "Swiss Army Miner" - description = "Perform 1,000 auto-tool-swaps" +title = "Swiss Army Miner" +description = "Perform 1,000 auto-tool-swaps" [advancement.challenge_excavation_omni_25k] - title = "Tool Belt Master" - description = "Perform 25,000 auto-tool-swaps" +title = "Tool Belt Master" +description = "Perform 25,000 auto-tool-swaps" [advancement.challenge_excavation_seismic_200] - title = "Echolocation" - description = "Trigger 200 seismic pings" +title = "Echolocation" +description = "Trigger 200 seismic pings" # --- HERBALISM --- [advancement.challenge_herbalism_growth_1k] - title = "Green Thumb" - description = "Grow 1,000 blocks via growth aura" +title = "Green Thumb" +description = "Grow 1,000 blocks via growth aura" [advancement.challenge_herbalism_growth_25k] - title = "Nature's Blessing" - description = "Grow 25,000 blocks" +title = "Nature's Blessing" +description = "Grow 25,000 blocks" [advancement.challenge_herbalism_hippo_500] - title = "Glutton" - description = "Gain 500 bonus saturation" +title = "Glutton" +description = "Gain 500 bonus saturation" [advancement.challenge_herbalism_myconid_100] - title = "Fungus Among Us" - description = "Craft 100 mycelium" +title = "Fungus Among Us" +description = "Craft 100 mycelium" [advancement.challenge_herbalism_terralid_200] - title = "Landscaper" - description = "Craft 200 grass blocks" +title = "Landscaper" +description = "Craft 200 grass blocks" [advancement.challenge_herbalism_cobweb_100] - title = "Silk Weaver" - description = "Craft 100 cobwebs" +title = "Silk Weaver" +description = "Craft 100 cobwebs" [advancement.challenge_herbalism_mushroom_100] - title = "Mooshroom Builder" - description = "Craft 100 mushroom blocks" +title = "Mooshroom Builder" +description = "Craft 100 mushroom blocks" [advancement.challenge_herbalism_dti_10k] - title = "Clean Harvest" - description = "Catch 10,000 items to inventory" +title = "Clean Harvest" +description = "Catch 10,000 items to inventory" [advancement.challenge_herbalism_shield_500] - title = "Thick Skin" - description = "Absorb 500 damage via hunger" +title = "Thick Skin" +description = "Absorb 500 damage via hunger" [advancement.challenge_herbalism_shield_5k] - title = "Insatiable Tank" - description = "Absorb 5,000 damage via hunger" +title = "Insatiable Tank" +description = "Absorb 5,000 damage via hunger" [advancement.challenge_herbalism_luck_100] - title = "Lucky Find" - description = "Get 100 lucky drops" +title = "Lucky Find" +description = "Get 100 lucky drops" [advancement.challenge_herbalism_luck_2500] - title = "Four Leaf Clover" - description = "Get 2,500 lucky drops" +title = "Four Leaf Clover" +description = "Get 2,500 lucky drops" [advancement.challenge_herbalism_replant_500] - title = "Sustainable Farmer" - description = "Replant 500 crops" +title = "Sustainable Farmer" +description = "Replant 500 crops" [advancement.challenge_herbalism_replant_25k] - title = "Industrial Agriculture" - description = "Replant 25,000 crops" +title = "Industrial Agriculture" +description = "Replant 25,000 crops" [advancement.challenge_herbalism_seed_1k] - title = "Johnny Appleseed" - description = "Plant 1,000 seeds via AOE" +title = "Johnny Appleseed" +description = "Plant 1,000 seeds via AOE" [advancement.challenge_herbalism_seed_25k] - title = "Field of Dreams" - description = "Plant 25,000 seeds via AOE" +title = "Field of Dreams" +description = "Plant 25,000 seeds via AOE" [advancement.challenge_herbalism_rooted_500] - title = "Gentle Step" - description = "Protect 500 farmland blocks" +title = "Gentle Step" +description = "Protect 500 farmland blocks" [advancement.challenge_herbalism_compost_1k] - title = "Composting Champion" - description = "Compost 1,000 items" +title = "Composting Champion" +description = "Compost 1,000 items" [advancement.challenge_herbalism_compost_25k] - title = "Zero Waste" - description = "Compost 25,000 items" +title = "Zero Waste" +description = "Compost 25,000 items" [advancement.challenge_herbalism_bee_100] - title = "Queen Bee" - description = "Attract 100 bees" +title = "Queen Bee" +description = "Attract 100 bees" [advancement.challenge_herbalism_spore_500] - title = "Spore Carrier" - description = "Spread 500 blocks via spore bloom" +title = "Spore Carrier" +description = "Spread 500 blocks via spore bloom" # --- HUNTER --- [advancement.challenge_hunter_adrenaline_100] - title = "Cornered Beast" - description = "Kill 100 mobs below 30% health" +title = "Cornered Beast" +description = "Kill 100 mobs below 30% health" [advancement.challenge_hunter_adrenaline_2500] - title = "Berserker" - description = "Kill 2,500 mobs in adrenaline" +title = "Berserker" +description = "Kill 2,500 mobs in adrenaline" [advancement.challenge_hunter_dti_10k] - title = "Clean Kill" - description = "Catch 10,000 items to inventory" +title = "Clean Kill" +description = "Catch 10,000 items to inventory" [advancement.challenge_hunter_invis_200] - title = "Ghost Protocol" - description = "Trigger invisibility 200 times from kills" +title = "Ghost Protocol" +description = "Trigger invisibility 200 times from kills" [advancement.challenge_hunter_jump_200] - title = "Spring Loaded" - description = "Trigger jump boost 200 times from kills" +title = "Spring Loaded" +description = "Trigger jump boost 200 times from kills" [advancement.challenge_hunter_luck_200] - title = "Lucky Shot" - description = "Trigger luck 200 times from kills" +title = "Lucky Shot" +description = "Trigger luck 200 times from kills" [advancement.challenge_hunter_regen_500] - title = "Wolverine" - description = "Regenerate 500 hearts from kill regen" +title = "Wolverine" +description = "Regenerate 500 hearts from kill regen" [advancement.challenge_hunter_resistance_500] - title = "Thick Hide" - description = "Trigger resistance 500 times from kills" +title = "Thick Hide" +description = "Trigger resistance 500 times from kills" [advancement.challenge_hunter_speed_200] - title = "Fight or Flight" - description = "Trigger speed 200 times from kills" +title = "Fight or Flight" +description = "Trigger speed 200 times from kills" [advancement.challenge_hunter_strength_200] - title = "Rage" - description = "Trigger strength 200 times from kills" +title = "Rage" +description = "Trigger strength 200 times from kills" [advancement.challenge_hunter_trophy_50] - title = "Taxidermist" - description = "Collect 50 trophy drops" +title = "Taxidermist" +description = "Collect 50 trophy drops" [advancement.challenge_hunter_trophy_heads_100] - title = "Trophy Room" - description = "Collect 100 mob heads" +title = "Trophy Room" +description = "Collect 100 mob heads" # --- NETHER --- [advancement.challenge_nether_wither_100] - title = "Wither-Proof" - description = "Negate 100 wither effects" +title = "Wither-Proof" +description = "Negate 100 wither effects" [advancement.challenge_nether_wither_1k] - title = "Death Defiant" - description = "Negate 1,000 wither effects" +title = "Death Defiant" +description = "Negate 1,000 wither effects" [advancement.challenge_nether_fire_200] - title = "Firewalker" - description = "Negate 200 fire damage instances" +title = "Firewalker" +description = "Negate 200 fire damage instances" [advancement.challenge_nether_fire_5k] - title = "Salamander" - description = "Negate 5,000 fire damage instances" +title = "Salamander" +description = "Negate 5,000 fire damage instances" [advancement.challenge_nether_skull_100] - title = "Skull Chucker" - description = "Throw 100 wither skulls" +title = "Skull Chucker" +description = "Throw 100 wither skulls" [advancement.challenge_nether_skull_kills_50] - title = "Wither Lord" - description = "Kill 50 mobs with wither skulls" +title = "Wither Lord" +description = "Kill 50 mobs with wither skulls" [advancement.challenge_nether_skull_long_bomb] - title = "Long Bomb" - description = "Hit a target 40+ blocks away with a skull" +title = "Long Bomb" +description = "Hit a target 40+ blocks away with a skull" [advancement.challenge_nether_lava_1k] - title = "Strider" - description = "Walk 1,000 blocks on lava" +title = "Strider" +description = "Walk 1,000 blocks on lava" [advancement.challenge_nether_lava_25k] - title = "Lava Surfer" - description = "Walk 25,000 blocks on lava" +title = "Lava Surfer" +description = "Walk 25,000 blocks on lava" [advancement.challenge_nether_ghast_500] - title = "Nether Veteran" - description = "Mitigate 500 nether mob damage" +title = "Nether Veteran" +description = "Mitigate 500 nether mob damage" [advancement.challenge_nether_blaze_200] - title = "Pyrophyte" - description = "Gain 200 hearts from fire damage" +title = "Pyrophyte" +description = "Gain 200 hearts from fire damage" [advancement.challenge_nether_blaze_2500] - title = "Flame Eater" - description = "Gain 2,500 hearts from fire damage" +title = "Flame Eater" +description = "Gain 2,500 hearts from fire damage" [advancement.challenge_nether_piglin_100] - title = "Gold Broker" - description = "Complete 100 improved piglin barters" +title = "Gold Broker" +description = "Complete 100 improved piglin barters" [advancement.challenge_nether_piglin_2500] - title = "Piglin Mogul" - description = "Complete 2,500 improved barters" +title = "Piglin Mogul" +description = "Complete 2,500 improved barters" # --- PICKAXE --- [advancement.challenge_pickaxe_autosmelt_1k] - title = "Walking Furnace" - description = "Auto-smelt 1,000 ores" +title = "Walking Furnace" +description = "Auto-smelt 1,000 ores" [advancement.challenge_pickaxe_autosmelt_25k] - title = "Smelter Supreme" - description = "Auto-smelt 25,000 ores" +title = "Smelter Supreme" +description = "Auto-smelt 25,000 ores" [advancement.challenge_pickaxe_chisel_500] - title = "Precision Miner" - description = "Chisel 500 extra ore drops" +title = "Precision Miner" +description = "Chisel 500 extra ore drops" [advancement.challenge_pickaxe_dti_25k] - title = "Clean Miner" - description = "Catch 25,000 items to inventory" +title = "Clean Miner" +description = "Catch 25,000 items to inventory" [advancement.challenge_pickaxe_spawner_10] - title = "Spawner Thief" - description = "Collect 10 spawners with silk touch" +title = "Spawner Thief" +description = "Collect 10 spawners with silk touch" [advancement.challenge_pickaxe_spawner_50] - title = "Zoo Keeper" - description = "Collect 50 spawners" +title = "Zoo Keeper" +description = "Collect 50 spawners" [advancement.challenge_pickaxe_veinminer_2500] - title = "Vein Ripper" - description = "Veinmine 2,500 ores" +title = "Vein Ripper" +description = "Veinmine 2,500 ores" [advancement.challenge_pickaxe_veinminer_20] - title = "Mother Lode" - description = "Veinmine 20+ ores in a single go" +title = "Mother Lode" +description = "Veinmine 20+ ores in a single go" [advancement.challenge_pickaxe_quarry_200] - title = "Prospector" - description = "Perform 200 ore scans" +title = "Prospector" +description = "Perform 200 ore scans" # --- RANGED --- [advancement.challenge_ranged_arrow_500] - title = "Frugal Fletcher" - description = "Recover 500 arrows" +title = "Frugal Fletcher" +description = "Recover 500 arrows" [advancement.challenge_ranged_arrow_10k] - title = "Infinite Quiver" - description = "Recover 10,000 arrows" +title = "Infinite Quiver" +description = "Recover 10,000 arrows" [advancement.challenge_ranged_web_200] - title = "Web Slinger" - description = "Trap 200 mobs with web bombs" +title = "Web Slinger" +description = "Trap 200 mobs with web bombs" [advancement.challenge_ranged_force_500] - title = "Sniper" - description = "Land 500 long-range hits (30+ blocks)" +title = "Sniper" +description = "Land 500 long-range hits (30+ blocks)" [advancement.challenge_ranged_lunge_200] - title = "Air Support" - description = "Perform 200 aerial lunges" +title = "Air Support" +description = "Perform 200 aerial lunges" [advancement.challenge_ranged_lunge_2500] - title = "Rocket Jump" - description = "Perform 2,500 aerial lunges" +title = "Rocket Jump" +description = "Perform 2,500 aerial lunges" [advancement.challenge_ranged_piercing_500] - title = "Shish Kebab" - description = "Pierce 500 extra targets" +title = "Shish Kebab" +description = "Pierce 500 extra targets" [advancement.challenge_ranged_piercing_4] - title = "Collateral" - description = "Hit 4+ enemies with a single arrow" +title = "Collateral" +description = "Hit 4+ enemies with a single arrow" [advancement.challenge_ranged_floaters_200] - title = "Up You Go" - description = "Levitate 200 targets" +title = "Up You Go" +description = "Levitate 200 targets" [advancement.challenge_ranged_pinning_300] - title = "Nailed Down" - description = "Pin 300 targets" +title = "Nailed Down" +description = "Pin 300 targets" [advancement.challenge_ranged_trajectory_100] - title = "Calculated" - description = "Kill 100 mobs while trajectory preview is active" +title = "Calculated" +description = "Kill 100 mobs while trajectory preview is active" [advancement.challenge_ranged_ricochet_kills_50] - title = "Trick Shot" - description = "Kill 50 mobs with ricocheted projectiles" +title = "Trick Shot" +description = "Kill 50 mobs with ricocheted projectiles" [advancement.challenge_ranged_ricochet_kills_500] - title = "Pinball Wizard" - description = "Kill 500 mobs with ricocheted projectiles" +title = "Pinball Wizard" +description = "Kill 500 mobs with ricocheted projectiles" # --- RIFT --- [advancement.challenge_rift_blink_500] - title = "Phase Shift" - description = "Blink 500 times" +title = "Phase Shift" +description = "Blink 500 times" [advancement.challenge_rift_blink_5k] - title = "Rift Walker" - description = "Blink 5,000 total blocks of distance" +title = "Rift Walker" +description = "Blink 5,000 total blocks of distance" [advancement.challenge_rift_enderchest_200] - title = "Pocket Dimension" - description = "Open your ender chest remotely 200 times" +title = "Pocket Dimension" +description = "Open your ender chest remotely 200 times" [advancement.challenge_rift_descent_100] - title = "Grounded" - description = "Cancel 100 levitation effects" +title = "Grounded" +description = "Cancel 100 levitation effects" [advancement.challenge_rift_descent_1k] - title = "Anti-Gravity" - description = "Cancel 1,000 levitation effects" +title = "Anti-Gravity" +description = "Cancel 1,000 levitation effects" [advancement.challenge_rift_gate_100] - title = "Gatekeeper" - description = "Teleport via rift gates 100 times" +title = "Gatekeeper" +description = "Teleport via rift gates 100 times" [advancement.challenge_rift_gate_50k_dist] - title = "Rift Network" - description = "Teleport 50,000 cumulative blocks via rift gates" +title = "Rift Network" +description = "Teleport 50,000 cumulative blocks via rift gates" [advancement.challenge_rift_resist_200] - title = "Void-Touched" - description = "Trigger rift resistance 200 times" +title = "Void-Touched" +description = "Trigger rift resistance 200 times" [advancement.challenge_rift_visage_100] - title = "Eye Contact" - description = "Survive 100 enderman stares" +title = "Eye Contact" +description = "Survive 100 enderman stares" [advancement.challenge_rift_visage_1k] - title = "Enderman Whisperer" - description = "Survive 1,000 enderman stares" +title = "Enderman Whisperer" +description = "Survive 1,000 enderman stares" [advancement.challenge_rift_access_100] - title = "Dimensional Pocket" - description = "Open containers remotely 100 times" +title = "Dimensional Pocket" +description = "Open containers remotely 100 times" [advancement.challenge_rift_access_2500] - title = "Astral Reach" - description = "Open containers remotely 2,500 times" +title = "Astral Reach" +description = "Open containers remotely 2,500 times" [advancement.challenge_rift_void_magnet_5k] - title = "Vacuum Cleaner" - description = "Pull 5,000 items from range" +title = "Vacuum Cleaner" +description = "Pull 5,000 items from range" [advancement.challenge_rift_void_magnet_50k] - title = "Black Hole" - description = "Pull 50,000 items from range" +title = "Black Hole" +description = "Pull 50,000 items from range" [advancement.challenge_rift_taglock_100] - title = "Voodoo Doll" - description = "Tag 100 entities into ender pearls" +title = "Voodoo Doll" +description = "Tag 100 entities into ender pearls" [advancement.challenge_rift_taglock_500] - title = "Puppet Master" - description = "Successfully taglock-teleport 500 entities" +title = "Puppet Master" +description = "Successfully taglock-teleport 500 entities" [advancement.challenge_rift_pocket_5k] - title = "Bottomless Pockets" - description = "Pull 5,000 items from ender chest during building" +title = "Bottomless Pockets" +description = "Pull 5,000 items from ender chest during building" [advancement.challenge_rift_pocket_store_10k] - title = "Ender Hoarder" - description = "Store 10,000 items into ender chest via sneak-drop" +title = "Ender Hoarder" +description = "Store 10,000 items into ender chest via sneak-drop" # --- SEABORNE --- [advancement.challenge_seaborne_fish_500] - title = "Master Angler" - description = "Catch 500 bonus fish" +title = "Master Angler" +description = "Catch 500 bonus fish" [advancement.challenge_seaborne_fish_5k] - title = "Old Man and the Sea" - description = "Catch 5,000 bonus fish" +title = "Old Man and the Sea" +description = "Catch 5,000 bonus fish" [advancement.challenge_seaborne_speed_10k] - title = "Dolphin" - description = "Swim 10,000 blocks" +title = "Dolphin" +description = "Swim 10,000 blocks" [advancement.challenge_seaborne_speed_100k] - title = "Aquaman" - description = "Swim 100,000 blocks" +title = "Aquaman" +description = "Swim 100,000 blocks" [advancement.challenge_seaborne_vision_72k] - title = "Deep Sea Explorer" - description = "Spend 1 cumulative hour underwater with vision" +title = "Deep Sea Explorer" +description = "Spend 1 cumulative hour underwater with vision" [advancement.challenge_seaborne_mining_2500] - title = "Submarine Miner" - description = "Mine 2,500 blocks underwater" +title = "Submarine Miner" +description = "Mine 2,500 blocks underwater" [advancement.challenge_seaborne_mining_25k] - title = "Atlantean Engineer" - description = "Mine 25,000 blocks underwater" +title = "Atlantean Engineer" +description = "Mine 25,000 blocks underwater" [advancement.challenge_seaborne_oxygen_12k] - title = "Iron Lungs" - description = "Gain 10 cumulative minutes of bonus air" +title = "Iron Lungs" +description = "Gain 10 cumulative minutes of bonus air" [advancement.challenge_seaborne_tidecaller_200] - title = "Tidal Force" - description = "Perform 200 tide dashes" +title = "Tidal Force" +description = "Perform 200 tide dashes" [advancement.challenge_seaborne_tidecaller_5k] - title = "Poseidon's Sprint" - description = "Perform 5,000 tide dashes" +title = "Poseidon's Sprint" +description = "Perform 5,000 tide dashes" [advancement.challenge_seaborne_pressure_1k] - title = "Deep Diver" - description = "Mine 1,000 blocks below sea level" +title = "Deep Diver" +description = "Mine 1,000 blocks below sea level" # --- STEALTH --- [advancement.challenge_stealth_ghost_100] - title = "Phantom Shield" - description = "Build max ghost armor 100 times" +title = "Phantom Shield" +description = "Build max ghost armor 100 times" [advancement.challenge_stealth_ghost_500] - title = "Glass Cannon Reversed" - description = "Absorb 500 hits with ghost armor" +title = "Glass Cannon Reversed" +description = "Absorb 500 hits with ghost armor" [advancement.challenge_stealth_sight_72k] - title = "Creature of the Night" - description = "Spend 1 hour sneaking with night vision" +title = "Creature of the Night" +description = "Spend 1 hour sneaking with night vision" [advancement.challenge_stealth_snatch_2500] - title = "Sticky Fingers" - description = "Snatch 2,500 items from mobs" +title = "Sticky Fingers" +description = "Snatch 2,500 items from mobs" [advancement.challenge_stealth_snatch_25k] - title = "Pickpocket" - description = "Snatch 25,000 items from mobs" +title = "Pickpocket" +description = "Snatch 25,000 items from mobs" [advancement.challenge_stealth_speed_5k] - title = "Swift Shadow" - description = "Traverse 5,000 blocks while speed-sneaking" +title = "Swift Shadow" +description = "Traverse 5,000 blocks while speed-sneaking" [advancement.challenge_stealth_ender_veil_200] - title = "Invisible to the Void" - description = "Avoid 200 enderman aggro triggers" +title = "Invisible to the Void" +description = "Avoid 200 enderman aggro triggers" [advancement.challenge_stealth_silent_200] - title = "Assassin" - description = "Land 200 backstabs" +title = "Assassin" +description = "Land 200 backstabs" [advancement.challenge_stealth_silent_5in10] - title = "Unseen Blade" - description = "Land 5 backstabs within 10 seconds" +title = "Unseen Blade" +description = "Land 5 backstabs within 10 seconds" [advancement.challenge_stealth_decoy_100] - title = "Body Double" - description = "Spawn 100 shadow decoys" +title = "Body Double" +description = "Spawn 100 shadow decoys" [advancement.challenge_stealth_decoy_distract_500] - title = "Master of Misdirection" - description = "Distract 500 mobs with decoys" +title = "Master of Misdirection" +description = "Distract 500 mobs with decoys" # --- SWORDS --- [advancement.challenge_swords_machete_2500] - title = "Jungle Explorer" - description = "Cut 2,500 foliage blocks" +title = "Jungle Explorer" +description = "Cut 2,500 foliage blocks" [advancement.challenge_swords_machete_25k] - title = "Trailblazer" - description = "Cut 25,000 foliage blocks" +title = "Trailblazer" +description = "Cut 25,000 foliage blocks" [advancement.challenge_swords_bloody_500] - title = "Bloodletter" - description = "Deal 500 hearts of bleed damage" +title = "Bloodletter" +description = "Deal 500 hearts of bleed damage" [advancement.challenge_swords_bloody_kills_100] - title = "Death by a Thousand Cuts" - description = "Kill 100 mobs with bleed alone" +title = "Death by a Thousand Cuts" +description = "Kill 100 mobs with bleed alone" [advancement.challenge_swords_poison_500] - title = "Venomous" - description = "Apply poison 500 times" +title = "Venomous" +description = "Apply poison 500 times" [advancement.challenge_swords_poison_kills_50] - title = "Toxic Blade" - description = "Kill 50 mobs with poison" +title = "Toxic Blade" +description = "Kill 50 mobs with poison" [advancement.challenge_swords_execute_200] - title = "Executioner" - description = "Execute 200 low-health mobs" +title = "Executioner" +description = "Execute 200 low-health mobs" [advancement.challenge_swords_execute_2500] - title = "Judge, Jury, Executioner" - description = "Execute 2,500 mobs" +title = "Judge, Jury, Executioner" +description = "Execute 2,500 mobs" [advancement.challenge_swords_execute_5in10] - title = "Mercy is Weakness" - description = "Execute 5 mobs in 10 seconds" +title = "Mercy is Weakness" +description = "Execute 5 mobs in 10 seconds" [advancement.challenge_swords_dual_1k] - title = "Ambidextrous" - description = "Deal 1,000 bonus dual-wield damage" +title = "Ambidextrous" +description = "Deal 1,000 bonus dual-wield damage" [advancement.challenge_swords_dual_25k] - title = "Twin Fangs" - description = "Deal 25,000 bonus dual-wield damage" +title = "Twin Fangs" +description = "Deal 25,000 bonus dual-wield damage" [advancement.challenge_swords_riposte_200] - title = "En Garde" - description = "Land 200 ripostes" +title = "En Garde" +description = "Land 200 ripostes" [advancement.challenge_swords_riposte_2500] - title = "Fencer" - description = "Land 2,500 ripostes" +title = "Fencer" +description = "Land 2,500 ripostes" [advancement.challenge_swords_riposte_3in5] - title = "Parry King" - description = "Land 3 ripostes within 5 seconds" +title = "Parry King" +description = "Land 3 ripostes within 5 seconds" [advancement.challenge_swords_cyclone_500] - title = "Whirlwind" - description = "Hit 500 mobs with crimson cyclone" +title = "Whirlwind" +description = "Hit 500 mobs with crimson cyclone" [advancement.challenge_swords_cyclone_5k] - title = "Storm of Blades" - description = "Hit 5,000 mobs with crimson cyclone" +title = "Storm of Blades" +description = "Hit 5,000 mobs with crimson cyclone" [advancement.challenge_swords_cyclone_6] - title = "Blender" - description = "Hit 6+ mobs with a single cyclone activation" +title = "Blender" +description = "Hit 6+ mobs with a single cyclone activation" # --- TAMING --- [advancement.challenge_taming_damage_500] - title = "Alpha Trainer" - description = "Your pets kill 500 mobs" +title = "Alpha Trainer" +description = "Your pets kill 500 mobs" [advancement.challenge_taming_damage_5k] - title = "Beast Master" - description = "Your pets kill 5,000 mobs" +title = "Beast Master" +description = "Your pets kill 5,000 mobs" [advancement.challenge_taming_health_boost_1728k] - title = "Guardian of Beasts" - description = "Keep pets health-boosted for 24 cumulative hours" +title = "Guardian of Beasts" +description = "Keep pets health-boosted for 24 cumulative hours" [advancement.challenge_taming_regen_1k] - title = "Healing Touch" - description = "Pets regenerate 1,000 hearts" +title = "Healing Touch" +description = "Pets regenerate 1,000 hearts" [advancement.challenge_taming_recall_100] - title = "Come Here Boy" - description = "Recall pets 100 times" +title = "Come Here Boy" +description = "Recall pets 100 times" [advancement.challenge_taming_recall_1k] - title = "Teleporting Kennel" - description = "Recall pets 1,000 times" +title = "Teleporting Kennel" +description = "Recall pets 1,000 times" [advancement.challenge_taming_pack_72k] - title = "Pack Leader" - description = "Keep pack leader aura active for 1 cumulative hour" +title = "Pack Leader" +description = "Keep pack leader aura active for 1 cumulative hour" [advancement.challenge_taming_shared_500] - title = "Loyal Shield" - description = "Take 500 hearts of damage for your pets" +title = "Loyal Shield" +description = "Take 500 hearts of damage for your pets" [advancement.challenge_taming_shared_5k] - title = "Sacrifice" - description = "Take 5,000 hearts of damage for your pets" +title = "Sacrifice" +description = "Take 5,000 hearts of damage for your pets" [advancement.challenge_taming_mounted_200] - title = "Cavalry" - description = "Kill 200 mobs while mounted" +title = "Cavalry" +description = "Kill 200 mobs while mounted" [advancement.challenge_taming_mounted_50k] - title = "Knight Errant" - description = "Travel 50,000 blocks while mounted" +title = "Knight Errant" +description = "Travel 50,000 blocks while mounted" # --- TRAG'OUL --- [advancement.challenge_tragoul_thorns_500] - title = "Living Cactus" - description = "Reflect 500 damage via blood thorns" +title = "Living Cactus" +description = "Reflect 500 damage via blood thorns" [advancement.challenge_tragoul_thorns_5k] - title = "Iron Maiden" - description = "Reflect 5,000 damage" +title = "Iron Maiden" +description = "Reflect 5,000 damage" [advancement.challenge_tragoul_thorns_kill] - title = "Don't Touch Me" - description = "Kill a mob purely from reflected damage" +title = "Don't Touch Me" +description = "Kill a mob purely from reflected damage" [advancement.challenge_tragoul_globe_1k] - title = "Equal Opportunity" - description = "Spread damage to 1,000 mobs via globe" +title = "Equal Opportunity" +description = "Spread damage to 1,000 mobs via globe" [advancement.challenge_tragoul_globe_5] - title = "Pain Dividend" - description = "Spread damage to 5+ mobs in one hit" +title = "Pain Dividend" +description = "Spread damage to 5+ mobs in one hit" [advancement.challenge_tragoul_healing_500] - title = "Vampire" - description = "Steal 500 hearts via life leech" +title = "Vampire" +description = "Steal 500 hearts via life leech" [advancement.challenge_tragoul_healing_10k] - title = "Blood Drinker" - description = "Steal 10,000 hearts" +title = "Blood Drinker" +description = "Steal 10,000 hearts" [advancement.challenge_tragoul_lance_200] - title = "Impaler" - description = "Spawn 200 blood lances" +title = "Impaler" +description = "Spawn 200 blood lances" [advancement.challenge_tragoul_lance_kills_100] - title = "Chain Killer" - description = "Kill 100 mobs with blood lances" +title = "Chain Killer" +description = "Kill 100 mobs with blood lances" [advancement.challenge_tragoul_pact_200] - title = "Blood Price" - description = "Sacrifice 200 hearts for power" +title = "Blood Price" +description = "Sacrifice 200 hearts for power" [advancement.challenge_tragoul_pact_kills_500] - title = "Pact Keeper" - description = "Kill 500 mobs while blood-empowered" +title = "Pact Keeper" +description = "Kill 500 mobs while blood-empowered" [advancement.challenge_tragoul_pact_all_in] - title = "All In" - description = "Activate blood pact below 3 hearts and kill" +title = "All In" +description = "Activate blood pact below 3 hearts and kill" [advancement.challenge_tragoul_bone_500] - title = "Bone Collector" - description = "Collect 500 bone orbs" +title = "Bone Collector" +description = "Collect 500 bone orbs" [advancement.challenge_tragoul_bone_5k] - title = "Harvester" - description = "Collect 5,000 bone orbs" +title = "Harvester" +description = "Collect 5,000 bone orbs" # --- UNARMED --- [advancement.challenge_unarmed_glass_100] - title = "Birthday Suit Brawler" - description = "Kill 100 mobs with no armor" +title = "Birthday Suit Brawler" +description = "Kill 100 mobs with no armor" [advancement.challenge_unarmed_glass_500] - title = "True Glass Cannon" - description = "Kill 500 mobs with no armor" +title = "True Glass Cannon" +description = "Kill 500 mobs with no armor" [advancement.challenge_unarmed_power_500] - title = "Bare Knuckle" - description = "Kill 500 mobs with bare fists" +title = "Bare Knuckle" +description = "Kill 500 mobs with bare fists" [advancement.challenge_unarmed_power_5k] - title = "One Punch" - description = "Kill 5,000 mobs with bare fists" +title = "One Punch" +description = "Kill 5,000 mobs with bare fists" [advancement.challenge_unarmed_sucker_500] - title = "Sucker Punch" - description = "Land 500 sprint punches" +title = "Sucker Punch" +description = "Land 500 sprint punches" [advancement.challenge_unarmed_knockout] - title = "Knockout Artist" - description = "One-shot kill 50 mobs with a sucker punch" +title = "Knockout Artist" +description = "One-shot kill 50 mobs with a sucker punch" [advancement.challenge_unarmed_combo_5k] - title = "Street Fighter" - description = "Land 5,000 total combo hits" +title = "Street Fighter" +description = "Land 5,000 total combo hits" [advancement.challenge_unarmed_combo_10] - title = "Combo Breaker" - description = "Reach a 10-hit combo" +title = "Combo Breaker" +description = "Reach a 10-hit combo" [advancement.challenge_unarmed_combo_25] - title = "Infinite Combo" - description = "Reach a 25-hit combo" +title = "Infinite Combo" +description = "Reach a 25-hit combo" [advancement.challenge_unarmed_charge_300] - title = "Battering Ram" - description = "Charge into 300 enemies" +title = "Battering Ram" +description = "Charge into 300 enemies" [advancement.challenge_unarmed_charge_kills_100] - title = "Unstoppable Force" - description = "Kill 100 mobs with battering charge" +title = "Unstoppable Force" +description = "Kill 100 mobs with battering charge" diff --git a/src/main/resources/es_ES.toml b/src/main/resources/es_ES.toml index d25a59ab0..789cd53d7 100644 --- a/src/main/resources/es_ES.toml +++ b/src/main/resources/es_ES.toml @@ -2,2060 +2,2060 @@ [advancement] [advancement.challenge_move_1k] - title = "¡A moverse!" - description = "Camina mas de 1 kilometro (1,000 bloques)" +title = "¡A moverse!" +description = "Camina mas de 1 kilometro (1,000 bloques)" [advancement.challenge_sprint_5k] - title = "¡Corre un 5K!" - description = "Camina mas de 5 kilometros (5,000 bloques)" +title = "¡Corre un 5K!" +description = "Camina mas de 5 kilometros (5,000 bloques)" [advancement.challenge_sprint_50k] - title = "¡Zoom de 50K!" - description = "Camina mas de 50 kilometros (50,000 bloques)" +title = "¡Zoom de 50K!" +description = "Camina mas de 50 kilometros (50,000 bloques)" [advancement.challenge_sprint_500k] - title = "¡¡Atraviesa el Universo!!" - description = "Camina mas de 500 kilometros (500,000 bloques)" +title = "¡¡Atraviesa el Universo!!" +description = "Camina mas de 500 kilometros (500,000 bloques)" [advancement.challenge_sprint_marathon] - title = "¡Corre un maraton (literal)!" - description = "¡Esprintea mas de 42,195 bloques!" +title = "¡Corre un maraton (literal)!" +description = "¡Esprintea mas de 42,195 bloques!" [advancement.challenge_place_1k] - title = "¡Constructor novato!" - description = "Coloca 1,000 bloques" +title = "¡Constructor novato!" +description = "Coloca 1,000 bloques" [advancement.challenge_place_5k] - title = "¡Constructor intermedio!" - description = "Coloca 5,000 bloques" +title = "¡Constructor intermedio!" +description = "Coloca 5,000 bloques" [advancement.challenge_place_50k] - title = "¡Constructor avanzado!" - description = "Coloca 50,000 bloques" +title = "¡Constructor avanzado!" +description = "Coloca 50,000 bloques" [advancement.challenge_place_500k] - title = "¡Maestro constructor!" - description = "Coloca 500,000 bloques" +title = "¡Maestro constructor!" +description = "Coloca 500,000 bloques" [advancement.challenge_place_5m] - title = "¡Acolito de la Simetria!" - description = "¡LA REALIDAD ES TU PATIO DE JUEGOS! (5 millones de bloques)" +title = "¡Acolito de la Simetria!" +description = "¡LA REALIDAD ES TU PATIO DE JUEGOS! (5 millones de bloques)" [advancement.challenge_chop_1k] - title = "¡Lenador novato!" - description = "Tala 1,000 bloques" +title = "¡Lenador novato!" +description = "Tala 1,000 bloques" [advancement.challenge_chop_5k] - title = "¡Lenador intermedio!" - description = "Tala 5,000 bloques" +title = "¡Lenador intermedio!" +description = "Tala 5,000 bloques" [advancement.challenge_chop_50k] - title = "¡Lenador avanzado!" - description = "Tala 50,000 bloques" +title = "¡Lenador avanzado!" +description = "Tala 50,000 bloques" [advancement.challenge_chop_500k] - title = "¡Maestro lenador!" - description = "Tala 500,000 bloques" +title = "¡Maestro lenador!" +description = "Tala 500,000 bloques" [advancement.challenge_chop_5m] - title = "Jackson el Perro" - description = "¡El mejor chico bueno! (5 millones de bloques)" +title = "Jackson el Perro" +description = "¡El mejor chico bueno! (5 millones de bloques)" [advancement.challenge_block_1k] - title = "¡Apenas bloqueando!" - description = "Bloquea 1000 golpes" +title = "¡Apenas bloqueando!" +description = "Bloquea 1000 golpes" [advancement.challenge_block_5k] - title = "¡Bloquear es divertido!" - description = "Bloquea 5000 golpes" +title = "¡Bloquear es divertido!" +description = "Bloquea 5000 golpes" [advancement.challenge_block_50k] - title = "¡Bloquear es mi vida!" - description = "Bloquea 50,000 golpes" +title = "¡Bloquear es mi vida!" +description = "Bloquea 50,000 golpes" [advancement.challenge_block_500k] - title = "¡Bloquear es mi proposito!" - description = "Bloquea 500,000 golpes" +title = "¡Bloquear es mi proposito!" +description = "Bloquea 500,000 golpes" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Bloquea 5,000,000 golpes" +title = "Die Hand Die Verletzt" +description = "Bloquea 5,000,000 golpes" [advancement.challenge_brew_1k] - title = "¡Alquimista novato!" - description = "Consume 1000 pociones" +title = "¡Alquimista novato!" +description = "Consume 1000 pociones" [advancement.challenge_brew_5k] - title = "¡Alquimista intermedio!" - description = "Consume 5000 pociones" +title = "¡Alquimista intermedio!" +description = "Consume 5000 pociones" [advancement.challenge_brew_50k] - title = "¡Alquimista avanzado!" - description = "Consume 50,000 pociones" +title = "¡Alquimista avanzado!" +description = "Consume 50,000 pociones" [advancement.challenge_brew_500k] - title = "¡Maestro alquimista!" - description = "Consume 500,000 pociones" +title = "¡Maestro alquimista!" +description = "Consume 500,000 pociones" [advancement.challenge_brew_5m] - title = "El Alquimista" - description = "Consume 5,000,000 pociones" +title = "El Alquimista" +description = "Consume 5,000,000 pociones" [advancement.challenge_brewsplash_1k] - title = "¡Salpicador de pociones novato!" - description = "Salpica 1000 pociones" +title = "¡Salpicador de pociones novato!" +description = "Salpica 1000 pociones" [advancement.challenge_brewsplash_5k] - title = "¡Salpicador de pociones intermedio!" - description = "Salpica 5000 pociones" +title = "¡Salpicador de pociones intermedio!" +description = "Salpica 5000 pociones" [advancement.challenge_brewsplash_50k] - title = "¡Salpicador de pociones avanzado!" - description = "Salpica 50,000 pociones" +title = "¡Salpicador de pociones avanzado!" +description = "Salpica 50,000 pociones" [advancement.challenge_brewsplash_500k] - title = "¡Maestro salpicador de pociones!" - description = "Salpica 500,000 pociones" +title = "¡Maestro salpicador de pociones!" +description = "Salpica 500,000 pociones" [advancement.challenge_brewsplash_5m] - title = "El Maestro de las Salpicaduras" - description = "Salpica 5,000,000 pociones" +title = "El Maestro de las Salpicaduras" +description = "Salpica 5,000,000 pociones" [advancement.challenge_craft_1k] - title = "¡Artesano astuto!" - description = "Fabrica 1000 objetos" +title = "¡Artesano astuto!" +description = "Fabrica 1000 objetos" [advancement.challenge_craft_5k] - title = "¡Artesano cascarrabias!" - description = "Fabrica 5000 objetos" +title = "¡Artesano cascarrabias!" +description = "Fabrica 5000 objetos" [advancement.challenge_craft_50k] - title = "¡Artesano servil!" - description = "Fabrica 50,000 objetos" +title = "¡Artesano servil!" +description = "Fabrica 50,000 objetos" [advancement.challenge_craft_500k] - title = "¡Artesano cacofonico!" - description = "Fabrica 500,000 objetos" +title = "¡Artesano cacofonico!" +description = "Fabrica 500,000 objetos" [advancement.challenge_craft_5m] - title = "McCraftface Calamitoso" - description = "Fabrica 5,000,000 objetos" +title = "McCraftface Calamitoso" +description = "Fabrica 5,000,000 objetos" [advancement.challenge_enchant_1k] - title = "¡Encantador novato!" - description = "Encanta 1000 objetos" +title = "¡Encantador novato!" +description = "Encanta 1000 objetos" [advancement.challenge_enchant_5k] - title = "¡Encantador intermedio!" - description = "Encanta 5000 objetos" +title = "¡Encantador intermedio!" +description = "Encanta 5000 objetos" [advancement.challenge_enchant_50k] - title = "¡Encantador avanzado!" - description = "Encanta 50,000 objetos" +title = "¡Encantador avanzado!" +description = "Encanta 50,000 objetos" [advancement.challenge_enchant_500k] - title = "¡Maestro encantador!" - description = "Encanta 500,000 objetos" +title = "¡Maestro encantador!" +description = "Encanta 500,000 objetos" [advancement.challenge_enchant_5m] - title = "Encantador Enigmatico" - description = "Encanta 5,000,000 objetos" +title = "Encantador Enigmatico" +description = "Encanta 5,000,000 objetos" [advancement.challenge_excavate_1k] - title = "¡Excavador entusiasta!" - description = "Excava 1000 bloques" +title = "¡Excavador entusiasta!" +description = "Excava 1000 bloques" [advancement.challenge_excavate_5k] - title = "¡Excavador intermedio!" - description = "Excava 5000 bloques" +title = "¡Excavador intermedio!" +description = "Excava 5000 bloques" [advancement.challenge_excavate_50k] - title = "¡Excavador avanzado!" - description = "Excava 50,000 bloques" +title = "¡Excavador avanzado!" +description = "Excava 50,000 bloques" [advancement.challenge_excavate_500k] - title = "¡Maestro excavador!" - description = "Excava 500,000 bloques" +title = "¡Maestro excavador!" +description = "Excava 500,000 bloques" [advancement.challenge_excavate_5m] - title = "Excavador Enigmatico" - description = "Excava 5,000,000 bloques" +title = "Excavador Enigmatico" +description = "Excava 5,000,000 bloques" [advancement.horrible_person] - title = "Eres una persona horrible" - description = "Incomprensible, de verdad" +title = "Eres una persona horrible" +description = "Incomprensible, de verdad" [advancement.challenge_turtle_egg_smasher] - title = "¡Rompe-huevos de tortuga!" - description = "Rompe 100 huevos de tortuga" +title = "¡Rompe-huevos de tortuga!" +description = "Rompe 100 huevos de tortuga" [advancement.challenge_turtle_egg_annihilator] - title = "¡Aniquilador de huevos de tortuga!" - description = "Rompe 500 huevos de tortuga" +title = "¡Aniquilador de huevos de tortuga!" +description = "Rompe 500 huevos de tortuga" [advancement.challenge_novice_hunter] - title = "¡Cazador novato!" - description = "Mata 100 entidades" +title = "¡Cazador novato!" +description = "Mata 100 entidades" [advancement.challenge_intermediate_hunter] - title = "¡Cazador intermedio!" - description = "Mata 500 entidades" +title = "¡Cazador intermedio!" +description = "Mata 500 entidades" [advancement.challenge_advanced_hunter] - title = "¡Cazador avanzado!" - description = "Mata 5000 entidades" +title = "¡Cazador avanzado!" +description = "Mata 5000 entidades" [advancement.challenge_creeper_conqueror] - title = "¡Conquistador de Creepers!" - description = "Mata 50 creepers" +title = "¡Conquistador de Creepers!" +description = "Mata 50 creepers" [advancement.challenge_creeper_annihilator] - title = "¡Aniquilador de Creepers!" - description = "Mata 200 creepers" +title = "¡Aniquilador de Creepers!" +description = "Mata 200 creepers" [advancement.challenge_pickaxe_1k] - title = "Minero novato" - description = "Rompe 1000 bloques" +title = "Minero novato" +description = "Rompe 1000 bloques" [advancement.challenge_pickaxe_5k] - title = "Minero habil" - description = "Rompe 5000 bloques" +title = "Minero habil" +description = "Rompe 5000 bloques" [advancement.challenge_pickaxe_50k] - title = "Minero experto" - description = "Rompe 50,000 bloques" +title = "Minero experto" +description = "Rompe 50,000 bloques" [advancement.challenge_pickaxe_500k] - title = "Maestro minero" - description = "Rompe 500,000 bloques" +title = "Maestro minero" +description = "Rompe 500,000 bloques" [advancement.challenge_pickaxe_5m] - title = "Minero legendario" - description = "Rompe 5,000,000 bloques" +title = "Minero legendario" +description = "Rompe 5,000,000 bloques" [advancement.challenge_eat_100] - title = "¡Tanto para comer!" - description = "¡Come mas de 100 objetos!" +title = "¡Tanto para comer!" +description = "¡Come mas de 100 objetos!" [advancement.challenge_eat_1000] - title = "¡Hambre insaciable!" - description = "¡Come mas de 1,000 objetos!" +title = "¡Hambre insaciable!" +description = "¡Come mas de 1,000 objetos!" [advancement.challenge_eat_10000] - title = "¡HAMBRE ETERNA!" - description = "¡Come mas de 10,000 objetos!" +title = "¡HAMBRE ETERNA!" +description = "¡Come mas de 10,000 objetos!" [advancement.challenge_harvest_100] - title = "Cosecha completa" - description = "¡Cosecha mas de 100 cultivos!" +title = "Cosecha completa" +description = "¡Cosecha mas de 100 cultivos!" [advancement.challenge_harvest_1000] - title = "Gran cosecha" - description = "¡Cosecha mas de 1,000 cultivos!" +title = "Gran cosecha" +description = "¡Cosecha mas de 1,000 cultivos!" [advancement.challenge_swim_1nm] - title = "¡Submarino humano!" - description = "Nada 1 milla nautica (1,852 bloques)" +title = "¡Submarino humano!" +description = "Nada 1 milla nautica (1,852 bloques)" [advancement.challenge_sneak_1k] - title = "Dolor de rodillas" - description = "Agachate mas de un kilometro (1,000 bloques)" +title = "Dolor de rodillas" +description = "Agachate mas de un kilometro (1,000 bloques)" [advancement.challenge_sneak_5k] - title = "Caminante de Sombras" - description = "Agachate mas de 5,000 bloques" +title = "Caminante de Sombras" +description = "Agachate mas de 5,000 bloques" [advancement.challenge_sneak_20k] - title = "Fantasma" - description = "Agachate mas de 20,000 bloques" +title = "Fantasma" +description = "Agachate mas de 20,000 bloques" [advancement.challenge_swim_5k] - title = "Buceador Profundo" - description = "Nada mas de 5,000 bloques" +title = "Buceador Profundo" +description = "Nada mas de 5,000 bloques" [advancement.challenge_swim_20k] - title = "Elegido de Poseidon" - description = "Nada mas de 20,000 bloques" +title = "Elegido de Poseidon" +description = "Nada mas de 20,000 bloques" [advancement.challenge_sword_100] - title = "Primera Sangre" - description = "Golpea 100 veces con una espada" +title = "Primera Sangre" +description = "Golpea 100 veces con una espada" [advancement.challenge_sword_1k] - title = "Danzante de Espadas" - description = "Golpea 1,000 veces con una espada" +title = "Danzante de Espadas" +description = "Golpea 1,000 veces con una espada" [advancement.challenge_sword_10k] - title = "Mil Cortes" - description = "Golpea 10,000 veces con una espada" +title = "Mil Cortes" +description = "Golpea 10,000 veces con una espada" [advancement.challenge_unarmed_100] - title = "Peleador Callejero" - description = "Golpea 100 veces sin armas" +title = "Peleador Callejero" +description = "Golpea 100 veces sin armas" [advancement.challenge_unarmed_1k] - title = "Punos de Hierro" - description = "Golpea 1,000 veces sin armas" +title = "Punos de Hierro" +description = "Golpea 1,000 veces sin armas" [advancement.challenge_unarmed_10k] - title = "One Punch" - description = "Golpea 10,000 veces sin armas" +title = "One Punch" +description = "Golpea 10,000 veces sin armas" [advancement.challenge_trag_1k] - title = "Precio de Sangre" - description = "Recibe 1,000 de dano" +title = "Precio de Sangre" +description = "Recibe 1,000 de dano" [advancement.challenge_trag_10k] - title = "Marea Carmesi" - description = "Recibe 10,000 de dano" +title = "Marea Carmesi" +description = "Recibe 10,000 de dano" [advancement.challenge_trag_100k] - title = "Avatar del Sufrimiento" - description = "Recibe 100,000 de dano" +title = "Avatar del Sufrimiento" +description = "Recibe 100,000 de dano" [advancement.challenge_ranged_100] - title = "Practica de Tiro" - description = "Dispara 100 proyectiles" +title = "Practica de Tiro" +description = "Dispara 100 proyectiles" [advancement.challenge_ranged_1k] - title = "Ojo de Halcon" - description = "Dispara 1,000 proyectiles" +title = "Ojo de Halcon" +description = "Dispara 1,000 proyectiles" [advancement.challenge_ranged_10k] - title = "Tormenta de Flechas" - description = "Dispara 10,000 proyectiles" +title = "Tormenta de Flechas" +description = "Dispara 10,000 proyectiles" [advancement.challenge_chronos_1h] - title = "Tic Tac" - description = "Pasa 1 hora en linea" +title = "Tic Tac" +description = "Pasa 1 hora en linea" [advancement.challenge_chronos_24h] - title = "Arenas del Tiempo" - description = "Pasa 24 horas en linea" +title = "Arenas del Tiempo" +description = "Pasa 24 horas en linea" [advancement.challenge_chronos_168h] - title = "Eterno" - description = "Pasa 168 horas (1 semana) en linea" +title = "Eterno" +description = "Pasa 168 horas (1 semana) en linea" [advancement.challenge_nether_50] - title = "Portero del Infierno" - description = "Mata 50 criaturas del Nether" +title = "Portero del Infierno" +description = "Mata 50 criaturas del Nether" [advancement.challenge_nether_500] - title = "Guardian del Abismo" - description = "Mata 500 criaturas del Nether" +title = "Guardian del Abismo" +description = "Mata 500 criaturas del Nether" [advancement.challenge_nether_5k] - title = "Senor del Nether" - description = "Mata 5,000 criaturas del Nether" +title = "Senor del Nether" +description = "Mata 5,000 criaturas del Nether" [advancement.challenge_rift_50] - title = "Anomalia Espacial" - description = "Teletransportate 50 veces" +title = "Anomalia Espacial" +description = "Teletransportate 50 veces" [advancement.challenge_rift_500] - title = "Caminante del Vacio" - description = "Teletransportate 500 veces" +title = "Caminante del Vacio" +description = "Teletransportate 500 veces" [advancement.challenge_rift_5k] - title = "Entre Mundos" - description = "Teletransportate 5,000 veces" +title = "Entre Mundos" +description = "Teletransportate 5,000 veces" [advancement.challenge_taming_10] - title = "Susurrador de Animales" - description = "Cria 10 animales" +title = "Susurrador de Animales" +description = "Cria 10 animales" [advancement.challenge_taming_50] - title = "Lider de la Manada" - description = "Cria 50 animales" +title = "Lider de la Manada" +description = "Cria 50 animales" [advancement.challenge_taming_500] - title = "Maestro de Bestias" - description = "Cria 500 animales" +title = "Maestro de Bestias" +description = "Cria 500 animales" # Agility [advancement.challenge_sprint_dist_5k] - title = "Demonio Veloz" - description = "Corre mas de 5 Kilometros (5.000 bloques)" +title = "Demonio Veloz" +description = "Corre mas de 5 Kilometros (5.000 bloques)" [advancement.challenge_sprint_dist_50k] - title = "Piernas de Rayo" - description = "Corre mas de 50 Kilometros (50.000 bloques)" +title = "Piernas de Rayo" +description = "Corre mas de 50 Kilometros (50.000 bloques)" [advancement.challenge_agility_swim_1k] - title = "Zancudo Acuatico" - description = "Nada mas de 1 Kilometro (1.000 bloques)" +title = "Zancudo Acuatico" +description = "Nada mas de 1 Kilometro (1.000 bloques)" [advancement.challenge_agility_swim_10k] - title = "Viajero Acuatico" - description = "Nada mas de 10 Kilometros (10.000 bloques)" +title = "Viajero Acuatico" +description = "Nada mas de 10 Kilometros (10.000 bloques)" [advancement.challenge_fly_1k] - title = "Danzarin del Cielo" - description = "Vuela mas de 1 Kilometro (1.000 bloques)" +title = "Danzarin del Cielo" +description = "Vuela mas de 1 Kilometro (1.000 bloques)" [advancement.challenge_fly_10k] - title = "Jinete del Viento" - description = "Vuela mas de 10 Kilometros (10.000 bloques)" +title = "Jinete del Viento" +description = "Vuela mas de 10 Kilometros (10.000 bloques)" [advancement.challenge_agility_sneak_500] - title = "Pasos Silenciosos" - description = "Camina agachado mas de 500 bloques" +title = "Pasos Silenciosos" +description = "Camina agachado mas de 500 bloques" [advancement.challenge_agility_sneak_5k] - title = "Pasos Fantasma" - description = "Camina agachado mas de 5 Kilometros (5.000 bloques)" +title = "Pasos Fantasma" +description = "Camina agachado mas de 5 Kilometros (5.000 bloques)" # Architect [advancement.challenge_demolish_500] - title = "Equipo de Demolicion" - description = "Rompe 500 bloques" +title = "Equipo de Demolicion" +description = "Rompe 500 bloques" [advancement.challenge_demolish_5k] - title = "Bola de Demolicion" - description = "Rompe 5.000 bloques" +title = "Bola de Demolicion" +description = "Rompe 5.000 bloques" [advancement.challenge_value_placed_10k] - title = "Constructor Valioso" - description = "Coloca bloques con un valor de 10.000" +title = "Constructor Valioso" +description = "Coloca bloques con un valor de 10.000" [advancement.challenge_value_placed_100k] - title = "Maestro Arquitecto" - description = "Coloca bloques con un valor de 100.000" +title = "Maestro Arquitecto" +description = "Coloca bloques con un valor de 100.000" [advancement.challenge_demolish_val_5k] - title = "Experto en Salvamento" - description = "Recupera 5.000 de valor de bloques por demolicion" +title = "Experto en Salvamento" +description = "Recupera 5.000 de valor de bloques por demolicion" [advancement.challenge_demolish_val_50k] - title = "Deconstruccion Total" - description = "Recupera 50.000 de valor de bloques por demolicion" +title = "Deconstruccion Total" +description = "Recupera 50.000 de valor de bloques por demolicion" [advancement.challenge_high_build_100] - title = "Constructor Celestial" - description = "Coloca 100 bloques por encima de Y=128" +title = "Constructor Celestial" +description = "Coloca 100 bloques por encima de Y=128" [advancement.challenge_high_build_1k] - title = "Arquitecto de las Nubes" - description = "Coloca 1.000 bloques por encima de Y=128" +title = "Arquitecto de las Nubes" +description = "Coloca 1.000 bloques por encima de Y=128" # Axes [advancement.challenge_axe_swing_500] - title = "Hachero" - description = "Balancea tu hacha 500 veces" +title = "Hachero" +description = "Balancea tu hacha 500 veces" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Balancea tu hacha 5.000 veces" +title = "Berserker" +description = "Balancea tu hacha 5.000 veces" [advancement.challenge_axe_damage_1k] - title = "Cuchilla" - description = "Inflige 1.000 de dano con hachas" +title = "Cuchilla" +description = "Inflige 1.000 de dano con hachas" [advancement.challenge_axe_damage_10k] - title = "Hacha del Verdugo" - description = "Inflige 10.000 de dano con hachas" +title = "Hacha del Verdugo" +description = "Inflige 10.000 de dano con hachas" [advancement.challenge_axe_value_5k] - title = "Comerciante de Madera" - description = "Cosecha madera con un valor de 5.000" +title = "Comerciante de Madera" +description = "Cosecha madera con un valor de 5.000" [advancement.challenge_axe_value_50k] - title = "Baron Maderero" - description = "Cosecha madera con un valor de 50.000" +title = "Baron Maderero" +description = "Cosecha madera con un valor de 50.000" [advancement.challenge_leaves_500] - title = "Soplador de Hojas" - description = "Despeja 500 bloques de hojas con un hacha" +title = "Soplador de Hojas" +description = "Despeja 500 bloques de hojas con un hacha" [advancement.challenge_leaves_5k] - title = "Defoliador" - description = "Despeja 5.000 bloques de hojas con un hacha" +title = "Defoliador" +description = "Despeja 5.000 bloques de hojas con un hacha" # Blocking [advancement.challenge_block_dmg_1k] - title = "Absorbente de Dano" - description = "Bloquea 1.000 de dano con un escudo" +title = "Absorbente de Dano" +description = "Bloquea 1.000 de dano con un escudo" [advancement.challenge_block_dmg_10k] - title = "Escudo Humano" - description = "Bloquea 10.000 de dano con un escudo" +title = "Escudo Humano" +description = "Bloquea 10.000 de dano con un escudo" [advancement.challenge_block_proj_100] - title = "Deflector de Flechas" - description = "Bloquea 100 proyectiles con un escudo" +title = "Deflector de Flechas" +description = "Bloquea 100 proyectiles con un escudo" [advancement.challenge_block_proj_1k] - title = "Escudo Anti-Proyectiles" - description = "Bloquea 1.000 proyectiles con un escudo" +title = "Escudo Anti-Proyectiles" +description = "Bloquea 1.000 proyectiles con un escudo" [advancement.challenge_block_melee_500] - title = "Maestro del Parry" - description = "Bloquea 500 ataques cuerpo a cuerpo con un escudo" +title = "Maestro del Parry" +description = "Bloquea 500 ataques cuerpo a cuerpo con un escudo" [advancement.challenge_block_melee_5k] - title = "Fortaleza de Hierro" - description = "Bloquea 5.000 ataques cuerpo a cuerpo con un escudo" +title = "Fortaleza de Hierro" +description = "Bloquea 5.000 ataques cuerpo a cuerpo con un escudo" [advancement.challenge_block_heavy_50] - title = "Tanque" - description = "Bloquea 50 ataques pesados (mas de 5 de dano)" +title = "Tanque" +description = "Bloquea 50 ataques pesados (mas de 5 de dano)" [advancement.challenge_block_heavy_500] - title = "Objeto Inamovible" - description = "Bloquea 500 ataques pesados (mas de 5 de dano)" +title = "Objeto Inamovible" +description = "Bloquea 500 ataques pesados (mas de 5 de dano)" # Brewing [advancement.challenge_brew_stands_10] - title = "Preparacion del Alquimista" - description = "Coloca 10 soportes para pociones" +title = "Preparacion del Alquimista" +description = "Coloca 10 soportes para pociones" [advancement.challenge_brew_stands_50] - title = "Fabrica de Pociones" - description = "Coloca 50 soportes para pociones" +title = "Fabrica de Pociones" +description = "Coloca 50 soportes para pociones" [advancement.challenge_brew_strong_25] - title = "Brebaje Fuerte" - description = "Consume 25 pociones mejoradas" +title = "Brebaje Fuerte" +description = "Consume 25 pociones mejoradas" [advancement.challenge_brew_strong_250] - title = "Potencia Maxima" - description = "Consume 250 pociones mejoradas" +title = "Potencia Maxima" +description = "Consume 250 pociones mejoradas" [advancement.challenge_brew_splash_hits_50] - title = "Zona de Salpicadura" - description = "Golpea a 50 entidades con pociones arrojadizas" +title = "Zona de Salpicadura" +description = "Golpea a 50 entidades con pociones arrojadizas" [advancement.challenge_brew_splash_hits_500] - title = "Doctor de la Peste" - description = "Golpea a 500 entidades con pociones arrojadizas" +title = "Doctor de la Peste" +description = "Golpea a 500 entidades con pociones arrojadizas" # Chronos [advancement.challenge_active_dist_1k] - title = "Inquieto" - description = "Viaja 1 Kilometro mientras estas activo" +title = "Inquieto" +description = "Viaja 1 Kilometro mientras estas activo" [advancement.challenge_active_dist_10k] - title = "Explorador" - description = "Viaja 10 Kilometros mientras estas activo" +title = "Explorador" +description = "Viaja 10 Kilometros mientras estas activo" [advancement.challenge_active_dist_100k] - title = "Movimiento Perpetuo" - description = "Viaja 100 Kilometros mientras estas activo" +title = "Movimiento Perpetuo" +description = "Viaja 100 Kilometros mientras estas activo" [advancement.challenge_beds_10] - title = "Madrugador" - description = "Duerme en una cama 10 veces" +title = "Madrugador" +description = "Duerme en una cama 10 veces" [advancement.challenge_beds_100] - title = "Saltador del Tiempo" - description = "Duerme en una cama 100 veces" +title = "Saltador del Tiempo" +description = "Duerme en una cama 100 veces" [advancement.challenge_chronos_tp_50] - title = "Cambio Temporal" - description = "Teletransportate 50 veces" +title = "Cambio Temporal" +description = "Teletransportate 50 veces" [advancement.challenge_chronos_tp_500] - title = "Distorsion Temporal" - description = "Teletransportate 500 veces" +title = "Distorsion Temporal" +description = "Teletransportate 500 veces" [advancement.challenge_chronos_deaths_10] - title = "Mortal" - description = "Muere 10 veces" +title = "Mortal" +description = "Muere 10 veces" [advancement.challenge_chronos_deaths_100] - title = "Desafiante de la Muerte" - description = "Muere 100 veces" +title = "Desafiante de la Muerte" +description = "Muere 100 veces" # Crafting [advancement.challenge_craft_value_10k] - title = "Valor Artesanal" - description = "Fabrica objetos con un valor total de 10.000" +title = "Valor Artesanal" +description = "Fabrica objetos con un valor total de 10.000" [advancement.challenge_craft_value_100k] - title = "Artesano" - description = "Fabrica objetos con un valor total de 100.000" +title = "Artesano" +description = "Fabrica objetos con un valor total de 100.000" [advancement.challenge_craft_tools_25] - title = "Herrero de Herramientas" - description = "Fabrica 25 herramientas" +title = "Herrero de Herramientas" +description = "Fabrica 25 herramientas" [advancement.challenge_craft_tools_250] - title = "Maestro Forjador" - description = "Fabrica 250 herramientas" +title = "Maestro Forjador" +description = "Fabrica 250 herramientas" [advancement.challenge_craft_armor_25] - title = "Herrero de Armaduras" - description = "Fabrica 25 piezas de armadura" +title = "Herrero de Armaduras" +description = "Fabrica 25 piezas de armadura" [advancement.challenge_craft_armor_250] - title = "Maestro Armero" - description = "Fabrica 250 piezas de armadura" +title = "Maestro Armero" +description = "Fabrica 250 piezas de armadura" [advancement.challenge_craft_mass_25k] - title = "Productor en Masa" - description = "Fabrica 25.000 objetos" +title = "Productor en Masa" +description = "Fabrica 25.000 objetos" [advancement.challenge_craft_mass_250k] - title = "Revolucion Industrial" - description = "Fabrica 250.000 objetos" +title = "Revolucion Industrial" +description = "Fabrica 250.000 objetos" # Discovery [advancement.challenge_discover_items_50] - title = "Coleccionista" - description = "Descubre 50 objetos unicos" +title = "Coleccionista" +description = "Descubre 50 objetos unicos" [advancement.challenge_discover_items_250] - title = "Catalogador" - description = "Descubre 250 objetos unicos" +title = "Catalogador" +description = "Descubre 250 objetos unicos" [advancement.challenge_discover_blocks_50] - title = "Topografo" - description = "Descubre 50 bloques unicos" +title = "Topografo" +description = "Descubre 50 bloques unicos" [advancement.challenge_discover_blocks_250] - title = "Geologo" - description = "Descubre 250 bloques unicos" +title = "Geologo" +description = "Descubre 250 bloques unicos" [advancement.challenge_discover_mobs_25] - title = "Observador" - description = "Descubre 25 criaturas unicas" +title = "Observador" +description = "Descubre 25 criaturas unicas" [advancement.challenge_discover_mobs_75] - title = "Naturalista" - description = "Descubre 75 criaturas unicas" +title = "Naturalista" +description = "Descubre 75 criaturas unicas" [advancement.challenge_discover_biomes_10] - title = "Errante" - description = "Descubre 10 biomas unicos" +title = "Errante" +description = "Descubre 10 biomas unicos" [advancement.challenge_discover_biomes_40] - title = "Trotamundos" - description = "Descubre 40 biomas unicos" +title = "Trotamundos" +description = "Descubre 40 biomas unicos" [advancement.challenge_discover_foods_10] - title = "Gourmet" - description = "Descubre 10 alimentos unicos" +title = "Gourmet" +description = "Descubre 10 alimentos unicos" [advancement.challenge_discover_foods_30] - title = "Maestro Culinario" - description = "Descubre 30 alimentos unicos" +title = "Maestro Culinario" +description = "Descubre 30 alimentos unicos" # Enchanting [advancement.challenge_enchant_power_100] - title = "Tejedor de Poder" - description = "Acumula 100 de poder de encantamiento" +title = "Tejedor de Poder" +description = "Acumula 100 de poder de encantamiento" [advancement.challenge_enchant_power_1k] - title = "Maestro Arcano" - description = "Acumula 1.000 de poder de encantamiento" +title = "Maestro Arcano" +description = "Acumula 1.000 de poder de encantamiento" [advancement.challenge_enchant_levels_1k] - title = "Gastador de Niveles" - description = "Gasta 1.000 niveles de experiencia en encantamientos" +title = "Gastador de Niveles" +description = "Gasta 1.000 niveles de experiencia en encantamientos" [advancement.challenge_enchant_levels_10k] - title = "Sumidero de XP" - description = "Gasta 10.000 niveles de experiencia en encantamientos" +title = "Sumidero de XP" +description = "Gasta 10.000 niveles de experiencia en encantamientos" [advancement.challenge_enchant_high_25] - title = "Apostador Fuerte" - description = "Realiza 25 encantamientos de nivel maximo" +title = "Apostador Fuerte" +description = "Realiza 25 encantamientos de nivel maximo" [advancement.challenge_enchant_high_250] - title = "Encantador Legendario" - description = "Realiza 250 encantamientos de nivel maximo" +title = "Encantador Legendario" +description = "Realiza 250 encantamientos de nivel maximo" [advancement.challenge_enchant_total_500] - title = "Quemador de Niveles" - description = "Gasta 500 niveles totales en encantamientos" +title = "Quemador de Niveles" +description = "Gasta 500 niveles totales en encantamientos" [advancement.challenge_enchant_total_5k] - title = "Inversion Arcana" - description = "Gasta 5.000 niveles totales en encantamientos" +title = "Inversion Arcana" +description = "Gasta 5.000 niveles totales en encantamientos" # Excavation [advancement.challenge_dig_swing_500] - title = "Cavador" - description = "Balancea tu pala 500 veces" +title = "Cavador" +description = "Balancea tu pala 500 veces" [advancement.challenge_dig_swing_5k] - title = "Excavador" - description = "Balancea tu pala 5.000 veces" +title = "Excavador" +description = "Balancea tu pala 5.000 veces" [advancement.challenge_dig_damage_1k] - title = "Caballero de la Pala" - description = "Inflige 1.000 de dano con una pala" +title = "Caballero de la Pala" +description = "Inflige 1.000 de dano con una pala" [advancement.challenge_dig_damage_10k] - title = "Maestro de la Pala" - description = "Inflige 10.000 de dano con una pala" +title = "Maestro de la Pala" +description = "Inflige 10.000 de dano con una pala" [advancement.challenge_dig_value_5k] - title = "Comerciante de Tierra" - description = "Excava bloques con un valor de 5.000" +title = "Comerciante de Tierra" +description = "Excava bloques con un valor de 5.000" [advancement.challenge_dig_value_50k] - title = "Baron de la Tierra" - description = "Excava bloques con un valor de 50.000" +title = "Baron de la Tierra" +description = "Excava bloques con un valor de 50.000" [advancement.challenge_dig_gravel_500] - title = "Triturador de Grava" - description = "Excava 500 bloques de grava, arena o arcilla" +title = "Triturador de Grava" +description = "Excava 500 bloques de grava, arena o arcilla" [advancement.challenge_dig_gravel_5k] - title = "Cernidor de Arena" - description = "Excava 5.000 bloques de grava, arena o arcilla" +title = "Cernidor de Arena" +description = "Excava 5.000 bloques de grava, arena o arcilla" # Herbalism [advancement.challenge_plant_100] - title = "Sembrador" - description = "Planta 100 cultivos" +title = "Sembrador" +description = "Planta 100 cultivos" [advancement.challenge_plant_1k] - title = "Mano Verde" - description = "Planta 1.000 cultivos" +title = "Mano Verde" +description = "Planta 1.000 cultivos" [advancement.challenge_plant_5k] - title = "Baron Agricola" - description = "Planta 5.000 cultivos" +title = "Baron Agricola" +description = "Planta 5.000 cultivos" [advancement.challenge_compost_50] - title = "Reciclador" - description = "Composta 50 objetos" +title = "Reciclador" +description = "Composta 50 objetos" [advancement.challenge_compost_500] - title = "Enriquecedor del Suelo" - description = "Composta 500 objetos" +title = "Enriquecedor del Suelo" +description = "Composta 500 objetos" [advancement.challenge_shear_50] - title = "Esquilador" - description = "Esquila 50 entidades" +title = "Esquilador" +description = "Esquila 50 entidades" [advancement.challenge_shear_250] - title = "Maestro del Rebano" - description = "Esquila 250 entidades" +title = "Maestro del Rebano" +description = "Esquila 250 entidades" # Hunter [advancement.challenge_kills_500] - title = "Cazador" - description = "Elimina 500 criaturas" +title = "Cazador" +description = "Elimina 500 criaturas" [advancement.challenge_kills_5k] - title = "Verdugo" - description = "Elimina 5.000 criaturas" +title = "Verdugo" +description = "Elimina 5.000 criaturas" [advancement.challenge_boss_1] - title = "Retador de Jefes" - description = "Derrota a un jefe" +title = "Retador de Jefes" +description = "Derrota a un jefe" [advancement.challenge_boss_10] - title = "Asesino de Leyendas" - description = "Derrota a 10 jefes" +title = "Asesino de Leyendas" +description = "Derrota a 10 jefes" # Nether [advancement.challenge_wither_dmg_500] - title = "Marchito" - description = "Soporta 500 de dano de Wither" +title = "Marchito" +description = "Soporta 500 de dano de Wither" [advancement.challenge_wither_dmg_5k] - title = "Superviviente de la Plaga" - description = "Soporta 5.000 de dano de Wither" +title = "Superviviente de la Plaga" +description = "Soporta 5.000 de dano de Wither" [advancement.challenge_wither_skel_25] - title = "Coleccionista de Huesos" - description = "Elimina 25 esqueletos Wither" +title = "Coleccionista de Huesos" +description = "Elimina 25 esqueletos Wither" [advancement.challenge_wither_skel_250] - title = "Azote de Esqueletos" - description = "Elimina 250 esqueletos Wither" +title = "Azote de Esqueletos" +description = "Elimina 250 esqueletos Wither" [advancement.challenge_wither_boss_1] - title = "Destructor del Wither" - description = "Derrota al Wither" +title = "Destructor del Wither" +description = "Derrota al Wither" [advancement.challenge_wither_boss_10] - title = "Dominador del Nether" - description = "Derrota al Wither 10 veces" +title = "Dominador del Nether" +description = "Derrota al Wither 10 veces" [advancement.challenge_roses_10] - title = "Jardinero de la Muerte" - description = "Rompe 10 rosas de Wither" +title = "Jardinero de la Muerte" +description = "Rompe 10 rosas de Wither" [advancement.challenge_roses_100] - title = "Coleccionista de Plaga" - description = "Rompe 100 rosas de Wither" +title = "Coleccionista de Plaga" +description = "Rompe 100 rosas de Wither" # Pickaxes [advancement.challenge_pick_swing_500] - title = "Brazo de Minero" - description = "Balancea tu pico 500 veces" +title = "Brazo de Minero" +description = "Balancea tu pico 500 veces" [advancement.challenge_pick_swing_5k] - title = "Constructor de Tuneles" - description = "Balancea tu pico 5.000 veces" +title = "Constructor de Tuneles" +description = "Balancea tu pico 5.000 veces" [advancement.challenge_pick_damage_1k] - title = "Luchador de Pico" - description = "Inflige 1.000 de dano con un pico" +title = "Luchador de Pico" +description = "Inflige 1.000 de dano con un pico" [advancement.challenge_pick_damage_10k] - title = "Pico de Guerra" - description = "Inflige 10.000 de dano con un pico" +title = "Pico de Guerra" +description = "Inflige 10.000 de dano con un pico" [advancement.challenge_pick_value_5k] - title = "Buscador de Gemas" - description = "Mina bloques con un valor de 5.000" +title = "Buscador de Gemas" +description = "Mina bloques con un valor de 5.000" [advancement.challenge_pick_value_50k] - title = "Baron del Mineral" - description = "Mina bloques con un valor de 50.000" +title = "Baron del Mineral" +description = "Mina bloques con un valor de 50.000" [advancement.challenge_pick_ores_500] - title = "Prospector" - description = "Mina 500 bloques de mineral" +title = "Prospector" +description = "Mina 500 bloques de mineral" [advancement.challenge_pick_ores_5k] - title = "Minero Maestro" - description = "Mina 5.000 bloques de mineral" +title = "Minero Maestro" +description = "Mina 5.000 bloques de mineral" # Ranged [advancement.challenge_ranged_dmg_1k] - title = "Tirador Certero" - description = "Inflige 1.000 de dano a distancia" +title = "Tirador Certero" +description = "Inflige 1.000 de dano a distancia" [advancement.challenge_ranged_dmg_10k] - title = "Arquero Letal" - description = "Inflige 10.000 de dano a distancia" +title = "Arquero Letal" +description = "Inflige 10.000 de dano a distancia" [advancement.challenge_ranged_dist_5k] - title = "Largo Alcance" - description = "Dispara proyectiles cubriendo 5.000 bloques de distancia total" +title = "Largo Alcance" +description = "Dispara proyectiles cubriendo 5.000 bloques de distancia total" [advancement.challenge_ranged_dist_50k] - title = "Tirador de Millas" - description = "Dispara proyectiles cubriendo 50.000 bloques de distancia total" +title = "Tirador de Millas" +description = "Dispara proyectiles cubriendo 50.000 bloques de distancia total" [advancement.challenge_ranged_kills_50] - title = "Arquero" - description = "Mata a 50 criaturas con armas a distancia" +title = "Arquero" +description = "Mata a 50 criaturas con armas a distancia" [advancement.challenge_ranged_kills_500] - title = "Maestro Arquero" - description = "Mata a 500 criaturas con armas a distancia" +title = "Maestro Arquero" +description = "Mata a 500 criaturas con armas a distancia" [advancement.challenge_longshot_25] - title = "Francotirador" - description = "Acierta 25 tiros de larga distancia (mas de 30 bloques)" +title = "Francotirador" +description = "Acierta 25 tiros de larga distancia (mas de 30 bloques)" [advancement.challenge_longshot_250] - title = "Ojo de Aguila" - description = "Acierta 250 tiros de larga distancia (mas de 30 bloques)" +title = "Ojo de Aguila" +description = "Acierta 250 tiros de larga distancia (mas de 30 bloques)" # Rift [advancement.challenge_rift_pearls_50] - title = "Lanzador de Perlas" - description = "Lanza 50 perlas de Ender" +title = "Lanzador de Perlas" +description = "Lanza 50 perlas de Ender" [advancement.challenge_rift_pearls_500] - title = "Adicto al Teletransporte" - description = "Lanza 500 perlas de Ender" +title = "Adicto al Teletransporte" +description = "Lanza 500 perlas de Ender" [advancement.challenge_rift_enderman_50] - title = "Cazador de Enderman" - description = "Elimina 50 endermen" +title = "Cazador de Enderman" +description = "Elimina 50 endermen" [advancement.challenge_rift_enderman_500] - title = "Acechador del Vacio" - description = "Elimina 500 endermen" +title = "Acechador del Vacio" +description = "Elimina 500 endermen" [advancement.challenge_rift_dragon_500] - title = "Luchador de Dragones" - description = "Inflige 500 de dano al Dragon del End" +title = "Luchador de Dragones" +description = "Inflige 500 de dano al Dragon del End" [advancement.challenge_rift_dragon_5k] - title = "Azote de Dragones" - description = "Inflige 5.000 de dano al Dragon del End" +title = "Azote de Dragones" +description = "Inflige 5.000 de dano al Dragon del End" [advancement.challenge_rift_crystal_10] - title = "Rompe Cristales" - description = "Destruye 10 cristales del End" +title = "Rompe Cristales" +description = "Destruye 10 cristales del End" [advancement.challenge_rift_crystal_100] - title = "Demoledor del End" - description = "Destruye 100 cristales del End" +title = "Demoledor del End" +description = "Destruye 100 cristales del End" # Seaborne [advancement.challenge_fish_25] - title = "Pescador" - description = "Atrapa 25 peces" +title = "Pescador" +description = "Atrapa 25 peces" [advancement.challenge_fish_250] - title = "Maestro Pescador" - description = "Atrapa 250 peces" +title = "Maestro Pescador" +description = "Atrapa 250 peces" [advancement.challenge_drowned_25] - title = "Cazador de Ahogados" - description = "Elimina 25 ahogados" +title = "Cazador de Ahogados" +description = "Elimina 25 ahogados" [advancement.challenge_drowned_250] - title = "Limpiador del Oceano" - description = "Elimina 250 ahogados" +title = "Limpiador del Oceano" +description = "Elimina 250 ahogados" [advancement.challenge_guardian_10] - title = "Cazador de Guardianes" - description = "Elimina 10 guardianes" +title = "Cazador de Guardianes" +description = "Elimina 10 guardianes" [advancement.challenge_guardian_100] - title = "Saqueador de Templos" - description = "Elimina 100 guardianes" +title = "Saqueador de Templos" +description = "Elimina 100 guardianes" [advancement.challenge_underwater_blocks_100] - title = "Minero Submarino" - description = "Rompe 100 bloques bajo el agua" +title = "Minero Submarino" +description = "Rompe 100 bloques bajo el agua" [advancement.challenge_underwater_blocks_1k] - title = "Ingeniero Acuatico" - description = "Rompe 1.000 bloques bajo el agua" +title = "Ingeniero Acuatico" +description = "Rompe 1.000 bloques bajo el agua" # Stealth [advancement.challenge_stealth_dmg_500] - title = "Apunalador" - description = "Inflige 500 de dano mientras te agachas" +title = "Apunalador" +description = "Inflige 500 de dano mientras te agachas" [advancement.challenge_stealth_dmg_5k] - title = "Asesino Silencioso" - description = "Inflige 5.000 de dano mientras te agachas" +title = "Asesino Silencioso" +description = "Inflige 5.000 de dano mientras te agachas" [advancement.challenge_stealth_kills_10] - title = "Asesino" - description = "Mata a 10 criaturas mientras te agachas" +title = "Asesino" +description = "Mata a 10 criaturas mientras te agachas" [advancement.challenge_stealth_kills_100] - title = "Segador de Sombras" - description = "Mata a 100 criaturas mientras te agachas" +title = "Segador de Sombras" +description = "Mata a 100 criaturas mientras te agachas" [advancement.challenge_stealth_time_1h] - title = "Paciente" - description = "Pasa 1 hora agachado (3.600 segundos)" +title = "Paciente" +description = "Pasa 1 hora agachado (3.600 segundos)" [advancement.challenge_stealth_time_10h] - title = "Maestro de las Sombras" - description = "Pasa 10 horas agachado (36.000 segundos)" +title = "Maestro de las Sombras" +description = "Pasa 10 horas agachado (36.000 segundos)" [advancement.challenge_stealth_arrows_50] - title = "Arquero Silencioso" - description = "Dispara 50 flechas mientras te agachas" +title = "Arquero Silencioso" +description = "Dispara 50 flechas mientras te agachas" [advancement.challenge_stealth_arrows_500] - title = "Arquero Fantasma" - description = "Dispara 500 flechas mientras te agachas" +title = "Arquero Fantasma" +description = "Dispara 500 flechas mientras te agachas" # Swords [advancement.challenge_sword_dmg_1k] - title = "Aprendiz de Espada" - description = "Inflige 1.000 de dano con espadas" +title = "Aprendiz de Espada" +description = "Inflige 1.000 de dano con espadas" [advancement.challenge_sword_dmg_10k] - title = "Espadachin" - description = "Inflige 10.000 de dano con espadas" +title = "Espadachin" +description = "Inflige 10.000 de dano con espadas" [advancement.challenge_sword_kills_50] - title = "Duelista" - description = "Mata a 50 criaturas con espadas" +title = "Duelista" +description = "Mata a 50 criaturas con espadas" [advancement.challenge_sword_kills_500] - title = "Gladiador" - description = "Mata a 500 criaturas con espadas" +title = "Gladiador" +description = "Mata a 500 criaturas con espadas" [advancement.challenge_sword_crit_50] - title = "Golpe Critico" - description = "Conecta 50 golpes criticos con espadas" +title = "Golpe Critico" +description = "Conecta 50 golpes criticos con espadas" [advancement.challenge_sword_crit_500] - title = "Maestro de la Precision" - description = "Conecta 500 golpes criticos con espadas" +title = "Maestro de la Precision" +description = "Conecta 500 golpes criticos con espadas" [advancement.challenge_sword_heavy_25] - title = "Golpeador Pesado" - description = "Conecta 25 golpes pesados con espadas (mas de 8 de dano)" +title = "Golpeador Pesado" +description = "Conecta 25 golpes pesados con espadas (mas de 8 de dano)" [advancement.challenge_sword_heavy_250] - title = "Golpe Devastador" - description = "Conecta 250 golpes pesados con espadas (mas de 8 de dano)" +title = "Golpe Devastador" +description = "Conecta 250 golpes pesados con espadas (mas de 8 de dano)" # Taming [advancement.challenge_pet_dmg_500] - title = "Entrenador de Bestias" - description = "Tus mascotas infligen 500 de dano total" +title = "Entrenador de Bestias" +description = "Tus mascotas infligen 500 de dano total" [advancement.challenge_pet_dmg_5k] - title = "Maestro de Guerra" - description = "Tus mascotas infligen 5.000 de dano total" +title = "Maestro de Guerra" +description = "Tus mascotas infligen 5.000 de dano total" [advancement.challenge_tamed_10] - title = "Amigo de los Animales" - description = "Domestica 10 animales" +title = "Amigo de los Animales" +description = "Domestica 10 animales" [advancement.challenge_tamed_100] - title = "Cuidador del Zoologico" - description = "Domestica 100 animales" +title = "Cuidador del Zoologico" +description = "Domestica 100 animales" [advancement.challenge_pet_kills_25] - title = "Tactica de Manada" - description = "Tus mascotas eliminan 25 criaturas" +title = "Tactica de Manada" +description = "Tus mascotas eliminan 25 criaturas" [advancement.challenge_pet_kills_250] - title = "Comandante Alfa" - description = "Tus mascotas eliminan 250 criaturas" +title = "Comandante Alfa" +description = "Tus mascotas eliminan 250 criaturas" [advancement.challenge_taming_2500] - title = "Experto en Cria" - description = "Cria 2.500 animales" +title = "Experto en Cria" +description = "Cria 2.500 animales" [advancement.challenge_taming_25k] - title = "Maestro Genetista" - description = "Cria 25.000 animales" +title = "Maestro Genetista" +description = "Cria 25.000 animales" # TragOul [advancement.challenge_trag_hits_500] - title = "Saco de Golpes" - description = "Recibe 500 golpes" +title = "Saco de Golpes" +description = "Recibe 500 golpes" [advancement.challenge_trag_hits_5k] - title = "Adicto al Castigo" - description = "Recibe 5.000 golpes" +title = "Adicto al Castigo" +description = "Recibe 5.000 golpes" [advancement.challenge_trag_deaths_10] - title = "Siete Vidas" - description = "Muere 10 veces" +title = "Siete Vidas" +description = "Muere 10 veces" [advancement.challenge_trag_deaths_100] - title = "Pesadilla Recurrente" - description = "Muere 100 veces" +title = "Pesadilla Recurrente" +description = "Muere 100 veces" [advancement.challenge_trag_fire_500] - title = "Victima del Fuego" - description = "Soporta 500 de dano por fuego" +title = "Victima del Fuego" +description = "Soporta 500 de dano por fuego" [advancement.challenge_trag_fire_5k] - title = "Fenix" - description = "Soporta 5.000 de dano por fuego" +title = "Fenix" +description = "Soporta 5.000 de dano por fuego" [advancement.challenge_trag_fall_500] - title = "Prueba de Gravedad" - description = "Soporta 500 de dano por caida" +title = "Prueba de Gravedad" +description = "Soporta 500 de dano por caida" [advancement.challenge_trag_fall_5k] - title = "Velocidad Terminal" - description = "Soporta 5.000 de dano por caida" +title = "Velocidad Terminal" +description = "Soporta 5.000 de dano por caida" # Unarmed [advancement.challenge_unarmed_dmg_1k] - title = "Peleador" - description = "Inflige 1.000 de dano a punos limpios" +title = "Peleador" +description = "Inflige 1.000 de dano a punos limpios" [advancement.challenge_unarmed_dmg_10k] - title = "Artista Marcial" - description = "Inflige 10.000 de dano a punos limpios" +title = "Artista Marcial" +description = "Inflige 10.000 de dano a punos limpios" [advancement.challenge_unarmed_kills_25] - title = "Punos Desnudos" - description = "Mata a 25 criaturas a punos limpios" +title = "Punos Desnudos" +description = "Mata a 25 criaturas a punos limpios" [advancement.challenge_unarmed_kills_250] - title = "Puno de Leyenda" - description = "Mata a 250 criaturas a punos limpios" +title = "Puno de Leyenda" +description = "Mata a 250 criaturas a punos limpios" [advancement.challenge_unarmed_crit_25] - title = "Golpe Critico" - description = "Conecta 25 golpes criticos a punos limpios" +title = "Golpe Critico" +description = "Conecta 25 golpes criticos a punos limpios" [advancement.challenge_unarmed_crit_250] - title = "Puno de Precision" - description = "Conecta 250 golpes criticos a punos limpios" +title = "Puno de Precision" +description = "Conecta 250 golpes criticos a punos limpios" [advancement.challenge_unarmed_heavy_25] - title = "Golpe de Poder" - description = "Conecta 25 golpes pesados a punos limpios (mas de 6 de dano)" +title = "Golpe de Poder" +description = "Conecta 25 golpes pesados a punos limpios (mas de 6 de dano)" [advancement.challenge_unarmed_heavy_250] - title = "Rey del Nocaut" - description = "Conecta 250 golpes pesados a punos limpios (mas de 6 de dano)" +title = "Rey del Nocaut" +description = "Conecta 250 golpes pesados a punos limpios (mas de 6 de dano)" # items [items] [items.bound_ender_peral] - name = "Traslador Relicario" - usage1 = "Shift + Clic izquierdo para vincular" - usage2 = "Clic derecho para acceder al inventario vinculado" +name = "Traslador Relicario" +usage1 = "Shift + Clic izquierdo para vincular" +usage2 = "Clic derecho para acceder al inventario vinculado" [items.bound_eye_of_ender] - name = "Ancla Ocular" - usage1 = "Clic derecho para consumir y teletransportarte a la ubicacion vinculada" - usage2 = "Shift + Clic izquierdo para vincular a un bloque" +name = "Ancla Ocular" +usage1 = "Clic derecho para consumir y teletransportarte a la ubicacion vinculada" +usage2 = "Shift + Clic izquierdo para vincular a un bloque" [items.bound_redstone_torch] - name = "Control Remoto de Redstone" - usage1 = "Clic derecho para crear un pulso de Redstone de 1 tick" - usage2 = "Shift + Clic izquierdo en un bloque 'Objetivo' para vincular" +name = "Control Remoto de Redstone" +usage1 = "Clic derecho para crear un pulso de Redstone de 1 tick" +usage2 = "Shift + Clic izquierdo en un bloque 'Objetivo' para vincular" [items.bound_snowball] - name = "¡Trampa de Telarana!" - usage1 = "Lanza para crear una trampa de telarana temporal en la ubicacion" +name = "¡Trampa de Telarana!" +usage1 = "Lanza para crear una trampa de telarana temporal en la ubicacion" [items.chrono_time_bottle] - name = "Tiempo en una Botella" - usage1 = "Almacena tiempo pasivamente mientras esta en tu inventario" - usage2 = "Clic derecho en bloques con temporizador o animales bebe para gastar tiempo almacenado" - stored = "Tiempo almacenado" +name = "Tiempo en una Botella" +usage1 = "Almacena tiempo pasivamente mientras esta en tu inventario" +usage2 = "Clic derecho en bloques con temporizador o animales bebe para gastar tiempo almacenado" +stored = "Tiempo almacenado" [items.chrono_time_bomb] - name = "Bomba de Tiempo" - usage1 = "Clic derecho para lanzar un rayo crono que crea un campo temporal" +name = "Bomba de Tiempo" +usage1 = "Clic derecho para lanzar un rayo crono que crea un campo temporal" [items.elevator_block] - name = "Bloque de Ascensor" - usage1 = "Salta para teletransportarte hacia arriba" - usage2 = "Agachate para teletransportarte hacia abajo" - usage3 = "Minimo 2 bloques de aire entre los ascensores" +name = "Bloque de Ascensor" +usage1 = "Salta para teletransportarte hacia arriba" +usage2 = "Agachate para teletransportarte hacia abajo" +usage3 = "Minimo 2 bloques de aire entre los ascensores" # snippets [snippets] [snippets.gui] - level = "Nivel" - knowledge = "conocimiento" - power_used = "Poder utilizado" - not_learned = "No aprendido" - xp = "XP para" - welcome = "¡Bienvenido!" - welcome_back = "¡Bienvenido de nuevo!" - xp_bonus_for_time = "XP por" - max_ability_power = "Poder maximo de habilidad" - unlock_this_by_clicking = "Desbloquea esto con Clic derecho: " - back = "Atras" - unlearn_all = "Desaprender todo" - unlearned_all = "Se desaprendio todo" +level = "Nivel" +knowledge = "conocimiento" +power_used = "Poder utilizado" +not_learned = "No aprendido" +xp = "XP para" +welcome = "¡Bienvenido!" +welcome_back = "¡Bienvenido de nuevo!" +xp_bonus_for_time = "XP por" +max_ability_power = "Poder maximo de habilidad" +unlock_this_by_clicking = "Desbloquea esto con Clic derecho: " +back = "Atras" +unlearn_all = "Desaprender todo" +unlearned_all = "Se desaprendio todo" [snippets.adapt_menu] - may_not_unlearn = "NO PUEDES DESAPRENDER" - may_unlearn = "PUEDES APRENDER/DESAPRENDER" - knowledge_cost = "Coste de conocimiento" - knowledge_available = "Conocimiento disponible" - already_learned = "Ya aprendido" - unlearn_refund = "Clic para desaprender y reembolsar" - no_refunds = "HARDCORE, REEMBOLSOS DESACTIVADOS" - knowledge = "conocimiento" - click_learn = "Clic para aprender" - no_knowledge = "(No tienes ningun conocimiento)" - you_only_have = "Solo tienes" - how_to_level_up = "Sube de nivel las habilidades para aumentar tu poder maximo." - not_enough_power = "¡No tienes suficiente poder! Cada nivel de habilidad cuesta 1 poder." - power = "poder" - power_drain = "Consumo de poder" - learned = "Aprendido " - unlearned = "Desaprendido " - activator_block = "Estanteria" +may_not_unlearn = "NO PUEDES DESAPRENDER" +may_unlearn = "PUEDES APRENDER/DESAPRENDER" +knowledge_cost = "Coste de conocimiento" +knowledge_available = "Conocimiento disponible" +already_learned = "Ya aprendido" +unlearn_refund = "Clic para desaprender y reembolsar" +no_refunds = "HARDCORE, REEMBOLSOS DESACTIVADOS" +knowledge = "conocimiento" +click_learn = "Clic para aprender" +no_knowledge = "(No tienes ningun conocimiento)" +you_only_have = "Solo tienes" +how_to_level_up = "Sube de nivel las habilidades para aumentar tu poder maximo." +not_enough_power = "¡No tienes suficiente poder! Cada nivel de habilidad cuesta 1 poder." +power = "poder" +power_drain = "Consumo de poder" +learned = "Aprendido " +unlearned = "Desaprendido " +activator_block = "Estanteria" [snippets.knowledge_orb] - contains = "contiene" - knowledge = "conocimiento" - rightclick = "Clic derecho" - togainknowledge = "para obtener este conocimiento" - knowledge_orb = "Orbe de Conocimiento" +contains = "contiene" +knowledge = "conocimiento" +rightclick = "Clic derecho" +togainknowledge = "para obtener este conocimiento" +knowledge_orb = "Orbe de Conocimiento" [snippets.experience_orb] - contains = "contiene" - xp = "Experiencia" - rightclick = "Clic derecho" - togainxp = "para obtener esta experiencia" - xporb = "Orbe de Experiencia" +contains = "contiene" +xp = "Experiencia" +rightclick = "Clic derecho" +togainxp = "para obtener esta experiencia" +xporb = "Orbe de Experiencia" # skill [skill] [skill.agility] - name = "Agilidad" - icon = "⇉" - description = "La agilidad es la capacidad de moverse rapida y fluidamente ante los obstaculos." +name = "Agilidad" +icon = "⇉" +description = "La agilidad es la capacidad de moverse rapida y fluidamente ante los obstaculos." [skill.architect] - name = "Arquitecto" - icon = "⬧" - description = "Las estructuras son los bloques de construccion del mundo. La realidad esta en tus manos, tuya para controlar." +name = "Arquitecto" +icon = "⬧" +description = "Las estructuras son los bloques de construccion del mundo. La realidad esta en tus manos, tuya para controlar." [skill.axes] - name = "Hachas" - icon = "🪓" - description1 = "Para que talar arboles, cuando podrias cortar " - description2 = "cosas" - description3 = "en su lugar, ¡el mismo resultado!" +name = "Hachas" +icon = "🪓" +description1 = "Para que talar arboles, cuando podrias cortar " +description2 = "cosas" +description3 = "en su lugar, ¡el mismo resultado!" [skill.brewing] - name = "Alquimia" - icon = "❦" - description = "Burbuja doble, burbuja triple, burbuja cuadruple... Aun no puedo poner esta pocion en un caldero" +name = "Alquimia" +icon = "❦" +description = "Burbuja doble, burbuja triple, burbuja cuadruple... Aun no puedo poner esta pocion en un caldero" [skill.blocking] - name = "Bloqueo" - icon = "🛡" - description = "Palos y piedras no romperan tus huesos, pero un escudo si." +name = "Bloqueo" +icon = "🛡" +description = "Palos y piedras no romperan tus huesos, pero un escudo si." [skill.crafting] - name = "Fabricacion" - icon = "⌂" - description = "Sin mas piezas que colocar, ¿por que no hacer otra?" +name = "Fabricacion" +icon = "⌂" +description = "Sin mas piezas que colocar, ¿por que no hacer otra?" [skill.discovery] - name = "Descubrimiento" - icon = "⚛" - description = "A medida que tu percepcion se expande, tu mente se despliega para descubrir aquello que no conocias." +name = "Descubrimiento" +icon = "⚛" +description = "A medida que tu percepcion se expande, tu mente se despliega para descubrir aquello que no conocias." [skill.enchanting] - name = "Encantamiento" - icon = "♰" - description = "¿De que vas? ¿Profecias, visiones, chácharas supersticiosas?" +name = "Encantamiento" +icon = "♰" +description = "¿De que vas? ¿Profecias, visiones, chácharas supersticiosas?" [skill.excavation] - name = "Excavacion" - icon = "ᛳ" - description = "Cava cava cava..." +name = "Excavacion" +icon = "ᛳ" +description = "Cava cava cava..." [skill.herbalism] - name = "Herbalismo" - icon = "⚘" - description = "No encuentro ninguna planta, pero puedo encontrar semillas y... ¿eso es... hierba?" +name = "Herbalismo" +icon = "⚘" +description = "No encuentro ninguna planta, pero puedo encontrar semillas y... ¿eso es... hierba?" [skill.hunter] - name = "Cazador" - icon = "☠" - description = "La caza trata del viaje, no del resultado." +name = "Cazador" +icon = "☠" +description = "La caza trata del viaje, no del resultado." [skill.nether] - name = "Nether" - icon = "₪" - description = "Desde las profundidades del mismisimo Nether." +name = "Nether" +icon = "₪" +description = "Desde las profundidades del mismisimo Nether." [skill.pickaxe] - name = "Pico" - icon = "⛏" - description = "Los enanos son los mineros, pero he aprendido un par de cosas con el tiempo. ¡SOY SUECO!" +name = "Pico" +icon = "⛏" +description = "Los enanos son los mineros, pero he aprendido un par de cosas con el tiempo. ¡SOY SUECO!" [skill.ranged] - name = "A distancia" - icon = "🏹" - description = "La distancia es la clave de la victoria, y la clave de la supervivencia." +name = "A distancia" +icon = "🏹" +description = "La distancia es la clave de la victoria, y la clave de la supervivencia." [skill.rift] - name = "Grieta" - icon = "❍" - description = "La Grieta es un arnes caustico, pero tu has dominado el arnes." +name = "Grieta" +icon = "❍" +description = "La Grieta es un arnes caustico, pero tu has dominado el arnes." [skill.seaborne] - name = "Maritimo" - icon = "🎣" - description = "Con esta habilidad, podras dominar las maravillas del agua." +name = "Maritimo" +icon = "🎣" +description = "Con esta habilidad, podras dominar las maravillas del agua." [skill.stealth] - name = "Sigilo" - icon = "☯" - description = "El arte de lo invisible. Camina entre las sombras." +name = "Sigilo" +icon = "☯" +description = "El arte de lo invisible. Camina entre las sombras." [skill.swords] - name = "Espadas" - icon = "⚔" - description = "¡Por el poder de PiedraGris!" +name = "Espadas" +icon = "⚔" +description = "¡Por el poder de PiedraGris!" [skill.taming] - name = "Domesticacion" - icon = "♥" - description = "Los loros y las abejas... ¿y tu?" +name = "Domesticacion" +icon = "♥" +description = "Los loros y las abejas... ¿y tu?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "La sangre fluye por las venas del universo. Constreñida por tus manos." +name = "TragOul" +icon = "🗡" +description = "La sangre fluye por las venas del universo. Constreñida por tus manos." [skill.chronos] - name = "Cronos" - icon = "🕒" - description = "Da cuerda al reloj del universo, experimenta el fluir. Rompe el reloj, conviertete en el." +name = "Cronos" +icon = "🕒" +description = "Da cuerda al reloj del universo, experimenta el fluir. Rompe el reloj, conviertete en el." [skill.unarmed] - name = "Desarmado" - icon = "»" - description = "Sin arma no significa sin fuerza." +name = "Desarmado" +icon = "»" +description = "Sin arma no significa sin fuerza." # agility [agility] [agility.armor_up] - name = "Blindaje" - description = "¡Consigue mas armadura cuanto mas tiempo esprintees!" - lore1 = "Armadura maxima" - lore2 = "Tiempo de blindaje" - lore = ["Armadura maxima", "Tiempo de blindaje"] +name = "Blindaje" +description = "¡Consigue mas armadura cuanto mas tiempo esprintees!" +lore1 = "Armadura maxima" +lore2 = "Tiempo de blindaje" +lore = ["Armadura maxima", "Tiempo de blindaje"] [agility.ladder_slide] - name = "Deslizamiento en Escalera" - description = "Sube y deslizate por las escaleras mucho mas rapido en ambas direcciones." - lore1 = "Multiplicador de velocidad en escalera" - lore2 = "Velocidad de descenso rapido" - lore = ["Multiplicador de velocidad en escalera", "Velocidad de descenso rapido"] +name = "Deslizamiento en Escalera" +description = "Sube y deslizate por las escaleras mucho mas rapido en ambas direcciones." +lore1 = "Multiplicador de velocidad en escalera" +lore2 = "Velocidad de descenso rapido" +lore = ["Multiplicador de velocidad en escalera", "Velocidad de descenso rapido"] [agility.super_jump] - name = "Super Salto" - description = "Ventaja de altura excepcional." - lore1 = "Altura maxima de salto" - lore2 = "¡Agachate + Salta para hacer un Super Salto!" - lore = ["Altura maxima de salto", "¡Agachate + Salta para hacer un Super Salto!"] +name = "Super Salto" +description = "Ventaja de altura excepcional." +lore1 = "Altura maxima de salto" +lore2 = "¡Agachate + Salta para hacer un Super Salto!" +lore = ["Altura maxima de salto", "¡Agachate + Salta para hacer un Super Salto!"] [agility.wall_jump] - name = "Salto de Pared" - description = "¡Manten Shift en el aire contra una pared para agarrarte y saltar!" - lore1 = "Saltos maximos" - lore2 = "Altura de salto" - lore = ["Saltos maximos", "Altura de salto"] +name = "Salto de Pared" +description = "¡Manten Shift en el aire contra una pared para agarrarte y saltar!" +lore1 = "Saltos maximos" +lore2 = "Altura de salto" +lore = ["Saltos maximos", "Altura de salto"] [agility.wind_up] - name = "Aceleracion" - description = "¡Ve mas rapido cuanto mas tiempo esprintees!" - lore1 = "Velocidad maxima" - lore2 = "Tiempo de aceleracion" - lore = ["Velocidad maxima", "Tiempo de aceleracion"] +name = "Aceleracion" +description = "¡Ve mas rapido cuanto mas tiempo esprintees!" +lore1 = "Velocidad maxima" +lore2 = "Tiempo de aceleracion" +lore = ["Velocidad maxima", "Tiempo de aceleracion"] # architect [architect] [architect.elevator] - name = "Ascensor" - description = "¡Esto te permite construir un ascensor para teletransportarte verticalmente rapido!" - lore1 = "Desbloquea la receta del ascensor: X=LANA, Y=PERLA DE ENDER" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Desbloquea la receta del ascensor: X=LANA, Y=PERLA DE ENDER", "XXX", "XYX", "XXX"] +name = "Ascensor" +description = "¡Esto te permite construir un ascensor para teletransportarte verticalmente rapido!" +lore1 = "Desbloquea la receta del ascensor: X=LANA, Y=PERLA DE ENDER" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Desbloquea la receta del ascensor: X=LANA, Y=PERLA DE ENDER", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Cimiento Magico" - description = "¡Esto te permite agacharte y colocar un cimiento temporal debajo de ti!" - lore1 = "Crea magicamente: " - lore2 = "¡Bloques debajo de ti!" - lore = ["Crea magicamente: ", "¡Bloques debajo de ti!"] +name = "Cimiento Magico" +description = "¡Esto te permite agacharte y colocar un cimiento temporal debajo de ti!" +lore1 = "Crea magicamente: " +lore2 = "¡Bloques debajo de ti!" +lore = ["Crea magicamente: ", "¡Bloques debajo de ti!"] [architect.glass] - name = "Vidrio con Toque de Seda" - description = "¡Esto te permite evitar la perdida de bloques de vidrio cuando los rompes con la mano vacia!" - lore1 = "Tus manos obtienen toque de seda para el vidrio" - lore = ["Tus manos obtienen toque de seda para el vidrio"] +name = "Vidrio con Toque de Seda" +description = "¡Esto te permite evitar la perdida de bloques de vidrio cuando los rompes con la mano vacia!" +lore1 = "Tus manos obtienen toque de seda para el vidrio" +lore = ["Tus manos obtienen toque de seda para el vidrio"] [architect.wireless_redstone] - name = "Control Remoto de Redstone" - description = "¡Esto te permite usar una antorcha de redstone para activar redstone de forma remota!" - lore1 = "Objetivo + Antorcha de Redstone + Perla de Ender = 1 Control Remoto de Redstone" - lore = ["Objetivo + Antorcha de Redstone + Perla de Ender = 1 Control Remoto de Redstone"] +name = "Control Remoto de Redstone" +description = "¡Esto te permite usar una antorcha de redstone para activar redstone de forma remota!" +lore1 = "Objetivo + Antorcha de Redstone + Perla de Ender = 1 Control Remoto de Redstone" +lore = ["Objetivo + Antorcha de Redstone + Perla de Ender = 1 Control Remoto de Redstone"] [architect.placement] - name = "Varita de Constructor" - description = "¡Te permite colocar multiples bloques a la vez! Para activar, agachate y sostén un bloque que coincida con el bloque al que miras y coloca. Ten en cuenta que puede que necesites moverte un poco para activar el limite de las cajas." - lore1 = "Necesitas" - lore2 = "bloques en tu mano para colocar esto" - lore3 = "Una Varita de Constructor de Material" - lore = ["Necesitas", "bloques en tu mano para colocar esto", "Una Varita de Constructor de Material"] +name = "Varita de Constructor" +description = "¡Te permite colocar multiples bloques a la vez! Para activar, agachate y sostén un bloque que coincida con el bloque al que miras y coloca. Ten en cuenta que puede que necesites moverte un poco para activar el limite de las cajas." +lore1 = "Necesitas" +lore2 = "bloques en tu mano para colocar esto" +lore3 = "Una Varita de Constructor de Material" +lore = ["Necesitas", "bloques en tu mano para colocar esto", "Una Varita de Constructor de Material"] # axe [axe] [axe.chop] - name = "Corte de Hacha" - description = "¡Tala arboles haciendo clic derecho en el tronco base!" - lore1 = "Bloques por corte" - lore2 = "Tiempo de recarga del corte" - lore3 = "Desgaste de herramienta" - lore = ["Bloques por corte", "Tiempo de recarga del corte", "Desgaste de herramienta"] +name = "Corte de Hacha" +description = "¡Tala arboles haciendo clic derecho en el tronco base!" +lore1 = "Bloques por corte" +lore2 = "Tiempo de recarga del corte" +lore3 = "Desgaste de herramienta" +lore = ["Bloques por corte", "Tiempo de recarga del corte", "Desgaste de herramienta"] [axe.log_swap] - name = "Intercambiador de Troncos de Lucy" - description = "¡Cambia el tipo de troncos en una mesa de crafteo!" - lore1 = "8 troncos de cualquier tipo + 1 brote = 8 troncos del tipo del brote" - lore = ["8 troncos de cualquier tipo + 1 brote = 8 troncos del tipo del brote"] +name = "Intercambiador de Troncos de Lucy" +description = "¡Cambia el tipo de troncos en una mesa de crafteo!" +lore1 = "8 troncos de cualquier tipo + 1 brote = 8 troncos del tipo del brote" +lore = ["8 troncos de cualquier tipo + 1 brote = 8 troncos del tipo del brote"] [axe.drop_to_inventory] - name = "Hacha: Soltar al Inventario" +name = "Hacha: Soltar al Inventario" [axe.ground_smash] - name = "Golpe de Tierra con Hacha" - description = "Salta, luego agachate y golpea a todos los enemigos cercanos." - lore1 = "Dano" - lore2 = "Radio de bloques" - lore3 = "Fuerza" - lore4 = "Tiempo de recarga del golpe" - lore = ["Dano", "Radio de bloques", "Fuerza", "Tiempo de recarga del golpe"] +name = "Golpe de Tierra con Hacha" +description = "Salta, luego agachate y golpea a todos los enemigos cercanos." +lore1 = "Dano" +lore2 = "Radio de bloques" +lore3 = "Fuerza" +lore4 = "Tiempo de recarga del golpe" +lore = ["Dano", "Radio de bloques", "Fuerza", "Tiempo de recarga del golpe"] [axe.leaf_miner] - name = "Minero de Hojas" - description = "¡Te permite romper hojas en masa de una vez!" - lore1 = "Agachate y mina HOJAS" - lore2 = "Rango de minado de hojas" - lore3 = "No obtendras los drops de las hojas (prevencion de exploits)" - lore = ["Agachate y mina HOJAS", "Rango de minado de hojas", "No obtendras los drops de las hojas (prevencion de exploits)"] +name = "Minero de Hojas" +description = "¡Te permite romper hojas en masa de una vez!" +lore1 = "Agachate y mina HOJAS" +lore2 = "Rango de minado de hojas" +lore3 = "No obtendras los drops de las hojas (prevencion de exploits)" +lore = ["Agachate y mina HOJAS", "Rango de minado de hojas", "No obtendras los drops de las hojas (prevencion de exploits)"] [axe.wood_miner] - name = "Minero de Madera" - description = "¡Te permite romper madera en masa de una vez!" - lore1 = "Agachate y mina MADERA/TRONCOS (no tablones)" - lore2 = "Rango de minado de madera" - lore3 = "Funciona con Soltar al Inventario" - lore = ["Agachate y mina MADERA/TRONCOS (no tablones)", "Rango de minado de madera", "Funciona con Soltar al Inventario"] +name = "Minero de Madera" +description = "¡Te permite romper madera en masa de una vez!" +lore1 = "Agachate y mina MADERA/TRONCOS (no tablones)" +lore2 = "Rango de minado de madera" +lore3 = "Funciona con Soltar al Inventario" +lore = ["Agachate y mina MADERA/TRONCOS (no tablones)", "Rango de minado de madera", "Funciona con Soltar al Inventario"] # brewing [brewing] [brewing.lingering] - name = "Brebaje Persistente" - description = "¡Las pociones preparadas duran mas!" - lore1 = "Duracion" - lore2 = "Duracion" - lore = ["Duracion", "Duracion"] +name = "Brebaje Persistente" +description = "¡Las pociones preparadas duran mas!" +lore1 = "Duracion" +lore2 = "Duracion" +lore = ["Duracion", "Duracion"] [brewing.super_heated] - name = "Brebaje Supercaliente" - description = "Los soportes para pociones funcionan mas rapido cuanto mas calientes esten." - lore1 = "Por bloque de fuego en contacto" - lore2 = "Por bloque de lava en contacto" - lore = ["Por bloque de fuego en contacto", "Por bloque de lava en contacto"] +name = "Brebaje Supercaliente" +description = "Los soportes para pociones funcionan mas rapido cuanto mas calientes esten." +lore1 = "Por bloque de fuego en contacto" +lore2 = "Por bloque de lava en contacto" +lore = ["Por bloque de fuego en contacto", "Por bloque de lava en contacto"] [brewing.darkness] - name = "Oscuridad Embotellada" - description = "No estoy seguro de por que necesitas esto, ¡pero aqui tienes!" - lore1 = "Pocion de Vision Nocturna + Concreto Negro = Pocion de Oscuridad (30 segundos)" - lore2 = "¡Cabe senalar que esto impide que el usuario esprintee!" - lore = ["Pocion de Vision Nocturna + Concreto Negro = Pocion de Oscuridad (30 segundos)", "¡Cabe senalar que esto impide que el usuario esprintee!"] +name = "Oscuridad Embotellada" +description = "No estoy seguro de por que necesitas esto, ¡pero aqui tienes!" +lore1 = "Pocion de Vision Nocturna + Concreto Negro = Pocion de Oscuridad (30 segundos)" +lore2 = "¡Cabe senalar que esto impide que el usuario esprintee!" +lore = ["Pocion de Vision Nocturna + Concreto Negro = Pocion de Oscuridad (30 segundos)", "¡Cabe senalar que esto impide que el usuario esprintee!"] [brewing.haste] - name = "Prisa Embotellada" - description = "Cuando la eficiencia no es suficiente" - lore1 = "Pocion de Velocidad + Fragmento de Amatista = Pocion de Prisa (60 segundos)" - lore2 = "Pocion de Velocidad + Bloque de Amatista = Pocion de Prisa-2 (30 segundos)" - lore = ["Pocion de Velocidad + Fragmento de Amatista = Pocion de Prisa (60 segundos)", "Pocion de Velocidad + Bloque de Amatista = Pocion de Prisa-2 (30 segundos)"] +name = "Prisa Embotellada" +description = "Cuando la eficiencia no es suficiente" +lore1 = "Pocion de Velocidad + Fragmento de Amatista = Pocion de Prisa (60 segundos)" +lore2 = "Pocion de Velocidad + Bloque de Amatista = Pocion de Prisa-2 (30 segundos)" +lore = ["Pocion de Velocidad + Fragmento de Amatista = Pocion de Prisa (60 segundos)", "Pocion de Velocidad + Bloque de Amatista = Pocion de Prisa-2 (30 segundos)"] [brewing.absorption] - name = "Absorcion Embotellada" - description = "¡Endurece el cuerpo!" - lore1 = "Curacion Instantanea + Cuarzo = Pocion de Absorcion (60 segundos)" - lore2 = "Curacion Instantanea + Bloque de Cuarzo = Pocion de Absorcion-2 (30 segundos)" - lore = ["Curacion Instantanea + Cuarzo = Pocion de Absorcion (60 segundos)", "Curacion Instantanea + Bloque de Cuarzo = Pocion de Absorcion-2 (30 segundos)"] +name = "Absorcion Embotellada" +description = "¡Endurece el cuerpo!" +lore1 = "Curacion Instantanea + Cuarzo = Pocion de Absorcion (60 segundos)" +lore2 = "Curacion Instantanea + Bloque de Cuarzo = Pocion de Absorcion-2 (30 segundos)" +lore = ["Curacion Instantanea + Cuarzo = Pocion de Absorcion (60 segundos)", "Curacion Instantanea + Bloque de Cuarzo = Pocion de Absorcion-2 (30 segundos)"] [brewing.fatigue] - name = "Fatiga Embotellada" - description = "¡Debilita el cuerpo!" - lore1 = "Pocion de Debilidad + Bola de Slime = Pocion de Fatiga (30 segundos)" - lore2 = "Pocion de Debilidad + Bloque de Slime = Pocion de Fatiga-2 (15 segundos)" - lore = ["Pocion de Debilidad + Bola de Slime = Pocion de Fatiga (30 segundos)", "Pocion de Debilidad + Bloque de Slime = Pocion de Fatiga-2 (15 segundos)"] +name = "Fatiga Embotellada" +description = "¡Debilita el cuerpo!" +lore1 = "Pocion de Debilidad + Bola de Slime = Pocion de Fatiga (30 segundos)" +lore2 = "Pocion de Debilidad + Bloque de Slime = Pocion de Fatiga-2 (15 segundos)" +lore = ["Pocion de Debilidad + Bola de Slime = Pocion de Fatiga (30 segundos)", "Pocion de Debilidad + Bloque de Slime = Pocion de Fatiga-2 (15 segundos)"] [brewing.hunger] - name = "Hambre Embotellada" - description = "¡Alimenta al insaciable!" - lore1 = "Pocion Rara + Carne Podrida = Pocion de Hambre (30 segundos)" - lore2 = "Pocion de Debilidad + Carne Podrida = Pocion de Hambre-3 (15 segundos)" - lore = ["Pocion Rara + Carne Podrida = Pocion de Hambre (30 segundos)", "Pocion de Debilidad + Carne Podrida = Pocion de Hambre-3 (15 segundos)"] +name = "Hambre Embotellada" +description = "¡Alimenta al insaciable!" +lore1 = "Pocion Rara + Carne Podrida = Pocion de Hambre (30 segundos)" +lore2 = "Pocion de Debilidad + Carne Podrida = Pocion de Hambre-3 (15 segundos)" +lore = ["Pocion Rara + Carne Podrida = Pocion de Hambre (30 segundos)", "Pocion de Debilidad + Carne Podrida = Pocion de Hambre-3 (15 segundos)"] [brewing.nausea] - name = "Nauseas Embotelladas" - description = "¡Me das asco!" - lore1 = "Pocion Rara + Champiñon Marron = Pocion de Nauseas (16 segundos)" - lore2 = "Pocion Rara + Hongo Carmesi = Pocion de Nauseas-2 (8 segundos)" - lore = ["Pocion Rara + Champiñon Marron = Pocion de Nauseas (16 segundos)", "Pocion Rara + Hongo Carmesi = Pocion de Nauseas-2 (8 segundos)"] +name = "Nauseas Embotelladas" +description = "¡Me das asco!" +lore1 = "Pocion Rara + Champiñon Marron = Pocion de Nauseas (16 segundos)" +lore2 = "Pocion Rara + Hongo Carmesi = Pocion de Nauseas-2 (8 segundos)" +lore = ["Pocion Rara + Champiñon Marron = Pocion de Nauseas (16 segundos)", "Pocion Rara + Hongo Carmesi = Pocion de Nauseas-2 (8 segundos)"] [brewing.blindness] - name = "Ceguera Embotellada" - description = "Eres una persona horrible..." - lore1 = "Pocion Rara + Saco de Tinta = Pocion de Ceguera (30 segundos)" - lore2 = "Pocion Rara + Saco de Tinta Brillante = Pocion de Ceguera-2 (15 segundos)" - lore = ["Pocion Rara + Saco de Tinta = Pocion de Ceguera (30 segundos)", "Pocion Rara + Saco de Tinta Brillante = Pocion de Ceguera-2 (15 segundos)"] +name = "Ceguera Embotellada" +description = "Eres una persona horrible..." +lore1 = "Pocion Rara + Saco de Tinta = Pocion de Ceguera (30 segundos)" +lore2 = "Pocion Rara + Saco de Tinta Brillante = Pocion de Ceguera-2 (15 segundos)" +lore = ["Pocion Rara + Saco de Tinta = Pocion de Ceguera (30 segundos)", "Pocion Rara + Saco de Tinta Brillante = Pocion de Ceguera-2 (15 segundos)"] [brewing.resistance] - name = "Resistencia Embotellada" - description = "¡Fortificacion en su maxima expresion!" - lore1 = "Pocion Rara + Lingote de Hierro = Pocion de Resistencia (60 segundos)" - lore2 = "Pocion Rara + Bloque de Hierro = Pocion de Resistencia-2 (30 segundos)" - lore = ["Pocion Rara + Lingote de Hierro = Pocion de Resistencia (60 segundos)", "Pocion Rara + Bloque de Hierro = Pocion de Resistencia-2 (30 segundos)"] +name = "Resistencia Embotellada" +description = "¡Fortificacion en su maxima expresion!" +lore1 = "Pocion Rara + Lingote de Hierro = Pocion de Resistencia (60 segundos)" +lore2 = "Pocion Rara + Bloque de Hierro = Pocion de Resistencia-2 (30 segundos)" +lore = ["Pocion Rara + Lingote de Hierro = Pocion de Resistencia (60 segundos)", "Pocion Rara + Bloque de Hierro = Pocion de Resistencia-2 (30 segundos)"] [brewing.health_boost] - name = "Vida Embotellada" - description = "Cuando la salud maxima no es suficiente..." - lore1 = "Pocion de Curacion Instantanea + Manzana Dorada = Pocion de Mejora de Salud (120 segundos)" - lore2 = "Pocion de Curacion Instantanea + Manzana Dorada Encantada = Pocion de Mejora de Salud-2 (120 segundos)" - lore = ["Pocion de Curacion Instantanea + Manzana Dorada = Pocion de Mejora de Salud (120 segundos)", "Pocion de Curacion Instantanea + Manzana Dorada Encantada = Pocion de Mejora de Salud-2 (120 segundos)"] +name = "Vida Embotellada" +description = "Cuando la salud maxima no es suficiente..." +lore1 = "Pocion de Curacion Instantanea + Manzana Dorada = Pocion de Mejora de Salud (120 segundos)" +lore2 = "Pocion de Curacion Instantanea + Manzana Dorada Encantada = Pocion de Mejora de Salud-2 (120 segundos)" +lore = ["Pocion de Curacion Instantanea + Manzana Dorada = Pocion de Mejora de Salud (120 segundos)", "Pocion de Curacion Instantanea + Manzana Dorada Encantada = Pocion de Mejora de Salud-2 (120 segundos)"] [brewing.decay] - name = "Deterioro Embotellado" - description = "¿Quien sabia que el detritus seria tan util?" - lore1 = "Pocion de Debilidad + Patata Venenosa = Pocion de Wither (16 segundos)" - lore2 = "Pocion de Debilidad + Raices Carmesi = Pocion de Wither-2 (8 segundos)" - lore = ["Pocion de Debilidad + Patata Venenosa = Pocion de Wither (16 segundos)", "Pocion de Debilidad + Raices Carmesi = Pocion de Wither-2 (8 segundos)"] +name = "Deterioro Embotellado" +description = "¿Quien sabia que el detritus seria tan util?" +lore1 = "Pocion de Debilidad + Patata Venenosa = Pocion de Wither (16 segundos)" +lore2 = "Pocion de Debilidad + Raices Carmesi = Pocion de Wither-2 (8 segundos)" +lore = ["Pocion de Debilidad + Patata Venenosa = Pocion de Wither (16 segundos)", "Pocion de Debilidad + Raices Carmesi = Pocion de Wither-2 (8 segundos)"] [brewing.saturation] - name = "Saturacion Embotellada" - description = "Sabes... ni siquiera tengo hambre..." - lore1 = "Pocion de Regeneracion + Patata al Horno = Pocion de Saturacion" - lore2 = "Pocion de Regeneracion + Fardo de Heno = Pocion de Saturacion-2" - lore = ["Pocion de Regeneracion + Patata al Horno = Pocion de Saturacion", "Pocion de Regeneracion + Fardo de Heno = Pocion de Saturacion-2"] +name = "Saturacion Embotellada" +description = "Sabes... ni siquiera tengo hambre..." +lore1 = "Pocion de Regeneracion + Patata al Horno = Pocion de Saturacion" +lore2 = "Pocion de Regeneracion + Fardo de Heno = Pocion de Saturacion-2" +lore = ["Pocion de Regeneracion + Patata al Horno = Pocion de Saturacion", "Pocion de Regeneracion + Fardo de Heno = Pocion de Saturacion-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Cadenas de Mefistofeles" - description = "Te permite fabricar armadura de cota de malla" - lore1 = "La receta de fabricacion es la misma que cualquier otra, pero con pepitas de hierro en su lugar" - lore = ["La receta de fabricacion es la misma que cualquier otra, pero con pepitas de hierro en su lugar"] +name = "Cadenas de Mefistofeles" +description = "Te permite fabricar armadura de cota de malla" +lore1 = "La receta de fabricacion es la misma que cualquier otra, pero con pepitas de hierro en su lugar" +lore = ["La receta de fabricacion es la misma que cualquier otra, pero con pepitas de hierro en su lugar"] [blocking.horse_armorer] - name = "Armadura de Caballo Fabricable" - description = "Te permite fabricar armadura de caballo" - lore1 = "Rodea una silla de montar con el material que quieras usar para fabricar la armadura" - lore = ["Rodea una silla de montar con el material que quieras usar para fabricar la armadura"] +name = "Armadura de Caballo Fabricable" +description = "Te permite fabricar armadura de caballo" +lore1 = "Rodea una silla de montar con el material que quieras usar para fabricar la armadura" +lore = ["Rodea una silla de montar con el material que quieras usar para fabricar la armadura"] [blocking.saddle_crafter] - name = "Silla de Montar Fabricable" - description = "Fabrica una silla de montar con cuero" - lore1 = "Receta: 5 Cuero:" - lore = ["Receta: 5 Cuero:"] +name = "Silla de Montar Fabricable" +description = "Fabrica una silla de montar con cuero" +lore1 = "Receta: 5 Cuero:" +lore = ["Receta: 5 Cuero:"] [blocking.multi_armor] - name = "Multi-Armadura" - description = "Vincula elitros a la armadura" - lore1 = "Esta es una habilidad increible para viajar." - lore2 = "¡Fusiona y cambia dinamicamente Armadura/Elitros sobre la marcha!" - lore3 = "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario." - lore4 = "Para desvincular la armadura, suelta el objeto agachado y se desmontara." - lore5 = "Si tu Multi-Armadura es destruida, perderas todos los objetos dentro de ella." - lore6 = "No necesito armadura, me decepciona..." - lore = ["Esta es una habilidad increible para viajar.", "¡Fusiona y cambia dinamicamente Armadura/Elitros sobre la marcha!", "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario.", "Para desvincular la armadura, suelta el objeto agachado y se desmontara.", "Si tu Multi-Armadura es destruida, perderas todos los objetos dentro de ella.", "No necesito armadura, me decepciona..."] +name = "Multi-Armadura" +description = "Vincula elitros a la armadura" +lore1 = "Esta es una habilidad increible para viajar." +lore2 = "¡Fusiona y cambia dinamicamente Armadura/Elitros sobre la marcha!" +lore3 = "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario." +lore4 = "Para desvincular la armadura, suelta el objeto agachado y se desmontara." +lore5 = "Si tu Multi-Armadura es destruida, perderas todos los objetos dentro de ella." +lore6 = "No necesito armadura, me decepciona..." +lore = ["Esta es una habilidad increible para viajar.", "¡Fusiona y cambia dinamicamente Armadura/Elitros sobre la marcha!", "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario.", "Para desvincular la armadura, suelta el objeto agachado y se desmontara.", "Si tu Multi-Armadura es destruida, perderas todos los objetos dentro de ella.", "No necesito armadura, me decepciona..."] # crafting [crafting] [crafting.deconstruction] - name = "Deconstruccion" - description = "¡Deconstruye bloques y objetos en componentes basicos recuperables!" - lore1 = "Suelta cualquier objeto al suelo." - lore2 = "Luego, agachate y haz clic derecho con tijeras" - lore = ["Suelta cualquier objeto al suelo.", "Luego, agachate y haz clic derecho con tijeras"] +name = "Deconstruccion" +description = "¡Deconstruye bloques y objetos en componentes basicos recuperables!" +lore1 = "Suelta cualquier objeto al suelo." +lore2 = "Luego, agachate y haz clic derecho con tijeras" +lore = ["Suelta cualquier objeto al suelo.", "Luego, agachate y haz clic derecho con tijeras"] [crafting.xp] - name = "XP de Fabricacion" - description = "Gana XP pasiva al fabricar" - lore1 = "Gana XP al fabricar" - lore = ["Gana XP al fabricar"] +name = "XP de Fabricacion" +description = "Gana XP pasiva al fabricar" +lore1 = "Gana XP al fabricar" +lore = ["Gana XP al fabricar"] [crafting.reconstruction] - name = "Reconstruccion de Minerales" - description = "¡Recrea minerales a partir de sus componentes basicos!" - lore1 = "8 de los drops y 1 anfitrion = 1 mineral (sin forma)" - lore2 = "Los drops deben estar fundidos (si aplica)" - lore3 = "No incluye: Restos, Cuarzo, Esmeraldas, etc..." - lore4 = "Anfitrion = Envoltorio. ej: Piedra, Netherrack, Pizarra Profunda" - lore = ["8 de los drops y 1 anfitrion = 1 mineral (sin forma)", "Los drops deben estar fundidos (si aplica)", "No incluye: Restos, Cuarzo, Esmeraldas, etc...", "Anfitrion = Envoltorio. ej: Piedra, Netherrack, Pizarra Profunda"] +name = "Reconstruccion de Minerales" +description = "¡Recrea minerales a partir de sus componentes basicos!" +lore1 = "8 de los drops y 1 anfitrion = 1 mineral (sin forma)" +lore2 = "Los drops deben estar fundidos (si aplica)" +lore3 = "No incluye: Restos, Cuarzo, Esmeraldas, etc..." +lore4 = "Anfitrion = Envoltorio. ej: Piedra, Netherrack, Pizarra Profunda" +lore = ["8 de los drops y 1 anfitrion = 1 mineral (sin forma)", "Los drops deben estar fundidos (si aplica)", "No incluye: Restos, Cuarzo, Esmeraldas, etc...", "Anfitrion = Envoltorio. ej: Piedra, Netherrack, Pizarra Profunda"] [crafting.leather] - name = "Cuero Fabricable" - description = "Fabrica cuero a partir de carne podrida" - lore1 = "¡Solo tirala (carne podrida) en la fogata!" - lore = ["¡Solo tirala (carne podrida) en la fogata!"] +name = "Cuero Fabricable" +description = "Fabrica cuero a partir de carne podrida" +lore1 = "¡Solo tirala (carne podrida) en la fogata!" +lore = ["¡Solo tirala (carne podrida) en la fogata!"] [crafting.backpacks] - name = "¡Mochilas de Boutilier!" - description = "¡Esto simplemente trae el Bundle de Mojang al juego!" - lore1 = "Necesitas estar en Supervivencia para usar esto" - lore2 = "XLX : Cuero, Rienda, Cuero" - lore3 = "XSX : Cuero, Barril, Cuero" - lore4 = "XCX : Cuero, Cofre, Cuero" - lore = ["Necesitas estar en Supervivencia para usar esto", "XLX : Cuero, Rienda, Cuero", "XSX : Cuero, Barril, Cuero", "XCX : Cuero, Cofre, Cuero"] +name = "¡Mochilas de Boutilier!" +description = "¡Esto simplemente trae el Bundle de Mojang al juego!" +lore1 = "Necesitas estar en Supervivencia para usar esto" +lore2 = "XLX : Cuero, Rienda, Cuero" +lore3 = "XSX : Cuero, Barril, Cuero" +lore4 = "XCX : Cuero, Cofre, Cuero" +lore = ["Necesitas estar en Supervivencia para usar esto", "XLX : Cuero, Rienda, Cuero", "XSX : Cuero, Barril, Cuero", "XCX : Cuero, Cofre, Cuero"] [crafting.stations] - name = "¡Mesas Portatiles!" - description = "¡Usa una mesa en la palma de tu mano!" - lore2 = "¡CUALQUIER OBJETO QUE OLVIDES EN LA MESA AL CERRARLA SE PIERDE PARA SIEMPRE!" - lore3 = "Mesas validas: Yunque, Fabricacion, Piedra de Afilar, Cartografia, Cortapiedras, Telar" - lore = ["¡CUALQUIER OBJETO QUE OLVIDES EN LA MESA AL CERRARLA SE PIERDE PARA SIEMPRE!", "Mesas validas: Yunque, Fabricacion, Piedra de Afilar, Cartografia, Cortapiedras, Telar"] +name = "¡Mesas Portatiles!" +description = "¡Usa una mesa en la palma de tu mano!" +lore2 = "¡CUALQUIER OBJETO QUE OLVIDES EN LA MESA AL CERRARLA SE PIERDE PARA SIEMPRE!" +lore3 = "Mesas validas: Yunque, Fabricacion, Piedra de Afilar, Cartografia, Cortapiedras, Telar" +lore = ["¡CUALQUIER OBJETO QUE OLVIDES EN LA MESA AL CERRARLA SE PIERDE PARA SIEMPRE!", "Mesas validas: Yunque, Fabricacion, Piedra de Afilar, Cartografia, Cortapiedras, Telar"] [crafting.skulls] - name = "¡Calaveras fabricables!" - description = "¡Usando materiales puedes fabricar calaveras de mobs!" - lore1 = "Rodea un bloque de hueso con lo siguiente para obtener una calavera:" - lore2 = "Zombi: Carne Podrida" - lore3 = "Esqueleto: Hueso" - lore4 = "Creeper: Polvora" - lore5 = "Wither: Ladrillo del Nether" - lore6 = "Dragon: Aliento de Dragon" - lore = ["Rodea un bloque de hueso con lo siguiente para obtener una calavera:", "Zombi: Carne Podrida", "Esqueleto: Hueso", "Creeper: Polvora", "Wither: Ladrillo del Nether", "Dragon: Aliento de Dragon"] +name = "¡Calaveras fabricables!" +description = "¡Usando materiales puedes fabricar calaveras de mobs!" +lore1 = "Rodea un bloque de hueso con lo siguiente para obtener una calavera:" +lore2 = "Zombi: Carne Podrida" +lore3 = "Esqueleto: Hueso" +lore4 = "Creeper: Polvora" +lore5 = "Wither: Ladrillo del Nether" +lore6 = "Dragon: Aliento de Dragon" +lore = ["Rodea un bloque de hueso con lo siguiente para obtener una calavera:", "Zombi: Carne Podrida", "Esqueleto: Hueso", "Creeper: Polvora", "Wither: Ladrillo del Nether", "Dragon: Aliento de Dragon"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Tiempo en una Botella" - description = "Lleva una botella temporal que almacena tiempo y gastalo para acelerar bloques con temporizador, cultivables y entidades envejecibles como animales bebe. Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio." - lore1 = "Segundos almacenados cargados cada tick" - lore2 = "Aceleracion temporal por segundo almacenado" - lore3 = "Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio" - lore = ["Segundos almacenados cargados cada tick", "Aceleracion temporal por segundo almacenado", "Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio"] +name = "Tiempo en una Botella" +description = "Lleva una botella temporal que almacena tiempo y gastalo para acelerar bloques con temporizador, cultivables y entidades envejecibles como animales bebe. Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio." +lore1 = "Segundos almacenados cargados cada tick" +lore2 = "Aceleracion temporal por segundo almacenado" +lore3 = "Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio" +lore = ["Segundos almacenados cargados cada tick", "Aceleracion temporal por segundo almacenado", "Receta (Sin forma): Pocion de Velocidad + Reloj + Frasco de Vidrio"] [chronos.aberrant_touch] - name = "Toque Aberrante" - description = "Los ataques cuerpo a cuerpo aplican lentitud acumulable a costa de hambre, con limites estrictos en PvP, y inmovilizan al objetivo a 5 acumulaciones." - lore1 = "Los ataques cuerpo a cuerpo aplican lentitud acumulable" - lore2 = "Duracion maxima de lentitud en PvE" - lore3 = "Amplificador maximo de lentitud en PvP" - lore = ["Los ataques cuerpo a cuerpo aplican lentitud acumulable", "Duracion maxima de lentitud en PvE", "Amplificador maximo de lentitud en PvP"] +name = "Toque Aberrante" +description = "Los ataques cuerpo a cuerpo aplican lentitud acumulable a costa de hambre, con limites estrictos en PvP, y inmovilizan al objetivo a 5 acumulaciones." +lore1 = "Los ataques cuerpo a cuerpo aplican lentitud acumulable" +lore2 = "Duracion maxima de lentitud en PvE" +lore3 = "Amplificador maximo de lentitud en PvP" +lore = ["Los ataques cuerpo a cuerpo aplican lentitud acumulable", "Duracion maxima de lentitud en PvE", "Amplificador maximo de lentitud en PvP"] [chronos.instant_recall] - name = "Retorno Instantaneo" - description = "Haz clic izquierdo o derecho con un reloj en mano para rebobinar a una instantanea reciente con salud y hambre restauradas." - lore1 = "Duracion del rebobinado" - lore2 = "Tiempo de recarga" - lore3 = "Sin reversion de inventario" - lore = ["Duracion del rebobinado", "Tiempo de recarga", "Sin reversion de inventario"] +name = "Retorno Instantaneo" +description = "Haz clic izquierdo o derecho con un reloj en mano para rebobinar a una instantanea reciente con salud y hambre restauradas." +lore1 = "Duracion del rebobinado" +lore2 = "Tiempo de recarga" +lore3 = "Sin reversion de inventario" +lore = ["Duracion del rebobinado", "Tiempo de recarga", "Sin reversion de inventario"] [chronos.time_bomb] - name = "Bomba de Tiempo" - description = "Lanza una bomba crono fabricada que crea un campo temporal, ralentiza entidades y congela proyectiles." - lore1 = "Radio del campo temporal" - lore2 = "Duracion del campo temporal" - lore3 = "Tiempo de recarga de la bomba" - lore4 = "Receta (Sin forma): Reloj + Bola de Nieve + Diamante + Arena" - lore = ["Radio del campo temporal", "Duracion del campo temporal", "Tiempo de recarga de la bomba", "Receta (Sin forma): Reloj + Bola de Nieve + Diamante + Arena"] +name = "Bomba de Tiempo" +description = "Lanza una bomba crono fabricada que crea un campo temporal, ralentiza entidades y congela proyectiles." +lore1 = "Radio del campo temporal" +lore2 = "Duracion del campo temporal" +lore3 = "Tiempo de recarga de la bomba" +lore4 = "Receta (Sin forma): Reloj + Bola de Nieve + Diamante + Arena" +lore = ["Radio del campo temporal", "Duracion del campo temporal", "Tiempo de recarga de la bomba", "Receta (Sin forma): Reloj + Bola de Nieve + Diamante + Arena"] # discovery [discovery] [discovery.armor] - name = "Armadura del Mundo" - description = "Armadura pasiva dependiendo de la dureza de los bloques cercanos." - lore1 = "Armadura pasiva" - lore2 = "Basada en la dureza de los bloques cercanos" - lore3 = "Fuerza de armadura:" - lore = ["Armadura pasiva", "Basada en la dureza de los bloques cercanos", "Fuerza de armadura:"] +name = "Armadura del Mundo" +description = "Armadura pasiva dependiendo de la dureza de los bloques cercanos." +lore1 = "Armadura pasiva" +lore2 = "Basada en la dureza de los bloques cercanos" +lore3 = "Fuerza de armadura:" +lore = ["Armadura pasiva", "Basada en la dureza de los bloques cercanos", "Fuerza de armadura:"] [discovery.unity] - name = "Unidad Experimental" - description = "Recoger orbes de experiencia anade XP a habilidades aleatorias." - lore1 = "XP " - lore2 = "Por orbe" - lore = ["XP ", "Por orbe"] +name = "Unidad Experimental" +description = "Recoger orbes de experiencia anade XP a habilidades aleatorias." +lore1 = "XP " +lore2 = "Por orbe" +lore = ["XP ", "Por orbe"] [discovery.resist] - name = "Resistencia Experimental" - description = "Consume experiencia para mitigar dano solo cuando un golpe te dejaria por debajo de 5 corazones o te mataria." - lore0 = "Se activa solo con salud critica (<= 5 corazones) una vez cada 15 segundos" - lore1 = " Dano reducido" - lore2 = "experiencia drenada" - lore = ["Se activa solo con salud critica (<= 5 corazones) una vez cada 15 segundos", " Dano reducido", "experiencia drenada"] +name = "Resistencia Experimental" +description = "Consume experiencia para mitigar dano solo cuando un golpe te dejaria por debajo de 5 corazones o te mataria." +lore0 = "Se activa solo con salud critica (<= 5 corazones) una vez cada 15 segundos" +lore1 = " Dano reducido" +lore2 = "experiencia drenada" +lore = ["Se activa solo con salud critica (<= 5 corazones) una vez cada 15 segundos", " Dano reducido", "experiencia drenada"] [discovery.villager] - name = "Atraccion de Aldeanos" - description = "¡Te permite obtener mejores intercambios con los aldeanos!" - lore1 = "Esto consume XP por interaccion con los aldeanos" - lore2 = "Probabilidad por interaccion de consumir XP y mejorar intercambios" - lore3 = "Drenaje de XP requerido por interaccion" - lore = ["Esto consume XP por interaccion con los aldeanos", "Probabilidad por interaccion de consumir XP y mejorar intercambios", "Drenaje de XP requerido por interaccion"] +name = "Atraccion de Aldeanos" +description = "¡Te permite obtener mejores intercambios con los aldeanos!" +lore1 = "Esto consume XP por interaccion con los aldeanos" +lore2 = "Probabilidad por interaccion de consumir XP y mejorar intercambios" +lore3 = "Drenaje de XP requerido por interaccion" +lore = ["Esto consume XP por interaccion con los aldeanos", "Probabilidad por interaccion de consumir XP y mejorar intercambios", "Drenaje de XP requerido por interaccion"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Retorno de Lapislazuli" - description = "A costa de 1 nivel mas de XP, tiene la posibilidad de darte lapislazuli gratis a cambio" - lore1 = "Por cada nivel, aumenta el coste de encantamiento en 1, pero puede devolver hasta 3 lapislazuli" - lore = ["Por cada nivel, aumenta el coste de encantamiento en 1, pero puede devolver hasta 3 lapislazuli"] +name = "Retorno de Lapislazuli" +description = "A costa de 1 nivel mas de XP, tiene la posibilidad de darte lapislazuli gratis a cambio" +lore1 = "Por cada nivel, aumenta el coste de encantamiento en 1, pero puede devolver hasta 3 lapislazuli" +lore = ["Por cada nivel, aumenta el coste de encantamiento en 1, pero puede devolver hasta 3 lapislazuli"] [enchanting.quick_enchant] - name = "Encantamiento Rapido" - description = "Encanta objetos haciendo clic con libros de encantamiento directamente sobre ellos." - lore1 = "Niveles maximos combinados" - lore2 = "No se puede encantar un objeto con mas de " - lore3 = "poder" - lore = ["Niveles maximos combinados", "No se puede encantar un objeto con mas de ", "poder"] +name = "Encantamiento Rapido" +description = "Encanta objetos haciendo clic con libros de encantamiento directamente sobre ellos." +lore1 = "Niveles maximos combinados" +lore2 = "No se puede encantar un objeto con mas de " +lore3 = "poder" +lore = ["Niveles maximos combinados", "No se puede encantar un objeto con mas de ", "poder"] [enchanting.return] - name = "Retorno de XP" - description = "La XP de encantamiento se te devuelve cuando encantas un objeto." - lore1 = "La experiencia gastada tiene una posibilidad de ser reembolsada cuando encantas un objeto" - lore2 = "Experiencia por encantamiento" - lore = ["La experiencia gastada tiene una posibilidad de ser reembolsada cuando encantas un objeto", "Experiencia por encantamiento"] +name = "Retorno de XP" +description = "La XP de encantamiento se te devuelve cuando encantas un objeto." +lore1 = "La experiencia gastada tiene una posibilidad de ser reembolsada cuando encantas un objeto" +lore2 = "Experiencia por encantamiento" +lore = ["La experiencia gastada tiene una posibilidad de ser reembolsada cuando encantas un objeto", "Experiencia por encantamiento"] # excavation [excavation] [excavation.haste] - name = "Excavador Veloz" - description = "¡Esto acelerara el proceso de excavacion con PRISA!" - lore1 = "Gana prisa mientras excavas" - lore2 = "x Niveles de prisa cuando empiezas a minar CUALQUIER bloque." - lore = ["Gana prisa mientras excavas", "x Niveles de prisa cuando empiezas a minar CUALQUIER bloque."] +name = "Excavador Veloz" +description = "¡Esto acelerara el proceso de excavacion con PRISA!" +lore1 = "Gana prisa mientras excavas" +lore2 = "x Niveles de prisa cuando empiezas a minar CUALQUIER bloque." +lore = ["Gana prisa mientras excavas", "x Niveles de prisa cuando empiezas a minar CUALQUIER bloque."] [excavation.spelunker] - name = "¡Espeleologo Supervidente!" - description = "¡Ve minerales con tus ojos, pero a traves del suelo!" - lore1 = "¡Mineral en tu mano secundaria, Bayas Luminosas en tu mano principal, y agachate!" - lore2 = "Rango de bloques: " - lore3 = "Consume Baya Luminosa al usar" - lore = ["¡Mineral en tu mano secundaria, Bayas Luminosas en tu mano principal, y agachate!", "Rango de bloques: ", "Consume Baya Luminosa al usar"] +name = "¡Espeleologo Supervidente!" +description = "¡Ve minerales con tus ojos, pero a traves del suelo!" +lore1 = "¡Mineral en tu mano secundaria, Bayas Luminosas en tu mano principal, y agachate!" +lore2 = "Rango de bloques: " +lore3 = "Consume Baya Luminosa al usar" +lore = ["¡Mineral en tu mano secundaria, Bayas Luminosas en tu mano principal, y agachate!", "Rango de bloques: ", "Consume Baya Luminosa al usar"] [excavation.drop_to_inventory] - name = "Pala: Soltar al Inventario" +name = "Pala: Soltar al Inventario" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "La Multiherramienta opulenta y sobrediseñada de Tackle" - lore1 = "Probablemente la mas poderosa de todas, te permite" - lore2 = "fusionar y cambiar herramientas dinamicamente sobre la marcha, segun tus necesidades." - lore3 = "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario." - lore4 = "Para desvincular herramientas, suelta el objeto agachado y se desmontara." - lore5 = "No puedes romper herramientas en esta multiherramienta pero no puedes usar herramientas rotas" - lore6 = "total de objetos fusionables." - lore7 = "¡Puedes usar cinco o seis herramientas, o solo una!" - lore = ["Probablemente la mas poderosa de todas, te permite", "fusionar y cambiar herramientas dinamicamente sobre la marcha, segun tus necesidades.", "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario.", "Para desvincular herramientas, suelta el objeto agachado y se desmontara.", "No puedes romper herramientas en esta multiherramienta pero no puedes usar herramientas rotas", "total de objetos fusionables.", "¡Puedes usar cinco o seis herramientas, o solo una!"] +name = "OMNI - T.O.O.L." +description = "La Multiherramienta opulenta y sobrediseñada de Tackle" +lore1 = "Probablemente la mas poderosa de todas, te permite" +lore2 = "fusionar y cambiar herramientas dinamicamente sobre la marcha, segun tus necesidades." +lore3 = "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario." +lore4 = "Para desvincular herramientas, suelta el objeto agachado y se desmontara." +lore5 = "No puedes romper herramientas en esta multiherramienta pero no puedes usar herramientas rotas" +lore6 = "total de objetos fusionables." +lore7 = "¡Puedes usar cinco o seis herramientas, o solo una!" +lore = ["Probablemente la mas poderosa de todas, te permite", "fusionar y cambiar herramientas dinamicamente sobre la marcha, segun tus necesidades.", "Para fusionar, haz Shift+clic en un objeto sobre otro en tu inventario.", "Para desvincular herramientas, suelta el objeto agachado y se desmontara.", "No puedes romper herramientas en esta multiherramienta pero no puedes usar herramientas rotas", "total de objetos fusionables.", "¡Puedes usar cinco o seis herramientas, o solo una!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Aura de Crecimiento" - description = "Haz crecer la naturaleza a tu alrededor en un aura" - lore1 = "Radio de bloques" - lore2 = "Fuerza del Aura de Crecimiento" - lore3 = "Coste de comida" - lore = ["Radio de bloques", "Fuerza del Aura de Crecimiento", "Coste de comida"] +name = "Aura de Crecimiento" +description = "Haz crecer la naturaleza a tu alrededor en un aura" +lore1 = "Radio de bloques" +lore2 = "Fuerza del Aura de Crecimiento" +lore3 = "Coste de comida" +lore = ["Radio de bloques", "Fuerza del Aura de Crecimiento", "Coste de comida"] [herbalism.hippo] - name = "Hipopotamo del Herbalista" - description = "Consumir comida te da mas saturacion" - lore1 = "Comida) puntos de saturacion adicionales al consumir" - lore = ["Comida) puntos de saturacion adicionales al consumir"] +name = "Hipopotamo del Herbalista" +description = "Consumir comida te da mas saturacion" +lore1 = "Comida) puntos de saturacion adicionales al consumir" +lore = ["Comida) puntos de saturacion adicionales al consumir"] [herbalism.myconid] - name = "Miconido del Herbalista" - description = "Te da la capacidad de fabricar micelio" - lore1 = "Cualquier tierra, y un champiñon marron y rojo fabricaran micelio." - lore = ["Cualquier tierra, y un champiñon marron y rojo fabricaran micelio."] +name = "Miconido del Herbalista" +description = "Te da la capacidad de fabricar micelio" +lore1 = "Cualquier tierra, y un champiñon marron y rojo fabricaran micelio." +lore = ["Cualquier tierra, y un champiñon marron y rojo fabricaran micelio."] [herbalism.terralid] - name = "Terralido del Herbalista" - description = "Te da la capacidad de fabricar bloques de cesped" - lore1 = "Tres semillas sobre 3 tierra fabricaran 3 bloques de cesped." - lore = ["Tres semillas sobre 3 tierra fabricaran 3 bloques de cesped."] +name = "Terralido del Herbalista" +description = "Te da la capacidad de fabricar bloques de cesped" +lore1 = "Tres semillas sobre 3 tierra fabricaran 3 bloques de cesped." +lore = ["Tres semillas sobre 3 tierra fabricaran 3 bloques de cesped."] [herbalism.cobweb] - name = "Creador de Telaranas" - description = "Te da la capacidad de fabricar telaranas en una mesa de crafteo" - lore1 = "Nueve hilos fabricaran una telarana." - lore = ["Nueve hilos fabricaran una telarana."] +name = "Creador de Telaranas" +description = "Te da la capacidad de fabricar telaranas en una mesa de crafteo" +lore1 = "Nueve hilos fabricaran una telarana." +lore = ["Nueve hilos fabricaran una telarana."] [herbalism.mushroom_blocks] - name = "Fabricante de Hongos" - description = "Te da la capacidad de fabricar bloques de hongo en una mesa de crafteo" - lore1 = "Cuatro hongos para hacer un bloque, o un bloque para hacer un tallo." - lore = ["Cuatro hongos para hacer un bloque, o un bloque para hacer un tallo."] +name = "Fabricante de Hongos" +description = "Te da la capacidad de fabricar bloques de hongo en una mesa de crafteo" +lore1 = "Cuatro hongos para hacer un bloque, o un bloque para hacer un tallo." +lore = ["Cuatro hongos para hacer un bloque, o un bloque para hacer un tallo."] [herbalism.drop_to_inventory] - name = "Azada: Soltar al Inventario" +name = "Azada: Soltar al Inventario" [herbalism.hungry_shield] - name = "Escudo Hambriento" - description = "Recibe dano a tu hambre antes que a tu salud." - lore1 = "Resistido por el hambre" - lore = ["Resistido por el hambre"] +name = "Escudo Hambriento" +description = "Recibe dano a tu hambre antes que a tu salud." +lore1 = "Resistido por el hambre" +lore = ["Resistido por el hambre"] [herbalism.luck] - name = "Suerte del Herbalista" - description = "Cuando rompes hierba/flores, tienes la posibilidad de obtener un objeto aleatorio" - lore0 = "Flores = Comida, y Hierba = Semillas" - lore1 = "Probabilidad de obtener un objeto al romper flores" - lore2 = "Probabilidad de obtener un objeto al romper hierba" - lore = ["Flores = Comida, y Hierba = Semillas", "Probabilidad de obtener un objeto al romper flores", "Probabilidad de obtener un objeto al romper hierba"] +name = "Suerte del Herbalista" +description = "Cuando rompes hierba/flores, tienes la posibilidad de obtener un objeto aleatorio" +lore0 = "Flores = Comida, y Hierba = Semillas" +lore1 = "Probabilidad de obtener un objeto al romper flores" +lore2 = "Probabilidad de obtener un objeto al romper hierba" +lore = ["Flores = Comida, y Hierba = Semillas", "Probabilidad de obtener un objeto al romper flores", "Probabilidad de obtener un objeto al romper hierba"] [herbalism.replant] - name = "Cosechar y Replantar" - description = "Haz clic derecho en un cultivo con una azada para cosechar y replantar." - lore1 = "Radio de replantado en bloques" - lore = ["Radio de replantado en bloques"] +name = "Cosechar y Replantar" +description = "Haz clic derecho en un cultivo con una azada para cosechar y replantar." +lore1 = "Radio de replantado en bloques" +lore = ["Radio de replantado en bloques"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalina" - description = "Inflige mas dano cuanto menos vida tengas (cuerpo a cuerpo)" - lore1 = "Dano maximo" - lore = ["Dano maximo"] +name = "Adrenalina" +description = "Inflige mas dano cuanto menos vida tengas (cuerpo a cuerpo)" +lore1 = "Dano maximo" +lore = ["Dano maximo"] [hunter.penalty] - name = "" - description = "" - lore1 = "Ganaras acumulaciones de veneno si te quedas sin hambre" - lore = ["Ganaras acumulaciones de veneno si te quedas sin hambre"] +name = "" +description = "" +lore1 = "Ganaras acumulaciones de veneno si te quedas sin hambre" +lore = ["Ganaras acumulaciones de veneno si te quedas sin hambre"] [hunter.drop_to_inventory] - name = "Objetos: Soltar al Inventario" - description = "Cuando matas algo / rompes un bloque con una espada, los drops se teletransportan a tu inventario" - lore1 = "Cada vez que un objeto cae de un mob/bloque que rompes, va a tu inventario si es posible." - lore = ["Cada vez que un objeto cae de un mob/bloque que rompes, va a tu inventario si es posible."] +name = "Objetos: Soltar al Inventario" +description = "Cuando matas algo / rompes un bloque con una espada, los drops se teletransportan a tu inventario" +lore1 = "Cada vez que un objeto cae de un mob/bloque que rompes, va a tu inventario si es posible." +lore = ["Cada vez que un objeto cae de un mob/bloque que rompes, va a tu inventario si es posible."] [hunter.invisibility] - name = "Paso Fugaz" - description = "Cuando te golpean obtienes invisibilidad, a costa de hambre" - lore1 = "Obtén invisibilidad pasiva al ser golpeado" - lore2 = "x Acumulaciones de invisibilidad durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Duracion de la invisibilidad" - lore = ["Obtén invisibilidad pasiva al ser golpeado", "x Acumulaciones de invisibilidad durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Duracion de la invisibilidad"] +name = "Paso Fugaz" +description = "Cuando te golpean obtienes invisibilidad, a costa de hambre" +lore1 = "Obtén invisibilidad pasiva al ser golpeado" +lore2 = "x Acumulaciones de invisibilidad durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Duracion de la invisibilidad" +lore = ["Obtén invisibilidad pasiva al ser golpeado", "x Acumulaciones de invisibilidad durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Duracion de la invisibilidad"] [hunter.jump_boost] - name = "Alturas del Cazador" - description = "Cuando te golpean obtienes impulso de salto, a costa de hambre" - lore1 = "Obtén impulso de salto pasivo al ser golpeado" - lore2 = "x Acumulaciones de impulso de salto durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de impulso de salto, no duracion." - lore = ["Obtén impulso de salto pasivo al ser golpeado", "x Acumulaciones de impulso de salto durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de impulso de salto, no duracion."] +name = "Alturas del Cazador" +description = "Cuando te golpean obtienes impulso de salto, a costa de hambre" +lore1 = "Obtén impulso de salto pasivo al ser golpeado" +lore2 = "x Acumulaciones de impulso de salto durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de impulso de salto, no duracion." +lore = ["Obtén impulso de salto pasivo al ser golpeado", "x Acumulaciones de impulso de salto durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de impulso de salto, no duracion."] [hunter.luck] - name = "Suerte del Cazador" - description = "Cuando te golpean obtienes suerte, a costa de hambre" - lore1 = "Obtén suerte pasiva al ser golpeado" - lore2 = "x Acumulaciones de suerte durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de suerte, no duracion." - lore = ["Obtén suerte pasiva al ser golpeado", "x Acumulaciones de suerte durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de suerte, no duracion."] +name = "Suerte del Cazador" +description = "Cuando te golpean obtienes suerte, a costa de hambre" +lore1 = "Obtén suerte pasiva al ser golpeado" +lore2 = "x Acumulaciones de suerte durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de suerte, no duracion." +lore = ["Obtén suerte pasiva al ser golpeado", "x Acumulaciones de suerte durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de suerte, no duracion."] [hunter.regen] - name = "Regeneracion del Cazador" - description = "Cuando te golpean obtienes regeneracion, a costa de hambre" - lore1 = "Obtén regeneracion pasiva al ser golpeado" - lore2 = "x Acumulaciones de regeneracion durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de regeneracion, no duracion." - lore = ["Obtén regeneracion pasiva al ser golpeado", "x Acumulaciones de regeneracion durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de regeneracion, no duracion."] +name = "Regeneracion del Cazador" +description = "Cuando te golpean obtienes regeneracion, a costa de hambre" +lore1 = "Obtén regeneracion pasiva al ser golpeado" +lore2 = "x Acumulaciones de regeneracion durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de regeneracion, no duracion." +lore = ["Obtén regeneracion pasiva al ser golpeado", "x Acumulaciones de regeneracion durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de regeneracion, no duracion."] [hunter.resistance] - name = "Resistencia del Cazador" - description = "Cuando te golpean obtienes resistencia, a costa de hambre" - lore1 = "Obtén resistencia pasiva al ser golpeado" - lore2 = "x Acumulaciones de resistencia durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de resistencia, no duracion." - lore = ["Obtén resistencia pasiva al ser golpeado", "x Acumulaciones de resistencia durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de resistencia, no duracion."] +name = "Resistencia del Cazador" +description = "Cuando te golpean obtienes resistencia, a costa de hambre" +lore1 = "Obtén resistencia pasiva al ser golpeado" +lore2 = "x Acumulaciones de resistencia durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de resistencia, no duracion." +lore = ["Obtén resistencia pasiva al ser golpeado", "x Acumulaciones de resistencia durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de resistencia, no duracion."] [hunter.speed] - name = "Velocidad del Cazador" - description = "Cuando te golpean obtienes velocidad, a costa de hambre" - lore1 = "Obtén velocidad pasiva al ser golpeado" - lore2 = "x Acumulaciones de velocidad durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de velocidad, no duracion." - lore = ["Obtén velocidad pasiva al ser golpeado", "x Acumulaciones de velocidad durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de velocidad, no duracion."] +name = "Velocidad del Cazador" +description = "Cuando te golpean obtienes velocidad, a costa de hambre" +lore1 = "Obtén velocidad pasiva al ser golpeado" +lore2 = "x Acumulaciones de velocidad durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de velocidad, no duracion." +lore = ["Obtén velocidad pasiva al ser golpeado", "x Acumulaciones de velocidad durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de velocidad, no duracion."] [hunter.strength] - name = "Fuerza del Cazador" - description = "Cuando te golpean obtienes fuerza, a costa de hambre" - lore1 = "Obtén fuerza pasiva al ser golpeado" - lore2 = "x Acumulaciones de fuerza durante 3 segundos al ser golpeado" - lore3 = "x Hambre acumulable" - lore4 = "Duracion y multiplicador de acumulaciones de hambre." - lore5 = "Multiplicador de acumulaciones de fuerza, no duracion." - lore = ["Obtén fuerza pasiva al ser golpeado", "x Acumulaciones de fuerza durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de fuerza, no duracion."] +name = "Fuerza del Cazador" +description = "Cuando te golpean obtienes fuerza, a costa de hambre" +lore1 = "Obtén fuerza pasiva al ser golpeado" +lore2 = "x Acumulaciones de fuerza durante 3 segundos al ser golpeado" +lore3 = "x Hambre acumulable" +lore4 = "Duracion y multiplicador de acumulaciones de hambre." +lore5 = "Multiplicador de acumulaciones de fuerza, no duracion." +lore = ["Obtén fuerza pasiva al ser golpeado", "x Acumulaciones de fuerza durante 3 segundos al ser golpeado", "x Hambre acumulable", "Duracion y multiplicador de acumulaciones de hambre.", "Multiplicador de acumulaciones de fuerza, no duracion."] # nether [nether] [nether.skull_toss] - name = "Lanzamiento de Cabeza de Wither" - description1 = "Desata tu Wither interior usando la" - description2 = "cabeza de" - description3 = "alguien." - lore1 = "Segundos de recarga entre lanzamientos de cabeza." - lore2 = "Usando Cabeza de Wither: Lanza una " - lore3 = "Cabeza de Wither" - lore4 = "que explota al impactar." - lore = ["Segundos de recarga entre lanzamientos de cabeza.", "Usando Cabeza de Wither: Lanza una ", "Cabeza de Wither", "que explota al impactar."] +name = "Lanzamiento de Cabeza de Wither" +description1 = "Desata tu Wither interior usando la" +description2 = "cabeza de" +description3 = "alguien." +lore1 = "Segundos de recarga entre lanzamientos de cabeza." +lore2 = "Usando Cabeza de Wither: Lanza una " +lore3 = "Cabeza de Wither" +lore4 = "que explota al impactar." +lore = ["Segundos de recarga entre lanzamientos de cabeza.", "Usando Cabeza de Wither: Lanza una ", "Cabeza de Wither", "que explota al impactar."] [nether.wither_resist] - name = "Resistencia al Wither" - description = "Resiste el marchitamiento mediante el poder de la Netherita." - lore1 = "probabilidad de negar el marchitamiento (por pieza)." - lore2 = "Pasiva: Llevar armadura de Netherita tiene probabilidad de negar " - lore3 = "el marchitamiento." - lore = ["probabilidad de negar el marchitamiento (por pieza).", "Pasiva: Llevar armadura de Netherita tiene probabilidad de negar ", "el marchitamiento."] +name = "Resistencia al Wither" +description = "Resiste el marchitamiento mediante el poder de la Netherita." +lore1 = "probabilidad de negar el marchitamiento (por pieza)." +lore2 = "Pasiva: Llevar armadura de Netherita tiene probabilidad de negar " +lore3 = "el marchitamiento." +lore = ["probabilidad de negar el marchitamiento (por pieza).", "Pasiva: Llevar armadura de Netherita tiene probabilidad de negar ", "el marchitamiento."] [nether.fire_resist] - name = "Resistencia al Fuego" - description = "Resiste el fuego endureciendo tu piel." - lore1 = "probabilidad de negar el efecto de quemadura!" - lore = ["probabilidad de negar el efecto de quemadura!"] +name = "Resistencia al Fuego" +description = "Resiste el fuego endureciendo tu piel." +lore1 = "probabilidad de negar el efecto de quemadura!" +lore = ["probabilidad de negar el efecto de quemadura!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Fundicion Automatica" - description = "Te permite fundir minerales vanilla extraidos" - lore1 = "Los minerales que se pueden fundir se funden automaticamente" - lore2 = "% de probabilidad de un extra" - lore = ["Los minerales que se pueden fundir se funden automaticamente", "% de probabilidad de un extra"] +name = "Fundicion Automatica" +description = "Te permite fundir minerales vanilla extraidos" +lore1 = "Los minerales que se pueden fundir se funden automaticamente" +lore2 = "% de probabilidad de un extra" +lore = ["Los minerales que se pueden fundir se funden automaticamente", "% de probabilidad de un extra"] [pickaxe.chisel] - name = "Cincel de Minerales" - description = "Haz clic derecho en minerales para cincelar mas mineral de ellos, con un gran coste de durabilidad." - lore1 = "Probabilidad de soltar" - lore2 = "Desgaste de herramienta" - lore = ["Probabilidad de soltar", "Desgaste de herramienta"] +name = "Cincel de Minerales" +description = "Haz clic derecho en minerales para cincelar mas mineral de ellos, con un gran coste de durabilidad." +lore1 = "Probabilidad de soltar" +lore2 = "Desgaste de herramienta" +lore = ["Probabilidad de soltar", "Desgaste de herramienta"] [pickaxe.drop_to_inventory] - name = "Pico: Soltar al Inventario" - description = "Cuando rompes un bloque, el objeto se teletransporta a tu inventario" - lore1 = "Cada vez que un objeto cae de un bloque que rompes, va a tu inventario si es posible." - lore = ["Cada vez que un objeto cae de un bloque que rompes, va a tu inventario si es posible."] +name = "Pico: Soltar al Inventario" +description = "Cuando rompes un bloque, el objeto se teletransporta a tu inventario" +lore1 = "Cada vez que un objeto cae de un bloque que rompes, va a tu inventario si es posible." +lore = ["Cada vez que un objeto cae de un bloque que rompes, va a tu inventario si es posible."] [pickaxe.silk_spawner] - name = "Pico Toque de Seda para Generadores" - description = "Hace que los generadores se suelten al romperse" - lore1 = "Hace que los generadores sean rompibles con toque de seda." - lore2 = "Hace que los generadores sean rompibles al agacharte." - lore = ["Hace que los generadores sean rompibles con toque de seda.", "Hace que los generadores sean rompibles al agacharte."] +name = "Pico Toque de Seda para Generadores" +description = "Hace que los generadores se suelten al romperse" +lore1 = "Hace que los generadores sean rompibles con toque de seda." +lore2 = "Hace que los generadores sean rompibles al agacharte." +lore = ["Hace que los generadores sean rompibles con toque de seda.", "Hace que los generadores sean rompibles al agacharte."] [pickaxe.vein_miner] - name = "Minero de Vetas" - description = "Te permite romper bloques en una veta/grupo de minerales vanilla" - lore1 = "Agachate y mina MINERALES" - lore2 = "Rango de minado de vetas" - lore3 = "¡Esta habilidad NO agrupa todos los drops!" - lore = ["Agachate y mina MINERALES", "Rango de minado de vetas", "¡Esta habilidad NO agrupa todos los drops!"] +name = "Minero de Vetas" +description = "Te permite romper bloques en una veta/grupo de minerales vanilla" +lore1 = "Agachate y mina MINERALES" +lore2 = "Rango de minado de vetas" +lore3 = "¡Esta habilidad NO agrupa todos los drops!" +lore = ["Agachate y mina MINERALES", "Rango de minado de vetas", "¡Esta habilidad NO agrupa todos los drops!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Recuperacion de Flechas" - description = "Recupera flechas despues de matar a un enemigo." - lore1 = "Probabilidad de recuperar flechas al golpear/matar" - lore2 = "Probabilidad: " - lore = ["Probabilidad de recuperar flechas al golpear/matar", "Probabilidad: "] +name = "Recuperacion de Flechas" +description = "Recupera flechas despues de matar a un enemigo." +lore1 = "Probabilidad de recuperar flechas al golpear/matar" +lore2 = "Probabilidad: " +lore = ["Probabilidad de recuperar flechas al golpear/matar", "Probabilidad: "] [ranged.web_shot] - name = "Trampa de Telarana" - description = "¡Rodea de telaranas a tu objetivo cuando lo golpeas!" - lore1 = "8 telaranas alrededor de una bola de nieve, ¡y lanza!" - lore2 = "segundos de jaula, aproximadamente." - lore = ["8 telaranas alrededor de una bola de nieve, ¡y lanza!", "segundos de jaula, aproximadamente."] +name = "Trampa de Telarana" +description = "¡Rodea de telaranas a tu objetivo cuando lo golpeas!" +lore1 = "8 telaranas alrededor de una bola de nieve, ¡y lanza!" +lore2 = "segundos de jaula, aproximadamente." +lore = ["8 telaranas alrededor de una bola de nieve, ¡y lanza!", "segundos de jaula, aproximadamente."] [ranged.force_shot] - name = "Tiro de Fuerza" - description = "¡Dispara proyectiles mas lejos y mas rapido!" - advancementname = "Tiro Largo" - advancementlore = "¡Acierta un disparo desde mas de 30 bloques de distancia!" - lore1 = "Velocidad de proyectil" - lore = ["Velocidad de proyectil"] +name = "Tiro de Fuerza" +description = "¡Dispara proyectiles mas lejos y mas rapido!" +advancementname = "Tiro Largo" +advancementlore = "¡Acierta un disparo desde mas de 30 bloques de distancia!" +lore1 = "Velocidad de proyectil" +lore = ["Velocidad de proyectil"] [ranged.lunge_shot] - name = "Tiro de Embestida" - description = "Mientras caes, tus flechas te lanzan en una direccion aleatoria" - lore1 = "Velocidad de rafaga aleatoria" - lore = ["Velocidad de rafaga aleatoria"] +name = "Tiro de Embestida" +description = "Mientras caes, tus flechas te lanzan en una direccion aleatoria" +lore1 = "Velocidad de rafaga aleatoria" +lore = ["Velocidad de rafaga aleatoria"] [ranged.arrow_piercing] - name = "Perforacion de Flechas" - description = "¡Anade perforacion a los proyectiles! ¡Dispara a traves de cosas!" - lore1 = "Objetivos perforados" - lore = ["Objetivos perforados"] +name = "Perforacion de Flechas" +description = "¡Anade perforacion a los proyectiles! ¡Dispara a traves de cosas!" +lore1 = "Objetivos perforados" +lore = ["Objetivos perforados"] # rift [rift] [rift.remote_access] - name = "Acceso Remoto" - description = "Extrae del vacio y accede a un contenedor marcado." - lore1 = "Perla de Ender + Brujula = Traslador Relicario" - lore2 = "Este objeto te permite acceder a contenedores de forma remota" - lore3 = "Una vez fabricado, mira el objeto para ver su uso" - notcontainer = "Eso no es un contenedor" - lore = ["Perla de Ender + Brujula = Traslador Relicario", "Este objeto te permite acceder a contenedores de forma remota", "Una vez fabricado, mira el objeto para ver su uso"] +name = "Acceso Remoto" +description = "Extrae del vacio y accede a un contenedor marcado." +lore1 = "Perla de Ender + Brujula = Traslador Relicario" +lore2 = "Este objeto te permite acceder a contenedores de forma remota" +lore3 = "Una vez fabricado, mira el objeto para ver su uso" +notcontainer = "Eso no es un contenedor" +lore = ["Perla de Ender + Brujula = Traslador Relicario", "Este objeto te permite acceder a contenedores de forma remota", "Una vez fabricado, mira el objeto para ver su uso"] [rift.blink] - name = "Parpadeo de Grieta" - description = "¡Teletransportacion instantanea de corto alcance, a solo un parpadeo!" - lore1 = "Bloques al parpadear (2x vertical)" - lore2 = "Mientras esprinteas: Doble toque en saltar para " - lore3 = "Parpadear" - lore = ["Bloques al parpadear (2x vertical)", "Mientras esprinteas: Doble toque en saltar para ", "Parpadear"] +name = "Parpadeo de Grieta" +description = "¡Teletransportacion instantanea de corto alcance, a solo un parpadeo!" +lore1 = "Bloques al parpadear (2x vertical)" +lore2 = "Mientras esprinteas: Doble toque en saltar para " +lore3 = "Parpadear" +lore = ["Bloques al parpadear (2x vertical)", "Mientras esprinteas: Doble toque en saltar para ", "Parpadear"] [rift.chest] - name = "Cofre de Ender Facil" - description = "Abre un cofre de Ender haciendo clic izquierdo con el en tu mano." - lore1 = "Haz clic en un cofre de Ender en tu mano para abrirlo (no lo coloques)" - lore = ["Haz clic en un cofre de Ender en tu mano para abrirlo (no lo coloques)"] +name = "Cofre de Ender Facil" +description = "Abre un cofre de Ender haciendo clic izquierdo con el en tu mano." +lore1 = "Haz clic en un cofre de Ender en tu mano para abrirlo (no lo coloques)" +lore = ["Haz clic en un cofre de Ender en tu mano para abrirlo (no lo coloques)"] [rift.descent] - name = "Anti-Levitacion" - description = "¿Estas cansado de estar atrapado en el aire? ¡Esta es la habilidad para ti!" - lore1 = "¡Simplemente agachate para descender y caeras a un ritmo menor al normal!" - lore2 = "Tiempo de recarga:" - lore = ["¡Simplemente agachate para descender y caeras a un ritmo menor al normal!", "Tiempo de recarga:"] +name = "Anti-Levitacion" +description = "¿Estas cansado de estar atrapado en el aire? ¡Esta es la habilidad para ti!" +lore1 = "¡Simplemente agachate para descender y caeras a un ritmo menor al normal!" +lore2 = "Tiempo de recarga:" +lore = ["¡Simplemente agachate para descender y caeras a un ritmo menor al normal!", "Tiempo de recarga:"] [rift.gate] - name = "Portal de Grieta" - description = "Teletransportate a una ubicacion marcada." - lore1 = "FABRICACION: Esmeralda + Fragmento de Amatista + Perla de Ender" - lore2 = "¡Lee antes de usar!" - lore3 = "5s de retraso, " - lore4 = "puedes morir mientras estas en esta animacion" - lore = ["FABRICACION: Esmeralda + Fragmento de Amatista + Perla de Ender", "¡Lee antes de usar!", "5s de retraso, ", "puedes morir mientras estas en esta animacion"] +name = "Portal de Grieta" +description = "Teletransportate a una ubicacion marcada." +lore1 = "FABRICACION: Esmeralda + Fragmento de Amatista + Perla de Ender" +lore2 = "¡Lee antes de usar!" +lore3 = "5s de retraso, " +lore4 = "puedes morir mientras estas en esta animacion" +lore = ["FABRICACION: Esmeralda + Fragmento de Amatista + Perla de Ender", "¡Lee antes de usar!", "5s de retraso, ", "puedes morir mientras estas en esta animacion"] [rift.resist] - name = "Resistencia de Grieta" - description = "Obtén resistencia al usar objetos y habilidades de Ender" - lore1 = "+ Pasiva: Proporciona resistencia cuando usas habilidades de grieta u objetos de Ender" - lore2 = "NO incluye el cofre de Ender portatil, solo las cosas que puedes consumir" - lore = ["+ Pasiva: Proporciona resistencia cuando usas habilidades de grieta u objetos de Ender", "NO incluye el cofre de Ender portatil, solo las cosas que puedes consumir"] +name = "Resistencia de Grieta" +description = "Obtén resistencia al usar objetos y habilidades de Ender" +lore1 = "+ Pasiva: Proporciona resistencia cuando usas habilidades de grieta u objetos de Ender" +lore2 = "NO incluye el cofre de Ender portatil, solo las cosas que puedes consumir" +lore = ["+ Pasiva: Proporciona resistencia cuando usas habilidades de grieta u objetos de Ender", "NO incluye el cofre de Ender portatil, solo las cosas que puedes consumir"] [rift.visage] - name = "Rostro de Grieta" - description = "Evita que los Enderman se vuelvan agresivos si tienes perlas de Ender en tu inventario." - lore1 = "Los Enderman no se volveran agresivos si tienes perlas de Ender en tu inventario." - lore = ["Los Enderman no se volveran agresivos si tienes perlas de Ender en tu inventario."] +name = "Rostro de Grieta" +description = "Evita que los Enderman se vuelvan agresivos si tienes perlas de Ender en tu inventario." +lore1 = "Los Enderman no se volveran agresivos si tienes perlas de Ender en tu inventario." +lore = ["Los Enderman no se volveran agresivos si tienes perlas de Ender en tu inventario."] # seaborn [seaborn] [seaborn.oxygen] - name = "Tanque de Oxigeno Organico" - description = "¡Almacena mas oxigeno en tus pequenos pulmones!" - lore1 = "Aumento de capacidad de oxigeno" - lore = ["Aumento de capacidad de oxigeno"] +name = "Tanque de Oxigeno Organico" +description = "¡Almacena mas oxigeno en tus pequenos pulmones!" +lore1 = "Aumento de capacidad de oxigeno" +lore = ["Aumento de capacidad de oxigeno"] [seaborn.fishers_fantasy] - name = "Fantasia del Pescador" - description = "¡Gana mas XP pescando y consigue mas peces!" - lore1 = "¡Por cada nivel hay posibilidad de obtener mas XP y peces!" - lore = ["¡Por cada nivel hay posibilidad de obtener mas XP y peces!"] +name = "Fantasia del Pescador" +description = "¡Gana mas XP pescando y consigue mas peces!" +lore1 = "¡Por cada nivel hay posibilidad de obtener mas XP y peces!" +lore = ["¡Por cada nivel hay posibilidad de obtener mas XP y peces!"] [seaborn.haste] - name = "Minero Tortuga" - description = "¡Mientras minas bajo el agua obtienes prisa!" - lore1 = "¡Prisa 3 se aplica bajo el agua mientras minas (se acumula con Afinidad Acuatica) despues de que desaparece el efecto de respiracion acuatica!" - lore = ["¡Prisa 3 se aplica bajo el agua mientras minas (se acumula con Afinidad Acuatica) despues de que desaparece el efecto de respiracion acuatica!"] +name = "Minero Tortuga" +description = "¡Mientras minas bajo el agua obtienes prisa!" +lore1 = "¡Prisa 3 se aplica bajo el agua mientras minas (se acumula con Afinidad Acuatica) despues de que desaparece el efecto de respiracion acuatica!" +lore = ["¡Prisa 3 se aplica bajo el agua mientras minas (se acumula con Afinidad Acuatica) despues de que desaparece el efecto de respiracion acuatica!"] [seaborn.night_vision] - name = "Vision de Tortuga" - description = "Mientras estas bajo el agua, obtienes vision nocturna" - lore1 = "¡Simplemente obtén vision nocturna bajo el agua despues de que desaparezca el efecto de respiracion acuatica!" - lore = ["¡Simplemente obtén vision nocturna bajo el agua despues de que desaparezca el efecto de respiracion acuatica!"] +name = "Vision de Tortuga" +description = "Mientras estas bajo el agua, obtienes vision nocturna" +lore1 = "¡Simplemente obtén vision nocturna bajo el agua despues de que desaparezca el efecto de respiracion acuatica!" +lore = ["¡Simplemente obtén vision nocturna bajo el agua despues de que desaparezca el efecto de respiracion acuatica!"] [seaborn.dolphin_grace] - name = "Gracia del Delfin" - description = "Nada como un delfin, sin los delfines" - lore1 = "+ Pasiva: obtén " - lore2 = "x velocidad (gracia de delfin)" - lore3 = "Precision de ingenieria aleman... espera, eso no esta bien... No compatible con Agilidad Acuatica" - lore = ["+ Pasiva: obtén ", "x velocidad (gracia de delfin)", "Precision de ingenieria aleman... espera, eso no esta bien... No compatible con Agilidad Acuatica"] +name = "Gracia del Delfin" +description = "Nada como un delfin, sin los delfines" +lore1 = "+ Pasiva: obtén " +lore2 = "x velocidad (gracia de delfin)" +lore3 = "Precision de ingenieria aleman... espera, eso no esta bien... No compatible con Agilidad Acuatica" +lore = ["+ Pasiva: obtén ", "x velocidad (gracia de delfin)", "Precision de ingenieria aleman... espera, eso no esta bien... No compatible con Agilidad Acuatica"] # stealth [stealth] [stealth.ghost_armor] - name = "Armadura Fantasma" - description = "Armadura que se construye lentamente cuando no recibes dano, dura 1 golpe" - lore1 = "Armadura maxima" - lore2 = "Velocidad" - lore = ["Armadura maxima", "Velocidad"] +name = "Armadura Fantasma" +description = "Armadura que se construye lentamente cuando no recibes dano, dura 1 golpe" +lore1 = "Armadura maxima" +lore2 = "Velocidad" +lore = ["Armadura maxima", "Velocidad"] [stealth.night_vision] - name = "Vision Sigilosa" - description = "Obtén vision nocturna mientras te agachas" - lore1 = "Obtén una rafaga de " - lore2 = "vision nocturna" - lore3 = "mientras te agachas" - lore = ["Obtén una rafaga de ", "vision nocturna", "mientras te agachas"] +name = "Vision Sigilosa" +description = "Obtén vision nocturna mientras te agachas" +lore1 = "Obtén una rafaga de " +lore2 = "vision nocturna" +lore3 = "mientras te agachas" +lore = ["Obtén una rafaga de ", "vision nocturna", "mientras te agachas"] [stealth.snatch] - name = "Arrebato de Objetos" - description = "¡Atrapa objetos caidos instantaneamente mientras te agachas!" - lore1 = "Radio de arrebato" - lore = ["Radio de arrebato"] +name = "Arrebato de Objetos" +description = "¡Atrapa objetos caidos instantaneamente mientras te agachas!" +lore1 = "Radio de arrebato" +lore = ["Radio de arrebato"] [stealth.speed] - name = "Velocidad Sigilosa" - description = "Obtén velocidad mientras te agachas" - lore1 = "Velocidad al agacharse" - lore = ["Velocidad al agacharse"] +name = "Velocidad Sigilosa" +description = "Obtén velocidad mientras te agachas" +lore1 = "Velocidad al agacharse" +lore = ["Velocidad al agacharse"] [stealth.ender_veil] - name = "Velo de Ender" - description = "No mas calabazas para evitar los ataques de Enderman" - lore1 = "Evita los ataques de Enderman mientras te agachas" - lore2 = "Evita todos los ataques de Enderman" - lore = ["Evita los ataques de Enderman mientras te agachas", "Evita todos los ataques de Enderman"] +name = "Velo de Ender" +description = "No mas calabazas para evitar los ataques de Enderman" +lore1 = "Evita los ataques de Enderman mientras te agachas" +lore2 = "Evita todos los ataques de Enderman" +lore = ["Evita los ataques de Enderman mientras te agachas", "Evita todos los ataques de Enderman"] # sword [sword] [sword.machete] - name = "Machete" - description = "¡Corta el follaje con facilidad!" - lore1 = "Radio de corte" - lore2 = "Tiempo de recarga del corte" - lore3 = "Desgaste de herramienta" - lore = ["Radio de corte", "Tiempo de recarga del corte", "Desgaste de herramienta"] +name = "Machete" +description = "¡Corta el follaje con facilidad!" +lore1 = "Radio de corte" +lore2 = "Tiempo de recarga del corte" +lore3 = "Desgaste de herramienta" +lore = ["Radio de corte", "Tiempo de recarga del corte", "Desgaste de herramienta"] [sword.bloody_blade] - name = "Hoja Sangrienta" - description = "¡Los golpes con tu espada causan sangrado!" - lore1 = "Golpear a una entidad viva con tu espada causa sangrado" - lore2 = "Duracion del sangrado" - lore3 = "Tiempo de recarga del sangrado" - lore = ["Golpear a una entidad viva con tu espada causa sangrado", "Duracion del sangrado", "Tiempo de recarga del sangrado"] +name = "Hoja Sangrienta" +description = "¡Los golpes con tu espada causan sangrado!" +lore1 = "Golpear a una entidad viva con tu espada causa sangrado" +lore2 = "Duracion del sangrado" +lore3 = "Tiempo de recarga del sangrado" +lore = ["Golpear a una entidad viva con tu espada causa sangrado", "Duracion del sangrado", "Tiempo de recarga del sangrado"] [sword.poisoned_blade] - name = "Hoja Envenenada" - description = "¡Los golpes con tu espada causan veneno!" - lore1 = "Golpear a una entidad viva con tu espada causa veneno" - lore2 = "Duracion del veneno" - lore3 = "Tiempo de recarga del veneno" - lore = ["Golpear a una entidad viva con tu espada causa veneno", "Duracion del veneno", "Tiempo de recarga del veneno"] +name = "Hoja Envenenada" +description = "¡Los golpes con tu espada causan veneno!" +lore1 = "Golpear a una entidad viva con tu espada causa veneno" +lore2 = "Duracion del veneno" +lore3 = "Tiempo de recarga del veneno" +lore = ["Golpear a una entidad viva con tu espada causa veneno", "Duracion del veneno", "Tiempo de recarga del veneno"] # taming [taming] [taming.damage] - name = "Dano de Domesticacion" - description = "Aumenta el dano infligido por tu animal domesticado." - lore1 = "Dano aumentado" - lore = ["Dano aumentado"] +name = "Dano de Domesticacion" +description = "Aumenta el dano infligido por tu animal domesticado." +lore1 = "Dano aumentado" +lore = ["Dano aumentado"] [taming.health] - name = "Salud de Domesticacion" - description = "Aumenta la salud de tu animal domesticado." - lore1 = "Salud aumentada" - lore = ["Salud aumentada"] +name = "Salud de Domesticacion" +description = "Aumenta la salud de tu animal domesticado." +lore1 = "Salud aumentada" +lore = ["Salud aumentada"] [taming.regeneration] - name = "Regeneracion de Domesticacion" - description = "Aumenta la regeneracion de tu animal domesticado." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Regeneracion de Domesticacion" +description = "Aumenta la regeneracion de tu animal domesticado." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Espinas" - description = "¡Refleja dano a tu atacante!" - lore1 = "Dano devuelto al ser golpeado" - lore = ["Dano devuelto al ser golpeado"] +name = "Espinas" +description = "¡Refleja dano a tu atacante!" +lore1 = "Dano devuelto al ser golpeado" +lore = ["Dano devuelto al ser golpeado"] [tragoul.globe] - name = "Globo de Dolor" - description = "¡Divide el dano que infliges segun la cantidad de enemigos a tu alrededor!" - lore1 = "Cuantos mas enemigos te rodean, menos dano infliges a cada uno" - lore2 = "Rango: " - lore3 = "Dano anadido a todas las entidades: " - lore = ["Cuantos mas enemigos te rodean, menos dano infliges a cada uno", "Rango: ", "Dano anadido a todas las entidades: "] +name = "Globo de Dolor" +description = "¡Divide el dano que infliges segun la cantidad de enemigos a tu alrededor!" +lore1 = "Cuantos mas enemigos te rodean, menos dano infliges a cada uno" +lore2 = "Rango: " +lore3 = "Dano anadido a todas las entidades: " +lore = ["Cuantos mas enemigos te rodean, menos dano infliges a cada uno", "Rango: ", "Dano anadido a todas las entidades: "] [tragoul.healing] - name = "Voluntad del Dolor" - description = "¡Recupera salud basandote en el dano que infliges!" - lore1 = "¡Hacer dano nunca se habia sentido tan bien! Curate con el dano infligido" - lore2 = "Hay una ventana de dano de 3 segundos para curacion y 1 segundo de recarga " - lore3 = "Curacion por porcentaje de dano: " - lore = ["¡Hacer dano nunca se habia sentido tan bien! Curate con el dano infligido", "Hay una ventana de dano de 3 segundos para curacion y 1 segundo de recarga ", "Curacion por porcentaje de dano: "] +name = "Voluntad del Dolor" +description = "¡Recupera salud basandote en el dano que infliges!" +lore1 = "¡Hacer dano nunca se habia sentido tan bien! Curate con el dano infligido" +lore2 = "Hay una ventana de dano de 3 segundos para curacion y 1 segundo de recarga " +lore3 = "Curacion por porcentaje de dano: " +lore = ["¡Hacer dano nunca se habia sentido tan bien! Curate con el dano infligido", "Hay una ventana de dano de 3 segundos para curacion y 1 segundo de recarga ", "Curacion por porcentaje de dano: "] [tragoul.lance] - name = "Lanzas de Cadaver" - description = "¡Matar a un enemigo o que una habilidad mate a un enemigo genera una lanza que dana a un enemigo cercano!" - lore1 = "Las lanzas buscaran desde cualquier cosa que mates, Y si esta habilidad mata a un enemigo." - lore2 = "Sacrifica una parte de tu vida para crear las lanzas (esto puede matarte)" - lore3 = "Lanzas maximas: 1 + " - lore = ["Las lanzas buscaran desde cualquier cosa que mates, Y si esta habilidad mata a un enemigo.", "Sacrifica una parte de tu vida para crear las lanzas (esto puede matarte)", "Lanzas maximas: 1 + "] +name = "Lanzas de Cadaver" +description = "¡Matar a un enemigo o que una habilidad mate a un enemigo genera una lanza que dana a un enemigo cercano!" +lore1 = "Las lanzas buscaran desde cualquier cosa que mates, Y si esta habilidad mata a un enemigo." +lore2 = "Sacrifica una parte de tu vida para crear las lanzas (esto puede matarte)" +lore3 = "Lanzas maximas: 1 + " +lore = ["Las lanzas buscaran desde cualquier cosa que mates, Y si esta habilidad mata a un enemigo.", "Sacrifica una parte de tu vida para crear las lanzas (esto puede matarte)", "Lanzas maximas: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Canon de Cristal" - description = "Dano extra sin armas cuanto menor sea tu valor de armadura" - lore1 = "x Dano a 0 de armadura" - lore2 = "Bonificacion de dano por nivel" - lore = ["x Dano a 0 de armadura", "Bonificacion de dano por nivel"] +name = "Canon de Cristal" +description = "Dano extra sin armas cuanto menor sea tu valor de armadura" +lore1 = "x Dano a 0 de armadura" +lore2 = "Bonificacion de dano por nivel" +lore = ["x Dano a 0 de armadura", "Bonificacion de dano por nivel"] [unarmed.power] - name = "Poder Desarmado" - description = "Dano sin armas mejorado" - lore1 = "Dano" - lore = ["Dano"] +name = "Poder Desarmado" +description = "Dano sin armas mejorado" +lore1 = "Dano" +lore = ["Dano"] [unarmed.sucker_punch] - name = "Golpe Traicionero" - description = "Golpes al esprintear, pero mas letales." - lore1 = "Dano" - lore2 = "El dano aumenta con tu velocidad mientras golpeas" - lore = ["Dano", "El dano aumenta con tu velocidad mientras golpeas"] +name = "Golpe Traicionero" +description = "Golpes al esprintear, pero mas letales." +lore1 = "Dano" +lore2 = "El dano aumenta con tu velocidad mientras golpeas" +lore = ["Dano", "El dano aumenta con tu velocidad mientras golpeas"] diff --git a/src/main/resources/fi_FI.toml b/src/main/resources/fi_FI.toml index a38e94ac4..fd268029b 100644 --- a/src/main/resources/fi_FI.toml +++ b/src/main/resources/fi_FI.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Täytyy liikkua!" - description = "Kävele yli 1 kilometri (1 000 lohkoa)" +title = "Täytyy liikkua!" +description = "Kävele yli 1 kilometri (1 000 lohkoa)" [advancement.challenge_sprint_5k] - title = "Juokse 5K!" - description = "Kävele yli 5 kilometriä (5 000 lohkoa)" +title = "Juokse 5K!" +description = "Kävele yli 5 kilometriä (5 000 lohkoa)" [advancement.challenge_sprint_50k] - title = "Zoomaa 50K!" - description = "Kävele yli 50 kilometriä (50 000 lohkoa)" +title = "Zoomaa 50K!" +description = "Kävele yli 50 kilometriä (50 000 lohkoa)" [advancement.challenge_sprint_500k] - title = "Halki maailmankaikkeuden!!" - description = "Kävele yli 500 kilometriä (500 000 lohkoa)" +title = "Halki maailmankaikkeuden!!" +description = "Kävele yli 500 kilometriä (500 000 lohkoa)" [advancement.challenge_sprint_marathon] - title = "Juokse (kirjaimellinen) maraton!" - description = "Juokse yli 42 195 lohkoa!" +title = "Juokse (kirjaimellinen) maraton!" +description = "Juokse yli 42 195 lohkoa!" [advancement.challenge_place_1k] - title = "Aloitteleva rakentaja!" - description = "Aseta 1 000 lohkoa" +title = "Aloitteleva rakentaja!" +description = "Aseta 1 000 lohkoa" [advancement.challenge_place_5k] - title = "Keskitason rakentaja!" - description = "Aseta 5 000 lohkoa" +title = "Keskitason rakentaja!" +description = "Aseta 5 000 lohkoa" [advancement.challenge_place_50k] - title = "Edistynyt rakentaja!" - description = "Aseta 50 000 lohkoa" +title = "Edistynyt rakentaja!" +description = "Aseta 50 000 lohkoa" [advancement.challenge_place_500k] - title = "Rakennusmestari!" - description = "Aseta 500 000 lohkoa" +title = "Rakennusmestari!" +description = "Aseta 500 000 lohkoa" [advancement.challenge_place_5m] - title = "Symmetrian akolyytti!" - description = "TODELLISUUS ON LEIKKIKENTTÄSI! (5 miljoonaa lohkoa)" +title = "Symmetrian akolyytti!" +description = "TODELLISUUS ON LEIKKIKENTTÄSI! (5 miljoonaa lohkoa)" [advancement.challenge_chop_1k] - title = "Aloitteleva metsuri!" - description = "Kaada 1 000 lohkoa" +title = "Aloitteleva metsuri!" +description = "Kaada 1 000 lohkoa" [advancement.challenge_chop_5k] - title = "Keskitason metsuri!" - description = "Kaada 5 000 lohkoa" +title = "Keskitason metsuri!" +description = "Kaada 5 000 lohkoa" [advancement.challenge_chop_50k] - title = "Edistynyt metsuri!" - description = "Kaada 50 000 lohkoa" +title = "Edistynyt metsuri!" +description = "Kaada 50 000 lohkoa" [advancement.challenge_chop_500k] - title = "Mestarimetsuri!" - description = "Kaada 500 000 lohkoa" +title = "Mestarimetsuri!" +description = "Kaada 500 000 lohkoa" [advancement.challenge_chop_5m] - title = "Jackson-koira" - description = "Maailman paras poika! (5 miljoonaa lohkoa)" +title = "Jackson-koira" +description = "Maailman paras poika! (5 miljoonaa lohkoa)" [advancement.challenge_block_1k] - title = "Tuskin torjuttu!" - description = "Torju 1000 osumaa" +title = "Tuskin torjuttu!" +description = "Torju 1000 osumaa" [advancement.challenge_block_5k] - title = "Torjuminen on hauskaa!" - description = "Torju 5000 osumaa" +title = "Torjuminen on hauskaa!" +description = "Torju 5000 osumaa" [advancement.challenge_block_50k] - title = "Torjuminen on elämäni!" - description = "Torju 50 000 osumaa" +title = "Torjuminen on elämäni!" +description = "Torju 50 000 osumaa" [advancement.challenge_block_500k] - title = "Torjuminen on tarkoitukseni!" - description = "Torju 500 000 osumaa" +title = "Torjuminen on tarkoitukseni!" +description = "Torju 500 000 osumaa" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Torju 5 000 000 osumaa" +title = "Die Hand Die Verletzt" +description = "Torju 5 000 000 osumaa" [advancement.challenge_brew_1k] - title = "Aloitteleva alkemisti!" - description = "Juo 1000 taikajuomaa" +title = "Aloitteleva alkemisti!" +description = "Juo 1000 taikajuomaa" [advancement.challenge_brew_5k] - title = "Keskitason alkemisti!" - description = "Juo 5000 taikajuomaa" +title = "Keskitason alkemisti!" +description = "Juo 5000 taikajuomaa" [advancement.challenge_brew_50k] - title = "Edistynyt alkemisti!" - description = "Juo 50 000 taikajuomaa" +title = "Edistynyt alkemisti!" +description = "Juo 50 000 taikajuomaa" [advancement.challenge_brew_500k] - title = "Mestarialkemisti!" - description = "Juo 500 000 taikajuomaa" +title = "Mestarialkemisti!" +description = "Juo 500 000 taikajuomaa" [advancement.challenge_brew_5m] - title = "Alkemisti" - description = "Juo 5 000 000 taikajuomaa" +title = "Alkemisti" +description = "Juo 5 000 000 taikajuomaa" [advancement.challenge_brewsplash_1k] - title = "Aloitteleva roiskuttaja!" - description = "Roiskuta 1000 taikajuomaa" +title = "Aloitteleva roiskuttaja!" +description = "Roiskuta 1000 taikajuomaa" [advancement.challenge_brewsplash_5k] - title = "Keskitason roiskuttaja!" - description = "Roiskuta 5000 taikajuomaa" +title = "Keskitason roiskuttaja!" +description = "Roiskuta 5000 taikajuomaa" [advancement.challenge_brewsplash_50k] - title = "Edistynyt roiskuttaja!" - description = "Roiskuta 50 000 taikajuomaa" +title = "Edistynyt roiskuttaja!" +description = "Roiskuta 50 000 taikajuomaa" [advancement.challenge_brewsplash_500k] - title = "Mestarioiskuttaja!" - description = "Roiskuta 500 000 taikajuomaa" +title = "Mestarioiskuttaja!" +description = "Roiskuta 500 000 taikajuomaa" [advancement.challenge_brewsplash_5m] - title = "Roiskumestari" - description = "Roiskuta 5 000 000 taikajuomaa" +title = "Roiskumestari" +description = "Roiskuta 5 000 000 taikajuomaa" [advancement.challenge_craft_1k] - title = "Taitava nikkaroija!" - description = "Valmista 1000 esinettä" +title = "Taitava nikkaroija!" +description = "Valmista 1000 esinettä" [advancement.challenge_craft_5k] - title = "Äkäinen nikkaroija!" - description = "Valmista 5000 esinettä" +title = "Äkäinen nikkaroija!" +description = "Valmista 5000 esinettä" [advancement.challenge_craft_50k] - title = "Uuttera nikkaroija!" - description = "Valmista 50 000 esinettä" +title = "Uuttera nikkaroija!" +description = "Valmista 50 000 esinettä" [advancement.challenge_craft_500k] - title = "Korviahuumaava nikkaroija!" - description = "Valmista 500 000 esinettä" +title = "Korviahuumaava nikkaroija!" +description = "Valmista 500 000 esinettä" [advancement.challenge_craft_5m] - title = "Tuhoisa McNikkaroija" - description = "Valmista 5 000 000 esinettä" +title = "Tuhoisa McNikkaroija" +description = "Valmista 5 000 000 esinettä" [advancement.challenge_enchant_1k] - title = "Aloitteleva lumooja!" - description = "Lumoa 1000 esinettä" +title = "Aloitteleva lumooja!" +description = "Lumoa 1000 esinettä" [advancement.challenge_enchant_5k] - title = "Keskitason lumooja!" - description = "Lumoa 5000 esinettä" +title = "Keskitason lumooja!" +description = "Lumoa 5000 esinettä" [advancement.challenge_enchant_50k] - title = "Edistynyt lumooja!" - description = "Lumoa 50 000 esinettä" +title = "Edistynyt lumooja!" +description = "Lumoa 50 000 esinettä" [advancement.challenge_enchant_500k] - title = "Mestarilumooja!" - description = "Lumoa 500 000 esinettä" +title = "Mestarilumooja!" +description = "Lumoa 500 000 esinettä" [advancement.challenge_enchant_5m] - title = "Arvoituksellinen lumooja" - description = "Lumoa 5 000 000 esinettä" +title = "Arvoituksellinen lumooja" +description = "Lumoa 5 000 000 esinettä" [advancement.challenge_excavate_1k] - title = "Innokas kaivaja!" - description = "Kaiva 1000 lohkoa" +title = "Innokas kaivaja!" +description = "Kaiva 1000 lohkoa" [advancement.challenge_excavate_5k] - title = "Keskitason kaivaja!" - description = "Kaiva 5000 lohkoa" +title = "Keskitason kaivaja!" +description = "Kaiva 5000 lohkoa" [advancement.challenge_excavate_50k] - title = "Edistynyt kaivaja!" - description = "Kaiva 50 000 lohkoa" +title = "Edistynyt kaivaja!" +description = "Kaiva 50 000 lohkoa" [advancement.challenge_excavate_500k] - title = "Mestarikaivaja!" - description = "Kaiva 500 000 lohkoa" +title = "Mestarikaivaja!" +description = "Kaiva 500 000 lohkoa" [advancement.challenge_excavate_5m] - title = "Arvoituksellinen kaivaja" - description = "Kaiva 5 000 000 lohkoa" +title = "Arvoituksellinen kaivaja" +description = "Kaiva 5 000 000 lohkoa" [advancement.horrible_person] - title = "Olet kauhea ihminen" - description = "Aivan käsittämätöntä, todella" +title = "Olet kauhea ihminen" +description = "Aivan käsittämätöntä, todella" [advancement.challenge_turtle_egg_smasher] - title = "Kilpikonnanmunien murskaja!" - description = "Riko 100 kilpikonnan munaa" +title = "Kilpikonnanmunien murskaja!" +description = "Riko 100 kilpikonnan munaa" [advancement.challenge_turtle_egg_annihilator] - title = "Kilpikonnanmunien tuhoaja!" - description = "Riko 500 kilpikonnan munaa" +title = "Kilpikonnanmunien tuhoaja!" +description = "Riko 500 kilpikonnan munaa" [advancement.challenge_novice_hunter] - title = "Aloitteleva metsästäjä!" - description = "Tapa 100 olentoa" +title = "Aloitteleva metsästäjä!" +description = "Tapa 100 olentoa" [advancement.challenge_intermediate_hunter] - title = "Keskitason metsästäjä!" - description = "Tapa 500 olentoa" +title = "Keskitason metsästäjä!" +description = "Tapa 500 olentoa" [advancement.challenge_advanced_hunter] - title = "Edistynyt metsästäjä!" - description = "Tapa 5000 olentoa" +title = "Edistynyt metsästäjä!" +description = "Tapa 5000 olentoa" [advancement.challenge_creeper_conqueror] - title = "Creeperien valloittaja!" - description = "Tapa 50 creeperiä" +title = "Creeperien valloittaja!" +description = "Tapa 50 creeperiä" [advancement.challenge_creeper_annihilator] - title = "Creeperien tuhoaja!" - description = "Tapa 200 creeperiä" +title = "Creeperien tuhoaja!" +description = "Tapa 200 creeperiä" [advancement.challenge_pickaxe_1k] - title = "Aloitteleva kaivosmies" - description = "Riko 1000 lohkoa" +title = "Aloitteleva kaivosmies" +description = "Riko 1000 lohkoa" [advancement.challenge_pickaxe_5k] - title = "Taitava kaivosmies" - description = "Riko 5000 lohkoa" +title = "Taitava kaivosmies" +description = "Riko 5000 lohkoa" [advancement.challenge_pickaxe_50k] - title = "Asiantunteva kaivosmies" - description = "Riko 50 000 lohkoa" +title = "Asiantunteva kaivosmies" +description = "Riko 50 000 lohkoa" [advancement.challenge_pickaxe_500k] - title = "Mestarikaivosmies" - description = "Riko 500 000 lohkoa" +title = "Mestarikaivosmies" +description = "Riko 500 000 lohkoa" [advancement.challenge_pickaxe_5m] - title = "Legendaarinen kaivosmies" - description = "Riko 5 000 000 lohkoa" +title = "Legendaarinen kaivosmies" +description = "Riko 5 000 000 lohkoa" [advancement.challenge_eat_100] - title = "Niin paljon syötävää!" - description = "Syö yli 100 esinettä!" +title = "Niin paljon syötävää!" +description = "Syö yli 100 esinettä!" [advancement.challenge_eat_1000] - title = "Sammumaton nälkä!" - description = "Syö yli 1 000 esinettä!" +title = "Sammumaton nälkä!" +description = "Syö yli 1 000 esinettä!" [advancement.challenge_eat_10000] - title = "IKUINEN NÄLKÄ!" - description = "Syö yli 10 000 esinettä!" +title = "IKUINEN NÄLKÄ!" +description = "Syö yli 10 000 esinettä!" [advancement.challenge_harvest_100] - title = "Täysi sato" - description = "Korjaa yli 100 satoa!" +title = "Täysi sato" +description = "Korjaa yli 100 satoa!" [advancement.challenge_harvest_1000] - title = "Suursato" - description = "Korjaa yli 1 000 satoa!" +title = "Suursato" +description = "Korjaa yli 1 000 satoa!" [advancement.challenge_swim_1nm] - title = "Ihmissukellusvene!" - description = "Ui 1 meripeninkulmaa (1 852 lohkoa)" +title = "Ihmissukellusvene!" +description = "Ui 1 meripeninkulmaa (1 852 lohkoa)" [advancement.challenge_sneak_1k] - title = "Polvikipu" - description = "Hiivi yli kilometri (1 000 lohkoa)" +title = "Polvikipu" +description = "Hiivi yli kilometri (1 000 lohkoa)" [advancement.challenge_sneak_5k] - title = "Varjokulkija" - description = "Hiivi yli 5 000 lohkoa" +title = "Varjokulkija" +description = "Hiivi yli 5 000 lohkoa" [advancement.challenge_sneak_20k] - title = "Haamu" - description = "Hiivi yli 20 000 lohkoa" +title = "Haamu" +description = "Hiivi yli 20 000 lohkoa" [advancement.challenge_swim_5k] - title = "Syvasukeltaja" - description = "Ui yli 5 000 lohkoa" +title = "Syvasukeltaja" +description = "Ui yli 5 000 lohkoa" [advancement.challenge_swim_20k] - title = "Poseidonin Valittu" - description = "Ui yli 20 000 lohkoa" +title = "Poseidonin Valittu" +description = "Ui yli 20 000 lohkoa" [advancement.challenge_sword_100] - title = "Ensiveri" - description = "Iske 100 kertaa miekalla" +title = "Ensiveri" +description = "Iske 100 kertaa miekalla" [advancement.challenge_sword_1k] - title = "Miekkatanssija" - description = "Iske 1 000 kertaa miekalla" +title = "Miekkatanssija" +description = "Iske 1 000 kertaa miekalla" [advancement.challenge_sword_10k] - title = "Tuhat Viiltoa" - description = "Iske 10 000 kertaa miekalla" +title = "Tuhat Viiltoa" +description = "Iske 10 000 kertaa miekalla" [advancement.challenge_unarmed_100] - title = "Baari-tappelija" - description = "Iske 100 kertaa paljain kasin" +title = "Baari-tappelija" +description = "Iske 100 kertaa paljain kasin" [advancement.challenge_unarmed_1k] - title = "Rautanyrkit" - description = "Iske 1 000 kertaa paljain kasin" +title = "Rautanyrkit" +description = "Iske 1 000 kertaa paljain kasin" [advancement.challenge_unarmed_10k] - title = "Yksi Isku" - description = "Iske 10 000 kertaa paljain kasin" +title = "Yksi Isku" +description = "Iske 10 000 kertaa paljain kasin" [advancement.challenge_trag_1k] - title = "Veren Hinta" - description = "Ota vastaan 1 000 vahinkoa" +title = "Veren Hinta" +description = "Ota vastaan 1 000 vahinkoa" [advancement.challenge_trag_10k] - title = "Purppuravyory" - description = "Ota vastaan 10 000 vahinkoa" +title = "Purppuravyory" +description = "Ota vastaan 10 000 vahinkoa" [advancement.challenge_trag_100k] - title = "Karsimyksen Avatar" - description = "Ota vastaan 100 000 vahinkoa" +title = "Karsimyksen Avatar" +description = "Ota vastaan 100 000 vahinkoa" [advancement.challenge_ranged_100] - title = "Tauluharjoitus" - description = "Ammu 100 ammusta" +title = "Tauluharjoitus" +description = "Ammu 100 ammusta" [advancement.challenge_ranged_1k] - title = "Haukansilma" - description = "Ammu 1 000 ammusta" +title = "Haukansilma" +description = "Ammu 1 000 ammusta" [advancement.challenge_ranged_10k] - title = "Nuolimyrsky" - description = "Ammu 10 000 ammusta" +title = "Nuolimyrsky" +description = "Ammu 10 000 ammusta" [advancement.challenge_chronos_1h] - title = "Tik Tak" - description = "Vieta 1 tunti verkossa" +title = "Tik Tak" +description = "Vieta 1 tunti verkossa" [advancement.challenge_chronos_24h] - title = "Ajan Hiekat" - description = "Vieta 24 tuntia verkossa" +title = "Ajan Hiekat" +description = "Vieta 24 tuntia verkossa" [advancement.challenge_chronos_168h] - title = "Ajaton" - description = "Vieta 168 tuntia (1 viikko) verkossa" +title = "Ajaton" +description = "Vieta 168 tuntia (1 viikko) verkossa" [advancement.challenge_nether_50] - title = "Helvetin Portinvartija" - description = "Tapa 50 nether-olentoa" +title = "Helvetin Portinvartija" +description = "Tapa 50 nether-olentoa" [advancement.challenge_nether_500] - title = "Syvyyksien Vartija" - description = "Tapa 500 nether-olentoa" +title = "Syvyyksien Vartija" +description = "Tapa 500 nether-olentoa" [advancement.challenge_nether_5k] - title = "Netherin Herra" - description = "Tapa 5 000 nether-olentoa" +title = "Netherin Herra" +description = "Tapa 5 000 nether-olentoa" [advancement.challenge_rift_50] - title = "Avaruusanomalia" - description = "Teleporttaa 50 kertaa" +title = "Avaruusanomalia" +description = "Teleporttaa 50 kertaa" [advancement.challenge_rift_500] - title = "Tyhjon Kulkija" - description = "Teleporttaa 500 kertaa" +title = "Tyhjon Kulkija" +description = "Teleporttaa 500 kertaa" [advancement.challenge_rift_5k] - title = "Maailmojen Valilla" - description = "Teleporttaa 5 000 kertaa" +title = "Maailmojen Valilla" +description = "Teleporttaa 5 000 kertaa" [advancement.challenge_taming_10] - title = "Elainten Kuiskaaja" - description = "Jalosta 10 elaintta" +title = "Elainten Kuiskaaja" +description = "Jalosta 10 elaintta" [advancement.challenge_taming_50] - title = "Lauman Johtaja" - description = "Jalosta 50 elaintta" +title = "Lauman Johtaja" +description = "Jalosta 50 elaintta" [advancement.challenge_taming_500] - title = "Petojen Herra" - description = "Jalosta 500 elaintta" +title = "Petojen Herra" +description = "Jalosta 500 elaintta" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Nopeuden Demoni" - description = "Juokse yli 5 Kilometria (5,000 lohkoa)" +title = "Nopeuden Demoni" +description = "Juokse yli 5 Kilometria (5,000 lohkoa)" [advancement.challenge_sprint_dist_50k] - title = "Salamajalat" - description = "Juokse yli 50 Kilometria (50,000 lohkoa)" +title = "Salamajalat" +description = "Juokse yli 50 Kilometria (50,000 lohkoa)" [advancement.challenge_agility_swim_1k] - title = "Vedenkävijä" - description = "Ui yli 1 Kilometri (1,000 lohkoa)" +title = "Vedenkävijä" +description = "Ui yli 1 Kilometri (1,000 lohkoa)" [advancement.challenge_agility_swim_10k] - title = "Meren Kulkija" - description = "Ui yli 10 Kilometria (10,000 lohkoa)" +title = "Meren Kulkija" +description = "Ui yli 10 Kilometria (10,000 lohkoa)" [advancement.challenge_fly_1k] - title = "Taivaan Tanssija" - description = "Lennä yli 1 Kilometri (1,000 lohkoa)" +title = "Taivaan Tanssija" +description = "Lennä yli 1 Kilometri (1,000 lohkoa)" [advancement.challenge_fly_10k] - title = "Tuulen Ratsastaja" - description = "Lennä yli 10 Kilometria (10,000 lohkoa)" +title = "Tuulen Ratsastaja" +description = "Lennä yli 10 Kilometria (10,000 lohkoa)" [advancement.challenge_agility_sneak_500] - title = "Hiljaiset Askeleet" - description = "Hiivy yli 500 lohkoa" +title = "Hiljaiset Askeleet" +description = "Hiivy yli 500 lohkoa" [advancement.challenge_agility_sneak_5k] - title = "Haamuaskeleet" - description = "Hiivy yli 5 Kilometria (5,000 lohkoa)" +title = "Haamuaskeleet" +description = "Hiivy yli 5 Kilometria (5,000 lohkoa)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Purkuporukka" - description = "Riko 500 lohkoa" +title = "Purkuporukka" +description = "Riko 500 lohkoa" [advancement.challenge_demolish_5k] - title = "Tuhoamispallo" - description = "Riko 5,000 lohkoa" +title = "Tuhoamispallo" +description = "Riko 5,000 lohkoa" [advancement.challenge_value_placed_10k] - title = "Arvokas Rakentaja" - description = "Aseta lohkoja 10,000 arvon edestä" +title = "Arvokas Rakentaja" +description = "Aseta lohkoja 10,000 arvon edestä" [advancement.challenge_value_placed_100k] - title = "Mestariarkkitehti" - description = "Aseta lohkoja 100,000 arvon edestä" +title = "Mestariarkkitehti" +description = "Aseta lohkoja 100,000 arvon edestä" [advancement.challenge_demolish_val_5k] - title = "Kierrätysasiantuntija" - description = "Pelasta 5,000 lohkoarvoa purkamisesta" +title = "Kierrätysasiantuntija" +description = "Pelasta 5,000 lohkoarvoa purkamisesta" [advancement.challenge_demolish_val_50k] - title = "Täysi Purku" - description = "Pelasta 50,000 lohkoarvoa purkamisesta" +title = "Täysi Purku" +description = "Pelasta 50,000 lohkoarvoa purkamisesta" [advancement.challenge_high_build_100] - title = "Taivaanrakentaja" - description = "Aseta 100 lohkoa Y=128 yläpuolelle" +title = "Taivaanrakentaja" +description = "Aseta 100 lohkoa Y=128 yläpuolelle" [advancement.challenge_high_build_1k] - title = "Pilviarkkitehti" - description = "Aseta 1,000 lohkoa Y=128 yläpuolelle" +title = "Pilviarkkitehti" +description = "Aseta 1,000 lohkoa Y=128 yläpuolelle" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Kirveen Heiluttaja" - description = "Heiluta kirvestä 500 kertaa" +title = "Kirveen Heiluttaja" +description = "Heiluta kirvestä 500 kertaa" [advancement.challenge_axe_swing_5k] - title = "Berserki" - description = "Heiluta kirvestä 5,000 kertaa" +title = "Berserki" +description = "Heiluta kirvestä 5,000 kertaa" [advancement.challenge_axe_damage_1k] - title = "Halkaisija" - description = "Tee 1,000 vahinkoa kirveellä" +title = "Halkaisija" +description = "Tee 1,000 vahinkoa kirveellä" [advancement.challenge_axe_damage_10k] - title = "Pyövelin Kirves" - description = "Tee 10,000 vahinkoa kirveellä" +title = "Pyövelin Kirves" +description = "Tee 10,000 vahinkoa kirveellä" [advancement.challenge_axe_value_5k] - title = "Puukauppias" - description = "Kerää puuta 5,000 arvon edestä" +title = "Puukauppias" +description = "Kerää puuta 5,000 arvon edestä" [advancement.challenge_axe_value_50k] - title = "Metsäparoni" - description = "Kerää puuta 50,000 arvon edestä" +title = "Metsäparoni" +description = "Kerää puuta 50,000 arvon edestä" [advancement.challenge_leaves_500] - title = "Lehtipuhallin" - description = "Raivaa 500 lehtilohkoa kirveellä" +title = "Lehtipuhallin" +description = "Raivaa 500 lehtilohkoa kirveellä" [advancement.challenge_leaves_5k] - title = "Lehdenpudottaja" - description = "Raivaa 5,000 lehtilohkoa kirveellä" +title = "Lehdenpudottaja" +description = "Raivaa 5,000 lehtilohkoa kirveellä" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Vahingon Imeijä" - description = "Torju 1,000 vahinkoa kilvellä" +title = "Vahingon Imeijä" +description = "Torju 1,000 vahinkoa kilvellä" [advancement.challenge_block_dmg_10k] - title = "Elävä Kilpi" - description = "Torju 10,000 vahinkoa kilvellä" +title = "Elävä Kilpi" +description = "Torju 10,000 vahinkoa kilvellä" [advancement.challenge_block_proj_100] - title = "Nuolenkääntäjä" - description = "Torju 100 ammusta kilvellä" +title = "Nuolenkääntäjä" +description = "Torju 100 ammusta kilvellä" [advancement.challenge_block_proj_1k] - title = "Ammuskilpi" - description = "Torju 1,000 ammusta kilvellä" +title = "Ammuskilpi" +description = "Torju 1,000 ammusta kilvellä" [advancement.challenge_block_melee_500] - title = "Torjuntamestari" - description = "Torju 500 lähitaisteluhyökkäystä kilvellä" +title = "Torjuntamestari" +description = "Torju 500 lähitaisteluhyökkäystä kilvellä" [advancement.challenge_block_melee_5k] - title = "Rautainen Linnake" - description = "Torju 5,000 lähitaisteluhyökkäystä kilvellä" +title = "Rautainen Linnake" +description = "Torju 5,000 lähitaisteluhyökkäystä kilvellä" [advancement.challenge_block_heavy_50] - title = "Panssarivaunu" - description = "Torju 50 raskasta hyökkäystä (yli 5 vahinkoa)" +title = "Panssarivaunu" +description = "Torju 50 raskasta hyökkäystä (yli 5 vahinkoa)" [advancement.challenge_block_heavy_500] - title = "Järkkymätön Esine" - description = "Torju 500 raskasta hyökkäystä (yli 5 vahinkoa)" +title = "Järkkymätön Esine" +description = "Torju 500 raskasta hyökkäystä (yli 5 vahinkoa)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Panimon Varusteet" - description = "Aseta 10 panemoteline" +title = "Panimon Varusteet" +description = "Aseta 10 panemoteline" [advancement.challenge_brew_stands_50] - title = "Liemitehdas" - description = "Aseta 50 panemotelinetta" +title = "Liemitehdas" +description = "Aseta 50 panemotelinetta" [advancement.challenge_brew_strong_25] - title = "Vahva Keitos" - description = "Juo 25 tehostettua liemea" +title = "Vahva Keitos" +description = "Juo 25 tehostettua liemea" [advancement.challenge_brew_strong_250] - title = "Maksimiteho" - description = "Juo 250 tehostettua liemea" +title = "Maksimiteho" +description = "Juo 250 tehostettua liemea" [advancement.challenge_brew_splash_hits_50] - title = "Roiskealue" - description = "Osuma 50 olentoon roiskejuomilla" +title = "Roiskealue" +description = "Osuma 50 olentoon roiskejuomilla" [advancement.challenge_brew_splash_hits_500] - title = "Ruttohtori" - description = "Osuma 500 olentoon roiskejuomilla" +title = "Ruttohtori" +description = "Osuma 500 olentoon roiskejuomilla" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Levoton" - description = "Matkusta 1 Kilometri aktiivisena" +title = "Levoton" +description = "Matkusta 1 Kilometri aktiivisena" [advancement.challenge_active_dist_10k] - title = "Tienraivaaja" - description = "Matkusta 10 Kilometria aktiivisena" +title = "Tienraivaaja" +description = "Matkusta 10 Kilometria aktiivisena" [advancement.challenge_active_dist_100k] - title = "Ikiliikkuja" - description = "Matkusta 100 Kilometria aktiivisena" +title = "Ikiliikkuja" +description = "Matkusta 100 Kilometria aktiivisena" [advancement.challenge_beds_10] - title = "Aikainen Herääjä" - description = "Nuku sängyssä 10 kertaa" +title = "Aikainen Herääjä" +description = "Nuku sängyssä 10 kertaa" [advancement.challenge_beds_100] - title = "Ajan Ohittaja" - description = "Nuku sängyssä 100 kertaa" +title = "Ajan Ohittaja" +description = "Nuku sängyssä 100 kertaa" [advancement.challenge_chronos_tp_50] - title = "Ajallinen Siirtymä" - description = "Teleporttaa 50 kertaa" +title = "Ajallinen Siirtymä" +description = "Teleporttaa 50 kertaa" [advancement.challenge_chronos_tp_500] - title = "Ajan Vääristymä" - description = "Teleporttaa 500 kertaa" +title = "Ajan Vääristymä" +description = "Teleporttaa 500 kertaa" [advancement.challenge_chronos_deaths_10] - title = "Kuolevainen" - description = "Kuole 10 kertaa" +title = "Kuolevainen" +description = "Kuole 10 kertaa" [advancement.challenge_chronos_deaths_100] - title = "Kuoleman Uhmaaja" - description = "Kuole 100 kertaa" +title = "Kuoleman Uhmaaja" +description = "Kuole 100 kertaa" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Käsityön Arvo" - description = "Valmista esineitä 10,000 kokonaisarvon edestä" +title = "Käsityön Arvo" +description = "Valmista esineitä 10,000 kokonaisarvon edestä" [advancement.challenge_craft_value_100k] - title = "Taitokäsityöläinen" - description = "Valmista esineitä 100,000 kokonaisarvon edestä" +title = "Taitokäsityöläinen" +description = "Valmista esineitä 100,000 kokonaisarvon edestä" [advancement.challenge_craft_tools_25] - title = "Työkaluseppä" - description = "Valmista 25 työkalua" +title = "Työkaluseppä" +description = "Valmista 25 työkalua" [advancement.challenge_craft_tools_250] - title = "Mestaritakoja" - description = "Valmista 250 työkalua" +title = "Mestaritakoja" +description = "Valmista 250 työkalua" [advancement.challenge_craft_armor_25] - title = "Haarniskaseppä" - description = "Valmista 25 haarniskakappaletta" +title = "Haarniskaseppä" +description = "Valmista 25 haarniskakappaletta" [advancement.challenge_craft_armor_250] - title = "Mestarihaarniskaseppä" - description = "Valmista 250 haarniskakappaletta" +title = "Mestarihaarniskaseppä" +description = "Valmista 250 haarniskakappaletta" [advancement.challenge_craft_mass_25k] - title = "Massatuottaja" - description = "Valmista 25,000 esinettä" +title = "Massatuottaja" +description = "Valmista 25,000 esinettä" [advancement.challenge_craft_mass_250k] - title = "Teollinen Vallankumous" - description = "Valmista 250,000 esinettä" +title = "Teollinen Vallankumous" +description = "Valmista 250,000 esinettä" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Keräilijä" - description = "Löydä 50 ainutlaatuista esinettä" +title = "Keräilijä" +description = "Löydä 50 ainutlaatuista esinettä" [advancement.challenge_discover_items_250] - title = "Luetteloija" - description = "Löydä 250 ainutlaatuista esinettä" +title = "Luetteloija" +description = "Löydä 250 ainutlaatuista esinettä" [advancement.challenge_discover_blocks_50] - title = "Kartoittaja" - description = "Löydä 50 ainutlaatuista lohkoa" +title = "Kartoittaja" +description = "Löydä 50 ainutlaatuista lohkoa" [advancement.challenge_discover_blocks_250] - title = "Geologi" - description = "Löydä 250 ainutlaatuista lohkoa" +title = "Geologi" +description = "Löydä 250 ainutlaatuista lohkoa" [advancement.challenge_discover_mobs_25] - title = "Tarkkailija" - description = "Löydä 25 ainutlaatuista olentoa" +title = "Tarkkailija" +description = "Löydä 25 ainutlaatuista olentoa" [advancement.challenge_discover_mobs_75] - title = "Luonnontutkija" - description = "Löydä 75 ainutlaatuista olentoa" +title = "Luonnontutkija" +description = "Löydä 75 ainutlaatuista olentoa" [advancement.challenge_discover_biomes_10] - title = "Vaeltaja" - description = "Löydä 10 ainutlaatuista biomia" +title = "Vaeltaja" +description = "Löydä 10 ainutlaatuista biomia" [advancement.challenge_discover_biomes_40] - title = "Maailmanmatkaaja" - description = "Löydä 40 ainutlaatuista biomia" +title = "Maailmanmatkaaja" +description = "Löydä 40 ainutlaatuista biomia" [advancement.challenge_discover_foods_10] - title = "Herkkusuu" - description = "Löydä 10 ainutlaatuista ruokaa" +title = "Herkkusuu" +description = "Löydä 10 ainutlaatuista ruokaa" [advancement.challenge_discover_foods_30] - title = "Keittiömestari" - description = "Löydä 30 ainutlaatuista ruokaa" +title = "Keittiömestari" +description = "Löydä 30 ainutlaatuista ruokaa" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Voiman Kutoja" - description = "Kerää 100 lumoamisvoimaa" +title = "Voiman Kutoja" +description = "Kerää 100 lumoamisvoimaa" [advancement.challenge_enchant_power_1k] - title = "Taikuuden Mestari" - description = "Kerää 1,000 lumoamisvoimaa" +title = "Taikuuden Mestari" +description = "Kerää 1,000 lumoamisvoimaa" [advancement.challenge_enchant_levels_1k] - title = "Tasojen Kuluttaja" - description = "Käytä 1,000 kokemustasoa lumoamiseen" +title = "Tasojen Kuluttaja" +description = "Käytä 1,000 kokemustasoa lumoamiseen" [advancement.challenge_enchant_levels_10k] - title = "XP-nielu" - description = "Käytä 10,000 kokemustasoa lumoamiseen" +title = "XP-nielu" +description = "Käytä 10,000 kokemustasoa lumoamiseen" [advancement.challenge_enchant_high_25] - title = "Suurpelaaja" - description = "Suorita 25 maksimitason lumoamista" +title = "Suurpelaaja" +description = "Suorita 25 maksimitason lumoamista" [advancement.challenge_enchant_high_250] - title = "Legendaarinen Lumooja" - description = "Suorita 250 maksimitason lumoamista" +title = "Legendaarinen Lumooja" +description = "Suorita 250 maksimitason lumoamista" [advancement.challenge_enchant_total_500] - title = "Tasojen Polttaja" - description = "Käytä 500 kokonaistasoa kaikkiin lumouksiin" +title = "Tasojen Polttaja" +description = "Käytä 500 kokonaistasoa kaikkiin lumouksiin" [advancement.challenge_enchant_total_5k] - title = "Taikainvestointi" - description = "Käytä 5,000 kokonaistasoa kaikkiin lumouksiin" +title = "Taikainvestointi" +description = "Käytä 5,000 kokonaistasoa kaikkiin lumouksiin" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Kaivaja" - description = "Heiluta lapiota 500 kertaa" +title = "Kaivaja" +description = "Heiluta lapiota 500 kertaa" [advancement.challenge_dig_swing_5k] - title = "Kaivinkone" - description = "Heiluta lapiota 5,000 kertaa" +title = "Kaivinkone" +description = "Heiluta lapiota 5,000 kertaa" [advancement.challenge_dig_damage_1k] - title = "Lapiosoturi" - description = "Tee 1,000 vahinkoa lapiolla" +title = "Lapiosoturi" +description = "Tee 1,000 vahinkoa lapiolla" [advancement.challenge_dig_damage_10k] - title = "Lapiomestari" - description = "Tee 10,000 vahinkoa lapiolla" +title = "Lapiomestari" +description = "Tee 10,000 vahinkoa lapiolla" [advancement.challenge_dig_value_5k] - title = "Maakauppias" - description = "Kaiva lohkoja 5,000 arvon edestä" +title = "Maakauppias" +description = "Kaiva lohkoja 5,000 arvon edestä" [advancement.challenge_dig_value_50k] - title = "Maaparoni" - description = "Kaiva lohkoja 50,000 arvon edestä" +title = "Maaparoni" +description = "Kaiva lohkoja 50,000 arvon edestä" [advancement.challenge_dig_gravel_500] - title = "Soranjauhaja" - description = "Kaiva 500 sora-, hiekka- tai savilohkoa" +title = "Soranjauhaja" +description = "Kaiva 500 sora-, hiekka- tai savilohkoa" [advancement.challenge_dig_gravel_5k] - title = "Hiekanseuloja" - description = "Kaiva 5,000 sora-, hiekka- tai savilohkoa" +title = "Hiekanseuloja" +description = "Kaiva 5,000 sora-, hiekka- tai savilohkoa" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Siementen Kylväjä" - description = "Istuta 100 kasvia" +title = "Siementen Kylväjä" +description = "Istuta 100 kasvia" [advancement.challenge_plant_1k] - title = "Vihreä Peukalo" - description = "Istuta 1,000 kasvia" +title = "Vihreä Peukalo" +description = "Istuta 1,000 kasvia" [advancement.challenge_plant_5k] - title = "Maatalousparoni" - description = "Istuta 5,000 kasvia" +title = "Maatalousparoni" +description = "Istuta 5,000 kasvia" [advancement.challenge_compost_50] - title = "Kierrättäjä" - description = "Kompostoi 50 esinettä" +title = "Kierrättäjä" +description = "Kompostoi 50 esinettä" [advancement.challenge_compost_500] - title = "Maan Rikastuttaja" - description = "Kompostoi 500 esinettä" +title = "Maan Rikastuttaja" +description = "Kompostoi 500 esinettä" [advancement.challenge_shear_50] - title = "Keritsijä" - description = "Keritä 50 olentoa" +title = "Keritsijä" +description = "Keritä 50 olentoa" [advancement.challenge_shear_250] - title = "Lauman Herra" - description = "Keritä 250 olentoa" +title = "Lauman Herra" +description = "Keritä 250 olentoa" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Tappaja" - description = "Tuhoa 500 olentoa" +title = "Tappaja" +description = "Tuhoa 500 olentoa" [advancement.challenge_kills_5k] - title = "Pyöveli" - description = "Tuhoa 5,000 olentoa" +title = "Pyöveli" +description = "Tuhoa 5,000 olentoa" [advancement.challenge_boss_1] - title = "Pomohaaastaja" - description = "Tuhoa pomo-olento" +title = "Pomohaaastaja" +description = "Tuhoa pomo-olento" [advancement.challenge_boss_10] - title = "Legendantuhoaja" - description = "Tuhoa 10 pomo-olentoa" +title = "Legendantuhoaja" +description = "Tuhoa 10 pomo-olentoa" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Kuihtunut" - description = "Kestä 500 kuihtumis-vahinkoa" +title = "Kuihtunut" +description = "Kestä 500 kuihtumis-vahinkoa" [advancement.challenge_wither_dmg_5k] - title = "Taudin Selviytyjä" - description = "Kestä 5,000 kuihtumis-vahinkoa" +title = "Taudin Selviytyjä" +description = "Kestä 5,000 kuihtumis-vahinkoa" [advancement.challenge_wither_skel_25] - title = "Luunkerääjä" - description = "Tuhoa 25 wither-luurankoa" +title = "Luunkerääjä" +description = "Tuhoa 25 wither-luurankoa" [advancement.challenge_wither_skel_250] - title = "Luurankojen Vitsaus" - description = "Tuhoa 250 wither-luurankoa" +title = "Luurankojen Vitsaus" +description = "Tuhoa 250 wither-luurankoa" [advancement.challenge_wither_boss_1] - title = "Witherin Tuhoaja" - description = "Voita Wither" +title = "Witherin Tuhoaja" +description = "Voita Wither" [advancement.challenge_wither_boss_10] - title = "Netherin Hallitsija" - description = "Voita Wither 10 kertaa" +title = "Netherin Hallitsija" +description = "Voita Wither 10 kertaa" [advancement.challenge_roses_10] - title = "Kuoleman Puutarhuri" - description = "Riko 10 wither-ruusua" +title = "Kuoleman Puutarhuri" +description = "Riko 10 wither-ruusua" [advancement.challenge_roses_100] - title = "Taudin Kerääjä" - description = "Riko 100 wither-ruusua" +title = "Taudin Kerääjä" +description = "Riko 100 wither-ruusua" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Kaivosmiehen Käsi" - description = "Heiluta hakuukirvestä 500 kertaa" +title = "Kaivosmiehen Käsi" +description = "Heiluta hakuukirvestä 500 kertaa" [advancement.challenge_pick_swing_5k] - title = "Tunnelinkaivaja" - description = "Heiluta hakuukirvestä 5,000 kertaa" +title = "Tunnelinkaivaja" +description = "Heiluta hakuukirvestä 5,000 kertaa" [advancement.challenge_pick_damage_1k] - title = "Hakutaistelija" - description = "Tee 1,000 vahinkoa hakuukirveellä" +title = "Hakutaistelija" +description = "Tee 1,000 vahinkoa hakuukirveellä" [advancement.challenge_pick_damage_10k] - title = "Sotahakku" - description = "Tee 10,000 vahinkoa hakuukirveellä" +title = "Sotahakku" +description = "Tee 10,000 vahinkoa hakuukirveellä" [advancement.challenge_pick_value_5k] - title = "Jalokivenetsijä" - description = "Louhii lohkoja 5,000 arvon edestä" +title = "Jalokivenetsijä" +description = "Louhii lohkoja 5,000 arvon edestä" [advancement.challenge_pick_value_50k] - title = "Malmiparoni" - description = "Louhii lohkoja 50,000 arvon edestä" +title = "Malmiparoni" +description = "Louhii lohkoja 50,000 arvon edestä" [advancement.challenge_pick_ores_500] - title = "Malminetsijä" - description = "Louhii 500 malmilohkoa" +title = "Malminetsijä" +description = "Louhii 500 malmilohkoa" [advancement.challenge_pick_ores_5k] - title = "Mestarikaivostyöläinen" - description = "Louhii 5,000 malmilohkoa" +title = "Mestarikaivostyöläinen" +description = "Louhii 5,000 malmilohkoa" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Tarkka-ampuja" - description = "Tee 1,000 etävahinkoa" +title = "Tarkka-ampuja" +description = "Tee 1,000 etävahinkoa" [advancement.challenge_ranged_dmg_10k] - title = "Tappava Jousimies" - description = "Tee 10,000 etävahinkoa" +title = "Tappava Jousimies" +description = "Tee 10,000 etävahinkoa" [advancement.challenge_ranged_dist_5k] - title = "Pitkä Kantama" - description = "Ammu ammuksia 5,000 lohkon kokonaismatkalle" +title = "Pitkä Kantama" +description = "Ammu ammuksia 5,000 lohkon kokonaismatkalle" [advancement.challenge_ranged_dist_50k] - title = "Mailia-ampuja" - description = "Ammu ammuksia 50,000 lohkon kokonaismatkalle" +title = "Mailia-ampuja" +description = "Ammu ammuksia 50,000 lohkon kokonaismatkalle" [advancement.challenge_ranged_kills_50] - title = "Jousimies" - description = "Tapa 50 olentoa etäaseilla" +title = "Jousimies" +description = "Tapa 50 olentoa etäaseilla" [advancement.challenge_ranged_kills_500] - title = "Mestarijousimies" - description = "Tapa 500 olentoa etäaseilla" +title = "Mestarijousimies" +description = "Tapa 500 olentoa etäaseilla" [advancement.challenge_longshot_25] - title = "Tarkkuuskivääri" - description = "Osuma 25 pitkän matkan osumaa (yli 30 lohkoa)" +title = "Tarkkuuskivääri" +description = "Osuma 25 pitkän matkan osumaa (yli 30 lohkoa)" [advancement.challenge_longshot_250] - title = "Kotkansilmä" - description = "Osuma 250 pitkän matkan osumaa (yli 30 lohkoa)" +title = "Kotkansilmä" +description = "Osuma 250 pitkän matkan osumaa (yli 30 lohkoa)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Helmenheittäjä" - description = "Heitä 50 enderhelmeä" +title = "Helmenheittäjä" +description = "Heitä 50 enderhelmeä" [advancement.challenge_rift_pearls_500] - title = "Teleporttiaddikti" - description = "Heitä 500 enderhelmeä" +title = "Teleporttiaddikti" +description = "Heitä 500 enderhelmeä" [advancement.challenge_rift_enderman_50] - title = "Enderman-metsästäjä" - description = "Tuhoa 50 endermania" +title = "Enderman-metsästäjä" +description = "Tuhoa 50 endermania" [advancement.challenge_rift_enderman_500] - title = "Tyhjyyden Vainooja" - description = "Tuhoa 500 endermania" +title = "Tyhjyyden Vainooja" +description = "Tuhoa 500 endermania" [advancement.challenge_rift_dragon_500] - title = "Lohikäärmetaistelija" - description = "Tee 500 vahinkoa Enderilohikäärmeelle" +title = "Lohikäärmetaistelija" +description = "Tee 500 vahinkoa Enderilohikäärmeelle" [advancement.challenge_rift_dragon_5k] - title = "Lohikäärmeen Vitsaus" - description = "Tee 5,000 vahinkoa Enderilohikäärmeelle" +title = "Lohikäärmeen Vitsaus" +description = "Tee 5,000 vahinkoa Enderilohikäärmeelle" [advancement.challenge_rift_crystal_10] - title = "Kristallinmurskaaja" - description = "Tuhoa 10 enderkristallia" +title = "Kristallinmurskaaja" +description = "Tuhoa 10 enderkristallia" [advancement.challenge_rift_crystal_100] - title = "Endin Tuhoaja" - description = "Tuhoa 100 enderkristallia" +title = "Endin Tuhoaja" +description = "Tuhoa 100 enderkristallia" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Onkija" - description = "Pyydystä 25 kalaa" +title = "Onkija" +description = "Pyydystä 25 kalaa" [advancement.challenge_fish_250] - title = "Mestarikalastaja" - description = "Pyydystä 250 kalaa" +title = "Mestarikalastaja" +description = "Pyydystä 250 kalaa" [advancement.challenge_drowned_25] - title = "Hukkuneiden Metsästäjä" - description = "Tuhoa 25 hukkunutta" +title = "Hukkuneiden Metsästäjä" +description = "Tuhoa 25 hukkunutta" [advancement.challenge_drowned_250] - title = "Valtameren Puhdistaja" - description = "Tuhoa 250 hukkunutta" +title = "Valtameren Puhdistaja" +description = "Tuhoa 250 hukkunutta" [advancement.challenge_guardian_10] - title = "Vartijantappaja" - description = "Tuhoa 10 vartijaa" +title = "Vartijantappaja" +description = "Tuhoa 10 vartijaa" [advancement.challenge_guardian_100] - title = "Temppelinhyökkääjä" - description = "Tuhoa 100 vartijaa" +title = "Temppelinhyökkääjä" +description = "Tuhoa 100 vartijaa" [advancement.challenge_underwater_blocks_100] - title = "Vedenalainen Kaivaja" - description = "Riko 100 lohkoa veden alla" +title = "Vedenalainen Kaivaja" +description = "Riko 100 lohkoa veden alla" [advancement.challenge_underwater_blocks_1k] - title = "Vesi-insinööri" - description = "Riko 1,000 lohkoa veden alla" +title = "Vesi-insinööri" +description = "Riko 1,000 lohkoa veden alla" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Selkäänpuukottaja" - description = "Tee 500 vahinkoa hiipien" +title = "Selkäänpuukottaja" +description = "Tee 500 vahinkoa hiipien" [advancement.challenge_stealth_dmg_5k] - title = "Hiljainen Tappaja" - description = "Tee 5,000 vahinkoa hiipien" +title = "Hiljainen Tappaja" +description = "Tee 5,000 vahinkoa hiipien" [advancement.challenge_stealth_kills_10] - title = "Salamurhaaja" - description = "Tapa 10 olentoa hiipien" +title = "Salamurhaaja" +description = "Tapa 10 olentoa hiipien" [advancement.challenge_stealth_kills_100] - title = "Varjojen Niittäjä" - description = "Tapa 100 olentoa hiipien" +title = "Varjojen Niittäjä" +description = "Tapa 100 olentoa hiipien" [advancement.challenge_stealth_time_1h] - title = "Kärsivällinen" - description = "Vietä 1 tunti hiipien (3,600 sekuntia)" +title = "Kärsivällinen" +description = "Vietä 1 tunti hiipien (3,600 sekuntia)" [advancement.challenge_stealth_time_10h] - title = "Varjojen Mestari" - description = "Vietä 10 tuntia hiipien (36,000 sekuntia)" +title = "Varjojen Mestari" +description = "Vietä 10 tuntia hiipien (36,000 sekuntia)" [advancement.challenge_stealth_arrows_50] - title = "Hiljainen Jousimies" - description = "Ammu 50 nuolta hiipien" +title = "Hiljainen Jousimies" +description = "Ammu 50 nuolta hiipien" [advancement.challenge_stealth_arrows_500] - title = "Haamujousimies" - description = "Ammu 500 nuolta hiipien" +title = "Haamujousimies" +description = "Ammu 500 nuolta hiipien" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Miekan Oppilas" - description = "Tee 1,000 vahinkoa miekalla" +title = "Miekan Oppilas" +description = "Tee 1,000 vahinkoa miekalla" [advancement.challenge_sword_dmg_10k] - title = "Miekkamies" - description = "Tee 10,000 vahinkoa miekalla" +title = "Miekkamies" +description = "Tee 10,000 vahinkoa miekalla" [advancement.challenge_sword_kills_50] - title = "Kaksintaistelija" - description = "Tapa 50 olentoa miekalla" +title = "Kaksintaistelija" +description = "Tapa 50 olentoa miekalla" [advancement.challenge_sword_kills_500] - title = "Gladiaattori" - description = "Tapa 500 olentoa miekalla" +title = "Gladiaattori" +description = "Tapa 500 olentoa miekalla" [advancement.challenge_sword_crit_50] - title = "Kriittinen Iskijä" - description = "Osuma 50 kriittistä osumaa miekalla" +title = "Kriittinen Iskijä" +description = "Osuma 50 kriittistä osumaa miekalla" [advancement.challenge_sword_crit_500] - title = "Tarkkuusmestari" - description = "Osuma 500 kriittistä osumaa miekalla" +title = "Tarkkuusmestari" +description = "Osuma 500 kriittistä osumaa miekalla" [advancement.challenge_sword_heavy_25] - title = "Raskas Lyönti" - description = "Osuma 25 raskasta osumaa miekalla (yli 8 vahinkoa)" +title = "Raskas Lyönti" +description = "Osuma 25 raskasta osumaa miekalla (yli 8 vahinkoa)" [advancement.challenge_sword_heavy_250] - title = "Tuhoisa Isku" - description = "Osuma 250 raskasta osumaa miekalla (yli 8 vahinkoa)" +title = "Tuhoisa Isku" +description = "Osuma 250 raskasta osumaa miekalla (yli 8 vahinkoa)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Pedon Kouluttaja" - description = "Lemmikkisi tekevät yhteensä 500 vahinkoa" +title = "Pedon Kouluttaja" +description = "Lemmikkisi tekevät yhteensä 500 vahinkoa" [advancement.challenge_pet_dmg_5k] - title = "Sodanjohtaja" - description = "Lemmikkisi tekevät yhteensä 5,000 vahinkoa" +title = "Sodanjohtaja" +description = "Lemmikkisi tekevät yhteensä 5,000 vahinkoa" [advancement.challenge_tamed_10] - title = "Eläinten Ystävä" - description = "Kesytä 10 eläintä" +title = "Eläinten Ystävä" +description = "Kesytä 10 eläintä" [advancement.challenge_tamed_100] - title = "Eläintarhanhoitaja" - description = "Kesytä 100 eläintä" +title = "Eläintarhanhoitaja" +description = "Kesytä 100 eläintä" [advancement.challenge_pet_kills_25] - title = "Laumataktiikka" - description = "Lemmikkisi tappavat 25 olentoa" +title = "Laumataktiikka" +description = "Lemmikkisi tappavat 25 olentoa" [advancement.challenge_pet_kills_250] - title = "Alfakomentaja" - description = "Lemmikkisi tappavat 250 olentoa" +title = "Alfakomentaja" +description = "Lemmikkisi tappavat 250 olentoa" [advancement.challenge_taming_2500] - title = "Jalostusasiantuntija" - description = "Jalosta 2,500 eläintä" +title = "Jalostusasiantuntija" +description = "Jalosta 2,500 eläintä" [advancement.challenge_taming_25k] - title = "Genetiikan Mestari" - description = "Jalosta 25,000 eläintä" +title = "Genetiikan Mestari" +description = "Jalosta 25,000 eläintä" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Nyrkkeilysäkki" - description = "Vastaanota 500 osumaa" +title = "Nyrkkeilysäkki" +description = "Vastaanota 500 osumaa" [advancement.challenge_trag_hits_5k] - title = "Rangaistuksen Rakastaja" - description = "Vastaanota 5,000 osumaa" +title = "Rangaistuksen Rakastaja" +description = "Vastaanota 5,000 osumaa" [advancement.challenge_trag_deaths_10] - title = "Yhdeksän Henkeä" - description = "Kuole 10 kertaa" +title = "Yhdeksän Henkeä" +description = "Kuole 10 kertaa" [advancement.challenge_trag_deaths_100] - title = "Toistuva Painajainen" - description = "Kuole 100 kertaa" +title = "Toistuva Painajainen" +description = "Kuole 100 kertaa" [advancement.challenge_trag_fire_500] - title = "Palon Uhri" - description = "Kestä 500 tulivahinkoa" +title = "Palon Uhri" +description = "Kestä 500 tulivahinkoa" [advancement.challenge_trag_fire_5k] - title = "Feeniks" - description = "Kestä 5,000 tulivahinkoa" +title = "Feeniks" +description = "Kestä 5,000 tulivahinkoa" [advancement.challenge_trag_fall_500] - title = "Painovoiman Testi" - description = "Kestä 500 putoamisvahinkoa" +title = "Painovoiman Testi" +description = "Kestä 500 putoamisvahinkoa" [advancement.challenge_trag_fall_5k] - title = "Loppunopeus" - description = "Kestä 5,000 putoamisvahinkoa" +title = "Loppunopeus" +description = "Kestä 5,000 putoamisvahinkoa" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Tappelija" - description = "Tee 1,000 vahinkoa paljain nyrkein" +title = "Tappelija" +description = "Tee 1,000 vahinkoa paljain nyrkein" [advancement.challenge_unarmed_dmg_10k] - title = "Taistelulajien Mestari" - description = "Tee 10,000 vahinkoa paljain nyrkein" +title = "Taistelulajien Mestari" +description = "Tee 10,000 vahinkoa paljain nyrkein" [advancement.challenge_unarmed_kills_25] - title = "Paljaat Nyrkiy" - description = "Tapa 25 olentoa paljain nyrkein" +title = "Paljaat Nyrkiy" +description = "Tapa 25 olentoa paljain nyrkein" [advancement.challenge_unarmed_kills_250] - title = "Legendan Nyrkki" - description = "Tapa 250 olentoa paljain nyrkein" +title = "Legendan Nyrkki" +description = "Tapa 250 olentoa paljain nyrkein" [advancement.challenge_unarmed_crit_25] - title = "Kriittinen Lyönti" - description = "Osuma 25 kriittistä osumaa paljain nyrkein" +title = "Kriittinen Lyönti" +description = "Osuma 25 kriittistä osumaa paljain nyrkein" [advancement.challenge_unarmed_crit_250] - title = "Tarkka Nyrkki" - description = "Osuma 250 kriittistä osumaa paljain nyrkein" +title = "Tarkka Nyrkki" +description = "Osuma 250 kriittistä osumaa paljain nyrkein" [advancement.challenge_unarmed_heavy_25] - title = "Voimaisku" - description = "Osuma 25 raskasta osumaa paljain nyrkein (yli 6 vahinkoa)" +title = "Voimaisku" +description = "Osuma 25 raskasta osumaa paljain nyrkein (yli 6 vahinkoa)" [advancement.challenge_unarmed_heavy_250] - title = "Tyrmäyskuningas" - description = "Osuma 250 raskasta osumaa paljain nyrkein (yli 6 vahinkoa)" +title = "Tyrmäyskuningas" +description = "Osuma 250 raskasta osumaa paljain nyrkein (yli 6 vahinkoa)" # items [items] [items.bound_ender_peral] - name = "Reliikki-avainportti" - usage1 = "Shift + Vasen klikkaus sitomiseen" - usage2 = "Oikea klikkaus päästäksesi sidottuun varastoon" +name = "Reliikki-avainportti" +usage1 = "Shift + Vasen klikkaus sitomiseen" +usage2 = "Oikea klikkaus päästäksesi sidottuun varastoon" [items.bound_eye_of_ender] - name = "Silmäankkuri" - usage1 = "Oikea klikkaus kuluttaaksesi ja teleportataksesi sidottuun sijaintiin" - usage2 = "Shift + Vasen klikkaus sitoaksesi lohkoon" +name = "Silmäankkuri" +usage1 = "Oikea klikkaus kuluttaaksesi ja teleportataksesi sidottuun sijaintiin" +usage2 = "Shift + Vasen klikkaus sitoaksesi lohkoon" [items.bound_redstone_torch] - name = "Redstonekaukosäädin" - usage1 = "Oikea klikkaus lähettääksesi 1 tikin redstone-pulssin" - usage2 = "Shift + Vasen klikkaus 'Kohde'-lohkoon sitoaksesi" +name = "Redstonekaukosäädin" +usage1 = "Oikea klikkaus lähettääksesi 1 tikin redstone-pulssin" +usage2 = "Shift + Vasen klikkaus 'Kohde'-lohkoon sitoaksesi" [items.bound_snowball] - name = "Seittiansa!" - usage1 = "Heitä luodaksesi väliaikainen seittiansa kohteeseen" +name = "Seittiansa!" +usage1 = "Heitä luodaksesi väliaikainen seittiansa kohteeseen" [items.chrono_time_bottle] - name = "Aikaa pullossa" - usage1 = "Kerää passiivisesti aikaa ollessaan varastossasi" - usage2 = "Oikea-klikkaa ajastettuja lohkoja tai eläinten poikasia käyttääksesi varastoitua aikaa" - stored = "Varastoitu aika" +name = "Aikaa pullossa" +usage1 = "Kerää passiivisesti aikaa ollessaan varastossasi" +usage2 = "Oikea-klikkaa ajastettuja lohkoja tai eläinten poikasia käyttääksesi varastoitua aikaa" +stored = "Varastoitu aika" [items.chrono_time_bomb] - name = "Aikapommi" - usage1 = "Oikea-klikkaa laukaistaksesi aikasalama, joka luo ajallisen kentän" +name = "Aikapommi" +usage1 = "Oikea-klikkaa laukaistaksesi aikasalama, joka luo ajallisen kentän" [items.elevator_block] - name = "Hissilohko" - usage1 = "Hyppää teleportataksesi ylös" - usage2 = "Kyykisty teleportataksesi alas" - usage3 = "Vähintään 2 ilmalohkoa hissien välillä" +name = "Hissilohko" +usage1 = "Hyppää teleportataksesi ylös" +usage2 = "Kyykisty teleportataksesi alas" +usage3 = "Vähintään 2 ilmalohkoa hissien välillä" # snippets [snippets] [snippets.gui] - level = "Taso" - knowledge = "tietämys" - power_used = "Voimaa käytetty" - not_learned = "Ei opittu" - xp = "XP:tä" - welcome = "Tervetuloa!" - welcome_back = "Tervetuloa takaisin!" - xp_bonus_for_time = "XP:tä" - max_ability_power = "Kykytehon enimmäismäärä" - unlock_this_by_clicking = "Avaa tämä oikea-klikkaamalla: " - back = "Takaisin" - unlearn_all = "Unohda kaikki" - unlearned_all = "Kaikki unohdettu" +level = "Taso" +knowledge = "tietämys" +power_used = "Voimaa käytetty" +not_learned = "Ei opittu" +xp = "XP:tä" +welcome = "Tervetuloa!" +welcome_back = "Tervetuloa takaisin!" +xp_bonus_for_time = "XP:tä" +max_ability_power = "Kykytehon enimmäismäärä" +unlock_this_by_clicking = "Avaa tämä oikea-klikkaamalla: " +back = "Takaisin" +unlearn_all = "Unohda kaikki" +unlearned_all = "Kaikki unohdettu" [snippets.adapt_menu] - may_not_unlearn = "ET VOI UNOHTAA" - may_unlearn = "VOIT OPPIA/UNOHTAA" - knowledge_cost = "Tietämyksen hinta" - knowledge_available = "Tietämystä saatavilla" - already_learned = "Jo opittu" - unlearn_refund = "Klikkaa unohtaaksesi ja saadaksesi hyvityksen" - no_refunds = "HARDCORE, HYVITYKSET POIS KÄYTÖSTÄ" - knowledge = "tietämys" - click_learn = "Klikkaa oppiaksesi" - no_knowledge = "(Sinulla ei ole tietämystä)" - you_only_have = "Sinulla on vain" - how_to_level_up = "Nosta taitotasoja kasvattaaksesi maksimitehoa." - not_enough_power = "Tehoa ei ole tarpeeksi! Jokainen kykytaso maksaa 1 tehon." - power = "teho" - power_drain = "Tehon kulutus" - learned = "Opittu " - unlearned = "Unohdettu " - activator_block = "Kirjahylly" +may_not_unlearn = "ET VOI UNOHTAA" +may_unlearn = "VOIT OPPIA/UNOHTAA" +knowledge_cost = "Tietämyksen hinta" +knowledge_available = "Tietämystä saatavilla" +already_learned = "Jo opittu" +unlearn_refund = "Klikkaa unohtaaksesi ja saadaksesi hyvityksen" +no_refunds = "HARDCORE, HYVITYKSET POIS KÄYTÖSTÄ" +knowledge = "tietämys" +click_learn = "Klikkaa oppiaksesi" +no_knowledge = "(Sinulla ei ole tietämystä)" +you_only_have = "Sinulla on vain" +how_to_level_up = "Nosta taitotasoja kasvattaaksesi maksimitehoa." +not_enough_power = "Tehoa ei ole tarpeeksi! Jokainen kykytaso maksaa 1 tehon." +power = "teho" +power_drain = "Tehon kulutus" +learned = "Opittu " +unlearned = "Unohdettu " +activator_block = "Kirjahylly" [snippets.knowledge_orb] - contains = "sisältää" - knowledge = "tietämys" - rightclick = "Oikea-klikkaa" - togainknowledge = "saadaksesi tämän tietämyksen" - knowledge_orb = "Tietämyspallo" +contains = "sisältää" +knowledge = "tietämys" +rightclick = "Oikea-klikkaa" +togainknowledge = "saadaksesi tämän tietämyksen" +knowledge_orb = "Tietämyspallo" [snippets.experience_orb] - contains = "sisältää" - xp = "Kokemus" - rightclick = "Oikea-klikkaa" - togainxp = "saadaksesi tämän kokemuksen" - xporb = "Kokemuspallo" +contains = "sisältää" +xp = "Kokemus" +rightclick = "Oikea-klikkaa" +togainxp = "saadaksesi tämän kokemuksen" +xporb = "Kokemuspallo" # skill [skill] [skill.agility] - name = "Ketteryys" - icon = "⇉" - description = "Ketteryys on kykyä liikkua nopeasti ja sujuvasti esteiden edessä." +name = "Ketteryys" +icon = "⇉" +description = "Ketteryys on kykyä liikkua nopeasti ja sujuvasti esteiden edessä." [skill.architect] - name = "Arkkitehti" - icon = "⬧" - description = "Rakenteet ovat maailman rakennuspalikoita. Todellisuus on käsissäsi, sinun hallittavanasi." +name = "Arkkitehti" +icon = "⬧" +description = "Rakenteet ovat maailman rakennuspalikoita. Todellisuus on käsissäsi, sinun hallittavanasi." [skill.axes] - name = "Kirveet" - icon = "🪓" - description1 = "Miksi kaataa puita, kun voit kaataa " - description2 = "asioita" - description3 = "sen sijaan, sama lopputulos!" +name = "Kirveet" +icon = "🪓" +description1 = "Miksi kaataa puita, kun voit kaataa " +description2 = "asioita" +description3 = "sen sijaan, sama lopputulos!" [skill.brewing] - name = "Panimotaito" - icon = "❦" - description = "Tuplakupla, triplakupla, nelinkertainen kupla - en silti saa tätä taikajuomaa kattilaan" +name = "Panimotaito" +icon = "❦" +description = "Tuplakupla, triplakupla, nelinkertainen kupla - en silti saa tätä taikajuomaa kattilaan" [skill.blocking] - name = "Torjunta" - icon = "🛡" - description = "Kepit ja kivet eivät murskaa luitasi, mutta kilpi kyllä." +name = "Torjunta" +icon = "🛡" +description = "Kepit ja kivet eivät murskaa luitasi, mutta kilpi kyllä." [skill.crafting] - name = "Valmistus" - icon = "⌂" - description = "Kun palasia ei ole enää jäljellä, miksi et tekisi lisää?" +name = "Valmistus" +icon = "⌂" +description = "Kun palasia ei ole enää jäljellä, miksi et tekisi lisää?" [skill.discovery] - name = "Löytöretki" - icon = "⚛" - description = "Kun havaintokykysi laajenee, mielesi avautuu löytämään sen, mitä et ennen huomannut." +name = "Löytöretki" +icon = "⚛" +description = "Kun havaintokykysi laajenee, mielesi avautuu löytämään sen, mitä et ennen huomannut." [skill.enchanting] - name = "Lumoaminen" - icon = "♰" - description = "Mistä sinä oikein puhut? Ennustukset, näyt, taikauskoinen höpötys?" +name = "Lumoaminen" +icon = "♰" +description = "Mistä sinä oikein puhut? Ennustukset, näyt, taikauskoinen höpötys?" [skill.excavation] - name = "Kaivaminen" - icon = "ᛳ" - description = "Kaiva kaiva koloa..." +name = "Kaivaminen" +icon = "ᛳ" +description = "Kaiva kaiva koloa..." [skill.herbalism] - name = "Yrttitaito" - icon = "⚘" - description = "En löydä kasveja, mutta löydän siemeniä ja - onko tuo... rikkaruohoa?" +name = "Yrttitaito" +icon = "⚘" +description = "En löydä kasveja, mutta löydän siemeniä ja - onko tuo... rikkaruohoa?" [skill.hunter] - name = "Metsästäjä" - icon = "☠" - description = "Metsästyksessä on kyse matkasta, ei lopputuloksesta." +name = "Metsästäjä" +icon = "☠" +description = "Metsästyksessä on kyse matkasta, ei lopputuloksesta." [skill.nether] - name = "Nether" - icon = "₪" - description = "Netherin syvyyksistä itsestään." +name = "Nether" +icon = "₪" +description = "Netherin syvyyksistä itsestään." [skill.pickaxe] - name = "Hakku" - icon = "⛏" - description = "Kääpiöt ovat kaivosmiehiä, mutta olen oppinut pari juttua aikanani. OLEN RUOTSALAINEN" +name = "Hakku" +icon = "⛏" +description = "Kääpiöt ovat kaivosmiehiä, mutta olen oppinut pari juttua aikanani. OLEN RUOTSALAINEN" [skill.ranged] - name = "Kantama" - icon = "🏹" - description = "Etäisyys on avain voittoon ja avain selviytymiseen." +name = "Kantama" +icon = "🏹" +description = "Etäisyys on avain voittoon ja avain selviytymiseen." [skill.rift] - name = "Repeämä" - icon = "❍" - description = "Repeämä on syövyttävä valjastus, mutta sinä olet valjastanut valjastuksen." +name = "Repeämä" +icon = "❍" +description = "Repeämä on syövyttävä valjastus, mutta sinä olet valjastanut valjastuksen." [skill.seaborne] - name = "Merenkulkija" - icon = "🎣" - description = "Tällä taidolla voit hallita veden ihmeitä." +name = "Merenkulkija" +icon = "🎣" +description = "Tällä taidolla voit hallita veden ihmeitä." [skill.stealth] - name = "Hiipimistaito" - icon = "☯" - description = "Näkymättömyyden taide. Kulje varjoissa." +name = "Hiipimistaito" +icon = "☯" +description = "Näkymättömyyden taide. Kulje varjoissa." [skill.swords] - name = "Miekat" - icon = "⚔" - description = "Harmaakiven voimalla!" +name = "Miekat" +icon = "⚔" +description = "Harmaakiven voimalla!" [skill.taming] - name = "Kesyttäminen" - icon = "♥" - description = "Papukaijat ja mehiläiset... entä sinä?" +name = "Kesyttäminen" +icon = "♥" +description = "Papukaijat ja mehiläiset... entä sinä?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Veri virtaa maailmankaikkeuden suonissa. Käsiesi puristuksessa." +name = "TragOul" +icon = "🗡" +description = "Veri virtaa maailmankaikkeuden suonissa. Käsiesi puristuksessa." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Vedä maailmankaikkeuden kelloa, koe virtaus. Riko kello, tule kelloksi." +name = "Chronos" +icon = "🕒" +description = "Vedä maailmankaikkeuden kelloa, koe virtaus. Riko kello, tule kelloksi." [skill.unarmed] - name = "Aseeton" - icon = "»" - description = "Ilman asetta ei tarkoita ilman voimaa." +name = "Aseeton" +icon = "»" +description = "Ilman asetta ei tarkoita ilman voimaa." # agility [agility] [agility.armor_up] - name = "Panssarointi" - description = "Saat enemmän panssaria mitä pidempään juokset!" - lore1 = "Maksimipanssari" - lore2 = "Panssaroinnin kertymisaika" - lore = ["Maksimipanssari", "Panssaroinnin kertymisaika"] +name = "Panssarointi" +description = "Saat enemmän panssaria mitä pidempään juokset!" +lore1 = "Maksimipanssari" +lore2 = "Panssaroinnin kertymisaika" +lore = ["Maksimipanssari", "Panssaroinnin kertymisaika"] [agility.ladder_slide] - name = "Tikasliuku" - description = "Kiipeä ja liu'u tikkailla paljon nopeammin molempiin suuntiin." - lore1 = "Tikasnopeuskerroin" - lore2 = "Nopea laskeutumisnopeus" - lore = ["Tikasnopeuskerroin", "Nopea laskeutumisnopeus"] +name = "Tikasliuku" +description = "Kiipeä ja liu'u tikkailla paljon nopeammin molempiin suuntiin." +lore1 = "Tikasnopeuskerroin" +lore2 = "Nopea laskeutumisnopeus" +lore = ["Tikasnopeuskerroin", "Nopea laskeutumisnopeus"] [agility.super_jump] - name = "Superhyppy" - description = "Poikkeuksellinen korkeusetu." - lore1 = "Maksimihyppykorkeus" - lore2 = "Kyykky + Hyppy tehdäksesi Superhypyn!" - lore = ["Maksimihyppykorkeus", "Kyykky + Hyppy tehdäksesi Superhypyn!"] +name = "Superhyppy" +description = "Poikkeuksellinen korkeusetu." +lore1 = "Maksimihyppykorkeus" +lore2 = "Kyykky + Hyppy tehdäksesi Superhypyn!" +lore = ["Maksimihyppykorkeus", "Kyykky + Hyppy tehdäksesi Superhypyn!"] [agility.wall_jump] - name = "Seinähyppy" - description = "Pidä kyykkyä painettuna ilmassa seinää vasten tarttuaksesi ja hypätäksesi!" - lore1 = "Maksimihypyt" - lore2 = "Hyppykorkeus" - lore = ["Maksimihypyt", "Hyppykorkeus"] +name = "Seinähyppy" +description = "Pidä kyykkyä painettuna ilmassa seinää vasten tarttuaksesi ja hypätäksesi!" +lore1 = "Maksimihypyt" +lore2 = "Hyppykorkeus" +lore = ["Maksimihypyt", "Hyppykorkeus"] [agility.wind_up] - name = "Kiihdytys" - description = "Nopeudu mitä pidempään juokset!" - lore1 = "Maksiminopeus" - lore2 = "Kiihdytysaika" - lore = ["Maksiminopeus", "Kiihdytysaika"] +name = "Kiihdytys" +description = "Nopeudu mitä pidempään juokset!" +lore1 = "Maksiminopeus" +lore2 = "Kiihdytysaika" +lore = ["Maksiminopeus", "Kiihdytysaika"] # architect [architect] [architect.elevator] - name = "Hissi" - description = "Tämä mahdollistaa hissin rakentamisen pystysuuntaiseen nopeaan teleporttaukseen!" - lore1 = "Avaa hissiresepti: X=VILLA, Y=ENDERHELMI" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Avaa hissiresepti: X=VILLA, Y=ENDERHELMI", "XXX", "XYX", "XXX"] +name = "Hissi" +description = "Tämä mahdollistaa hissin rakentamisen pystysuuntaiseen nopeaan teleporttaukseen!" +lore1 = "Avaa hissiresepti: X=VILLA, Y=ENDERHELMI" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Avaa hissiresepti: X=VILLA, Y=ENDERHELMI", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Maaginen perusta" - description = "Tämä mahdollistaa kyykistymisen ja väliaikaisen perustan luomisen allesi!" - lore1 = "Luo maagisesti: " - lore2 = "lohkoa allesi!" - lore = ["Luo maagisesti: ", "lohkoa allesi!"] +name = "Maaginen perusta" +description = "Tämä mahdollistaa kyykistymisen ja väliaikaisen perustan luomisen allesi!" +lore1 = "Luo maagisesti: " +lore2 = "lohkoa allesi!" +lore = ["Luo maagisesti: ", "lohkoa allesi!"] [architect.glass] - name = "Silkkikosketus-lasi" - description = "Tämä estää lasilohkojen häviämisen, kun rikot ne tyhjällä kädellä!" - lore1 = "Kätesi saavat silkkikosketuksen lasille" - lore = ["Kätesi saavat silkkikosketuksen lasille"] +name = "Silkkikosketus-lasi" +description = "Tämä estää lasilohkojen häviämisen, kun rikot ne tyhjällä kädellä!" +lore1 = "Kätesi saavat silkkikosketuksen lasille" +lore = ["Kätesi saavat silkkikosketuksen lasille"] [architect.wireless_redstone] - name = "Redstonekaukosäädin" - description = "Tämä mahdollistaa redstone-soihdun käytön redstonen kytkemiseen etänä!" - lore1 = "Kohde + Redstone-soihtu + Enderhelmi = 1 Redstonekaukosäädin" - lore = ["Kohde + Redstone-soihtu + Enderhelmi = 1 Redstonekaukosäädin"] +name = "Redstonekaukosäädin" +description = "Tämä mahdollistaa redstone-soihdun käytön redstonen kytkemiseen etänä!" +lore1 = "Kohde + Redstone-soihtu + Enderhelmi = 1 Redstonekaukosäädin" +lore = ["Kohde + Redstone-soihtu + Enderhelmi = 1 Redstonekaukosäädin"] [architect.placement] - name = "Rakentajan sauva" - description = "Mahdollistaa useiden lohkojen asettamisen kerralla! Aktivoi kyykistymällä ja pidä kädessäsi lohkoa, joka vastaa katsomasi lohkoa, ja aseta! Muista, saatat joutua liikkumaan hieman rajauksen käynnistämiseksi!" - lore1 = "Tarvitset" - lore2 = "lohkoa kädessäsi tämän asettamiseen" - lore3 = "Materiaalinen rakentajan sauva" - lore = ["Tarvitset", "lohkoa kädessäsi tämän asettamiseen", "Materiaalinen rakentajan sauva"] +name = "Rakentajan sauva" +description = "Mahdollistaa useiden lohkojen asettamisen kerralla! Aktivoi kyykistymällä ja pidä kädessäsi lohkoa, joka vastaa katsomasi lohkoa, ja aseta! Muista, saatat joutua liikkumaan hieman rajauksen käynnistämiseksi!" +lore1 = "Tarvitset" +lore2 = "lohkoa kädessäsi tämän asettamiseen" +lore3 = "Materiaalinen rakentajan sauva" +lore = ["Tarvitset", "lohkoa kädessäsi tämän asettamiseen", "Materiaalinen rakentajan sauva"] # axe [axe] [axe.chop] - name = "Kirveenhakkuu" - description = "Kaada puita oikea-klikkaamalla alinta tukkia!" - lore1 = "Lohkoja per hakkuu" - lore2 = "Hakkuun jäähtymisaika" - lore3 = "Työkalun kuluminen" - lore = ["Lohkoja per hakkuu", "Hakkuun jäähtymisaika", "Työkalun kuluminen"] +name = "Kirveenhakkuu" +description = "Kaada puita oikea-klikkaamalla alinta tukkia!" +lore1 = "Lohkoja per hakkuu" +lore2 = "Hakkuun jäähtymisaika" +lore3 = "Työkalun kuluminen" +lore = ["Lohkoja per hakkuu", "Hakkuun jäähtymisaika", "Työkalun kuluminen"] [axe.log_swap] - name = "Lucyn tukkinvaihtaja" - description = "Vaihda tukkien lajia valmistuspöydässä!" - lore1 = "8 tukkia mitä tahansa + 1 taimi = 8 tukkia taimen lajia" - lore = ["8 tukkia mitä tahansa + 1 taimi = 8 tukkia taimen lajia"] +name = "Lucyn tukkinvaihtaja" +description = "Vaihda tukkien lajia valmistuspöydässä!" +lore1 = "8 tukkia mitä tahansa + 1 taimi = 8 tukkia taimen lajia" +lore = ["8 tukkia mitä tahansa + 1 taimi = 8 tukkia taimen lajia"] [axe.drop_to_inventory] - name = "Kirves: pudota varastoon" +name = "Kirves: pudota varastoon" [axe.ground_smash] - name = "Kirveenmurskaus" - description = "Hyppää, kyykisty ja murskaa kaikki läheiset viholliset." - lore1 = "Vahinko" - lore2 = "Lohkon säde" - lore3 = "Voima" - lore4 = "Murskauksen jäähtymisaika" - lore = ["Vahinko", "Lohkon säde", "Voima", "Murskauksen jäähtymisaika"] +name = "Kirveenmurskaus" +description = "Hyppää, kyykisty ja murskaa kaikki läheiset viholliset." +lore1 = "Vahinko" +lore2 = "Lohkon säde" +lore3 = "Voima" +lore4 = "Murskauksen jäähtymisaika" +lore = ["Vahinko", "Lohkon säde", "Voima", "Murskauksen jäähtymisaika"] [axe.leaf_miner] - name = "Lehtilouhija" - description = "Mahdollistaa useiden lehtien rikkomisen kerralla!" - lore1 = "Kyykisty ja louhi LEHTIÄ" - lore2 = "lehtilouhinnan kantama" - lore3 = "Et saa pudotuksia lehdistä (huijauksenesto)" - lore = ["Kyykisty ja louhi LEHTIÄ", "lehtilouhinnan kantama", "Et saa pudotuksia lehdistä (huijauksenesto)"] +name = "Lehtilouhija" +description = "Mahdollistaa useiden lehtien rikkomisen kerralla!" +lore1 = "Kyykisty ja louhi LEHTIÄ" +lore2 = "lehtilouhinnan kantama" +lore3 = "Et saa pudotuksia lehdistä (huijauksenesto)" +lore = ["Kyykisty ja louhi LEHTIÄ", "lehtilouhinnan kantama", "Et saa pudotuksia lehdistä (huijauksenesto)"] [axe.wood_miner] - name = "Puulouhija" - description = "Mahdollistaa useiden puulohkojen rikkomisen kerralla!" - lore1 = "Kyykisty ja louhi PUITA/TUKKEJA (ei lankkuja)" - lore2 = "puulouhinnan kantama" - lore3 = "Toimii pudota varastoon -toiminnon kanssa" - lore = ["Kyykisty ja louhi PUITA/TUKKEJA (ei lankkuja)", "puulouhinnan kantama", "Toimii pudota varastoon -toiminnon kanssa"] +name = "Puulouhija" +description = "Mahdollistaa useiden puulohkojen rikkomisen kerralla!" +lore1 = "Kyykisty ja louhi PUITA/TUKKEJA (ei lankkuja)" +lore2 = "puulouhinnan kantama" +lore3 = "Toimii pudota varastoon -toiminnon kanssa" +lore = ["Kyykisty ja louhi PUITA/TUKKEJA (ei lankkuja)", "puulouhinnan kantama", "Toimii pudota varastoon -toiminnon kanssa"] # brewing [brewing] [brewing.lingering] - name = "Viipyvä panimo" - description = "Pannut taikajuomat kestävät pidempään!" - lore1 = "Kesto" - lore2 = "Kesto" - lore = ["Kesto", "Kesto"] +name = "Viipyvä panimo" +description = "Pannut taikajuomat kestävät pidempään!" +lore1 = "Kesto" +lore2 = "Kesto" +lore = ["Kesto", "Kesto"] [brewing.super_heated] - name = "Ylikuumennettu panimo" - description = "Panimojalat toimivat sitä nopeammin mitä kuumempia ne ovat." - lore1 = "Koskettavaa tulilohkoa kohden" - lore2 = "Koskettavaa laavalohkoa kohden" - lore = ["Koskettavaa tulilohkoa kohden", "Koskettavaa laavalohkoa kohden"] +name = "Ylikuumennettu panimo" +description = "Panimojalat toimivat sitä nopeammin mitä kuumempia ne ovat." +lore1 = "Koskettavaa tulilohkoa kohden" +lore2 = "Koskettavaa laavalohkoa kohden" +lore = ["Koskettavaa tulilohkoa kohden", "Koskettavaa laavalohkoa kohden"] [brewing.darkness] - name = "Pullotettu pimeys" - description = "Miksi tarvitset tätä, mutta ole hyvä!" - lore1 = "Yönäkötaikajuoma + musta betoni = Pimeyden taikajuoma (30 sekuntia)" - lore2 = "Huomaa, että tämä estää käyttäjää juoksemasta!" - lore = ["Yönäkötaikajuoma + musta betoni = Pimeyden taikajuoma (30 sekuntia)", "Huomaa, että tämä estää käyttäjää juoksemasta!"] +name = "Pullotettu pimeys" +description = "Miksi tarvitset tätä, mutta ole hyvä!" +lore1 = "Yönäkötaikajuoma + musta betoni = Pimeyden taikajuoma (30 sekuntia)" +lore2 = "Huomaa, että tämä estää käyttäjää juoksemasta!" +lore = ["Yönäkötaikajuoma + musta betoni = Pimeyden taikajuoma (30 sekuntia)", "Huomaa, että tämä estää käyttäjää juoksemasta!"] [brewing.haste] - name = "Pullotettu kiire" - description = "Kun tehokkuus ei riitä" - lore1 = "Nopeustaikajuoma + ametistinsiru = Kiireen taikajuoma (60 sekuntia)" - lore2 = "Nopeustaikajuoma + ametistilohko = Kiireen taikajuoma-2 (30 sekuntia)" - lore = ["Nopeustaikajuoma + ametistinsiru = Kiireen taikajuoma (60 sekuntia)", "Nopeustaikajuoma + ametistilohko = Kiireen taikajuoma-2 (30 sekuntia)"] +name = "Pullotettu kiire" +description = "Kun tehokkuus ei riitä" +lore1 = "Nopeustaikajuoma + ametistinsiru = Kiireen taikajuoma (60 sekuntia)" +lore2 = "Nopeustaikajuoma + ametistilohko = Kiireen taikajuoma-2 (30 sekuntia)" +lore = ["Nopeustaikajuoma + ametistinsiru = Kiireen taikajuoma (60 sekuntia)", "Nopeustaikajuoma + ametistilohko = Kiireen taikajuoma-2 (30 sekuntia)"] [brewing.absorption] - name = "Pullotettu imeytyminen" - description = "Koveta keho!" - lore1 = "Pikaparannus + kvartsi = Imeytymisen taikajuoma (60 sekuntia)" - lore2 = "Pikaparannus + kvartsilohko = Imeytymisen taikajuoma-2 (30 sekuntia)" - lore = ["Pikaparannus + kvartsi = Imeytymisen taikajuoma (60 sekuntia)", "Pikaparannus + kvartsilohko = Imeytymisen taikajuoma-2 (30 sekuntia)"] +name = "Pullotettu imeytyminen" +description = "Koveta keho!" +lore1 = "Pikaparannus + kvartsi = Imeytymisen taikajuoma (60 sekuntia)" +lore2 = "Pikaparannus + kvartsilohko = Imeytymisen taikajuoma-2 (30 sekuntia)" +lore = ["Pikaparannus + kvartsi = Imeytymisen taikajuoma (60 sekuntia)", "Pikaparannus + kvartsilohko = Imeytymisen taikajuoma-2 (30 sekuntia)"] [brewing.fatigue] - name = "Pullotettu väsymys" - description = "Heikennä kehoa!" - lore1 = "Heikkoustaikajuoma + limapallo = Väsymyksen taikajuoma (30 sekuntia)" - lore2 = "Heikkoustaikajuoma + limalohko = Väsymyksen taikajuoma-2 (15 sekuntia)" - lore = ["Heikkoustaikajuoma + limapallo = Väsymyksen taikajuoma (30 sekuntia)", "Heikkoustaikajuoma + limalohko = Väsymyksen taikajuoma-2 (15 sekuntia)"] +name = "Pullotettu väsymys" +description = "Heikennä kehoa!" +lore1 = "Heikkoustaikajuoma + limapallo = Väsymyksen taikajuoma (30 sekuntia)" +lore2 = "Heikkoustaikajuoma + limalohko = Väsymyksen taikajuoma-2 (15 sekuntia)" +lore = ["Heikkoustaikajuoma + limapallo = Väsymyksen taikajuoma (30 sekuntia)", "Heikkoustaikajuoma + limalohko = Väsymyksen taikajuoma-2 (15 sekuntia)"] [brewing.hunger] - name = "Pullotettu nälkä" - description = "Ruoki kyltymätöntä!" - lore1 = "Hankala taikajuoma + mätäliha = Nälän taikajuoma (30 sekuntia)" - lore2 = "Heikkoustaikajuoma + mätäliha = Nälän taikajuoma-3 (15 sekuntia)" - lore = ["Hankala taikajuoma + mätäliha = Nälän taikajuoma (30 sekuntia)", "Heikkoustaikajuoma + mätäliha = Nälän taikajuoma-3 (15 sekuntia)"] +name = "Pullotettu nälkä" +description = "Ruoki kyltymätöntä!" +lore1 = "Hankala taikajuoma + mätäliha = Nälän taikajuoma (30 sekuntia)" +lore2 = "Heikkoustaikajuoma + mätäliha = Nälän taikajuoma-3 (15 sekuntia)" +lore = ["Hankala taikajuoma + mätäliha = Nälän taikajuoma (30 sekuntia)", "Heikkoustaikajuoma + mätäliha = Nälän taikajuoma-3 (15 sekuntia)"] [brewing.nausea] - name = "Pullotettu pahoinvointi" - description = "Teet minut kipeäksi!" - lore1 = "Hankala taikajuoma + ruskea sieni = Pahoinvoinnin taikajuoma (16 sekuntia)" - lore2 = "Hankala taikajuoma + karmiinisieni = Pahoinvoinnin taikajuoma-2 (8 sekuntia)" - lore = ["Hankala taikajuoma + ruskea sieni = Pahoinvoinnin taikajuoma (16 sekuntia)", "Hankala taikajuoma + karmiinisieni = Pahoinvoinnin taikajuoma-2 (8 sekuntia)"] +name = "Pullotettu pahoinvointi" +description = "Teet minut kipeäksi!" +lore1 = "Hankala taikajuoma + ruskea sieni = Pahoinvoinnin taikajuoma (16 sekuntia)" +lore2 = "Hankala taikajuoma + karmiinisieni = Pahoinvoinnin taikajuoma-2 (8 sekuntia)" +lore = ["Hankala taikajuoma + ruskea sieni = Pahoinvoinnin taikajuoma (16 sekuntia)", "Hankala taikajuoma + karmiinisieni = Pahoinvoinnin taikajuoma-2 (8 sekuntia)"] [brewing.blindness] - name = "Pullotettu sokeus" - description = "Olet kauhea ihminen..." - lore1 = "Hankala taikajuoma + mustekassipussi = Sokeuden taikajuoma (30 sekuntia)" - lore2 = "Hankala taikajuoma + hehkuva mustekassipussi = Sokeuden taikajuoma-2 (15 sekuntia)" - lore = ["Hankala taikajuoma + mustekassipussi = Sokeuden taikajuoma (30 sekuntia)", "Hankala taikajuoma + hehkuva mustekassipussi = Sokeuden taikajuoma-2 (15 sekuntia)"] +name = "Pullotettu sokeus" +description = "Olet kauhea ihminen..." +lore1 = "Hankala taikajuoma + mustekassipussi = Sokeuden taikajuoma (30 sekuntia)" +lore2 = "Hankala taikajuoma + hehkuva mustekassipussi = Sokeuden taikajuoma-2 (15 sekuntia)" +lore = ["Hankala taikajuoma + mustekassipussi = Sokeuden taikajuoma (30 sekuntia)", "Hankala taikajuoma + hehkuva mustekassipussi = Sokeuden taikajuoma-2 (15 sekuntia)"] [brewing.resistance] - name = "Pullotettu vastustuskyky" - description = "Linnoitusta parhaimmillaan!" - lore1 = "Hankala taikajuoma + rautaharkko = Vastustuksen taikajuoma (60 sekuntia)" - lore2 = "Hankala taikajuoma + rautalohko = Vastustuksen taikajuoma-2 (30 sekuntia)" - lore = ["Hankala taikajuoma + rautaharkko = Vastustuksen taikajuoma (60 sekuntia)", "Hankala taikajuoma + rautalohko = Vastustuksen taikajuoma-2 (30 sekuntia)"] +name = "Pullotettu vastustuskyky" +description = "Linnoitusta parhaimmillaan!" +lore1 = "Hankala taikajuoma + rautaharkko = Vastustuksen taikajuoma (60 sekuntia)" +lore2 = "Hankala taikajuoma + rautalohko = Vastustuksen taikajuoma-2 (30 sekuntia)" +lore = ["Hankala taikajuoma + rautaharkko = Vastustuksen taikajuoma (60 sekuntia)", "Hankala taikajuoma + rautalohko = Vastustuksen taikajuoma-2 (30 sekuntia)"] [brewing.health_boost] - name = "Pullotettu elämä" - description = "Kun maksimiterveys ei riitä..." - lore1 = "Pikaparannus-taikajuoma + kultaomena = Terveyslisän taikajuoma (120 sekuntia)" - lore2 = "Pikaparannus-taikajuoma + lumottu kultaomena = Terveyslisän taikajuoma-2 (120 sekuntia)" - lore = ["Pikaparannus-taikajuoma + kultaomena = Terveyslisän taikajuoma (120 sekuntia)", "Pikaparannus-taikajuoma + lumottu kultaomena = Terveyslisän taikajuoma-2 (120 sekuntia)"] +name = "Pullotettu elämä" +description = "Kun maksimiterveys ei riitä..." +lore1 = "Pikaparannus-taikajuoma + kultaomena = Terveyslisän taikajuoma (120 sekuntia)" +lore2 = "Pikaparannus-taikajuoma + lumottu kultaomena = Terveyslisän taikajuoma-2 (120 sekuntia)" +lore = ["Pikaparannus-taikajuoma + kultaomena = Terveyslisän taikajuoma (120 sekuntia)", "Pikaparannus-taikajuoma + lumottu kultaomena = Terveyslisän taikajuoma-2 (120 sekuntia)"] [brewing.decay] - name = "Pullotettu rappeutuminen" - description = "Kuka tiesi, että jäte olisi niin hyödyllistä?" - lore1 = "Heikkoustaikajuoma + myrkkyperuna = Witherin taikajuoma (16 sekuntia)" - lore2 = "Heikkoustaikajuoma + karmiinijuuret = Witherin taikajuoma-2 (8 sekuntia)" - lore = ["Heikkoustaikajuoma + myrkkyperuna = Witherin taikajuoma (16 sekuntia)", "Heikkoustaikajuoma + karmiinijuuret = Witherin taikajuoma-2 (8 sekuntia)"] +name = "Pullotettu rappeutuminen" +description = "Kuka tiesi, että jäte olisi niin hyödyllistä?" +lore1 = "Heikkoustaikajuoma + myrkkyperuna = Witherin taikajuoma (16 sekuntia)" +lore2 = "Heikkoustaikajuoma + karmiinijuuret = Witherin taikajuoma-2 (8 sekuntia)" +lore = ["Heikkoustaikajuoma + myrkkyperuna = Witherin taikajuoma (16 sekuntia)", "Heikkoustaikajuoma + karmiinijuuret = Witherin taikajuoma-2 (8 sekuntia)"] [brewing.saturation] - name = "Pullotettu kylläisyys" - description = "Tiedätkö mitä... Minulla ei ole edes nälkä..." - lore1 = "Uudistumistaikajuoma + uuniperuna = Kylläisyyden taikajuoma" - lore2 = "Uudistumistaikajuoma + heinäpaali = Kylläisyyden taikajuoma-2" - lore = ["Uudistumistaikajuoma + uuniperuna = Kylläisyyden taikajuoma", "Uudistumistaikajuoma + heinäpaali = Kylläisyyden taikajuoma-2"] +name = "Pullotettu kylläisyys" +description = "Tiedätkö mitä... Minulla ei ole edes nälkä..." +lore1 = "Uudistumistaikajuoma + uuniperuna = Kylläisyyden taikajuoma" +lore2 = "Uudistumistaikajuoma + heinäpaali = Kylläisyyden taikajuoma-2" +lore = ["Uudistumistaikajuoma + uuniperuna = Kylläisyyden taikajuoma", "Uudistumistaikajuoma + heinäpaali = Kylläisyyden taikajuoma-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Mefistofeleen ketjut" - description = "Mahdollistaa rengaspanssarin valmistamisen" - lore1 = "Valmistusresepti on sama kuin muillakin, mutta rautahipuilla" - lore = ["Valmistusresepti on sama kuin muillakin, mutta rautahipuilla"] +name = "Mefistofeleen ketjut" +description = "Mahdollistaa rengaspanssarin valmistamisen" +lore1 = "Valmistusresepti on sama kuin muillakin, mutta rautahipuilla" +lore = ["Valmistusresepti on sama kuin muillakin, mutta rautahipuilla"] [blocking.horse_armorer] - name = "Valmistettava hevosenhaarniska" - description = "Mahdollistaa hevosenhaarniskan valmistamisen" - lore1 = "Ympäröi satula haluamallasi materiaalilla haarniskan valmistamiseen" - lore = ["Ympäröi satula haluamallasi materiaalilla haarniskan valmistamiseen"] +name = "Valmistettava hevosenhaarniska" +description = "Mahdollistaa hevosenhaarniskan valmistamisen" +lore1 = "Ympäröi satula haluamallasi materiaalilla haarniskan valmistamiseen" +lore = ["Ympäröi satula haluamallasi materiaalilla haarniskan valmistamiseen"] [blocking.saddle_crafter] - name = "Valmistettava satula" - description = "Valmista satula nahasta" - lore1 = "Resepti: 5 nahkaa:" - lore = ["Resepti: 5 nahkaa:"] +name = "Valmistettava satula" +description = "Valmista satula nahasta" +lore1 = "Resepti: 5 nahkaa:" +lore = ["Resepti: 5 nahkaa:"] [blocking.multi_armor] - name = "Monipanssari" - description = "Sido Elytrat panssariin" - lore1 = "Tämä on uskomaton taito matkustamiseen." - lore2 = "Yhdistä ja vaihda panssaria/Elytraa dynaamisesti lennossa!" - lore3 = "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi." - lore4 = "Irrottaaksesi panssarin, kyykky-pudota esine niin se puretaan." - lore5 = "Jos monipanssarisi tuhoutuu, menetät kaikki siinä olevat esineet." - lore6 = "En tarvitse panssaria, se on pettymys..." - lore = ["Tämä on uskomaton taito matkustamiseen.", "Yhdistä ja vaihda panssaria/Elytraa dynaamisesti lennossa!", "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi.", "Irrottaaksesi panssarin, kyykky-pudota esine niin se puretaan.", "Jos monipanssarisi tuhoutuu, menetät kaikki siinä olevat esineet.", "En tarvitse panssaria, se on pettymys..."] +name = "Monipanssari" +description = "Sido Elytrat panssariin" +lore1 = "Tämä on uskomaton taito matkustamiseen." +lore2 = "Yhdistä ja vaihda panssaria/Elytraa dynaamisesti lennossa!" +lore3 = "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi." +lore4 = "Irrottaaksesi panssarin, kyykky-pudota esine niin se puretaan." +lore5 = "Jos monipanssarisi tuhoutuu, menetät kaikki siinä olevat esineet." +lore6 = "En tarvitse panssaria, se on pettymys..." +lore = ["Tämä on uskomaton taito matkustamiseen.", "Yhdistä ja vaihda panssaria/Elytraa dynaamisesti lennossa!", "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi.", "Irrottaaksesi panssarin, kyykky-pudota esine niin se puretaan.", "Jos monipanssarisi tuhoutuu, menetät kaikki siinä olevat esineet.", "En tarvitse panssaria, se on pettymys..."] # crafting [crafting] [crafting.deconstruction] - name = "Purkaminen" - description = "Pura lohkot ja esineet pelastettaviksi perusosiksi!" - lore1 = "Pudota mikä tahansa esine maahan." - lore2 = "Sitten kyykisty ja oikea-klikkaa saksilla" - lore = ["Pudota mikä tahansa esine maahan.", "Sitten kyykisty ja oikea-klikkaa saksilla"] +name = "Purkaminen" +description = "Pura lohkot ja esineet pelastettaviksi perusosiksi!" +lore1 = "Pudota mikä tahansa esine maahan." +lore2 = "Sitten kyykisty ja oikea-klikkaa saksilla" +lore = ["Pudota mikä tahansa esine maahan.", "Sitten kyykisty ja oikea-klikkaa saksilla"] [crafting.xp] - name = "Valmistus-XP" - description = "Saat passiivista XP:tä valmistaessasi" - lore1 = "Saat XP:tä valmistaessasi" - lore = ["Saat XP:tä valmistaessasi"] +name = "Valmistus-XP" +description = "Saat passiivista XP:tä valmistaessasi" +lore1 = "Saat XP:tä valmistaessasi" +lore = ["Saat XP:tä valmistaessasi"] [crafting.reconstruction] - name = "Malmin jälleenrakennus" - description = "Valmista malmeja uudelleen niiden perusosista!" - lore1 = "8 pudotusta ja 1 isäntä = 1 malmi (muodoton)" - lore2 = "Pudotukset on sulatettava (jos sovellettavissa)" - lore3 = "Ei sisällä: romua, kvartsia, smaragdeja jne..." - lore4 = "Isäntä = kotelo, esim.: kivi, netherrack, syvälupaus" - lore = ["8 pudotusta ja 1 isäntä = 1 malmi (muodoton)", "Pudotukset on sulatettava (jos sovellettavissa)", "Ei sisällä: romua, kvartsia, smaragdeja jne...", "Isäntä = kotelo, esim.: kivi, netherrack, syvälupaus"] +name = "Malmin jälleenrakennus" +description = "Valmista malmeja uudelleen niiden perusosista!" +lore1 = "8 pudotusta ja 1 isäntä = 1 malmi (muodoton)" +lore2 = "Pudotukset on sulatettava (jos sovellettavissa)" +lore3 = "Ei sisällä: romua, kvartsia, smaragdeja jne..." +lore4 = "Isäntä = kotelo, esim.: kivi, netherrack, syvälupaus" +lore = ["8 pudotusta ja 1 isäntä = 1 malmi (muodoton)", "Pudotukset on sulatettava (jos sovellettavissa)", "Ei sisällä: romua, kvartsia, smaragdeja jne...", "Isäntä = kotelo, esim.: kivi, netherrack, syvälupaus"] [crafting.leather] - name = "Valmistettava nahka" - description = "Valmista nahkaa mätälihasta" - lore1 = "Heitä se (mätäliha) nuotiolle!" - lore = ["Heitä se (mätäliha) nuotiolle!"] +name = "Valmistettava nahka" +description = "Valmista nahkaa mätälihasta" +lore1 = "Heitä se (mätäliha) nuotiolle!" +lore = ["Heitä se (mätäliha) nuotiolle!"] [crafting.backpacks] - name = "Boutilierin reput!" - description = "Tämä tuo Mojangin nipun peliin!" - lore1 = "Sinun on oltava selviytymistilassa käyttääksesi tätä" - lore2 = "XLX : Nahka, talutushihna, nahka" - lore3 = "XSX : Nahka, tynnyri, nahka" - lore4 = "XCX : Nahka, arkku, nahka" - lore = ["Sinun on oltava selviytymistilassa käyttääksesi tätä", "XLX : Nahka, talutushihna, nahka", "XSX : Nahka, tynnyri, nahka", "XCX : Nahka, arkku, nahka"] +name = "Boutilierin reput!" +description = "Tämä tuo Mojangin nipun peliin!" +lore1 = "Sinun on oltava selviytymistilassa käyttääksesi tätä" +lore2 = "XLX : Nahka, talutushihna, nahka" +lore3 = "XSX : Nahka, tynnyri, nahka" +lore4 = "XCX : Nahka, arkku, nahka" +lore = ["Sinun on oltava selviytymistilassa käyttääksesi tätä", "XLX : Nahka, talutushihna, nahka", "XSX : Nahka, tynnyri, nahka", "XCX : Nahka, arkku, nahka"] [crafting.stations] - name = "Kannettavat pöydät!" - description = "Käytä pöytää kämmenelläsi!" - lore2 = "KAIKKI ESINEET, JOTKA UNOHDAT PÖYTÄÄN SEN SULKEUTUESSA, KATOAVAT IKUISESTI!" - lore3 = "Kelvolliset pöydät: alasin, valmistuspöytä, hiomakivi, kartanpiirtäjänpöytä, kivenleikkuri, kangaspuut" - lore = ["KAIKKI ESINEET, JOTKA UNOHDAT PÖYTÄÄN SEN SULKEUTUESSA, KATOAVAT IKUISESTI!", "Kelvolliset pöydät: alasin, valmistuspöytä, hiomakivi, kartanpiirtäjänpöytä, kivenleikkuri, kangaspuut"] +name = "Kannettavat pöydät!" +description = "Käytä pöytää kämmenelläsi!" +lore2 = "KAIKKI ESINEET, JOTKA UNOHDAT PÖYTÄÄN SEN SULKEUTUESSA, KATOAVAT IKUISESTI!" +lore3 = "Kelvolliset pöydät: alasin, valmistuspöytä, hiomakivi, kartanpiirtäjänpöytä, kivenleikkuri, kangaspuut" +lore = ["KAIKKI ESINEET, JOTKA UNOHDAT PÖYTÄÄN SEN SULKEUTUESSA, KATOAVAT IKUISESTI!", "Kelvolliset pöydät: alasin, valmistuspöytä, hiomakivi, kartanpiirtäjänpöytä, kivenleikkuri, kangaspuut"] [crafting.skulls] - name = "Valmistettavat kallot!" - description = "Materiaalien avulla voit valmistaa hirviöiden kalloja!" - lore1 = "Ympäröi luulohko seuraavilla saadaksesi kallon:" - lore2 = "Zombi: mätäliha" - lore3 = "Luuranko: luu" - lore4 = "Creeper: ruuti" - lore5 = "Wither: nether-tiili" - lore6 = "Lohikäärme: lohikäärmeen henkäys" - lore = ["Ympäröi luulohko seuraavilla saadaksesi kallon:", "Zombi: mätäliha", "Luuranko: luu", "Creeper: ruuti", "Wither: nether-tiili", "Lohikäärme: lohikäärmeen henkäys"] +name = "Valmistettavat kallot!" +description = "Materiaalien avulla voit valmistaa hirviöiden kalloja!" +lore1 = "Ympäröi luulohko seuraavilla saadaksesi kallon:" +lore2 = "Zombi: mätäliha" +lore3 = "Luuranko: luu" +lore4 = "Creeper: ruuti" +lore5 = "Wither: nether-tiili" +lore6 = "Lohikäärme: lohikäärmeen henkäys" +lore = ["Ympäröi luulohko seuraavilla saadaksesi kallon:", "Zombi: mätäliha", "Luuranko: luu", "Creeper: ruuti", "Wither: nether-tiili", "Lohikäärme: lohikäärmeen henkäys"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Aikaa pullossa" - description = "Kanna ajallista pulloa, joka varastoi aikaa ja kuluta sitä nopeuttaaksesi ajastettuja lohkoja, kasvavia ja ikääntyviä olentoja kuten eläinten poikasia. Resepti (muodoton): nopeustaikajuoma + kello + lasipullo." - lore1 = "Varastoidut sekunnit ladattu per tikki" - lore2 = "Ajan kiihdytys per varastoitu sekunti" - lore3 = "Resepti (muodoton): nopeustaikajuoma + kello + lasipullo" - lore = ["Varastoidut sekunnit ladattu per tikki", "Ajan kiihdytys per varastoitu sekunti", "Resepti (muodoton): nopeustaikajuoma + kello + lasipullo"] +name = "Aikaa pullossa" +description = "Kanna ajallista pulloa, joka varastoi aikaa ja kuluta sitä nopeuttaaksesi ajastettuja lohkoja, kasvavia ja ikääntyviä olentoja kuten eläinten poikasia. Resepti (muodoton): nopeustaikajuoma + kello + lasipullo." +lore1 = "Varastoidut sekunnit ladattu per tikki" +lore2 = "Ajan kiihdytys per varastoitu sekunti" +lore3 = "Resepti (muodoton): nopeustaikajuoma + kello + lasipullo" +lore = ["Varastoidut sekunnit ladattu per tikki", "Ajan kiihdytys per varastoitu sekunti", "Resepti (muodoton): nopeustaikajuoma + kello + lasipullo"] [chronos.aberrant_touch] - name = "Poikkeava kosketus" - description = "Lähitaisteluiskut lisäävät pinoutuvaa hitautta nälän kustannuksella, tiukoilla PvP-rajoituksilla, ja juurruttavat kohteet 5 pinolla." - lore1 = "Lähitaisteluiskut lisäävät pinoutuvaa hitautta" - lore2 = "PvE-hitauden keston enimmäisraja" - lore3 = "PvP-hitauden vahvuuden enimmäisraja" - lore = ["Lähitaisteluiskut lisäävät pinoutuvaa hitautta", "PvE-hitauden keston enimmäisraja", "PvP-hitauden vahvuuden enimmäisraja"] +name = "Poikkeava kosketus" +description = "Lähitaisteluiskut lisäävät pinoutuvaa hitautta nälän kustannuksella, tiukoilla PvP-rajoituksilla, ja juurruttavat kohteet 5 pinolla." +lore1 = "Lähitaisteluiskut lisäävät pinoutuvaa hitautta" +lore2 = "PvE-hitauden keston enimmäisraja" +lore3 = "PvP-hitauden vahvuuden enimmäisraja" +lore = ["Lähitaisteluiskut lisäävät pinoutuvaa hitautta", "PvE-hitauden keston enimmäisraja", "PvP-hitauden vahvuuden enimmäisraja"] [chronos.instant_recall] - name = "Pikakelaus" - description = "Vasen tai oikea klikkaus kellolla kädessä kelataksesi takaisin äskettäiseen tilanteeseen terveys ja nälkä palautettuna." - lore1 = "Kelauksen kesto" - lore2 = "Jäähtymisaika" - lore3 = "Ei varaston palautusta" - lore = ["Kelauksen kesto", "Jäähtymisaika", "Ei varaston palautusta"] +name = "Pikakelaus" +description = "Vasen tai oikea klikkaus kellolla kädessä kelataksesi takaisin äskettäiseen tilanteeseen terveys ja nälkä palautettuna." +lore1 = "Kelauksen kesto" +lore2 = "Jäähtymisaika" +lore3 = "Ei varaston palautusta" +lore = ["Kelauksen kesto", "Jäähtymisaika", "Ei varaston palautusta"] [chronos.time_bomb] - name = "Aikapommi" - description = "Heitä valmistettu aikapommi, joka luo ajallisen kentän, hidastaa olentoja ja jäädyttää ammuksia." - lore1 = "Ajallisen kentän säde" - lore2 = "Ajallisen kentän kesto" - lore3 = "Pommin jäähtymisaika" - lore4 = "Resepti (muodoton): kello + lumipallo + timantti + hiekka" - lore = ["Ajallisen kentän säde", "Ajallisen kentän kesto", "Pommin jäähtymisaika", "Resepti (muodoton): kello + lumipallo + timantti + hiekka"] +name = "Aikapommi" +description = "Heitä valmistettu aikapommi, joka luo ajallisen kentän, hidastaa olentoja ja jäädyttää ammuksia." +lore1 = "Ajallisen kentän säde" +lore2 = "Ajallisen kentän kesto" +lore3 = "Pommin jäähtymisaika" +lore4 = "Resepti (muodoton): kello + lumipallo + timantti + hiekka" +lore = ["Ajallisen kentän säde", "Ajallisen kentän kesto", "Pommin jäähtymisaika", "Resepti (muodoton): kello + lumipallo + timantti + hiekka"] # discovery [discovery] [discovery.armor] - name = "Maailmanpanssari" - description = "Passiivinen panssari riippuen läheisten lohkojen kovuudesta." - lore1 = "Passiivinen panssari" - lore2 = "Perustuu läheisten lohkojen kovuuteen" - lore3 = "Panssarin vahvuus:" - lore = ["Passiivinen panssari", "Perustuu läheisten lohkojen kovuuteen", "Panssarin vahvuus:"] +name = "Maailmanpanssari" +description = "Passiivinen panssari riippuen läheisten lohkojen kovuudesta." +lore1 = "Passiivinen panssari" +lore2 = "Perustuu läheisten lohkojen kovuuteen" +lore3 = "Panssarin vahvuus:" +lore = ["Passiivinen panssari", "Perustuu läheisten lohkojen kovuuteen", "Panssarin vahvuus:"] [discovery.unity] - name = "Kokeellinen yhtenäisyys" - description = "Kokemuspallojen kerääminen lisää XP:tä satunnaisiin taitoihin." - lore1 = "XP " - lore2 = "per pallo" - lore = ["XP ", "per pallo"] +name = "Kokeellinen yhtenäisyys" +description = "Kokemuspallojen kerääminen lisää XP:tä satunnaisiin taitoihin." +lore1 = "XP " +lore2 = "per pallo" +lore = ["XP ", "per pallo"] [discovery.resist] - name = "Kokeellinen vastustuskyky" - description = "Kuluta kokemusta lievittääksesi vahinkoa vain kun isku pudottaisi sinut alle 5 sydämen tai tappaisi sinut." - lore0 = "Aktivoituu vain kriittisellä terveydellä (<= 5 sydäntä) kerran 15 sekunnissa" - lore1 = " Vähennetty vahinko" - lore2 = "kokemusta kulutettu" - lore = ["Aktivoituu vain kriittisellä terveydellä (<= 5 sydäntä) kerran 15 sekunnissa", " Vähennetty vahinko", "kokemusta kulutettu"] +name = "Kokeellinen vastustuskyky" +description = "Kuluta kokemusta lievittääksesi vahinkoa vain kun isku pudottaisi sinut alle 5 sydämen tai tappaisi sinut." +lore0 = "Aktivoituu vain kriittisellä terveydellä (<= 5 sydäntä) kerran 15 sekunnissa" +lore1 = " Vähennetty vahinko" +lore2 = "kokemusta kulutettu" +lore = ["Aktivoituu vain kriittisellä terveydellä (<= 5 sydäntä) kerran 15 sekunnissa", " Vähennetty vahinko", "kokemusta kulutettu"] [discovery.villager] - name = "Kyläläisten vetovoima" - description = "Mahdollistaa paremmat kaupat kyläläisten kanssa!" - lore1 = "Tämä kuluttaa XP:tä per vuorovaikutus kyläläisten kanssa" - lore2 = "Mahdollisuus per vuorovaikutus kuluttaa XP:tä ja parantaa kauppoja" - lore3 = "vaadittu XP:n kulutus per vuorovaikutus" - lore = ["Tämä kuluttaa XP:tä per vuorovaikutus kyläläisten kanssa", "Mahdollisuus per vuorovaikutus kuluttaa XP:tä ja parantaa kauppoja", "vaadittu XP:n kulutus per vuorovaikutus"] +name = "Kyläläisten vetovoima" +description = "Mahdollistaa paremmat kaupat kyläläisten kanssa!" +lore1 = "Tämä kuluttaa XP:tä per vuorovaikutus kyläläisten kanssa" +lore2 = "Mahdollisuus per vuorovaikutus kuluttaa XP:tä ja parantaa kauppoja" +lore3 = "vaadittu XP:n kulutus per vuorovaikutus" +lore = ["Tämä kuluttaa XP:tä per vuorovaikutus kyläläisten kanssa", "Mahdollisuus per vuorovaikutus kuluttaa XP:tä ja parantaa kauppoja", "vaadittu XP:n kulutus per vuorovaikutus"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapislazulin palautus" - description = "Yhden ylimääräisen XP-tason hinnalla, sinulla on mahdollisuus saada ilmaista lapislazulia takaisin" - lore1 = "Jokaista tasoa kohden lumoamisen hinta nousee yhdellä, mutta voi palauttaa jopa 3 lapislazulia" - lore = ["Jokaista tasoa kohden lumoamisen hinta nousee yhdellä, mutta voi palauttaa jopa 3 lapislazulia"] +name = "Lapislazulin palautus" +description = "Yhden ylimääräisen XP-tason hinnalla, sinulla on mahdollisuus saada ilmaista lapislazulia takaisin" +lore1 = "Jokaista tasoa kohden lumoamisen hinta nousee yhdellä, mutta voi palauttaa jopa 3 lapislazulia" +lore = ["Jokaista tasoa kohden lumoamisen hinta nousee yhdellä, mutta voi palauttaa jopa 3 lapislazulia"] [enchanting.quick_enchant] - name = "Pikaklikkaus-lumoaminen" - description = "Lumoa esineitä klikkaamalla lumouskirjoja suoraan niiden päälle." - lore1 = "Yhdistettyjen tasojen enimmäismäärä" - lore2 = "Esinettä ei voi luomita enempää kuin " - lore3 = "tehoa" - lore = ["Yhdistettyjen tasojen enimmäismäärä", "Esinettä ei voi luomita enempää kuin ", "tehoa"] +name = "Pikaklikkaus-lumoaminen" +description = "Lumoa esineitä klikkaamalla lumouskirjoja suoraan niiden päälle." +lore1 = "Yhdistettyjen tasojen enimmäismäärä" +lore2 = "Esinettä ei voi luomita enempää kuin " +lore3 = "tehoa" +lore = ["Yhdistettyjen tasojen enimmäismäärä", "Esinettä ei voi luomita enempää kuin ", "tehoa"] [enchanting.return] - name = "XP:n palautus" - description = "Luomis-XP palautetaan sinulle kun luomat esineen." - lore1 = "Käytetyllä kokemuksella on mahdollisuus palautua kun luomat esineen" - lore2 = "Kokemusta per luomus" - lore = ["Käytetyllä kokemuksella on mahdollisuus palautua kun luomat esineen", "Kokemusta per luomus"] +name = "XP:n palautus" +description = "Luomis-XP palautetaan sinulle kun luomat esineen." +lore1 = "Käytetyllä kokemuksella on mahdollisuus palautua kun luomat esineen" +lore2 = "Kokemusta per luomus" +lore = ["Käytetyllä kokemuksella on mahdollisuus palautua kun luomat esineen", "Kokemusta per luomus"] # excavation [excavation] [excavation.haste] - name = "Kiireinen kaivaja" - description = "Tämä nopeuttaa kaivamista KIIREellä!" - lore1 = "Saat kiire-efektin kaivaessasi" - lore2 = "x tasoa kiire-efektiä kun alat louhia MITÄ TAHANSA lohkoa." - lore = ["Saat kiire-efektin kaivaessasi", "x tasoa kiire-efektiä kun alat louhia MITÄ TAHANSA lohkoa."] +name = "Kiireinen kaivaja" +description = "Tämä nopeuttaa kaivamista KIIREellä!" +lore1 = "Saat kiire-efektin kaivaessasi" +lore2 = "x tasoa kiire-efektiä kun alat louhia MITÄ TAHANSA lohkoa." +lore = ["Saat kiire-efektin kaivaessasi", "x tasoa kiire-efektiä kun alat louhia MITÄ TAHANSA lohkoa."] [excavation.spelunker] - name = "Supernäköinen luolastotutkija!" - description = "Näe malmit silmilläsi, maan läpi!" - lore1 = "Malmi sivukädessäsi, hehkumarjat pääkädessäsi ja kyykisty!" - lore2 = "Lohkojen kantama: " - lore3 = "Kuluttaa hehkumarjan käytettäessä" - lore = ["Malmi sivukädessäsi, hehkumarjat pääkädessäsi ja kyykisty!", "Lohkojen kantama: ", "Kuluttaa hehkumarjan käytettäessä"] +name = "Supernäköinen luolastotutkija!" +description = "Näe malmit silmilläsi, maan läpi!" +lore1 = "Malmi sivukädessäsi, hehkumarjat pääkädessäsi ja kyykisty!" +lore2 = "Lohkojen kantama: " +lore3 = "Kuluttaa hehkumarjan käytettäessä" +lore = ["Malmi sivukädessäsi, hehkumarjat pääkädessäsi ja kyykisty!", "Lohkojen kantama: ", "Kuluttaa hehkumarjan käytettäessä"] [excavation.drop_to_inventory] - name = "Lapio: pudota varastoon" +name = "Lapio: pudota varastoon" [excavation.omni_tool] - name = "OMNI-T.Y.Ö.K.A.L.U." - description = "Tacklen ylisuunniteltu ylellinen monitoimityökalu" - lore1 = "Luultavasti tehokkain monista, antaa sinun" - lore2 = "dynaamisesti yhdistää ja vaihtaa työkaluja lennossa tarpeidesi mukaan." - lore3 = "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi." - lore4 = "Irrottaaksesi työkalut, kyykky-pudota esine niin se puretaan." - lore5 = "Et voi rikkoa työkaluja tässä monitoimityökalussa, mutta et voi käyttää rikkinäisiä työkaluja" - lore6 = "yhdistettäviä esineitä yhteensä." - lore7 = "Voisit käyttää viittä tai kuutta työkalua, tai vain yhtä!" - lore = ["Luultavasti tehokkain monista, antaa sinun", "dynaamisesti yhdistää ja vaihtaa työkaluja lennossa tarpeidesi mukaan.", "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi.", "Irrottaaksesi työkalut, kyykky-pudota esine niin se puretaan.", "Et voi rikkoa työkaluja tässä monitoimityökalussa, mutta et voi käyttää rikkinäisiä työkaluja", "yhdistettäviä esineitä yhteensä.", "Voisit käyttää viittä tai kuutta työkalua, tai vain yhtä!"] +name = "OMNI-T.Y.Ö.K.A.L.U." +description = "Tacklen ylisuunniteltu ylellinen monitoimityökalu" +lore1 = "Luultavasti tehokkain monista, antaa sinun" +lore2 = "dynaamisesti yhdistää ja vaihtaa työkaluja lennossa tarpeidesi mukaan." +lore3 = "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi." +lore4 = "Irrottaaksesi työkalut, kyykky-pudota esine niin se puretaan." +lore5 = "Et voi rikkoa työkaluja tässä monitoimityökalussa, mutta et voi käyttää rikkinäisiä työkaluja" +lore6 = "yhdistettäviä esineitä yhteensä." +lore7 = "Voisit käyttää viittä tai kuutta työkalua, tai vain yhtä!" +lore = ["Luultavasti tehokkain monista, antaa sinun", "dynaamisesti yhdistää ja vaihtaa työkaluja lennossa tarpeidesi mukaan.", "Yhdistääksesi, shift-klikkaa esine toisen päälle varastossasi.", "Irrottaaksesi työkalut, kyykky-pudota esine niin se puretaan.", "Et voi rikkoa työkaluja tässä monitoimityökalussa, mutta et voi käyttää rikkinäisiä työkaluja", "yhdistettäviä esineitä yhteensä.", "Voisit käyttää viittä tai kuutta työkalua, tai vain yhtä!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Kasvuaura" - description = "Kasvata luontoa ympärilläsi auralla" - lore1 = "Lohkon säde" - lore2 = "Kasvuauran vahvuus" - lore3 = "Ruokakustannus" - lore = ["Lohkon säde", "Kasvuauran vahvuus", "Ruokakustannus"] +name = "Kasvuaura" +description = "Kasvata luontoa ympärilläsi auralla" +lore1 = "Lohkon säde" +lore2 = "Kasvuauran vahvuus" +lore3 = "Ruokakustannus" +lore = ["Lohkon säde", "Kasvuauran vahvuus", "Ruokakustannus"] [herbalism.hippo] - name = "Yrttiläisen virtahepo" - description = "Ruoan syöminen antaa sinulle enemmän kylläisyyttä" - lore1 = "Ruoka) lisäkylläisyyspisteitä kulutuksessa" - lore = ["Ruoka) lisäkylläisyyspisteitä kulutuksessa"] +name = "Yrttiläisen virtahepo" +description = "Ruoan syöminen antaa sinulle enemmän kylläisyyttä" +lore1 = "Ruoka) lisäkylläisyyspisteitä kulutuksessa" +lore = ["Ruoka) lisäkylläisyyspisteitä kulutuksessa"] [herbalism.myconid] - name = "Yrttiläisen mykonidi" - description = "Antaa sinulle kyvyn valmistaa myseliumia" - lore1 = "Mikä tahansa multa ja ruskea & punainen sieni valmistaa myseliumia." - lore = ["Mikä tahansa multa ja ruskea & punainen sieni valmistaa myseliumia."] +name = "Yrttiläisen mykonidi" +description = "Antaa sinulle kyvyn valmistaa myseliumia" +lore1 = "Mikä tahansa multa ja ruskea & punainen sieni valmistaa myseliumia." +lore = ["Mikä tahansa multa ja ruskea & punainen sieni valmistaa myseliumia."] [herbalism.terralid] - name = "Yrttiläisen terralidi" - description = "Antaa sinulle kyvyn valmistaa ruoholohkoja" - lore1 = "Kolme siementä 3 mullan päällä valmistaa 3 ruoholohkoa." - lore = ["Kolme siementä 3 mullan päällä valmistaa 3 ruoholohkoa."] +name = "Yrttiläisen terralidi" +description = "Antaa sinulle kyvyn valmistaa ruoholohkoja" +lore1 = "Kolme siementä 3 mullan päällä valmistaa 3 ruoholohkoa." +lore = ["Kolme siementä 3 mullan päällä valmistaa 3 ruoholohkoa."] [herbalism.cobweb] - name = "Seitinkutoja" - description = "Antaa sinulle kyvyn valmistaa hämähäkinseittejä valmistuspöydässä" - lore1 = "Yhdeksän lankaa valmistaa hämähäkinseitin." - lore = ["Yhdeksän lankaa valmistaa hämähäkinseitin."] +name = "Seitinkutoja" +description = "Antaa sinulle kyvyn valmistaa hämähäkinseittejä valmistuspöydässä" +lore1 = "Yhdeksän lankaa valmistaa hämähäkinseitin." +lore = ["Yhdeksän lankaa valmistaa hämähäkinseitin."] [herbalism.mushroom_blocks] - name = "Sienentekijä" - description = "Antaa sinulle kyvyn valmistaa sienilohkoja valmistuspöydässä" - lore1 = "Neljä sientä lohkon tekemiseen, tai lohko varren tekemiseen." - lore = ["Neljä sientä lohkon tekemiseen, tai lohko varren tekemiseen."] +name = "Sienentekijä" +description = "Antaa sinulle kyvyn valmistaa sienilohkoja valmistuspöydässä" +lore1 = "Neljä sientä lohkon tekemiseen, tai lohko varren tekemiseen." +lore = ["Neljä sientä lohkon tekemiseen, tai lohko varren tekemiseen."] [herbalism.drop_to_inventory] - name = "Kuokka: pudota varastoon" +name = "Kuokka: pudota varastoon" [herbalism.hungry_shield] - name = "Nälkäkilpi" - description = "Ota vahinkoa nälkääsi ennen terveyttäsi." - lore1 = "Nälän vastustama" - lore = ["Nälän vastustama"] +name = "Nälkäkilpi" +description = "Ota vahinkoa nälkääsi ennen terveyttäsi." +lore1 = "Nälän vastustama" +lore = ["Nälän vastustama"] [herbalism.luck] - name = "Yrttiläisen onni" - description = "Kun rikot ruohoa/kukkia, sinulla on mahdollisuus saada satunnainen esine" - lore0 = "Kukat = ruokaa, ja ruoho = siemeniä" - lore1 = "Mahdollisuus saada esine kukkien rikkomisesta" - lore2 = "Mahdollisuus saada esine ruohon rikkomisesta" - lore = ["Kukat = ruokaa, ja ruoho = siemeniä", "Mahdollisuus saada esine kukkien rikkomisesta", "Mahdollisuus saada esine ruohon rikkomisesta"] +name = "Yrttiläisen onni" +description = "Kun rikot ruohoa/kukkia, sinulla on mahdollisuus saada satunnainen esine" +lore0 = "Kukat = ruokaa, ja ruoho = siemeniä" +lore1 = "Mahdollisuus saada esine kukkien rikkomisesta" +lore2 = "Mahdollisuus saada esine ruohon rikkomisesta" +lore = ["Kukat = ruokaa, ja ruoho = siemeniä", "Mahdollisuus saada esine kukkien rikkomisesta", "Mahdollisuus saada esine ruohon rikkomisesta"] [herbalism.replant] - name = "Korjaa ja istuta uudelleen" - description = "Oikea-klikkaa satoa kuokalla korjataksesi ja istuttaaksesi se uudelleen." - lore1 = "Uudelleenistutuksen lohkosäde" - lore = ["Uudelleenistutuksen lohkosäde"] +name = "Korjaa ja istuta uudelleen" +description = "Oikea-klikkaa satoa kuokalla korjataksesi ja istuttaaksesi se uudelleen." +lore1 = "Uudelleenistutuksen lohkosäde" +lore = ["Uudelleenistutuksen lohkosäde"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenaliini" - description = "Tee enemmän vahinkoa mitä vähemmän terveyttä sinulla on (lähitaistelu)" - lore1 = "Maksimivahinko" - lore = ["Maksimivahinko"] +name = "Adrenaliini" +description = "Tee enemmän vahinkoa mitä vähemmän terveyttä sinulla on (lähitaistelu)" +lore1 = "Maksimivahinko" +lore = ["Maksimivahinko"] [hunter.penalty] - name = "" - description = "" - lore1 = "Saat myrkkykertymiä, jos nälkäsi loppuu" - lore = ["Saat myrkkykertymiä, jos nälkäsi loppuu"] +name = "" +description = "" +lore1 = "Saat myrkkykertymiä, jos nälkäsi loppuu" +lore = ["Saat myrkkykertymiä, jos nälkäsi loppuu"] [hunter.drop_to_inventory] - name = "Esineet: pudota varastoon" - description = "Kun tapat jotain / rikot lohkon miekalla, pudotukset teleporttaavat varastoosi" - lore1 = "Aina kun esine putoaa hirviöstä/rikkomastasi lohkosta, se menee varastoosi jos mahdollista." - lore = ["Aina kun esine putoaa hirviöstä/rikkomastasi lohkosta, se menee varastoosi jos mahdollista."] +name = "Esineet: pudota varastoon" +description = "Kun tapat jotain / rikot lohkon miekalla, pudotukset teleporttaavat varastoosi" +lore1 = "Aina kun esine putoaa hirviöstä/rikkomastasi lohkosta, se menee varastoosi jos mahdollista." +lore = ["Aina kun esine putoaa hirviöstä/rikkomastasi lohkosta, se menee varastoosi jos mahdollista."] [hunter.invisibility] - name = "Katoava askel" - description = "Kun sinua lyödään, saat näkymättömyyttä nälän kustannuksella" - lore1 = "Saat passiivista näkymättömyyttä iskusta" - lore2 = "x näkymättömyyskertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Näkymättömyyden kesto" - lore = ["Saat passiivista näkymättömyyttä iskusta", "x näkymättömyyskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Näkymättömyyden kesto"] +name = "Katoava askel" +description = "Kun sinua lyödään, saat näkymättömyyttä nälän kustannuksella" +lore1 = "Saat passiivista näkymättömyyttä iskusta" +lore2 = "x näkymättömyyskertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Näkymättömyyden kesto" +lore = ["Saat passiivista näkymättömyyttä iskusta", "x näkymättömyyskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Näkymättömyyden kesto"] [hunter.jump_boost] - name = "Metsästäjän korkeudet" - description = "Kun sinua lyödään, saat hyppytehostusta nälän kustannuksella" - lore1 = "Saat passiivista hyppytehostusta iskusta" - lore2 = "x hyppytehostuskertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Hyppytehostuksen kertymäkerroin, ei kesto." - lore = ["Saat passiivista hyppytehostusta iskusta", "x hyppytehostuskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Hyppytehostuksen kertymäkerroin, ei kesto."] +name = "Metsästäjän korkeudet" +description = "Kun sinua lyödään, saat hyppytehostusta nälän kustannuksella" +lore1 = "Saat passiivista hyppytehostusta iskusta" +lore2 = "x hyppytehostuskertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Hyppytehostuksen kertymäkerroin, ei kesto." +lore = ["Saat passiivista hyppytehostusta iskusta", "x hyppytehostuskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Hyppytehostuksen kertymäkerroin, ei kesto."] [hunter.luck] - name = "Metsästäjän tuuri" - description = "Kun sinua lyödään, saat onnea nälän kustannuksella" - lore1 = "Saat passiivista onnea iskusta" - lore2 = "x onnenkertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Onnenkertymien kerroin, ei kesto." - lore = ["Saat passiivista onnea iskusta", "x onnenkertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Onnenkertymien kerroin, ei kesto."] +name = "Metsästäjän tuuri" +description = "Kun sinua lyödään, saat onnea nälän kustannuksella" +lore1 = "Saat passiivista onnea iskusta" +lore2 = "x onnenkertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Onnenkertymien kerroin, ei kesto." +lore = ["Saat passiivista onnea iskusta", "x onnenkertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Onnenkertymien kerroin, ei kesto."] [hunter.regen] - name = "Metsästäjän uudistuminen" - description = "Kun sinua lyödään, saat uudistumista nälän kustannuksella" - lore1 = "Saat passiivista uudistumista iskusta" - lore2 = "x uudistumiskertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Uudistumiskertymien kerroin, ei kesto." - lore = ["Saat passiivista uudistumista iskusta", "x uudistumiskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Uudistumiskertymien kerroin, ei kesto."] +name = "Metsästäjän uudistuminen" +description = "Kun sinua lyödään, saat uudistumista nälän kustannuksella" +lore1 = "Saat passiivista uudistumista iskusta" +lore2 = "x uudistumiskertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Uudistumiskertymien kerroin, ei kesto." +lore = ["Saat passiivista uudistumista iskusta", "x uudistumiskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Uudistumiskertymien kerroin, ei kesto."] [hunter.resistance] - name = "Metsästäjän vastustuskyky" - description = "Kun sinua lyödään, saat vastustuskykyä nälän kustannuksella" - lore1 = "Saat passiivista vastustuskykyä iskusta" - lore2 = "x vastustuskykykertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Vastustuskykykertymien kerroin, ei kesto." - lore = ["Saat passiivista vastustuskykyä iskusta", "x vastustuskykykertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Vastustuskykykertymien kerroin, ei kesto."] +name = "Metsästäjän vastustuskyky" +description = "Kun sinua lyödään, saat vastustuskykyä nälän kustannuksella" +lore1 = "Saat passiivista vastustuskykyä iskusta" +lore2 = "x vastustuskykykertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Vastustuskykykertymien kerroin, ei kesto." +lore = ["Saat passiivista vastustuskykyä iskusta", "x vastustuskykykertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Vastustuskykykertymien kerroin, ei kesto."] [hunter.speed] - name = "Metsästäjän nopeus" - description = "Kun sinua lyödään, saat nopeutta nälän kustannuksella" - lore1 = "Saat passiivista nopeutta iskusta" - lore2 = "x nopeuskertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Nopeuskertymien kerroin, ei kesto." - lore = ["Saat passiivista nopeutta iskusta", "x nopeuskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Nopeuskertymien kerroin, ei kesto."] +name = "Metsästäjän nopeus" +description = "Kun sinua lyödään, saat nopeutta nälän kustannuksella" +lore1 = "Saat passiivista nopeutta iskusta" +lore2 = "x nopeuskertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Nopeuskertymien kerroin, ei kesto." +lore = ["Saat passiivista nopeutta iskusta", "x nopeuskertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Nopeuskertymien kerroin, ei kesto."] [hunter.strength] - name = "Metsästäjän voima" - description = "Kun sinua lyödään, saat voimaa nälän kustannuksella" - lore1 = "Saat passiivista voimaa iskusta" - lore2 = "x voimakertymää 3 sekuntia iskun jälkeen" - lore3 = "x kertyvä nälkä" - lore4 = "Nälkäkertymien kesto ja kerroin." - lore5 = "Voimakertymien kerroin, ei kesto." - lore = ["Saat passiivista voimaa iskusta", "x voimakertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Voimakertymien kerroin, ei kesto."] +name = "Metsästäjän voima" +description = "Kun sinua lyödään, saat voimaa nälän kustannuksella" +lore1 = "Saat passiivista voimaa iskusta" +lore2 = "x voimakertymää 3 sekuntia iskun jälkeen" +lore3 = "x kertyvä nälkä" +lore4 = "Nälkäkertymien kesto ja kerroin." +lore5 = "Voimakertymien kerroin, ei kesto." +lore = ["Saat passiivista voimaa iskusta", "x voimakertymää 3 sekuntia iskun jälkeen", "x kertyvä nälkä", "Nälkäkertymien kesto ja kerroin.", "Voimakertymien kerroin, ei kesto."] # nether [nether] [nether.skull_toss] - name = "Wither-kallon heitto" - description1 = "Vapauta sisäinen Witherisi käyttämällä" - description2 = "jonkun" - description3 = "päätä." - lore1 = "Sekuntia jäähtymisaikaa kallon heittojen välillä." - lore2 = "Wither-kallon käyttö: Heitä " - lore3 = "Wither-kallo" - lore4 = "räjähtää osuessaan." - lore = ["Sekuntia jäähtymisaikaa kallon heittojen välillä.", "Wither-kallon käyttö: Heitä ", "Wither-kallo", "räjähtää osuessaan."] +name = "Wither-kallon heitto" +description1 = "Vapauta sisäinen Witherisi käyttämällä" +description2 = "jonkun" +description3 = "päätä." +lore1 = "Sekuntia jäähtymisaikaa kallon heittojen välillä." +lore2 = "Wither-kallon käyttö: Heitä " +lore3 = "Wither-kallo" +lore4 = "räjähtää osuessaan." +lore = ["Sekuntia jäähtymisaikaa kallon heittojen välillä.", "Wither-kallon käyttö: Heitä ", "Wither-kallo", "räjähtää osuessaan."] [nether.wither_resist] - name = "Wither-vastustus" - description = "Vastustaa witheriä netheriittipanssarin voimalla." - lore1 = "mahdollisuus estää witheri (per osa)." - lore2 = "Passiivinen: netheriittipanssarin käyttö antaa mahdollisuuden estää " - lore3 = "witheri." - lore = ["mahdollisuus estää witheri (per osa).", "Passiivinen: netheriittipanssarin käyttö antaa mahdollisuuden estää ", "witheri."] +name = "Wither-vastustus" +description = "Vastustaa witheriä netheriittipanssarin voimalla." +lore1 = "mahdollisuus estää witheri (per osa)." +lore2 = "Passiivinen: netheriittipanssarin käyttö antaa mahdollisuuden estää " +lore3 = "witheri." +lore = ["mahdollisuus estää witheri (per osa).", "Passiivinen: netheriittipanssarin käyttö antaa mahdollisuuden estää ", "witheri."] [nether.fire_resist] - name = "Tulenkestävyys" - description = "Vastustaa tulta kovettamalla ihosi." - lore1 = "mahdollisuus estää palaminen!" - lore = ["mahdollisuus estää palaminen!"] +name = "Tulenkestävyys" +description = "Vastustaa tulta kovettamalla ihosi." +lore1 = "mahdollisuus estää palaminen!" +lore = ["mahdollisuus estää palaminen!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Automaattisulatus" - description = "Mahdollistaa louhittujen perusmalmien sulattamisen" - lore1 = "Sulatettavat malmit sulatetaan automaattisesti" - lore2 = "% mahdollisuus ylimääräiseen" - lore = ["Sulatettavat malmit sulatetaan automaattisesti", "% mahdollisuus ylimääräiseen"] +name = "Automaattisulatus" +description = "Mahdollistaa louhittujen perusmalmien sulattamisen" +lore1 = "Sulatettavat malmit sulatetaan automaattisesti" +lore2 = "% mahdollisuus ylimääräiseen" +lore = ["Sulatettavat malmit sulatetaan automaattisesti", "% mahdollisuus ylimääräiseen"] [pickaxe.chisel] - name = "Malmitaltta" - description = "Oikea-klikkaa malmeja talttataksesi niistä lisää malmia, kovilla kestävyyskustannuksilla." - lore1 = "Mahdollisuus pudottaa" - lore2 = "Työkalun kuluminen" - lore = ["Mahdollisuus pudottaa", "Työkalun kuluminen"] +name = "Malmitaltta" +description = "Oikea-klikkaa malmeja talttataksesi niistä lisää malmia, kovilla kestävyyskustannuksilla." +lore1 = "Mahdollisuus pudottaa" +lore2 = "Työkalun kuluminen" +lore = ["Mahdollisuus pudottaa", "Työkalun kuluminen"] [pickaxe.drop_to_inventory] - name = "Hakku: pudota varastoon" - description = "Kun rikot lohkon, se teleporttaa esineen varastoosi" - lore1 = "Aina kun esine putoaa rikkomastasi lohkosta, se menee varastoosi jos mahdollista." - lore = ["Aina kun esine putoaa rikkomastasi lohkosta, se menee varastoosi jos mahdollista."] +name = "Hakku: pudota varastoon" +description = "Kun rikot lohkon, se teleporttaa esineen varastoosi" +lore1 = "Aina kun esine putoaa rikkomastasi lohkosta, se menee varastoosi jos mahdollista." +lore = ["Aina kun esine putoaa rikkomastasi lohkosta, se menee varastoosi jos mahdollista."] [pickaxe.silk_spawner] - name = "Hakun silkkispawneri" - description = "Spawnerit putoavat rikkoutuessaan" - lore1 = "Spawnerit voi rikkoa silkkikosketuksella." - lore2 = "Spawnerit voi rikkoa kyykistymällä." - lore = ["Spawnerit voi rikkoa silkkikosketuksella.", "Spawnerit voi rikkoa kyykistymällä."] +name = "Hakun silkkispawneri" +description = "Spawnerit putoavat rikkoutuessaan" +lore1 = "Spawnerit voi rikkoa silkkikosketuksella." +lore2 = "Spawnerit voi rikkoa kyykistymällä." +lore = ["Spawnerit voi rikkoa silkkikosketuksella.", "Spawnerit voi rikkoa kyykistymällä."] [pickaxe.vein_miner] - name = "Suonilouhija" - description = "Mahdollistaa lohkojen rikkomisen malmisuonesta/ryhmästä" - lore1 = "Kyykisty ja louhi MALMEJA" - lore2 = "suonilouhinnan kantama" - lore3 = "Tämä taito EI ryhmitä kaikkia pudotuksia yhteen!" - lore = ["Kyykisty ja louhi MALMEJA", "suonilouhinnan kantama", "Tämä taito EI ryhmitä kaikkia pudotuksia yhteen!"] +name = "Suonilouhija" +description = "Mahdollistaa lohkojen rikkomisen malmisuonesta/ryhmästä" +lore1 = "Kyykisty ja louhi MALMEJA" +lore2 = "suonilouhinnan kantama" +lore3 = "Tämä taito EI ryhmitä kaikkia pudotuksia yhteen!" +lore = ["Kyykisty ja louhi MALMEJA", "suonilouhinnan kantama", "Tämä taito EI ryhmitä kaikkia pudotuksia yhteen!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Nuolen palautus" - description = "Palauta nuolet vihollisen tappamisen jälkeen." - lore1 = "Mahdollisuus palauttaa nuolia osumasta/taposta" - lore2 = "Mahdollisuus: " - lore = ["Mahdollisuus palauttaa nuolia osumasta/taposta", "Mahdollisuus: "] +name = "Nuolen palautus" +description = "Palauta nuolet vihollisen tappamisen jälkeen." +lore1 = "Mahdollisuus palauttaa nuolia osumasta/taposta" +lore2 = "Mahdollisuus: " +lore = ["Mahdollisuus palauttaa nuolia osumasta/taposta", "Mahdollisuus: "] [ranged.web_shot] - name = "Seittiansa" - description = "Ympäröi kohde hämähäkinseiteillä osuessasi!" - lore1 = "8 hämähäkinseittiä lumipallon ympärille ja heitä!" - lore2 = "sekuntia häkkiä, suunnilleen." - lore = ["8 hämähäkinseittiä lumipallon ympärille ja heitä!", "sekuntia häkkiä, suunnilleen."] +name = "Seittiansa" +description = "Ympäröi kohde hämähäkinseiteillä osuessasi!" +lore1 = "8 hämähäkinseittiä lumipallon ympärille ja heitä!" +lore2 = "sekuntia häkkiä, suunnilleen." +lore = ["8 hämähäkinseittiä lumipallon ympärille ja heitä!", "sekuntia häkkiä, suunnilleen."] [ranged.force_shot] - name = "Voimalaukaus" - description = "Ammu ammuksia pidemmälle, nopeammin!" - advancementname = "Pitkä laukaus" - advancementlore = "Osu yli 30 lohkon päästä!" - lore1 = "Ammuksen nopeus" - lore = ["Ammuksen nopeus"] +name = "Voimalaukaus" +description = "Ammu ammuksia pidemmälle, nopeammin!" +advancementname = "Pitkä laukaus" +advancementlore = "Osu yli 30 lohkon päästä!" +lore1 = "Ammuksen nopeus" +lore = ["Ammuksen nopeus"] [ranged.lunge_shot] - name = "Syöksylaukaus" - description = "Pudotessasi nuolesi heittävät sinua satunnaiseen suuntaan" - lore1 = "Satunnainen purskausnopeus" - lore = ["Satunnainen purskausnopeus"] +name = "Syöksylaukaus" +description = "Pudotessasi nuolesi heittävät sinua satunnaiseen suuntaan" +lore1 = "Satunnainen purskausnopeus" +lore = ["Satunnainen purskausnopeus"] [ranged.arrow_piercing] - name = "Nuolen lävistys" - description = "Lisää lävistys ammuksiin! Ammu asioiden läpi!" - lore1 = "Lävistyskohteet" - lore = ["Lävistyskohteet"] +name = "Nuolen lävistys" +description = "Lisää lävistys ammuksiin! Ammu asioiden läpi!" +lore1 = "Lävistyskohteet" +lore = ["Lävistyskohteet"] # rift [rift] [rift.remote_access] - name = "Etäkäyttö" - description = "Vedä tyhjyydestä ja pääse merkittyyn säiliöön." - lore1 = "Enderhelmi + kompassi = Reliikkiavainportti" - lore2 = "Tämä esine mahdollistaa säiliöiden etäkäytön" - lore3 = "Valmistuksen jälkeen katso esinettä nähdäksesi käyttöohjeet" - notcontainer = "Tuo ei ole säiliö" - lore = ["Enderhelmi + kompassi = Reliikkiavainportti", "Tämä esine mahdollistaa säiliöiden etäkäytön", "Valmistuksen jälkeen katso esinettä nähdäksesi käyttöohjeet"] +name = "Etäkäyttö" +description = "Vedä tyhjyydestä ja pääse merkittyyn säiliöön." +lore1 = "Enderhelmi + kompassi = Reliikkiavainportti" +lore2 = "Tämä esine mahdollistaa säiliöiden etäkäytön" +lore3 = "Valmistuksen jälkeen katso esinettä nähdäksesi käyttöohjeet" +notcontainer = "Tuo ei ole säiliö" +lore = ["Enderhelmi + kompassi = Reliikkiavainportti", "Tämä esine mahdollistaa säiliöiden etäkäytön", "Valmistuksen jälkeen katso esinettä nähdäksesi käyttöohjeet"] [rift.blink] - name = "Repeämävälähdys" - description = "Lyhyen kantaman välitön teleporttaus, vain silmänräpäys!" - lore1 = "Lohkoja per välähdys (2x pystysuunnassa)" - lore2 = "Juostessa: Kaksoisnapauta hyppyä " - lore3 = "välähtääksesi" - lore = ["Lohkoja per välähdys (2x pystysuunnassa)", "Juostessa: Kaksoisnapauta hyppyä ", "välähtääksesi"] +name = "Repeämävälähdys" +description = "Lyhyen kantaman välitön teleporttaus, vain silmänräpäys!" +lore1 = "Lohkoja per välähdys (2x pystysuunnassa)" +lore2 = "Juostessa: Kaksoisnapauta hyppyä " +lore3 = "välähtääksesi" +lore = ["Lohkoja per välähdys (2x pystysuunnassa)", "Juostessa: Kaksoisnapauta hyppyä ", "välähtääksesi"] [rift.chest] - name = "Helppo enderarkku" - description = "Avaa enderarkku vasemmalla klikkauksella sitä kädessäsi." - lore1 = "Klikkaa enderarkkua kädessäsi avataksesi (älä vain aseta sitä)" - lore = ["Klikkaa enderarkkua kädessäsi avataksesi (älä vain aseta sitä)"] +name = "Helppo enderarkku" +description = "Avaa enderarkku vasemmalla klikkauksella sitä kädessäsi." +lore1 = "Klikkaa enderarkkua kädessäsi avataksesi (älä vain aseta sitä)" +lore = ["Klikkaa enderarkkua kädessäsi avataksesi (älä vain aseta sitä)"] [rift.descent] - name = "Leijumisenvastaisuus" - description = "Oletko kyllästynyt olemaan jumissa ilmassa? Tämä taito on sinua varten!" - lore1 = "Kyykisty vain laskeutaaksesi, ja putoat normaalia hitaammin!" - lore2 = "Jäähtymisaika:" - lore = ["Kyykisty vain laskeutaaksesi, ja putoat normaalia hitaammin!", "Jäähtymisaika:"] +name = "Leijumisenvastaisuus" +description = "Oletko kyllästynyt olemaan jumissa ilmassa? Tämä taito on sinua varten!" +lore1 = "Kyykisty vain laskeutaaksesi, ja putoat normaalia hitaammin!" +lore2 = "Jäähtymisaika:" +lore = ["Kyykisty vain laskeutaaksesi, ja putoat normaalia hitaammin!", "Jäähtymisaika:"] [rift.gate] - name = "Repeämäportti" - description = "Teleporttaa merkittyyn sijaintiin." - lore1 = "VALMISTUS: Smaragdi + ametistinsiru + enderhelmi" - lore2 = "Lue ennen käyttöä!" - lore3 = "5 sekunnin viive, " - lore4 = "voit kuolla tämän animaation aikana" - lore = ["VALMISTUS: Smaragdi + ametistinsiru + enderhelmi", "Lue ennen käyttöä!", "5 sekunnin viive, ", "voit kuolla tämän animaation aikana"] +name = "Repeämäportti" +description = "Teleporttaa merkittyyn sijaintiin." +lore1 = "VALMISTUS: Smaragdi + ametistinsiru + enderhelmi" +lore2 = "Lue ennen käyttöä!" +lore3 = "5 sekunnin viive, " +lore4 = "voit kuolla tämän animaation aikana" +lore = ["VALMISTUS: Smaragdi + ametistinsiru + enderhelmi", "Lue ennen käyttöä!", "5 sekunnin viive, ", "voit kuolla tämän animaation aikana"] [rift.resist] - name = "Repeämävastustus" - description = "Saat vastustuskykyä käyttäessäsi ender-esineitä ja -kykyjä" - lore1 = "+ Passiivinen: Antaa vastustuskykyä kun käytät repeämäkykyjä tai ender-esineitä" - lore2 = "EI sisällä kannettavaa enderarkkua, vain kulutettavia esineitä" - lore = ["+ Passiivinen: Antaa vastustuskykyä kun käytät repeämäkykyjä tai ender-esineitä", "EI sisällä kannettavaa enderarkkua, vain kulutettavia esineitä"] +name = "Repeämävastustus" +description = "Saat vastustuskykyä käyttäessäsi ender-esineitä ja -kykyjä" +lore1 = "+ Passiivinen: Antaa vastustuskykyä kun käytät repeämäkykyjä tai ender-esineitä" +lore2 = "EI sisällä kannettavaa enderarkkua, vain kulutettavia esineitä" +lore = ["+ Passiivinen: Antaa vastustuskykyä kun käytät repeämäkykyjä tai ender-esineitä", "EI sisällä kannettavaa enderarkkua, vain kulutettavia esineitä"] [rift.visage] - name = "Repeämänaamio" - description = "Estää endermiehiä muuttumasta aggressiivisiksi, jos sinulla on enderhelmiä varastossasi." - lore1 = "Endermiehet eivät muutu aggressiivisiksi, jos sinulla on enderhelmiä varastossasi." - lore = ["Endermiehet eivät muutu aggressiivisiksi, jos sinulla on enderhelmiä varastossasi."] +name = "Repeämänaamio" +description = "Estää endermiehiä muuttumasta aggressiivisiksi, jos sinulla on enderhelmiä varastossasi." +lore1 = "Endermiehet eivät muutu aggressiivisiksi, jos sinulla on enderhelmiä varastossasi." +lore = ["Endermiehet eivät muutu aggressiivisiksi, jos sinulla on enderhelmiä varastossasi."] # seaborn [seaborn] [seaborn.oxygen] - name = "Orgaaninen happitankki" - description = "Pidä enemmän happea pienissä keuhkoissasi!" - lore1 = "Happikapasiteetin lisäys" - lore = ["Happikapasiteetin lisäys"] +name = "Orgaaninen happitankki" +description = "Pidä enemmän happea pienissä keuhkoissasi!" +lore1 = "Happikapasiteetin lisäys" +lore = ["Happikapasiteetin lisäys"] [seaborn.fishers_fantasy] - name = "Kalastajan fantasia" - description = "Ansaitse enemmän XP:tä kalastuksesta ja saa enemmän kalaa!" - lore1 = "Jokaisella tasolla on mahdollisuus saada lisää XP:tä ja kalaa!" - lore = ["Jokaisella tasolla on mahdollisuus saada lisää XP:tä ja kalaa!"] +name = "Kalastajan fantasia" +description = "Ansaitse enemmän XP:tä kalastuksesta ja saa enemmän kalaa!" +lore1 = "Jokaisella tasolla on mahdollisuus saada lisää XP:tä ja kalaa!" +lore = ["Jokaisella tasolla on mahdollisuus saada lisää XP:tä ja kalaa!"] [seaborn.haste] - name = "Kilpikonnakaivaja" - description = "Veden alla louhiessasi saat kiire-efektin!" - lore1 = "Kiire 3 aktivoituu veden alla louhittaessa (pinoutuu vesitehon kanssa) vedenhengitysefektin päätyttyä!" - lore = ["Kiire 3 aktivoituu veden alla louhittaessa (pinoutuu vesitehon kanssa) vedenhengitysefektin päätyttyä!"] +name = "Kilpikonnakaivaja" +description = "Veden alla louhiessasi saat kiire-efektin!" +lore1 = "Kiire 3 aktivoituu veden alla louhittaessa (pinoutuu vesitehon kanssa) vedenhengitysefektin päätyttyä!" +lore = ["Kiire 3 aktivoituu veden alla louhittaessa (pinoutuu vesitehon kanssa) vedenhengitysefektin päätyttyä!"] [seaborn.night_vision] - name = "Kilpikonnan näkö" - description = "Veden alla saat yönäön" - lore1 = "Saat yönäön veden alla vedenhengitysefektin päätyttyä!" - lore = ["Saat yönäön veden alla vedenhengitysefektin päätyttyä!"] +name = "Kilpikonnan näkö" +description = "Veden alla saat yönäön" +lore1 = "Saat yönäön veden alla vedenhengitysefektin päätyttyä!" +lore = ["Saat yönäön veden alla vedenhengitysefektin päätyttyä!"] [seaborn.dolphin_grace] - name = "Delfiinin sulous" - description = "Ui kuin delfiini, ilman delfiinejä" - lore1 = "+ Passiivinen: saat " - lore2 = "x nopeutta (delfiinin sulous)" - lore3 = "tarkkaa saksalaista insinööritaitoa- hetkinen, tuo ei ole oikein... Ei yhteensopiva syvyyden kulkijan kanssa" - lore = ["+ Passiivinen: saat ", "x nopeutta (delfiinin sulous)", "tarkkaa saksalaista insinööritaitoa- hetkinen, tuo ei ole oikein... Ei yhteensopiva syvyyden kulkijan kanssa"] +name = "Delfiinin sulous" +description = "Ui kuin delfiini, ilman delfiinejä" +lore1 = "+ Passiivinen: saat " +lore2 = "x nopeutta (delfiinin sulous)" +lore3 = "tarkkaa saksalaista insinööritaitoa- hetkinen, tuo ei ole oikein... Ei yhteensopiva syvyyden kulkijan kanssa" +lore = ["+ Passiivinen: saat ", "x nopeutta (delfiinin sulous)", "tarkkaa saksalaista insinööritaitoa- hetkinen, tuo ei ole oikein... Ei yhteensopiva syvyyden kulkijan kanssa"] # stealth [stealth] [stealth.ghost_armor] - name = "Haamupanssari" - description = "Hitaasti kertyvä panssari kun et ota vahinkoa, kestää 1 iskun" - lore1 = "Maksimipanssari" - lore2 = "Nopeus" - lore = ["Maksimipanssari", "Nopeus"] +name = "Haamupanssari" +description = "Hitaasti kertyvä panssari kun et ota vahinkoa, kestää 1 iskun" +lore1 = "Maksimipanssari" +lore2 = "Nopeus" +lore = ["Maksimipanssari", "Nopeus"] [stealth.night_vision] - name = "Hiipimisen yönäkö" - description = "Saat yönäön hiipimisen aikana" - lore1 = "Saat purskauksen " - lore2 = "yönäköä" - lore3 = "hiipimisen aikana" - lore = ["Saat purskauksen ", "yönäköä", "hiipimisen aikana"] +name = "Hiipimisen yönäkö" +description = "Saat yönäön hiipimisen aikana" +lore1 = "Saat purskauksen " +lore2 = "yönäköä" +lore3 = "hiipimisen aikana" +lore = ["Saat purskauksen ", "yönäköä", "hiipimisen aikana"] [stealth.snatch] - name = "Esinenappaus" - description = "Nappaa pudonneet esineet heti hiipimisen aikana!" - lore1 = "Nappaussäde" - lore = ["Nappaussäde"] +name = "Esinenappaus" +description = "Nappaa pudonneet esineet heti hiipimisen aikana!" +lore1 = "Nappaussäde" +lore = ["Nappaussäde"] [stealth.speed] - name = "Hiipimisnopeus" - description = "Saat nopeutta hiipimisen aikana" - lore1 = "Hiipimisnopeus" - lore = ["Hiipimisnopeus"] +name = "Hiipimisnopeus" +description = "Saat nopeutta hiipimisen aikana" +lore1 = "Hiipimisnopeus" +lore = ["Hiipimisnopeus"] [stealth.ender_veil] - name = "Enderverhoa" - description = "Ei enää kurpitsoja endermiesiskujen estämiseen" - lore1 = "Estä endermiesiskut hiipimisen aikana" - lore2 = "Estä kaikki endermiesiskut" - lore = ["Estä endermiesiskut hiipimisen aikana", "Estä kaikki endermiesiskut"] +name = "Enderverhoa" +description = "Ei enää kurpitsoja endermiesiskujen estämiseen" +lore1 = "Estä endermiesiskut hiipimisen aikana" +lore2 = "Estä kaikki endermiesiskut" +lore = ["Estä endermiesiskut hiipimisen aikana", "Estä kaikki endermiesiskut"] # sword [sword] [sword.machete] - name = "Viidakkoveitsi" - description = "Leikkaa kasvillisuuden läpi helposti!" - lore1 = "Viiltosäde" - lore2 = "Hakkuun jäähtymisaika" - lore3 = "Työkalun kuluminen" - lore = ["Viiltosäde", "Hakkuun jäähtymisaika", "Työkalun kuluminen"] +name = "Viidakkoveitsi" +description = "Leikkaa kasvillisuuden läpi helposti!" +lore1 = "Viiltosäde" +lore2 = "Hakkuun jäähtymisaika" +lore3 = "Työkalun kuluminen" +lore = ["Viiltosäde", "Hakkuun jäähtymisaika", "Työkalun kuluminen"] [sword.bloody_blade] - name = "Verinen terä" - description = "Iskut miekallasi aiheuttavat verenvuotoa!" - lore1 = "Elävän olennon lyöminen miekallasi aiheuttaa verenvuotoa" - lore2 = "Verenvuodon kesto" - lore3 = "Verenvuodon jäähtymisaika" - lore = ["Elävän olennon lyöminen miekallasi aiheuttaa verenvuotoa", "Verenvuodon kesto", "Verenvuodon jäähtymisaika"] +name = "Verinen terä" +description = "Iskut miekallasi aiheuttavat verenvuotoa!" +lore1 = "Elävän olennon lyöminen miekallasi aiheuttaa verenvuotoa" +lore2 = "Verenvuodon kesto" +lore3 = "Verenvuodon jäähtymisaika" +lore = ["Elävän olennon lyöminen miekallasi aiheuttaa verenvuotoa", "Verenvuodon kesto", "Verenvuodon jäähtymisaika"] [sword.poisoned_blade] - name = "Myrkytetty terä" - description = "Iskut miekallasi aiheuttavat myrkytyksen!" - lore1 = "Elävän olennon lyöminen miekallasi aiheuttaa myrkytyksen" - lore2 = "Myrkytyksen kesto" - lore3 = "Myrkytyksen jäähtymisaika" - lore = ["Elävän olennon lyöminen miekallasi aiheuttaa myrkytyksen", "Myrkytyksen kesto", "Myrkytyksen jäähtymisaika"] +name = "Myrkytetty terä" +description = "Iskut miekallasi aiheuttavat myrkytyksen!" +lore1 = "Elävän olennon lyöminen miekallasi aiheuttaa myrkytyksen" +lore2 = "Myrkytyksen kesto" +lore3 = "Myrkytyksen jäähtymisaika" +lore = ["Elävän olennon lyöminen miekallasi aiheuttaa myrkytyksen", "Myrkytyksen kesto", "Myrkytyksen jäähtymisaika"] # taming [taming] [taming.damage] - name = "Kesyn vahinko" - description = "Kasvata kesytettyjen eläintesi aiheuttamaa vahinkoa." - lore1 = "Kasvanut vahinko" - lore = ["Kasvanut vahinko"] +name = "Kesyn vahinko" +description = "Kasvata kesytettyjen eläintesi aiheuttamaa vahinkoa." +lore1 = "Kasvanut vahinko" +lore = ["Kasvanut vahinko"] [taming.health] - name = "Kesyn terveys" - description = "Kasvata kesytettyjen eläintesi terveyttä." - lore1 = "Kasvanut terveys" - lore = ["Kasvanut terveys"] +name = "Kesyn terveys" +description = "Kasvata kesytettyjen eläintesi terveyttä." +lore1 = "Kasvanut terveys" +lore = ["Kasvanut terveys"] [taming.regeneration] - name = "Kesyn uudistuminen" - description = "Kasvata kesytettyjen eläintesi uudistumista." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Kesyn uudistuminen" +description = "Kasvata kesytettyjen eläintesi uudistumista." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Piikit" - description = "Heijasta vahinko takaisin hyökkääjääsi!" - lore1 = "Vahinko palautettu iskusta" - lore = ["Vahinko palautettu iskusta"] +name = "Piikit" +description = "Heijasta vahinko takaisin hyökkääjääsi!" +lore1 = "Vahinko palautettu iskusta" +lore = ["Vahinko palautettu iskusta"] [tragoul.globe] - name = "Tuskien pallo" - description = "Jaa aiheuttamasi vahinko ympärilläsi olevien vihollisten määrän perusteella!" - lore1 = "Mitä enemmän vihollisia ympärilläsi, sitä vähemmän vahinkoa kukin saa" - lore2 = "Kantama: " - lore3 = "Lisävahinko kaikille olennoille: " - lore = ["Mitä enemmän vihollisia ympärilläsi, sitä vähemmän vahinkoa kukin saa", "Kantama: ", "Lisävahinko kaikille olennoille: "] +name = "Tuskien pallo" +description = "Jaa aiheuttamasi vahinko ympärilläsi olevien vihollisten määrän perusteella!" +lore1 = "Mitä enemmän vihollisia ympärilläsi, sitä vähemmän vahinkoa kukin saa" +lore2 = "Kantama: " +lore3 = "Lisävahinko kaikille olennoille: " +lore = ["Mitä enemmän vihollisia ympärilläsi, sitä vähemmän vahinkoa kukin saa", "Kantama: ", "Lisävahinko kaikille olennoille: "] [tragoul.healing] - name = "Tuskan tahto" - description = "Saat terveyttä takaisin aiheuttamasi vahingon perusteella!" - lore1 = "Vahingoittaminen ei ole koskaan tuntunut näin hyvältä! Parannu aiheuttamastasi vahingosta" - lore2 = "3 sekunnin vahinkoikkuna parantumiselle ja 1 sekunnin jäähtymisaika " - lore3 = "Parantuminen per vahinkoprosentti: " - lore = ["Vahingoittaminen ei ole koskaan tuntunut näin hyvältä! Parannu aiheuttamastasi vahingosta", "3 sekunnin vahinkoikkuna parantumiselle ja 1 sekunnin jäähtymisaika ", "Parantuminen per vahinkoprosentti: "] +name = "Tuskan tahto" +description = "Saat terveyttä takaisin aiheuttamasi vahingon perusteella!" +lore1 = "Vahingoittaminen ei ole koskaan tuntunut näin hyvältä! Parannu aiheuttamastasi vahingosta" +lore2 = "3 sekunnin vahinkoikkuna parantumiselle ja 1 sekunnin jäähtymisaika " +lore3 = "Parantuminen per vahinkoprosentti: " +lore = ["Vahingoittaminen ei ole koskaan tuntunut näin hyvältä! Parannu aiheuttamastasi vahingosta", "3 sekunnin vahinkoikkuna parantumiselle ja 1 sekunnin jäähtymisaika ", "Parantuminen per vahinkoprosentti: "] [tragoul.lance] - name = "Ruumiskeihäät" - description = "Vihollisen tappaminen tai kyvyn tappaessa vihollisen synnyttää keihään, joka vahingoittaa läheistä vihollista!" - lore1 = "Keihäät etsivät kohteen kaikesta tappamastasi, JA jos tämä kyky tappaa vihollisen." - lore2 = "Uhraa osa elämästäsi keihäiden luomiseen (tämä voi tappaa sinut)" - lore3 = "Maksimikeihäät: 1 + " - lore = ["Keihäät etsivät kohteen kaikesta tappamastasi, JA jos tämä kyky tappaa vihollisen.", "Uhraa osa elämästäsi keihäiden luomiseen (tämä voi tappaa sinut)", "Maksimikeihäät: 1 + "] +name = "Ruumiskeihäät" +description = "Vihollisen tappaminen tai kyvyn tappaessa vihollisen synnyttää keihään, joka vahingoittaa läheistä vihollista!" +lore1 = "Keihäät etsivät kohteen kaikesta tappamastasi, JA jos tämä kyky tappaa vihollisen." +lore2 = "Uhraa osa elämästäsi keihäiden luomiseen (tämä voi tappaa sinut)" +lore3 = "Maksimikeihäät: 1 + " +lore = ["Keihäät etsivät kohteen kaikesta tappamastasi, JA jos tämä kyky tappaa vihollisen.", "Uhraa osa elämästäsi keihäiden luomiseen (tämä voi tappaa sinut)", "Maksimikeihäät: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Lasitykki" - description = "Bonusvahinko aseettomana mitä pienempi panssariarvosi on" - lore1 = "x vahinko 0 panssarilla" - lore2 = "Tasokohtainen bonusvahinko" - lore = ["x vahinko 0 panssarilla", "Tasokohtainen bonusvahinko"] +name = "Lasitykki" +description = "Bonusvahinko aseettomana mitä pienempi panssariarvosi on" +lore1 = "x vahinko 0 panssarilla" +lore2 = "Tasokohtainen bonusvahinko" +lore = ["x vahinko 0 panssarilla", "Tasokohtainen bonusvahinko"] [unarmed.power] - name = "Aseettoman voima" - description = "Parannettu aseeton vahinko" - lore1 = "Vahinko" - lore = ["Vahinko"] +name = "Aseettoman voima" +description = "Parannettu aseeton vahinko" +lore1 = "Vahinko" +lore = ["Vahinko"] [unarmed.sucker_punch] - name = "Yllätysisku" - description = "Juoksulyönnit, mutta tappavampia." - lore1 = "Vahinko" - lore2 = "Vahinko kasvaa nopeuden mukaan lyönnin aikana" - lore = ["Vahinko", "Vahinko kasvaa nopeuden mukaan lyönnin aikana"] +name = "Yllätysisku" +description = "Juoksulyönnit, mutta tappavampia." +lore1 = "Vahinko" +lore2 = "Vahinko kasvaa nopeuden mukaan lyönnin aikana" +lore = ["Vahinko", "Vahinko kasvaa nopeuden mukaan lyönnin aikana"] diff --git a/src/main/resources/fr_FR.toml b/src/main/resources/fr_FR.toml index 9d2d66658..79968889c 100644 --- a/src/main/resources/fr_FR.toml +++ b/src/main/resources/fr_FR.toml @@ -2,2060 +2,2060 @@ [advancement] [advancement.challenge_move_1k] - title = "Faut bouger !" - description = "Marchez plus d'1 kilometre (1 000 blocs)" +title = "Faut bouger !" +description = "Marchez plus d'1 kilometre (1 000 blocs)" [advancement.challenge_sprint_5k] - title = "Sprintez un 5K !" - description = "Marchez plus de 5 kilometres (5 000 blocs)" +title = "Sprintez un 5K !" +description = "Marchez plus de 5 kilometres (5 000 blocs)" [advancement.challenge_sprint_50k] - title = "Foncez un 50K !" - description = "Marchez plus de 50 kilometres (50 000 blocs)" +title = "Foncez un 50K !" +description = "Marchez plus de 50 kilometres (50 000 blocs)" [advancement.challenge_sprint_500k] - title = "Traversez l'Univers !!" - description = "Marchez plus de 500 kilometres (500 000 blocs)" +title = "Traversez l'Univers !!" +description = "Marchez plus de 500 kilometres (500 000 blocs)" [advancement.challenge_sprint_marathon] - title = "Sprintez un marathon (au sens propre) !" - description = "Sprintez plus de 42 195 blocs !" +title = "Sprintez un marathon (au sens propre) !" +description = "Sprintez plus de 42 195 blocs !" [advancement.challenge_place_1k] - title = "Constructeur novice !" - description = "Placez 1 000 blocs" +title = "Constructeur novice !" +description = "Placez 1 000 blocs" [advancement.challenge_place_5k] - title = "Constructeur intermediaire !" - description = "Placez 5 000 blocs" +title = "Constructeur intermediaire !" +description = "Placez 5 000 blocs" [advancement.challenge_place_50k] - title = "Constructeur avance !" - description = "Placez 50 000 blocs" +title = "Constructeur avance !" +description = "Placez 50 000 blocs" [advancement.challenge_place_500k] - title = "Maitre batisseur !" - description = "Placez 500 000 blocs" +title = "Maitre batisseur !" +description = "Placez 500 000 blocs" [advancement.challenge_place_5m] - title = "Acolyte de la Symetrie !" - description = "LA REALITE EST VOTRE TERRAIN DE JEU ! (5 millions de blocs)" +title = "Acolyte de la Symetrie !" +description = "LA REALITE EST VOTRE TERRAIN DE JEU ! (5 millions de blocs)" [advancement.challenge_chop_1k] - title = "Bucheron novice !" - description = "Coupez 1 000 blocs" +title = "Bucheron novice !" +description = "Coupez 1 000 blocs" [advancement.challenge_chop_5k] - title = "Bucheron intermediaire !" - description = "Coupez 5 000 blocs" +title = "Bucheron intermediaire !" +description = "Coupez 5 000 blocs" [advancement.challenge_chop_50k] - title = "Bucheron avance !" - description = "Coupez 50 000 blocs" +title = "Bucheron avance !" +description = "Coupez 50 000 blocs" [advancement.challenge_chop_500k] - title = "Maitre bucheron !" - description = "Coupez 500 000 blocs" +title = "Maitre bucheron !" +description = "Coupez 500 000 blocs" [advancement.challenge_chop_5m] - title = "Jackson le chien" - description = "Le meilleur toutou ! (5 millions de blocs)" +title = "Jackson le chien" +description = "Le meilleur toutou ! (5 millions de blocs)" [advancement.challenge_block_1k] - title = "A peine un blocage !" - description = "Bloquez 1 000 coups" +title = "A peine un blocage !" +description = "Bloquez 1 000 coups" [advancement.challenge_block_5k] - title = "Bloquer c'est amusant !" - description = "Bloquez 5 000 coups" +title = "Bloquer c'est amusant !" +description = "Bloquez 5 000 coups" [advancement.challenge_block_50k] - title = "Bloquer c'est ma vie !" - description = "Bloquez 50 000 coups" +title = "Bloquer c'est ma vie !" +description = "Bloquez 50 000 coups" [advancement.challenge_block_500k] - title = "Bloquer c'est ma raison d'etre !" - description = "Bloquez 500 000 coups" +title = "Bloquer c'est ma raison d'etre !" +description = "Bloquez 500 000 coups" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Bloquez 5 000 000 de coups" +title = "Die Hand Die Verletzt" +description = "Bloquez 5 000 000 de coups" [advancement.challenge_brew_1k] - title = "Alchimiste novice !" - description = "Consommez 1 000 potions" +title = "Alchimiste novice !" +description = "Consommez 1 000 potions" [advancement.challenge_brew_5k] - title = "Alchimiste intermediaire !" - description = "Consommez 5 000 potions" +title = "Alchimiste intermediaire !" +description = "Consommez 5 000 potions" [advancement.challenge_brew_50k] - title = "Alchimiste avance !" - description = "Consommez 50 000 potions" +title = "Alchimiste avance !" +description = "Consommez 50 000 potions" [advancement.challenge_brew_500k] - title = "Maitre alchimiste !" - description = "Consommez 500 000 potions" +title = "Maitre alchimiste !" +description = "Consommez 500 000 potions" [advancement.challenge_brew_5m] - title = "L'Alchimiste" - description = "Consommez 5 000 000 de potions" +title = "L'Alchimiste" +description = "Consommez 5 000 000 de potions" [advancement.challenge_brewsplash_1k] - title = "Lanceur de potions novice !" - description = "Lancez 1 000 potions a eclaboussures" +title = "Lanceur de potions novice !" +description = "Lancez 1 000 potions a eclaboussures" [advancement.challenge_brewsplash_5k] - title = "Lanceur de potions intermediaire !" - description = "Lancez 5 000 potions a eclaboussures" +title = "Lanceur de potions intermediaire !" +description = "Lancez 5 000 potions a eclaboussures" [advancement.challenge_brewsplash_50k] - title = "Lanceur de potions avance !" - description = "Lancez 50 000 potions a eclaboussures" +title = "Lanceur de potions avance !" +description = "Lancez 50 000 potions a eclaboussures" [advancement.challenge_brewsplash_500k] - title = "Maitre lanceur de potions !" - description = "Lancez 500 000 potions a eclaboussures" +title = "Maitre lanceur de potions !" +description = "Lancez 500 000 potions a eclaboussures" [advancement.challenge_brewsplash_5m] - title = "Le Maitre des eclaboussures" - description = "Lancez 5 000 000 de potions a eclaboussures" +title = "Le Maitre des eclaboussures" +description = "Lancez 5 000 000 de potions a eclaboussures" [advancement.challenge_craft_1k] - title = "Artisan astucieux !" - description = "Fabriquez 1 000 objets" +title = "Artisan astucieux !" +description = "Fabriquez 1 000 objets" [advancement.challenge_craft_5k] - title = "Artisan acariatre !" - description = "Fabriquez 5 000 objets" +title = "Artisan acariatre !" +description = "Fabriquez 5 000 objets" [advancement.challenge_craft_50k] - title = "Artisan servile !" - description = "Fabriquez 50 000 objets" +title = "Artisan servile !" +description = "Fabriquez 50 000 objets" [advancement.challenge_craft_500k] - title = "Artisan cacophonique !" - description = "Fabriquez 500 000 objets" +title = "Artisan cacophonique !" +description = "Fabriquez 500 000 objets" [advancement.challenge_craft_5m] - title = "McCraftface calamiteux" - description = "Fabriquez 5 000 000 d'objets" +title = "McCraftface calamiteux" +description = "Fabriquez 5 000 000 d'objets" [advancement.challenge_enchant_1k] - title = "Enchanteur novice !" - description = "Enchantez 1 000 objets" +title = "Enchanteur novice !" +description = "Enchantez 1 000 objets" [advancement.challenge_enchant_5k] - title = "Enchanteur intermediaire !" - description = "Enchantez 5 000 objets" +title = "Enchanteur intermediaire !" +description = "Enchantez 5 000 objets" [advancement.challenge_enchant_50k] - title = "Enchanteur avance !" - description = "Enchantez 50 000 objets" +title = "Enchanteur avance !" +description = "Enchantez 50 000 objets" [advancement.challenge_enchant_500k] - title = "Maitre enchanteur !" - description = "Enchantez 500 000 objets" +title = "Maitre enchanteur !" +description = "Enchantez 500 000 objets" [advancement.challenge_enchant_5m] - title = "Enchanteur enigmatique" - description = "Enchantez 5 000 000 d'objets" +title = "Enchanteur enigmatique" +description = "Enchantez 5 000 000 d'objets" [advancement.challenge_excavate_1k] - title = "Excavateur enthousiaste !" - description = "Excavez 1 000 blocs" +title = "Excavateur enthousiaste !" +description = "Excavez 1 000 blocs" [advancement.challenge_excavate_5k] - title = "Excavateur intermediaire !" - description = "Excavez 5 000 blocs" +title = "Excavateur intermediaire !" +description = "Excavez 5 000 blocs" [advancement.challenge_excavate_50k] - title = "Excavateur avance !" - description = "Excavez 50 000 blocs" +title = "Excavateur avance !" +description = "Excavez 50 000 blocs" [advancement.challenge_excavate_500k] - title = "Maitre excavateur !" - description = "Excavez 500 000 blocs" +title = "Maitre excavateur !" +description = "Excavez 500 000 blocs" [advancement.challenge_excavate_5m] - title = "Excavateur enigmatique" - description = "Excavez 5 000 000 de blocs" +title = "Excavateur enigmatique" +description = "Excavez 5 000 000 de blocs" [advancement.horrible_person] - title = "Vous etes une personne horrible" - description = "Insondable, vraiment" +title = "Vous etes une personne horrible" +description = "Insondable, vraiment" [advancement.challenge_turtle_egg_smasher] - title = "Briseur d'oeufs de tortue !" - description = "Cassez 100 oeufs de tortue" +title = "Briseur d'oeufs de tortue !" +description = "Cassez 100 oeufs de tortue" [advancement.challenge_turtle_egg_annihilator] - title = "Annihilateur d'oeufs de tortue !" - description = "Cassez 500 oeufs de tortue" +title = "Annihilateur d'oeufs de tortue !" +description = "Cassez 500 oeufs de tortue" [advancement.challenge_novice_hunter] - title = "Chasseur novice !" - description = "Tuez 100 entites" +title = "Chasseur novice !" +description = "Tuez 100 entites" [advancement.challenge_intermediate_hunter] - title = "Chasseur intermediaire !" - description = "Tuez 500 entites" +title = "Chasseur intermediaire !" +description = "Tuez 500 entites" [advancement.challenge_advanced_hunter] - title = "Chasseur avance !" - description = "Tuez 5 000 entites" +title = "Chasseur avance !" +description = "Tuez 5 000 entites" [advancement.challenge_creeper_conqueror] - title = "Conquerant de Creepers !" - description = "Tuez 50 Creepers" +title = "Conquerant de Creepers !" +description = "Tuez 50 Creepers" [advancement.challenge_creeper_annihilator] - title = "Annihilateur de Creepers !" - description = "Tuez 200 Creepers" +title = "Annihilateur de Creepers !" +description = "Tuez 200 Creepers" [advancement.challenge_pickaxe_1k] - title = "Mineur novice" - description = "Cassez 1 000 blocs" +title = "Mineur novice" +description = "Cassez 1 000 blocs" [advancement.challenge_pickaxe_5k] - title = "Mineur qualifie" - description = "Cassez 5 000 blocs" +title = "Mineur qualifie" +description = "Cassez 5 000 blocs" [advancement.challenge_pickaxe_50k] - title = "Mineur expert" - description = "Cassez 50 000 blocs" +title = "Mineur expert" +description = "Cassez 50 000 blocs" [advancement.challenge_pickaxe_500k] - title = "Maitre mineur" - description = "Cassez 500 000 blocs" +title = "Maitre mineur" +description = "Cassez 500 000 blocs" [advancement.challenge_pickaxe_5m] - title = "Mineur legendaire" - description = "Cassez 5 000 000 de blocs" +title = "Mineur legendaire" +description = "Cassez 5 000 000 de blocs" [advancement.challenge_eat_100] - title = "Tant a manger !" - description = "Mangez plus de 100 objets !" +title = "Tant a manger !" +description = "Mangez plus de 100 objets !" [advancement.challenge_eat_1000] - title = "Faim insatiable !" - description = "Mangez plus de 1 000 objets !" +title = "Faim insatiable !" +description = "Mangez plus de 1 000 objets !" [advancement.challenge_eat_10000] - title = "FAIM ETERNELLE !" - description = "Mangez plus de 10 000 objets !" +title = "FAIM ETERNELLE !" +description = "Mangez plus de 10 000 objets !" [advancement.challenge_harvest_100] - title = "Pleine recolte" - description = "Recoltez plus de 100 cultures !" +title = "Pleine recolte" +description = "Recoltez plus de 100 cultures !" [advancement.challenge_harvest_1000] - title = "Grande recolte" - description = "Recoltez plus de 1 000 cultures !" +title = "Grande recolte" +description = "Recoltez plus de 1 000 cultures !" [advancement.challenge_swim_1nm] - title = "Sous-marin humain !" - description = "Nagez 1 mille nautique (1 852 blocs)" +title = "Sous-marin humain !" +description = "Nagez 1 mille nautique (1 852 blocs)" [advancement.challenge_sneak_1k] - title = "Mal aux genoux" - description = "Accroupissez-vous sur plus d'un kilometre (1 000 blocs)" +title = "Mal aux genoux" +description = "Accroupissez-vous sur plus d'un kilometre (1 000 blocs)" [advancement.challenge_sneak_5k] - title = "Marcheur de l'Ombre" - description = "Accroupissez-vous sur plus de 5 000 blocs" +title = "Marcheur de l'Ombre" +description = "Accroupissez-vous sur plus de 5 000 blocs" [advancement.challenge_sneak_20k] - title = "Fantome" - description = "Accroupissez-vous sur plus de 20 000 blocs" +title = "Fantome" +description = "Accroupissez-vous sur plus de 20 000 blocs" [advancement.challenge_swim_5k] - title = "Plongeur des Abysses" - description = "Nagez sur plus de 5 000 blocs" +title = "Plongeur des Abysses" +description = "Nagez sur plus de 5 000 blocs" [advancement.challenge_swim_20k] - title = "Elu de Poseidon" - description = "Nagez sur plus de 20 000 blocs" +title = "Elu de Poseidon" +description = "Nagez sur plus de 20 000 blocs" [advancement.challenge_sword_100] - title = "Premier Sang" - description = "Infligez 100 coups avec une epee" +title = "Premier Sang" +description = "Infligez 100 coups avec une epee" [advancement.challenge_sword_1k] - title = "Danseur de Lames" - description = "Infligez 1 000 coups avec une epee" +title = "Danseur de Lames" +description = "Infligez 1 000 coups avec une epee" [advancement.challenge_sword_10k] - title = "Mille Entailles" - description = "Infligez 10 000 coups avec une epee" +title = "Mille Entailles" +description = "Infligez 10 000 coups avec une epee" [advancement.challenge_unarmed_100] - title = "Bagarreur" - description = "Infligez 100 coups a mains nues" +title = "Bagarreur" +description = "Infligez 100 coups a mains nues" [advancement.challenge_unarmed_1k] - title = "Poings de Fer" - description = "Infligez 1 000 coups a mains nues" +title = "Poings de Fer" +description = "Infligez 1 000 coups a mains nues" [advancement.challenge_unarmed_10k] - title = "One Punch" - description = "Infligez 10 000 coups a mains nues" +title = "One Punch" +description = "Infligez 10 000 coups a mains nues" [advancement.challenge_trag_1k] - title = "Prix du Sang" - description = "Subissez 1 000 degats" +title = "Prix du Sang" +description = "Subissez 1 000 degats" [advancement.challenge_trag_10k] - title = "Maree Pourpre" - description = "Subissez 10 000 degats" +title = "Maree Pourpre" +description = "Subissez 10 000 degats" [advancement.challenge_trag_100k] - title = "Avatar de la Souffrance" - description = "Subissez 100 000 degats" +title = "Avatar de la Souffrance" +description = "Subissez 100 000 degats" [advancement.challenge_ranged_100] - title = "Exercice de Tir" - description = "Tirez 100 projectiles" +title = "Exercice de Tir" +description = "Tirez 100 projectiles" [advancement.challenge_ranged_1k] - title = "Oeil de Faucon" - description = "Tirez 1 000 projectiles" +title = "Oeil de Faucon" +description = "Tirez 1 000 projectiles" [advancement.challenge_ranged_10k] - title = "Tempete de Fleches" - description = "Tirez 10 000 projectiles" +title = "Tempete de Fleches" +description = "Tirez 10 000 projectiles" [advancement.challenge_chronos_1h] - title = "Tic Tac" - description = "Passez 1 heure en ligne" +title = "Tic Tac" +description = "Passez 1 heure en ligne" [advancement.challenge_chronos_24h] - title = "Sables du Temps" - description = "Passez 24 heures en ligne" +title = "Sables du Temps" +description = "Passez 24 heures en ligne" [advancement.challenge_chronos_168h] - title = "Intemporel" - description = "Passez 168 heures (1 semaine) en ligne" +title = "Intemporel" +description = "Passez 168 heures (1 semaine) en ligne" [advancement.challenge_nether_50] - title = "Gardien de l'Enfer" - description = "Tuez 50 creatures du Nether" +title = "Gardien de l'Enfer" +description = "Tuez 50 creatures du Nether" [advancement.challenge_nether_500] - title = "Gardien de l'Abime" - description = "Tuez 500 creatures du Nether" +title = "Gardien de l'Abime" +description = "Tuez 500 creatures du Nether" [advancement.challenge_nether_5k] - title = "Seigneur du Nether" - description = "Tuez 5 000 creatures du Nether" +title = "Seigneur du Nether" +description = "Tuez 5 000 creatures du Nether" [advancement.challenge_rift_50] - title = "Anomalie Spatiale" - description = "Teleportez-vous 50 fois" +title = "Anomalie Spatiale" +description = "Teleportez-vous 50 fois" [advancement.challenge_rift_500] - title = "Marcheur du Vide" - description = "Teleportez-vous 500 fois" +title = "Marcheur du Vide" +description = "Teleportez-vous 500 fois" [advancement.challenge_rift_5k] - title = "Entre les Mondes" - description = "Teleportez-vous 5 000 fois" +title = "Entre les Mondes" +description = "Teleportez-vous 5 000 fois" [advancement.challenge_taming_10] - title = "Murmureur d'Animaux" - description = "Elevez 10 animaux" +title = "Murmureur d'Animaux" +description = "Elevez 10 animaux" [advancement.challenge_taming_50] - title = "Chef de Meute" - description = "Elevez 50 animaux" +title = "Chef de Meute" +description = "Elevez 50 animaux" [advancement.challenge_taming_500] - title = "Maitre des Betes" - description = "Elevez 500 animaux" +title = "Maitre des Betes" +description = "Elevez 500 animaux" # Agility [advancement.challenge_sprint_dist_5k] - title = "Demon de la Vitesse" - description = "Sprintez sur plus de 5 Kilometres (5.000 blocs)" +title = "Demon de la Vitesse" +description = "Sprintez sur plus de 5 Kilometres (5.000 blocs)" [advancement.challenge_sprint_dist_50k] - title = "Jambes de Foudre" - description = "Sprintez sur plus de 50 Kilometres (50.000 blocs)" +title = "Jambes de Foudre" +description = "Sprintez sur plus de 50 Kilometres (50.000 blocs)" [advancement.challenge_agility_swim_1k] - title = "Marcheur des Eaux" - description = "Nagez sur plus de 1 Kilometre (1.000 blocs)" +title = "Marcheur des Eaux" +description = "Nagez sur plus de 1 Kilometre (1.000 blocs)" [advancement.challenge_agility_swim_10k] - title = "Voyageur Aquatique" - description = "Nagez sur plus de 10 Kilometres (10.000 blocs)" +title = "Voyageur Aquatique" +description = "Nagez sur plus de 10 Kilometres (10.000 blocs)" [advancement.challenge_fly_1k] - title = "Danseur du Ciel" - description = "Volez sur plus de 1 Kilometre (1.000 blocs)" +title = "Danseur du Ciel" +description = "Volez sur plus de 1 Kilometre (1.000 blocs)" [advancement.challenge_fly_10k] - title = "Cavalier du Vent" - description = "Volez sur plus de 10 Kilometres (10.000 blocs)" +title = "Cavalier du Vent" +description = "Volez sur plus de 10 Kilometres (10.000 blocs)" [advancement.challenge_agility_sneak_500] - title = "Pas Discrets" - description = "Deplacez-vous furtivement sur plus de 500 blocs" +title = "Pas Discrets" +description = "Deplacez-vous furtivement sur plus de 500 blocs" [advancement.challenge_agility_sneak_5k] - title = "Pas Fantomes" - description = "Deplacez-vous furtivement sur plus de 5 Kilometres (5.000 blocs)" +title = "Pas Fantomes" +description = "Deplacez-vous furtivement sur plus de 5 Kilometres (5.000 blocs)" # Architect [advancement.challenge_demolish_500] - title = "Equipe de Demolition" - description = "Cassez 500 blocs" +title = "Equipe de Demolition" +description = "Cassez 500 blocs" [advancement.challenge_demolish_5k] - title = "Boule de Demolition" - description = "Cassez 5.000 blocs" +title = "Boule de Demolition" +description = "Cassez 5.000 blocs" [advancement.challenge_value_placed_10k] - title = "Batisseur de Valeur" - description = "Placez des blocs d'une valeur de 10.000" +title = "Batisseur de Valeur" +description = "Placez des blocs d'une valeur de 10.000" [advancement.challenge_value_placed_100k] - title = "Maitre Architecte" - description = "Placez des blocs d'une valeur de 100.000" +title = "Maitre Architecte" +description = "Placez des blocs d'une valeur de 100.000" [advancement.challenge_demolish_val_5k] - title = "Expert en Recuperation" - description = "Recuperez 5.000 de valeur de blocs par demolition" +title = "Expert en Recuperation" +description = "Recuperez 5.000 de valeur de blocs par demolition" [advancement.challenge_demolish_val_50k] - title = "Deconstruction Totale" - description = "Recuperez 50.000 de valeur de blocs par demolition" +title = "Deconstruction Totale" +description = "Recuperez 50.000 de valeur de blocs par demolition" [advancement.challenge_high_build_100] - title = "Batisseur Celeste" - description = "Placez 100 blocs au-dessus de Y=128" +title = "Batisseur Celeste" +description = "Placez 100 blocs au-dessus de Y=128" [advancement.challenge_high_build_1k] - title = "Architecte des Nuages" - description = "Placez 1.000 blocs au-dessus de Y=128" +title = "Architecte des Nuages" +description = "Placez 1.000 blocs au-dessus de Y=128" # Axes [advancement.challenge_axe_swing_500] - title = "Bucheron" - description = "Balancez votre hache 500 fois" +title = "Bucheron" +description = "Balancez votre hache 500 fois" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Balancez votre hache 5.000 fois" +title = "Berserker" +description = "Balancez votre hache 5.000 fois" [advancement.challenge_axe_damage_1k] - title = "Fendeur" - description = "Infligez 1.000 degats avec des haches" +title = "Fendeur" +description = "Infligez 1.000 degats avec des haches" [advancement.challenge_axe_damage_10k] - title = "Hache du Bourreau" - description = "Infligez 10.000 degats avec des haches" +title = "Hache du Bourreau" +description = "Infligez 10.000 degats avec des haches" [advancement.challenge_axe_value_5k] - title = "Marchand de Bois" - description = "Recoltez du bois d'une valeur de 5.000" +title = "Marchand de Bois" +description = "Recoltez du bois d'une valeur de 5.000" [advancement.challenge_axe_value_50k] - title = "Baron du Bois" - description = "Recoltez du bois d'une valeur de 50.000" +title = "Baron du Bois" +description = "Recoltez du bois d'une valeur de 50.000" [advancement.challenge_leaves_500] - title = "Souffleur de Feuilles" - description = "Eliminez 500 blocs de feuilles avec une hache" +title = "Souffleur de Feuilles" +description = "Eliminez 500 blocs de feuilles avec une hache" [advancement.challenge_leaves_5k] - title = "Defoliateur" - description = "Eliminez 5.000 blocs de feuilles avec une hache" +title = "Defoliateur" +description = "Eliminez 5.000 blocs de feuilles avec une hache" # Blocking [advancement.challenge_block_dmg_1k] - title = "Absorbeur de Degats" - description = "Bloquez 1.000 degats avec un bouclier" +title = "Absorbeur de Degats" +description = "Bloquez 1.000 degats avec un bouclier" [advancement.challenge_block_dmg_10k] - title = "Bouclier Humain" - description = "Bloquez 10.000 degats avec un bouclier" +title = "Bouclier Humain" +description = "Bloquez 10.000 degats avec un bouclier" [advancement.challenge_block_proj_100] - title = "Deflecteur de Fleches" - description = "Bloquez 100 projectiles avec un bouclier" +title = "Deflecteur de Fleches" +description = "Bloquez 100 projectiles avec un bouclier" [advancement.challenge_block_proj_1k] - title = "Bouclier Anti-Projectiles" - description = "Bloquez 1.000 projectiles avec un bouclier" +title = "Bouclier Anti-Projectiles" +description = "Bloquez 1.000 projectiles avec un bouclier" [advancement.challenge_block_melee_500] - title = "Maitre de la Parade" - description = "Bloquez 500 attaques de melee avec un bouclier" +title = "Maitre de la Parade" +description = "Bloquez 500 attaques de melee avec un bouclier" [advancement.challenge_block_melee_5k] - title = "Forteresse de Fer" - description = "Bloquez 5.000 attaques de melee avec un bouclier" +title = "Forteresse de Fer" +description = "Bloquez 5.000 attaques de melee avec un bouclier" [advancement.challenge_block_heavy_50] - title = "Tank" - description = "Bloquez 50 attaques lourdes (plus de 5 degats)" +title = "Tank" +description = "Bloquez 50 attaques lourdes (plus de 5 degats)" [advancement.challenge_block_heavy_500] - title = "Objet Immuable" - description = "Bloquez 500 attaques lourdes (plus de 5 degats)" +title = "Objet Immuable" +description = "Bloquez 500 attaques lourdes (plus de 5 degats)" # Brewing [advancement.challenge_brew_stands_10] - title = "Installation du Brasseur" - description = "Placez 10 alambics" +title = "Installation du Brasseur" +description = "Placez 10 alambics" [advancement.challenge_brew_stands_50] - title = "Fabrique de Potions" - description = "Placez 50 alambics" +title = "Fabrique de Potions" +description = "Placez 50 alambics" [advancement.challenge_brew_strong_25] - title = "Breuvage Puissant" - description = "Consommez 25 potions ameliorees" +title = "Breuvage Puissant" +description = "Consommez 25 potions ameliorees" [advancement.challenge_brew_strong_250] - title = "Puissance Maximale" - description = "Consommez 250 potions ameliorees" +title = "Puissance Maximale" +description = "Consommez 250 potions ameliorees" [advancement.challenge_brew_splash_hits_50] - title = "Zone d'Eclaboussure" - description = "Touchez 50 entites avec des potions jetables" +title = "Zone d'Eclaboussure" +description = "Touchez 50 entites avec des potions jetables" [advancement.challenge_brew_splash_hits_500] - title = "Medecin de Peste" - description = "Touchez 500 entites avec des potions jetables" +title = "Medecin de Peste" +description = "Touchez 500 entites avec des potions jetables" # Chronos [advancement.challenge_active_dist_1k] - title = "Agite" - description = "Parcourez 1 Kilometre en etant actif" +title = "Agite" +description = "Parcourez 1 Kilometre en etant actif" [advancement.challenge_active_dist_10k] - title = "Eclaireur" - description = "Parcourez 10 Kilometres en etant actif" +title = "Eclaireur" +description = "Parcourez 10 Kilometres en etant actif" [advancement.challenge_active_dist_100k] - title = "Mouvement Perpetuel" - description = "Parcourez 100 Kilometres en etant actif" +title = "Mouvement Perpetuel" +description = "Parcourez 100 Kilometres en etant actif" [advancement.challenge_beds_10] - title = "Leve-tot" - description = "Dormez dans un lit 10 fois" +title = "Leve-tot" +description = "Dormez dans un lit 10 fois" [advancement.challenge_beds_100] - title = "Sauteur de Temps" - description = "Dormez dans un lit 100 fois" +title = "Sauteur de Temps" +description = "Dormez dans un lit 100 fois" [advancement.challenge_chronos_tp_50] - title = "Decalage Temporel" - description = "Teleportez-vous 50 fois" +title = "Decalage Temporel" +description = "Teleportez-vous 50 fois" [advancement.challenge_chronos_tp_500] - title = "Distorsion Temporelle" - description = "Teleportez-vous 500 fois" +title = "Distorsion Temporelle" +description = "Teleportez-vous 500 fois" [advancement.challenge_chronos_deaths_10] - title = "Mortel" - description = "Mourez 10 fois" +title = "Mortel" +description = "Mourez 10 fois" [advancement.challenge_chronos_deaths_100] - title = "Defieur de la Mort" - description = "Mourez 100 fois" +title = "Defieur de la Mort" +description = "Mourez 100 fois" # Crafting [advancement.challenge_craft_value_10k] - title = "Valeur Artisanale" - description = "Fabriquez des objets d'une valeur totale de 10.000" +title = "Valeur Artisanale" +description = "Fabriquez des objets d'une valeur totale de 10.000" [advancement.challenge_craft_value_100k] - title = "Artisan" - description = "Fabriquez des objets d'une valeur totale de 100.000" +title = "Artisan" +description = "Fabriquez des objets d'une valeur totale de 100.000" [advancement.challenge_craft_tools_25] - title = "Forgeur d'Outils" - description = "Fabriquez 25 outils" +title = "Forgeur d'Outils" +description = "Fabriquez 25 outils" [advancement.challenge_craft_tools_250] - title = "Maitre Forgeron" - description = "Fabriquez 250 outils" +title = "Maitre Forgeron" +description = "Fabriquez 250 outils" [advancement.challenge_craft_armor_25] - title = "Forgeur d'Armures" - description = "Fabriquez 25 pieces d'armure" +title = "Forgeur d'Armures" +description = "Fabriquez 25 pieces d'armure" [advancement.challenge_craft_armor_250] - title = "Maitre Armurier" - description = "Fabriquez 250 pieces d'armure" +title = "Maitre Armurier" +description = "Fabriquez 250 pieces d'armure" [advancement.challenge_craft_mass_25k] - title = "Producteur en Masse" - description = "Fabriquez 25.000 objets" +title = "Producteur en Masse" +description = "Fabriquez 25.000 objets" [advancement.challenge_craft_mass_250k] - title = "Revolution Industrielle" - description = "Fabriquez 250.000 objets" +title = "Revolution Industrielle" +description = "Fabriquez 250.000 objets" # Discovery [advancement.challenge_discover_items_50] - title = "Collectionneur" - description = "Decouvrez 50 objets uniques" +title = "Collectionneur" +description = "Decouvrez 50 objets uniques" [advancement.challenge_discover_items_250] - title = "Catalogueur" - description = "Decouvrez 250 objets uniques" +title = "Catalogueur" +description = "Decouvrez 250 objets uniques" [advancement.challenge_discover_blocks_50] - title = "Arpenteur" - description = "Decouvrez 50 blocs uniques" +title = "Arpenteur" +description = "Decouvrez 50 blocs uniques" [advancement.challenge_discover_blocks_250] - title = "Geologue" - description = "Decouvrez 250 blocs uniques" +title = "Geologue" +description = "Decouvrez 250 blocs uniques" [advancement.challenge_discover_mobs_25] - title = "Observateur" - description = "Decouvrez 25 creatures uniques" +title = "Observateur" +description = "Decouvrez 25 creatures uniques" [advancement.challenge_discover_mobs_75] - title = "Naturaliste" - description = "Decouvrez 75 creatures uniques" +title = "Naturaliste" +description = "Decouvrez 75 creatures uniques" [advancement.challenge_discover_biomes_10] - title = "Vagabond" - description = "Decouvrez 10 biomes uniques" +title = "Vagabond" +description = "Decouvrez 10 biomes uniques" [advancement.challenge_discover_biomes_40] - title = "Globe-Trotteur" - description = "Decouvrez 40 biomes uniques" +title = "Globe-Trotteur" +description = "Decouvrez 40 biomes uniques" [advancement.challenge_discover_foods_10] - title = "Gourmet" - description = "Decouvrez 10 aliments uniques" +title = "Gourmet" +description = "Decouvrez 10 aliments uniques" [advancement.challenge_discover_foods_30] - title = "Maitre Culinaire" - description = "Decouvrez 30 aliments uniques" +title = "Maitre Culinaire" +description = "Decouvrez 30 aliments uniques" # Enchanting [advancement.challenge_enchant_power_100] - title = "Tisseur de Pouvoir" - description = "Accumulez 100 de puissance d'enchantement" +title = "Tisseur de Pouvoir" +description = "Accumulez 100 de puissance d'enchantement" [advancement.challenge_enchant_power_1k] - title = "Maitre des Arcanes" - description = "Accumulez 1.000 de puissance d'enchantement" +title = "Maitre des Arcanes" +description = "Accumulez 1.000 de puissance d'enchantement" [advancement.challenge_enchant_levels_1k] - title = "Depensier de Niveaux" - description = "Depensez 1.000 niveaux d'experience en enchantements" +title = "Depensier de Niveaux" +description = "Depensez 1.000 niveaux d'experience en enchantements" [advancement.challenge_enchant_levels_10k] - title = "Gouffre a XP" - description = "Depensez 10.000 niveaux d'experience en enchantements" +title = "Gouffre a XP" +description = "Depensez 10.000 niveaux d'experience en enchantements" [advancement.challenge_enchant_high_25] - title = "Flambeur" - description = "Effectuez 25 enchantements de niveau maximum" +title = "Flambeur" +description = "Effectuez 25 enchantements de niveau maximum" [advancement.challenge_enchant_high_250] - title = "Enchanteur Legendaire" - description = "Effectuez 250 enchantements de niveau maximum" +title = "Enchanteur Legendaire" +description = "Effectuez 250 enchantements de niveau maximum" [advancement.challenge_enchant_total_500] - title = "Bruleur de Niveaux" - description = "Depensez 500 niveaux au total en enchantements" +title = "Bruleur de Niveaux" +description = "Depensez 500 niveaux au total en enchantements" [advancement.challenge_enchant_total_5k] - title = "Investissement Arcanique" - description = "Depensez 5.000 niveaux au total en enchantements" +title = "Investissement Arcanique" +description = "Depensez 5.000 niveaux au total en enchantements" # Excavation [advancement.challenge_dig_swing_500] - title = "Pelleteur" - description = "Balancez votre pelle 500 fois" +title = "Pelleteur" +description = "Balancez votre pelle 500 fois" [advancement.challenge_dig_swing_5k] - title = "Excavateur" - description = "Balancez votre pelle 5.000 fois" +title = "Excavateur" +description = "Balancez votre pelle 5.000 fois" [advancement.challenge_dig_damage_1k] - title = "Chevalier a la Pelle" - description = "Infligez 1.000 degats avec une pelle" +title = "Chevalier a la Pelle" +description = "Infligez 1.000 degats avec une pelle" [advancement.challenge_dig_damage_10k] - title = "Maitre de la Pelle" - description = "Infligez 10.000 degats avec une pelle" +title = "Maitre de la Pelle" +description = "Infligez 10.000 degats avec une pelle" [advancement.challenge_dig_value_5k] - title = "Marchand de Terre" - description = "Excavez des blocs d'une valeur de 5.000" +title = "Marchand de Terre" +description = "Excavez des blocs d'une valeur de 5.000" [advancement.challenge_dig_value_50k] - title = "Baron de la Terre" - description = "Excavez des blocs d'une valeur de 50.000" +title = "Baron de la Terre" +description = "Excavez des blocs d'une valeur de 50.000" [advancement.challenge_dig_gravel_500] - title = "Broyeur de Gravier" - description = "Creusez 500 blocs de gravier, sable ou argile" +title = "Broyeur de Gravier" +description = "Creusez 500 blocs de gravier, sable ou argile" [advancement.challenge_dig_gravel_5k] - title = "Tamiseur de Sable" - description = "Creusez 5.000 blocs de gravier, sable ou argile" +title = "Tamiseur de Sable" +description = "Creusez 5.000 blocs de gravier, sable ou argile" # Herbalism [advancement.challenge_plant_100] - title = "Semeur" - description = "Plantez 100 cultures" +title = "Semeur" +description = "Plantez 100 cultures" [advancement.challenge_plant_1k] - title = "Main Verte" - description = "Plantez 1.000 cultures" +title = "Main Verte" +description = "Plantez 1.000 cultures" [advancement.challenge_plant_5k] - title = "Baron Agricole" - description = "Plantez 5.000 cultures" +title = "Baron Agricole" +description = "Plantez 5.000 cultures" [advancement.challenge_compost_50] - title = "Recycleur" - description = "Compostez 50 objets" +title = "Recycleur" +description = "Compostez 50 objets" [advancement.challenge_compost_500] - title = "Enrichisseur de Sol" - description = "Compostez 500 objets" +title = "Enrichisseur de Sol" +description = "Compostez 500 objets" [advancement.challenge_shear_50] - title = "Tondeur" - description = "Tondez 50 entites" +title = "Tondeur" +description = "Tondez 50 entites" [advancement.challenge_shear_250] - title = "Maitre du Troupeau" - description = "Tondez 250 entites" +title = "Maitre du Troupeau" +description = "Tondez 250 entites" # Hunter [advancement.challenge_kills_500] - title = "Tueur" - description = "Abattez 500 creatures" +title = "Tueur" +description = "Abattez 500 creatures" [advancement.challenge_kills_5k] - title = "Bourreau" - description = "Abattez 5.000 creatures" +title = "Bourreau" +description = "Abattez 5.000 creatures" [advancement.challenge_boss_1] - title = "Defieur de Boss" - description = "Terrassez un boss" +title = "Defieur de Boss" +description = "Terrassez un boss" [advancement.challenge_boss_10] - title = "Tueur de Legendes" - description = "Terrassez 10 boss" +title = "Tueur de Legendes" +description = "Terrassez 10 boss" # Nether [advancement.challenge_wither_dmg_500] - title = "Fletri" - description = "Endurez 500 degats de Wither" +title = "Fletri" +description = "Endurez 500 degats de Wither" [advancement.challenge_wither_dmg_5k] - title = "Survivant du Fleau" - description = "Endurez 5.000 degats de Wither" +title = "Survivant du Fleau" +description = "Endurez 5.000 degats de Wither" [advancement.challenge_wither_skel_25] - title = "Collectionneur d'Os" - description = "Abattez 25 squelettes Wither" +title = "Collectionneur d'Os" +description = "Abattez 25 squelettes Wither" [advancement.challenge_wither_skel_250] - title = "Fleau des Squelettes" - description = "Abattez 250 squelettes Wither" +title = "Fleau des Squelettes" +description = "Abattez 250 squelettes Wither" [advancement.challenge_wither_boss_1] - title = "Pourfendeur du Wither" - description = "Vainquez le Wither" +title = "Pourfendeur du Wither" +description = "Vainquez le Wither" [advancement.challenge_wither_boss_10] - title = "Dominateur du Nether" - description = "Vainquez le Wither 10 fois" +title = "Dominateur du Nether" +description = "Vainquez le Wither 10 fois" [advancement.challenge_roses_10] - title = "Jardinier de la Mort" - description = "Cassez 10 roses de Wither" +title = "Jardinier de la Mort" +description = "Cassez 10 roses de Wither" [advancement.challenge_roses_100] - title = "Collectionneur de Fleau" - description = "Cassez 100 roses de Wither" +title = "Collectionneur de Fleau" +description = "Cassez 100 roses de Wither" # Pickaxes [advancement.challenge_pick_swing_500] - title = "Bras de Mineur" - description = "Balancez votre pioche 500 fois" +title = "Bras de Mineur" +description = "Balancez votre pioche 500 fois" [advancement.challenge_pick_swing_5k] - title = "Creuseur de Tunnels" - description = "Balancez votre pioche 5.000 fois" +title = "Creuseur de Tunnels" +description = "Balancez votre pioche 5.000 fois" [advancement.challenge_pick_damage_1k] - title = "Combattant a la Pioche" - description = "Infligez 1.000 degats avec une pioche" +title = "Combattant a la Pioche" +description = "Infligez 1.000 degats avec une pioche" [advancement.challenge_pick_damage_10k] - title = "Pioche de Guerre" - description = "Infligez 10.000 degats avec une pioche" +title = "Pioche de Guerre" +description = "Infligez 10.000 degats avec une pioche" [advancement.challenge_pick_value_5k] - title = "Chercheur de Gemmes" - description = "Minez des blocs d'une valeur de 5.000" +title = "Chercheur de Gemmes" +description = "Minez des blocs d'une valeur de 5.000" [advancement.challenge_pick_value_50k] - title = "Baron du Minerai" - description = "Minez des blocs d'une valeur de 50.000" +title = "Baron du Minerai" +description = "Minez des blocs d'une valeur de 50.000" [advancement.challenge_pick_ores_500] - title = "Prospecteur" - description = "Minez 500 blocs de minerai" +title = "Prospecteur" +description = "Minez 500 blocs de minerai" [advancement.challenge_pick_ores_5k] - title = "Maitre Mineur" - description = "Minez 5.000 blocs de minerai" +title = "Maitre Mineur" +description = "Minez 5.000 blocs de minerai" # Ranged [advancement.challenge_ranged_dmg_1k] - title = "Tireur d'Elite" - description = "Infligez 1.000 degats a distance" +title = "Tireur d'Elite" +description = "Infligez 1.000 degats a distance" [advancement.challenge_ranged_dmg_10k] - title = "Archer Mortel" - description = "Infligez 10.000 degats a distance" +title = "Archer Mortel" +description = "Infligez 10.000 degats a distance" [advancement.challenge_ranged_dist_5k] - title = "Longue Portee" - description = "Tirez des projectiles couvrant 5.000 blocs de distance totale" +title = "Longue Portee" +description = "Tirez des projectiles couvrant 5.000 blocs de distance totale" [advancement.challenge_ranged_dist_50k] - title = "Tireur au Kilometre" - description = "Tirez des projectiles couvrant 50.000 blocs de distance totale" +title = "Tireur au Kilometre" +description = "Tirez des projectiles couvrant 50.000 blocs de distance totale" [advancement.challenge_ranged_kills_50] - title = "Archer" - description = "Tuez 50 creatures avec des armes a distance" +title = "Archer" +description = "Tuez 50 creatures avec des armes a distance" [advancement.challenge_ranged_kills_500] - title = "Maitre Archer" - description = "Tuez 500 creatures avec des armes a distance" +title = "Maitre Archer" +description = "Tuez 500 creatures avec des armes a distance" [advancement.challenge_longshot_25] - title = "Tireur Embusque" - description = "Reussissez 25 tirs a longue portee (plus de 30 blocs)" +title = "Tireur Embusque" +description = "Reussissez 25 tirs a longue portee (plus de 30 blocs)" [advancement.challenge_longshot_250] - title = "Oeil d'Aigle" - description = "Reussissez 250 tirs a longue portee (plus de 30 blocs)" +title = "Oeil d'Aigle" +description = "Reussissez 250 tirs a longue portee (plus de 30 blocs)" # Rift [advancement.challenge_rift_pearls_50] - title = "Lanceur de Perles" - description = "Lancez 50 perles de l'Ender" +title = "Lanceur de Perles" +description = "Lancez 50 perles de l'Ender" [advancement.challenge_rift_pearls_500] - title = "Accro a la Teleportation" - description = "Lancez 500 perles de l'Ender" +title = "Accro a la Teleportation" +description = "Lancez 500 perles de l'Ender" [advancement.challenge_rift_enderman_50] - title = "Chasseur d'Endermen" - description = "Abattez 50 endermen" +title = "Chasseur d'Endermen" +description = "Abattez 50 endermen" [advancement.challenge_rift_enderman_500] - title = "Rodeur du Vide" - description = "Abattez 500 endermen" +title = "Rodeur du Vide" +description = "Abattez 500 endermen" [advancement.challenge_rift_dragon_500] - title = "Combattant de Dragon" - description = "Infligez 500 degats au Dragon de l'Ender" +title = "Combattant de Dragon" +description = "Infligez 500 degats au Dragon de l'Ender" [advancement.challenge_rift_dragon_5k] - title = "Fleau du Dragon" - description = "Infligez 5.000 degats au Dragon de l'Ender" +title = "Fleau du Dragon" +description = "Infligez 5.000 degats au Dragon de l'Ender" [advancement.challenge_rift_crystal_10] - title = "Briseur de Cristaux" - description = "Detruisez 10 cristaux de l'End" +title = "Briseur de Cristaux" +description = "Detruisez 10 cristaux de l'End" [advancement.challenge_rift_crystal_100] - title = "Demolisseur de l'End" - description = "Detruisez 100 cristaux de l'End" +title = "Demolisseur de l'End" +description = "Detruisez 100 cristaux de l'End" # Seaborne [advancement.challenge_fish_25] - title = "Pecheur" - description = "Attrapez 25 poissons" +title = "Pecheur" +description = "Attrapez 25 poissons" [advancement.challenge_fish_250] - title = "Maitre Pecheur" - description = "Attrapez 250 poissons" +title = "Maitre Pecheur" +description = "Attrapez 250 poissons" [advancement.challenge_drowned_25] - title = "Chasseur de Noyes" - description = "Abattez 25 noyes" +title = "Chasseur de Noyes" +description = "Abattez 25 noyes" [advancement.challenge_drowned_250] - title = "Nettoyeur de l'Ocean" - description = "Abattez 250 noyes" +title = "Nettoyeur de l'Ocean" +description = "Abattez 250 noyes" [advancement.challenge_guardian_10] - title = "Tueur de Gardiens" - description = "Abattez 10 gardiens" +title = "Tueur de Gardiens" +description = "Abattez 10 gardiens" [advancement.challenge_guardian_100] - title = "Pilleur de Temple" - description = "Abattez 100 gardiens" +title = "Pilleur de Temple" +description = "Abattez 100 gardiens" [advancement.challenge_underwater_blocks_100] - title = "Mineur Sous-Marin" - description = "Cassez 100 blocs sous l'eau" +title = "Mineur Sous-Marin" +description = "Cassez 100 blocs sous l'eau" [advancement.challenge_underwater_blocks_1k] - title = "Ingenieur Aquatique" - description = "Cassez 1.000 blocs sous l'eau" +title = "Ingenieur Aquatique" +description = "Cassez 1.000 blocs sous l'eau" # Stealth [advancement.challenge_stealth_dmg_500] - title = "Poignardeur" - description = "Infligez 500 degats en etant accroupi" +title = "Poignardeur" +description = "Infligez 500 degats en etant accroupi" [advancement.challenge_stealth_dmg_5k] - title = "Tueur Silencieux" - description = "Infligez 5.000 degats en etant accroupi" +title = "Tueur Silencieux" +description = "Infligez 5.000 degats en etant accroupi" [advancement.challenge_stealth_kills_10] - title = "Assassin" - description = "Tuez 10 creatures en etant accroupi" +title = "Assassin" +description = "Tuez 10 creatures en etant accroupi" [advancement.challenge_stealth_kills_100] - title = "Faucheur des Ombres" - description = "Tuez 100 creatures en etant accroupi" +title = "Faucheur des Ombres" +description = "Tuez 100 creatures en etant accroupi" [advancement.challenge_stealth_time_1h] - title = "Patient" - description = "Passez 1 heure accroupi (3.600 secondes)" +title = "Patient" +description = "Passez 1 heure accroupi (3.600 secondes)" [advancement.challenge_stealth_time_10h] - title = "Maitre des Ombres" - description = "Passez 10 heures accroupi (36.000 secondes)" +title = "Maitre des Ombres" +description = "Passez 10 heures accroupi (36.000 secondes)" [advancement.challenge_stealth_arrows_50] - title = "Archer Silencieux" - description = "Tirez 50 fleches en etant accroupi" +title = "Archer Silencieux" +description = "Tirez 50 fleches en etant accroupi" [advancement.challenge_stealth_arrows_500] - title = "Archer Fantome" - description = "Tirez 500 fleches en etant accroupi" +title = "Archer Fantome" +description = "Tirez 500 fleches en etant accroupi" # Swords [advancement.challenge_sword_dmg_1k] - title = "Apprenti de la Lame" - description = "Infligez 1.000 degats avec des epees" +title = "Apprenti de la Lame" +description = "Infligez 1.000 degats avec des epees" [advancement.challenge_sword_dmg_10k] - title = "Epeeiste" - description = "Infligez 10.000 degats avec des epees" +title = "Epeeiste" +description = "Infligez 10.000 degats avec des epees" [advancement.challenge_sword_kills_50] - title = "Duelliste" - description = "Tuez 50 creatures avec des epees" +title = "Duelliste" +description = "Tuez 50 creatures avec des epees" [advancement.challenge_sword_kills_500] - title = "Gladiateur" - description = "Tuez 500 creatures avec des epees" +title = "Gladiateur" +description = "Tuez 500 creatures avec des epees" [advancement.challenge_sword_crit_50] - title = "Frappeur Critique" - description = "Placez 50 coups critiques avec des epees" +title = "Frappeur Critique" +description = "Placez 50 coups critiques avec des epees" [advancement.challenge_sword_crit_500] - title = "Maitre de la Precision" - description = "Placez 500 coups critiques avec des epees" +title = "Maitre de la Precision" +description = "Placez 500 coups critiques avec des epees" [advancement.challenge_sword_heavy_25] - title = "Frappeur Lourd" - description = "Placez 25 coups lourds avec des epees (plus de 8 degats)" +title = "Frappeur Lourd" +description = "Placez 25 coups lourds avec des epees (plus de 8 degats)" [advancement.challenge_sword_heavy_250] - title = "Coup Devastateur" - description = "Placez 250 coups lourds avec des epees (plus de 8 degats)" +title = "Coup Devastateur" +description = "Placez 250 coups lourds avec des epees (plus de 8 degats)" # Taming [advancement.challenge_pet_dmg_500] - title = "Dresseur de Betes" - description = "Vos animaux infligent 500 degats au total" +title = "Dresseur de Betes" +description = "Vos animaux infligent 500 degats au total" [advancement.challenge_pet_dmg_5k] - title = "Maitre de Guerre" - description = "Vos animaux infligent 5.000 degats au total" +title = "Maitre de Guerre" +description = "Vos animaux infligent 5.000 degats au total" [advancement.challenge_tamed_10] - title = "Ami des Animaux" - description = "Apprivoisez 10 animaux" +title = "Ami des Animaux" +description = "Apprivoisez 10 animaux" [advancement.challenge_tamed_100] - title = "Gardien de Zoo" - description = "Apprivoisez 100 animaux" +title = "Gardien de Zoo" +description = "Apprivoisez 100 animaux" [advancement.challenge_pet_kills_25] - title = "Tactique de Meute" - description = "Vos animaux abattent 25 creatures" +title = "Tactique de Meute" +description = "Vos animaux abattent 25 creatures" [advancement.challenge_pet_kills_250] - title = "Commandant Alpha" - description = "Vos animaux abattent 250 creatures" +title = "Commandant Alpha" +description = "Vos animaux abattent 250 creatures" [advancement.challenge_taming_2500] - title = "Expert en Elevage" - description = "Elevez 2.500 animaux" +title = "Expert en Elevage" +description = "Elevez 2.500 animaux" [advancement.challenge_taming_25k] - title = "Maitre Geneticien" - description = "Elevez 25.000 animaux" +title = "Maitre Geneticien" +description = "Elevez 25.000 animaux" # TragOul [advancement.challenge_trag_hits_500] - title = "Sac de Frappe" - description = "Recevez 500 coups" +title = "Sac de Frappe" +description = "Recevez 500 coups" [advancement.challenge_trag_hits_5k] - title = "Masochiste" - description = "Recevez 5.000 coups" +title = "Masochiste" +description = "Recevez 5.000 coups" [advancement.challenge_trag_deaths_10] - title = "Neuf Vies" - description = "Mourez 10 fois" +title = "Neuf Vies" +description = "Mourez 10 fois" [advancement.challenge_trag_deaths_100] - title = "Cauchemar Recurrent" - description = "Mourez 100 fois" +title = "Cauchemar Recurrent" +description = "Mourez 100 fois" [advancement.challenge_trag_fire_500] - title = "Victime du Feu" - description = "Endurez 500 degats de feu" +title = "Victime du Feu" +description = "Endurez 500 degats de feu" [advancement.challenge_trag_fire_5k] - title = "Phenix" - description = "Endurez 5.000 degats de feu" +title = "Phenix" +description = "Endurez 5.000 degats de feu" [advancement.challenge_trag_fall_500] - title = "Test de Gravite" - description = "Endurez 500 degats de chute" +title = "Test de Gravite" +description = "Endurez 500 degats de chute" [advancement.challenge_trag_fall_5k] - title = "Vitesse Terminale" - description = "Endurez 5.000 degats de chute" +title = "Vitesse Terminale" +description = "Endurez 5.000 degats de chute" # Unarmed [advancement.challenge_unarmed_dmg_1k] - title = "Bagarreur" - description = "Infligez 1.000 degats a mains nues" +title = "Bagarreur" +description = "Infligez 1.000 degats a mains nues" [advancement.challenge_unarmed_dmg_10k] - title = "Artiste Martial" - description = "Infligez 10.000 degats a mains nues" +title = "Artiste Martial" +description = "Infligez 10.000 degats a mains nues" [advancement.challenge_unarmed_kills_25] - title = "Poings Nus" - description = "Tuez 25 creatures a mains nues" +title = "Poings Nus" +description = "Tuez 25 creatures a mains nues" [advancement.challenge_unarmed_kills_250] - title = "Poing de Legende" - description = "Tuez 250 creatures a mains nues" +title = "Poing de Legende" +description = "Tuez 250 creatures a mains nues" [advancement.challenge_unarmed_crit_25] - title = "Coup de Poing Critique" - description = "Placez 25 coups critiques a mains nues" +title = "Coup de Poing Critique" +description = "Placez 25 coups critiques a mains nues" [advancement.challenge_unarmed_crit_250] - title = "Poing de Precision" - description = "Placez 250 coups critiques a mains nues" +title = "Poing de Precision" +description = "Placez 250 coups critiques a mains nues" [advancement.challenge_unarmed_heavy_25] - title = "Coup de Poing Puissant" - description = "Placez 25 coups lourds a mains nues (plus de 6 degats)" +title = "Coup de Poing Puissant" +description = "Placez 25 coups lourds a mains nues (plus de 6 degats)" [advancement.challenge_unarmed_heavy_250] - title = "Roi du Knockout" - description = "Placez 250 coups lourds a mains nues (plus de 6 degats)" +title = "Roi du Knockout" +description = "Placez 250 coups lourds a mains nues (plus de 6 degats)" # items [items] [items.bound_ender_peral] - name = "Portoloin reliquaire" - usage1 = "Maj + Clic gauche pour lier" - usage2 = "Clic droit pour acceder a l'inventaire lie" +name = "Portoloin reliquaire" +usage1 = "Maj + Clic gauche pour lier" +usage2 = "Clic droit pour acceder a l'inventaire lie" [items.bound_eye_of_ender] - name = "Ancre oculaire" - usage1 = "Clic droit pour consommer et se teleporter a l'emplacement lie" - usage2 = "Maj + Clic gauche pour lier a un bloc" +name = "Ancre oculaire" +usage1 = "Clic droit pour consommer et se teleporter a l'emplacement lie" +usage2 = "Maj + Clic gauche pour lier a un bloc" [items.bound_redstone_torch] - name = "Telecommande Redstone" - usage1 = "Clic droit pour creer une impulsion Redstone d'1 tick" - usage2 = "Maj + Clic gauche sur un bloc 'Cible' pour lier" +name = "Telecommande Redstone" +usage1 = "Clic droit pour creer une impulsion Redstone d'1 tick" +usage2 = "Maj + Clic gauche sur un bloc 'Cible' pour lier" [items.bound_snowball] - name = "Piege de toile !" - usage1 = "Lancez pour creer un piege de toile temporaire a cet emplacement" +name = "Piege de toile !" +usage1 = "Lancez pour creer un piege de toile temporaire a cet emplacement" [items.chrono_time_bottle] - name = "Temps en bouteille" - usage1 = "Stocke passivement du temps dans votre inventaire" - usage2 = "Clic droit sur des blocs temporises ou des bebes animaux pour depenser le temps stocke" - stored = "Temps stocke" +name = "Temps en bouteille" +usage1 = "Stocke passivement du temps dans votre inventaire" +usage2 = "Clic droit sur des blocs temporises ou des bebes animaux pour depenser le temps stocke" +stored = "Temps stocke" [items.chrono_time_bomb] - name = "Bombe temporelle" - usage1 = "Clic droit pour lancer un projectile chrono qui cree un champ temporel" +name = "Bombe temporelle" +usage1 = "Clic droit pour lancer un projectile chrono qui cree un champ temporel" [items.elevator_block] - name = "Bloc d'ascenseur" - usage1 = "Sautez pour vous teleporter vers le haut" - usage2 = "Accroupissez-vous pour vous teleporter vers le bas" - usage3 = "Minimum de 2 blocs d'air entre les ascenseurs" +name = "Bloc d'ascenseur" +usage1 = "Sautez pour vous teleporter vers le haut" +usage2 = "Accroupissez-vous pour vous teleporter vers le bas" +usage3 = "Minimum de 2 blocs d'air entre les ascenseurs" # snippets [snippets] [snippets.gui] - level = "Niveau" - knowledge = "connaissance" - power_used = "Puissance utilisee" - not_learned = "Non appris" - xp = "XP pour" - welcome = "Bienvenue !" - welcome_back = "Content de vous revoir !" - xp_bonus_for_time = "XP pour" - max_ability_power = "Puissance de capacite maximale" - unlock_this_by_clicking = "Debloquez ceci par Clic droit : " - back = "Retour" - unlearn_all = "Tout desapprendre" - unlearned_all = "Tout desappris" +level = "Niveau" +knowledge = "connaissance" +power_used = "Puissance utilisee" +not_learned = "Non appris" +xp = "XP pour" +welcome = "Bienvenue !" +welcome_back = "Content de vous revoir !" +xp_bonus_for_time = "XP pour" +max_ability_power = "Puissance de capacite maximale" +unlock_this_by_clicking = "Debloquez ceci par Clic droit : " +back = "Retour" +unlearn_all = "Tout desapprendre" +unlearned_all = "Tout desappris" [snippets.adapt_menu] - may_not_unlearn = "VOUS NE POUVEZ PAS DESAPPRENDRE" - may_unlearn = "VOUS POUVEZ APPRENDRE/DESAPPRENDRE" - knowledge_cost = "Cout en connaissance" - knowledge_available = "Connaissance disponible" - already_learned = "Deja appris" - unlearn_refund = "Cliquez pour desapprendre et rembourser" - no_refunds = "HARDCORE, REMBOURSEMENTS DESACTIVES" - knowledge = "connaissance" - click_learn = "Cliquez pour apprendre" - no_knowledge = "(Vous n'avez aucune connaissance)" - you_only_have = "Vous n'avez que" - how_to_level_up = "Montez vos competences en niveau pour augmenter votre puissance maximale." - not_enough_power = "Pas assez de puissance ! Chaque niveau de capacite coute 1 puissance." - power = "puissance" - power_drain = "Drain de puissance" - learned = "Appris " - unlearned = "Desappris " - activator_block = "Bibliotheque" +may_not_unlearn = "VOUS NE POUVEZ PAS DESAPPRENDRE" +may_unlearn = "VOUS POUVEZ APPRENDRE/DESAPPRENDRE" +knowledge_cost = "Cout en connaissance" +knowledge_available = "Connaissance disponible" +already_learned = "Deja appris" +unlearn_refund = "Cliquez pour desapprendre et rembourser" +no_refunds = "HARDCORE, REMBOURSEMENTS DESACTIVES" +knowledge = "connaissance" +click_learn = "Cliquez pour apprendre" +no_knowledge = "(Vous n'avez aucune connaissance)" +you_only_have = "Vous n'avez que" +how_to_level_up = "Montez vos competences en niveau pour augmenter votre puissance maximale." +not_enough_power = "Pas assez de puissance ! Chaque niveau de capacite coute 1 puissance." +power = "puissance" +power_drain = "Drain de puissance" +learned = "Appris " +unlearned = "Desappris " +activator_block = "Bibliotheque" [snippets.knowledge_orb] - contains = "contient" - knowledge = "connaissance" - rightclick = "Clic droit" - togainknowledge = "pour obtenir cette connaissance" - knowledge_orb = "Orbe de connaissance" +contains = "contient" +knowledge = "connaissance" +rightclick = "Clic droit" +togainknowledge = "pour obtenir cette connaissance" +knowledge_orb = "Orbe de connaissance" [snippets.experience_orb] - contains = "contient" - xp = "Experience" - rightclick = "Clic droit" - togainxp = "pour obtenir cette experience" - xporb = "Orbe d'experience" +contains = "contient" +xp = "Experience" +rightclick = "Clic droit" +togainxp = "pour obtenir cette experience" +xporb = "Orbe d'experience" # skill [skill] [skill.agility] - name = "Agilite" - icon = "⇉" - description = "L'agilite est la capacite a se deplacer rapidement et avec fluidite face aux obstacles." +name = "Agilite" +icon = "⇉" +description = "L'agilite est la capacite a se deplacer rapidement et avec fluidite face aux obstacles." [skill.architect] - name = "Architecte" - icon = "⬧" - description = "Les structures sont les fondations du monde. La realite est entre vos mains, a vous de la controler." +name = "Architecte" +icon = "⬧" +description = "Les structures sont les fondations du monde. La realite est entre vos mains, a vous de la controler." [skill.axes] - name = "Haches" - icon = "🪓" - description1 = "Pourquoi abattre des arbres, quand on peut abattre des " - description2 = "choses" - description3 = "a la place, meme resultat !" +name = "Haches" +icon = "🪓" +description1 = "Pourquoi abattre des arbres, quand on peut abattre des " +description2 = "choses" +description3 = "a la place, meme resultat !" [skill.brewing] - name = "Alchimie" - icon = "❦" - description = "Double bulle, triple bulle, quadruple bulle - je n'arrive toujours pas a mettre cette potion dans un chaudron" +name = "Alchimie" +icon = "❦" +description = "Double bulle, triple bulle, quadruple bulle - je n'arrive toujours pas a mettre cette potion dans un chaudron" [skill.blocking] - name = "Blocage" - icon = "🛡" - description = "Les batons et les cailloux ne casseront pas vos os, mais un bouclier, si." +name = "Blocage" +icon = "🛡" +description = "Les batons et les cailloux ne casseront pas vos os, mais un bouclier, si." [skill.crafting] - name = "Artisanat" - icon = "⌂" - description = "Plus de pieces a placer ? Pourquoi ne pas en fabriquer d'autres ?" +name = "Artisanat" +icon = "⌂" +description = "Plus de pieces a placer ? Pourquoi ne pas en fabriquer d'autres ?" [skill.discovery] - name = "Decouverte" - icon = "⚛" - description = "A mesure que votre perception s'elargit, votre esprit se devoile pour decouvrir ce que vous ignoriez." +name = "Decouverte" +icon = "⚛" +description = "A mesure que votre perception s'elargit, votre esprit se devoile pour decouvrir ce que vous ignoriez." [skill.enchanting] - name = "Enchantement" - icon = "♰" - description = "De quoi parlez-vous ? Propheties, visions, superstitions en tout genre ?" +name = "Enchantement" +icon = "♰" +description = "De quoi parlez-vous ? Propheties, visions, superstitions en tout genre ?" [skill.excavation] - name = "Excavation" - icon = "ᛳ" - description = "On creuse, on creuse un trou..." +name = "Excavation" +icon = "ᛳ" +description = "On creuse, on creuse un trou..." [skill.herbalism] - name = "Herboristerie" - icon = "⚘" - description = "Je ne trouve aucune plante, mais je trouve des graines et... c'est de la mauvaise herbe ?" +name = "Herboristerie" +icon = "⚘" +description = "Je ne trouve aucune plante, mais je trouve des graines et... c'est de la mauvaise herbe ?" [skill.hunter] - name = "Chasseur" - icon = "☠" - description = "La chasse, c'est le voyage, pas le resultat." +name = "Chasseur" +icon = "☠" +description = "La chasse, c'est le voyage, pas le resultat." [skill.nether] - name = "Nether" - icon = "₪" - description = "Des profondeurs du Nether lui-meme." +name = "Nether" +icon = "₪" +description = "Des profondeurs du Nether lui-meme." [skill.pickaxe] - name = "Pioche" - icon = "⛏" - description = "Les nains sont les mineurs, mais j'ai appris un truc ou deux avec le temps. JE SUIS SUEDOIS" +name = "Pioche" +icon = "⛏" +description = "Les nains sont les mineurs, mais j'ai appris un truc ou deux avec le temps. JE SUIS SUEDOIS" [skill.ranged] - name = "A distance" - icon = "🏹" - description = "La distance est la cle de la victoire, et la cle de la survie." +name = "A distance" +icon = "🏹" +description = "La distance est la cle de la victoire, et la cle de la survie." [skill.rift] - name = "Faille" - icon = "❍" - description = "La Faille est un harnais caustique, mais vous avez dompte le harnais." +name = "Faille" +icon = "❍" +description = "La Faille est un harnais caustique, mais vous avez dompte le harnais." [skill.seaborne] - name = "Maritime" - icon = "🎣" - description = "Avec cette competence, vous pourrez dompter les merveilles de l'eau." +name = "Maritime" +icon = "🎣" +description = "Avec cette competence, vous pourrez dompter les merveilles de l'eau." [skill.stealth] - name = "Furtivite" - icon = "☯" - description = "L'art de l'invisible. Marchez dans les ombres." +name = "Furtivite" +icon = "☯" +description = "L'art de l'invisible. Marchez dans les ombres." [skill.swords] - name = "Epees" - icon = "⚔" - description = "Par le pouvoir de GreyStone !" +name = "Epees" +icon = "⚔" +description = "Par le pouvoir de GreyStone !" [skill.taming] - name = "Apprivoisement" - icon = "♥" - description = "Les perroquets et les abeilles... et vous ?" +name = "Apprivoisement" +icon = "♥" +description = "Les perroquets et les abeilles... et vous ?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Le sang coule dans les veines de l'univers. Contraint par vos mains." +name = "TragOul" +icon = "🗡" +description = "Le sang coule dans les veines de l'univers. Contraint par vos mains." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Remontez l'horloge de l'univers, vivez le flux. Brisez l'horloge, devenez-la." +name = "Chronos" +icon = "🕒" +description = "Remontez l'horloge de l'univers, vivez le flux. Brisez l'horloge, devenez-la." [skill.unarmed] - name = "Mains nues" - icon = "»" - description = "Sans arme ne veut pas dire sans force." +name = "Mains nues" +icon = "»" +description = "Sans arme ne veut pas dire sans force." # agility [agility] [agility.armor_up] - name = "Armure renforcee" - description = "Gagnez plus d'armure plus vous sprintez longtemps !" - lore1 = "Armure maximale" - lore2 = "Temps de montee en armure" - lore = ["Armure maximale", "Temps de montee en armure"] +name = "Armure renforcee" +description = "Gagnez plus d'armure plus vous sprintez longtemps !" +lore1 = "Armure maximale" +lore2 = "Temps de montee en armure" +lore = ["Armure maximale", "Temps de montee en armure"] [agility.ladder_slide] - name = "Glissade sur echelle" - description = "Grimpez et glissez sur les echelles beaucoup plus vite dans les deux directions." - lore1 = "Multiplicateur de vitesse sur echelle" - lore2 = "Vitesse de descente rapide" - lore = ["Multiplicateur de vitesse sur echelle", "Vitesse de descente rapide"] +name = "Glissade sur echelle" +description = "Grimpez et glissez sur les echelles beaucoup plus vite dans les deux directions." +lore1 = "Multiplicateur de vitesse sur echelle" +lore2 = "Vitesse de descente rapide" +lore = ["Multiplicateur de vitesse sur echelle", "Vitesse de descente rapide"] [agility.super_jump] - name = "Super saut" - description = "Avantage de hauteur exceptionnel." - lore1 = "Hauteur de saut maximale" - lore2 = "Accroupi + Saut pour faire un Super Saut !" - lore = ["Hauteur de saut maximale", "Accroupi + Saut pour faire un Super Saut !"] +name = "Super saut" +description = "Avantage de hauteur exceptionnel." +lore1 = "Hauteur de saut maximale" +lore2 = "Accroupi + Saut pour faire un Super Saut !" +lore = ["Hauteur de saut maximale", "Accroupi + Saut pour faire un Super Saut !"] [agility.wall_jump] - name = "Saut mural" - description = "Maintenez Maj en l'air contre un mur pour vous y accrocher et sauter !" - lore1 = "Sauts maximum" - lore2 = "Hauteur du saut" - lore = ["Sauts maximum", "Hauteur du saut"] +name = "Saut mural" +description = "Maintenez Maj en l'air contre un mur pour vous y accrocher et sauter !" +lore1 = "Sauts maximum" +lore2 = "Hauteur du saut" +lore = ["Sauts maximum", "Hauteur du saut"] [agility.wind_up] - name = "Montee en puissance" - description = "Accelerez plus vous sprintez longtemps !" - lore1 = "Vitesse maximale" - lore2 = "Temps de montee en puissance" - lore = ["Vitesse maximale", "Temps de montee en puissance"] +name = "Montee en puissance" +description = "Accelerez plus vous sprintez longtemps !" +lore1 = "Vitesse maximale" +lore2 = "Temps de montee en puissance" +lore = ["Vitesse maximale", "Temps de montee en puissance"] # architect [architect] [architect.elevator] - name = "Ascenseur" - description = "Cela vous permet de construire un ascenseur pour vous teleporter verticalement rapidement !" - lore1 = "Debloque la recette d'ascenseur : X=LAINE, Y=PERLE DE L'ENDER" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Debloque la recette d'ascenseur : X=LAINE, Y=PERLE DE L'ENDER", "XXX", "XYX", "XXX"] +name = "Ascenseur" +description = "Cela vous permet de construire un ascenseur pour vous teleporter verticalement rapidement !" +lore1 = "Debloque la recette d'ascenseur : X=LAINE, Y=PERLE DE L'ENDER" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Debloque la recette d'ascenseur : X=LAINE, Y=PERLE DE L'ENDER", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Fondation magique" - description = "Cela vous permet de vous accroupir et de placer une fondation temporaire sous vous !" - lore1 = "Creez magiquement : " - lore2 = "Blocs sous vous !" - lore = ["Creez magiquement : ", "Blocs sous vous !"] +name = "Fondation magique" +description = "Cela vous permet de vous accroupir et de placer une fondation temporaire sous vous !" +lore1 = "Creez magiquement : " +lore2 = "Blocs sous vous !" +lore = ["Creez magiquement : ", "Blocs sous vous !"] [architect.glass] - name = "Verre toucher de soie" - description = "Cela vous permet d'eviter la perte de blocs de verre lorsque vous les cassez a main nue !" - lore1 = "Vos mains obtiennent Toucher de soie pour le verre" - lore = ["Vos mains obtiennent Toucher de soie pour le verre"] +name = "Verre toucher de soie" +description = "Cela vous permet d'eviter la perte de blocs de verre lorsque vous les cassez a main nue !" +lore1 = "Vos mains obtiennent Toucher de soie pour le verre" +lore = ["Vos mains obtiennent Toucher de soie pour le verre"] [architect.wireless_redstone] - name = "Telecommande Redstone" - description = "Cela vous permet d'utiliser une torche de redstone pour activer la redstone a distance !" - lore1 = "Cible + Torche de Redstone + Perle de l'Ender = 1 Telecommande Redstone" - lore = ["Cible + Torche de Redstone + Perle de l'Ender = 1 Telecommande Redstone"] +name = "Telecommande Redstone" +description = "Cela vous permet d'utiliser une torche de redstone pour activer la redstone a distance !" +lore1 = "Cible + Torche de Redstone + Perle de l'Ender = 1 Telecommande Redstone" +lore = ["Cible + Torche de Redstone + Perle de l'Ender = 1 Telecommande Redstone"] [architect.placement] - name = "Baguette du batisseur" - description = "Vous permet de placer plusieurs blocs a la fois ! Accroupissez-vous, tenez un bloc identique a celui que vous visez et placez ! Vous devrez peut-etre bouger un peu pour declencher la delimitation des zones !" - lore1 = "Vous avez besoin de" - lore2 = "blocs dans votre main pour placer ceci" - lore3 = "Une baguette de batisseur en materiaux" - lore = ["Vous avez besoin de", "blocs dans votre main pour placer ceci", "Une baguette de batisseur en materiaux"] +name = "Baguette du batisseur" +description = "Vous permet de placer plusieurs blocs a la fois ! Accroupissez-vous, tenez un bloc identique a celui que vous visez et placez ! Vous devrez peut-etre bouger un peu pour declencher la delimitation des zones !" +lore1 = "Vous avez besoin de" +lore2 = "blocs dans votre main pour placer ceci" +lore3 = "Une baguette de batisseur en materiaux" +lore = ["Vous avez besoin de", "blocs dans votre main pour placer ceci", "Une baguette de batisseur en materiaux"] # axe [axe] [axe.chop] - name = "Coup de hache" - description = "Abattez des arbres en faisant un clic droit sur la buche de base !" - lore1 = "Blocs par coup" - lore2 = "Temps de recharge du coup" - lore3 = "Usure de l'outil" - lore = ["Blocs par coup", "Temps de recharge du coup", "Usure de l'outil"] +name = "Coup de hache" +description = "Abattez des arbres en faisant un clic droit sur la buche de base !" +lore1 = "Blocs par coup" +lore2 = "Temps de recharge du coup" +lore3 = "Usure de l'outil" +lore = ["Blocs par coup", "Temps de recharge du coup", "Usure de l'outil"] [axe.log_swap] - name = "Echangeur de buches de Lucy" - description = "Changez le type de buches dans une table d'artisanat !" - lore1 = "8 buches de n'importe quel type + 1 pousse = 8 buches du type de la pousse" - lore = ["8 buches de n'importe quel type + 1 pousse = 8 buches du type de la pousse"] +name = "Echangeur de buches de Lucy" +description = "Changez le type de buches dans une table d'artisanat !" +lore1 = "8 buches de n'importe quel type + 1 pousse = 8 buches du type de la pousse" +lore = ["8 buches de n'importe quel type + 1 pousse = 8 buches du type de la pousse"] [axe.drop_to_inventory] - name = "Hache : depot direct en inventaire" +name = "Hache : depot direct en inventaire" [axe.ground_smash] - name = "Frappe au sol a la hache" - description = "Sautez, puis accroupissez-vous et ecrasez tous les ennemis proches." - lore1 = "Degats" - lore2 = "Rayon en blocs" - lore3 = "Force" - lore4 = "Temps de recharge de la frappe" - lore = ["Degats", "Rayon en blocs", "Force", "Temps de recharge de la frappe"] +name = "Frappe au sol a la hache" +description = "Sautez, puis accroupissez-vous et ecrasez tous les ennemis proches." +lore1 = "Degats" +lore2 = "Rayon en blocs" +lore3 = "Force" +lore4 = "Temps de recharge de la frappe" +lore = ["Degats", "Rayon en blocs", "Force", "Temps de recharge de la frappe"] [axe.leaf_miner] - name = "Mineur de feuilles" - description = "Vous permet de casser des feuilles en masse !" - lore1 = "Accroupissez-vous et minez les FEUILLES" - lore2 = "portee du minage de feuilles" - lore3 = "Vous n'obtiendrez pas les drops des feuilles (prevention d'exploit)" - lore = ["Accroupissez-vous et minez les FEUILLES", "portee du minage de feuilles", "Vous n'obtiendrez pas les drops des feuilles (prevention d'exploit)"] +name = "Mineur de feuilles" +description = "Vous permet de casser des feuilles en masse !" +lore1 = "Accroupissez-vous et minez les FEUILLES" +lore2 = "portee du minage de feuilles" +lore3 = "Vous n'obtiendrez pas les drops des feuilles (prevention d'exploit)" +lore = ["Accroupissez-vous et minez les FEUILLES", "portee du minage de feuilles", "Vous n'obtiendrez pas les drops des feuilles (prevention d'exploit)"] [axe.wood_miner] - name = "Mineur de bois" - description = "Vous permet de casser du bois en masse !" - lore1 = "Accroupissez-vous et minez le BOIS/BUCHES (pas les planches)" - lore2 = "portee du minage de bois" - lore3 = "Fonctionne avec le depot direct en inventaire" - lore = ["Accroupissez-vous et minez le BOIS/BUCHES (pas les planches)", "portee du minage de bois", "Fonctionne avec le depot direct en inventaire"] +name = "Mineur de bois" +description = "Vous permet de casser du bois en masse !" +lore1 = "Accroupissez-vous et minez le BOIS/BUCHES (pas les planches)" +lore2 = "portee du minage de bois" +lore3 = "Fonctionne avec le depot direct en inventaire" +lore = ["Accroupissez-vous et minez le BOIS/BUCHES (pas les planches)", "portee du minage de bois", "Fonctionne avec le depot direct en inventaire"] # brewing [brewing] [brewing.lingering] - name = "Infusion persistante" - description = "Les potions que vous preparez durent plus longtemps !" - lore1 = "Duree" - lore2 = "Duree" - lore = ["Duree", "Duree"] +name = "Infusion persistante" +description = "Les potions que vous preparez durent plus longtemps !" +lore1 = "Duree" +lore2 = "Duree" +lore = ["Duree", "Duree"] [brewing.super_heated] - name = "Infusion surchauffee" - description = "Les alambics fonctionnent plus vite plus ils sont chauds." - lore1 = "Par bloc de feu adjacent" - lore2 = "Par bloc de lave adjacent" - lore = ["Par bloc de feu adjacent", "Par bloc de lave adjacent"] +name = "Infusion surchauffee" +description = "Les alambics fonctionnent plus vite plus ils sont chauds." +lore1 = "Par bloc de feu adjacent" +lore2 = "Par bloc de lave adjacent" +lore = ["Par bloc de feu adjacent", "Par bloc de lave adjacent"] [brewing.darkness] - name = "Tenebres en bouteille" - description = "On ne sait pas pourquoi vous en avez besoin, mais voila !" - lore1 = "Potion de vision nocturne + Beton noir = Potion de tenebres (30 secondes)" - lore2 = "A noter que cela empeche l'utilisateur de sprinter !" - lore = ["Potion de vision nocturne + Beton noir = Potion de tenebres (30 secondes)", "A noter que cela empeche l'utilisateur de sprinter !"] +name = "Tenebres en bouteille" +description = "On ne sait pas pourquoi vous en avez besoin, mais voila !" +lore1 = "Potion de vision nocturne + Beton noir = Potion de tenebres (30 secondes)" +lore2 = "A noter que cela empeche l'utilisateur de sprinter !" +lore = ["Potion de vision nocturne + Beton noir = Potion de tenebres (30 secondes)", "A noter que cela empeche l'utilisateur de sprinter !"] [brewing.haste] - name = "Hate en bouteille" - description = "Quand l'efficacite ne suffit pas" - lore1 = "Potion de vitesse + Eclat d'amethyste = Potion de hate (60 secondes)" - lore2 = "Potion de vitesse + Bloc d'amethyste = Potion de hate-2 (30 secondes)" - lore = ["Potion de vitesse + Eclat d'amethyste = Potion de hate (60 secondes)", "Potion de vitesse + Bloc d'amethyste = Potion de hate-2 (30 secondes)"] +name = "Hate en bouteille" +description = "Quand l'efficacite ne suffit pas" +lore1 = "Potion de vitesse + Eclat d'amethyste = Potion de hate (60 secondes)" +lore2 = "Potion de vitesse + Bloc d'amethyste = Potion de hate-2 (30 secondes)" +lore = ["Potion de vitesse + Eclat d'amethyste = Potion de hate (60 secondes)", "Potion de vitesse + Bloc d'amethyste = Potion de hate-2 (30 secondes)"] [brewing.absorption] - name = "Absorption en bouteille" - description = "Renforcez le corps !" - lore1 = "Soin instantane + Quartz = Potion d'absorption (60 secondes)" - lore2 = "Soin instantane + Bloc de quartz = Potion d'absorption-2 (30 secondes)" - lore = ["Soin instantane + Quartz = Potion d'absorption (60 secondes)", "Soin instantane + Bloc de quartz = Potion d'absorption-2 (30 secondes)"] +name = "Absorption en bouteille" +description = "Renforcez le corps !" +lore1 = "Soin instantane + Quartz = Potion d'absorption (60 secondes)" +lore2 = "Soin instantane + Bloc de quartz = Potion d'absorption-2 (30 secondes)" +lore = ["Soin instantane + Quartz = Potion d'absorption (60 secondes)", "Soin instantane + Bloc de quartz = Potion d'absorption-2 (30 secondes)"] [brewing.fatigue] - name = "Fatigue en bouteille" - description = "Affaiblissez le corps !" - lore1 = "Potion de faiblesse + Boule de slime = Potion de fatigue (30 secondes)" - lore2 = "Potion de faiblesse + Bloc de slime = Potion de fatigue-2 (15 secondes)" - lore = ["Potion de faiblesse + Boule de slime = Potion de fatigue (30 secondes)", "Potion de faiblesse + Bloc de slime = Potion de fatigue-2 (15 secondes)"] +name = "Fatigue en bouteille" +description = "Affaiblissez le corps !" +lore1 = "Potion de faiblesse + Boule de slime = Potion de fatigue (30 secondes)" +lore2 = "Potion de faiblesse + Bloc de slime = Potion de fatigue-2 (15 secondes)" +lore = ["Potion de faiblesse + Boule de slime = Potion de fatigue (30 secondes)", "Potion de faiblesse + Bloc de slime = Potion de fatigue-2 (15 secondes)"] [brewing.hunger] - name = "Faim en bouteille" - description = "Nourrissez l'insatiable !" - lore1 = "Potion bizarre + Chair putrefiee = Potion de faim (30 secondes)" - lore2 = "Potion de faiblesse + Chair putrefiee = Potion de faim-3 (15 secondes)" - lore = ["Potion bizarre + Chair putrefiee = Potion de faim (30 secondes)", "Potion de faiblesse + Chair putrefiee = Potion de faim-3 (15 secondes)"] +name = "Faim en bouteille" +description = "Nourrissez l'insatiable !" +lore1 = "Potion bizarre + Chair putrefiee = Potion de faim (30 secondes)" +lore2 = "Potion de faiblesse + Chair putrefiee = Potion de faim-3 (15 secondes)" +lore = ["Potion bizarre + Chair putrefiee = Potion de faim (30 secondes)", "Potion de faiblesse + Chair putrefiee = Potion de faim-3 (15 secondes)"] [brewing.nausea] - name = "Nausee en bouteille" - description = "Vous me rendez malade !" - lore1 = "Potion bizarre + Champignon brun = Potion de nausee (16 secondes)" - lore2 = "Potion bizarre + Champignon cramoisi = Potion de nausee-2 (8 secondes)" - lore = ["Potion bizarre + Champignon brun = Potion de nausee (16 secondes)", "Potion bizarre + Champignon cramoisi = Potion de nausee-2 (8 secondes)"] +name = "Nausee en bouteille" +description = "Vous me rendez malade !" +lore1 = "Potion bizarre + Champignon brun = Potion de nausee (16 secondes)" +lore2 = "Potion bizarre + Champignon cramoisi = Potion de nausee-2 (8 secondes)" +lore = ["Potion bizarre + Champignon brun = Potion de nausee (16 secondes)", "Potion bizarre + Champignon cramoisi = Potion de nausee-2 (8 secondes)"] [brewing.blindness] - name = "Cecite en bouteille" - description = "Vous etes une personne horrible..." - lore1 = "Potion bizarre + Poche d'encre = Potion de cecite (30 secondes)" - lore2 = "Potion bizarre + Poche d'encre lumineuse = Potion de cecite-2 (15 secondes)" - lore = ["Potion bizarre + Poche d'encre = Potion de cecite (30 secondes)", "Potion bizarre + Poche d'encre lumineuse = Potion de cecite-2 (15 secondes)"] +name = "Cecite en bouteille" +description = "Vous etes une personne horrible..." +lore1 = "Potion bizarre + Poche d'encre = Potion de cecite (30 secondes)" +lore2 = "Potion bizarre + Poche d'encre lumineuse = Potion de cecite-2 (15 secondes)" +lore = ["Potion bizarre + Poche d'encre = Potion de cecite (30 secondes)", "Potion bizarre + Poche d'encre lumineuse = Potion de cecite-2 (15 secondes)"] [brewing.resistance] - name = "Resistance en bouteille" - description = "La fortification a son summum !" - lore1 = "Potion bizarre + Lingot de fer = Potion de resistance (60 secondes)" - lore2 = "Potion bizarre + Bloc de fer = Potion de resistance-2 (30 secondes)" - lore = ["Potion bizarre + Lingot de fer = Potion de resistance (60 secondes)", "Potion bizarre + Bloc de fer = Potion de resistance-2 (30 secondes)"] +name = "Resistance en bouteille" +description = "La fortification a son summum !" +lore1 = "Potion bizarre + Lingot de fer = Potion de resistance (60 secondes)" +lore2 = "Potion bizarre + Bloc de fer = Potion de resistance-2 (30 secondes)" +lore = ["Potion bizarre + Lingot de fer = Potion de resistance (60 secondes)", "Potion bizarre + Bloc de fer = Potion de resistance-2 (30 secondes)"] [brewing.health_boost] - name = "Vie en bouteille" - description = "Quand la sante maximale ne suffit pas..." - lore1 = "Potion de soin instantane + Pomme doree = Potion de boost de sante (120 secondes)" - lore2 = "Potion de soin instantane + Pomme doree enchantee = Potion de boost de sante-2 (120 secondes)" - lore = ["Potion de soin instantane + Pomme doree = Potion de boost de sante (120 secondes)", "Potion de soin instantane + Pomme doree enchantee = Potion de boost de sante-2 (120 secondes)"] +name = "Vie en bouteille" +description = "Quand la sante maximale ne suffit pas..." +lore1 = "Potion de soin instantane + Pomme doree = Potion de boost de sante (120 secondes)" +lore2 = "Potion de soin instantane + Pomme doree enchantee = Potion de boost de sante-2 (120 secondes)" +lore = ["Potion de soin instantane + Pomme doree = Potion de boost de sante (120 secondes)", "Potion de soin instantane + Pomme doree enchantee = Potion de boost de sante-2 (120 secondes)"] [brewing.decay] - name = "Decomposition en bouteille" - description = "Qui aurait cru que les detritus seraient si utiles ?" - lore1 = "Potion de faiblesse + Pomme de terre empoisonnee = Potion de Wither (16 secondes)" - lore2 = "Potion de faiblesse + Racines cramoisies = Potion de Wither-2 (8 secondes)" - lore = ["Potion de faiblesse + Pomme de terre empoisonnee = Potion de Wither (16 secondes)", "Potion de faiblesse + Racines cramoisies = Potion de Wither-2 (8 secondes)"] +name = "Decomposition en bouteille" +description = "Qui aurait cru que les detritus seraient si utiles ?" +lore1 = "Potion de faiblesse + Pomme de terre empoisonnee = Potion de Wither (16 secondes)" +lore2 = "Potion de faiblesse + Racines cramoisies = Potion de Wither-2 (8 secondes)" +lore = ["Potion de faiblesse + Pomme de terre empoisonnee = Potion de Wither (16 secondes)", "Potion de faiblesse + Racines cramoisies = Potion de Wither-2 (8 secondes)"] [brewing.saturation] - name = "Saturation en bouteille" - description = "Tu sais quoi... j'ai meme pas faim..." - lore1 = "Potion de regeneration + Pomme de terre cuite = Potion de saturation" - lore2 = "Potion de regeneration + Botte de foin = Potion de saturation-2" - lore = ["Potion de regeneration + Pomme de terre cuite = Potion de saturation", "Potion de regeneration + Botte de foin = Potion de saturation-2"] +name = "Saturation en bouteille" +description = "Tu sais quoi... j'ai meme pas faim..." +lore1 = "Potion de regeneration + Pomme de terre cuite = Potion de saturation" +lore2 = "Potion de regeneration + Botte de foin = Potion de saturation-2" +lore = ["Potion de regeneration + Pomme de terre cuite = Potion de saturation", "Potion de regeneration + Botte de foin = Potion de saturation-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Chaines de Mephistopheles" - description = "Vous permet de fabriquer une armure en cotte de mailles" - lore1 = "La recette de fabrication est la meme que les autres, mais avec des pepites de fer a la place" - lore = ["La recette de fabrication est la meme que les autres, mais avec des pepites de fer a la place"] +name = "Chaines de Mephistopheles" +description = "Vous permet de fabriquer une armure en cotte de mailles" +lore1 = "La recette de fabrication est la meme que les autres, mais avec des pepites de fer a la place" +lore = ["La recette de fabrication est la meme que les autres, mais avec des pepites de fer a la place"] [blocking.horse_armorer] - name = "Armure de cheval fabricable" - description = "Vous permet de fabriquer une armure de cheval" - lore1 = "Entourez une selle avec le materiau que vous souhaitez utiliser pour fabriquer l'armure" - lore = ["Entourez une selle avec le materiau que vous souhaitez utiliser pour fabriquer l'armure"] +name = "Armure de cheval fabricable" +description = "Vous permet de fabriquer une armure de cheval" +lore1 = "Entourez une selle avec le materiau que vous souhaitez utiliser pour fabriquer l'armure" +lore = ["Entourez une selle avec le materiau que vous souhaitez utiliser pour fabriquer l'armure"] [blocking.saddle_crafter] - name = "Selle fabricable" - description = "Fabriquez une selle avec du cuir" - lore1 = "Recette : 5 Cuir :" - lore = ["Recette : 5 Cuir :"] +name = "Selle fabricable" +description = "Fabriquez une selle avec du cuir" +lore1 = "Recette : 5 Cuir :" +lore = ["Recette : 5 Cuir :"] [blocking.multi_armor] - name = "Multi-armure" - description = "Liez des Elytres a une armure" - lore1 = "C'est une competence incroyable pour voyager." - lore2 = "Fusionnez et changez dynamiquement Armure/Elytre a la volee !" - lore3 = "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire." - lore4 = "Pour delier l'armure, lacher l'objet en etant accroupi, et il se desassemblera." - lore5 = "Si votre Multi-Armure est detruite, vous perdrez tous les objets qu'elle contient." - lore6 = "Je n'ai pas besoin d'armure, ca me decoit..." - lore = ["C'est une competence incroyable pour voyager.", "Fusionnez et changez dynamiquement Armure/Elytre a la volee !", "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire.", "Pour delier l'armure, lacher l'objet en etant accroupi, et il se desassemblera.", "Si votre Multi-Armure est detruite, vous perdrez tous les objets qu'elle contient.", "Je n'ai pas besoin d'armure, ca me decoit..."] +name = "Multi-armure" +description = "Liez des Elytres a une armure" +lore1 = "C'est une competence incroyable pour voyager." +lore2 = "Fusionnez et changez dynamiquement Armure/Elytre a la volee !" +lore3 = "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire." +lore4 = "Pour delier l'armure, lacher l'objet en etant accroupi, et il se desassemblera." +lore5 = "Si votre Multi-Armure est detruite, vous perdrez tous les objets qu'elle contient." +lore6 = "Je n'ai pas besoin d'armure, ca me decoit..." +lore = ["C'est une competence incroyable pour voyager.", "Fusionnez et changez dynamiquement Armure/Elytre a la volee !", "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire.", "Pour delier l'armure, lacher l'objet en etant accroupi, et il se desassemblera.", "Si votre Multi-Armure est detruite, vous perdrez tous les objets qu'elle contient.", "Je n'ai pas besoin d'armure, ca me decoit..."] # crafting [crafting] [crafting.deconstruction] - name = "Deconstruction" - description = "Deconstruisez des blocs et objets en composants de base recuperables !" - lore1 = "Lachez n'importe quel objet au sol." - lore2 = "Puis, accroupissez-vous et faites un clic droit avec des cisailles" - lore = ["Lachez n'importe quel objet au sol.", "Puis, accroupissez-vous et faites un clic droit avec des cisailles"] +name = "Deconstruction" +description = "Deconstruisez des blocs et objets en composants de base recuperables !" +lore1 = "Lachez n'importe quel objet au sol." +lore2 = "Puis, accroupissez-vous et faites un clic droit avec des cisailles" +lore = ["Lachez n'importe quel objet au sol.", "Puis, accroupissez-vous et faites un clic droit avec des cisailles"] [crafting.xp] - name = "XP d'artisanat" - description = "Gagnez de l'XP passive en fabriquant" - lore1 = "Gagnez de l'XP en fabriquant" - lore = ["Gagnez de l'XP en fabriquant"] +name = "XP d'artisanat" +description = "Gagnez de l'XP passive en fabriquant" +lore1 = "Gagnez de l'XP en fabriquant" +lore = ["Gagnez de l'XP en fabriquant"] [crafting.reconstruction] - name = "Reconstruction de minerai" - description = "Refabriquez des minerais a partir de leurs composants de base !" - lore1 = "8 des drops et 1 hote = 1 minerai (sans forme)" - lore2 = "Les drops doivent etre fondus (le cas echeant)" - lore3 = "Non inclus : debris, quartz, emeraudes, etc." - lore4 = "Hote = enveloppe. Ex : Pierre, Netherrack, Ardoise des abimes" - lore = ["8 des drops et 1 hote = 1 minerai (sans forme)", "Les drops doivent etre fondus (le cas echeant)", "Non inclus : debris, quartz, emeraudes, etc.", "Hote = enveloppe. Ex : Pierre, Netherrack, Ardoise des abimes"] +name = "Reconstruction de minerai" +description = "Refabriquez des minerais a partir de leurs composants de base !" +lore1 = "8 des drops et 1 hote = 1 minerai (sans forme)" +lore2 = "Les drops doivent etre fondus (le cas echeant)" +lore3 = "Non inclus : debris, quartz, emeraudes, etc." +lore4 = "Hote = enveloppe. Ex : Pierre, Netherrack, Ardoise des abimes" +lore = ["8 des drops et 1 hote = 1 minerai (sans forme)", "Les drops doivent etre fondus (le cas echeant)", "Non inclus : debris, quartz, emeraudes, etc.", "Hote = enveloppe. Ex : Pierre, Netherrack, Ardoise des abimes"] [crafting.leather] - name = "Cuir fabricable" - description = "Fabriquez du cuir a partir de chair putrefiee" - lore1 = "Jetez-la (la chair putrefiee) sur le feu de camp !" - lore = ["Jetez-la (la chair putrefiee) sur le feu de camp !"] +name = "Cuir fabricable" +description = "Fabriquez du cuir a partir de chair putrefiee" +lore1 = "Jetez-la (la chair putrefiee) sur le feu de camp !" +lore = ["Jetez-la (la chair putrefiee) sur le feu de camp !"] [crafting.backpacks] - name = "Sacs a dos du Boutilier !" - description = "Cela ajoute simplement le Baluchon de Mojang au jeu !" - lore1 = "Vous devez etre en mode Survie pour l'utiliser" - lore2 = "XLX : Cuir, Laisse, Cuir" - lore3 = "XSX : Cuir, Tonneau, Cuir" - lore4 = "XCX : Cuir, Coffre, Cuir" - lore = ["Vous devez etre en mode Survie pour l'utiliser", "XLX : Cuir, Laisse, Cuir", "XSX : Cuir, Tonneau, Cuir", "XCX : Cuir, Coffre, Cuir"] +name = "Sacs a dos du Boutilier !" +description = "Cela ajoute simplement le Baluchon de Mojang au jeu !" +lore1 = "Vous devez etre en mode Survie pour l'utiliser" +lore2 = "XLX : Cuir, Laisse, Cuir" +lore3 = "XSX : Cuir, Tonneau, Cuir" +lore4 = "XCX : Cuir, Coffre, Cuir" +lore = ["Vous devez etre en mode Survie pour l'utiliser", "XLX : Cuir, Laisse, Cuir", "XSX : Cuir, Tonneau, Cuir", "XCX : Cuir, Coffre, Cuir"] [crafting.stations] - name = "Tables portables !" - description = "Utilisez une table dans la paume de votre main !" - lore2 = "TOUS LES OBJETS OUBLIES DANS LA TABLE A SA FERMETURE SONT PERDUS A JAMAIS !" - lore3 = "Tables valides : Enclume, Etabli, Meule, Table de cartographie, Tailleur de pierre, Metier a tisser" - lore = ["TOUS LES OBJETS OUBLIES DANS LA TABLE A SA FERMETURE SONT PERDUS A JAMAIS !", "Tables valides : Enclume, Etabli, Meule, Table de cartographie, Tailleur de pierre, Metier a tisser"] +name = "Tables portables !" +description = "Utilisez une table dans la paume de votre main !" +lore2 = "TOUS LES OBJETS OUBLIES DANS LA TABLE A SA FERMETURE SONT PERDUS A JAMAIS !" +lore3 = "Tables valides : Enclume, Etabli, Meule, Table de cartographie, Tailleur de pierre, Metier a tisser" +lore = ["TOUS LES OBJETS OUBLIES DANS LA TABLE A SA FERMETURE SONT PERDUS A JAMAIS !", "Tables valides : Enclume, Etabli, Meule, Table de cartographie, Tailleur de pierre, Metier a tisser"] [crafting.skulls] - name = "Cranes fabricables !" - description = "En utilisant des materiaux, vous pouvez fabriquer des cranes de monstres !" - lore1 = "Entourez un bloc d'os avec les elements suivants pour obtenir un crane :" - lore2 = "Zombie : Chair putrefiee" - lore3 = "Squelette : Os" - lore4 = "Creeper : Poudre a canon" - lore5 = "Wither : Brique du Nether" - lore6 = "Dragon : Souffle du dragon" - lore = ["Entourez un bloc d'os avec les elements suivants pour obtenir un crane :", "Zombie : Chair putrefiee", "Squelette : Os", "Creeper : Poudre a canon", "Wither : Brique du Nether", "Dragon : Souffle du dragon"] +name = "Cranes fabricables !" +description = "En utilisant des materiaux, vous pouvez fabriquer des cranes de monstres !" +lore1 = "Entourez un bloc d'os avec les elements suivants pour obtenir un crane :" +lore2 = "Zombie : Chair putrefiee" +lore3 = "Squelette : Os" +lore4 = "Creeper : Poudre a canon" +lore5 = "Wither : Brique du Nether" +lore6 = "Dragon : Souffle du dragon" +lore = ["Entourez un bloc d'os avec les elements suivants pour obtenir un crane :", "Zombie : Chair putrefiee", "Squelette : Os", "Creeper : Poudre a canon", "Wither : Brique du Nether", "Dragon : Souffle du dragon"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Temps en bouteille" - description = "Transportez une bouteille temporelle qui stocke du temps et depensez-le pour accelerer les blocs temporises, les cultures et les entites vieillissantes comme les bebes animaux. Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre." - lore1 = "Secondes stockees chargees a chaque tick" - lore2 = "Acceleration temporelle par seconde stockee" - lore3 = "Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre" - lore = ["Secondes stockees chargees a chaque tick", "Acceleration temporelle par seconde stockee", "Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre"] +name = "Temps en bouteille" +description = "Transportez une bouteille temporelle qui stocke du temps et depensez-le pour accelerer les blocs temporises, les cultures et les entites vieillissantes comme les bebes animaux. Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre." +lore1 = "Secondes stockees chargees a chaque tick" +lore2 = "Acceleration temporelle par seconde stockee" +lore3 = "Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre" +lore = ["Secondes stockees chargees a chaque tick", "Acceleration temporelle par seconde stockee", "Recette (sans forme) : Potion de rapidite + Horloge + Fiole en verre"] [chronos.aberrant_touch] - name = "Toucher aberrant" - description = "Les attaques en melee appliquent une lenteur cumulative au cout de la faim, avec des plafonds stricts en JcJ, et immobilisent les cibles a 5 charges." - lore1 = "Les attaques en melee appliquent une lenteur cumulative" - lore2 = "Plafond de duree de lenteur en JcE" - lore3 = "Plafond d'amplificateur de lenteur en JcJ" - lore = ["Les attaques en melee appliquent une lenteur cumulative", "Plafond de duree de lenteur en JcE", "Plafond d'amplificateur de lenteur en JcJ"] +name = "Toucher aberrant" +description = "Les attaques en melee appliquent une lenteur cumulative au cout de la faim, avec des plafonds stricts en JcJ, et immobilisent les cibles a 5 charges." +lore1 = "Les attaques en melee appliquent une lenteur cumulative" +lore2 = "Plafond de duree de lenteur en JcE" +lore3 = "Plafond d'amplificateur de lenteur en JcJ" +lore = ["Les attaques en melee appliquent une lenteur cumulative", "Plafond de duree de lenteur en JcE", "Plafond d'amplificateur de lenteur en JcJ"] [chronos.instant_recall] - name = "Rappel instantane" - description = "Clic gauche ou droit avec une horloge en main pour revenir a un instantane recent avec sante et faim restaurees." - lore1 = "Duree du retour en arriere" - lore2 = "Temps de recharge" - lore3 = "Pas de retour d'inventaire" - lore = ["Duree du retour en arriere", "Temps de recharge", "Pas de retour d'inventaire"] +name = "Rappel instantane" +description = "Clic gauche ou droit avec une horloge en main pour revenir a un instantane recent avec sante et faim restaurees." +lore1 = "Duree du retour en arriere" +lore2 = "Temps de recharge" +lore3 = "Pas de retour d'inventaire" +lore = ["Duree du retour en arriere", "Temps de recharge", "Pas de retour d'inventaire"] [chronos.time_bomb] - name = "Bombe temporelle" - description = "Lancez une bombe chrono fabriquee qui cree un champ temporel, ralentit les entites et gele les projectiles." - lore1 = "Rayon du champ temporel" - lore2 = "Duree du champ temporel" - lore3 = "Temps de recharge de la bombe" - lore4 = "Recette (sans forme) : Horloge + Boule de neige + Diamant + Sable" - lore = ["Rayon du champ temporel", "Duree du champ temporel", "Temps de recharge de la bombe", "Recette (sans forme) : Horloge + Boule de neige + Diamant + Sable"] +name = "Bombe temporelle" +description = "Lancez une bombe chrono fabriquee qui cree un champ temporel, ralentit les entites et gele les projectiles." +lore1 = "Rayon du champ temporel" +lore2 = "Duree du champ temporel" +lore3 = "Temps de recharge de la bombe" +lore4 = "Recette (sans forme) : Horloge + Boule de neige + Diamant + Sable" +lore = ["Rayon du champ temporel", "Duree du champ temporel", "Temps de recharge de la bombe", "Recette (sans forme) : Horloge + Boule de neige + Diamant + Sable"] # discovery [discovery] [discovery.armor] - name = "Armure du monde" - description = "Armure passive selon la durete des blocs environnants." - lore1 = "Armure passive" - lore2 = "Basee sur la durete des blocs a proximite" - lore3 = "Force de l'armure :" - lore = ["Armure passive", "Basee sur la durete des blocs a proximite", "Force de l'armure :"] +name = "Armure du monde" +description = "Armure passive selon la durete des blocs environnants." +lore1 = "Armure passive" +lore2 = "Basee sur la durete des blocs a proximite" +lore3 = "Force de l'armure :" +lore = ["Armure passive", "Basee sur la durete des blocs a proximite", "Force de l'armure :"] [discovery.unity] - name = "Unite experimentale" - description = "Ramasser des orbes d'experience ajoute de l'XP a des competences aleatoires." - lore1 = "XP " - lore2 = "Par orbe" - lore = ["XP ", "Par orbe"] +name = "Unite experimentale" +description = "Ramasser des orbes d'experience ajoute de l'XP a des competences aleatoires." +lore1 = "XP " +lore2 = "Par orbe" +lore = ["XP ", "Par orbe"] [discovery.resist] - name = "Resistance experimentale" - description = "Consommez de l'experience pour attenuer les degats uniquement quand un coup vous ferait tomber en dessous de 5 coeurs ou vous tuerait." - lore0 = "Se declenche uniquement a sante critique (<= 5 coeurs) une fois toutes les 15 secondes" - lore1 = " Degats reduits" - lore2 = "experience drainnee" - lore = ["Se declenche uniquement a sante critique (<= 5 coeurs) une fois toutes les 15 secondes", " Degats reduits", "experience drainnee"] +name = "Resistance experimentale" +description = "Consommez de l'experience pour attenuer les degats uniquement quand un coup vous ferait tomber en dessous de 5 coeurs ou vous tuerait." +lore0 = "Se declenche uniquement a sante critique (<= 5 coeurs) une fois toutes les 15 secondes" +lore1 = " Degats reduits" +lore2 = "experience drainnee" +lore = ["Se declenche uniquement a sante critique (<= 5 coeurs) une fois toutes les 15 secondes", " Degats reduits", "experience drainnee"] [discovery.villager] - name = "Attraction villageoise" - description = "Vous permet d'obtenir de meilleurs echanges avec les villageois !" - lore1 = "Cela consomme de l'XP par interaction avec les villageois" - lore2 = "Chance par interaction de consommer de l'XP et d'ameliorer les echanges" - lore3 = "XP requise drainee par interaction" - lore = ["Cela consomme de l'XP par interaction avec les villageois", "Chance par interaction de consommer de l'XP et d'ameliorer les echanges", "XP requise drainee par interaction"] +name = "Attraction villageoise" +description = "Vous permet d'obtenir de meilleurs echanges avec les villageois !" +lore1 = "Cela consomme de l'XP par interaction avec les villageois" +lore2 = "Chance par interaction de consommer de l'XP et d'ameliorer les echanges" +lore3 = "XP requise drainee par interaction" +lore = ["Cela consomme de l'XP par interaction avec les villageois", "Chance par interaction de consommer de l'XP et d'ameliorer les echanges", "XP requise drainee par interaction"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Retour de lapis" - description = "Au cout d'1 niveau d'XP supplementaire, avec une chance de vous donner du lapis gratuit en retour" - lore1 = "Pour chaque niveau, le cout d'enchantement augmente de 1, mais peut rendre jusqu'a 3 lapis" - lore = ["Pour chaque niveau, le cout d'enchantement augmente de 1, mais peut rendre jusqu'a 3 lapis"] +name = "Retour de lapis" +description = "Au cout d'1 niveau d'XP supplementaire, avec une chance de vous donner du lapis gratuit en retour" +lore1 = "Pour chaque niveau, le cout d'enchantement augmente de 1, mais peut rendre jusqu'a 3 lapis" +lore = ["Pour chaque niveau, le cout d'enchantement augmente de 1, mais peut rendre jusqu'a 3 lapis"] [enchanting.quick_enchant] - name = "Enchantement rapide au clic" - description = "Enchantez des objets en cliquant directement sur eux avec des livres d'enchantement." - lore1 = "Niveaux combines maximum" - lore2 = "Impossible d'enchanter un objet avec plus de " - lore3 = "puissance" - lore = ["Niveaux combines maximum", "Impossible d'enchanter un objet avec plus de ", "puissance"] +name = "Enchantement rapide au clic" +description = "Enchantez des objets en cliquant directement sur eux avec des livres d'enchantement." +lore1 = "Niveaux combines maximum" +lore2 = "Impossible d'enchanter un objet avec plus de " +lore3 = "puissance" +lore = ["Niveaux combines maximum", "Impossible d'enchanter un objet avec plus de ", "puissance"] [enchanting.return] - name = "Retour d'XP" - description = "L'XP d'enchantement vous est rendue lorsque vous enchantez un objet." - lore1 = "L'experience depensee a une chance d'etre remboursee lorsque vous enchantez un objet" - lore2 = "Experience par enchantement" - lore = ["L'experience depensee a une chance d'etre remboursee lorsque vous enchantez un objet", "Experience par enchantement"] +name = "Retour d'XP" +description = "L'XP d'enchantement vous est rendue lorsque vous enchantez un objet." +lore1 = "L'experience depensee a une chance d'etre remboursee lorsque vous enchantez un objet" +lore2 = "Experience par enchantement" +lore = ["L'experience depensee a une chance d'etre remboursee lorsque vous enchantez un objet", "Experience par enchantement"] # excavation [excavation] [excavation.haste] - name = "Excavateur presse" - description = "Cela accelerera le processus d'excavation, avec de la HATE !" - lore1 = "Gagnez la hate en excavant" - lore2 = "x niveaux de hate quand vous commencez a miner N'IMPORTE QUEL bloc." - lore = ["Gagnez la hate en excavant", "x niveaux de hate quand vous commencez a miner N'IMPORTE QUEL bloc."] +name = "Excavateur presse" +description = "Cela accelerera le processus d'excavation, avec de la HATE !" +lore1 = "Gagnez la hate en excavant" +lore2 = "x niveaux de hate quand vous commencez a miner N'IMPORTE QUEL bloc." +lore = ["Gagnez la hate en excavant", "x niveaux de hate quand vous commencez a miner N'IMPORTE QUEL bloc."] [excavation.spelunker] - name = "Speleonologue super-voyant !" - description = "Voyez les minerais avec vos yeux, mais a travers le sol !" - lore1 = "Minerai dans votre main secondaire, baies lumineuses dans votre main principale, et accroupissez-vous !" - lore2 = "Portee en blocs : " - lore3 = "Consomme une baie lumineuse a l'utilisation" - lore = ["Minerai dans votre main secondaire, baies lumineuses dans votre main principale, et accroupissez-vous !", "Portee en blocs : ", "Consomme une baie lumineuse a l'utilisation"] +name = "Speleonologue super-voyant !" +description = "Voyez les minerais avec vos yeux, mais a travers le sol !" +lore1 = "Minerai dans votre main secondaire, baies lumineuses dans votre main principale, et accroupissez-vous !" +lore2 = "Portee en blocs : " +lore3 = "Consomme une baie lumineuse a l'utilisation" +lore = ["Minerai dans votre main secondaire, baies lumineuses dans votre main principale, et accroupissez-vous !", "Portee en blocs : ", "Consomme une baie lumineuse a l'utilisation"] [excavation.drop_to_inventory] - name = "Pelle : depot direct en inventaire" +name = "Pelle : depot direct en inventaire" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Le Leatherman luxueux et surconcu de Tackle" - lore1 = "Probablement le plus puissant, il vous permet de" - lore2 = "fusionner et changer dynamiquement vos outils a la volee, selon vos besoins." - lore3 = "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire." - lore4 = "Pour delier les outils, lachez l'objet en etant accroupi, et il se desassemblera." - lore5 = "Vous ne pouvez pas casser les outils dans ce Leatherman mais vous ne pouvez pas utiliser des outils casses" - lore6 = "objets fusionnables au total." - lore7 = "Vous pourriez utiliser cinq ou six outils, ou juste un seul !" - lore = ["Probablement le plus puissant, il vous permet de", "fusionner et changer dynamiquement vos outils a la volee, selon vos besoins.", "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire.", "Pour delier les outils, lachez l'objet en etant accroupi, et il se desassemblera.", "Vous ne pouvez pas casser les outils dans ce Leatherman mais vous ne pouvez pas utiliser des outils casses", "objets fusionnables au total.", "Vous pourriez utiliser cinq ou six outils, ou juste un seul !"] +name = "OMNI - T.O.O.L." +description = "Le Leatherman luxueux et surconcu de Tackle" +lore1 = "Probablement le plus puissant, il vous permet de" +lore2 = "fusionner et changer dynamiquement vos outils a la volee, selon vos besoins." +lore3 = "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire." +lore4 = "Pour delier les outils, lachez l'objet en etant accroupi, et il se desassemblera." +lore5 = "Vous ne pouvez pas casser les outils dans ce Leatherman mais vous ne pouvez pas utiliser des outils casses" +lore6 = "objets fusionnables au total." +lore7 = "Vous pourriez utiliser cinq ou six outils, ou juste un seul !" +lore = ["Probablement le plus puissant, il vous permet de", "fusionner et changer dynamiquement vos outils a la volee, selon vos besoins.", "Pour fusionner, Maj-cliquez un objet sur un autre dans votre inventaire.", "Pour delier les outils, lachez l'objet en etant accroupi, et il se desassemblera.", "Vous ne pouvez pas casser les outils dans ce Leatherman mais vous ne pouvez pas utiliser des outils casses", "objets fusionnables au total.", "Vous pourriez utiliser cinq ou six outils, ou juste un seul !"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Aura de croissance" - description = "Faites pousser la nature autour de vous dans une aura" - lore1 = "Rayon en blocs" - lore2 = "Force de l'aura de croissance" - lore3 = "Cout en nourriture" - lore = ["Rayon en blocs", "Force de l'aura de croissance", "Cout en nourriture"] +name = "Aura de croissance" +description = "Faites pousser la nature autour de vous dans une aura" +lore1 = "Rayon en blocs" +lore2 = "Force de l'aura de croissance" +lore3 = "Cout en nourriture" +lore = ["Rayon en blocs", "Force de l'aura de croissance", "Cout en nourriture"] [herbalism.hippo] - name = "Hippopotame de l'herboriste" - description = "Consommer de la nourriture vous donne plus de saturation" - lore1 = "Nourriture) points de saturation supplementaires a la consommation" - lore = ["Nourriture) points de saturation supplementaires a la consommation"] +name = "Hippopotame de l'herboriste" +description = "Consommer de la nourriture vous donne plus de saturation" +lore1 = "Nourriture) points de saturation supplementaires a la consommation" +lore = ["Nourriture) points de saturation supplementaires a la consommation"] [herbalism.myconid] - name = "Myconide de l'herboriste" - description = "Vous donne la capacite de fabriquer du mycelium" - lore1 = "N'importe quelle terre, plus un champignon brun et un rouge, fabriqueront du mycelium." - lore = ["N'importe quelle terre, plus un champignon brun et un rouge, fabriqueront du mycelium."] +name = "Myconide de l'herboriste" +description = "Vous donne la capacite de fabriquer du mycelium" +lore1 = "N'importe quelle terre, plus un champignon brun et un rouge, fabriqueront du mycelium." +lore = ["N'importe quelle terre, plus un champignon brun et un rouge, fabriqueront du mycelium."] [herbalism.terralid] - name = "Terralide de l'herboriste" - description = "Vous donne la capacite de fabriquer des blocs d'herbe" - lore1 = "Trois graines, au-dessus de 3 terres, fabriqueront 3 blocs d'herbe." - lore = ["Trois graines, au-dessus de 3 terres, fabriqueront 3 blocs d'herbe."] +name = "Terralide de l'herboriste" +description = "Vous donne la capacite de fabriquer des blocs d'herbe" +lore1 = "Trois graines, au-dessus de 3 terres, fabriqueront 3 blocs d'herbe." +lore = ["Trois graines, au-dessus de 3 terres, fabriqueront 3 blocs d'herbe."] [herbalism.cobweb] - name = "Createur de toiles" - description = "Vous donne la capacite de fabriquer des toiles d'araignee dans une table d'artisanat" - lore1 = "Neuf ficelles fabriqueront une toile d'araignee." - lore = ["Neuf ficelles fabriqueront une toile d'araignee."] +name = "Createur de toiles" +description = "Vous donne la capacite de fabriquer des toiles d'araignee dans une table d'artisanat" +lore1 = "Neuf ficelles fabriqueront une toile d'araignee." +lore = ["Neuf ficelles fabriqueront une toile d'araignee."] [herbalism.mushroom_blocks] - name = "Fabricant de champignons" - description = "Vous donne la capacite de fabriquer des blocs de champignon dans une table d'artisanat" - lore1 = "Quatre champignons pour faire un bloc, ou un bloc pour faire une tige." - lore = ["Quatre champignons pour faire un bloc, ou un bloc pour faire une tige."] +name = "Fabricant de champignons" +description = "Vous donne la capacite de fabriquer des blocs de champignon dans une table d'artisanat" +lore1 = "Quatre champignons pour faire un bloc, ou un bloc pour faire une tige." +lore = ["Quatre champignons pour faire un bloc, ou un bloc pour faire une tige."] [herbalism.drop_to_inventory] - name = "Houe : depot direct en inventaire" +name = "Houe : depot direct en inventaire" [herbalism.hungry_shield] - name = "Bouclier affame" - description = "Subissez des degats a votre faim avant votre sante." - lore1 = "Resiste par la faim" - lore = ["Resiste par la faim"] +name = "Bouclier affame" +description = "Subissez des degats a votre faim avant votre sante." +lore1 = "Resiste par la faim" +lore = ["Resiste par la faim"] [herbalism.luck] - name = "Chance de l'herboriste" - description = "Quand vous cassez de l'herbe/des fleurs, vous avez une chance d'obtenir un objet aleatoire" - lore0 = "Fleurs = Nourriture, et Herbe = Graines" - lore1 = "Chance d'obtenir un objet en cassant des fleurs" - lore2 = "Chance d'obtenir un objet en cassant de l'herbe" - lore = ["Fleurs = Nourriture, et Herbe = Graines", "Chance d'obtenir un objet en cassant des fleurs", "Chance d'obtenir un objet en cassant de l'herbe"] +name = "Chance de l'herboriste" +description = "Quand vous cassez de l'herbe/des fleurs, vous avez une chance d'obtenir un objet aleatoire" +lore0 = "Fleurs = Nourriture, et Herbe = Graines" +lore1 = "Chance d'obtenir un objet en cassant des fleurs" +lore2 = "Chance d'obtenir un objet en cassant de l'herbe" +lore = ["Fleurs = Nourriture, et Herbe = Graines", "Chance d'obtenir un objet en cassant des fleurs", "Chance d'obtenir un objet en cassant de l'herbe"] [herbalism.replant] - name = "Recolte et replantation" - description = "Clic droit sur une culture avec une houe pour la recolter et la replanter." - lore1 = "Rayon de replantation en blocs" - lore = ["Rayon de replantation en blocs"] +name = "Recolte et replantation" +description = "Clic droit sur une culture avec une houe pour la recolter et la replanter." +lore1 = "Rayon de replantation en blocs" +lore = ["Rayon de replantation en blocs"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenaline" - description = "Infligez plus de degats plus votre sante est basse (melee)" - lore1 = "Degats maximum" - lore = ["Degats maximum"] +name = "Adrenaline" +description = "Infligez plus de degats plus votre sante est basse (melee)" +lore1 = "Degats maximum" +lore = ["Degats maximum"] [hunter.penalty] - name = "" - description = "" - lore1 = "Vous gagnerez des charges de poison si vous n'avez plus de faim" - lore = ["Vous gagnerez des charges de poison si vous n'avez plus de faim"] +name = "" +description = "" +lore1 = "Vous gagnerez des charges de poison si vous n'avez plus de faim" +lore = ["Vous gagnerez des charges de poison si vous n'avez plus de faim"] [hunter.drop_to_inventory] - name = "Objets : depot direct en inventaire" - description = "Quand vous tuez quelque chose / cassez un bloc avec une epee, les drops sont teleportes dans votre inventaire" - lore1 = "Chaque fois qu'un objet tombe d'un mob/bloc que vous cassez, il va dans votre inventaire si possible." - lore = ["Chaque fois qu'un objet tombe d'un mob/bloc que vous cassez, il va dans votre inventaire si possible."] +name = "Objets : depot direct en inventaire" +description = "Quand vous tuez quelque chose / cassez un bloc avec une epee, les drops sont teleportes dans votre inventaire" +lore1 = "Chaque fois qu'un objet tombe d'un mob/bloc que vous cassez, il va dans votre inventaire si possible." +lore = ["Chaque fois qu'un objet tombe d'un mob/bloc que vous cassez, il va dans votre inventaire si possible."] [hunter.invisibility] - name = "Pas evanescent" - description = "Quand vous etes frappe, vous gagnez l'invisibilite, au cout de la faim" - lore1 = "Gagnez l'invisibilite passive quand vous etes frappe" - lore2 = "x charges d'invisibilite pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "Duree d'invisibilite" - lore = ["Gagnez l'invisibilite passive quand vous etes frappe", "x charges d'invisibilite pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "Duree d'invisibilite"] +name = "Pas evanescent" +description = "Quand vous etes frappe, vous gagnez l'invisibilite, au cout de la faim" +lore1 = "Gagnez l'invisibilite passive quand vous etes frappe" +lore2 = "x charges d'invisibilite pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "Duree d'invisibilite" +lore = ["Gagnez l'invisibilite passive quand vous etes frappe", "x charges d'invisibilite pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "Duree d'invisibilite"] [hunter.jump_boost] - name = "Hauteurs du chasseur" - description = "Quand vous etes frappe, vous gagnez un boost de saut, au cout de la faim" - lore1 = "Gagnez un boost de saut passif quand vous etes frappe" - lore2 = "x charges de boost de saut pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "Le boost de saut cumule le multiplicateur, pas la duree." - lore = ["Gagnez un boost de saut passif quand vous etes frappe", "x charges de boost de saut pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "Le boost de saut cumule le multiplicateur, pas la duree."] +name = "Hauteurs du chasseur" +description = "Quand vous etes frappe, vous gagnez un boost de saut, au cout de la faim" +lore1 = "Gagnez un boost de saut passif quand vous etes frappe" +lore2 = "x charges de boost de saut pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "Le boost de saut cumule le multiplicateur, pas la duree." +lore = ["Gagnez un boost de saut passif quand vous etes frappe", "x charges de boost de saut pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "Le boost de saut cumule le multiplicateur, pas la duree."] [hunter.luck] - name = "Chance du chasseur" - description = "Quand vous etes frappe, vous gagnez de la chance, au cout de la faim" - lore1 = "Gagnez de la chance passive quand vous etes frappe" - lore2 = "x charges de chance pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "La chance cumule le multiplicateur, pas la duree." - lore = ["Gagnez de la chance passive quand vous etes frappe", "x charges de chance pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La chance cumule le multiplicateur, pas la duree."] +name = "Chance du chasseur" +description = "Quand vous etes frappe, vous gagnez de la chance, au cout de la faim" +lore1 = "Gagnez de la chance passive quand vous etes frappe" +lore2 = "x charges de chance pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "La chance cumule le multiplicateur, pas la duree." +lore = ["Gagnez de la chance passive quand vous etes frappe", "x charges de chance pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La chance cumule le multiplicateur, pas la duree."] [hunter.regen] - name = "Regeneration du chasseur" - description = "Quand vous etes frappe, vous gagnez de la regeneration, au cout de la faim" - lore1 = "Gagnez de la regeneration passive quand vous etes frappe" - lore2 = "x charges de regeneration pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "La regeneration cumule le multiplicateur, pas la duree." - lore = ["Gagnez de la regeneration passive quand vous etes frappe", "x charges de regeneration pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La regeneration cumule le multiplicateur, pas la duree."] +name = "Regeneration du chasseur" +description = "Quand vous etes frappe, vous gagnez de la regeneration, au cout de la faim" +lore1 = "Gagnez de la regeneration passive quand vous etes frappe" +lore2 = "x charges de regeneration pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "La regeneration cumule le multiplicateur, pas la duree." +lore = ["Gagnez de la regeneration passive quand vous etes frappe", "x charges de regeneration pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La regeneration cumule le multiplicateur, pas la duree."] [hunter.resistance] - name = "Resistance du chasseur" - description = "Quand vous etes frappe, vous gagnez de la resistance, au cout de la faim" - lore1 = "Gagnez de la resistance passive quand vous etes frappe" - lore2 = "x charges de resistance pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "La resistance cumule le multiplicateur, pas la duree." - lore = ["Gagnez de la resistance passive quand vous etes frappe", "x charges de resistance pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La resistance cumule le multiplicateur, pas la duree."] +name = "Resistance du chasseur" +description = "Quand vous etes frappe, vous gagnez de la resistance, au cout de la faim" +lore1 = "Gagnez de la resistance passive quand vous etes frappe" +lore2 = "x charges de resistance pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "La resistance cumule le multiplicateur, pas la duree." +lore = ["Gagnez de la resistance passive quand vous etes frappe", "x charges de resistance pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La resistance cumule le multiplicateur, pas la duree."] [hunter.speed] - name = "Vitesse du chasseur" - description = "Quand vous etes frappe, vous gagnez de la vitesse, au cout de la faim" - lore1 = "Gagnez de la vitesse passive quand vous etes frappe" - lore2 = "x charges de vitesse pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "La vitesse cumule le multiplicateur, pas la duree." - lore = ["Gagnez de la vitesse passive quand vous etes frappe", "x charges de vitesse pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La vitesse cumule le multiplicateur, pas la duree."] +name = "Vitesse du chasseur" +description = "Quand vous etes frappe, vous gagnez de la vitesse, au cout de la faim" +lore1 = "Gagnez de la vitesse passive quand vous etes frappe" +lore2 = "x charges de vitesse pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "La vitesse cumule le multiplicateur, pas la duree." +lore = ["Gagnez de la vitesse passive quand vous etes frappe", "x charges de vitesse pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La vitesse cumule le multiplicateur, pas la duree."] [hunter.strength] - name = "Force du chasseur" - description = "Quand vous etes frappe, vous gagnez de la force, au cout de la faim" - lore1 = "Gagnez de la force passive quand vous etes frappe" - lore2 = "x charges de force pendant 3 secondes quand vous etes touche" - lore3 = "x faim cumulative" - lore4 = "La faim cumule duree et multiplicateur." - lore5 = "La force cumule le multiplicateur, pas la duree." - lore = ["Gagnez de la force passive quand vous etes frappe", "x charges de force pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La force cumule le multiplicateur, pas la duree."] +name = "Force du chasseur" +description = "Quand vous etes frappe, vous gagnez de la force, au cout de la faim" +lore1 = "Gagnez de la force passive quand vous etes frappe" +lore2 = "x charges de force pendant 3 secondes quand vous etes touche" +lore3 = "x faim cumulative" +lore4 = "La faim cumule duree et multiplicateur." +lore5 = "La force cumule le multiplicateur, pas la duree." +lore = ["Gagnez de la force passive quand vous etes frappe", "x charges de force pendant 3 secondes quand vous etes touche", "x faim cumulative", "La faim cumule duree et multiplicateur.", "La force cumule le multiplicateur, pas la duree."] # nether [nether] [nether.skull_toss] - name = "Lancer de crane de Wither" - description1 = "Liberez votre Wither interieur en utilisant" - description2 = "la tete de" - description3 = "quelqu'un." - lore1 = "Secondes de temps de recharge entre les lancers de crane." - lore2 = "Avec un crane de Wither : Lancez un " - lore3 = "Crane de Wither" - lore4 = "qui explose a l'impact." - lore = ["Secondes de temps de recharge entre les lancers de crane.", "Avec un crane de Wither : Lancez un ", "Crane de Wither", "qui explose a l'impact."] +name = "Lancer de crane de Wither" +description1 = "Liberez votre Wither interieur en utilisant" +description2 = "la tete de" +description3 = "quelqu'un." +lore1 = "Secondes de temps de recharge entre les lancers de crane." +lore2 = "Avec un crane de Wither : Lancez un " +lore3 = "Crane de Wither" +lore4 = "qui explose a l'impact." +lore = ["Secondes de temps de recharge entre les lancers de crane.", "Avec un crane de Wither : Lancez un ", "Crane de Wither", "qui explose a l'impact."] [nether.wither_resist] - name = "Resistance au Wither" - description = "Resiste au fletrissement grace au pouvoir de la Netherite." - lore1 = "chance d'annuler le fletrissement (par piece)." - lore2 = "Passif : Porter une armure en Netherite a une chance d'annuler " - lore3 = "le fletrissement." - lore = ["chance d'annuler le fletrissement (par piece).", "Passif : Porter une armure en Netherite a une chance d'annuler ", "le fletrissement."] +name = "Resistance au Wither" +description = "Resiste au fletrissement grace au pouvoir de la Netherite." +lore1 = "chance d'annuler le fletrissement (par piece)." +lore2 = "Passif : Porter une armure en Netherite a une chance d'annuler " +lore3 = "le fletrissement." +lore = ["chance d'annuler le fletrissement (par piece).", "Passif : Porter une armure en Netherite a une chance d'annuler ", "le fletrissement."] [nether.fire_resist] - name = "Resistance au feu" - description = "Resiste au feu en durcissant votre peau." - lore1 = "chance d'annuler l'effet de brulure !" - lore = ["chance d'annuler l'effet de brulure !"] +name = "Resistance au feu" +description = "Resiste au feu en durcissant votre peau." +lore1 = "chance d'annuler l'effet de brulure !" +lore = ["chance d'annuler l'effet de brulure !"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Fonte automatique" - description = "Vous permet de fondre les minerais standards mines" - lore1 = "Les minerais pouvant etre fondus le sont automatiquement" - lore2 = "% de chance d'en obtenir un supplementaire" - lore = ["Les minerais pouvant etre fondus le sont automatiquement", "% de chance d'en obtenir un supplementaire"] +name = "Fonte automatique" +description = "Vous permet de fondre les minerais standards mines" +lore1 = "Les minerais pouvant etre fondus le sont automatiquement" +lore2 = "% de chance d'en obtenir un supplementaire" +lore = ["Les minerais pouvant etre fondus le sont automatiquement", "% de chance d'en obtenir un supplementaire"] [pickaxe.chisel] - name = "Ciseau a minerai" - description = "Clic droit sur les minerais pour en extraire davantage, au prix d'une forte usure de durabilite." - lore1 = "Chance de drop" - lore2 = "Usure de l'outil" - lore = ["Chance de drop", "Usure de l'outil"] +name = "Ciseau a minerai" +description = "Clic droit sur les minerais pour en extraire davantage, au prix d'une forte usure de durabilite." +lore1 = "Chance de drop" +lore2 = "Usure de l'outil" +lore = ["Chance de drop", "Usure de l'outil"] [pickaxe.drop_to_inventory] - name = "Pioche : depot direct en inventaire" - description = "Quand vous cassez un bloc, l'objet est teleporte dans votre inventaire" - lore1 = "Chaque fois qu'un objet tombe d'un bloc que vous cassez, il va dans votre inventaire si possible." - lore = ["Chaque fois qu'un objet tombe d'un bloc que vous cassez, il va dans votre inventaire si possible."] +name = "Pioche : depot direct en inventaire" +description = "Quand vous cassez un bloc, l'objet est teleporte dans votre inventaire" +lore1 = "Chaque fois qu'un objet tombe d'un bloc que vous cassez, il va dans votre inventaire si possible." +lore = ["Chaque fois qu'un objet tombe d'un bloc que vous cassez, il va dans votre inventaire si possible."] [pickaxe.silk_spawner] - name = "Pioche toucher de soie sur les generateurs" - description = "Les generateurs de monstres tombent quand ils sont casses" - lore1 = "Rend les generateurs cassables avec Toucher de soie." - lore2 = "Rend les generateurs cassables en etant accroupi." - lore = ["Rend les generateurs cassables avec Toucher de soie.", "Rend les generateurs cassables en etant accroupi."] +name = "Pioche toucher de soie sur les generateurs" +description = "Les generateurs de monstres tombent quand ils sont casses" +lore1 = "Rend les generateurs cassables avec Toucher de soie." +lore2 = "Rend les generateurs cassables en etant accroupi." +lore = ["Rend les generateurs cassables avec Toucher de soie.", "Rend les generateurs cassables en etant accroupi."] [pickaxe.vein_miner] - name = "Mineur de filon" - description = "Vous permet de casser des blocs dans un filon/groupe de minerais standards" - lore1 = "Accroupissez-vous et minez les MINERAIS" - lore2 = "portee du minage de filon" - lore3 = "Cette competence ne regroupe PAS tous les drops ensemble !" - lore = ["Accroupissez-vous et minez les MINERAIS", "portee du minage de filon", "Cette competence ne regroupe PAS tous les drops ensemble !"] +name = "Mineur de filon" +description = "Vous permet de casser des blocs dans un filon/groupe de minerais standards" +lore1 = "Accroupissez-vous et minez les MINERAIS" +lore2 = "portee du minage de filon" +lore3 = "Cette competence ne regroupe PAS tous les drops ensemble !" +lore = ["Accroupissez-vous et minez les MINERAIS", "portee du minage de filon", "Cette competence ne regroupe PAS tous les drops ensemble !"] # ranged [ranged] [ranged.arrow_recovery] - name = "Recuperation de fleches" - description = "Recuperez des fleches apres avoir tue un ennemi." - lore1 = "Chance de recuperer des fleches en touchant/tuant" - lore2 = "Chance : " - lore = ["Chance de recuperer des fleches en touchant/tuant", "Chance : "] +name = "Recuperation de fleches" +description = "Recuperez des fleches apres avoir tue un ennemi." +lore1 = "Chance de recuperer des fleches en touchant/tuant" +lore2 = "Chance : " +lore = ["Chance de recuperer des fleches en touchant/tuant", "Chance : "] [ranged.web_shot] - name = "Piege de toile" - description = "Entourez votre cible de toiles d'araignee quand vous la touchez !" - lore1 = "8 toiles d'araignee autour d'une boule de neige, et lancez !" - lore2 = "secondes de cage, environ." - lore = ["8 toiles d'araignee autour d'une boule de neige, et lancez !", "secondes de cage, environ."] +name = "Piege de toile" +description = "Entourez votre cible de toiles d'araignee quand vous la touchez !" +lore1 = "8 toiles d'araignee autour d'une boule de neige, et lancez !" +lore2 = "secondes de cage, environ." +lore = ["8 toiles d'araignee autour d'une boule de neige, et lancez !", "secondes de cage, environ."] [ranged.force_shot] - name = "Tir puissant" - description = "Tirez des projectiles plus loin, plus vite !" - advancementname = "Tir longue distance" - advancementlore = "Touchez une cible a plus de 30 blocs de distance !" - lore1 = "Vitesse du projectile" - lore = ["Vitesse du projectile"] +name = "Tir puissant" +description = "Tirez des projectiles plus loin, plus vite !" +advancementname = "Tir longue distance" +advancementlore = "Touchez une cible a plus de 30 blocs de distance !" +lore1 = "Vitesse du projectile" +lore = ["Vitesse du projectile"] [ranged.lunge_shot] - name = "Tir en fente" - description = "En chute, vos fleches vous propulsent dans une direction aleatoire" - lore1 = "Vitesse de propulsion aleatoire" - lore = ["Vitesse de propulsion aleatoire"] +name = "Tir en fente" +description = "En chute, vos fleches vous propulsent dans une direction aleatoire" +lore1 = "Vitesse de propulsion aleatoire" +lore = ["Vitesse de propulsion aleatoire"] [ranged.arrow_piercing] - name = "Fleche percante" - description = "Ajoute le percage aux projectiles ! Tirez a travers les choses !" - lore1 = "Cibles percees" - lore = ["Cibles percees"] +name = "Fleche percante" +description = "Ajoute le percage aux projectiles ! Tirez a travers les choses !" +lore1 = "Cibles percees" +lore = ["Cibles percees"] # rift [rift] [rift.remote_access] - name = "Acces a distance" - description = "Puisez dans le vide et accedez a un conteneur marque." - lore1 = "Perle de l'Ender + Boussole = Portoloin reliquaire" - lore2 = "Cet objet vous permet d'acceder aux conteneurs a distance" - lore3 = "Une fois fabrique, regardez l'objet pour voir son utilisation" - notcontainer = "Ce n'est pas un conteneur" - lore = ["Perle de l'Ender + Boussole = Portoloin reliquaire", "Cet objet vous permet d'acceder aux conteneurs a distance", "Une fois fabrique, regardez l'objet pour voir son utilisation"] +name = "Acces a distance" +description = "Puisez dans le vide et accedez a un conteneur marque." +lore1 = "Perle de l'Ender + Boussole = Portoloin reliquaire" +lore2 = "Cet objet vous permet d'acceder aux conteneurs a distance" +lore3 = "Une fois fabrique, regardez l'objet pour voir son utilisation" +notcontainer = "Ce n'est pas un conteneur" +lore = ["Perle de l'Ender + Boussole = Portoloin reliquaire", "Cet objet vous permet d'acceder aux conteneurs a distance", "Une fois fabrique, regardez l'objet pour voir son utilisation"] [rift.blink] - name = "Eclair de Faille" - description = "Teleportation instantanee a courte portee, en un clin d'oeil !" - lore1 = "Blocs par eclair (2x vertical)" - lore2 = "En sprintant : Appuyez deux fois sur Saut pour " - lore3 = "Eclairs" - lore = ["Blocs par eclair (2x vertical)", "En sprintant : Appuyez deux fois sur Saut pour ", "Eclairs"] +name = "Eclair de Faille" +description = "Teleportation instantanee a courte portee, en un clin d'oeil !" +lore1 = "Blocs par eclair (2x vertical)" +lore2 = "En sprintant : Appuyez deux fois sur Saut pour " +lore3 = "Eclairs" +lore = ["Blocs par eclair (2x vertical)", "En sprintant : Appuyez deux fois sur Saut pour ", "Eclairs"] [rift.chest] - name = "Coffre de l'Ender facile" - description = "Ouvrez un coffre de l'Ender en faisant un clic gauche avec en main." - lore1 = "Cliquez sur un coffre de l'Ender dans votre main pour l'ouvrir (ne le placez pas)" - lore = ["Cliquez sur un coffre de l'Ender dans votre main pour l'ouvrir (ne le placez pas)"] +name = "Coffre de l'Ender facile" +description = "Ouvrez un coffre de l'Ender en faisant un clic gauche avec en main." +lore1 = "Cliquez sur un coffre de l'Ender dans votre main pour l'ouvrir (ne le placez pas)" +lore = ["Cliquez sur un coffre de l'Ender dans votre main pour l'ouvrir (ne le placez pas)"] [rift.descent] - name = "Anti-levitation" - description = "Vous en avez assez d'etre coince en l'air ? Cette competence est faite pour vous !" - lore1 = "Accroupissez-vous simplement pour descendre, et vous tomberez a une vitesse inferieure a la normale !" - lore2 = "Temps de recharge :" - lore = ["Accroupissez-vous simplement pour descendre, et vous tomberez a une vitesse inferieure a la normale !", "Temps de recharge :"] +name = "Anti-levitation" +description = "Vous en avez assez d'etre coince en l'air ? Cette competence est faite pour vous !" +lore1 = "Accroupissez-vous simplement pour descendre, et vous tomberez a une vitesse inferieure a la normale !" +lore2 = "Temps de recharge :" +lore = ["Accroupissez-vous simplement pour descendre, et vous tomberez a une vitesse inferieure a la normale !", "Temps de recharge :"] [rift.gate] - name = "Portail de Faille" - description = "Teleportez-vous a un emplacement marque." - lore1 = "FABRICATION : Emeraude + Eclat d'amethyste + Perle de l'Ender" - lore2 = "Lisez avant d'utiliser !" - lore3 = "5s de delai, " - lore4 = "vous pouvez mourir pendant cette animation" - lore = ["FABRICATION : Emeraude + Eclat d'amethyste + Perle de l'Ender", "Lisez avant d'utiliser !", "5s de delai, ", "vous pouvez mourir pendant cette animation"] +name = "Portail de Faille" +description = "Teleportez-vous a un emplacement marque." +lore1 = "FABRICATION : Emeraude + Eclat d'amethyste + Perle de l'Ender" +lore2 = "Lisez avant d'utiliser !" +lore3 = "5s de delai, " +lore4 = "vous pouvez mourir pendant cette animation" +lore = ["FABRICATION : Emeraude + Eclat d'amethyste + Perle de l'Ender", "Lisez avant d'utiliser !", "5s de delai, ", "vous pouvez mourir pendant cette animation"] [rift.resist] - name = "Resistance de Faille" - description = "Gagnez de la resistance en utilisant des objets et capacites de l'Ender" - lore1 = "+ Passif : Fournit de la resistance quand vous utilisez des capacites de Faille ou des objets de l'Ender" - lore2 = "N'inclut PAS le coffre de l'Ender portable, seulement les objets consommables" - lore = ["+ Passif : Fournit de la resistance quand vous utilisez des capacites de Faille ou des objets de l'Ender", "N'inclut PAS le coffre de l'Ender portable, seulement les objets consommables"] +name = "Resistance de Faille" +description = "Gagnez de la resistance en utilisant des objets et capacites de l'Ender" +lore1 = "+ Passif : Fournit de la resistance quand vous utilisez des capacites de Faille ou des objets de l'Ender" +lore2 = "N'inclut PAS le coffre de l'Ender portable, seulement les objets consommables" +lore = ["+ Passif : Fournit de la resistance quand vous utilisez des capacites de Faille ou des objets de l'Ender", "N'inclut PAS le coffre de l'Ender portable, seulement les objets consommables"] [rift.visage] - name = "Visage de Faille" - description = "Empeche les Endermen de devenir agressifs si vous avez des perles de l'Ender dans votre inventaire." - lore1 = "Les Endermen ne deviendront pas agressifs si vous avez des perles de l'Ender dans votre inventaire." - lore = ["Les Endermen ne deviendront pas agressifs si vous avez des perles de l'Ender dans votre inventaire."] +name = "Visage de Faille" +description = "Empeche les Endermen de devenir agressifs si vous avez des perles de l'Ender dans votre inventaire." +lore1 = "Les Endermen ne deviendront pas agressifs si vous avez des perles de l'Ender dans votre inventaire." +lore = ["Les Endermen ne deviendront pas agressifs si vous avez des perles de l'Ender dans votre inventaire."] # seaborn [seaborn] [seaborn.oxygen] - name = "Reservoir d'oxygene organique" - description = "Retenez plus d'oxygene dans vos petits poumons !" - lore1 = "Augmentation de la capacite en oxygene" - lore = ["Augmentation de la capacite en oxygene"] +name = "Reservoir d'oxygene organique" +description = "Retenez plus d'oxygene dans vos petits poumons !" +lore1 = "Augmentation de la capacite en oxygene" +lore = ["Augmentation de la capacite en oxygene"] [seaborn.fishers_fantasy] - name = "Fantaisie du pecheur" - description = "Gagnez plus d'XP en pechant et obtenez plus de poisson !" - lore1 = "Pour chaque niveau, il y a une chance d'obtenir plus d'XP et de poisson !" - lore = ["Pour chaque niveau, il y a une chance d'obtenir plus d'XP et de poisson !"] +name = "Fantaisie du pecheur" +description = "Gagnez plus d'XP en pechant et obtenez plus de poisson !" +lore1 = "Pour chaque niveau, il y a une chance d'obtenir plus d'XP et de poisson !" +lore = ["Pour chaque niveau, il y a une chance d'obtenir plus d'XP et de poisson !"] [seaborn.haste] - name = "Mineur tortue" - description = "En minant sous l'eau, vous gagnez la hate !" - lore1 = "Hate 3 est appliquee sous l'eau pendant le minage (se cumule avec Affinite aquatique) apres que votre effet de respiration aquatique se dissipe !" - lore = ["Hate 3 est appliquee sous l'eau pendant le minage (se cumule avec Affinite aquatique) apres que votre effet de respiration aquatique se dissipe !"] +name = "Mineur tortue" +description = "En minant sous l'eau, vous gagnez la hate !" +lore1 = "Hate 3 est appliquee sous l'eau pendant le minage (se cumule avec Affinite aquatique) apres que votre effet de respiration aquatique se dissipe !" +lore = ["Hate 3 est appliquee sous l'eau pendant le minage (se cumule avec Affinite aquatique) apres que votre effet de respiration aquatique se dissipe !"] [seaborn.night_vision] - name = "Vision de tortue" - description = "Sous l'eau, vous gagnez la vision nocturne" - lore1 = "Gagnez simplement la vision nocturne sous l'eau apres que votre effet de respiration aquatique se dissipe !" - lore = ["Gagnez simplement la vision nocturne sous l'eau apres que votre effet de respiration aquatique se dissipe !"] +name = "Vision de tortue" +description = "Sous l'eau, vous gagnez la vision nocturne" +lore1 = "Gagnez simplement la vision nocturne sous l'eau apres que votre effet de respiration aquatique se dissipe !" +lore = ["Gagnez simplement la vision nocturne sous l'eau apres que votre effet de respiration aquatique se dissipe !"] [seaborn.dolphin_grace] - name = "Grace du dauphin" - description = "Nagez comme un dauphin, sans les dauphins" - lore1 = "+ Passif : gagnez " - lore2 = "x vitesse (grace du dauphin)" - lore3 = "Precision allemande de l'ingeni... attendez c'est pas ca... Non compatible avec Pas de profondeur" - lore = ["+ Passif : gagnez ", "x vitesse (grace du dauphin)", "Precision allemande de l'ingeni... attendez c'est pas ca... Non compatible avec Pas de profondeur"] +name = "Grace du dauphin" +description = "Nagez comme un dauphin, sans les dauphins" +lore1 = "+ Passif : gagnez " +lore2 = "x vitesse (grace du dauphin)" +lore3 = "Precision allemande de l'ingeni... attendez c'est pas ca... Non compatible avec Pas de profondeur" +lore = ["+ Passif : gagnez ", "x vitesse (grace du dauphin)", "Precision allemande de l'ingeni... attendez c'est pas ca... Non compatible avec Pas de profondeur"] # stealth [stealth] [stealth.ghost_armor] - name = "Armure fantome" - description = "Armure qui se construit lentement quand vous ne subissez pas de degats, dure 1 coup" - lore1 = "Armure maximale" - lore2 = "Vitesse" - lore = ["Armure maximale", "Vitesse"] +name = "Armure fantome" +description = "Armure qui se construit lentement quand vous ne subissez pas de degats, dure 1 coup" +lore1 = "Armure maximale" +lore2 = "Vitesse" +lore = ["Armure maximale", "Vitesse"] [stealth.night_vision] - name = "Vision furtive" - description = "Gagnez la vision nocturne en etant accroupi" - lore1 = "Gagnez une salve de " - lore2 = "vision nocturne" - lore3 = "en etant accroupi" - lore = ["Gagnez une salve de ", "vision nocturne", "en etant accroupi"] +name = "Vision furtive" +description = "Gagnez la vision nocturne en etant accroupi" +lore1 = "Gagnez une salve de " +lore2 = "vision nocturne" +lore3 = "en etant accroupi" +lore = ["Gagnez une salve de ", "vision nocturne", "en etant accroupi"] [stealth.snatch] - name = "Chapardage d'objet" - description = "Ramassez les objets au sol instantanement en etant accroupi !" - lore1 = "Rayon de chapardage" - lore = ["Rayon de chapardage"] +name = "Chapardage d'objet" +description = "Ramassez les objets au sol instantanement en etant accroupi !" +lore1 = "Rayon de chapardage" +lore = ["Rayon de chapardage"] [stealth.speed] - name = "Vitesse furtive" - description = "Gagnez de la vitesse en etant accroupi" - lore1 = "Vitesse en mode accroupi" - lore = ["Vitesse en mode accroupi"] +name = "Vitesse furtive" +description = "Gagnez de la vitesse en etant accroupi" +lore1 = "Vitesse en mode accroupi" +lore = ["Vitesse en mode accroupi"] [stealth.ender_veil] - name = "Voile de l'Ender" - description = "Plus besoin de citrouilles pour empecher les attaques d'Endermen" - lore1 = "Empeche les attaques d'Endermen en etant accroupi" - lore2 = "Empeche toutes les attaques d'Endermen" - lore = ["Empeche les attaques d'Endermen en etant accroupi", "Empeche toutes les attaques d'Endermen"] +name = "Voile de l'Ender" +description = "Plus besoin de citrouilles pour empecher les attaques d'Endermen" +lore1 = "Empeche les attaques d'Endermen en etant accroupi" +lore2 = "Empeche toutes les attaques d'Endermen" +lore = ["Empeche les attaques d'Endermen en etant accroupi", "Empeche toutes les attaques d'Endermen"] # sword [sword] [sword.machete] - name = "Machette" - description = "Tranchez le feuillage avec facilite !" - lore1 = "Rayon de tranche" - lore2 = "Temps de recharge de coupe" - lore3 = "Usure de l'outil" - lore = ["Rayon de tranche", "Temps de recharge de coupe", "Usure de l'outil"] +name = "Machette" +description = "Tranchez le feuillage avec facilite !" +lore1 = "Rayon de tranche" +lore2 = "Temps de recharge de coupe" +lore3 = "Usure de l'outil" +lore = ["Rayon de tranche", "Temps de recharge de coupe", "Usure de l'outil"] [sword.bloody_blade] - name = "Lame sanglante" - description = "Les coups d'epee provoquent le saignement !" - lore1 = "Frapper une entite vivante avec votre epee provoque le saignement" - lore2 = "Duree du saignement" - lore3 = "Temps de recharge du saignement" - lore = ["Frapper une entite vivante avec votre epee provoque le saignement", "Duree du saignement", "Temps de recharge du saignement"] +name = "Lame sanglante" +description = "Les coups d'epee provoquent le saignement !" +lore1 = "Frapper une entite vivante avec votre epee provoque le saignement" +lore2 = "Duree du saignement" +lore3 = "Temps de recharge du saignement" +lore = ["Frapper une entite vivante avec votre epee provoque le saignement", "Duree du saignement", "Temps de recharge du saignement"] [sword.poisoned_blade] - name = "Lame empoisonnee" - description = "Les coups d'epee provoquent l'empoisonnement !" - lore1 = "Frapper une entite vivante avec votre epee provoque l'empoisonnement" - lore2 = "Duree du poison" - lore3 = "Temps de recharge du poison" - lore = ["Frapper une entite vivante avec votre epee provoque l'empoisonnement", "Duree du poison", "Temps de recharge du poison"] +name = "Lame empoisonnee" +description = "Les coups d'epee provoquent l'empoisonnement !" +lore1 = "Frapper une entite vivante avec votre epee provoque l'empoisonnement" +lore2 = "Duree du poison" +lore3 = "Temps de recharge du poison" +lore = ["Frapper une entite vivante avec votre epee provoque l'empoisonnement", "Duree du poison", "Temps de recharge du poison"] # taming [taming] [taming.damage] - name = "Degats d'apprivoisement" - description = "Augmentez les degats infliges par vos animaux apprivoises." - lore1 = "Degats augmentes" - lore = ["Degats augmentes"] +name = "Degats d'apprivoisement" +description = "Augmentez les degats infliges par vos animaux apprivoises." +lore1 = "Degats augmentes" +lore = ["Degats augmentes"] [taming.health] - name = "Sante d'apprivoisement" - description = "Augmentez la sante de vos animaux apprivoises." - lore1 = "Sante augmentee" - lore = ["Sante augmentee"] +name = "Sante d'apprivoisement" +description = "Augmentez la sante de vos animaux apprivoises." +lore1 = "Sante augmentee" +lore = ["Sante augmentee"] [taming.regeneration] - name = "Regeneration d'apprivoisement" - description = "Augmentez la regeneration de vos animaux apprivoises." - lore1 = "PV/s" - lore = ["PV/s"] +name = "Regeneration d'apprivoisement" +description = "Augmentez la regeneration de vos animaux apprivoises." +lore1 = "PV/s" +lore = ["PV/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Epines" - description = "Renvoyez les degats a votre attaquant !" - lore1 = "Degats renvoyes quand vous etes frappe" - lore = ["Degats renvoyes quand vous etes frappe"] +name = "Epines" +description = "Renvoyez les degats a votre attaquant !" +lore1 = "Degats renvoyes quand vous etes frappe" +lore = ["Degats renvoyes quand vous etes frappe"] [tragoul.globe] - name = "Globe de douleur" - description = "Repartissez les degats que vous infligez selon le nombre d'ennemis autour de vous !" - lore1 = "Plus il y a d'ennemis autour de vous, moins vous infligez de degats a chacun" - lore2 = "Portee : " - lore3 = "Degats supplementaires a toutes les entites : " - lore = ["Plus il y a d'ennemis autour de vous, moins vous infligez de degats a chacun", "Portee : ", "Degats supplementaires a toutes les entites : "] +name = "Globe de douleur" +description = "Repartissez les degats que vous infligez selon le nombre d'ennemis autour de vous !" +lore1 = "Plus il y a d'ennemis autour de vous, moins vous infligez de degats a chacun" +lore2 = "Portee : " +lore3 = "Degats supplementaires a toutes les entites : " +lore = ["Plus il y a d'ennemis autour de vous, moins vous infligez de degats a chacun", "Portee : ", "Degats supplementaires a toutes les entites : "] [tragoul.healing] - name = "Volonte de souffrance" - description = "Regagnez de la sante en fonction des degats que vous infligez !" - lore1 = "Faire du mal n'a jamais fait autant de bien ! Soignez-vous grace aux degats infliges" - lore2 = "Il y a une fenetre de degats de 3 secondes, pour le soin et 1 seconde de temps de recharge " - lore3 = "Soin par pourcentage de degats : " - lore = ["Faire du mal n'a jamais fait autant de bien ! Soignez-vous grace aux degats infliges", "Il y a une fenetre de degats de 3 secondes, pour le soin et 1 seconde de temps de recharge ", "Soin par pourcentage de degats : "] +name = "Volonte de souffrance" +description = "Regagnez de la sante en fonction des degats que vous infligez !" +lore1 = "Faire du mal n'a jamais fait autant de bien ! Soignez-vous grace aux degats infliges" +lore2 = "Il y a une fenetre de degats de 3 secondes, pour le soin et 1 seconde de temps de recharge " +lore3 = "Soin par pourcentage de degats : " +lore = ["Faire du mal n'a jamais fait autant de bien ! Soignez-vous grace aux degats infliges", "Il y a une fenetre de degats de 3 secondes, pour le soin et 1 seconde de temps de recharge ", "Soin par pourcentage de degats : "] [tragoul.lance] - name = "Lances de cadavre" - description = "Tuer un ennemi ou qu'une capacite tue un ennemi fait apparaitre une lance qui inflige des degats a un ennemi proche !" - lore1 = "Les lances jaillissent de tout ce que vous tuez, ET si cette capacite tue un ennemi." - lore2 = "Sacrifiez une partie de votre vie pour creer les lances (cela peut vous tuer)" - lore3 = "Lances max : 1 + " - lore = ["Les lances jaillissent de tout ce que vous tuez, ET si cette capacite tue un ennemi.", "Sacrifiez une partie de votre vie pour creer les lances (cela peut vous tuer)", "Lances max : 1 + "] +name = "Lances de cadavre" +description = "Tuer un ennemi ou qu'une capacite tue un ennemi fait apparaitre une lance qui inflige des degats a un ennemi proche !" +lore1 = "Les lances jaillissent de tout ce que vous tuez, ET si cette capacite tue un ennemi." +lore2 = "Sacrifiez une partie de votre vie pour creer les lances (cela peut vous tuer)" +lore3 = "Lances max : 1 + " +lore = ["Les lances jaillissent de tout ce que vous tuez, ET si cette capacite tue un ennemi.", "Sacrifiez une partie de votre vie pour creer les lances (cela peut vous tuer)", "Lances max : 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Canon de verre" - description = "Bonus de degats a mains nues plus votre valeur d'armure est basse" - lore1 = "x degats a 0 armure" - lore2 = "Degats bonus par niveau" - lore = ["x degats a 0 armure", "Degats bonus par niveau"] +name = "Canon de verre" +description = "Bonus de degats a mains nues plus votre valeur d'armure est basse" +lore1 = "x degats a 0 armure" +lore2 = "Degats bonus par niveau" +lore = ["x degats a 0 armure", "Degats bonus par niveau"] [unarmed.power] - name = "Puissance a mains nues" - description = "Degats a mains nues ameliores" - lore1 = "Degats" - lore = ["Degats"] +name = "Puissance a mains nues" +description = "Degats a mains nues ameliores" +lore1 = "Degats" +lore = ["Degats"] [unarmed.sucker_punch] - name = "Coup en traitre" - description = "Coups de poing en sprint, mais plus mortels." - lore1 = "Degats" - lore2 = "Les degats augmentent avec votre vitesse lors de la frappe" - lore = ["Degats", "Les degats augmentent avec votre vitesse lors de la frappe"] +name = "Coup en traitre" +description = "Coups de poing en sprint, mais plus mortels." +lore1 = "Degats" +lore2 = "Les degats augmentent avec votre vitesse lors de la frappe" +lore = ["Degats", "Les degats augmentent avec votre vitesse lors de la frappe"] diff --git a/src/main/resources/he_IL.toml b/src/main/resources/he_IL.toml index e60867864..dd6f27810 100644 --- a/src/main/resources/he_IL.toml +++ b/src/main/resources/he_IL.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "חייב לזוז!" - description = "ללכת יותר מקילומטר אחד (1,000 בלוקים)" +title = "חייב לזוז!" +description = "ללכת יותר מקילומטר אחד (1,000 בלוקים)" [advancement.challenge_sprint_5k] - title = "ספרינט 5K!" - description = "ללכת יותר מ-5 קילומטרים (5,000 בלוקים)" +title = "ספרינט 5K!" +description = "ללכת יותר מ-5 קילומטרים (5,000 בלוקים)" [advancement.challenge_sprint_50k] - title = "זום של 50K!" - description = "ללכת יותר מ-50 קילומטרים (50,000 בלוקים)" +title = "זום של 50K!" +description = "ללכת יותר מ-50 קילומטרים (50,000 בלוקים)" [advancement.challenge_sprint_500k] - title = "לחצות את היקום!!" - description = "ללכת יותר מ-500 קילומטרים (500,000 בלוקים)" +title = "לחצות את היקום!!" +description = "ללכת יותר מ-500 קילומטרים (500,000 בלוקים)" [advancement.challenge_sprint_marathon] - title = "ספרינט מרתון (תרתי משמע)!" - description = "לרוץ מעל 42,195 בלוקים!" +title = "ספרינט מרתון (תרתי משמע)!" +description = "לרוץ מעל 42,195 בלוקים!" [advancement.challenge_place_1k] - title = "בונה מתחיל!" - description = "הנח 1,000 בלוקים" +title = "בונה מתחיל!" +description = "הנח 1,000 בלוקים" [advancement.challenge_place_5k] - title = "בונה בינוני!" - description = "הנח 5,000 בלוקים" +title = "בונה בינוני!" +description = "הנח 5,000 בלוקים" [advancement.challenge_place_50k] - title = "בונה מתקדם!" - description = "הנח 50,000 בלוקים" +title = "בונה מתקדם!" +description = "הנח 50,000 בלוקים" [advancement.challenge_place_500k] - title = "בונה מאסטר!" - description = "הנח 500,000 בלוקים" +title = "בונה מאסטר!" +description = "הנח 500,000 בלוקים" [advancement.challenge_place_5m] - title = "תלמיד הסימטריה!" - description = "המציאות היא מגרש המשחקים שלך! (5 מיליון בלוקים)" +title = "תלמיד הסימטריה!" +description = "המציאות היא מגרש המשחקים שלך! (5 מיליון בלוקים)" [advancement.challenge_chop_1k] - title = "חוטב עצים מתחיל!" - description = "חטוב 1,000 בלוקים" +title = "חוטב עצים מתחיל!" +description = "חטוב 1,000 בלוקים" [advancement.challenge_chop_5k] - title = "חוטב עצים בינוני!" - description = "חטוב 5,000 בלוקים" +title = "חוטב עצים בינוני!" +description = "חטוב 5,000 בלוקים" [advancement.challenge_chop_50k] - title = "חוטב עצים מתקדם!" - description = "חטוב 50,000 בלוקים" +title = "חוטב עצים מתקדם!" +description = "חטוב 50,000 בלוקים" [advancement.challenge_chop_500k] - title = "חוטב עצים מאסטר!" - description = "חטוב 500,000 בלוקים" +title = "חוטב עצים מאסטר!" +description = "חטוב 500,000 בלוקים" [advancement.challenge_chop_5m] - title = "ג'קסון הכלב" - description = "הילד הכי טוב שיש! (5 מיליון בלוקים)" +title = "ג'קסון הכלב" +description = "הילד הכי טוב שיש! (5 מיליון בלוקים)" [advancement.challenge_block_1k] - title = "בקושי חוסם!" - description = "חסום 1000 מכות" +title = "בקושי חוסם!" +description = "חסום 1000 מכות" [advancement.challenge_block_5k] - title = "חסימה זה כיף!" - description = "חסום 5000 מכות" +title = "חסימה זה כיף!" +description = "חסום 5000 מכות" [advancement.challenge_block_50k] - title = "חסימה זה החיים שלי!" - description = "חסום 50,000 מכות" +title = "חסימה זה החיים שלי!" +description = "חסום 50,000 מכות" [advancement.challenge_block_500k] - title = "חסימה היא הייעוד שלי!" - description = "חסום 500,000 מכות" +title = "חסימה היא הייעוד שלי!" +description = "חסום 500,000 מכות" [advancement.challenge_block_5m] - title = "היד הכואבת" - description = "חסום 5,000,000 מכות" +title = "היד הכואבת" +description = "חסום 5,000,000 מכות" [advancement.challenge_brew_1k] - title = "אלכימאי מתחיל!" - description = "צרוך 1000 שיקויים" +title = "אלכימאי מתחיל!" +description = "צרוך 1000 שיקויים" [advancement.challenge_brew_5k] - title = "אלכימאי בינוני!" - description = "צרוך 5000 שיקויים" +title = "אלכימאי בינוני!" +description = "צרוך 5000 שיקויים" [advancement.challenge_brew_50k] - title = "אלכימאי מתקדם!" - description = "צרוך 50,000 שיקויים" +title = "אלכימאי מתקדם!" +description = "צרוך 50,000 שיקויים" [advancement.challenge_brew_500k] - title = "אלכימאי מאסטר!" - description = "צרוך 500,000 שיקויים" +title = "אלכימאי מאסטר!" +description = "צרוך 500,000 שיקויים" [advancement.challenge_brew_5m] - title = "האלכימאי" - description = "צרוך 5,000,000 שיקויים" +title = "האלכימאי" +description = "צרוך 5,000,000 שיקויים" [advancement.challenge_brewsplash_1k] - title = "מתיז שיקויים מתחיל!" - description = "התז 1000 שיקויים" +title = "מתיז שיקויים מתחיל!" +description = "התז 1000 שיקויים" [advancement.challenge_brewsplash_5k] - title = "מתיז שיקויים בינוני!" - description = "התז 5000 שיקויים" +title = "מתיז שיקויים בינוני!" +description = "התז 5000 שיקויים" [advancement.challenge_brewsplash_50k] - title = "מתיז שיקויים מתקדם!" - description = "התז 50,000 שיקויים" +title = "מתיז שיקויים מתקדם!" +description = "התז 50,000 שיקויים" [advancement.challenge_brewsplash_500k] - title = "מתיז שיקויים מאסטר!" - description = "התז 500,000 שיקויים" +title = "מתיז שיקויים מאסטר!" +description = "התז 500,000 שיקויים" [advancement.challenge_brewsplash_5m] - title = "מייסטר ההתזה" - description = "התז 5,000,000 שיקויים" +title = "מייסטר ההתזה" +description = "התז 5,000,000 שיקויים" [advancement.challenge_craft_1k] - title = "יוצר מלאכתי!" - description = "צור 1000 פריטים" +title = "יוצר מלאכתי!" +description = "צור 1000 פריטים" [advancement.challenge_craft_5k] - title = "יוצר עקשן!" - description = "צור 5000 פריטים" +title = "יוצר עקשן!" +description = "צור 5000 פריטים" [advancement.challenge_craft_50k] - title = "יוצר מסור!" - description = "צור 50,000 פריטים" +title = "יוצר מסור!" +description = "צור 50,000 פריטים" [advancement.challenge_craft_500k] - title = "יוצר רועש!" - description = "צור 500,000 פריטים" +title = "יוצר רועש!" +description = "צור 500,000 פריטים" [advancement.challenge_craft_5m] - title = "מיסטר יוצר-פנים" - description = "צור 5,000,000 פריטים" +title = "מיסטר יוצר-פנים" +description = "צור 5,000,000 פריטים" [advancement.challenge_enchant_1k] - title = "קוסם מתחיל!" - description = "כשף 1000 פריטים" +title = "קוסם מתחיל!" +description = "כשף 1000 פריטים" [advancement.challenge_enchant_5k] - title = "קוסם בינוני!" - description = "כשף 5000 פריטים" +title = "קוסם בינוני!" +description = "כשף 5000 פריטים" [advancement.challenge_enchant_50k] - title = "קוסם מתקדם!" - description = "כשף 50,000 פריטים" +title = "קוסם מתקדם!" +description = "כשף 50,000 פריטים" [advancement.challenge_enchant_500k] - title = "קוסם מאסטר!" - description = "כשף 500,000 פריטים" +title = "קוסם מאסטר!" +description = "כשף 500,000 פריטים" [advancement.challenge_enchant_5m] - title = "הקוסם החידתי" - description = "כשף 5,000,000 פריטים" +title = "הקוסם החידתי" +description = "כשף 5,000,000 פריטים" [advancement.challenge_excavate_1k] - title = "חופר נלהב!" - description = "חפור 1000 בלוקים" +title = "חופר נלהב!" +description = "חפור 1000 בלוקים" [advancement.challenge_excavate_5k] - title = "חופר בינוני!" - description = "חפור 5000 בלוקים" +title = "חופר בינוני!" +description = "חפור 5000 בלוקים" [advancement.challenge_excavate_50k] - title = "חופר מתקדם!" - description = "חפור 50,000 בלוקים" +title = "חופר מתקדם!" +description = "חפור 50,000 בלוקים" [advancement.challenge_excavate_500k] - title = "חופר מאסטר!" - description = "חפור 500,000 בלוקים" +title = "חופר מאסטר!" +description = "חפור 500,000 בלוקים" [advancement.challenge_excavate_5m] - title = "החופר החידתי" - description = "חפור 5,000,000 בלוקים" +title = "החופר החידתי" +description = "חפור 5,000,000 בלוקים" [advancement.horrible_person] - title = "אתה אדם נורא" - description = "בלתי נתפס, באמת" +title = "אתה אדם נורא" +description = "בלתי נתפס, באמת" [advancement.challenge_turtle_egg_smasher] - title = "מרסק ביצי צבים!" - description = "שבור 100 ביצי צבים" +title = "מרסק ביצי צבים!" +description = "שבור 100 ביצי צבים" [advancement.challenge_turtle_egg_annihilator] - title = "משמיד ביצי צבים!" - description = "שבור 500 ביצי צבים" +title = "משמיד ביצי צבים!" +description = "שבור 500 ביצי צבים" [advancement.challenge_novice_hunter] - title = "צייד מתחיל!" - description = "הרוג 100 ישויות" +title = "צייד מתחיל!" +description = "הרוג 100 ישויות" [advancement.challenge_intermediate_hunter] - title = "צייד בינוני!" - description = "הרוג 500 ישויות" +title = "צייד בינוני!" +description = "הרוג 500 ישויות" [advancement.challenge_advanced_hunter] - title = "צייד מתקדם!" - description = "הרוג 5000 ישויות" +title = "צייד מתקדם!" +description = "הרוג 5000 ישויות" [advancement.challenge_creeper_conqueror] - title = "כובש קריפרים!" - description = "הרוג 50 קריפרים" +title = "כובש קריפרים!" +description = "הרוג 50 קריפרים" [advancement.challenge_creeper_annihilator] - title = "משמיד קריפרים!" - description = "הרוג 200 קריפרים" +title = "משמיד קריפרים!" +description = "הרוג 200 קריפרים" [advancement.challenge_pickaxe_1k] - title = "כורה מתחיל" - description = "שבור 1000 בלוקים" +title = "כורה מתחיל" +description = "שבור 1000 בלוקים" [advancement.challenge_pickaxe_5k] - title = "כורה מיומן" - description = "שבור 5000 בלוקים" +title = "כורה מיומן" +description = "שבור 5000 בלוקים" [advancement.challenge_pickaxe_50k] - title = "כורה מומחה" - description = "שבור 50,000 בלוקים" +title = "כורה מומחה" +description = "שבור 50,000 בלוקים" [advancement.challenge_pickaxe_500k] - title = "כורה מאסטר" - description = "שבור 500,000 בלוקים" +title = "כורה מאסטר" +description = "שבור 500,000 בלוקים" [advancement.challenge_pickaxe_5m] - title = "כורה אגדי" - description = "שבור 5,000,000 בלוקים" +title = "כורה אגדי" +description = "שבור 5,000,000 בלוקים" [advancement.challenge_eat_100] - title = "כל כך הרבה לאכול!" - description = "אכול מעל 100 פריטים!" +title = "כל כך הרבה לאכול!" +description = "אכול מעל 100 פריטים!" [advancement.challenge_eat_1000] - title = "רעב בלתי ניתן לריווי!" - description = "אכול מעל 1,000 פריטים!" +title = "רעב בלתי ניתן לריווי!" +description = "אכול מעל 1,000 פריטים!" [advancement.challenge_eat_10000] - title = "רעב נצחי!" - description = "אכול מעל 10,000 פריטים!" +title = "רעב נצחי!" +description = "אכול מעל 10,000 פריטים!" [advancement.challenge_harvest_100] - title = "קציר מלא" - description = "קצור מעל 100 יבולים!" +title = "קציר מלא" +description = "קצור מעל 100 יבולים!" [advancement.challenge_harvest_1000] - title = "קציר גדול" - description = "קצור מעל 1,000 יבולים!" +title = "קציר גדול" +description = "קצור מעל 1,000 יבולים!" [advancement.challenge_swim_1nm] - title = "צוללת אנושית!" - description = "שחה מייל ימי אחד (1,852 בלוקים)" +title = "צוללת אנושית!" +description = "שחה מייל ימי אחד (1,852 בלוקים)" [advancement.challenge_sneak_1k] - title = "כאב ברכיים" - description = "התגנב מעל קילומטר (1,000 בלוקים)" +title = "כאב ברכיים" +description = "התגנב מעל קילומטר (1,000 בלוקים)" [advancement.challenge_sneak_5k] - title = "הולך הצללים" - description = "התגנב מעל 5,000 בלוקים" +title = "הולך הצללים" +description = "התגנב מעל 5,000 בלוקים" [advancement.challenge_sneak_20k] - title = "רוח רפאים" - description = "התגנב מעל 20,000 בלוקים" +title = "רוח רפאים" +description = "התגנב מעל 20,000 בלוקים" [advancement.challenge_swim_5k] - title = "צולל מעמקים" - description = "שחה מעל 5,000 בלוקים" +title = "צולל מעמקים" +description = "שחה מעל 5,000 בלוקים" [advancement.challenge_swim_20k] - title = "בחיר פוסידון" - description = "שחה מעל 20,000 בלוקים" +title = "בחיר פוסידון" +description = "שחה מעל 20,000 בלוקים" [advancement.challenge_sword_100] - title = "דם ראשון" - description = "פגע 100 פעמים בחרב" +title = "דם ראשון" +description = "פגע 100 פעמים בחרב" [advancement.challenge_sword_1k] - title = "רוקד החרבות" - description = "פגע 1,000 פעמים בחרב" +title = "רוקד החרבות" +description = "פגע 1,000 פעמים בחרב" [advancement.challenge_sword_10k] - title = "אלף חתכים" - description = "פגע 10,000 פעמים בחרב" +title = "אלף חתכים" +description = "פגע 10,000 פעמים בחרב" [advancement.challenge_unarmed_100] - title = "קטטן בר" - description = "פגע 100 פעמים בידיים חשופות" +title = "קטטן בר" +description = "פגע 100 פעמים בידיים חשופות" [advancement.challenge_unarmed_1k] - title = "אגרופי ברזל" - description = "פגע 1,000 פעמים בידיים חשופות" +title = "אגרופי ברזל" +description = "פגע 1,000 פעמים בידיים חשופות" [advancement.challenge_unarmed_10k] - title = "אגרוף אחד" - description = "פגע 10,000 פעמים בידיים חשופות" +title = "אגרוף אחד" +description = "פגע 10,000 פעמים בידיים חשופות" [advancement.challenge_trag_1k] - title = "מחיר הדם" - description = "קבל 1,000 נזק" +title = "מחיר הדם" +description = "קבל 1,000 נזק" [advancement.challenge_trag_10k] - title = "גאות ארגמן" - description = "קבל 10,000 נזק" +title = "גאות ארגמן" +description = "קבל 10,000 נזק" [advancement.challenge_trag_100k] - title = "אווטאר הסבל" - description = "קבל 100,000 נזק" +title = "אווטאר הסבל" +description = "קבל 100,000 נזק" [advancement.challenge_ranged_100] - title = "תרגול מטרות" - description = "ירה 100 קליעים" +title = "תרגול מטרות" +description = "ירה 100 קליעים" [advancement.challenge_ranged_1k] - title = "עין הנץ" - description = "ירה 1,000 קליעים" +title = "עין הנץ" +description = "ירה 1,000 קליעים" [advancement.challenge_ranged_10k] - title = "סערת חיצים" - description = "ירה 10,000 קליעים" +title = "סערת חיצים" +description = "ירה 10,000 קליעים" [advancement.challenge_chronos_1h] - title = "טיק טוק" - description = "בלה שעה אחת מחובר" +title = "טיק טוק" +description = "בלה שעה אחת מחובר" [advancement.challenge_chronos_24h] - title = "חולות הזמן" - description = "בלה 24 שעות מחובר" +title = "חולות הזמן" +description = "בלה 24 שעות מחובר" [advancement.challenge_chronos_168h] - title = "נצחי" - description = "בלה 168 שעות (שבוע) מחובר" +title = "נצחי" +description = "בלה 168 שעות (שבוע) מחובר" [advancement.challenge_nether_50] - title = "שומר שערי הגיהנום" - description = "הרוג 50 יצורי נתר" +title = "שומר שערי הגיהנום" +description = "הרוג 50 יצורי נתר" [advancement.challenge_nether_500] - title = "שומר התהום" - description = "הרוג 500 יצורי נתר" +title = "שומר התהום" +description = "הרוג 500 יצורי נתר" [advancement.challenge_nether_5k] - title = "אדון הנתר" - description = "הרוג 5,000 יצורי נתר" +title = "אדון הנתר" +description = "הרוג 5,000 יצורי נתר" [advancement.challenge_rift_50] - title = "חריגה מרחבית" - description = "השתגר 50 פעמים" +title = "חריגה מרחבית" +description = "השתגר 50 פעמים" [advancement.challenge_rift_500] - title = "הולך הריק" - description = "השתגר 500 פעמים" +title = "הולך הריק" +description = "השתגר 500 פעמים" [advancement.challenge_rift_5k] - title = "בין העולמות" - description = "השתגר 5,000 פעמים" +title = "בין העולמות" +description = "השתגר 5,000 פעמים" [advancement.challenge_taming_10] - title = "לוחש לחיות" - description = "הרבה 10 חיות" +title = "לוחש לחיות" +description = "הרבה 10 חיות" [advancement.challenge_taming_50] - title = "מנהיג הלהקה" - description = "הרבה 50 חיות" +title = "מנהיג הלהקה" +description = "הרבה 50 חיות" [advancement.challenge_taming_500] - title = "אדון החיות" - description = "הרבה 500 חיות" +title = "אדון החיות" +description = "הרבה 500 חיות" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "שד המהירות" - description = "רוץ מעל 5 קילומטרים (5,000 בלוקים)" +title = "שד המהירות" +description = "רוץ מעל 5 קילומטרים (5,000 בלוקים)" [advancement.challenge_sprint_dist_50k] - title = "רגלי ברק" - description = "רוץ מעל 50 קילומטרים (50,000 בלוקים)" +title = "רגלי ברק" +description = "רוץ מעל 50 קילומטרים (50,000 בלוקים)" [advancement.challenge_agility_swim_1k] - title = "פוסע על המים" - description = "שחה מעל קילומטר אחד (1,000 בלוקים)" +title = "פוסע על המים" +description = "שחה מעל קילומטר אחד (1,000 בלוקים)" [advancement.challenge_agility_swim_10k] - title = "מסע ימי" - description = "שחה מעל 10 קילומטרים (10,000 בלוקים)" +title = "מסע ימי" +description = "שחה מעל 10 קילומטרים (10,000 בלוקים)" [advancement.challenge_fly_1k] - title = "רקדן השמיים" - description = "טוס מעל קילומטר אחד (1,000 בלוקים)" +title = "רקדן השמיים" +description = "טוס מעל קילומטר אחד (1,000 בלוקים)" [advancement.challenge_fly_10k] - title = "רוכב הרוח" - description = "טוס מעל 10 קילומטרים (10,000 בלוקים)" +title = "רוכב הרוח" +description = "טוס מעל 10 קילומטרים (10,000 בלוקים)" [advancement.challenge_agility_sneak_500] - title = "צעדים שקטים" - description = "התגנב מעל 500 בלוקים" +title = "צעדים שקטים" +description = "התגנב מעל 500 בלוקים" [advancement.challenge_agility_sneak_5k] - title = "צעדי רפאים" - description = "התגנב מעל 5 קילומטרים (5,000 בלוקים)" +title = "צעדי רפאים" +description = "התגנב מעל 5 קילומטרים (5,000 בלוקים)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "צוות הריסה" - description = "שבור 500 בלוקים" +title = "צוות הריסה" +description = "שבור 500 בלוקים" [advancement.challenge_demolish_5k] - title = "כדור ההריסה" - description = "שבור 5,000 בלוקים" +title = "כדור ההריסה" +description = "שבור 5,000 בלוקים" [advancement.challenge_value_placed_10k] - title = "בנאי בעל ערך" - description = "הנח בלוקים בשווי 10,000" +title = "בנאי בעל ערך" +description = "הנח בלוקים בשווי 10,000" [advancement.challenge_value_placed_100k] - title = "אדריכל ראשי" - description = "הנח בלוקים בשווי 100,000" +title = "אדריכל ראשי" +description = "הנח בלוקים בשווי 100,000" [advancement.challenge_demolish_val_5k] - title = "מומחה מיחזור" - description = "מחזר 5,000 ערך בלוקים מהריסה" +title = "מומחה מיחזור" +description = "מחזר 5,000 ערך בלוקים מהריסה" [advancement.challenge_demolish_val_50k] - title = "פירוק מוחלט" - description = "מחזר 50,000 ערך בלוקים מהריסה" +title = "פירוק מוחלט" +description = "מחזר 50,000 ערך בלוקים מהריסה" [advancement.challenge_high_build_100] - title = "בנאי גבהים" - description = "הנח 100 בלוקים מעל Y=128" +title = "בנאי גבהים" +description = "הנח 100 בלוקים מעל Y=128" [advancement.challenge_high_build_1k] - title = "אדריכל העננים" - description = "הנח 1,000 בלוקים מעל Y=128" +title = "אדריכל העננים" +description = "הנח 1,000 בלוקים מעל Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "מניף הגרזן" - description = "הנף את הגרזן 500 פעמים" +title = "מניף הגרזן" +description = "הנף את הגרזן 500 פעמים" [advancement.challenge_axe_swing_5k] - title = "ברסרקר" - description = "הנף את הגרזן 5,000 פעמים" +title = "ברסרקר" +description = "הנף את הגרזן 5,000 פעמים" [advancement.challenge_axe_damage_1k] - title = "מבתר" - description = "גרום 1,000 נזק עם גרזנים" +title = "מבתר" +description = "גרום 1,000 נזק עם גרזנים" [advancement.challenge_axe_damage_10k] - title = "גרזן התליין" - description = "גרום 10,000 נזק עם גרזנים" +title = "גרזן התליין" +description = "גרום 10,000 נזק עם גרזנים" [advancement.challenge_axe_value_5k] - title = "סוחר עצים" - description = "כרות עץ בשווי 5,000" +title = "סוחר עצים" +description = "כרות עץ בשווי 5,000" [advancement.challenge_axe_value_50k] - title = "מלך הכריתה" - description = "כרות עץ בשווי 50,000" +title = "מלך הכריתה" +description = "כרות עץ בשווי 50,000" [advancement.challenge_leaves_500] - title = "מפוח עלים" - description = "נקה 500 בלוקי עלים עם גרזן" +title = "מפוח עלים" +description = "נקה 500 בלוקי עלים עם גרזן" [advancement.challenge_leaves_5k] - title = "משיר העלים" - description = "נקה 5,000 בלוקי עלים עם גרזן" +title = "משיר העלים" +description = "נקה 5,000 בלוקי עלים עם גרזן" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "סופג נזק" - description = "חסום 1,000 נזק עם מגן" +title = "סופג נזק" +description = "חסום 1,000 נזק עם מגן" [advancement.challenge_block_dmg_10k] - title = "מגן אנושי" - description = "חסום 10,000 נזק עם מגן" +title = "מגן אנושי" +description = "חסום 10,000 נזק עם מגן" [advancement.challenge_block_proj_100] - title = "מסיט חצים" - description = "חסום 100 קליעים עם מגן" +title = "מסיט חצים" +description = "חסום 100 קליעים עם מגן" [advancement.challenge_block_proj_1k] - title = "מגן קליעים" - description = "חסום 1,000 קליעים עם מגן" +title = "מגן קליעים" +description = "חסום 1,000 קליעים עם מגן" [advancement.challenge_block_melee_500] - title = "אמן הפריירה" - description = "חסום 500 תקיפות קרב קרוב עם מגן" +title = "אמן הפריירה" +description = "חסום 500 תקיפות קרב קרוב עם מגן" [advancement.challenge_block_melee_5k] - title = "מבצר הברזל" - description = "חסום 5,000 תקיפות קרב קרוב עם מגן" +title = "מבצר הברזל" +description = "חסום 5,000 תקיפות קרב קרוב עם מגן" [advancement.challenge_block_heavy_50] - title = "טנק" - description = "חסום 50 תקיפות כבדות (מעל 5 נזק)" +title = "טנק" +description = "חסום 50 תקיפות כבדות (מעל 5 נזק)" [advancement.challenge_block_heavy_500] - title = "עצם בלתי ניתן להזזה" - description = "חסום 500 תקיפות כבדות (מעל 5 נזק)" +title = "עצם בלתי ניתן להזזה" +description = "חסום 500 תקיפות כבדות (מעל 5 נזק)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "תחנת שיקויים" - description = "הנח 10 מעמדי שיקויים" +title = "תחנת שיקויים" +description = "הנח 10 מעמדי שיקויים" [advancement.challenge_brew_stands_50] - title = "מפעל שיקויים" - description = "הנח 50 מעמדי שיקויים" +title = "מפעל שיקויים" +description = "הנח 50 מעמדי שיקויים" [advancement.challenge_brew_strong_25] - title = "שיקוי חזק" - description = "שתה 25 שיקויים משודרגים" +title = "שיקוי חזק" +description = "שתה 25 שיקויים משודרגים" [advancement.challenge_brew_strong_250] - title = "עוצמה מרבית" - description = "שתה 250 שיקויים משודרגים" +title = "עוצמה מרבית" +description = "שתה 250 שיקויים משודרגים" [advancement.challenge_brew_splash_hits_50] - title = "אזור התזה" - description = "פגע ב-50 ישויות עם שיקויי התזה" +title = "אזור התזה" +description = "פגע ב-50 ישויות עם שיקויי התזה" [advancement.challenge_brew_splash_hits_500] - title = "רופא המגיפה" - description = "פגע ב-500 ישויות עם שיקויי התזה" +title = "רופא המגיפה" +description = "פגע ב-500 ישויות עם שיקויי התזה" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "חסר מנוחה" - description = "עבור קילומטר אחד בזמן פעיל" +title = "חסר מנוחה" +description = "עבור קילומטר אחד בזמן פעיל" [advancement.challenge_active_dist_10k] - title = "סולל דרכים" - description = "עבור 10 קילומטרים בזמן פעיל" +title = "סולל דרכים" +description = "עבור 10 קילומטרים בזמן פעיל" [advancement.challenge_active_dist_100k] - title = "תנועה מתמדת" - description = "עבור 100 קילומטרים בזמן פעיל" +title = "תנועה מתמדת" +description = "עבור 100 קילומטרים בזמן פעיל" [advancement.challenge_beds_10] - title = "משכים קום" - description = "ישן במיטה 10 פעמים" +title = "משכים קום" +description = "ישן במיטה 10 פעמים" [advancement.challenge_beds_100] - title = "מדלג על הזמן" - description = "ישן במיטה 100 פעמים" +title = "מדלג על הזמן" +description = "ישן במיטה 100 פעמים" [advancement.challenge_chronos_tp_50] - title = "הסטה זמנית" - description = "השתגר 50 פעמים" +title = "הסטה זמנית" +description = "השתגר 50 פעמים" [advancement.challenge_chronos_tp_500] - title = "עיוות זמן" - description = "השתגר 500 פעמים" +title = "עיוות זמן" +description = "השתגר 500 פעמים" [advancement.challenge_chronos_deaths_10] - title = "בן תמותה" - description = "מות 10 פעמים" +title = "בן תמותה" +description = "מות 10 פעמים" [advancement.challenge_chronos_deaths_100] - title = "מתגרה במוות" - description = "מות 100 פעמים" +title = "מתגרה במוות" +description = "מות 100 פעמים" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "יצירה בעלת ערך" - description = "צור חפצים בשווי כולל של 10,000" +title = "יצירה בעלת ערך" +description = "צור חפצים בשווי כולל של 10,000" [advancement.challenge_craft_value_100k] - title = "אומן" - description = "צור חפצים בשווי כולל של 100,000" +title = "אומן" +description = "צור חפצים בשווי כולל של 100,000" [advancement.challenge_craft_tools_25] - title = "יוצר כלים" - description = "צור 25 כלים" +title = "יוצר כלים" +description = "צור 25 כלים" [advancement.challenge_craft_tools_250] - title = "נפח ראשי" - description = "צור 250 כלים" +title = "נפח ראשי" +description = "צור 250 כלים" [advancement.challenge_craft_armor_25] - title = "יוצר שריון" - description = "צור 25 חלקי שריון" +title = "יוצר שריון" +description = "צור 25 חלקי שריון" [advancement.challenge_craft_armor_250] - title = "אמן השריון" - description = "צור 250 חלקי שריון" +title = "אמן השריון" +description = "צור 250 חלקי שריון" [advancement.challenge_craft_mass_25k] - title = "ייצור המוני" - description = "צור 25,000 חפצים" +title = "ייצור המוני" +description = "צור 25,000 חפצים" [advancement.challenge_craft_mass_250k] - title = "מהפכה תעשייתית" - description = "צור 250,000 חפצים" +title = "מהפכה תעשייתית" +description = "צור 250,000 חפצים" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "אספן" - description = "גלה 50 חפצים ייחודיים" +title = "אספן" +description = "גלה 50 חפצים ייחודיים" [advancement.challenge_discover_items_250] - title = "מקטלג" - description = "גלה 250 חפצים ייחודיים" +title = "מקטלג" +description = "גלה 250 חפצים ייחודיים" [advancement.challenge_discover_blocks_50] - title = "סוקר" - description = "גלה 50 בלוקים ייחודיים" +title = "סוקר" +description = "גלה 50 בלוקים ייחודיים" [advancement.challenge_discover_blocks_250] - title = "גיאולוג" - description = "גלה 250 בלוקים ייחודיים" +title = "גיאולוג" +description = "גלה 250 בלוקים ייחודיים" [advancement.challenge_discover_mobs_25] - title = "צופה" - description = "גלה 25 מובים ייחודיים" +title = "צופה" +description = "גלה 25 מובים ייחודיים" [advancement.challenge_discover_mobs_75] - title = "חוקר טבע" - description = "גלה 75 מובים ייחודיים" +title = "חוקר טבע" +description = "גלה 75 מובים ייחודיים" [advancement.challenge_discover_biomes_10] - title = "נווד" - description = "גלה 10 ביומים ייחודיים" +title = "נווד" +description = "גלה 10 ביומים ייחודיים" [advancement.challenge_discover_biomes_40] - title = "מטייל העולם" - description = "גלה 40 ביומים ייחודיים" +title = "מטייל העולם" +description = "גלה 40 ביומים ייחודיים" [advancement.challenge_discover_foods_10] - title = "שפית" - description = "גלה 10 מאכלים ייחודיים" +title = "שפית" +description = "גלה 10 מאכלים ייחודיים" [advancement.challenge_discover_foods_30] - title = "מאסטר שף" - description = "גלה 30 מאכלים ייחודיים" +title = "מאסטר שף" +description = "גלה 30 מאכלים ייחודיים" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "אורג כוח" - description = "צבור 100 כוח כישוף" +title = "אורג כוח" +description = "צבור 100 כוח כישוף" [advancement.challenge_enchant_power_1k] - title = "אדון הקסם" - description = "צבור 1,000 כוח כישוף" +title = "אדון הקסם" +description = "צבור 1,000 כוח כישוף" [advancement.challenge_enchant_levels_1k] - title = "מבזבז רמות" - description = "הוצא 1,000 רמות ניסיון על כישוף" +title = "מבזבז רמות" +description = "הוצא 1,000 רמות ניסיון על כישוף" [advancement.challenge_enchant_levels_10k] - title = "בור ניסיון" - description = "הוצא 10,000 רמות ניסיון על כישוף" +title = "בור ניסיון" +description = "הוצא 10,000 רמות ניסיון על כישוף" [advancement.challenge_enchant_high_25] - title = "מהמר גדול" - description = "בצע 25 כישופים ברמה מקסימלית" +title = "מהמר גדול" +description = "בצע 25 כישופים ברמה מקסימלית" [advancement.challenge_enchant_high_250] - title = "מכשף אגדי" - description = "בצע 250 כישופים ברמה מקסימלית" +title = "מכשף אגדי" +description = "בצע 250 כישופים ברמה מקסימלית" [advancement.challenge_enchant_total_500] - title = "שורף רמות" - description = "הוצא 500 רמות סה\"כ על כל הכישופים" +title = "שורף רמות" +description = "הוצא 500 רמות סה\"כ על כל הכישופים" [advancement.challenge_enchant_total_5k] - title = "השקעה קסומה" - description = "הוצא 5,000 רמות סה\"כ על כל הכישופים" +title = "השקעה קסומה" +description = "הוצא 5,000 רמות סה\"כ על כל הכישופים" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "חופר" - description = "הנף את האת 500 פעמים" +title = "חופר" +description = "הנף את האת 500 פעמים" [advancement.challenge_dig_swing_5k] - title = "מחפרון" - description = "הנף את האת 5,000 פעמים" +title = "מחפרון" +description = "הנף את האת 5,000 פעמים" [advancement.challenge_dig_damage_1k] - title = "אביר האת" - description = "גרום 1,000 נזק עם את" +title = "אביר האת" +description = "גרום 1,000 נזק עם את" [advancement.challenge_dig_damage_10k] - title = "אדון האת" - description = "גרום 10,000 נזק עם את" +title = "אדון האת" +description = "גרום 10,000 נזק עם את" [advancement.challenge_dig_value_5k] - title = "סוחר אדמה" - description = "חפור בלוקים בשווי 5,000" +title = "סוחר אדמה" +description = "חפור בלוקים בשווי 5,000" [advancement.challenge_dig_value_50k] - title = "אדון האדמות" - description = "חפור בלוקים בשווי 50,000" +title = "אדון האדמות" +description = "חפור בלוקים בשווי 50,000" [advancement.challenge_dig_gravel_500] - title = "טוחן חצץ" - description = "חפור 500 בלוקי חצץ, חול או חרסית" +title = "טוחן חצץ" +description = "חפור 500 בלוקי חצץ, חול או חרסית" [advancement.challenge_dig_gravel_5k] - title = "מנפה חול" - description = "חפור 5,000 בלוקי חצץ, חול או חרסית" +title = "מנפה חול" +description = "חפור 5,000 בלוקי חצץ, חול או חרסית" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "זורע" - description = "שתול 100 גידולים" +title = "זורע" +description = "שתול 100 גידולים" [advancement.challenge_plant_1k] - title = "אגודל ירוק" - description = "שתול 1,000 גידולים" +title = "אגודל ירוק" +description = "שתול 1,000 גידולים" [advancement.challenge_plant_5k] - title = "מלך החקלאות" - description = "שתול 5,000 גידולים" +title = "מלך החקלאות" +description = "שתול 5,000 גידולים" [advancement.challenge_compost_50] - title = "ממחזר" - description = "הכנס 50 חפצים לקומפוסט" +title = "ממחזר" +description = "הכנס 50 חפצים לקומפוסט" [advancement.challenge_compost_500] - title = "מעשיר אדמה" - description = "הכנס 500 חפצים לקומפוסט" +title = "מעשיר אדמה" +description = "הכנס 500 חפצים לקומפוסט" [advancement.challenge_shear_50] - title = "גוזז" - description = "גזוז 50 ישויות" +title = "גוזז" +description = "גזוז 50 ישויות" [advancement.challenge_shear_250] - title = "אדון העדר" - description = "גזוז 250 ישויות" +title = "אדון העדר" +description = "גזוז 250 ישויות" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "קוטל" - description = "הרוג 500 יצורים" +title = "קוטל" +description = "הרוג 500 יצורים" [advancement.challenge_kills_5k] - title = "תליין" - description = "הרוג 5,000 יצורים" +title = "תליין" +description = "הרוג 5,000 יצורים" [advancement.challenge_boss_1] - title = "מאתגר בוסים" - description = "הרוג מוב בוס" +title = "מאתגר בוסים" +description = "הרוג מוב בוס" [advancement.challenge_boss_10] - title = "רוצח אגדות" - description = "הרוג 10 מובי בוס" +title = "רוצח אגדות" +description = "הרוג 10 מובי בוס" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "נקלע לכמישה" - description = "ספוג 500 נזק כמישה" +title = "נקלע לכמישה" +description = "ספוג 500 נזק כמישה" [advancement.challenge_wither_dmg_5k] - title = "שורד המגיפה" - description = "ספוג 5,000 נזק כמישה" +title = "שורד המגיפה" +description = "ספוג 5,000 נזק כמישה" [advancement.challenge_wither_skel_25] - title = "אוסף עצמות" - description = "הרוג 25 שלדי ויתר" +title = "אוסף עצמות" +description = "הרוג 25 שלדי ויתר" [advancement.challenge_wither_skel_250] - title = "מכת השלדים" - description = "הרוג 250 שלדי ויתר" +title = "מכת השלדים" +description = "הרוג 250 שלדי ויתר" [advancement.challenge_wither_boss_1] - title = "קוטל הויתר" - description = "הבס את הויתר" +title = "קוטל הויתר" +description = "הבס את הויתר" [advancement.challenge_wither_boss_10] - title = "שליט הנתר" - description = "הבס את הויתר 10 פעמים" +title = "שליט הנתר" +description = "הבס את הויתר 10 פעמים" [advancement.challenge_roses_10] - title = "גנן המוות" - description = "שבור 10 ורדי ויתר" +title = "גנן המוות" +description = "שבור 10 ורדי ויתר" [advancement.challenge_roses_100] - title = "אוסף הכמישה" - description = "שבור 100 ורדי ויתר" +title = "אוסף הכמישה" +description = "שבור 100 ורדי ויתר" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "זרוע הכורה" - description = "הנף את המכוש 500 פעמים" +title = "זרוע הכורה" +description = "הנף את המכוש 500 פעמים" [advancement.challenge_pick_swing_5k] - title = "כורה מנהרות" - description = "הנף את המכוש 5,000 פעמים" +title = "כורה מנהרות" +description = "הנף את המכוש 5,000 פעמים" [advancement.challenge_pick_damage_1k] - title = "לוחם מכוש" - description = "גרום 1,000 נזק עם מכוש" +title = "לוחם מכוש" +description = "גרום 1,000 נזק עם מכוש" [advancement.challenge_pick_damage_10k] - title = "מכוש מלחמה" - description = "גרום 10,000 נזק עם מכוש" +title = "מכוש מלחמה" +description = "גרום 10,000 נזק עם מכוש" [advancement.challenge_pick_value_5k] - title = "מוצא אבני חן" - description = "כרה בלוקים בשווי 5,000" +title = "מוצא אבני חן" +description = "כרה בלוקים בשווי 5,000" [advancement.challenge_pick_value_50k] - title = "מלך המחצבים" - description = "כרה בלוקים בשווי 50,000" +title = "מלך המחצבים" +description = "כרה בלוקים בשווי 50,000" [advancement.challenge_pick_ores_500] - title = "מחפש מחצבים" - description = "כרה 500 בלוקי עפרה" +title = "מחפש מחצבים" +description = "כרה 500 בלוקי עפרה" [advancement.challenge_pick_ores_5k] - title = "כורה ראשי" - description = "כרה 5,000 בלוקי עפרה" +title = "כורה ראשי" +description = "כרה 5,000 בלוקי עפרה" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "צלף מדויק" - description = "גרום 1,000 נזק מרחוק" +title = "צלף מדויק" +description = "גרום 1,000 נזק מרחוק" [advancement.challenge_ranged_dmg_10k] - title = "קשת קטלני" - description = "גרום 10,000 נזק מרחוק" +title = "קשת קטלני" +description = "גרום 10,000 נזק מרחוק" [advancement.challenge_ranged_dist_5k] - title = "טווח ארוך" - description = "ירה קליעים שכיסו מרחק כולל של 5,000 בלוקים" +title = "טווח ארוך" +description = "ירה קליעים שכיסו מרחק כולל של 5,000 בלוקים" [advancement.challenge_ranged_dist_50k] - title = "יורה לקילומטרים" - description = "ירה קליעים שכיסו מרחק כולל של 50,000 בלוקים" +title = "יורה לקילומטרים" +description = "ירה קליעים שכיסו מרחק כולל של 50,000 בלוקים" [advancement.challenge_ranged_kills_50] - title = "קשת" - description = "הרוג 50 מובים עם נשק טווח" +title = "קשת" +description = "הרוג 50 מובים עם נשק טווח" [advancement.challenge_ranged_kills_500] - title = "קשת מאסטר" - description = "הרוג 500 מובים עם נשק טווח" +title = "קשת מאסטר" +description = "הרוג 500 מובים עם נשק טווח" [advancement.challenge_longshot_25] - title = "צלף" - description = "פגע 25 פגיעות מרחוק (מעל 30 בלוקים)" +title = "צלף" +description = "פגע 25 פגיעות מרחוק (מעל 30 בלוקים)" [advancement.challenge_longshot_250] - title = "עין הנשר" - description = "פגע 250 פגיעות מרחוק (מעל 30 בלוקים)" +title = "עין הנשר" +description = "פגע 250 פגיעות מרחוק (מעל 30 בלוקים)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "זורק פנינים" - description = "זרוק 50 פניני אנדר" +title = "זורק פנינים" +description = "זרוק 50 פניני אנדר" [advancement.challenge_rift_pearls_500] - title = "מכור לשיגור" - description = "זרוק 500 פניני אנדר" +title = "מכור לשיגור" +description = "זרוק 500 פניני אנדר" [advancement.challenge_rift_enderman_50] - title = "צייד אנדרמנים" - description = "הרוג 50 אנדרמנים" +title = "צייד אנדרמנים" +description = "הרוג 50 אנדרמנים" [advancement.challenge_rift_enderman_500] - title = "רודף הריק" - description = "הרוג 500 אנדרמנים" +title = "רודף הריק" +description = "הרוג 500 אנדרמנים" [advancement.challenge_rift_dragon_500] - title = "לוחם הדרקון" - description = "גרום 500 נזק לדרקון האנדר" +title = "לוחם הדרקון" +description = "גרום 500 נזק לדרקון האנדר" [advancement.challenge_rift_dragon_5k] - title = "מכת הדרקון" - description = "גרום 5,000 נזק לדרקון האנדר" +title = "מכת הדרקון" +description = "גרום 5,000 נזק לדרקון האנדר" [advancement.challenge_rift_crystal_10] - title = "שובר גבישים" - description = "השמד 10 גבישי קצה" +title = "שובר גבישים" +description = "השמד 10 גבישי קצה" [advancement.challenge_rift_crystal_100] - title = "הורס הקצה" - description = "השמד 100 גבישי קצה" +title = "הורס הקצה" +description = "השמד 100 גבישי קצה" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "דייג" - description = "תפוס 25 דגים" +title = "דייג" +description = "תפוס 25 דגים" [advancement.challenge_fish_250] - title = "דייג מאסטר" - description = "תפוס 250 דגים" +title = "דייג מאסטר" +description = "תפוס 250 דגים" [advancement.challenge_drowned_25] - title = "צייד טבועים" - description = "הרוג 25 טבועים" +title = "צייד טבועים" +description = "הרוג 25 טבועים" [advancement.challenge_drowned_250] - title = "מנקה האוקיינוס" - description = "הרוג 250 טבועים" +title = "מנקה האוקיינוס" +description = "הרוג 250 טבועים" [advancement.challenge_guardian_10] - title = "קוטל שומרים" - description = "הרוג 10 שומרים" +title = "קוטל שומרים" +description = "הרוג 10 שומרים" [advancement.challenge_guardian_100] - title = "פורץ מקדשים" - description = "הרוג 100 שומרים" +title = "פורץ מקדשים" +description = "הרוג 100 שומרים" [advancement.challenge_underwater_blocks_100] - title = "כורה תת-מימי" - description = "שבור 100 בלוקים מתחת למים" +title = "כורה תת-מימי" +description = "שבור 100 בלוקים מתחת למים" [advancement.challenge_underwater_blocks_1k] - title = "מהנדס תת-מימי" - description = "שבור 1,000 בלוקים מתחת למים" +title = "מהנדס תת-מימי" +description = "שבור 1,000 בלוקים מתחת למים" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "דוקר גב" - description = "גרום 500 נזק בזחילה" +title = "דוקר גב" +description = "גרום 500 נזק בזחילה" [advancement.challenge_stealth_dmg_5k] - title = "רוצח שקט" - description = "גרום 5,000 נזק בזחילה" +title = "רוצח שקט" +description = "גרום 5,000 נזק בזחילה" [advancement.challenge_stealth_kills_10] - title = "מתנקש" - description = "הרוג 10 מובים בזחילה" +title = "מתנקש" +description = "הרוג 10 מובים בזחילה" [advancement.challenge_stealth_kills_100] - title = "קוצר הצללים" - description = "הרוג 100 מובים בזחילה" +title = "קוצר הצללים" +description = "הרוג 100 מובים בזחילה" [advancement.challenge_stealth_time_1h] - title = "סבלני" - description = "בלה שעה אחת בזחילה (3,600 שניות)" +title = "סבלני" +description = "בלה שעה אחת בזחילה (3,600 שניות)" [advancement.challenge_stealth_time_10h] - title = "אדון הצללים" - description = "בלה 10 שעות בזחילה (36,000 שניות)" +title = "אדון הצללים" +description = "בלה 10 שעות בזחילה (36,000 שניות)" [advancement.challenge_stealth_arrows_50] - title = "קשת שקט" - description = "ירה 50 חצים בזחילה" +title = "קשת שקט" +description = "ירה 50 חצים בזחילה" [advancement.challenge_stealth_arrows_500] - title = "קשת הרפאים" - description = "ירה 500 חצים בזחילה" +title = "קשת הרפאים" +description = "ירה 500 חצים בזחילה" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "חניך החרב" - description = "גרום 1,000 נזק עם חרבות" +title = "חניך החרב" +description = "גרום 1,000 נזק עם חרבות" [advancement.challenge_sword_dmg_10k] - title = "סייף" - description = "גרום 10,000 נזק עם חרבות" +title = "סייף" +description = "גרום 10,000 נזק עם חרבות" [advancement.challenge_sword_kills_50] - title = "דו-קרבן" - description = "הרוג 50 מובים עם חרבות" +title = "דו-קרבן" +description = "הרוג 50 מובים עם חרבות" [advancement.challenge_sword_kills_500] - title = "גלדיאטור" - description = "הרוג 500 מובים עם חרבות" +title = "גלדיאטור" +description = "הרוג 500 מובים עם חרבות" [advancement.challenge_sword_crit_50] - title = "מכה קריטית" - description = "נחות 50 מכות קריטיות עם חרבות" +title = "מכה קריטית" +description = "נחות 50 מכות קריטיות עם חרבות" [advancement.challenge_sword_crit_500] - title = "אמן הדיוק" - description = "נחות 500 מכות קריטיות עם חרבות" +title = "אמן הדיוק" +description = "נחות 500 מכות קריטיות עם חרבות" [advancement.challenge_sword_heavy_25] - title = "מכה כבדה" - description = "נחות 25 מכות כבדות עם חרבות (מעל 8 נזק)" +title = "מכה כבדה" +description = "נחות 25 מכות כבדות עם חרבות (מעל 8 נזק)" [advancement.challenge_sword_heavy_250] - title = "מכה הרסנית" - description = "נחות 250 מכות כבדות עם חרבות (מעל 8 נזק)" +title = "מכה הרסנית" +description = "נחות 250 מכות כבדות עם חרבות (מעל 8 נזק)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "מאלף חיות" - description = "חיות המחמד שלך גורמות 500 נזק כולל" +title = "מאלף חיות" +description = "חיות המחמד שלך גורמות 500 נזק כולל" [advancement.challenge_pet_dmg_5k] - title = "אדון המלחמה" - description = "חיות המחמד שלך גורמות 5,000 נזק כולל" +title = "אדון המלחמה" +description = "חיות המחמד שלך גורמות 5,000 נזק כולל" [advancement.challenge_tamed_10] - title = "ידיד בעלי חיים" - description = "אלף 10 חיות" +title = "ידיד בעלי חיים" +description = "אלף 10 חיות" [advancement.challenge_tamed_100] - title = "שומר גן החיות" - description = "אלף 100 חיות" +title = "שומר גן החיות" +description = "אלף 100 חיות" [advancement.challenge_pet_kills_25] - title = "טקטיקת להקה" - description = "חיות המחמד שלך הורגות 25 יצורים" +title = "טקטיקת להקה" +description = "חיות המחמד שלך הורגות 25 יצורים" [advancement.challenge_pet_kills_250] - title = "מפקד הלהקה" - description = "חיות המחמד שלך הורגות 250 יצורים" +title = "מפקד הלהקה" +description = "חיות המחמד שלך הורגות 250 יצורים" [advancement.challenge_taming_2500] - title = "מומחה רבייה" - description = "הרבה 2,500 חיות" +title = "מומחה רבייה" +description = "הרבה 2,500 חיות" [advancement.challenge_taming_25k] - title = "מאסטר גנטיקה" - description = "הרבה 25,000 חיות" +title = "מאסטר גנטיקה" +description = "הרבה 25,000 חיות" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "שק חבטות" - description = "קבל 500 מכות" +title = "שק חבטות" +description = "קבל 500 מכות" [advancement.challenge_trag_hits_5k] - title = "מכור לסבל" - description = "קבל 5,000 מכות" +title = "מכור לסבל" +description = "קבל 5,000 מכות" [advancement.challenge_trag_deaths_10] - title = "תשע נשמות" - description = "מות 10 פעמים" +title = "תשע נשמות" +description = "מות 10 פעמים" [advancement.challenge_trag_deaths_100] - title = "סיוט חוזר" - description = "מות 100 פעמים" +title = "סיוט חוזר" +description = "מות 100 פעמים" [advancement.challenge_trag_fire_500] - title = "קורבן אש" - description = "ספוג 500 נזק אש" +title = "קורבן אש" +description = "ספוג 500 נזק אש" [advancement.challenge_trag_fire_5k] - title = "עוף החול" - description = "ספוג 5,000 נזק אש" +title = "עוף החול" +description = "ספוג 5,000 נזק אש" [advancement.challenge_trag_fall_500] - title = "בדיקת כבידה" - description = "ספוג 500 נזק נפילה" +title = "בדיקת כבידה" +description = "ספוג 500 נזק נפילה" [advancement.challenge_trag_fall_5k] - title = "מהירות סופית" - description = "ספוג 5,000 נזק נפילה" +title = "מהירות סופית" +description = "ספוג 5,000 נזק נפילה" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "מתאגרף" - description = "גרום 1,000 נזק באגרופים חשופים" +title = "מתאגרף" +description = "גרום 1,000 נזק באגרופים חשופים" [advancement.challenge_unarmed_dmg_10k] - title = "אמן לחימה" - description = "גרום 10,000 נזק באגרופים חשופים" +title = "אמן לחימה" +description = "גרום 10,000 נזק באגרופים חשופים" [advancement.challenge_unarmed_kills_25] - title = "אגרוף חשוף" - description = "הרוג 25 מובים באגרופים חשופים" +title = "אגרוף חשוף" +description = "הרוג 25 מובים באגרופים חשופים" [advancement.challenge_unarmed_kills_250] - title = "אגרוף האגדה" - description = "הרוג 250 מובים באגרופים חשופים" +title = "אגרוף האגדה" +description = "הרוג 250 מובים באגרופים חשופים" [advancement.challenge_unarmed_crit_25] - title = "אגרוף קריטי" - description = "נחות 25 מכות קריטיות באגרופים חשופים" +title = "אגרוף קריטי" +description = "נחות 25 מכות קריטיות באגרופים חשופים" [advancement.challenge_unarmed_crit_250] - title = "אגרוף מדויק" - description = "נחות 250 מכות קריטיות באגרופים חשופים" +title = "אגרוף מדויק" +description = "נחות 250 מכות קריטיות באגרופים חשופים" [advancement.challenge_unarmed_heavy_25] - title = "אגרוף כוח" - description = "נחות 25 מכות כבדות באגרופים חשופים (מעל 6 נזק)" +title = "אגרוף כוח" +description = "נחות 25 מכות כבדות באגרופים חשופים (מעל 6 נזק)" [advancement.challenge_unarmed_heavy_250] - title = "מלך הנוקאאוט" - description = "נחות 250 מכות כבדות באגרופים חשופים (מעל 6 נזק)" +title = "מלך הנוקאאוט" +description = "נחות 250 מכות כבדות באגרופים חשופים (מעל 6 נזק)" # items [items] [items.bound_ender_peral] - name = "מפתח שער רליקוויה" - usage1 = "Shift + לחיצה שמאלית כדי לאגד" - usage2 = "לחיצה ימנית כדי לגשת למלאי המאוגד" +name = "מפתח שער רליקוויה" +usage1 = "Shift + לחיצה שמאלית כדי לאגד" +usage2 = "לחיצה ימנית כדי לגשת למלאי המאוגד" [items.bound_eye_of_ender] - name = "עוגן עיני" - usage1 = "לחיצה ימנית כדי לצרוך ולהשתגר למיקום המאוגד" - usage2 = "Shift + לחיצה שמאלית כדי לאגד לבלוק" +name = "עוגן עיני" +usage1 = "לחיצה ימנית כדי לצרוך ולהשתגר למיקום המאוגד" +usage2 = "Shift + לחיצה שמאלית כדי לאגד לבלוק" [items.bound_redstone_torch] - name = "שלט רדסטון" - usage1 = "לחיצה ימנית כדי ליצור פולס רדסטון של טיק אחד" - usage2 = "Shift + לחיצה שמאלית על בלוק 'מטרה' כדי לאגד" +name = "שלט רדסטון" +usage1 = "לחיצה ימנית כדי ליצור פולס רדסטון של טיק אחד" +usage2 = "Shift + לחיצה שמאלית על בלוק 'מטרה' כדי לאגד" [items.bound_snowball] - name = "מלכודת קורים!" - usage1 = "זרוק כדי ליצור מלכודת קורים זמנית במיקום" +name = "מלכודת קורים!" +usage1 = "זרוק כדי ליצור מלכודת קורים זמנית במיקום" [items.chrono_time_bottle] - name = "זמן בבקבוק" - usage1 = "אוגר זמן באופן פסיבי כשהוא במלאי שלך" - usage2 = "לחיצה ימנית על בלוקים מתוזמנים או חיות תינוק כדי לבזבז זמן אגור" - stored = "זמן אגור" +name = "זמן בבקבוק" +usage1 = "אוגר זמן באופן פסיבי כשהוא במלאי שלך" +usage2 = "לחיצה ימנית על בלוקים מתוזמנים או חיות תינוק כדי לבזבז זמן אגור" +stored = "זמן אגור" [items.chrono_time_bomb] - name = "פצצת זמן" - usage1 = "לחיצה ימנית כדי לשגר בליסטיקת כרונו שיוצרת שדה טמפורלי" +name = "פצצת זמן" +usage1 = "לחיצה ימנית כדי לשגר בליסטיקת כרונו שיוצרת שדה טמפורלי" [items.elevator_block] - name = "בלוק מעלית" - usage1 = "קפוץ כדי להשתגר למעלה" - usage2 = "התכופף כדי להשתגר למטה" - usage3 = "מינימום 2 בלוקי אוויר בין המעליות" +name = "בלוק מעלית" +usage1 = "קפוץ כדי להשתגר למעלה" +usage2 = "התכופף כדי להשתגר למטה" +usage3 = "מינימום 2 בלוקי אוויר בין המעליות" # snippets [snippets] [snippets.gui] - level = "רמה" - knowledge = "ידע" - power_used = "כוח בשימוש" - not_learned = "לא נלמד" - xp = "XP עד" - welcome = "ברוך הבא!" - welcome_back = "ברוך שובך!" - xp_bonus_for_time = "XP עבור" - max_ability_power = "כוח יכולת מרבי" - unlock_this_by_clicking = "שחרר את הנעילה על ידי לחיצה ימנית: " - back = "חזור" - unlearn_all = "שכח הכל" - unlearned_all = "נשכח הכל" +level = "רמה" +knowledge = "ידע" +power_used = "כוח בשימוש" +not_learned = "לא נלמד" +xp = "XP עד" +welcome = "ברוך הבא!" +welcome_back = "ברוך שובך!" +xp_bonus_for_time = "XP עבור" +max_ability_power = "כוח יכולת מרבי" +unlock_this_by_clicking = "שחרר את הנעילה על ידי לחיצה ימנית: " +back = "חזור" +unlearn_all = "שכח הכל" +unlearned_all = "נשכח הכל" [snippets.adapt_menu] - may_not_unlearn = "אסור לשכוח" - may_unlearn = "ניתן ללמוד/לשכוח" - knowledge_cost = "עלות ידע" - knowledge_available = "ידע זמין" - already_learned = "כבר נלמד" - unlearn_refund = "לחץ כדי לשכוח ולקבל החזר" - no_refunds = "הארדקור, החזרים מושבתים" - knowledge = "ידע" - click_learn = "לחץ כדי ללמוד" - no_knowledge = "(אין לך ידע)" - you_only_have = "יש לך רק" - how_to_level_up = "עלה ברמת מיומנויות כדי להגדיל את הכוח המרבי שלך." - not_enough_power = "אין מספיק כוח! כל רמת יכולת עולה 1 כוח." - power = "כוח" - power_drain = "ניקוז כוח" - learned = "נלמד " - unlearned = "נשכח " - activator_block = "מדף ספרים" +may_not_unlearn = "אסור לשכוח" +may_unlearn = "ניתן ללמוד/לשכוח" +knowledge_cost = "עלות ידע" +knowledge_available = "ידע זמין" +already_learned = "כבר נלמד" +unlearn_refund = "לחץ כדי לשכוח ולקבל החזר" +no_refunds = "הארדקור, החזרים מושבתים" +knowledge = "ידע" +click_learn = "לחץ כדי ללמוד" +no_knowledge = "(אין לך ידע)" +you_only_have = "יש לך רק" +how_to_level_up = "עלה ברמת מיומנויות כדי להגדיל את הכוח המרבי שלך." +not_enough_power = "אין מספיק כוח! כל רמת יכולת עולה 1 כוח." +power = "כוח" +power_drain = "ניקוז כוח" +learned = "נלמד " +unlearned = "נשכח " +activator_block = "מדף ספרים" [snippets.knowledge_orb] - contains = "מכיל" - knowledge = "ידע" - rightclick = "לחיצה ימנית" - togainknowledge = "כדי לקבל את הידע הזה" - knowledge_orb = "כדור ידע" +contains = "מכיל" +knowledge = "ידע" +rightclick = "לחיצה ימנית" +togainknowledge = "כדי לקבל את הידע הזה" +knowledge_orb = "כדור ידע" [snippets.experience_orb] - contains = "מכיל" - xp = "ניסיון" - rightclick = "לחיצה ימנית" - togainxp = "כדי לקבל את הניסיון הזה" - xporb = "כדור ניסיון" +contains = "מכיל" +xp = "ניסיון" +rightclick = "לחיצה ימנית" +togainxp = "כדי לקבל את הניסיון הזה" +xporb = "כדור ניסיון" # skill [skill] [skill.agility] - name = "זריזות" - icon = "⇉" - description = "זריזות היא היכולת לנוע במהירות ובגמישות מול מכשולים." +name = "זריזות" +icon = "⇉" +description = "זריזות היא היכולת לנוע במהירות ובגמישות מול מכשולים." [skill.architect] - name = "אדריכלות" - icon = "⬧" - description = "מבנים הם אבני הבניין של העולם. המציאות בידיים שלך, שלך לשלוט." +name = "אדריכלות" +icon = "⬧" +description = "מבנים הם אבני הבניין של העולם. המציאות בידיים שלך, שלך לשלוט." [skill.axes] - name = "גרזנים" - icon = "🪓" - description1 = "למה לחטוב עצים, כשאפשר לחטוב " - description2 = "דברים" - description3 = "במקום, אותה תוצאה סופית!" +name = "גרזנים" +icon = "🪓" +description1 = "למה לחטוב עצים, כשאפשר לחטוב " +description2 = "דברים" +description3 = "במקום, אותה תוצאה סופית!" [skill.brewing] - name = "בישול שיקויים" - icon = "❦" - description = "בועה כפולה, בועה משולשת, בועה מרובעת- אני עדיין לא מצליח להכניס את השיקוי הזה לקדרה" +name = "בישול שיקויים" +icon = "❦" +description = "בועה כפולה, בועה משולשת, בועה מרובעת- אני עדיין לא מצליח להכניס את השיקוי הזה לקדרה" [skill.blocking] - name = "חסימה" - icon = "🛡" - description = "מקלות ואבנים לא ישברו לך עצמות, אבל מגן כן." +name = "חסימה" +icon = "🛡" +description = "מקלות ואבנים לא ישברו לך עצמות, אבל מגן כן." [skill.crafting] - name = "יצירה" - icon = "⌂" - description = "בלי שנותרו עוד חלקים למקם, למה לא ליצור עוד?" +name = "יצירה" +icon = "⌂" +description = "בלי שנותרו עוד חלקים למקם, למה לא ליצור עוד?" [skill.discovery] - name = "גילוי" - icon = "⚛" - description = "ככל שהתפיסה שלך מתרחבת, מוחך נפתח לגלות את מה שלא ידעת." +name = "גילוי" +icon = "⚛" +description = "ככל שהתפיסה שלך מתרחבת, מוחך נפתח לגלות את מה שלא ידעת." [skill.enchanting] - name = "כישוף" - icon = "♰" - description = "על מה אתה מדבר? נבואות, חזיונות, שטויות אמונה טפלה?" +name = "כישוף" +icon = "♰" +description = "על מה אתה מדבר? נבואות, חזיונות, שטויות אמונה טפלה?" [skill.excavation] - name = "חפירה" - icon = "ᛳ" - description = "חפירה חפירה חור..." +name = "חפירה" +icon = "ᛳ" +description = "חפירה חפירה חור..." [skill.herbalism] - name = "צמחי מרפא" - icon = "⚘" - description = "אני לא מוצא צמחים, אבל אני מוצא כמה זרעים ו- זה... עשב?" +name = "צמחי מרפא" +icon = "⚘" +description = "אני לא מוצא צמחים, אבל אני מוצא כמה זרעים ו- זה... עשב?" [skill.hunter] - name = "צייד" - icon = "☠" - description = "ציד זה המסע ולא התוצאה." +name = "צייד" +icon = "☠" +description = "ציד זה המסע ולא התוצאה." [skill.nether] - name = "נתר" - icon = "₪" - description = "ממעמקי הנתר עצמו." +name = "נתר" +icon = "₪" +description = "ממעמקי הנתר עצמו." [skill.pickaxe] - name = "מכוש" - icon = "⛏" - description = "גמדים הם הכורים, אבל למדתי דבר או שניים בזמני. אני שוודי!" +name = "מכוש" +icon = "⛏" +description = "גמדים הם הכורים, אבל למדתי דבר או שניים בזמני. אני שוודי!" [skill.ranged] - name = "טווח" - icon = "🏹" - description = "מרחק הוא המפתח לניצחון, והמפתח להישרדות." +name = "טווח" +icon = "🏹" +description = "מרחק הוא המפתח לניצחון, והמפתח להישרדות." [skill.rift] - name = "קרע" - icon = "❍" - description = "הקרע הוא רתמה צורבת, אבל רתמת את הרתמה." +name = "קרע" +icon = "❍" +description = "הקרע הוא רתמה צורבת, אבל רתמת את הרתמה." [skill.seaborne] - name = "ימי" - icon = "🎣" - description = "עם מיומנות זו, תוכל לשלוט בפלאות המים." +name = "ימי" +icon = "🎣" +description = "עם מיומנות זו, תוכל לשלוט בפלאות המים." [skill.stealth] - name = "התגנבות" - icon = "☯" - description = "אומנות הבלתי נראה. ללכת בצללים." +name = "התגנבות" +icon = "☯" +description = "אומנות הבלתי נראה. ללכת בצללים." [skill.swords] - name = "חרבות" - icon = "⚔" - description = "בכוחו של גריי-סטון!" +name = "חרבות" +icon = "⚔" +description = "בכוחו של גריי-סטון!" [skill.taming] - name = "אילוף" - icon = "♥" - description = "התוכים והדבורים... ואתה?" +name = "אילוף" +icon = "♥" +description = "התוכים והדבורים... ואתה?" [skill.tragoul] - name = "טראגול" - icon = "🗡" - description = "הדם זורם בעורקי היקום. מכווץ בידיים שלך." +name = "טראגול" +icon = "🗡" +description = "הדם זורם בעורקי היקום. מכווץ בידיים שלך." [skill.chronos] - name = "כרונוס" - icon = "🕒" - description = "כוון את שעון היקום, חווה את הזרימה. שבור את השעון, הפוך אליו." +name = "כרונוס" +icon = "🕒" +description = "כוון את שעון היקום, חווה את הזרימה. שבור את השעון, הפוך אליו." [skill.unarmed] - name = "לא חמוש" - icon = "»" - description = "בלי נשק לא אומר בלי כוח." +name = "לא חמוש" +icon = "»" +description = "בלי נשק לא אומר בלי כוח." # agility [agility] [agility.armor_up] - name = "שריון-אפ" - description = "קבל יותר שריון ככל שאתה רץ יותר זמן!" - lore1 = "שריון מרבי" - lore2 = "זמן שריון-אפ" - lore = ["שריון מרבי", "זמן שריון-אפ"] +name = "שריון-אפ" +description = "קבל יותר שריון ככל שאתה רץ יותר זמן!" +lore1 = "שריון מרבי" +lore2 = "זמן שריון-אפ" +lore = ["שריון מרבי", "זמן שריון-אפ"] [agility.ladder_slide] - name = "החלקת סולם" - description = "טפס והחלק על סולמות הרבה יותר מהר בשני הכיוונים." - lore1 = "מכפיל מהירות סולם" - lore2 = "מהירות ירידה מהירה" - lore = ["מכפיל מהירות סולם", "מהירות ירידה מהירה"] +name = "החלקת סולם" +description = "טפס והחלק על סולמות הרבה יותר מהר בשני הכיוונים." +lore1 = "מכפיל מהירות סולם" +lore2 = "מהירות ירידה מהירה" +lore = ["מכפיל מהירות סולם", "מהירות ירידה מהירה"] [agility.super_jump] - name = "סופר קפיצה" - description = "יתרון גובה יוצא דופן." - lore1 = "גובה קפיצה מרבי" - lore2 = "התכופף + קפוץ לסופר קפיצה!" - lore = ["גובה קפיצה מרבי", "התכופף + קפוץ לסופר קפיצה!"] +name = "סופר קפיצה" +description = "יתרון גובה יוצא דופן." +lore1 = "גובה קפיצה מרבי" +lore2 = "התכופף + קפוץ לסופר קפיצה!" +lore = ["גובה קפיצה מרבי", "התכופף + קפוץ לסופר קפיצה!"] [agility.wall_jump] - name = "קפיצת קיר" - description = "החזק shift באוויר ליד קיר כדי להיתלות ולקפוץ!" - lore1 = "מספר קפיצות מרבי" - lore2 = "גובה קפיצה" - lore = ["מספר קפיצות מרבי", "גובה קפיצה"] +name = "קפיצת קיר" +description = "החזק shift באוויר ליד קיר כדי להיתלות ולקפוץ!" +lore1 = "מספר קפיצות מרבי" +lore2 = "גובה קפיצה" +lore = ["מספר קפיצות מרבי", "גובה קפיצה"] [agility.wind_up] - name = "תאוצה" - description = "הפוך מהיר יותר ככל שאתה רץ זמן רב יותר!" - lore1 = "מהירות מרבית" - lore2 = "זמן תאוצה" - lore = ["מהירות מרבית", "זמן תאוצה"] +name = "תאוצה" +description = "הפוך מהיר יותר ככל שאתה רץ זמן רב יותר!" +lore1 = "מהירות מרבית" +lore2 = "זמן תאוצה" +lore = ["מהירות מרבית", "זמן תאוצה"] # architect [architect] [architect.elevator] - name = "מעלית" - description = "זה מאפשר לך לבנות מעלית לשיגור אנכי מהיר!" - lore1 = "פותח מתכון מעלית: X=צמר, Y=פנינת אנדר" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["פותח מתכון מעלית: X=צמר, Y=פנינת אנדר", "XXX", "XYX", "XXX"] +name = "מעלית" +description = "זה מאפשר לך לבנות מעלית לשיגור אנכי מהיר!" +lore1 = "פותח מתכון מעלית: X=צמר, Y=פנינת אנדר" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["פותח מתכון מעלית: X=צמר, Y=פנינת אנדר", "XXX", "XYX", "XXX"] [architect.foundation] - name = "יסוד קסום" - description = "מאפשר לך להתכופף ולהניח יסוד זמני מתחתיך!" - lore1 = "צור בקסם: " - lore2 = "בלוקים מתחתיך!" - lore = ["צור בקסם: ", "בלוקים מתחתיך!"] +name = "יסוד קסום" +description = "מאפשר לך להתכופף ולהניח יסוד זמני מתחתיך!" +lore1 = "צור בקסם: " +lore2 = "בלוקים מתחתיך!" +lore = ["צור בקסם: ", "בלוקים מתחתיך!"] [architect.glass] - name = "זכוכית מגע-משי" - description = "מאפשר לך למנוע אובדן בלוקי זכוכית כשאתה שובר אותם ביד ריקה!" - lore1 = "הידיים שלך מקבלות מגע-משי לזכוכית" - lore = ["הידיים שלך מקבלות מגע-משי לזכוכית"] +name = "זכוכית מגע-משי" +description = "מאפשר לך למנוע אובדן בלוקי זכוכית כשאתה שובר אותם ביד ריקה!" +lore1 = "הידיים שלך מקבלות מגע-משי לזכוכית" +lore = ["הידיים שלך מקבלות מגע-משי לזכוכית"] [architect.wireless_redstone] - name = "שלט רדסטון" - description = "מאפשר לך להשתמש בלפיד רדסטון כדי להפעיל רדסטון מרחוק!" - lore1 = "מטרה + לפיד רדסטון + פנינת אנדר = שלט רדסטון אחד" - lore = ["מטרה + לפיד רדסטון + פנינת אנדר = שלט רדסטון אחד"] +name = "שלט רדסטון" +description = "מאפשר לך להשתמש בלפיד רדסטון כדי להפעיל רדסטון מרחוק!" +lore1 = "מטרה + לפיד רדסטון + פנינת אנדר = שלט רדסטון אחד" +lore = ["מטרה + לפיד רדסטון + פנינת אנדר = שלט רדסטון אחד"] [architect.placement] - name = "שרביט בנאים" - description = "מאפשר לך להניח מספר בלוקים בבת אחת! התכופף, והחזק בלוק שמתאים לבלוק שאתה מסתכל עליו והנח! שים לב, ייתכן שתצטרך לזוז מעט כדי לסמן את התיבות!" - lore1 = "אתה צריך" - lore2 = "בלוקים ביד כדי להניח את זה" - lore3 = "שרביט בנאים מחומר" - lore = ["אתה צריך", "בלוקים ביד כדי להניח את זה", "שרביט בנאים מחומר"] +name = "שרביט בנאים" +description = "מאפשר לך להניח מספר בלוקים בבת אחת! התכופף, והחזק בלוק שמתאים לבלוק שאתה מסתכל עליו והנח! שים לב, ייתכן שתצטרך לזוז מעט כדי לסמן את התיבות!" +lore1 = "אתה צריך" +lore2 = "בלוקים ביד כדי להניח את זה" +lore3 = "שרביט בנאים מחומר" +lore = ["אתה צריך", "בלוקים ביד כדי להניח את זה", "שרביט בנאים מחומר"] # axe [axe] [axe.chop] - name = "חיתוך גרזן" - description = "חטוב עצים על ידי לחיצה ימנית על בול הבסיס!" - lore1 = "בלוקים לכל חיתוך" - lore2 = "זמן קירור חיתוך" - lore3 = "בלאי כלי" - lore = ["בלוקים לכל חיתוך", "זמן קירור חיתוך", "בלאי כלי"] +name = "חיתוך גרזן" +description = "חטוב עצים על ידי לחיצה ימנית על בול הבסיס!" +lore1 = "בלוקים לכל חיתוך" +lore2 = "זמן קירור חיתוך" +lore3 = "בלאי כלי" +lore = ["בלוקים לכל חיתוך", "זמן קירור חיתוך", "בלאי כלי"] [axe.log_swap] - name = "מחליף הבולים של לוסי" - description = "שנה את סוג הבולים בשולחן יצירה!" - lore1 = "8 בולים מכל סוג + 1 שתיל = 8 בולים מסוג השתיל" - lore = ["8 בולים מכל סוג + 1 שתיל = 8 בולים מסוג השתיל"] +name = "מחליף הבולים של לוסי" +description = "שנה את סוג הבולים בשולחן יצירה!" +lore1 = "8 בולים מכל סוג + 1 שתיל = 8 בולים מסוג השתיל" +lore = ["8 בולים מכל סוג + 1 שתיל = 8 בולים מסוג השתיל"] [axe.drop_to_inventory] - name = "גרזן-ישר-למלאי" +name = "גרזן-ישר-למלאי" [axe.ground_smash] - name = "מעיכת קרקע בגרזן" - description = "קפוץ, ואז התכופף ומעך את כל האויבים הקרובים." - lore1 = "נזק" - lore2 = "רדיוס בלוקים" - lore3 = "כוח" - lore4 = "זמן קירור מעיכה" - lore = ["נזק", "רדיוס בלוקים", "כוח", "זמן קירור מעיכה"] +name = "מעיכת קרקע בגרזן" +description = "קפוץ, ואז התכופף ומעך את כל האויבים הקרובים." +lore1 = "נזק" +lore2 = "רדיוס בלוקים" +lore3 = "כוח" +lore4 = "זמן קירור מעיכה" +lore = ["נזק", "רדיוס בלוקים", "כוח", "זמן קירור מעיכה"] [axe.leaf_miner] - name = "כורה עלים" - description = "מאפשר לך לשבור עלים בכמות בבת אחת!" - lore1 = "התכופף, וכרה עלים" - lore2 = "טווח כריית עלים" - lore3 = "לא תקבל את הדרופים מהעלים (מניעת ניצול)" - lore = ["התכופף, וכרה עלים", "טווח כריית עלים", "לא תקבל את הדרופים מהעלים (מניעת ניצול)"] +name = "כורה עלים" +description = "מאפשר לך לשבור עלים בכמות בבת אחת!" +lore1 = "התכופף, וכרה עלים" +lore2 = "טווח כריית עלים" +lore3 = "לא תקבל את הדרופים מהעלים (מניעת ניצול)" +lore = ["התכופף, וכרה עלים", "טווח כריית עלים", "לא תקבל את הדרופים מהעלים (מניעת ניצול)"] [axe.wood_miner] - name = "כורה עץ" - description = "מאפשר לך לשבור עץ בכמות בבת אחת!" - lore1 = "התכופף, וכרה עץ/בולים (לא קרשים)" - lore2 = "טווח כריית עץ" - lore3 = "עובד עם ישר-למלאי" - lore = ["התכופף, וכרה עץ/בולים (לא קרשים)", "טווח כריית עץ", "עובד עם ישר-למלאי"] +name = "כורה עץ" +description = "מאפשר לך לשבור עץ בכמות בבת אחת!" +lore1 = "התכופף, וכרה עץ/בולים (לא קרשים)" +lore2 = "טווח כריית עץ" +lore3 = "עובד עם ישר-למלאי" +lore = ["התכופף, וכרה עץ/בולים (לא קרשים)", "טווח כריית עץ", "עובד עם ישר-למלאי"] # brewing [brewing] [brewing.lingering] - name = "שיקוי מתמשך" - description = "שיקויים מבושלים נמשכים זמן רב יותר!" - lore1 = "משך" - lore2 = "משך" - lore = ["משך", "משך"] +name = "שיקוי מתמשך" +description = "שיקויים מבושלים נמשכים זמן רב יותר!" +lore1 = "משך" +lore2 = "משך" +lore = ["משך", "משך"] [brewing.super_heated] - name = "בישול סופר-חם" - description = "דוכני בישול עובדים מהר יותר ככל שהם חמים יותר." - lore1 = "לכל בלוק אש נוגע" - lore2 = "לכל בלוק לבה נוגע" - lore = ["לכל בלוק אש נוגע", "לכל בלוק לבה נוגע"] +name = "בישול סופר-חם" +description = "דוכני בישול עובדים מהר יותר ככל שהם חמים יותר." +lore1 = "לכל בלוק אש נוגע" +lore2 = "לכל בלוק לבה נוגע" +lore = ["לכל בלוק אש נוגע", "לכל בלוק לבה נוגע"] [brewing.darkness] - name = "חושך בבקבוק" - description = "לא בטוח למה אתה צריך את זה, אבל הנה לך!" - lore1 = "שיקוי ראיית לילה + בטון שחור = שיקוי חושך (30 שניות)" - lore2 = "שים לב שזה מונע מהמשתמש לרוץ!" - lore = ["שיקוי ראיית לילה + בטון שחור = שיקוי חושך (30 שניות)", "שים לב שזה מונע מהמשתמש לרוץ!"] +name = "חושך בבקבוק" +description = "לא בטוח למה אתה צריך את זה, אבל הנה לך!" +lore1 = "שיקוי ראיית לילה + בטון שחור = שיקוי חושך (30 שניות)" +lore2 = "שים לב שזה מונע מהמשתמש לרוץ!" +lore = ["שיקוי ראיית לילה + בטון שחור = שיקוי חושך (30 שניות)", "שים לב שזה מונע מהמשתמש לרוץ!"] [brewing.haste] - name = "חיפזון בבקבוק" - description = "כשיעילות לא מספיקה" - lore1 = "שיקוי מהירות + רסיס אמטיסט = שיקוי חיפזון (60 שניות)" - lore2 = "שיקוי מהירות + בלוק אמטיסט = שיקוי חיפזון-2 (30 שניות)" - lore = ["שיקוי מהירות + רסיס אמטיסט = שיקוי חיפזון (60 שניות)", "שיקוי מהירות + בלוק אמטיסט = שיקוי חיפזון-2 (30 שניות)"] +name = "חיפזון בבקבוק" +description = "כשיעילות לא מספיקה" +lore1 = "שיקוי מהירות + רסיס אמטיסט = שיקוי חיפזון (60 שניות)" +lore2 = "שיקוי מהירות + בלוק אמטיסט = שיקוי חיפזון-2 (30 שניות)" +lore = ["שיקוי מהירות + רסיס אמטיסט = שיקוי חיפזון (60 שניות)", "שיקוי מהירות + בלוק אמטיסט = שיקוי חיפזון-2 (30 שניות)"] [brewing.absorption] - name = "ספיגה בבקבוק" - description = "חזק את הגוף!" - lore1 = "ריפוי מיידי + קוורץ = שיקוי ספיגה (60 שניות)" - lore2 = "ריפוי מיידי + בלוק קוורץ = שיקוי ספיגה-2 (30 שניות)" - lore = ["ריפוי מיידי + קוורץ = שיקוי ספיגה (60 שניות)", "ריפוי מיידי + בלוק קוורץ = שיקוי ספיגה-2 (30 שניות)"] +name = "ספיגה בבקבוק" +description = "חזק את הגוף!" +lore1 = "ריפוי מיידי + קוורץ = שיקוי ספיגה (60 שניות)" +lore2 = "ריפוי מיידי + בלוק קוורץ = שיקוי ספיגה-2 (30 שניות)" +lore = ["ריפוי מיידי + קוורץ = שיקוי ספיגה (60 שניות)", "ריפוי מיידי + בלוק קוורץ = שיקוי ספיגה-2 (30 שניות)"] [brewing.fatigue] - name = "עייפות בבקבוק" - description = "החלש את הגוף!" - lore1 = "שיקוי חולשה + כדור רפש = שיקוי עייפות (30 שניות)" - lore2 = "שיקוי חולשה + בלוק רפש = שיקוי עייפות-2 (15 שניות)" - lore = ["שיקוי חולשה + כדור רפש = שיקוי עייפות (30 שניות)", "שיקוי חולשה + בלוק רפש = שיקוי עייפות-2 (15 שניות)"] +name = "עייפות בבקבוק" +description = "החלש את הגוף!" +lore1 = "שיקוי חולשה + כדור רפש = שיקוי עייפות (30 שניות)" +lore2 = "שיקוי חולשה + בלוק רפש = שיקוי עייפות-2 (15 שניות)" +lore = ["שיקוי חולשה + כדור רפש = שיקוי עייפות (30 שניות)", "שיקוי חולשה + בלוק רפש = שיקוי עייפות-2 (15 שניות)"] [brewing.hunger] - name = "רעב בבקבוק" - description = "האכל את הבלתי שביע!" - lore1 = "שיקוי מוזר + בשר רקוב = שיקוי רעב (30 שניות)" - lore2 = "שיקוי חולשה + בשר רקוב = שיקוי רעב-3 (15 שניות)" - lore = ["שיקוי מוזר + בשר רקוב = שיקוי רעב (30 שניות)", "שיקוי חולשה + בשר רקוב = שיקוי רעב-3 (15 שניות)"] +name = "רעב בבקבוק" +description = "האכל את הבלתי שביע!" +lore1 = "שיקוי מוזר + בשר רקוב = שיקוי רעב (30 שניות)" +lore2 = "שיקוי חולשה + בשר רקוב = שיקוי רעב-3 (15 שניות)" +lore = ["שיקוי מוזר + בשר רקוב = שיקוי רעב (30 שניות)", "שיקוי חולשה + בשר רקוב = שיקוי רעב-3 (15 שניות)"] [brewing.nausea] - name = "בחילה בבקבוק" - description = "אתה מחליא אותי!" - lore1 = "שיקוי מוזר + פטריה חומה = שיקוי בחילה (16 שניות)" - lore2 = "שיקוי מוזר + פטריית ארגמן = שיקוי בחילה-2 (8 שניות)" - lore = ["שיקוי מוזר + פטריה חומה = שיקוי בחילה (16 שניות)", "שיקוי מוזר + פטריית ארגמן = שיקוי בחילה-2 (8 שניות)"] +name = "בחילה בבקבוק" +description = "אתה מחליא אותי!" +lore1 = "שיקוי מוזר + פטריה חומה = שיקוי בחילה (16 שניות)" +lore2 = "שיקוי מוזר + פטריית ארגמן = שיקוי בחילה-2 (8 שניות)" +lore = ["שיקוי מוזר + פטריה חומה = שיקוי בחילה (16 שניות)", "שיקוי מוזר + פטריית ארגמן = שיקוי בחילה-2 (8 שניות)"] [brewing.blindness] - name = "עיוורון בבקבוק" - description = "אתה אדם נורא..." - lore1 = "שיקוי מוזר + שק דיו = שיקוי עיוורון (30 שניות)" - lore2 = "שיקוי מוזר + שק דיו זוהר = שיקוי עיוורון-2 (15 שניות)" - lore = ["שיקוי מוזר + שק דיו = שיקוי עיוורון (30 שניות)", "שיקוי מוזר + שק דיו זוהר = שיקוי עיוורון-2 (15 שניות)"] +name = "עיוורון בבקבוק" +description = "אתה אדם נורא..." +lore1 = "שיקוי מוזר + שק דיו = שיקוי עיוורון (30 שניות)" +lore2 = "שיקוי מוזר + שק דיו זוהר = שיקוי עיוורון-2 (15 שניות)" +lore = ["שיקוי מוזר + שק דיו = שיקוי עיוורון (30 שניות)", "שיקוי מוזר + שק דיו זוהר = שיקוי עיוורון-2 (15 שניות)"] [brewing.resistance] - name = "התנגדות בבקבוק" - description = "ביצור במיטבו!" - lore1 = "שיקוי מוזר + מטיל ברזל = שיקוי התנגדות (60 שניות)" - lore2 = "שיקוי מוזר + בלוק ברזל = שיקוי התנגדות-2 (30 שניות)" - lore = ["שיקוי מוזר + מטיל ברזל = שיקוי התנגדות (60 שניות)", "שיקוי מוזר + בלוק ברזל = שיקוי התנגדות-2 (30 שניות)"] +name = "התנגדות בבקבוק" +description = "ביצור במיטבו!" +lore1 = "שיקוי מוזר + מטיל ברזל = שיקוי התנגדות (60 שניות)" +lore2 = "שיקוי מוזר + בלוק ברזל = שיקוי התנגדות-2 (30 שניות)" +lore = ["שיקוי מוזר + מטיל ברזל = שיקוי התנגדות (60 שניות)", "שיקוי מוזר + בלוק ברזל = שיקוי התנגדות-2 (30 שניות)"] [brewing.health_boost] - name = "חיים בבקבוק" - description = "כשבריאות מרבית לא מספיקה..." - lore1 = "שיקוי ריפוי מיידי + תפוח זהב = שיקוי חיזוק בריאות (120 שניות)" - lore2 = "שיקוי ריפוי מיידי + תפוח זהב קסום = שיקוי חיזוק בריאות-2 (120 שניות)" - lore = ["שיקוי ריפוי מיידי + תפוח זהב = שיקוי חיזוק בריאות (120 שניות)", "שיקוי ריפוי מיידי + תפוח זהב קסום = שיקוי חיזוק בריאות-2 (120 שניות)"] +name = "חיים בבקבוק" +description = "כשבריאות מרבית לא מספיקה..." +lore1 = "שיקוי ריפוי מיידי + תפוח זהב = שיקוי חיזוק בריאות (120 שניות)" +lore2 = "שיקוי ריפוי מיידי + תפוח זהב קסום = שיקוי חיזוק בריאות-2 (120 שניות)" +lore = ["שיקוי ריפוי מיידי + תפוח זהב = שיקוי חיזוק בריאות (120 שניות)", "שיקוי ריפוי מיידי + תפוח זהב קסום = שיקוי חיזוק בריאות-2 (120 שניות)"] [brewing.decay] - name = "ריקבון בבקבוק" - description = "מי ידע שפסולת תהיה כל כך שימושית?" - lore1 = "שיקוי חולשה + תפוח אדמה רעיל = שיקוי כמילה (16 שניות)" - lore2 = "שיקוי חולשה + שורשי ארגמן = שיקוי כמילה-2 (8 שניות)" - lore = ["שיקוי חולשה + תפוח אדמה רעיל = שיקוי כמילה (16 שניות)", "שיקוי חולשה + שורשי ארגמן = שיקוי כמילה-2 (8 שניות)"] +name = "ריקבון בבקבוק" +description = "מי ידע שפסולת תהיה כל כך שימושית?" +lore1 = "שיקוי חולשה + תפוח אדמה רעיל = שיקוי כמילה (16 שניות)" +lore2 = "שיקוי חולשה + שורשי ארגמן = שיקוי כמילה-2 (8 שניות)" +lore = ["שיקוי חולשה + תפוח אדמה רעיל = שיקוי כמילה (16 שניות)", "שיקוי חולשה + שורשי ארגמן = שיקוי כמילה-2 (8 שניות)"] [brewing.saturation] - name = "רוויה בבקבוק" - description = "אתה יודע... אני אפילו לא רעב..." - lore1 = "שיקוי התחדשות + תפוח אדמה אפוי = שיקוי רוויה" - lore2 = "שיקוי התחדשות + חבילת חציר = שיקוי רוויה-2" - lore = ["שיקוי התחדשות + תפוח אדמה אפוי = שיקוי רוויה", "שיקוי התחדשות + חבילת חציר = שיקוי רוויה-2"] +name = "רוויה בבקבוק" +description = "אתה יודע... אני אפילו לא רעב..." +lore1 = "שיקוי התחדשות + תפוח אדמה אפוי = שיקוי רוויה" +lore2 = "שיקוי התחדשות + חבילת חציר = שיקוי רוויה-2" +lore = ["שיקוי התחדשות + תפוח אדמה אפוי = שיקוי רוויה", "שיקוי התחדשות + חבילת חציר = שיקוי רוויה-2"] # blocking [blocking] [blocking.chain_armorer] - name = "שרשראות מפיסטופלס" - description = "מאפשר לך ליצור שריון שרשרת" - lore1 = "מתכון היצירה זהה לכל שריון אחר, אבל עם גושי ברזל קטנים במקום" - lore = ["מתכון היצירה זהה לכל שריון אחר, אבל עם גושי ברזל קטנים במקום"] +name = "שרשראות מפיסטופלס" +description = "מאפשר לך ליצור שריון שרשרת" +lore1 = "מתכון היצירה זהה לכל שריון אחר, אבל עם גושי ברזל קטנים במקום" +lore = ["מתכון היצירה זהה לכל שריון אחר, אבל עם גושי ברזל קטנים במקום"] [blocking.horse_armorer] - name = "שריון סוס בר-יצירה" - description = "מאפשר לך ליצור שריון סוס" - lore1 = "הקף אוכף עם החומר שבו תרצה להשתמש ליצירת השריון" - lore = ["הקף אוכף עם החומר שבו תרצה להשתמש ליצירת השריון"] +name = "שריון סוס בר-יצירה" +description = "מאפשר לך ליצור שריון סוס" +lore1 = "הקף אוכף עם החומר שבו תרצה להשתמש ליצירת השריון" +lore = ["הקף אוכף עם החומר שבו תרצה להשתמש ליצירת השריון"] [blocking.saddle_crafter] - name = "אוכף בר-יצירה" - description = "צור אוכף עם עור" - lore1 = "מתכון: 5 עורות:" - lore = ["מתכון: 5 עורות:"] +name = "אוכף בר-יצירה" +description = "צור אוכף עם עור" +lore1 = "מתכון: 5 עורות:" +lore = ["מתכון: 5 עורות:"] [blocking.multi_armor] - name = "רב-שריון" - description = "אגד אליטרות לשריון" - lore1 = "זוהי מיומנות מדהימה לנסיעות." - lore2 = "מזג ושנה שריון/אליטרה באופן דינמי תוך כדי תנועה!" - lore3 = "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך." - lore4 = "לשחרור שריון, התכופף-והפל את הפריט, והוא יתפרק." - lore5 = "אם הרב-שריון שלך נהרס, תאבד את כל הפריטים שבו." - lore6 = "אני לא צריך שריון, זה מאכזב אותי..." - lore = ["זוהי מיומנות מדהימה לנסיעות.", "מזג ושנה שריון/אליטרה באופן דינמי תוך כדי תנועה!", "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך.", "לשחרור שריון, התכופף-והפל את הפריט, והוא יתפרק.", "אם הרב-שריון שלך נהרס, תאבד את כל הפריטים שבו.", "אני לא צריך שריון, זה מאכזב אותי..."] +name = "רב-שריון" +description = "אגד אליטרות לשריון" +lore1 = "זוהי מיומנות מדהימה לנסיעות." +lore2 = "מזג ושנה שריון/אליטרה באופן דינמי תוך כדי תנועה!" +lore3 = "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך." +lore4 = "לשחרור שריון, התכופף-והפל את הפריט, והוא יתפרק." +lore5 = "אם הרב-שריון שלך נהרס, תאבד את כל הפריטים שבו." +lore6 = "אני לא צריך שריון, זה מאכזב אותי..." +lore = ["זוהי מיומנות מדהימה לנסיעות.", "מזג ושנה שריון/אליטרה באופן דינמי תוך כדי תנועה!", "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך.", "לשחרור שריון, התכופף-והפל את הפריט, והוא יתפרק.", "אם הרב-שריון שלך נהרס, תאבד את כל הפריטים שבו.", "אני לא צריך שריון, זה מאכזב אותי..."] # crafting [crafting] [crafting.deconstruction] - name = "פירוק" - description = "פרק בלוקים ופריטים לרכיבי בסיס שניתן להציל!" - lore1 = "הפל פריט כלשהו לרצפה." - lore2 = "אז, התכופף ולחץ לחיצה ימנית עם מספריים" - lore = ["הפל פריט כלשהו לרצפה.", "אז, התכופף ולחץ לחיצה ימנית עם מספריים"] +name = "פירוק" +description = "פרק בלוקים ופריטים לרכיבי בסיס שניתן להציל!" +lore1 = "הפל פריט כלשהו לרצפה." +lore2 = "אז, התכופף ולחץ לחיצה ימנית עם מספריים" +lore = ["הפל פריט כלשהו לרצפה.", "אז, התכופף ולחץ לחיצה ימנית עם מספריים"] [crafting.xp] - name = "ניסיון יצירה" - description = "קבל ניסיון פסיבי בעת יצירה" - lore1 = "קבל ניסיון בעת יצירה" - lore = ["קבל ניסיון בעת יצירה"] +name = "ניסיון יצירה" +description = "קבל ניסיון פסיבי בעת יצירה" +lore1 = "קבל ניסיון בעת יצירה" +lore = ["קבל ניסיון בעת יצירה"] [crafting.reconstruction] - name = "שחזור עפרות" - description = "צור מחדש עפרות מרכיבי הבסיס שלהם!" - lore1 = "8 מהדרופים ו-1 מארח = 1 עפרה (ללא צורה)" - lore2 = "הדרופים חייבים להיות מותכים (אם רלוונטי)" - lore3 = "לא כולל: גרוטאות, קוורץ, ואזמרגדים וכו'..." - lore4 = "מארח = עטיפה. כלומר: אבן, נתראק, צפחה עמוקה" - lore = ["8 מהדרופים ו-1 מארח = 1 עפרה (ללא צורה)", "הדרופים חייבים להיות מותכים (אם רלוונטי)", "לא כולל: גרוטאות, קוורץ, ואזמרגדים וכו'...", "מארח = עטיפה. כלומר: אבן, נתראק, צפחה עמוקה"] +name = "שחזור עפרות" +description = "צור מחדש עפרות מרכיבי הבסיס שלהם!" +lore1 = "8 מהדרופים ו-1 מארח = 1 עפרה (ללא צורה)" +lore2 = "הדרופים חייבים להיות מותכים (אם רלוונטי)" +lore3 = "לא כולל: גרוטאות, קוורץ, ואזמרגדים וכו'..." +lore4 = "מארח = עטיפה. כלומר: אבן, נתראק, צפחה עמוקה" +lore = ["8 מהדרופים ו-1 מארח = 1 עפרה (ללא צורה)", "הדרופים חייבים להיות מותכים (אם רלוונטי)", "לא כולל: גרוטאות, קוורץ, ואזמרגדים וכו'...", "מארח = עטיפה. כלומר: אבן, נתראק, צפחה עמוקה"] [crafting.leather] - name = "עור בר-יצירה" - description = "צור עור מבשר רקוב" - lore1 = "פשוט זרוק אותו (בשר רקוב) על המדורה!" - lore = ["פשוט זרוק אותו (בשר רקוב) על המדורה!"] +name = "עור בר-יצירה" +description = "צור עור מבשר רקוב" +lore1 = "פשוט זרוק אותו (בשר רקוב) על המדורה!" +lore = ["פשוט זרוק אותו (בשר רקוב) על המדורה!"] [crafting.backpacks] - name = "התרמילים של בוטילייה!" - description = "זה פשוט מביא את צרור מוג'אנג למשחק!" - lore1 = "אתה צריך להיות במצב הישרדות כדי להשתמש בזה" - lore2 = "XLX : עור, רצועה, עור" - lore3 = "XSX : עור, קופסת חבית, עור" - lore4 = "XCX : עור, ארגז, עור" - lore = ["אתה צריך להיות במצב הישרדות כדי להשתמש בזה", "XLX : עור, רצועה, עור", "XSX : עור, קופסת חבית, עור", "XCX : עור, ארגז, עור"] +name = "התרמילים של בוטילייה!" +description = "זה פשוט מביא את צרור מוג'אנג למשחק!" +lore1 = "אתה צריך להיות במצב הישרדות כדי להשתמש בזה" +lore2 = "XLX : עור, רצועה, עור" +lore3 = "XSX : עור, קופסת חבית, עור" +lore4 = "XCX : עור, ארגז, עור" +lore = ["אתה צריך להיות במצב הישרדות כדי להשתמש בזה", "XLX : עור, רצועה, עור", "XSX : עור, קופסת חבית, עור", "XCX : עור, ארגז, עור"] [crafting.stations] - name = "שולחנות ניידים!" - description = "השתמש בשולחן מכף ידך!" - lore2 = "כל פריט שתשכח בשולחן כשהוא נסגר אבוד לנצח!" - lore3 = "שולחנות תקפים: סדן, יצירה, אבן השחזה, קרטוגרפיה, חותך אבן, נול" - lore = ["כל פריט שתשכח בשולחן כשהוא נסגר אבוד לנצח!", "שולחנות תקפים: סדן, יצירה, אבן השחזה, קרטוגרפיה, חותך אבן, נול"] +name = "שולחנות ניידים!" +description = "השתמש בשולחן מכף ידך!" +lore2 = "כל פריט שתשכח בשולחן כשהוא נסגר אבוד לנצח!" +lore3 = "שולחנות תקפים: סדן, יצירה, אבן השחזה, קרטוגרפיה, חותך אבן, נול" +lore = ["כל פריט שתשכח בשולחן כשהוא נסגר אבוד לנצח!", "שולחנות תקפים: סדן, יצירה, אבן השחזה, קרטוגרפיה, חותך אבן, נול"] [crafting.skulls] - name = "גולגולות בנות-יצירה!" - description = "באמצעות חומרים תוכל ליצור גולגולות מפלצות!" - lore1 = "הקף בלוק עצם עם הדברים הבאים כדי לקבל גולגולת:" - lore2 = "זומבי: בשר רקוב" - lore3 = "שלד: עצם" - lore4 = "קריפר: אבק שריפה" - lore5 = "וויתר: לבנת נתר" - lore6 = "דרקון: נשימת דרקון" - lore = ["הקף בלוק עצם עם הדברים הבאים כדי לקבל גולגולת:", "זומבי: בשר רקוב", "שלד: עצם", "קריפר: אבק שריפה", "וויתר: לבנת נתר", "דרקון: נשימת דרקון"] +name = "גולגולות בנות-יצירה!" +description = "באמצעות חומרים תוכל ליצור גולגולות מפלצות!" +lore1 = "הקף בלוק עצם עם הדברים הבאים כדי לקבל גולגולת:" +lore2 = "זומבי: בשר רקוב" +lore3 = "שלד: עצם" +lore4 = "קריפר: אבק שריפה" +lore5 = "וויתר: לבנת נתר" +lore6 = "דרקון: נשימת דרקון" +lore = ["הקף בלוק עצם עם הדברים הבאים כדי לקבל גולגולת:", "זומבי: בשר רקוב", "שלד: עצם", "קריפר: אבק שריפה", "וויתר: לבנת נתר", "דרקון: נשימת דרקון"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "זמן בבקבוק" - description = "נשא בקבוק טמפורלי שאוגר זמן והוצא אותו כדי להאיץ בלוקים מתוזמנים, גידולים, וישויות בעלות גיל כמו חיות תינוק. מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית." - lore1 = "שניות נטענות כל טיק" - lore2 = "האצת זמן לכל שנייה אגורה" - lore3 = "מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית" - lore = ["שניות נטענות כל טיק", "האצת זמן לכל שנייה אגורה", "מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית"] +name = "זמן בבקבוק" +description = "נשא בקבוק טמפורלי שאוגר זמן והוצא אותו כדי להאיץ בלוקים מתוזמנים, גידולים, וישויות בעלות גיל כמו חיות תינוק. מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית." +lore1 = "שניות נטענות כל טיק" +lore2 = "האצת זמן לכל שנייה אגורה" +lore3 = "מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית" +lore = ["שניות נטענות כל טיק", "האצת זמן לכל שנייה אגורה", "מתכון (ללא צורה): שיקוי מהירות + שעון + בקבוק זכוכית"] [chronos.aberrant_touch] - name = "מגע סוטה" - description = "מכות קרב מחילות האטה מצטברת על חשבון רעב, עם מגבלות PvP קפדניות, ומשרשות מטרות ב-5 ערימות." - lore1 = "מכות קרב מחילות האטה מצטברת" - lore2 = "מגבלת משך האטה ב-PvE" - lore3 = "מגבלת עוצמת האטה ב-PvP" - lore = ["מכות קרב מחילות האטה מצטברת", "מגבלת משך האטה ב-PvE", "מגבלת עוצמת האטה ב-PvP"] +name = "מגע סוטה" +description = "מכות קרב מחילות האטה מצטברת על חשבון רעב, עם מגבלות PvP קפדניות, ומשרשות מטרות ב-5 ערימות." +lore1 = "מכות קרב מחילות האטה מצטברת" +lore2 = "מגבלת משך האטה ב-PvE" +lore3 = "מגבלת עוצמת האטה ב-PvP" +lore = ["מכות קרב מחילות האטה מצטברת", "מגבלת משך האטה ב-PvE", "מגבלת עוצמת האטה ב-PvP"] [chronos.instant_recall] - name = "ריקול מיידי" - description = "לחץ שמאל או ימין עם שעון ביד כדי להריץ אחורה לתמונת מצב אחרונה עם שחזור בריאות ורעב." - lore1 = "משך הרצה לאחור" - lore2 = "זמן קירור" - lore3 = "ללא שחזור מלאי" - lore = ["משך הרצה לאחור", "זמן קירור", "ללא שחזור מלאי"] +name = "ריקול מיידי" +description = "לחץ שמאל או ימין עם שעון ביד כדי להריץ אחורה לתמונת מצב אחרונה עם שחזור בריאות ורעב." +lore1 = "משך הרצה לאחור" +lore2 = "זמן קירור" +lore3 = "ללא שחזור מלאי" +lore = ["משך הרצה לאחור", "זמן קירור", "ללא שחזור מלאי"] [chronos.time_bomb] - name = "פצצת זמן" - description = "זרוק פצצת כרונו מעוצבת שיוצרת שדה טמפורלי, מאטה ישויות, ומקפיאה קליעים." - lore1 = "רדיוס שדה טמפורלי" - lore2 = "משך שדה טמפורלי" - lore3 = "זמן קירור פצצה" - lore4 = "מתכון (ללא צורה): שעון + כדור שלג + יהלום + חול" - lore = ["רדיוס שדה טמפורלי", "משך שדה טמפורלי", "זמן קירור פצצה", "מתכון (ללא צורה): שעון + כדור שלג + יהלום + חול"] +name = "פצצת זמן" +description = "זרוק פצצת כרונו מעוצבת שיוצרת שדה טמפורלי, מאטה ישויות, ומקפיאה קליעים." +lore1 = "רדיוס שדה טמפורלי" +lore2 = "משך שדה טמפורלי" +lore3 = "זמן קירור פצצה" +lore4 = "מתכון (ללא צורה): שעון + כדור שלג + יהלום + חול" +lore = ["רדיוס שדה טמפורלי", "משך שדה טמפורלי", "זמן קירור פצצה", "מתכון (ללא צורה): שעון + כדור שלג + יהלום + חול"] # discovery [discovery] [discovery.armor] - name = "שריון עולמי" - description = "שריון פסיבי בהתאם לקשיות הבלוקים הסמוכים." - lore1 = "שריון פסיבי" - lore2 = "מבוסס על קשיות בלוקים סמוכים" - lore3 = "עוצמת שריון:" - lore = ["שריון פסיבי", "מבוסס על קשיות בלוקים סמוכים", "עוצמת שריון:"] +name = "שריון עולמי" +description = "שריון פסיבי בהתאם לקשיות הבלוקים הסמוכים." +lore1 = "שריון פסיבי" +lore2 = "מבוסס על קשיות בלוקים סמוכים" +lore3 = "עוצמת שריון:" +lore = ["שריון פסיבי", "מבוסס על קשיות בלוקים סמוכים", "עוצמת שריון:"] [discovery.unity] - name = "אחדות ניסיונית" - description = "איסוף כדורי ניסיון מוסיף XP למיומנויות אקראיות." - lore1 = "XP " - lore2 = "לכל כדור" - lore = ["XP ", "לכל כדור"] +name = "אחדות ניסיונית" +description = "איסוף כדורי ניסיון מוסיף XP למיומנויות אקראיות." +lore1 = "XP " +lore2 = "לכל כדור" +lore = ["XP ", "לכל כדור"] [discovery.resist] - name = "התנגדות ניסיונית" - description = "צרוך ניסיון כדי להקטין נזק רק כשמכה עלולה להפיל אותך מתחת ל-5 לבבות או להרוג אותך." - lore0 = "מופעל רק בבריאות קריטית (<= 5 לבבות) פעם ב-15 שניות" - lore1 = " נזק מופחת" - lore2 = "ניסיון מנוקז" - lore = ["מופעל רק בבריאות קריטית (<= 5 לבבות) פעם ב-15 שניות", " נזק מופחת", "ניסיון מנוקז"] +name = "התנגדות ניסיונית" +description = "צרוך ניסיון כדי להקטין נזק רק כשמכה עלולה להפיל אותך מתחת ל-5 לבבות או להרוג אותך." +lore0 = "מופעל רק בבריאות קריטית (<= 5 לבבות) פעם ב-15 שניות" +lore1 = " נזק מופחת" +lore2 = "ניסיון מנוקז" +lore = ["מופעל רק בבריאות קריטית (<= 5 לבבות) פעם ב-15 שניות", " נזק מופחת", "ניסיון מנוקז"] [discovery.villager] - name = "משיכת כפריים" - description = "מאפשר לך לקבל עסקאות טובות יותר עם כפריים!" - lore1 = "צורך XP לכל אינטראקציה עם כפריים" - lore2 = "סיכוי לכל אינטראקציה לצרוך XP ולשפר עסקאות" - lore3 = "ניקוז XP נדרש לכל אינטראקציה" - lore = ["צורך XP לכל אינטראקציה עם כפריים", "סיכוי לכל אינטראקציה לצרוך XP ולשפר עסקאות", "ניקוז XP נדרש לכל אינטראקציה"] +name = "משיכת כפריים" +description = "מאפשר לך לקבל עסקאות טובות יותר עם כפריים!" +lore1 = "צורך XP לכל אינטראקציה עם כפריים" +lore2 = "סיכוי לכל אינטראקציה לצרוך XP ולשפר עסקאות" +lore3 = "ניקוז XP נדרש לכל אינטראקציה" +lore = ["צורך XP לכל אינטראקציה עם כפריים", "סיכוי לכל אינטראקציה לצרוך XP ולשפר עסקאות", "ניקוז XP נדרש לכל אינטראקציה"] # enchanting [enchanting] [enchanting.lapis_return] - name = "החזר לאפיס" - description = "במחיר רמה אחת נוספת של XP, ויש סיכוי לקבל לאפיס חינם בתמורה" - lore1 = "לכל רמה, מגדיל את עלות הכישוף ב-1, אבל יכול להחזיר עד 3 לאפיס" - lore = ["לכל רמה, מגדיל את עלות הכישוף ב-1, אבל יכול להחזיר עד 3 לאפיס"] +name = "החזר לאפיס" +description = "במחיר רמה אחת נוספת של XP, ויש סיכוי לקבל לאפיס חינם בתמורה" +lore1 = "לכל רמה, מגדיל את עלות הכישוף ב-1, אבל יכול להחזיר עד 3 לאפיס" +lore = ["לכל רמה, מגדיל את עלות הכישוף ב-1, אבל יכול להחזיר עד 3 לאפיס"] [enchanting.quick_enchant] - name = "כישוף בלחיצה מהירה" - description = "כשף פריטים על ידי לחיצה על ספרי כישוף ישירות עליהם." - lore1 = "רמות משולבות מרביות" - lore2 = "לא ניתן לכשף פריט עם יותר מ-" - lore3 = "כוח" - lore = ["רמות משולבות מרביות", "לא ניתן לכשף פריט עם יותר מ-", "כוח"] +name = "כישוף בלחיצה מהירה" +description = "כשף פריטים על ידי לחיצה על ספרי כישוף ישירות עליהם." +lore1 = "רמות משולבות מרביות" +lore2 = "לא ניתן לכשף פריט עם יותר מ-" +lore3 = "כוח" +lore = ["רמות משולבות מרביות", "לא ניתן לכשף פריט עם יותר מ-", "כוח"] [enchanting.return] - name = "החזר XP" - description = "ניסיון כישוף מוחזר אליך כשאתה מכשף פריט." - lore1 = "לניסיון שהוצאת יש סיכוי להיות מוחזר כשאתה מכשף פריט" - lore2 = "ניסיון לכל כישוף" - lore = ["לניסיון שהוצאת יש סיכוי להיות מוחזר כשאתה מכשף פריט", "ניסיון לכל כישוף"] +name = "החזר XP" +description = "ניסיון כישוף מוחזר אליך כשאתה מכשף פריט." +lore1 = "לניסיון שהוצאת יש סיכוי להיות מוחזר כשאתה מכשף פריט" +lore2 = "ניסיון לכל כישוף" +lore = ["לניסיון שהוצאת יש סיכוי להיות מוחזר כשאתה מכשף פריט", "ניסיון לכל כישוף"] # excavation [excavation] [excavation.haste] - name = "חופר חפוז" - description = "זה יאיץ את תהליך החפירה, עם חיפזון!" - lore1 = "קבל חיפזון בזמן חפירה" - lore2 = "x רמות חיפזון כשאתה מתחיל לכרות כל בלוק." - lore = ["קבל חיפזון בזמן חפירה", "x רמות חיפזון כשאתה מתחיל לכרות כל בלוק."] +name = "חופר חפוז" +description = "זה יאיץ את תהליך החפירה, עם חיפזון!" +lore1 = "קבל חיפזון בזמן חפירה" +lore2 = "x רמות חיפזון כשאתה מתחיל לכרות כל בלוק." +lore = ["קבל חיפזון בזמן חפירה", "x רמות חיפזון כשאתה מתחיל לכרות כל בלוק."] [excavation.spelunker] - name = "חוקר מערות על-חושי!" - description = "ראה עפרות בעיניך, דרך האדמה!" - lore1 = "עפרה ביד השנייה, גרגרי זוהר ביד הראשית, והתכופף!" - lore2 = "טווח בלוקים: " - lore3 = "צורך גרגר זוהר בשימוש" - lore = ["עפרה ביד השנייה, גרגרי זוהר ביד הראשית, והתכופף!", "טווח בלוקים: ", "צורך גרגר זוהר בשימוש"] +name = "חוקר מערות על-חושי!" +description = "ראה עפרות בעיניך, דרך האדמה!" +lore1 = "עפרה ביד השנייה, גרגרי זוהר ביד הראשית, והתכופף!" +lore2 = "טווח בלוקים: " +lore3 = "צורך גרגר זוהר בשימוש" +lore = ["עפרה ביד השנייה, גרגרי זוהר ביד הראשית, והתכופף!", "טווח בלוקים: ", "צורך גרגר זוהר בשימוש"] [excavation.drop_to_inventory] - name = "את-ישר-למלאי" +name = "את-ישר-למלאי" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "הלדרמן המפואר המעוצב-יתר של טאקל" - lore1 = "כנראה החזק מבין רבים מאפשר לך" - lore2 = "למזג ולשנות כלים באופן דינמי, בהתאם לצרכים שלך." - lore3 = "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך." - lore4 = "לשחרור כלים, התכופף-והפל את הפריט, והוא יתפרק." - lore5 = "אי אפשר לשבור כלים בלדרמן הזה אבל אי אפשר להשתמש בכלים שבורים" - lore6 = "פריטים מרביים הניתנים למיזוג." - lore7 = "אפשר להשתמש בחמישה או שישה כלים, או רק אחד!" - lore = ["כנראה החזק מבין רבים מאפשר לך", "למזג ולשנות כלים באופן דינמי, בהתאם לצרכים שלך.", "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך.", "לשחרור כלים, התכופף-והפל את הפריט, והוא יתפרק.", "אי אפשר לשבור כלים בלדרמן הזה אבל אי אפשר להשתמש בכלים שבורים", "פריטים מרביים הניתנים למיזוג.", "אפשר להשתמש בחמישה או שישה כלים, או רק אחד!"] +name = "OMNI - T.O.O.L." +description = "הלדרמן המפואר המעוצב-יתר של טאקל" +lore1 = "כנראה החזק מבין רבים מאפשר לך" +lore2 = "למזג ולשנות כלים באופן דינמי, בהתאם לצרכים שלך." +lore3 = "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך." +lore4 = "לשחרור כלים, התכופף-והפל את הפריט, והוא יתפרק." +lore5 = "אי אפשר לשבור כלים בלדרמן הזה אבל אי אפשר להשתמש בכלים שבורים" +lore6 = "פריטים מרביים הניתנים למיזוג." +lore7 = "אפשר להשתמש בחמישה או שישה כלים, או רק אחד!" +lore = ["כנראה החזק מבין רבים מאפשר לך", "למזג ולשנות כלים באופן דינמי, בהתאם לצרכים שלך.", "למיזוג, לחץ shift על פריט מעל אחר במלאי שלך.", "לשחרור כלים, התכופף-והפל את הפריט, והוא יתפרק.", "אי אפשר לשבור כלים בלדרמן הזה אבל אי אפשר להשתמש בכלים שבורים", "פריטים מרביים הניתנים למיזוג.", "אפשר להשתמש בחמישה או שישה כלים, או רק אחד!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "הילת צמיחה" - description = "גדל טבע סביבך בהילה" - lore1 = "רדיוס בלוקים" - lore2 = "עוצמת הילת צמיחה" - lore3 = "עלות אוכל" - lore = ["רדיוס בלוקים", "עוצמת הילת צמיחה", "עלות אוכל"] +name = "הילת צמיחה" +description = "גדל טבע סביבך בהילה" +lore1 = "רדיוס בלוקים" +lore2 = "עוצמת הילת צמיחה" +lore3 = "עלות אוכל" +lore = ["רדיוס בלוקים", "עוצמת הילת צמיחה", "עלות אוכל"] [herbalism.hippo] - name = "היפו של הצמחאי" - description = "צריכת אוכל נותנת לך יותר רוויה" - lore1 = "אוכל) נקודות רוויה נוספות בצריכה" - lore = ["אוכל) נקודות רוויה נוספות בצריכה"] +name = "היפו של הצמחאי" +description = "צריכת אוכל נותנת לך יותר רוויה" +lore1 = "אוכל) נקודות רוויה נוספות בצריכה" +lore = ["אוכל) נקודות רוויה נוספות בצריכה"] [herbalism.myconid] - name = "מיקוניד של הצמחאי" - description = "נותן לך את היכולת ליצור מיצליום" - lore1 = "כל אדמה, ופטריה חומה ואדומה ייצרו מיצליום." - lore = ["כל אדמה, ופטריה חומה ואדומה ייצרו מיצליום."] +name = "מיקוניד של הצמחאי" +description = "נותן לך את היכולת ליצור מיצליום" +lore1 = "כל אדמה, ופטריה חומה ואדומה ייצרו מיצליום." +lore = ["כל אדמה, ופטריה חומה ואדומה ייצרו מיצליום."] [herbalism.terralid] - name = "טראליד של הצמחאי" - description = "נותן לך את היכולת ליצור בלוקי דשא" - lore1 = "שלושה זרעים, מעל 3 אדמה, ייצרו 3 בלוקי דשא." - lore = ["שלושה זרעים, מעל 3 אדמה, ייצרו 3 בלוקי דשא."] +name = "טראליד של הצמחאי" +description = "נותן לך את היכולת ליצור בלוקי דשא" +lore1 = "שלושה זרעים, מעל 3 אדמה, ייצרו 3 בלוקי דשא." +lore = ["שלושה זרעים, מעל 3 אדמה, ייצרו 3 בלוקי דשא."] [herbalism.cobweb] - name = "יוצר קורים" - description = "נותן לך את היכולת ליצור קורי עכביש בשולחן יצירה" - lore1 = "תשעה חוטים ייצרו קור עכביש." - lore = ["תשעה חוטים ייצרו קור עכביש."] +name = "יוצר קורים" +description = "נותן לך את היכולת ליצור קורי עכביש בשולחן יצירה" +lore1 = "תשעה חוטים ייצרו קור עכביש." +lore = ["תשעה חוטים ייצרו קור עכביש."] [herbalism.mushroom_blocks] - name = "יוצר פטריות" - description = "נותן לך את היכולת ליצור בלוקי פטריות בשולחן יצירה" - lore1 = "ארבע פטריות ליצירת בלוק, או בלוק ליצירת גזע." - lore = ["ארבע פטריות ליצירת בלוק, או בלוק ליצירת גזע."] +name = "יוצר פטריות" +description = "נותן לך את היכולת ליצור בלוקי פטריות בשולחן יצירה" +lore1 = "ארבע פטריות ליצירת בלוק, או בלוק ליצירת גזע." +lore = ["ארבע פטריות ליצירת בלוק, או בלוק ליצירת גזע."] [herbalism.drop_to_inventory] - name = "מעדר-ישר-למלאי" +name = "מעדר-ישר-למלאי" [herbalism.hungry_shield] - name = "מגן רעב" - description = "קבל נזק לרעב שלך לפני הבריאות." - lore1 = "מופחת על ידי רעב" - lore = ["מופחת על ידי רעב"] +name = "מגן רעב" +description = "קבל נזק לרעב שלך לפני הבריאות." +lore1 = "מופחת על ידי רעב" +lore = ["מופחת על ידי רעב"] [herbalism.luck] - name = "מזל הצמחאי" - description = "כשאתה שובר דשא/פרחים, יש סיכוי לקבל פריט אקראי" - lore0 = "פרחים = אוכל, ודשא = זרעים" - lore1 = "סיכוי לקבל פריט משבירת פרחים" - lore2 = "סיכוי לקבל פריט משבירת דשא" - lore = ["פרחים = אוכל, ודשא = זרעים", "סיכוי לקבל פריט משבירת פרחים", "סיכוי לקבל פריט משבירת דשא"] +name = "מזל הצמחאי" +description = "כשאתה שובר דשא/פרחים, יש סיכוי לקבל פריט אקראי" +lore0 = "פרחים = אוכל, ודשא = זרעים" +lore1 = "סיכוי לקבל פריט משבירת פרחים" +lore2 = "סיכוי לקבל פריט משבירת דשא" +lore = ["פרחים = אוכל, ודשא = זרעים", "סיכוי לקבל פריט משבירת פרחים", "סיכוי לקבל פריט משבירת דשא"] [herbalism.replant] - name = "קציר ושתילה מחדש" - description = "לחץ ימני על יבול עם מעדר כדי לקצור ולשתול מחדש." - lore1 = "רדיוס שתילה מחדש של בלוקים" - lore = ["רדיוס שתילה מחדש של בלוקים"] +name = "קציר ושתילה מחדש" +description = "לחץ ימני על יבול עם מעדר כדי לקצור ולשתול מחדש." +lore1 = "רדיוס שתילה מחדש של בלוקים" +lore = ["רדיוס שתילה מחדש של בלוקים"] # hunter [hunter] [hunter.adrenaline] - name = "אדרנלין" - description = "גרום יותר נזק ככל שהבריאות שלך נמוכה יותר (קרב קרוב)" - lore1 = "נזק מרבי" - lore = ["נזק מרבי"] +name = "אדרנלין" +description = "גרום יותר נזק ככל שהבריאות שלך נמוכה יותר (קרב קרוב)" +lore1 = "נזק מרבי" +lore = ["נזק מרבי"] [hunter.penalty] - name = "" - description = "" - lore1 = "תקבל ערימות רעל אם נגמר לך הרעב" - lore = ["תקבל ערימות רעל אם נגמר לך הרעב"] +name = "" +description = "" +lore1 = "תקבל ערימות רעל אם נגמר לך הרעב" +lore = ["תקבל ערימות רעל אם נגמר לך הרעב"] [hunter.drop_to_inventory] - name = "פריטים-ישר-למלאי" - description = "כשאתה הורג משהו / שובר בלוק עם חרב, הדרופים משתגרים למלאי שלך" - lore1 = "בכל פעם שנופל פריט ממפלצת/בלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר." - lore = ["בכל פעם שנופל פריט ממפלצת/בלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר."] +name = "פריטים-ישר-למלאי" +description = "כשאתה הורג משהו / שובר בלוק עם חרב, הדרופים משתגרים למלאי שלך" +lore1 = "בכל פעם שנופל פריט ממפלצת/בלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר." +lore = ["בכל פעם שנופל פריט ממפלצת/בלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר."] [hunter.invisibility] - name = "צעד נעלם" - description = "כשנפגע אתה מקבל אי-נראות, על חשבון רעב" - lore1 = "קבל אי-נראות פסיבית כשנפגע" - lore2 = "x ערימות אי-נראות למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "משך אי-נראות" - lore = ["קבל אי-נראות פסיבית כשנפגע", "x ערימות אי-נראות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "משך אי-נראות"] +name = "צעד נעלם" +description = "כשנפגע אתה מקבל אי-נראות, על חשבון רעב" +lore1 = "קבל אי-נראות פסיבית כשנפגע" +lore2 = "x ערימות אי-נראות למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "משך אי-נראות" +lore = ["קבל אי-נראות פסיבית כשנפגע", "x ערימות אי-נראות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "משך אי-נראות"] [hunter.jump_boost] - name = "גבהים של הצייד" - description = "כשנפגע אתה מקבל חיזוק קפיצה, על חשבון רעב" - lore1 = "קבל חיזוק קפיצה פסיבי כשנפגע" - lore2 = "x ערימות חיזוק קפיצה למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות חיזוק קפיצה, לא משך." - lore = ["קבל חיזוק קפיצה פסיבי כשנפגע", "x ערימות חיזוק קפיצה למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות חיזוק קפיצה, לא משך."] +name = "גבהים של הצייד" +description = "כשנפגע אתה מקבל חיזוק קפיצה, על חשבון רעב" +lore1 = "קבל חיזוק קפיצה פסיבי כשנפגע" +lore2 = "x ערימות חיזוק קפיצה למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות חיזוק קפיצה, לא משך." +lore = ["קבל חיזוק קפיצה פסיבי כשנפגע", "x ערימות חיזוק קפיצה למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות חיזוק קפיצה, לא משך."] [hunter.luck] - name = "מזל הצייד" - description = "כשנפגע אתה מקבל מזל, על חשבון רעב" - lore1 = "קבל מזל פסיבי כשנפגע" - lore2 = "x ערימות מזל למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות מזל, לא משך." - lore = ["קבל מזל פסיבי כשנפגע", "x ערימות מזל למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות מזל, לא משך."] +name = "מזל הצייד" +description = "כשנפגע אתה מקבל מזל, על חשבון רעב" +lore1 = "קבל מזל פסיבי כשנפגע" +lore2 = "x ערימות מזל למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות מזל, לא משך." +lore = ["קבל מזל פסיבי כשנפגע", "x ערימות מזל למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות מזל, לא משך."] [hunter.regen] - name = "התחדשות הצייד" - description = "כשנפגע אתה מקבל התחדשות, על חשבון רעב" - lore1 = "קבל התחדשות פסיבית כשנפגע" - lore2 = "x ערימות התחדשות למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות התחדשות, לא משך." - lore = ["קבל התחדשות פסיבית כשנפגע", "x ערימות התחדשות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות התחדשות, לא משך."] +name = "התחדשות הצייד" +description = "כשנפגע אתה מקבל התחדשות, על חשבון רעב" +lore1 = "קבל התחדשות פסיבית כשנפגע" +lore2 = "x ערימות התחדשות למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות התחדשות, לא משך." +lore = ["קבל התחדשות פסיבית כשנפגע", "x ערימות התחדשות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות התחדשות, לא משך."] [hunter.resistance] - name = "התנגדות הצייד" - description = "כשנפגע אתה מקבל התנגדות, על חשבון רעב" - lore1 = "קבל התנגדות פסיבית כשנפגע" - lore2 = "x ערימות התנגדות למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות התנגדות, לא משך." - lore = ["קבל התנגדות פסיבית כשנפגע", "x ערימות התנגדות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות התנגדות, לא משך."] +name = "התנגדות הצייד" +description = "כשנפגע אתה מקבל התנגדות, על חשבון רעב" +lore1 = "קבל התנגדות פסיבית כשנפגע" +lore2 = "x ערימות התנגדות למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות התנגדות, לא משך." +lore = ["קבל התנגדות פסיבית כשנפגע", "x ערימות התנגדות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות התנגדות, לא משך."] [hunter.speed] - name = "מהירות הצייד" - description = "כשנפגע אתה מקבל מהירות, על חשבון רעב" - lore1 = "קבל מהירות פסיבית כשנפגע" - lore2 = "x ערימות מהירות למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות מהירות, לא משך." - lore = ["קבל מהירות פסיבית כשנפגע", "x ערימות מהירות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות מהירות, לא משך."] +name = "מהירות הצייד" +description = "כשנפגע אתה מקבל מהירות, על חשבון רעב" +lore1 = "קבל מהירות פסיבית כשנפגע" +lore2 = "x ערימות מהירות למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות מהירות, לא משך." +lore = ["קבל מהירות פסיבית כשנפגע", "x ערימות מהירות למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות מהירות, לא משך."] [hunter.strength] - name = "עוצמת הצייד" - description = "כשנפגע אתה מקבל עוצמה, על חשבון רעב" - lore1 = "קבל עוצמה פסיבית כשנפגע" - lore2 = "x ערימות עוצמה למשך 3 שניות בפגיעה" - lore3 = "x רעב מצטבר" - lore4 = "משך ומכפיל ערימות רעב." - lore5 = "מכפיל ערימות עוצמה, לא משך." - lore = ["קבל עוצמה פסיבית כשנפגע", "x ערימות עוצמה למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות עוצמה, לא משך."] +name = "עוצמת הצייד" +description = "כשנפגע אתה מקבל עוצמה, על חשבון רעב" +lore1 = "קבל עוצמה פסיבית כשנפגע" +lore2 = "x ערימות עוצמה למשך 3 שניות בפגיעה" +lore3 = "x רעב מצטבר" +lore4 = "משך ומכפיל ערימות רעב." +lore5 = "מכפיל ערימות עוצמה, לא משך." +lore = ["קבל עוצמה פסיבית כשנפגע", "x ערימות עוצמה למשך 3 שניות בפגיעה", "x רעב מצטבר", "משך ומכפיל ערימות רעב.", "מכפיל ערימות עוצמה, לא משך."] # nether [nether] [nether.skull_toss] - name = "זריקת גולגולת וויתר" - description1 = "שחרר את הוויתר הפנימי שלך על ידי שימוש" - description2 = "במישהו" - description3 = "ראש." - lore1 = "שניות קירור בין זריקות גולגולת." - lore2 = "שימוש בגולגולת וויתר: זרוק " - lore3 = "גולגולת וויתר" - lore4 = "שמתפוצצת בפגיעה." - lore = ["שניות קירור בין זריקות גולגולת.", "שימוש בגולגולת וויתר: זרוק ", "גולגולת וויתר", "שמתפוצצת בפגיעה."] +name = "זריקת גולגולת וויתר" +description1 = "שחרר את הוויתר הפנימי שלך על ידי שימוש" +description2 = "במישהו" +description3 = "ראש." +lore1 = "שניות קירור בין זריקות גולגולת." +lore2 = "שימוש בגולגולת וויתר: זרוק " +lore3 = "גולגולת וויתר" +lore4 = "שמתפוצצת בפגיעה." +lore = ["שניות קירור בין זריקות גולגולת.", "שימוש בגולגולת וויתר: זרוק ", "גולגולת וויתר", "שמתפוצצת בפגיעה."] [nether.wither_resist] - name = "התנגדות לכמילה" - description = "מתנגד לכמילה דרך כוחו של נתרייט." - lore1 = "סיכוי לבטל כמילה (לכל חלק)." - lore2 = "פסיבי: לבישת שריון נתרייט מעניקה סיכוי לבטל " - lore3 = "כמילה." - lore = ["סיכוי לבטל כמילה (לכל חלק).", "פסיבי: לבישת שריון נתרייט מעניקה סיכוי לבטל ", "כמילה."] +name = "התנגדות לכמילה" +description = "מתנגד לכמילה דרך כוחו של נתרייט." +lore1 = "סיכוי לבטל כמילה (לכל חלק)." +lore2 = "פסיבי: לבישת שריון נתרייט מעניקה סיכוי לבטל " +lore3 = "כמילה." +lore = ["סיכוי לבטל כמילה (לכל חלק).", "פסיבי: לבישת שריון נתרייט מעניקה סיכוי לבטל ", "כמילה."] [nether.fire_resist] - name = "התנגדות לאש" - description = "מתנגד לאש על ידי הקשחת העור." - lore1 = "סיכוי לבטל את אפקט הבעירה!" - lore = ["סיכוי לבטל את אפקט הבעירה!"] +name = "התנגדות לאש" +description = "מתנגד לאש על ידי הקשחת העור." +lore1 = "סיכוי לבטל את אפקט הבעירה!" +lore = ["סיכוי לבטל את אפקט הבעירה!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "התכה אוטומטית" - description = "מאפשר לך להתיך עפרות וניל שנכרו" - lore1 = "עפרות הניתנות להתכה מותכות אוטומטית" - lore2 = "% סיכוי לתוספת" - lore = ["עפרות הניתנות להתכה מותכות אוטומטית", "% סיכוי לתוספת"] +name = "התכה אוטומטית" +description = "מאפשר לך להתיך עפרות וניל שנכרו" +lore1 = "עפרות הניתנות להתכה מותכות אוטומטית" +lore2 = "% סיכוי לתוספת" +lore = ["עפרות הניתנות להתכה מותכות אוטומטית", "% סיכוי לתוספת"] [pickaxe.chisel] - name = "אזמל עפרות" - description = "לחץ ימני על עפרות כדי לאזמל עוד עפרה מהן, בעלות עמידות חמורה." - lore1 = "סיכוי להפלה" - lore2 = "בלאי כלי" - lore = ["סיכוי להפלה", "בלאי כלי"] +name = "אזמל עפרות" +description = "לחץ ימני על עפרות כדי לאזמל עוד עפרה מהן, בעלות עמידות חמורה." +lore1 = "סיכוי להפלה" +lore2 = "בלאי כלי" +lore = ["סיכוי להפלה", "בלאי כלי"] [pickaxe.drop_to_inventory] - name = "מכוש-ישר-למלאי" - description = "כשאתה שובר בלוק, הפריט משתגר למלאי שלך" - lore1 = "בכל פעם שנופל פריט מבלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר." - lore = ["בכל פעם שנופל פריט מבלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר."] +name = "מכוש-ישר-למלאי" +description = "כשאתה שובר בלוק, הפריט משתגר למלאי שלך" +lore1 = "בכל פעם שנופל פריט מבלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר." +lore = ["בכל פעם שנופל פריט מבלוק שאתה שובר, הוא נכנס למלאי שלך אם אפשר."] [pickaxe.silk_spawner] - name = "מכוש משי-יוצר-מפלצות" - description = "גורם ליוצרי מפלצות ליפול כשנשברים" - lore1 = "הופך יוצרי מפלצות לשבירים עם מגע-משי." - lore2 = "הופך יוצרי מפלצות לשבירים תוך כדי התכופפות." - lore = ["הופך יוצרי מפלצות לשבירים עם מגע-משי.", "הופך יוצרי מפלצות לשבירים תוך כדי התכופפות."] +name = "מכוש משי-יוצר-מפלצות" +description = "גורם ליוצרי מפלצות ליפול כשנשברים" +lore1 = "הופך יוצרי מפלצות לשבירים עם מגע-משי." +lore2 = "הופך יוצרי מפלצות לשבירים תוך כדי התכופפות." +lore = ["הופך יוצרי מפלצות לשבירים עם מגע-משי.", "הופך יוצרי מפלצות לשבירים תוך כדי התכופפות."] [pickaxe.vein_miner] - name = "כורה ורידים" - description = "מאפשר לך לשבור בלוקים בוריד/אשכול של עפרות וניל" - lore1 = "התכופף, וכרה עפרות" - lore2 = "טווח כריית ורידים" - lore3 = "מיומנות זו לא מקבצת את כל הדרופים יחד!" - lore = ["התכופף, וכרה עפרות", "טווח כריית ורידים", "מיומנות זו לא מקבצת את כל הדרופים יחד!"] +name = "כורה ורידים" +description = "מאפשר לך לשבור בלוקים בוריד/אשכול של עפרות וניל" +lore1 = "התכופף, וכרה עפרות" +lore2 = "טווח כריית ורידים" +lore3 = "מיומנות זו לא מקבצת את כל הדרופים יחד!" +lore = ["התכופף, וכרה עפרות", "טווח כריית ורידים", "מיומנות זו לא מקבצת את כל הדרופים יחד!"] # ranged [ranged] [ranged.arrow_recovery] - name = "שחזור חצים" - description = "שחזר חצים אחרי שהרגת אויב." - lore1 = "סיכוי לשחזר חצים בפגיעה/הריגה" - lore2 = "סיכוי: " - lore = ["סיכוי לשחזר חצים בפגיעה/הריגה", "סיכוי: "] +name = "שחזור חצים" +description = "שחזר חצים אחרי שהרגת אויב." +lore1 = "סיכוי לשחזר חצים בפגיעה/הריגה" +lore2 = "סיכוי: " +lore = ["סיכוי לשחזר חצים בפגיעה/הריגה", "סיכוי: "] [ranged.web_shot] - name = "מלכודת קורים" - description = "הקף את המטרה שלך בקורי עכביש כשאתה פוגע בה!" - lore1 = "8 קורי עכביש סביב כדור שלג, וזרוק!" - lore2 = "שניות של כלוב, בערך." - lore = ["8 קורי עכביש סביב כדור שלג, וזרוק!", "שניות של כלוב, בערך."] +name = "מלכודת קורים" +description = "הקף את המטרה שלך בקורי עכביש כשאתה פוגע בה!" +lore1 = "8 קורי עכביש סביב כדור שלג, וזרוק!" +lore2 = "שניות של כלוב, בערך." +lore = ["8 קורי עכביש סביב כדור שלג, וזרוק!", "שניות של כלוב, בערך."] [ranged.force_shot] - name = "יריית כוח" - description = "ירה קליעים רחוק ומהר יותר!" - advancementname = "יריה ארוכה" - advancementlore = "נחת יריה ממרחק של מעל 30 בלוקים!" - lore1 = "מהירות קליע" - lore = ["מהירות קליע"] +name = "יריית כוח" +description = "ירה קליעים רחוק ומהר יותר!" +advancementname = "יריה ארוכה" +advancementlore = "נחת יריה ממרחק של מעל 30 בלוקים!" +lore1 = "מהירות קליע" +lore = ["מהירות קליע"] [ranged.lunge_shot] - name = "יריית זינוק" - description = "בזמן נפילה החצים שלך זורקים אותך לכיוון אקראי" - lore1 = "מהירות פרץ אקראית" - lore = ["מהירות פרץ אקראית"] +name = "יריית זינוק" +description = "בזמן נפילה החצים שלך זורקים אותך לכיוון אקראי" +lore1 = "מהירות פרץ אקראית" +lore = ["מהירות פרץ אקראית"] [ranged.arrow_piercing] - name = "חץ חודר" - description = "מוסיף חדירה לקליעים! ירה דרך דברים!" - lore1 = "מטרות חדירה" - lore = ["מטרות חדירה"] +name = "חץ חודר" +description = "מוסיף חדירה לקליעים! ירה דרך דברים!" +lore1 = "מטרות חדירה" +lore = ["מטרות חדירה"] # rift [rift] [rift.remote_access] - name = "גישה מרחוק" - description = "שלוף מהריק, והיכנס למכולה מסומנת." - lore1 = "פנינת אנדר + מצפן = מפתח שער רליקוויה" - lore2 = "פריט זה מאפשר לך לגשת למכולות מרחוק" - lore3 = "אחרי היצירה הסתכל על הפריט לראות שימוש" - notcontainer = "זה לא מכולה" - lore = ["פנינת אנדר + מצפן = מפתח שער רליקוויה", "פריט זה מאפשר לך לגשת למכולות מרחוק", "אחרי היצירה הסתכל על הפריט לראות שימוש"] +name = "גישה מרחוק" +description = "שלוף מהריק, והיכנס למכולה מסומנת." +lore1 = "פנינת אנדר + מצפן = מפתח שער רליקוויה" +lore2 = "פריט זה מאפשר לך לגשת למכולות מרחוק" +lore3 = "אחרי היצירה הסתכל על הפריט לראות שימוש" +notcontainer = "זה לא מכולה" +lore = ["פנינת אנדר + מצפן = מפתח שער רליקוויה", "פריט זה מאפשר לך לגשת למכולות מרחוק", "אחרי היצירה הסתכל על הפריט לראות שימוש"] [rift.blink] - name = "הבזק קרע" - description = "שיגור מיידי לטווח קצר, רק הבזק!" - lore1 = "בלוקים בהבזק (2x אנכי)" - lore2 = "בזמן ריצה: הקש כפול על קפיצה כדי " - lore3 = "להבזק" - lore = ["בלוקים בהבזק (2x אנכי)", "בזמן ריצה: הקש כפול על קפיצה כדי ", "להבזק"] +name = "הבזק קרע" +description = "שיגור מיידי לטווח קצר, רק הבזק!" +lore1 = "בלוקים בהבזק (2x אנכי)" +lore2 = "בזמן ריצה: הקש כפול על קפיצה כדי " +lore3 = "להבזק" +lore = ["בלוקים בהבזק (2x אנכי)", "בזמן ריצה: הקש כפול על קפיצה כדי ", "להבזק"] [rift.chest] - name = "ארגז אנדר קל" - description = "פתח ארגז אנדר על ידי לחיצה שמאלית עליו ביד." - lore1 = "לחץ על ארגז אנדר ביד כדי לפתוח (פשוט אל תניח אותו)" - lore = ["לחץ על ארגז אנדר ביד כדי לפתוח (פשוט אל תניח אותו)"] +name = "ארגז אנדר קל" +description = "פתח ארגז אנדר על ידי לחיצה שמאלית עליו ביד." +lore1 = "לחץ על ארגז אנדר ביד כדי לפתוח (פשוט אל תניח אותו)" +lore = ["לחץ על ארגז אנדר ביד כדי לפתוח (פשוט אל תניח אותו)"] [rift.descent] - name = "נגד-ריחוף" - description = "נמאס לך להיתקע באוויר? זו המיומנות בשבילך!" - lore1 = "פשוט התכופף כדי לרדת, ותיפול בקצב איטי מהרגיל!" - lore2 = "זמן קירור:" - lore = ["פשוט התכופף כדי לרדת, ותיפול בקצב איטי מהרגיל!", "זמן קירור:"] +name = "נגד-ריחוף" +description = "נמאס לך להיתקע באוויר? זו המיומנות בשבילך!" +lore1 = "פשוט התכופף כדי לרדת, ותיפול בקצב איטי מהרגיל!" +lore2 = "זמן קירור:" +lore = ["פשוט התכופף כדי לרדת, ותיפול בקצב איטי מהרגיל!", "זמן קירור:"] [rift.gate] - name = "שער קרע" - description = "השתגר למיקום מסומן." - lore1 = "יצירה: אזמרגד + רסיס אמטיסט + פנינת אנדר" - lore2 = "קרא לפני השימוש!" - lore3 = "השהיה של 5 שניות, " - lore4 = "אתה יכול למות בזמן האנימציה הזו" - lore = ["יצירה: אזמרגד + רסיס אמטיסט + פנינת אנדר", "קרא לפני השימוש!", "השהיה של 5 שניות, ", "אתה יכול למות בזמן האנימציה הזו"] +name = "שער קרע" +description = "השתגר למיקום מסומן." +lore1 = "יצירה: אזמרגד + רסיס אמטיסט + פנינת אנדר" +lore2 = "קרא לפני השימוש!" +lore3 = "השהיה של 5 שניות, " +lore4 = "אתה יכול למות בזמן האנימציה הזו" +lore = ["יצירה: אזמרגד + רסיס אמטיסט + פנינת אנדר", "קרא לפני השימוש!", "השהיה של 5 שניות, ", "אתה יכול למות בזמן האנימציה הזו"] [rift.resist] - name = "התנגדות קרע" - description = "קבל התנגדות בעת שימוש בפריטי ויכולות אנדר" - lore1 = "+ פסיבי: מעניק התנגדות כשאתה משתמש ביכולות קרע, או פריטי אנדר" - lore2 = "לא כולל ארגז אנדר נייד, רק דברים שאתה יכול לצרוך" - lore = ["+ פסיבי: מעניק התנגדות כשאתה משתמש ביכולות קרע, או פריטי אנדר", "לא כולל ארגז אנדר נייד, רק דברים שאתה יכול לצרוך"] +name = "התנגדות קרע" +description = "קבל התנגדות בעת שימוש בפריטי ויכולות אנדר" +lore1 = "+ פסיבי: מעניק התנגדות כשאתה משתמש ביכולות קרע, או פריטי אנדר" +lore2 = "לא כולל ארגז אנדר נייד, רק דברים שאתה יכול לצרוך" +lore = ["+ פסיבי: מעניק התנגדות כשאתה משתמש ביכולות קרע, או פריטי אנדר", "לא כולל ארגז אנדר נייד, רק דברים שאתה יכול לצרוך"] [rift.visage] - name = "מסווה קרע" - description = "מונע מאנדרמנים להפוך לעוינים אם יש לך פניני אנדר במלאי." - lore1 = "אנדרמנים לא יהפכו לעוינים אם יש לך פניני אנדר במלאי." - lore = ["אנדרמנים לא יהפכו לעוינים אם יש לך פניני אנדר במלאי."] +name = "מסווה קרע" +description = "מונע מאנדרמנים להפוך לעוינים אם יש לך פניני אנדר במלאי." +lore1 = "אנדרמנים לא יהפכו לעוינים אם יש לך פניני אנדר במלאי." +lore = ["אנדרמנים לא יהפכו לעוינים אם יש לך פניני אנדר במלאי."] # seaborn [seaborn] [seaborn.oxygen] - name = "מיכל חמצן אורגני" - description = "החזק יותר חמצן בריאות הזעירות שלך!" - lore1 = "הגדלת קיבולת חמצן" - lore = ["הגדלת קיבולת חמצן"] +name = "מיכל חמצן אורגני" +description = "החזק יותר חמצן בריאות הזעירות שלך!" +lore1 = "הגדלת קיבולת חמצן" +lore = ["הגדלת קיבולת חמצן"] [seaborn.fishers_fantasy] - name = "פנטזיית הדייג" - description = "הרווח יותר XP מדיג, וקבל יותר דגים!" - lore1 = "לכל רמה יש סיכוי לקבל יותר XP ודגים!" - lore = ["לכל רמה יש סיכוי לקבל יותר XP ודגים!"] +name = "פנטזיית הדייג" +description = "הרווח יותר XP מדיג, וקבל יותר דגים!" +lore1 = "לכל רמה יש סיכוי לקבל יותר XP ודגים!" +lore = ["לכל רמה יש סיכוי לקבל יותר XP ודגים!"] [seaborn.haste] - name = "כורה צבים" - description = "בזמן כרייה מתחת למים אתה מקבל חיפזון!" - lore1 = "חיפזון 3 מופעל מתחת למים בזמן כרייה (מצטבר עם זיקת מים) אחרי שאפקט נשימת המים שלך נגמר!" - lore = ["חיפזון 3 מופעל מתחת למים בזמן כרייה (מצטבר עם זיקת מים) אחרי שאפקט נשימת המים שלך נגמר!"] +name = "כורה צבים" +description = "בזמן כרייה מתחת למים אתה מקבל חיפזון!" +lore1 = "חיפזון 3 מופעל מתחת למים בזמן כרייה (מצטבר עם זיקת מים) אחרי שאפקט נשימת המים שלך נגמר!" +lore = ["חיפזון 3 מופעל מתחת למים בזמן כרייה (מצטבר עם זיקת מים) אחרי שאפקט נשימת המים שלך נגמר!"] [seaborn.night_vision] - name = "חזון הצב" - description = "בזמן שאתה מתחת למים, אתה מקבל ראיית לילה" - lore1 = "פשוט קבל ראיית לילה מתחת למים אחרי שאפקט נשימת המים שלך נגמר!" - lore = ["פשוט קבל ראיית לילה מתחת למים אחרי שאפקט נשימת המים שלך נגמר!"] +name = "חזון הצב" +description = "בזמן שאתה מתחת למים, אתה מקבל ראיית לילה" +lore1 = "פשוט קבל ראיית לילה מתחת למים אחרי שאפקט נשימת המים שלך נגמר!" +lore = ["פשוט קבל ראיית לילה מתחת למים אחרי שאפקט נשימת המים שלך נגמר!"] [seaborn.dolphin_grace] - name = "חסד הדולפין" - description = "שחה כמו דולפין, בלי הדולפינים" - lore1 = "+ פסיבי: קבל " - lore2 = "x מהירות (חסד דולפין)" - lore3 = "הנדסה גרמנית מדויק- רגע זה לא נכון... לא תואם עם צעד עמוק" - lore = ["+ פסיבי: קבל ", "x מהירות (חסד דולפין)", "הנדסה גרמנית מדויק- רגע זה לא נכון... לא תואם עם צעד עמוק"] +name = "חסד הדולפין" +description = "שחה כמו דולפין, בלי הדולפינים" +lore1 = "+ פסיבי: קבל " +lore2 = "x מהירות (חסד דולפין)" +lore3 = "הנדסה גרמנית מדויק- רגע זה לא נכון... לא תואם עם צעד עמוק" +lore = ["+ פסיבי: קבל ", "x מהירות (חסד דולפין)", "הנדסה גרמנית מדויק- רגע זה לא נכון... לא תואם עם צעד עמוק"] # stealth [stealth] [stealth.ghost_armor] - name = "שריון הרוח" - description = "שריון שנבנה לאט כשלא נפגע, מחזיק מכה אחת" - lore1 = "שריון מרבי" - lore2 = "מהירות" - lore = ["שריון מרבי", "מהירות"] +name = "שריון הרוח" +description = "שריון שנבנה לאט כשלא נפגע, מחזיק מכה אחת" +lore1 = "שריון מרבי" +lore2 = "מהירות" +lore = ["שריון מרבי", "מהירות"] [stealth.night_vision] - name = "ראיית התגנבות" - description = "קבל ראיית לילה תוך כדי התגנבות" - lore1 = "קבל פרץ של " - lore2 = "ראיית לילה" - lore3 = "תוך כדי התגנבות" - lore = ["קבל פרץ של ", "ראיית לילה", "תוך כדי התגנבות"] +name = "ראיית התגנבות" +description = "קבל ראיית לילה תוך כדי התגנבות" +lore1 = "קבל פרץ של " +lore2 = "ראיית לילה" +lore3 = "תוך כדי התגנבות" +lore = ["קבל פרץ של ", "ראיית לילה", "תוך כדי התגנבות"] [stealth.snatch] - name = "חטיפת פריטים" - description = "חטוף פריטים שנפלו מיד תוך כדי התגנבות!" - lore1 = "רדיוס חטיפה" - lore = ["רדיוס חטיפה"] +name = "חטיפת פריטים" +description = "חטוף פריטים שנפלו מיד תוך כדי התגנבות!" +lore1 = "רדיוס חטיפה" +lore = ["רדיוס חטיפה"] [stealth.speed] - name = "מהירות התגנבות" - description = "קבל מהירות תוך כדי התגנבות" - lore1 = "מהירות התגנבות" - lore = ["מהירות התגנבות"] +name = "מהירות התגנבות" +description = "קבל מהירות תוך כדי התגנבות" +lore1 = "מהירות התגנבות" +lore = ["מהירות התגנבות"] [stealth.ender_veil] - name = "צעיף אנדר" - description = "לא עוד דלעות כדי למנוע התקפות אנדרמנים" - lore1 = "מנע התקפות אנדרמנים תוך כדי התגנבות" - lore2 = "מנע את כל התקפות האנדרמנים" - lore = ["מנע התקפות אנדרמנים תוך כדי התגנבות", "מנע את כל התקפות האנדרמנים"] +name = "צעיף אנדר" +description = "לא עוד דלעות כדי למנוע התקפות אנדרמנים" +lore1 = "מנע התקפות אנדרמנים תוך כדי התגנבות" +lore2 = "מנע את כל התקפות האנדרמנים" +lore = ["מנע התקפות אנדרמנים תוך כדי התגנבות", "מנע את כל התקפות האנדרמנים"] # sword [sword] [sword.machete] - name = "מצ'טה" - description = "חתוך דרך שיחים בקלות!" - lore1 = "רדיוס חיתוך" - lore2 = "זמן קירור חיתוך" - lore3 = "בלאי כלי" - lore = ["רדיוס חיתוך", "זמן קירור חיתוך", "בלאי כלי"] +name = "מצ'טה" +description = "חתוך דרך שיחים בקלות!" +lore1 = "רדיוס חיתוך" +lore2 = "זמן קירור חיתוך" +lore3 = "בלאי כלי" +lore = ["רדיוס חיתוך", "זמן קירור חיתוך", "בלאי כלי"] [sword.bloody_blade] - name = "להב מדמם" - description = "מכות עם החרב שלך גורמות לדימום!" - lore1 = "פגיעה בישות חיה עם החרב שלך גורמת לדימום" - lore2 = "משך דימום" - lore3 = "זמן קירור דימום" - lore = ["פגיעה בישות חיה עם החרב שלך גורמת לדימום", "משך דימום", "זמן קירור דימום"] +name = "להב מדמם" +description = "מכות עם החרב שלך גורמות לדימום!" +lore1 = "פגיעה בישות חיה עם החרב שלך גורמת לדימום" +lore2 = "משך דימום" +lore3 = "זמן קירור דימום" +lore = ["פגיעה בישות חיה עם החרב שלך גורמת לדימום", "משך דימום", "זמן קירור דימום"] [sword.poisoned_blade] - name = "להב מורעל" - description = "מכות עם החרב שלך גורמות להרעלה!" - lore1 = "פגיעה בישות חיה עם החרב שלך גורמת להרעלה" - lore2 = "משך הרעלה" - lore3 = "זמן קירור הרעלה" - lore = ["פגיעה בישות חיה עם החרב שלך גורמת להרעלה", "משך הרעלה", "זמן קירור הרעלה"] +name = "להב מורעל" +description = "מכות עם החרב שלך גורמות להרעלה!" +lore1 = "פגיעה בישות חיה עם החרב שלך גורמת להרעלה" +lore2 = "משך הרעלה" +lore3 = "זמן קירור הרעלה" +lore = ["פגיעה בישות חיה עם החרב שלך גורמת להרעלה", "משך הרעלה", "זמן קירור הרעלה"] # taming [taming] [taming.damage] - name = "נזק מאולף" - description = "הגדל את הנזק שחיות מאולפות שלך גורמות." - lore1 = "נזק מוגבר" - lore = ["נזק מוגבר"] +name = "נזק מאולף" +description = "הגדל את הנזק שחיות מאולפות שלך גורמות." +lore1 = "נזק מוגבר" +lore = ["נזק מוגבר"] [taming.health] - name = "בריאות מאולף" - description = "הגדל את הבריאות של חיות מאולפות שלך." - lore1 = "בריאות מוגברת" - lore = ["בריאות מוגברת"] +name = "בריאות מאולף" +description = "הגדל את הבריאות של חיות מאולפות שלך." +lore1 = "בריאות מוגברת" +lore = ["בריאות מוגברת"] [taming.regeneration] - name = "התחדשות מאולף" - description = "הגדל את ההתחדשות של חיות מאולפות שלך." - lore1 = "HP/s" - lore = ["HP/s"] +name = "התחדשות מאולף" +description = "הגדל את ההתחדשות של חיות מאולפות שלך." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "קוצים" - description = "החזר נזק לתוקף שלך!" - lore1 = "נזק מוחזר כשנפגע" - lore = ["נזק מוחזר כשנפגע"] +name = "קוצים" +description = "החזר נזק לתוקף שלך!" +lore1 = "נזק מוחזר כשנפגע" +lore = ["נזק מוחזר כשנפגע"] [tragoul.globe] - name = "כדור הכאב" - description = "חלק את הנזק שאתה גורם בהתאם למספר האויבים סביבך!" - lore1 = "ככל שיותר אויבים סביבך, כך פחות נזק אתה גורם לכל אחד" - lore2 = "טווח: " - lore3 = "נזק נוסף לכל הישויות: " - lore = ["ככל שיותר אויבים סביבך, כך פחות נזק אתה גורם לכל אחד", "טווח: ", "נזק נוסף לכל הישויות: "] +name = "כדור הכאב" +description = "חלק את הנזק שאתה גורם בהתאם למספר האויבים סביבך!" +lore1 = "ככל שיותר אויבים סביבך, כך פחות נזק אתה גורם לכל אחד" +lore2 = "טווח: " +lore3 = "נזק נוסף לכל הישויות: " +lore = ["ככל שיותר אויבים סביבך, כך פחות נזק אתה גורם לכל אחד", "טווח: ", "נזק נוסף לכל הישויות: "] [tragoul.healing] - name = "רצון הכאב" - description = "שחזר בריאות בהתאם לנזק שאתה גורם!" - lore1 = "לפגוע בדברים מעולם לא הרגיש כל כך טוב! התרפא מנזק שגרמת" - lore2 = "יש חלון נזק של 3 שניות, לריפוי וזמן קירור של שנייה " - lore3 = "ריפוי לכל אחוז נזק: " - lore = ["לפגוע בדברים מעולם לא הרגיש כל כך טוב! התרפא מנזק שגרמת", "יש חלון נזק של 3 שניות, לריפוי וזמן קירור של שנייה ", "ריפוי לכל אחוז נזק: "] +name = "רצון הכאב" +description = "שחזר בריאות בהתאם לנזק שאתה גורם!" +lore1 = "לפגוע בדברים מעולם לא הרגיש כל כך טוב! התרפא מנזק שגרמת" +lore2 = "יש חלון נזק של 3 שניות, לריפוי וזמן קירור של שנייה " +lore3 = "ריפוי לכל אחוז נזק: " +lore = ["לפגוע בדברים מעולם לא הרגיש כל כך טוב! התרפא מנזק שגרמת", "יש חלון נזק של 3 שניות, לריפוי וזמן קירור של שנייה ", "ריפוי לכל אחוז נזק: "] [tragoul.lance] - name = "חניתות גופות" - description = "הריגת אויב או הריגה על ידי יכולת יוצרת חנית שפוגעת באויב קרוב!" - lore1 = "חניתות ישוגרו מכל דבר שאתה הורג, וגם אם יכולת זו הורגת אויב." - lore2 = "הקרב חלק מהחיים שלך כדי ליצור חניתות (זה יכול להרוג אותך)" - lore3 = "חניתות מרביות: 1 + " - lore = ["חניתות ישוגרו מכל דבר שאתה הורג, וגם אם יכולת זו הורגת אויב.", "הקרב חלק מהחיים שלך כדי ליצור חניתות (זה יכול להרוג אותך)", "חניתות מרביות: 1 + "] +name = "חניתות גופות" +description = "הריגת אויב או הריגה על ידי יכולת יוצרת חנית שפוגעת באויב קרוב!" +lore1 = "חניתות ישוגרו מכל דבר שאתה הורג, וגם אם יכולת זו הורגת אויב." +lore2 = "הקרב חלק מהחיים שלך כדי ליצור חניתות (זה יכול להרוג אותך)" +lore3 = "חניתות מרביות: 1 + " +lore = ["חניתות ישוגרו מכל דבר שאתה הורג, וגם אם יכולת זו הורגת אויב.", "הקרב חלק מהחיים שלך כדי ליצור חניתות (זה יכול להרוג אותך)", "חניתות מרביות: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "תותח זכוכית" - description = "נזק בונוס לא חמוש ככל שערך השריון שלך נמוך יותר" - lore1 = "x נזק ב-0 שריון" - lore2 = "נזק בונוס לכל רמה" - lore = ["x נזק ב-0 שריון", "נזק בונוס לכל רמה"] +name = "תותח זכוכית" +description = "נזק בונוס לא חמוש ככל שערך השריון שלך נמוך יותר" +lore1 = "x נזק ב-0 שריון" +lore2 = "נזק בונוס לכל רמה" +lore = ["x נזק ב-0 שריון", "נזק בונוס לכל רמה"] [unarmed.power] - name = "כוח לא חמוש" - description = "נזק לא חמוש משופר" - lore1 = "נזק" - lore = ["נזק"] +name = "כוח לא חמוש" +description = "נזק לא חמוש משופר" +lore1 = "נזק" +lore = ["נזק"] [unarmed.sucker_punch] - name = "אגרוף מתגנב" - description = "אגרופי ספרינט, אבל קטלניים יותר." - lore1 = "נזק" - lore2 = "הנזק גדל עם המהירות שלך בזמן האגרוף" - lore = ["נזק", "הנזק גדל עם המהירות שלך בזמן האגרוף"] +name = "אגרוף מתגנב" +description = "אגרופי ספרינט, אבל קטלניים יותר." +lore1 = "נזק" +lore2 = "הנזק גדל עם המהירות שלך בזמן האגרוף" +lore = ["נזק", "הנזק גדל עם המהירות שלך בזמן האגרוף"] diff --git a/src/main/resources/it_IT.toml b/src/main/resources/it_IT.toml index cde2ce9ed..2486792e8 100644 --- a/src/main/resources/it_IT.toml +++ b/src/main/resources/it_IT.toml @@ -2,2060 +2,2060 @@ [advancement] [advancement.challenge_move_1k] - title = "Devo muovermi!" - description = "Cammina per 1 chilometro (1.000 blocchi)" +title = "Devo muovermi!" +description = "Cammina per 1 chilometro (1.000 blocchi)" [advancement.challenge_sprint_5k] - title = "Scatta per 5 km!" - description = "Cammina per 5 chilometri (5.000 blocchi)" +title = "Scatta per 5 km!" +description = "Cammina per 5 chilometri (5.000 blocchi)" [advancement.challenge_sprint_50k] - title = "Sfreccia per 50 km!" - description = "Cammina per oltre 50 chilometri (50.000 blocchi)" +title = "Sfreccia per 50 km!" +description = "Cammina per oltre 50 chilometri (50.000 blocchi)" [advancement.challenge_sprint_500k] - title = "Attraversa l'universo!!" - description = "Cammina per oltre 500 chilometri (500.000 blocchi)" +title = "Attraversa l'universo!!" +description = "Cammina per oltre 500 chilometri (500.000 blocchi)" [advancement.challenge_sprint_marathon] - title = "Scatta una maratona (letterale)!" - description = "Scatta per oltre 42.195 blocchi!" +title = "Scatta una maratona (letterale)!" +description = "Scatta per oltre 42.195 blocchi!" [advancement.challenge_place_1k] - title = "Costruttore principiante!" - description = "Posiziona 1.000 blocchi" +title = "Costruttore principiante!" +description = "Posiziona 1.000 blocchi" [advancement.challenge_place_5k] - title = "Costruttore intermedio!" - description = "Posiziona 5.000 blocchi" +title = "Costruttore intermedio!" +description = "Posiziona 5.000 blocchi" [advancement.challenge_place_50k] - title = "Costruttore avanzato!" - description = "Posiziona 50.000 blocchi" +title = "Costruttore avanzato!" +description = "Posiziona 50.000 blocchi" [advancement.challenge_place_500k] - title = "Mastro costruttore!" - description = "Posiziona 500.000 blocchi" +title = "Mastro costruttore!" +description = "Posiziona 500.000 blocchi" [advancement.challenge_place_5m] - title = "Accolito della Simmetria!" - description = "LA REALTA' E' IL TUO PARCO GIOCHI! (5 milioni di blocchi)" +title = "Accolito della Simmetria!" +description = "LA REALTA' E' IL TUO PARCO GIOCHI! (5 milioni di blocchi)" [advancement.challenge_chop_1k] - title = "Boscaiolo principiante!" - description = "Taglia 1.000 blocchi" +title = "Boscaiolo principiante!" +description = "Taglia 1.000 blocchi" [advancement.challenge_chop_5k] - title = "Boscaiolo intermedio!" - description = "Taglia 5.000 blocchi" +title = "Boscaiolo intermedio!" +description = "Taglia 5.000 blocchi" [advancement.challenge_chop_50k] - title = "Boscaiolo avanzato!" - description = "Taglia 50.000 blocchi" +title = "Boscaiolo avanzato!" +description = "Taglia 50.000 blocchi" [advancement.challenge_chop_500k] - title = "Maestro boscaiolo!" - description = "Taglia 500.000 blocchi" +title = "Maestro boscaiolo!" +description = "Taglia 500.000 blocchi" [advancement.challenge_chop_5m] - title = "Jackson il cane" - description = "Il migliore dei bravi ragazzi! (5 milioni di blocchi)" +title = "Jackson il cane" +description = "Il migliore dei bravi ragazzi! (5 milioni di blocchi)" [advancement.challenge_block_1k] - title = "Blocco a malapena!" - description = "Blocca 1000 colpi" +title = "Blocco a malapena!" +description = "Blocca 1000 colpi" [advancement.challenge_block_5k] - title = "Bloccare e' divertente!" - description = "Blocca 5000 colpi" +title = "Bloccare e' divertente!" +description = "Blocca 5000 colpi" [advancement.challenge_block_50k] - title = "Bloccare e' la mia vita!" - description = "Blocca 50.000 colpi" +title = "Bloccare e' la mia vita!" +description = "Blocca 50.000 colpi" [advancement.challenge_block_500k] - title = "Bloccare e' il mio scopo!" - description = "Blocca 500.000 colpi" +title = "Bloccare e' il mio scopo!" +description = "Blocca 500.000 colpi" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Blocca 5.000.000 di colpi" +title = "Die Hand Die Verletzt" +description = "Blocca 5.000.000 di colpi" [advancement.challenge_brew_1k] - title = "Alchimista principiante!" - description = "Consuma 1000 pozioni" +title = "Alchimista principiante!" +description = "Consuma 1000 pozioni" [advancement.challenge_brew_5k] - title = "Alchimista intermedio!" - description = "Consuma 5000 pozioni" +title = "Alchimista intermedio!" +description = "Consuma 5000 pozioni" [advancement.challenge_brew_50k] - title = "Alchimista avanzato!" - description = "Consuma 50.000 pozioni" +title = "Alchimista avanzato!" +description = "Consuma 50.000 pozioni" [advancement.challenge_brew_500k] - title = "Maestro alchimista!" - description = "Consuma 500.000 pozioni" +title = "Maestro alchimista!" +description = "Consuma 500.000 pozioni" [advancement.challenge_brew_5m] - title = "L'Alchimista" - description = "Consuma 5.000.000 di pozioni" +title = "L'Alchimista" +description = "Consuma 5.000.000 di pozioni" [advancement.challenge_brewsplash_1k] - title = "Lanciatore di pozioni principiante!" - description = "Lancia 1000 pozioni" +title = "Lanciatore di pozioni principiante!" +description = "Lancia 1000 pozioni" [advancement.challenge_brewsplash_5k] - title = "Lanciatore di pozioni intermedio!" - description = "Lancia 5000 pozioni" +title = "Lanciatore di pozioni intermedio!" +description = "Lancia 5000 pozioni" [advancement.challenge_brewsplash_50k] - title = "Lanciatore di pozioni avanzato!" - description = "Lancia 50.000 pozioni" +title = "Lanciatore di pozioni avanzato!" +description = "Lancia 50.000 pozioni" [advancement.challenge_brewsplash_500k] - title = "Maestro lanciatore di pozioni!" - description = "Lancia 500.000 pozioni" +title = "Maestro lanciatore di pozioni!" +description = "Lancia 500.000 pozioni" [advancement.challenge_brewsplash_5m] - title = "Il Maestro degli Schizzi" - description = "Lancia 5.000.000 di pozioni" +title = "Il Maestro degli Schizzi" +description = "Lancia 5.000.000 di pozioni" [advancement.challenge_craft_1k] - title = "Artigiano furbo!" - description = "Crea 1000 oggetti" +title = "Artigiano furbo!" +description = "Crea 1000 oggetti" [advancement.challenge_craft_5k] - title = "Artigiano irascibile!" - description = "Crea 5000 oggetti" +title = "Artigiano irascibile!" +description = "Crea 5000 oggetti" [advancement.challenge_craft_50k] - title = "Artigiano servile!" - description = "Crea 50.000 oggetti" +title = "Artigiano servile!" +description = "Crea 50.000 oggetti" [advancement.challenge_craft_500k] - title = "Artigiano cacofonico!" - description = "Crea 500.000 oggetti" +title = "Artigiano cacofonico!" +description = "Crea 500.000 oggetti" [advancement.challenge_craft_5m] - title = "Il Calamitoso McCraftface" - description = "Crea 5.000.000 di oggetti" +title = "Il Calamitoso McCraftface" +description = "Crea 5.000.000 di oggetti" [advancement.challenge_enchant_1k] - title = "Incantatore principiante!" - description = "Incanta 1000 oggetti" +title = "Incantatore principiante!" +description = "Incanta 1000 oggetti" [advancement.challenge_enchant_5k] - title = "Incantatore intermedio!" - description = "Incanta 5000 oggetti" +title = "Incantatore intermedio!" +description = "Incanta 5000 oggetti" [advancement.challenge_enchant_50k] - title = "Incantatore avanzato!" - description = "Incanta 50.000 oggetti" +title = "Incantatore avanzato!" +description = "Incanta 50.000 oggetti" [advancement.challenge_enchant_500k] - title = "Maestro incantatore!" - description = "Incanta 500.000 oggetti" +title = "Maestro incantatore!" +description = "Incanta 500.000 oggetti" [advancement.challenge_enchant_5m] - title = "Incantatore enigmatico" - description = "Incanta 5.000.000 di oggetti" +title = "Incantatore enigmatico" +description = "Incanta 5.000.000 di oggetti" [advancement.challenge_excavate_1k] - title = "Scavatore entusiasta!" - description = "Scava 1000 blocchi" +title = "Scavatore entusiasta!" +description = "Scava 1000 blocchi" [advancement.challenge_excavate_5k] - title = "Scavatore intermedio!" - description = "Scava 5000 blocchi" +title = "Scavatore intermedio!" +description = "Scava 5000 blocchi" [advancement.challenge_excavate_50k] - title = "Scavatore avanzato!" - description = "Scava 50.000 blocchi" +title = "Scavatore avanzato!" +description = "Scava 50.000 blocchi" [advancement.challenge_excavate_500k] - title = "Maestro scavatore!" - description = "Scava 500.000 blocchi" +title = "Maestro scavatore!" +description = "Scava 500.000 blocchi" [advancement.challenge_excavate_5m] - title = "Scavatore enigmatico" - description = "Scava 5.000.000 di blocchi" +title = "Scavatore enigmatico" +description = "Scava 5.000.000 di blocchi" [advancement.horrible_person] - title = "Sei una persona orribile" - description = "Inconcepibile, davvero" +title = "Sei una persona orribile" +description = "Inconcepibile, davvero" [advancement.challenge_turtle_egg_smasher] - title = "Spaccauova di tartaruga!" - description = "Rompi 100 uova di tartaruga" +title = "Spaccauova di tartaruga!" +description = "Rompi 100 uova di tartaruga" [advancement.challenge_turtle_egg_annihilator] - title = "Annientatore di uova di tartaruga!" - description = "Rompi 500 uova di tartaruga" +title = "Annientatore di uova di tartaruga!" +description = "Rompi 500 uova di tartaruga" [advancement.challenge_novice_hunter] - title = "Cacciatore principiante!" - description = "Uccidi 100 entita'" +title = "Cacciatore principiante!" +description = "Uccidi 100 entita'" [advancement.challenge_intermediate_hunter] - title = "Cacciatore intermedio!" - description = "Uccidi 500 entita'" +title = "Cacciatore intermedio!" +description = "Uccidi 500 entita'" [advancement.challenge_advanced_hunter] - title = "Cacciatore avanzato!" - description = "Uccidi 5000 entita'" +title = "Cacciatore avanzato!" +description = "Uccidi 5000 entita'" [advancement.challenge_creeper_conqueror] - title = "Conquistatore di Creeper!" - description = "Uccidi 50 creeper" +title = "Conquistatore di Creeper!" +description = "Uccidi 50 creeper" [advancement.challenge_creeper_annihilator] - title = "Annientatore di Creeper!" - description = "Uccidi 200 creeper" +title = "Annientatore di Creeper!" +description = "Uccidi 200 creeper" [advancement.challenge_pickaxe_1k] - title = "Minatore principiante" - description = "Rompi 1000 blocchi" +title = "Minatore principiante" +description = "Rompi 1000 blocchi" [advancement.challenge_pickaxe_5k] - title = "Minatore abile" - description = "Rompi 5000 blocchi" +title = "Minatore abile" +description = "Rompi 5000 blocchi" [advancement.challenge_pickaxe_50k] - title = "Minatore esperto" - description = "Rompi 50.000 blocchi" +title = "Minatore esperto" +description = "Rompi 50.000 blocchi" [advancement.challenge_pickaxe_500k] - title = "Maestro minatore" - description = "Rompi 500.000 blocchi" +title = "Maestro minatore" +description = "Rompi 500.000 blocchi" [advancement.challenge_pickaxe_5m] - title = "Minatore leggendario" - description = "Rompi 5.000.000 di blocchi" +title = "Minatore leggendario" +description = "Rompi 5.000.000 di blocchi" [advancement.challenge_eat_100] - title = "Cosi' tanto da mangiare!" - description = "Mangia piu' di 100 oggetti!" +title = "Cosi' tanto da mangiare!" +description = "Mangia piu' di 100 oggetti!" [advancement.challenge_eat_1000] - title = "Fame insaziabile!" - description = "Mangia piu' di 1.000 oggetti!" +title = "Fame insaziabile!" +description = "Mangia piu' di 1.000 oggetti!" [advancement.challenge_eat_10000] - title = "FAME ETERNA!" - description = "Mangia piu' di 10.000 oggetti!" +title = "FAME ETERNA!" +description = "Mangia piu' di 10.000 oggetti!" [advancement.challenge_harvest_100] - title = "Raccolto abbondante" - description = "Raccogli piu' di 100 colture!" +title = "Raccolto abbondante" +description = "Raccogli piu' di 100 colture!" [advancement.challenge_harvest_1000] - title = "Grande raccolto" - description = "Raccogli piu' di 1.000 colture!" +title = "Grande raccolto" +description = "Raccogli piu' di 1.000 colture!" [advancement.challenge_swim_1nm] - title = "Sottomarino umano!" - description = "Nuota per 1 miglio nautico (1.852 blocchi)" +title = "Sottomarino umano!" +description = "Nuota per 1 miglio nautico (1.852 blocchi)" [advancement.challenge_sneak_1k] - title = "Dolore alle ginocchia" - description = "Accovacciati per oltre un chilometro (1.000 blocchi)" +title = "Dolore alle ginocchia" +description = "Accovacciati per oltre un chilometro (1.000 blocchi)" [advancement.challenge_sneak_5k] - title = "Camminatore d'Ombra" - description = "Accovacciati per oltre 5.000 blocchi" +title = "Camminatore d'Ombra" +description = "Accovacciati per oltre 5.000 blocchi" [advancement.challenge_sneak_20k] - title = "Fantasma" - description = "Accovacciati per oltre 20.000 blocchi" +title = "Fantasma" +description = "Accovacciati per oltre 20.000 blocchi" [advancement.challenge_swim_5k] - title = "Sommozzatore" - description = "Nuota per oltre 5.000 blocchi" +title = "Sommozzatore" +description = "Nuota per oltre 5.000 blocchi" [advancement.challenge_swim_20k] - title = "Prescelto di Poseidon" - description = "Nuota per oltre 20.000 blocchi" +title = "Prescelto di Poseidon" +description = "Nuota per oltre 20.000 blocchi" [advancement.challenge_sword_100] - title = "Primo Sangue" - description = "Colpisci 100 volte con una spada" +title = "Primo Sangue" +description = "Colpisci 100 volte con una spada" [advancement.challenge_sword_1k] - title = "Danzatore di Lame" - description = "Colpisci 1.000 volte con una spada" +title = "Danzatore di Lame" +description = "Colpisci 1.000 volte con una spada" [advancement.challenge_sword_10k] - title = "Mille Tagli" - description = "Colpisci 10.000 volte con una spada" +title = "Mille Tagli" +description = "Colpisci 10.000 volte con una spada" [advancement.challenge_unarmed_100] - title = "Attaccabrighe" - description = "Colpisci 100 volte a mani nude" +title = "Attaccabrighe" +description = "Colpisci 100 volte a mani nude" [advancement.challenge_unarmed_1k] - title = "Pugni di Ferro" - description = "Colpisci 1.000 volte a mani nude" +title = "Pugni di Ferro" +description = "Colpisci 1.000 volte a mani nude" [advancement.challenge_unarmed_10k] - title = "One Punch" - description = "Colpisci 10.000 volte a mani nude" +title = "One Punch" +description = "Colpisci 10.000 volte a mani nude" [advancement.challenge_trag_1k] - title = "Prezzo di Sangue" - description = "Subisci 1.000 danni" +title = "Prezzo di Sangue" +description = "Subisci 1.000 danni" [advancement.challenge_trag_10k] - title = "Marea Cremisi" - description = "Subisci 10.000 danni" +title = "Marea Cremisi" +description = "Subisci 10.000 danni" [advancement.challenge_trag_100k] - title = "Avatar della Sofferenza" - description = "Subisci 100.000 danni" +title = "Avatar della Sofferenza" +description = "Subisci 100.000 danni" [advancement.challenge_ranged_100] - title = "Esercizio di Tiro" - description = "Lancia 100 proiettili" +title = "Esercizio di Tiro" +description = "Lancia 100 proiettili" [advancement.challenge_ranged_1k] - title = "Occhio di Falco" - description = "Lancia 1.000 proiettili" +title = "Occhio di Falco" +description = "Lancia 1.000 proiettili" [advancement.challenge_ranged_10k] - title = "Tempesta di Frecce" - description = "Lancia 10.000 proiettili" +title = "Tempesta di Frecce" +description = "Lancia 10.000 proiettili" [advancement.challenge_chronos_1h] - title = "Tic Tac" - description = "Trascorri 1 ora online" +title = "Tic Tac" +description = "Trascorri 1 ora online" [advancement.challenge_chronos_24h] - title = "Sabbie del Tempo" - description = "Trascorri 24 ore online" +title = "Sabbie del Tempo" +description = "Trascorri 24 ore online" [advancement.challenge_chronos_168h] - title = "Senza Tempo" - description = "Trascorri 168 ore (1 settimana) online" +title = "Senza Tempo" +description = "Trascorri 168 ore (1 settimana) online" [advancement.challenge_nether_50] - title = "Guardiano dell'Inferno" - description = "Uccidi 50 creature del Nether" +title = "Guardiano dell'Inferno" +description = "Uccidi 50 creature del Nether" [advancement.challenge_nether_500] - title = "Guardiano dell'Abisso" - description = "Uccidi 500 creature del Nether" +title = "Guardiano dell'Abisso" +description = "Uccidi 500 creature del Nether" [advancement.challenge_nether_5k] - title = "Signore del Nether" - description = "Uccidi 5.000 creature del Nether" +title = "Signore del Nether" +description = "Uccidi 5.000 creature del Nether" [advancement.challenge_rift_50] - title = "Anomalia Spaziale" - description = "Teletrasportati 50 volte" +title = "Anomalia Spaziale" +description = "Teletrasportati 50 volte" [advancement.challenge_rift_500] - title = "Camminatore del Vuoto" - description = "Teletrasportati 500 volte" +title = "Camminatore del Vuoto" +description = "Teletrasportati 500 volte" [advancement.challenge_rift_5k] - title = "Tra i Mondi" - description = "Teletrasportati 5.000 volte" +title = "Tra i Mondi" +description = "Teletrasportati 5.000 volte" [advancement.challenge_taming_10] - title = "Sussurratore di Animali" - description = "Alleva 10 animali" +title = "Sussurratore di Animali" +description = "Alleva 10 animali" [advancement.challenge_taming_50] - title = "Capobranco" - description = "Alleva 50 animali" +title = "Capobranco" +description = "Alleva 50 animali" [advancement.challenge_taming_500] - title = "Domatore di Bestie" - description = "Alleva 500 animali" +title = "Domatore di Bestie" +description = "Alleva 500 animali" # Agility [advancement.challenge_sprint_dist_5k] - title = "Demone della Velocita" - description = "Corri per oltre 5 Chilometri (5.000 blocchi)" +title = "Demone della Velocita" +description = "Corri per oltre 5 Chilometri (5.000 blocchi)" [advancement.challenge_sprint_dist_50k] - title = "Gambe Fulminee" - description = "Corri per oltre 50 Chilometri (50.000 blocchi)" +title = "Gambe Fulminee" +description = "Corri per oltre 50 Chilometri (50.000 blocchi)" [advancement.challenge_agility_swim_1k] - title = "Camminatore d'Acqua" - description = "Nuota per oltre 1 Chilometro (1.000 blocchi)" +title = "Camminatore d'Acqua" +description = "Nuota per oltre 1 Chilometro (1.000 blocchi)" [advancement.challenge_agility_swim_10k] - title = "Viaggiatore Acquatico" - description = "Nuota per oltre 10 Chilometri (10.000 blocchi)" +title = "Viaggiatore Acquatico" +description = "Nuota per oltre 10 Chilometri (10.000 blocchi)" [advancement.challenge_fly_1k] - title = "Danzatore del Cielo" - description = "Vola per oltre 1 Chilometro (1.000 blocchi)" +title = "Danzatore del Cielo" +description = "Vola per oltre 1 Chilometro (1.000 blocchi)" [advancement.challenge_fly_10k] - title = "Cavaliere del Vento" - description = "Vola per oltre 10 Chilometri (10.000 blocchi)" +title = "Cavaliere del Vento" +description = "Vola per oltre 10 Chilometri (10.000 blocchi)" [advancement.challenge_agility_sneak_500] - title = "Passi Silenziosi" - description = "Muoviti furtivamente per oltre 500 blocchi" +title = "Passi Silenziosi" +description = "Muoviti furtivamente per oltre 500 blocchi" [advancement.challenge_agility_sneak_5k] - title = "Passi Fantasma" - description = "Muoviti furtivamente per oltre 5 Chilometri (5.000 blocchi)" +title = "Passi Fantasma" +description = "Muoviti furtivamente per oltre 5 Chilometri (5.000 blocchi)" # Architect [advancement.challenge_demolish_500] - title = "Squadra di Demolizione" - description = "Distruggi 500 blocchi" +title = "Squadra di Demolizione" +description = "Distruggi 500 blocchi" [advancement.challenge_demolish_5k] - title = "Palla da Demolizione" - description = "Distruggi 5.000 blocchi" +title = "Palla da Demolizione" +description = "Distruggi 5.000 blocchi" [advancement.challenge_value_placed_10k] - title = "Costruttore di Valore" - description = "Piazza blocchi del valore di 10.000" +title = "Costruttore di Valore" +description = "Piazza blocchi del valore di 10.000" [advancement.challenge_value_placed_100k] - title = "Maestro Architetto" - description = "Piazza blocchi del valore di 100.000" +title = "Maestro Architetto" +description = "Piazza blocchi del valore di 100.000" [advancement.challenge_demolish_val_5k] - title = "Esperto di Recupero" - description = "Recupera 5.000 di valore dai blocchi demoliti" +title = "Esperto di Recupero" +description = "Recupera 5.000 di valore dai blocchi demoliti" [advancement.challenge_demolish_val_50k] - title = "Decostruzione Totale" - description = "Recupera 50.000 di valore dai blocchi demoliti" +title = "Decostruzione Totale" +description = "Recupera 50.000 di valore dai blocchi demoliti" [advancement.challenge_high_build_100] - title = "Costruttore Celeste" - description = "Piazza 100 blocchi sopra Y=128" +title = "Costruttore Celeste" +description = "Piazza 100 blocchi sopra Y=128" [advancement.challenge_high_build_1k] - title = "Architetto delle Nuvole" - description = "Piazza 1.000 blocchi sopra Y=128" +title = "Architetto delle Nuvole" +description = "Piazza 1.000 blocchi sopra Y=128" # Axes [advancement.challenge_axe_swing_500] - title = "Boscaiolo" - description = "Brandisci la tua ascia 500 volte" +title = "Boscaiolo" +description = "Brandisci la tua ascia 500 volte" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Brandisci la tua ascia 5.000 volte" +title = "Berserker" +description = "Brandisci la tua ascia 5.000 volte" [advancement.challenge_axe_damage_1k] - title = "Spaccalegna" - description = "Infliggi 1.000 danni con le asce" +title = "Spaccalegna" +description = "Infliggi 1.000 danni con le asce" [advancement.challenge_axe_damage_10k] - title = "Ascia del Boia" - description = "Infliggi 10.000 danni con le asce" +title = "Ascia del Boia" +description = "Infliggi 10.000 danni con le asce" [advancement.challenge_axe_value_5k] - title = "Mercante di Legname" - description = "Raccogli legname del valore di 5.000" +title = "Mercante di Legname" +description = "Raccogli legname del valore di 5.000" [advancement.challenge_axe_value_50k] - title = "Barone del Legno" - description = "Raccogli legname del valore di 50.000" +title = "Barone del Legno" +description = "Raccogli legname del valore di 50.000" [advancement.challenge_leaves_500] - title = "Soffiatore di Foglie" - description = "Elimina 500 blocchi di foglie con un'ascia" +title = "Soffiatore di Foglie" +description = "Elimina 500 blocchi di foglie con un'ascia" [advancement.challenge_leaves_5k] - title = "Defogliatore" - description = "Elimina 5.000 blocchi di foglie con un'ascia" +title = "Defogliatore" +description = "Elimina 5.000 blocchi di foglie con un'ascia" # Blocking [advancement.challenge_block_dmg_1k] - title = "Assorbitore di Danni" - description = "Blocca 1.000 danni con uno scudo" +title = "Assorbitore di Danni" +description = "Blocca 1.000 danni con uno scudo" [advancement.challenge_block_dmg_10k] - title = "Scudo Umano" - description = "Blocca 10.000 danni con uno scudo" +title = "Scudo Umano" +description = "Blocca 10.000 danni con uno scudo" [advancement.challenge_block_proj_100] - title = "Deflettore di Frecce" - description = "Blocca 100 proiettili con uno scudo" +title = "Deflettore di Frecce" +description = "Blocca 100 proiettili con uno scudo" [advancement.challenge_block_proj_1k] - title = "Scudo Anti-Proiettili" - description = "Blocca 1.000 proiettili con uno scudo" +title = "Scudo Anti-Proiettili" +description = "Blocca 1.000 proiettili con uno scudo" [advancement.challenge_block_melee_500] - title = "Maestro della Parata" - description = "Blocca 500 attacchi ravvicinati con uno scudo" +title = "Maestro della Parata" +description = "Blocca 500 attacchi ravvicinati con uno scudo" [advancement.challenge_block_melee_5k] - title = "Fortezza di Ferro" - description = "Blocca 5.000 attacchi ravvicinati con uno scudo" +title = "Fortezza di Ferro" +description = "Blocca 5.000 attacchi ravvicinati con uno scudo" [advancement.challenge_block_heavy_50] - title = "Carro Armato" - description = "Blocca 50 attacchi pesanti (oltre 5 danni)" +title = "Carro Armato" +description = "Blocca 50 attacchi pesanti (oltre 5 danni)" [advancement.challenge_block_heavy_500] - title = "Oggetto Inamovibile" - description = "Blocca 500 attacchi pesanti (oltre 5 danni)" +title = "Oggetto Inamovibile" +description = "Blocca 500 attacchi pesanti (oltre 5 danni)" # Brewing [advancement.challenge_brew_stands_10] - title = "Preparazione dell'Alchimista" - description = "Piazza 10 alambicchi" +title = "Preparazione dell'Alchimista" +description = "Piazza 10 alambicchi" [advancement.challenge_brew_stands_50] - title = "Fabbrica di Pozioni" - description = "Piazza 50 alambicchi" +title = "Fabbrica di Pozioni" +description = "Piazza 50 alambicchi" [advancement.challenge_brew_strong_25] - title = "Miscela Potente" - description = "Consuma 25 pozioni potenziate" +title = "Miscela Potente" +description = "Consuma 25 pozioni potenziate" [advancement.challenge_brew_strong_250] - title = "Potenza Massima" - description = "Consuma 250 pozioni potenziate" +title = "Potenza Massima" +description = "Consuma 250 pozioni potenziate" [advancement.challenge_brew_splash_hits_50] - title = "Zona di Schizzo" - description = "Colpisci 50 entita con pozioni da lancio" +title = "Zona di Schizzo" +description = "Colpisci 50 entita con pozioni da lancio" [advancement.challenge_brew_splash_hits_500] - title = "Medico della Peste" - description = "Colpisci 500 entita con pozioni da lancio" +title = "Medico della Peste" +description = "Colpisci 500 entita con pozioni da lancio" # Chronos [advancement.challenge_active_dist_1k] - title = "Irrequieto" - description = "Viaggia per 1 Chilometro mentre sei attivo" +title = "Irrequieto" +description = "Viaggia per 1 Chilometro mentre sei attivo" [advancement.challenge_active_dist_10k] - title = "Esploratore" - description = "Viaggia per 10 Chilometri mentre sei attivo" +title = "Esploratore" +description = "Viaggia per 10 Chilometri mentre sei attivo" [advancement.challenge_active_dist_100k] - title = "Moto Perpetuo" - description = "Viaggia per 100 Chilometri mentre sei attivo" +title = "Moto Perpetuo" +description = "Viaggia per 100 Chilometri mentre sei attivo" [advancement.challenge_beds_10] - title = "Mattiniero" - description = "Dormi in un letto 10 volte" +title = "Mattiniero" +description = "Dormi in un letto 10 volte" [advancement.challenge_beds_100] - title = "Saltatore del Tempo" - description = "Dormi in un letto 100 volte" +title = "Saltatore del Tempo" +description = "Dormi in un letto 100 volte" [advancement.challenge_chronos_tp_50] - title = "Spostamento Temporale" - description = "Teletrasportati 50 volte" +title = "Spostamento Temporale" +description = "Teletrasportati 50 volte" [advancement.challenge_chronos_tp_500] - title = "Distorsione Temporale" - description = "Teletrasportati 500 volte" +title = "Distorsione Temporale" +description = "Teletrasportati 500 volte" [advancement.challenge_chronos_deaths_10] - title = "Mortale" - description = "Muori 10 volte" +title = "Mortale" +description = "Muori 10 volte" [advancement.challenge_chronos_deaths_100] - title = "Sfidante della Morte" - description = "Muori 100 volte" +title = "Sfidante della Morte" +description = "Muori 100 volte" # Crafting [advancement.challenge_craft_value_10k] - title = "Valore Artigianale" - description = "Crea oggetti del valore totale di 10.000" +title = "Valore Artigianale" +description = "Crea oggetti del valore totale di 10.000" [advancement.challenge_craft_value_100k] - title = "Artigiano" - description = "Crea oggetti del valore totale di 100.000" +title = "Artigiano" +description = "Crea oggetti del valore totale di 100.000" [advancement.challenge_craft_tools_25] - title = "Fabbro di Utensili" - description = "Crea 25 utensili" +title = "Fabbro di Utensili" +description = "Crea 25 utensili" [advancement.challenge_craft_tools_250] - title = "Maestro Forgiatore" - description = "Crea 250 utensili" +title = "Maestro Forgiatore" +description = "Crea 250 utensili" [advancement.challenge_craft_armor_25] - title = "Fabbro di Armature" - description = "Crea 25 pezzi di armatura" +title = "Fabbro di Armature" +description = "Crea 25 pezzi di armatura" [advancement.challenge_craft_armor_250] - title = "Maestro Armaiolo" - description = "Crea 250 pezzi di armatura" +title = "Maestro Armaiolo" +description = "Crea 250 pezzi di armatura" [advancement.challenge_craft_mass_25k] - title = "Produttore di Massa" - description = "Crea 25.000 oggetti" +title = "Produttore di Massa" +description = "Crea 25.000 oggetti" [advancement.challenge_craft_mass_250k] - title = "Rivoluzione Industriale" - description = "Crea 250.000 oggetti" +title = "Rivoluzione Industriale" +description = "Crea 250.000 oggetti" # Discovery [advancement.challenge_discover_items_50] - title = "Collezionista" - description = "Scopri 50 oggetti unici" +title = "Collezionista" +description = "Scopri 50 oggetti unici" [advancement.challenge_discover_items_250] - title = "Catalogatore" - description = "Scopri 250 oggetti unici" +title = "Catalogatore" +description = "Scopri 250 oggetti unici" [advancement.challenge_discover_blocks_50] - title = "Topografo" - description = "Scopri 50 blocchi unici" +title = "Topografo" +description = "Scopri 50 blocchi unici" [advancement.challenge_discover_blocks_250] - title = "Geologo" - description = "Scopri 250 blocchi unici" +title = "Geologo" +description = "Scopri 250 blocchi unici" [advancement.challenge_discover_mobs_25] - title = "Osservatore" - description = "Scopri 25 creature uniche" +title = "Osservatore" +description = "Scopri 25 creature uniche" [advancement.challenge_discover_mobs_75] - title = "Naturalista" - description = "Scopri 75 creature uniche" +title = "Naturalista" +description = "Scopri 75 creature uniche" [advancement.challenge_discover_biomes_10] - title = "Vagabondo" - description = "Scopri 10 biomi unici" +title = "Vagabondo" +description = "Scopri 10 biomi unici" [advancement.challenge_discover_biomes_40] - title = "Giramondo" - description = "Scopri 40 biomi unici" +title = "Giramondo" +description = "Scopri 40 biomi unici" [advancement.challenge_discover_foods_10] - title = "Buongustaio" - description = "Scopri 10 cibi unici" +title = "Buongustaio" +description = "Scopri 10 cibi unici" [advancement.challenge_discover_foods_30] - title = "Maestro Culinario" - description = "Scopri 30 cibi unici" +title = "Maestro Culinario" +description = "Scopri 30 cibi unici" # Enchanting [advancement.challenge_enchant_power_100] - title = "Tessitore di Potere" - description = "Accumula 100 di potere d'incantesimo" +title = "Tessitore di Potere" +description = "Accumula 100 di potere d'incantesimo" [advancement.challenge_enchant_power_1k] - title = "Maestro Arcano" - description = "Accumula 1.000 di potere d'incantesimo" +title = "Maestro Arcano" +description = "Accumula 1.000 di potere d'incantesimo" [advancement.challenge_enchant_levels_1k] - title = "Spendaccione di Livelli" - description = "Spendi 1.000 livelli di esperienza in incantesimi" +title = "Spendaccione di Livelli" +description = "Spendi 1.000 livelli di esperienza in incantesimi" [advancement.challenge_enchant_levels_10k] - title = "Divoratore di XP" - description = "Spendi 10.000 livelli di esperienza in incantesimi" +title = "Divoratore di XP" +description = "Spendi 10.000 livelli di esperienza in incantesimi" [advancement.challenge_enchant_high_25] - title = "Giocatore d'Azzardo" - description = "Esegui 25 incantesimi di livello massimo" +title = "Giocatore d'Azzardo" +description = "Esegui 25 incantesimi di livello massimo" [advancement.challenge_enchant_high_250] - title = "Incantatore Leggendario" - description = "Esegui 250 incantesimi di livello massimo" +title = "Incantatore Leggendario" +description = "Esegui 250 incantesimi di livello massimo" [advancement.challenge_enchant_total_500] - title = "Bruciatore di Livelli" - description = "Spendi 500 livelli totali in incantesimi" +title = "Bruciatore di Livelli" +description = "Spendi 500 livelli totali in incantesimi" [advancement.challenge_enchant_total_5k] - title = "Investimento Arcano" - description = "Spendi 5.000 livelli totali in incantesimi" +title = "Investimento Arcano" +description = "Spendi 5.000 livelli totali in incantesimi" # Excavation [advancement.challenge_dig_swing_500] - title = "Scavatore" - description = "Brandisci la tua pala 500 volte" +title = "Scavatore" +description = "Brandisci la tua pala 500 volte" [advancement.challenge_dig_swing_5k] - title = "Escavatore" - description = "Brandisci la tua pala 5.000 volte" +title = "Escavatore" +description = "Brandisci la tua pala 5.000 volte" [advancement.challenge_dig_damage_1k] - title = "Cavaliere della Pala" - description = "Infliggi 1.000 danni con una pala" +title = "Cavaliere della Pala" +description = "Infliggi 1.000 danni con una pala" [advancement.challenge_dig_damage_10k] - title = "Maestro della Pala" - description = "Infliggi 10.000 danni con una pala" +title = "Maestro della Pala" +description = "Infliggi 10.000 danni con una pala" [advancement.challenge_dig_value_5k] - title = "Mercante di Terra" - description = "Scava blocchi del valore di 5.000" +title = "Mercante di Terra" +description = "Scava blocchi del valore di 5.000" [advancement.challenge_dig_value_50k] - title = "Barone della Terra" - description = "Scava blocchi del valore di 50.000" +title = "Barone della Terra" +description = "Scava blocchi del valore di 50.000" [advancement.challenge_dig_gravel_500] - title = "Frantumatore di Ghiaia" - description = "Scava 500 blocchi di ghiaia, sabbia o argilla" +title = "Frantumatore di Ghiaia" +description = "Scava 500 blocchi di ghiaia, sabbia o argilla" [advancement.challenge_dig_gravel_5k] - title = "Setacciatore di Sabbia" - description = "Scava 5.000 blocchi di ghiaia, sabbia o argilla" +title = "Setacciatore di Sabbia" +description = "Scava 5.000 blocchi di ghiaia, sabbia o argilla" # Herbalism [advancement.challenge_plant_100] - title = "Seminatore" - description = "Pianta 100 colture" +title = "Seminatore" +description = "Pianta 100 colture" [advancement.challenge_plant_1k] - title = "Pollice Verde" - description = "Pianta 1.000 colture" +title = "Pollice Verde" +description = "Pianta 1.000 colture" [advancement.challenge_plant_5k] - title = "Barone Agricolo" - description = "Pianta 5.000 colture" +title = "Barone Agricolo" +description = "Pianta 5.000 colture" [advancement.challenge_compost_50] - title = "Riciclatore" - description = "Composta 50 oggetti" +title = "Riciclatore" +description = "Composta 50 oggetti" [advancement.challenge_compost_500] - title = "Arricchitore del Suolo" - description = "Composta 500 oggetti" +title = "Arricchitore del Suolo" +description = "Composta 500 oggetti" [advancement.challenge_shear_50] - title = "Tosatore" - description = "Tosa 50 entita" +title = "Tosatore" +description = "Tosa 50 entita" [advancement.challenge_shear_250] - title = "Maestro del Gregge" - description = "Tosa 250 entita" +title = "Maestro del Gregge" +description = "Tosa 250 entita" # Hunter [advancement.challenge_kills_500] - title = "Sterminatore" - description = "Uccidi 500 creature" +title = "Sterminatore" +description = "Uccidi 500 creature" [advancement.challenge_kills_5k] - title = "Carnefice" - description = "Uccidi 5.000 creature" +title = "Carnefice" +description = "Uccidi 5.000 creature" [advancement.challenge_boss_1] - title = "Sfidante di Boss" - description = "Sconfiggi un boss" +title = "Sfidante di Boss" +description = "Sconfiggi un boss" [advancement.challenge_boss_10] - title = "Uccisore di Leggende" - description = "Sconfiggi 10 boss" +title = "Uccisore di Leggende" +description = "Sconfiggi 10 boss" # Nether [advancement.challenge_wither_dmg_500] - title = "Avvizzito" - description = "Sopporta 500 danni da Wither" +title = "Avvizzito" +description = "Sopporta 500 danni da Wither" [advancement.challenge_wither_dmg_5k] - title = "Sopravvissuto alla Piaga" - description = "Sopporta 5.000 danni da Wither" +title = "Sopravvissuto alla Piaga" +description = "Sopporta 5.000 danni da Wither" [advancement.challenge_wither_skel_25] - title = "Collezionista di Ossa" - description = "Uccidi 25 scheletri Wither" +title = "Collezionista di Ossa" +description = "Uccidi 25 scheletri Wither" [advancement.challenge_wither_skel_250] - title = "Flagello degli Scheletri" - description = "Uccidi 250 scheletri Wither" +title = "Flagello degli Scheletri" +description = "Uccidi 250 scheletri Wither" [advancement.challenge_wither_boss_1] - title = "Uccisore del Wither" - description = "Sconfiggi il Wither" +title = "Uccisore del Wither" +description = "Sconfiggi il Wither" [advancement.challenge_wither_boss_10] - title = "Dominatore del Nether" - description = "Sconfiggi il Wither 10 volte" +title = "Dominatore del Nether" +description = "Sconfiggi il Wither 10 volte" [advancement.challenge_roses_10] - title = "Giardiniere della Morte" - description = "Distruggi 10 rose del Wither" +title = "Giardiniere della Morte" +description = "Distruggi 10 rose del Wither" [advancement.challenge_roses_100] - title = "Collezionista di Piaghe" - description = "Distruggi 100 rose del Wither" +title = "Collezionista di Piaghe" +description = "Distruggi 100 rose del Wither" # Pickaxes [advancement.challenge_pick_swing_500] - title = "Braccio del Minatore" - description = "Brandisci il tuo piccone 500 volte" +title = "Braccio del Minatore" +description = "Brandisci il tuo piccone 500 volte" [advancement.challenge_pick_swing_5k] - title = "Scavatore di Tunnel" - description = "Brandisci il tuo piccone 5.000 volte" +title = "Scavatore di Tunnel" +description = "Brandisci il tuo piccone 5.000 volte" [advancement.challenge_pick_damage_1k] - title = "Combattente col Piccone" - description = "Infliggi 1.000 danni con un piccone" +title = "Combattente col Piccone" +description = "Infliggi 1.000 danni con un piccone" [advancement.challenge_pick_damage_10k] - title = "Piccone da Guerra" - description = "Infliggi 10.000 danni con un piccone" +title = "Piccone da Guerra" +description = "Infliggi 10.000 danni con un piccone" [advancement.challenge_pick_value_5k] - title = "Cercatore di Gemme" - description = "Mina blocchi del valore di 5.000" +title = "Cercatore di Gemme" +description = "Mina blocchi del valore di 5.000" [advancement.challenge_pick_value_50k] - title = "Barone dei Minerali" - description = "Mina blocchi del valore di 50.000" +title = "Barone dei Minerali" +description = "Mina blocchi del valore di 50.000" [advancement.challenge_pick_ores_500] - title = "Cercatore" - description = "Mina 500 blocchi di minerale" +title = "Cercatore" +description = "Mina 500 blocchi di minerale" [advancement.challenge_pick_ores_5k] - title = "Maestro Minatore" - description = "Mina 5.000 blocchi di minerale" +title = "Maestro Minatore" +description = "Mina 5.000 blocchi di minerale" # Ranged [advancement.challenge_ranged_dmg_1k] - title = "Tiratore Scelto" - description = "Infliggi 1.000 danni a distanza" +title = "Tiratore Scelto" +description = "Infliggi 1.000 danni a distanza" [advancement.challenge_ranged_dmg_10k] - title = "Arciere Letale" - description = "Infliggi 10.000 danni a distanza" +title = "Arciere Letale" +description = "Infliggi 10.000 danni a distanza" [advancement.challenge_ranged_dist_5k] - title = "Lunga Gittata" - description = "Spara proiettili coprendo 5.000 blocchi di distanza totale" +title = "Lunga Gittata" +description = "Spara proiettili coprendo 5.000 blocchi di distanza totale" [advancement.challenge_ranged_dist_50k] - title = "Tiratore Kilometrico" - description = "Spara proiettili coprendo 50.000 blocchi di distanza totale" +title = "Tiratore Kilometrico" +description = "Spara proiettili coprendo 50.000 blocchi di distanza totale" [advancement.challenge_ranged_kills_50] - title = "Arciere" - description = "Uccidi 50 creature con armi a distanza" +title = "Arciere" +description = "Uccidi 50 creature con armi a distanza" [advancement.challenge_ranged_kills_500] - title = "Maestro Arciere" - description = "Uccidi 500 creature con armi a distanza" +title = "Maestro Arciere" +description = "Uccidi 500 creature con armi a distanza" [advancement.challenge_longshot_25] - title = "Cecchino" - description = "Metti a segno 25 tiri a lunga distanza (oltre 30 blocchi)" +title = "Cecchino" +description = "Metti a segno 25 tiri a lunga distanza (oltre 30 blocchi)" [advancement.challenge_longshot_250] - title = "Occhio d'Aquila" - description = "Metti a segno 250 tiri a lunga distanza (oltre 30 blocchi)" +title = "Occhio d'Aquila" +description = "Metti a segno 250 tiri a lunga distanza (oltre 30 blocchi)" # Rift [advancement.challenge_rift_pearls_50] - title = "Lanciatore di Perle" - description = "Lancia 50 perle dell'Ender" +title = "Lanciatore di Perle" +description = "Lancia 50 perle dell'Ender" [advancement.challenge_rift_pearls_500] - title = "Dipendente dal Teletrasporto" - description = "Lancia 500 perle dell'Ender" +title = "Dipendente dal Teletrasporto" +description = "Lancia 500 perle dell'Ender" [advancement.challenge_rift_enderman_50] - title = "Cacciatore di Enderman" - description = "Uccidi 50 endermen" +title = "Cacciatore di Enderman" +description = "Uccidi 50 endermen" [advancement.challenge_rift_enderman_500] - title = "Predatore del Vuoto" - description = "Uccidi 500 endermen" +title = "Predatore del Vuoto" +description = "Uccidi 500 endermen" [advancement.challenge_rift_dragon_500] - title = "Combattente di Draghi" - description = "Infliggi 500 danni al Drago dell'End" +title = "Combattente di Draghi" +description = "Infliggi 500 danni al Drago dell'End" [advancement.challenge_rift_dragon_5k] - title = "Flagello dei Draghi" - description = "Infliggi 5.000 danni al Drago dell'End" +title = "Flagello dei Draghi" +description = "Infliggi 5.000 danni al Drago dell'End" [advancement.challenge_rift_crystal_10] - title = "Spaccacristalli" - description = "Distruggi 10 cristalli dell'End" +title = "Spaccacristalli" +description = "Distruggi 10 cristalli dell'End" [advancement.challenge_rift_crystal_100] - title = "Demolitore dell'End" - description = "Distruggi 100 cristalli dell'End" +title = "Demolitore dell'End" +description = "Distruggi 100 cristalli dell'End" # Seaborne [advancement.challenge_fish_25] - title = "Pescatore" - description = "Cattura 25 pesci" +title = "Pescatore" +description = "Cattura 25 pesci" [advancement.challenge_fish_250] - title = "Maestro Pescatore" - description = "Cattura 250 pesci" +title = "Maestro Pescatore" +description = "Cattura 250 pesci" [advancement.challenge_drowned_25] - title = "Cacciatore di Annegati" - description = "Uccidi 25 annegati" +title = "Cacciatore di Annegati" +description = "Uccidi 25 annegati" [advancement.challenge_drowned_250] - title = "Pulitore dell'Oceano" - description = "Uccidi 250 annegati" +title = "Pulitore dell'Oceano" +description = "Uccidi 250 annegati" [advancement.challenge_guardian_10] - title = "Uccisore di Guardiani" - description = "Uccidi 10 guardiani" +title = "Uccisore di Guardiani" +description = "Uccidi 10 guardiani" [advancement.challenge_guardian_100] - title = "Razziatore di Templi" - description = "Uccidi 100 guardiani" +title = "Razziatore di Templi" +description = "Uccidi 100 guardiani" [advancement.challenge_underwater_blocks_100] - title = "Minatore Subacqueo" - description = "Distruggi 100 blocchi sott'acqua" +title = "Minatore Subacqueo" +description = "Distruggi 100 blocchi sott'acqua" [advancement.challenge_underwater_blocks_1k] - title = "Ingegnere Acquatico" - description = "Distruggi 1.000 blocchi sott'acqua" +title = "Ingegnere Acquatico" +description = "Distruggi 1.000 blocchi sott'acqua" # Stealth [advancement.challenge_stealth_dmg_500] - title = "Pugnalatore alle Spalle" - description = "Infliggi 500 danni mentre sei accovacciato" +title = "Pugnalatore alle Spalle" +description = "Infliggi 500 danni mentre sei accovacciato" [advancement.challenge_stealth_dmg_5k] - title = "Assassino Silenzioso" - description = "Infliggi 5.000 danni mentre sei accovacciato" +title = "Assassino Silenzioso" +description = "Infliggi 5.000 danni mentre sei accovacciato" [advancement.challenge_stealth_kills_10] - title = "Assassino" - description = "Uccidi 10 creature mentre sei accovacciato" +title = "Assassino" +description = "Uccidi 10 creature mentre sei accovacciato" [advancement.challenge_stealth_kills_100] - title = "Mietitore delle Ombre" - description = "Uccidi 100 creature mentre sei accovacciato" +title = "Mietitore delle Ombre" +description = "Uccidi 100 creature mentre sei accovacciato" [advancement.challenge_stealth_time_1h] - title = "Paziente" - description = "Trascorri 1 ora accovacciato (3.600 secondi)" +title = "Paziente" +description = "Trascorri 1 ora accovacciato (3.600 secondi)" [advancement.challenge_stealth_time_10h] - title = "Maestro delle Ombre" - description = "Trascorri 10 ore accovacciato (36.000 secondi)" +title = "Maestro delle Ombre" +description = "Trascorri 10 ore accovacciato (36.000 secondi)" [advancement.challenge_stealth_arrows_50] - title = "Arciere Silenzioso" - description = "Scagliare 50 frecce mentre sei accovacciato" +title = "Arciere Silenzioso" +description = "Scagliare 50 frecce mentre sei accovacciato" [advancement.challenge_stealth_arrows_500] - title = "Arciere Fantasma" - description = "Scagliare 500 frecce mentre sei accovacciato" +title = "Arciere Fantasma" +description = "Scagliare 500 frecce mentre sei accovacciato" # Swords [advancement.challenge_sword_dmg_1k] - title = "Apprendista della Lama" - description = "Infliggi 1.000 danni con le spade" +title = "Apprendista della Lama" +description = "Infliggi 1.000 danni con le spade" [advancement.challenge_sword_dmg_10k] - title = "Spadaccino" - description = "Infliggi 10.000 danni con le spade" +title = "Spadaccino" +description = "Infliggi 10.000 danni con le spade" [advancement.challenge_sword_kills_50] - title = "Duellante" - description = "Uccidi 50 creature con le spade" +title = "Duellante" +description = "Uccidi 50 creature con le spade" [advancement.challenge_sword_kills_500] - title = "Gladiatore" - description = "Uccidi 500 creature con le spade" +title = "Gladiatore" +description = "Uccidi 500 creature con le spade" [advancement.challenge_sword_crit_50] - title = "Colpo Critico" - description = "Metti a segno 50 colpi critici con le spade" +title = "Colpo Critico" +description = "Metti a segno 50 colpi critici con le spade" [advancement.challenge_sword_crit_500] - title = "Maestro della Precisione" - description = "Metti a segno 500 colpi critici con le spade" +title = "Maestro della Precisione" +description = "Metti a segno 500 colpi critici con le spade" [advancement.challenge_sword_heavy_25] - title = "Colpo Pesante" - description = "Metti a segno 25 colpi pesanti con le spade (oltre 8 danni)" +title = "Colpo Pesante" +description = "Metti a segno 25 colpi pesanti con le spade (oltre 8 danni)" [advancement.challenge_sword_heavy_250] - title = "Colpo Devastante" - description = "Metti a segno 250 colpi pesanti con le spade (oltre 8 danni)" +title = "Colpo Devastante" +description = "Metti a segno 250 colpi pesanti con le spade (oltre 8 danni)" # Taming [advancement.challenge_pet_dmg_500] - title = "Addestratore di Bestie" - description = "I tuoi animali infliggono 500 danni totali" +title = "Addestratore di Bestie" +description = "I tuoi animali infliggono 500 danni totali" [advancement.challenge_pet_dmg_5k] - title = "Maestro di Guerra" - description = "I tuoi animali infliggono 5.000 danni totali" +title = "Maestro di Guerra" +description = "I tuoi animali infliggono 5.000 danni totali" [advancement.challenge_tamed_10] - title = "Amico degli Animali" - description = "Addomestica 10 animali" +title = "Amico degli Animali" +description = "Addomestica 10 animali" [advancement.challenge_tamed_100] - title = "Guardiano dello Zoo" - description = "Addomestica 100 animali" +title = "Guardiano dello Zoo" +description = "Addomestica 100 animali" [advancement.challenge_pet_kills_25] - title = "Tattica del Branco" - description = "I tuoi animali uccidono 25 creature" +title = "Tattica del Branco" +description = "I tuoi animali uccidono 25 creature" [advancement.challenge_pet_kills_250] - title = "Comandante Alfa" - description = "I tuoi animali uccidono 250 creature" +title = "Comandante Alfa" +description = "I tuoi animali uccidono 250 creature" [advancement.challenge_taming_2500] - title = "Esperto di Allevamento" - description = "Alleva 2.500 animali" +title = "Esperto di Allevamento" +description = "Alleva 2.500 animali" [advancement.challenge_taming_25k] - title = "Maestro Genetista" - description = "Alleva 25.000 animali" +title = "Maestro Genetista" +description = "Alleva 25.000 animali" # TragOul [advancement.challenge_trag_hits_500] - title = "Sacco da Pugni" - description = "Ricevi 500 colpi" +title = "Sacco da Pugni" +description = "Ricevi 500 colpi" [advancement.challenge_trag_hits_5k] - title = "Ghiottone di Punizioni" - description = "Ricevi 5.000 colpi" +title = "Ghiottone di Punizioni" +description = "Ricevi 5.000 colpi" [advancement.challenge_trag_deaths_10] - title = "Nove Vite" - description = "Muori 10 volte" +title = "Nove Vite" +description = "Muori 10 volte" [advancement.challenge_trag_deaths_100] - title = "Incubo Ricorrente" - description = "Muori 100 volte" +title = "Incubo Ricorrente" +description = "Muori 100 volte" [advancement.challenge_trag_fire_500] - title = "Vittima del Fuoco" - description = "Sopporta 500 danni da fuoco" +title = "Vittima del Fuoco" +description = "Sopporta 500 danni da fuoco" [advancement.challenge_trag_fire_5k] - title = "Fenice" - description = "Sopporta 5.000 danni da fuoco" +title = "Fenice" +description = "Sopporta 5.000 danni da fuoco" [advancement.challenge_trag_fall_500] - title = "Test di Gravita" - description = "Sopporta 500 danni da caduta" +title = "Test di Gravita" +description = "Sopporta 500 danni da caduta" [advancement.challenge_trag_fall_5k] - title = "Velocita Terminale" - description = "Sopporta 5.000 danni da caduta" +title = "Velocita Terminale" +description = "Sopporta 5.000 danni da caduta" # Unarmed [advancement.challenge_unarmed_dmg_1k] - title = "Attaccabrighe" - description = "Infliggi 1.000 danni a mani nude" +title = "Attaccabrighe" +description = "Infliggi 1.000 danni a mani nude" [advancement.challenge_unarmed_dmg_10k] - title = "Artista Marziale" - description = "Infliggi 10.000 danni a mani nude" +title = "Artista Marziale" +description = "Infliggi 10.000 danni a mani nude" [advancement.challenge_unarmed_kills_25] - title = "Pugni Nudi" - description = "Uccidi 25 creature a mani nude" +title = "Pugni Nudi" +description = "Uccidi 25 creature a mani nude" [advancement.challenge_unarmed_kills_250] - title = "Pugno di Leggenda" - description = "Uccidi 250 creature a mani nude" +title = "Pugno di Leggenda" +description = "Uccidi 250 creature a mani nude" [advancement.challenge_unarmed_crit_25] - title = "Pugno Critico" - description = "Metti a segno 25 colpi critici a mani nude" +title = "Pugno Critico" +description = "Metti a segno 25 colpi critici a mani nude" [advancement.challenge_unarmed_crit_250] - title = "Pugno di Precisione" - description = "Metti a segno 250 colpi critici a mani nude" +title = "Pugno di Precisione" +description = "Metti a segno 250 colpi critici a mani nude" [advancement.challenge_unarmed_heavy_25] - title = "Pugno Potente" - description = "Metti a segno 25 colpi pesanti a mani nude (oltre 6 danni)" +title = "Pugno Potente" +description = "Metti a segno 25 colpi pesanti a mani nude (oltre 6 danni)" [advancement.challenge_unarmed_heavy_250] - title = "Re del Knockout" - description = "Metti a segno 250 colpi pesanti a mani nude (oltre 6 danni)" +title = "Re del Knockout" +description = "Metti a segno 250 colpi pesanti a mani nude (oltre 6 danni)" # items [items] [items.bound_ender_peral] - name = "Portachiave del Reliquiario" - usage1 = "Shift + Clic sinistro per legare" - usage2 = "Clic destro per accedere all'inventario legato" +name = "Portachiave del Reliquiario" +usage1 = "Shift + Clic sinistro per legare" +usage2 = "Clic destro per accedere all'inventario legato" [items.bound_eye_of_ender] - name = "Ancora Oculare" - usage1 = "Clic destro per consumare e teletrasportarsi alla posizione legata" - usage2 = "Shift + Clic sinistro per legare a un blocco" +name = "Ancora Oculare" +usage1 = "Clic destro per consumare e teletrasportarsi alla posizione legata" +usage2 = "Shift + Clic sinistro per legare a un blocco" [items.bound_redstone_torch] - name = "Telecomando di Redstone" - usage1 = "Clic destro per creare un impulso di Redstone da 1 tick" - usage2 = "Shift + Clic sinistro su un blocco 'Bersaglio' per legare" +name = "Telecomando di Redstone" +usage1 = "Clic destro per creare un impulso di Redstone da 1 tick" +usage2 = "Shift + Clic sinistro su un blocco 'Bersaglio' per legare" [items.bound_snowball] - name = "Trappola di Ragnatele!" - usage1 = "Lancia per creare una trappola di ragnatele temporanea nella posizione" +name = "Trappola di Ragnatele!" +usage1 = "Lancia per creare una trappola di ragnatele temporanea nella posizione" [items.chrono_time_bottle] - name = "Tempo in Bottiglia" - usage1 = "Accumula tempo passivamente mentre e' nel tuo inventario" - usage2 = "Clic destro su blocchi temporizzati o cuccioli per spendere il tempo accumulato" - stored = "Tempo Accumulato" +name = "Tempo in Bottiglia" +usage1 = "Accumula tempo passivamente mentre e' nel tuo inventario" +usage2 = "Clic destro su blocchi temporizzati o cuccioli per spendere il tempo accumulato" +stored = "Tempo Accumulato" [items.chrono_time_bomb] - name = "Bomba Temporale" - usage1 = "Clic destro per lanciare un proiettile crono che crea un campo temporale" +name = "Bomba Temporale" +usage1 = "Clic destro per lanciare un proiettile crono che crea un campo temporale" [items.elevator_block] - name = "Blocco Ascensore" - usage1 = "Salta per teletrasportarti in alto" - usage2 = "Accovacciati per teletrasportarti in basso" - usage3 = "Minimo 2 blocchi d'aria tra gli ascensori" +name = "Blocco Ascensore" +usage1 = "Salta per teletrasportarti in alto" +usage2 = "Accovacciati per teletrasportarti in basso" +usage3 = "Minimo 2 blocchi d'aria tra gli ascensori" # snippets [snippets] [snippets.gui] - level = "Livello" - knowledge = "conoscenza" - power_used = "Potere utilizzato" - not_learned = "Non appreso" - xp = "XP per" - welcome = "Benvenuto!" - welcome_back = "Bentornato!" - xp_bonus_for_time = "XP per" - max_ability_power = "Potere massimo delle abilita'" - unlock_this_by_clicking = "Sblocca questo facendo clic destro: " - back = "Indietro" - unlearn_all = "Disimpara tutto" - unlearned_all = "Tutto disimparato" +level = "Livello" +knowledge = "conoscenza" +power_used = "Potere utilizzato" +not_learned = "Non appreso" +xp = "XP per" +welcome = "Benvenuto!" +welcome_back = "Bentornato!" +xp_bonus_for_time = "XP per" +max_ability_power = "Potere massimo delle abilita'" +unlock_this_by_clicking = "Sblocca questo facendo clic destro: " +back = "Indietro" +unlearn_all = "Disimpara tutto" +unlearned_all = "Tutto disimparato" [snippets.adapt_menu] - may_not_unlearn = "NON PUOI DISIMPARARE" - may_unlearn = "PUOI IMPARARE/DISIMPARARE" - knowledge_cost = "Costo di conoscenza" - knowledge_available = "Conoscenza disponibile" - already_learned = "Gia' appreso" - unlearn_refund = "Clicca per disimparare e rimborsare" - no_refunds = "HARDCORE, RIMBORSI DISABILITATI" - knowledge = "conoscenza" - click_learn = "Clicca per imparare" - no_knowledge = "(Non hai alcuna conoscenza)" - you_only_have = "Hai solo" - how_to_level_up = "Aumenta il livello delle abilita' per incrementare il tuo potere massimo." - not_enough_power = "Potere insufficiente! Ogni livello di abilita' costa 1 potere." - power = "potere" - power_drain = "Consumo di potere" - learned = "Appreso " - unlearned = "Disimparato " - activator_block = "Libreria" +may_not_unlearn = "NON PUOI DISIMPARARE" +may_unlearn = "PUOI IMPARARE/DISIMPARARE" +knowledge_cost = "Costo di conoscenza" +knowledge_available = "Conoscenza disponibile" +already_learned = "Gia' appreso" +unlearn_refund = "Clicca per disimparare e rimborsare" +no_refunds = "HARDCORE, RIMBORSI DISABILITATI" +knowledge = "conoscenza" +click_learn = "Clicca per imparare" +no_knowledge = "(Non hai alcuna conoscenza)" +you_only_have = "Hai solo" +how_to_level_up = "Aumenta il livello delle abilita' per incrementare il tuo potere massimo." +not_enough_power = "Potere insufficiente! Ogni livello di abilita' costa 1 potere." +power = "potere" +power_drain = "Consumo di potere" +learned = "Appreso " +unlearned = "Disimparato " +activator_block = "Libreria" [snippets.knowledge_orb] - contains = "contiene" - knowledge = "conoscenza" - rightclick = "Clic destro" - togainknowledge = "per ottenere questa conoscenza" - knowledge_orb = "Sfera della Conoscenza" +contains = "contiene" +knowledge = "conoscenza" +rightclick = "Clic destro" +togainknowledge = "per ottenere questa conoscenza" +knowledge_orb = "Sfera della Conoscenza" [snippets.experience_orb] - contains = "contiene" - xp = "Esperienza" - rightclick = "Clic destro" - togainxp = "per ottenere questa esperienza" - xporb = "Sfera dell'Esperienza" +contains = "contiene" +xp = "Esperienza" +rightclick = "Clic destro" +togainxp = "per ottenere questa esperienza" +xporb = "Sfera dell'Esperienza" # skill [skill] [skill.agility] - name = "Agilita'" - icon = "⇉" - description = "L'agilita' e' la capacita' di muoversi rapidamente e fluidamente di fronte agli ostacoli." +name = "Agilita'" +icon = "⇉" +description = "L'agilita' e' la capacita' di muoversi rapidamente e fluidamente di fronte agli ostacoli." [skill.architect] - name = "Architetto" - icon = "⬧" - description = "Le strutture sono i mattoni del mondo. La realta' e' nelle tue mani, tua da controllare." +name = "Architetto" +icon = "⬧" +description = "Le strutture sono i mattoni del mondo. La realta' e' nelle tue mani, tua da controllare." [skill.axes] - name = "Asce" - icon = "🪓" - description1 = "Perche' abbattere alberi, quando puoi tagliare " - description2 = "cose" - description3 = "al loro posto, stesso risultato finale!" +name = "Asce" +icon = "🪓" +description1 = "Perche' abbattere alberi, quando puoi tagliare " +description2 = "cose" +description3 = "al loro posto, stesso risultato finale!" [skill.brewing] - name = "Alchimia" - icon = "❦" - description = "Doppia bolla, tripla bolla, quadrupla bolla - ancora non riesco a mettere questa pozione in un calderone" +name = "Alchimia" +icon = "❦" +description = "Doppia bolla, tripla bolla, quadrupla bolla - ancora non riesco a mettere questa pozione in un calderone" [skill.blocking] - name = "Difesa" - icon = "🛡" - description = "Bastoni e pietre non ti spezzeranno le ossa, ma uno scudo si'." +name = "Difesa" +icon = "🛡" +description = "Bastoni e pietre non ti spezzeranno le ossa, ma uno scudo si'." [skill.crafting] - name = "Artigianato" - icon = "⌂" - description = "Non essendoci piu' pezzi da piazzare, perche' non crearne un altro?" +name = "Artigianato" +icon = "⌂" +description = "Non essendoci piu' pezzi da piazzare, perche' non crearne un altro?" [skill.discovery] - name = "Scoperta" - icon = "⚛" - description = "Mentre la tua percezione si espande, la tua mente si dipana per scoprire cio' che non conoscevi." +name = "Scoperta" +icon = "⚛" +description = "Mentre la tua percezione si espande, la tua mente si dipana per scoprire cio' che non conoscevi." [skill.enchanting] - name = "Incantamento" - icon = "♰" - description = "Di che stai parlando? Profezie, visioni, chiacchiere superstiziose?" +name = "Incantamento" +icon = "♰" +description = "Di che stai parlando? Profezie, visioni, chiacchiere superstiziose?" [skill.excavation] - name = "Scavo" - icon = "ᛳ" - description = "Scava scava un buco..." +name = "Scavo" +icon = "ᛳ" +description = "Scava scava un buco..." [skill.herbalism] - name = "Erboristeria" - icon = "⚘" - description = "Non trovo nessuna pianta, ma trovo dei semi e... quella e'... erba?" +name = "Erboristeria" +icon = "⚘" +description = "Non trovo nessuna pianta, ma trovo dei semi e... quella e'... erba?" [skill.hunter] - name = "Cacciatore" - icon = "☠" - description = "La caccia riguarda il viaggio, non il risultato." +name = "Cacciatore" +icon = "☠" +description = "La caccia riguarda il viaggio, non il risultato." [skill.nether] - name = "Nether" - icon = "₪" - description = "Dalle profondita' del Nether stesso." +name = "Nether" +icon = "₪" +description = "Dalle profondita' del Nether stesso." [skill.pickaxe] - name = "Piccone" - icon = "⛏" - description = "I nani sono i minatori, ma ho imparato un paio di cose col tempo. SONO SVEDESE" +name = "Piccone" +icon = "⛏" +description = "I nani sono i minatori, ma ho imparato un paio di cose col tempo. SONO SVEDESE" [skill.ranged] - name = "A distanza" - icon = "🏹" - description = "La distanza e' la chiave della vittoria, e la chiave della sopravvivenza." +name = "A distanza" +icon = "🏹" +description = "La distanza e' la chiave della vittoria, e la chiave della sopravvivenza." [skill.rift] - name = "Varco" - icon = "❍" - description = "Il Varco e' un'imbracatura caustica, ma tu hai imbrigliato l'imbracatura." +name = "Varco" +icon = "❍" +description = "Il Varco e' un'imbracatura caustica, ma tu hai imbrigliato l'imbracatura." [skill.seaborne] - name = "Marittimo" - icon = "🎣" - description = "Con questa abilita', potrai dominare le meraviglie dell'acqua." +name = "Marittimo" +icon = "🎣" +description = "Con questa abilita', potrai dominare le meraviglie dell'acqua." [skill.stealth] - name = "Furtivita'" - icon = "☯" - description = "L'arte dell'invisibile. Cammina nelle ombre." +name = "Furtivita'" +icon = "☯" +description = "L'arte dell'invisibile. Cammina nelle ombre." [skill.swords] - name = "Spade" - icon = "⚔" - description = "Per il potere della Pietra Grigia!" +name = "Spade" +icon = "⚔" +description = "Per il potere della Pietra Grigia!" [skill.taming] - name = "Addomesticamento" - icon = "♥" - description = "I pappagalli e le api... e tu?" +name = "Addomesticamento" +icon = "♥" +description = "I pappagalli e le api... e tu?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Il sangue scorre nelle vene dell'universo. Costretto dalle tue mani." +name = "TragOul" +icon = "🗡" +description = "Il sangue scorre nelle vene dell'universo. Costretto dalle tue mani." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Carica l'orologio dell'universo, sperimenta il flusso. Rompi l'orologio, diventa l'orologio." +name = "Chronos" +icon = "🕒" +description = "Carica l'orologio dell'universo, sperimenta il flusso. Rompi l'orologio, diventa l'orologio." [skill.unarmed] - name = "Disarmato" - icon = "»" - description = "Senza un'arma non significa senza forza." +name = "Disarmato" +icon = "»" +description = "Senza un'arma non significa senza forza." # agility [agility] [agility.armor_up] - name = "Armatura Crescente" - description = "Ottieni piu' armatura quanto piu' a lungo scatti!" - lore1 = "Armatura massima" - lore2 = "Tempo di accumulo armatura" - lore = ["Armatura massima", "Tempo di accumulo armatura"] +name = "Armatura Crescente" +description = "Ottieni piu' armatura quanto piu' a lungo scatti!" +lore1 = "Armatura massima" +lore2 = "Tempo di accumulo armatura" +lore = ["Armatura massima", "Tempo di accumulo armatura"] [agility.ladder_slide] - name = "Scivolata sulla Scala" - description = "Sali e scendi le scale molto piu' velocemente in entrambe le direzioni." - lore1 = "Moltiplicatore velocita' scala" - lore2 = "Velocita' discesa rapida" - lore = ["Moltiplicatore velocita' scala", "Velocita' discesa rapida"] +name = "Scivolata sulla Scala" +description = "Sali e scendi le scale molto piu' velocemente in entrambe le direzioni." +lore1 = "Moltiplicatore velocita' scala" +lore2 = "Velocita' discesa rapida" +lore = ["Moltiplicatore velocita' scala", "Velocita' discesa rapida"] [agility.super_jump] - name = "Super Salto" - description = "Vantaggio eccezionale in altezza." - lore1 = "Altezza massima di salto" - lore2 = "Accovacciati + Salta per il Super Salto!" - lore = ["Altezza massima di salto", "Accovacciati + Salta per il Super Salto!"] +name = "Super Salto" +description = "Vantaggio eccezionale in altezza." +lore1 = "Altezza massima di salto" +lore2 = "Accovacciati + Salta per il Super Salto!" +lore = ["Altezza massima di salto", "Accovacciati + Salta per il Super Salto!"] [agility.wall_jump] - name = "Salto dal Muro" - description = "Tieni premuto Shift a mezz'aria contro un muro per aggrapparti e saltare!" - lore1 = "Salti massimi" - lore2 = "Altezza di salto" - lore = ["Salti massimi", "Altezza di salto"] +name = "Salto dal Muro" +description = "Tieni premuto Shift a mezz'aria contro un muro per aggrapparti e saltare!" +lore1 = "Salti massimi" +lore2 = "Altezza di salto" +lore = ["Salti massimi", "Altezza di salto"] [agility.wind_up] - name = "Accelerazione" - description = "Diventa piu' veloce quanto piu' a lungo scatti!" - lore1 = "Velocita' massima" - lore2 = "Tempo di accelerazione" - lore = ["Velocita' massima", "Tempo di accelerazione"] +name = "Accelerazione" +description = "Diventa piu' veloce quanto piu' a lungo scatti!" +lore1 = "Velocita' massima" +lore2 = "Tempo di accelerazione" +lore = ["Velocita' massima", "Tempo di accelerazione"] # architect [architect] [architect.elevator] - name = "Ascensore" - description = "Ti permette di costruire un ascensore per teletrasportarti verticalmente in fretta!" - lore1 = "Sblocca la ricetta dell'ascensore: X=LANA, Y=PERLA DI ENDER" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Sblocca la ricetta dell'ascensore: X=LANA, Y=PERLA DI ENDER", "XXX", "XYX", "XXX"] +name = "Ascensore" +description = "Ti permette di costruire un ascensore per teletrasportarti verticalmente in fretta!" +lore1 = "Sblocca la ricetta dell'ascensore: X=LANA, Y=PERLA DI ENDER" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Sblocca la ricetta dell'ascensore: X=LANA, Y=PERLA DI ENDER", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Fondamenta Magiche" - description = "Ti permette di accovacciarti e posizionare delle fondamenta temporanee sotto di te!" - lore1 = "Crea magicamente: " - lore2 = "Blocchi sotto di te!" - lore = ["Crea magicamente: ", "Blocchi sotto di te!"] +name = "Fondamenta Magiche" +description = "Ti permette di accovacciarti e posizionare delle fondamenta temporanee sotto di te!" +lore1 = "Crea magicamente: " +lore2 = "Blocchi sotto di te!" +lore = ["Crea magicamente: ", "Blocchi sotto di te!"] [architect.glass] - name = "Vetro Tocco di Velluto" - description = "Ti permette di evitare la perdita di blocchi di vetro quando li rompi a mani nude!" - lore1 = "Le tue mani ottengono Tocco di Velluto per il Vetro" - lore = ["Le tue mani ottengono Tocco di Velluto per il Vetro"] +name = "Vetro Tocco di Velluto" +description = "Ti permette di evitare la perdita di blocchi di vetro quando li rompi a mani nude!" +lore1 = "Le tue mani ottengono Tocco di Velluto per il Vetro" +lore = ["Le tue mani ottengono Tocco di Velluto per il Vetro"] [architect.wireless_redstone] - name = "Telecomando di Redstone" - description = "Ti permette di usare una torcia di redstone per attivare la redstone a distanza!" - lore1 = "Bersaglio + Torcia di Redstone + Perla di Ender = 1 Telecomando di Redstone" - lore = ["Bersaglio + Torcia di Redstone + Perla di Ender = 1 Telecomando di Redstone"] +name = "Telecomando di Redstone" +description = "Ti permette di usare una torcia di redstone per attivare la redstone a distanza!" +lore1 = "Bersaglio + Torcia di Redstone + Perla di Ender = 1 Telecomando di Redstone" +lore = ["Bersaglio + Torcia di Redstone + Perla di Ender = 1 Telecomando di Redstone"] [architect.placement] - name = "Bacchetta del Costruttore" - description = "Ti permette di posizionare piu' blocchi alla volta. Per attivarla accovacciati e tieni un blocco che corrisponde a quello che stai guardando e posiziona! Potrebbe essere necessario spostarsi un po' per attivare il riquadro!" - lore1 = "Ti servono" - lore2 = "blocchi in mano per posizionare questo" - lore3 = "Bacchetta del Costruttore di Materiali" - lore = ["Ti servono", "blocchi in mano per posizionare questo", "Bacchetta del Costruttore di Materiali"] +name = "Bacchetta del Costruttore" +description = "Ti permette di posizionare piu' blocchi alla volta. Per attivarla accovacciati e tieni un blocco che corrisponde a quello che stai guardando e posiziona! Potrebbe essere necessario spostarsi un po' per attivare il riquadro!" +lore1 = "Ti servono" +lore2 = "blocchi in mano per posizionare questo" +lore3 = "Bacchetta del Costruttore di Materiali" +lore = ["Ti servono", "blocchi in mano per posizionare questo", "Bacchetta del Costruttore di Materiali"] # axe [axe] [axe.chop] - name = "Taglio con l'Ascia" - description = "Abbatti gli alberi facendo clic destro sul tronco alla base!" - lore1 = "Blocchi per taglio" - lore2 = "Tempo di recupero del taglio" - lore3 = "Usura dell'attrezzo" - lore = ["Blocchi per taglio", "Tempo di recupero del taglio", "Usura dell'attrezzo"] +name = "Taglio con l'Ascia" +description = "Abbatti gli alberi facendo clic destro sul tronco alla base!" +lore1 = "Blocchi per taglio" +lore2 = "Tempo di recupero del taglio" +lore3 = "Usura dell'attrezzo" +lore = ["Blocchi per taglio", "Tempo di recupero del taglio", "Usura dell'attrezzo"] [axe.log_swap] - name = "Scambiatore di Tronchi di Lucy" - description = "Cambia il tipo di tronchi in un Banco da Lavoro!" - lore1 = "8 tronchi di qualsiasi tipo + 1 arboscello = 8 tronchi del tipo dell'arboscello" - lore = ["8 tronchi di qualsiasi tipo + 1 arboscello = 8 tronchi del tipo dell'arboscello"] +name = "Scambiatore di Tronchi di Lucy" +description = "Cambia il tipo di tronchi in un Banco da Lavoro!" +lore1 = "8 tronchi di qualsiasi tipo + 1 arboscello = 8 tronchi del tipo dell'arboscello" +lore = ["8 tronchi di qualsiasi tipo + 1 arboscello = 8 tronchi del tipo dell'arboscello"] [axe.drop_to_inventory] - name = "Ascia Raccogli-nell'Inventario" +name = "Ascia Raccogli-nell'Inventario" [axe.ground_smash] - name = "Schianto a Terra con l'Ascia" - description = "Salta, poi accovacciati e colpisci tutti i nemici vicini." - lore1 = "Danno" - lore2 = "Raggio in blocchi" - lore3 = "Forza" - lore4 = "Tempo di recupero dello schianto" - lore = ["Danno", "Raggio in blocchi", "Forza", "Tempo di recupero dello schianto"] +name = "Schianto a Terra con l'Ascia" +description = "Salta, poi accovacciati e colpisci tutti i nemici vicini." +lore1 = "Danno" +lore2 = "Raggio in blocchi" +lore3 = "Forza" +lore4 = "Tempo di recupero dello schianto" +lore = ["Danno", "Raggio in blocchi", "Forza", "Tempo di recupero dello schianto"] [axe.leaf_miner] - name = "Sfogliatore" - description = "Ti permette di rompere grandi quantita' di foglie in una volta!" - lore1 = "Accovacciati e mina le FOGLIE" - lore2 = "raggio di raccolta foglie" - lore3 = "Non otterrai i drop dalle foglie (Prevenzione Exploit)" - lore = ["Accovacciati e mina le FOGLIE", "raggio di raccolta foglie", "Non otterrai i drop dalle foglie (Prevenzione Exploit)"] +name = "Sfogliatore" +description = "Ti permette di rompere grandi quantita' di foglie in una volta!" +lore1 = "Accovacciati e mina le FOGLIE" +lore2 = "raggio di raccolta foglie" +lore3 = "Non otterrai i drop dalle foglie (Prevenzione Exploit)" +lore = ["Accovacciati e mina le FOGLIE", "raggio di raccolta foglie", "Non otterrai i drop dalle foglie (Prevenzione Exploit)"] [axe.wood_miner] - name = "Taglia-legna" - description = "Ti permette di rompere grandi quantita' di legno in una volta!" - lore1 = "Accovacciati e mina LEGNO/TRONCHI (non assi)" - lore2 = "raggio di raccolta legno" - lore3 = "Funziona con Raccogli nell'Inventario" - lore = ["Accovacciati e mina LEGNO/TRONCHI (non assi)", "raggio di raccolta legno", "Funziona con Raccogli nell'Inventario"] +name = "Taglia-legna" +description = "Ti permette di rompere grandi quantita' di legno in una volta!" +lore1 = "Accovacciati e mina LEGNO/TRONCHI (non assi)" +lore2 = "raggio di raccolta legno" +lore3 = "Funziona con Raccogli nell'Inventario" +lore = ["Accovacciati e mina LEGNO/TRONCHI (non assi)", "raggio di raccolta legno", "Funziona con Raccogli nell'Inventario"] # brewing [brewing] [brewing.lingering] - name = "Infuso Persistente" - description = "Le pozioni preparate durano piu' a lungo!" - lore1 = "Durata" - lore2 = "Durata" - lore = ["Durata", "Durata"] +name = "Infuso Persistente" +description = "Le pozioni preparate durano piu' a lungo!" +lore1 = "Durata" +lore2 = "Durata" +lore = ["Durata", "Durata"] [brewing.super_heated] - name = "Infuso Surriscaldato" - description = "I supporti per pozioni funzionano piu' velocemente quanto piu' sono caldi." - lore1 = "Per blocco di fuoco a contatto" - lore2 = "Per blocco di lava a contatto" - lore = ["Per blocco di fuoco a contatto", "Per blocco di lava a contatto"] +name = "Infuso Surriscaldato" +description = "I supporti per pozioni funzionano piu' velocemente quanto piu' sono caldi." +lore1 = "Per blocco di fuoco a contatto" +lore2 = "Per blocco di lava a contatto" +lore = ["Per blocco di fuoco a contatto", "Per blocco di lava a contatto"] [brewing.darkness] - name = "Oscurita' in Bottiglia" - description = "Non so perche' ne hai bisogno, ma eccotela!" - lore1 = "Pozione di Visione Notturna + Cemento Nero = Pozione dell'Oscurita' (30 secondi)" - lore2 = "Nota bene: questo impedisce all'utilizzatore di scattare!" - lore = ["Pozione di Visione Notturna + Cemento Nero = Pozione dell'Oscurita' (30 secondi)", "Nota bene: questo impedisce all'utilizzatore di scattare!"] +name = "Oscurita' in Bottiglia" +description = "Non so perche' ne hai bisogno, ma eccotela!" +lore1 = "Pozione di Visione Notturna + Cemento Nero = Pozione dell'Oscurita' (30 secondi)" +lore2 = "Nota bene: questo impedisce all'utilizzatore di scattare!" +lore = ["Pozione di Visione Notturna + Cemento Nero = Pozione dell'Oscurita' (30 secondi)", "Nota bene: questo impedisce all'utilizzatore di scattare!"] [brewing.haste] - name = "Fretta in Bottiglia" - description = "Quando l'Efficienza non basta" - lore1 = "Pozione di Velocita' + Frammento di Ametista = Pozione della Fretta (60 secondi)" - lore2 = "Pozione di Velocita' + Blocco di Ametista = Pozione della Fretta-2 (30 secondi)" - lore = ["Pozione di Velocita' + Frammento di Ametista = Pozione della Fretta (60 secondi)", "Pozione di Velocita' + Blocco di Ametista = Pozione della Fretta-2 (30 secondi)"] +name = "Fretta in Bottiglia" +description = "Quando l'Efficienza non basta" +lore1 = "Pozione di Velocita' + Frammento di Ametista = Pozione della Fretta (60 secondi)" +lore2 = "Pozione di Velocita' + Blocco di Ametista = Pozione della Fretta-2 (30 secondi)" +lore = ["Pozione di Velocita' + Frammento di Ametista = Pozione della Fretta (60 secondi)", "Pozione di Velocita' + Blocco di Ametista = Pozione della Fretta-2 (30 secondi)"] [brewing.absorption] - name = "Assorbimento in Bottiglia" - description = "Rafforza il corpo!" - lore1 = "Guarigione Istantanea + Quarzo = Pozione dell'Assorbimento (60 secondi)" - lore2 = "Guarigione Istantanea + Blocco di Quarzo = Pozione dell'Assorbimento-2 (30 secondi)" - lore = ["Guarigione Istantanea + Quarzo = Pozione dell'Assorbimento (60 secondi)", "Guarigione Istantanea + Blocco di Quarzo = Pozione dell'Assorbimento-2 (30 secondi)"] +name = "Assorbimento in Bottiglia" +description = "Rafforza il corpo!" +lore1 = "Guarigione Istantanea + Quarzo = Pozione dell'Assorbimento (60 secondi)" +lore2 = "Guarigione Istantanea + Blocco di Quarzo = Pozione dell'Assorbimento-2 (30 secondi)" +lore = ["Guarigione Istantanea + Quarzo = Pozione dell'Assorbimento (60 secondi)", "Guarigione Istantanea + Blocco di Quarzo = Pozione dell'Assorbimento-2 (30 secondi)"] [brewing.fatigue] - name = "Fatica in Bottiglia" - description = "Indebolisci il corpo!" - lore1 = "Pozione di Debolezza + Palla di Slime = Pozione della Fatica (30 secondi)" - lore2 = "Pozione di Debolezza + Blocco di Slime = Pozione della Fatica-2 (15 secondi)" - lore = ["Pozione di Debolezza + Palla di Slime = Pozione della Fatica (30 secondi)", "Pozione di Debolezza + Blocco di Slime = Pozione della Fatica-2 (15 secondi)"] +name = "Fatica in Bottiglia" +description = "Indebolisci il corpo!" +lore1 = "Pozione di Debolezza + Palla di Slime = Pozione della Fatica (30 secondi)" +lore2 = "Pozione di Debolezza + Blocco di Slime = Pozione della Fatica-2 (15 secondi)" +lore = ["Pozione di Debolezza + Palla di Slime = Pozione della Fatica (30 secondi)", "Pozione di Debolezza + Blocco di Slime = Pozione della Fatica-2 (15 secondi)"] [brewing.hunger] - name = "Fame in Bottiglia" - description = "Dai da mangiare all'insaziabile!" - lore1 = "Pozione Sconclusionata + Carne Marcia = Pozione della Fame (30 secondi)" - lore2 = "Pozione di Debolezza + Carne Marcia = Pozione della Fame-3 (15 secondi)" - lore = ["Pozione Sconclusionata + Carne Marcia = Pozione della Fame (30 secondi)", "Pozione di Debolezza + Carne Marcia = Pozione della Fame-3 (15 secondi)"] +name = "Fame in Bottiglia" +description = "Dai da mangiare all'insaziabile!" +lore1 = "Pozione Sconclusionata + Carne Marcia = Pozione della Fame (30 secondi)" +lore2 = "Pozione di Debolezza + Carne Marcia = Pozione della Fame-3 (15 secondi)" +lore = ["Pozione Sconclusionata + Carne Marcia = Pozione della Fame (30 secondi)", "Pozione di Debolezza + Carne Marcia = Pozione della Fame-3 (15 secondi)"] [brewing.nausea] - name = "Nausea in Bottiglia" - description = "Mi fai venire il voltastomaco!" - lore1 = "Pozione Sconclusionata + Fungo Marrone = Pozione della Nausea (16 secondi)" - lore2 = "Pozione Sconclusionata + Fungo Cremisi = Pozione della Nausea-2 (8 secondi)" - lore = ["Pozione Sconclusionata + Fungo Marrone = Pozione della Nausea (16 secondi)", "Pozione Sconclusionata + Fungo Cremisi = Pozione della Nausea-2 (8 secondi)"] +name = "Nausea in Bottiglia" +description = "Mi fai venire il voltastomaco!" +lore1 = "Pozione Sconclusionata + Fungo Marrone = Pozione della Nausea (16 secondi)" +lore2 = "Pozione Sconclusionata + Fungo Cremisi = Pozione della Nausea-2 (8 secondi)" +lore = ["Pozione Sconclusionata + Fungo Marrone = Pozione della Nausea (16 secondi)", "Pozione Sconclusionata + Fungo Cremisi = Pozione della Nausea-2 (8 secondi)"] [brewing.blindness] - name = "Cecita' in Bottiglia" - description = "Sei una persona orribile..." - lore1 = "Pozione Sconclusionata + Sacca d'Inchiostro = Pozione della Cecita' (30 secondi)" - lore2 = "Pozione Sconclusionata + Sacca d'Inchiostro Luminosa = Pozione della Cecita'-2 (15 secondi)" - lore = ["Pozione Sconclusionata + Sacca d'Inchiostro = Pozione della Cecita' (30 secondi)", "Pozione Sconclusionata + Sacca d'Inchiostro Luminosa = Pozione della Cecita'-2 (15 secondi)"] +name = "Cecita' in Bottiglia" +description = "Sei una persona orribile..." +lore1 = "Pozione Sconclusionata + Sacca d'Inchiostro = Pozione della Cecita' (30 secondi)" +lore2 = "Pozione Sconclusionata + Sacca d'Inchiostro Luminosa = Pozione della Cecita'-2 (15 secondi)" +lore = ["Pozione Sconclusionata + Sacca d'Inchiostro = Pozione della Cecita' (30 secondi)", "Pozione Sconclusionata + Sacca d'Inchiostro Luminosa = Pozione della Cecita'-2 (15 secondi)"] [brewing.resistance] - name = "Resistenza in Bottiglia" - description = "Fortificazione al suo massimo!" - lore1 = "Pozione Sconclusionata + Lingotto di Ferro = Pozione della Resistenza (60 secondi)" - lore2 = "Pozione Sconclusionata + Blocco di Ferro = Pozione della Resistenza-2 (30 secondi)" - lore = ["Pozione Sconclusionata + Lingotto di Ferro = Pozione della Resistenza (60 secondi)", "Pozione Sconclusionata + Blocco di Ferro = Pozione della Resistenza-2 (30 secondi)"] +name = "Resistenza in Bottiglia" +description = "Fortificazione al suo massimo!" +lore1 = "Pozione Sconclusionata + Lingotto di Ferro = Pozione della Resistenza (60 secondi)" +lore2 = "Pozione Sconclusionata + Blocco di Ferro = Pozione della Resistenza-2 (30 secondi)" +lore = ["Pozione Sconclusionata + Lingotto di Ferro = Pozione della Resistenza (60 secondi)", "Pozione Sconclusionata + Blocco di Ferro = Pozione della Resistenza-2 (30 secondi)"] [brewing.health_boost] - name = "Vita in Bottiglia" - description = "Quando la salute massima non basta..." - lore1 = "Pozione di Guarigione Istantanea + Mela d'Oro = Pozione di Salute Potenziata (120 secondi)" - lore2 = "Pozione di Guarigione Istantanea + Mela d'Oro Incantata = Pozione di Salute Potenziata-2 (120 secondi)" - lore = ["Pozione di Guarigione Istantanea + Mela d'Oro = Pozione di Salute Potenziata (120 secondi)", "Pozione di Guarigione Istantanea + Mela d'Oro Incantata = Pozione di Salute Potenziata-2 (120 secondi)"] +name = "Vita in Bottiglia" +description = "Quando la salute massima non basta..." +lore1 = "Pozione di Guarigione Istantanea + Mela d'Oro = Pozione di Salute Potenziata (120 secondi)" +lore2 = "Pozione di Guarigione Istantanea + Mela d'Oro Incantata = Pozione di Salute Potenziata-2 (120 secondi)" +lore = ["Pozione di Guarigione Istantanea + Mela d'Oro = Pozione di Salute Potenziata (120 secondi)", "Pozione di Guarigione Istantanea + Mela d'Oro Incantata = Pozione di Salute Potenziata-2 (120 secondi)"] [brewing.decay] - name = "Decadimento in Bottiglia" - description = "Chi l'avrebbe mai detto che il Detritus sarebbe stato cosi' utile?" - lore1 = "Pozione di Debolezza + Patata Velenosa = Pozione dell'Avvizzimento (16 secondi)" - lore2 = "Pozione di Debolezza + Radici Cremisi = Pozione dell'Avvizzimento-2 (8 secondi)" - lore = ["Pozione di Debolezza + Patata Velenosa = Pozione dell'Avvizzimento (16 secondi)", "Pozione di Debolezza + Radici Cremisi = Pozione dell'Avvizzimento-2 (8 secondi)"] +name = "Decadimento in Bottiglia" +description = "Chi l'avrebbe mai detto che il Detritus sarebbe stato cosi' utile?" +lore1 = "Pozione di Debolezza + Patata Velenosa = Pozione dell'Avvizzimento (16 secondi)" +lore2 = "Pozione di Debolezza + Radici Cremisi = Pozione dell'Avvizzimento-2 (8 secondi)" +lore = ["Pozione di Debolezza + Patata Velenosa = Pozione dell'Avvizzimento (16 secondi)", "Pozione di Debolezza + Radici Cremisi = Pozione dell'Avvizzimento-2 (8 secondi)"] [brewing.saturation] - name = "Saturazione in Bottiglia" - description = "Sai... non ho nemmeno fame..." - lore1 = "Pozione di Rigenerazione + Patata al Forno = Pozione della Saturazione" - lore2 = "Pozione di Rigenerazione + Balla di Fieno = Pozione della Saturazione-2" - lore = ["Pozione di Rigenerazione + Patata al Forno = Pozione della Saturazione", "Pozione di Rigenerazione + Balla di Fieno = Pozione della Saturazione-2"] +name = "Saturazione in Bottiglia" +description = "Sai... non ho nemmeno fame..." +lore1 = "Pozione di Rigenerazione + Patata al Forno = Pozione della Saturazione" +lore2 = "Pozione di Rigenerazione + Balla di Fieno = Pozione della Saturazione-2" +lore = ["Pozione di Rigenerazione + Patata al Forno = Pozione della Saturazione", "Pozione di Rigenerazione + Balla di Fieno = Pozione della Saturazione-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Catene di Mefistofele" - description = "Ti permette di creare armature di maglia" - lore1 = "La ricetta e' la stessa di qualsiasi altra armatura, ma con pepite di ferro" - lore = ["La ricetta e' la stessa di qualsiasi altra armatura, ma con pepite di ferro"] +name = "Catene di Mefistofele" +description = "Ti permette di creare armature di maglia" +lore1 = "La ricetta e' la stessa di qualsiasi altra armatura, ma con pepite di ferro" +lore = ["La ricetta e' la stessa di qualsiasi altra armatura, ma con pepite di ferro"] [blocking.horse_armorer] - name = "Armatura per Cavalli Artigianale" - description = "Ti permette di creare armature per cavalli" - lore1 = "Circonda una sella con il materiale che vuoi usare per forgiare l'armatura" - lore = ["Circonda una sella con il materiale che vuoi usare per forgiare l'armatura"] +name = "Armatura per Cavalli Artigianale" +description = "Ti permette di creare armature per cavalli" +lore1 = "Circonda una sella con il materiale che vuoi usare per forgiare l'armatura" +lore = ["Circonda una sella con il materiale che vuoi usare per forgiare l'armatura"] [blocking.saddle_crafter] - name = "Sella Artigianale" - description = "Crea una Sella con la Pelle" - lore1 = "Ricetta: 5 Pelle:" - lore = ["Ricetta: 5 Pelle:"] +name = "Sella Artigianale" +description = "Crea una Sella con la Pelle" +lore1 = "Ricetta: 5 Pelle:" +lore = ["Ricetta: 5 Pelle:"] [blocking.multi_armor] - name = "Multi-Armatura" - description = "Lega le Elitre all'Armatura" - lore1 = "Un'abilita' straordinaria per viaggiare." - lore2 = "Unisci e cambia dinamicamente Armatura/Elitre al volo!" - lore3 = "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario." - lore4 = "Per separare l'Armatura, Lascia cadere accovacciandoti e si smontera'." - lore5 = "Se la tua MultiArmatura viene distrutta, perderai tutti gli oggetti contenuti." - lore6 = "Non ho bisogno di armature, mi deludono..." - lore = ["Un'abilita' straordinaria per viaggiare.", "Unisci e cambia dinamicamente Armatura/Elitre al volo!", "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario.", "Per separare l'Armatura, Lascia cadere accovacciandoti e si smontera'.", "Se la tua MultiArmatura viene distrutta, perderai tutti gli oggetti contenuti.", "Non ho bisogno di armature, mi deludono..."] +name = "Multi-Armatura" +description = "Lega le Elitre all'Armatura" +lore1 = "Un'abilita' straordinaria per viaggiare." +lore2 = "Unisci e cambia dinamicamente Armatura/Elitre al volo!" +lore3 = "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario." +lore4 = "Per separare l'Armatura, Lascia cadere accovacciandoti e si smontera'." +lore5 = "Se la tua MultiArmatura viene distrutta, perderai tutti gli oggetti contenuti." +lore6 = "Non ho bisogno di armature, mi deludono..." +lore = ["Un'abilita' straordinaria per viaggiare.", "Unisci e cambia dinamicamente Armatura/Elitre al volo!", "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario.", "Per separare l'Armatura, Lascia cadere accovacciandoti e si smontera'.", "Se la tua MultiArmatura viene distrutta, perderai tutti gli oggetti contenuti.", "Non ho bisogno di armature, mi deludono..."] # crafting [crafting] [crafting.deconstruction] - name = "Decostruzione" - description = "Decostruisci blocchi e oggetti nei loro componenti base recuperabili!" - lore1 = "Lascia cadere un qualsiasi oggetto a terra." - lore2 = "Poi, accovacciati e fai Clic destro con le Cesoie" - lore = ["Lascia cadere un qualsiasi oggetto a terra.", "Poi, accovacciati e fai Clic destro con le Cesoie"] +name = "Decostruzione" +description = "Decostruisci blocchi e oggetti nei loro componenti base recuperabili!" +lore1 = "Lascia cadere un qualsiasi oggetto a terra." +lore2 = "Poi, accovacciati e fai Clic destro con le Cesoie" +lore = ["Lascia cadere un qualsiasi oggetto a terra.", "Poi, accovacciati e fai Clic destro con le Cesoie"] [crafting.xp] - name = "XP Artigianato" - description = "Guadagna XP passivi quando crei oggetti" - lore1 = "Guadagna XP quando crei oggetti" - lore = ["Guadagna XP quando crei oggetti"] +name = "XP Artigianato" +description = "Guadagna XP passivi quando crei oggetti" +lore1 = "Guadagna XP quando crei oggetti" +lore = ["Guadagna XP quando crei oggetti"] [crafting.reconstruction] - name = "Ricostruzione Minerali" - description = "Ricrea i minerali dai loro componenti base!" - lore1 = "8 dei drop e 1 ospite = 1 minerale (senza forma)" - lore2 = "I drop devono essere fusi (se applicabile)" - lore3 = "Esclusi: Scarti, Quarzo e Smeraldi ecc..." - lore4 = "Ospite = Involucro, cioe': Pietra, Netherrack, Ardesia Abissale" - lore = ["8 dei drop e 1 ospite = 1 minerale (senza forma)", "I drop devono essere fusi (se applicabile)", "Esclusi: Scarti, Quarzo e Smeraldi ecc...", "Ospite = Involucro, cioe': Pietra, Netherrack, Ardesia Abissale"] +name = "Ricostruzione Minerali" +description = "Ricrea i minerali dai loro componenti base!" +lore1 = "8 dei drop e 1 ospite = 1 minerale (senza forma)" +lore2 = "I drop devono essere fusi (se applicabile)" +lore3 = "Esclusi: Scarti, Quarzo e Smeraldi ecc..." +lore4 = "Ospite = Involucro, cioe': Pietra, Netherrack, Ardesia Abissale" +lore = ["8 dei drop e 1 ospite = 1 minerale (senza forma)", "I drop devono essere fusi (se applicabile)", "Esclusi: Scarti, Quarzo e Smeraldi ecc...", "Ospite = Involucro, cioe': Pietra, Netherrack, Ardesia Abissale"] [crafting.leather] - name = "Pelle Artigianale" - description = "Crea Pelle dalla Carne Marcia" - lore1 = "Basta gettarla (carne marcia) sul fuoco da campo!" - lore = ["Basta gettarla (carne marcia) sul fuoco da campo!"] +name = "Pelle Artigianale" +description = "Crea Pelle dalla Carne Marcia" +lore1 = "Basta gettarla (carne marcia) sul fuoco da campo!" +lore = ["Basta gettarla (carne marcia) sul fuoco da campo!"] [crafting.backpacks] - name = "Gli Zaini di un Boutilier!" - description = "Questo porta il Fagotto di Mojang nel gioco!" - lore1 = "Devi essere in Sopravvivenza per usarlo" - lore2 = "XLX : Pelle, Guinzaglio, Pelle" - lore3 = "XSX : Pelle, Barile, Pelle" - lore4 = "XCX : Pelle, Baule, Pelle" - lore = ["Devi essere in Sopravvivenza per usarlo", "XLX : Pelle, Guinzaglio, Pelle", "XSX : Pelle, Barile, Pelle", "XCX : Pelle, Baule, Pelle"] +name = "Gli Zaini di un Boutilier!" +description = "Questo porta il Fagotto di Mojang nel gioco!" +lore1 = "Devi essere in Sopravvivenza per usarlo" +lore2 = "XLX : Pelle, Guinzaglio, Pelle" +lore3 = "XSX : Pelle, Barile, Pelle" +lore4 = "XCX : Pelle, Baule, Pelle" +lore = ["Devi essere in Sopravvivenza per usarlo", "XLX : Pelle, Guinzaglio, Pelle", "XSX : Pelle, Barile, Pelle", "XCX : Pelle, Baule, Pelle"] [crafting.stations] - name = "Tavoli Portatili!" - description = "Usa un tavolo nel palmo della tua mano!" - lore2 = "TUTTI GLI OGGETTI DIMENTICATI NEL TAVOLO ALLA CHIUSURA SONO PERSI PER SEMPRE!" - lore3 = "Tavoli validi: Incudine, Banco da Lavoro, Mola, Cartografia, Tagliapietre, Telaio" - lore = ["TUTTI GLI OGGETTI DIMENTICATI NEL TAVOLO ALLA CHIUSURA SONO PERSI PER SEMPRE!", "Tavoli validi: Incudine, Banco da Lavoro, Mola, Cartografia, Tagliapietre, Telaio"] +name = "Tavoli Portatili!" +description = "Usa un tavolo nel palmo della tua mano!" +lore2 = "TUTTI GLI OGGETTI DIMENTICATI NEL TAVOLO ALLA CHIUSURA SONO PERSI PER SEMPRE!" +lore3 = "Tavoli validi: Incudine, Banco da Lavoro, Mola, Cartografia, Tagliapietre, Telaio" +lore = ["TUTTI GLI OGGETTI DIMENTICATI NEL TAVOLO ALLA CHIUSURA SONO PERSI PER SEMPRE!", "Tavoli validi: Incudine, Banco da Lavoro, Mola, Cartografia, Tagliapietre, Telaio"] [crafting.skulls] - name = "Teschi Artigianali!" - description = "Usando dei materiali puoi creare teschi di mob!" - lore1 = "Circonda un Blocco d'Ossa con i seguenti per ottenere un teschio:" - lore2 = "Zombie: Carne Marcia" - lore3 = "Scheletro: Osso" - lore4 = "Creeper: Polvere da Sparo" - lore5 = "Wither: Mattone del Nether" - lore6 = "Drago: Soffio del Drago" - lore = ["Circonda un Blocco d'Ossa con i seguenti per ottenere un teschio:", "Zombie: Carne Marcia", "Scheletro: Osso", "Creeper: Polvere da Sparo", "Wither: Mattone del Nether", "Drago: Soffio del Drago"] +name = "Teschi Artigianali!" +description = "Usando dei materiali puoi creare teschi di mob!" +lore1 = "Circonda un Blocco d'Ossa con i seguenti per ottenere un teschio:" +lore2 = "Zombie: Carne Marcia" +lore3 = "Scheletro: Osso" +lore4 = "Creeper: Polvere da Sparo" +lore5 = "Wither: Mattone del Nether" +lore6 = "Drago: Soffio del Drago" +lore = ["Circonda un Blocco d'Ossa con i seguenti per ottenere un teschio:", "Zombie: Carne Marcia", "Scheletro: Osso", "Creeper: Polvere da Sparo", "Wither: Mattone del Nether", "Drago: Soffio del Drago"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Tempo in Bottiglia" - description = "Porta con te una bottiglia temporale che accumula tempo e spendilo per accelerare blocchi temporizzati, coltivazioni e creature invecchiabili come i cuccioli. Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro." - lore1 = "Secondi accumulati per tick" - lore2 = "Accelerazione temporale per secondo accumulato" - lore3 = "Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro" - lore = ["Secondi accumulati per tick", "Accelerazione temporale per secondo accumulato", "Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro"] +name = "Tempo in Bottiglia" +description = "Porta con te una bottiglia temporale che accumula tempo e spendilo per accelerare blocchi temporizzati, coltivazioni e creature invecchiabili come i cuccioli. Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro." +lore1 = "Secondi accumulati per tick" +lore2 = "Accelerazione temporale per secondo accumulato" +lore3 = "Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro" +lore = ["Secondi accumulati per tick", "Accelerazione temporale per secondo accumulato", "Ricetta (senza forma): Pozione di Rapidita' + Orologio + Ampolla di Vetro"] [chronos.aberrant_touch] - name = "Tocco Aberrante" - description = "Gli attacchi in mischia applicano lentezza cumulativa al costo della fame, con limiti PvP rigorosi, e immobilizzano i bersagli a 5 accumuli." - lore1 = "Gli attacchi in mischia applicano lentezza cumulativa" - lore2 = "Durata massima lentezza PvE" - lore3 = "Amplificatore massimo lentezza PvP" - lore = ["Gli attacchi in mischia applicano lentezza cumulativa", "Durata massima lentezza PvE", "Amplificatore massimo lentezza PvP"] +name = "Tocco Aberrante" +description = "Gli attacchi in mischia applicano lentezza cumulativa al costo della fame, con limiti PvP rigorosi, e immobilizzano i bersagli a 5 accumuli." +lore1 = "Gli attacchi in mischia applicano lentezza cumulativa" +lore2 = "Durata massima lentezza PvE" +lore3 = "Amplificatore massimo lentezza PvP" +lore = ["Gli attacchi in mischia applicano lentezza cumulativa", "Durata massima lentezza PvE", "Amplificatore massimo lentezza PvP"] [chronos.instant_recall] - name = "Richiamo Istantaneo" - description = "Fai clic sinistro o destro con un orologio in mano per riavvolgere a un'istantanea recente con salute e fame ripristinate." - lore1 = "Durata del riavvolgimento" - lore2 = "Tempo di recupero" - lore3 = "Nessun ripristino dell'inventario" - lore = ["Durata del riavvolgimento", "Tempo di recupero", "Nessun ripristino dell'inventario"] +name = "Richiamo Istantaneo" +description = "Fai clic sinistro o destro con un orologio in mano per riavvolgere a un'istantanea recente con salute e fame ripristinate." +lore1 = "Durata del riavvolgimento" +lore2 = "Tempo di recupero" +lore3 = "Nessun ripristino dell'inventario" +lore = ["Durata del riavvolgimento", "Tempo di recupero", "Nessun ripristino dell'inventario"] [chronos.time_bomb] - name = "Bomba Temporale" - description = "Lancia una bomba crono forgiata che crea un campo temporale, rallenta le entita' e congela i proiettili." - lore1 = "Raggio del campo temporale" - lore2 = "Durata del campo temporale" - lore3 = "Tempo di recupero della bomba" - lore4 = "Ricetta (senza forma): Orologio + Palla di Neve + Diamante + Sabbia" - lore = ["Raggio del campo temporale", "Durata del campo temporale", "Tempo di recupero della bomba", "Ricetta (senza forma): Orologio + Palla di Neve + Diamante + Sabbia"] +name = "Bomba Temporale" +description = "Lancia una bomba crono forgiata che crea un campo temporale, rallenta le entita' e congela i proiettili." +lore1 = "Raggio del campo temporale" +lore2 = "Durata del campo temporale" +lore3 = "Tempo di recupero della bomba" +lore4 = "Ricetta (senza forma): Orologio + Palla di Neve + Diamante + Sabbia" +lore = ["Raggio del campo temporale", "Durata del campo temporale", "Tempo di recupero della bomba", "Ricetta (senza forma): Orologio + Palla di Neve + Diamante + Sabbia"] # discovery [discovery] [discovery.armor] - name = "Armatura del Mondo" - description = "Armatura passiva basata sulla durezza dei blocchi circostanti." - lore1 = "Armatura passiva" - lore2 = "Basata sulla durezza dei blocchi vicini" - lore3 = "Forza dell'armatura:" - lore = ["Armatura passiva", "Basata sulla durezza dei blocchi vicini", "Forza dell'armatura:"] +name = "Armatura del Mondo" +description = "Armatura passiva basata sulla durezza dei blocchi circostanti." +lore1 = "Armatura passiva" +lore2 = "Basata sulla durezza dei blocchi vicini" +lore3 = "Forza dell'armatura:" +lore = ["Armatura passiva", "Basata sulla durezza dei blocchi vicini", "Forza dell'armatura:"] [discovery.unity] - name = "Unita' Sperimentale" - description = "Raccogliere sfere di esperienza aggiunge XP ad abilita' casuali." - lore1 = "XP " - lore2 = "Per sfera" - lore = ["XP ", "Per sfera"] +name = "Unita' Sperimentale" +description = "Raccogliere sfere di esperienza aggiunge XP ad abilita' casuali." +lore1 = "XP " +lore2 = "Per sfera" +lore = ["XP ", "Per sfera"] [discovery.resist] - name = "Resistenza Sperimentale" - description = "Consuma esperienza per mitigare i danni solo quando un colpo ti farebbe scendere sotto i 5 cuori o ucciderti." - lore0 = "Si attiva solo a salute critica (<= 5 cuori) una volta ogni 15 secondi" - lore1 = " Danno ridotto" - lore2 = "esperienza drenata" - lore = ["Si attiva solo a salute critica (<= 5 cuori) una volta ogni 15 secondi", " Danno ridotto", "esperienza drenata"] +name = "Resistenza Sperimentale" +description = "Consuma esperienza per mitigare i danni solo quando un colpo ti farebbe scendere sotto i 5 cuori o ucciderti." +lore0 = "Si attiva solo a salute critica (<= 5 cuori) una volta ogni 15 secondi" +lore1 = " Danno ridotto" +lore2 = "esperienza drenata" +lore = ["Si attiva solo a salute critica (<= 5 cuori) una volta ogni 15 secondi", " Danno ridotto", "esperienza drenata"] [discovery.villager] - name = "Attrazione dei Villici" - description = "Ti permette di ottenere scambi migliori con i villici!" - lore1 = "Questo consuma XP per interazione con i villici" - lore2 = "Probabilita' per interazione di consumare XP e migliorare gli scambi" - lore3 = "XP richiesti drenati per interazione" - lore = ["Questo consuma XP per interazione con i villici", "Probabilita' per interazione di consumare XP e migliorare gli scambi", "XP richiesti drenati per interazione"] +name = "Attrazione dei Villici" +description = "Ti permette di ottenere scambi migliori con i villici!" +lore1 = "Questo consuma XP per interazione con i villici" +lore2 = "Probabilita' per interazione di consumare XP e migliorare gli scambi" +lore3 = "XP richiesti drenati per interazione" +lore = ["Questo consuma XP per interazione con i villici", "Probabilita' per interazione di consumare XP e migliorare gli scambi", "XP richiesti drenati per interazione"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Ritorno del Lapislazzuli" - description = "Al costo di 1 livello in piu' di XP, ha una probabilita' di restituirti lapislazzuli gratis" - lore1 = "Per ogni livello, aumenta il costo dell'incantamento di 1, ma puo' restituire fino a 3 Lapislazzuli" - lore = ["Per ogni livello, aumenta il costo dell'incantamento di 1, ma puo' restituire fino a 3 Lapislazzuli"] +name = "Ritorno del Lapislazzuli" +description = "Al costo di 1 livello in piu' di XP, ha una probabilita' di restituirti lapislazzuli gratis" +lore1 = "Per ogni livello, aumenta il costo dell'incantamento di 1, ma puo' restituire fino a 3 Lapislazzuli" +lore = ["Per ogni livello, aumenta il costo dell'incantamento di 1, ma puo' restituire fino a 3 Lapislazzuli"] [enchanting.quick_enchant] - name = "Incantamento Rapido" - description = "Incanta oggetti cliccando i libri degli incantesimi direttamente su di essi." - lore1 = "Livelli massimi combinati" - lore2 = "Non puoi incantare un oggetto con piu' di " - lore3 = "potere" - lore = ["Livelli massimi combinati", "Non puoi incantare un oggetto con piu' di ", "potere"] +name = "Incantamento Rapido" +description = "Incanta oggetti cliccando i libri degli incantesimi direttamente su di essi." +lore1 = "Livelli massimi combinati" +lore2 = "Non puoi incantare un oggetto con piu' di " +lore3 = "potere" +lore = ["Livelli massimi combinati", "Non puoi incantare un oggetto con piu' di ", "potere"] [enchanting.return] - name = "Ritorno di XP" - description = "L'XP di incantamento ti viene restituita quando incanti un oggetto." - lore1 = "L'esperienza spesa ha una probabilita' di essere rimborsata quando incanti un oggetto" - lore2 = "Esperienza per incantamento" - lore = ["L'esperienza spesa ha una probabilita' di essere rimborsata quando incanti un oggetto", "Esperienza per incantamento"] +name = "Ritorno di XP" +description = "L'XP di incantamento ti viene restituita quando incanti un oggetto." +lore1 = "L'esperienza spesa ha una probabilita' di essere rimborsata quando incanti un oggetto" +lore2 = "Esperienza per incantamento" +lore = ["L'esperienza spesa ha una probabilita' di essere rimborsata quando incanti un oggetto", "Esperienza per incantamento"] # excavation [excavation] [excavation.haste] - name = "Scavatore Frettoloso" - description = "Accelera il processo di scavo con la FRETTA!" - lore1 = "Ottieni Fretta durante lo scavo" - lore2 = "x livelli di Fretta quando inizi a minare QUALSIASI blocco." - lore = ["Ottieni Fretta durante lo scavo", "x livelli di Fretta quando inizi a minare QUALSIASI blocco."] +name = "Scavatore Frettoloso" +description = "Accelera il processo di scavo con la FRETTA!" +lore1 = "Ottieni Fretta durante lo scavo" +lore2 = "x livelli di Fretta quando inizi a minare QUALSIASI blocco." +lore = ["Ottieni Fretta durante lo scavo", "x livelli di Fretta quando inizi a minare QUALSIASI blocco."] [excavation.spelunker] - name = "Speleologo Super-Veggente!" - description = "Vedi i minerali con i tuoi occhi, ma attraverso il terreno!" - lore1 = "Minerale nella mano secondaria, Bacche Luminose nella mano principale, e accovacciati!" - lore2 = "Raggio in blocchi: " - lore3 = "Consuma una Bacca Luminosa ad ogni uso" - lore = ["Minerale nella mano secondaria, Bacche Luminose nella mano principale, e accovacciati!", "Raggio in blocchi: ", "Consuma una Bacca Luminosa ad ogni uso"] +name = "Speleologo Super-Veggente!" +description = "Vedi i minerali con i tuoi occhi, ma attraverso il terreno!" +lore1 = "Minerale nella mano secondaria, Bacche Luminose nella mano principale, e accovacciati!" +lore2 = "Raggio in blocchi: " +lore3 = "Consuma una Bacca Luminosa ad ogni uso" +lore = ["Minerale nella mano secondaria, Bacche Luminose nella mano principale, e accovacciati!", "Raggio in blocchi: ", "Consuma una Bacca Luminosa ad ogni uso"] [excavation.drop_to_inventory] - name = "Pala Raccogli-nell'Inventario" +name = "Pala Raccogli-nell'Inventario" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "L'opulento multiuso di design eccessivo di Tackle" - lore1 = "Probabilmente il piu' potente tra i tanti, ti permette di" - lore2 = "unire e cambiare dinamicamente gli attrezzi al volo, in base alle tue esigenze." - lore3 = "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario." - lore4 = "Per separare gli attrezzi, lascia cadere l'oggetto accovacciandoti e si smontera'." - lore5 = "Non puoi rompere gli attrezzi in questo multiuso ma non puoi usare attrezzi rotti" - lore6 = "oggetti unificabili totali." - lore7 = "Potresti usare cinque o sei attrezzi, oppure uno solo!" - lore = ["Probabilmente il piu' potente tra i tanti, ti permette di", "unire e cambiare dinamicamente gli attrezzi al volo, in base alle tue esigenze.", "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario.", "Per separare gli attrezzi, lascia cadere l'oggetto accovacciandoti e si smontera'.", "Non puoi rompere gli attrezzi in questo multiuso ma non puoi usare attrezzi rotti", "oggetti unificabili totali.", "Potresti usare cinque o sei attrezzi, oppure uno solo!"] +name = "OMNI - T.O.O.L." +description = "L'opulento multiuso di design eccessivo di Tackle" +lore1 = "Probabilmente il piu' potente tra i tanti, ti permette di" +lore2 = "unire e cambiare dinamicamente gli attrezzi al volo, in base alle tue esigenze." +lore3 = "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario." +lore4 = "Per separare gli attrezzi, lascia cadere l'oggetto accovacciandoti e si smontera'." +lore5 = "Non puoi rompere gli attrezzi in questo multiuso ma non puoi usare attrezzi rotti" +lore6 = "oggetti unificabili totali." +lore7 = "Potresti usare cinque o sei attrezzi, oppure uno solo!" +lore = ["Probabilmente il piu' potente tra i tanti, ti permette di", "unire e cambiare dinamicamente gli attrezzi al volo, in base alle tue esigenze.", "Per unire, Shift+Clic su un oggetto sopra un altro nel tuo inventario.", "Per separare gli attrezzi, lascia cadere l'oggetto accovacciandoti e si smontera'.", "Non puoi rompere gli attrezzi in questo multiuso ma non puoi usare attrezzi rotti", "oggetti unificabili totali.", "Potresti usare cinque o sei attrezzi, oppure uno solo!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Aura di Crescita" - description = "Fai crescere la natura intorno a te in un'aura" - lore1 = "Raggio in blocchi" - lore2 = "Forza dell'Aura di Crescita" - lore3 = "Costo in cibo" - lore = ["Raggio in blocchi", "Forza dell'Aura di Crescita", "Costo in cibo"] +name = "Aura di Crescita" +description = "Fai crescere la natura intorno a te in un'aura" +lore1 = "Raggio in blocchi" +lore2 = "Forza dell'Aura di Crescita" +lore3 = "Costo in cibo" +lore = ["Raggio in blocchi", "Forza dell'Aura di Crescita", "Costo in cibo"] [herbalism.hippo] - name = "Ippopotamo dell'Erborista" - description = "Consumare cibo ti da' piu' saturazione" - lore1 = "Cibo) punti di saturazione aggiuntivi al consumo" - lore = ["Cibo) punti di saturazione aggiuntivi al consumo"] +name = "Ippopotamo dell'Erborista" +description = "Consumare cibo ti da' piu' saturazione" +lore1 = "Cibo) punti di saturazione aggiuntivi al consumo" +lore = ["Cibo) punti di saturazione aggiuntivi al consumo"] [herbalism.myconid] - name = "Miconide dell'Erborista" - description = "Ti da' la capacita' di creare il Micelio" - lore1 = "Qualsiasi Terra, e un Fungo Marrone e Rosso creeranno il Micelio." - lore = ["Qualsiasi Terra, e un Fungo Marrone e Rosso creeranno il Micelio."] +name = "Miconide dell'Erborista" +description = "Ti da' la capacita' di creare il Micelio" +lore1 = "Qualsiasi Terra, e un Fungo Marrone e Rosso creeranno il Micelio." +lore = ["Qualsiasi Terra, e un Fungo Marrone e Rosso creeranno il Micelio."] [herbalism.terralid] - name = "Terralide dell'Erborista" - description = "Ti da' la capacita' di creare Blocchi d'Erba" - lore1 = "Tre semi, sopra 3 Terra, creeranno 3 Blocchi d'Erba." - lore = ["Tre semi, sopra 3 Terra, creeranno 3 Blocchi d'Erba."] +name = "Terralide dell'Erborista" +description = "Ti da' la capacita' di creare Blocchi d'Erba" +lore1 = "Tre semi, sopra 3 Terra, creeranno 3 Blocchi d'Erba." +lore = ["Tre semi, sopra 3 Terra, creeranno 3 Blocchi d'Erba."] [herbalism.cobweb] - name = "Creatore di Ragnatele" - description = "Ti da' la capacita' di creare Ragnatele in un Banco da Lavoro" - lore1 = "Nove Fili creeranno una Ragnatela." - lore = ["Nove Fili creeranno una Ragnatela."] +name = "Creatore di Ragnatele" +description = "Ti da' la capacita' di creare Ragnatele in un Banco da Lavoro" +lore1 = "Nove Fili creeranno una Ragnatela." +lore = ["Nove Fili creeranno una Ragnatela."] [herbalism.mushroom_blocks] - name = "Creatore di Funghi" - description = "Ti da' la capacita' di creare Blocchi di Fungo in un Banco da Lavoro" - lore1 = "Quattro Funghi per fare un blocco, o un blocco per fare un gambo." - lore = ["Quattro Funghi per fare un blocco, o un blocco per fare un gambo."] +name = "Creatore di Funghi" +description = "Ti da' la capacita' di creare Blocchi di Fungo in un Banco da Lavoro" +lore1 = "Quattro Funghi per fare un blocco, o un blocco per fare un gambo." +lore = ["Quattro Funghi per fare un blocco, o un blocco per fare un gambo."] [herbalism.drop_to_inventory] - name = "Zappa Raccogli-nell'Inventario" +name = "Zappa Raccogli-nell'Inventario" [herbalism.hungry_shield] - name = "Scudo della Fame" - description = "Subisci danni alla fame prima che alla salute." - lore1 = "Resistenza tramite Fame" - lore = ["Resistenza tramite Fame"] +name = "Scudo della Fame" +description = "Subisci danni alla fame prima che alla salute." +lore1 = "Resistenza tramite Fame" +lore = ["Resistenza tramite Fame"] [herbalism.luck] - name = "Fortuna dell'Erborista" - description = "Quando rompi Erba/Fiori, hai una probabilita' di ottenere un oggetto casuale" - lore0 = "Fiori = Cibo, e Erba = Semi" - lore1 = "Probabilita' di ottenere un oggetto rompendo i Fiori" - lore2 = "Probabilita' di ottenere un oggetto rompendo l'Erba" - lore = ["Fiori = Cibo, e Erba = Semi", "Probabilita' di ottenere un oggetto rompendo i Fiori", "Probabilita' di ottenere un oggetto rompendo l'Erba"] +name = "Fortuna dell'Erborista" +description = "Quando rompi Erba/Fiori, hai una probabilita' di ottenere un oggetto casuale" +lore0 = "Fiori = Cibo, e Erba = Semi" +lore1 = "Probabilita' di ottenere un oggetto rompendo i Fiori" +lore2 = "Probabilita' di ottenere un oggetto rompendo l'Erba" +lore = ["Fiori = Cibo, e Erba = Semi", "Probabilita' di ottenere un oggetto rompendo i Fiori", "Probabilita' di ottenere un oggetto rompendo l'Erba"] [herbalism.replant] - name = "Raccogli e Ripianta" - description = "Fai clic destro su una coltura con una zappa per raccogliere e ripiantare." - lore1 = "Raggio di reimpianto in blocchi" - lore = ["Raggio di reimpianto in blocchi"] +name = "Raccogli e Ripianta" +description = "Fai clic destro su una coltura con una zappa per raccogliere e ripiantare." +lore1 = "Raggio di reimpianto in blocchi" +lore = ["Raggio di reimpianto in blocchi"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalina" - description = "Infliggi piu' danni quanto piu' e' bassa la tua salute (Mischia)" - lore1 = "Danno massimo" - lore = ["Danno massimo"] +name = "Adrenalina" +description = "Infliggi piu' danni quanto piu' e' bassa la tua salute (Mischia)" +lore1 = "Danno massimo" +lore = ["Danno massimo"] [hunter.penalty] - name = "" - description = "" - lore1 = "Otterrai accumuli di Veleno se esaurisci la fame" - lore = ["Otterrai accumuli di Veleno se esaurisci la fame"] +name = "" +description = "" +lore1 = "Otterrai accumuli di Veleno se esaurisci la fame" +lore = ["Otterrai accumuli di Veleno se esaurisci la fame"] [hunter.drop_to_inventory] - name = "Oggetti Raccogli-nell'Inventario" - description = "Quando uccidi qualcosa / Rompi un blocco con una spada, i drop vengono teletrasportati nel tuo inventario" - lore1 = "Ogni volta che un oggetto viene lasciato da un mob/blocco che rompi, finisce nel tuo inventario se possibile." - lore = ["Ogni volta che un oggetto viene lasciato da un mob/blocco che rompi, finisce nel tuo inventario se possibile."] +name = "Oggetti Raccogli-nell'Inventario" +description = "Quando uccidi qualcosa / Rompi un blocco con una spada, i drop vengono teletrasportati nel tuo inventario" +lore1 = "Ogni volta che un oggetto viene lasciato da un mob/blocco che rompi, finisce nel tuo inventario se possibile." +lore = ["Ogni volta che un oggetto viene lasciato da un mob/blocco che rompi, finisce nel tuo inventario se possibile."] [hunter.invisibility] - name = "Passo Evanescente" - description = "Quando vieni colpito ottieni invisibilita', al costo della fame" - lore1 = "Ottieni invisibilita' passiva quando vieni colpito" - lore2 = "x accumuli di Invisibilita' per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Durata dell'invisibilita'" - lore = ["Ottieni invisibilita' passiva quando vieni colpito", "x accumuli di Invisibilita' per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Durata dell'invisibilita'"] +name = "Passo Evanescente" +description = "Quando vieni colpito ottieni invisibilita', al costo della fame" +lore1 = "Ottieni invisibilita' passiva quando vieni colpito" +lore2 = "x accumuli di Invisibilita' per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Durata dell'invisibilita'" +lore = ["Ottieni invisibilita' passiva quando vieni colpito", "x accumuli di Invisibilita' per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Durata dell'invisibilita'"] [hunter.jump_boost] - name = "Altezze del Cacciatore" - description = "Quando vieni colpito ottieni potenziamento del salto, al costo della fame" - lore1 = "Ottieni potenziamento del salto passivo quando vieni colpito" - lore2 = "x accumuli di Potenziamento Salto per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Potenziamento Salto, non durata." - lore = ["Ottieni potenziamento del salto passivo quando vieni colpito", "x accumuli di Potenziamento Salto per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Potenziamento Salto, non durata."] +name = "Altezze del Cacciatore" +description = "Quando vieni colpito ottieni potenziamento del salto, al costo della fame" +lore1 = "Ottieni potenziamento del salto passivo quando vieni colpito" +lore2 = "x accumuli di Potenziamento Salto per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Potenziamento Salto, non durata." +lore = ["Ottieni potenziamento del salto passivo quando vieni colpito", "x accumuli di Potenziamento Salto per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Potenziamento Salto, non durata."] [hunter.luck] - name = "Fortuna del Cacciatore" - description = "Quando vieni colpito ottieni fortuna, al costo della fame" - lore1 = "Ottieni fortuna passiva quando vieni colpito" - lore2 = "x accumuli di Fortuna per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Fortuna, non durata." - lore = ["Ottieni fortuna passiva quando vieni colpito", "x accumuli di Fortuna per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Fortuna, non durata."] +name = "Fortuna del Cacciatore" +description = "Quando vieni colpito ottieni fortuna, al costo della fame" +lore1 = "Ottieni fortuna passiva quando vieni colpito" +lore2 = "x accumuli di Fortuna per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Fortuna, non durata." +lore = ["Ottieni fortuna passiva quando vieni colpito", "x accumuli di Fortuna per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Fortuna, non durata."] [hunter.regen] - name = "Rigenerazione del Cacciatore" - description = "Quando vieni colpito ottieni rigenerazione, al costo della fame" - lore1 = "Ottieni rigenerazione passiva quando vieni colpito" - lore2 = "x accumuli di Rigenerazione per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Rigenerazione, non durata." - lore = ["Ottieni rigenerazione passiva quando vieni colpito", "x accumuli di Rigenerazione per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Rigenerazione, non durata."] +name = "Rigenerazione del Cacciatore" +description = "Quando vieni colpito ottieni rigenerazione, al costo della fame" +lore1 = "Ottieni rigenerazione passiva quando vieni colpito" +lore2 = "x accumuli di Rigenerazione per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Rigenerazione, non durata." +lore = ["Ottieni rigenerazione passiva quando vieni colpito", "x accumuli di Rigenerazione per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Rigenerazione, non durata."] [hunter.resistance] - name = "Resistenza del Cacciatore" - description = "Quando vieni colpito ottieni resistenza, al costo della fame" - lore1 = "Ottieni resistenza passiva quando vieni colpito" - lore2 = "x accumuli di Resistenza per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Resistenza, non durata." - lore = ["Ottieni resistenza passiva quando vieni colpito", "x accumuli di Resistenza per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Resistenza, non durata."] +name = "Resistenza del Cacciatore" +description = "Quando vieni colpito ottieni resistenza, al costo della fame" +lore1 = "Ottieni resistenza passiva quando vieni colpito" +lore2 = "x accumuli di Resistenza per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Resistenza, non durata." +lore = ["Ottieni resistenza passiva quando vieni colpito", "x accumuli di Resistenza per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Resistenza, non durata."] [hunter.speed] - name = "Velocita' del Cacciatore" - description = "Quando vieni colpito ottieni velocita', al costo della fame" - lore1 = "Ottieni velocita' passiva quando vieni colpito" - lore2 = "x accumuli di Velocita' per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Velocita', non durata." - lore = ["Ottieni velocita' passiva quando vieni colpito", "x accumuli di Velocita' per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Velocita', non durata."] +name = "Velocita' del Cacciatore" +description = "Quando vieni colpito ottieni velocita', al costo della fame" +lore1 = "Ottieni velocita' passiva quando vieni colpito" +lore2 = "x accumuli di Velocita' per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Velocita', non durata." +lore = ["Ottieni velocita' passiva quando vieni colpito", "x accumuli di Velocita' per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Velocita', non durata."] [hunter.strength] - name = "Forza del Cacciatore" - description = "Quando vieni colpito ottieni forza, al costo della fame" - lore1 = "Ottieni forza passiva quando vieni colpito" - lore2 = "x accumuli di Forza per 3 secondi al colpo" - lore3 = "x Fame cumulativa" - lore4 = "Durata e moltiplicatore degli accumuli di fame." - lore5 = "Moltiplicatore degli accumuli di Forza, non durata." - lore = ["Ottieni forza passiva quando vieni colpito", "x accumuli di Forza per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Forza, non durata."] +name = "Forza del Cacciatore" +description = "Quando vieni colpito ottieni forza, al costo della fame" +lore1 = "Ottieni forza passiva quando vieni colpito" +lore2 = "x accumuli di Forza per 3 secondi al colpo" +lore3 = "x Fame cumulativa" +lore4 = "Durata e moltiplicatore degli accumuli di fame." +lore5 = "Moltiplicatore degli accumuli di Forza, non durata." +lore = ["Ottieni forza passiva quando vieni colpito", "x accumuli di Forza per 3 secondi al colpo", "x Fame cumulativa", "Durata e moltiplicatore degli accumuli di fame.", "Moltiplicatore degli accumuli di Forza, non durata."] # nether [nether] [nether.skull_toss] - name = "Lancio del Teschio di Wither" - description1 = "Scatena il tuo Wither interiore usando la" - description2 = "testa" - description3 = "di qualcuno." - lore1 = "Secondi di recupero tra un lancio e l'altro." - lore2 = "Usando un Teschio di Wither: Lancia un " - lore3 = "Teschio di Wither" - lore4 = "che esplode all'impatto." - lore = ["Secondi di recupero tra un lancio e l'altro.", "Usando un Teschio di Wither: Lancia un ", "Teschio di Wither", "che esplode all'impatto."] +name = "Lancio del Teschio di Wither" +description1 = "Scatena il tuo Wither interiore usando la" +description2 = "testa" +description3 = "di qualcuno." +lore1 = "Secondi di recupero tra un lancio e l'altro." +lore2 = "Usando un Teschio di Wither: Lancia un " +lore3 = "Teschio di Wither" +lore4 = "che esplode all'impatto." +lore = ["Secondi di recupero tra un lancio e l'altro.", "Usando un Teschio di Wither: Lancia un ", "Teschio di Wither", "che esplode all'impatto."] [nether.wither_resist] - name = "Resistenza all'Avvizzimento" - description = "Resisti all'avvizzimento grazie al potere della Netherite." - lore1 = "probabilita' di annullare l'avvizzimento (per pezzo)." - lore2 = "Passiva: Indossare armatura di Netherite ha una probabilita' di annullare " - lore3 = "l'avvizzimento." - lore = ["probabilita' di annullare l'avvizzimento (per pezzo).", "Passiva: Indossare armatura di Netherite ha una probabilita' di annullare ", "l'avvizzimento."] +name = "Resistenza all'Avvizzimento" +description = "Resisti all'avvizzimento grazie al potere della Netherite." +lore1 = "probabilita' di annullare l'avvizzimento (per pezzo)." +lore2 = "Passiva: Indossare armatura di Netherite ha una probabilita' di annullare " +lore3 = "l'avvizzimento." +lore = ["probabilita' di annullare l'avvizzimento (per pezzo).", "Passiva: Indossare armatura di Netherite ha una probabilita' di annullare ", "l'avvizzimento."] [nether.fire_resist] - name = "Resistenza al Fuoco" - description = "Resisti al fuoco indurendo la tua pelle." - lore1 = "probabilita' di annullare l'effetto di bruciatura!" - lore = ["probabilita' di annullare l'effetto di bruciatura!"] +name = "Resistenza al Fuoco" +description = "Resisti al fuoco indurendo la tua pelle." +lore1 = "probabilita' di annullare l'effetto di bruciatura!" +lore = ["probabilita' di annullare l'effetto di bruciatura!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Fusione Automatica" - description = "Ti permette di fondere automaticamente i minerali Vanilla estratti" - lore1 = "I minerali che possono essere fusi vengono fusi automaticamente" - lore2 = "% di probabilita' per un extra" - lore = ["I minerali che possono essere fusi vengono fusi automaticamente", "% di probabilita' per un extra"] +name = "Fusione Automatica" +description = "Ti permette di fondere automaticamente i minerali Vanilla estratti" +lore1 = "I minerali che possono essere fusi vengono fusi automaticamente" +lore2 = "% di probabilita' per un extra" +lore = ["I minerali che possono essere fusi vengono fusi automaticamente", "% di probabilita' per un extra"] [pickaxe.chisel] - name = "Scalpello per Minerali" - description = "Fai clic destro sui minerali per scalpellarli ed estrarne di piu', a un grave costo di durabilita'." - lore1 = "Probabilita' di drop" - lore2 = "Usura dell'attrezzo" - lore = ["Probabilita' di drop", "Usura dell'attrezzo"] +name = "Scalpello per Minerali" +description = "Fai clic destro sui minerali per scalpellarli ed estrarne di piu', a un grave costo di durabilita'." +lore1 = "Probabilita' di drop" +lore2 = "Usura dell'attrezzo" +lore = ["Probabilita' di drop", "Usura dell'attrezzo"] [pickaxe.drop_to_inventory] - name = "Piccone Raccogli-nell'Inventario" - description = "Quando rompi un blocco, l'oggetto viene teletrasportato nel tuo inventario" - lore1 = "Ogni volta che un oggetto viene lasciato da un blocco che rompi, finisce nel tuo inventario se possibile." - lore = ["Ogni volta che un oggetto viene lasciato da un blocco che rompi, finisce nel tuo inventario se possibile."] +name = "Piccone Raccogli-nell'Inventario" +description = "Quando rompi un blocco, l'oggetto viene teletrasportato nel tuo inventario" +lore1 = "Ogni volta che un oggetto viene lasciato da un blocco che rompi, finisce nel tuo inventario se possibile." +lore = ["Ogni volta che un oggetto viene lasciato da un blocco che rompi, finisce nel tuo inventario se possibile."] [pickaxe.silk_spawner] - name = "Piccone Tocco di Velluto Spawner" - description = "Fa cadere gli Spawner quando vengono rotti" - lore1 = "Rende gli Spawner rompibili con Tocco di Velluto." - lore2 = "Rende gli Spawner rompibili mentre sei accovacciato." - lore = ["Rende gli Spawner rompibili con Tocco di Velluto.", "Rende gli Spawner rompibili mentre sei accovacciato."] +name = "Piccone Tocco di Velluto Spawner" +description = "Fa cadere gli Spawner quando vengono rotti" +lore1 = "Rende gli Spawner rompibili con Tocco di Velluto." +lore2 = "Rende gli Spawner rompibili mentre sei accovacciato." +lore = ["Rende gli Spawner rompibili con Tocco di Velluto.", "Rende gli Spawner rompibili mentre sei accovacciato."] [pickaxe.vein_miner] - name = "Minatore di Vene" - description = "Ti permette di rompere blocchi in una vena/gruppo di minerali Vanilla" - lore1 = "Accovacciati e mina i MINERALI" - lore2 = "raggio di estrazione a vena" - lore3 = "Questa abilita' NON raggruppa tutti i drop!" - lore = ["Accovacciati e mina i MINERALI", "raggio di estrazione a vena", "Questa abilita' NON raggruppa tutti i drop!"] +name = "Minatore di Vene" +description = "Ti permette di rompere blocchi in una vena/gruppo di minerali Vanilla" +lore1 = "Accovacciati e mina i MINERALI" +lore2 = "raggio di estrazione a vena" +lore3 = "Questa abilita' NON raggruppa tutti i drop!" +lore = ["Accovacciati e mina i MINERALI", "raggio di estrazione a vena", "Questa abilita' NON raggruppa tutti i drop!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Recupero Frecce" - description = "Recupera le frecce dopo aver ucciso un nemico." - lore1 = "Probabilita' di recuperare le frecce al colpo/uccisione" - lore2 = "Probabilita': " - lore = ["Probabilita' di recuperare le frecce al colpo/uccisione", "Probabilita': "] +name = "Recupero Frecce" +description = "Recupera le frecce dopo aver ucciso un nemico." +lore1 = "Probabilita' di recuperare le frecce al colpo/uccisione" +lore2 = "Probabilita': " +lore = ["Probabilita' di recuperare le frecce al colpo/uccisione", "Probabilita': "] [ranged.web_shot] - name = "Trappola di Ragnatele" - description = "Avvolgi ragnatele attorno al bersaglio quando lo colpisci!" - lore1 = "8 Ragnatele attorno a una Palla di Neve, e lancia!" - lore2 = "secondi di gabbia, circa." - lore = ["8 Ragnatele attorno a una Palla di Neve, e lancia!", "secondi di gabbia, circa."] +name = "Trappola di Ragnatele" +description = "Avvolgi ragnatele attorno al bersaglio quando lo colpisci!" +lore1 = "8 Ragnatele attorno a una Palla di Neve, e lancia!" +lore2 = "secondi di gabbia, circa." +lore = ["8 Ragnatele attorno a una Palla di Neve, e lancia!", "secondi di gabbia, circa."] [ranged.force_shot] - name = "Colpo di Forza" - description = "Spara proiettili piu' lontano, piu' velocemente!" - advancementname = "Tiro Lungo" - advancementlore = "Colpisci un bersaglio a oltre 30 blocchi di distanza!" - lore1 = "Velocita' del proiettile" - lore = ["Velocita' del proiettile"] +name = "Colpo di Forza" +description = "Spara proiettili piu' lontano, piu' velocemente!" +advancementname = "Tiro Lungo" +advancementlore = "Colpisci un bersaglio a oltre 30 blocchi di distanza!" +lore1 = "Velocita' del proiettile" +lore = ["Velocita' del proiettile"] [ranged.lunge_shot] - name = "Colpo d'Affondo" - description = "Mentre cadi, le tue frecce ti lanciano in una direzione casuale" - lore1 = "Velocita' di spinta casuale" - lore = ["Velocita' di spinta casuale"] +name = "Colpo d'Affondo" +description = "Mentre cadi, le tue frecce ti lanciano in una direzione casuale" +lore1 = "Velocita' di spinta casuale" +lore = ["Velocita' di spinta casuale"] [ranged.arrow_piercing] - name = "Freccia Perforante" - description = "Aggiunge Perforazione ai proiettili! Spara attraverso le cose!" - lore1 = "Bersagli perforati" - lore = ["Bersagli perforati"] +name = "Freccia Perforante" +description = "Aggiunge Perforazione ai proiettili! Spara attraverso le cose!" +lore1 = "Bersagli perforati" +lore = ["Bersagli perforati"] # rift [rift] [rift.remote_access] - name = "Accesso Remoto" - description = "Pesca dal vuoto e accedi a un contenitore contrassegnato." - lore1 = "Perla di Ender + Bussola = Portachiave del Reliquiario" - lore2 = "Questo oggetto ti permette di accedere ai contenitori a distanza" - lore3 = "Una volta creato, guarda l'oggetto per vederne l'utilizzo" - notcontainer = "Quello non e' un contenitore" - lore = ["Perla di Ender + Bussola = Portachiave del Reliquiario", "Questo oggetto ti permette di accedere ai contenitori a distanza", "Una volta creato, guarda l'oggetto per vederne l'utilizzo"] +name = "Accesso Remoto" +description = "Pesca dal vuoto e accedi a un contenitore contrassegnato." +lore1 = "Perla di Ender + Bussola = Portachiave del Reliquiario" +lore2 = "Questo oggetto ti permette di accedere ai contenitori a distanza" +lore3 = "Una volta creato, guarda l'oggetto per vederne l'utilizzo" +notcontainer = "Quello non e' un contenitore" +lore = ["Perla di Ender + Bussola = Portachiave del Reliquiario", "Questo oggetto ti permette di accedere ai contenitori a distanza", "Una volta creato, guarda l'oggetto per vederne l'utilizzo"] [rift.blink] - name = "Lampo del Varco" - description = "Teletrasporto istantaneo a corto raggio, a solo un battito di ciglia!" - lore1 = "Blocchi per lampo (2x verticale)" - lore2 = "Mentre scatti: Premi Salto due volte per " - lore3 = "Lampeggiare" - lore = ["Blocchi per lampo (2x verticale)", "Mentre scatti: Premi Salto due volte per ", "Lampeggiare"] +name = "Lampo del Varco" +description = "Teletrasporto istantaneo a corto raggio, a solo un battito di ciglia!" +lore1 = "Blocchi per lampo (2x verticale)" +lore2 = "Mentre scatti: Premi Salto due volte per " +lore3 = "Lampeggiare" +lore = ["Blocchi per lampo (2x verticale)", "Mentre scatti: Premi Salto due volte per ", "Lampeggiare"] [rift.chest] - name = "Baule dell'End Facile" - description = "Apri un Baule dell'End facendo clic sinistro tenendolo in mano." - lore1 = "Clicca un Baule dell'End nella tua mano per aprirlo (non piazzarlo)" - lore = ["Clicca un Baule dell'End nella tua mano per aprirlo (non piazzarlo)"] +name = "Baule dell'End Facile" +description = "Apri un Baule dell'End facendo clic sinistro tenendolo in mano." +lore1 = "Clicca un Baule dell'End nella tua mano per aprirlo (non piazzarlo)" +lore = ["Clicca un Baule dell'End nella tua mano per aprirlo (non piazzarlo)"] [rift.descent] - name = "Anti-Levitazione" - description = "Sei stanco di restare bloccato in aria? Questa e' l'abilita' che fa per te!" - lore1 = "Accovacciati per scendere, e cadrai a una velocita' inferiore al normale!" - lore2 = "Tempo di recupero:" - lore = ["Accovacciati per scendere, e cadrai a una velocita' inferiore al normale!", "Tempo di recupero:"] +name = "Anti-Levitazione" +description = "Sei stanco di restare bloccato in aria? Questa e' l'abilita' che fa per te!" +lore1 = "Accovacciati per scendere, e cadrai a una velocita' inferiore al normale!" +lore2 = "Tempo di recupero:" +lore = ["Accovacciati per scendere, e cadrai a una velocita' inferiore al normale!", "Tempo di recupero:"] [rift.gate] - name = "Portale del Varco" - description = "Teletrasportati a un luogo contrassegnato." - lore1 = "CREAZIONE: Smeraldo + Frammento di Ametista + Perla di Ender" - lore2 = "Leggi prima di usare!" - lore3 = "Ritardo di 5 secondi, " - lore4 = "puoi morire durante questa animazione" - lore = ["CREAZIONE: Smeraldo + Frammento di Ametista + Perla di Ender", "Leggi prima di usare!", "Ritardo di 5 secondi, ", "puoi morire durante questa animazione"] +name = "Portale del Varco" +description = "Teletrasportati a un luogo contrassegnato." +lore1 = "CREAZIONE: Smeraldo + Frammento di Ametista + Perla di Ender" +lore2 = "Leggi prima di usare!" +lore3 = "Ritardo di 5 secondi, " +lore4 = "puoi morire durante questa animazione" +lore = ["CREAZIONE: Smeraldo + Frammento di Ametista + Perla di Ender", "Leggi prima di usare!", "Ritardo di 5 secondi, ", "puoi morire durante questa animazione"] [rift.resist] - name = "Resistenza del Varco" - description = "Ottieni Resistenza quando usi oggetti e abilita' dell'Ender" - lore1 = "+ Passiva: Fornisce resistenza quando usi abilita' del Varco o oggetti dell'Ender" - lore2 = "NON include il Baule dell'End portatile, solo cose che puoi consumare" - lore = ["+ Passiva: Fornisce resistenza quando usi abilita' del Varco o oggetti dell'Ender", "NON include il Baule dell'End portatile, solo cose che puoi consumare"] +name = "Resistenza del Varco" +description = "Ottieni Resistenza quando usi oggetti e abilita' dell'Ender" +lore1 = "+ Passiva: Fornisce resistenza quando usi abilita' del Varco o oggetti dell'Ender" +lore2 = "NON include il Baule dell'End portatile, solo cose che puoi consumare" +lore = ["+ Passiva: Fornisce resistenza quando usi abilita' del Varco o oggetti dell'Ender", "NON include il Baule dell'End portatile, solo cose che puoi consumare"] [rift.visage] - name = "Sembianza del Varco" - description = "Impedisce agli Enderman di diventare aggressivi se hai Perle di Ender nel tuo inventario." - lore1 = "Gli Enderman non diventeranno aggressivi se hai Perle di Ender nel tuo inventario." - lore = ["Gli Enderman non diventeranno aggressivi se hai Perle di Ender nel tuo inventario."] +name = "Sembianza del Varco" +description = "Impedisce agli Enderman di diventare aggressivi se hai Perle di Ender nel tuo inventario." +lore1 = "Gli Enderman non diventeranno aggressivi se hai Perle di Ender nel tuo inventario." +lore = ["Gli Enderman non diventeranno aggressivi se hai Perle di Ender nel tuo inventario."] # seaborn [seaborn] [seaborn.oxygen] - name = "Bombola d'Ossigeno Organica" - description = "Trattieni piu' ossigeno nei tuoi piccoli polmoni!" - lore1 = "Aumento della capacita' di ossigeno" - lore = ["Aumento della capacita' di ossigeno"] +name = "Bombola d'Ossigeno Organica" +description = "Trattieni piu' ossigeno nei tuoi piccoli polmoni!" +lore1 = "Aumento della capacita' di ossigeno" +lore = ["Aumento della capacita' di ossigeno"] [seaborn.fishers_fantasy] - name = "Fantasia del Pescatore" - description = "Guadagna piu' XP dalla pesca e ottieni piu' pesci!" - lore1 = "Per ogni livello c'e' una probabilita' di ottenere piu' XP e Pesci!" - lore = ["Per ogni livello c'e' una probabilita' di ottenere piu' XP e Pesci!"] +name = "Fantasia del Pescatore" +description = "Guadagna piu' XP dalla pesca e ottieni piu' pesci!" +lore1 = "Per ogni livello c'e' una probabilita' di ottenere piu' XP e Pesci!" +lore = ["Per ogni livello c'e' una probabilita' di ottenere piu' XP e Pesci!"] [seaborn.haste] - name = "Minatore Tartaruga" - description = "Mentre mini sott'acqua ottieni Fretta!" - lore1 = "Fretta 3 viene applicata sott'acqua durante l'estrazione (si accumula con Affinita' Acquatica) dopo che l'effetto di respirazione acquatica svanisce!" - lore = ["Fretta 3 viene applicata sott'acqua durante l'estrazione (si accumula con Affinita' Acquatica) dopo che l'effetto di respirazione acquatica svanisce!"] +name = "Minatore Tartaruga" +description = "Mentre mini sott'acqua ottieni Fretta!" +lore1 = "Fretta 3 viene applicata sott'acqua durante l'estrazione (si accumula con Affinita' Acquatica) dopo che l'effetto di respirazione acquatica svanisce!" +lore = ["Fretta 3 viene applicata sott'acqua durante l'estrazione (si accumula con Affinita' Acquatica) dopo che l'effetto di respirazione acquatica svanisce!"] [seaborn.night_vision] - name = "Visione della Tartaruga" - description = "Mentre sei sott'acqua, ottieni Visione Notturna" - lore1 = "Ottieni semplicemente Visione Notturna mentre sei sott'acqua dopo che l'effetto di respirazione acquatica svanisce!" - lore = ["Ottieni semplicemente Visione Notturna mentre sei sott'acqua dopo che l'effetto di respirazione acquatica svanisce!"] +name = "Visione della Tartaruga" +description = "Mentre sei sott'acqua, ottieni Visione Notturna" +lore1 = "Ottieni semplicemente Visione Notturna mentre sei sott'acqua dopo che l'effetto di respirazione acquatica svanisce!" +lore = ["Ottieni semplicemente Visione Notturna mentre sei sott'acqua dopo che l'effetto di respirazione acquatica svanisce!"] [seaborn.dolphin_grace] - name = "Grazia del Delfino" - description = "Nuota come un delfino, senza i delfini" - lore1 = "+ Passiva: ottieni " - lore2 = "x velocita' (grazia del delfino)" - lore3 = "ingegneria tedesca di precisi- aspetta, non e' giusto... Non compatibile con Passo del Fondale" - lore = ["+ Passiva: ottieni ", "x velocita' (grazia del delfino)", "ingegneria tedesca di precisi- aspetta, non e' giusto... Non compatibile con Passo del Fondale"] +name = "Grazia del Delfino" +description = "Nuota come un delfino, senza i delfini" +lore1 = "+ Passiva: ottieni " +lore2 = "x velocita' (grazia del delfino)" +lore3 = "ingegneria tedesca di precisi- aspetta, non e' giusto... Non compatibile con Passo del Fondale" +lore = ["+ Passiva: ottieni ", "x velocita' (grazia del delfino)", "ingegneria tedesca di precisi- aspetta, non e' giusto... Non compatibile con Passo del Fondale"] # stealth [stealth] [stealth.ghost_armor] - name = "Armatura Fantasma" - description = "Armatura ad accumulo lento quando non subisci danni, dura per 1 colpo" - lore1 = "Armatura massima" - lore2 = "Velocita'" - lore = ["Armatura massima", "Velocita'"] +name = "Armatura Fantasma" +description = "Armatura ad accumulo lento quando non subisci danni, dura per 1 colpo" +lore1 = "Armatura massima" +lore2 = "Velocita'" +lore = ["Armatura massima", "Velocita'"] [stealth.night_vision] - name = "Visione Furtiva" - description = "Ottieni visione notturna mentre sei accovacciato" - lore1 = "Ottieni un'ondata di " - lore2 = "visione notturna" - lore3 = "mentre sei accovacciato" - lore = ["Ottieni un'ondata di ", "visione notturna", "mentre sei accovacciato"] +name = "Visione Furtiva" +description = "Ottieni visione notturna mentre sei accovacciato" +lore1 = "Ottieni un'ondata di " +lore2 = "visione notturna" +lore3 = "mentre sei accovacciato" +lore = ["Ottieni un'ondata di ", "visione notturna", "mentre sei accovacciato"] [stealth.snatch] - name = "Arraffa Oggetti" - description = "Arraffa istantaneamente gli oggetti caduti mentre sei accovacciato!" - lore1 = "Raggio di raccolta" - lore = ["Raggio di raccolta"] +name = "Arraffa Oggetti" +description = "Arraffa istantaneamente gli oggetti caduti mentre sei accovacciato!" +lore1 = "Raggio di raccolta" +lore = ["Raggio di raccolta"] [stealth.speed] - name = "Velocita' Furtiva" - description = "Ottieni velocita' mentre sei accovacciato" - lore1 = "Velocita' furtiva" - lore = ["Velocita' furtiva"] +name = "Velocita' Furtiva" +description = "Ottieni velocita' mentre sei accovacciato" +lore1 = "Velocita' furtiva" +lore = ["Velocita' furtiva"] [stealth.ender_veil] - name = "Velo dell'Ender" - description = "Niente piu' zucche per prevenire gli attacchi degli Enderman" - lore1 = "Previeni gli attacchi degli Enderman mentre sei accovacciato" - lore2 = "Previeni tutti gli attacchi degli Enderman" - lore = ["Previeni gli attacchi degli Enderman mentre sei accovacciato", "Previeni tutti gli attacchi degli Enderman"] +name = "Velo dell'Ender" +description = "Niente piu' zucche per prevenire gli attacchi degli Enderman" +lore1 = "Previeni gli attacchi degli Enderman mentre sei accovacciato" +lore2 = "Previeni tutti gli attacchi degli Enderman" +lore = ["Previeni gli attacchi degli Enderman mentre sei accovacciato", "Previeni tutti gli attacchi degli Enderman"] # sword [sword] [sword.machete] - name = "Machete" - description = "Taglia il fogliame con facilita'!" - lore1 = "Raggio del fendente" - lore2 = "Tempo di recupero del taglio" - lore3 = "Usura dell'attrezzo" - lore = ["Raggio del fendente", "Tempo di recupero del taglio", "Usura dell'attrezzo"] +name = "Machete" +description = "Taglia il fogliame con facilita'!" +lore1 = "Raggio del fendente" +lore2 = "Tempo di recupero del taglio" +lore3 = "Usura dell'attrezzo" +lore = ["Raggio del fendente", "Tempo di recupero del taglio", "Usura dell'attrezzo"] [sword.bloody_blade] - name = "Lama Insanguinata" - description = "I colpi con la tua spada causano Sanguinamento!" - lore1 = "Colpire un'entita' vivente con la tua spada causa Sanguinamento" - lore2 = "Durata del sanguinamento" - lore3 = "Tempo di recupero del sanguinamento" - lore = ["Colpire un'entita' vivente con la tua spada causa Sanguinamento", "Durata del sanguinamento", "Tempo di recupero del sanguinamento"] +name = "Lama Insanguinata" +description = "I colpi con la tua spada causano Sanguinamento!" +lore1 = "Colpire un'entita' vivente con la tua spada causa Sanguinamento" +lore2 = "Durata del sanguinamento" +lore3 = "Tempo di recupero del sanguinamento" +lore = ["Colpire un'entita' vivente con la tua spada causa Sanguinamento", "Durata del sanguinamento", "Tempo di recupero del sanguinamento"] [sword.poisoned_blade] - name = "Lama Avvelenata" - description = "I colpi con la tua spada causano Veleno!" - lore1 = "Colpire un'entita' vivente con la tua spada causa Veleno" - lore2 = "Durata del veleno" - lore3 = "Tempo di recupero del veleno" - lore = ["Colpire un'entita' vivente con la tua spada causa Veleno", "Durata del veleno", "Tempo di recupero del veleno"] +name = "Lama Avvelenata" +description = "I colpi con la tua spada causano Veleno!" +lore1 = "Colpire un'entita' vivente con la tua spada causa Veleno" +lore2 = "Durata del veleno" +lore3 = "Tempo di recupero del veleno" +lore = ["Colpire un'entita' vivente con la tua spada causa Veleno", "Durata del veleno", "Tempo di recupero del veleno"] # taming [taming] [taming.damage] - name = "Danno dell'Addomesticato" - description = "Aumenta i danni inflitti dal tuo animale addomesticato." - lore1 = "Danno aumentato" - lore = ["Danno aumentato"] +name = "Danno dell'Addomesticato" +description = "Aumenta i danni inflitti dal tuo animale addomesticato." +lore1 = "Danno aumentato" +lore = ["Danno aumentato"] [taming.health] - name = "Salute dell'Addomesticato" - description = "Aumenta la salute del tuo animale addomesticato." - lore1 = "Salute aumentata" - lore = ["Salute aumentata"] +name = "Salute dell'Addomesticato" +description = "Aumenta la salute del tuo animale addomesticato." +lore1 = "Salute aumentata" +lore = ["Salute aumentata"] [taming.regeneration] - name = "Rigenerazione dell'Addomesticato" - description = "Aumenta la rigenerazione del tuo animale addomesticato." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Rigenerazione dell'Addomesticato" +description = "Aumenta la rigenerazione del tuo animale addomesticato." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Spine" - description = "Rifletti il danno sul tuo aggressore!" - lore1 = "Danno restituito quando vieni colpito" - lore = ["Danno restituito quando vieni colpito"] +name = "Spine" +description = "Rifletti il danno sul tuo aggressore!" +lore1 = "Danno restituito quando vieni colpito" +lore = ["Danno restituito quando vieni colpito"] [tragoul.globe] - name = "Globo del Dolore" - description = "Dividi il danno che infliggi in base al numero di nemici intorno a te!" - lore1 = "Piu' nemici ti circondano, meno danni infliggi a ciascuno di loro" - lore2 = "Raggio: " - lore3 = "Danno aggiuntivo a tutte le entita': " - lore = ["Piu' nemici ti circondano, meno danni infliggi a ciascuno di loro", "Raggio: ", "Danno aggiuntivo a tutte le entita': "] +name = "Globo del Dolore" +description = "Dividi il danno che infliggi in base al numero di nemici intorno a te!" +lore1 = "Piu' nemici ti circondano, meno danni infliggi a ciascuno di loro" +lore2 = "Raggio: " +lore3 = "Danno aggiuntivo a tutte le entita': " +lore = ["Piu' nemici ti circondano, meno danni infliggi a ciascuno di loro", "Raggio: ", "Danno aggiuntivo a tutte le entita': "] [tragoul.healing] - name = "Volonta' del Dolore" - description = "Recupera salute in base al danno che infliggi!" - lore1 = "Fare del male non e' mai stato cosi' piacevole! Guarisci dal danno inflitto" - lore2 = "C'e' una finestra di danno di 3 secondi per la guarigione e un tempo di recupero di 1 secondo " - lore3 = "Guarigione per percentuale di danno: " - lore = ["Fare del male non e' mai stato cosi' piacevole! Guarisci dal danno inflitto", "C'e' una finestra di danno di 3 secondi per la guarigione e un tempo di recupero di 1 secondo ", "Guarigione per percentuale di danno: "] +name = "Volonta' del Dolore" +description = "Recupera salute in base al danno che infliggi!" +lore1 = "Fare del male non e' mai stato cosi' piacevole! Guarisci dal danno inflitto" +lore2 = "C'e' una finestra di danno di 3 secondi per la guarigione e un tempo di recupero di 1 secondo " +lore3 = "Guarigione per percentuale di danno: " +lore = ["Fare del male non e' mai stato cosi' piacevole! Guarisci dal danno inflitto", "C'e' una finestra di danno di 3 secondi per la guarigione e un tempo di recupero di 1 secondo ", "Guarigione per percentuale di danno: "] [tragoul.lance] - name = "Lance di Cadavere" - description = "Uccidere un nemico o avere un'abilita' che uccide un nemico genera una lancia che infligge danni a un nemico vicino!" - lore1 = "Le Lance cercheranno da qualsiasi cosa tu uccida, E se questa abilita' uccide un nemico." - lore2 = "Sacrifica una parte della tua vita per creare le lance (puo' ucciderti)" - lore3 = "Lance massime: 1 + " - lore = ["Le Lance cercheranno da qualsiasi cosa tu uccida, E se questa abilita' uccide un nemico.", "Sacrifica una parte della tua vita per creare le lance (puo' ucciderti)", "Lance massime: 1 + "] +name = "Lance di Cadavere" +description = "Uccidere un nemico o avere un'abilita' che uccide un nemico genera una lancia che infligge danni a un nemico vicino!" +lore1 = "Le Lance cercheranno da qualsiasi cosa tu uccida, E se questa abilita' uccide un nemico." +lore2 = "Sacrifica una parte della tua vita per creare le lance (puo' ucciderti)" +lore3 = "Lance massime: 1 + " +lore = ["Le Lance cercheranno da qualsiasi cosa tu uccida, E se questa abilita' uccide un nemico.", "Sacrifica una parte della tua vita per creare le lance (puo' ucciderti)", "Lance massime: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Cannone di Vetro" - description = "Danno disarmato bonus quanto piu' basso e' il tuo valore di armatura" - lore1 = "x Danno a 0 armatura" - lore2 = "Danno bonus per livello" - lore = ["x Danno a 0 armatura", "Danno bonus per livello"] +name = "Cannone di Vetro" +description = "Danno disarmato bonus quanto piu' basso e' il tuo valore di armatura" +lore1 = "x Danno a 0 armatura" +lore2 = "Danno bonus per livello" +lore = ["x Danno a 0 armatura", "Danno bonus per livello"] [unarmed.power] - name = "Potenza Disarmata" - description = "Danno disarmato migliorato" - lore1 = "Danno" - lore = ["Danno"] +name = "Potenza Disarmata" +description = "Danno disarmato migliorato" +lore1 = "Danno" +lore = ["Danno"] [unarmed.sucker_punch] - name = "Pugno a Tradimento" - description = "Pugni in corsa, ma piu' letali." - lore1 = "Danno" - lore2 = "Il danno aumenta con la tua velocita' mentre colpisci" - lore = ["Danno", "Il danno aumenta con la tua velocita' mentre colpisci"] +name = "Pugno a Tradimento" +description = "Pugni in corsa, ma piu' letali." +lore1 = "Danno" +lore2 = "Il danno aumenta con la tua velocita' mentre colpisci" +lore = ["Danno", "Il danno aumenta con la tua velocita' mentre colpisci"] diff --git a/src/main/resources/ja-JP.toml b/src/main/resources/ja-JP.toml index b54639185..0d456f14e 100644 --- a/src/main/resources/ja-JP.toml +++ b/src/main/resources/ja-JP.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "動かなきゃ!" - description = "1キロメートル(1,000ブロック)以上歩く" +title = "動かなきゃ!" +description = "1キロメートル(1,000ブロック)以上歩く" [advancement.challenge_sprint_5k] - title = "5Kを全力疾走!" - description = "5キロメートル(5,000ブロック)以上歩く" +title = "5Kを全力疾走!" +description = "5キロメートル(5,000ブロック)以上歩く" [advancement.challenge_sprint_50k] - title = "50Kをズーム!" - description = "50キロメートル(50,000ブロック)以上歩く" +title = "50Kをズーム!" +description = "50キロメートル(50,000ブロック)以上歩く" [advancement.challenge_sprint_500k] - title = "宇宙を横断せよ!!" - description = "500キロメートル(500,000ブロック)以上歩く" +title = "宇宙を横断せよ!!" +description = "500キロメートル(500,000ブロック)以上歩く" [advancement.challenge_sprint_marathon] - title = "(文字通りの)マラソンを全力疾走!" - description = "42,195ブロックを全力疾走!" +title = "(文字通りの)マラソンを全力疾走!" +description = "42,195ブロックを全力疾走!" [advancement.challenge_place_1k] - title = "初心者ビルダー!" - description = "1,000個のブロックを設置する" +title = "初心者ビルダー!" +description = "1,000個のブロックを設置する" [advancement.challenge_place_5k] - title = "中級ビルダー!" - description = "5,000個のブロックを設置する" +title = "中級ビルダー!" +description = "5,000個のブロックを設置する" [advancement.challenge_place_50k] - title = "上級ビルダー!" - description = "50,000個のブロックを設置する" +title = "上級ビルダー!" +description = "50,000個のブロックを設置する" [advancement.challenge_place_500k] - title = "マスタービルダー!" - description = "500,000個のブロックを設置する" +title = "マスタービルダー!" +description = "500,000個のブロックを設置する" [advancement.challenge_place_5m] - title = "シンメトリーの侍者!" - description = "現実はあなたの遊び場だ!(500万ブロック)" +title = "シンメトリーの侍者!" +description = "現実はあなたの遊び場だ!(500万ブロック)" [advancement.challenge_chop_1k] - title = "新米きこり!" - description = "1,000ブロックを伐採する" +title = "新米きこり!" +description = "1,000ブロックを伐採する" [advancement.challenge_chop_5k] - title = "中級きこり!" - description = "5,000ブロックを伐採する" +title = "中級きこり!" +description = "5,000ブロックを伐採する" [advancement.challenge_chop_50k] - title = "上級きこり!" - description = "50,000ブロックを伐採する" +title = "上級きこり!" +description = "50,000ブロックを伐採する" [advancement.challenge_chop_500k] - title = "きこりの達人!" - description = "500,000ブロックを伐採する" +title = "きこりの達人!" +description = "500,000ブロックを伐採する" [advancement.challenge_chop_5m] - title = "犬のジャクソン" - description = "最高のいい子!(500万ブロック)" +title = "犬のジャクソン" +description = "最高のいい子!(500万ブロック)" [advancement.challenge_block_1k] - title = "かろうじて防御!" - description = "1000回の攻撃をブロックする" +title = "かろうじて防御!" +description = "1000回の攻撃をブロックする" [advancement.challenge_block_5k] - title = "防御は楽しい!" - description = "5000回の攻撃をブロックする" +title = "防御は楽しい!" +description = "5000回の攻撃をブロックする" [advancement.challenge_block_50k] - title = "防御こそ我が人生!" - description = "50,000回の攻撃をブロックする" +title = "防御こそ我が人生!" +description = "50,000回の攻撃をブロックする" [advancement.challenge_block_500k] - title = "防御こそ我が使命!" - description = "500,000回の攻撃をブロックする" +title = "防御こそ我が使命!" +description = "500,000回の攻撃をブロックする" [advancement.challenge_block_5m] - title = "傷つける手" - description = "5,000,000回の攻撃をブロックする" +title = "傷つける手" +description = "5,000,000回の攻撃をブロックする" [advancement.challenge_brew_1k] - title = "新米錬金術師!" - description = "ポーションを1000個消費する" +title = "新米錬金術師!" +description = "ポーションを1000個消費する" [advancement.challenge_brew_5k] - title = "中級錬金術師!" - description = "ポーションを5000個消費する" +title = "中級錬金術師!" +description = "ポーションを5000個消費する" [advancement.challenge_brew_50k] - title = "上級錬金術師!" - description = "ポーションを50,000個消費する" +title = "上級錬金術師!" +description = "ポーションを50,000個消費する" [advancement.challenge_brew_500k] - title = "錬金術の達人!" - description = "ポーションを500,000個消費する" +title = "錬金術の達人!" +description = "ポーションを500,000個消費する" [advancement.challenge_brew_5m] - title = "錬金術師" - description = "ポーションを5,000,000個消費する" +title = "錬金術師" +description = "ポーションを5,000,000個消費する" [advancement.challenge_brewsplash_1k] - title = "初心者スプラッシュ師!" - description = "1000個のスプラッシュポーションを投げる" +title = "初心者スプラッシュ師!" +description = "1000個のスプラッシュポーションを投げる" [advancement.challenge_brewsplash_5k] - title = "中級スプラッシュ師!" - description = "5000個のスプラッシュポーションを投げる" +title = "中級スプラッシュ師!" +description = "5000個のスプラッシュポーションを投げる" [advancement.challenge_brewsplash_50k] - title = "上級スプラッシュ師!" - description = "50,000個のスプラッシュポーションを投げる" +title = "上級スプラッシュ師!" +description = "50,000個のスプラッシュポーションを投げる" [advancement.challenge_brewsplash_500k] - title = "マスタースプラッシュ師!" - description = "500,000個のスプラッシュポーションを投げる" +title = "マスタースプラッシュ師!" +description = "500,000個のスプラッシュポーションを投げる" [advancement.challenge_brewsplash_5m] - title = "スプラッシュマイスター" - description = "5,000,000個のスプラッシュポーションを投げる" +title = "スプラッシュマイスター" +description = "5,000,000個のスプラッシュポーションを投げる" [advancement.challenge_craft_1k] - title = "器用なクラフター!" - description = "1000個のアイテムを作成する" +title = "器用なクラフター!" +description = "1000個のアイテムを作成する" [advancement.challenge_craft_5k] - title = "気難しいクラフター!" - description = "5000個のアイテムを作成する" +title = "気難しいクラフター!" +description = "5000個のアイテムを作成する" [advancement.challenge_craft_50k] - title = "献身的なクラフター!" - description = "50,000個のアイテムを作成する" +title = "献身的なクラフター!" +description = "50,000個のアイテムを作成する" [advancement.challenge_craft_500k] - title = "騒々しいクラフター!" - description = "500,000個のアイテムを作成する" +title = "騒々しいクラフター!" +description = "500,000個のアイテムを作成する" [advancement.challenge_craft_5m] - title = "災厄のマックラフト" - description = "5,000,000個のアイテムを作成する" +title = "災厄のマックラフト" +description = "5,000,000個のアイテムを作成する" [advancement.challenge_enchant_1k] - title = "初心者エンチャンター!" - description = "1000個のアイテムをエンチャントする" +title = "初心者エンチャンター!" +description = "1000個のアイテムをエンチャントする" [advancement.challenge_enchant_5k] - title = "中級エンチャンター!" - description = "5000個のアイテムをエンチャントする" +title = "中級エンチャンター!" +description = "5000個のアイテムをエンチャントする" [advancement.challenge_enchant_50k] - title = "上級エンチャンター!" - description = "50,000個のアイテムをエンチャントする" +title = "上級エンチャンター!" +description = "50,000個のアイテムをエンチャントする" [advancement.challenge_enchant_500k] - title = "マスターエンチャンター!" - description = "500,000個のアイテムをエンチャントする" +title = "マスターエンチャンター!" +description = "500,000個のアイテムをエンチャントする" [advancement.challenge_enchant_5m] - title = "謎めくエンチャンター" - description = "5,000,000個のアイテムをエンチャントする" +title = "謎めくエンチャンター" +description = "5,000,000個のアイテムをエンチャントする" [advancement.challenge_excavate_1k] - title = "魅せられた発掘者!" - description = "1000ブロックを発掘する" +title = "魅せられた発掘者!" +description = "1000ブロックを発掘する" [advancement.challenge_excavate_5k] - title = "中級発掘者!" - description = "5000ブロックを発掘する" +title = "中級発掘者!" +description = "5000ブロックを発掘する" [advancement.challenge_excavate_50k] - title = "上級発掘者!" - description = "50,000ブロックを発掘する" +title = "上級発掘者!" +description = "50,000ブロックを発掘する" [advancement.challenge_excavate_500k] - title = "発掘の達人!" - description = "500,000ブロックを発掘する" +title = "発掘の達人!" +description = "500,000ブロックを発掘する" [advancement.challenge_excavate_5m] - title = "謎めく発掘者" - description = "5,000,000ブロックを発掘する" +title = "謎めく発掘者" +description = "5,000,000ブロックを発掘する" [advancement.horrible_person] - title = "あなたはひどい人だ" - description = "本当に計り知れない" +title = "あなたはひどい人だ" +description = "本当に計り知れない" [advancement.challenge_turtle_egg_smasher] - title = "カメの卵クラッシャー!" - description = "カメの卵を100個割る" +title = "カメの卵クラッシャー!" +description = "カメの卵を100個割る" [advancement.challenge_turtle_egg_annihilator] - title = "カメの卵殲滅者!" - description = "カメの卵を500個割る" +title = "カメの卵殲滅者!" +description = "カメの卵を500個割る" [advancement.challenge_novice_hunter] - title = "初心者ハンター!" - description = "100体のエンティティを倒す" +title = "初心者ハンター!" +description = "100体のエンティティを倒す" [advancement.challenge_intermediate_hunter] - title = "中級ハンター!" - description = "500体のエンティティを倒す" +title = "中級ハンター!" +description = "500体のエンティティを倒す" [advancement.challenge_advanced_hunter] - title = "上級ハンター!" - description = "5000体のエンティティを倒す" +title = "上級ハンター!" +description = "5000体のエンティティを倒す" [advancement.challenge_creeper_conqueror] - title = "クリーパー征服者!" - description = "クリーパーを50体倒す" +title = "クリーパー征服者!" +description = "クリーパーを50体倒す" [advancement.challenge_creeper_annihilator] - title = "クリーパー殲滅者!" - description = "クリーパーを200体倒す" +title = "クリーパー殲滅者!" +description = "クリーパーを200体倒す" [advancement.challenge_pickaxe_1k] - title = "初心者マイナー" - description = "1000個のブロックを壊す" +title = "初心者マイナー" +description = "1000個のブロックを壊す" [advancement.challenge_pickaxe_5k] - title = "熟練マイナー" - description = "5000個のブロックを壊す" +title = "熟練マイナー" +description = "5000個のブロックを壊す" [advancement.challenge_pickaxe_50k] - title = "エキスパートマイナー" - description = "50,000個のブロックを壊す" +title = "エキスパートマイナー" +description = "50,000個のブロックを壊す" [advancement.challenge_pickaxe_500k] - title = "マスターマイナー" - description = "500,000個のブロックを壊す" +title = "マスターマイナー" +description = "500,000個のブロックを壊す" [advancement.challenge_pickaxe_5m] - title = "伝説のマイナー" - description = "5,000,000個のブロックを壊す" +title = "伝説のマイナー" +description = "5,000,000個のブロックを壊す" [advancement.challenge_eat_100] - title = "食べるものがいっぱい!" - description = "100個以上のアイテムを食べよう!" +title = "食べるものがいっぱい!" +description = "100個以上のアイテムを食べよう!" [advancement.challenge_eat_1000] - title = "止められない空腹!" - description = "1,000個以上のアイテムを食べよう!" +title = "止められない空腹!" +description = "1,000個以上のアイテムを食べよう!" [advancement.challenge_eat_10000] - title = "永遠の飢餓!" - description = "10,000個以上のアイテムを食べよう!" +title = "永遠の飢餓!" +description = "10,000個以上のアイテムを食べよう!" [advancement.challenge_harvest_100] - title = "豊穣の収穫" - description = "100個以上の作物を収穫しよう!" +title = "豊穣の収穫" +description = "100個以上の作物を収穫しよう!" [advancement.challenge_harvest_1000] - title = "大豊穣の収穫" - description = "1,000個以上の作物を収穫しよう!" +title = "大豊穣の収穫" +description = "1,000個以上の作物を収穫しよう!" [advancement.challenge_swim_1nm] - title = "人間潜水艦!" - description = "1海里(1,852ブロック)泳ぐ" +title = "人間潜水艦!" +description = "1海里(1,852ブロック)泳ぐ" [advancement.challenge_sneak_1k] - title = "膝の痛み" - description = "1キロメートル(1,000ブロック)以上こっそり進む" +title = "膝の痛み" +description = "1キロメートル(1,000ブロック)以上こっそり進む" [advancement.challenge_sneak_5k] - title = "影の歩行者" - description = "5,000ブロック以上こっそり進む" +title = "影の歩行者" +description = "5,000ブロック以上こっそり進む" [advancement.challenge_sneak_20k] - title = "幽霊" - description = "20,000ブロック以上こっそり進む" +title = "幽霊" +description = "20,000ブロック以上こっそり進む" [advancement.challenge_swim_5k] - title = "深海ダイバー" - description = "5,000ブロック以上泳ぐ" +title = "深海ダイバー" +description = "5,000ブロック以上泳ぐ" [advancement.challenge_swim_20k] - title = "ポセイドンの選ばれし者" - description = "20,000ブロック以上泳ぐ" +title = "ポセイドンの選ばれし者" +description = "20,000ブロック以上泳ぐ" [advancement.challenge_sword_100] - title = "初めての血" - description = "剣で100回攻撃する" +title = "初めての血" +description = "剣で100回攻撃する" [advancement.challenge_sword_1k] - title = "剣舞" - description = "剣で1,000回攻撃する" +title = "剣舞" +description = "剣で1,000回攻撃する" [advancement.challenge_sword_10k] - title = "千の斬撃" - description = "剣で10,000回攻撃する" +title = "千の斬撃" +description = "剣で10,000回攻撃する" [advancement.challenge_unarmed_100] - title = "酒場の喧嘩屋" - description = "素手で100回攻撃する" +title = "酒場の喧嘩屋" +description = "素手で100回攻撃する" [advancement.challenge_unarmed_1k] - title = "鉄の拳" - description = "素手で1,000回攻撃する" +title = "鉄の拳" +description = "素手で1,000回攻撃する" [advancement.challenge_unarmed_10k] - title = "ワンパンチ" - description = "素手で10,000回攻撃する" +title = "ワンパンチ" +description = "素手で10,000回攻撃する" [advancement.challenge_trag_1k] - title = "血の代償" - description = "1,000ダメージを受ける" +title = "血の代償" +description = "1,000ダメージを受ける" [advancement.challenge_trag_10k] - title = "紅の潮流" - description = "10,000ダメージを受ける" +title = "紅の潮流" +description = "10,000ダメージを受ける" [advancement.challenge_trag_100k] - title = "苦痛の化身" - description = "100,000ダメージを受ける" +title = "苦痛の化身" +description = "100,000ダメージを受ける" [advancement.challenge_ranged_100] - title = "射撃練習" - description = "100発の飛び道具を発射する" +title = "射撃練習" +description = "100発の飛び道具を発射する" [advancement.challenge_ranged_1k] - title = "ホークアイ" - description = "1,000発の飛び道具を発射する" +title = "ホークアイ" +description = "1,000発の飛び道具を発射する" [advancement.challenge_ranged_10k] - title = "矢の嵐" - description = "10,000発の飛び道具を発射する" +title = "矢の嵐" +description = "10,000発の飛び道具を発射する" [advancement.challenge_chronos_1h] - title = "チクタク" - description = "1時間オンラインで過ごす" +title = "チクタク" +description = "1時間オンラインで過ごす" [advancement.challenge_chronos_24h] - title = "時の砂" - description = "24時間オンラインで過ごす" +title = "時の砂" +description = "24時間オンラインで過ごす" [advancement.challenge_chronos_168h] - title = "時を超えし者" - description = "168時間(1週間)オンラインで過ごす" +title = "時を超えし者" +description = "168時間(1週間)オンラインで過ごす" [advancement.challenge_nether_50] - title = "地獄の門番" - description = "ネザーの生物を50体倒す" +title = "地獄の門番" +description = "ネザーの生物を50体倒す" [advancement.challenge_nether_500] - title = "深淵の番人" - description = "ネザーの生物を500体倒す" +title = "深淵の番人" +description = "ネザーの生物を500体倒す" [advancement.challenge_nether_5k] - title = "ネザーの支配者" - description = "ネザーの生物を5,000体倒す" +title = "ネザーの支配者" +description = "ネザーの生物を5,000体倒す" [advancement.challenge_rift_50] - title = "空間の歪み" - description = "50回テレポートする" +title = "空間の歪み" +description = "50回テレポートする" [advancement.challenge_rift_500] - title = "虚空の歩行者" - description = "500回テレポートする" +title = "虚空の歩行者" +description = "500回テレポートする" [advancement.challenge_rift_5k] - title = "世界の狭間" - description = "5,000回テレポートする" +title = "世界の狭間" +description = "5,000回テレポートする" [advancement.challenge_taming_10] - title = "動物のささやき手" - description = "10匹の動物を繁殖させる" +title = "動物のささやき手" +description = "10匹の動物を繁殖させる" [advancement.challenge_taming_50] - title = "群れのリーダー" - description = "50匹の動物を繁殖させる" +title = "群れのリーダー" +description = "50匹の動物を繁殖させる" [advancement.challenge_taming_500] - title = "猛獣使い" - description = "500匹の動物を繁殖させる" +title = "猛獣使い" +description = "500匹の動物を繁殖させる" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "疾風の脚" - description = "5キロメートル(5,000ブロック)以上をダッシュする" +title = "疾風の脚" +description = "5キロメートル(5,000ブロック)以上をダッシュする" [advancement.challenge_sprint_dist_50k] - title = "稲妻の脚" - description = "50キロメートル(50,000ブロック)以上をダッシュする" +title = "稲妻の脚" +description = "50キロメートル(50,000ブロック)以上をダッシュする" [advancement.challenge_agility_swim_1k] - title = "水面走り" - description = "1キロメートル(1,000ブロック)以上を泳ぐ" +title = "水面走り" +description = "1キロメートル(1,000ブロック)以上を泳ぐ" [advancement.challenge_agility_swim_10k] - title = "水の旅人" - description = "10キロメートル(10,000ブロック)以上を泳ぐ" +title = "水の旅人" +description = "10キロメートル(10,000ブロック)以上を泳ぐ" [advancement.challenge_fly_1k] - title = "空の舞" - description = "1キロメートル(1,000ブロック)以上を飛ぶ" +title = "空の舞" +description = "1キロメートル(1,000ブロック)以上を飛ぶ" [advancement.challenge_fly_10k] - title = "風乗り" - description = "10キロメートル(10,000ブロック)以上を飛ぶ" +title = "風乗り" +description = "10キロメートル(10,000ブロック)以上を飛ぶ" [advancement.challenge_agility_sneak_500] - title = "静かな足取り" - description = "500ブロック以上をスニークする" +title = "静かな足取り" +description = "500ブロック以上をスニークする" [advancement.challenge_agility_sneak_5k] - title = "幻影の歩み" - description = "5キロメートル(5,000ブロック)以上をスニークする" +title = "幻影の歩み" +description = "5キロメートル(5,000ブロック)以上をスニークする" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "解体班" - description = "500ブロックを破壊する" +title = "解体班" +description = "500ブロックを破壊する" [advancement.challenge_demolish_5k] - title = "破壊球" - description = "5,000ブロックを破壊する" +title = "破壊球" +description = "5,000ブロックを破壊する" [advancement.challenge_value_placed_10k] - title = "価値ある建築家" - description = "10,000の価値分のブロックを設置する" +title = "価値ある建築家" +description = "10,000の価値分のブロックを設置する" [advancement.challenge_value_placed_100k] - title = "建築の達人" - description = "100,000の価値分のブロックを設置する" +title = "建築の達人" +description = "100,000の価値分のブロックを設置する" [advancement.challenge_demolish_val_5k] - title = "回収の達人" - description = "解体から5,000の価値を回収する" +title = "回収の達人" +description = "解体から5,000の価値を回収する" [advancement.challenge_demolish_val_50k] - title = "完全解体" - description = "解体から50,000の価値を回収する" +title = "完全解体" +description = "解体から50,000の価値を回収する" [advancement.challenge_high_build_100] - title = "天空の建築家" - description = "Y=128以上に100ブロックを設置する" +title = "天空の建築家" +description = "Y=128以上に100ブロックを設置する" [advancement.challenge_high_build_1k] - title = "雲上の建築士" - description = "Y=128以上に1,000ブロックを設置する" +title = "雲上の建築士" +description = "Y=128以上に1,000ブロックを設置する" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "斧振り" - description = "斧を500回振る" +title = "斧振り" +description = "斧を500回振る" [advancement.challenge_axe_swing_5k] - title = "狂戦士" - description = "斧を5,000回振る" +title = "狂戦士" +description = "斧を5,000回振る" [advancement.challenge_axe_damage_1k] - title = "断裂者" - description = "斧で1,000ダメージを与える" +title = "断裂者" +description = "斧で1,000ダメージを与える" [advancement.challenge_axe_damage_10k] - title = "処刑人の斧" - description = "斧で10,000ダメージを与える" +title = "処刑人の斧" +description = "斧で10,000ダメージを与える" [advancement.challenge_axe_value_5k] - title = "木材商人" - description = "5,000の価値分の木材を収穫する" +title = "木材商人" +description = "5,000の価値分の木材を収穫する" [advancement.challenge_axe_value_50k] - title = "木材王" - description = "50,000の価値分の木材を収穫する" +title = "木材王" +description = "50,000の価値分の木材を収穫する" [advancement.challenge_leaves_500] - title = "落葉掃き" - description = "斧で500の葉ブロックを除去する" +title = "落葉掃き" +description = "斧で500の葉ブロックを除去する" [advancement.challenge_leaves_5k] - title = "落葉粉砕機" - description = "斧で5,000の葉ブロックを除去する" +title = "落葉粉砕機" +description = "斧で5,000の葉ブロックを除去する" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "ダメージ吸収" - description = "盾で1,000ダメージをブロックする" +title = "ダメージ吸収" +description = "盾で1,000ダメージをブロックする" [advancement.challenge_block_dmg_10k] - title = "人間の盾" - description = "盾で10,000ダメージをブロックする" +title = "人間の盾" +description = "盾で10,000ダメージをブロックする" [advancement.challenge_block_proj_100] - title = "矢弾き" - description = "盾で100の飛び道具をブロックする" +title = "矢弾き" +description = "盾で100の飛び道具をブロックする" [advancement.challenge_block_proj_1k] - title = "飛び道具の盾" - description = "盾で1,000の飛び道具をブロックする" +title = "飛び道具の盾" +description = "盾で1,000の飛び道具をブロックする" [advancement.challenge_block_melee_500] - title = "受け流しの達人" - description = "盾で500の近接攻撃をブロックする" +title = "受け流しの達人" +description = "盾で500の近接攻撃をブロックする" [advancement.challenge_block_melee_5k] - title = "鉄の要塞" - description = "盾で5,000の近接攻撃をブロックする" +title = "鉄の要塞" +description = "盾で5,000の近接攻撃をブロックする" [advancement.challenge_block_heavy_50] - title = "戦車" - description = "50の強力な攻撃(5ダメージ超)をブロックする" +title = "戦車" +description = "50の強力な攻撃(5ダメージ超)をブロックする" [advancement.challenge_block_heavy_500] - title = "不動の壁" - description = "500の強力な攻撃(5ダメージ超)をブロックする" +title = "不動の壁" +description = "500の強力な攻撃(5ダメージ超)をブロックする" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "醸造の準備" - description = "醸造台を10台設置する" +title = "醸造の準備" +description = "醸造台を10台設置する" [advancement.challenge_brew_stands_50] - title = "ポーション工場" - description = "醸造台を50台設置する" +title = "ポーション工場" +description = "醸造台を50台設置する" [advancement.challenge_brew_strong_25] - title = "強力な醸造" - description = "強化されたポーションを25本飲む" +title = "強力な醸造" +description = "強化されたポーションを25本飲む" [advancement.challenge_brew_strong_250] - title = "最大効力" - description = "強化されたポーションを250本飲む" +title = "最大効力" +description = "強化されたポーションを250本飲む" [advancement.challenge_brew_splash_hits_50] - title = "飛散範囲" - description = "スプラッシュポーションで50体のエンティティに命中する" +title = "飛散範囲" +description = "スプラッシュポーションで50体のエンティティに命中する" [advancement.challenge_brew_splash_hits_500] - title = "疫病医" - description = "スプラッシュポーションで500体のエンティティに命中する" +title = "疫病医" +description = "スプラッシュポーションで500体のエンティティに命中する" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "落ち着きなし" - description = "活動中に1キロメートルを移動する" +title = "落ち着きなし" +description = "活動中に1キロメートルを移動する" [advancement.challenge_active_dist_10k] - title = "道の開拓者" - description = "活動中に10キロメートルを移動する" +title = "道の開拓者" +description = "活動中に10キロメートルを移動する" [advancement.challenge_active_dist_100k] - title = "永久運動" - description = "活動中に100キロメートルを移動する" +title = "永久運動" +description = "活動中に100キロメートルを移動する" [advancement.challenge_beds_10] - title = "早起き" - description = "ベッドで10回眠る" +title = "早起き" +description = "ベッドで10回眠る" [advancement.challenge_beds_100] - title = "時間飛ばし" - description = "ベッドで100回眠る" +title = "時間飛ばし" +description = "ベッドで100回眠る" [advancement.challenge_chronos_tp_50] - title = "時空転移" - description = "50回テレポートする" +title = "時空転移" +description = "50回テレポートする" [advancement.challenge_chronos_tp_500] - title = "時間の歪み" - description = "500回テレポートする" +title = "時間の歪み" +description = "500回テレポートする" [advancement.challenge_chronos_deaths_10] - title = "死すべき者" - description = "10回死ぬ" +title = "死すべき者" +description = "10回死ぬ" [advancement.challenge_chronos_deaths_100] - title = "死を超える者" - description = "100回死ぬ" +title = "死を超える者" +description = "100回死ぬ" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "製作の価値" - description = "合計10,000の価値のアイテムをクラフトする" +title = "製作の価値" +description = "合計10,000の価値のアイテムをクラフトする" [advancement.challenge_craft_value_100k] - title = "職人" - description = "合計100,000の価値のアイテムをクラフトする" +title = "職人" +description = "合計100,000の価値のアイテムをクラフトする" [advancement.challenge_craft_tools_25] - title = "道具鍛冶" - description = "25個の道具をクラフトする" +title = "道具鍛冶" +description = "25個の道具をクラフトする" [advancement.challenge_craft_tools_250] - title = "鍛造の達人" - description = "250個の道具をクラフトする" +title = "鍛造の達人" +description = "250個の道具をクラフトする" [advancement.challenge_craft_armor_25] - title = "防具鍛冶" - description = "25個の防具をクラフトする" +title = "防具鍛冶" +description = "25個の防具をクラフトする" [advancement.challenge_craft_armor_250] - title = "防具の達人" - description = "250個の防具をクラフトする" +title = "防具の達人" +description = "250個の防具をクラフトする" [advancement.challenge_craft_mass_25k] - title = "大量生産" - description = "25,000個のアイテムをクラフトする" +title = "大量生産" +description = "25,000個のアイテムをクラフトする" [advancement.challenge_craft_mass_250k] - title = "産業革命" - description = "250,000個のアイテムをクラフトする" +title = "産業革命" +description = "250,000個のアイテムをクラフトする" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "収集家" - description = "50種類のアイテムを発見する" +title = "収集家" +description = "50種類のアイテムを発見する" [advancement.challenge_discover_items_250] - title = "目録係" - description = "250種類のアイテムを発見する" +title = "目録係" +description = "250種類のアイテムを発見する" [advancement.challenge_discover_blocks_50] - title = "測量士" - description = "50種類のブロックを発見する" +title = "測量士" +description = "50種類のブロックを発見する" [advancement.challenge_discover_blocks_250] - title = "地質学者" - description = "250種類のブロックを発見する" +title = "地質学者" +description = "250種類のブロックを発見する" [advancement.challenge_discover_mobs_25] - title = "観察者" - description = "25種類のモブを発見する" +title = "観察者" +description = "25種類のモブを発見する" [advancement.challenge_discover_mobs_75] - title = "博物学者" - description = "75種類のモブを発見する" +title = "博物学者" +description = "75種類のモブを発見する" [advancement.challenge_discover_biomes_10] - title = "放浪者" - description = "10種類のバイオームを発見する" +title = "放浪者" +description = "10種類のバイオームを発見する" [advancement.challenge_discover_biomes_40] - title = "世界旅行者" - description = "40種類のバイオームを発見する" +title = "世界旅行者" +description = "40種類のバイオームを発見する" [advancement.challenge_discover_foods_10] - title = "美食家" - description = "10種類の食べ物を発見する" +title = "美食家" +description = "10種類の食べ物を発見する" [advancement.challenge_discover_foods_30] - title = "料理の達人" - description = "30種類の食べ物を発見する" +title = "料理の達人" +description = "30種類の食べ物を発見する" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "力の織り手" - description = "エンチャントパワーを100蓄積する" +title = "力の織り手" +description = "エンチャントパワーを100蓄積する" [advancement.challenge_enchant_power_1k] - title = "秘術の達人" - description = "エンチャントパワーを1,000蓄積する" +title = "秘術の達人" +description = "エンチャントパワーを1,000蓄積する" [advancement.challenge_enchant_levels_1k] - title = "レベル消費者" - description = "エンチャントに1,000経験レベルを使う" +title = "レベル消費者" +description = "エンチャントに1,000経験レベルを使う" [advancement.challenge_enchant_levels_10k] - title = "経験値の泉" - description = "エンチャントに10,000経験レベルを使う" +title = "経験値の泉" +description = "エンチャントに10,000経験レベルを使う" [advancement.challenge_enchant_high_25] - title = "大勝負" - description = "最大レベルのエンチャントを25回行う" +title = "大勝負" +description = "最大レベルのエンチャントを25回行う" [advancement.challenge_enchant_high_250] - title = "伝説のエンチャンター" - description = "最大レベルのエンチャントを250回行う" +title = "伝説のエンチャンター" +description = "最大レベルのエンチャントを250回行う" [advancement.challenge_enchant_total_500] - title = "レベル燃焼" - description = "全エンチャントで合計500レベルを使う" +title = "レベル燃焼" +description = "全エンチャントで合計500レベルを使う" [advancement.challenge_enchant_total_5k] - title = "秘術の投資" - description = "全エンチャントで合計5,000レベルを使う" +title = "秘術の投資" +description = "全エンチャントで合計5,000レベルを使う" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "掘り師" - description = "シャベルを500回振る" +title = "掘り師" +description = "シャベルを500回振る" [advancement.challenge_dig_swing_5k] - title = "掘削者" - description = "シャベルを5,000回振る" +title = "掘削者" +description = "シャベルを5,000回振る" [advancement.challenge_dig_damage_1k] - title = "シャベルの騎士" - description = "シャベルで1,000ダメージを与える" +title = "シャベルの騎士" +description = "シャベルで1,000ダメージを与える" [advancement.challenge_dig_damage_10k] - title = "シャベルの達人" - description = "シャベルで10,000ダメージを与える" +title = "シャベルの達人" +description = "シャベルで10,000ダメージを与える" [advancement.challenge_dig_value_5k] - title = "土の商人" - description = "5,000の価値分のブロックを掘削する" +title = "土の商人" +description = "5,000の価値分のブロックを掘削する" [advancement.challenge_dig_value_50k] - title = "大地の支配者" - description = "50,000の価値分のブロックを掘削する" +title = "大地の支配者" +description = "50,000の価値分のブロックを掘削する" [advancement.challenge_dig_gravel_500] - title = "砂利砕き" - description = "砂利、砂、粘土を500ブロック掘る" +title = "砂利砕き" +description = "砂利、砂、粘土を500ブロック掘る" [advancement.challenge_dig_gravel_5k] - title = "砂ふるい" - description = "砂利、砂、粘土を5,000ブロック掘る" +title = "砂ふるい" +description = "砂利、砂、粘土を5,000ブロック掘る" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "種まき人" - description = "100の作物を植える" +title = "種まき人" +description = "100の作物を植える" [advancement.challenge_plant_1k] - title = "緑の親指" - description = "1,000の作物を植える" +title = "緑の親指" +description = "1,000の作物を植える" [advancement.challenge_plant_5k] - title = "農業王" - description = "5,000の作物を植える" +title = "農業王" +description = "5,000の作物を植える" [advancement.challenge_compost_50] - title = "再利用者" - description = "50個のアイテムを堆肥にする" +title = "再利用者" +description = "50個のアイテムを堆肥にする" [advancement.challenge_compost_500] - title = "土壌改良" - description = "500個のアイテムを堆肥にする" +title = "土壌改良" +description = "500個のアイテムを堆肥にする" [advancement.challenge_shear_50] - title = "毛刈り師" - description = "50体のエンティティを毛刈りする" +title = "毛刈り師" +description = "50体のエンティティを毛刈りする" [advancement.challenge_shear_250] - title = "群れの長" - description = "250体のエンティティを毛刈りする" +title = "群れの長" +description = "250体のエンティティを毛刈りする" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "狩人" - description = "500体の生物を倒す" +title = "狩人" +description = "500体の生物を倒す" [advancement.challenge_kills_5k] - title = "処刑人" - description = "5,000体の生物を倒す" +title = "処刑人" +description = "5,000体の生物を倒す" [advancement.challenge_boss_1] - title = "ボス挑戦者" - description = "ボスモブを1体倒す" +title = "ボス挑戦者" +description = "ボスモブを1体倒す" [advancement.challenge_boss_10] - title = "伝説殺し" - description = "ボスモブを10体倒す" +title = "伝説殺し" +description = "ボスモブを10体倒す" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "衰弱" - description = "ウィザーダメージを500耐える" +title = "衰弱" +description = "ウィザーダメージを500耐える" [advancement.challenge_wither_dmg_5k] - title = "枯死の生存者" - description = "ウィザーダメージを5,000耐える" +title = "枯死の生存者" +description = "ウィザーダメージを5,000耐える" [advancement.challenge_wither_skel_25] - title = "骨の収集者" - description = "ウィザースケルトンを25体倒す" +title = "骨の収集者" +description = "ウィザースケルトンを25体倒す" [advancement.challenge_wither_skel_250] - title = "骸骨の天敵" - description = "ウィザースケルトンを250体倒す" +title = "骸骨の天敵" +description = "ウィザースケルトンを250体倒す" [advancement.challenge_wither_boss_1] - title = "ウィザー討伐" - description = "ウィザーを倒す" +title = "ウィザー討伐" +description = "ウィザーを倒す" [advancement.challenge_wither_boss_10] - title = "ネザーの支配者" - description = "ウィザーを10回倒す" +title = "ネザーの支配者" +description = "ウィザーを10回倒す" [advancement.challenge_roses_10] - title = "死の庭師" - description = "ウィザーローズを10本壊す" +title = "死の庭師" +description = "ウィザーローズを10本壊す" [advancement.challenge_roses_100] - title = "枯死の収集者" - description = "ウィザーローズを100本壊す" +title = "枯死の収集者" +description = "ウィザーローズを100本壊す" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "鉱夫の腕" - description = "つるはしを500回振る" +title = "鉱夫の腕" +description = "つるはしを500回振る" [advancement.challenge_pick_swing_5k] - title = "坑道掘り" - description = "つるはしを5,000回振る" +title = "坑道掘り" +description = "つるはしを5,000回振る" [advancement.challenge_pick_damage_1k] - title = "つるはし戦士" - description = "つるはしで1,000ダメージを与える" +title = "つるはし戦士" +description = "つるはしで1,000ダメージを与える" [advancement.challenge_pick_damage_10k] - title = "戦のつるはし" - description = "つるはしで10,000ダメージを与える" +title = "戦のつるはし" +description = "つるはしで10,000ダメージを与える" [advancement.challenge_pick_value_5k] - title = "宝石探し" - description = "5,000の価値分のブロックを採掘する" +title = "宝石探し" +description = "5,000の価値分のブロックを採掘する" [advancement.challenge_pick_value_50k] - title = "鉱石王" - description = "50,000の価値分のブロックを採掘する" +title = "鉱石王" +description = "50,000の価値分のブロックを採掘する" [advancement.challenge_pick_ores_500] - title = "探鉱者" - description = "500の鉱石ブロックを採掘する" +title = "探鉱者" +description = "500の鉱石ブロックを採掘する" [advancement.challenge_pick_ores_5k] - title = "採掘の達人" - description = "5,000の鉱石ブロックを採掘する" +title = "採掘の達人" +description = "5,000の鉱石ブロックを採掘する" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "射撃の名手" - description = "遠距離ダメージを1,000与える" +title = "射撃の名手" +description = "遠距離ダメージを1,000与える" [advancement.challenge_ranged_dmg_10k] - title = "致命の射手" - description = "遠距離ダメージを10,000与える" +title = "致命の射手" +description = "遠距離ダメージを10,000与える" [advancement.challenge_ranged_dist_5k] - title = "長距離射撃" - description = "飛び道具の合計飛距離5,000ブロックを達成する" +title = "長距離射撃" +description = "飛び道具の合計飛距離5,000ブロックを達成する" [advancement.challenge_ranged_dist_50k] - title = "遠射の王" - description = "飛び道具の合計飛距離50,000ブロックを達成する" +title = "遠射の王" +description = "飛び道具の合計飛距離50,000ブロックを達成する" [advancement.challenge_ranged_kills_50] - title = "弓兵" - description = "遠距離武器で50体のモブを倒す" +title = "弓兵" +description = "遠距離武器で50体のモブを倒す" [advancement.challenge_ranged_kills_500] - title = "弓の達人" - description = "遠距離武器で500体のモブを倒す" +title = "弓の達人" +description = "遠距離武器で500体のモブを倒す" [advancement.challenge_longshot_25] - title = "狙撃手" - description = "長距離射撃(30ブロック超)を25回命中させる" +title = "狙撃手" +description = "長距離射撃(30ブロック超)を25回命中させる" [advancement.challenge_longshot_250] - title = "鷹の目" - description = "長距離射撃(30ブロック超)を250回命中させる" +title = "鷹の目" +description = "長距離射撃(30ブロック超)を250回命中させる" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "真珠投げ" - description = "エンダーパールを50個投げる" +title = "真珠投げ" +description = "エンダーパールを50個投げる" [advancement.challenge_rift_pearls_500] - title = "転送中毒" - description = "エンダーパールを500個投げる" +title = "転送中毒" +description = "エンダーパールを500個投げる" [advancement.challenge_rift_enderman_50] - title = "エンダーマン狩り" - description = "エンダーマンを50体倒す" +title = "エンダーマン狩り" +description = "エンダーマンを50体倒す" [advancement.challenge_rift_enderman_500] - title = "虚空の追跡者" - description = "エンダーマンを500体倒す" +title = "虚空の追跡者" +description = "エンダーマンを500体倒す" [advancement.challenge_rift_dragon_500] - title = "竜と闘う者" - description = "エンダードラゴンに500ダメージを与える" +title = "竜と闘う者" +description = "エンダードラゴンに500ダメージを与える" [advancement.challenge_rift_dragon_5k] - title = "竜殺し" - description = "エンダードラゴンに5,000ダメージを与える" +title = "竜殺し" +description = "エンダードラゴンに5,000ダメージを与える" [advancement.challenge_rift_crystal_10] - title = "水晶破壊者" - description = "エンドクリスタルを10個破壊する" +title = "水晶破壊者" +description = "エンドクリスタルを10個破壊する" [advancement.challenge_rift_crystal_100] - title = "エンドの破壊者" - description = "エンドクリスタルを100個破壊する" +title = "エンドの破壊者" +description = "エンドクリスタルを100個破壊する" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "釣り人" - description = "25匹の魚を釣る" +title = "釣り人" +description = "25匹の魚を釣る" [advancement.challenge_fish_250] - title = "釣りの達人" - description = "250匹の魚を釣る" +title = "釣りの達人" +description = "250匹の魚を釣る" [advancement.challenge_drowned_25] - title = "溺死体狩り" - description = "ドラウンドを25体倒す" +title = "溺死体狩り" +description = "ドラウンドを25体倒す" [advancement.challenge_drowned_250] - title = "海の浄化者" - description = "ドラウンドを250体倒す" +title = "海の浄化者" +description = "ドラウンドを250体倒す" [advancement.challenge_guardian_10] - title = "ガーディアン討伐" - description = "ガーディアンを10体倒す" +title = "ガーディアン討伐" +description = "ガーディアンを10体倒す" [advancement.challenge_guardian_100] - title = "神殿襲撃者" - description = "ガーディアンを100体倒す" +title = "神殿襲撃者" +description = "ガーディアンを100体倒す" [advancement.challenge_underwater_blocks_100] - title = "水中採掘者" - description = "水中で100ブロックを破壊する" +title = "水中採掘者" +description = "水中で100ブロックを破壊する" [advancement.challenge_underwater_blocks_1k] - title = "水中技師" - description = "水中で1,000ブロックを破壊する" +title = "水中技師" +description = "水中で1,000ブロックを破壊する" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "背後からの一刺し" - description = "スニーク中に500ダメージを与える" +title = "背後からの一刺し" +description = "スニーク中に500ダメージを与える" [advancement.challenge_stealth_dmg_5k] - title = "沈黙の殺し屋" - description = "スニーク中に5,000ダメージを与える" +title = "沈黙の殺し屋" +description = "スニーク中に5,000ダメージを与える" [advancement.challenge_stealth_kills_10] - title = "暗殺者" - description = "スニーク中に10体のモブを倒す" +title = "暗殺者" +description = "スニーク中に10体のモブを倒す" [advancement.challenge_stealth_kills_100] - title = "影の死神" - description = "スニーク中に100体のモブを倒す" +title = "影の死神" +description = "スニーク中に100体のモブを倒す" [advancement.challenge_stealth_time_1h] - title = "忍耐" - description = "1時間(3,600秒)スニークする" +title = "忍耐" +description = "1時間(3,600秒)スニークする" [advancement.challenge_stealth_time_10h] - title = "影の支配者" - description = "10時間(36,000秒)スニークする" +title = "影の支配者" +description = "10時間(36,000秒)スニークする" [advancement.challenge_stealth_arrows_50] - title = "静かなる射手" - description = "スニーク中に50本の矢を放つ" +title = "静かなる射手" +description = "スニーク中に50本の矢を放つ" [advancement.challenge_stealth_arrows_500] - title = "幻影の弓手" - description = "スニーク中に500本の矢を放つ" +title = "幻影の弓手" +description = "スニーク中に500本の矢を放つ" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "剣の見習い" - description = "剣で1,000ダメージを与える" +title = "剣の見習い" +description = "剣で1,000ダメージを与える" [advancement.challenge_sword_dmg_10k] - title = "剣士" - description = "剣で10,000ダメージを与える" +title = "剣士" +description = "剣で10,000ダメージを与える" [advancement.challenge_sword_kills_50] - title = "決闘者" - description = "剣で50体のモブを倒す" +title = "決闘者" +description = "剣で50体のモブを倒す" [advancement.challenge_sword_kills_500] - title = "剣闘士" - description = "剣で500体のモブを倒す" +title = "剣闘士" +description = "剣で500体のモブを倒す" [advancement.challenge_sword_crit_50] - title = "会心の一撃" - description = "剣でクリティカルヒットを50回決める" +title = "会心の一撃" +description = "剣でクリティカルヒットを50回決める" [advancement.challenge_sword_crit_500] - title = "精密の達人" - description = "剣でクリティカルヒットを500回決める" +title = "精密の達人" +description = "剣でクリティカルヒットを500回決める" [advancement.challenge_sword_heavy_25] - title = "重い一撃" - description = "剣で強力な一撃(8ダメージ超)を25回決める" +title = "重い一撃" +description = "剣で強力な一撃(8ダメージ超)を25回決める" [advancement.challenge_sword_heavy_250] - title = "壊滅の一撃" - description = "剣で強力な一撃(8ダメージ超)を250回決める" +title = "壊滅の一撃" +description = "剣で強力な一撃(8ダメージ超)を250回決める" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "獣の調教師" - description = "ペットが合計500ダメージを与える" +title = "獣の調教師" +description = "ペットが合計500ダメージを与える" [advancement.challenge_pet_dmg_5k] - title = "戦の支配者" - description = "ペットが合計5,000ダメージを与える" +title = "戦の支配者" +description = "ペットが合計5,000ダメージを与える" [advancement.challenge_tamed_10] - title = "動物の友" - description = "10匹の動物を手懐ける" +title = "動物の友" +description = "10匹の動物を手懐ける" [advancement.challenge_tamed_100] - title = "動物園の管理人" - description = "100匹の動物を手懐ける" +title = "動物園の管理人" +description = "100匹の動物を手懐ける" [advancement.challenge_pet_kills_25] - title = "群れの戦術" - description = "ペットが25体の生物を倒す" +title = "群れの戦術" +description = "ペットが25体の生物を倒す" [advancement.challenge_pet_kills_250] - title = "群れの指揮官" - description = "ペットが250体の生物を倒す" +title = "群れの指揮官" +description = "ペットが250体の生物を倒す" [advancement.challenge_taming_2500] - title = "繁殖の専門家" - description = "2,500匹の動物を繁殖させる" +title = "繁殖の専門家" +description = "2,500匹の動物を繁殖させる" [advancement.challenge_taming_25k] - title = "遺伝学の達人" - description = "25,000匹の動物を繁殖させる" +title = "遺伝学の達人" +description = "25,000匹の動物を繁殖させる" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "打たれ役" - description = "500回攻撃を受ける" +title = "打たれ役" +description = "500回攻撃を受ける" [advancement.challenge_trag_hits_5k] - title = "痛みの虜" - description = "5,000回攻撃を受ける" +title = "痛みの虜" +description = "5,000回攻撃を受ける" [advancement.challenge_trag_deaths_10] - title = "九つの命" - description = "10回死ぬ" +title = "九つの命" +description = "10回死ぬ" [advancement.challenge_trag_deaths_100] - title = "繰り返す悪夢" - description = "100回死ぬ" +title = "繰り返す悪夢" +description = "100回死ぬ" [advancement.challenge_trag_fire_500] - title = "炎の犠牲者" - description = "炎ダメージを500耐える" +title = "炎の犠牲者" +description = "炎ダメージを500耐える" [advancement.challenge_trag_fire_5k] - title = "不死鳥" - description = "炎ダメージを5,000耐える" +title = "不死鳥" +description = "炎ダメージを5,000耐える" [advancement.challenge_trag_fall_500] - title = "重力確認" - description = "落下ダメージを500耐える" +title = "重力確認" +description = "落下ダメージを500耐える" [advancement.challenge_trag_fall_5k] - title = "終端速度" - description = "落下ダメージを5,000耐える" +title = "終端速度" +description = "落下ダメージを5,000耐える" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "喧嘩屋" - description = "素手で1,000ダメージを与える" +title = "喧嘩屋" +description = "素手で1,000ダメージを与える" [advancement.challenge_unarmed_dmg_10k] - title = "武術家" - description = "素手で10,000ダメージを与える" +title = "武術家" +description = "素手で10,000ダメージを与える" [advancement.challenge_unarmed_kills_25] - title = "素手の拳" - description = "素手で25体のモブを倒す" +title = "素手の拳" +description = "素手で25体のモブを倒す" [advancement.challenge_unarmed_kills_250] - title = "拳の伝説" - description = "素手で250体のモブを倒す" +title = "拳の伝説" +description = "素手で250体のモブを倒す" [advancement.challenge_unarmed_crit_25] - title = "会心の拳" - description = "素手でクリティカルヒットを25回決める" +title = "会心の拳" +description = "素手でクリティカルヒットを25回決める" [advancement.challenge_unarmed_crit_250] - title = "精密の拳" - description = "素手でクリティカルヒットを250回決める" +title = "精密の拳" +description = "素手でクリティカルヒットを250回決める" [advancement.challenge_unarmed_heavy_25] - title = "剛拳" - description = "素手で強力な一撃(6ダメージ超)を25回決める" +title = "剛拳" +description = "素手で強力な一撃(6ダメージ超)を25回決める" [advancement.challenge_unarmed_heavy_250] - title = "一撃必殺" - description = "素手で強力な一撃(6ダメージ超)を250回決める" +title = "一撃必殺" +description = "素手で強力な一撃(6ダメージ超)を250回決める" # items [items] [items.bound_ender_peral] - name = "聖遺物ポートキー" - usage1 = "Shift + 左クリックでバインド" - usage2 = "右クリックでバインドされたインベントリにアクセス" +name = "聖遺物ポートキー" +usage1 = "Shift + 左クリックでバインド" +usage2 = "右クリックでバインドされたインベントリにアクセス" [items.bound_eye_of_ender] - name = "眼球アンカー" - usage1 = "右クリックで消費し、バインドされた場所にテレポート" - usage2 = "Shift + 左クリックでブロックにバインド" +name = "眼球アンカー" +usage1 = "右クリックで消費し、バインドされた場所にテレポート" +usage2 = "Shift + 左クリックでブロックにバインド" [items.bound_redstone_torch] - name = "レッドストーンリモコン" - usage1 = "右クリックで1ティックのレッドストーンパルスを作成" - usage2 = "Shift + 左クリックで「ターゲット」ブロックにバインド" +name = "レッドストーンリモコン" +usage1 = "右クリックで1ティックのレッドストーンパルスを作成" +usage2 = "Shift + 左クリックで「ターゲット」ブロックにバインド" [items.bound_snowball] - name = "ウェブスネア!" - usage1 = "投げるとその場所に一時的なクモの巣の罠が作成される" +name = "ウェブスネア!" +usage1 = "投げるとその場所に一時的なクモの巣の罠が作成される" [items.chrono_time_bottle] - name = "時のボトル" - usage1 = "インベントリにある間、受動的に時間を蓄える" - usage2 = "時間経過するブロックや子どもの動物を右クリックして蓄えた時間を消費" - stored = "蓄えた時間" +name = "時のボトル" +usage1 = "インベントリにある間、受動的に時間を蓄える" +usage2 = "時間経過するブロックや子どもの動物を右クリックして蓄えた時間を消費" +stored = "蓄えた時間" [items.chrono_time_bomb] - name = "タイムボム" - usage1 = "右クリックで時空の場を生成するクロノボルトを発射" +name = "タイムボム" +usage1 = "右クリックで時空の場を生成するクロノボルトを発射" [items.elevator_block] - name = "エレベーターブロック" - usage1 = "ジャンプで上にテレポート" - usage2 = "シフトで下にテレポート" - usage3 = "エレベーター間には最低2ブロックの空間が必要" +name = "エレベーターブロック" +usage1 = "ジャンプで上にテレポート" +usage2 = "シフトで下にテレポート" +usage3 = "エレベーター間には最低2ブロックの空間が必要" # snippets [snippets] [snippets.gui] - level = "レベル" - knowledge = "知識" - power_used = "使用パワー" - not_learned = "未習得" - xp = "XPまで" - welcome = "ようこそ!" - welcome_back = "おかえりなさい!" - xp_bonus_for_time = "XP対象" - max_ability_power = "最大アビリティパワー" - unlock_this_by_clicking = "右クリックでアンロック:" - back = "戻る" - unlearn_all = "すべて忘れる" - unlearned_all = "すべて忘却した" +level = "レベル" +knowledge = "知識" +power_used = "使用パワー" +not_learned = "未習得" +xp = "XPまで" +welcome = "ようこそ!" +welcome_back = "おかえりなさい!" +xp_bonus_for_time = "XP対象" +max_ability_power = "最大アビリティパワー" +unlock_this_by_clicking = "右クリックでアンロック:" +back = "戻る" +unlearn_all = "すべて忘れる" +unlearned_all = "すべて忘却した" [snippets.adapt_menu] - may_not_unlearn = "忘却不可" - may_unlearn = "習得/忘却可能" - knowledge_cost = "知識コスト" - knowledge_available = "利用可能な知識" - already_learned = "習得済み" - unlearn_refund = "クリックして忘却し返金する" - no_refunds = "ハードコア:返金無効" - knowledge = "知識" - click_learn = "クリックして習得" - no_knowledge = "(知識がありません)" - you_only_have = "所持しているのは" - how_to_level_up = "スキルをレベルアップして最大パワーを上げましょう。" - not_enough_power = "パワーが足りません!各アビリティレベルには1パワー必要です。" - power = "パワー" - power_drain = "パワー消費" - learned = "習得した " - unlearned = "忘却した " - activator_block = "本棚" +may_not_unlearn = "忘却不可" +may_unlearn = "習得/忘却可能" +knowledge_cost = "知識コスト" +knowledge_available = "利用可能な知識" +already_learned = "習得済み" +unlearn_refund = "クリックして忘却し返金する" +no_refunds = "ハードコア:返金無効" +knowledge = "知識" +click_learn = "クリックして習得" +no_knowledge = "(知識がありません)" +you_only_have = "所持しているのは" +how_to_level_up = "スキルをレベルアップして最大パワーを上げましょう。" +not_enough_power = "パワーが足りません!各アビリティレベルには1パワー必要です。" +power = "パワー" +power_drain = "パワー消費" +learned = "習得した " +unlearned = "忘却した " +activator_block = "本棚" [snippets.knowledge_orb] - contains = "含む" - knowledge = "知識" - rightclick = "右クリック" - togainknowledge = "でこの知識を得る" - knowledge_orb = "知識のオーブ" +contains = "含む" +knowledge = "知識" +rightclick = "右クリック" +togainknowledge = "でこの知識を得る" +knowledge_orb = "知識のオーブ" [snippets.experience_orb] - contains = "含む" - xp = "経験値" - rightclick = "右クリック" - togainxp = "でこの経験値を得る" - xporb = "経験値オーブ" +contains = "含む" +xp = "経験値" +rightclick = "右クリック" +togainxp = "でこの経験値を得る" +xporb = "経験値オーブ" # skill [skill] [skill.agility] - name = "敏捷性" - icon = "⇉" - description = "敏捷性とは、障害物に直面しても素早くしなやかに動く能力である。" +name = "敏捷性" +icon = "⇉" +description = "敏捷性とは、障害物に直面しても素早くしなやかに動く能力である。" [skill.architect] - name = "建築家" - icon = "⬧" - description = "構造は世界の基盤である。現実はあなたの手の中にあり、あなたがコントロールする。" +name = "建築家" +icon = "⬧" +description = "構造は世界の基盤である。現実はあなたの手の中にあり、あなたがコントロールする。" [skill.axes] - name = "斧" - icon = "🪓" - description1 = "なぜ木を切り倒すのか、" - description2 = "モノ" - description3 = "を切った方が結果は同じだろう!" +name = "斧" +icon = "🪓" +description1 = "なぜ木を切り倒すのか、" +description2 = "モノ" +description3 = "を切った方が結果は同じだろう!" [skill.brewing] - name = "醸造" - icon = "❦" - description = "ダブルバブル、トリプルバブル、クアドラプルバブル――まだこのポーションを大釜に入れられない" +name = "醸造" +icon = "❦" +description = "ダブルバブル、トリプルバブル、クアドラプルバブル――まだこのポーションを大釜に入れられない" [skill.blocking] - name = "防御" - icon = "🛡" - description = "棒や石では骨は折れない、だが盾なら折れる。" +name = "防御" +icon = "🛡" +description = "棒や石では骨は折れない、だが盾なら折れる。" [skill.crafting] - name = "クラフト" - icon = "⌂" - description = "置くピースがもう無いなら、新しく作ればいいじゃない?" +name = "クラフト" +icon = "⌂" +description = "置くピースがもう無いなら、新しく作ればいいじゃない?" [skill.discovery] - name = "発見" - icon = "⚛" - description = "知覚が広がるにつれ、あなたの心は解き放たれ、見えなかったものが見えてくる。" +name = "発見" +icon = "⚛" +description = "知覚が広がるにつれ、あなたの心は解き放たれ、見えなかったものが見えてくる。" [skill.enchanting] - name = "エンチャント" - icon = "♰" - description = "何を言っているんだ?予言、幻視、迷信的なたわ言?" +name = "エンチャント" +icon = "♰" +description = "何を言っているんだ?予言、幻視、迷信的なたわ言?" [skill.excavation] - name = "発掘" - icon = "ᛳ" - description = "掘って掘って穴を掘る…" +name = "発掘" +icon = "ᛳ" +description = "掘って掘って穴を掘る…" [skill.herbalism] - name = "薬草学" - icon = "⚘" - description = "植物が見つからないが、種なら見つかる…これはもしかして、雑草?" +name = "薬草学" +icon = "⚘" +description = "植物が見つからないが、種なら見つかる…これはもしかして、雑草?" [skill.hunter] - name = "ハンター" - icon = "☠" - description = "狩りとは結果ではなく、その道のりにある。" +name = "ハンター" +icon = "☠" +description = "狩りとは結果ではなく、その道のりにある。" [skill.nether] - name = "ネザー" - icon = "₪" - description = "ネザーの深淵そのものより。" +name = "ネザー" +icon = "₪" +description = "ネザーの深淵そのものより。" [skill.pickaxe] - name = "つるはし" - icon = "⛏" - description = "ドワーフが鉱夫だが、俺も多少は学んだ。俺はスウェーデン人だ!" +name = "つるはし" +icon = "⛏" +description = "ドワーフが鉱夫だが、俺も多少は学んだ。俺はスウェーデン人だ!" [skill.ranged] - name = "遠距離" - icon = "🏹" - description = "距離こそが勝利の鍵であり、生存の鍵である。" +name = "遠距離" +icon = "🏹" +description = "距離こそが勝利の鍵であり、生存の鍵である。" [skill.rift] - name = "リフト" - icon = "❍" - description = "リフトは危険な力だが、あなたはその力を手懐けた。" +name = "リフト" +icon = "❍" +description = "リフトは危険な力だが、あなたはその力を手懐けた。" [skill.seaborne] - name = "海の民" - icon = "🎣" - description = "このスキルで、水の奇跡を自在に操ることができる。" +name = "海の民" +icon = "🎣" +description = "このスキルで、水の奇跡を自在に操ることができる。" [skill.stealth] - name = "隠密" - icon = "☯" - description = "見えざる者の技。影の中を歩け。" +name = "隠密" +icon = "☯" +description = "見えざる者の技。影の中を歩け。" [skill.swords] - name = "剣術" - icon = "⚔" - description = "グレイストーンの力をもって!" +name = "剣術" +icon = "⚔" +description = "グレイストーンの力をもって!" [skill.taming] - name = "飼育" - icon = "♥" - description = "オウムとミツバチ…それと、あなたは?" +name = "飼育" +icon = "♥" +description = "オウムとミツバチ…それと、あなたは?" [skill.tragoul] - name = "トラゴウル" - icon = "🗡" - description = "血は宇宙の血管を流れる。あなたの手で締め付けられながら。" +name = "トラゴウル" +icon = "🗡" +description = "血は宇宙の血管を流れる。あなたの手で締め付けられながら。" [skill.chronos] - name = "クロノス" - icon = "🕒" - description = "宇宙の時計を巻き、流れを体験せよ。時計を壊し、時計そのものになれ。" +name = "クロノス" +icon = "🕒" +description = "宇宙の時計を巻き、流れを体験せよ。時計を壊し、時計そのものになれ。" [skill.unarmed] - name = "素手" - icon = "»" - description = "武器が無くとも、力が無いわけではない。" +name = "素手" +icon = "»" +description = "武器が無くとも、力が無いわけではない。" # agility [agility] [agility.armor_up] - name = "アーマーアップ" - description = "ダッシュし続けるほど防具が強化される!" - lore1 = "最大防具値" - lore2 = "防具強化時間" - lore = ["最大防具値", "防具強化時間"] +name = "アーマーアップ" +description = "ダッシュし続けるほど防具が強化される!" +lore1 = "最大防具値" +lore2 = "防具強化時間" +lore = ["最大防具値", "防具強化時間"] [agility.ladder_slide] - name = "ハシゴスライド" - description = "ハシゴの上り下りがどちらも大幅に速くなる。" - lore1 = "ハシゴ速度倍率" - lore2 = "高速下降速度" - lore = ["ハシゴ速度倍率", "高速下降速度"] +name = "ハシゴスライド" +description = "ハシゴの上り下りがどちらも大幅に速くなる。" +lore1 = "ハシゴ速度倍率" +lore2 = "高速下降速度" +lore = ["ハシゴ速度倍率", "高速下降速度"] [agility.super_jump] - name = "スーパージャンプ" - description = "圧倒的な高さのアドバンテージ。" - lore1 = "最大ジャンプ高度" - lore2 = "スニーク + ジャンプでスーパージャンプ!" - lore = ["最大ジャンプ高度", "スニーク + ジャンプでスーパージャンプ!"] +name = "スーパージャンプ" +description = "圧倒的な高さのアドバンテージ。" +lore1 = "最大ジャンプ高度" +lore2 = "スニーク + ジャンプでスーパージャンプ!" +lore = ["最大ジャンプ高度", "スニーク + ジャンプでスーパージャンプ!"] [agility.wall_jump] - name = "壁ジャンプ" - description = "空中で壁に向かってシフトを押し続けると壁に張り付いてジャンプ!" - lore1 = "最大ジャンプ回数" - lore2 = "ジャンプの高さ" - lore = ["最大ジャンプ回数", "ジャンプの高さ"] +name = "壁ジャンプ" +description = "空中で壁に向かってシフトを押し続けると壁に張り付いてジャンプ!" +lore1 = "最大ジャンプ回数" +lore2 = "ジャンプの高さ" +lore = ["最大ジャンプ回数", "ジャンプの高さ"] [agility.wind_up] - name = "ウィンドアップ" - description = "ダッシュし続けるほど加速する!" - lore1 = "最高速度" - lore2 = "加速時間" - lore = ["最高速度", "加速時間"] +name = "ウィンドアップ" +description = "ダッシュし続けるほど加速する!" +lore1 = "最高速度" +lore2 = "加速時間" +lore = ["最高速度", "加速時間"] # architect [architect] [architect.elevator] - name = "エレベーター" - description = "垂直方向に高速テレポートするエレベーターを建設できる!" - lore1 = "エレベーターのレシピを解放:X=羊毛、Y=エンダーパール" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["エレベーターのレシピを解放:X=羊毛、Y=エンダーパール", "XXX", "XYX", "XXX"] +name = "エレベーター" +description = "垂直方向に高速テレポートするエレベーターを建設できる!" +lore1 = "エレベーターのレシピを解放:X=羊毛、Y=エンダーパール" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["エレベーターのレシピを解放:X=羊毛、Y=エンダーパール", "XXX", "XYX", "XXX"] [architect.foundation] - name = "魔法の足場" - description = "スニークして自分の足元に仮の足場を生成できる!" - lore1 = "魔法で生成:" - lore2 = "足元にブロック!" - lore = ["魔法で生成:", "足元にブロック!"] +name = "魔法の足場" +description = "スニークして自分の足元に仮の足場を生成できる!" +lore1 = "魔法で生成:" +lore2 = "足元にブロック!" +lore = ["魔法で生成:", "足元にブロック!"] [architect.glass] - name = "シルクタッチガラス" - description = "素手でガラスブロックを壊してもガラスが失われなくなる!" - lore1 = "素手にガラスへのシルクタッチ効果を付与" - lore = ["素手にガラスへのシルクタッチ効果を付与"] +name = "シルクタッチガラス" +description = "素手でガラスブロックを壊してもガラスが失われなくなる!" +lore1 = "素手にガラスへのシルクタッチ効果を付与" +lore = ["素手にガラスへのシルクタッチ効果を付与"] [architect.wireless_redstone] - name = "レッドストーンリモコン" - description = "レッドストーンのたいまつを使って遠隔でレッドストーンを切り替えられる!" - lore1 = "ターゲット + レッドストーンのたいまつ + エンダーパール = レッドストーンリモコン1個" - lore = ["ターゲット + レッドストーンのたいまつ + エンダーパール = レッドストーンリモコン1個"] +name = "レッドストーンリモコン" +description = "レッドストーンのたいまつを使って遠隔でレッドストーンを切り替えられる!" +lore1 = "ターゲット + レッドストーンのたいまつ + エンダーパール = レッドストーンリモコン1個" +lore = ["ターゲット + レッドストーンのたいまつ + エンダーパール = レッドストーンリモコン1個"] [architect.placement] - name = "ビルダーワンド" - description = "複数のブロックを一度に設置可能!スニークして、見ているブロックと同じブロックを手に持って設置しよう。範囲をトリガーするには少し動く必要がある場合もある!" - lore1 = "必要数" - lore2 = "ブロックを手に持って設置する" - lore3 = "素材ビルダーワンド" - lore = ["必要数", "ブロックを手に持って設置する", "素材ビルダーワンド"] +name = "ビルダーワンド" +description = "複数のブロックを一度に設置可能!スニークして、見ているブロックと同じブロックを手に持って設置しよう。範囲をトリガーするには少し動く必要がある場合もある!" +lore1 = "必要数" +lore2 = "ブロックを手に持って設置する" +lore3 = "素材ビルダーワンド" +lore = ["必要数", "ブロックを手に持って設置する", "素材ビルダーワンド"] # axe [axe] [axe.chop] - name = "斧で伐採" - description = "木の根元を右クリックして一気に伐採!" - lore1 = "1回の伐採ブロック数" - lore2 = "伐採クールダウン" - lore3 = "ツール消耗" - lore = ["1回の伐採ブロック数", "伐採クールダウン", "ツール消耗"] +name = "斧で伐採" +description = "木の根元を右クリックして一気に伐採!" +lore1 = "1回の伐採ブロック数" +lore2 = "伐採クールダウン" +lore3 = "ツール消耗" +lore = ["1回の伐採ブロック数", "伐採クールダウン", "ツール消耗"] [axe.log_swap] - name = "ルーシーの丸太交換" - description = "作業台で丸太の種類を変えよう!" - lore1 = "任意の丸太8本 + 苗木1本 = 苗木の種類の丸太8本" - lore = ["任意の丸太8本 + 苗木1本 = 苗木の種類の丸太8本"] +name = "ルーシーの丸太交換" +description = "作業台で丸太の種類を変えよう!" +lore1 = "任意の丸太8本 + 苗木1本 = 苗木の種類の丸太8本" +lore = ["任意の丸太8本 + 苗木1本 = 苗木の種類の丸太8本"] [axe.drop_to_inventory] - name = "斧ドロップ→インベントリ" +name = "斧ドロップ→インベントリ" [axe.ground_smash] - name = "斧グラウンドスマッシュ" - description = "ジャンプしてからスニークで周囲の敵を叩き潰す。" - lore1 = "ダメージ" - lore2 = "ブロック半径" - lore3 = "吹き飛ばし力" - lore4 = "スマッシュクールダウン" - lore = ["ダメージ", "ブロック半径", "吹き飛ばし力", "スマッシュクールダウン"] +name = "斧グラウンドスマッシュ" +description = "ジャンプしてからスニークで周囲の敵を叩き潰す。" +lore1 = "ダメージ" +lore2 = "ブロック半径" +lore3 = "吹き飛ばし力" +lore4 = "スマッシュクールダウン" +lore = ["ダメージ", "ブロック半径", "吹き飛ばし力", "スマッシュクールダウン"] [axe.leaf_miner] - name = "リーフマイナー" - description = "大量の葉を一度に破壊できる!" - lore1 = "スニークして葉を採掘" - lore2 = "リーフマイニング範囲" - lore3 = "葉からのドロップは取得不可(悪用防止)" - lore = ["スニークして葉を採掘", "リーフマイニング範囲", "葉からのドロップは取得不可(悪用防止)"] +name = "リーフマイナー" +description = "大量の葉を一度に破壊できる!" +lore1 = "スニークして葉を採掘" +lore2 = "リーフマイニング範囲" +lore3 = "葉からのドロップは取得不可(悪用防止)" +lore = ["スニークして葉を採掘", "リーフマイニング範囲", "葉からのドロップは取得不可(悪用防止)"] [axe.wood_miner] - name = "ウッドマイナー" - description = "大量の木材を一度に破壊できる!" - lore1 = "スニークして木材/原木を採掘(板材は対象外)" - lore2 = "ウッドマイニング範囲" - lore3 = "ドロップ→インベントリ対応" - lore = ["スニークして木材/原木を採掘(板材は対象外)", "ウッドマイニング範囲", "ドロップ→インベントリ対応"] +name = "ウッドマイナー" +description = "大量の木材を一度に破壊できる!" +lore1 = "スニークして木材/原木を採掘(板材は対象外)" +lore2 = "ウッドマイニング範囲" +lore3 = "ドロップ→インベントリ対応" +lore = ["スニークして木材/原木を採掘(板材は対象外)", "ウッドマイニング範囲", "ドロップ→インベントリ対応"] # brewing [brewing] [brewing.lingering] - name = "残留醸造" - description = "醸造したポーションの効果が長持ちする!" - lore1 = "持続時間" - lore2 = "持続時間" - lore = ["持続時間", "持続時間"] +name = "残留醸造" +description = "醸造したポーションの効果が長持ちする!" +lore1 = "持続時間" +lore2 = "持続時間" +lore = ["持続時間", "持続時間"] [brewing.super_heated] - name = "超加熱醸造" - description = "醸造台は熱いほど速く動作する。" - lore1 = "接触する火ブロック1つにつき" - lore2 = "接触する溶岩ブロック1つにつき" - lore = ["接触する火ブロック1つにつき", "接触する溶岩ブロック1つにつき"] +name = "超加熱醸造" +description = "醸造台は熱いほど速く動作する。" +lore1 = "接触する火ブロック1つにつき" +lore2 = "接触する溶岩ブロック1つにつき" +lore = ["接触する火ブロック1つにつき", "接触する溶岩ブロック1つにつき"] [brewing.darkness] - name = "瓶詰めの暗闇" - description = "なぜこれが必要かは分からないけど、はいどうぞ!" - lore1 = "暗視ポーション + 黒コンクリート = 暗闇のポーション(30秒)" - lore2 = "注意:このポーションを使うとダッシュできなくなる!" - lore = ["暗視ポーション + 黒コンクリート = 暗闇のポーション(30秒)", "注意:このポーションを使うとダッシュできなくなる!"] +name = "瓶詰めの暗闇" +description = "なぜこれが必要かは分からないけど、はいどうぞ!" +lore1 = "暗視ポーション + 黒コンクリート = 暗闇のポーション(30秒)" +lore2 = "注意:このポーションを使うとダッシュできなくなる!" +lore = ["暗視ポーション + 黒コンクリート = 暗闇のポーション(30秒)", "注意:このポーションを使うとダッシュできなくなる!"] [brewing.haste] - name = "瓶詰めの採掘速度上昇" - description = "効率強化では足りない時に" - lore1 = "俊敏のポーション + アメジストの欠片 = 採掘速度上昇のポーション(60秒)" - lore2 = "俊敏のポーション + アメジストブロック = 採掘速度上昇IIのポーション(30秒)" - lore = ["俊敏のポーション + アメジストの欠片 = 採掘速度上昇のポーション(60秒)", "俊敏のポーション + アメジストブロック = 採掘速度上昇IIのポーション(30秒)"] +name = "瓶詰めの採掘速度上昇" +description = "効率強化では足りない時に" +lore1 = "俊敏のポーション + アメジストの欠片 = 採掘速度上昇のポーション(60秒)" +lore2 = "俊敏のポーション + アメジストブロック = 採掘速度上昇IIのポーション(30秒)" +lore = ["俊敏のポーション + アメジストの欠片 = 採掘速度上昇のポーション(60秒)", "俊敏のポーション + アメジストブロック = 採掘速度上昇IIのポーション(30秒)"] [brewing.absorption] - name = "瓶詰めの衝撃吸収" - description = "体を硬くしろ!" - lore1 = "即時回復 + クォーツ = 衝撃吸収のポーション(60秒)" - lore2 = "即時回復 + クォーツブロック = 衝撃吸収IIのポーション(30秒)" - lore = ["即時回復 + クォーツ = 衝撃吸収のポーション(60秒)", "即時回復 + クォーツブロック = 衝撃吸収IIのポーション(30秒)"] +name = "瓶詰めの衝撃吸収" +description = "体を硬くしろ!" +lore1 = "即時回復 + クォーツ = 衝撃吸収のポーション(60秒)" +lore2 = "即時回復 + クォーツブロック = 衝撃吸収IIのポーション(30秒)" +lore = ["即時回復 + クォーツ = 衝撃吸収のポーション(60秒)", "即時回復 + クォーツブロック = 衝撃吸収IIのポーション(30秒)"] [brewing.fatigue] - name = "瓶詰めの採掘疲労" - description = "体を弱体化させろ!" - lore1 = "弱体化のポーション + スライムボール = 採掘疲労のポーション(30秒)" - lore2 = "弱体化のポーション + スライムブロック = 採掘疲労IIのポーション(15秒)" - lore = ["弱体化のポーション + スライムボール = 採掘疲労のポーション(30秒)", "弱体化のポーション + スライムブロック = 採掘疲労IIのポーション(15秒)"] +name = "瓶詰めの採掘疲労" +description = "体を弱体化させろ!" +lore1 = "弱体化のポーション + スライムボール = 採掘疲労のポーション(30秒)" +lore2 = "弱体化のポーション + スライムブロック = 採掘疲労IIのポーション(15秒)" +lore = ["弱体化のポーション + スライムボール = 採掘疲労のポーション(30秒)", "弱体化のポーション + スライムブロック = 採掘疲労IIのポーション(15秒)"] [brewing.hunger] - name = "瓶詰めの空腹" - description = "飽くなき食欲に!" - lore1 = "奇妙なポーション + 腐った肉 = 空腹のポーション(30秒)" - lore2 = "弱体化のポーション + 腐った肉 = 空腹IIIのポーション(15秒)" - lore = ["奇妙なポーション + 腐った肉 = 空腹のポーション(30秒)", "弱体化のポーション + 腐った肉 = 空腹IIIのポーション(15秒)"] +name = "瓶詰めの空腹" +description = "飽くなき食欲に!" +lore1 = "奇妙なポーション + 腐った肉 = 空腹のポーション(30秒)" +lore2 = "弱体化のポーション + 腐った肉 = 空腹IIIのポーション(15秒)" +lore = ["奇妙なポーション + 腐った肉 = 空腹のポーション(30秒)", "弱体化のポーション + 腐った肉 = 空腹IIIのポーション(15秒)"] [brewing.nausea] - name = "瓶詰めの吐き気" - description = "気分が悪くなるぞ!" - lore1 = "奇妙なポーション + 茶色キノコ = 吐き気のポーション(16秒)" - lore2 = "奇妙なポーション + 真紅のキノコ = 吐き気IIのポーション(8秒)" - lore = ["奇妙なポーション + 茶色キノコ = 吐き気のポーション(16秒)", "奇妙なポーション + 真紅のキノコ = 吐き気IIのポーション(8秒)"] +name = "瓶詰めの吐き気" +description = "気分が悪くなるぞ!" +lore1 = "奇妙なポーション + 茶色キノコ = 吐き気のポーション(16秒)" +lore2 = "奇妙なポーション + 真紅のキノコ = 吐き気IIのポーション(8秒)" +lore = ["奇妙なポーション + 茶色キノコ = 吐き気のポーション(16秒)", "奇妙なポーション + 真紅のキノコ = 吐き気IIのポーション(8秒)"] [brewing.blindness] - name = "瓶詰めの盲目" - description = "あなたはひどい人だ…" - lore1 = "奇妙なポーション + イカスミ = 盲目のポーション(30秒)" - lore2 = "奇妙なポーション + 輝くイカスミ = 盲目IIのポーション(15秒)" - lore = ["奇妙なポーション + イカスミ = 盲目のポーション(30秒)", "奇妙なポーション + 輝くイカスミ = 盲目IIのポーション(15秒)"] +name = "瓶詰めの盲目" +description = "あなたはひどい人だ…" +lore1 = "奇妙なポーション + イカスミ = 盲目のポーション(30秒)" +lore2 = "奇妙なポーション + 輝くイカスミ = 盲目IIのポーション(15秒)" +lore = ["奇妙なポーション + イカスミ = 盲目のポーション(30秒)", "奇妙なポーション + 輝くイカスミ = 盲目IIのポーション(15秒)"] [brewing.resistance] - name = "瓶詰めの耐性" - description = "究極の防御!" - lore1 = "奇妙なポーション + 鉄インゴット = 耐性のポーション(60秒)" - lore2 = "奇妙なポーション + 鉄ブロック = 耐性IIのポーション(30秒)" - lore = ["奇妙なポーション + 鉄インゴット = 耐性のポーション(60秒)", "奇妙なポーション + 鉄ブロック = 耐性IIのポーション(30秒)"] +name = "瓶詰めの耐性" +description = "究極の防御!" +lore1 = "奇妙なポーション + 鉄インゴット = 耐性のポーション(60秒)" +lore2 = "奇妙なポーション + 鉄ブロック = 耐性IIのポーション(30秒)" +lore = ["奇妙なポーション + 鉄インゴット = 耐性のポーション(60秒)", "奇妙なポーション + 鉄ブロック = 耐性IIのポーション(30秒)"] [brewing.health_boost] - name = "瓶詰めの生命力" - description = "最大体力では足りない時に…" - lore1 = "即時回復ポーション + 金のリンゴ = 体力増強のポーション(120秒)" - lore2 = "即時回復ポーション + エンチャント金のリンゴ = 体力増強IIのポーション(120秒)" - lore = ["即時回復ポーション + 金のリンゴ = 体力増強のポーション(120秒)", "即時回復ポーション + エンチャント金のリンゴ = 体力増強IIのポーション(120秒)"] +name = "瓶詰めの生命力" +description = "最大体力では足りない時に…" +lore1 = "即時回復ポーション + 金のリンゴ = 体力増強のポーション(120秒)" +lore2 = "即時回復ポーション + エンチャント金のリンゴ = 体力増強IIのポーション(120秒)" +lore = ["即時回復ポーション + 金のリンゴ = 体力増強のポーション(120秒)", "即時回復ポーション + エンチャント金のリンゴ = 体力増強IIのポーション(120秒)"] [brewing.decay] - name = "瓶詰めの衰退" - description = "腐敗物がこんなに役立つとは!" - lore1 = "弱体化のポーション + 毒のジャガイモ = ウィザーのポーション(16秒)" - lore2 = "弱体化のポーション + 真紅の根 = ウィザーIIのポーション(8秒)" - lore = ["弱体化のポーション + 毒のジャガイモ = ウィザーのポーション(16秒)", "弱体化のポーション + 真紅の根 = ウィザーIIのポーション(8秒)"] +name = "瓶詰めの衰退" +description = "腐敗物がこんなに役立つとは!" +lore1 = "弱体化のポーション + 毒のジャガイモ = ウィザーのポーション(16秒)" +lore2 = "弱体化のポーション + 真紅の根 = ウィザーIIのポーション(8秒)" +lore = ["弱体化のポーション + 毒のジャガイモ = ウィザーのポーション(16秒)", "弱体化のポーション + 真紅の根 = ウィザーIIのポーション(8秒)"] [brewing.saturation] - name = "瓶詰めの満腹" - description = "なあ…もうお腹いっぱいなんだけど…" - lore1 = "再生のポーション + ベイクドポテト = 満腹のポーション" - lore2 = "再生のポーション + 干し草の俵 = 満腹IIのポーション" - lore = ["再生のポーション + ベイクドポテト = 満腹のポーション", "再生のポーション + 干し草の俵 = 満腹IIのポーション"] +name = "瓶詰めの満腹" +description = "なあ…もうお腹いっぱいなんだけど…" +lore1 = "再生のポーション + ベイクドポテト = 満腹のポーション" +lore2 = "再生のポーション + 干し草の俵 = 満腹IIのポーション" +lore = ["再生のポーション + ベイクドポテト = 満腹のポーション", "再生のポーション + 干し草の俵 = 満腹IIのポーション"] # blocking [blocking] [blocking.chain_armorer] - name = "メフィストフェレスの鎖" - description = "チェーン装備をクラフトできるようになる" - lore1 = "クラフトレシピは通常の防具と同じだが、鉄塊を使用する" - lore = ["クラフトレシピは通常の防具と同じだが、鉄塊を使用する"] +name = "メフィストフェレスの鎖" +description = "チェーン装備をクラフトできるようになる" +lore1 = "クラフトレシピは通常の防具と同じだが、鉄塊を使用する" +lore = ["クラフトレシピは通常の防具と同じだが、鉄塊を使用する"] [blocking.horse_armorer] - name = "クラフト可能な馬鎧" - description = "馬の鎧をクラフトできるようになる" - lore1 = "サドルを使用したい素材で囲んでクラフト" - lore = ["サドルを使用したい素材で囲んでクラフト"] +name = "クラフト可能な馬鎧" +description = "馬の鎧をクラフトできるようになる" +lore1 = "サドルを使用したい素材で囲んでクラフト" +lore = ["サドルを使用したい素材で囲んでクラフト"] [blocking.saddle_crafter] - name = "クラフト可能なサドル" - description = "革でサドルをクラフトする" - lore1 = "レシピ:革5個:" - lore = ["レシピ:革5個:"] +name = "クラフト可能なサドル" +description = "革でサドルをクラフトする" +lore1 = "レシピ:革5個:" +lore = ["レシピ:革5個:"] [blocking.multi_armor] - name = "マルチアーマー" - description = "エリトラを防具に装着する" - lore1 = "移動に最適な素晴らしいスキル。" - lore2 = "防具とエリトラを動的に合体・変更可能!" - lore3 = "合体するには、インベントリ内でアイテムをシフトクリック。" - lore4 = "防具を分離するには、スニークしながらドロップで分解。" - lore5 = "マルチアーマーが破壊されると、中のアイテムはすべて失われる。" - lore6 = "防具なんて要らない、がっかりだ…" - lore = ["移動に最適な素晴らしいスキル。", "防具とエリトラを動的に合体・変更可能!", "合体するには、インベントリ内でアイテムをシフトクリック。", "防具を分離するには、スニークしながらドロップで分解。", "マルチアーマーが破壊されると、中のアイテムはすべて失われる。", "防具なんて要らない、がっかりだ…"] +name = "マルチアーマー" +description = "エリトラを防具に装着する" +lore1 = "移動に最適な素晴らしいスキル。" +lore2 = "防具とエリトラを動的に合体・変更可能!" +lore3 = "合体するには、インベントリ内でアイテムをシフトクリック。" +lore4 = "防具を分離するには、スニークしながらドロップで分解。" +lore5 = "マルチアーマーが破壊されると、中のアイテムはすべて失われる。" +lore6 = "防具なんて要らない、がっかりだ…" +lore = ["移動に最適な素晴らしいスキル。", "防具とエリトラを動的に合体・変更可能!", "合体するには、インベントリ内でアイテムをシフトクリック。", "防具を分離するには、スニークしながらドロップで分解。", "マルチアーマーが破壊されると、中のアイテムはすべて失われる。", "防具なんて要らない、がっかりだ…"] # crafting [crafting] [crafting.deconstruction] - name = "分解" - description = "ブロックやアイテムを再利用可能な基本素材に分解!" - lore1 = "任意のアイテムを地面にドロップ。" - lore2 = "その後、スニークしながらハサミで右クリック" - lore = ["任意のアイテムを地面にドロップ。", "その後、スニークしながらハサミで右クリック"] +name = "分解" +description = "ブロックやアイテムを再利用可能な基本素材に分解!" +lore1 = "任意のアイテムを地面にドロップ。" +lore2 = "その後、スニークしながらハサミで右クリック" +lore = ["任意のアイテムを地面にドロップ。", "その後、スニークしながらハサミで右クリック"] [crafting.xp] - name = "クラフトXP" - description = "クラフト時にパッシブXPを獲得" - lore1 = "クラフト時にXPを獲得" - lore = ["クラフト時にXPを獲得"] +name = "クラフトXP" +description = "クラフト時にパッシブXPを獲得" +lore1 = "クラフト時にXPを獲得" +lore = ["クラフト時にXPを獲得"] [crafting.reconstruction] - name = "鉱石再構築" - description = "基本素材から鉱石を再クラフト!" - lore1 = "ドロップ8個 + ホスト1個 = 鉱石1個(不定形)" - lore2 = "ドロップは製錬済みである必要あり(該当する場合)" - lore3 = "スクラップ、ネザークォーツ、エメラルドなどは対象外" - lore4 = "ホスト = 外殻。例:石、ネザーラック、深層岩" - lore = ["ドロップ8個 + ホスト1個 = 鉱石1個(不定形)", "ドロップは製錬済みである必要あり(該当する場合)", "スクラップ、ネザークォーツ、エメラルドなどは対象外", "ホスト = 外殻。例:石、ネザーラック、深層岩"] +name = "鉱石再構築" +description = "基本素材から鉱石を再クラフト!" +lore1 = "ドロップ8個 + ホスト1個 = 鉱石1個(不定形)" +lore2 = "ドロップは製錬済みである必要あり(該当する場合)" +lore3 = "スクラップ、ネザークォーツ、エメラルドなどは対象外" +lore4 = "ホスト = 外殻。例:石、ネザーラック、深層岩" +lore = ["ドロップ8個 + ホスト1個 = 鉱石1個(不定形)", "ドロップは製錬済みである必要あり(該当する場合)", "スクラップ、ネザークォーツ、エメラルドなどは対象外", "ホスト = 外殻。例:石、ネザーラック、深層岩"] [crafting.leather] - name = "クラフト可能な革" - description = "腐った肉から革をクラフト" - lore1 = "腐った肉を焚き火に乗せるだけ!" - lore = ["腐った肉を焚き火に乗せるだけ!"] +name = "クラフト可能な革" +description = "腐った肉から革をクラフト" +lore1 = "腐った肉を焚き火に乗せるだけ!" +lore = ["腐った肉を焚き火に乗せるだけ!"] [crafting.backpacks] - name = "ブティリエのバックパック!" - description = "Mojangのバンドルをゲームに導入!" - lore1 = "サバイバルモードでのみ使用可能" - lore2 = "XLX:革、リード、革" - lore3 = "XSX:革、樽、革" - lore4 = "XCX:革、チェスト、革" - lore = ["サバイバルモードでのみ使用可能", "XLX:革、リード、革", "XSX:革、樽、革", "XCX:革、チェスト、革"] +name = "ブティリエのバックパック!" +description = "Mojangのバンドルをゲームに導入!" +lore1 = "サバイバルモードでのみ使用可能" +lore2 = "XLX:革、リード、革" +lore3 = "XSX:革、樽、革" +lore4 = "XCX:革、チェスト、革" +lore = ["サバイバルモードでのみ使用可能", "XLX:革、リード、革", "XSX:革、樽、革", "XCX:革、チェスト、革"] [crafting.stations] - name = "ポータブルテーブル!" - description = "手のひらでテーブルを使おう!" - lore2 = "テーブルを閉じた時に残したアイテムは永遠に失われます!" - lore3 = "対応テーブル:金床、作業台、砥石、製図台、石切り台、織機" - lore = ["テーブルを閉じた時に残したアイテムは永遠に失われます!", "対応テーブル:金床、作業台、砥石、製図台、石切り台、織機"] +name = "ポータブルテーブル!" +description = "手のひらでテーブルを使おう!" +lore2 = "テーブルを閉じた時に残したアイテムは永遠に失われます!" +lore3 = "対応テーブル:金床、作業台、砥石、製図台、石切り台、織機" +lore = ["テーブルを閉じた時に残したアイテムは永遠に失われます!", "対応テーブル:金床、作業台、砥石、製図台、石切り台、織機"] [crafting.skulls] - name = "クラフト可能な頭蓋骨!" - description = "素材を使ってモブの頭蓋骨をクラフト!" - lore1 = "骨ブロックを以下の素材で囲んで頭蓋骨を作成:" - lore2 = "ゾンビ:腐った肉" - lore3 = "スケルトン:骨" - lore4 = "クリーパー:火薬" - lore5 = "ウィザー:ネザーレンガ" - lore6 = "ドラゴン:ドラゴンブレス" - lore = ["骨ブロックを以下の素材で囲んで頭蓋骨を作成:", "ゾンビ:腐った肉", "スケルトン:骨", "クリーパー:火薬", "ウィザー:ネザーレンガ", "ドラゴン:ドラゴンブレス"] +name = "クラフト可能な頭蓋骨!" +description = "素材を使ってモブの頭蓋骨をクラフト!" +lore1 = "骨ブロックを以下の素材で囲んで頭蓋骨を作成:" +lore2 = "ゾンビ:腐った肉" +lore3 = "スケルトン:骨" +lore4 = "クリーパー:火薬" +lore5 = "ウィザー:ネザーレンガ" +lore6 = "ドラゴン:ドラゴンブレス" +lore = ["骨ブロックを以下の素材で囲んで頭蓋骨を作成:", "ゾンビ:腐った肉", "スケルトン:骨", "クリーパー:火薬", "ウィザー:ネザーレンガ", "ドラゴン:ドラゴンブレス"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "時のボトル" - description = "時間を蓄える時空のボトルを持ち歩き、蓄えた時間を消費して時間経過ブロック、成長可能ブロック、子どもの動物などのエイジャブルエンティティを加速させる。レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶。" - lore1 = "ティックごとの蓄積秒数" - lore2 = "蓄えた1秒あたりの時間加速" - lore3 = "レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶" - lore = ["ティックごとの蓄積秒数", "蓄えた1秒あたりの時間加速", "レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶"] +name = "時のボトル" +description = "時間を蓄える時空のボトルを持ち歩き、蓄えた時間を消費して時間経過ブロック、成長可能ブロック、子どもの動物などのエイジャブルエンティティを加速させる。レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶。" +lore1 = "ティックごとの蓄積秒数" +lore2 = "蓄えた1秒あたりの時間加速" +lore3 = "レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶" +lore = ["ティックごとの蓄積秒数", "蓄えた1秒あたりの時間加速", "レシピ(不定形):俊敏のポーション + 時計 + ガラス瓶"] [chronos.aberrant_touch] - name = "異常な触れ" - description = "近接攻撃が空腹を消費して鈍化をスタックさせ、厳格なPvP制限付きで5スタック時にターゲットを拘束する。" - lore1 = "近接攻撃で鈍化をスタック" - lore2 = "PvE鈍化持続時間上限" - lore3 = "PvP鈍化増幅上限" - lore = ["近接攻撃で鈍化をスタック", "PvE鈍化持続時間上限", "PvP鈍化増幅上限"] +name = "異常な触れ" +description = "近接攻撃が空腹を消費して鈍化をスタックさせ、厳格なPvP制限付きで5スタック時にターゲットを拘束する。" +lore1 = "近接攻撃で鈍化をスタック" +lore2 = "PvE鈍化持続時間上限" +lore3 = "PvP鈍化増幅上限" +lore = ["近接攻撃で鈍化をスタック", "PvE鈍化持続時間上限", "PvP鈍化増幅上限"] [chronos.instant_recall] - name = "インスタントリコール" - description = "時計を手に持って左または右クリックすると、体力と空腹が回復された状態で最近のスナップショットに巻き戻す。" - lore1 = "巻き戻し時間" - lore2 = "クールダウン" - lore3 = "インベントリは巻き戻されない" - lore = ["巻き戻し時間", "クールダウン", "インベントリは巻き戻されない"] +name = "インスタントリコール" +description = "時計を手に持って左または右クリックすると、体力と空腹が回復された状態で最近のスナップショットに巻き戻す。" +lore1 = "巻き戻し時間" +lore2 = "クールダウン" +lore3 = "インベントリは巻き戻されない" +lore = ["巻き戻し時間", "クールダウン", "インベントリは巻き戻されない"] [chronos.time_bomb] - name = "タイムボム" - description = "クラフトしたクロノボムを投げて時空の場を生成し、エンティティを減速させ、飛び道具を凍結させる。" - lore1 = "時空フィールド半径" - lore2 = "時空フィールド持続時間" - lore3 = "ボムクールダウン" - lore4 = "レシピ(不定形):時計 + 雪玉 + ダイヤモンド + 砂" - lore = ["時空フィールド半径", "時空フィールド持続時間", "ボムクールダウン", "レシピ(不定形):時計 + 雪玉 + ダイヤモンド + 砂"] +name = "タイムボム" +description = "クラフトしたクロノボムを投げて時空の場を生成し、エンティティを減速させ、飛び道具を凍結させる。" +lore1 = "時空フィールド半径" +lore2 = "時空フィールド持続時間" +lore3 = "ボムクールダウン" +lore4 = "レシピ(不定形):時計 + 雪玉 + ダイヤモンド + 砂" +lore = ["時空フィールド半径", "時空フィールド持続時間", "ボムクールダウン", "レシピ(不定形):時計 + 雪玉 + ダイヤモンド + 砂"] # discovery [discovery] [discovery.armor] - name = "ワールドアーマー" - description = "周囲のブロック硬度に応じたパッシブ防具。" - lore1 = "パッシブ防具" - lore2 = "周囲のブロック硬度に基づく" - lore3 = "防具強度:" - lore = ["パッシブ防具", "周囲のブロック硬度に基づく", "防具強度:"] +name = "ワールドアーマー" +description = "周囲のブロック硬度に応じたパッシブ防具。" +lore1 = "パッシブ防具" +lore2 = "周囲のブロック硬度に基づく" +lore3 = "防具強度:" +lore = ["パッシブ防具", "周囲のブロック硬度に基づく", "防具強度:"] [discovery.unity] - name = "実験的ユニティ" - description = "経験値オーブを回収するとランダムなスキルにXPが追加される。" - lore1 = "XP " - lore2 = "オーブごと" - lore = ["XP ", "オーブごと"] +name = "実験的ユニティ" +description = "経験値オーブを回収するとランダムなスキルにXPが追加される。" +lore1 = "XP " +lore2 = "オーブごと" +lore = ["XP ", "オーブごと"] [discovery.resist] - name = "実験的レジスタンス" - description = "体力が5ハート以下に落ちるか致命的なダメージを受けた時のみ、経験値を消費してダメージを軽減する。" - lore0 = "クリティカルヘルス時(5ハート以下)にのみ15秒に1回発動" - lore1 = " 軽減ダメージ" - lore2 = "消費経験値" - lore = ["クリティカルヘルス時(5ハート以下)にのみ15秒に1回発動", " 軽減ダメージ", "消費経験値"] +name = "実験的レジスタンス" +description = "体力が5ハート以下に落ちるか致命的なダメージを受けた時のみ、経験値を消費してダメージを軽減する。" +lore0 = "クリティカルヘルス時(5ハート以下)にのみ15秒に1回発動" +lore1 = " 軽減ダメージ" +lore2 = "消費経験値" +lore = ["クリティカルヘルス時(5ハート以下)にのみ15秒に1回発動", " 軽減ダメージ", "消費経験値"] [discovery.villager] - name = "村人の魅了" - description = "村人とのより良い取引が可能になる!" - lore1 = "村人との取引ごとにXPを消費" - lore2 = "取引ごとにXPを消費して取引を強化するチャンス" - lore3 = "取引ごとの必要XP消費量" - lore = ["村人との取引ごとにXPを消費", "取引ごとにXPを消費して取引を強化するチャンス", "取引ごとの必要XP消費量"] +name = "村人の魅了" +description = "村人とのより良い取引が可能になる!" +lore1 = "村人との取引ごとにXPを消費" +lore2 = "取引ごとにXPを消費して取引を強化するチャンス" +lore3 = "取引ごとの必要XP消費量" +lore = ["村人との取引ごとにXPを消費", "取引ごとにXPを消費して取引を強化するチャンス", "取引ごとの必要XP消費量"] # enchanting [enchanting] [enchanting.lapis_return] - name = "ラピスリターン" - description = "1レベル分追加のXPを消費する代わりに、ラピスラズリが無料で返ってくるチャンスがある" - lore1 = "レベルごとにエンチャント費用が1増加するが、最大3個のラピスを返却する可能性がある" - lore = ["レベルごとにエンチャント費用が1増加するが、最大3個のラピスを返却する可能性がある"] +name = "ラピスリターン" +description = "1レベル分追加のXPを消費する代わりに、ラピスラズリが無料で返ってくるチャンスがある" +lore1 = "レベルごとにエンチャント費用が1増加するが、最大3個のラピスを返却する可能性がある" +lore = ["レベルごとにエンチャント費用が1増加するが、最大3個のラピスを返却する可能性がある"] [enchanting.quick_enchant] - name = "クイッククリックエンチャント" - description = "エンチャントの本をアイテムに直接クリックしてエンチャント。" - lore1 = "最大合成レベル" - lore2 = "アイテムにエンチャントできる最大値は" - lore3 = "パワー" - lore = ["最大合成レベル", "アイテムにエンチャントできる最大値は", "パワー"] +name = "クイッククリックエンチャント" +description = "エンチャントの本をアイテムに直接クリックしてエンチャント。" +lore1 = "最大合成レベル" +lore2 = "アイテムにエンチャントできる最大値は" +lore3 = "パワー" +lore = ["最大合成レベル", "アイテムにエンチャントできる最大値は", "パワー"] [enchanting.return] - name = "XPリターン" - description = "エンチャント時に使用したXPが返還される。" - lore1 = "エンチャント時に消費した経験値が返還されるチャンスがある" - lore2 = "エンチャントごとの経験値" - lore = ["エンチャント時に消費した経験値が返還されるチャンスがある", "エンチャントごとの経験値"] +name = "XPリターン" +description = "エンチャント時に使用したXPが返還される。" +lore1 = "エンチャント時に消費した経験値が返還されるチャンスがある" +lore2 = "エンチャントごとの経験値" +lore = ["エンチャント時に消費した経験値が返還されるチャンスがある", "エンチャントごとの経験値"] # excavation [excavation] [excavation.haste] - name = "急速発掘者" - description = "採掘速度上昇で発掘が加速する!" - lore1 = "発掘中に採掘速度上昇を獲得" - lore2 = "任意のブロック採掘開始時にxレベルの採掘速度上昇" - lore = ["発掘中に採掘速度上昇を獲得", "任意のブロック採掘開始時にxレベルの採掘速度上昇"] +name = "急速発掘者" +description = "採掘速度上昇で発掘が加速する!" +lore1 = "発掘中に採掘速度上昇を獲得" +lore2 = "任意のブロック採掘開始時にxレベルの採掘速度上昇" +lore = ["発掘中に採掘速度上昇を獲得", "任意のブロック採掘開始時にxレベルの採掘速度上昇"] [excavation.spelunker] - name = "超視力スペランカー!" - description = "地面越しに鉱石が見える!" - lore1 = "オフハンドに鉱石、メインハンドにグロウベリーを持ってスニーク!" - lore2 = "ブロック範囲:" - lore3 = "使用時にグロウベリーを消費" - lore = ["オフハンドに鉱石、メインハンドにグロウベリーを持ってスニーク!", "ブロック範囲:", "使用時にグロウベリーを消費"] +name = "超視力スペランカー!" +description = "地面越しに鉱石が見える!" +lore1 = "オフハンドに鉱石、メインハンドにグロウベリーを持ってスニーク!" +lore2 = "ブロック範囲:" +lore3 = "使用時にグロウベリーを消費" +lore = ["オフハンドに鉱石、メインハンドにグロウベリーを持ってスニーク!", "ブロック範囲:", "使用時にグロウベリーを消費"] [excavation.drop_to_inventory] - name = "シャベルドロップ→インベントリ" +name = "シャベルドロップ→インベントリ" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "タックルの過剰設計な豪華レザーマン" - lore1 = "おそらく最も強力なツール、" - lore2 = "必要に応じてツールを動的に合体・変更できる。" - lore3 = "合体するには、インベントリ内でアイテムをシフトクリック。" - lore4 = "ツールを分離するには、スニークしながらドロップで分解。" - lore5 = "このレザーマンのツールは壊れないが、壊れたツールは使用不可" - lore6 = "合体可能なアイテム総数。" - lore7 = "5つや6つのツールを使うか、たった1つで済ませるか!" - lore = ["おそらく最も強力なツール、", "必要に応じてツールを動的に合体・変更できる。", "合体するには、インベントリ内でアイテムをシフトクリック。", "ツールを分離するには、スニークしながらドロップで分解。", "このレザーマンのツールは壊れないが、壊れたツールは使用不可", "合体可能なアイテム総数。", "5つや6つのツールを使うか、たった1つで済ませるか!"] +name = "OMNI - T.O.O.L." +description = "タックルの過剰設計な豪華レザーマン" +lore1 = "おそらく最も強力なツール、" +lore2 = "必要に応じてツールを動的に合体・変更できる。" +lore3 = "合体するには、インベントリ内でアイテムをシフトクリック。" +lore4 = "ツールを分離するには、スニークしながらドロップで分解。" +lore5 = "このレザーマンのツールは壊れないが、壊れたツールは使用不可" +lore6 = "合体可能なアイテム総数。" +lore7 = "5つや6つのツールを使うか、たった1つで済ませるか!" +lore = ["おそらく最も強力なツール、", "必要に応じてツールを動的に合体・変更できる。", "合体するには、インベントリ内でアイテムをシフトクリック。", "ツールを分離するには、スニークしながらドロップで分解。", "このレザーマンのツールは壊れないが、壊れたツールは使用不可", "合体可能なアイテム総数。", "5つや6つのツールを使うか、たった1つで済ませるか!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "成長のオーラ" - description = "周囲の自然をオーラで成長させる" - lore1 = "ブロック半径" - lore2 = "成長オーラの強さ" - lore3 = "食料コスト" - lore = ["ブロック半径", "成長オーラの強さ", "食料コスト"] +name = "成長のオーラ" +description = "周囲の自然をオーラで成長させる" +lore1 = "ブロック半径" +lore2 = "成長オーラの強さ" +lore3 = "食料コスト" +lore = ["ブロック半径", "成長オーラの強さ", "食料コスト"] [herbalism.hippo] - name = "薬草師のカバ" - description = "食べ物を消費すると満腹度が追加回復する" - lore1 = "食べ物消費時に追加の満腹度ポイント" - lore = ["食べ物消費時に追加の満腹度ポイント"] +name = "薬草師のカバ" +description = "食べ物を消費すると満腹度が追加回復する" +lore1 = "食べ物消費時に追加の満腹度ポイント" +lore = ["食べ物消費時に追加の満腹度ポイント"] [herbalism.myconid] - name = "薬草師のマイコニッド" - description = "菌糸ブロックをクラフトする能力を得る" - lore1 = "任意の土と茶色&赤キノコで菌糸をクラフトできる。" - lore = ["任意の土と茶色&赤キノコで菌糸をクラフトできる。"] +name = "薬草師のマイコニッド" +description = "菌糸ブロックをクラフトする能力を得る" +lore1 = "任意の土と茶色&赤キノコで菌糸をクラフトできる。" +lore = ["任意の土と茶色&赤キノコで菌糸をクラフトできる。"] [herbalism.terralid] - name = "薬草師のテラリッド" - description = "草ブロックをクラフトする能力を得る" - lore1 = "3つの種を3つの土の上に配置すると、草ブロック3個をクラフトできる。" - lore = ["3つの種を3つの土の上に配置すると、草ブロック3個をクラフトできる。"] +name = "薬草師のテラリッド" +description = "草ブロックをクラフトする能力を得る" +lore1 = "3つの種を3つの土の上に配置すると、草ブロック3個をクラフトできる。" +lore = ["3つの種を3つの土の上に配置すると、草ブロック3個をクラフトできる。"] [herbalism.cobweb] - name = "クモの巣クリエイター" - description = "作業台でクモの巣をクラフトする能力を得る" - lore1 = "糸9個でクモの巣をクラフトできる。" - lore = ["糸9個でクモの巣をクラフトできる。"] +name = "クモの巣クリエイター" +description = "作業台でクモの巣をクラフトする能力を得る" +lore1 = "糸9個でクモの巣をクラフトできる。" +lore = ["糸9個でクモの巣をクラフトできる。"] [herbalism.mushroom_blocks] - name = "キノコメイカー" - description = "作業台でキノコブロックをクラフトする能力を得る" - lore1 = "キノコ4個でブロックを作成、またはブロックから茎を作成。" - lore = ["キノコ4個でブロックを作成、またはブロックから茎を作成。"] +name = "キノコメイカー" +description = "作業台でキノコブロックをクラフトする能力を得る" +lore1 = "キノコ4個でブロックを作成、またはブロックから茎を作成。" +lore = ["キノコ4個でブロックを作成、またはブロックから茎を作成。"] [herbalism.drop_to_inventory] - name = "クワドロップ→インベントリ" +name = "クワドロップ→インベントリ" [herbalism.hungry_shield] - name = "ハングリーシールド" - description = "体力の前に空腹でダメージを受ける。" - lore1 = "空腹による軽減" - lore = ["空腹による軽減"] +name = "ハングリーシールド" +description = "体力の前に空腹でダメージを受ける。" +lore1 = "空腹による軽減" +lore = ["空腹による軽減"] [herbalism.luck] - name = "薬草師の幸運" - description = "草や花を壊すとランダムなアイテムを得るチャンスがある" - lore0 = "花=食べ物、草=種" - lore1 = "花を壊した時のアイテム獲得チャンス" - lore2 = "草を壊した時のアイテム獲得チャンス" - lore = ["花=食べ物、草=種", "花を壊した時のアイテム獲得チャンス", "草を壊した時のアイテム獲得チャンス"] +name = "薬草師の幸運" +description = "草や花を壊すとランダムなアイテムを得るチャンスがある" +lore0 = "花=食べ物、草=種" +lore1 = "花を壊した時のアイテム獲得チャンス" +lore2 = "草を壊した時のアイテム獲得チャンス" +lore = ["花=食べ物、草=種", "花を壊した時のアイテム獲得チャンス", "草を壊した時のアイテム獲得チャンス"] [herbalism.replant] - name = "収穫&再植え" - description = "クワで作物を右クリックすると収穫して再植えする。" - lore1 = "ブロック再植え半径" - lore = ["ブロック再植え半径"] +name = "収穫&再植え" +description = "クワで作物を右クリックすると収穫して再植えする。" +lore1 = "ブロック再植え半径" +lore = ["ブロック再植え半径"] # hunter [hunter] [hunter.adrenaline] - name = "アドレナリン" - description = "体力が低いほど近接ダメージが増加する" - lore1 = "最大ダメージ" - lore = ["最大ダメージ"] +name = "アドレナリン" +description = "体力が低いほど近接ダメージが増加する" +lore1 = "最大ダメージ" +lore = ["最大ダメージ"] [hunter.penalty] - name = "" - description = "" - lore1 = "空腹が尽きると毒スタックが付与される" - lore = ["空腹が尽きると毒スタックが付与される"] +name = "" +description = "" +lore1 = "空腹が尽きると毒スタックが付与される" +lore = ["空腹が尽きると毒スタックが付与される"] [hunter.drop_to_inventory] - name = "アイテムドロップ→インベントリ" - description = "何かを倒したり剣でブロックを壊すとドロップがインベントリに転送される" - lore1 = "モブやブロックからのドロップアイテムが可能な限りインベントリに入る。" - lore = ["モブやブロックからのドロップアイテムが可能な限りインベントリに入る。"] +name = "アイテムドロップ→インベントリ" +description = "何かを倒したり剣でブロックを壊すとドロップがインベントリに転送される" +lore1 = "モブやブロックからのドロップアイテムが可能な限りインベントリに入る。" +lore = ["モブやブロックからのドロップアイテムが可能な限りインベントリに入る。"] [hunter.invisibility] - name = "消失の一歩" - description = "攻撃を受けると空腹を代償に透明化を得る" - lore1 = "攻撃を受けるとパッシブ透明化を獲得" - lore2 = "攻撃を受けてx回3秒間透明化がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "透明化の持続時間" - lore = ["攻撃を受けるとパッシブ透明化を獲得", "攻撃を受けてx回3秒間透明化がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "透明化の持続時間"] +name = "消失の一歩" +description = "攻撃を受けると空腹を代償に透明化を得る" +lore1 = "攻撃を受けるとパッシブ透明化を獲得" +lore2 = "攻撃を受けてx回3秒間透明化がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "透明化の持続時間" +lore = ["攻撃を受けるとパッシブ透明化を獲得", "攻撃を受けてx回3秒間透明化がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "透明化の持続時間"] [hunter.jump_boost] - name = "ハンターの高み" - description = "攻撃を受けると空腹を代償にジャンプ力上昇を得る" - lore1 = "攻撃を受けるとパッシブジャンプ力上昇を獲得" - lore2 = "攻撃を受けてx回3秒間ジャンプ力上昇がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "ジャンプ力上昇の倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブジャンプ力上昇を獲得", "攻撃を受けてx回3秒間ジャンプ力上昇がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "ジャンプ力上昇の倍率(持続時間ではない)。"] +name = "ハンターの高み" +description = "攻撃を受けると空腹を代償にジャンプ力上昇を得る" +lore1 = "攻撃を受けるとパッシブジャンプ力上昇を獲得" +lore2 = "攻撃を受けてx回3秒間ジャンプ力上昇がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "ジャンプ力上昇の倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブジャンプ力上昇を獲得", "攻撃を受けてx回3秒間ジャンプ力上昇がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "ジャンプ力上昇の倍率(持続時間ではない)。"] [hunter.luck] - name = "ハンターの幸運" - description = "攻撃を受けると空腹を代償に幸運を得る" - lore1 = "攻撃を受けるとパッシブ幸運を獲得" - lore2 = "攻撃を受けてx回3秒間幸運がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "幸運の倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブ幸運を獲得", "攻撃を受けてx回3秒間幸運がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "幸運の倍率(持続時間ではない)。"] +name = "ハンターの幸運" +description = "攻撃を受けると空腹を代償に幸運を得る" +lore1 = "攻撃を受けるとパッシブ幸運を獲得" +lore2 = "攻撃を受けてx回3秒間幸運がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "幸運の倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブ幸運を獲得", "攻撃を受けてx回3秒間幸運がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "幸運の倍率(持続時間ではない)。"] [hunter.regen] - name = "ハンターの再生" - description = "攻撃を受けると空腹を代償に再生を得る" - lore1 = "攻撃を受けるとパッシブ再生を獲得" - lore2 = "攻撃を受けてx回3秒間再生がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "再生の倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブ再生を獲得", "攻撃を受けてx回3秒間再生がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "再生の倍率(持続時間ではない)。"] +name = "ハンターの再生" +description = "攻撃を受けると空腹を代償に再生を得る" +lore1 = "攻撃を受けるとパッシブ再生を獲得" +lore2 = "攻撃を受けてx回3秒間再生がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "再生の倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブ再生を獲得", "攻撃を受けてx回3秒間再生がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "再生の倍率(持続時間ではない)。"] [hunter.resistance] - name = "ハンターの耐性" - description = "攻撃を受けると空腹を代償に耐性を得る" - lore1 = "攻撃を受けるとパッシブ耐性を獲得" - lore2 = "攻撃を受けてx回3秒間耐性がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "耐性の倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブ耐性を獲得", "攻撃を受けてx回3秒間耐性がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "耐性の倍率(持続時間ではない)。"] +name = "ハンターの耐性" +description = "攻撃を受けると空腹を代償に耐性を得る" +lore1 = "攻撃を受けるとパッシブ耐性を獲得" +lore2 = "攻撃を受けてx回3秒間耐性がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "耐性の倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブ耐性を獲得", "攻撃を受けてx回3秒間耐性がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "耐性の倍率(持続時間ではない)。"] [hunter.speed] - name = "ハンターの俊足" - description = "攻撃を受けると空腹を代償にスピードを得る" - lore1 = "攻撃を受けるとパッシブスピードを獲得" - lore2 = "攻撃を受けてx回3秒間スピードがスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "スピードの倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブスピードを獲得", "攻撃を受けてx回3秒間スピードがスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "スピードの倍率(持続時間ではない)。"] +name = "ハンターの俊足" +description = "攻撃を受けると空腹を代償にスピードを得る" +lore1 = "攻撃を受けるとパッシブスピードを獲得" +lore2 = "攻撃を受けてx回3秒間スピードがスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "スピードの倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブスピードを獲得", "攻撃を受けてx回3秒間スピードがスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "スピードの倍率(持続時間ではない)。"] [hunter.strength] - name = "ハンターの剛力" - description = "攻撃を受けると空腹を代償に攻撃力を得る" - lore1 = "攻撃を受けるとパッシブ攻撃力を獲得" - lore2 = "攻撃を受けてx回3秒間攻撃力がスタック" - lore3 = "x空腹がスタック" - lore4 = "空腹スタックの持続時間と倍率。" - lore5 = "攻撃力の倍率(持続時間ではない)。" - lore = ["攻撃を受けるとパッシブ攻撃力を獲得", "攻撃を受けてx回3秒間攻撃力がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "攻撃力の倍率(持続時間ではない)。"] +name = "ハンターの剛力" +description = "攻撃を受けると空腹を代償に攻撃力を得る" +lore1 = "攻撃を受けるとパッシブ攻撃力を獲得" +lore2 = "攻撃を受けてx回3秒間攻撃力がスタック" +lore3 = "x空腹がスタック" +lore4 = "空腹スタックの持続時間と倍率。" +lore5 = "攻撃力の倍率(持続時間ではない)。" +lore = ["攻撃を受けるとパッシブ攻撃力を獲得", "攻撃を受けてx回3秒間攻撃力がスタック", "x空腹がスタック", "空腹スタックの持続時間と倍率。", "攻撃力の倍率(持続時間ではない)。"] # nether [nether] [nether.skull_toss] - name = "ウィザースカル投擲" - description1 = "内なるウィザーを解き放て、" - description2 = "誰かの" - description3 = "頭を使って。" - lore1 = "スカル投擲間のクールダウン秒数。" - lore2 = "ウィザースカル使用:投げる " - lore3 = "ウィザースカル" - lore4 = "着弾時に爆発。" - lore = ["スカル投擲間のクールダウン秒数。", "ウィザースカル使用:投げる ", "ウィザースカル", "着弾時に爆発。"] +name = "ウィザースカル投擲" +description1 = "内なるウィザーを解き放て、" +description2 = "誰かの" +description3 = "頭を使って。" +lore1 = "スカル投擲間のクールダウン秒数。" +lore2 = "ウィザースカル使用:投げる " +lore3 = "ウィザースカル" +lore4 = "着弾時に爆発。" +lore = ["スカル投擲間のクールダウン秒数。", "ウィザースカル使用:投げる ", "ウィザースカル", "着弾時に爆発。"] [nether.wither_resist] - name = "ウィザー耐性" - description = "ネザライトの力でウィザー効果に抵抗する。" - lore1 = "ウィザー効果を無効化する確率(部位ごと)。" - lore2 = "パッシブ:ネザライト防具装備時にウィザー効果を" - lore3 = "無効化する場合がある。" - lore = ["ウィザー効果を無効化する確率(部位ごと)。", "パッシブ:ネザライト防具装備時にウィザー効果を", "無効化する場合がある。"] +name = "ウィザー耐性" +description = "ネザライトの力でウィザー効果に抵抗する。" +lore1 = "ウィザー効果を無効化する確率(部位ごと)。" +lore2 = "パッシブ:ネザライト防具装備時にウィザー効果を" +lore3 = "無効化する場合がある。" +lore = ["ウィザー効果を無効化する確率(部位ごと)。", "パッシブ:ネザライト防具装備時にウィザー効果を", "無効化する場合がある。"] [nether.fire_resist] - name = "火炎耐性" - description = "肌を硬化させて火に抵抗する。" - lore1 = "炎上効果を無効化する確率!" - lore = ["炎上効果を無効化する確率!"] +name = "火炎耐性" +description = "肌を硬化させて火に抵抗する。" +lore1 = "炎上効果を無効化する確率!" +lore = ["炎上効果を無効化する確率!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "自動精錬" - description = "採掘したバニラ鉱石を自動で精錬する" - lore1 = "精錬可能な鉱石は自動で精錬される" - lore2 = "%の確率で追加ドロップ" - lore = ["精錬可能な鉱石は自動で精錬される", "%の確率で追加ドロップ"] +name = "自動精錬" +description = "採掘したバニラ鉱石を自動で精錬する" +lore1 = "精錬可能な鉱石は自動で精錬される" +lore2 = "%の確率で追加ドロップ" +lore = ["精錬可能な鉱石は自動で精錬される", "%の確率で追加ドロップ"] [pickaxe.chisel] - name = "鉱石チゼル" - description = "鉱石を右クリックして追加の鉱石を掘り出す。耐久値を大きく消耗する。" - lore1 = "ドロップ確率" - lore2 = "ツール消耗" - lore = ["ドロップ確率", "ツール消耗"] +name = "鉱石チゼル" +description = "鉱石を右クリックして追加の鉱石を掘り出す。耐久値を大きく消耗する。" +lore1 = "ドロップ確率" +lore2 = "ツール消耗" +lore = ["ドロップ確率", "ツール消耗"] [pickaxe.drop_to_inventory] - name = "つるはしドロップ→インベントリ" - description = "ブロックを壊すとアイテムがインベントリに直接入る" - lore1 = "ブロックからのドロップアイテムが可能な限りインベントリに入る。" - lore = ["ブロックからのドロップアイテムが可能な限りインベントリに入る。"] +name = "つるはしドロップ→インベントリ" +description = "ブロックを壊すとアイテムがインベントリに直接入る" +lore1 = "ブロックからのドロップアイテムが可能な限りインベントリに入る。" +lore = ["ブロックからのドロップアイテムが可能な限りインベントリに入る。"] [pickaxe.silk_spawner] - name = "つるはしシルクスポナー" - description = "スポナーが破壊時にドロップするようになる" - lore1 = "シルクタッチでスポナーを破壊可能にする。" - lore2 = "スニーク中にスポナーを破壊可能にする。" - lore = ["シルクタッチでスポナーを破壊可能にする。", "スニーク中にスポナーを破壊可能にする。"] +name = "つるはしシルクスポナー" +description = "スポナーが破壊時にドロップするようになる" +lore1 = "シルクタッチでスポナーを破壊可能にする。" +lore2 = "スニーク中にスポナーを破壊可能にする。" +lore = ["シルクタッチでスポナーを破壊可能にする。", "スニーク中にスポナーを破壊可能にする。"] [pickaxe.vein_miner] - name = "ヴェインマイナー" - description = "バニラ鉱石の鉱脈/クラスターを一度に破壊できる" - lore1 = "スニークして鉱石を採掘" - lore2 = "ヴェインマイニング範囲" - lore3 = "このスキルはドロップをまとめません!" - lore = ["スニークして鉱石を採掘", "ヴェインマイニング範囲", "このスキルはドロップをまとめません!"] +name = "ヴェインマイナー" +description = "バニラ鉱石の鉱脈/クラスターを一度に破壊できる" +lore1 = "スニークして鉱石を採掘" +lore2 = "ヴェインマイニング範囲" +lore3 = "このスキルはドロップをまとめません!" +lore = ["スニークして鉱石を採掘", "ヴェインマイニング範囲", "このスキルはドロップをまとめません!"] # ranged [ranged] [ranged.arrow_recovery] - name = "矢の回収" - description = "敵を倒した後に矢を回収する。" - lore1 = "ヒット/キル時の矢回収確率" - lore2 = "確率:" - lore = ["ヒット/キル時の矢回収確率", "確率:"] +name = "矢の回収" +description = "敵を倒した後に矢を回収する。" +lore1 = "ヒット/キル時の矢回収確率" +lore2 = "確率:" +lore = ["ヒット/キル時の矢回収確率", "確率:"] [ranged.web_shot] - name = "ウェブスネア" - description = "ターゲットに命中時、周囲にクモの巣を展開する!" - lore1 = "雪玉を8個のクモの巣で囲んで投げろ!" - lore2 = "約数秒間の檻効果。" - lore = ["雪玉を8個のクモの巣で囲んで投げろ!", "約数秒間の檻効果。"] +name = "ウェブスネア" +description = "ターゲットに命中時、周囲にクモの巣を展開する!" +lore1 = "雪玉を8個のクモの巣で囲んで投げろ!" +lore2 = "約数秒間の檻効果。" +lore = ["雪玉を8個のクモの巣で囲んで投げろ!", "約数秒間の檻効果。"] [ranged.force_shot] - name = "フォースショット" - description = "射出物をより速く、より遠くへ飛ばす!" - advancementname = "ロングショット" - advancementlore = "30ブロック以上離れた場所から命中させろ!" - lore1 = "射出速度" - lore = ["射出速度"] +name = "フォースショット" +description = "射出物をより速く、より遠くへ飛ばす!" +advancementname = "ロングショット" +advancementlore = "30ブロック以上離れた場所から命中させろ!" +lore1 = "射出速度" +lore = ["射出速度"] [ranged.lunge_shot] - name = "ランジショット" - description = "落下中に矢がランダムな方向に吹き飛ばす" - lore1 = "ランダムバースト速度" - lore = ["ランダムバースト速度"] +name = "ランジショット" +description = "落下中に矢がランダムな方向に吹き飛ばす" +lore1 = "ランダムバースト速度" +lore = ["ランダムバースト速度"] [ranged.arrow_piercing] - name = "矢の貫通" - description = "射出物に貫通を付与!対象を貫通して射撃!" - lore1 = "貫通対象数" - lore = ["貫通対象数"] +name = "矢の貫通" +description = "射出物に貫通を付与!対象を貫通して射撃!" +lore1 = "貫通対象数" +lore = ["貫通対象数"] # rift [rift] [rift.remote_access] - name = "リモートアクセス" - description = "虚空から引き寄せ、マークしたコンテナにアクセスする。" - lore1 = "エンダーパール + コンパス = 聖遺物ポートキー" - lore2 = "このアイテムでコンテナに遠隔アクセスできる" - lore3 = "クラフト後、アイテムの説明で使い方を確認" - notcontainer = "それはコンテナではありません" - lore = ["エンダーパール + コンパス = 聖遺物ポートキー", "このアイテムでコンテナに遠隔アクセスできる", "クラフト後、アイテムの説明で使い方を確認"] +name = "リモートアクセス" +description = "虚空から引き寄せ、マークしたコンテナにアクセスする。" +lore1 = "エンダーパール + コンパス = 聖遺物ポートキー" +lore2 = "このアイテムでコンテナに遠隔アクセスできる" +lore3 = "クラフト後、アイテムの説明で使い方を確認" +notcontainer = "それはコンテナではありません" +lore = ["エンダーパール + コンパス = 聖遺物ポートキー", "このアイテムでコンテナに遠隔アクセスできる", "クラフト後、アイテムの説明で使い方を確認"] [rift.blink] - name = "リフトブリンク" - description = "短距離瞬間移動、瞬きひとつで!" - lore1 = "ブリンク時のブロック数(垂直2倍)" - lore2 = "ダッシュ中:ジャンプを2回押して" - lore3 = "ブリンク" - lore = ["ブリンク時のブロック数(垂直2倍)", "ダッシュ中:ジャンプを2回押して", "ブリンク"] +name = "リフトブリンク" +description = "短距離瞬間移動、瞬きひとつで!" +lore1 = "ブリンク時のブロック数(垂直2倍)" +lore2 = "ダッシュ中:ジャンプを2回押して" +lore3 = "ブリンク" +lore = ["ブリンク時のブロック数(垂直2倍)", "ダッシュ中:ジャンプを2回押して", "ブリンク"] [rift.chest] - name = "簡易エンダーチェスト" - description = "手に持ったエンダーチェストを左クリックで開く。" - lore1 = "手に持ったエンダーチェストをクリックで開く(設置しないこと)" - lore = ["手に持ったエンダーチェストをクリックで開く(設置しないこと)"] +name = "簡易エンダーチェスト" +description = "手に持ったエンダーチェストを左クリックで開く。" +lore1 = "手に持ったエンダーチェストをクリックで開く(設置しないこと)" +lore = ["手に持ったエンダーチェストをクリックで開く(設置しないこと)"] [rift.descent] - name = "浮遊無効化" - description = "空中に浮かされるのが嫌?このスキルで解決!" - lore1 = "スニークで降下すると、通常より緩やかに落下する!" - lore2 = "クールダウン:" - lore = ["スニークで降下すると、通常より緩やかに落下する!", "クールダウン:"] +name = "浮遊無効化" +description = "空中に浮かされるのが嫌?このスキルで解決!" +lore1 = "スニークで降下すると、通常より緩やかに落下する!" +lore2 = "クールダウン:" +lore = ["スニークで降下すると、通常より緩やかに落下する!", "クールダウン:"] [rift.gate] - name = "リフトゲート" - description = "マークした場所にテレポートする。" - lore1 = "クラフト:エメラルド + アメジストの欠片 + エンダーパール" - lore2 = "使用前に必読!" - lore3 = "5秒の遅延、" - lore4 = "この演出中に死亡する可能性あり" - lore = ["クラフト:エメラルド + アメジストの欠片 + エンダーパール", "使用前に必読!", "5秒の遅延、", "この演出中に死亡する可能性あり"] +name = "リフトゲート" +description = "マークした場所にテレポートする。" +lore1 = "クラフト:エメラルド + アメジストの欠片 + エンダーパール" +lore2 = "使用前に必読!" +lore3 = "5秒の遅延、" +lore4 = "この演出中に死亡する可能性あり" +lore = ["クラフト:エメラルド + アメジストの欠片 + エンダーパール", "使用前に必読!", "5秒の遅延、", "この演出中に死亡する可能性あり"] [rift.resist] - name = "リフト耐性" - description = "エンダーアイテムやアビリティ使用時に耐性を獲得" - lore1 = "+ パッシブ:リフトアビリティやエンダーアイテム使用時に耐性を付与" - lore2 = "ポータブルエンダーチェストは対象外、消費可能なアイテムのみ" - lore = ["+ パッシブ:リフトアビリティやエンダーアイテム使用時に耐性を付与", "ポータブルエンダーチェストは対象外、消費可能なアイテムのみ"] +name = "リフト耐性" +description = "エンダーアイテムやアビリティ使用時に耐性を獲得" +lore1 = "+ パッシブ:リフトアビリティやエンダーアイテム使用時に耐性を付与" +lore2 = "ポータブルエンダーチェストは対象外、消費可能なアイテムのみ" +lore = ["+ パッシブ:リフトアビリティやエンダーアイテム使用時に耐性を付与", "ポータブルエンダーチェストは対象外、消費可能なアイテムのみ"] [rift.visage] - name = "リフトヴィサージュ" - description = "インベントリにエンダーパールがあるとエンダーマンが敵対しなくなる。" - lore1 = "エンダーパールを所持しているとエンダーマンが敵対しなくなる。" - lore = ["エンダーパールを所持しているとエンダーマンが敵対しなくなる。"] +name = "リフトヴィサージュ" +description = "インベントリにエンダーパールがあるとエンダーマンが敵対しなくなる。" +lore1 = "エンダーパールを所持しているとエンダーマンが敵対しなくなる。" +lore = ["エンダーパールを所持しているとエンダーマンが敵対しなくなる。"] # seaborn [seaborn] [seaborn.oxygen] - name = "有機酸素タンク" - description = "小さな肺でもっと多くの酸素を蓄えろ!" - lore1 = "酸素容量増加" - lore = ["酸素容量増加"] +name = "有機酸素タンク" +description = "小さな肺でもっと多くの酸素を蓄えろ!" +lore1 = "酸素容量増加" +lore = ["酸素容量増加"] [seaborn.fishers_fantasy] - name = "釣り人の幻想" - description = "釣りでより多くのXPと魚を獲得!" - lore1 = "レベルごとにXPと魚の追加獲得チャンスが増加!" - lore = ["レベルごとにXPと魚の追加獲得チャンスが増加!"] +name = "釣り人の幻想" +description = "釣りでより多くのXPと魚を獲得!" +lore1 = "レベルごとにXPと魚の追加獲得チャンスが増加!" +lore = ["レベルごとにXPと魚の追加獲得チャンスが増加!"] [seaborn.haste] - name = "タートルマイナー" - description = "水中で採掘すると採掘速度上昇を得る!" - lore1 = "水中呼吸の効果が切れた後、水中で採掘すると採掘速度上昇IIIが適用される(水中採掘と重複可能)!" - lore = ["水中呼吸の効果が切れた後、水中で採掘すると採掘速度上昇IIIが適用される(水中採掘と重複可能)!"] +name = "タートルマイナー" +description = "水中で採掘すると採掘速度上昇を得る!" +lore1 = "水中呼吸の効果が切れた後、水中で採掘すると採掘速度上昇IIIが適用される(水中採掘と重複可能)!" +lore = ["水中呼吸の効果が切れた後、水中で採掘すると採掘速度上昇IIIが適用される(水中採掘と重複可能)!"] [seaborn.night_vision] - name = "タートルビジョン" - description = "水中で暗視を得る" - lore1 = "水中呼吸の効果が切れた後、水中で暗視を獲得!" - lore = ["水中呼吸の効果が切れた後、水中で暗視を獲得!"] +name = "タートルビジョン" +description = "水中で暗視を得る" +lore1 = "水中呼吸の効果が切れた後、水中で暗視を獲得!" +lore = ["水中呼吸の効果が切れた後、水中で暗視を獲得!"] [seaborn.dolphin_grace] - name = "イルカの加護" - description = "イルカなしでイルカのように泳ぐ" - lore1 = "+ パッシブ:" - lore2 = "xスピード(イルカの加護)を獲得" - lore3 = "精密なドイツのエンジニアリ…いや違った…水中歩行とは併用不可" - lore = ["+ パッシブ:", "xスピード(イルカの加護)を獲得", "精密なドイツのエンジニアリ…いや違った…水中歩行とは併用不可"] +name = "イルカの加護" +description = "イルカなしでイルカのように泳ぐ" +lore1 = "+ パッシブ:" +lore2 = "xスピード(イルカの加護)を獲得" +lore3 = "精密なドイツのエンジニアリ…いや違った…水中歩行とは併用不可" +lore = ["+ パッシブ:", "xスピード(イルカの加護)を獲得", "精密なドイツのエンジニアリ…いや違った…水中歩行とは併用不可"] # stealth [stealth] [stealth.ghost_armor] - name = "ゴーストアーマー" - description = "ダメージを受けていない間徐々に防具が蓄積し、1回の攻撃で消滅する" - lore1 = "最大防具値" - lore2 = "蓄積速度" - lore = ["最大防具値", "蓄積速度"] +name = "ゴーストアーマー" +description = "ダメージを受けていない間徐々に防具が蓄積し、1回の攻撃で消滅する" +lore1 = "最大防具値" +lore2 = "蓄積速度" +lore = ["最大防具値", "蓄積速度"] [stealth.night_vision] - name = "ステルスビジョン" - description = "スニーク中に暗視を獲得する" - lore1 = "一瞬で" - lore2 = "暗視" - lore3 = "をスニーク中に獲得" - lore = ["一瞬で", "暗視", "をスニーク中に獲得"] +name = "ステルスビジョン" +description = "スニーク中に暗視を獲得する" +lore1 = "一瞬で" +lore2 = "暗視" +lore3 = "をスニーク中に獲得" +lore = ["一瞬で", "暗視", "をスニーク中に獲得"] [stealth.snatch] - name = "アイテムスナッチ" - description = "スニーク中にドロップアイテムを瞬時に回収!" - lore1 = "スナッチ半径" - lore = ["スナッチ半径"] +name = "アイテムスナッチ" +description = "スニーク中にドロップアイテムを瞬時に回収!" +lore1 = "スナッチ半径" +lore = ["スナッチ半径"] [stealth.speed] - name = "スニーク速度" - description = "スニーク中にスピードを得る" - lore1 = "スニーク速度" - lore = ["スニーク速度"] +name = "スニーク速度" +description = "スニーク中にスピードを得る" +lore1 = "スニーク速度" +lore = ["スニーク速度"] [stealth.ender_veil] - name = "エンダーヴェール" - description = "カボチャ無しでエンダーマンの敵対を防ぐ" - lore1 = "スニーク中にエンダーマンの攻撃を防ぐ" - lore2 = "エンダーマンの攻撃をすべて防ぐ" - lore = ["スニーク中にエンダーマンの攻撃を防ぐ", "エンダーマンの攻撃をすべて防ぐ"] +name = "エンダーヴェール" +description = "カボチャ無しでエンダーマンの敵対を防ぐ" +lore1 = "スニーク中にエンダーマンの攻撃を防ぐ" +lore2 = "エンダーマンの攻撃をすべて防ぐ" +lore = ["スニーク中にエンダーマンの攻撃を防ぐ", "エンダーマンの攻撃をすべて防ぐ"] # sword [sword] [sword.machete] - name = "マチェーテ" - description = "草木を容易く切り払え!" - lore1 = "斬撃半径" - lore2 = "斬撃クールダウン" - lore3 = "ツール消耗" - lore = ["斬撃半径", "斬撃クールダウン", "ツール消耗"] +name = "マチェーテ" +description = "草木を容易く切り払え!" +lore1 = "斬撃半径" +lore2 = "斬撃クールダウン" +lore3 = "ツール消耗" +lore = ["斬撃半径", "斬撃クールダウン", "ツール消耗"] [sword.bloody_blade] - name = "血塗れの刃" - description = "剣での攻撃が出血を引き起こす!" - lore1 = "剣で生物を攻撃すると出血を引き起こす" - lore2 = "出血持続時間" - lore3 = "出血クールダウン" - lore = ["剣で生物を攻撃すると出血を引き起こす", "出血持続時間", "出血クールダウン"] +name = "血塗れの刃" +description = "剣での攻撃が出血を引き起こす!" +lore1 = "剣で生物を攻撃すると出血を引き起こす" +lore2 = "出血持続時間" +lore3 = "出血クールダウン" +lore = ["剣で生物を攻撃すると出血を引き起こす", "出血持続時間", "出血クールダウン"] [sword.poisoned_blade] - name = "毒の刃" - description = "剣での攻撃が毒を引き起こす!" - lore1 = "剣で生物を攻撃すると毒を引き起こす" - lore2 = "毒の持続時間" - lore3 = "毒クールダウン" - lore = ["剣で生物を攻撃すると毒を引き起こす", "毒の持続時間", "毒クールダウン"] +name = "毒の刃" +description = "剣での攻撃が毒を引き起こす!" +lore1 = "剣で生物を攻撃すると毒を引き起こす" +lore2 = "毒の持続時間" +lore3 = "毒クールダウン" +lore = ["剣で生物を攻撃すると毒を引き起こす", "毒の持続時間", "毒クールダウン"] # taming [taming] [taming.damage] - name = "飼育ダメージ" - description = "飼いならした動物の攻撃力を増加させる。" - lore1 = "ダメージ増加" - lore = ["ダメージ増加"] +name = "飼育ダメージ" +description = "飼いならした動物の攻撃力を増加させる。" +lore1 = "ダメージ増加" +lore = ["ダメージ増加"] [taming.health] - name = "飼育体力" - description = "飼いならした動物の体力を増加させる。" - lore1 = "体力増加" - lore = ["体力増加"] +name = "飼育体力" +description = "飼いならした動物の体力を増加させる。" +lore1 = "体力増加" +lore = ["体力増加"] [taming.regeneration] - name = "飼育再生" - description = "飼いならした動物の再生力を増加させる。" - lore1 = "HP/秒" - lore = ["HP/秒"] +name = "飼育再生" +description = "飼いならした動物の再生力を増加させる。" +lore1 = "HP/秒" +lore = ["HP/秒"] # tragoul [tragoul] [tragoul.thorns] - name = "棘" - description = "受けたダメージを攻撃者に反射する!" - lore1 = "被弾時の反射ダメージ" - lore = ["被弾時の反射ダメージ"] +name = "棘" +description = "受けたダメージを攻撃者に反射する!" +lore1 = "被弾時の反射ダメージ" +lore = ["被弾時の反射ダメージ"] [tragoul.globe] - name = "苦痛の球" - description = "周囲の敵の数に応じて与えるダメージを分散する!" - lore1 = "周囲に敵が多いほど、それぞれに与えるダメージは減少する" - lore2 = "範囲:" - lore3 = "全エンティティへの追加ダメージ:" - lore = ["周囲に敵が多いほど、それぞれに与えるダメージは減少する", "範囲:", "全エンティティへの追加ダメージ:"] +name = "苦痛の球" +description = "周囲の敵の数に応じて与えるダメージを分散する!" +lore1 = "周囲に敵が多いほど、それぞれに与えるダメージは減少する" +lore2 = "範囲:" +lore3 = "全エンティティへの追加ダメージ:" +lore = ["周囲に敵が多いほど、それぞれに与えるダメージは減少する", "範囲:", "全エンティティへの追加ダメージ:"] [tragoul.healing] - name = "苦痛の意志" - description = "与えたダメージに応じて体力が回復する!" - lore1 = "傷つけることがこんなに気持ちいいとは!与ダメージから回復" - lore2 = "3秒間のダメージ判定期間があり、回復と1秒間のクールダウンがある" - lore3 = "ダメージあたりの回復率:" - lore = ["傷つけることがこんなに気持ちいいとは!与ダメージから回復", "3秒間のダメージ判定期間があり、回復と1秒間のクールダウンがある", "ダメージあたりの回復率:"] +name = "苦痛の意志" +description = "与えたダメージに応じて体力が回復する!" +lore1 = "傷つけることがこんなに気持ちいいとは!与ダメージから回復" +lore2 = "3秒間のダメージ判定期間があり、回復と1秒間のクールダウンがある" +lore3 = "ダメージあたりの回復率:" +lore = ["傷つけることがこんなに気持ちいいとは!与ダメージから回復", "3秒間のダメージ判定期間があり、回復と1秒間のクールダウンがある", "ダメージあたりの回復率:"] [tragoul.lance] - name = "屍槍" - description = "敵を倒すかアビリティで敵を倒すと、近くの敵にダメージを与える槍が出現する!" - lore1 = "槍は倒した全ての対象から飛び出し、このアビリティで敵を倒した場合も発動する。" - lore2 = "槍を生成するために自分の体力を犠牲にする(これで死ぬこともある)" - lore3 = "最大槍数:1 + " - lore = ["槍は倒した全ての対象から飛び出し、このアビリティで敵を倒した場合も発動する。", "槍を生成するために自分の体力を犠牲にする(これで死ぬこともある)", "最大槍数:1 + "] +name = "屍槍" +description = "敵を倒すかアビリティで敵を倒すと、近くの敵にダメージを与える槍が出現する!" +lore1 = "槍は倒した全ての対象から飛び出し、このアビリティで敵を倒した場合も発動する。" +lore2 = "槍を生成するために自分の体力を犠牲にする(これで死ぬこともある)" +lore3 = "最大槍数:1 + " +lore = ["槍は倒した全ての対象から飛び出し、このアビリティで敵を倒した場合も発動する。", "槍を生成するために自分の体力を犠牲にする(これで死ぬこともある)", "最大槍数:1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "ガラスの大砲" - description = "防具値が低いほど素手ダメージにボーナスが付く" - lore1 = "x 防具値0時のダメージ" - lore2 = "レベルごとのボーナスダメージ" - lore = ["x 防具値0時のダメージ", "レベルごとのボーナスダメージ"] +name = "ガラスの大砲" +description = "防具値が低いほど素手ダメージにボーナスが付く" +lore1 = "x 防具値0時のダメージ" +lore2 = "レベルごとのボーナスダメージ" +lore = ["x 防具値0時のダメージ", "レベルごとのボーナスダメージ"] [unarmed.power] - name = "素手の力" - description = "素手ダメージが向上する" - lore1 = "ダメージ" - lore = ["ダメージ"] +name = "素手の力" +description = "素手ダメージが向上する" +lore1 = "ダメージ" +lore = ["ダメージ"] [unarmed.sucker_punch] - name = "不意打ち" - description = "ダッシュパンチがさらに致命的になる。" - lore1 = "ダメージ" - lore2 = "パンチ中の移動速度に応じてダメージが増加する" - lore = ["ダメージ", "パンチ中の移動速度に応じてダメージが増加する"] +name = "不意打ち" +description = "ダッシュパンチがさらに致命的になる。" +lore1 = "ダメージ" +lore2 = "パンチ中の移動速度に応じてダメージが増加する" +lore = ["ダメージ", "パンチ中の移動速度に応じてダメージが増加する"] diff --git a/src/main/resources/ko_KR.toml b/src/main/resources/ko_KR.toml index fc73f70f6..ad0d30c10 100644 --- a/src/main/resources/ko_KR.toml +++ b/src/main/resources/ko_KR.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "움직입시다!" - description = "1KM 이상 이동하기 (1,000 블럭)" +title = "움직입시다!" +description = "1KM 이상 이동하기 (1,000 블럭)" [advancement.challenge_sprint_5k] - title = "5KM 뜀박질!" - description = "5KM 이상 이동하기 (5,000 블럭)" +title = "5KM 뜀박질!" +description = "5KM 이상 이동하기 (5,000 블럭)" [advancement.challenge_sprint_50k] - title = "50KM 전력질주!" - description = "50KM 이상 이동하기 (50,000 블럭)" +title = "50KM 전력질주!" +description = "50KM 이상 이동하기 (50,000 블럭)" [advancement.challenge_sprint_500k] - title = "우주를 횡단하라!!" - description = "500KM 이상 이동하기 (500,000 블럭)" +title = "우주를 횡단하라!!" +description = "500KM 이상 이동하기 (500,000 블럭)" [advancement.challenge_sprint_marathon] - title = "(진짜) 마라톤 뛸 시간!" - description = "42,195 블럭을 달리세요!" +title = "(진짜) 마라톤 뛸 시간!" +description = "42,195 블럭을 달리세요!" [advancement.challenge_place_1k] - title = "풋내기 건축가!" - description = "블럭 1,000개를 배치하세요" +title = "풋내기 건축가!" +description = "블럭 1,000개를 배치하세요" [advancement.challenge_place_5k] - title = "중급 건축가!" - description = "블럭 5,000개를 배치하세요" +title = "중급 건축가!" +description = "블럭 5,000개를 배치하세요" [advancement.challenge_place_50k] - title = "숙련된 건축가!" - description = "블럭 50,000개를 배치하세요" +title = "숙련된 건축가!" +description = "블럭 50,000개를 배치하세요" [advancement.challenge_place_500k] - title = "마스터 건축가!" - description = "블럭 500,000개를 배치하세요" +title = "마스터 건축가!" +description = "블럭 500,000개를 배치하세요" [advancement.challenge_place_5m] - title = "대칭의 수호자!" - description = "현실이 당신의 놀이터입니다! (5백만 블럭)" +title = "대칭의 수호자!" +description = "현실이 당신의 놀이터입니다! (5백만 블럭)" [advancement.challenge_chop_1k] - title = "풋내기 나무꾼!" - description = "나무 블럭 1,000개를 베세요" +title = "풋내기 나무꾼!" +description = "나무 블럭 1,000개를 베세요" [advancement.challenge_chop_5k] - title = "중급 나무꾼!" - description = "나무 블럭 5,000개를 베세요" +title = "중급 나무꾼!" +description = "나무 블럭 5,000개를 베세요" [advancement.challenge_chop_50k] - title = "숙련된 나무꾼!" - description = "나무 블럭 50,000개를 베세요" +title = "숙련된 나무꾼!" +description = "나무 블럭 50,000개를 베세요" [advancement.challenge_chop_500k] - title = "마스터 나무꾼!" - description = "나무 블럭 500,000개를 베세요" +title = "마스터 나무꾼!" +description = "나무 블럭 500,000개를 베세요" [advancement.challenge_chop_5m] - title = "잭슨이라는 개" - description = "세상에서 제일 착한 강아지! (5백만 블럭)" +title = "잭슨이라는 개" +description = "세상에서 제일 착한 강아지! (5백만 블럭)" [advancement.challenge_block_1k] - title = "겨우 막는 중!" - description = "공격 1,000번을 막으세요" +title = "겨우 막는 중!" +description = "공격 1,000번을 막으세요" [advancement.challenge_block_5k] - title = "막는 게 재밌어!" - description = "공격 5,000번을 막으세요" +title = "막는 게 재밌어!" +description = "공격 5,000번을 막으세요" [advancement.challenge_block_50k] - title = "막기가 내 인생이야!" - description = "공격 50,000번을 막으세요" +title = "막기가 내 인생이야!" +description = "공격 50,000번을 막으세요" [advancement.challenge_block_500k] - title = "막기가 내 존재 이유야!" - description = "공격 500,000번을 막으세요" +title = "막기가 내 존재 이유야!" +description = "공격 500,000번을 막으세요" [advancement.challenge_block_5m] - title = "아픈 손" - description = "공격 5,000,000번을 막으세요" +title = "아픈 손" +description = "공격 5,000,000번을 막으세요" [advancement.challenge_brew_1k] - title = "풋내기 연금술사!" - description = "물약 1,000개를 사용하세요" +title = "풋내기 연금술사!" +description = "물약 1,000개를 사용하세요" [advancement.challenge_brew_5k] - title = "중급 연금술사!" - description = "물약 5,000개를 사용하세요" +title = "중급 연금술사!" +description = "물약 5,000개를 사용하세요" [advancement.challenge_brew_50k] - title = "숙련된 연금술사!" - description = "물약 50,000개를 사용하세요" +title = "숙련된 연금술사!" +description = "물약 50,000개를 사용하세요" [advancement.challenge_brew_500k] - title = "마스터 연금술사!" - description = "물약 500,000개를 사용하세요" +title = "마스터 연금술사!" +description = "물약 500,000개를 사용하세요" [advancement.challenge_brew_5m] - title = "위대한 연금술사" - description = "물약 5,000,000개를 사용하세요" +title = "위대한 연금술사" +description = "물약 5,000,000개를 사용하세요" [advancement.challenge_brewsplash_1k] - title = "풋내기 투척사!" - description = "물약 1,000개를 던지세요" +title = "풋내기 투척사!" +description = "물약 1,000개를 던지세요" [advancement.challenge_brewsplash_5k] - title = "중급 투척사!" - description = "물약 5,000개를 던지세요" +title = "중급 투척사!" +description = "물약 5,000개를 던지세요" [advancement.challenge_brewsplash_50k] - title = "숙련된 투척사!" - description = "물약 50,000개를 던지세요" +title = "숙련된 투척사!" +description = "물약 50,000개를 던지세요" [advancement.challenge_brewsplash_500k] - title = "마스터 투척사!" - description = "물약 500,000개를 던지세요" +title = "마스터 투척사!" +description = "물약 500,000개를 던지세요" [advancement.challenge_brewsplash_5m] - title = "투척의 달인" - description = "물약 5,000,000개를 던지세요" +title = "투척의 달인" +description = "물약 5,000,000개를 던지세요" [advancement.challenge_craft_1k] - title = "손재주 좋은 제작자!" - description = "아이템 1,000개를 제작하세요" +title = "손재주 좋은 제작자!" +description = "아이템 1,000개를 제작하세요" [advancement.challenge_craft_5k] - title = "심술궂은 제작자!" - description = "아이템 5,000개를 제작하세요" +title = "심술궂은 제작자!" +description = "아이템 5,000개를 제작하세요" [advancement.challenge_craft_50k] - title = "헌신적인 제작자!" - description = "아이템 50,000개를 제작하세요" +title = "헌신적인 제작자!" +description = "아이템 50,000개를 제작하세요" [advancement.challenge_craft_500k] - title = "요란한 제작자!" - description = "아이템 500,000개를 제작하세요" +title = "요란한 제작자!" +description = "아이템 500,000개를 제작하세요" [advancement.challenge_craft_5m] - title = "재앙의 제작왕" - description = "아이템 5,000,000개를 제작하세요" +title = "재앙의 제작왕" +description = "아이템 5,000,000개를 제작하세요" [advancement.challenge_enchant_1k] - title = "풋내기 마법부여사!" - description = "아이템 1,000개를 마법부여하세요" +title = "풋내기 마법부여사!" +description = "아이템 1,000개를 마법부여하세요" [advancement.challenge_enchant_5k] - title = "중급 마법부여사!" - description = "아이템 5,000개를 마법부여하세요" +title = "중급 마법부여사!" +description = "아이템 5,000개를 마법부여하세요" [advancement.challenge_enchant_50k] - title = "숙련된 마법부여사!" - description = "아이템 50,000개를 마법부여하세요" +title = "숙련된 마법부여사!" +description = "아이템 50,000개를 마법부여하세요" [advancement.challenge_enchant_500k] - title = "마스터 마법부여사!" - description = "아이템 500,000개를 마법부여하세요" +title = "마스터 마법부여사!" +description = "아이템 500,000개를 마법부여하세요" [advancement.challenge_enchant_5m] - title = "신비로운 마법부여사" - description = "아이템 5,000,000개를 마법부여하세요" +title = "신비로운 마법부여사" +description = "아이템 5,000,000개를 마법부여하세요" [advancement.challenge_excavate_1k] - title = "의욕 넘치는 발굴가!" - description = "블럭 1,000개를 파내세요" +title = "의욕 넘치는 발굴가!" +description = "블럭 1,000개를 파내세요" [advancement.challenge_excavate_5k] - title = "중급 발굴가!" - description = "블럭 5,000개를 파내세요" +title = "중급 발굴가!" +description = "블럭 5,000개를 파내세요" [advancement.challenge_excavate_50k] - title = "숙련된 발굴가!" - description = "블럭 50,000개를 파내세요" +title = "숙련된 발굴가!" +description = "블럭 50,000개를 파내세요" [advancement.challenge_excavate_500k] - title = "마스터 발굴가!" - description = "블럭 500,000개를 파내세요" +title = "마스터 발굴가!" +description = "블럭 500,000개를 파내세요" [advancement.challenge_excavate_5m] - title = "신비로운 발굴가" - description = "블럭 5,000,000개를 파내세요" +title = "신비로운 발굴가" +description = "블럭 5,000,000개를 파내세요" [advancement.horrible_person] - title = "당신은 끔찍한 사람입니다" - description = "정말 이해할 수 없군요" +title = "당신은 끔찍한 사람입니다" +description = "정말 이해할 수 없군요" [advancement.challenge_turtle_egg_smasher] - title = "거북이 알 분쇄기!" - description = "거북이 알 100개 깨기" +title = "거북이 알 분쇄기!" +description = "거북이 알 100개 깨기" [advancement.challenge_turtle_egg_annihilator] - title = "거북이 알 섬멸자!" - description = "거북이 알 500개 깨기" +title = "거북이 알 섬멸자!" +description = "거북이 알 500개 깨기" [advancement.challenge_novice_hunter] - title = "초보 사냥꾼!" - description = "엔티티 100마리 처치" +title = "초보 사냥꾼!" +description = "엔티티 100마리 처치" [advancement.challenge_intermediate_hunter] - title = "중급 사냥꾼!" - description = "엔티티 500마리 처치" +title = "중급 사냥꾼!" +description = "엔티티 500마리 처치" [advancement.challenge_advanced_hunter] - title = "고급 사냥꾼!" - description = "엔티티 5,000마리 처치" +title = "고급 사냥꾼!" +description = "엔티티 5,000마리 처치" [advancement.challenge_creeper_conqueror] - title = "크리퍼 정복자!" - description = "크리퍼 50마리 처치" +title = "크리퍼 정복자!" +description = "크리퍼 50마리 처치" [advancement.challenge_creeper_annihilator] - title = "크리퍼 섬멸자!" - description = "크리퍼 200마리 처치" +title = "크리퍼 섬멸자!" +description = "크리퍼 200마리 처치" [advancement.challenge_pickaxe_1k] - title = "초보 광부" - description = "블록 1,000개 깨기" +title = "초보 광부" +description = "블록 1,000개 깨기" [advancement.challenge_pickaxe_5k] - title = "숙련된 광부" - description = "블록 5,000개 깨기" +title = "숙련된 광부" +description = "블록 5,000개 깨기" [advancement.challenge_pickaxe_50k] - title = "전문 광부" - description = "블록 50,000개 깨기" +title = "전문 광부" +description = "블록 50,000개 깨기" [advancement.challenge_pickaxe_500k] - title = "마스터 광부" - description = "블록 500,000개 깨기" +title = "마스터 광부" +description = "블록 500,000개 깨기" [advancement.challenge_pickaxe_5m] - title = "전설의 광부" - description = "블록 5,000,000개 깨기" +title = "전설의 광부" +description = "블록 5,000,000개 깨기" [advancement.challenge_eat_100] - title = "먹을 게 너무 많아!" - description = "아이템 100개 이상 먹기!" +title = "먹을 게 너무 많아!" +description = "아이템 100개 이상 먹기!" [advancement.challenge_eat_1000] - title = "채워지지 않는 허기!" - description = "아이템 1,000개 이상 먹기!" +title = "채워지지 않는 허기!" +description = "아이템 1,000개 이상 먹기!" [advancement.challenge_eat_10000] - title = "영원한 굶주림!" - description = "아이템 10,000개 이상 먹기!" +title = "영원한 굶주림!" +description = "아이템 10,000개 이상 먹기!" [advancement.challenge_harvest_100] - title = "풍성한 수확" - description = "작물 100개 이상 수확하기!" +title = "풍성한 수확" +description = "작물 100개 이상 수확하기!" [advancement.challenge_harvest_1000] - title = "대풍년" - description = "작물 1,000개 이상 수확하기!" +title = "대풍년" +description = "작물 1,000개 이상 수확하기!" [advancement.challenge_swim_1nm] - title = "인간 잠수함!" - description = "1해리 수영하기 (1,852 블록)" +title = "인간 잠수함!" +description = "1해리 수영하기 (1,852 블록)" [advancement.challenge_sneak_1k] - title = "무릎 통증" - description = "1킬로미터 이상 숨어 이동하기 (1,000 블록)" +title = "무릎 통증" +description = "1킬로미터 이상 숨어 이동하기 (1,000 블록)" [advancement.challenge_sneak_5k] - title = "그림자 보행자" - description = "5,000블록 이상 웅크려 이동하기" +title = "그림자 보행자" +description = "5,000블록 이상 웅크려 이동하기" [advancement.challenge_sneak_20k] - title = "유령" - description = "20,000블록 이상 웅크려 이동하기" +title = "유령" +description = "20,000블록 이상 웅크려 이동하기" [advancement.challenge_swim_5k] - title = "심해 잠수부" - description = "5,000블록 이상 수영하기" +title = "심해 잠수부" +description = "5,000블록 이상 수영하기" [advancement.challenge_swim_20k] - title = "포세이돈의 선택" - description = "20,000블록 이상 수영하기" +title = "포세이돈의 선택" +description = "20,000블록 이상 수영하기" [advancement.challenge_sword_100] - title = "첫 번째 피" - description = "검으로 100번 공격하기" +title = "첫 번째 피" +description = "검으로 100번 공격하기" [advancement.challenge_sword_1k] - title = "검무" - description = "검으로 1,000번 공격하기" +title = "검무" +description = "검으로 1,000번 공격하기" [advancement.challenge_sword_10k] - title = "천 개의 베기" - description = "검으로 10,000번 공격하기" +title = "천 개의 베기" +description = "검으로 10,000번 공격하기" [advancement.challenge_unarmed_100] - title = "술집 싸움꾼" - description = "맨손으로 100번 공격하기" +title = "술집 싸움꾼" +description = "맨손으로 100번 공격하기" [advancement.challenge_unarmed_1k] - title = "강철 주먹" - description = "맨손으로 1,000번 공격하기" +title = "강철 주먹" +description = "맨손으로 1,000번 공격하기" [advancement.challenge_unarmed_10k] - title = "원펀치" - description = "맨손으로 10,000번 공격하기" +title = "원펀치" +description = "맨손으로 10,000번 공격하기" [advancement.challenge_trag_1k] - title = "피의 대가" - description = "1,000 데미지 받기" +title = "피의 대가" +description = "1,000 데미지 받기" [advancement.challenge_trag_10k] - title = "핏빛 파도" - description = "10,000 데미지 받기" +title = "핏빛 파도" +description = "10,000 데미지 받기" [advancement.challenge_trag_100k] - title = "고통의 화신" - description = "100,000 데미지 받기" +title = "고통의 화신" +description = "100,000 데미지 받기" [advancement.challenge_ranged_100] - title = "사격 연습" - description = "투사체 100발 발사하기" +title = "사격 연습" +description = "투사체 100발 발사하기" [advancement.challenge_ranged_1k] - title = "호크아이" - description = "투사체 1,000발 발사하기" +title = "호크아이" +description = "투사체 1,000발 발사하기" [advancement.challenge_ranged_10k] - title = "화살의 폭풍" - description = "투사체 10,000발 발사하기" +title = "화살의 폭풍" +description = "투사체 10,000발 발사하기" [advancement.challenge_chronos_1h] - title = "째깍째깍" - description = "1시간 동안 온라인 상태 유지하기" +title = "째깍째깍" +description = "1시간 동안 온라인 상태 유지하기" [advancement.challenge_chronos_24h] - title = "시간의 모래" - description = "24시간 동안 온라인 상태 유지하기" +title = "시간의 모래" +description = "24시간 동안 온라인 상태 유지하기" [advancement.challenge_chronos_168h] - title = "영원한 자" - description = "168시간(1주일) 동안 온라인 상태 유지하기" +title = "영원한 자" +description = "168시간(1주일) 동안 온라인 상태 유지하기" [advancement.challenge_nether_50] - title = "지옥의 문지기" - description = "네더 생물 50마리 처치하기" +title = "지옥의 문지기" +description = "네더 생물 50마리 처치하기" [advancement.challenge_nether_500] - title = "심연의 수호자" - description = "네더 생물 500마리 처치하기" +title = "심연의 수호자" +description = "네더 생물 500마리 처치하기" [advancement.challenge_nether_5k] - title = "네더의 군주" - description = "네더 생물 5,000마리 처치하기" +title = "네더의 군주" +description = "네더 생물 5,000마리 처치하기" [advancement.challenge_rift_50] - title = "공간 이상" - description = "50번 텔레포트하기" +title = "공간 이상" +description = "50번 텔레포트하기" [advancement.challenge_rift_500] - title = "공허의 보행자" - description = "500번 텔레포트하기" +title = "공허의 보행자" +description = "500번 텔레포트하기" [advancement.challenge_rift_5k] - title = "세계의 틈새" - description = "5,000번 텔레포트하기" +title = "세계의 틈새" +description = "5,000번 텔레포트하기" [advancement.challenge_taming_10] - title = "동물의 속삭임" - description = "동물 10마리 번식시키기" +title = "동물의 속삭임" +description = "동물 10마리 번식시키기" [advancement.challenge_taming_50] - title = "무리의 리더" - description = "동물 50마리 번식시키기" +title = "무리의 리더" +description = "동물 50마리 번식시키기" [advancement.challenge_taming_500] - title = "야수의 지배자" - description = "동물 500마리 번식시키기" +title = "야수의 지배자" +description = "동물 500마리 번식시키기" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "질주의 악마" - description = "5킬로미터(5,000블록) 이상 질주하기" +title = "질주의 악마" +description = "5킬로미터(5,000블록) 이상 질주하기" [advancement.challenge_sprint_dist_50k] - title = "번개 다리" - description = "50킬로미터(50,000블록) 이상 질주하기" +title = "번개 다리" +description = "50킬로미터(50,000블록) 이상 질주하기" [advancement.challenge_agility_swim_1k] - title = "소금쟁이" - description = "1킬로미터(1,000블록) 이상 수영하기" +title = "소금쟁이" +description = "1킬로미터(1,000블록) 이상 수영하기" [advancement.challenge_agility_swim_10k] - title = "수중 항해사" - description = "10킬로미터(10,000블록) 이상 수영하기" +title = "수중 항해사" +description = "10킬로미터(10,000블록) 이상 수영하기" [advancement.challenge_fly_1k] - title = "하늘의 춤" - description = "1킬로미터(1,000블록) 이상 비행하기" +title = "하늘의 춤" +description = "1킬로미터(1,000블록) 이상 비행하기" [advancement.challenge_fly_10k] - title = "바람 타는 자" - description = "10킬로미터(10,000블록) 이상 비행하기" +title = "바람 타는 자" +description = "10킬로미터(10,000블록) 이상 비행하기" [advancement.challenge_agility_sneak_500] - title = "조용한 발걸음" - description = "500블록 이상 웅크리기" +title = "조용한 발걸음" +description = "500블록 이상 웅크리기" [advancement.challenge_agility_sneak_5k] - title = "유령 걸음" - description = "5킬로미터(5,000블록) 이상 웅크리기" +title = "유령 걸음" +description = "5킬로미터(5,000블록) 이상 웅크리기" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "철거 부대" - description = "500블록 파괴하기" +title = "철거 부대" +description = "500블록 파괴하기" [advancement.challenge_demolish_5k] - title = "파괴의 공" - description = "5,000블록 파괴하기" +title = "파괴의 공" +description = "5,000블록 파괴하기" [advancement.challenge_value_placed_10k] - title = "가치 있는 건축가" - description = "10,000 가치의 블록 설치하기" +title = "가치 있는 건축가" +description = "10,000 가치의 블록 설치하기" [advancement.challenge_value_placed_100k] - title = "건축의 대가" - description = "100,000 가치의 블록 설치하기" +title = "건축의 대가" +description = "100,000 가치의 블록 설치하기" [advancement.challenge_demolish_val_5k] - title = "회수 전문가" - description = "철거에서 5,000 가치를 회수하기" +title = "회수 전문가" +description = "철거에서 5,000 가치를 회수하기" [advancement.challenge_demolish_val_50k] - title = "완전 해체" - description = "철거에서 50,000 가치를 회수하기" +title = "완전 해체" +description = "철거에서 50,000 가치를 회수하기" [advancement.challenge_high_build_100] - title = "하늘 건축가" - description = "Y=128 위에 100블록 설치하기" +title = "하늘 건축가" +description = "Y=128 위에 100블록 설치하기" [advancement.challenge_high_build_1k] - title = "구름 위의 건축사" - description = "Y=128 위에 1,000블록 설치하기" +title = "구름 위의 건축사" +description = "Y=128 위에 1,000블록 설치하기" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "도끼 휘두르기" - description = "도끼를 500번 휘두르기" +title = "도끼 휘두르기" +description = "도끼를 500번 휘두르기" [advancement.challenge_axe_swing_5k] - title = "광전사" - description = "도끼를 5,000번 휘두르기" +title = "광전사" +description = "도끼를 5,000번 휘두르기" [advancement.challenge_axe_damage_1k] - title = "절단자" - description = "도끼로 1,000 데미지 입히기" +title = "절단자" +description = "도끼로 1,000 데미지 입히기" [advancement.challenge_axe_damage_10k] - title = "처형인의 도끼" - description = "도끼로 10,000 데미지 입히기" +title = "처형인의 도끼" +description = "도끼로 10,000 데미지 입히기" [advancement.challenge_axe_value_5k] - title = "목재 상인" - description = "5,000 가치의 목재를 수확하기" +title = "목재 상인" +description = "5,000 가치의 목재를 수확하기" [advancement.challenge_axe_value_50k] - title = "벌목 재벌" - description = "50,000 가치의 목재를 수확하기" +title = "벌목 재벌" +description = "50,000 가치의 목재를 수확하기" [advancement.challenge_leaves_500] - title = "낙엽 청소기" - description = "도끼로 잎 블록 500개 제거하기" +title = "낙엽 청소기" +description = "도끼로 잎 블록 500개 제거하기" [advancement.challenge_leaves_5k] - title = "낙엽 분쇄기" - description = "도끼로 잎 블록 5,000개 제거하기" +title = "낙엽 분쇄기" +description = "도끼로 잎 블록 5,000개 제거하기" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "데미지 흡수" - description = "방패로 1,000 데미지 막기" +title = "데미지 흡수" +description = "방패로 1,000 데미지 막기" [advancement.challenge_block_dmg_10k] - title = "인간 방패" - description = "방패로 10,000 데미지 막기" +title = "인간 방패" +description = "방패로 10,000 데미지 막기" [advancement.challenge_block_proj_100] - title = "화살 튕기기" - description = "방패로 투사체 100개 막기" +title = "화살 튕기기" +description = "방패로 투사체 100개 막기" [advancement.challenge_block_proj_1k] - title = "투사체 방패" - description = "방패로 투사체 1,000개 막기" +title = "투사체 방패" +description = "방패로 투사체 1,000개 막기" [advancement.challenge_block_melee_500] - title = "패리의 달인" - description = "방패로 근접 공격 500회 막기" +title = "패리의 달인" +description = "방패로 근접 공격 500회 막기" [advancement.challenge_block_melee_5k] - title = "철의 요새" - description = "방패로 근접 공격 5,000회 막기" +title = "철의 요새" +description = "방패로 근접 공격 5,000회 막기" [advancement.challenge_block_heavy_50] - title = "탱커" - description = "강력한 공격(5 데미지 초과) 50회 막기" +title = "탱커" +description = "강력한 공격(5 데미지 초과) 50회 막기" [advancement.challenge_block_heavy_500] - title = "움직이지 않는 벽" - description = "강력한 공격(5 데미지 초과) 500회 막기" +title = "움직이지 않는 벽" +description = "강력한 공격(5 데미지 초과) 500회 막기" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "양조 준비" - description = "양조기 10개 설치하기" +title = "양조 준비" +description = "양조기 10개 설치하기" [advancement.challenge_brew_stands_50] - title = "포션 공장" - description = "양조기 50개 설치하기" +title = "포션 공장" +description = "양조기 50개 설치하기" [advancement.challenge_brew_strong_25] - title = "강력한 양조" - description = "강화된 포션 25개 마시기" +title = "강력한 양조" +description = "강화된 포션 25개 마시기" [advancement.challenge_brew_strong_250] - title = "최대 효능" - description = "강화된 포션 250개 마시기" +title = "최대 효능" +description = "강화된 포션 250개 마시기" [advancement.challenge_brew_splash_hits_50] - title = "투척 범위" - description = "투척 포션으로 엔티티 50마리 맞추기" +title = "투척 범위" +description = "투척 포션으로 엔티티 50마리 맞추기" [advancement.challenge_brew_splash_hits_500] - title = "역병 의사" - description = "투척 포션으로 엔티티 500마리 맞추기" +title = "역병 의사" +description = "투척 포션으로 엔티티 500마리 맞추기" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "쉬지 않는 자" - description = "활동 중 1킬로미터 이동하기" +title = "쉬지 않는 자" +description = "활동 중 1킬로미터 이동하기" [advancement.challenge_active_dist_10k] - title = "길 개척자" - description = "활동 중 10킬로미터 이동하기" +title = "길 개척자" +description = "활동 중 10킬로미터 이동하기" [advancement.challenge_active_dist_100k] - title = "영원한 움직임" - description = "활동 중 100킬로미터 이동하기" +title = "영원한 움직임" +description = "활동 중 100킬로미터 이동하기" [advancement.challenge_beds_10] - title = "이른 기상" - description = "침대에서 10번 자기" +title = "이른 기상" +description = "침대에서 10번 자기" [advancement.challenge_beds_100] - title = "시간 건너뛰기" - description = "침대에서 100번 자기" +title = "시간 건너뛰기" +description = "침대에서 100번 자기" [advancement.challenge_chronos_tp_50] - title = "시공간 이동" - description = "50번 텔레포트하기" +title = "시공간 이동" +description = "50번 텔레포트하기" [advancement.challenge_chronos_tp_500] - title = "시간의 왜곡" - description = "500번 텔레포트하기" +title = "시간의 왜곡" +description = "500번 텔레포트하기" [advancement.challenge_chronos_deaths_10] - title = "필멸자" - description = "10번 죽기" +title = "필멸자" +description = "10번 죽기" [advancement.challenge_chronos_deaths_100] - title = "죽음을 이기는 자" - description = "100번 죽기" +title = "죽음을 이기는 자" +description = "100번 죽기" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "제작의 가치" - description = "총 10,000 가치의 아이템 제작하기" +title = "제작의 가치" +description = "총 10,000 가치의 아이템 제작하기" [advancement.challenge_craft_value_100k] - title = "장인" - description = "총 100,000 가치의 아이템 제작하기" +title = "장인" +description = "총 100,000 가치의 아이템 제작하기" [advancement.challenge_craft_tools_25] - title = "도구 대장장이" - description = "도구 25개 제작하기" +title = "도구 대장장이" +description = "도구 25개 제작하기" [advancement.challenge_craft_tools_250] - title = "단조의 대가" - description = "도구 250개 제작하기" +title = "단조의 대가" +description = "도구 250개 제작하기" [advancement.challenge_craft_armor_25] - title = "갑옷 대장장이" - description = "갑옷 25개 제작하기" +title = "갑옷 대장장이" +description = "갑옷 25개 제작하기" [advancement.challenge_craft_armor_250] - title = "갑옷의 대가" - description = "갑옷 250개 제작하기" +title = "갑옷의 대가" +description = "갑옷 250개 제작하기" [advancement.challenge_craft_mass_25k] - title = "대량 생산" - description = "아이템 25,000개 제작하기" +title = "대량 생산" +description = "아이템 25,000개 제작하기" [advancement.challenge_craft_mass_250k] - title = "산업 혁명" - description = "아이템 250,000개 제작하기" +title = "산업 혁명" +description = "아이템 250,000개 제작하기" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "수집가" - description = "고유 아이템 50종 발견하기" +title = "수집가" +description = "고유 아이템 50종 발견하기" [advancement.challenge_discover_items_250] - title = "목록 작성자" - description = "고유 아이템 250종 발견하기" +title = "목록 작성자" +description = "고유 아이템 250종 발견하기" [advancement.challenge_discover_blocks_50] - title = "측량사" - description = "고유 블록 50종 발견하기" +title = "측량사" +description = "고유 블록 50종 발견하기" [advancement.challenge_discover_blocks_250] - title = "지질학자" - description = "고유 블록 250종 발견하기" +title = "지질학자" +description = "고유 블록 250종 발견하기" [advancement.challenge_discover_mobs_25] - title = "관찰자" - description = "고유 몹 25종 발견하기" +title = "관찰자" +description = "고유 몹 25종 발견하기" [advancement.challenge_discover_mobs_75] - title = "박물학자" - description = "고유 몹 75종 발견하기" +title = "박물학자" +description = "고유 몹 75종 발견하기" [advancement.challenge_discover_biomes_10] - title = "방랑자" - description = "고유 바이옴 10종 발견하기" +title = "방랑자" +description = "고유 바이옴 10종 발견하기" [advancement.challenge_discover_biomes_40] - title = "세계 여행자" - description = "고유 바이옴 40종 발견하기" +title = "세계 여행자" +description = "고유 바이옴 40종 발견하기" [advancement.challenge_discover_foods_10] - title = "미식가" - description = "고유 음식 10종 발견하기" +title = "미식가" +description = "고유 음식 10종 발견하기" [advancement.challenge_discover_foods_30] - title = "요리의 달인" - description = "고유 음식 30종 발견하기" +title = "요리의 달인" +description = "고유 음식 30종 발견하기" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "힘의 직공" - description = "인챈트 파워 100 축적하기" +title = "힘의 직공" +description = "인챈트 파워 100 축적하기" [advancement.challenge_enchant_power_1k] - title = "비술의 대가" - description = "인챈트 파워 1,000 축적하기" +title = "비술의 대가" +description = "인챈트 파워 1,000 축적하기" [advancement.challenge_enchant_levels_1k] - title = "레벨 소모자" - description = "인챈트에 경험 레벨 1,000 사용하기" +title = "레벨 소모자" +description = "인챈트에 경험 레벨 1,000 사용하기" [advancement.challenge_enchant_levels_10k] - title = "경험치의 무덤" - description = "인챈트에 경험 레벨 10,000 사용하기" +title = "경험치의 무덤" +description = "인챈트에 경험 레벨 10,000 사용하기" [advancement.challenge_enchant_high_25] - title = "도박사" - description = "최대 레벨 인챈트 25회 수행하기" +title = "도박사" +description = "최대 레벨 인챈트 25회 수행하기" [advancement.challenge_enchant_high_250] - title = "전설의 인챈터" - description = "최대 레벨 인챈트 250회 수행하기" +title = "전설의 인챈터" +description = "최대 레벨 인챈트 250회 수행하기" [advancement.challenge_enchant_total_500] - title = "레벨 연소" - description = "모든 인챈트에 총 500레벨 사용하기" +title = "레벨 연소" +description = "모든 인챈트에 총 500레벨 사용하기" [advancement.challenge_enchant_total_5k] - title = "비술의 투자" - description = "모든 인챈트에 총 5,000레벨 사용하기" +title = "비술의 투자" +description = "모든 인챈트에 총 5,000레벨 사용하기" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "삽질꾼" - description = "삽을 500번 휘두르기" +title = "삽질꾼" +description = "삽을 500번 휘두르기" [advancement.challenge_dig_swing_5k] - title = "굴착기" - description = "삽을 5,000번 휘두르기" +title = "굴착기" +description = "삽을 5,000번 휘두르기" [advancement.challenge_dig_damage_1k] - title = "삽의 기사" - description = "삽으로 1,000 데미지 입히기" +title = "삽의 기사" +description = "삽으로 1,000 데미지 입히기" [advancement.challenge_dig_damage_10k] - title = "삽의 대가" - description = "삽으로 10,000 데미지 입히기" +title = "삽의 대가" +description = "삽으로 10,000 데미지 입히기" [advancement.challenge_dig_value_5k] - title = "흙 상인" - description = "5,000 가치의 블록 굴착하기" +title = "흙 상인" +description = "5,000 가치의 블록 굴착하기" [advancement.challenge_dig_value_50k] - title = "대지의 지배자" - description = "50,000 가치의 블록 굴착하기" +title = "대지의 지배자" +description = "50,000 가치의 블록 굴착하기" [advancement.challenge_dig_gravel_500] - title = "자갈 분쇄기" - description = "자갈, 모래, 점토 블록 500개 파기" +title = "자갈 분쇄기" +description = "자갈, 모래, 점토 블록 500개 파기" [advancement.challenge_dig_gravel_5k] - title = "모래 체질" - description = "자갈, 모래, 점토 블록 5,000개 파기" +title = "모래 체질" +description = "자갈, 모래, 점토 블록 5,000개 파기" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "씨 뿌리는 자" - description = "작물 100개 심기" +title = "씨 뿌리는 자" +description = "작물 100개 심기" [advancement.challenge_plant_1k] - title = "녹색 손가락" - description = "작물 1,000개 심기" +title = "녹색 손가락" +description = "작물 1,000개 심기" [advancement.challenge_plant_5k] - title = "농업 재벌" - description = "작물 5,000개 심기" +title = "농업 재벌" +description = "작물 5,000개 심기" [advancement.challenge_compost_50] - title = "재활용가" - description = "아이템 50개 퇴비로 만들기" +title = "재활용가" +description = "아이템 50개 퇴비로 만들기" [advancement.challenge_compost_500] - title = "토양 개량가" - description = "아이템 500개 퇴비로 만들기" +title = "토양 개량가" +description = "아이템 500개 퇴비로 만들기" [advancement.challenge_shear_50] - title = "양털 깎는 자" - description = "엔티티 50마리 양털 깎기" +title = "양털 깎는 자" +description = "엔티티 50마리 양털 깎기" [advancement.challenge_shear_250] - title = "무리의 주인" - description = "엔티티 250마리 양털 깎기" +title = "무리의 주인" +description = "엔티티 250마리 양털 깎기" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "사냥꾼" - description = "생물 500마리 처치하기" +title = "사냥꾼" +description = "생물 500마리 처치하기" [advancement.challenge_kills_5k] - title = "처형인" - description = "생물 5,000마리 처치하기" +title = "처형인" +description = "생물 5,000마리 처치하기" [advancement.challenge_boss_1] - title = "보스 도전자" - description = "보스 몹 1마리 처치하기" +title = "보스 도전자" +description = "보스 몹 1마리 처치하기" [advancement.challenge_boss_10] - title = "전설 사냥꾼" - description = "보스 몹 10마리 처치하기" +title = "전설 사냥꾼" +description = "보스 몹 10마리 처치하기" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "시듦" - description = "위더 데미지 500 견디기" +title = "시듦" +description = "위더 데미지 500 견디기" [advancement.challenge_wither_dmg_5k] - title = "역병의 생존자" - description = "위더 데미지 5,000 견디기" +title = "역병의 생존자" +description = "위더 데미지 5,000 견디기" [advancement.challenge_wither_skel_25] - title = "뼈 수집가" - description = "위더 스켈레톤 25마리 처치하기" +title = "뼈 수집가" +description = "위더 스켈레톤 25마리 처치하기" [advancement.challenge_wither_skel_250] - title = "해골의 천적" - description = "위더 스켈레톤 250마리 처치하기" +title = "해골의 천적" +description = "위더 스켈레톤 250마리 처치하기" [advancement.challenge_wither_boss_1] - title = "위더 처치자" - description = "위더 처치하기" +title = "위더 처치자" +description = "위더 처치하기" [advancement.challenge_wither_boss_10] - title = "네더의 지배자" - description = "위더 10번 처치하기" +title = "네더의 지배자" +description = "위더 10번 처치하기" [advancement.challenge_roses_10] - title = "죽음의 정원사" - description = "위더 장미 10송이 부수기" +title = "죽음의 정원사" +description = "위더 장미 10송이 부수기" [advancement.challenge_roses_100] - title = "역병 수집가" - description = "위더 장미 100송이 부수기" +title = "역병 수집가" +description = "위더 장미 100송이 부수기" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "광부의 팔" - description = "곡괭이를 500번 휘두르기" +title = "광부의 팔" +description = "곡괭이를 500번 휘두르기" [advancement.challenge_pick_swing_5k] - title = "터널 굴착자" - description = "곡괭이를 5,000번 휘두르기" +title = "터널 굴착자" +description = "곡괭이를 5,000번 휘두르기" [advancement.challenge_pick_damage_1k] - title = "곡괭이 전사" - description = "곡괭이로 1,000 데미지 입히기" +title = "곡괭이 전사" +description = "곡괭이로 1,000 데미지 입히기" [advancement.challenge_pick_damage_10k] - title = "전쟁의 곡괭이" - description = "곡괭이로 10,000 데미지 입히기" +title = "전쟁의 곡괭이" +description = "곡괭이로 10,000 데미지 입히기" [advancement.challenge_pick_value_5k] - title = "보석 탐색자" - description = "5,000 가치의 블록 채굴하기" +title = "보석 탐색자" +description = "5,000 가치의 블록 채굴하기" [advancement.challenge_pick_value_50k] - title = "광석 재벌" - description = "50,000 가치의 블록 채굴하기" +title = "광석 재벌" +description = "50,000 가치의 블록 채굴하기" [advancement.challenge_pick_ores_500] - title = "탐광자" - description = "광석 블록 500개 채굴하기" +title = "탐광자" +description = "광석 블록 500개 채굴하기" [advancement.challenge_pick_ores_5k] - title = "채굴의 대가" - description = "광석 블록 5,000개 채굴하기" +title = "채굴의 대가" +description = "광석 블록 5,000개 채굴하기" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "명사수" - description = "원거리 데미지 1,000 입히기" +title = "명사수" +description = "원거리 데미지 1,000 입히기" [advancement.challenge_ranged_dmg_10k] - title = "치명적 궁수" - description = "원거리 데미지 10,000 입히기" +title = "치명적 궁수" +description = "원거리 데미지 10,000 입히기" [advancement.challenge_ranged_dist_5k] - title = "장거리 사격" - description = "투사체 총 비행 거리 5,000블록 달성하기" +title = "장거리 사격" +description = "투사체 총 비행 거리 5,000블록 달성하기" [advancement.challenge_ranged_dist_50k] - title = "초장거리 사수" - description = "투사체 총 비행 거리 50,000블록 달성하기" +title = "초장거리 사수" +description = "투사체 총 비행 거리 50,000블록 달성하기" [advancement.challenge_ranged_kills_50] - title = "궁수" - description = "원거리 무기로 몹 50마리 처치하기" +title = "궁수" +description = "원거리 무기로 몹 50마리 처치하기" [advancement.challenge_ranged_kills_500] - title = "궁술의 대가" - description = "원거리 무기로 몹 500마리 처치하기" +title = "궁술의 대가" +description = "원거리 무기로 몹 500마리 처치하기" [advancement.challenge_longshot_25] - title = "저격수" - description = "장거리 사격(30블록 초과) 25회 명중하기" +title = "저격수" +description = "장거리 사격(30블록 초과) 25회 명중하기" [advancement.challenge_longshot_250] - title = "매의 눈" - description = "장거리 사격(30블록 초과) 250회 명중하기" +title = "매의 눈" +description = "장거리 사격(30블록 초과) 250회 명중하기" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "진주 던지기" - description = "엔더 진주 50개 던지기" +title = "진주 던지기" +description = "엔더 진주 50개 던지기" [advancement.challenge_rift_pearls_500] - title = "텔레포트 중독" - description = "엔더 진주 500개 던지기" +title = "텔레포트 중독" +description = "엔더 진주 500개 던지기" [advancement.challenge_rift_enderman_50] - title = "엔더맨 사냥꾼" - description = "엔더맨 50마리 처치하기" +title = "엔더맨 사냥꾼" +description = "엔더맨 50마리 처치하기" [advancement.challenge_rift_enderman_500] - title = "공허의 추적자" - description = "엔더맨 500마리 처치하기" +title = "공허의 추적자" +description = "엔더맨 500마리 처치하기" [advancement.challenge_rift_dragon_500] - title = "용과 싸우는 자" - description = "엔더 드래곤에게 500 데미지 입히기" +title = "용과 싸우는 자" +description = "엔더 드래곤에게 500 데미지 입히기" [advancement.challenge_rift_dragon_5k] - title = "용살자" - description = "엔더 드래곤에게 5,000 데미지 입히기" +title = "용살자" +description = "엔더 드래곤에게 5,000 데미지 입히기" [advancement.challenge_rift_crystal_10] - title = "수정 파괴자" - description = "엔드 수정 10개 파괴하기" +title = "수정 파괴자" +description = "엔드 수정 10개 파괴하기" [advancement.challenge_rift_crystal_100] - title = "엔드 파괴자" - description = "엔드 수정 100개 파괴하기" +title = "엔드 파괴자" +description = "엔드 수정 100개 파괴하기" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "낚시꾼" - description = "물고기 25마리 잡기" +title = "낚시꾼" +description = "물고기 25마리 잡기" [advancement.challenge_fish_250] - title = "낚시의 달인" - description = "물고기 250마리 잡기" +title = "낚시의 달인" +description = "물고기 250마리 잡기" [advancement.challenge_drowned_25] - title = "드라운드 사냥꾼" - description = "드라운드 25마리 처치하기" +title = "드라운드 사냥꾼" +description = "드라운드 25마리 처치하기" [advancement.challenge_drowned_250] - title = "바다 정화자" - description = "드라운드 250마리 처치하기" +title = "바다 정화자" +description = "드라운드 250마리 처치하기" [advancement.challenge_guardian_10] - title = "가디언 처치자" - description = "가디언 10마리 처치하기" +title = "가디언 처치자" +description = "가디언 10마리 처치하기" [advancement.challenge_guardian_100] - title = "신전 침략자" - description = "가디언 100마리 처치하기" +title = "신전 침략자" +description = "가디언 100마리 처치하기" [advancement.challenge_underwater_blocks_100] - title = "수중 채굴자" - description = "수중에서 블록 100개 파괴하기" +title = "수중 채굴자" +description = "수중에서 블록 100개 파괴하기" [advancement.challenge_underwater_blocks_1k] - title = "수중 기술자" - description = "수중에서 블록 1,000개 파괴하기" +title = "수중 기술자" +description = "수중에서 블록 1,000개 파괴하기" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "등 뒤의 칼" - description = "웅크리기 중 500 데미지 입히기" +title = "등 뒤의 칼" +description = "웅크리기 중 500 데미지 입히기" [advancement.challenge_stealth_dmg_5k] - title = "침묵의 살인자" - description = "웅크리기 중 5,000 데미지 입히기" +title = "침묵의 살인자" +description = "웅크리기 중 5,000 데미지 입히기" [advancement.challenge_stealth_kills_10] - title = "암살자" - description = "웅크리기 중 몹 10마리 처치하기" +title = "암살자" +description = "웅크리기 중 몹 10마리 처치하기" [advancement.challenge_stealth_kills_100] - title = "그림자 사신" - description = "웅크리기 중 몹 100마리 처치하기" +title = "그림자 사신" +description = "웅크리기 중 몹 100마리 처치하기" [advancement.challenge_stealth_time_1h] - title = "인내" - description = "1시간(3,600초) 웅크리기" +title = "인내" +description = "1시간(3,600초) 웅크리기" [advancement.challenge_stealth_time_10h] - title = "그림자의 지배자" - description = "10시간(36,000초) 웅크리기" +title = "그림자의 지배자" +description = "10시간(36,000초) 웅크리기" [advancement.challenge_stealth_arrows_50] - title = "조용한 궁수" - description = "웅크리기 중 화살 50발 발사하기" +title = "조용한 궁수" +description = "웅크리기 중 화살 50발 발사하기" [advancement.challenge_stealth_arrows_500] - title = "유령 궁수" - description = "웅크리기 중 화살 500발 발사하기" +title = "유령 궁수" +description = "웅크리기 중 화살 500발 발사하기" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "검의 견습생" - description = "검으로 1,000 데미지 입히기" +title = "검의 견습생" +description = "검으로 1,000 데미지 입히기" [advancement.challenge_sword_dmg_10k] - title = "검사" - description = "검으로 10,000 데미지 입히기" +title = "검사" +description = "검으로 10,000 데미지 입히기" [advancement.challenge_sword_kills_50] - title = "결투사" - description = "검으로 몹 50마리 처치하기" +title = "결투사" +description = "검으로 몹 50마리 처치하기" [advancement.challenge_sword_kills_500] - title = "검투사" - description = "검으로 몹 500마리 처치하기" +title = "검투사" +description = "검으로 몹 500마리 처치하기" [advancement.challenge_sword_crit_50] - title = "치명타" - description = "검으로 치명타 50회 달성하기" +title = "치명타" +description = "검으로 치명타 50회 달성하기" [advancement.challenge_sword_crit_500] - title = "정밀의 달인" - description = "검으로 치명타 500회 달성하기" +title = "정밀의 달인" +description = "검으로 치명타 500회 달성하기" [advancement.challenge_sword_heavy_25] - title = "강타" - description = "검으로 강력한 일격(8 데미지 초과) 25회 달성하기" +title = "강타" +description = "검으로 강력한 일격(8 데미지 초과) 25회 달성하기" [advancement.challenge_sword_heavy_250] - title = "파멸의 일격" - description = "검으로 강력한 일격(8 데미지 초과) 250회 달성하기" +title = "파멸의 일격" +description = "검으로 강력한 일격(8 데미지 초과) 250회 달성하기" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "야수 조련사" - description = "펫이 총 500 데미지 입히기" +title = "야수 조련사" +description = "펫이 총 500 데미지 입히기" [advancement.challenge_pet_dmg_5k] - title = "전쟁의 주인" - description = "펫이 총 5,000 데미지 입히기" +title = "전쟁의 주인" +description = "펫이 총 5,000 데미지 입히기" [advancement.challenge_tamed_10] - title = "동물의 친구" - description = "동물 10마리 길들이기" +title = "동물의 친구" +description = "동물 10마리 길들이기" [advancement.challenge_tamed_100] - title = "동물원 관리인" - description = "동물 100마리 길들이기" +title = "동물원 관리인" +description = "동물 100마리 길들이기" [advancement.challenge_pet_kills_25] - title = "무리의 전술" - description = "펫이 생물 25마리 처치하기" +title = "무리의 전술" +description = "펫이 생물 25마리 처치하기" [advancement.challenge_pet_kills_250] - title = "우두머리 지휘관" - description = "펫이 생물 250마리 처치하기" +title = "우두머리 지휘관" +description = "펫이 생물 250마리 처치하기" [advancement.challenge_taming_2500] - title = "번식 전문가" - description = "동물 2,500마리 번식시키기" +title = "번식 전문가" +description = "동물 2,500마리 번식시키기" [advancement.challenge_taming_25k] - title = "유전학의 대가" - description = "동물 25,000마리 번식시키기" +title = "유전학의 대가" +description = "동물 25,000마리 번식시키기" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "샌드백" - description = "500번 맞기" +title = "샌드백" +description = "500번 맞기" [advancement.challenge_trag_hits_5k] - title = "고통의 미식가" - description = "5,000번 맞기" +title = "고통의 미식가" +description = "5,000번 맞기" [advancement.challenge_trag_deaths_10] - title = "아홉 개의 생명" - description = "10번 죽기" +title = "아홉 개의 생명" +description = "10번 죽기" [advancement.challenge_trag_deaths_100] - title = "반복되는 악몽" - description = "100번 죽기" +title = "반복되는 악몽" +description = "100번 죽기" [advancement.challenge_trag_fire_500] - title = "화상 피해자" - description = "화염 데미지 500 견디기" +title = "화상 피해자" +description = "화염 데미지 500 견디기" [advancement.challenge_trag_fire_5k] - title = "불사조" - description = "화염 데미지 5,000 견디기" +title = "불사조" +description = "화염 데미지 5,000 견디기" [advancement.challenge_trag_fall_500] - title = "중력 확인" - description = "낙하 데미지 500 견디기" +title = "중력 확인" +description = "낙하 데미지 500 견디기" [advancement.challenge_trag_fall_5k] - title = "종단 속도" - description = "낙하 데미지 5,000 견디기" +title = "종단 속도" +description = "낙하 데미지 5,000 견디기" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "싸움꾼" - description = "맨주먹으로 1,000 데미지 입히기" +title = "싸움꾼" +description = "맨주먹으로 1,000 데미지 입히기" [advancement.challenge_unarmed_dmg_10k] - title = "무술가" - description = "맨주먹으로 10,000 데미지 입히기" +title = "무술가" +description = "맨주먹으로 10,000 데미지 입히기" [advancement.challenge_unarmed_kills_25] - title = "맨주먹 싸움" - description = "맨주먹으로 몹 25마리 처치하기" +title = "맨주먹 싸움" +description = "맨주먹으로 몹 25마리 처치하기" [advancement.challenge_unarmed_kills_250] - title = "주먹의 전설" - description = "맨주먹으로 몹 250마리 처치하기" +title = "주먹의 전설" +description = "맨주먹으로 몹 250마리 처치하기" [advancement.challenge_unarmed_crit_25] - title = "치명적 주먹" - description = "맨주먹으로 치명타 25회 달성하기" +title = "치명적 주먹" +description = "맨주먹으로 치명타 25회 달성하기" [advancement.challenge_unarmed_crit_250] - title = "정밀한 주먹" - description = "맨주먹으로 치명타 250회 달성하기" +title = "정밀한 주먹" +description = "맨주먹으로 치명타 250회 달성하기" [advancement.challenge_unarmed_heavy_25] - title = "강력한 주먹" - description = "맨주먹으로 강력한 일격(6 데미지 초과) 25회 달성하기" +title = "강력한 주먹" +description = "맨주먹으로 강력한 일격(6 데미지 초과) 25회 달성하기" [advancement.challenge_unarmed_heavy_250] - title = "일격필살" - description = "맨주먹으로 강력한 일격(6 데미지 초과) 250회 달성하기" +title = "일격필살" +description = "맨주먹으로 강력한 일격(6 데미지 초과) 250회 달성하기" # items [items] [items.bound_ender_peral] - name = "유물 포트키" - usage1 = "Shift + 좌클릭으로 연결" - usage2 = "우클릭으로 연결된 인벤토리에 접근" +name = "유물 포트키" +usage1 = "Shift + 좌클릭으로 연결" +usage2 = "우클릭으로 연결된 인벤토리에 접근" [items.bound_eye_of_ender] - name = "안구 닻" - usage1 = "우클릭하여 소모하고 연결된 위치로 순간이동" - usage2 = "Shift + 좌클릭으로 블록에 연결" +name = "안구 닻" +usage1 = "우클릭하여 소모하고 연결된 위치로 순간이동" +usage2 = "Shift + 좌클릭으로 블록에 연결" [items.bound_redstone_torch] - name = "레드스톤 리모컨" - usage1 = "우클릭으로 1틱 레드스톤 신호 발생" - usage2 = "'대상' 블록에 Shift + 좌클릭으로 연결" +name = "레드스톤 리모컨" +usage1 = "우클릭으로 1틱 레드스톤 신호 발생" +usage2 = "'대상' 블록에 Shift + 좌클릭으로 연결" [items.bound_snowball] - name = "거미줄 올가미!" - usage1 = "던져서 해당 위치에 임시 거미줄 함정을 만듭니다" +name = "거미줄 올가미!" +usage1 = "던져서 해당 위치에 임시 거미줄 함정을 만듭니다" [items.chrono_time_bottle] - name = "시간을 담은 병" - usage1 = "인벤토리에 있는 동안 수동으로 시간을 저장합니다" - usage2 = "우클릭으로 시간이 걸리는 블록이나 새끼 동물에게 저장된 시간을 사용합니다" - stored = "저장된 시간" +name = "시간을 담은 병" +usage1 = "인벤토리에 있는 동안 수동으로 시간을 저장합니다" +usage2 = "우클릭으로 시간이 걸리는 블록이나 새끼 동물에게 저장된 시간을 사용합니다" +stored = "저장된 시간" [items.chrono_time_bomb] - name = "시간 폭탄" - usage1 = "우클릭하여 시간의 장을 생성하는 시간 화살을 발사합니다" +name = "시간 폭탄" +usage1 = "우클릭하여 시간의 장을 생성하는 시간 화살을 발사합니다" [items.elevator_block] - name = "엘리베이터 블록" - usage1 = "점프하여 위로 순간이동" - usage2 = "Shift로 아래로 순간이동" - usage3 = "엘리베이터 사이에 최소 2칸의 공기 블록이 필요합니다" +name = "엘리베이터 블록" +usage1 = "점프하여 위로 순간이동" +usage2 = "Shift로 아래로 순간이동" +usage3 = "엘리베이터 사이에 최소 2칸의 공기 블록이 필요합니다" # snippets [snippets] [snippets.gui] - level = "레벨" - knowledge = "지식" - power_used = "사용 중인 능력치" - not_learned = "미습득" - xp = "XP까지" - welcome = "환영합니다!" - welcome_back = "다시 오신 것을 환영합니다!" - xp_bonus_for_time = "XP 보너스" - max_ability_power = "최대 능력 파워" - unlock_this_by_clicking = "우클릭하여 잠금 해제: " - back = "뒤로" - unlearn_all = "모두 잊기" - unlearned_all = "모두 잊었습니다" +level = "레벨" +knowledge = "지식" +power_used = "사용 중인 능력치" +not_learned = "미습득" +xp = "XP까지" +welcome = "환영합니다!" +welcome_back = "다시 오신 것을 환영합니다!" +xp_bonus_for_time = "XP 보너스" +max_ability_power = "최대 능력 파워" +unlock_this_by_clicking = "우클릭하여 잠금 해제: " +back = "뒤로" +unlearn_all = "모두 잊기" +unlearned_all = "모두 잊었습니다" [snippets.adapt_menu] - may_not_unlearn = "잊을 수 없습니다" - may_unlearn = "배우기/잊기 가능" - knowledge_cost = "지식 비용" - knowledge_available = "사용 가능한 지식" - already_learned = "이미 습득함" - unlearn_refund = "클릭하여 잊기 및 환불" - no_refunds = "하드코어, 환불 불가" - knowledge = "지식" - click_learn = "클릭하여 배우기" - no_knowledge = "(지식이 없습니다)" - you_only_have = "현재 보유량:" - how_to_level_up = "스킬 레벨을 올려 최대 파워를 높이세요." - not_enough_power = "파워가 부족합니다! 각 능력 레벨은 파워 1을 소모합니다." - power = "파워" - power_drain = "파워 소모" - learned = "습득 완료 " - unlearned = "잊음 " - activator_block = "책장" +may_not_unlearn = "잊을 수 없습니다" +may_unlearn = "배우기/잊기 가능" +knowledge_cost = "지식 비용" +knowledge_available = "사용 가능한 지식" +already_learned = "이미 습득함" +unlearn_refund = "클릭하여 잊기 및 환불" +no_refunds = "하드코어, 환불 불가" +knowledge = "지식" +click_learn = "클릭하여 배우기" +no_knowledge = "(지식이 없습니다)" +you_only_have = "현재 보유량:" +how_to_level_up = "스킬 레벨을 올려 최대 파워를 높이세요." +not_enough_power = "파워가 부족합니다! 각 능력 레벨은 파워 1을 소모합니다." +power = "파워" +power_drain = "파워 소모" +learned = "습득 완료 " +unlearned = "잊음 " +activator_block = "책장" [snippets.knowledge_orb] - contains = "포함" - knowledge = "지식" - rightclick = "우클릭" - togainknowledge = "하여 이 지식을 획득" - knowledge_orb = "지식의 구슬" +contains = "포함" +knowledge = "지식" +rightclick = "우클릭" +togainknowledge = "하여 이 지식을 획득" +knowledge_orb = "지식의 구슬" [snippets.experience_orb] - contains = "포함" - xp = "경험치" - rightclick = "우클릭" - togainxp = "하여 이 경험치를 획득" - xporb = "경험치 구슬" +contains = "포함" +xp = "경험치" +rightclick = "우클릭" +togainxp = "하여 이 경험치를 획득" +xporb = "경험치 구슬" # skill [skill] [skill.agility] - name = "민첩" - icon = "⇉" - description = "민첩은 장애물 앞에서 빠르고 유연하게 움직이는 능력입니다." +name = "민첩" +icon = "⇉" +description = "민첩은 장애물 앞에서 빠르고 유연하게 움직이는 능력입니다." [skill.architect] - name = "건축" - icon = "⬧" - description = "구조물은 세계의 기초입니다. 현실은 당신의 손에 달려 있으며, 당신이 지배합니다." +name = "건축" +icon = "⬧" +description = "구조물은 세계의 기초입니다. 현실은 당신의 손에 달려 있으며, 당신이 지배합니다." [skill.axes] - name = "도끼" - icon = "🪓" - description1 = "왜 나무를 베겠어, " - description2 = "것들을" - description3 = " 벨 수 있는데, 결과는 똑같잖아!" +name = "도끼" +icon = "🪓" +description1 = "왜 나무를 베겠어, " +description2 = "것들을" +description3 = " 벨 수 있는데, 결과는 똑같잖아!" [skill.brewing] - name = "양조" - icon = "❦" - description = "두 배 거품, 세 배 거품, 네 배 거품- 아직도 이 물약을 가마솥에 넣을 수 없다니" +name = "양조" +icon = "❦" +description = "두 배 거품, 세 배 거품, 네 배 거품- 아직도 이 물약을 가마솥에 넣을 수 없다니" [skill.blocking] - name = "방어" - icon = "🛡" - description = "돌멩이와 막대기로는 뼈가 부러지지 않지만, 방패는 가능하죠." +name = "방어" +icon = "🛡" +description = "돌멩이와 막대기로는 뼈가 부러지지 않지만, 방패는 가능하죠." [skill.crafting] - name = "제작" - icon = "⌂" - description = "놓을 조각이 더 이상 없다면, 하나 더 만들면 되지 않겠어?" +name = "제작" +icon = "⌂" +description = "놓을 조각이 더 이상 없다면, 하나 더 만들면 되지 않겠어?" [skill.discovery] - name = "탐험" - icon = "⚛" - description = "인식이 확장될수록, 마음이 풀려 알지 못했던 것들을 발견하게 됩니다." +name = "탐험" +icon = "⚛" +description = "인식이 확장될수록, 마음이 풀려 알지 못했던 것들을 발견하게 됩니다." [skill.enchanting] - name = "마법부여" - icon = "♰" - description = "무슨 소리를 하는 거야? 예언, 환상, 미신적인 헛소리?" +name = "마법부여" +icon = "♰" +description = "무슨 소리를 하는 거야? 예언, 환상, 미신적인 헛소리?" [skill.excavation] - name = "발굴" - icon = "ᛳ" - description = "파고 파고 또 파고..." +name = "발굴" +icon = "ᛳ" +description = "파고 파고 또 파고..." [skill.herbalism] - name = "약초학" - icon = "⚘" - description = "식물은 못 찾겠는데 씨앗은 좀 찾았고- 저건... 잡초인가?" +name = "약초학" +icon = "⚘" +description = "식물은 못 찾겠는데 씨앗은 좀 찾았고- 저건... 잡초인가?" [skill.hunter] - name = "사냥" - icon = "☠" - description = "사냥은 결과가 아닌 여정에 관한 것입니다." +name = "사냥" +icon = "☠" +description = "사냥은 결과가 아닌 여정에 관한 것입니다." [skill.nether] - name = "네더" - icon = "₪" - description = "네더 그 자체의 심연에서 비롯된 힘." +name = "네더" +icon = "₪" +description = "네더 그 자체의 심연에서 비롯된 힘." [skill.pickaxe] - name = "곡괭이" - icon = "⛏" - description = "드워프가 광부이긴 하지만, 나도 나름 경험을 쌓았다고. 나는 스웨덴인이야!" +name = "곡괭이" +icon = "⛏" +description = "드워프가 광부이긴 하지만, 나도 나름 경험을 쌓았다고. 나는 스웨덴인이야!" [skill.ranged] - name = "원거리" - icon = "🏹" - description = "거리가 승리의 열쇠이자, 생존의 열쇠입니다." +name = "원거리" +icon = "🏹" +description = "거리가 승리의 열쇠이자, 생존의 열쇠입니다." [skill.rift] - name = "균열" - icon = "❍" - description = "균열은 부식성 속박이지만, 당신은 그 속박을 다스렸습니다." +name = "균열" +icon = "❍" +description = "균열은 부식성 속박이지만, 당신은 그 속박을 다스렸습니다." [skill.seaborne] - name = "해양" - icon = "🎣" - description = "이 스킬로 물의 경이로움을 다스릴 수 있습니다." +name = "해양" +icon = "🎣" +description = "이 스킬로 물의 경이로움을 다스릴 수 있습니다." [skill.stealth] - name = "은신" - icon = "☯" - description = "보이지 않는 자의 예술. 그림자 속을 걸으라." +name = "은신" +icon = "☯" +description = "보이지 않는 자의 예술. 그림자 속을 걸으라." [skill.swords] - name = "검술" - icon = "⚔" - description = "그레이스톤의 힘으로!" +name = "검술" +icon = "⚔" +description = "그레이스톤의 힘으로!" [skill.taming] - name = "조련" - icon = "♥" - description = "앵무새와 벌들... 그리고 당신은?" +name = "조련" +icon = "♥" +description = "앵무새와 벌들... 그리고 당신은?" [skill.tragoul] - name = "트라골" - icon = "🗡" - description = "피는 우주의 혈관을 타고 흐릅니다. 당신의 손에 의해 조여지며." +name = "트라골" +icon = "🗡" +description = "피는 우주의 혈관을 타고 흐릅니다. 당신의 손에 의해 조여지며." [skill.chronos] - name = "크로노스" - icon = "🕒" - description = "우주의 시계를 감고, 그 흐름을 경험하세요. 시계를 부수고, 그 흐름이 되어 보세요." +name = "크로노스" +icon = "🕒" +description = "우주의 시계를 감고, 그 흐름을 경험하세요. 시계를 부수고, 그 흐름이 되어 보세요." [skill.unarmed] - name = "비무장" - icon = "»" - description = "무기가 없다고 힘이 없는 것은 아닙니다." +name = "비무장" +icon = "»" +description = "무기가 없다고 힘이 없는 것은 아닙니다." # agility [agility] [agility.armor_up] - name = "방어구 강화" - description = "오래 달릴수록 더 많은 방어력을 얻습니다!" - lore1 = "최대 방어력" - lore2 = "방어구 강화 시간" - lore = ["최대 방어력", "방어구 강화 시간"] +name = "방어구 강화" +description = "오래 달릴수록 더 많은 방어력을 얻습니다!" +lore1 = "최대 방어력" +lore2 = "방어구 강화 시간" +lore = ["최대 방어력", "방어구 강화 시간"] [agility.ladder_slide] - name = "사다리 미끄러짐" - description = "사다리를 양 방향으로 훨씬 빠르게 오르내립니다." - lore1 = "사다리 속도 배수" - lore2 = "빠른 하강 속도" - lore = ["사다리 속도 배수", "빠른 하강 속도"] +name = "사다리 미끄러짐" +description = "사다리를 양 방향으로 훨씬 빠르게 오르내립니다." +lore1 = "사다리 속도 배수" +lore2 = "빠른 하강 속도" +lore = ["사다리 속도 배수", "빠른 하강 속도"] [agility.super_jump] - name = "슈퍼 점프" - description = "탁월한 높이의 이점." - lore1 = "최대 점프 높이" - lore2 = "웅크리기 + 점프로 슈퍼 점프!" - lore = ["최대 점프 높이", "웅크리기 + 점프로 슈퍼 점프!"] +name = "슈퍼 점프" +description = "탁월한 높이의 이점." +lore1 = "최대 점프 높이" +lore2 = "웅크리기 + 점프로 슈퍼 점프!" +lore = ["최대 점프 높이", "웅크리기 + 점프로 슈퍼 점프!"] [agility.wall_jump] - name = "벽 점프" - description = "공중에서 벽에 붙어 Shift를 눌러 벽에 매달리고 점프하세요!" - lore1 = "최대 점프 횟수" - lore2 = "점프 높이" - lore = ["최대 점프 횟수", "점프 높이"] +name = "벽 점프" +description = "공중에서 벽에 붙어 Shift를 눌러 벽에 매달리고 점프하세요!" +lore1 = "최대 점프 횟수" +lore2 = "점프 높이" +lore = ["최대 점프 횟수", "점프 높이"] [agility.wind_up] - name = "가속" - description = "오래 달릴수록 더 빨라집니다!" - lore1 = "최대 속도" - lore2 = "가속 시간" - lore = ["최대 속도", "가속 시간"] +name = "가속" +description = "오래 달릴수록 더 빨라집니다!" +lore1 = "최대 속도" +lore2 = "가속 시간" +lore = ["최대 속도", "가속 시간"] # architect [architect] [architect.elevator] - name = "엘리베이터" - description = "수직으로 빠르게 순간이동하는 엘리베이터를 건설할 수 있습니다!" - lore1 = "엘리베이터 레시피 해금: X=양털, Y=엔더 진주" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["엘리베이터 레시피 해금: X=양털, Y=엔더 진주", "XXX", "XYX", "XXX"] +name = "엘리베이터" +description = "수직으로 빠르게 순간이동하는 엘리베이터를 건설할 수 있습니다!" +lore1 = "엘리베이터 레시피 해금: X=양털, Y=엔더 진주" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["엘리베이터 레시피 해금: X=양털, Y=엔더 진주", "XXX", "XYX", "XXX"] [architect.foundation] - name = "마법 발판" - description = "웅크려서 발 밑에 임시 발판을 생성합니다!" - lore1 = "마법으로 생성: " - lore2 = "발 밑의 블록!" - lore = ["마법으로 생성: ", "발 밑의 블록!"] +name = "마법 발판" +description = "웅크려서 발 밑에 임시 발판을 생성합니다!" +lore1 = "마법으로 생성: " +lore2 = "발 밑의 블록!" +lore = ["마법으로 생성: ", "발 밑의 블록!"] [architect.glass] - name = "유리 섬세한 손길" - description = "맨손으로 유리 블록을 깨도 잃지 않게 됩니다!" - lore1 = "유리에 대해 섬세한 손길 효과를 얻습니다" - lore = ["유리에 대해 섬세한 손길 효과를 얻습니다"] +name = "유리 섬세한 손길" +description = "맨손으로 유리 블록을 깨도 잃지 않게 됩니다!" +lore1 = "유리에 대해 섬세한 손길 효과를 얻습니다" +lore = ["유리에 대해 섬세한 손길 효과를 얻습니다"] [architect.wireless_redstone] - name = "레드스톤 리모컨" - description = "레드스톤 횃불을 사용하여 원격으로 레드스톤을 전환할 수 있습니다!" - lore1 = "대상 + 레드스톤 횃불 + 엔더 진주 = 레드스톤 리모컨 1개" - lore = ["대상 + 레드스톤 횃불 + 엔더 진주 = 레드스톤 리모컨 1개"] +name = "레드스톤 리모컨" +description = "레드스톤 횃불을 사용하여 원격으로 레드스톤을 전환할 수 있습니다!" +lore1 = "대상 + 레드스톤 횃불 + 엔더 진주 = 레드스톤 리모컨 1개" +lore = ["대상 + 레드스톤 횃불 + 엔더 진주 = 레드스톤 리모컨 1개"] [architect.placement] - name = "건축가의 지팡이" - description = "한 번에 여러 블록을 설치할 수 있습니다! 웅크리고, 바라보는 블록과 같은 블록을 들고 설치하세요! 범위 지정을 위해 약간 움직여야 할 수 있습니다!" - lore1 = "이것을 설치하려면" - lore2 = "블록이 손에 필요합니다" - lore3 = "재료 건축가의 지팡이" - lore = ["이것을 설치하려면", "블록이 손에 필요합니다", "재료 건축가의 지팡이"] +name = "건축가의 지팡이" +description = "한 번에 여러 블록을 설치할 수 있습니다! 웅크리고, 바라보는 블록과 같은 블록을 들고 설치하세요! 범위 지정을 위해 약간 움직여야 할 수 있습니다!" +lore1 = "이것을 설치하려면" +lore2 = "블록이 손에 필요합니다" +lore3 = "재료 건축가의 지팡이" +lore = ["이것을 설치하려면", "블록이 손에 필요합니다", "재료 건축가의 지팡이"] # axe [axe] [axe.chop] - name = "도끼 벌목" - description = "나무 밑동을 우클릭하여 나무를 베세요!" - lore1 = "벌목당 블록 수" - lore2 = "벌목 쿨타임" - lore3 = "도구 마모" - lore = ["벌목당 블록 수", "벌목 쿨타임", "도구 마모"] +name = "도끼 벌목" +description = "나무 밑동을 우클릭하여 나무를 베세요!" +lore1 = "벌목당 블록 수" +lore2 = "벌목 쿨타임" +lore3 = "도구 마모" +lore = ["벌목당 블록 수", "벌목 쿨타임", "도구 마모"] [axe.log_swap] - name = "루시의 원목 교환기" - description = "제작대에서 원목의 종류를 변경하세요!" - lore1 = "아무 종류의 원목 8개 + 묘목 1개 = 묘목 종류의 원목 8개" - lore = ["아무 종류의 원목 8개 + 묘목 1개 = 묘목 종류의 원목 8개"] +name = "루시의 원목 교환기" +description = "제작대에서 원목의 종류를 변경하세요!" +lore1 = "아무 종류의 원목 8개 + 묘목 1개 = 묘목 종류의 원목 8개" +lore = ["아무 종류의 원목 8개 + 묘목 1개 = 묘목 종류의 원목 8개"] [axe.drop_to_inventory] - name = "도끼 인벤토리 드롭" +name = "도끼 인벤토리 드롭" [axe.ground_smash] - name = "도끼 지면 강타" - description = "점프한 후, 웅크려서 주변의 모든 적을 강타합니다." - lore1 = "피해량" - lore2 = "블록 반경" - lore3 = "힘" - lore4 = "강타 쿨타임" - lore = ["피해량", "블록 반경", "힘", "강타 쿨타임"] +name = "도끼 지면 강타" +description = "점프한 후, 웅크려서 주변의 모든 적을 강타합니다." +lore1 = "피해량" +lore2 = "블록 반경" +lore3 = "힘" +lore4 = "강타 쿨타임" +lore = ["피해량", "블록 반경", "힘", "강타 쿨타임"] [axe.leaf_miner] - name = "나뭇잎 채굴자" - description = "나뭇잎을 한꺼번에 부술 수 있습니다!" - lore1 = "웅크리고 나뭇잎을 캐세요" - lore2 = "나뭇잎 채굴 범위" - lore3 = "나뭇잎에서 아이템이 드롭되지 않습니다 (악용 방지)" - lore = ["웅크리고 나뭇잎을 캐세요", "나뭇잎 채굴 범위", "나뭇잎에서 아이템이 드롭되지 않습니다 (악용 방지)"] +name = "나뭇잎 채굴자" +description = "나뭇잎을 한꺼번에 부술 수 있습니다!" +lore1 = "웅크리고 나뭇잎을 캐세요" +lore2 = "나뭇잎 채굴 범위" +lore3 = "나뭇잎에서 아이템이 드롭되지 않습니다 (악용 방지)" +lore = ["웅크리고 나뭇잎을 캐세요", "나뭇잎 채굴 범위", "나뭇잎에서 아이템이 드롭되지 않습니다 (악용 방지)"] [axe.wood_miner] - name = "목재 채굴자" - description = "원목을 한꺼번에 부술 수 있습니다!" - lore1 = "웅크리고 원목/나무를 캐세요 (판자 제외)" - lore2 = "목재 채굴 범위" - lore3 = "인벤토리 드롭과 함께 작동" - lore = ["웅크리고 원목/나무를 캐세요 (판자 제외)", "목재 채굴 범위", "인벤토리 드롭과 함께 작동"] +name = "목재 채굴자" +description = "원목을 한꺼번에 부술 수 있습니다!" +lore1 = "웅크리고 원목/나무를 캐세요 (판자 제외)" +lore2 = "목재 채굴 범위" +lore3 = "인벤토리 드롭과 함께 작동" +lore = ["웅크리고 원목/나무를 캐세요 (판자 제외)", "목재 채굴 범위", "인벤토리 드롭과 함께 작동"] # brewing [brewing] [brewing.lingering] - name = "잔류 양조" - description = "양조한 물약의 효과가 더 오래 지속됩니다!" - lore1 = "지속시간" - lore2 = "지속시간" - lore = ["지속시간", "지속시간"] +name = "잔류 양조" +description = "양조한 물약의 효과가 더 오래 지속됩니다!" +lore1 = "지속시간" +lore2 = "지속시간" +lore = ["지속시간", "지속시간"] [brewing.super_heated] - name = "초고열 양조" - description = "양조기가 뜨거울수록 더 빠르게 작동합니다." - lore1 = "인접한 불 블록당" - lore2 = "인접한 용암 블록당" - lore = ["인접한 불 블록당", "인접한 용암 블록당"] +name = "초고열 양조" +description = "양조기가 뜨거울수록 더 빠르게 작동합니다." +lore1 = "인접한 불 블록당" +lore2 = "인접한 용암 블록당" +lore = ["인접한 불 블록당", "인접한 용암 블록당"] [brewing.darkness] - name = "병에 담긴 어둠" - description = "왜 이게 필요한지는 모르겠지만, 여기 있습니다!" - lore1 = "야간 투시 물약 + 검은색 콘크리트 = 어둠의 물약 (30초)" - lore2 = "이 물약은 대상이 질주하는 것을 방지합니다!" - lore = ["야간 투시 물약 + 검은색 콘크리트 = 어둠의 물약 (30초)", "이 물약은 대상이 질주하는 것을 방지합니다!"] +name = "병에 담긴 어둠" +description = "왜 이게 필요한지는 모르겠지만, 여기 있습니다!" +lore1 = "야간 투시 물약 + 검은색 콘크리트 = 어둠의 물약 (30초)" +lore2 = "이 물약은 대상이 질주하는 것을 방지합니다!" +lore = ["야간 투시 물약 + 검은색 콘크리트 = 어둠의 물약 (30초)", "이 물약은 대상이 질주하는 것을 방지합니다!"] [brewing.haste] - name = "병에 담긴 성급함" - description = "효율 마법이 충분하지 않을 때" - lore1 = "신속 물약 + 자수정 조각 = 성급함의 물약 (60초)" - lore2 = "신속 물약 + 자수정 블록 = 성급함의 물약-2 (30초)" - lore = ["신속 물약 + 자수정 조각 = 성급함의 물약 (60초)", "신속 물약 + 자수정 블록 = 성급함의 물약-2 (30초)"] +name = "병에 담긴 성급함" +description = "효율 마법이 충분하지 않을 때" +lore1 = "신속 물약 + 자수정 조각 = 성급함의 물약 (60초)" +lore2 = "신속 물약 + 자수정 블록 = 성급함의 물약-2 (30초)" +lore = ["신속 물약 + 자수정 조각 = 성급함의 물약 (60초)", "신속 물약 + 자수정 블록 = 성급함의 물약-2 (30초)"] [brewing.absorption] - name = "병에 담긴 흡수" - description = "몸을 강화하라!" - lore1 = "즉시 치유 + 석영 = 흡수의 물약 (60초)" - lore2 = "즉시 치유 + 석영 블록 = 흡수의 물약-2 (30초)" - lore = ["즉시 치유 + 석영 = 흡수의 물약 (60초)", "즉시 치유 + 석영 블록 = 흡수의 물약-2 (30초)"] +name = "병에 담긴 흡수" +description = "몸을 강화하라!" +lore1 = "즉시 치유 + 석영 = 흡수의 물약 (60초)" +lore2 = "즉시 치유 + 석영 블록 = 흡수의 물약-2 (30초)" +lore = ["즉시 치유 + 석영 = 흡수의 물약 (60초)", "즉시 치유 + 석영 블록 = 흡수의 물약-2 (30초)"] [brewing.fatigue] - name = "병에 담긴 피로" - description = "몸을 약화시켜라!" - lore1 = "나약함 물약 + 슬라임볼 = 피로의 물약 (30초)" - lore2 = "나약함 물약 + 슬라임 블록 = 피로의 물약-2 (15초)" - lore = ["나약함 물약 + 슬라임볼 = 피로의 물약 (30초)", "나약함 물약 + 슬라임 블록 = 피로의 물약-2 (15초)"] +name = "병에 담긴 피로" +description = "몸을 약화시켜라!" +lore1 = "나약함 물약 + 슬라임볼 = 피로의 물약 (30초)" +lore2 = "나약함 물약 + 슬라임 블록 = 피로의 물약-2 (15초)" +lore = ["나약함 물약 + 슬라임볼 = 피로의 물약 (30초)", "나약함 물약 + 슬라임 블록 = 피로의 물약-2 (15초)"] [brewing.hunger] - name = "병에 담긴 굶주림" - description = "끝없는 식욕을 채워라!" - lore1 = "어색한 물약 + 썩은 살점 = 굶주림의 물약 (30초)" - lore2 = "나약함 물약 + 썩은 살점 = 굶주림의 물약-3 (15초)" - lore = ["어색한 물약 + 썩은 살점 = 굶주림의 물약 (30초)", "나약함 물약 + 썩은 살점 = 굶주림의 물약-3 (15초)"] +name = "병에 담긴 굶주림" +description = "끝없는 식욕을 채워라!" +lore1 = "어색한 물약 + 썩은 살점 = 굶주림의 물약 (30초)" +lore2 = "나약함 물약 + 썩은 살점 = 굶주림의 물약-3 (15초)" +lore = ["어색한 물약 + 썩은 살점 = 굶주림의 물약 (30초)", "나약함 물약 + 썩은 살점 = 굶주림의 물약-3 (15초)"] [brewing.nausea] - name = "병에 담긴 메스꺼움" - description = "당신 때문에 속이 메스껍군!" - lore1 = "어색한 물약 + 갈색 버섯 = 메스꺼움의 물약 (16초)" - lore2 = "어색한 물약 + 진홍빛 균 = 메스꺼움의 물약-2 (8초)" - lore = ["어색한 물약 + 갈색 버섯 = 메스꺼움의 물약 (16초)", "어색한 물약 + 진홍빛 균 = 메스꺼움의 물약-2 (8초)"] +name = "병에 담긴 메스꺼움" +description = "당신 때문에 속이 메스껍군!" +lore1 = "어색한 물약 + 갈색 버섯 = 메스꺼움의 물약 (16초)" +lore2 = "어색한 물약 + 진홍빛 균 = 메스꺼움의 물약-2 (8초)" +lore = ["어색한 물약 + 갈색 버섯 = 메스꺼움의 물약 (16초)", "어색한 물약 + 진홍빛 균 = 메스꺼움의 물약-2 (8초)"] [brewing.blindness] - name = "병에 담긴 실명" - description = "당신은 정말 끔찍한 사람이군요..." - lore1 = "어색한 물약 + 먹물 주머니 = 실명의 물약 (30초)" - lore2 = "어색한 물약 + 발광 먹물 주머니 = 실명의 물약-2 (15초)" - lore = ["어색한 물약 + 먹물 주머니 = 실명의 물약 (30초)", "어색한 물약 + 발광 먹물 주머니 = 실명의 물약-2 (15초)"] +name = "병에 담긴 실명" +description = "당신은 정말 끔찍한 사람이군요..." +lore1 = "어색한 물약 + 먹물 주머니 = 실명의 물약 (30초)" +lore2 = "어색한 물약 + 발광 먹물 주머니 = 실명의 물약-2 (15초)" +lore = ["어색한 물약 + 먹물 주머니 = 실명의 물약 (30초)", "어색한 물약 + 발광 먹물 주머니 = 실명의 물약-2 (15초)"] [brewing.resistance] - name = "병에 담긴 저항" - description = "최상의 요새화!" - lore1 = "어색한 물약 + 철 주괴 = 저항의 물약 (60초)" - lore2 = "어색한 물약 + 철 블록 = 저항의 물약-2 (30초)" - lore = ["어색한 물약 + 철 주괴 = 저항의 물약 (60초)", "어색한 물약 + 철 블록 = 저항의 물약-2 (30초)"] +name = "병에 담긴 저항" +description = "최상의 요새화!" +lore1 = "어색한 물약 + 철 주괴 = 저항의 물약 (60초)" +lore2 = "어색한 물약 + 철 블록 = 저항의 물약-2 (30초)" +lore = ["어색한 물약 + 철 주괴 = 저항의 물약 (60초)", "어색한 물약 + 철 블록 = 저항의 물약-2 (30초)"] [brewing.health_boost] - name = "병에 담긴 생명" - description = "최대 체력이 부족할 때..." - lore1 = "즉시 치유 물약 + 황금 사과 = 체력 증진 물약 (120초)" - lore2 = "즉시 치유 물약 + 마법이 부여된 황금 사과 = 체력 증진 물약-2 (120초)" - lore = ["즉시 치유 물약 + 황금 사과 = 체력 증진 물약 (120초)", "즉시 치유 물약 + 마법이 부여된 황금 사과 = 체력 증진 물약-2 (120초)"] +name = "병에 담긴 생명" +description = "최대 체력이 부족할 때..." +lore1 = "즉시 치유 물약 + 황금 사과 = 체력 증진 물약 (120초)" +lore2 = "즉시 치유 물약 + 마법이 부여된 황금 사과 = 체력 증진 물약-2 (120초)" +lore = ["즉시 치유 물약 + 황금 사과 = 체력 증진 물약 (120초)", "즉시 치유 물약 + 마법이 부여된 황금 사과 = 체력 증진 물약-2 (120초)"] [brewing.decay] - name = "병에 담긴 부패" - description = "찌꺼기가 이렇게 유용할 줄 누가 알았겠어?" - lore1 = "나약함 물약 + 독이 든 감자 = 시듦의 물약 (16초)" - lore2 = "나약함 물약 + 진홍빛 뿌리 = 시듦의 물약-2 (8초)" - lore = ["나약함 물약 + 독이 든 감자 = 시듦의 물약 (16초)", "나약함 물약 + 진홍빛 뿌리 = 시듦의 물약-2 (8초)"] +name = "병에 담긴 부패" +description = "찌꺼기가 이렇게 유용할 줄 누가 알았겠어?" +lore1 = "나약함 물약 + 독이 든 감자 = 시듦의 물약 (16초)" +lore2 = "나약함 물약 + 진홍빛 뿌리 = 시듦의 물약-2 (8초)" +lore = ["나약함 물약 + 독이 든 감자 = 시듦의 물약 (16초)", "나약함 물약 + 진홍빛 뿌리 = 시듦의 물약-2 (8초)"] [brewing.saturation] - name = "병에 담긴 포만감" - description = "있잖아... 나 배도 안 고프네..." - lore1 = "재생 물약 + 구운 감자 = 포만감의 물약" - lore2 = "재생 물약 + 건초 더미 = 포만감의 물약-2" - lore = ["재생 물약 + 구운 감자 = 포만감의 물약", "재생 물약 + 건초 더미 = 포만감의 물약-2"] +name = "병에 담긴 포만감" +description = "있잖아... 나 배도 안 고프네..." +lore1 = "재생 물약 + 구운 감자 = 포만감의 물약" +lore2 = "재생 물약 + 건초 더미 = 포만감의 물약-2" +lore = ["재생 물약 + 구운 감자 = 포만감의 물약", "재생 물약 + 건초 더미 = 포만감의 물약-2"] # blocking [blocking] [blocking.chain_armorer] - name = "메피스토펠레스의 사슬" - description = "사슬 갑옷을 제작할 수 있습니다" - lore1 = "제작법은 다른 갑옷과 동일하지만, 철 조각을 대신 사용합니다" - lore = ["제작법은 다른 갑옷과 동일하지만, 철 조각을 대신 사용합니다"] +name = "메피스토펠레스의 사슬" +description = "사슬 갑옷을 제작할 수 있습니다" +lore1 = "제작법은 다른 갑옷과 동일하지만, 철 조각을 대신 사용합니다" +lore = ["제작법은 다른 갑옷과 동일하지만, 철 조각을 대신 사용합니다"] [blocking.horse_armorer] - name = "제작 가능한 말 갑옷" - description = "말 갑옷을 제작할 수 있습니다" - lore1 = "안장을 원하는 재료로 둘러싸서 갑옷을 제작하세요" - lore = ["안장을 원하는 재료로 둘러싸서 갑옷을 제작하세요"] +name = "제작 가능한 말 갑옷" +description = "말 갑옷을 제작할 수 있습니다" +lore1 = "안장을 원하는 재료로 둘러싸서 갑옷을 제작하세요" +lore = ["안장을 원하는 재료로 둘러싸서 갑옷을 제작하세요"] [blocking.saddle_crafter] - name = "제작 가능한 안장" - description = "가죽으로 안장을 제작합니다" - lore1 = "레시피: 가죽 5개:" - lore = ["레시피: 가죽 5개:"] +name = "제작 가능한 안장" +description = "가죽으로 안장을 제작합니다" +lore1 = "레시피: 가죽 5개:" +lore = ["레시피: 가죽 5개:"] [blocking.multi_armor] - name = "다중 방어구" - description = "겉날개를 갑옷에 결합합니다" - lore1 = "이동에 매우 유용한 스킬입니다." - lore2 = "갑옷/겉날개를 즉석에서 합치고 변경하세요!" - lore3 = "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요." - lore4 = "방어구를 분리하려면, 해당 아이템을 웅크린 채 버리면 분해됩니다." - lore5 = "합친 방어구가 파괴되면, 그 안의 모든 아이템을 잃게 됩니다." - lore6 = "갑옷은 필요 없어, 실망스럽네..." - lore = ["이동에 매우 유용한 스킬입니다.", "갑옷/겉날개를 즉석에서 합치고 변경하세요!", "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요.", "방어구를 분리하려면, 해당 아이템을 웅크린 채 버리면 분해됩니다.", "합친 방어구가 파괴되면, 그 안의 모든 아이템을 잃게 됩니다.", "갑옷은 필요 없어, 실망스럽네..."] +name = "다중 방어구" +description = "겉날개를 갑옷에 결합합니다" +lore1 = "이동에 매우 유용한 스킬입니다." +lore2 = "갑옷/겉날개를 즉석에서 합치고 변경하세요!" +lore3 = "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요." +lore4 = "방어구를 분리하려면, 해당 아이템을 웅크린 채 버리면 분해됩니다." +lore5 = "합친 방어구가 파괴되면, 그 안의 모든 아이템을 잃게 됩니다." +lore6 = "갑옷은 필요 없어, 실망스럽네..." +lore = ["이동에 매우 유용한 스킬입니다.", "갑옷/겉날개를 즉석에서 합치고 변경하세요!", "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요.", "방어구를 분리하려면, 해당 아이템을 웅크린 채 버리면 분해됩니다.", "합친 방어구가 파괴되면, 그 안의 모든 아이템을 잃게 됩니다.", "갑옷은 필요 없어, 실망스럽네..."] # crafting [crafting] [crafting.deconstruction] - name = "해체" - description = "블록과 아이템을 기본 재료로 분해하세요!" - lore1 = "아이템을 땅에 떨어뜨리세요." - lore2 = "그런 다음, 웅크리고 가위로 우클릭하세요" - lore = ["아이템을 땅에 떨어뜨리세요.", "그런 다음, 웅크리고 가위로 우클릭하세요"] +name = "해체" +description = "블록과 아이템을 기본 재료로 분해하세요!" +lore1 = "아이템을 땅에 떨어뜨리세요." +lore2 = "그런 다음, 웅크리고 가위로 우클릭하세요" +lore = ["아이템을 땅에 떨어뜨리세요.", "그런 다음, 웅크리고 가위로 우클릭하세요"] [crafting.xp] - name = "제작 경험치" - description = "제작 시 패시브 XP를 획득합니다" - lore1 = "제작 시 XP 획득" - lore = ["제작 시 XP 획득"] +name = "제작 경험치" +description = "제작 시 패시브 XP를 획득합니다" +lore1 = "제작 시 XP 획득" +lore = ["제작 시 XP 획득"] [crafting.reconstruction] - name = "광석 복원" - description = "기본 재료로 광석을 다시 만드세요!" - lore1 = "드롭 아이템 8개 + 모체 1개 = 광석 1개 (비정형)" - lore2 = "드롭 아이템은 제련되어야 합니다 (해당되는 경우)" - lore3 = "제외: 파편, 석영, 에메랄드 등..." - lore4 = "모체 = 감싸는 블록. 예: 돌, 네더랙, 심층암" - lore = ["드롭 아이템 8개 + 모체 1개 = 광석 1개 (비정형)", "드롭 아이템은 제련되어야 합니다 (해당되는 경우)", "제외: 파편, 석영, 에메랄드 등...", "모체 = 감싸는 블록. 예: 돌, 네더랙, 심층암"] +name = "광석 복원" +description = "기본 재료로 광석을 다시 만드세요!" +lore1 = "드롭 아이템 8개 + 모체 1개 = 광석 1개 (비정형)" +lore2 = "드롭 아이템은 제련되어야 합니다 (해당되는 경우)" +lore3 = "제외: 파편, 석영, 에메랄드 등..." +lore4 = "모체 = 감싸는 블록. 예: 돌, 네더랙, 심층암" +lore = ["드롭 아이템 8개 + 모체 1개 = 광석 1개 (비정형)", "드롭 아이템은 제련되어야 합니다 (해당되는 경우)", "제외: 파편, 석영, 에메랄드 등...", "모체 = 감싸는 블록. 예: 돌, 네더랙, 심층암"] [crafting.leather] - name = "제작 가능한 가죽" - description = "썩은 살점으로 가죽을 만듭니다" - lore1 = "모닥불 위에 (썩은 살점을) 올려놓기만 하면 됩니다!" - lore = ["모닥불 위에 (썩은 살점을) 올려놓기만 하면 됩니다!"] +name = "제작 가능한 가죽" +description = "썩은 살점으로 가죽을 만듭니다" +lore1 = "모닥불 위에 (썩은 살점을) 올려놓기만 하면 됩니다!" +lore = ["모닥불 위에 (썩은 살점을) 올려놓기만 하면 됩니다!"] [crafting.backpacks] - name = "부틸리에의 배낭!" - description = "모장의 묶음을 게임에 추가합니다!" - lore1 = "사용하려면 서바이벌 모드여야 합니다" - lore2 = "XLX : 가죽, 끈, 가죽" - lore3 = "XSX : 가죽, 통, 가죽" - lore4 = "XCX : 가죽, 상자, 가죽" - lore = ["사용하려면 서바이벌 모드여야 합니다", "XLX : 가죽, 끈, 가죽", "XSX : 가죽, 통, 가죽", "XCX : 가죽, 상자, 가죽"] +name = "부틸리에의 배낭!" +description = "모장의 묶음을 게임에 추가합니다!" +lore1 = "사용하려면 서바이벌 모드여야 합니다" +lore2 = "XLX : 가죽, 끈, 가죽" +lore3 = "XSX : 가죽, 통, 가죽" +lore4 = "XCX : 가죽, 상자, 가죽" +lore = ["사용하려면 서바이벌 모드여야 합니다", "XLX : 가죽, 끈, 가죽", "XSX : 가죽, 통, 가죽", "XCX : 가죽, 상자, 가죽"] [crafting.stations] - name = "휴대용 작업대!" - description = "손바닥 위에서 작업대를 사용하세요!" - lore2 = "작업대를 닫을 때 남겨둔 아이템은 영원히 사라집니다!" - lore3 = "사용 가능한 작업대: 모루, 제작대, 숫돌, 지도 제작대, 석재 절단기, 베틀" - lore = ["작업대를 닫을 때 남겨둔 아이템은 영원히 사라집니다!", "사용 가능한 작업대: 모루, 제작대, 숫돌, 지도 제작대, 석재 절단기, 베틀"] +name = "휴대용 작업대!" +description = "손바닥 위에서 작업대를 사용하세요!" +lore2 = "작업대를 닫을 때 남겨둔 아이템은 영원히 사라집니다!" +lore3 = "사용 가능한 작업대: 모루, 제작대, 숫돌, 지도 제작대, 석재 절단기, 베틀" +lore = ["작업대를 닫을 때 남겨둔 아이템은 영원히 사라집니다!", "사용 가능한 작업대: 모루, 제작대, 숫돌, 지도 제작대, 석재 절단기, 베틀"] [crafting.skulls] - name = "제작 가능한 머리!" - description = "재료를 사용하여 몹 머리를 제작할 수 있습니다!" - lore1 = "뼈 블록을 다음 재료로 둘러싸면 머리를 얻습니다:" - lore2 = "좀비: 썩은 살점" - lore3 = "스켈레톤: 뼈" - lore4 = "크리퍼: 화약" - lore5 = "위더: 네더 벽돌" - lore6 = "드래곤: 드래곤의 숨결" - lore = ["뼈 블록을 다음 재료로 둘러싸면 머리를 얻습니다:", "좀비: 썩은 살점", "스켈레톤: 뼈", "크리퍼: 화약", "위더: 네더 벽돌", "드래곤: 드래곤의 숨결"] +name = "제작 가능한 머리!" +description = "재료를 사용하여 몹 머리를 제작할 수 있습니다!" +lore1 = "뼈 블록을 다음 재료로 둘러싸면 머리를 얻습니다:" +lore2 = "좀비: 썩은 살점" +lore3 = "스켈레톤: 뼈" +lore4 = "크리퍼: 화약" +lore5 = "위더: 네더 벽돌" +lore6 = "드래곤: 드래곤의 숨결" +lore = ["뼈 블록을 다음 재료로 둘러싸면 머리를 얻습니다:", "좀비: 썩은 살점", "스켈레톤: 뼈", "크리퍼: 화약", "위더: 네더 벽돌", "드래곤: 드래곤의 숨결"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "시간을 담은 병" - description = "시간을 저장하는 시간의 병을 들고 다니며, 저장된 시간을 사용하여 시간이 걸리는 블록, 작물, 새끼 동물과 같은 성장 가능한 엔티티를 가속시킵니다. 레시피 (비정형): 신속의 물약 + 시계 + 유리병." - lore1 = "틱마다 충전되는 저장 초" - lore2 = "저장된 초당 시간 가속" - lore3 = "레시피 (비정형): 신속의 물약 + 시계 + 유리병" - lore = ["틱마다 충전되는 저장 초", "저장된 초당 시간 가속", "레시피 (비정형): 신속의 물약 + 시계 + 유리병"] +name = "시간을 담은 병" +description = "시간을 저장하는 시간의 병을 들고 다니며, 저장된 시간을 사용하여 시간이 걸리는 블록, 작물, 새끼 동물과 같은 성장 가능한 엔티티를 가속시킵니다. 레시피 (비정형): 신속의 물약 + 시계 + 유리병." +lore1 = "틱마다 충전되는 저장 초" +lore2 = "저장된 초당 시간 가속" +lore3 = "레시피 (비정형): 신속의 물약 + 시계 + 유리병" +lore = ["틱마다 충전되는 저장 초", "저장된 초당 시간 가속", "레시피 (비정형): 신속의 물약 + 시계 + 유리병"] [chronos.aberrant_touch] - name = "이질적 손길" - description = "근접 공격이 배고픔을 대가로 중첩되는 구속을 적용하며, 엄격한 PvP 제한이 있고, 5스택 시 대상을 속박합니다." - lore1 = "근접 공격이 중첩되는 구속을 적용" - lore2 = "PvE 구속 지속시간 상한" - lore3 = "PvP 구속 증폭 상한" - lore = ["근접 공격이 중첩되는 구속을 적용", "PvE 구속 지속시간 상한", "PvP 구속 증폭 상한"] +name = "이질적 손길" +description = "근접 공격이 배고픔을 대가로 중첩되는 구속을 적용하며, 엄격한 PvP 제한이 있고, 5스택 시 대상을 속박합니다." +lore1 = "근접 공격이 중첩되는 구속을 적용" +lore2 = "PvE 구속 지속시간 상한" +lore3 = "PvP 구속 증폭 상한" +lore = ["근접 공격이 중첩되는 구속을 적용", "PvE 구속 지속시간 상한", "PvP 구속 증폭 상한"] [chronos.instant_recall] - name = "즉시 회귀" - description = "시계를 손에 들고 좌클릭 또는 우클릭하여 최근 스냅샷으로 되돌아가며, 체력과 배고픔이 복구됩니다." - lore1 = "되감기 시간" - lore2 = "쿨타임" - lore3 = "인벤토리 롤백 없음" - lore = ["되감기 시간", "쿨타임", "인벤토리 롤백 없음"] +name = "즉시 회귀" +description = "시계를 손에 들고 좌클릭 또는 우클릭하여 최근 스냅샷으로 되돌아가며, 체력과 배고픔이 복구됩니다." +lore1 = "되감기 시간" +lore2 = "쿨타임" +lore3 = "인벤토리 롤백 없음" +lore = ["되감기 시간", "쿨타임", "인벤토리 롤백 없음"] [chronos.time_bomb] - name = "시간 폭탄" - description = "제작된 시간 폭탄을 던져 시간의 장을 생성하고, 엔티티를 느리게 하며, 투사체를 정지시킵니다." - lore1 = "시간 장의 반경" - lore2 = "시간 장의 지속시간" - lore3 = "폭탄 쿨타임" - lore4 = "레시피 (비정형): 시계 + 눈덩이 + 다이아몬드 + 모래" - lore = ["시간 장의 반경", "시간 장의 지속시간", "폭탄 쿨타임", "레시피 (비정형): 시계 + 눈덩이 + 다이아몬드 + 모래"] +name = "시간 폭탄" +description = "제작된 시간 폭탄을 던져 시간의 장을 생성하고, 엔티티를 느리게 하며, 투사체를 정지시킵니다." +lore1 = "시간 장의 반경" +lore2 = "시간 장의 지속시간" +lore3 = "폭탄 쿨타임" +lore4 = "레시피 (비정형): 시계 + 눈덩이 + 다이아몬드 + 모래" +lore = ["시간 장의 반경", "시간 장의 지속시간", "폭탄 쿨타임", "레시피 (비정형): 시계 + 눈덩이 + 다이아몬드 + 모래"] # discovery [discovery] [discovery.armor] - name = "세계의 갑옷" - description = "주변 블록 경도에 따른 패시브 방어력." - lore1 = "패시브 방어력" - lore2 = "주변 블록 경도 기준" - lore3 = "방어력 강도:" - lore = ["패시브 방어력", "주변 블록 경도 기준", "방어력 강도:"] +name = "세계의 갑옷" +description = "주변 블록 경도에 따른 패시브 방어력." +lore1 = "패시브 방어력" +lore2 = "주변 블록 경도 기준" +lore3 = "방어력 강도:" +lore = ["패시브 방어력", "주변 블록 경도 기준", "방어력 강도:"] [discovery.unity] - name = "실험적 통합" - description = "경험치 구슬을 모으면 랜덤 스킬에 XP가 추가됩니다." - lore1 = "XP " - lore2 = "구슬당" - lore = ["XP ", "구슬당"] +name = "실험적 통합" +description = "경험치 구슬을 모으면 랜덤 스킬에 XP가 추가됩니다." +lore1 = "XP " +lore2 = "구슬당" +lore = ["XP ", "구슬당"] [discovery.resist] - name = "실험적 저항" - description = "피격 시 체력이 5칸 이하로 떨어지거나 죽을 위기일 때만 경험치를 소모하여 피해를 완화합니다." - lore0 = "위험 체력(<= 5칸)에서만 15초마다 한 번 발동" - lore1 = " 피해 감소" - lore2 = "경험치 소모" - lore = ["위험 체력(<= 5칸)에서만 15초마다 한 번 발동", " 피해 감소", "경험치 소모"] +name = "실험적 저항" +description = "피격 시 체력이 5칸 이하로 떨어지거나 죽을 위기일 때만 경험치를 소모하여 피해를 완화합니다." +lore0 = "위험 체력(<= 5칸)에서만 15초마다 한 번 발동" +lore1 = " 피해 감소" +lore2 = "경험치 소모" +lore = ["위험 체력(<= 5칸)에서만 15초마다 한 번 발동", " 피해 감소", "경험치 소모"] [discovery.villager] - name = "주민 매력" - description = "주민과 더 좋은 거래를 할 수 있습니다!" - lore1 = "주민과 상호작용할 때마다 XP를 소모합니다" - lore2 = "상호작용당 XP를 소비하고 거래를 강화할 확률" - lore3 = "상호작용당 필요한 XP 소모량" - lore = ["주민과 상호작용할 때마다 XP를 소모합니다", "상호작용당 XP를 소비하고 거래를 강화할 확률", "상호작용당 필요한 XP 소모량"] +name = "주민 매력" +description = "주민과 더 좋은 거래를 할 수 있습니다!" +lore1 = "주민과 상호작용할 때마다 XP를 소모합니다" +lore2 = "상호작용당 XP를 소비하고 거래를 강화할 확률" +lore3 = "상호작용당 필요한 XP 소모량" +lore = ["주민과 상호작용할 때마다 XP를 소모합니다", "상호작용당 XP를 소비하고 거래를 강화할 확률", "상호작용당 필요한 XP 소모량"] # enchanting [enchanting] [enchanting.lapis_return] - name = "청금석 반환" - description = "XP 1레벨 추가 비용 대신, 무료 청금석을 돌려받을 확률이 있습니다" - lore1 = "레벨당 마법부여 비용이 1 증가하지만, 최대 3개의 청금석을 돌려받을 수 있습니다" - lore = ["레벨당 마법부여 비용이 1 증가하지만, 최대 3개의 청금석을 돌려받을 수 있습니다"] +name = "청금석 반환" +description = "XP 1레벨 추가 비용 대신, 무료 청금석을 돌려받을 확률이 있습니다" +lore1 = "레벨당 마법부여 비용이 1 증가하지만, 최대 3개의 청금석을 돌려받을 수 있습니다" +lore = ["레벨당 마법부여 비용이 1 증가하지만, 최대 3개의 청금석을 돌려받을 수 있습니다"] [enchanting.quick_enchant] - name = "퀵클릭 마법부여" - description = "마법부여 책을 아이템에 직접 클릭하여 마법을 부여합니다." - lore1 = "최대 합산 레벨" - lore2 = "아이템에 다음을 초과하여 마법부여할 수 없습니다 " - lore3 = "파워" - lore = ["최대 합산 레벨", "아이템에 다음을 초과하여 마법부여할 수 없습니다 ", "파워"] +name = "퀵클릭 마법부여" +description = "마법부여 책을 아이템에 직접 클릭하여 마법을 부여합니다." +lore1 = "최대 합산 레벨" +lore2 = "아이템에 다음을 초과하여 마법부여할 수 없습니다 " +lore3 = "파워" +lore = ["최대 합산 레벨", "아이템에 다음을 초과하여 마법부여할 수 없습니다 ", "파워"] [enchanting.return] - name = "XP 반환" - description = "아이템에 마법부여 시 사용한 XP가 돌아옵니다." - lore1 = "아이템을 마법부여할 때 소비한 경험치가 환불될 확률이 있습니다" - lore2 = "마법부여당 경험치" - lore = ["아이템을 마법부여할 때 소비한 경험치가 환불될 확률이 있습니다", "마법부여당 경험치"] +name = "XP 반환" +description = "아이템에 마법부여 시 사용한 XP가 돌아옵니다." +lore1 = "아이템을 마법부여할 때 소비한 경험치가 환불될 확률이 있습니다" +lore2 = "마법부여당 경험치" +lore = ["아이템을 마법부여할 때 소비한 경험치가 환불될 확률이 있습니다", "마법부여당 경험치"] # excavation [excavation] [excavation.haste] - name = "성급한 발굴가" - description = "성급함 효과로 발굴 속도가 빨라집니다!" - lore1 = "발굴 중 성급함 획득" - lore2 = "x 블록을 캐기 시작하면 성급함 레벨 획득." - lore = ["발굴 중 성급함 획득", "x 블록을 캐기 시작하면 성급함 레벨 획득."] +name = "성급한 발굴가" +description = "성급함 효과로 발굴 속도가 빨라집니다!" +lore1 = "발굴 중 성급함 획득" +lore2 = "x 블록을 캐기 시작하면 성급함 레벨 획득." +lore = ["발굴 중 성급함 획득", "x 블록을 캐기 시작하면 성급함 레벨 획득."] [excavation.spelunker] - name = "초감각 탐험가!" - description = "땅 너머로 광석을 볼 수 있습니다!" - lore1 = "보조 손에 광석, 주 손에 발광 열매를 들고 웅크리세요!" - lore2 = "블록 범위: " - lore3 = "사용 시 발광 열매 소모" - lore = ["보조 손에 광석, 주 손에 발광 열매를 들고 웅크리세요!", "블록 범위: ", "사용 시 발광 열매 소모"] +name = "초감각 탐험가!" +description = "땅 너머로 광석을 볼 수 있습니다!" +lore1 = "보조 손에 광석, 주 손에 발광 열매를 들고 웅크리세요!" +lore2 = "블록 범위: " +lore3 = "사용 시 발광 열매 소모" +lore = ["보조 손에 광석, 주 손에 발광 열매를 들고 웅크리세요!", "블록 범위: ", "사용 시 발광 열매 소모"] [excavation.drop_to_inventory] - name = "삽 인벤토리 드롭" +name = "삽 인벤토리 드롭" [excavation.omni_tool] - name = "OMNI - 도구" - description = "태클의 과하게 설계된 화려한 멀티툴" - lore1 = "가장 강력한 기능 중 하나로, 다음을 할 수 있습니다" - lore2 = "필요에 따라 도구를 즉석에서 합치고 변경할 수 있습니다." - lore3 = "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요." - lore4 = "도구를 분리하려면, 해당 도구를 웅크린 채 버리면 분해됩니다." - lore5 = "이 멀티툴에서 도구를 파괴할 수 없지만 부서진 도구는 사용할 수 없습니다" - lore6 = "합칠 수 있는 총 아이템 수." - lore7 = "도구를 5~6개 쓸 수도 있고, 하나만 쓸 수도 있습니다!" - lore = ["가장 강력한 기능 중 하나로, 다음을 할 수 있습니다", "필요에 따라 도구를 즉석에서 합치고 변경할 수 있습니다.", "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요.", "도구를 분리하려면, 해당 도구를 웅크린 채 버리면 분해됩니다.", "이 멀티툴에서 도구를 파괴할 수 없지만 부서진 도구는 사용할 수 없습니다", "합칠 수 있는 총 아이템 수.", "도구를 5~6개 쓸 수도 있고, 하나만 쓸 수도 있습니다!"] +name = "OMNI - 도구" +description = "태클의 과하게 설계된 화려한 멀티툴" +lore1 = "가장 강력한 기능 중 하나로, 다음을 할 수 있습니다" +lore2 = "필요에 따라 도구를 즉석에서 합치고 변경할 수 있습니다." +lore3 = "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요." +lore4 = "도구를 분리하려면, 해당 도구를 웅크린 채 버리면 분해됩니다." +lore5 = "이 멀티툴에서 도구를 파괴할 수 없지만 부서진 도구는 사용할 수 없습니다" +lore6 = "합칠 수 있는 총 아이템 수." +lore7 = "도구를 5~6개 쓸 수도 있고, 하나만 쓸 수도 있습니다!" +lore = ["가장 강력한 기능 중 하나로, 다음을 할 수 있습니다", "필요에 따라 도구를 즉석에서 합치고 변경할 수 있습니다.", "합치려면 인벤토리에서 아이템을 다른 아이템 위에 Shift 클릭하세요.", "도구를 분리하려면, 해당 도구를 웅크린 채 버리면 분해됩니다.", "이 멀티툴에서 도구를 파괴할 수 없지만 부서진 도구는 사용할 수 없습니다", "합칠 수 있는 총 아이템 수.", "도구를 5~6개 쓸 수도 있고, 하나만 쓸 수도 있습니다!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "성장의 오라" - description = "주변의 자연을 오라로 성장시킵니다" - lore1 = "블록 반경" - lore2 = "성장 오라 강도" - lore3 = "배고픔 비용" - lore = ["블록 반경", "성장 오라 강도", "배고픔 비용"] +name = "성장의 오라" +description = "주변의 자연을 오라로 성장시킵니다" +lore1 = "블록 반경" +lore2 = "성장 오라 강도" +lore3 = "배고픔 비용" +lore = ["블록 반경", "성장 오라 강도", "배고픔 비용"] [herbalism.hippo] - name = "약초학자의 하마" - description = "음식을 먹으면 더 많은 포만감을 얻습니다" - lore1 = "음식) 섭취 시 추가 포만감 포인트" - lore = ["음식) 섭취 시 추가 포만감 포인트"] +name = "약초학자의 하마" +description = "음식을 먹으면 더 많은 포만감을 얻습니다" +lore1 = "음식) 섭취 시 추가 포만감 포인트" +lore = ["음식) 섭취 시 추가 포만감 포인트"] [herbalism.myconid] - name = "약초학자의 균류" - description = "균사체를 제작할 수 있는 능력을 부여합니다" - lore1 = "아무 흙 + 갈색 버섯 + 빨간 버섯으로 균사체를 제작합니다." - lore = ["아무 흙 + 갈색 버섯 + 빨간 버섯으로 균사체를 제작합니다."] +name = "약초학자의 균류" +description = "균사체를 제작할 수 있는 능력을 부여합니다" +lore1 = "아무 흙 + 갈색 버섯 + 빨간 버섯으로 균사체를 제작합니다." +lore = ["아무 흙 + 갈색 버섯 + 빨간 버섯으로 균사체를 제작합니다."] [herbalism.terralid] - name = "약초학자의 대지" - description = "잔디 블록을 제작할 수 있는 능력을 부여합니다" - lore1 = "씨앗 3개를 흙 3개 위에 놓으면 잔디 블록 3개를 제작합니다." - lore = ["씨앗 3개를 흙 3개 위에 놓으면 잔디 블록 3개를 제작합니다."] +name = "약초학자의 대지" +description = "잔디 블록을 제작할 수 있는 능력을 부여합니다" +lore1 = "씨앗 3개를 흙 3개 위에 놓으면 잔디 블록 3개를 제작합니다." +lore = ["씨앗 3개를 흙 3개 위에 놓으면 잔디 블록 3개를 제작합니다."] [herbalism.cobweb] - name = "거미줄 제작자" - description = "제작대에서 거미줄을 만들 수 있는 능력을 부여합니다" - lore1 = "실 9개로 거미줄을 만듭니다." - lore = ["실 9개로 거미줄을 만듭니다."] +name = "거미줄 제작자" +description = "제작대에서 거미줄을 만들 수 있는 능력을 부여합니다" +lore1 = "실 9개로 거미줄을 만듭니다." +lore = ["실 9개로 거미줄을 만듭니다."] [herbalism.mushroom_blocks] - name = "버섯 블록 제작자" - description = "제작대에서 버섯 블록을 만들 수 있는 능력을 부여합니다" - lore1 = "버섯 4개로 블록을, 블록으로 줄기를 만듭니다." - lore = ["버섯 4개로 블록을, 블록으로 줄기를 만듭니다."] +name = "버섯 블록 제작자" +description = "제작대에서 버섯 블록을 만들 수 있는 능력을 부여합니다" +lore1 = "버섯 4개로 블록을, 블록으로 줄기를 만듭니다." +lore = ["버섯 4개로 블록을, 블록으로 줄기를 만듭니다."] [herbalism.drop_to_inventory] - name = "괭이 인벤토리 드롭" +name = "괭이 인벤토리 드롭" [herbalism.hungry_shield] - name = "굶주린 방패" - description = "체력 대신 배고픔으로 피해를 받습니다." - lore1 = "배고픔으로 저항" - lore = ["배고픔으로 저항"] +name = "굶주린 방패" +description = "체력 대신 배고픔으로 피해를 받습니다." +lore1 = "배고픔으로 저항" +lore = ["배고픔으로 저항"] [herbalism.luck] - name = "약초학자의 행운" - description = "풀/꽃을 부수면 일정 확률로 랜덤 아이템을 얻습니다" - lore0 = "꽃 = 음식, 풀 = 씨앗" - lore1 = "꽃을 부수면 아이템을 얻을 확률" - lore2 = "풀을 부수면 아이템을 얻을 확률" - lore = ["꽃 = 음식, 풀 = 씨앗", "꽃을 부수면 아이템을 얻을 확률", "풀을 부수면 아이템을 얻을 확률"] +name = "약초학자의 행운" +description = "풀/꽃을 부수면 일정 확률로 랜덤 아이템을 얻습니다" +lore0 = "꽃 = 음식, 풀 = 씨앗" +lore1 = "꽃을 부수면 아이템을 얻을 확률" +lore2 = "풀을 부수면 아이템을 얻을 확률" +lore = ["꽃 = 음식, 풀 = 씨앗", "꽃을 부수면 아이템을 얻을 확률", "풀을 부수면 아이템을 얻을 확률"] [herbalism.replant] - name = "수확 및 재파종" - description = "괭이로 작물을 우클릭하여 수확하고 다시 심습니다." - lore1 = "블록 재파종 반경" - lore = ["블록 재파종 반경"] +name = "수확 및 재파종" +description = "괭이로 작물을 우클릭하여 수확하고 다시 심습니다." +lore1 = "블록 재파종 반경" +lore = ["블록 재파종 반경"] # hunter [hunter] [hunter.adrenaline] - name = "아드레날린" - description = "체력이 낮을수록 더 많은 피해를 줍니다 (근접)" - lore1 = "최대 피해량" - lore = ["최대 피해량"] +name = "아드레날린" +description = "체력이 낮을수록 더 많은 피해를 줍니다 (근접)" +lore1 = "최대 피해량" +lore = ["최대 피해량"] [hunter.penalty] - name = "" - description = "" - lore1 = "배고픔이 바닥나면 독 스택을 얻습니다" - lore = ["배고픔이 바닥나면 독 스택을 얻습니다"] +name = "" +description = "" +lore1 = "배고픔이 바닥나면 독 스택을 얻습니다" +lore = ["배고픔이 바닥나면 독 스택을 얻습니다"] [hunter.drop_to_inventory] - name = "아이템 인벤토리 드롭" - description = "무언가를 처치하거나 / 검으로 블록을 부수면 드롭 아이템이 인벤토리로 들어갑니다" - lore1 = "몹/블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다." - lore = ["몹/블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다."] +name = "아이템 인벤토리 드롭" +description = "무언가를 처치하거나 / 검으로 블록을 부수면 드롭 아이템이 인벤토리로 들어갑니다" +lore1 = "몹/블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다." +lore = ["몹/블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다."] [hunter.invisibility] - name = "소멸의 발걸음" - description = "피격 시 배고픔을 대가로 투명화를 얻습니다" - lore1 = "피격 시 패시브 투명화 획득" - lore2 = "x 피격 시 3초간 투명화 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "투명화 지속시간" - lore = ["피격 시 패시브 투명화 획득", "x 피격 시 3초간 투명화 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "투명화 지속시간"] +name = "소멸의 발걸음" +description = "피격 시 배고픔을 대가로 투명화를 얻습니다" +lore1 = "피격 시 패시브 투명화 획득" +lore2 = "x 피격 시 3초간 투명화 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "투명화 지속시간" +lore = ["피격 시 패시브 투명화 획득", "x 피격 시 3초간 투명화 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "투명화 지속시간"] [hunter.jump_boost] - name = "사냥꾼의 높이" - description = "피격 시 배고픔을 대가로 점프 강화를 얻습니다" - lore1 = "피격 시 패시브 점프 강화 획득" - lore2 = "x 피격 시 3초간 점프 강화 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "점프 강화 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 점프 강화 획득", "x 피격 시 3초간 점프 강화 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "점프 강화 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 높이" +description = "피격 시 배고픔을 대가로 점프 강화를 얻습니다" +lore1 = "피격 시 패시브 점프 강화 획득" +lore2 = "x 피격 시 3초간 점프 강화 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "점프 강화 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 점프 강화 획득", "x 피격 시 3초간 점프 강화 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "점프 강화 중첩 배수 (지속시간 아님)."] [hunter.luck] - name = "사냥꾼의 행운" - description = "피격 시 배고픔을 대가로 행운을 얻습니다" - lore1 = "피격 시 패시브 행운 획득" - lore2 = "x 피격 시 3초간 행운 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "행운 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 행운 획득", "x 피격 시 3초간 행운 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "행운 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 행운" +description = "피격 시 배고픔을 대가로 행운을 얻습니다" +lore1 = "피격 시 패시브 행운 획득" +lore2 = "x 피격 시 3초간 행운 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "행운 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 행운 획득", "x 피격 시 3초간 행운 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "행운 중첩 배수 (지속시간 아님)."] [hunter.regen] - name = "사냥꾼의 재생" - description = "피격 시 배고픔을 대가로 재생력을 얻습니다" - lore1 = "피격 시 패시브 재생력 획득" - lore2 = "x 피격 시 3초간 재생력 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "재생력 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 재생력 획득", "x 피격 시 3초간 재생력 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "재생력 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 재생" +description = "피격 시 배고픔을 대가로 재생력을 얻습니다" +lore1 = "피격 시 패시브 재생력 획득" +lore2 = "x 피격 시 3초간 재생력 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "재생력 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 재생력 획득", "x 피격 시 3초간 재생력 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "재생력 중첩 배수 (지속시간 아님)."] [hunter.resistance] - name = "사냥꾼의 저항" - description = "피격 시 배고픔을 대가로 저항력을 얻습니다" - lore1 = "피격 시 패시브 저항력 획득" - lore2 = "x 피격 시 3초간 저항력 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "저항력 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 저항력 획득", "x 피격 시 3초간 저항력 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "저항력 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 저항" +description = "피격 시 배고픔을 대가로 저항력을 얻습니다" +lore1 = "피격 시 패시브 저항력 획득" +lore2 = "x 피격 시 3초간 저항력 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "저항력 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 저항력 획득", "x 피격 시 3초간 저항력 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "저항력 중첩 배수 (지속시간 아님)."] [hunter.speed] - name = "사냥꾼의 속도" - description = "피격 시 배고픔을 대가로 속도를 얻습니다" - lore1 = "피격 시 패시브 속도 획득" - lore2 = "x 피격 시 3초간 속도 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "속도 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 속도 획득", "x 피격 시 3초간 속도 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "속도 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 속도" +description = "피격 시 배고픔을 대가로 속도를 얻습니다" +lore1 = "피격 시 패시브 속도 획득" +lore2 = "x 피격 시 3초간 속도 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "속도 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 속도 획득", "x 피격 시 3초간 속도 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "속도 중첩 배수 (지속시간 아님)."] [hunter.strength] - name = "사냥꾼의 힘" - description = "피격 시 배고픔을 대가로 힘을 얻습니다" - lore1 = "피격 시 패시브 힘 획득" - lore2 = "x 피격 시 3초간 힘 중첩" - lore3 = "x 중첩 배고픔" - lore4 = "배고픔 중첩의 지속시간과 배수." - lore5 = "힘 중첩 배수 (지속시간 아님)." - lore = ["피격 시 패시브 힘 획득", "x 피격 시 3초간 힘 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "힘 중첩 배수 (지속시간 아님)."] +name = "사냥꾼의 힘" +description = "피격 시 배고픔을 대가로 힘을 얻습니다" +lore1 = "피격 시 패시브 힘 획득" +lore2 = "x 피격 시 3초간 힘 중첩" +lore3 = "x 중첩 배고픔" +lore4 = "배고픔 중첩의 지속시간과 배수." +lore5 = "힘 중첩 배수 (지속시간 아님)." +lore = ["피격 시 패시브 힘 획득", "x 피격 시 3초간 힘 중첩", "x 중첩 배고픔", "배고픔 중첩의 지속시간과 배수.", "힘 중첩 배수 (지속시간 아님)."] # nether [nether] [nether.skull_toss] - name = "위더 해골 투척" - description1 = "내면의 위더를 해방하세요," - description2 = "누군가의" - description3 = "머리를 사용하여." - lore1 = "해골 투척 사이의 쿨타임 초." - lore2 = "위더 해골 사용: 던지기 " - lore3 = "위더 해골" - lore4 = "충돌 시 폭발." - lore = ["해골 투척 사이의 쿨타임 초.", "위더 해골 사용: 던지기 ", "위더 해골", "충돌 시 폭발."] +name = "위더 해골 투척" +description1 = "내면의 위더를 해방하세요," +description2 = "누군가의" +description3 = "머리를 사용하여." +lore1 = "해골 투척 사이의 쿨타임 초." +lore2 = "위더 해골 사용: 던지기 " +lore3 = "위더 해골" +lore4 = "충돌 시 폭발." +lore = ["해골 투척 사이의 쿨타임 초.", "위더 해골 사용: 던지기 ", "위더 해골", "충돌 시 폭발."] [nether.wither_resist] - name = "위더 저항" - description = "네더라이트의 힘으로 시듦을 저항합니다." - lore1 = "시듦을 무효화할 확률 (부위당)." - lore2 = "패시브: 네더라이트 갑옷 착용 시 일정 확률로 무효화 " - lore3 = "시듦 효과." - lore = ["시듦을 무효화할 확률 (부위당).", "패시브: 네더라이트 갑옷 착용 시 일정 확률로 무효화 ", "시듦 효과."] +name = "위더 저항" +description = "네더라이트의 힘으로 시듦을 저항합니다." +lore1 = "시듦을 무효화할 확률 (부위당)." +lore2 = "패시브: 네더라이트 갑옷 착용 시 일정 확률로 무효화 " +lore3 = "시듦 효과." +lore = ["시듦을 무효화할 확률 (부위당).", "패시브: 네더라이트 갑옷 착용 시 일정 확률로 무효화 ", "시듦 효과."] [nether.fire_resist] - name = "화염 저항" - description = "피부를 단단하게 하여 불에 저항합니다." - lore1 = "불타는 효과를 무효화할 확률!" - lore = ["불타는 효과를 무효화할 확률!"] +name = "화염 저항" +description = "피부를 단단하게 하여 불에 저항합니다." +lore1 = "불타는 효과를 무효화할 확률!" +lore = ["불타는 효과를 무효화할 확률!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "자동 제련" - description = "채굴한 바닐라 광석을 자동으로 제련합니다" - lore1 = "제련 가능한 광석이 자동으로 제련됩니다" - lore2 = "% 확률로 추가 드롭" - lore = ["제련 가능한 광석이 자동으로 제련됩니다", "% 확률로 추가 드롭"] +name = "자동 제련" +description = "채굴한 바닐라 광석을 자동으로 제련합니다" +lore1 = "제련 가능한 광석이 자동으로 제련됩니다" +lore2 = "% 확률로 추가 드롭" +lore = ["제련 가능한 광석이 자동으로 제련됩니다", "% 확률로 추가 드롭"] [pickaxe.chisel] - name = "광석 끌" - description = "광석을 우클릭하여 더 많은 광석을 캐낼 수 있지만, 내구도 소모가 심합니다." - lore1 = "드롭 확률" - lore2 = "도구 마모" - lore = ["드롭 확률", "도구 마모"] +name = "광석 끌" +description = "광석을 우클릭하여 더 많은 광석을 캐낼 수 있지만, 내구도 소모가 심합니다." +lore1 = "드롭 확률" +lore2 = "도구 마모" +lore = ["드롭 확률", "도구 마모"] [pickaxe.drop_to_inventory] - name = "곡괭이 인벤토리 드롭" - description = "블록을 깨면 아이템이 인벤토리로 바로 들어갑니다" - lore1 = "블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다." - lore = ["블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다."] +name = "곡괭이 인벤토리 드롭" +description = "블록을 깨면 아이템이 인벤토리로 바로 들어갑니다" +lore1 = "블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다." +lore = ["블록에서 아이템이 드롭될 때마다 가능하면 인벤토리로 직접 들어갑니다."] [pickaxe.silk_spawner] - name = "곡괭이 섬세한 스포너" - description = "스포너를 파괴할 때 드롭되게 합니다" - lore1 = "섬세한 손길로 스포너를 부술 수 있게 합니다." - lore2 = "웅크린 채로 스포너를 부술 수 있게 합니다." - lore = ["섬세한 손길로 스포너를 부술 수 있게 합니다.", "웅크린 채로 스포너를 부술 수 있게 합니다."] +name = "곡괭이 섬세한 스포너" +description = "스포너를 파괴할 때 드롭되게 합니다" +lore1 = "섬세한 손길로 스포너를 부술 수 있게 합니다." +lore2 = "웅크린 채로 스포너를 부술 수 있게 합니다." +lore = ["섬세한 손길로 스포너를 부술 수 있게 합니다.", "웅크린 채로 스포너를 부술 수 있게 합니다."] [pickaxe.vein_miner] - name = "광맥 채굴" - description = "바닐라 광석의 맥/덩어리를 한꺼번에 캘 수 있습니다" - lore1 = "웅크리고 광석을 캐세요" - lore2 = "광맥 채굴 범위" - lore3 = "이 스킬은 모든 드롭을 하나로 합치지 않습니다!" - lore = ["웅크리고 광석을 캐세요", "광맥 채굴 범위", "이 스킬은 모든 드롭을 하나로 합치지 않습니다!"] +name = "광맥 채굴" +description = "바닐라 광석의 맥/덩어리를 한꺼번에 캘 수 있습니다" +lore1 = "웅크리고 광석을 캐세요" +lore2 = "광맥 채굴 범위" +lore3 = "이 스킬은 모든 드롭을 하나로 합치지 않습니다!" +lore = ["웅크리고 광석을 캐세요", "광맥 채굴 범위", "이 스킬은 모든 드롭을 하나로 합치지 않습니다!"] # ranged [ranged] [ranged.arrow_recovery] - name = "화살 회수" - description = "적을 죽인 후 화살을 회수합니다." - lore1 = "적중/처치 시 화살 회수 확률" - lore2 = "확률: " - lore = ["적중/처치 시 화살 회수 확률", "확률: "] +name = "화살 회수" +description = "적을 죽인 후 화살을 회수합니다." +lore1 = "적중/처치 시 화살 회수 확률" +lore2 = "확률: " +lore = ["적중/처치 시 화살 회수 확률", "확률: "] [ranged.web_shot] - name = "거미줄 올가미" - description = "대상을 맞추면 주위에 거미줄이 생깁니다!" - lore1 = "눈덩이 주변에 거미줄 8개, 그리고 던지세요!" - lore2 = "대략적인 가두기 시간(초)." - lore = ["눈덩이 주변에 거미줄 8개, 그리고 던지세요!", "대략적인 가두기 시간(초)."] +name = "거미줄 올가미" +description = "대상을 맞추면 주위에 거미줄이 생깁니다!" +lore1 = "눈덩이 주변에 거미줄 8개, 그리고 던지세요!" +lore2 = "대략적인 가두기 시간(초)." +lore = ["눈덩이 주변에 거미줄 8개, 그리고 던지세요!", "대략적인 가두기 시간(초)."] [ranged.force_shot] - name = "강력 사격" - description = "더 멀리, 더 빠르게 투사체를 쏩니다!" - advancementname = "장거리 사격" - advancementlore = "30블록 이상 떨어진 곳에서 적중시키세요!" - lore1 = "투사체 속도" - lore = ["투사체 속도"] +name = "강력 사격" +description = "더 멀리, 더 빠르게 투사체를 쏩니다!" +advancementname = "장거리 사격" +advancementlore = "30블록 이상 떨어진 곳에서 적중시키세요!" +lore1 = "투사체 속도" +lore = ["투사체 속도"] [ranged.lunge_shot] - name = "돌진 사격" - description = "낙하 중 화살을 쏘면 랜덤 방향으로 튕겨납니다" - lore1 = "랜덤 돌진 속도" - lore = ["랜덤 돌진 속도"] +name = "돌진 사격" +description = "낙하 중 화살을 쏘면 랜덤 방향으로 튕겨납니다" +lore1 = "랜덤 돌진 속도" +lore = ["랜덤 돌진 속도"] [ranged.arrow_piercing] - name = "화살 관통" - description = "투사체에 관통을 추가합니다! 적을 꿰뚫으세요!" - lore1 = "관통 대상 수" - lore = ["관통 대상 수"] +name = "화살 관통" +description = "투사체에 관통을 추가합니다! 적을 꿰뚫으세요!" +lore1 = "관통 대상 수" +lore = ["관통 대상 수"] # rift [rift] [rift.remote_access] - name = "원격 접근" - description = "공허에서 끌어와 표시된 보관함에 접근합니다." - lore1 = "엔더 진주 + 나침반 = 유물 포트키" - lore2 = "이 아이템으로 원격으로 보관함에 접근할 수 있습니다" - lore3 = "제작 후 아이템을 보고 사용법을 확인하세요" - notcontainer = "그것은 보관함이 아닙니다" - lore = ["엔더 진주 + 나침반 = 유물 포트키", "이 아이템으로 원격으로 보관함에 접근할 수 있습니다", "제작 후 아이템을 보고 사용법을 확인하세요"] +name = "원격 접근" +description = "공허에서 끌어와 표시된 보관함에 접근합니다." +lore1 = "엔더 진주 + 나침반 = 유물 포트키" +lore2 = "이 아이템으로 원격으로 보관함에 접근할 수 있습니다" +lore3 = "제작 후 아이템을 보고 사용법을 확인하세요" +notcontainer = "그것은 보관함이 아닙니다" +lore = ["엔더 진주 + 나침반 = 유물 포트키", "이 아이템으로 원격으로 보관함에 접근할 수 있습니다", "제작 후 아이템을 보고 사용법을 확인하세요"] [rift.blink] - name = "균열 점멸" - description = "근거리 즉시 순간이동, 눈 깜짝할 사이!" - lore1 = "점멸 블록 수 (수직 2배)" - lore2 = "질주 중: 점프를 두 번 탭하여 " - lore3 = "점멸" - lore = ["점멸 블록 수 (수직 2배)", "질주 중: 점프를 두 번 탭하여 ", "점멸"] +name = "균열 점멸" +description = "근거리 즉시 순간이동, 눈 깜짝할 사이!" +lore1 = "점멸 블록 수 (수직 2배)" +lore2 = "질주 중: 점프를 두 번 탭하여 " +lore3 = "점멸" +lore = ["점멸 블록 수 (수직 2배)", "질주 중: 점프를 두 번 탭하여 ", "점멸"] [rift.chest] - name = "간편 엔더 상자" - description = "손에 든 엔더 상자를 좌클릭하여 엽니다." - lore1 = "손에 든 엔더 상자를 클릭하여 열기 (설치하지 마세요)" - lore = ["손에 든 엔더 상자를 클릭하여 열기 (설치하지 마세요)"] +name = "간편 엔더 상자" +description = "손에 든 엔더 상자를 좌클릭하여 엽니다." +lore1 = "손에 든 엔더 상자를 클릭하여 열기 (설치하지 마세요)" +lore = ["손에 든 엔더 상자를 클릭하여 열기 (설치하지 마세요)"] [rift.descent] - name = "공중부양 방지" - description = "공중에 떠 있는 게 지겹나요? 이 스킬이 바로 당신을 위한 것입니다!" - lore1 = "웅크리면 하강하며, 일반보다 느린 속도로 떨어집니다!" - lore2 = "쿨타임:" - lore = ["웅크리면 하강하며, 일반보다 느린 속도로 떨어집니다!", "쿨타임:"] +name = "공중부양 방지" +description = "공중에 떠 있는 게 지겹나요? 이 스킬이 바로 당신을 위한 것입니다!" +lore1 = "웅크리면 하강하며, 일반보다 느린 속도로 떨어집니다!" +lore2 = "쿨타임:" +lore = ["웅크리면 하강하며, 일반보다 느린 속도로 떨어집니다!", "쿨타임:"] [rift.gate] - name = "균열 게이트" - description = "표시된 위치로 순간이동합니다." - lore1 = "제작: 에메랄드 + 자수정 조각 + 엔더 진주" - lore2 = "사용 전에 읽어보세요!" - lore3 = "5초 지연, " - lore4 = "이 애니메이션 중에 죽을 수 있습니다" - lore = ["제작: 에메랄드 + 자수정 조각 + 엔더 진주", "사용 전에 읽어보세요!", "5초 지연, ", "이 애니메이션 중에 죽을 수 있습니다"] +name = "균열 게이트" +description = "표시된 위치로 순간이동합니다." +lore1 = "제작: 에메랄드 + 자수정 조각 + 엔더 진주" +lore2 = "사용 전에 읽어보세요!" +lore3 = "5초 지연, " +lore4 = "이 애니메이션 중에 죽을 수 있습니다" +lore = ["제작: 에메랄드 + 자수정 조각 + 엔더 진주", "사용 전에 읽어보세요!", "5초 지연, ", "이 애니메이션 중에 죽을 수 있습니다"] [rift.resist] - name = "균열 저항" - description = "엔더 아이템 및 능력 사용 시 저항력을 얻습니다" - lore1 = "+ 패시브: 균열 능력이나 엔더 아이템 사용 시 저항력 제공" - lore2 = "휴대용 엔더 상자 제외, 소모 가능한 것만 해당" - lore = ["+ 패시브: 균열 능력이나 엔더 아이템 사용 시 저항력 제공", "휴대용 엔더 상자 제외, 소모 가능한 것만 해당"] +name = "균열 저항" +description = "엔더 아이템 및 능력 사용 시 저항력을 얻습니다" +lore1 = "+ 패시브: 균열 능력이나 엔더 아이템 사용 시 저항력 제공" +lore2 = "휴대용 엔더 상자 제외, 소모 가능한 것만 해당" +lore = ["+ 패시브: 균열 능력이나 엔더 아이템 사용 시 저항력 제공", "휴대용 엔더 상자 제외, 소모 가능한 것만 해당"] [rift.visage] - name = "균열 형상" - description = "인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다." - lore1 = "인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다." - lore = ["인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다."] +name = "균열 형상" +description = "인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다." +lore1 = "인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다." +lore = ["인벤토리에 엔더 진주가 있으면 엔더맨이 적대적으로 변하지 않습니다."] # seaborn [seaborn] [seaborn.oxygen] - name = "유기 산소 탱크" - description = "작은 폐에 더 많은 산소를 담으세요!" - lore1 = "산소 용량 증가" - lore = ["산소 용량 증가"] +name = "유기 산소 탱크" +description = "작은 폐에 더 많은 산소를 담으세요!" +lore1 = "산소 용량 증가" +lore = ["산소 용량 증가"] [seaborn.fishers_fantasy] - name = "낚시꾼의 환상" - description = "낚시에서 더 많은 XP와 물고기를 얻으세요!" - lore1 = "레벨마다 더 많은 XP와 물고기를 얻을 확률이 있습니다!" - lore = ["레벨마다 더 많은 XP와 물고기를 얻을 확률이 있습니다!"] +name = "낚시꾼의 환상" +description = "낚시에서 더 많은 XP와 물고기를 얻으세요!" +lore1 = "레벨마다 더 많은 XP와 물고기를 얻을 확률이 있습니다!" +lore = ["레벨마다 더 많은 XP와 물고기를 얻을 확률이 있습니다!"] [seaborn.haste] - name = "거북이 광부" - description = "수중에서 채굴 시 성급함을 얻습니다!" - lore1 = "수중 호흡 효과가 사라진 후 수중에서 채굴 시 성급함 3이 적용됩니다 (친수성과 중첩)!" - lore = ["수중 호흡 효과가 사라진 후 수중에서 채굴 시 성급함 3이 적용됩니다 (친수성과 중첩)!"] +name = "거북이 광부" +description = "수중에서 채굴 시 성급함을 얻습니다!" +lore1 = "수중 호흡 효과가 사라진 후 수중에서 채굴 시 성급함 3이 적용됩니다 (친수성과 중첩)!" +lore = ["수중 호흡 효과가 사라진 후 수중에서 채굴 시 성급함 3이 적용됩니다 (친수성과 중첩)!"] [seaborn.night_vision] - name = "거북이의 시야" - description = "수중에서 야간 투시를 얻습니다" - lore1 = "수중 호흡 효과가 사라진 후 간단히 수중에서 야간 투시를 얻습니다!" - lore = ["수중 호흡 효과가 사라진 후 간단히 수중에서 야간 투시를 얻습니다!"] +name = "거북이의 시야" +description = "수중에서 야간 투시를 얻습니다" +lore1 = "수중 호흡 효과가 사라진 후 간단히 수중에서 야간 투시를 얻습니다!" +lore = ["수중 호흡 효과가 사라진 후 간단히 수중에서 야간 투시를 얻습니다!"] [seaborn.dolphin_grace] - name = "돌고래의 은총" - description = "돌고래 없이 돌고래처럼 수영합니다" - lore1 = "+ 패시브: 획득 " - lore2 = "x 속도 (돌고래의 은총)" - lore3 = "정밀한 독일 공학이- 잠깐 그건 아니고... 물갈퀴와 호환되지 않습니다" - lore = ["+ 패시브: 획득 ", "x 속도 (돌고래의 은총)", "정밀한 독일 공학이- 잠깐 그건 아니고... 물갈퀴와 호환되지 않습니다"] +name = "돌고래의 은총" +description = "돌고래 없이 돌고래처럼 수영합니다" +lore1 = "+ 패시브: 획득 " +lore2 = "x 속도 (돌고래의 은총)" +lore3 = "정밀한 독일 공학이- 잠깐 그건 아니고... 물갈퀴와 호환되지 않습니다" +lore = ["+ 패시브: 획득 ", "x 속도 (돌고래의 은총)", "정밀한 독일 공학이- 잠깐 그건 아니고... 물갈퀴와 호환되지 않습니다"] # stealth [stealth] [stealth.ghost_armor] - name = "유령의 갑옷" - description = "피해를 받지 않을 때 천천히 방어력이 쌓이며, 1회 피격 시 소멸" - lore1 = "최대 방어력" - lore2 = "속도" - lore = ["최대 방어력", "속도"] +name = "유령의 갑옷" +description = "피해를 받지 않을 때 천천히 방어력이 쌓이며, 1회 피격 시 소멸" +lore1 = "최대 방어력" +lore2 = "속도" +lore = ["최대 방어력", "속도"] [stealth.night_vision] - name = "은신 투시" - description = "웅크리는 동안 야간 투시를 얻습니다" - lore1 = "한 차례의 " - lore2 = "야간 투시" - lore3 = "를 웅크리는 동안 얻습니다" - lore = ["한 차례의 ", "야간 투시", "를 웅크리는 동안 얻습니다"] +name = "은신 투시" +description = "웅크리는 동안 야간 투시를 얻습니다" +lore1 = "한 차례의 " +lore2 = "야간 투시" +lore3 = "를 웅크리는 동안 얻습니다" +lore = ["한 차례의 ", "야간 투시", "를 웅크리는 동안 얻습니다"] [stealth.snatch] - name = "아이템 낚아채기" - description = "웅크리는 동안 드롭된 아이템을 즉시 낚아챕니다!" - lore1 = "낚아채기 반경" - lore = ["낚아채기 반경"] +name = "아이템 낚아채기" +description = "웅크리는 동안 드롭된 아이템을 즉시 낚아챕니다!" +lore1 = "낚아채기 반경" +lore = ["낚아채기 반경"] [stealth.speed] - name = "은신 속도" - description = "웅크리는 동안 속도를 얻습니다" - lore1 = "은신 속도" - lore = ["은신 속도"] +name = "은신 속도" +description = "웅크리는 동안 속도를 얻습니다" +lore1 = "은신 속도" +lore = ["은신 속도"] [stealth.ender_veil] - name = "엔더 장막" - description = "엔더맨 공격을 방지하기 위해 호박이 더 이상 필요 없습니다" - lore1 = "웅크리는 동안 엔더맨 공격을 방지" - lore2 = "모든 엔더맨 공격을 방지" - lore = ["웅크리는 동안 엔더맨 공격을 방지", "모든 엔더맨 공격을 방지"] +name = "엔더 장막" +description = "엔더맨 공격을 방지하기 위해 호박이 더 이상 필요 없습니다" +lore1 = "웅크리는 동안 엔더맨 공격을 방지" +lore2 = "모든 엔더맨 공격을 방지" +lore = ["웅크리는 동안 엔더맨 공격을 방지", "모든 엔더맨 공격을 방지"] # sword [sword] [sword.machete] - name = "마체테" - description = "풀잎을 쉽게 베어냅니다!" - lore1 = "베기 반경" - lore2 = "베기 쿨타임" - lore3 = "도구 마모" - lore = ["베기 반경", "베기 쿨타임", "도구 마모"] +name = "마체테" +description = "풀잎을 쉽게 베어냅니다!" +lore1 = "베기 반경" +lore2 = "베기 쿨타임" +lore3 = "도구 마모" +lore = ["베기 반경", "베기 쿨타임", "도구 마모"] [sword.bloody_blade] - name = "피묻은 칼날" - description = "검으로 공격하면 출혈을 일으킵니다!" - lore1 = "검으로 생물을 공격하면 출혈이 발생합니다" - lore2 = "출혈 지속시간" - lore3 = "출혈 쿨타임" - lore = ["검으로 생물을 공격하면 출혈이 발생합니다", "출혈 지속시간", "출혈 쿨타임"] +name = "피묻은 칼날" +description = "검으로 공격하면 출혈을 일으킵니다!" +lore1 = "검으로 생물을 공격하면 출혈이 발생합니다" +lore2 = "출혈 지속시간" +lore3 = "출혈 쿨타임" +lore = ["검으로 생물을 공격하면 출혈이 발생합니다", "출혈 지속시간", "출혈 쿨타임"] [sword.poisoned_blade] - name = "독검" - description = "검으로 공격하면 독을 일으킵니다!" - lore1 = "검으로 생물을 공격하면 독이 발생합니다" - lore2 = "독 지속시간" - lore3 = "독 쿨타임" - lore = ["검으로 생물을 공격하면 독이 발생합니다", "독 지속시간", "독 쿨타임"] +name = "독검" +description = "검으로 공격하면 독을 일으킵니다!" +lore1 = "검으로 생물을 공격하면 독이 발생합니다" +lore2 = "독 지속시간" +lore3 = "독 쿨타임" +lore = ["검으로 생물을 공격하면 독이 발생합니다", "독 지속시간", "독 쿨타임"] # taming [taming] [taming.damage] - name = "길들인 동물 피해량" - description = "길들인 동물이 주는 피해를 증가시킵니다." - lore1 = "피해량 증가" - lore = ["피해량 증가"] +name = "길들인 동물 피해량" +description = "길들인 동물이 주는 피해를 증가시킵니다." +lore1 = "피해량 증가" +lore = ["피해량 증가"] [taming.health] - name = "길들인 동물 체력" - description = "길들인 동물의 체력을 증가시킵니다." - lore1 = "체력 증가" - lore = ["체력 증가"] +name = "길들인 동물 체력" +description = "길들인 동물의 체력을 증가시킵니다." +lore1 = "체력 증가" +lore = ["체력 증가"] [taming.regeneration] - name = "길들인 동물 재생" - description = "길들인 동물의 재생력을 증가시킵니다." - lore1 = "HP/초" - lore = ["HP/초"] +name = "길들인 동물 재생" +description = "길들인 동물의 재생력을 증가시킵니다." +lore1 = "HP/초" +lore = ["HP/초"] # tragoul [tragoul] [tragoul.thorns] - name = "가시" - description = "공격자에게 피해를 반사합니다!" - lore1 = "피격 시 반사되는 피해량" - lore = ["피격 시 반사되는 피해량"] +name = "가시" +description = "공격자에게 피해를 반사합니다!" +lore1 = "피격 시 반사되는 피해량" +lore = ["피격 시 반사되는 피해량"] [tragoul.globe] - name = "고통의 구체" - description = "주변 적의 수에 따라 피해를 나눕니다!" - lore1 = "주변에 적이 많을수록 각 적에게 주는 피해가 줄어듭니다" - lore2 = "범위: " - lore3 = "모든 엔티티에 추가 피해: " - lore = ["주변에 적이 많을수록 각 적에게 주는 피해가 줄어듭니다", "범위: ", "모든 엔티티에 추가 피해: "] +name = "고통의 구체" +description = "주변 적의 수에 따라 피해를 나눕니다!" +lore1 = "주변에 적이 많을수록 각 적에게 주는 피해가 줄어듭니다" +lore2 = "범위: " +lore3 = "모든 엔티티에 추가 피해: " +lore = ["주변에 적이 많을수록 각 적에게 주는 피해가 줄어듭니다", "범위: ", "모든 엔티티에 추가 피해: "] [tragoul.healing] - name = "고통의 의지" - description = "가한 피해에 비례하여 체력을 회복합니다!" - lore1 = "상처 입히는 것이 이렇게 기분 좋을 줄이야! 가한 피해로 회복" - lore2 = "3초의 피해 창과 1초의 쿨타임이 있습니다 " - lore3 = "피해 퍼센트당 회복량: " - lore = ["상처 입히는 것이 이렇게 기분 좋을 줄이야! 가한 피해로 회복", "3초의 피해 창과 1초의 쿨타임이 있습니다 ", "피해 퍼센트당 회복량: "] +name = "고통의 의지" +description = "가한 피해에 비례하여 체력을 회복합니다!" +lore1 = "상처 입히는 것이 이렇게 기분 좋을 줄이야! 가한 피해로 회복" +lore2 = "3초의 피해 창과 1초의 쿨타임이 있습니다 " +lore3 = "피해 퍼센트당 회복량: " +lore = ["상처 입히는 것이 이렇게 기분 좋을 줄이야! 가한 피해로 회복", "3초의 피해 창과 1초의 쿨타임이 있습니다 ", "피해 퍼센트당 회복량: "] [tragoul.lance] - name = "시체 창" - description = "적을 죽이거나 능력으로 적을 처치하면, 근처 적에게 피해를 주는 창이 생성됩니다!" - lore1 = "창은 당신이 처치한 모든 대상에서 발사되며, 이 능력이 적을 죽일 경우에도 발동합니다." - lore2 = "창을 만들기 위해 생명력의 일부를 희생합니다 (이것이 당신을 죽일 수 있습니다)" - lore3 = "최대 창: 1 + " - lore = ["창은 당신이 처치한 모든 대상에서 발사되며, 이 능력이 적을 죽일 경우에도 발동합니다.", "창을 만들기 위해 생명력의 일부를 희생합니다 (이것이 당신을 죽일 수 있습니다)", "최대 창: 1 + "] +name = "시체 창" +description = "적을 죽이거나 능력으로 적을 처치하면, 근처 적에게 피해를 주는 창이 생성됩니다!" +lore1 = "창은 당신이 처치한 모든 대상에서 발사되며, 이 능력이 적을 죽일 경우에도 발동합니다." +lore2 = "창을 만들기 위해 생명력의 일부를 희생합니다 (이것이 당신을 죽일 수 있습니다)" +lore3 = "최대 창: 1 + " +lore = ["창은 당신이 처치한 모든 대상에서 발사되며, 이 능력이 적을 죽일 경우에도 발동합니다.", "창을 만들기 위해 생명력의 일부를 희생합니다 (이것이 당신을 죽일 수 있습니다)", "최대 창: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "유리 대포" - description = "방어력이 낮을수록 비무장 추가 피해" - lore1 = "x 방어력 0일 때 피해량" - lore2 = "레벨당 추가 피해" - lore = ["x 방어력 0일 때 피해량", "레벨당 추가 피해"] +name = "유리 대포" +description = "방어력이 낮을수록 비무장 추가 피해" +lore1 = "x 방어력 0일 때 피해량" +lore2 = "레벨당 추가 피해" +lore = ["x 방어력 0일 때 피해량", "레벨당 추가 피해"] [unarmed.power] - name = "비무장 파워" - description = "향상된 비무장 피해" - lore1 = "피해량" - lore = ["피해량"] +name = "비무장 파워" +description = "향상된 비무장 피해" +lore1 = "피해량" +lore = ["피해량"] [unarmed.sucker_punch] - name = "기습 펀치" - description = "질주 펀치, 하지만 더 치명적으로." - lore1 = "피해량" - lore2 = "펀치를 날리며 달리는 속도에 따라 피해가 증가합니다" - lore = ["피해량", "펀치를 날리며 달리는 속도에 따라 피해가 증가합니다"] +name = "기습 펀치" +description = "질주 펀치, 하지만 더 치명적으로." +lore1 = "피해량" +lore2 = "펀치를 날리며 달리는 속도에 따라 피해가 증가합니다" +lore = ["피해량", "펀치를 날리며 달리는 속도에 따라 피해가 증가합니다"] diff --git a/src/main/resources/lt_LT.toml b/src/main/resources/lt_LT.toml index f28cc24a8..3705b53cb 100644 --- a/src/main/resources/lt_LT.toml +++ b/src/main/resources/lt_LT.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Laikas pajudeti!" - description = "Nueikite daugiau nei 1 kilometra (1,000 bloku)" +title = "Laikas pajudeti!" +description = "Nueikite daugiau nei 1 kilometra (1,000 bloku)" [advancement.challenge_sprint_5k] - title = "Sprintas 5K!" - description = "Nueikite daugiau nei 5 kilometrus (5,000 bloku)" +title = "Sprintas 5K!" +description = "Nueikite daugiau nei 5 kilometrus (5,000 bloku)" [advancement.challenge_sprint_50k] - title = "Staigus 50K!" - description = "Nueikite daugiau nei 50 kilometru (50,000 bloku)" +title = "Staigus 50K!" +description = "Nueikite daugiau nei 50 kilometru (50,000 bloku)" [advancement.challenge_sprint_500k] - title = "Keliaukite per Visata!!" - description = "Nueikite daugiau nei 500 kilometru (500,000 bloku)" +title = "Keliaukite per Visata!!" +description = "Nueikite daugiau nei 500 kilometru (500,000 bloku)" [advancement.challenge_sprint_marathon] - title = "Sprintas ir maratonas viename!" - description = "Sprintas virs 42,195 bloku!" +title = "Sprintas ir maratonas viename!" +description = "Sprintas virs 42,195 bloku!" [advancement.challenge_place_1k] - title = "Naujas Statybininkas!" - description = "Pastatyk 1,000 Bloku" +title = "Naujas Statybininkas!" +description = "Pastatyk 1,000 Bloku" [advancement.challenge_place_5k] - title = "Tarpinis Statybininkas!" - description = "Pastatyk 5,000 Bloku" +title = "Tarpinis Statybininkas!" +description = "Pastatyk 5,000 Bloku" [advancement.challenge_place_50k] - title = "Pazangus Statybininkas!" - description = "Pastatyk 50,000 Bloku" +title = "Pazangus Statybininkas!" +description = "Pastatyk 50,000 Bloku" [advancement.challenge_place_500k] - title = "Profesionalus Statybininkas!" - description = "Pastatyk 500,000 Bloku" +title = "Profesionalus Statybininkas!" +description = "Pastatyk 500,000 Bloku" [advancement.challenge_place_5m] - title = "Simetrijos Akolitas!" - description = "REALYBE YRA JUSU ZAIDIMU AIKSTELE! (5 Milijonai bloku)" +title = "Simetrijos Akolitas!" +description = "REALYBE YRA JUSU ZAIDIMU AIKSTELE! (5 Milijonai bloku)" [advancement.challenge_chop_1k] - title = "Naujas Medkirtys!" - description = "Nukirsk 1,000 Bloku" +title = "Naujas Medkirtys!" +description = "Nukirsk 1,000 Bloku" [advancement.challenge_chop_5k] - title = "Tarpinis Medkirtys!" - description = "Nukirsk 5,000 Bloku" +title = "Tarpinis Medkirtys!" +description = "Nukirsk 5,000 Bloku" [advancement.challenge_chop_50k] - title = "Pazenges Medkirtys!" - description = "Nukirsk 50,000 Bloku" +title = "Pazenges Medkirtys!" +description = "Nukirsk 50,000 Bloku" [advancement.challenge_chop_500k] - title = "Profesionalus Medkirtys!" - description = "Nukirskite 500,000 Bloku" +title = "Profesionalus Medkirtys!" +description = "Nukirskite 500,000 Bloku" [advancement.challenge_chop_5m] - title = "Tikras Gateristas" - description = "Kieciausias Darbininkas! (5 Milijonai Bloku)" +title = "Tikras Gateristas" +description = "Kieciausias Darbininkas! (5 Milijonai Bloku)" [advancement.challenge_block_1k] - title = "Vos Pabegi!" - description = "Uzblokuok 1000 Smugiu" +title = "Vos Pabegi!" +description = "Uzblokuok 1000 Smugiu" [advancement.challenge_block_5k] - title = "Blokuoti yra Smagu!" - description = "Uzblokuok 5000 Smugiu" +title = "Blokuoti yra Smagu!" +description = "Uzblokuok 5000 Smugiu" [advancement.challenge_block_50k] - title = "Blokavimas yra mano gyvenimas!" - description = "Uzblokuok 50,000 Smugiu" +title = "Blokavimas yra mano gyvenimas!" +description = "Uzblokuok 50,000 Smugiu" [advancement.challenge_block_500k] - title = "Blokavimas yra mano tikslas!" - description = "Uzblokuok 500,000 Smugiu" +title = "Blokavimas yra mano tikslas!" +description = "Uzblokuok 500,000 Smugiu" [advancement.challenge_block_5m] - title = "Ranka, kuria skauda" - description = "Uzblokuok 5,000,000 Smugiu" +title = "Ranka, kuria skauda" +description = "Uzblokuok 5,000,000 Smugiu" [advancement.challenge_brew_1k] - title = "Naujas Alchemikas!" - description = "Suvartok 1000 Eliksyru" +title = "Naujas Alchemikas!" +description = "Suvartok 1000 Eliksyru" [advancement.challenge_brew_5k] - title = "Tarpinis Alchemikas!" - description = "Suvartok 5000 Eliksyru" +title = "Tarpinis Alchemikas!" +description = "Suvartok 5000 Eliksyru" [advancement.challenge_brew_50k] - title = "Pazenges Alchemikas!" - description = "Suvartok 50,000 Eliksyru" +title = "Pazenges Alchemikas!" +description = "Suvartok 50,000 Eliksyru" [advancement.challenge_brew_500k] - title = "Profesionalus Alchemikas!" - description = "Suvartok 500,000 Eliksyru" +title = "Profesionalus Alchemikas!" +description = "Suvartok 500,000 Eliksyru" [advancement.challenge_brew_5m] - title = "Alchemikas" - description = "Suvartok 5,000,000 Eliksyru" +title = "Alchemikas" +description = "Suvartok 5,000,000 Eliksyru" [advancement.challenge_brewsplash_1k] - title = "Naujas Eliksyru metytojas!" - description = "Sudauzyk 1000 Eliksyru" +title = "Naujas Eliksyru metytojas!" +description = "Sudauzyk 1000 Eliksyru" [advancement.challenge_brewsplash_5k] - title = "Tarpinis Eliksyru metytojas!" - description = "Sudauzyk 5000 Eliksyru" +title = "Tarpinis Eliksyru metytojas!" +description = "Sudauzyk 5000 Eliksyru" [advancement.challenge_brewsplash_50k] - title = "Pazenges Eliksyru metytojas!" - description = "Sudauzyk 50,000 Eliksyru" +title = "Pazenges Eliksyru metytojas!" +description = "Sudauzyk 50,000 Eliksyru" [advancement.challenge_brewsplash_500k] - title = "Profesionalus Eliksyru metytojas!" - description = "Sudauzyk 500,000 Eliksyru" +title = "Profesionalus Eliksyru metytojas!" +description = "Sudauzyk 500,000 Eliksyru" [advancement.challenge_brewsplash_5m] - title = "Metymo Profesionalas" - description = "Sudauzyk 5,000,000 Eliksyru" +title = "Metymo Profesionalas" +description = "Sudauzyk 5,000,000 Eliksyru" [advancement.challenge_craft_1k] - title = "Gamintojėlis!" - description = "Pagamink 1000 Daiktu" +title = "Gamintojėlis!" +description = "Pagamink 1000 Daiktu" [advancement.challenge_craft_5k] - title = "Ispudingas Meistras!" - description = "Pagamink 5000 Daiktu" +title = "Ispudingas Meistras!" +description = "Pagamink 5000 Daiktu" [advancement.challenge_craft_50k] - title = "Paslaugus Meistras!" - description = "Pagamink 50,000 Daiktu" +title = "Paslaugus Meistras!" +description = "Pagamink 50,000 Daiktu" [advancement.challenge_craft_500k] - title = "Kakofoninis Gamintojas!" - description = "Pagamink 500,000 Daiktu" +title = "Kakofoninis Gamintojas!" +description = "Pagamink 500,000 Daiktu" [advancement.challenge_craft_5m] - title = "Greitarankis" - description = "Pagamink 5,000,000 Daiktu" +title = "Greitarankis" +description = "Pagamink 5,000,000 Daiktu" [advancement.challenge_enchant_1k] - title = "Naujasis Burtininkas!" - description = "Uzburkite 1000 Daiktu" +title = "Naujasis Burtininkas!" +description = "Uzburkite 1000 Daiktu" [advancement.challenge_enchant_5k] - title = "Tarpinis Burtininkas!" - description = "Uzburkite 5000 Daiktu" +title = "Tarpinis Burtininkas!" +description = "Uzburkite 5000 Daiktu" [advancement.challenge_enchant_50k] - title = "Pazenges Burtininkas!" - description = "Uzburkite 50,000 Daiktu" +title = "Pazenges Burtininkas!" +description = "Uzburkite 50,000 Daiktu" [advancement.challenge_enchant_500k] - title = "Profesionalus Burtininkas!" - description = "Uzburkite 500,000 Daiktu" +title = "Profesionalus Burtininkas!" +description = "Uzburkite 500,000 Daiktu" [advancement.challenge_enchant_5m] - title = "Mislingasis Keretojas" - description = "Uzburkite 5,000,000 Daiktu" +title = "Mislingasis Keretojas" +description = "Uzburkite 5,000,000 Daiktu" [advancement.challenge_excavate_1k] - title = "Suviliotas Ekskavatoristas!" - description = "Iskaskite 1000 Bloku" +title = "Suviliotas Ekskavatoristas!" +description = "Iskaskite 1000 Bloku" [advancement.challenge_excavate_5k] - title = "Tarpinis Ekskavatoristas!" - description = "Iskaskite 5000 Bloku" +title = "Tarpinis Ekskavatoristas!" +description = "Iskaskite 5000 Bloku" [advancement.challenge_excavate_50k] - title = "Pazangus Ekskavatoristas!" - description = "Iskaskite 50,000 Bloku" +title = "Pazangus Ekskavatoristas!" +description = "Iskaskite 50,000 Bloku" [advancement.challenge_excavate_500k] - title = "Profesionalus Ekskavatoristas!" - description = "Iskaskite 500,000 Bloku" +title = "Profesionalus Ekskavatoristas!" +description = "Iskaskite 500,000 Bloku" [advancement.challenge_excavate_5m] - title = "Mislingas Ekskavatoristas" - description = "Iskaskite 5,000,000 Bloku" +title = "Mislingas Ekskavatoristas" +description = "Iskaskite 5,000,000 Bloku" [advancement.horrible_person] - title = "Jus Esate Siaubingas Zmogus" - description = "Nesuvokiama, tikrai" +title = "Jus Esate Siaubingas Zmogus" +description = "Nesuvokiama, tikrai" [advancement.challenge_turtle_egg_smasher] - title = "Vezlio kiausiniu dauzыtojas!" - description = "Sudauzykite 100 vezlio kiausiniu" +title = "Vezlio kiausiniu dauzыtojas!" +description = "Sudauzykite 100 vezlio kiausiniu" [advancement.challenge_turtle_egg_annihilator] - title = "Vezlio kiausiniu naikintojas!" - description = "Sudauzykite 500 vezliu kiausiniu" +title = "Vezlio kiausiniu naikintojas!" +description = "Sudauzykite 500 vezliu kiausiniu" [advancement.challenge_novice_hunter] - title = "Naujoku medziotojas!" - description = "Nuzudykite 100 subjektu" +title = "Naujoku medziotojas!" +description = "Nuzudykite 100 subjektu" [advancement.challenge_intermediate_hunter] - title = "Vidutinio lygio medziotojas!" - description = "Nuzudykite 500 subjektu" +title = "Vidutinio lygio medziotojas!" +description = "Nuzudykite 500 subjektu" [advancement.challenge_advanced_hunter] - title = "Pazangus medziotojas!" - description = "Nuzudykite 5000 subjektu" +title = "Pazangus medziotojas!" +description = "Nuzudykite 5000 subjektu" [advancement.challenge_creeper_conqueror] - title = "Kryperio Uzkariautojas!" - description = "Nuzudykite 50 kryperių" +title = "Kryperio Uzkariautojas!" +description = "Nuzudykite 50 kryperių" [advancement.challenge_creeper_annihilator] - title = "Kryperių Naikintojas!" - description = "Nuzudykite 200 kryperių" +title = "Kryperių Naikintojas!" +description = "Nuzudykite 200 kryperių" [advancement.challenge_pickaxe_1k] - title = "Pradedantysis Kalnakasys" - description = "Sugriaukite 1000 Bloku" +title = "Pradedantysis Kalnakasys" +description = "Sugriaukite 1000 Bloku" [advancement.challenge_pickaxe_5k] - title = "Iguděs Kalnakasys" - description = "Sugriaukite 5000 Bloku" +title = "Iguděs Kalnakasys" +description = "Sugriaukite 5000 Bloku" [advancement.challenge_pickaxe_50k] - title = "Ekspertas Kalnakasys" - description = "Sugriaukite 50,000 Bloku" +title = "Ekspertas Kalnakasys" +description = "Sugriaukite 50,000 Bloku" [advancement.challenge_pickaxe_500k] - title = "Meistras Kalnakasys" - description = "Sugriaukite 500,000 Bloku" +title = "Meistras Kalnakasys" +description = "Sugriaukite 500,000 Bloku" [advancement.challenge_pickaxe_5m] - title = "Legendinis Kalnakasys" - description = "Sugriaukite 5,000,000 Bloku" +title = "Legendinis Kalnakasys" +description = "Sugriaukite 5,000,000 Bloku" [advancement.challenge_eat_100] - title = "Tiek daug valgyti!" - description = "Suvalgykite daugiau nei 100 Maisto!" +title = "Tiek daug valgyti!" +description = "Suvalgykite daugiau nei 100 Maisto!" [advancement.challenge_eat_1000] - title = "Nenumaldomas Alkis!" - description = "Suvalgykite daugiau nei 1,000 Maisto!" +title = "Nenumaldomas Alkis!" +description = "Suvalgykite daugiau nei 1,000 Maisto!" [advancement.challenge_eat_10000] - title = "AMZINAS ALKIS!" - description = "Suvalgykite daugiau nei 10,000 Maisto!" +title = "AMZINAS ALKIS!" +description = "Suvalgykite daugiau nei 10,000 Maisto!" [advancement.challenge_harvest_100] - title = "Pilnas Derlius" - description = "Nuimkite daugiau nei 100 paseliu!" +title = "Pilnas Derlius" +description = "Nuimkite daugiau nei 100 paseliu!" [advancement.challenge_harvest_1000] - title = "Didysis Derlius" - description = "Nuimkite daugiau nei 1,000 paseliu!" +title = "Didysis Derlius" +description = "Nuimkite daugiau nei 1,000 paseliu!" [advancement.challenge_swim_1nm] - title = "Zmogus Povandeninis Laivas!" - description = "Plaukite 1 jurmyle (1,852 blokai)" +title = "Zmogus Povandeninis Laivas!" +description = "Plaukite 1 jurmyle (1,852 blokai)" [advancement.challenge_sneak_1k] - title = "Kelio Skausmas" - description = "Nusliauzti daugiau nei kilometra (1,000 bloku)" +title = "Kelio Skausmas" +description = "Nusliauzti daugiau nei kilometra (1,000 bloku)" [advancement.challenge_sneak_5k] - title = "Seseliu Klajunas" - description = "Nuslinkti daugiau nei 5 000 bloku" +title = "Seseliu Klajunas" +description = "Nuslinkti daugiau nei 5 000 bloku" [advancement.challenge_sneak_20k] - title = "Vaiduoklis" - description = "Nuslinkti daugiau nei 20 000 bloku" +title = "Vaiduoklis" +description = "Nuslinkti daugiau nei 20 000 bloku" [advancement.challenge_swim_5k] - title = "Gilus Naras" - description = "Nuplaukti daugiau nei 5 000 bloku" +title = "Gilus Naras" +description = "Nuplaukti daugiau nei 5 000 bloku" [advancement.challenge_swim_20k] - title = "Poseidono Isrinktasis" - description = "Nuplaukti daugiau nei 20 000 bloku" +title = "Poseidono Isrinktasis" +description = "Nuplaukti daugiau nei 20 000 bloku" [advancement.challenge_sword_100] - title = "Pirmas Kraujas" - description = "Smogti 100 kartu kardu" +title = "Pirmas Kraujas" +description = "Smogti 100 kartu kardu" [advancement.challenge_sword_1k] - title = "Asmenio Sokejas" - description = "Smogti 1 000 kartu kardu" +title = "Asmenio Sokejas" +description = "Smogti 1 000 kartu kardu" [advancement.challenge_sword_10k] - title = "Tukstantis Ipjovimu" - description = "Smogti 10 000 kartu kardu" +title = "Tukstantis Ipjovimu" +description = "Smogti 10 000 kartu kardu" [advancement.challenge_unarmed_100] - title = "Baro Mustainis" - description = "Smogti 100 kartu be ginklo" +title = "Baro Mustainis" +description = "Smogti 100 kartu be ginklo" [advancement.challenge_unarmed_1k] - title = "Geleziniai Kumsciai" - description = "Smogti 1 000 kartu be ginklo" +title = "Geleziniai Kumsciai" +description = "Smogti 1 000 kartu be ginklo" [advancement.challenge_unarmed_10k] - title = "Vienas Smugis" - description = "Smogti 10 000 kartu be ginklo" +title = "Vienas Smugis" +description = "Smogti 10 000 kartu be ginklo" [advancement.challenge_trag_1k] - title = "Kraujo Kaina" - description = "Gauti 1 000 zalos" +title = "Kraujo Kaina" +description = "Gauti 1 000 zalos" [advancement.challenge_trag_10k] - title = "Raudonas Potvynis" - description = "Gauti 10 000 zalos" +title = "Raudonas Potvynis" +description = "Gauti 10 000 zalos" [advancement.challenge_trag_100k] - title = "Kancios Avataras" - description = "Gauti 100 000 zalos" +title = "Kancios Avataras" +description = "Gauti 100 000 zalos" [advancement.challenge_ranged_100] - title = "Taikinio Pratybos" - description = "Isouti 100 sviediniai" +title = "Taikinio Pratybos" +description = "Isouti 100 sviediniai" [advancement.challenge_ranged_1k] - title = "Vanago Akis" - description = "Isouti 1 000 sviediniai" +title = "Vanago Akis" +description = "Isouti 1 000 sviediniai" [advancement.challenge_ranged_10k] - title = "Strelu Audra" - description = "Isouti 10 000 sviediniai" +title = "Strelu Audra" +description = "Isouti 10 000 sviediniai" [advancement.challenge_chronos_1h] - title = "Tik Tak" - description = "Praleisti 1 valanda prisijungus" +title = "Tik Tak" +description = "Praleisti 1 valanda prisijungus" [advancement.challenge_chronos_24h] - title = "Laiko Smeliai" - description = "Praleisti 24 valandas prisijungus" +title = "Laiko Smeliai" +description = "Praleisti 24 valandas prisijungus" [advancement.challenge_chronos_168h] - title = "Belaikiksnis" - description = "Praleisti 168 valandas (1 savaite) prisijungus" +title = "Belaikiksnis" +description = "Praleisti 168 valandas (1 savaite) prisijungus" [advancement.challenge_nether_50] - title = "Pragaro Vartininkas" - description = "Nudobti 50 nethero butyniu" +title = "Pragaro Vartininkas" +description = "Nudobti 50 nethero butyniu" [advancement.challenge_nether_500] - title = "Bedugnio Sargas" - description = "Nudobti 500 nethero butyniu" +title = "Bedugnio Sargas" +description = "Nudobti 500 nethero butyniu" [advancement.challenge_nether_5k] - title = "Nethero Valdovas" - description = "Nudobti 5 000 nethero butyniu" +title = "Nethero Valdovas" +description = "Nudobti 5 000 nethero butyniu" [advancement.challenge_rift_50] - title = "Erdves Anomalija" - description = "Teleportuotis 50 kartu" +title = "Erdves Anomalija" +description = "Teleportuotis 50 kartu" [advancement.challenge_rift_500] - title = "Tustumos Klajunas" - description = "Teleportuotis 500 kartu" +title = "Tustumos Klajunas" +description = "Teleportuotis 500 kartu" [advancement.challenge_rift_5k] - title = "Tarp Pasauliu" - description = "Teleportuotis 5 000 kartu" +title = "Tarp Pasauliu" +description = "Teleportuotis 5 000 kartu" [advancement.challenge_taming_10] - title = "Gyvunu Sunebejas" - description = "Dauginti 10 gyvunu" +title = "Gyvunu Sunebejas" +description = "Dauginti 10 gyvunu" [advancement.challenge_taming_50] - title = "Bures Lyderis" - description = "Dauginti 50 gyvunu" +title = "Bures Lyderis" +description = "Dauginti 50 gyvunu" [advancement.challenge_taming_500] - title = "Zveriu Valdovas" - description = "Dauginti 500 gyvunu" +title = "Zveriu Valdovas" +description = "Dauginti 500 gyvunu" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Greicio Demonas" - description = "Nubek daugiau nei 5 Kilometrus (5,000 bloku)" +title = "Greicio Demonas" +description = "Nubek daugiau nei 5 Kilometrus (5,000 bloku)" [advancement.challenge_sprint_dist_50k] - title = "Zaibisku Kojos" - description = "Nubek daugiau nei 50 Kilometru (50,000 bloku)" +title = "Zaibisku Kojos" +description = "Nubek daugiau nei 50 Kilometru (50,000 bloku)" [advancement.challenge_agility_swim_1k] - title = "Vandens Begiklis" - description = "Nuplaukite daugiau nei 1 Kilometra (1,000 bloku)" +title = "Vandens Begiklis" +description = "Nuplaukite daugiau nei 1 Kilometra (1,000 bloku)" [advancement.challenge_agility_swim_10k] - title = "Vandeninis Keliautojas" - description = "Nuplaukite daugiau nei 10 Kilometru (10,000 bloku)" +title = "Vandeninis Keliautojas" +description = "Nuplaukite daugiau nei 10 Kilometru (10,000 bloku)" [advancement.challenge_fly_1k] - title = "Dangaus Sokejas" - description = "Nuskriskite daugiau nei 1 Kilometra (1,000 bloku)" +title = "Dangaus Sokejas" +description = "Nuskriskite daugiau nei 1 Kilometra (1,000 bloku)" [advancement.challenge_fly_10k] - title = "Vejo Raitelis" - description = "Nuskriskite daugiau nei 10 Kilometru (10,000 bloku)" +title = "Vejo Raitelis" +description = "Nuskriskite daugiau nei 10 Kilometru (10,000 bloku)" [advancement.challenge_agility_sneak_500] - title = "Tylius Zingsniai" - description = "Prislinkinite daugiau nei 500 bloku" +title = "Tylius Zingsniai" +description = "Prislinkinite daugiau nei 500 bloku" [advancement.challenge_agility_sneak_5k] - title = "Seseliu Zingsniai" - description = "Prislinkinite daugiau nei 5 Kilometrus (5,000 bloku)" +title = "Seseliu Zingsniai" +description = "Prislinkinite daugiau nei 5 Kilometrus (5,000 bloku)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Griovimo Komanda" - description = "Sunaikinkite 500 bloku" +title = "Griovimo Komanda" +description = "Sunaikinkite 500 bloku" [advancement.challenge_demolish_5k] - title = "Griovimo Rutulys" - description = "Sunaikinkite 5,000 bloku" +title = "Griovimo Rutulys" +description = "Sunaikinkite 5,000 bloku" [advancement.challenge_value_placed_10k] - title = "Vertingas Statytojas" - description = "Pastatykite bloku uz 10,000 vertes" +title = "Vertingas Statytojas" +description = "Pastatykite bloku uz 10,000 vertes" [advancement.challenge_value_placed_100k] - title = "Architekturos Meistras" - description = "Pastatykite bloku uz 100,000 vertes" +title = "Architekturos Meistras" +description = "Pastatykite bloku uz 100,000 vertes" [advancement.challenge_demolish_val_5k] - title = "Utilizavimo Ekspertas" - description = "Isgelbékit 5,000 bloku vertes is griovimo" +title = "Utilizavimo Ekspertas" +description = "Isgelbékit 5,000 bloku vertes is griovimo" [advancement.challenge_demolish_val_50k] - title = "Visiskas Isardymas" - description = "Isgelbékit 50,000 bloku vertes is griovimo" +title = "Visiskas Isardymas" +description = "Isgelbékit 50,000 bloku vertes is griovimo" [advancement.challenge_high_build_100] - title = "Dangaus Statytojas" - description = "Pastatykite 100 bloku virs Y=128" +title = "Dangaus Statytojas" +description = "Pastatykite 100 bloku virs Y=128" [advancement.challenge_high_build_1k] - title = "Debesu Architektas" - description = "Pastatykite 1,000 bloku virs Y=128" +title = "Debesu Architektas" +description = "Pastatykite 1,000 bloku virs Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Kirviasukys" - description = "Mostelekite kirviu 500 kartu" +title = "Kirviasukys" +description = "Mostelekite kirviu 500 kartu" [advancement.challenge_axe_swing_5k] - title = "Berserkas" - description = "Mostelekite kirviu 5,000 kartu" +title = "Berserkas" +description = "Mostelekite kirviu 5,000 kartu" [advancement.challenge_axe_damage_1k] - title = "Kapotojas" - description = "Padarykite 1,000 zalos kirviu" +title = "Kapotojas" +description = "Padarykite 1,000 zalos kirviu" [advancement.challenge_axe_damage_10k] - title = "Budelio Kirvis" - description = "Padarykite 10,000 zalos kirviu" +title = "Budelio Kirvis" +description = "Padarykite 10,000 zalos kirviu" [advancement.challenge_axe_value_5k] - title = "Medienos Prekeivis" - description = "Surinkite medienos uz 5,000 vertes" +title = "Medienos Prekeivis" +description = "Surinkite medienos uz 5,000 vertes" [advancement.challenge_axe_value_50k] - title = "Medienos Baronas" - description = "Surinkite medienos uz 50,000 vertes" +title = "Medienos Baronas" +description = "Surinkite medienos uz 50,000 vertes" [advancement.challenge_leaves_500] - title = "Lapu Pustejas" - description = "Isvalkite 500 lapu bloku kirviu" +title = "Lapu Pustejas" +description = "Isvalkite 500 lapu bloku kirviu" [advancement.challenge_leaves_5k] - title = "Nulapotojas" - description = "Isvalkite 5,000 lapu bloku kirviu" +title = "Nulapotojas" +description = "Isvalkite 5,000 lapu bloku kirviu" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Zalos Sugerejas" - description = "Uzblokuokite 1,000 zalos skydu" +title = "Zalos Sugerejas" +description = "Uzblokuokite 1,000 zalos skydu" [advancement.challenge_block_dmg_10k] - title = "Gyvas Skydas" - description = "Uzblokuokite 10,000 zalos skydu" +title = "Gyvas Skydas" +description = "Uzblokuokite 10,000 zalos skydu" [advancement.challenge_block_proj_100] - title = "Strelu Atmustojas" - description = "Uzblokuokite 100 sviediniu skydu" +title = "Strelu Atmustojas" +description = "Uzblokuokite 100 sviediniu skydu" [advancement.challenge_block_proj_1k] - title = "Sviediniu Skydas" - description = "Uzblokuokite 1,000 sviediniu skydu" +title = "Sviediniu Skydas" +description = "Uzblokuokite 1,000 sviediniu skydu" [advancement.challenge_block_melee_500] - title = "Atremimu Meistras" - description = "Uzblokuokite 500 artimos kovos ataku skydu" +title = "Atremimu Meistras" +description = "Uzblokuokite 500 artimos kovos ataku skydu" [advancement.challenge_block_melee_5k] - title = "Gelezine Tvirtove" - description = "Uzblokuokite 5,000 artimos kovos ataku skydu" +title = "Gelezine Tvirtove" +description = "Uzblokuokite 5,000 artimos kovos ataku skydu" [advancement.challenge_block_heavy_50] - title = "Tankas" - description = "Uzblokuokite 50 sunkiu ataku (virs 5 zalos)" +title = "Tankas" +description = "Uzblokuokite 50 sunkiu ataku (virs 5 zalos)" [advancement.challenge_block_heavy_500] - title = "Nepajudinamas Objektas" - description = "Uzblokuokite 500 sunkiu ataku (virs 5 zalos)" +title = "Nepajudinamas Objektas" +description = "Uzblokuokite 500 sunkiu ataku (virs 5 zalos)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Virejo Dirbtuve" - description = "Pastatykite 10 virimo stovu" +title = "Virejo Dirbtuve" +description = "Pastatykite 10 virimo stovu" [advancement.challenge_brew_stands_50] - title = "Eliksyru Fabrikas" - description = "Pastatykite 50 virimo stovu" +title = "Eliksyru Fabrikas" +description = "Pastatykite 50 virimo stovu" [advancement.challenge_brew_strong_25] - title = "Stiprus Gérimas" - description = "Isgerkite 25 pagerintu eliksyru" +title = "Stiprus Gérimas" +description = "Isgerkite 25 pagerintu eliksyru" [advancement.challenge_brew_strong_250] - title = "Maksimalus Stiprumas" - description = "Isgerkite 250 pagerintu eliksyru" +title = "Maksimalus Stiprumas" +description = "Isgerkite 250 pagerintu eliksyru" [advancement.challenge_brew_splash_hits_50] - title = "Purskimo Zona" - description = "Pataikykite i 50 butyniu purskiamaisiais eliksyrais" +title = "Purskimo Zona" +description = "Pataikykite i 50 butyniu purskiamaisiais eliksyrais" [advancement.challenge_brew_splash_hits_500] - title = "Maro Daktaras" - description = "Pataikykite i 500 butyniu purskiamaisiais eliksyrais" +title = "Maro Daktaras" +description = "Pataikykite i 500 butyniu purskiamaisiais eliksyrais" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Neramus" - description = "Nukeliaukite 1 Kilometra budami aktyvus" +title = "Neramus" +description = "Nukeliaukite 1 Kilometra budami aktyvus" [advancement.challenge_active_dist_10k] - title = "Keliu Zinovas" - description = "Nukeliaukite 10 Kilometru budami aktyvus" +title = "Keliu Zinovas" +description = "Nukeliaukite 10 Kilometru budami aktyvus" [advancement.challenge_active_dist_100k] - title = "Amzinas Judéjimas" - description = "Nukeliaukite 100 Kilometru budami aktyvus" +title = "Amzinas Judéjimas" +description = "Nukeliaukite 100 Kilometru budami aktyvus" [advancement.challenge_beds_10] - title = "Ankstyvasis Paukstis" - description = "Miegokite lovoje 10 kartu" +title = "Ankstyvasis Paukstis" +description = "Miegokite lovoje 10 kartu" [advancement.challenge_beds_100] - title = "Laiko Praleidinétojas" - description = "Miegokite lovoje 100 kartu" +title = "Laiko Praleidinétojas" +description = "Miegokite lovoje 100 kartu" [advancement.challenge_chronos_tp_50] - title = "Laiko Poslinkis" - description = "Teleportuokités 50 kartu" +title = "Laiko Poslinkis" +description = "Teleportuokités 50 kartu" [advancement.challenge_chronos_tp_500] - title = "Laiko Iskreipimas" - description = "Teleportuokités 500 kartu" +title = "Laiko Iskreipimas" +description = "Teleportuokités 500 kartu" [advancement.challenge_chronos_deaths_10] - title = "Mirtingasis" - description = "Mirkite 10 kartu" +title = "Mirtingasis" +description = "Mirkite 10 kartu" [advancement.challenge_chronos_deaths_100] - title = "Mirties Isskukejas" - description = "Mirkite 100 kartu" +title = "Mirties Isskukejas" +description = "Mirkite 100 kartu" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Amatininko Verte" - description = "Pagaminkite daiktu uz 10,000 bendros vertes" +title = "Amatininko Verte" +description = "Pagaminkite daiktu uz 10,000 bendros vertes" [advancement.challenge_craft_value_100k] - title = "Meistras Amatininkas" - description = "Pagaminkite daiktu uz 100,000 bendros vertes" +title = "Meistras Amatininkas" +description = "Pagaminkite daiktu uz 100,000 bendros vertes" [advancement.challenge_craft_tools_25] - title = "Irankiu Kalvis" - description = "Pagaminkite 25 irankes" +title = "Irankiu Kalvis" +description = "Pagaminkite 25 irankes" [advancement.challenge_craft_tools_250] - title = "Kalves Meistras" - description = "Pagaminkite 250 irankes" +title = "Kalves Meistras" +description = "Pagaminkite 250 irankes" [advancement.challenge_craft_armor_25] - title = "Sarviu Kalvis" - description = "Pagaminkite 25 sarvu dalis" +title = "Sarviu Kalvis" +description = "Pagaminkite 25 sarvu dalis" [advancement.challenge_craft_armor_250] - title = "Sarviu Meistras" - description = "Pagaminkite 250 sarvu dalis" +title = "Sarviu Meistras" +description = "Pagaminkite 250 sarvu dalis" [advancement.challenge_craft_mass_25k] - title = "Masine Gamyba" - description = "Pagaminkite 25,000 daiktu" +title = "Masine Gamyba" +description = "Pagaminkite 25,000 daiktu" [advancement.challenge_craft_mass_250k] - title = "Pramonine Revoliucija" - description = "Pagaminkite 250,000 daiktu" +title = "Pramonine Revoliucija" +description = "Pagaminkite 250,000 daiktu" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Rinkejas" - description = "Atraskite 50 unikalu daiktu" +title = "Rinkejas" +description = "Atraskite 50 unikalu daiktu" [advancement.challenge_discover_items_250] - title = "Kataloguotojas" - description = "Atraskite 250 unikalu daiktu" +title = "Kataloguotojas" +description = "Atraskite 250 unikalu daiktu" [advancement.challenge_discover_blocks_50] - title = "Tyrinetoja" - description = "Atraskite 50 unikalu bloku" +title = "Tyrinetoja" +description = "Atraskite 50 unikalu bloku" [advancement.challenge_discover_blocks_250] - title = "Geologas" - description = "Atraskite 250 unikalu bloku" +title = "Geologas" +description = "Atraskite 250 unikalu bloku" [advancement.challenge_discover_mobs_25] - title = "Stebetojas" - description = "Atraskite 25 unikalias butybes" +title = "Stebetojas" +description = "Atraskite 25 unikalias butybes" [advancement.challenge_discover_mobs_75] - title = "Gamtininkas" - description = "Atraskite 75 unikalias butybes" +title = "Gamtininkas" +description = "Atraskite 75 unikalias butybes" [advancement.challenge_discover_biomes_10] - title = "Klajoklys" - description = "Atraskite 10 unikalu biomu" +title = "Klajoklys" +description = "Atraskite 10 unikalu biomu" [advancement.challenge_discover_biomes_40] - title = "Pasaulio Keliautojas" - description = "Atraskite 40 unikalu biomu" +title = "Pasaulio Keliautojas" +description = "Atraskite 40 unikalu biomu" [advancement.challenge_discover_foods_10] - title = "Gurmantas" - description = "Atraskite 10 unikaliu maisto produktu" +title = "Gurmantas" +description = "Atraskite 10 unikaliu maisto produktu" [advancement.challenge_discover_foods_30] - title = "Kulinarijos Meistras" - description = "Atraskite 30 unikaliu maisto produktu" +title = "Kulinarijos Meistras" +description = "Atraskite 30 unikaliu maisto produktu" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Galios Audéjas" - description = "Sukaupkite 100 uzkerejimo galios" +title = "Galios Audéjas" +description = "Sukaupkite 100 uzkerejimo galios" [advancement.challenge_enchant_power_1k] - title = "Magijos Meistras" - description = "Sukaupkite 1,000 uzkerejimo galios" +title = "Magijos Meistras" +description = "Sukaupkite 1,000 uzkerejimo galios" [advancement.challenge_enchant_levels_1k] - title = "Lygiu Leidejas" - description = "Isleiskite 1,000 patirties lygiu uzkerejimams" +title = "Lygiu Leidejas" +description = "Isleiskite 1,000 patirties lygiu uzkerejimams" [advancement.challenge_enchant_levels_10k] - title = "XP Ryklys" - description = "Isleiskite 10,000 patirties lygiu uzkerejimams" +title = "XP Ryklys" +description = "Isleiskite 10,000 patirties lygiu uzkerejimams" [advancement.challenge_enchant_high_25] - title = "Aukstu Statymu Zaidejas" - description = "Atlikite 25 maksimalaus lygio uzkerejimus" +title = "Aukstu Statymu Zaidejas" +description = "Atlikite 25 maksimalaus lygio uzkerejimus" [advancement.challenge_enchant_high_250] - title = "Legendinis Uzkeretoja" - description = "Atlikite 250 maksimalaus lygio uzkerejimus" +title = "Legendinis Uzkeretoja" +description = "Atlikite 250 maksimalaus lygio uzkerejimus" [advancement.challenge_enchant_total_500] - title = "Lygiu Degintoja" - description = "Isleiskite 500 viso lygiu visiems uzkerejimams" +title = "Lygiu Degintoja" +description = "Isleiskite 500 viso lygiu visiems uzkerejimams" [advancement.challenge_enchant_total_5k] - title = "Magine Investicija" - description = "Isleiskite 5,000 viso lygiu visiems uzkerejimams" +title = "Magine Investicija" +description = "Isleiskite 5,000 viso lygiu visiems uzkerejimams" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Kasejas" - description = "Mostelekite kastuvu 500 kartu" +title = "Kasejas" +description = "Mostelekite kastuvu 500 kartu" [advancement.challenge_dig_swing_5k] - title = "Ekskavatorius" - description = "Mostelekite kastuvu 5,000 kartu" +title = "Ekskavatorius" +description = "Mostelekite kastuvu 5,000 kartu" [advancement.challenge_dig_damage_1k] - title = "Kastuvo Riteris" - description = "Padarykite 1,000 zalos kastuvu" +title = "Kastuvo Riteris" +description = "Padarykite 1,000 zalos kastuvu" [advancement.challenge_dig_damage_10k] - title = "Kastuvo Meistras" - description = "Padarykite 10,000 zalos kastuvu" +title = "Kastuvo Meistras" +description = "Padarykite 10,000 zalos kastuvu" [advancement.challenge_dig_value_5k] - title = "Purvo Prekeivis" - description = "Iskaskite bloku uz 5,000 vertes" +title = "Purvo Prekeivis" +description = "Iskaskite bloku uz 5,000 vertes" [advancement.challenge_dig_value_50k] - title = "Zemes Baronas" - description = "Iskaskite bloku uz 50,000 vertes" +title = "Zemes Baronas" +description = "Iskaskite bloku uz 50,000 vertes" [advancement.challenge_dig_gravel_500] - title = "Zvirgzdo Malimas" - description = "Iskaskite 500 zvirgzdo, smelo ar molio bloku" +title = "Zvirgzdo Malimas" +description = "Iskaskite 500 zvirgzdo, smelo ar molio bloku" [advancement.challenge_dig_gravel_5k] - title = "Smelo Sijotuvas" - description = "Iskaskite 5,000 zvirgzdo, smelo ar molio bloku" +title = "Smelo Sijotuvas" +description = "Iskaskite 5,000 zvirgzdo, smelo ar molio bloku" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Séjéjas" - description = "Pasodinkite 100 paselu" +title = "Séjéjas" +description = "Pasodinkite 100 paselu" [advancement.challenge_plant_1k] - title = "Zaliasis Pirstas" - description = "Pasodinkite 1,000 paselu" +title = "Zaliasis Pirstas" +description = "Pasodinkite 1,000 paselu" [advancement.challenge_plant_5k] - title = "Zemes Ukio Baronas" - description = "Pasodinkite 5,000 paselu" +title = "Zemes Ukio Baronas" +description = "Pasodinkite 5,000 paselu" [advancement.challenge_compost_50] - title = "Perdirbéjas" - description = "Kompostuokite 50 daiktu" +title = "Perdirbéjas" +description = "Kompostuokite 50 daiktu" [advancement.challenge_compost_500] - title = "Dirvos Turtintojas" - description = "Kompostuokite 500 daiktu" +title = "Dirvos Turtintojas" +description = "Kompostuokite 500 daiktu" [advancement.challenge_shear_50] - title = "Kirpéjas" - description = "Apkirpkite 50 butybes" +title = "Kirpéjas" +description = "Apkirpkite 50 butybes" [advancement.challenge_shear_250] - title = "Bandos Valdovas" - description = "Apkirpkite 250 butybes" +title = "Bandos Valdovas" +description = "Apkirpkite 250 butybes" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Zabikas" - description = "Uzmuskit 500 padaru" +title = "Zabikas" +description = "Uzmuskit 500 padaru" [advancement.challenge_kills_5k] - title = "Egzekutorius" - description = "Uzmuskit 5,000 padaru" +title = "Egzekutorius" +description = "Uzmuskit 5,000 padaru" [advancement.challenge_boss_1] - title = "Boso Issaukejas" - description = "Uzmuskit bosa" +title = "Boso Issaukejas" +description = "Uzmuskit bosa" [advancement.challenge_boss_10] - title = "Legendu Zabikas" - description = "Uzmuskit 10 bosu" +title = "Legendu Zabikas" +description = "Uzmuskit 10 bosu" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Nuvytes" - description = "Isgyvenkit 500 nuvytimo zalos" +title = "Nuvytes" +description = "Isgyvenkit 500 nuvytimo zalos" [advancement.challenge_wither_dmg_5k] - title = "Raupus Isgyvenes" - description = "Isgyvenkit 5,000 nuvytimo zalos" +title = "Raupus Isgyvenes" +description = "Isgyvenkit 5,000 nuvytimo zalos" [advancement.challenge_wither_skel_25] - title = "Kaulu Rinkejas" - description = "Uzmuskit 25 viterinius skeletus" +title = "Kaulu Rinkejas" +description = "Uzmuskit 25 viterinius skeletus" [advancement.challenge_wither_skel_250] - title = "Skeletu Riksté" - description = "Uzmuskit 250 viteriniu skeletu" +title = "Skeletu Riksté" +description = "Uzmuskit 250 viteriniu skeletu" [advancement.challenge_wither_boss_1] - title = "Viterio Zabikas" - description = "Nugalekit Viteri" +title = "Viterio Zabikas" +description = "Nugalekit Viteri" [advancement.challenge_wither_boss_10] - title = "Nethero Valdovas" - description = "Nugalekit Viteri 10 kartu" +title = "Nethero Valdovas" +description = "Nugalekit Viteri 10 kartu" [advancement.challenge_roses_10] - title = "Mirties Sodininkas" - description = "Sunaikinkite 10 viterio roziu" +title = "Mirties Sodininkas" +description = "Sunaikinkite 10 viterio roziu" [advancement.challenge_roses_100] - title = "Raupus Rinkejas" - description = "Sunaikinkite 100 viterio roziu" +title = "Raupus Rinkejas" +description = "Sunaikinkite 100 viterio roziu" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Kalnakasio Ranka" - description = "Mostelekite kastuvu 500 kartu" +title = "Kalnakasio Ranka" +description = "Mostelekite kastuvu 500 kartu" [advancement.challenge_pick_swing_5k] - title = "Tuneliu Kasejas" - description = "Mostelekite kastuvu 5,000 kartu" +title = "Tuneliu Kasejas" +description = "Mostelekite kastuvu 5,000 kartu" [advancement.challenge_pick_damage_1k] - title = "Kastuvo Kovotojas" - description = "Padarykite 1,000 zalos kastuvu" +title = "Kastuvo Kovotojas" +description = "Padarykite 1,000 zalos kastuvu" [advancement.challenge_pick_damage_10k] - title = "Kovinis Kastuvas" - description = "Padarykite 10,000 zalos kastuvu" +title = "Kovinis Kastuvas" +description = "Padarykite 10,000 zalos kastuvu" [advancement.challenge_pick_value_5k] - title = "Brangakmeniu Ieškotojas" - description = "Iskaskite bloku uz 5,000 vertes" +title = "Brangakmeniu Ieškotojas" +description = "Iskaskite bloku uz 5,000 vertes" [advancement.challenge_pick_value_50k] - title = "Rudos Baronas" - description = "Iskaskite bloku uz 50,000 vertes" +title = "Rudos Baronas" +description = "Iskaskite bloku uz 50,000 vertes" [advancement.challenge_pick_ores_500] - title = "Zvalgas" - description = "Iskaskite 500 rudos bloku" +title = "Zvalgas" +description = "Iskaskite 500 rudos bloku" [advancement.challenge_pick_ores_5k] - title = "Kasyklu Meistras" - description = "Iskaskite 5,000 rudos bloku" +title = "Kasyklu Meistras" +description = "Iskaskite 5,000 rudos bloku" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Taiklus Saulys" - description = "Padarykite 1,000 nuotolines zalos" +title = "Taiklus Saulys" +description = "Padarykite 1,000 nuotolines zalos" [advancement.challenge_ranged_dmg_10k] - title = "Mirtinas Lankininkas" - description = "Padarykite 10,000 nuotolines zalos" +title = "Mirtinas Lankininkas" +description = "Padarykite 10,000 nuotolines zalos" [advancement.challenge_ranged_dist_5k] - title = "Tolimas Nuotolis" - description = "Paleiskite sviedinius 5,000 bloku bendru atstumu" +title = "Tolimas Nuotolis" +description = "Paleiskite sviedinius 5,000 bloku bendru atstumu" [advancement.challenge_ranged_dist_50k] - title = "Mylios Saulys" - description = "Paleiskite sviedinius 50,000 bloku bendru atstumu" +title = "Mylios Saulys" +description = "Paleiskite sviedinius 50,000 bloku bendru atstumu" [advancement.challenge_ranged_kills_50] - title = "Lankininkas" - description = "Uzmuskit 50 butybes nuotoliniais ginklais" +title = "Lankininkas" +description = "Uzmuskit 50 butybes nuotoliniais ginklais" [advancement.challenge_ranged_kills_500] - title = "Lanku Meistras" - description = "Uzmuskit 500 butybes nuotoliniais ginklais" +title = "Lanku Meistras" +description = "Uzmuskit 500 butybes nuotoliniais ginklais" [advancement.challenge_longshot_25] - title = "Snaiperis" - description = "Pataikykite 25 tolimo nuotolio suvisus (per 30 bloku)" +title = "Snaiperis" +description = "Pataikykite 25 tolimo nuotolio suvisus (per 30 bloku)" [advancement.challenge_longshot_250] - title = "Erelinis Akis" - description = "Pataikykite 250 tolimo nuotolio suvisus (per 30 bloku)" +title = "Erelinis Akis" +description = "Pataikykite 250 tolimo nuotolio suvisus (per 30 bloku)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Perliu Metykas" - description = "Meskite 50 enderio perlu" +title = "Perliu Metykas" +description = "Meskite 50 enderio perlu" [advancement.challenge_rift_pearls_500] - title = "Teleportacijos Manijakas" - description = "Meskite 500 enderio perlu" +title = "Teleportacijos Manijakas" +description = "Meskite 500 enderio perlu" [advancement.challenge_rift_enderman_50] - title = "Endermenu Medžiotojas" - description = "Uzmuskit 50 endermenu" +title = "Endermenu Medžiotojas" +description = "Uzmuskit 50 endermenu" [advancement.challenge_rift_enderman_500] - title = "Tustumos Seklys" - description = "Uzmuskit 500 endermenu" +title = "Tustumos Seklys" +description = "Uzmuskit 500 endermenu" [advancement.challenge_rift_dragon_500] - title = "Drakonu Kovotojas" - description = "Padarykite 500 zalos Enderio Drakonui" +title = "Drakonu Kovotojas" +description = "Padarykite 500 zalos Enderio Drakonui" [advancement.challenge_rift_dragon_5k] - title = "Drakono Riksté" - description = "Padarykite 5,000 zalos Enderio Drakonui" +title = "Drakono Riksté" +description = "Padarykite 5,000 zalos Enderio Drakonui" [advancement.challenge_rift_crystal_10] - title = "Kristalu Lauzytoja" - description = "Sunaikinkite 10 enderio kristalu" +title = "Kristalu Lauzytoja" +description = "Sunaikinkite 10 enderio kristalu" [advancement.challenge_rift_crystal_100] - title = "Endo Griovéjas" - description = "Sunaikinkite 100 enderio kristalu" +title = "Endo Griovéjas" +description = "Sunaikinkite 100 enderio kristalu" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Zvejys" - description = "Pagaukite 25 zuvis" +title = "Zvejys" +description = "Pagaukite 25 zuvis" [advancement.challenge_fish_250] - title = "Zvejybos Meistras" - description = "Pagaukite 250 zuviu" +title = "Zvejybos Meistras" +description = "Pagaukite 250 zuviu" [advancement.challenge_drowned_25] - title = "Skandintu Mediotojas" - description = "Uzmuskit 25 skandintus" +title = "Skandintu Mediotojas" +description = "Uzmuskit 25 skandintus" [advancement.challenge_drowned_250] - title = "Vandenyno Valytojas" - description = "Uzmuskit 250 skandintu" +title = "Vandenyno Valytojas" +description = "Uzmuskit 250 skandintu" [advancement.challenge_guardian_10] - title = "Sargybinu Zabikas" - description = "Uzmuskit 10 sargybiniu" +title = "Sargybinu Zabikas" +description = "Uzmuskit 10 sargybiniu" [advancement.challenge_guardian_100] - title = "Sventyklos Plesikas" - description = "Uzmuskit 100 sargybiniu" +title = "Sventyklos Plesikas" +description = "Uzmuskit 100 sargybiniu" [advancement.challenge_underwater_blocks_100] - title = "Povandeninis Kasyklis" - description = "Sunaikinkite 100 bloku po vandeniu" +title = "Povandeninis Kasyklis" +description = "Sunaikinkite 100 bloku po vandeniu" [advancement.challenge_underwater_blocks_1k] - title = "Vandens Inzinierius" - description = "Sunaikinkite 1,000 bloku po vandeniu" +title = "Vandens Inzinierius" +description = "Sunaikinkite 1,000 bloku po vandeniu" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Nugaros Durtuvas" - description = "Padarykite 500 zalos slinkinantis" +title = "Nugaros Durtuvas" +description = "Padarykite 500 zalos slinkinantis" [advancement.challenge_stealth_dmg_5k] - title = "Tylus Zabikas" - description = "Padarykite 5,000 zalos slinkinantis" +title = "Tylus Zabikas" +description = "Padarykite 5,000 zalos slinkinantis" [advancement.challenge_stealth_kills_10] - title = "Zudikis" - description = "Uzmuskit 10 butybes slinkinantis" +title = "Zudikis" +description = "Uzmuskit 10 butybes slinkinantis" [advancement.challenge_stealth_kills_100] - title = "Seseliu Pjovéjas" - description = "Uzmuskit 100 butybes slinkinantis" +title = "Seseliu Pjovéjas" +description = "Uzmuskit 100 butybes slinkinantis" [advancement.challenge_stealth_time_1h] - title = "Kantriai" - description = "Praleiskite 1 valanda slinkinant (3,600 sekundziu)" +title = "Kantriai" +description = "Praleiskite 1 valanda slinkinant (3,600 sekundziu)" [advancement.challenge_stealth_time_10h] - title = "Seseliu Meistras" - description = "Praleiskite 10 valandu slinkinant (36,000 sekundziu)" +title = "Seseliu Meistras" +description = "Praleiskite 10 valandu slinkinant (36,000 sekundziu)" [advancement.challenge_stealth_arrows_50] - title = "Tylus Lankininkas" - description = "Paleiskite 50 strelu slinkinantis" +title = "Tylus Lankininkas" +description = "Paleiskite 50 strelu slinkinantis" [advancement.challenge_stealth_arrows_500] - title = "Vaidulaklio Lankininkas" - description = "Paleiskite 500 strelu slinkinantis" +title = "Vaidulaklio Lankininkas" +description = "Paleiskite 500 strelu slinkinantis" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Kalavijo Mokinys" - description = "Padarykite 1,000 zalos kalaviju" +title = "Kalavijo Mokinys" +description = "Padarykite 1,000 zalos kalaviju" [advancement.challenge_sword_dmg_10k] - title = "Kalavijininkas" - description = "Padarykite 10,000 zalos kalaviju" +title = "Kalavijininkas" +description = "Padarykite 10,000 zalos kalaviju" [advancement.challenge_sword_kills_50] - title = "Dvikovejas" - description = "Uzmuskit 50 butybes kalaviju" +title = "Dvikovejas" +description = "Uzmuskit 50 butybes kalaviju" [advancement.challenge_sword_kills_500] - title = "Gladiatorius" - description = "Uzmuskit 500 butybes kalaviju" +title = "Gladiatorius" +description = "Uzmuskit 500 butybes kalaviju" [advancement.challenge_sword_crit_50] - title = "Kritinis Smugis" - description = "Pataikykite 50 kritiniu smugiu kalaviju" +title = "Kritinis Smugis" +description = "Pataikykite 50 kritiniu smugiu kalaviju" [advancement.challenge_sword_crit_500] - title = "Tikslumo Meistras" - description = "Pataikykite 500 kritiniu smugiu kalaviju" +title = "Tikslumo Meistras" +description = "Pataikykite 500 kritiniu smugiu kalaviju" [advancement.challenge_sword_heavy_25] - title = "Sunkus Smugis" - description = "Pataikykite 25 sunkiu smugiu kalaviju (virs 8 zalos)" +title = "Sunkus Smugis" +description = "Pataikykite 25 sunkiu smugiu kalaviju (virs 8 zalos)" [advancement.challenge_sword_heavy_250] - title = "Niokojantis Smugis" - description = "Pataikykite 250 sunkiu smugiu kalaviju (virs 8 zalos)" +title = "Niokojantis Smugis" +description = "Pataikykite 250 sunkiu smugiu kalaviju (virs 8 zalos)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Zveriu Dresuotojas" - description = "Jusu augintiniu bendrai padaryta 500 zalos" +title = "Zveriu Dresuotojas" +description = "Jusu augintiniu bendrai padaryta 500 zalos" [advancement.challenge_pet_dmg_5k] - title = "Karo Vadovas" - description = "Jusu augintiniu bendrai padaryta 5,000 zalos" +title = "Karo Vadovas" +description = "Jusu augintiniu bendrai padaryta 5,000 zalos" [advancement.challenge_tamed_10] - title = "Gyvunu Draugas" - description = "Prisijaukinkite 10 gyvunu" +title = "Gyvunu Draugas" +description = "Prisijaukinkite 10 gyvunu" [advancement.challenge_tamed_100] - title = "Zoologijos Sodo Priziuretojas" - description = "Prisijaukinkite 100 gyvunu" +title = "Zoologijos Sodo Priziuretojas" +description = "Prisijaukinkite 100 gyvunu" [advancement.challenge_pet_kills_25] - title = "Bandos Taktika" - description = "Jusu augintiniai uzmusé 25 padarus" +title = "Bandos Taktika" +description = "Jusu augintiniai uzmusé 25 padarus" [advancement.challenge_pet_kills_250] - title = "Alfos Vadas" - description = "Jusu augintiniai uzmusé 250 padaru" +title = "Alfos Vadas" +description = "Jusu augintiniai uzmusé 250 padaru" [advancement.challenge_taming_2500] - title = "Veisimo Ekspertas" - description = "Paveiskite 2,500 gyvunu" +title = "Veisimo Ekspertas" +description = "Paveiskite 2,500 gyvunu" [advancement.challenge_taming_25k] - title = "Genetikos Meistras" - description = "Paveiskite 25,000 gyvunu" +title = "Genetikos Meistras" +description = "Paveiskite 25,000 gyvunu" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Musimo Maisas" - description = "Gaukite 500 smugiu" +title = "Musimo Maisas" +description = "Gaukite 500 smugiu" [advancement.challenge_trag_hits_5k] - title = "Kanciniu Megejas" - description = "Gaukite 5,000 smugiu" +title = "Kanciniu Megejas" +description = "Gaukite 5,000 smugiu" [advancement.challenge_trag_deaths_10] - title = "Devyni Gyvenimai" - description = "Mirkite 10 kartu" +title = "Devyni Gyvenimai" +description = "Mirkite 10 kartu" [advancement.challenge_trag_deaths_100] - title = "Pasikartojantis Kosmeras" - description = "Mirkite 100 kartu" +title = "Pasikartojantis Kosmeras" +description = "Mirkite 100 kartu" [advancement.challenge_trag_fire_500] - title = "Ugnis Auka" - description = "Isgyvenkit 500 ugnies zalos" +title = "Ugnis Auka" +description = "Isgyvenkit 500 ugnies zalos" [advancement.challenge_trag_fire_5k] - title = "Feniksas" - description = "Isgyvenkit 5,000 ugnies zalos" +title = "Feniksas" +description = "Isgyvenkit 5,000 ugnies zalos" [advancement.challenge_trag_fall_500] - title = "Gravitacijos Patikrinimas" - description = "Isgyvenkit 500 kritimo zalos" +title = "Gravitacijos Patikrinimas" +description = "Isgyvenkit 500 kritimo zalos" [advancement.challenge_trag_fall_5k] - title = "Galutinis Greitis" - description = "Isgyvenkit 5,000 kritimo zalos" +title = "Galutinis Greitis" +description = "Isgyvenkit 5,000 kritimo zalos" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Mustynes" - description = "Padarykite 1,000 zalos plikemis rankomis" +title = "Mustynes" +description = "Padarykite 1,000 zalos plikemis rankomis" [advancement.challenge_unarmed_dmg_10k] - title = "Koviniu Menu Meistras" - description = "Padarykite 10,000 zalos plikemis rankomis" +title = "Koviniu Menu Meistras" +description = "Padarykite 10,000 zalos plikemis rankomis" [advancement.challenge_unarmed_kills_25] - title = "Plikos Kumstys" - description = "Uzmuskit 25 butybes plikemis rankomis" +title = "Plikos Kumstys" +description = "Uzmuskit 25 butybes plikemis rankomis" [advancement.challenge_unarmed_kills_250] - title = "Legendos Kumstis" - description = "Uzmuskit 250 butybes plikemis rankomis" +title = "Legendos Kumstis" +description = "Uzmuskit 250 butybes plikemis rankomis" [advancement.challenge_unarmed_crit_25] - title = "Kritinis Smugis Kumstimi" - description = "Pataikykite 25 kritiniu smugiu plikemis rankomis" +title = "Kritinis Smugis Kumstimi" +description = "Pataikykite 25 kritiniu smugiu plikemis rankomis" [advancement.challenge_unarmed_crit_250] - title = "Tiksli Kumstis" - description = "Pataikykite 250 kritiniu smugiu plikemis rankomis" +title = "Tiksli Kumstis" +description = "Pataikykite 250 kritiniu smugiu plikemis rankomis" [advancement.challenge_unarmed_heavy_25] - title = "Galingas Smugis" - description = "Pataikykite 25 sunkiu smugiu plikemis rankomis (virs 6 zalos)" +title = "Galingas Smugis" +description = "Pataikykite 25 sunkiu smugiu plikemis rankomis (virs 6 zalos)" [advancement.challenge_unarmed_heavy_250] - title = "Nokautu Karalius" - description = "Pataikykite 250 sunkiu smugiu plikemis rankomis (virs 6 zalos)" +title = "Nokautu Karalius" +description = "Pataikykite 250 sunkiu smugiu plikemis rankomis (virs 6 zalos)" # items [items] [items.bound_ender_peral] - name = "Relikvijoriaus Prievado Raktas" - usage1 = "Shift + Kairys paspaudimas, kad susieti" - usage2 = "Desinys paspaudimas, kad pasiektumete susieta inventoriu" +name = "Relikvijoriaus Prievado Raktas" +usage1 = "Shift + Kairys paspaudimas, kad susieti" +usage2 = "Desinys paspaudimas, kad pasiektumete susieta inventoriu" [items.bound_eye_of_ender] - name = "Akiu Inkaras" - usage1 = "Desinys Paspaudimas, kad vartoti ir teleportuotis i susieta vieta" - usage2 = "Shift + Kairys paspaudimas, kad susieti prie bloko" +name = "Akiu Inkaras" +usage1 = "Desinys Paspaudimas, kad vartoti ir teleportuotis i susieta vieta" +usage2 = "Shift + Kairys paspaudimas, kad susieti prie bloko" [items.bound_redstone_torch] - name = "Raudonakmenio Valdymo Pultas" - usage1 = "Desinys Paspaudimas, kad sukurti 1-tick raudonakmenio impulsa" - usage2 = "Shift + Kairys paspaudimas ant tikslaus bloko, kad butu galima susieti" +name = "Raudonakmenio Valdymo Pultas" +usage1 = "Desinys Paspaudimas, kad sukurti 1-tick raudonakmenio impulsa" +usage2 = "Shift + Kairys paspaudimas ant tikslaus bloko, kad butu galima susieti" [items.bound_snowball] - name = "Voratinkliu Spastai!" - usage1 = "Meskite, kad vietoje sukurtumete laikinus voratinkliu spastus" +name = "Voratinkliu Spastai!" +usage1 = "Meskite, kad vietoje sukurtumete laikinus voratinkliu spastus" [items.chrono_time_bottle] - name = "Laikas Butelyje" - usage1 = "Pasyviai kaupia laika buredamas jusu inventoriuje" - usage2 = "Desinys paspaudimas ant laiko bloku ar mazyliu gyvunu, kad isnaudotumete sukapta laika" - stored = "Sukauptas Laikas" +name = "Laikas Butelyje" +usage1 = "Pasyviai kaupia laika buredamas jusu inventoriuje" +usage2 = "Desinys paspaudimas ant laiko bloku ar mazyliu gyvunu, kad isnaudotumete sukapta laika" +stored = "Sukauptas Laikas" [items.chrono_time_bomb] - name = "Laiko Bomba" - usage1 = "Desinys paspaudimas, kad paleistumete chrono sviedini, kuris sukuria laikini lauka" +name = "Laiko Bomba" +usage1 = "Desinys paspaudimas, kad paleistumete chrono sviedini, kuris sukuria laikini lauka" [items.elevator_block] - name = "Lifto Blokas" - usage1 = "Persokti, kad teleportuotis aukstyn" - usage2 = "Shift, kad teleportuotumetes zemyn" - usage3 = "Maziausiai 2 oro blokai tarp liftu" +name = "Lifto Blokas" +usage1 = "Persokti, kad teleportuotis aukstyn" +usage2 = "Shift, kad teleportuotumetes zemyn" +usage3 = "Maziausiai 2 oro blokai tarp liftu" # snippets [snippets] [snippets.gui] - level = "Lygis" - knowledge = "zinios" - power_used = "Galios" - not_learned = "Neismokta" - xp = "XP iki" - welcome = "Sveiki!" - welcome_back = "Sveiki sugriže!" - xp_bonus_for_time = "XP uz" - max_ability_power = "Didziausia Gebejimu Galia" - unlock_this_by_clicking = "Atrakinkite tai paspausdami desiniu peles mygtuku: " - back = "Grizti" - unlearn_all = "Pamirsti viska" - unlearned_all = "Viskas pamirsta" +level = "Lygis" +knowledge = "zinios" +power_used = "Galios" +not_learned = "Neismokta" +xp = "XP iki" +welcome = "Sveiki!" +welcome_back = "Sveiki sugriže!" +xp_bonus_for_time = "XP uz" +max_ability_power = "Didziausia Gebejimu Galia" +unlock_this_by_clicking = "Atrakinkite tai paspausdami desiniu peles mygtuku: " +back = "Grizti" +unlearn_all = "Pamirsti viska" +unlearned_all = "Viskas pamirsta" [snippets.adapt_menu] - may_not_unlearn = "JUS NEGALESITE PAMIRSTI" - may_unlearn = "GALITE ISMOKTI/PAMIRSTI" - knowledge_cost = "Ziniu kaina" - knowledge_available = "Zinios Prieinama" - already_learned = "Jau ismokta" - unlearn_refund = "Spausk, kad pamirstum & Atlygintu" - no_refunds = "HARDCORE, GRAZINIMAS ISJUNGTAS" - knowledge = "ziniu" - click_learn = "Spausk, kad ismoktum" - no_knowledge = "(Jus neturite pakankamai ziniu)" - you_only_have = "Tu turi tik" - how_to_level_up = "Pasikelkite igudzius, kad padidintumete maksimalia galia." - not_enough_power = "Nepakanka galios! Kiekvienas Gebejimu lygis kainuoja 1 galia." - power = "galia" - power_drain = "Galios Eikvavimas" - learned = "Ismokta " - unlearned = "Pamirsta " - activator_block = "Knygu Lentyna" +may_not_unlearn = "JUS NEGALESITE PAMIRSTI" +may_unlearn = "GALITE ISMOKTI/PAMIRSTI" +knowledge_cost = "Ziniu kaina" +knowledge_available = "Zinios Prieinama" +already_learned = "Jau ismokta" +unlearn_refund = "Spausk, kad pamirstum & Atlygintu" +no_refunds = "HARDCORE, GRAZINIMAS ISJUNGTAS" +knowledge = "ziniu" +click_learn = "Spausk, kad ismoktum" +no_knowledge = "(Jus neturite pakankamai ziniu)" +you_only_have = "Tu turi tik" +how_to_level_up = "Pasikelkite igudzius, kad padidintumete maksimalia galia." +not_enough_power = "Nepakanka galios! Kiekvienas Gebejimu lygis kainuoja 1 galia." +power = "galia" +power_drain = "Galios Eikvavimas" +learned = "Ismokta " +unlearned = "Pamirsta " +activator_block = "Knygu Lentyna" [snippets.knowledge_orb] - contains = "yra" - knowledge = "ziniu" - rightclick = "Desinys-paspaudimas" - togainknowledge = ", kad igyti siu ziniu" - knowledge_orb = "Ziniu Rutulys" +contains = "yra" +knowledge = "ziniu" +rightclick = "Desinys-paspaudimas" +togainknowledge = ", kad igyti siu ziniu" +knowledge_orb = "Ziniu Rutulys" [snippets.experience_orb] - contains = "yra" - xp = "Patirtis" - rightclick = "Desinys-paspaudimas" - togainxp = "igyti sios patirties" - xporb = "Patirties Rutulys" +contains = "yra" +xp = "Patirtis" +rightclick = "Desinys-paspaudimas" +togainxp = "igyti sios patirties" +xporb = "Patirties Rutulys" # skill [skill] [skill.agility] - name = "Judrumas" - icon = "⇉" - description = "Judrumas - tai gebejimas greitai ir sklandziai judeti susiduris su kliutimis." +name = "Judrumas" +icon = "⇉" +description = "Judrumas - tai gebejimas greitai ir sklandziai judeti susiduris su kliutimis." [skill.architect] - name = "Architektas" - icon = "⬧" - description = "Strukturos yra pasaulio statybiniai blokai. Realybe yra jusu rankose, jus turite tai valdyti." +name = "Architektas" +icon = "⬧" +description = "Strukturos yra pasaulio statybiniai blokai. Realybe yra jusu rankose, jus turite tai valdyti." [skill.axes] - name = "Medkirtyste" - icon = "🪓" - description1 = "Kam kirsti medzius, kai gali kirsti " - description2 = "dalykus" - description3 = "vietoj to vistiek tas pats dalykas!" +name = "Medkirtyste" +icon = "🪓" +description1 = "Kam kirsti medzius, kai gali kirsti " +description2 = "dalykus" +description3 = "vietoj to vistiek tas pats dalykas!" [skill.brewing] - name = "Vyrimas" - icon = "❦" - description = "Dvigubas burbulas, Trigubas burbulas, Keturgubas burbulas - as vis tiek dar negaliu ipilti sio gerimo i katila" +name = "Vyrimas" +icon = "❦" +description = "Dvigubas burbulas, Trigubas burbulas, Keturgubas burbulas - as vis tiek dar negaliu ipilti sio gerimo i katila" [skill.blocking] - name = "Blokavimas" - icon = "🛡" - description = "Lazdos ir akmenys tau kaulu nesulauzys, bet skydas sulauzys." +name = "Blokavimas" +icon = "🛡" +description = "Lazdos ir akmenys tau kaulu nesulauzys, bet skydas sulauzys." [skill.crafting] - name = "Gamyba" - icon = "⌂" - description = "Jei nebeliko resursu, kuriuos butu galima ideti, kodel gi nepadarius kitu?" +name = "Gamyba" +icon = "⌂" +description = "Jei nebeliko resursu, kuriuos butu galima ideti, kodel gi nepadarius kitu?" [skill.discovery] - name = "Aptikimas" - icon = "⚛" - description = "Kai jusu suvokimas pleciasi, jusu protas issiskleidzia ir atranda tai, ko nepadarete." +name = "Aptikimas" +icon = "⚛" +description = "Kai jusu suvokimas pleciasi, jusu protas issiskleidzia ir atranda tai, ko nepadarete." [skill.enchanting] - name = "Kerejimas" - icon = "♰" - description = "Ka tu darai? Pranasystes, regejimai, prietaringi slamstai?" +name = "Kerejimas" +icon = "♰" +description = "Ka tu darai? Pranasystes, regejimai, prietaringi slamstai?" [skill.excavation] - name = "Kasinejimas" - icon = "ᛳ" - description = "Kasam Kasam Duobe..." +name = "Kasinejimas" +icon = "ᛳ" +description = "Kasam Kasam Duobe..." [skill.herbalism] - name = "Zolininkуste" - icon = "⚘" - description = "As nerandu augalu, bet galiu rasti keleta seklu ir... ar tai... Piktzole?" +name = "Zolininkуste" +icon = "⚘" +description = "As nerandu augalu, bet galiu rasti keleta seklu ir... ar tai... Piktzole?" [skill.hunter] - name = "Medziotojas" - icon = "☠" - description = "Medzioklе - tai kelione, o ne rezultatas." +name = "Medziotojas" +icon = "☠" +description = "Medzioklе - tai kelione, o ne rezultatas." [skill.nether] - name = "Pragaras" - icon = "₪" - description = "Is paties pragaro gelmiu." +name = "Pragaras" +icon = "₪" +description = "Is paties pragaro gelmiu." [skill.pickaxe] - name = "Kirtiklis" - icon = "⛏" - description = "Nykstukай yra kalnakasiai, bet as ismokau daiktu ar du per savo laika. AS SVEDAS" +name = "Kirtiklis" +icon = "⛏" +description = "Nykstukай yra kalnakasiai, bet as ismokau daiktu ar du per savo laika. AS SVEDAS" [skill.ranged] - name = "Nuotolis" - icon = "🏹" - description = "Atstumas yra raktas i pergale ir raktas i islikima." +name = "Nuotolis" +icon = "🏹" +description = "Atstumas yra raktas i pergale ir raktas i islikima." [skill.rift] - name = "Plyšys" - icon = "❍" - description = "Plyšys yra edanti jungtis, bet jus sutramdete ta jungti." +name = "Plyšys" +icon = "❍" +description = "Plyšys yra edanti jungtis, bet jus sutramdete ta jungti." [skill.seaborne] - name = "Jurininkas" - icon = "🎣" - description = "Turedami si igudi galesite patirti vandens stebuklus." +name = "Jurininkas" +icon = "🎣" +description = "Turedami si igudi galesite patirti vandens stebuklus." [skill.stealth] - name = "Slaptumas" - icon = "☯" - description = "Nematomo menas. Vaiksciokite seselyje." +name = "Slaptumas" +icon = "☯" +description = "Nematomo menas. Vaiksciokite seselyje." [skill.swords] - name = "Kardai" - icon = "⚔" - description = "Pilko Akmens galia!" +name = "Kardai" +icon = "⚔" +description = "Pilko Akmens galia!" [skill.taming] - name = "Prisijaukinimas" - icon = "♥" - description = "Papugos ir bites... ir tu?" +name = "Prisijaukinimas" +icon = "♥" +description = "Papugos ir bites... ir tu?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Kraujas teka visatos gyslomis. Suspaustas tavo rankomis." +name = "TragOul" +icon = "🗡" +description = "Kraujas teka visatos gyslomis. Suspaustas tavo rankomis." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Sukite visatos laikrodi, patirkite tekme. Sudauzyk laikrodi, tapk juo." +name = "Chronos" +icon = "🕒" +description = "Sukite visatos laikrodi, patirkite tekme. Sudauzyk laikrodi, tapk juo." [skill.unarmed] - name = "Beginklis" - icon = "»" - description = "Be ginklo nebunate be jegos." +name = "Beginklis" +icon = "»" +description = "Be ginklo nebunate be jegos." # agility [agility] [agility.armor_up] - name = "Sarvai Aukstyn" - description = "Kuo ilgiau begsite, tuo daugiau sarvu!" - lore1 = "Max Sarvai" - lore2 = "Sarvai Aukstyn laikas" - lore = ["Max Sarvai", "Sarvai Aukstyn laikas"] +name = "Sarvai Aukstyn" +description = "Kuo ilgiau begsite, tuo daugiau sarvu!" +lore1 = "Max Sarvai" +lore2 = "Sarvai Aukstyn laikas" +lore = ["Max Sarvai", "Sarvai Aukstyn laikas"] [agility.ladder_slide] - name = "Slydimas Kopečiomis" - description = "Lipkite ir slyskit kopečiomis daug greičiau abiem kryptimis." - lore1 = "Kopečiu greicio daugiklis" - lore2 = "Greito nusileidimo greitis" - lore = ["Kopečiu greicio daugiklis", "Greito nusileidimo greitis"] +name = "Slydimas Kopečiomis" +description = "Lipkite ir slyskit kopečiomis daug greičiau abiem kryptimis." +lore1 = "Kopečiu greicio daugiklis" +lore2 = "Greito nusileidimo greitis" +lore = ["Kopečiu greicio daugiklis", "Greito nusileidimo greitis"] [agility.super_jump] - name = "Super Suolis" - description = "Isskirtinis auksčio pranasumas." - lore1 = "Maksimalus suolio aukstis" - lore2 = "Selinti + Pasokti, kad panaudoti super suoli!" - lore = ["Maksimalus suolio aukstis", "Selinti + Pasokti, kad panaudoti super suoli!"] +name = "Super Suolis" +description = "Isskirtinis auksčio pranasumas." +lore1 = "Maksimalus suolio aukstis" +lore2 = "Selinti + Pasokti, kad panaudoti super suoli!" +lore = ["Maksimalus suolio aukstis", "Selinti + Pasokti, kad panaudoti super suoli!"] [agility.wall_jump] - name = "Sienos Suolis" - description = "Laikykite shift, kol ore nuo sienos iki sienos ir sokinekite!" - lore1 = "Max Suoliai" - lore2 = "Suolio aukstis" - lore = ["Max Suoliai", "Suolio aukstis"] +name = "Sienos Suolis" +description = "Laikykite shift, kol ore nuo sienos iki sienos ir sokinekite!" +lore1 = "Max Suoliai" +lore2 = "Suolio aukstis" +lore = ["Max Suoliai", "Suolio aukstis"] [agility.wind_up] - name = "Greitis" - description = "Kuo ilgiau begsit, beksite greiciau!" - lore1 = "Max Greitis" - lore2 = "Uzbaigimo laikas" - lore = ["Max Greitis", "Uzbaigimo laikas"] +name = "Greitis" +description = "Kuo ilgiau begsit, beksite greiciau!" +lore1 = "Max Greitis" +lore2 = "Uzbaigimo laikas" +lore = ["Max Greitis", "Uzbaigimo laikas"] # architect [architect] [architect.elevator] - name = "Liftas" - description = "Tai leidzia jums pastatyti lifta, kuris greitai leis teleportuotis vertikaliai!" - lore1 = "Atrakina lifto recepta: X=VILNA, Y=ENDERIO PERLAS" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Atrakina lifto recepta: X=VILNA, Y=ENDERIO PERLAS", "XXX", "XYX", "XXX"] +name = "Liftas" +description = "Tai leidzia jums pastatyti lifta, kuris greitai leis teleportuotis vertikaliai!" +lore1 = "Atrakina lifto recepta: X=VILNA, Y=ENDERIO PERLAS" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Atrakina lifto recepta: X=VILNA, Y=ENDERIO PERLAS", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Magiskas Fondas" - description = "Tai leidzia selinti ir pasideti po savimi laikina pagrinda!" - lore1 = "Magiskai sukurti: " - lore2 = "Blokai po tavimi!" - lore = ["Magiskai sukurti: ", "Blokai po tavimi!"] +name = "Magiskas Fondas" +description = "Tai leidzia selinti ir pasideti po savimi laikina pagrinda!" +lore1 = "Magiskai sukurti: " +lore2 = "Blokai po tavimi!" +lore = ["Magiskai sukurti: ", "Blokai po tavimi!"] [architect.glass] - name = "Silko Jutiklinis Stiklas" - description = "Tai leidzia is esmes isvengti stiklo blokeliu praradimo, kai juos sulauzуsite tuscia ranka!" - lore1 = "Jusu rankos igauna silko prisilietima stiklui" - lore = ["Jusu rankos igauna silko prisilietima stiklui"] +name = "Silko Jutiklinis Stiklas" +description = "Tai leidzia is esmes isvengti stiklo blokeliu praradimo, kai juos sulauzуsite tuscia ranka!" +lore1 = "Jusu rankos igauna silko prisilietima stiklui" +lore = ["Jusu rankos igauna silko prisilietima stiklui"] [architect.wireless_redstone] - name = "Raudonakmenio Valdymo Pultas" - description = "Tai leidzia naudoti raudonakmenio degla, kad perjungtumete Raudonakmeni nuotoliniu budu!" - lore1 = "Tikslas + Raudonakmenio Deglas + Enderio Perlas = 1 Raudonakmenio Nuotolinio Valdymo Pultas" - lore = ["Tikslas + Raudonakmenio Deglas + Enderio Perlas = 1 Raudonakmenio Nuotolinio Valdymo Pultas"] +name = "Raudonakmenio Valdymo Pultas" +description = "Tai leidzia naudoti raudonakmenio degla, kad perjungtumete Raudonakmeni nuotoliniu budu!" +lore1 = "Tikslas + Raudonakmenio Deglas + Enderio Perlas = 1 Raudonakmenio Nuotolinio Valdymo Pultas" +lore = ["Tikslas + Raudonakmenio Deglas + Enderio Perlas = 1 Raudonakmenio Nuotolinio Valdymo Pultas"] [architect.placement] - name = "Statybininku Lazdele" - description = "Leidzia deti kelis blokus vienu metu, kad suaktyvintumete spauskite shift, ir dekite toki pati bloka i kuri ziurite! Atminkite, kad gali tekti siek tiek pajudeti, kad suaktyvintumete langeliu apribojima!" - lore1 = "Tau reikia" - lore2 = "bloku, kad statyti" - lore3 = "Medziagu statybininku lazdele" - lore = ["Tau reikia", "bloku, kad statyti", "Medziagu statybininku lazdele"] +name = "Statybininku Lazdele" +description = "Leidzia deti kelis blokus vienu metu, kad suaktyvintumete spauskite shift, ir dekite toki pati bloka i kuri ziurite! Atminkite, kad gali tekti siek tiek pajudeti, kad suaktyvintumete langeliu apribojima!" +lore1 = "Tau reikia" +lore2 = "bloku, kad statyti" +lore3 = "Medziagu statybininku lazdele" +lore = ["Tau reikia", "bloku, kad statyti", "Medziagu statybininku lazdele"] # axe [axe] [axe.chop] - name = "Kapotojas" - description = "Nukirskite medzius desiniu peles mygtuku spausdami ant pagrindinio rasto!" - lore1 = "Blokai per nukirtima" - lore2 = "Kirtimo laiko tarpas" - lore3 = "Irankio susidevejimas" - lore = ["Blokai per nukirtima", "Kirtimo laiko tarpas", "Irankio susidevejimas"] +name = "Kapotojas" +description = "Nukirskite medzius desiniu peles mygtuku spausdami ant pagrindinio rasto!" +lore1 = "Blokai per nukirtima" +lore2 = "Kirtimo laiko tarpas" +lore3 = "Irankio susidevejimas" +lore = ["Blokai per nukirtima", "Kirtimo laiko tarpas", "Irankio susidevejimas"] [axe.log_swap] - name = "Liucijos Rastu Keitiklis" - description = "Pakeiskite rastu tipa darbastalyje!" - lore1 = "8 betkokio tipo rastai + 1 sodinukas = 8 sodinuko tipo rastas" - lore = ["8 betkokio tipo rastai + 1 sodinukas = 8 sodinuko tipo rastas"] +name = "Liucijos Rastu Keitiklis" +description = "Pakeiskite rastu tipa darbastalyje!" +lore1 = "8 betkokio tipo rastai + 1 sodinukas = 8 sodinuko tipo rastas" +lore = ["8 betkokio tipo rastai + 1 sodinukas = 8 sodinuko tipo rastas"] [axe.drop_to_inventory] - name = "Kirvio Trauka" +name = "Kirvio Trauka" [axe.ground_smash] - name = "Zemes Drebejimas" - description = "Pasokite, tada pritupkite ir sudauzykite visus netoliese esancius priesus." - lore1 = "Zala" - lore2 = "Bloku spindulys" - lore3 = "Jega" - lore4 = "Drebejimo laiko tarpas" - lore = ["Zala", "Bloku spindulys", "Jega", "Drebejimo laiko tarpas"] +name = "Zemes Drebejimas" +description = "Pasokite, tada pritupkite ir sudauzykite visus netoliese esancius priesus." +lore1 = "Zala" +lore2 = "Bloku spindulys" +lore3 = "Jega" +lore4 = "Drebejimo laiko tarpas" +lore = ["Zala", "Bloku spindulys", "Jega", "Drebejimo laiko tarpas"] [axe.leaf_miner] - name = "Lapu Kirtejas" - description = "Leidzia vienu metu sudraskyti masinius lapus!" - lore1 = "Selink, ir kirsk LAPUS" - lore2 = "lapu kirtimo spindulys" - lore3 = "Negausite dropu nuo lapu (Exploit prevencija)" - lore = ["Selink, ir kirsk LAPUS", "lapu kirtimo spindulys", "Negausite dropu nuo lapu (Exploit prevencija)"] +name = "Lapu Kirtejas" +description = "Leidzia vienu metu sudraskyti masinius lapus!" +lore1 = "Selink, ir kirsk LAPUS" +lore2 = "lapu kirtimo spindulys" +lore3 = "Negausite dropu nuo lapu (Exploit prevencija)" +lore = ["Selink, ir kirsk LAPUS", "lapu kirtimo spindulys", "Negausite dropu nuo lapu (Exploit prevencija)"] [axe.wood_miner] - name = "Medienos Kirtejas" - description = "Leidzia is karto nukirsti masini kieki medienos!" - lore1 = "Selink, ir kirsk mediena ( Ne Lentas )" - lore2 = "Medienos kirtimo spindulys" - lore3 = "Veikia kartu su Kirvio Trauka" - lore = ["Selink, ir kirsk mediena ( Ne Lentas )", "Medienos kirtimo spindulys", "Veikia kartu su Kirvio Trauka"] +name = "Medienos Kirtejas" +description = "Leidzia is karto nukirsti masini kieki medienos!" +lore1 = "Selink, ir kirsk mediena ( Ne Lentas )" +lore2 = "Medienos kirtimo spindulys" +lore3 = "Veikia kartu su Kirvio Trauka" +lore = ["Selink, ir kirsk mediena ( Ne Lentas )", "Medienos kirtimo spindulys", "Veikia kartu su Kirvio Trauka"] # brewing [brewing] [brewing.lingering] - name = "Uzsiteses Vyrimas" - description = "Paruosti eliksyrai issilaiko ilgiau!" - lore1 = "Trukme" - lore2 = "Trukme" - lore = ["Trukme", "Trukme"] +name = "Uzsiteses Vyrimas" +description = "Paruosti eliksyrai issilaiko ilgiau!" +lore1 = "Trukme" +lore2 = "Trukme" +lore = ["Trukme", "Trukme"] [brewing.super_heated] - name = "Super Ikaites Stovas" - description = "Eliksyru stovai dirba greiciau, kai yra kuo karstesni." - lore1 = "Uz Priliesta Ugnies Bloka" - lore2 = "Uz Priliesta Lavos Bloka" - lore = ["Uz Priliesta Ugnies Bloka", "Uz Priliesta Lavos Bloka"] +name = "Super Ikaites Stovas" +description = "Eliksyru stovai dirba greiciau, kai yra kuo karstesni." +lore1 = "Uz Priliesta Ugnies Bloka" +lore2 = "Uz Priliesta Lavos Bloka" +lore = ["Uz Priliesta Ugnies Bloka", "Uz Priliesta Lavos Bloka"] [brewing.darkness] - name = "Buteliu Tamsa" - description = "Nezinome, kodel jums to reikia, bet stai!" - lore1 = "Nakcies Matymo Eliksyras + Juodas Betonas = Tamsos Eliksyras (30 sekundziu)" - lore2 = "Reiketu pazymeti, kad tai neleidzia zaideju beti greitai!" - lore = ["Nakcies Matymo Eliksyras + Juodas Betonas = Tamsos Eliksyras (30 sekundziu)", "Reiketu pazymeti, kad tai neleidzia zaideju beti greitai!"] +name = "Buteliu Tamsa" +description = "Nezinome, kodel jums to reikia, bet stai!" +lore1 = "Nakcies Matymo Eliksyras + Juodas Betonas = Tamsos Eliksyras (30 sekundziu)" +lore2 = "Reiketu pazymeti, kad tai neleidzia zaideju beti greitai!" +lore = ["Nakcies Matymo Eliksyras + Juodas Betonas = Tamsos Eliksyras (30 sekundziu)", "Reiketu pazymeti, kad tai neleidzia zaideju beti greitai!"] [brewing.haste] - name = "Greitas Kasimas" - description = "Kai efektyvumo nepakanka" - lore1 = "Greicio eliksyras + ametisto suke = greito kasimo eliksyras (60 sekundziu)" - lore2 = "Greicio eliksyras + ametisto blokas = greito kasimo eliksyras-2 (30 sekundziu)" - lore = ["Greicio eliksyras + ametisto suke = greito kasimo eliksyras (60 sekundziu)", "Greicio eliksyras + ametisto blokas = greito kasimo eliksyras-2 (30 sekundziu)"] +name = "Greitas Kasimas" +description = "Kai efektyvumo nepakanka" +lore1 = "Greicio eliksyras + ametisto suke = greito kasimo eliksyras (60 sekundziu)" +lore2 = "Greicio eliksyras + ametisto blokas = greito kasimo eliksyras-2 (30 sekundziu)" +lore = ["Greicio eliksyras + ametisto suke = greito kasimo eliksyras (60 sekundziu)", "Greicio eliksyras + ametisto blokas = greito kasimo eliksyras-2 (30 sekundziu)"] [brewing.absorption] - name = "Absorbcija" - description = "Grudinti kuna!" - lore1 = "Momentinis gydymas + kvarcas = absorbcijos eliksyras (60 sekundziu)" - lore2 = "Momentinis gydymas + kvarco blokas = absorbcijos eliksyras-2 (30 sekundziu)" - lore = ["Momentinis gydymas + kvarcas = absorbcijos eliksyras (60 sekundziu)", "Momentinis gydymas + kvarco blokas = absorbcijos eliksyras-2 (30 sekundziu)"] +name = "Absorbcija" +description = "Grudinti kuna!" +lore1 = "Momentinis gydymas + kvarcas = absorbcijos eliksyras (60 sekundziu)" +lore2 = "Momentinis gydymas + kvarco blokas = absorbcijos eliksyras-2 (30 sekundziu)" +lore = ["Momentinis gydymas + kvarcas = absorbcijos eliksyras (60 sekundziu)", "Momentinis gydymas + kvarco blokas = absorbcijos eliksyras-2 (30 sekundziu)"] [brewing.fatigue] - name = "Nuovargis" - description = "Susilpninkite kuna!" - lore1 = "Silpnumo Eliksyras + Gleiviu Kamuolys = Nuovargio Eliksyras (30 sekundziu)" - lore2 = "Silpnumo Eliksyras + Gleiviu Blokas = Nuovargio Eliksyras-2 (15 sekundziu)" - lore = ["Silpnumo Eliksyras + Gleiviu Kamuolys = Nuovargio Eliksyras (30 sekundziu)", "Silpnumo Eliksyras + Gleiviu Blokas = Nuovargio Eliksyras-2 (15 sekundziu)"] +name = "Nuovargis" +description = "Susilpninkite kuna!" +lore1 = "Silpnumo Eliksyras + Gleiviu Kamuolys = Nuovargio Eliksyras (30 sekundziu)" +lore2 = "Silpnumo Eliksyras + Gleiviu Blokas = Nuovargio Eliksyras-2 (15 sekundziu)" +lore = ["Silpnumo Eliksyras + Gleiviu Kamuolys = Nuovargio Eliksyras (30 sekundziu)", "Silpnumo Eliksyras + Gleiviu Blokas = Nuovargio Eliksyras-2 (15 sekundziu)"] [brewing.hunger] - name = "Badas" - description = "Maitinkite nepasotinamaji!" - lore1 = "Keistas Eliksyras + Supuvusi Mesa = Bado Eliksyras (30 sekundziu)" - lore2 = "Silpnumo Eliksyras + Supuvusi Mesa = Bado Eliksyras-3 (15 sekundziu)" - lore = ["Keistas Eliksyras + Supuvusi Mesa = Bado Eliksyras (30 sekundziu)", "Silpnumo Eliksyras + Supuvusi Mesa = Bado Eliksyras-3 (15 sekundziu)"] +name = "Badas" +description = "Maitinkite nepasotinamaji!" +lore1 = "Keistas Eliksyras + Supuvusi Mesa = Bado Eliksyras (30 sekundziu)" +lore2 = "Silpnumo Eliksyras + Supuvusi Mesa = Bado Eliksyras-3 (15 sekundziu)" +lore = ["Keistas Eliksyras + Supuvusi Mesa = Bado Eliksyras (30 sekundziu)", "Silpnumo Eliksyras + Supuvusi Mesa = Bado Eliksyras-3 (15 sekundziu)"] [brewing.nausea] - name = "Pykinimas" - description = "Man nuo taves bloga!" - lore1 = "Keistas Eliksyras + Rudas Grybas = Pykinimo Eliksyras (16 sekundziu)" - lore2 = "Keistas Eliksyras + Raudonasis Grybas (Pragaro) = Pykinimo Eliksyras-2 (8 sekundziu)" - lore = ["Keistas Eliksyras + Rudas Grybas = Pykinimo Eliksyras (16 sekundziu)", "Keistas Eliksyras + Raudonasis Grybas (Pragaro) = Pykinimo Eliksyras-2 (8 sekundziu)"] +name = "Pykinimas" +description = "Man nuo taves bloga!" +lore1 = "Keistas Eliksyras + Rudas Grybas = Pykinimo Eliksyras (16 sekundziu)" +lore2 = "Keistas Eliksyras + Raudonasis Grybas (Pragaro) = Pykinimo Eliksyras-2 (8 sekundziu)" +lore = ["Keistas Eliksyras + Rudas Grybas = Pykinimo Eliksyras (16 sekundziu)", "Keistas Eliksyras + Raudonasis Grybas (Pragaro) = Pykinimo Eliksyras-2 (8 sekundziu)"] [brewing.blindness] - name = "Aklumas" - description = "Tu esi baisus zmogus..." - lore1 = "Keistas Eliksyras + Rasalo Maisas = Aklumo Eliksyras (30 sekundziu)" - lore2 = "Keistas Eliksyras + Svytintis Rasalo Maisas = Aklumo Eliksyras-2 (15 sekundziu)" - lore = ["Keistas Eliksyras + Rasalo Maisas = Aklumo Eliksyras (30 sekundziu)", "Keistas Eliksyras + Svytintis Rasalo Maisas = Aklumo Eliksyras-2 (15 sekundziu)"] +name = "Aklumas" +description = "Tu esi baisus zmogus..." +lore1 = "Keistas Eliksyras + Rasalo Maisas = Aklumo Eliksyras (30 sekundziu)" +lore2 = "Keistas Eliksyras + Svytintis Rasalo Maisas = Aklumo Eliksyras-2 (15 sekundziu)" +lore = ["Keistas Eliksyras + Rasalo Maisas = Aklumo Eliksyras (30 sekundziu)", "Keistas Eliksyras + Svytintis Rasalo Maisas = Aklumo Eliksyras-2 (15 sekundziu)"] [brewing.resistance] - name = "Atsparumas" - description = "Itvirtinimai geriausiu budu!" - lore1 = "Keistas Eliksyras + Gelezies Liejinys = Atsparumo Eliksyras (60 sekundziu)" - lore2 = "Keistas Eliksyras + Gelezies Blokas = Atsparumo Eliksyras-2 (30 sekundziu)" - lore = ["Keistas Eliksyras + Gelezies Liejinys = Atsparumo Eliksyras (60 sekundziu)", "Keistas Eliksyras + Gelezies Blokas = Atsparumo Eliksyras-2 (30 sekundziu)"] +name = "Atsparumas" +description = "Itvirtinimai geriausiu budu!" +lore1 = "Keistas Eliksyras + Gelezies Liejinys = Atsparumo Eliksyras (60 sekundziu)" +lore2 = "Keistas Eliksyras + Gelezies Blokas = Atsparumo Eliksyras-2 (30 sekundziu)" +lore = ["Keistas Eliksyras + Gelezies Liejinys = Atsparumo Eliksyras (60 sekundziu)", "Keistas Eliksyras + Gelezies Blokas = Atsparumo Eliksyras-2 (30 sekundziu)"] [brewing.health_boost] - name = "Gyvybe" - description = "Kai maksimaliu gyvybiu nepakanka..." - lore1 = "Momentinis Gydymas Eliksyras + Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras (120 sekundziu)" - lore2 = "Momentinis Gydymas Eliksyras + Uzburtas Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras-2 (120 sekundziu)" - lore = ["Momentinis Gydymas Eliksyras + Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras (120 sekundziu)", "Momentinis Gydymas Eliksyras + Uzburtas Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras-2 (120 sekundziu)"] +name = "Gyvybe" +description = "Kai maksimaliu gyvybiu nepakanka..." +lore1 = "Momentinis Gydymas Eliksyras + Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras (120 sekundziu)" +lore2 = "Momentinis Gydymas Eliksyras + Uzburtas Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras-2 (120 sekundziu)" +lore = ["Momentinis Gydymas Eliksyras + Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras (120 sekundziu)", "Momentinis Gydymas Eliksyras + Uzburtas Auksinis Obuolys = Sveikatos Stiprinimo Eliksyras-2 (120 sekundziu)"] [brewing.decay] - name = "Nykimas" - description = "Kas zinojo, kad Detritus bus toks naudingas?" - lore1 = "Silpnumo Eliksyras + Nuodinga Bulve = Nykimo Eliksyras (16 sekundziu)" - lore2 = "Silpnumo Eliksyras + Raudonio Saknys = Nykimo Eliksyras-2 (8 sekundes)" - lore = ["Silpnumo Eliksyras + Nuodinga Bulve = Nykimo Eliksyras (16 sekundziu)", "Silpnumo Eliksyras + Raudonio Saknys = Nykimo Eliksyras-2 (8 sekundes)"] +name = "Nykimas" +description = "Kas zinojo, kad Detritus bus toks naudingas?" +lore1 = "Silpnumo Eliksyras + Nuodinga Bulve = Nykimo Eliksyras (16 sekundziu)" +lore2 = "Silpnumo Eliksyras + Raudonio Saknys = Nykimo Eliksyras-2 (8 sekundes)" +lore = ["Silpnumo Eliksyras + Nuodinga Bulve = Nykimo Eliksyras (16 sekundziu)", "Silpnumo Eliksyras + Raudonio Saknys = Nykimo Eliksyras-2 (8 sekundes)"] [brewing.saturation] - name = "Sotulis" - description = "Zinai... as net ne alkanas..." - lore1 = "Regeneracijos Eliksyras + Kepta Bulve = Sotulio Eliksyras" - lore2 = "Regeneracijos Eliksyras + Sieno Rysulys = Sotulio Eliksyras-2" - lore = ["Regeneracijos Eliksyras + Kepta Bulve = Sotulio Eliksyras", "Regeneracijos Eliksyras + Sieno Rysulys = Sotulio Eliksyras-2"] +name = "Sotulis" +description = "Zinai... as net ne alkanas..." +lore1 = "Regeneracijos Eliksyras + Kepta Bulve = Sotulio Eliksyras" +lore2 = "Regeneracijos Eliksyras + Sieno Rysulys = Sotulio Eliksyras-2" +lore = ["Regeneracijos Eliksyras + Kepta Bulve = Sotulio Eliksyras", "Regeneracijos Eliksyras + Sieno Rysulys = Sotulio Eliksyras-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Mefistofelio Grandines" - description = "Leidzia jums gaminti grandininius sarvus." - lore1 = "Gaminimo receptas yra toks pat kaip ir bet kuris kitas, taciau vietoj jo naudojami gelezies grynuoliai" - lore = ["Gaminimo receptas yra toks pat kaip ir bet kuris kitas, taciau vietoj jo naudojami gelezies grynuoliai"] +name = "Mefistofelio Grandines" +description = "Leidzia jums gaminti grandininius sarvus." +lore1 = "Gaminimo receptas yra toks pat kaip ir bet kuris kitas, taciau vietoj jo naudojami gelezies grynuoliai" +lore = ["Gaminimo receptas yra toks pat kaip ir bet kuris kitas, taciau vietoj jo naudojami gelezies grynuoliai"] [blocking.horse_armorer] - name = "Gaminami Arkliu Sarvai" - description = "Leidzia pasigaminti arkliu sarvus" - lore1 = "Apsupkite balna medziaga, kuria norite naudoti sarvams gaminti" - lore = ["Apsupkite balna medziaga, kuria norite naudoti sarvams gaminti"] +name = "Gaminami Arkliu Sarvai" +description = "Leidzia pasigaminti arkliu sarvus" +lore1 = "Apsupkite balna medziaga, kuria norite naudoti sarvams gaminti" +lore = ["Apsupkite balna medziaga, kuria norite naudoti sarvams gaminti"] [blocking.saddle_crafter] - name = "Gaminamas Balnas" - description = "Pagaminkite balna is odos" - lore1 = "Receptas: 5 Odos:" - lore = ["Receptas: 5 Odos:"] +name = "Gaminamas Balnas" +description = "Pagaminkite balna is odos" +lore1 = "Receptas: 5 Odos:" +lore = ["Receptas: 5 Odos:"] [blocking.multi_armor] - name = "Multi-Sarvai" - description = "Prirskite Elytra prie sarvu" - lore1 = "Tai nuostabus igudis keliaujant." - lore2 = "Dinamiskai sujunkite ir keiskite Sarvus/Sparnus skraidant!" - lore3 = "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto." - lore4 = "Noredami atristi Sarva, Selinkite-Meskite daikta ir jis bus isardytas." - lore5 = "Jei jusu Multi-Sarvai bus sunaikinti, prarasite visus jame esancius daiktus." - lore6 = "Man nereikia sarvu, tai mane nuvilia..." - lore = ["Tai nuostabus igudis keliaujant.", "Dinamiskai sujunkite ir keiskite Sarvus/Sparnus skraidant!", "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto.", "Noredami atristi Sarva, Selinkite-Meskite daikta ir jis bus isardytas.", "Jei jusu Multi-Sarvai bus sunaikinti, prarasite visus jame esancius daiktus.", "Man nereikia sarvu, tai mane nuvilia..."] +name = "Multi-Sarvai" +description = "Prirskite Elytra prie sarvu" +lore1 = "Tai nuostabus igudis keliaujant." +lore2 = "Dinamiskai sujunkite ir keiskite Sarvus/Sparnus skraidant!" +lore3 = "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto." +lore4 = "Noredami atristi Sarva, Selinkite-Meskite daikta ir jis bus isardytas." +lore5 = "Jei jusu Multi-Sarvai bus sunaikinti, prarasite visus jame esancius daiktus." +lore6 = "Man nereikia sarvu, tai mane nuvilia..." +lore = ["Tai nuostabus igudis keliaujant.", "Dinamiskai sujunkite ir keiskite Sarvus/Sparnus skraidant!", "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto.", "Noredami atristi Sarva, Selinkite-Meskite daikta ir jis bus isardytas.", "Jei jusu Multi-Sarvai bus sunaikinti, prarasite visus jame esancius daiktus.", "Man nereikia sarvu, tai mane nuvilia..."] # crafting [crafting] [crafting.deconstruction] - name = "Dekonstrukcija" - description = "Isardykite blokus ir daiktus i isgelbejamus pagrindinius komponentus!" - lore1 = "Numeskite bet koki daikta ant zemes." - lore2 = "Tada Selinkite ir Desiniu peles mygtuku spauskite su Zirklemis" - lore = ["Numeskite bet koki daikta ant zemes.", "Tada Selinkite ir Desiniu peles mygtuku spauskite su Zirklemis"] +name = "Dekonstrukcija" +description = "Isardykite blokus ir daiktus i isgelbejamus pagrindinius komponentus!" +lore1 = "Numeskite bet koki daikta ant zemes." +lore2 = "Tada Selinkite ir Desiniu peles mygtuku spauskite su Zirklemis" +lore = ["Numeskite bet koki daikta ant zemes.", "Tada Selinkite ir Desiniu peles mygtuku spauskite su Zirklemis"] [crafting.xp] - name = "Gamybos XP" - description = "Gamindami gaukite pasyvu XP" - lore1 = "Gaukite XP gamindami" - lore = ["Gaukite XP gamindami"] +name = "Gamybos XP" +description = "Gamindami gaukite pasyvu XP" +lore1 = "Gaukite XP gamindami" +lore = ["Gaukite XP gamindami"] [crafting.reconstruction] - name = "Rudu Rekonstrukcija" - description = "Perdirbkite rudas is ju baziniu komponentu!" - lore1 = "8 dropai ir 1 priimantis = 1 ruda (beforme)" - lore2 = "Dropai turi buti islydyti (jei taikoma)" - lore3 = "Neiskaitant: Neiskaitant atraizu, kvarco, smaragdu ir kt." - lore4 = "Seimininkas = Apgaubas, t. y.: Akmuo, Neterio Akmuo, Gilakemnis." - lore = ["8 dropai ir 1 priimantis = 1 ruda (beforme)", "Dropai turi buti islydyti (jei taikoma)", "Neiskaitant: Neiskaitant atraizu, kvarco, smaragdu ir kt.", "Seimininkas = Apgaubas, t. y.: Akmuo, Neterio Akmuo, Gilakemnis."] +name = "Rudu Rekonstrukcija" +description = "Perdirbkite rudas is ju baziniu komponentu!" +lore1 = "8 dropai ir 1 priimantis = 1 ruda (beforme)" +lore2 = "Dropai turi buti islydyti (jei taikoma)" +lore3 = "Neiskaitant: Neiskaitant atraizu, kvarco, smaragdu ir kt." +lore4 = "Seimininkas = Apgaubas, t. y.: Akmuo, Neterio Akmuo, Gilakemnis." +lore = ["8 dropai ir 1 priimantis = 1 ruda (beforme)", "Dropai turi buti islydyti (jei taikoma)", "Neiskaitant: Neiskaitant atraizu, kvarco, smaragdu ir kt.", "Seimininkas = Apgaubas, t. y.: Akmuo, Neterio Akmuo, Gilakemnis."] [crafting.leather] - name = "Gaminama Oda" - description = "Oda gaminama is supuvusios mesos" - lore1 = "Tiesiog ismeskite supuvusia mesa ant lauzo!" - lore = ["Tiesiog ismeskite supuvusia mesa ant lauzo!"] +name = "Gaminama Oda" +description = "Oda gaminama is supuvusios mesos" +lore1 = "Tiesiog ismeskite supuvusia mesa ant lauzo!" +lore = ["Tiesiog ismeskite supuvusia mesa ant lauzo!"] [crafting.backpacks] - name = "Butiljerio Kuprinės!" - description = "Tai tiesiog inesa i zaidima Mojang rysuli!" - lore1 = "Noredami tai naudoti, turite buti islikimo rezime." - lore2 = "XLX : Oda, Pavadelis, Oda" - lore3 = "XSX : Oda, Statines Deze, Oda" - lore4 = "XCX : Oda, Skrynia, Oda" - lore = ["Noredami tai naudoti, turite buti islikimo rezime.", "XLX : Oda, Pavadelis, Oda", "XSX : Oda, Statines Deze, Oda", "XCX : Oda, Skrynia, Oda"] +name = "Butiljerio Kuprinės!" +description = "Tai tiesiog inesa i zaidima Mojang rysuli!" +lore1 = "Noredami tai naudoti, turite buti islikimo rezime." +lore2 = "XLX : Oda, Pavadelis, Oda" +lore3 = "XSX : Oda, Statines Deze, Oda" +lore4 = "XCX : Oda, Skrynia, Oda" +lore = ["Noredami tai naudoti, turite buti islikimo rezime.", "XLX : Oda, Pavadelis, Oda", "XSX : Oda, Statines Deze, Oda", "XCX : Oda, Skrynia, Oda"] [crafting.stations] - name = "Nesiojami Stalai!" - description = "Naudokite stala savo rankoje!" - lore2 = "VISI DAIKTAI, KURIUOS PALIKSITE ANT STALO KAI UZDARYSITE, BUS PRARASTI VISAM!" - lore3 = "Galiojantys stalai: Priekalas, Darbastalis, Tekelas, Kartografijos stalas, Akmens pjaustykle, Stakles" - lore = ["VISI DAIKTAI, KURIUOS PALIKSITE ANT STALO KAI UZDARYSITE, BUS PRARASTI VISAM!", "Galiojantys stalai: Priekalas, Darbastalis, Tekelas, Kartografijos stalas, Akmens pjaustykle, Stakles"] +name = "Nesiojami Stalai!" +description = "Naudokite stala savo rankoje!" +lore2 = "VISI DAIKTAI, KURIUOS PALIKSITE ANT STALO KAI UZDARYSITE, BUS PRARASTI VISAM!" +lore3 = "Galiojantys stalai: Priekalas, Darbastalis, Tekelas, Kartografijos stalas, Akmens pjaustykle, Stakles" +lore = ["VISI DAIKTAI, KURIUOS PALIKSITE ANT STALO KAI UZDARYSITE, BUS PRARASTI VISAM!", "Galiojantys stalai: Priekalas, Darbastalis, Tekelas, Kartografijos stalas, Akmens pjaustykle, Stakles"] [crafting.skulls] - name = "Gaminamos Galvos!" - description = "Naudodami medziagas galite pagaminti monstru galvas!" - lore1 = "Apsupkite kaulu bloka siais daiktais, kad gautumete galva:" - lore2 = "Zombio: Supuvusi Mesa" - lore3 = "Skeletono: Kaulas" - lore4 = "Pasaluno: Parakas" - lore5 = "Viterio: Pragaro Plytos" - lore6 = "Drakono: Drakono Dvelksmas" - lore = ["Apsupkite kaulu bloka siais daiktais, kad gautumete galva:", "Zombio: Supuvusi Mesa", "Skeletono: Kaulas", "Pasaluno: Parakas", "Viterio: Pragaro Plytos", "Drakono: Drakono Dvelksmas"] +name = "Gaminamos Galvos!" +description = "Naudodami medziagas galite pagaminti monstru galvas!" +lore1 = "Apsupkite kaulu bloka siais daiktais, kad gautumete galva:" +lore2 = "Zombio: Supuvusi Mesa" +lore3 = "Skeletono: Kaulas" +lore4 = "Pasaluno: Parakas" +lore5 = "Viterio: Pragaro Plytos" +lore6 = "Drakono: Drakono Dvelksmas" +lore = ["Apsupkite kaulu bloka siais daiktais, kad gautumete galva:", "Zombio: Supuvusi Mesa", "Skeletono: Kaulas", "Pasaluno: Parakas", "Viterio: Pragaro Plytos", "Drakono: Drakono Dvelksmas"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Laikas Butelyje" - description = "Nesiokit temporalu buteli, kuris kaupia laika ir leiskite ji, kad pagreitintumete laikinius blokus, auginamus augalus ir senestancias butybes, tokias kaip mazyliai gyvunai. Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis." - lore1 = "Sukauptos sekundes kraunamos kiekviena tiksenima" - lore2 = "Laiko pagreitejimas uz kiekviena sukapta sekunde" - lore3 = "Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis" - lore = ["Sukauptos sekundes kraunamos kiekviena tiksenima", "Laiko pagreitejimas uz kiekviena sukapta sekunde", "Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis"] +name = "Laikas Butelyje" +description = "Nesiokit temporalu buteli, kuris kaupia laika ir leiskite ji, kad pagreitintumete laikinius blokus, auginamus augalus ir senestancias butybes, tokias kaip mazyliai gyvunai. Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis." +lore1 = "Sukauptos sekundes kraunamos kiekviena tiksenima" +lore2 = "Laiko pagreitejimas uz kiekviena sukapta sekunde" +lore3 = "Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis" +lore = ["Sukauptos sekundes kraunamos kiekviena tiksenima", "Laiko pagreitejimas uz kiekviena sukapta sekunde", "Receptas (Beforme): Greitumo Eliksyras + Laikrodis + Stiklinis Butelis"] [chronos.aberrant_touch] - name = "Iškrypęs Prisilietimas" - description = "Artimos kovos smogiaj suteikia besikaupianti suletejima alkio saskaita, su griežtais PvP apribojimais, ir isaldo taikinius prie 5 kaupimo." - lore1 = "Artimos kovos smugiai suteikia besikaupianti suletejima" - lore2 = "PvE suletejimo trukmes riba" - lore3 = "PvP suletejimo stiprumo riba" - lore = ["Artimos kovos smugiai suteikia besikaupianti suletejima", "PvE suletejimo trukmes riba", "PvP suletejimo stiprumo riba"] +name = "Iškrypęs Prisilietimas" +description = "Artimos kovos smogiaj suteikia besikaupianti suletejima alkio saskaita, su griežtais PvP apribojimais, ir isaldo taikinius prie 5 kaupimo." +lore1 = "Artimos kovos smugiai suteikia besikaupianti suletejima" +lore2 = "PvE suletejimo trukmes riba" +lore3 = "PvP suletejimo stiprumo riba" +lore = ["Artimos kovos smugiai suteikia besikaupianti suletejima", "PvE suletejimo trukmes riba", "PvP suletejimo stiprumo riba"] [chronos.instant_recall] - name = "Momentinis Atšaukimas" - description = "Kairiuoju arba desiniu peles mygtuku spauskite su laikrodziu rankoje, kad persuktu laika atgal i naujausiu momentini vaizda su atkurta sveikata ir alkiu." - lore1 = "Persukimo trukme" - lore2 = "Atvesimas" - lore3 = "Inventorius nesigrazina" - lore = ["Persukimo trukme", "Atvesimas", "Inventorius nesigrazina"] +name = "Momentinis Atšaukimas" +description = "Kairiuoju arba desiniu peles mygtuku spauskite su laikrodziu rankoje, kad persuktu laika atgal i naujausiu momentini vaizda su atkurta sveikata ir alkiu." +lore1 = "Persukimo trukme" +lore2 = "Atvesimas" +lore3 = "Inventorius nesigrazina" +lore = ["Persukimo trukme", "Atvesimas", "Inventorius nesigrazina"] [chronos.time_bomb] - name = "Laiko Bomba" - description = "Meskite sukurta chrono bomba, kuri sukuria temporalu lauka, suletina butybes ir sustingdo sviedinys." - lore1 = "Temporalinio lauko spindulys" - lore2 = "Temporalinio lauko trukme" - lore3 = "Bombos atvesimas" - lore4 = "Receptas (Beforme): Laikrodis + Sniegio gniuzte + Deimantas + Smėlis" - lore = ["Temporalinio lauko spindulys", "Temporalinio lauko trukme", "Bombos atvesimas", "Receptas (Beforme): Laikrodis + Sniegio gniuzte + Deimantas + Smėlis"] +name = "Laiko Bomba" +description = "Meskite sukurta chrono bomba, kuri sukuria temporalu lauka, suletina butybes ir sustingdo sviedinys." +lore1 = "Temporalinio lauko spindulys" +lore2 = "Temporalinio lauko trukme" +lore3 = "Bombos atvesimas" +lore4 = "Receptas (Beforme): Laikrodis + Sniegio gniuzte + Deimantas + Smėlis" +lore = ["Temporalinio lauko spindulys", "Temporalinio lauko trukme", "Bombos atvesimas", "Receptas (Beforme): Laikrodis + Sniegio gniuzte + Deimantas + Smėlis"] # discovery [discovery] [discovery.armor] - name = "Pasaulio Sarvai" - description = "Pasyvus sarvai, priklausomai nuo salia esancio bloko kietumo." - lore1 = "Pasyvus Sarvai" - lore2 = "Remiantis salia esancio bloko kietumu" - lore3 = "Sarvu Stiprumas:" - lore = ["Pasyvus Sarvai", "Remiantis salia esancio bloko kietumu", "Sarvu Stiprumas:"] +name = "Pasaulio Sarvai" +description = "Pasyvus sarvai, priklausomai nuo salia esancio bloko kietumo." +lore1 = "Pasyvus Sarvai" +lore2 = "Remiantis salia esancio bloko kietumu" +lore3 = "Sarvu Stiprumas:" +lore = ["Pasyvus Sarvai", "Remiantis salia esancio bloko kietumu", "Sarvu Stiprumas:"] [discovery.unity] - name = "Eksperimentine Vienybe" - description = "Patirties kamuoliu rinkimas prideda XP prie atsitiktiniu igudzių." - lore1 = "XP " - lore2 = "Per Kamuoli" - lore = ["XP ", "Per Kamuoli"] +name = "Eksperimentine Vienybe" +description = "Patirties kamuoliu rinkimas prideda XP prie atsitiktiniu igudzių." +lore1 = "XP " +lore2 = "Per Kamuoli" +lore = ["XP ", "Per Kamuoli"] [discovery.resist] - name = "Eksperimentinis Atsparumas" - description = "Sunaudokite patirtі, kad sumazintumete zala tik tada, kai smugis numustu jus zemiau 5 sirdziu arba nuzudytu." - lore0 = "Suveikia tik esant kritinei sveikatai (<= 5 sirdzių) karta per 15 sekundziu" - lore1 = " Sumazinta zala" - lore2 = "patirtis issekusi" - lore = ["Suveikia tik esant kritinei sveikatai (<= 5 sirdzių) karta per 15 sekundziu", " Sumazinta zala", "patirtis issekusi"] +name = "Eksperimentinis Atsparumas" +description = "Sunaudokite patirtі, kad sumazintumete zala tik tada, kai smugis numustu jus zemiau 5 sirdziu arba nuzudytu." +lore0 = "Suveikia tik esant kritinei sveikatai (<= 5 sirdzių) karta per 15 sekundziu" +lore1 = " Sumazinta zala" +lore2 = "patirtis issekusi" +lore = ["Suveikia tik esant kritinei sveikatai (<= 5 sirdzių) karta per 15 sekundziu", " Sumazinta zala", "patirtis issekusi"] [discovery.villager] - name = "Kaimieciu Atrakcija" - description = "Leidzia sudaryti geresnius sandorius su kaimo gyventojais!" - lore1 = "Tai sunaudoja XP vienam bendravimui su kaimieciais" - lore2 = "Tikimybe per saveika sunaudoti XP ir pagerinti sandorius" - lore3 = "reikalingas XP sunaudojimas vienai saveikai" - lore = ["Tai sunaudoja XP vienam bendravimui su kaimieciais", "Tikimybe per saveika sunaudoti XP ir pagerinti sandorius", "reikalingas XP sunaudojimas vienai saveikai"] +name = "Kaimieciu Atrakcija" +description = "Leidzia sudaryti geresnius sandorius su kaimo gyventojais!" +lore1 = "Tai sunaudoja XP vienam bendravimui su kaimieciais" +lore2 = "Tikimybe per saveika sunaudoti XP ir pagerinti sandorius" +lore3 = "reikalingas XP sunaudojimas vienai saveikai" +lore = ["Tai sunaudoja XP vienam bendravimui su kaimieciais", "Tikimybe per saveika sunaudoti XP ir pagerinti sandorius", "reikalingas XP sunaudojimas vienai saveikai"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapis Sugrizimas" - description = "Kaina dar 1 XP lygis, o mainais turite sansa gauti nemokamu lapis" - lore1 = "Kiekvienam lygiui tai padidina kerejimo kaina - 1, bet gali grazinti daugiau nei 3 lapis" - lore = ["Kiekvienam lygiui tai padidina kerejimo kaina - 1, bet gali grazinti daugiau nei 3 lapis"] +name = "Lapis Sugrizimas" +description = "Kaina dar 1 XP lygis, o mainais turite sansa gauti nemokamu lapis" +lore1 = "Kiekvienam lygiui tai padidina kerejimo kaina - 1, bet gali grazinti daugiau nei 3 lapis" +lore = ["Kiekvienam lygiui tai padidina kerejimo kaina - 1, bet gali grazinti daugiau nei 3 lapis"] [enchanting.quick_enchant] - name = "Staigus Burtas" - description = "Uzburkite daiktus dedami uzburtas knygas tiesiai ant ju." - lore1 = "Maksimalus Kombinuotas Lygis" - lore2 = "Negalima uzburti daikto, kuriame yra daugiau nei " - lore3 = "galia" - lore = ["Maksimalus Kombinuotas Lygis", "Negalima uzburti daikto, kuriame yra daugiau nei ", "galia"] +name = "Staigus Burtas" +description = "Uzburkite daiktus dedami uzburtas knygas tiesiai ant ju." +lore1 = "Maksimalus Kombinuotas Lygis" +lore2 = "Negalima uzburti daikto, kuriame yra daugiau nei " +lore3 = "galia" +lore = ["Maksimalus Kombinuotas Lygis", "Negalima uzburti daikto, kuriame yra daugiau nei ", "galia"] [enchanting.return] - name = "XP Grazinimas" - description = "Buriant jums XP grazinamas, kai uzburiate daikta." - lore1 = "Isleista patirtis turi galimybe buti grazinta, kai uzburiate daikta" - lore2 = "Patirtis per Burta" - lore = ["Isleista patirtis turi galimybe buti grazinta, kai uzburiate daikta", "Patirtis per Burta"] +name = "XP Grazinimas" +description = "Buriant jums XP grazinamas, kai uzburiate daikta." +lore1 = "Isleista patirtis turi galimybe buti grazinta, kai uzburiate daikta" +lore2 = "Patirtis per Burta" +lore = ["Isleista patirtis turi galimybe buti grazinta, kai uzburiate daikta", "Patirtis per Burta"] # excavation [excavation] [excavation.haste] - name = "Skubus Ekskavatoristas" - description = "Tai paspartins kasimo procesa, su GREITU KASIMU!" - lore1 = "Gaukite greita kasima kasant" - lore2 = "x Greito kasimo lygiai, kai pradedate kasti bet koki bloka." - lore = ["Gaukite greita kasima kasant", "x Greito kasimo lygiai, kai pradedate kasti bet koki bloka."] +name = "Skubus Ekskavatoristas" +description = "Tai paspartins kasimo procesa, su GREITU KASIMU!" +lore1 = "Gaukite greita kasima kasant" +lore2 = "x Greito kasimo lygiai, kai pradedate kasti bet koki bloka." +lore = ["Gaukite greita kasima kasant", "x Greito kasimo lygiai, kai pradedate kasti bet koki bloka."] [excavation.spelunker] - name = "Super-Ziurovas Spelunkeris!" - description = "Ziurekite i rudas pro zeme!" - lore1 = "Ruda laikykite rankose, o Svytincias uogas laikykite pagrindineje rankoje ir Sneak'inkite!" - lore2 = "Bloku diapazonas: " - lore3 = "Naudojant sunaudoja svytincias uogas" - lore = ["Ruda laikykite rankose, o Svytincias uogas laikykite pagrindineje rankoje ir Sneak'inkite!", "Bloku diapazonas: ", "Naudojant sunaudoja svytincias uogas"] +name = "Super-Ziurovas Spelunkeris!" +description = "Ziurekite i rudas pro zeme!" +lore1 = "Ruda laikykite rankose, o Svytincias uogas laikykite pagrindineje rankoje ir Sneak'inkite!" +lore2 = "Bloku diapazonas: " +lore3 = "Naudojant sunaudoja svytincias uogas" +lore = ["Ruda laikykite rankose, o Svytincias uogas laikykite pagrindineje rankoje ir Sneak'inkite!", "Bloku diapazonas: ", "Naudojant sunaudoja svytincias uogas"] [excavation.drop_to_inventory] - name = "Kastuvo Trauka" +name = "Kastuvo Trauka" [excavation.omni_tool] - name = "OMNI - I.R.A.N.K.I.S" - description = "Tackle perdetais prabangus Leatherman" - lore1 = "Tikriausiai galingiausias is daugelio leidzia jums" - lore2 = "keisti irankius skrydzio metu, atsizvelgiant i jusu poreikius." - lore3 = "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto." - lore4 = "Noredami atristi irankius, Selinkite-Meskite daikta ir bus isardytas." - lore5 = "Jus negalite sulauzyti irankiu siame odu zmoguje, bet ir negalite naudoti sulauzytu irankiu" - lore6 = "visi galimi sujungti daiktai." - lore7 = "Galite naudoti penkis ar sesius irankius arba tik viena!" - lore = ["Tikriausiai galingiausias is daugelio leidzia jums", "keisti irankius skrydzio metu, atsizvelgiant i jusu poreikius.", "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto.", "Noredami atristi irankius, Selinkite-Meskite daikta ir bus isardytas.", "Jus negalite sulauzyti irankiu siame odu zmoguje, bet ir negalite naudoti sulauzytu irankiu", "visi galimi sujungti daiktai.", "Galite naudoti penkis ar sesius irankius arba tik viena!"] +name = "OMNI - I.R.A.N.K.I.S" +description = "Tackle perdetais prabangus Leatherman" +lore1 = "Tikriausiai galingiausias is daugelio leidzia jums" +lore2 = "keisti irankius skrydzio metu, atsizvelgiant i jusu poreikius." +lore3 = "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto." +lore4 = "Noredami atristi irankius, Selinkite-Meskite daikta ir bus isardytas." +lore5 = "Jus negalite sulauzyti irankiu siame odu zmoguje, bet ir negalite naudoti sulauzytu irankiu" +lore6 = "visi galimi sujungti daiktai." +lore7 = "Galite naudoti penkis ar sesius irankius arba tik viena!" +lore = ["Tikriausiai galingiausias is daugelio leidzia jums", "keisti irankius skrydzio metu, atsizvelgiant i jusu poreikius.", "Jei norite sujungti, su shift spauskite daikta virs kito inventoriuje esancio daikto.", "Noredami atristi irankius, Selinkite-Meskite daikta ir bus isardytas.", "Jus negalite sulauzyti irankiu siame odu zmoguje, bet ir negalite naudoti sulauzytu irankiu", "visi galimi sujungti daiktai.", "Galite naudoti penkis ar sesius irankius arba tik viena!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Augimo Aura" - description = "Auginkite gamta aplink save auroje" - lore1 = "Bloko Spindulys" - lore2 = "Augimo Auros Stiprumas" - lore3 = "Maisto Kaina" - lore = ["Bloko Spindulys", "Augimo Auros Stiprumas", "Maisto Kaina"] +name = "Augimo Aura" +description = "Auginkite gamta aplink save auroje" +lore1 = "Bloko Spindulys" +lore2 = "Augimo Auros Stiprumas" +lore3 = "Maisto Kaina" +lore = ["Bloko Spindulys", "Augimo Auros Stiprumas", "Maisto Kaina"] [herbalism.hippo] - name = "Zolininko Begemotas" - description = "Maisto vartojimas suteikia daugiau sotumo" - lore1 = "Maistas) papildomi vartojimo prisotinimo taskai" - lore = ["Maistas) papildomi vartojimo prisotinimo taskai"] +name = "Zolininko Begemotas" +description = "Maisto vartojimas suteikia daugiau sotumo" +lore1 = "Maistas) papildomi vartojimo prisotinimo taskai" +lore = ["Maistas) papildomi vartojimo prisotinimo taskai"] [herbalism.myconid] - name = "Zolininku Mikonidas" - description = "Suteikia jums galimybe gaminti grybiena" - lore1 = "Bet koks zemes blokas ir rudas & raudonas grybas sukurs grybiena." - lore = ["Bet koks zemes blokas ir rudas & raudonas grybas sukurs grybiena."] +name = "Zolininku Mikonidas" +description = "Suteikia jums galimybe gaminti grybiena" +lore1 = "Bet koks zemes blokas ir rudas & raudonas grybas sukurs grybiena." +lore = ["Bet koks zemes blokas ir rudas & raudonas grybas sukurs grybiena."] [herbalism.terralid] - name = "Zolininko Zole" - description = "Suteikia jums galimybe gaminti zoles blokus" - lore1 = "Trys seklos, daugiau nei 3 zemes blokai ir sukurs 3 zoles blokus." - lore = ["Trys seklos, daugiau nei 3 zemes blokai ir sukurs 3 zoles blokus."] +name = "Zolininko Zole" +description = "Suteikia jums galimybe gaminti zoles blokus" +lore1 = "Trys seklos, daugiau nei 3 zemes blokai ir sukurs 3 zoles blokus." +lore = ["Trys seklos, daugiau nei 3 zemes blokai ir sukurs 3 zoles blokus."] [herbalism.cobweb] - name = "Voratinkliu Kurejas" - description = "Suteikia galimybe kurti voratinklius darbastalyje" - lore1 = "Devyni siulai pagamins voratinkli." - lore = ["Devyni siulai pagamins voratinkli."] +name = "Voratinkliu Kurejas" +description = "Suteikia galimybe kurti voratinklius darbastalyje" +lore1 = "Devyni siulai pagamins voratinkli." +lore = ["Devyni siulai pagamins voratinkli."] [herbalism.mushroom_blocks] - name = "Grybu Gamintojas" - description = "Suteikia jums galimybe gaminti grybu blokus darbastalyje" - lore1 = "Keturi grybai, kad padarytumete bloka, arba blokas, kad padarytumete stieba." - lore = ["Keturi grybai, kad padarytumete bloka, arba blokas, kad padarytumete stieba."] +name = "Grybu Gamintojas" +description = "Suteikia jums galimybe gaminti grybu blokus darbastalyje" +lore1 = "Keturi grybai, kad padarytumete bloka, arba blokas, kad padarytumete stieba." +lore = ["Keturi grybai, kad padarytumete bloka, arba blokas, kad padarytumete stieba."] [herbalism.drop_to_inventory] - name = "Kauptuko Trauka" +name = "Kauptuko Trauka" [herbalism.hungry_shield] - name = "Alkanas Skydas" - description = "Gaukite zala savo alkiui pries savo gyvybes." - lore1 = "Apsaugota nuo Alkio" - lore = ["Apsaugota nuo Alkio"] +name = "Alkanas Skydas" +description = "Gaukite zala savo alkiui pries savo gyvybes." +lore1 = "Apsaugota nuo Alkio" +lore = ["Apsaugota nuo Alkio"] [herbalism.luck] - name = "Zolininko Sekme" - description = "Kai sulauzysite zole/geles, turite galimybe gauti atsitiktini daikta" - lore0 = "Geles = Maistas, ir Zole = Seklos" - lore1 = "Galimybe gauti daikta griaunant geles" - lore2 = "Galimybe gauti daikta sugriovus zole" - lore = ["Geles = Maistas, ir Zole = Seklos", "Galimybe gauti daikta griaunant geles", "Galimybe gauti daikta sugriovus zole"] +name = "Zolininko Sekme" +description = "Kai sulauzysite zole/geles, turite galimybe gauti atsitiktini daikta" +lore0 = "Geles = Maistas, ir Zole = Seklos" +lore1 = "Galimybe gauti daikta griaunant geles" +lore2 = "Galimybe gauti daikta sugriovus zole" +lore = ["Geles = Maistas, ir Zole = Seklos", "Galimybe gauti daikta griaunant geles", "Galimybe gauti daikta sugriovus zole"] [herbalism.replant] - name = "Derliaus Nuemimas ir Persodinimas" - description = "Desiniu peles mygtuku spauskite ant paseliu su kapliu, kad nuimtumete derliu ir persodintumete." - lore1 = "Bloku Persodinimo Spindulys" - lore = ["Bloku Persodinimo Spindulys"] +name = "Derliaus Nuemimas ir Persodinimas" +description = "Desiniu peles mygtuku spauskite ant paseliu su kapliu, kad nuimtumete derliu ir persodintumete." +lore1 = "Bloku Persodinimo Spindulys" +lore = ["Bloku Persodinimo Spindulys"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalinas" - description = "Kuo maziau gyvybiu, tuo daugiau zalos padarysite (Artimoje kovoje)" - lore1 = "Maksimali zala" - lore = ["Maksimali zala"] +name = "Adrenalinas" +description = "Kuo maziau gyvybiu, tuo daugiau zalos padarysite (Artimoje kovoje)" +lore1 = "Maksimali zala" +lore = ["Maksimali zala"] [hunter.penalty] - name = "" - description = "" - lore1 = "Jus gausite vis daugiau nuodu, jeigu tapsite alkanas" - lore = ["Jus gausite vis daugiau nuodu, jeigu tapsite alkanas"] +name = "" +description = "" +lore1 = "Jus gausite vis daugiau nuodu, jeigu tapsite alkanas" +lore = ["Jus gausite vis daugiau nuodu, jeigu tapsite alkanas"] [hunter.drop_to_inventory] - name = "Daiktu Trauka" - description = "Kai ka nors nuzudote/sulauzote bloka kardu, daiktai atsiras jusu inventoriuje" - lore1 = "Kiekviena karta, kai daiktas iskrenta is monstro/bloko, kuri sugriaunate, patenka i jusu inventoriu, jei imanoma." - lore = ["Kiekviena karta, kai daiktas iskrenta is monstro/bloko, kuri sugriaunate, patenka i jusu inventoriu, jei imanoma."] +name = "Daiktu Trauka" +description = "Kai ka nors nuzudote/sulauzote bloka kardu, daiktai atsiras jusu inventoriuje" +lore1 = "Kiekviena karta, kai daiktas iskrenta is monstro/bloko, kuri sugriaunate, patenka i jusu inventoriu, jei imanoma." +lore = ["Kiekviena karta, kai daiktas iskrenta is monstro/bloko, kuri sugriaunate, patenka i jusu inventoriu, jei imanoma."] [hunter.invisibility] - name = "Nykstantis Zingsnis" - description = "Kai esi sumustas, tu igyji nematomuma, tai isnaudos daug alkio" - lore1 = "Smugio metu igykite pasyvu nematomuma" - lore2 = "x Nematomumas kaupiasi 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Nematomumo trukme" - lore = ["Smugio metu igykite pasyvu nematomuma", "x Nematomumas kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Nematomumo trukme"] +name = "Nykstantis Zingsnis" +description = "Kai esi sumustas, tu igyji nematomuma, tai isnaudos daug alkio" +lore1 = "Smugio metu igykite pasyvu nematomuma" +lore2 = "x Nematomumas kaupiasi 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Nematomumo trukme" +lore = ["Smugio metu igykite pasyvu nematomuma", "x Nematomumas kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Nematomumo trukme"] [hunter.jump_boost] - name = "Medziodojo Auksumos" - description = "Kai tave istiko ispuolis, tu igyji suolio impulsa, tai kainuos alki" - lore1 = "Igykite pasyvu suolio padidinima smugio metu" - lore2 = "x Suoliavimas kaupiasi 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Suoliavimo kaups daugikli, o ne trukme" - lore = ["Igykite pasyvu suolio padidinima smugio metu", "x Suoliavimas kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Suoliavimo kaups daugikli, o ne trukme"] +name = "Medziodojo Auksumos" +description = "Kai tave istiko ispuolis, tu igyji suolio impulsa, tai kainuos alki" +lore1 = "Igykite pasyvu suolio padidinima smugio metu" +lore2 = "x Suoliavimas kaupiasi 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Suoliavimo kaups daugikli, o ne trukme" +lore = ["Igykite pasyvu suolio padidinima smugio metu", "x Suoliavimas kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Suoliavimo kaups daugikli, o ne trukme"] [hunter.luck] - name = "Medziodojo Sekme" - description = "Kai tave uzklumpa, tu laimi sekme, tai kainuos alki" - lore1 = "Pasiekite pasyvia sekme, kai nukentesete" - lore2 = "x Sekme kaupiasi 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Sekme kaups daugikli, o ne trukme." - lore = ["Pasiekite pasyvia sekme, kai nukentesete", "x Sekme kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Sekme kaups daugikli, o ne trukme."] +name = "Medziodojo Sekme" +description = "Kai tave uzklumpa, tu laimi sekme, tai kainuos alki" +lore1 = "Pasiekite pasyvia sekme, kai nukentesete" +lore2 = "x Sekme kaupiasi 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Sekme kaups daugikli, o ne trukme." +lore = ["Pasiekite pasyvia sekme, kai nukentesete", "x Sekme kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Sekme kaups daugikli, o ne trukme."] [hunter.regen] - name = "Medziodojo Regeneracija" - description = "Kai esi sumustas, tu atsiregenruosi, tai kainuos alki" - lore1 = "Igykite pasyvia regeneracija, kai itrenkia" - lore2 = "x Regeneracija kaupsis 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Regeneracija kaups daugikli, ne trukme." - lore = ["Igykite pasyvia regeneracija, kai itrenkia", "x Regeneracija kaupsis 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Regeneracija kaups daugikli, ne trukme."] +name = "Medziodojo Regeneracija" +description = "Kai esi sumustas, tu atsiregenruosi, tai kainuos alki" +lore1 = "Igykite pasyvia regeneracija, kai itrenkia" +lore2 = "x Regeneracija kaupsis 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Regeneracija kaups daugikli, ne trukme." +lore = ["Igykite pasyvia regeneracija, kai itrenkia", "x Regeneracija kaupsis 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Regeneracija kaups daugikli, ne trukme."] [hunter.resistance] - name = "Medziodojo Atsparumas" - description = "Kai tave uzklumpa, tu igyji atsparuma, tai kainuos alki" - lore1 = "Igykite pasyvu pasipriesinima, kai itrenkia" - lore2 = "x Atsparumas kaupsis 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Atsparumas kaups daugikli, o ne trukme." - lore = ["Igykite pasyvu pasipriesinima, kai itrenkia", "x Atsparumas kaupsis 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Atsparumas kaups daugikli, o ne trukme."] +name = "Medziodojo Atsparumas" +description = "Kai tave uzklumpa, tu igyji atsparuma, tai kainuos alki" +lore1 = "Igykite pasyvu pasipriesinima, kai itrenkia" +lore2 = "x Atsparumas kaupsis 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Atsparumas kaups daugikli, o ne trukme." +lore = ["Igykite pasyvu pasipriesinima, kai itrenkia", "x Atsparumas kaupsis 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Atsparumas kaups daugikli, o ne trukme."] [hunter.speed] - name = "Medziodojo Greitis" - description = "Kai tave uzklumpa, tu igyji greiti, tai kainuos alki" - lore1 = "Smugio metu padidinkite pasyvu greiti" - lore2 = "x Greitis kaupiasi 3 sekundes po smugio" - lore3 = "x Kaupiamas alkis" - lore4 = "Badas kaups trukme ir daugikli." - lore5 = "Greicio kaups daugikli, o ne trukme." - lore = ["Smugio metu padidinkite pasyvu greiti", "x Greitis kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Greicio kaups daugikli, o ne trukme."] +name = "Medziodojo Greitis" +description = "Kai tave uzklumpa, tu igyji greiti, tai kainuos alki" +lore1 = "Smugio metu padidinkite pasyvu greiti" +lore2 = "x Greitis kaupiasi 3 sekundes po smugio" +lore3 = "x Kaupiamas alkis" +lore4 = "Badas kaups trukme ir daugikli." +lore5 = "Greicio kaups daugikli, o ne trukme." +lore = ["Smugio metu padidinkite pasyvu greiti", "x Greitis kaupiasi 3 sekundes po smugio", "x Kaupiamas alkis", "Badas kaups trukme ir daugikli.", "Greicio kaups daugikli, o ne trukme."] [hunter.strength] - name = "Medziodojo Galia" - description = "Kai tave musa, tu igyji jegu, tai kainuos alki" - lore1 = "Smugio metu igykite pasyvios jegos" - lore2 = "x Jega kaupiasi 3 sekundes po smugio" - lore3 = "x Susikraunantis Alkis" - lore4 = "Bado didejimo trukme ir daugiklis." - lore5 = "Jegos kruvos daugiklis, ne trukme." - lore = ["Smugio metu igykite pasyvios jegos", "x Jega kaupiasi 3 sekundes po smugio", "x Susikraunantis Alkis", "Bado didejimo trukme ir daugiklis.", "Jegos kruvos daugiklis, ne trukme."] +name = "Medziodojo Galia" +description = "Kai tave musa, tu igyji jegu, tai kainuos alki" +lore1 = "Smugio metu igykite pasyvios jegos" +lore2 = "x Jega kaupiasi 3 sekundes po smugio" +lore3 = "x Susikraunantis Alkis" +lore4 = "Bado didejimo trukme ir daugiklis." +lore5 = "Jegos kruvos daugiklis, ne trukme." +lore = ["Smugio metu igykite pasyvios jegos", "x Jega kaupiasi 3 sekundes po smugio", "x Susikraunantis Alkis", "Bado didejimo trukme ir daugiklis.", "Jegos kruvos daugiklis, ne trukme."] # nether [nether] [nether.skull_toss] - name = "Nudziuvusios Kaukoles Metimas" - description1 = "Islaisvinkite savo vidini vytuma naudodami" - description2 = "kazkieno" - description3 = "galva." - lore1 = "sekundes tarp kaukoles metimu." - lore2 = "Viterio naudojimas: ismeskite " - lore3 = "Viterio Galva" - lore4 = "sprogsta nuo smugio." - lore = ["sekundes tarp kaukoles metimu.", "Viterio naudojimas: ismeskite ", "Viterio Galva", "sprogsta nuo smugio."] +name = "Nudziuvusios Kaukoles Metimas" +description1 = "Islaisvinkite savo vidini vytuma naudodami" +description2 = "kazkieno" +description3 = "galva." +lore1 = "sekundes tarp kaukoles metimu." +lore2 = "Viterio naudojimas: ismeskite " +lore3 = "Viterio Galva" +lore4 = "sprogsta nuo smugio." +lore = ["sekundes tarp kaukoles metimu.", "Viterio naudojimas: ismeskite ", "Viterio Galva", "sprogsta nuo smugio."] [nether.wither_resist] - name = "Atsparumas Vytimui" - description = "Priesinasi vytimui del neterito galios." - lore1 = "galimybe paneigti vytima (vienam luitui)." - lore2 = "Pasyvus: Neterito sarvu devejimas turi galimybe panaikinti " - lore3 = "vytima." - lore = ["galimybe paneigti vytima (vienam luitui).", "Pasyvus: Neterito sarvu devejimas turi galimybe panaikinti ", "vytima."] +name = "Atsparumas Vytimui" +description = "Priesinasi vytimui del neterito galios." +lore1 = "galimybe paneigti vytima (vienam luitui)." +lore2 = "Pasyvus: Neterito sarvu devejimas turi galimybe panaikinti " +lore3 = "vytima." +lore = ["galimybe paneigti vytima (vienam luitui).", "Pasyvus: Neterito sarvu devejimas turi galimybe panaikinti ", "vytima."] [nether.fire_resist] - name = "Atsparumas Ugniai" - description = "Atsparus ugniai, nes sukietina oda." - lore1 = "galimybe paneigti deginimo efekta!" - lore = ["galimybe paneigti deginimo efekta!"] +name = "Atsparumas Ugniai" +description = "Atsparus ugniai, nes sukietina oda." +lore1 = "galimybe paneigti deginimo efekta!" +lore = ["galimybe paneigti deginimo efekta!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Automatinis Lydymas" - description = "Leidzia lydyti iskastas rudas." - lore1 = "Rudos, kurias galima lydyti, islydomos automatiskai" - lore2 = "% galimybe gauti papildoma" - lore = ["Rudos, kurias galima lydyti, islydomos automatiskai", "% galimybe gauti papildoma"] +name = "Automatinis Lydymas" +description = "Leidzia lydyti iskastas rudas." +lore1 = "Rudos, kurias galima lydyti, islydomos automatiskai" +lore2 = "% galimybe gauti papildoma" +lore = ["Rudos, kurias galima lydyti, islydomos automatiskai", "% galimybe gauti papildoma"] [pickaxe.chisel] - name = "Rudos Kaltas" - description = "Desiniu peles mygtuku spauskite ant Rudos, kad is ju isgautumete daugiau rudos uz didele ilgaamziskumo kaina." - lore1 = "Galimybe numesti" - lore2 = "Irankiu susidevejimas" - lore = ["Galimybe numesti", "Irankiu susidevejimas"] +name = "Rudos Kaltas" +description = "Desiniu peles mygtuku spauskite ant Rudos, kad is ju isgautumete daugiau rudos uz didele ilgaamziskumo kaina." +lore1 = "Galimybe numesti" +lore2 = "Irankiu susidevejimas" +lore = ["Galimybe numesti", "Irankiu susidevejimas"] [pickaxe.drop_to_inventory] - name = "Kirtiklio Trauka" - description = "Kai nugrausite bloka, daiktas atsiras jusu inventoriuje" - lore1 = "Vietoj to, kad daiktas iskrenta is bloko, daiktas pateks i jusu inventoriu, jei imanoma." - lore = ["Vietoj to, kad daiktas iskrenta is bloko, daiktas pateks i jusu inventoriu, jei imanoma."] +name = "Kirtiklio Trauka" +description = "Kai nugrausite bloka, daiktas atsiras jusu inventoriuje" +lore1 = "Vietoj to, kad daiktas iskrenta is bloko, daiktas pateks i jusu inventoriu, jei imanoma." +lore = ["Vietoj to, kad daiktas iskrenta is bloko, daiktas pateks i jusu inventoriu, jei imanoma."] [pickaxe.silk_spawner] - name = "Kirtiklis Silko-Narvas" - description = "Priverszia Narvus nukristi, kai buna sugriauti" - lore1 = "Padaro narvus sugriaunamais silko prisilietimu." - lore2 = "Padaro narvus sugriaunamais selinant." - lore = ["Padaro narvus sugriaunamais silko prisilietimu.", "Padaro narvus sugriaunamais selinant."] +name = "Kirtiklis Silko-Narvas" +description = "Priverszia Narvus nukristi, kai buna sugriauti" +lore1 = "Padaro narvus sugriaunamais silko prisilietimu." +lore2 = "Padaro narvus sugriaunamais selinant." +lore = ["Padaro narvus sugriaunamais silko prisilietimu.", "Padaro narvus sugriaunamais selinant."] [pickaxe.vein_miner] - name = "Veinkasejas" - description = "Leidzia sugriauti rudu blokus gysloje/klasteryje" - lore1 = "Selink, ir kask RUDAS" - lore2 = "vein kasybos diapazonas" - lore3 = "Sis igudis negrupuoja visu dropu kartu!" - lore = ["Selink, ir kask RUDAS", "vein kasybos diapazonas", "Sis igudis negrupuoja visu dropu kartu!"] +name = "Veinkasejas" +description = "Leidzia sugriauti rudu blokus gysloje/klasteryje" +lore1 = "Selink, ir kask RUDAS" +lore2 = "vein kasybos diapazonas" +lore3 = "Sis igudis negrupuoja visu dropu kartu!" +lore = ["Selink, ir kask RUDAS", "vein kasybos diapazonas", "Sis igudis negrupuoja visu dropu kartu!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Streles Susigrazinimas" - description = "Atgaukite streles po to, kai nuzudete priesa." - lore1 = "Galimybe susigrazinti streles ant smugio/nuzudymo" - lore2 = "Sansas: " - lore = ["Galimybe susigrazinti streles ant smugio/nuzudymo", "Sansas: "] +name = "Streles Susigrazinimas" +description = "Atgaukite streles po to, kai nuzudete priesa." +lore1 = "Galimybe susigrazinti streles ant smugio/nuzudymo" +lore2 = "Sansas: " +lore = ["Galimybe susigrazinti streles ant smugio/nuzudymo", "Sansas: "] [ranged.web_shot] - name = "Voratinkliu Spastai" - description = "Apjuociate voratinkliais aplink savo taikini, kai i juos pataikote!" - lore1 = "8 Voratinkliai aplink Sniego gniuzte ir galite mesti!" - lore2 = "narvo sekundziu, apytiksliai." - lore = ["8 Voratinkliai aplink Sniego gniuzte ir galite mesti!", "narvo sekundziu, apytiksliai."] +name = "Voratinkliu Spastai" +description = "Apjuociate voratinkliais aplink savo taikini, kai i juos pataikote!" +lore1 = "8 Voratinkliai aplink Sniego gniuzte ir galite mesti!" +lore2 = "narvo sekundziu, apytiksliai." +lore = ["8 Voratinkliai aplink Sniego gniuzte ir galite mesti!", "narvo sekundziu, apytiksliai."] [ranged.force_shot] - name = "Stiprus Suvis" - description = "Saudykite sviedinys toliau, greiciau!" - advancementname = "Tolimas Suvis" - advancementlore = "Nusaukite is daugiau nei 30 bloku!" - lore1 = "Sviedinio Greitis" - lore = ["Sviedinio Greitis"] +name = "Stiprus Suvis" +description = "Saudykite sviedinys toliau, greiciau!" +advancementname = "Tolimas Suvis" +advancementlore = "Nusaukite is daugiau nei 30 bloku!" +lore1 = "Sviedinio Greitis" +lore = ["Sviedinio Greitis"] [ranged.lunge_shot] - name = "Itūpstas" - description = "Krintant jusu streles sviedzia jus atsitiktine kryptimi" - lore1 = "Atsitiktinis Sprogimo Greitis" - lore = ["Atsitiktinis Sprogimo Greitis"] +name = "Itūpstas" +description = "Krintant jusu streles sviedzia jus atsitiktine kryptimi" +lore1 = "Atsitiktinis Sprogimo Greitis" +lore = ["Atsitiktinis Sprogimo Greitis"] [ranged.arrow_piercing] - name = "Pradurincios Streles" - description = "Prideda pradurima prie sviedinių! Saudykite kiaurai per dalykus!" - lore1 = "Taikiniu Pradurimas" - lore = ["Taikiniu Pradurimas"] +name = "Pradurincios Streles" +description = "Prideda pradurima prie sviedinių! Saudykite kiaurai per dalykus!" +lore1 = "Taikiniu Pradurimas" +lore = ["Taikiniu Pradurimas"] # rift [rift] [rift.remote_access] - name = "Nuotolinis Prisijungimas" - description = "Istraukite is tustumes ir supilkite i pazymeta talpykla." - lore1 = "Enderio Perlas + Kompasas = Relikvijoriaus Prievado Raktas" - lore2 = "Sis daiktas leidzia nuotoliniu budu pasiekti konteinerius" - lore3 = "Pagamine daikta paziurekite i ji, kad pamatytumete vartosena" - notcontainer = "Tai ne konteineris" - lore = ["Enderio Perlas + Kompasas = Relikvijoriaus Prievado Raktas", "Sis daiktas leidzia nuotoliniu budu pasiekti konteinerius", "Pagamine daikta paziurekite i ji, kad pamatytumete vartosena"] +name = "Nuotolinis Prisijungimas" +description = "Istraukite is tustumes ir supilkite i pazymeta talpykla." +lore1 = "Enderio Perlas + Kompasas = Relikvijoriaus Prievado Raktas" +lore2 = "Sis daiktas leidzia nuotoliniu budu pasiekti konteinerius" +lore3 = "Pagamine daikta paziurekite i ji, kad pamatytumete vartosena" +notcontainer = "Tai ne konteineris" +lore = ["Enderio Perlas + Kompasas = Relikvijoriaus Prievado Raktas", "Sis daiktas leidzia nuotoliniu budu pasiekti konteinerius", "Pagamine daikta paziurekite i ji, kad pamatytumete vartosena"] [rift.blink] - name = "Plysio Teleportacija" - description = "Momentine teleportacija trumpu nuotoliu, vos akimirksniu!" - lore1 = "Blokai teleportacijai (2x Vertikaliai)" - lore2 = "Sprinto metu: Dukart pasokite, kad " - lore3 = "Teleportuotumetės" - lore = ["Blokai teleportacijai (2x Vertikaliai)", "Sprinto metu: Dukart pasokite, kad ", "Teleportuotumetės"] +name = "Plysio Teleportacija" +description = "Momentine teleportacija trumpu nuotoliu, vos akimirksniu!" +lore1 = "Blokai teleportacijai (2x Vertikaliai)" +lore2 = "Sprinto metu: Dukart pasokite, kad " +lore3 = "Teleportuotumetės" +lore = ["Blokai teleportacijai (2x Vertikaliai)", "Sprinto metu: Dukart pasokite, kad ", "Teleportuotumetės"] [rift.chest] - name = "Lengva Enderio Skrynia" - description = "Atidarykite enderio skrynia kairiuoju peles mygtuku, laikydami ja rankoje." - lore1 = "Spauskite ant Enderio Skrynios rankoje, kad atidarytumete (deti nereikia)" - lore = ["Spauskite ant Enderio Skrynios rankoje, kad atidarytumete (deti nereikia)"] +name = "Lengva Enderio Skrynia" +description = "Atidarykite enderio skrynia kairiuoju peles mygtuku, laikydami ja rankoje." +lore1 = "Spauskite ant Enderio Skrynios rankoje, kad atidarytumete (deti nereikia)" +lore = ["Spauskite ant Enderio Skrynios rankoje, kad atidarytumete (deti nereikia)"] [rift.descent] - name = "Anti-Levitacija" - description = "Ar pavargote buti istrige ore? Sis igudis kaip tik jums!" - lore1 = "Tiesiog sneak'inkite, kad nusileistumete, ir krisite mazesniu nei iprasta greiciau!" - lore2 = "Atvesimas:" - lore = ["Tiesiog sneak'inkite, kad nusileistumete, ir krisite mazesniu nei iprasta greiciau!", "Atvesimas:"] +name = "Anti-Levitacija" +description = "Ar pavargote buti istrige ore? Sis igudis kaip tik jums!" +lore1 = "Tiesiog sneak'inkite, kad nusileistumete, ir krisite mazesniu nei iprasta greiciau!" +lore2 = "Atvesimas:" +lore = ["Tiesiog sneak'inkite, kad nusileistumete, ir krisite mazesniu nei iprasta greiciau!", "Atvesimas:"] [rift.gate] - name = "Plysio Vartai" - description = "Teleportuokites i pazymeta vieta." - lore1 = "GAMYBA: Smaragdas + Ametisto suke + Enderio Perlas" - lore2 = "Perskaitykite pries naudodami!" - lore3 = "5s uzdelsimas, " - lore4 = "galite mirti, kol esate sioje animacijoje" - lore = ["GAMYBA: Smaragdas + Ametisto suke + Enderio Perlas", "Perskaitykite pries naudodami!", "5s uzdelsimas, ", "galite mirti, kol esate sioje animacijoje"] +name = "Plysio Vartai" +description = "Teleportuokites i pazymeta vieta." +lore1 = "GAMYBA: Smaragdas + Ametisto suke + Enderio Perlas" +lore2 = "Perskaitykite pries naudodami!" +lore3 = "5s uzdelsimas, " +lore4 = "galite mirti, kol esate sioje animacijoje" +lore = ["GAMYBA: Smaragdas + Ametisto suke + Enderio Perlas", "Perskaitykite pries naudodami!", "5s uzdelsimas, ", "galite mirti, kol esate sioje animacijoje"] [rift.resist] - name = "Plysio Atsparumas" - description = "Igykite apsauga naudodami Enderio daiktus & galimybes" - lore1 = "+ Pasyvus: Suteikia apsauga, kai naudojate Plysio sugebejimus arba Enderio daiktus." - lore2 = "Neieina nesiojamoji Enderio Skrynia, ieina tik tai, ka galite vartoti" - lore = ["+ Pasyvus: Suteikia apsauga, kai naudojate Plysio sugebejimus arba Enderio daiktus.", "Neieina nesiojamoji Enderio Skrynia, ieina tik tai, ka galite vartoti"] +name = "Plysio Atsparumas" +description = "Igykite apsauga naudodami Enderio daiktus & galimybes" +lore1 = "+ Pasyvus: Suteikia apsauga, kai naudojate Plysio sugebejimus arba Enderio daiktus." +lore2 = "Neieina nesiojamoji Enderio Skrynia, ieina tik tai, ka galite vartoti" +lore = ["+ Pasyvus: Suteikia apsauga, kai naudojate Plysio sugebejimus arba Enderio daiktus.", "Neieina nesiojamoji Enderio Skrynia, ieina tik tai, ka galite vartoti"] [rift.visage] - name = "Plysio Vizazas" - description = "Neleidzia Endermenams tapti agresyviems, jei jusu inventoriuje yra Ender perlu." - lore1 = "Endermenai netaps agresyvus, jei jusu inventoriuje yra Ender perlu." - lore = ["Endermenai netaps agresyvus, jei jusu inventoriuje yra Ender perlu."] +name = "Plysio Vizazas" +description = "Neleidzia Endermenams tapti agresyviems, jei jusu inventoriuje yra Ender perlu." +lore1 = "Endermenai netaps agresyvus, jei jusu inventoriuje yra Ender perlu." +lore = ["Endermenai netaps agresyvus, jei jusu inventoriuje yra Ender perlu."] # seaborn [seaborn] [seaborn.oxygen] - name = "Organinis Deguonies Bakas" - description = "Sulaikykite daugiau deguonies savo mazuose plauciuose!" - lore1 = "Deguonies Talpos Padidejimas" - lore = ["Deguonies Talpos Padidejimas"] +name = "Organinis Deguonies Bakas" +description = "Sulaikykite daugiau deguonies savo mazuose plauciuose!" +lore1 = "Deguonies Talpos Padidejimas" +lore = ["Deguonies Talpos Padidejimas"] [seaborn.fishers_fantasy] - name = "Zvejolio Fantazija" - description = "Uzsidirbkite daugiau XP is zvejybos ir gaukite daugiau zuvies!" - lore1 = "Kiekviename lygyje yra galimybe gauti daugiau XP ir zuvies!" - lore = ["Kiekviename lygyje yra galimybe gauti daugiau XP ir zuvies!"] +name = "Zvejolio Fantazija" +description = "Uzsidirbkite daugiau XP is zvejybos ir gaukite daugiau zuvies!" +lore1 = "Kiekviename lygyje yra galimybe gauti daugiau XP ir zuvies!" +lore = ["Kiekviename lygyje yra galimybe gauti daugiau XP ir zuvies!"] [seaborn.haste] - name = "Vezlys Sachtininkas" - description = "Kasdami po vandeniu jus gaunate greita kasima!" - lore1 = "Greitas Kasimas 3 igijamas po vandeniu kasant (prisideda kartu ir Naro burtas), kai isnyksta vandens kvepavimo efektas!" - lore = ["Greitas Kasimas 3 igijamas po vandeniu kasant (prisideda kartu ir Naro burtas), kai isnyksta vandens kvepavimo efektas!"] +name = "Vezlys Sachtininkas" +description = "Kasdami po vandeniu jus gaunate greita kasima!" +lore1 = "Greitas Kasimas 3 igijamas po vandeniu kasant (prisideda kartu ir Naro burtas), kai isnyksta vandens kvepavimo efektas!" +lore = ["Greitas Kasimas 3 igijamas po vandeniu kasant (prisideda kartu ir Naro burtas), kai isnyksta vandens kvepavimo efektas!"] [seaborn.night_vision] - name = "Vezlio Regejimas" - description = "Budami po vandeniu, igyjate naktini matyma" - lore1 = "Tiesiog gaukite naktini matyma budami po vandeniu, kai isnyksta vandens kvepavimo efektas!" - lore = ["Tiesiog gaukite naktini matyma budami po vandeniu, kai isnyksta vandens kvepavimo efektas!"] +name = "Vezlio Regejimas" +description = "Budami po vandeniu, igyjate naktini matyma" +lore1 = "Tiesiog gaukite naktini matyma budami po vandeniu, kai isnyksta vandens kvepavimo efektas!" +lore = ["Tiesiog gaukite naktini matyma budami po vandeniu, kai isnyksta vandens kvepavimo efektas!"] [seaborn.dolphin_grace] - name = "Delfinu Malone" - description = "Plauk kaip delfinas, be delfinu" - lore1 = "+ Pasyvus: gauk " - lore2 = "x greiti (delfinu malone)" - lore3 = "tiksli vokieciu inzinerija - palaukite, tai negerai... Nesuderinama su Gilumu plaukimu" - lore = ["+ Pasyvus: gauk ", "x greiti (delfinu malone)", "tiksli vokieciu inzinerija - palaukite, tai negerai... Nesuderinama su Gilumu plaukimu"] +name = "Delfinu Malone" +description = "Plauk kaip delfinas, be delfinu" +lore1 = "+ Pasyvus: gauk " +lore2 = "x greiti (delfinu malone)" +lore3 = "tiksli vokieciu inzinerija - palaukite, tai negerai... Nesuderinama su Gilumu plaukimu" +lore = ["+ Pasyvus: gauk ", "x greiti (delfinu malone)", "tiksli vokieciu inzinerija - palaukite, tai negerai... Nesuderinama su Gilumu plaukimu"] # stealth [stealth] [stealth.ghost_armor] - name = "Vaiduoklio Sarvai" - description = "Letai generuojami sarvai, kai negaunama zalos, veikia tik 1 smugiui" - lore1 = "Max Sarvai" - lore2 = "Greitis" - lore = ["Max Sarvai", "Greitis"] +name = "Vaiduoklio Sarvai" +description = "Letai generuojami sarvai, kai negaunama zalos, veikia tik 1 smugiui" +lore1 = "Max Sarvai" +lore2 = "Greitis" +lore = ["Max Sarvai", "Greitis"] [stealth.night_vision] - name = "Slaptas Regejimas" - description = "Igykite naktini matyma selinant" - lore1 = "Gauk " - lore2 = "naktini matyma " - lore3 = "selinant" - lore = ["Gauk ", "naktini matyma ", "selinant"] +name = "Slaptas Regejimas" +description = "Igykite naktini matyma selinant" +lore1 = "Gauk " +lore2 = "naktini matyma " +lore3 = "selinant" +lore = ["Gauk ", "naktini matyma ", "selinant"] [stealth.snatch] - name = "Daiktu Paciupimas" - description = "Paciupkite numestus daiktus akimirksniu selinant!" - lore1 = "Paciupimo Spindulys" - lore = ["Paciupimo Spindulys"] +name = "Daiktu Paciupimas" +description = "Paciupkite numestus daiktus akimirksniu selinant!" +lore1 = "Paciupimo Spindulys" +lore = ["Paciupimo Spindulys"] [stealth.speed] - name = "Selinimo Greitis" - description = "Igykite greiti selinant" - lore1 = "Selinimo Greitis" - lore = ["Selinimo Greitis"] +name = "Selinimo Greitis" +description = "Igykite greiti selinant" +lore1 = "Selinimo Greitis" +lore = ["Selinimo Greitis"] [stealth.ender_veil] - name = "Enderveilis" - description = "Daugiau jokiu moliugu, kad butu isvengta Endermano ataku" - lore1 = "Isvengti endermano ataku selinant" - lore2 = "Isvengti visu endermano ataku" - lore = ["Isvengti endermano ataku selinant", "Isvengti visu endermano ataku"] +name = "Enderveilis" +description = "Daugiau jokiu moliugu, kad butu isvengta Endermano ataku" +lore1 = "Isvengti endermano ataku selinant" +lore2 = "Isvengti visu endermano ataku" +lore = ["Isvengti endermano ataku selinant", "Isvengti visu endermano ataku"] # sword [sword] [sword.machete] - name = "Macete" - description = "Lengvai nupjaukite lapija!" - lore1 = "Smugio Spindulys" - lore2 = "Pjovimo veikimo laiko tarpas" - lore3 = "Irankiu Susidevejimas" - lore = ["Smugio Spindulys", "Pjovimo veikimo laiko tarpas", "Irankiu Susidevejimas"] +name = "Macete" +description = "Lengvai nupjaukite lapija!" +lore1 = "Smugio Spindulys" +lore2 = "Pjovimo veikimo laiko tarpas" +lore3 = "Irankiu Susidevejimas" +lore = ["Smugio Spindulys", "Pjovimo veikimo laiko tarpas", "Irankiu Susidevejimas"] [sword.bloody_blade] - name = "Kruvini Asmenys" - description = "Smugiuojant kardu, sukelia kraujavima!" - lore1 = "Smugis i Gyva butybe savo kardu sukelia kraujavima" - lore2 = "Kraujavimo trukme" - lore3 = "Kraujavimo veikimo laiko tarpas" - lore = ["Smugis i Gyva butybe savo kardu sukelia kraujavima", "Kraujavimo trukme", "Kraujavimo veikimo laiko tarpas"] +name = "Kruvini Asmenys" +description = "Smugiuojant kardu, sukelia kraujavima!" +lore1 = "Smugis i Gyva butybe savo kardu sukelia kraujavima" +lore2 = "Kraujavimo trukme" +lore3 = "Kraujavimo veikimo laiko tarpas" +lore = ["Smugis i Gyva butybe savo kardu sukelia kraujavima", "Kraujavimo trukme", "Kraujavimo veikimo laiko tarpas"] [sword.poisoned_blade] - name = "Apnuodyti Asmenys" - description = "Smugiuojant kardu, sukelia nuodus!" - lore1 = "Smogdamas gyvai butybei savo kardu butybe apnuodijama" - lore2 = "Nuodu trukme" - lore3 = "Nuodu veikimo laiko tarpas" - lore = ["Smogdamas gyvai butybei savo kardu butybe apnuodijama", "Nuodu trukme", "Nuodu veikimo laiko tarpas"] +name = "Apnuodyti Asmenys" +description = "Smugiuojant kardu, sukelia nuodus!" +lore1 = "Smogdamas gyvai butybei savo kardu butybe apnuodijama" +lore2 = "Nuodu trukme" +lore3 = "Nuodu veikimo laiko tarpas" +lore = ["Smogdamas gyvai butybei savo kardu butybe apnuodijama", "Nuodu trukme", "Nuodu veikimo laiko tarpas"] # taming [taming] [taming.damage] - name = "Prisijaukinimo Zala" - description = "Padidinkite savo prijaukintam gyvunui daroma zala." - lore1 = "Padidejusi Zala" - lore = ["Padidejusi Zala"] +name = "Prisijaukinimo Zala" +description = "Padidinkite savo prijaukintam gyvunui daroma zala." +lore1 = "Padidejusi Zala" +lore = ["Padidejusi Zala"] [taming.health] - name = "Prisijaukinimo Sveikata" - description = "Pagerinkite savo prijaukintu gyvunu sveikata." - lore1 = "Padidejes gyvybiu skaicius" - lore = ["Padidejes gyvybiu skaicius"] +name = "Prisijaukinimo Sveikata" +description = "Pagerinkite savo prijaukintu gyvunu sveikata." +lore1 = "Padidejes gyvybiu skaicius" +lore = ["Padidejes gyvybiu skaicius"] [taming.regeneration] - name = "Prisijaukinimo Regeneracija" - description = "Padidinkite savo prijaukinto gyvuno regeneracija." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Prisijaukinimo Regeneracija" +description = "Padidinkite savo prijaukinto gyvuno regeneracija." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Spygliai" - description = "Atmuskite zala savo uzpuolikui!" - lore1 = "Atkersija atgal, kai uzpuola" - lore = ["Atkersija atgal, kai uzpuola"] +name = "Spygliai" +description = "Atmuskite zala savo uzpuolikui!" +lore1 = "Atkersija atgal, kai uzpuola" +lore = ["Atkersija atgal, kai uzpuola"] [tragoul.globe] - name = "Skausmo Rutulys" - description = "Padalykite padaryta zala pagal aplink jus esanciu priesu skaiciu!" - lore1 = "Kuo daugiau priesu aplink jus, tuo maziau zalos darote kiekvienam is ju." - lore2 = "Diapazonas: " - lore3 = "Prideta zala visiems subjektams: " - lore = ["Kuo daugiau priesu aplink jus, tuo maziau zalos darote kiekvienam is ju.", "Diapazonas: ", "Prideta zala visiems subjektams: "] +name = "Skausmo Rutulys" +description = "Padalykite padaryta zala pagal aplink jus esanciu priesu skaiciu!" +lore1 = "Kuo daugiau priesu aplink jus, tuo maziau zalos darote kiekvienam is ju." +lore2 = "Diapazonas: " +lore3 = "Prideta zala visiems subjektams: " +lore = ["Kuo daugiau priesu aplink jus, tuo maziau zalos darote kiekvienam is ju.", "Diapazonas: ", "Prideta zala visiems subjektams: "] [tragoul.healing] - name = "Skausmo Valia" - description = "Atkurkite sveikata pagal padaryta zala!" - lore1 = "Zaloti dalykus dar niekada nebuvo taip gerai! Gydymas nuo padarytos zalos" - lore2 = "Yra 3 sekundziu zalos langas, gydymo ir 1 sekundes atvesimas. " - lore3 = "Gydymas pagal zalos procenta: " - lore = ["Zaloti dalykus dar niekada nebuvo taip gerai! Gydymas nuo padarytos zalos", "Yra 3 sekundziu zalos langas, gydymo ir 1 sekundes atvesimas. ", "Gydymas pagal zalos procenta: "] +name = "Skausmo Valia" +description = "Atkurkite sveikata pagal padaryta zala!" +lore1 = "Zaloti dalykus dar niekada nebuvo taip gerai! Gydymas nuo padarytos zalos" +lore2 = "Yra 3 sekundziu zalos langas, gydymo ir 1 sekundes atvesimas. " +lore3 = "Gydymas pagal zalos procenta: " +lore = ["Zaloti dalykus dar niekada nebuvo taip gerai! Gydymas nuo padarytos zalos", "Yra 3 sekundziu zalos langas, gydymo ir 1 sekundes atvesimas. ", "Gydymas pagal zalos procenta: "] [tragoul.lance] - name = "Lavonu Ietys" - description = "Nuzudzius priesa arba atlikus gebejima nuzudyti priesa, atsiranda ietis, daranti zala netoliese esanciam priesui!" - lore1 = "Is bet ko, ka nuzudysite, ir jei sis gebejimas nuzudys priesa, bus ieskoma ieties." - lore2 = "Paaukokite dali savo gyvybes, kad sukurtumete ietis (tai gali jus nuzudyti)." - lore3 = "Max Ietys: 1 + " - lore = ["Is bet ko, ka nuzudysite, ir jei sis gebejimas nuzudys priesa, bus ieskoma ieties.", "Paaukokite dali savo gyvybes, kad sukurtumete ietis (tai gali jus nuzudyti).", "Max Ietys: 1 + "] +name = "Lavonu Ietys" +description = "Nuzudzius priesa arba atlikus gebejima nuzudyti priesa, atsiranda ietis, daranti zala netoliese esanciam priesui!" +lore1 = "Is bet ko, ka nuzudysite, ir jei sis gebejimas nuzudys priesa, bus ieskoma ieties." +lore2 = "Paaukokite dali savo gyvybes, kad sukurtumete ietis (tai gali jus nuzudyti)." +lore3 = "Max Ietys: 1 + " +lore = ["Is bet ko, ka nuzudysite, ir jei sis gebejimas nuzudys priesa, bus ieskoma ieties.", "Paaukokite dali savo gyvybes, kad sukurtumete ietis (tai gali jus nuzudyti).", "Max Ietys: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Stiklo Patranka" - description = "Bonusas uz beginkle zala, tuo mazesne jusu sarvu verte" - lore1 = "x Zala prie 0 sarvu" - lore2 = "Pagal lygi bonus zala" - lore = ["x Zala prie 0 sarvu", "Pagal lygi bonus zala"] +name = "Stiklo Patranka" +description = "Bonusas uz beginkle zala, tuo mazesne jusu sarvu verte" +lore1 = "x Zala prie 0 sarvu" +lore2 = "Pagal lygi bonus zala" +lore = ["x Zala prie 0 sarvu", "Pagal lygi bonus zala"] [unarmed.power] - name = "Beginkle Galia" - description = "Patobulinta beginkle zala." - lore1 = "Zala" - lore = ["Zala"] +name = "Beginkle Galia" +description = "Patobulinta beginkle zala." +lore1 = "Zala" +lore = ["Zala"] [unarmed.sucker_punch] - name = "Klastingas Smugis" - description = "Sprinto smugiai, bet mirtingesni." - lore1 = "Zala" - lore2 = "Zala dideja didejant greiciui smugiuojant" - lore = ["Zala", "Zala dideja didejant greiciui smugiuojant"] +name = "Klastingas Smugis" +description = "Sprinto smugiai, bet mirtingesni." +lore1 = "Zala" +lore2 = "Zala dideja didejant greiciui smugiuojant" +lore = ["Zala", "Zala dideja didejant greiciui smugiuojant"] diff --git a/src/main/resources/nl_NL.toml b/src/main/resources/nl_NL.toml index fa8fe8af0..8321f42ef 100644 --- a/src/main/resources/nl_NL.toml +++ b/src/main/resources/nl_NL.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Moet bewegen!" - description = "Loop meer dan 1 kilometer (1.000 blokken)" +title = "Moet bewegen!" +description = "Loop meer dan 1 kilometer (1.000 blokken)" [advancement.challenge_sprint_5k] - title = "Sprint een 5K!" - description = "Loop meer dan 5 kilometer (5.000 blokken)" +title = "Sprint een 5K!" +description = "Loop meer dan 5 kilometer (5.000 blokken)" [advancement.challenge_sprint_50k] - title = "Zoom een 50K!" - description = "Loop meer dan 50 kilometer (50.000 blokken)" +title = "Zoom een 50K!" +description = "Loop meer dan 50 kilometer (50.000 blokken)" [advancement.challenge_sprint_500k] - title = "Doorkruis het heelal!!" - description = "Loop meer dan 500 kilometer (500.000 blokken)" +title = "Doorkruis het heelal!!" +description = "Loop meer dan 500 kilometer (500.000 blokken)" [advancement.challenge_sprint_marathon] - title = "Sprint een (letterlijke) Marathon!" - description = "Sprint meer dan 42.195 blokken!" +title = "Sprint een (letterlijke) Marathon!" +description = "Sprint meer dan 42.195 blokken!" [advancement.challenge_place_1k] - title = "Beginnende Bouwer!" - description = "Plaats 1.000 blokken" +title = "Beginnende Bouwer!" +description = "Plaats 1.000 blokken" [advancement.challenge_place_5k] - title = "Gemiddelde Bouwer!" - description = "Plaats 5.000 blokken" +title = "Gemiddelde Bouwer!" +description = "Plaats 5.000 blokken" [advancement.challenge_place_50k] - title = "Gevorderde Bouwer!" - description = "Plaats 50.000 blokken" +title = "Gevorderde Bouwer!" +description = "Plaats 50.000 blokken" [advancement.challenge_place_500k] - title = "Meester Bouwer!" - description = "Plaats 500.000 blokken" +title = "Meester Bouwer!" +description = "Plaats 500.000 blokken" [advancement.challenge_place_5m] - title = "Acoliet van de Symmetrie!" - description = "DE WERKELIJKHEID IS JOUW SPEELTUIN! (5 miljoen blokken)" +title = "Acoliet van de Symmetrie!" +description = "DE WERKELIJKHEID IS JOUW SPEELTUIN! (5 miljoen blokken)" [advancement.challenge_chop_1k] - title = "Beginnende Houthakker!" - description = "Hak 1.000 blokken" +title = "Beginnende Houthakker!" +description = "Hak 1.000 blokken" [advancement.challenge_chop_5k] - title = "Gemiddelde Houthakker!" - description = "Hak 5.000 blokken" +title = "Gemiddelde Houthakker!" +description = "Hak 5.000 blokken" [advancement.challenge_chop_50k] - title = "Gevorderde Houthakker!" - description = "Hak 50.000 blokken" +title = "Gevorderde Houthakker!" +description = "Hak 50.000 blokken" [advancement.challenge_chop_500k] - title = "Meester Houthakker!" - description = "Hak 500.000 blokken" +title = "Meester Houthakker!" +description = "Hak 500.000 blokken" [advancement.challenge_chop_5m] - title = "Jackson de Hond" - description = "De allerbeste brave jongen! (5 miljoen blokken)" +title = "Jackson de Hond" +description = "De allerbeste brave jongen! (5 miljoen blokken)" [advancement.challenge_block_1k] - title = "Amper Geblokkeerd!" - description = "Blokkeer 1000 treffers" +title = "Amper Geblokkeerd!" +description = "Blokkeer 1000 treffers" [advancement.challenge_block_5k] - title = "Blokkeren is Leuk!" - description = "Blokkeer 5000 treffers" +title = "Blokkeren is Leuk!" +description = "Blokkeer 5000 treffers" [advancement.challenge_block_50k] - title = "Blokkeren is mijn Leven!" - description = "Blokkeer 50.000 treffers" +title = "Blokkeren is mijn Leven!" +description = "Blokkeer 50.000 treffers" [advancement.challenge_block_500k] - title = "Blokkeren is mijn Doel!" - description = "Blokkeer 500.000 treffers" +title = "Blokkeren is mijn Doel!" +description = "Blokkeer 500.000 treffers" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Blokkeer 5.000.000 treffers" +title = "Die Hand Die Verletzt" +description = "Blokkeer 5.000.000 treffers" [advancement.challenge_brew_1k] - title = "Beginnende Alchemist!" - description = "Consumeer 1000 drankjes" +title = "Beginnende Alchemist!" +description = "Consumeer 1000 drankjes" [advancement.challenge_brew_5k] - title = "Gemiddelde Alchemist!" - description = "Consumeer 5000 drankjes" +title = "Gemiddelde Alchemist!" +description = "Consumeer 5000 drankjes" [advancement.challenge_brew_50k] - title = "Gevorderde Alchemist!" - description = "Consumeer 50.000 drankjes" +title = "Gevorderde Alchemist!" +description = "Consumeer 50.000 drankjes" [advancement.challenge_brew_500k] - title = "Meester Alchemist!" - description = "Consumeer 500.000 drankjes" +title = "Meester Alchemist!" +description = "Consumeer 500.000 drankjes" [advancement.challenge_brew_5m] - title = "De Alchemist" - description = "Consumeer 5.000.000 drankjes" +title = "De Alchemist" +description = "Consumeer 5.000.000 drankjes" [advancement.challenge_brewsplash_1k] - title = "Beginnende Drankje-Gooier!" - description = "Gooi 1000 drankjes" +title = "Beginnende Drankje-Gooier!" +description = "Gooi 1000 drankjes" [advancement.challenge_brewsplash_5k] - title = "Gemiddelde Drankje-Gooier!" - description = "Gooi 5000 drankjes" +title = "Gemiddelde Drankje-Gooier!" +description = "Gooi 5000 drankjes" [advancement.challenge_brewsplash_50k] - title = "Gevorderde Drankje-Gooier!" - description = "Gooi 50.000 drankjes" +title = "Gevorderde Drankje-Gooier!" +description = "Gooi 50.000 drankjes" [advancement.challenge_brewsplash_500k] - title = "Meester Drankje-Gooier!" - description = "Gooi 500.000 drankjes" +title = "Meester Drankje-Gooier!" +description = "Gooi 500.000 drankjes" [advancement.challenge_brewsplash_5m] - title = "De Gooi-Meester" - description = "Gooi 5.000.000 drankjes" +title = "De Gooi-Meester" +description = "Gooi 5.000.000 drankjes" [advancement.challenge_craft_1k] - title = "Handige Knutselaar!" - description = "Maak 1000 voorwerpen" +title = "Handige Knutselaar!" +description = "Maak 1000 voorwerpen" [advancement.challenge_craft_5k] - title = "Chagrijnige Knutselaar!" - description = "Maak 5000 voorwerpen" +title = "Chagrijnige Knutselaar!" +description = "Maak 5000 voorwerpen" [advancement.challenge_craft_50k] - title = "Onderdanige Knutselaar!" - description = "Maak 50.000 voorwerpen" +title = "Onderdanige Knutselaar!" +description = "Maak 50.000 voorwerpen" [advancement.challenge_craft_500k] - title = "Kakofone Knutselaar!" - description = "Maak 500.000 voorwerpen" +title = "Kakofone Knutselaar!" +description = "Maak 500.000 voorwerpen" [advancement.challenge_craft_5m] - title = "Rampzalige McCraftface" - description = "Maak 5.000.000 voorwerpen" +title = "Rampzalige McCraftface" +description = "Maak 5.000.000 voorwerpen" [advancement.challenge_enchant_1k] - title = "Beginnende Betoveraar!" - description = "Betover 1000 voorwerpen" +title = "Beginnende Betoveraar!" +description = "Betover 1000 voorwerpen" [advancement.challenge_enchant_5k] - title = "Gemiddelde Betoveraar!" - description = "Betover 5000 voorwerpen" +title = "Gemiddelde Betoveraar!" +description = "Betover 5000 voorwerpen" [advancement.challenge_enchant_50k] - title = "Gevorderde Betoveraar!" - description = "Betover 50.000 voorwerpen" +title = "Gevorderde Betoveraar!" +description = "Betover 50.000 voorwerpen" [advancement.challenge_enchant_500k] - title = "Meester Betoveraar!" - description = "Betover 500.000 voorwerpen" +title = "Meester Betoveraar!" +description = "Betover 500.000 voorwerpen" [advancement.challenge_enchant_5m] - title = "Raadselachtige Betoveraar" - description = "Betover 5.000.000 voorwerpen" +title = "Raadselachtige Betoveraar" +description = "Betover 5.000.000 voorwerpen" [advancement.challenge_excavate_1k] - title = "Enthousiaste Graver!" - description = "Graaf 1000 blokken op" +title = "Enthousiaste Graver!" +description = "Graaf 1000 blokken op" [advancement.challenge_excavate_5k] - title = "Gemiddelde Graver!" - description = "Graaf 5000 blokken op" +title = "Gemiddelde Graver!" +description = "Graaf 5000 blokken op" [advancement.challenge_excavate_50k] - title = "Gevorderde Graver!" - description = "Graaf 50.000 blokken op" +title = "Gevorderde Graver!" +description = "Graaf 50.000 blokken op" [advancement.challenge_excavate_500k] - title = "Meester Graver!" - description = "Graaf 500.000 blokken op" +title = "Meester Graver!" +description = "Graaf 500.000 blokken op" [advancement.challenge_excavate_5m] - title = "Raadselachtige Graver" - description = "Graaf 5.000.000 blokken op" +title = "Raadselachtige Graver" +description = "Graaf 5.000.000 blokken op" [advancement.horrible_person] - title = "Je bent een verschrikkelijk persoon" - description = "Ondoorgrondelijk, echt waar" +title = "Je bent een verschrikkelijk persoon" +description = "Ondoorgrondelijk, echt waar" [advancement.challenge_turtle_egg_smasher] - title = "Schildpadei-Verbrijzelaar!" - description = "Breek 100 schildpadeieren" +title = "Schildpadei-Verbrijzelaar!" +description = "Breek 100 schildpadeieren" [advancement.challenge_turtle_egg_annihilator] - title = "Schildpadei-Vernietiger!" - description = "Breek 500 schildpadeieren" +title = "Schildpadei-Vernietiger!" +description = "Breek 500 schildpadeieren" [advancement.challenge_novice_hunter] - title = "Beginnende Jager!" - description = "Dood 100 entiteiten" +title = "Beginnende Jager!" +description = "Dood 100 entiteiten" [advancement.challenge_intermediate_hunter] - title = "Gemiddelde Jager!" - description = "Dood 500 entiteiten" +title = "Gemiddelde Jager!" +description = "Dood 500 entiteiten" [advancement.challenge_advanced_hunter] - title = "Gevorderde Jager!" - description = "Dood 5000 entiteiten" +title = "Gevorderde Jager!" +description = "Dood 5000 entiteiten" [advancement.challenge_creeper_conqueror] - title = "Creeper-Veroveraar!" - description = "Dood 50 creepers" +title = "Creeper-Veroveraar!" +description = "Dood 50 creepers" [advancement.challenge_creeper_annihilator] - title = "Creeper-Vernietiger!" - description = "Dood 200 creepers" +title = "Creeper-Vernietiger!" +description = "Dood 200 creepers" [advancement.challenge_pickaxe_1k] - title = "Beginnende Mijnwerker" - description = "Breek 1000 blokken" +title = "Beginnende Mijnwerker" +description = "Breek 1000 blokken" [advancement.challenge_pickaxe_5k] - title = "Bekwame Mijnwerker" - description = "Breek 5000 blokken" +title = "Bekwame Mijnwerker" +description = "Breek 5000 blokken" [advancement.challenge_pickaxe_50k] - title = "Deskundige Mijnwerker" - description = "Breek 50.000 blokken" +title = "Deskundige Mijnwerker" +description = "Breek 50.000 blokken" [advancement.challenge_pickaxe_500k] - title = "Meester Mijnwerker" - description = "Breek 500.000 blokken" +title = "Meester Mijnwerker" +description = "Breek 500.000 blokken" [advancement.challenge_pickaxe_5m] - title = "Legendarische Mijnwerker" - description = "Breek 5.000.000 blokken" +title = "Legendarische Mijnwerker" +description = "Breek 5.000.000 blokken" [advancement.challenge_eat_100] - title = "Zoveel te eten!" - description = "Eet meer dan 100 items!" +title = "Zoveel te eten!" +description = "Eet meer dan 100 items!" [advancement.challenge_eat_1000] - title = "Onlesbare Honger!" - description = "Eet meer dan 1.000 items!" +title = "Onlesbare Honger!" +description = "Eet meer dan 1.000 items!" [advancement.challenge_eat_10000] - title = "EEUWIGDURENDE HONGER!" - description = "Eet meer dan 10.000 items!" +title = "EEUWIGDURENDE HONGER!" +description = "Eet meer dan 10.000 items!" [advancement.challenge_harvest_100] - title = "Volledige Oogst" - description = "Oogst meer dan 100 gewassen!" +title = "Volledige Oogst" +description = "Oogst meer dan 100 gewassen!" [advancement.challenge_harvest_1000] - title = "Grote Oogst" - description = "Oogst meer dan 1.000 gewassen!" +title = "Grote Oogst" +description = "Oogst meer dan 1.000 gewassen!" [advancement.challenge_swim_1nm] - title = "Menselijke Onderzeeboot!" - description = "Zwem 1 zeemijl (1.852 blokken)" +title = "Menselijke Onderzeeboot!" +description = "Zwem 1 zeemijl (1.852 blokken)" [advancement.challenge_sneak_1k] - title = "Kniepijn" - description = "Sluip meer dan een kilometer (1.000 blokken)" +title = "Kniepijn" +description = "Sluip meer dan een kilometer (1.000 blokken)" [advancement.challenge_sneak_5k] - title = "Schaduwloper" - description = "Sluip meer dan 5.000 blokken" +title = "Schaduwloper" +description = "Sluip meer dan 5.000 blokken" [advancement.challenge_sneak_20k] - title = "Geest" - description = "Sluip meer dan 20.000 blokken" +title = "Geest" +description = "Sluip meer dan 20.000 blokken" [advancement.challenge_swim_5k] - title = "Diepzeeduiker" - description = "Zwem meer dan 5.000 blokken" +title = "Diepzeeduiker" +description = "Zwem meer dan 5.000 blokken" [advancement.challenge_swim_20k] - title = "Poseidons Uitverkorene" - description = "Zwem meer dan 20.000 blokken" +title = "Poseidons Uitverkorene" +description = "Zwem meer dan 20.000 blokken" [advancement.challenge_sword_100] - title = "Eerste Bloed" - description = "Land 100 slagen met een zwaard" +title = "Eerste Bloed" +description = "Land 100 slagen met een zwaard" [advancement.challenge_sword_1k] - title = "Zwaardendanser" - description = "Land 1.000 slagen met een zwaard" +title = "Zwaardendanser" +description = "Land 1.000 slagen met een zwaard" [advancement.challenge_sword_10k] - title = "Duizend Sneden" - description = "Land 10.000 slagen met een zwaard" +title = "Duizend Sneden" +description = "Land 10.000 slagen met een zwaard" [advancement.challenge_unarmed_100] - title = "Kroegvechter" - description = "Land 100 ongewapende slagen" +title = "Kroegvechter" +description = "Land 100 ongewapende slagen" [advancement.challenge_unarmed_1k] - title = "IJzeren Vuisten" - description = "Land 1.000 ongewapende slagen" +title = "IJzeren Vuisten" +description = "Land 1.000 ongewapende slagen" [advancement.challenge_unarmed_10k] - title = "Een Klap" - description = "Land 10.000 ongewapende slagen" +title = "Een Klap" +description = "Land 10.000 ongewapende slagen" [advancement.challenge_trag_1k] - title = "Bloedprijs" - description = "Ontvang 1.000 schade" +title = "Bloedprijs" +description = "Ontvang 1.000 schade" [advancement.challenge_trag_10k] - title = "Rode Vloed" - description = "Ontvang 10.000 schade" +title = "Rode Vloed" +description = "Ontvang 10.000 schade" [advancement.challenge_trag_100k] - title = "Avatar van Lijden" - description = "Ontvang 100.000 schade" +title = "Avatar van Lijden" +description = "Ontvang 100.000 schade" [advancement.challenge_ranged_100] - title = "Schietoefening" - description = "Vuur 100 projectielen af" +title = "Schietoefening" +description = "Vuur 100 projectielen af" [advancement.challenge_ranged_1k] - title = "Havikoog" - description = "Vuur 1.000 projectielen af" +title = "Havikoog" +description = "Vuur 1.000 projectielen af" [advancement.challenge_ranged_10k] - title = "Pijlenstorm" - description = "Vuur 10.000 projectielen af" +title = "Pijlenstorm" +description = "Vuur 10.000 projectielen af" [advancement.challenge_chronos_1h] - title = "Tik Tak" - description = "Breng 1 uur online door" +title = "Tik Tak" +description = "Breng 1 uur online door" [advancement.challenge_chronos_24h] - title = "Zand der Tijd" - description = "Breng 24 uur online door" +title = "Zand der Tijd" +description = "Breng 24 uur online door" [advancement.challenge_chronos_168h] - title = "Tijdloos" - description = "Breng 168 uur (1 week) online door" +title = "Tijdloos" +description = "Breng 168 uur (1 week) online door" [advancement.challenge_nether_50] - title = "Poortwachter van de Hel" - description = "Versla 50 netherwezens" +title = "Poortwachter van de Hel" +description = "Versla 50 netherwezens" [advancement.challenge_nether_500] - title = "Wachter van de Afgrond" - description = "Versla 500 netherwezens" +title = "Wachter van de Afgrond" +description = "Versla 500 netherwezens" [advancement.challenge_nether_5k] - title = "Heer van de Nether" - description = "Versla 5.000 netherwezens" +title = "Heer van de Nether" +description = "Versla 5.000 netherwezens" [advancement.challenge_rift_50] - title = "Ruimtelijke Afwijking" - description = "Teleporteer 50 keer" +title = "Ruimtelijke Afwijking" +description = "Teleporteer 50 keer" [advancement.challenge_rift_500] - title = "Leegtewandelaar" - description = "Teleporteer 500 keer" +title = "Leegtewandelaar" +description = "Teleporteer 500 keer" [advancement.challenge_rift_5k] - title = "Tussen Werelden" - description = "Teleporteer 5.000 keer" +title = "Tussen Werelden" +description = "Teleporteer 5.000 keer" [advancement.challenge_taming_10] - title = "Dierenfluisteraar" - description = "Fok 10 dieren" +title = "Dierenfluisteraar" +description = "Fok 10 dieren" [advancement.challenge_taming_50] - title = "Roedelleider" - description = "Fok 50 dieren" +title = "Roedelleider" +description = "Fok 50 dieren" [advancement.challenge_taming_500] - title = "Beestenmeester" - description = "Fok 500 dieren" +title = "Beestenmeester" +description = "Fok 500 dieren" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Snelheidsdemon" - description = "Sprint meer dan 5 Kilometer (5,000 blokken)" +title = "Snelheidsdemon" +description = "Sprint meer dan 5 Kilometer (5,000 blokken)" [advancement.challenge_sprint_dist_50k] - title = "Bliksembenen" - description = "Sprint meer dan 50 Kilometer (50,000 blokken)" +title = "Bliksembenen" +description = "Sprint meer dan 50 Kilometer (50,000 blokken)" [advancement.challenge_agility_swim_1k] - title = "Waterloper" - description = "Zwem meer dan 1 Kilometer (1,000 blokken)" +title = "Waterloper" +description = "Zwem meer dan 1 Kilometer (1,000 blokken)" [advancement.challenge_agility_swim_10k] - title = "Aquatische Reiziger" - description = "Zwem meer dan 10 Kilometer (10,000 blokken)" +title = "Aquatische Reiziger" +description = "Zwem meer dan 10 Kilometer (10,000 blokken)" [advancement.challenge_fly_1k] - title = "Hemeldanser" - description = "Vlieg meer dan 1 Kilometer (1,000 blokken)" +title = "Hemeldanser" +description = "Vlieg meer dan 1 Kilometer (1,000 blokken)" [advancement.challenge_fly_10k] - title = "Windrijder" - description = "Vlieg meer dan 10 Kilometer (10,000 blokken)" +title = "Windrijder" +description = "Vlieg meer dan 10 Kilometer (10,000 blokken)" [advancement.challenge_agility_sneak_500] - title = "Stille Stappen" - description = "Sluip meer dan 500 blokken" +title = "Stille Stappen" +description = "Sluip meer dan 500 blokken" [advancement.challenge_agility_sneak_5k] - title = "Spookstappen" - description = "Sluip meer dan 5 Kilometer (5,000 blokken)" +title = "Spookstappen" +description = "Sluip meer dan 5 Kilometer (5,000 blokken)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Sloopploeg" - description = "Vernietig 500 blokken" +title = "Sloopploeg" +description = "Vernietig 500 blokken" [advancement.challenge_demolish_5k] - title = "Sloopkogel" - description = "Vernietig 5,000 blokken" +title = "Sloopkogel" +description = "Vernietig 5,000 blokken" [advancement.challenge_value_placed_10k] - title = "Waardevolle Bouwer" - description = "Plaats blokken ter waarde van 10,000" +title = "Waardevolle Bouwer" +description = "Plaats blokken ter waarde van 10,000" [advancement.challenge_value_placed_100k] - title = "Meester Architect" - description = "Plaats blokken ter waarde van 100,000" +title = "Meester Architect" +description = "Plaats blokken ter waarde van 100,000" [advancement.challenge_demolish_val_5k] - title = "Bergingsexpert" - description = "Berg 5,000 blokwaarde uit sloop" +title = "Bergingsexpert" +description = "Berg 5,000 blokwaarde uit sloop" [advancement.challenge_demolish_val_50k] - title = "Totale Afbraak" - description = "Berg 50,000 blokwaarde uit sloop" +title = "Totale Afbraak" +description = "Berg 50,000 blokwaarde uit sloop" [advancement.challenge_high_build_100] - title = "Hemelbouwer" - description = "Plaats 100 blokken boven Y=128" +title = "Hemelbouwer" +description = "Plaats 100 blokken boven Y=128" [advancement.challenge_high_build_1k] - title = "Wolkenarchitect" - description = "Plaats 1,000 blokken boven Y=128" +title = "Wolkenarchitect" +description = "Plaats 1,000 blokken boven Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Bijlzwaaier" - description = "Zwaai je bijl 500 keer" +title = "Bijlzwaaier" +description = "Zwaai je bijl 500 keer" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Zwaai je bijl 5,000 keer" +title = "Berserker" +description = "Zwaai je bijl 5,000 keer" [advancement.challenge_axe_damage_1k] - title = "Kliever" - description = "Deel 1,000 schade uit met bijlen" +title = "Kliever" +description = "Deel 1,000 schade uit met bijlen" [advancement.challenge_axe_damage_10k] - title = "Beulsbijl" - description = "Deel 10,000 schade uit met bijlen" +title = "Beulsbijl" +description = "Deel 10,000 schade uit met bijlen" [advancement.challenge_axe_value_5k] - title = "Houthandelaar" - description = "Oogst 5,000 waarde aan hout" +title = "Houthandelaar" +description = "Oogst 5,000 waarde aan hout" [advancement.challenge_axe_value_50k] - title = "Houtbaron" - description = "Oogst 50,000 waarde aan hout" +title = "Houtbaron" +description = "Oogst 50,000 waarde aan hout" [advancement.challenge_leaves_500] - title = "Bladblazer" - description = "Verwijder 500 bladblokken met een bijl" +title = "Bladblazer" +description = "Verwijder 500 bladblokken met een bijl" [advancement.challenge_leaves_5k] - title = "Ontbladeraar" - description = "Verwijder 5,000 bladblokken met een bijl" +title = "Ontbladeraar" +description = "Verwijder 5,000 bladblokken met een bijl" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Schadedemper" - description = "Blokkeer 1,000 schade met een schild" +title = "Schadedemper" +description = "Blokkeer 1,000 schade met een schild" [advancement.challenge_block_dmg_10k] - title = "Menselijk Schild" - description = "Blokkeer 10,000 schade met een schild" +title = "Menselijk Schild" +description = "Blokkeer 10,000 schade met een schild" [advancement.challenge_block_proj_100] - title = "Pijlafweerder" - description = "Blokkeer 100 projectielen met een schild" +title = "Pijlafweerder" +description = "Blokkeer 100 projectielen met een schild" [advancement.challenge_block_proj_1k] - title = "Projectielschild" - description = "Blokkeer 1,000 projectielen met een schild" +title = "Projectielschild" +description = "Blokkeer 1,000 projectielen met een schild" [advancement.challenge_block_melee_500] - title = "Pareermeester" - description = "Blokkeer 500 melee-aanvallen met een schild" +title = "Pareermeester" +description = "Blokkeer 500 melee-aanvallen met een schild" [advancement.challenge_block_melee_5k] - title = "IJzeren Vesting" - description = "Blokkeer 5,000 melee-aanvallen met een schild" +title = "IJzeren Vesting" +description = "Blokkeer 5,000 melee-aanvallen met een schild" [advancement.challenge_block_heavy_50] - title = "Tank" - description = "Blokkeer 50 zware aanvallen (meer dan 5 schade)" +title = "Tank" +description = "Blokkeer 50 zware aanvallen (meer dan 5 schade)" [advancement.challenge_block_heavy_500] - title = "Onbeweegbaar Object" - description = "Blokkeer 500 zware aanvallen (meer dan 5 schade)" +title = "Onbeweegbaar Object" +description = "Blokkeer 500 zware aanvallen (meer dan 5 schade)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Brouwerij Opzet" - description = "Plaats 10 brouwerstandaarden" +title = "Brouwerij Opzet" +description = "Plaats 10 brouwerstandaarden" [advancement.challenge_brew_stands_50] - title = "Drankjesfabriek" - description = "Plaats 50 brouwerstandaarden" +title = "Drankjesfabriek" +description = "Plaats 50 brouwerstandaarden" [advancement.challenge_brew_strong_25] - title = "Sterk Brouwsel" - description = "Verbruik 25 verbeterde drankjes" +title = "Sterk Brouwsel" +description = "Verbruik 25 verbeterde drankjes" [advancement.challenge_brew_strong_250] - title = "Maximale Kracht" - description = "Verbruik 250 verbeterde drankjes" +title = "Maximale Kracht" +description = "Verbruik 250 verbeterde drankjes" [advancement.challenge_brew_splash_hits_50] - title = "Spatzone" - description = "Raak 50 entiteiten met spatdrankjes" +title = "Spatzone" +description = "Raak 50 entiteiten met spatdrankjes" [advancement.challenge_brew_splash_hits_500] - title = "Pestdokter" - description = "Raak 500 entiteiten met spatdrankjes" +title = "Pestdokter" +description = "Raak 500 entiteiten met spatdrankjes" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Rusteloze Ziel" - description = "Reis 1 Kilometer terwijl je actief bent" +title = "Rusteloze Ziel" +description = "Reis 1 Kilometer terwijl je actief bent" [advancement.challenge_active_dist_10k] - title = "Padvinder" - description = "Reis 10 Kilometer terwijl je actief bent" +title = "Padvinder" +description = "Reis 10 Kilometer terwijl je actief bent" [advancement.challenge_active_dist_100k] - title = "Eeuwige Beweging" - description = "Reis 100 Kilometer terwijl je actief bent" +title = "Eeuwige Beweging" +description = "Reis 100 Kilometer terwijl je actief bent" [advancement.challenge_beds_10] - title = "Vroege Vogel" - description = "Slaap 10 keer in een bed" +title = "Vroege Vogel" +description = "Slaap 10 keer in een bed" [advancement.challenge_beds_100] - title = "Tijdspringer" - description = "Slaap 100 keer in een bed" +title = "Tijdspringer" +description = "Slaap 100 keer in een bed" [advancement.challenge_chronos_tp_50] - title = "Tijdverschuiving" - description = "Teleporteer 50 keer" +title = "Tijdverschuiving" +description = "Teleporteer 50 keer" [advancement.challenge_chronos_tp_500] - title = "Tijdkronkel" - description = "Teleporteer 500 keer" +title = "Tijdkronkel" +description = "Teleporteer 500 keer" [advancement.challenge_chronos_deaths_10] - title = "Sterfelijk" - description = "Sterf 10 keer" +title = "Sterfelijk" +description = "Sterf 10 keer" [advancement.challenge_chronos_deaths_100] - title = "Doodstarter" - description = "Sterf 100 keer" +title = "Doodstarter" +description = "Sterf 100 keer" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Ambachtelijke Waarde" - description = "Maak items ter waarde van 10,000 totaal" +title = "Ambachtelijke Waarde" +description = "Maak items ter waarde van 10,000 totaal" [advancement.challenge_craft_value_100k] - title = "Ambachtsman" - description = "Maak items ter waarde van 100,000 totaal" +title = "Ambachtsman" +description = "Maak items ter waarde van 100,000 totaal" [advancement.challenge_craft_tools_25] - title = "Gereedschapmaker" - description = "Maak 25 gereedschappen" +title = "Gereedschapmaker" +description = "Maak 25 gereedschappen" [advancement.challenge_craft_tools_250] - title = "Meestersmid" - description = "Maak 250 gereedschappen" +title = "Meestersmid" +description = "Maak 250 gereedschappen" [advancement.challenge_craft_armor_25] - title = "Wapensmid" - description = "Maak 25 stukken wapenrusting" +title = "Wapensmid" +description = "Maak 25 stukken wapenrusting" [advancement.challenge_craft_armor_250] - title = "Meester Wapensmid" - description = "Maak 250 stukken wapenrusting" +title = "Meester Wapensmid" +description = "Maak 250 stukken wapenrusting" [advancement.challenge_craft_mass_25k] - title = "Massaproductie" - description = "Maak 25,000 items" +title = "Massaproductie" +description = "Maak 25,000 items" [advancement.challenge_craft_mass_250k] - title = "Industriele Revolutie" - description = "Maak 250,000 items" +title = "Industriele Revolutie" +description = "Maak 250,000 items" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Verzamelaar" - description = "Ontdek 50 unieke items" +title = "Verzamelaar" +description = "Ontdek 50 unieke items" [advancement.challenge_discover_items_250] - title = "Catalogiseerder" - description = "Ontdek 250 unieke items" +title = "Catalogiseerder" +description = "Ontdek 250 unieke items" [advancement.challenge_discover_blocks_50] - title = "Landmeter" - description = "Ontdek 50 unieke blokken" +title = "Landmeter" +description = "Ontdek 50 unieke blokken" [advancement.challenge_discover_blocks_250] - title = "Geoloog" - description = "Ontdek 250 unieke blokken" +title = "Geoloog" +description = "Ontdek 250 unieke blokken" [advancement.challenge_discover_mobs_25] - title = "Waarnemer" - description = "Ontdek 25 unieke mobs" +title = "Waarnemer" +description = "Ontdek 25 unieke mobs" [advancement.challenge_discover_mobs_75] - title = "Naturalist" - description = "Ontdek 75 unieke mobs" +title = "Naturalist" +description = "Ontdek 75 unieke mobs" [advancement.challenge_discover_biomes_10] - title = "Zwerver" - description = "Ontdek 10 unieke biomen" +title = "Zwerver" +description = "Ontdek 10 unieke biomen" [advancement.challenge_discover_biomes_40] - title = "Wereldreiziger" - description = "Ontdek 40 unieke biomen" +title = "Wereldreiziger" +description = "Ontdek 40 unieke biomen" [advancement.challenge_discover_foods_10] - title = "Fijnproever" - description = "Ontdek 10 unieke voedselsoorten" +title = "Fijnproever" +description = "Ontdek 10 unieke voedselsoorten" [advancement.challenge_discover_foods_30] - title = "Culinair Meester" - description = "Ontdek 30 unieke voedselsoorten" +title = "Culinair Meester" +description = "Ontdek 30 unieke voedselsoorten" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Krachtwever" - description = "Verzamel 100 betoveringskracht" +title = "Krachtwever" +description = "Verzamel 100 betoveringskracht" [advancement.challenge_enchant_power_1k] - title = "Arcane Meester" - description = "Verzamel 1,000 betoveringskracht" +title = "Arcane Meester" +description = "Verzamel 1,000 betoveringskracht" [advancement.challenge_enchant_levels_1k] - title = "Levelverbruiker" - description = "Besteed 1,000 ervaringsniveaus aan betoveringen" +title = "Levelverbruiker" +description = "Besteed 1,000 ervaringsniveaus aan betoveringen" [advancement.challenge_enchant_levels_10k] - title = "Ervaringsput" - description = "Besteed 10,000 ervaringsniveaus aan betoveringen" +title = "Ervaringsput" +description = "Besteed 10,000 ervaringsniveaus aan betoveringen" [advancement.challenge_enchant_high_25] - title = "Hoge Inzet" - description = "Voer 25 betoveringen op maximaal niveau uit" +title = "Hoge Inzet" +description = "Voer 25 betoveringen op maximaal niveau uit" [advancement.challenge_enchant_high_250] - title = "Legendarische Betoveraar" - description = "Voer 250 betoveringen op maximaal niveau uit" +title = "Legendarische Betoveraar" +description = "Voer 250 betoveringen op maximaal niveau uit" [advancement.challenge_enchant_total_500] - title = "Levelverbrander" - description = "Besteed 500 totale niveaus aan alle betoveringen" +title = "Levelverbrander" +description = "Besteed 500 totale niveaus aan alle betoveringen" [advancement.challenge_enchant_total_5k] - title = "Arcane Investering" - description = "Besteed 5,000 totale niveaus aan alle betoveringen" +title = "Arcane Investering" +description = "Besteed 5,000 totale niveaus aan alle betoveringen" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Graver" - description = "Zwaai je schop 500 keer" +title = "Graver" +description = "Zwaai je schop 500 keer" [advancement.challenge_dig_swing_5k] - title = "Graafmachine" - description = "Zwaai je schop 5,000 keer" +title = "Graafmachine" +description = "Zwaai je schop 5,000 keer" [advancement.challenge_dig_damage_1k] - title = "Schopridder" - description = "Deel 1,000 schade uit met een schop" +title = "Schopridder" +description = "Deel 1,000 schade uit met een schop" [advancement.challenge_dig_damage_10k] - title = "Schopmeester" - description = "Deel 10,000 schade uit met een schop" +title = "Schopmeester" +description = "Deel 10,000 schade uit met een schop" [advancement.challenge_dig_value_5k] - title = "Aardhandelaar" - description = "Graaf 5,000 waarde aan blokken op" +title = "Aardhandelaar" +description = "Graaf 5,000 waarde aan blokken op" [advancement.challenge_dig_value_50k] - title = "Aardbaron" - description = "Graaf 50,000 waarde aan blokken op" +title = "Aardbaron" +description = "Graaf 50,000 waarde aan blokken op" [advancement.challenge_dig_gravel_500] - title = "Grindmaler" - description = "Graaf 500 grind-, zand- of kleiblekken" +title = "Grindmaler" +description = "Graaf 500 grind-, zand- of kleiblekken" [advancement.challenge_dig_gravel_5k] - title = "Zandzifter" - description = "Graaf 5,000 grind-, zand- of kleiblekken" +title = "Zandzifter" +description = "Graaf 5,000 grind-, zand- of kleiblekken" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Zaadzaaier" - description = "Plant 100 gewassen" +title = "Zaadzaaier" +description = "Plant 100 gewassen" [advancement.challenge_plant_1k] - title = "Groene Vingers" - description = "Plant 1,000 gewassen" +title = "Groene Vingers" +description = "Plant 1,000 gewassen" [advancement.challenge_plant_5k] - title = "Landbouwbaron" - description = "Plant 5,000 gewassen" +title = "Landbouwbaron" +description = "Plant 5,000 gewassen" [advancement.challenge_compost_50] - title = "Recycler" - description = "Composteer 50 items" +title = "Recycler" +description = "Composteer 50 items" [advancement.challenge_compost_500] - title = "Bodemverrijker" - description = "Composteer 500 items" +title = "Bodemverrijker" +description = "Composteer 500 items" [advancement.challenge_shear_50] - title = "Schaapscheerder" - description = "Scheer 50 entiteiten" +title = "Schaapscheerder" +description = "Scheer 50 entiteiten" [advancement.challenge_shear_250] - title = "Kudde Meester" - description = "Scheer 250 entiteiten" +title = "Kudde Meester" +description = "Scheer 250 entiteiten" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Jager" - description = "Versla 500 wezens" +title = "Jager" +description = "Versla 500 wezens" [advancement.challenge_kills_5k] - title = "Beul" - description = "Versla 5,000 wezens" +title = "Beul" +description = "Versla 5,000 wezens" [advancement.challenge_boss_1] - title = "Baasuitdager" - description = "Versla een baasmob" +title = "Baasuitdager" +description = "Versla een baasmob" [advancement.challenge_boss_10] - title = "Legendedoder" - description = "Versla 10 baasmobs" +title = "Legendedoder" +description = "Versla 10 baasmobs" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Verwelkt" - description = "Doorsta 500 witherschade" +title = "Verwelkt" +description = "Doorsta 500 witherschade" [advancement.challenge_wither_dmg_5k] - title = "Plaagoverlevende" - description = "Doorsta 5,000 witherschade" +title = "Plaagoverlevende" +description = "Doorsta 5,000 witherschade" [advancement.challenge_wither_skel_25] - title = "Bottenverzamelaar" - description = "Versla 25 witherskeletten" +title = "Bottenverzamelaar" +description = "Versla 25 witherskeletten" [advancement.challenge_wither_skel_250] - title = "Skeletvloek" - description = "Versla 250 witherskeletten" +title = "Skeletvloek" +description = "Versla 250 witherskeletten" [advancement.challenge_wither_boss_1] - title = "Withervernietiger" - description = "Versla de Wither" +title = "Withervernietiger" +description = "Versla de Wither" [advancement.challenge_wither_boss_10] - title = "Netherheerser" - description = "Versla de Wither 10 keer" +title = "Netherheerser" +description = "Versla de Wither 10 keer" [advancement.challenge_roses_10] - title = "Dodentuinier" - description = "Breek 10 witherrosen" +title = "Dodentuinier" +description = "Breek 10 witherrosen" [advancement.challenge_roses_100] - title = "Plaagverzamelaar" - description = "Breek 100 witherrosen" +title = "Plaagverzamelaar" +description = "Breek 100 witherrosen" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Mijnwerkersarm" - description = "Zwaai je houweel 500 keer" +title = "Mijnwerkersarm" +description = "Zwaai je houweel 500 keer" [advancement.challenge_pick_swing_5k] - title = "Tunnelgraver" - description = "Zwaai je houweel 5,000 keer" +title = "Tunnelgraver" +description = "Zwaai je houweel 5,000 keer" [advancement.challenge_pick_damage_1k] - title = "Houweelvechter" - description = "Deel 1,000 schade uit met een houweel" +title = "Houweelvechter" +description = "Deel 1,000 schade uit met een houweel" [advancement.challenge_pick_damage_10k] - title = "Oorlogshouweel" - description = "Deel 10,000 schade uit met een houweel" +title = "Oorlogshouweel" +description = "Deel 10,000 schade uit met een houweel" [advancement.challenge_pick_value_5k] - title = "Edelsteenvinder" - description = "Mijn 5,000 waarde aan blokken" +title = "Edelsteenvinder" +description = "Mijn 5,000 waarde aan blokken" [advancement.challenge_pick_value_50k] - title = "Ertsbaron" - description = "Mijn 50,000 waarde aan blokken" +title = "Ertsbaron" +description = "Mijn 50,000 waarde aan blokken" [advancement.challenge_pick_ores_500] - title = "Goudzoeker" - description = "Mijn 500 ertsblokken" +title = "Goudzoeker" +description = "Mijn 500 ertsblokken" [advancement.challenge_pick_ores_5k] - title = "Meestermijnwerker" - description = "Mijn 5,000 ertsblokken" +title = "Meestermijnwerker" +description = "Mijn 5,000 ertsblokken" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Scherpschutter" - description = "Deel 1,000 afstandsschade uit" +title = "Scherpschutter" +description = "Deel 1,000 afstandsschade uit" [advancement.challenge_ranged_dmg_10k] - title = "Dodelijke Boogschutter" - description = "Deel 10,000 afstandsschade uit" +title = "Dodelijke Boogschutter" +description = "Deel 10,000 afstandsschade uit" [advancement.challenge_ranged_dist_5k] - title = "Lange Afstand" - description = "Schiet projectielen over een totale afstand van 5,000 blokken" +title = "Lange Afstand" +description = "Schiet projectielen over een totale afstand van 5,000 blokken" [advancement.challenge_ranged_dist_50k] - title = "Kilometerschutter" - description = "Schiet projectielen over een totale afstand van 50,000 blokken" +title = "Kilometerschutter" +description = "Schiet projectielen over een totale afstand van 50,000 blokken" [advancement.challenge_ranged_kills_50] - title = "Boogschutter" - description = "Dood 50 mobs met afstandswapens" +title = "Boogschutter" +description = "Dood 50 mobs met afstandswapens" [advancement.challenge_ranged_kills_500] - title = "Meesterboogschutter" - description = "Dood 500 mobs met afstandswapens" +title = "Meesterboogschutter" +description = "Dood 500 mobs met afstandswapens" [advancement.challenge_longshot_25] - title = "Sluipschutter" - description = "Land 25 langeafstandsschoten (meer dan 30 blokken)" +title = "Sluipschutter" +description = "Land 25 langeafstandsschoten (meer dan 30 blokken)" [advancement.challenge_longshot_250] - title = "Arendsoog" - description = "Land 250 langeafstandsschoten (meer dan 30 blokken)" +title = "Arendsoog" +description = "Land 250 langeafstandsschoten (meer dan 30 blokken)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Parelgooier" - description = "Gooi 50 enderparels" +title = "Parelgooier" +description = "Gooi 50 enderparels" [advancement.challenge_rift_pearls_500] - title = "Teleportverslaafde" - description = "Gooi 500 enderparels" +title = "Teleportverslaafde" +description = "Gooi 500 enderparels" [advancement.challenge_rift_enderman_50] - title = "Endermanjager" - description = "Versla 50 endermannen" +title = "Endermanjager" +description = "Versla 50 endermannen" [advancement.challenge_rift_enderman_500] - title = "Leegtesluiper" - description = "Versla 500 endermannen" +title = "Leegtesluiper" +description = "Versla 500 endermannen" [advancement.challenge_rift_dragon_500] - title = "Drakenvechter" - description = "Deel 500 schade toe aan de Ender Draak" +title = "Drakenvechter" +description = "Deel 500 schade toe aan de Ender Draak" [advancement.challenge_rift_dragon_5k] - title = "Drakenvloek" - description = "Deel 5,000 schade toe aan de Ender Draak" +title = "Drakenvloek" +description = "Deel 5,000 schade toe aan de Ender Draak" [advancement.challenge_rift_crystal_10] - title = "Kristalbreker" - description = "Vernietig 10 endkristallen" +title = "Kristalbreker" +description = "Vernietig 10 endkristallen" [advancement.challenge_rift_crystal_100] - title = "Endvernietiger" - description = "Vernietig 100 endkristallen" +title = "Endvernietiger" +description = "Vernietig 100 endkristallen" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Hengelaar" - description = "Vang 25 vissen" +title = "Hengelaar" +description = "Vang 25 vissen" [advancement.challenge_fish_250] - title = "Meestervisser" - description = "Vang 250 vissen" +title = "Meestervisser" +description = "Vang 250 vissen" [advancement.challenge_drowned_25] - title = "Verdronkenjager" - description = "Versla 25 verdronkenen" +title = "Verdronkenjager" +description = "Versla 25 verdronkenen" [advancement.challenge_drowned_250] - title = "Oceaanreiniger" - description = "Versla 250 verdronkenen" +title = "Oceaanreiniger" +description = "Versla 250 verdronkenen" [advancement.challenge_guardian_10] - title = "Bewakerdoder" - description = "Versla 10 bewakers" +title = "Bewakerdoder" +description = "Versla 10 bewakers" [advancement.challenge_guardian_100] - title = "Tempelplunderaar" - description = "Versla 100 bewakers" +title = "Tempelplunderaar" +description = "Versla 100 bewakers" [advancement.challenge_underwater_blocks_100] - title = "Onderwatermijnwerker" - description = "Breek 100 blokken onder water" +title = "Onderwatermijnwerker" +description = "Breek 100 blokken onder water" [advancement.challenge_underwater_blocks_1k] - title = "Aquatisch Ingenieur" - description = "Breek 1,000 blokken onder water" +title = "Aquatisch Ingenieur" +description = "Breek 1,000 blokken onder water" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Rugsteker" - description = "Deel 500 schade uit terwijl je sluipt" +title = "Rugsteker" +description = "Deel 500 schade uit terwijl je sluipt" [advancement.challenge_stealth_dmg_5k] - title = "Stille Moordenaar" - description = "Deel 5,000 schade uit terwijl je sluipt" +title = "Stille Moordenaar" +description = "Deel 5,000 schade uit terwijl je sluipt" [advancement.challenge_stealth_kills_10] - title = "Sluipmoordenaar" - description = "Dood 10 mobs terwijl je sluipt" +title = "Sluipmoordenaar" +description = "Dood 10 mobs terwijl je sluipt" [advancement.challenge_stealth_kills_100] - title = "Schaduwmaaier" - description = "Dood 100 mobs terwijl je sluipt" +title = "Schaduwmaaier" +description = "Dood 100 mobs terwijl je sluipt" [advancement.challenge_stealth_time_1h] - title = "Geduldig" - description = "Breng 1 uur sluipend door (3,600 seconden)" +title = "Geduldig" +description = "Breng 1 uur sluipend door (3,600 seconden)" [advancement.challenge_stealth_time_10h] - title = "Meester der Schaduwen" - description = "Breng 10 uur sluipend door (36,000 seconden)" +title = "Meester der Schaduwen" +description = "Breng 10 uur sluipend door (36,000 seconden)" [advancement.challenge_stealth_arrows_50] - title = "Stille Boogschutter" - description = "Schiet 50 pijlen terwijl je sluipt" +title = "Stille Boogschutter" +description = "Schiet 50 pijlen terwijl je sluipt" [advancement.challenge_stealth_arrows_500] - title = "Spookboogschutter" - description = "Schiet 500 pijlen terwijl je sluipt" +title = "Spookboogschutter" +description = "Schiet 500 pijlen terwijl je sluipt" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Zwaardleerling" - description = "Deel 1,000 schade uit met zwaarden" +title = "Zwaardleerling" +description = "Deel 1,000 schade uit met zwaarden" [advancement.challenge_sword_dmg_10k] - title = "Zwaardvechter" - description = "Deel 10,000 schade uit met zwaarden" +title = "Zwaardvechter" +description = "Deel 10,000 schade uit met zwaarden" [advancement.challenge_sword_kills_50] - title = "Duelleur" - description = "Dood 50 mobs met zwaarden" +title = "Duelleur" +description = "Dood 50 mobs met zwaarden" [advancement.challenge_sword_kills_500] - title = "Gladiator" - description = "Dood 500 mobs met zwaarden" +title = "Gladiator" +description = "Dood 500 mobs met zwaarden" [advancement.challenge_sword_crit_50] - title = "Kritieke Klap" - description = "Land 50 kritieke treffers met zwaarden" +title = "Kritieke Klap" +description = "Land 50 kritieke treffers met zwaarden" [advancement.challenge_sword_crit_500] - title = "Precisiemeester" - description = "Land 500 kritieke treffers met zwaarden" +title = "Precisiemeester" +description = "Land 500 kritieke treffers met zwaarden" [advancement.challenge_sword_heavy_25] - title = "Zware Klap" - description = "Land 25 zware treffers met zwaarden (meer dan 8 schade)" +title = "Zware Klap" +description = "Land 25 zware treffers met zwaarden (meer dan 8 schade)" [advancement.challenge_sword_heavy_250] - title = "Verwoestende Slag" - description = "Land 250 zware treffers met zwaarden (meer dan 8 schade)" +title = "Verwoestende Slag" +description = "Land 250 zware treffers met zwaarden (meer dan 8 schade)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Beestentrainer" - description = "Je huisdieren delen 500 totale schade uit" +title = "Beestentrainer" +description = "Je huisdieren delen 500 totale schade uit" [advancement.challenge_pet_dmg_5k] - title = "Oorlogsmeester" - description = "Je huisdieren delen 5,000 totale schade uit" +title = "Oorlogsmeester" +description = "Je huisdieren delen 5,000 totale schade uit" [advancement.challenge_tamed_10] - title = "Dierenvriend" - description = "Tem 10 dieren" +title = "Dierenvriend" +description = "Tem 10 dieren" [advancement.challenge_tamed_100] - title = "Dierentuinverzorger" - description = "Tem 100 dieren" +title = "Dierentuinverzorger" +description = "Tem 100 dieren" [advancement.challenge_pet_kills_25] - title = "Roedeltactiek" - description = "Je huisdieren verslaan 25 wezens" +title = "Roedeltactiek" +description = "Je huisdieren verslaan 25 wezens" [advancement.challenge_pet_kills_250] - title = "Alfacommandant" - description = "Je huisdieren verslaan 250 wezens" +title = "Alfacommandant" +description = "Je huisdieren verslaan 250 wezens" [advancement.challenge_taming_2500] - title = "Fokexpert" - description = "Fok 2,500 dieren" +title = "Fokexpert" +description = "Fok 2,500 dieren" [advancement.challenge_taming_25k] - title = "Geneticameester" - description = "Fok 25,000 dieren" +title = "Geneticameester" +description = "Fok 25,000 dieren" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Bokszak" - description = "Ontvang 500 treffers" +title = "Bokszak" +description = "Ontvang 500 treffers" [advancement.challenge_trag_hits_5k] - title = "Gulzig voor Straf" - description = "Ontvang 5,000 treffers" +title = "Gulzig voor Straf" +description = "Ontvang 5,000 treffers" [advancement.challenge_trag_deaths_10] - title = "Negen Levens" - description = "Sterf 10 keer" +title = "Negen Levens" +description = "Sterf 10 keer" [advancement.challenge_trag_deaths_100] - title = "Terugkerende Nachtmerrie" - description = "Sterf 100 keer" +title = "Terugkerende Nachtmerrie" +description = "Sterf 100 keer" [advancement.challenge_trag_fire_500] - title = "Brandslachtoffer" - description = "Doorsta 500 vuurschade" +title = "Brandslachtoffer" +description = "Doorsta 500 vuurschade" [advancement.challenge_trag_fire_5k] - title = "Feniks" - description = "Doorsta 5,000 vuurschade" +title = "Feniks" +description = "Doorsta 5,000 vuurschade" [advancement.challenge_trag_fall_500] - title = "Zwaartekrachttest" - description = "Doorsta 500 valschade" +title = "Zwaartekrachttest" +description = "Doorsta 500 valschade" [advancement.challenge_trag_fall_5k] - title = "Eindsnelheid" - description = "Doorsta 5,000 valschade" +title = "Eindsnelheid" +description = "Doorsta 5,000 valschade" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Vechtersbaas" - description = "Deel 1,000 schade uit met blote vuisten" +title = "Vechtersbaas" +description = "Deel 1,000 schade uit met blote vuisten" [advancement.challenge_unarmed_dmg_10k] - title = "Vechtsportmeester" - description = "Deel 10,000 schade uit met blote vuisten" +title = "Vechtsportmeester" +description = "Deel 10,000 schade uit met blote vuisten" [advancement.challenge_unarmed_kills_25] - title = "Blote Knokkel" - description = "Dood 25 mobs met blote vuisten" +title = "Blote Knokkel" +description = "Dood 25 mobs met blote vuisten" [advancement.challenge_unarmed_kills_250] - title = "Vuist der Legende" - description = "Dood 250 mobs met blote vuisten" +title = "Vuist der Legende" +description = "Dood 250 mobs met blote vuisten" [advancement.challenge_unarmed_crit_25] - title = "Kritieke Stomp" - description = "Land 25 kritieke treffers met blote vuisten" +title = "Kritieke Stomp" +description = "Land 25 kritieke treffers met blote vuisten" [advancement.challenge_unarmed_crit_250] - title = "Precisiepunch" - description = "Land 250 kritieke treffers met blote vuisten" +title = "Precisiepunch" +description = "Land 250 kritieke treffers met blote vuisten" [advancement.challenge_unarmed_heavy_25] - title = "Krachtstomp" - description = "Land 25 zware treffers met blote vuisten (meer dan 6 schade)" +title = "Krachtstomp" +description = "Land 25 zware treffers met blote vuisten (meer dan 6 schade)" [advancement.challenge_unarmed_heavy_250] - title = "Knockoutkoning" - description = "Land 250 zware treffers met blote vuisten (meer dan 6 schade)" +title = "Knockoutkoning" +description = "Land 250 zware treffers met blote vuisten (meer dan 6 schade)" # items [items] [items.bound_ender_peral] - name = "Reliekschrijn Portkey" - usage1 = "Shift + Linkerklik om te binden" - usage2 = "Rechtsklik om de gebonden inventaris te openen" +name = "Reliekschrijn Portkey" +usage1 = "Shift + Linkerklik om te binden" +usage2 = "Rechtsklik om de gebonden inventaris te openen" [items.bound_eye_of_ender] - name = "Oculair Anker" - usage1 = "Rechtsklik om te consumeren en naar de gebonden locatie te teleporteren" - usage2 = "Shift + Linkerklik om aan een blok te binden" +name = "Oculair Anker" +usage1 = "Rechtsklik om te consumeren en naar de gebonden locatie te teleporteren" +usage2 = "Shift + Linkerklik om aan een blok te binden" [items.bound_redstone_torch] - name = "Redstone-Afstandsbediening" - usage1 = "Rechtsklik om een 1-Tick Redstone-puls te maken" - usage2 = "Shift + Linkerklik op een 'Doel'-blok om te binden" +name = "Redstone-Afstandsbediening" +usage1 = "Rechtsklik om een 1-Tick Redstone-puls te maken" +usage2 = "Shift + Linkerklik op een 'Doel'-blok om te binden" [items.bound_snowball] - name = "Webstrik!" - usage1 = "Gooi om een tijdelijke webval op de locatie te maken" +name = "Webstrik!" +usage1 = "Gooi om een tijdelijke webval op de locatie te maken" [items.chrono_time_bottle] - name = "Tijd in een Fles" - usage1 = "Slaat passief tijd op terwijl het in je inventaris zit" - usage2 = "Rechtsklik op getimede blokken of babydieren om opgeslagen tijd te besteden" - stored = "Opgeslagen Tijd" +name = "Tijd in een Fles" +usage1 = "Slaat passief tijd op terwijl het in je inventaris zit" +usage2 = "Rechtsklik op getimede blokken of babydieren om opgeslagen tijd te besteden" +stored = "Opgeslagen Tijd" [items.chrono_time_bomb] - name = "Tijdbom" - usage1 = "Rechtsklik om een chronobolt te lanceren die een temporeel veld creëert" +name = "Tijdbom" +usage1 = "Rechtsklik om een chronobolt te lanceren die een temporeel veld creëert" [items.elevator_block] - name = "Liftblok" - usage1 = "Spring om omhoog te teleporteren" - usage2 = "Shift om omlaag te teleporteren" - usage3 = "Minimaal 2 luchtblokken tussen de liften" +name = "Liftblok" +usage1 = "Spring om omhoog te teleporteren" +usage2 = "Shift om omlaag te teleporteren" +usage3 = "Minimaal 2 luchtblokken tussen de liften" # snippets [snippets] [snippets.gui] - level = "Niveau" - knowledge = "kennis" - power_used = "Kracht Gebruikt" - not_learned = "Niet Geleerd" - xp = "XP tot" - welcome = "Welkom!" - welcome_back = "Welkom terug!" - xp_bonus_for_time = "XP voor" - max_ability_power = "Maximale Vaardigheidskracht" - unlock_this_by_clicking = "Ontgrendel dit door Rechts te Klikken: " - back = "Terug" - unlearn_all = "Alles afleren" - unlearned_all = "Alles afgeleerd" +level = "Niveau" +knowledge = "kennis" +power_used = "Kracht Gebruikt" +not_learned = "Niet Geleerd" +xp = "XP tot" +welcome = "Welkom!" +welcome_back = "Welkom terug!" +xp_bonus_for_time = "XP voor" +max_ability_power = "Maximale Vaardigheidskracht" +unlock_this_by_clicking = "Ontgrendel dit door Rechts te Klikken: " +back = "Terug" +unlearn_all = "Alles afleren" +unlearned_all = "Alles afgeleerd" [snippets.adapt_menu] - may_not_unlearn = "JE MAG NIET AFLEREN" - may_unlearn = "JE MAG LEREN/AFLEREN" - knowledge_cost = "Kenniskosten" - knowledge_available = "Kennis Beschikbaar" - already_learned = "Al Geleerd" - unlearn_refund = "Klik om Af te Leren en Terug te Krijgen" - no_refunds = "HARDCORE, TERUGBETALINGEN UITGESCHAKELD" - knowledge = "kennis" - click_learn = "Klik om te Leren" - no_knowledge = "(Je hebt geen kennis)" - you_only_have = "Je hebt slechts" - how_to_level_up = "Level vaardigheden omhoog om je maximale kracht te verhogen." - not_enough_power = "Niet genoeg kracht! Elk vaardigheidsniveau kost 1 kracht." - power = "kracht" - power_drain = "Krachtverbruik" - learned = "Geleerd " - unlearned = "Afgeleerd " - activator_block = "Boekenplank" +may_not_unlearn = "JE MAG NIET AFLEREN" +may_unlearn = "JE MAG LEREN/AFLEREN" +knowledge_cost = "Kenniskosten" +knowledge_available = "Kennis Beschikbaar" +already_learned = "Al Geleerd" +unlearn_refund = "Klik om Af te Leren en Terug te Krijgen" +no_refunds = "HARDCORE, TERUGBETALINGEN UITGESCHAKELD" +knowledge = "kennis" +click_learn = "Klik om te Leren" +no_knowledge = "(Je hebt geen kennis)" +you_only_have = "Je hebt slechts" +how_to_level_up = "Level vaardigheden omhoog om je maximale kracht te verhogen." +not_enough_power = "Niet genoeg kracht! Elk vaardigheidsniveau kost 1 kracht." +power = "kracht" +power_drain = "Krachtverbruik" +learned = "Geleerd " +unlearned = "Afgeleerd " +activator_block = "Boekenplank" [snippets.knowledge_orb] - contains = "bevat" - knowledge = "kennis" - rightclick = "Rechtsklik" - togainknowledge = "om deze kennis te verkrijgen" - knowledge_orb = "Kennisbol" +contains = "bevat" +knowledge = "kennis" +rightclick = "Rechtsklik" +togainknowledge = "om deze kennis te verkrijgen" +knowledge_orb = "Kennisbol" [snippets.experience_orb] - contains = "bevat" - xp = "Ervaring" - rightclick = "Rechtsklik" - togainxp = "om deze ervaring te verkrijgen" - xporb = "Ervaringsbol" +contains = "bevat" +xp = "Ervaring" +rightclick = "Rechtsklik" +togainxp = "om deze ervaring te verkrijgen" +xporb = "Ervaringsbol" # skill [skill] [skill.agility] - name = "Behendigheid" - icon = "⇉" - description = "Behendigheid is het vermogen om snel en soepel te bewegen ondanks obstakels." +name = "Behendigheid" +icon = "⇉" +description = "Behendigheid is het vermogen om snel en soepel te bewegen ondanks obstakels." [skill.architect] - name = "Architect" - icon = "⬧" - description = "Structuren zijn de bouwstenen van de wereld. De werkelijkheid ligt in jouw handen, aan jou om te beheersen." +name = "Architect" +icon = "⬧" +description = "Structuren zijn de bouwstenen van de wereld. De werkelijkheid ligt in jouw handen, aan jou om te beheersen." [skill.axes] - name = "Bijlen" - icon = "🪓" - description1 = "Waarom bomen hakken, als je ook " - description2 = "dingen" - description3 = "kunt hakken, zelfde resultaat!" +name = "Bijlen" +icon = "🪓" +description1 = "Waarom bomen hakken, als je ook " +description2 = "dingen" +description3 = "kunt hakken, zelfde resultaat!" [skill.brewing] - name = "Brouwen" - icon = "❦" - description = "Dubbel Bubbel, Driedubbel Bubbel, Vierdubbel Bubbel - Ik kan dit drankje nog steeds niet in een ketel doen" +name = "Brouwen" +icon = "❦" +description = "Dubbel Bubbel, Driedubbel Bubbel, Vierdubbel Bubbel - Ik kan dit drankje nog steeds niet in een ketel doen" [skill.blocking] - name = "Blokkeren" - icon = "🛡" - description = "Stokken en stenen zullen je botten niet breken, maar een schild wel." +name = "Blokkeren" +icon = "🛡" +description = "Stokken en stenen zullen je botten niet breken, maar een schild wel." [skill.crafting] - name = "Knutselen" - icon = "⌂" - description = "Als er geen stukken meer zijn om te plaatsen, waarom niet gewoon een nieuwe maken?" +name = "Knutselen" +icon = "⌂" +description = "Als er geen stukken meer zijn om te plaatsen, waarom niet gewoon een nieuwe maken?" [skill.discovery] - name = "Ontdekking" - icon = "⚛" - description = "Terwijl je waarneming zich uitbreidt, ontvouwt je geest zich om te ontdekken wat je niet wist." +name = "Ontdekking" +icon = "⚛" +description = "Terwijl je waarneming zich uitbreidt, ontvouwt je geest zich om te ontdekken wat je niet wist." [skill.enchanting] - name = "Betoveren" - icon = "♰" - description = "Waar heb je het over? Profetieen, visioenen, bijgelovig gebrabbel?" +name = "Betoveren" +icon = "♰" +description = "Waar heb je het over? Profetieen, visioenen, bijgelovig gebrabbel?" [skill.excavation] - name = "Graven" - icon = "ᛳ" - description = "Graaf graaf een gat..." +name = "Graven" +icon = "ᛳ" +description = "Graaf graaf een gat..." [skill.herbalism] - name = "Kruidkunde" - icon = "⚘" - description = "Ik kan geen planten vinden, maar wel wat zaadjes en- is dat... wiet?" +name = "Kruidkunde" +icon = "⚘" +description = "Ik kan geen planten vinden, maar wel wat zaadjes en- is dat... wiet?" [skill.hunter] - name = "Jager" - icon = "☠" - description = "Jagen gaat om de reis, niet het resultaat." +name = "Jager" +icon = "☠" +description = "Jagen gaat om de reis, niet het resultaat." [skill.nether] - name = "Nether" - icon = "₪" - description = "Uit de diepten van de Nether zelf." +name = "Nether" +icon = "₪" +description = "Uit de diepten van de Nether zelf." [skill.pickaxe] - name = "Pikhouweel" - icon = "⛏" - description = "Dwergen zijn de mijnwerkers, maar ik heb ook het een en ander geleerd in mijn tijd. IK BEN ZWEEDS" +name = "Pikhouweel" +icon = "⛏" +description = "Dwergen zijn de mijnwerkers, maar ik heb ook het een en ander geleerd in mijn tijd. IK BEN ZWEEDS" [skill.ranged] - name = "Afstandswapens" - icon = "🏹" - description = "Afstand is de sleutel tot overwinning, en de sleutel tot overleven." +name = "Afstandswapens" +icon = "🏹" +description = "Afstand is de sleutel tot overwinning, en de sleutel tot overleven." [skill.rift] - name = "Rift" - icon = "❍" - description = "De Rift is een bijtend harnas, maar jij hebt het harnas getemd." +name = "Rift" +icon = "❍" +description = "De Rift is een bijtend harnas, maar jij hebt het harnas getemd." [skill.seaborne] - name = "Zeevaarder" - icon = "🎣" - description = "Met deze vaardigheid kun je de wonderen van het water beheersen." +name = "Zeevaarder" +icon = "🎣" +description = "Met deze vaardigheid kun je de wonderen van het water beheersen." [skill.stealth] - name = "Stealth" - icon = "☯" - description = "De kunst van het ongeziene. Wandel in de schaduwen." +name = "Stealth" +icon = "☯" +description = "De kunst van het ongeziene. Wandel in de schaduwen." [skill.swords] - name = "Zwaarden" - icon = "⚔" - description = "Bij de kracht van GreyStone!" +name = "Zwaarden" +icon = "⚔" +description = "Bij de kracht van GreyStone!" [skill.taming] - name = "Temmen" - icon = "♥" - description = "De papegaaien en de bijen... en jij?" +name = "Temmen" +icon = "♥" +description = "De papegaaien en de bijen... en jij?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Bloed stroomt door de aderen van het universum. Samengeknepen door jouw handen." +name = "TragOul" +icon = "🗡" +description = "Bloed stroomt door de aderen van het universum. Samengeknepen door jouw handen." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Wind de klok van het universum op, ervaar de stroom. Breek de klok, word hem." +name = "Chronos" +icon = "🕒" +description = "Wind de klok van het universum op, ervaar de stroom. Breek de klok, word hem." [skill.unarmed] - name = "Ongewapend" - icon = "»" - description = "Zonder wapen is niet zonder kracht." +name = "Ongewapend" +icon = "»" +description = "Zonder wapen is niet zonder kracht." # agility [agility] [agility.armor_up] - name = "Pantser-Up" - description = "Krijg meer pantser hoe langer je sprint!" - lore1 = "Maximaal Pantser" - lore2 = "Pantser-Up Tijd" - lore = ["Maximaal Pantser", "Pantser-Up Tijd"] +name = "Pantser-Up" +description = "Krijg meer pantser hoe langer je sprint!" +lore1 = "Maximaal Pantser" +lore2 = "Pantser-Up Tijd" +lore = ["Maximaal Pantser", "Pantser-Up Tijd"] [agility.ladder_slide] - name = "Ladderglijden" - description = "Klim en glijd veel sneller over ladders in beide richtingen." - lore1 = "Laddersnelheid vermenigvuldiger" - lore2 = "Snelle afdalingssnelheid" - lore = ["Laddersnelheid vermenigvuldiger", "Snelle afdalingssnelheid"] +name = "Ladderglijden" +description = "Klim en glijd veel sneller over ladders in beide richtingen." +lore1 = "Laddersnelheid vermenigvuldiger" +lore2 = "Snelle afdalingssnelheid" +lore = ["Laddersnelheid vermenigvuldiger", "Snelle afdalingssnelheid"] [agility.super_jump] - name = "Super Sprong" - description = "Uitzonderlijk hoogtevoordeel." - lore1 = "Maximale Spronghoogte" - lore2 = "Sluip + Spring voor Super Sprong!" - lore = ["Maximale Spronghoogte", "Sluip + Spring voor Super Sprong!"] +name = "Super Sprong" +description = "Uitzonderlijk hoogtevoordeel." +lore1 = "Maximale Spronghoogte" +lore2 = "Sluip + Spring voor Super Sprong!" +lore = ["Maximale Spronghoogte", "Sluip + Spring voor Super Sprong!"] [agility.wall_jump] - name = "Muursprong" - description = "Houd shift ingedrukt in de lucht tegen een muur om je vast te klampen en te springen!" - lore1 = "Max Sprongen" - lore2 = "Spronghoogte" - lore = ["Max Sprongen", "Spronghoogte"] +name = "Muursprong" +description = "Houd shift ingedrukt in de lucht tegen een muur om je vast te klampen en te springen!" +lore1 = "Max Sprongen" +lore2 = "Spronghoogte" +lore = ["Max Sprongen", "Spronghoogte"] [agility.wind_up] - name = "Opwinden" - description = "Word sneller hoe langer je sprint!" - lore1 = "Max Snelheid" - lore2 = "Opwindtijd" - lore = ["Max Snelheid", "Opwindtijd"] +name = "Opwinden" +description = "Word sneller hoe langer je sprint!" +lore1 = "Max Snelheid" +lore2 = "Opwindtijd" +lore = ["Max Snelheid", "Opwindtijd"] # architect [architect] [architect.elevator] - name = "Lift" - description = "Hiermee kun je een lift bouwen om snel verticaal te teleporteren!" - lore1 = "Ontgrendelt liftrecept: X=WOL, Y=ENDERPAREL" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Ontgrendelt liftrecept: X=WOL, Y=ENDERPAREL", "XXX", "XYX", "XXX"] +name = "Lift" +description = "Hiermee kun je een lift bouwen om snel verticaal te teleporteren!" +lore1 = "Ontgrendelt liftrecept: X=WOL, Y=ENDERPAREL" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Ontgrendelt liftrecept: X=WOL, Y=ENDERPAREL", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Magisch Fundament" - description = "Hiermee kun je sluipen en een tijdelijk fundament onder je plaatsen!" - lore1 = "Creeer magisch: " - lore2 = "Blokken onder je!" - lore = ["Creeer magisch: ", "Blokken onder je!"] +name = "Magisch Fundament" +description = "Hiermee kun je sluipen en een tijdelijk fundament onder je plaatsen!" +lore1 = "Creeer magisch: " +lore2 = "Blokken onder je!" +lore = ["Creeer magisch: ", "Blokken onder je!"] [architect.glass] - name = "Silk-Touch Glas" - description = "Hiermee voorkom je het verlies van glasblokken wanneer je ze met een lege hand breekt!" - lore1 = "Je handen krijgen silk touch voor Glas" - lore = ["Je handen krijgen silk touch voor Glas"] +name = "Silk-Touch Glas" +description = "Hiermee voorkom je het verlies van glasblokken wanneer je ze met een lege hand breekt!" +lore1 = "Je handen krijgen silk touch voor Glas" +lore = ["Je handen krijgen silk touch voor Glas"] [architect.wireless_redstone] - name = "Redstone-Afstandsbediening" - description = "Hiermee kun je een redstonefakkel gebruiken om redstone op afstand te schakelen!" - lore1 = "Doelwit + Redstonefakkel + Enderparel = 1 Redstone-Afstandsbediening" - lore = ["Doelwit + Redstonefakkel + Enderparel = 1 Redstone-Afstandsbediening"] +name = "Redstone-Afstandsbediening" +description = "Hiermee kun je een redstonefakkel gebruiken om redstone op afstand te schakelen!" +lore1 = "Doelwit + Redstonefakkel + Enderparel = 1 Redstone-Afstandsbediening" +lore = ["Doelwit + Redstonefakkel + Enderparel = 1 Redstone-Afstandsbediening"] [architect.placement] - name = "Bouwerstaf" - description = "Hiermee kun je meerdere blokken tegelijk plaatsen. Sluip en houd een blok vast dat overeenkomt met het blok waar je naar kijkt en plaats! Mogelijk moet je even bewegen om de selectie te activeren!" - lore1 = "Je hebt" - lore2 = "blokken in je hand nodig om dit te plaatsen" - lore3 = "Een Materialen Bouwerstaf" - lore = ["Je hebt", "blokken in je hand nodig om dit te plaatsen", "Een Materialen Bouwerstaf"] +name = "Bouwerstaf" +description = "Hiermee kun je meerdere blokken tegelijk plaatsen. Sluip en houd een blok vast dat overeenkomt met het blok waar je naar kijkt en plaats! Mogelijk moet je even bewegen om de selectie te activeren!" +lore1 = "Je hebt" +lore2 = "blokken in je hand nodig om dit te plaatsen" +lore3 = "Een Materialen Bouwerstaf" +lore = ["Je hebt", "blokken in je hand nodig om dit te plaatsen", "Een Materialen Bouwerstaf"] # axe [axe] [axe.chop] - name = "Bijlhak" - description = "Hak bomen om door met rechts op het onderste stamblok te klikken!" - lore1 = "Blokken Per Hak" - lore2 = "Hak-Cooldown" - lore3 = "Gereedschapsslijtage" - lore = ["Blokken Per Hak", "Hak-Cooldown", "Gereedschapsslijtage"] +name = "Bijlhak" +description = "Hak bomen om door met rechts op het onderste stamblok te klikken!" +lore1 = "Blokken Per Hak" +lore2 = "Hak-Cooldown" +lore3 = "Gereedschapsslijtage" +lore = ["Blokken Per Hak", "Hak-Cooldown", "Gereedschapsslijtage"] [axe.log_swap] - name = "Lucy's Houtwisselaar" - description = "Verander het type houtblokken in een Werktafel!" - lore1 = "8 stammen van elk type + 1 boompje = 8 stammen van het type van het boompje" - lore = ["8 stammen van elk type + 1 boompje = 8 stammen van het type van het boompje"] +name = "Lucy's Houtwisselaar" +description = "Verander het type houtblokken in een Werktafel!" +lore1 = "8 stammen van elk type + 1 boompje = 8 stammen van het type van het boompje" +lore = ["8 stammen van elk type + 1 boompje = 8 stammen van het type van het boompje"] [axe.drop_to_inventory] - name = "Bijl Drop-naar-Inventaris" +name = "Bijl Drop-naar-Inventaris" [axe.ground_smash] - name = "Bijl Grondinslag" - description = "Spring, sluip vervolgens en sla alle vijanden in de buurt." - lore1 = "Schade" - lore2 = "Blokken Radius" - lore3 = "Kracht" - lore4 = "Inslag-Cooldown" - lore = ["Schade", "Blokken Radius", "Kracht", "Inslag-Cooldown"] +name = "Bijl Grondinslag" +description = "Spring, sluip vervolgens en sla alle vijanden in de buurt." +lore1 = "Schade" +lore2 = "Blokken Radius" +lore3 = "Kracht" +lore4 = "Inslag-Cooldown" +lore = ["Schade", "Blokken Radius", "Kracht", "Inslag-Cooldown"] [axe.leaf_miner] - name = "Bladmijnwerker" - description = "Hiermee kun je grote hoeveelheden bladeren tegelijk breken!" - lore1 = "Sluip en mijn BLADEREN" - lore2 = "bereik van bladmijnbouw" - lore3 = "Je krijgt geen drops van de bladeren (Exploit Preventie)" - lore = ["Sluip en mijn BLADEREN", "bereik van bladmijnbouw", "Je krijgt geen drops van de bladeren (Exploit Preventie)"] +name = "Bladmijnwerker" +description = "Hiermee kun je grote hoeveelheden bladeren tegelijk breken!" +lore1 = "Sluip en mijn BLADEREN" +lore2 = "bereik van bladmijnbouw" +lore3 = "Je krijgt geen drops van de bladeren (Exploit Preventie)" +lore = ["Sluip en mijn BLADEREN", "bereik van bladmijnbouw", "Je krijgt geen drops van de bladeren (Exploit Preventie)"] [axe.wood_miner] - name = "Houtmijnwerker" - description = "Hiermee kun je grote hoeveelheden hout tegelijk breken!" - lore1 = "Sluip en mijn HOUT/STAMMEN (geen planken)" - lore2 = "bereik van houtmijnbouw" - lore3 = "Werkt met Drop-naar-Inventaris" - lore = ["Sluip en mijn HOUT/STAMMEN (geen planken)", "bereik van houtmijnbouw", "Werkt met Drop-naar-Inventaris"] +name = "Houtmijnwerker" +description = "Hiermee kun je grote hoeveelheden hout tegelijk breken!" +lore1 = "Sluip en mijn HOUT/STAMMEN (geen planken)" +lore2 = "bereik van houtmijnbouw" +lore3 = "Werkt met Drop-naar-Inventaris" +lore = ["Sluip en mijn HOUT/STAMMEN (geen planken)", "bereik van houtmijnbouw", "Werkt met Drop-naar-Inventaris"] # brewing [brewing] [brewing.lingering] - name = "Aanhoudend Brouwsel" - description = "Gebrouwen drankjes duren langer!" - lore1 = "Duur" - lore2 = "Duur" - lore = ["Duur", "Duur"] +name = "Aanhoudend Brouwsel" +description = "Gebrouwen drankjes duren langer!" +lore1 = "Duur" +lore2 = "Duur" +lore = ["Duur", "Duur"] [brewing.super_heated] - name = "Superverhit Brouwsel" - description = "Brouwstandaarden werken sneller hoe heter ze zijn." - lore1 = "Per aanrakend vuurblok" - lore2 = "Per aanrakend lavablok" - lore = ["Per aanrakend vuurblok", "Per aanrakend lavablok"] +name = "Superverhit Brouwsel" +description = "Brouwstandaarden werken sneller hoe heter ze zijn." +lore1 = "Per aanrakend vuurblok" +lore2 = "Per aanrakend lavablok" +lore = ["Per aanrakend vuurblok", "Per aanrakend lavablok"] [brewing.darkness] - name = "Gebottelde Duisternis" - description = "Niet zeker waarom je dit nodig hebt, maar alsjeblieft!" - lore1 = "Nachtzichtdrankje + Zwart Beton = Duisternisdrankje (30 seconden)" - lore2 = "Let op: dit voorkomt dat de gebruiker kan sprinten!" - lore = ["Nachtzichtdrankje + Zwart Beton = Duisternisdrankje (30 seconden)", "Let op: dit voorkomt dat de gebruiker kan sprinten!"] +name = "Gebottelde Duisternis" +description = "Niet zeker waarom je dit nodig hebt, maar alsjeblieft!" +lore1 = "Nachtzichtdrankje + Zwart Beton = Duisternisdrankje (30 seconden)" +lore2 = "Let op: dit voorkomt dat de gebruiker kan sprinten!" +lore = ["Nachtzichtdrankje + Zwart Beton = Duisternisdrankje (30 seconden)", "Let op: dit voorkomt dat de gebruiker kan sprinten!"] [brewing.haste] - name = "Gebottelde Haast" - description = "Wanneer efficientie niet genoeg is" - lore1 = "Snelheidsdrankje + Amethist Scherf = Haastdrankje (60 seconden)" - lore2 = "Snelheidsdrankje + Amethist Blok = Haastdrankje-2 (30 seconden)" - lore = ["Snelheidsdrankje + Amethist Scherf = Haastdrankje (60 seconden)", "Snelheidsdrankje + Amethist Blok = Haastdrankje-2 (30 seconden)"] +name = "Gebottelde Haast" +description = "Wanneer efficientie niet genoeg is" +lore1 = "Snelheidsdrankje + Amethist Scherf = Haastdrankje (60 seconden)" +lore2 = "Snelheidsdrankje + Amethist Blok = Haastdrankje-2 (30 seconden)" +lore = ["Snelheidsdrankje + Amethist Scherf = Haastdrankje (60 seconden)", "Snelheidsdrankje + Amethist Blok = Haastdrankje-2 (30 seconden)"] [brewing.absorption] - name = "Gebottelde Absorptie" - description = "Verhard het lichaam!" - lore1 = "Directe Genezing + Kwarts = Absorptiedrankje (60 seconden)" - lore2 = "Directe Genezing + Kwartsblok = Absorptiedrankje-2 (30 seconden)" - lore = ["Directe Genezing + Kwarts = Absorptiedrankje (60 seconden)", "Directe Genezing + Kwartsblok = Absorptiedrankje-2 (30 seconden)"] +name = "Gebottelde Absorptie" +description = "Verhard het lichaam!" +lore1 = "Directe Genezing + Kwarts = Absorptiedrankje (60 seconden)" +lore2 = "Directe Genezing + Kwartsblok = Absorptiedrankje-2 (30 seconden)" +lore = ["Directe Genezing + Kwarts = Absorptiedrankje (60 seconden)", "Directe Genezing + Kwartsblok = Absorptiedrankje-2 (30 seconden)"] [brewing.fatigue] - name = "Gebottelde Vermoeidheid" - description = "Verzwak het lichaam!" - lore1 = "Zwaktedrankje + Slijmbal = Vermoeidheidsdrankje (30 seconden)" - lore2 = "Zwaktedrankje + Slijmblok = Vermoeidheidsdrankje-2 (15 seconden)" - lore = ["Zwaktedrankje + Slijmbal = Vermoeidheidsdrankje (30 seconden)", "Zwaktedrankje + Slijmblok = Vermoeidheidsdrankje-2 (15 seconden)"] +name = "Gebottelde Vermoeidheid" +description = "Verzwak het lichaam!" +lore1 = "Zwaktedrankje + Slijmbal = Vermoeidheidsdrankje (30 seconden)" +lore2 = "Zwaktedrankje + Slijmblok = Vermoeidheidsdrankje-2 (15 seconden)" +lore = ["Zwaktedrankje + Slijmbal = Vermoeidheidsdrankje (30 seconden)", "Zwaktedrankje + Slijmblok = Vermoeidheidsdrankje-2 (15 seconden)"] [brewing.hunger] - name = "Gebottelde Honger" - description = "Voed de onverzadigbare!" - lore1 = "Onhandig Drankje + Rottend Vlees = Hongerdrankje (30 seconden)" - lore2 = "Zwaktedrankje + Rottend Vlees = Hongerdrankje-3 (15 seconden)" - lore = ["Onhandig Drankje + Rottend Vlees = Hongerdrankje (30 seconden)", "Zwaktedrankje + Rottend Vlees = Hongerdrankje-3 (15 seconden)"] +name = "Gebottelde Honger" +description = "Voed de onverzadigbare!" +lore1 = "Onhandig Drankje + Rottend Vlees = Hongerdrankje (30 seconden)" +lore2 = "Zwaktedrankje + Rottend Vlees = Hongerdrankje-3 (15 seconden)" +lore = ["Onhandig Drankje + Rottend Vlees = Hongerdrankje (30 seconden)", "Zwaktedrankje + Rottend Vlees = Hongerdrankje-3 (15 seconden)"] [brewing.nausea] - name = "Gebottelde Misselijkheid" - description = "Je maakt me misselijk!" - lore1 = "Onhandig Drankje + Bruine Paddenstoel = Misselijkheidsdrankje (16 seconden)" - lore2 = "Onhandig Drankje + Karmozijn Schimmel = Misselijkheidsdrankje-2 (8 seconden)" - lore = ["Onhandig Drankje + Bruine Paddenstoel = Misselijkheidsdrankje (16 seconden)", "Onhandig Drankje + Karmozijn Schimmel = Misselijkheidsdrankje-2 (8 seconden)"] +name = "Gebottelde Misselijkheid" +description = "Je maakt me misselijk!" +lore1 = "Onhandig Drankje + Bruine Paddenstoel = Misselijkheidsdrankje (16 seconden)" +lore2 = "Onhandig Drankje + Karmozijn Schimmel = Misselijkheidsdrankje-2 (8 seconden)" +lore = ["Onhandig Drankje + Bruine Paddenstoel = Misselijkheidsdrankje (16 seconden)", "Onhandig Drankje + Karmozijn Schimmel = Misselijkheidsdrankje-2 (8 seconden)"] [brewing.blindness] - name = "Gebottelde Blindheid" - description = "Je bent een verschrikkelijk persoon..." - lore1 = "Onhandig Drankje + Inktzak = Blindheidsdrankje (30 seconden)" - lore2 = "Onhandig Drankje + Lichtgevende Inktzak = Blindheidsdrankje-2 (15 seconden)" - lore = ["Onhandig Drankje + Inktzak = Blindheidsdrankje (30 seconden)", "Onhandig Drankje + Lichtgevende Inktzak = Blindheidsdrankje-2 (15 seconden)"] +name = "Gebottelde Blindheid" +description = "Je bent een verschrikkelijk persoon..." +lore1 = "Onhandig Drankje + Inktzak = Blindheidsdrankje (30 seconden)" +lore2 = "Onhandig Drankje + Lichtgevende Inktzak = Blindheidsdrankje-2 (15 seconden)" +lore = ["Onhandig Drankje + Inktzak = Blindheidsdrankje (30 seconden)", "Onhandig Drankje + Lichtgevende Inktzak = Blindheidsdrankje-2 (15 seconden)"] [brewing.resistance] - name = "Gebottelde Weerstand" - description = "Fortificatie op zijn best!" - lore1 = "Onhandig Drankje + IJzerstaaf = Weerstandsdrankje (60 seconden)" - lore2 = "Onhandig Drankje + IJzerblok = Weerstandsdrankje-2 (30 seconden)" - lore = ["Onhandig Drankje + IJzerstaaf = Weerstandsdrankje (60 seconden)", "Onhandig Drankje + IJzerblok = Weerstandsdrankje-2 (30 seconden)"] +name = "Gebottelde Weerstand" +description = "Fortificatie op zijn best!" +lore1 = "Onhandig Drankje + IJzerstaaf = Weerstandsdrankje (60 seconden)" +lore2 = "Onhandig Drankje + IJzerblok = Weerstandsdrankje-2 (30 seconden)" +lore = ["Onhandig Drankje + IJzerstaaf = Weerstandsdrankje (60 seconden)", "Onhandig Drankje + IJzerblok = Weerstandsdrankje-2 (30 seconden)"] [brewing.health_boost] - name = "Gebotteld Leven" - description = "Wanneer maximale gezondheid niet genoeg is..." - lore1 = "Directe-Genezingsdrankje + Gouden Appel = Gezondheidsboostdrankje (120 seconden)" - lore2 = "Directe-Genezingsdrankje + Betoverde Gouden Appel = Gezondheidsboostdrankje-2 (120 seconden)" - lore = ["Directe-Genezingsdrankje + Gouden Appel = Gezondheidsboostdrankje (120 seconden)", "Directe-Genezingsdrankje + Betoverde Gouden Appel = Gezondheidsboostdrankje-2 (120 seconden)"] +name = "Gebotteld Leven" +description = "Wanneer maximale gezondheid niet genoeg is..." +lore1 = "Directe-Genezingsdrankje + Gouden Appel = Gezondheidsboostdrankje (120 seconden)" +lore2 = "Directe-Genezingsdrankje + Betoverde Gouden Appel = Gezondheidsboostdrankje-2 (120 seconden)" +lore = ["Directe-Genezingsdrankje + Gouden Appel = Gezondheidsboostdrankje (120 seconden)", "Directe-Genezingsdrankje + Betoverde Gouden Appel = Gezondheidsboostdrankje-2 (120 seconden)"] [brewing.decay] - name = "Gebotteld Verval" - description = "Wie wist dat afval zo nuttig zou zijn?" - lore1 = "Zwaktedrankje + Giftige Aardappel = Witherdrankje (16 seconden)" - lore2 = "Zwaktedrankje + Karmozijn Wortels = Witherdrankje-2 (8 seconden)" - lore = ["Zwaktedrankje + Giftige Aardappel = Witherdrankje (16 seconden)", "Zwaktedrankje + Karmozijn Wortels = Witherdrankje-2 (8 seconden)"] +name = "Gebotteld Verval" +description = "Wie wist dat afval zo nuttig zou zijn?" +lore1 = "Zwaktedrankje + Giftige Aardappel = Witherdrankje (16 seconden)" +lore2 = "Zwaktedrankje + Karmozijn Wortels = Witherdrankje-2 (8 seconden)" +lore = ["Zwaktedrankje + Giftige Aardappel = Witherdrankje (16 seconden)", "Zwaktedrankje + Karmozijn Wortels = Witherdrankje-2 (8 seconden)"] [brewing.saturation] - name = "Gebottelde Verzadiging" - description = "Weet je... ik heb niet eens honger..." - lore1 = "Regeneratiedrankje + Gebakken Aardappel = Verzadigingsdrankje" - lore2 = "Regeneratiedrankje + Hooibaal = Verzadigingsdrankje-2" - lore = ["Regeneratiedrankje + Gebakken Aardappel = Verzadigingsdrankje", "Regeneratiedrankje + Hooibaal = Verzadigingsdrankje-2"] +name = "Gebottelde Verzadiging" +description = "Weet je... ik heb niet eens honger..." +lore1 = "Regeneratiedrankje + Gebakken Aardappel = Verzadigingsdrankje" +lore2 = "Regeneratiedrankje + Hooibaal = Verzadigingsdrankje-2" +lore = ["Regeneratiedrankje + Gebakken Aardappel = Verzadigingsdrankje", "Regeneratiedrankje + Hooibaal = Verzadigingsdrankje-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Ketens van Mephistopheles" - description = "Hiermee kun je malienpantser maken" - lore1 = "Het receptis hetzelfde als elk ander, maar met ijzerklompjes in plaats van staafjes" - lore = ["Het receptis hetzelfde als elk ander, maar met ijzerklompjes in plaats van staafjes"] +name = "Ketens van Mephistopheles" +description = "Hiermee kun je malienpantser maken" +lore1 = "Het receptis hetzelfde als elk ander, maar met ijzerklompjes in plaats van staafjes" +lore = ["Het receptis hetzelfde als elk ander, maar met ijzerklompjes in plaats van staafjes"] [blocking.horse_armorer] - name = "Maakbaar Paardenharnas" - description = "Hiermee kun je paardenharnas maken" - lore1 = "Omring een zadel met het materiaal dat je wilt gebruiken om het harnas te maken" - lore = ["Omring een zadel met het materiaal dat je wilt gebruiken om het harnas te maken"] +name = "Maakbaar Paardenharnas" +description = "Hiermee kun je paardenharnas maken" +lore1 = "Omring een zadel met het materiaal dat je wilt gebruiken om het harnas te maken" +lore = ["Omring een zadel met het materiaal dat je wilt gebruiken om het harnas te maken"] [blocking.saddle_crafter] - name = "Maakbaar Zadel" - description = "Maak een zadel met leer" - lore1 = "Recept: 5 Leer:" - lore = ["Recept: 5 Leer:"] +name = "Maakbaar Zadel" +description = "Maak een zadel met leer" +lore1 = "Recept: 5 Leer:" +lore = ["Recept: 5 Leer:"] [blocking.multi_armor] - name = "Multi-Pantser" - description = "Bind Elytra's aan pantser" - lore1 = "Dit is een geweldige vaardigheid om te reizen." - lore2 = "Dynamisch samenvoegen en wisselen van Pantser/Elytra terwijl je vliegt!" - lore3 = "Om samen te voegen, shift-klik een item op een ander in je inventaris." - lore4 = "Om pantser los te koppelen, sluip en drop het item, en het wordt gedemonteerd." - lore5 = "Als je Multi-Pantser wordt vernietigd, verlies je alle items erin." - lore6 = "Ik heb geen pantser nodig, het stelt me teleur..." - lore = ["Dit is een geweldige vaardigheid om te reizen.", "Dynamisch samenvoegen en wisselen van Pantser/Elytra terwijl je vliegt!", "Om samen te voegen, shift-klik een item op een ander in je inventaris.", "Om pantser los te koppelen, sluip en drop het item, en het wordt gedemonteerd.", "Als je Multi-Pantser wordt vernietigd, verlies je alle items erin.", "Ik heb geen pantser nodig, het stelt me teleur..."] +name = "Multi-Pantser" +description = "Bind Elytra's aan pantser" +lore1 = "Dit is een geweldige vaardigheid om te reizen." +lore2 = "Dynamisch samenvoegen en wisselen van Pantser/Elytra terwijl je vliegt!" +lore3 = "Om samen te voegen, shift-klik een item op een ander in je inventaris." +lore4 = "Om pantser los te koppelen, sluip en drop het item, en het wordt gedemonteerd." +lore5 = "Als je Multi-Pantser wordt vernietigd, verlies je alle items erin." +lore6 = "Ik heb geen pantser nodig, het stelt me teleur..." +lore = ["Dit is een geweldige vaardigheid om te reizen.", "Dynamisch samenvoegen en wisselen van Pantser/Elytra terwijl je vliegt!", "Om samen te voegen, shift-klik een item op een ander in je inventaris.", "Om pantser los te koppelen, sluip en drop het item, en het wordt gedemonteerd.", "Als je Multi-Pantser wordt vernietigd, verlies je alle items erin.", "Ik heb geen pantser nodig, het stelt me teleur..."] # crafting [crafting] [crafting.deconstruction] - name = "Deconstructie" - description = "Ontmantel blokken & items tot bruikbare basiscomponenten!" - lore1 = "Laat een voorwerp op de grond vallen." - lore2 = "Sluip vervolgens en rechtsklik met een schaar" - lore = ["Laat een voorwerp op de grond vallen.", "Sluip vervolgens en rechtsklik met een schaar"] +name = "Deconstructie" +description = "Ontmantel blokken & items tot bruikbare basiscomponenten!" +lore1 = "Laat een voorwerp op de grond vallen." +lore2 = "Sluip vervolgens en rechtsklik met een schaar" +lore = ["Laat een voorwerp op de grond vallen.", "Sluip vervolgens en rechtsklik met een schaar"] [crafting.xp] - name = "Knutsel-XP" - description = "Verkrijg passieve XP bij het knutselen" - lore1 = "Verdien XP bij het knutselen" - lore = ["Verdien XP bij het knutselen"] +name = "Knutsel-XP" +description = "Verkrijg passieve XP bij het knutselen" +lore1 = "Verdien XP bij het knutselen" +lore = ["Verdien XP bij het knutselen"] [crafting.reconstruction] - name = "Erts Reconstructie" - description = "Maak ertsen opnieuw van hun basiscomponenten!" - lore1 = "8 van de drops en 1 gastheer = 1 erts (vormloos)" - lore2 = "Drops moeten gesmolten zijn (indien van toepassing)" - lore3 = "Exclusief: Schroot, Kwarts, en Smaragden etc..." - lore4 = "Gastheer = Omhulsel. bijv: Steen, Netherrack, Deepslate" - lore = ["8 van de drops en 1 gastheer = 1 erts (vormloos)", "Drops moeten gesmolten zijn (indien van toepassing)", "Exclusief: Schroot, Kwarts, en Smaragden etc...", "Gastheer = Omhulsel. bijv: Steen, Netherrack, Deepslate"] +name = "Erts Reconstructie" +description = "Maak ertsen opnieuw van hun basiscomponenten!" +lore1 = "8 van de drops en 1 gastheer = 1 erts (vormloos)" +lore2 = "Drops moeten gesmolten zijn (indien van toepassing)" +lore3 = "Exclusief: Schroot, Kwarts, en Smaragden etc..." +lore4 = "Gastheer = Omhulsel. bijv: Steen, Netherrack, Deepslate" +lore = ["8 van de drops en 1 gastheer = 1 erts (vormloos)", "Drops moeten gesmolten zijn (indien van toepassing)", "Exclusief: Schroot, Kwarts, en Smaragden etc...", "Gastheer = Omhulsel. bijv: Steen, Netherrack, Deepslate"] [crafting.leather] - name = "Maakbaar Leer" - description = "Maak leer van rottend vlees" - lore1 = "Gooi het (rottend vlees) gewoon op het kampvuur!" - lore = ["Gooi het (rottend vlees) gewoon op het kampvuur!"] +name = "Maakbaar Leer" +description = "Maak leer van rottend vlees" +lore1 = "Gooi het (rottend vlees) gewoon op het kampvuur!" +lore = ["Gooi het (rottend vlees) gewoon op het kampvuur!"] [crafting.backpacks] - name = "De Rugzakken van een Boutilier!" - description = "Dit brengt gewoon de Mojang Bundel in het spel!" - lore1 = "Je moet in Survival zijn om dit te gebruiken" - lore2 = "XLX : Leer, Leidtouw, Leer" - lore3 = "XSX : Leer, Vat, Leer" - lore4 = "XCX : Leer, Kist, Leer" - lore = ["Je moet in Survival zijn om dit te gebruiken", "XLX : Leer, Leidtouw, Leer", "XSX : Leer, Vat, Leer", "XCX : Leer, Kist, Leer"] +name = "De Rugzakken van een Boutilier!" +description = "Dit brengt gewoon de Mojang Bundel in het spel!" +lore1 = "Je moet in Survival zijn om dit te gebruiken" +lore2 = "XLX : Leer, Leidtouw, Leer" +lore3 = "XSX : Leer, Vat, Leer" +lore4 = "XCX : Leer, Kist, Leer" +lore = ["Je moet in Survival zijn om dit te gebruiken", "XLX : Leer, Leidtouw, Leer", "XSX : Leer, Vat, Leer", "XCX : Leer, Kist, Leer"] [crafting.stations] - name = "Draagbare Tafels!" - description = "Gebruik een werkstation in de palm van je hand!" - lore2 = "ALLE ITEMS DIE JE IN DE TAFEL VERGEET BIJ HET SLUITEN ZIJN VOOR ALTIJD VERLOREN!" - lore3 = "Geldige tafels: Aambeeld, Werktafel, Slijpsteen, Cartografietafel, Steenhouwer, Weefgetouw" - lore = ["ALLE ITEMS DIE JE IN DE TAFEL VERGEET BIJ HET SLUITEN ZIJN VOOR ALTIJD VERLOREN!", "Geldige tafels: Aambeeld, Werktafel, Slijpsteen, Cartografietafel, Steenhouwer, Weefgetouw"] +name = "Draagbare Tafels!" +description = "Gebruik een werkstation in de palm van je hand!" +lore2 = "ALLE ITEMS DIE JE IN DE TAFEL VERGEET BIJ HET SLUITEN ZIJN VOOR ALTIJD VERLOREN!" +lore3 = "Geldige tafels: Aambeeld, Werktafel, Slijpsteen, Cartografietafel, Steenhouwer, Weefgetouw" +lore = ["ALLE ITEMS DIE JE IN DE TAFEL VERGEET BIJ HET SLUITEN ZIJN VOOR ALTIJD VERLOREN!", "Geldige tafels: Aambeeld, Werktafel, Slijpsteen, Cartografietafel, Steenhouwer, Weefgetouw"] [crafting.skulls] - name = "Maakbare Schedels!" - description = "Met materialen kun je mobschedels maken!" - lore1 = "Omring een botblok met het volgende om een schedel te krijgen:" - lore2 = "Zombie: Rottend Vlees" - lore3 = "Skelet: Bot" - lore4 = "Creeper: Buskruit" - lore5 = "Wither: Netherbaksteen" - lore6 = "Draak: Drakenadem" - lore = ["Omring een botblok met het volgende om een schedel te krijgen:", "Zombie: Rottend Vlees", "Skelet: Bot", "Creeper: Buskruit", "Wither: Netherbaksteen", "Draak: Drakenadem"] +name = "Maakbare Schedels!" +description = "Met materialen kun je mobschedels maken!" +lore1 = "Omring een botblok met het volgende om een schedel te krijgen:" +lore2 = "Zombie: Rottend Vlees" +lore3 = "Skelet: Bot" +lore4 = "Creeper: Buskruit" +lore5 = "Wither: Netherbaksteen" +lore6 = "Draak: Drakenadem" +lore = ["Omring een botblok met het volgende om een schedel te krijgen:", "Zombie: Rottend Vlees", "Skelet: Bot", "Creeper: Buskruit", "Wither: Netherbaksteen", "Draak: Drakenadem"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Tijd in een Fles" - description = "Draag een temporele fles die tijd opslaat en besteed het om getimede blokken, groeibare gewassen en verouderbare entiteiten zoals babydieren te versnellen. Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles." - lore1 = "Opgeslagen seconden opgeladen per tick" - lore2 = "Tijdversnelling per opgeslagen seconde" - lore3 = "Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles" - lore = ["Opgeslagen seconden opgeladen per tick", "Tijdversnelling per opgeslagen seconde", "Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles"] +name = "Tijd in een Fles" +description = "Draag een temporele fles die tijd opslaat en besteed het om getimede blokken, groeibare gewassen en verouderbare entiteiten zoals babydieren te versnellen. Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles." +lore1 = "Opgeslagen seconden opgeladen per tick" +lore2 = "Tijdversnelling per opgeslagen seconde" +lore3 = "Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles" +lore = ["Opgeslagen seconden opgeladen per tick", "Tijdversnelling per opgeslagen seconde", "Recept (Vormloos): Snelheidsdrankje + Klok + Glazen Fles"] [chronos.aberrant_touch] - name = "Afwijkende Aanraking" - description = "Melee-aanvallen geven stapelende traagheid ten koste van honger, met strikte PvP-limieten, en verankeren doelwitten bij 5 stapels." - lore1 = "Melee-aanvallen geven stapelende traagheid" - lore2 = "PvE traagheidsduur limiet" - lore3 = "PvP traagheidsversterker limiet" - lore = ["Melee-aanvallen geven stapelende traagheid", "PvE traagheidsduur limiet", "PvP traagheidsversterker limiet"] +name = "Afwijkende Aanraking" +description = "Melee-aanvallen geven stapelende traagheid ten koste van honger, met strikte PvP-limieten, en verankeren doelwitten bij 5 stapels." +lore1 = "Melee-aanvallen geven stapelende traagheid" +lore2 = "PvE traagheidsduur limiet" +lore3 = "PvP traagheidsversterker limiet" +lore = ["Melee-aanvallen geven stapelende traagheid", "PvE traagheidsduur limiet", "PvP traagheidsversterker limiet"] [chronos.instant_recall] - name = "Directe Terugroeping" - description = "Links- of rechtsklik met een klok in je hand om terug te spoelen naar een recent snapshot met herstelde gezondheid en honger." - lore1 = "Terugspoelduur" - lore2 = "Cooldown" - lore3 = "Geen inventaris-terugdraaiing" - lore = ["Terugspoelduur", "Cooldown", "Geen inventaris-terugdraaiing"] +name = "Directe Terugroeping" +description = "Links- of rechtsklik met een klok in je hand om terug te spoelen naar een recent snapshot met herstelde gezondheid en honger." +lore1 = "Terugspoelduur" +lore2 = "Cooldown" +lore3 = "Geen inventaris-terugdraaiing" +lore = ["Terugspoelduur", "Cooldown", "Geen inventaris-terugdraaiing"] [chronos.time_bomb] - name = "Tijdbom" - description = "Gooi een gemaakte chronobom die een temporeel veld creëert, entiteiten vertraagt en projectielen bevriest." - lore1 = "Temporeel veld radius" - lore2 = "Temporeel veld duur" - lore3 = "Bom-cooldown" - lore4 = "Recept (Vormloos): Klok + Sneeuwbal + Diamant + Zand" - lore = ["Temporeel veld radius", "Temporeel veld duur", "Bom-cooldown", "Recept (Vormloos): Klok + Sneeuwbal + Diamant + Zand"] +name = "Tijdbom" +description = "Gooi een gemaakte chronobom die een temporeel veld creëert, entiteiten vertraagt en projectielen bevriest." +lore1 = "Temporeel veld radius" +lore2 = "Temporeel veld duur" +lore3 = "Bom-cooldown" +lore4 = "Recept (Vormloos): Klok + Sneeuwbal + Diamant + Zand" +lore = ["Temporeel veld radius", "Temporeel veld duur", "Bom-cooldown", "Recept (Vormloos): Klok + Sneeuwbal + Diamant + Zand"] # discovery [discovery] [discovery.armor] - name = "Wereldpantser" - description = "Passief pantser afhankelijk van de hardheid van nabije blokken." - lore1 = "Passief Pantser" - lore2 = "Gebaseerd op nabije blokhardheid" - lore3 = "Pantsersterkte:" - lore = ["Passief Pantser", "Gebaseerd op nabije blokhardheid", "Pantsersterkte:"] +name = "Wereldpantser" +description = "Passief pantser afhankelijk van de hardheid van nabije blokken." +lore1 = "Passief Pantser" +lore2 = "Gebaseerd op nabije blokhardheid" +lore3 = "Pantsersterkte:" +lore = ["Passief Pantser", "Gebaseerd op nabije blokhardheid", "Pantsersterkte:"] [discovery.unity] - name = "Experimentele Eenheid" - description = "Het verzamelen van ervaringsbollen voegt XP toe aan willekeurige vaardigheden." - lore1 = "XP " - lore2 = "Per Bol" - lore = ["XP ", "Per Bol"] +name = "Experimentele Eenheid" +description = "Het verzamelen van ervaringsbollen voegt XP toe aan willekeurige vaardigheden." +lore1 = "XP " +lore2 = "Per Bol" +lore = ["XP ", "Per Bol"] [discovery.resist] - name = "Experimentele Weerstand" - description = "Verbruik ervaring om schade te verminderen alleen wanneer een treffer je onder 5 harten zou brengen of je zou doden." - lore0 = "Activeert alleen bij kritieke gezondheid (<= 5 harten) eens per 15 seconden" - lore1 = " Verminderde Schade" - lore2 = "ervaring verbruikt" - lore = ["Activeert alleen bij kritieke gezondheid (<= 5 harten) eens per 15 seconden", " Verminderde Schade", "ervaring verbruikt"] +name = "Experimentele Weerstand" +description = "Verbruik ervaring om schade te verminderen alleen wanneer een treffer je onder 5 harten zou brengen of je zou doden." +lore0 = "Activeert alleen bij kritieke gezondheid (<= 5 harten) eens per 15 seconden" +lore1 = " Verminderde Schade" +lore2 = "ervaring verbruikt" +lore = ["Activeert alleen bij kritieke gezondheid (<= 5 harten) eens per 15 seconden", " Verminderde Schade", "ervaring verbruikt"] [discovery.villager] - name = "Dorpelings Aantrekking" - description = "Hiermee kun je betere aanbiedingen bij dorpelingen krijgen!" - lore1 = "Dit verbruikt XP per interactie met dorpelingen" - lore2 = "Kans per interactie om XP te verbruiken en aanbiedingen te verbeteren" - lore3 = "vereiste XP-verbruik per interactie" - lore = ["Dit verbruikt XP per interactie met dorpelingen", "Kans per interactie om XP te verbruiken en aanbiedingen te verbeteren", "vereiste XP-verbruik per interactie"] +name = "Dorpelings Aantrekking" +description = "Hiermee kun je betere aanbiedingen bij dorpelingen krijgen!" +lore1 = "Dit verbruikt XP per interactie met dorpelingen" +lore2 = "Kans per interactie om XP te verbruiken en aanbiedingen te verbeteren" +lore3 = "vereiste XP-verbruik per interactie" +lore = ["Dit verbruikt XP per interactie met dorpelingen", "Kans per interactie om XP te verbruiken en aanbiedingen te verbeteren", "vereiste XP-verbruik per interactie"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapis Teruggave" - description = "Ten koste van 1 extra level XP, met een kans om gratis lapis terug te krijgen" - lore1 = "Voor elk niveau verhoogt het de kosten van betoveren met 1, maar kan tot 3 Lapis teruggeven" - lore = ["Voor elk niveau verhoogt het de kosten van betoveren met 1, maar kan tot 3 Lapis teruggeven"] +name = "Lapis Teruggave" +description = "Ten koste van 1 extra level XP, met een kans om gratis lapis terug te krijgen" +lore1 = "Voor elk niveau verhoogt het de kosten van betoveren met 1, maar kan tot 3 Lapis teruggeven" +lore = ["Voor elk niveau verhoogt het de kosten van betoveren met 1, maar kan tot 3 Lapis teruggeven"] [enchanting.quick_enchant] - name = "Snelklik Betoveren" - description = "Betover items door met betoveringsboeken direct erop te klikken." - lore1 = "Max Gecombineerde Niveaus" - lore2 = "Kan een item niet betoveren met meer dan " - lore3 = "kracht" - lore = ["Max Gecombineerde Niveaus", "Kan een item niet betoveren met meer dan ", "kracht"] +name = "Snelklik Betoveren" +description = "Betover items door met betoveringsboeken direct erop te klikken." +lore1 = "Max Gecombineerde Niveaus" +lore2 = "Kan een item niet betoveren met meer dan " +lore3 = "kracht" +lore = ["Max Gecombineerde Niveaus", "Kan een item niet betoveren met meer dan ", "kracht"] [enchanting.return] - name = "XP Teruggave" - description = "Betover-XP wordt aan je teruggegeven wanneer je een item betoverT." - lore1 = "Uitgegeven ervaring heeft een kans om terugbetaald te worden wanneer je een item betoverT" - lore2 = "Ervaring per Betovering" - lore = ["Uitgegeven ervaring heeft een kans om terugbetaald te worden wanneer je een item betoverT", "Ervaring per Betovering"] +name = "XP Teruggave" +description = "Betover-XP wordt aan je teruggegeven wanneer je een item betoverT." +lore1 = "Uitgegeven ervaring heeft een kans om terugbetaald te worden wanneer je een item betoverT" +lore2 = "Ervaring per Betovering" +lore = ["Uitgegeven ervaring heeft een kans om terugbetaald te worden wanneer je een item betoverT", "Ervaring per Betovering"] # excavation [excavation] [excavation.haste] - name = "Haastige Graver" - description = "Dit versnelt het graafproces, met HAAST!" - lore1 = "Verkrijg Haast tijdens het graven" - lore2 = "x niveaus haast wanneer je ELK blok begint te mijnen." - lore = ["Verkrijg Haast tijdens het graven", "x niveaus haast wanneer je ELK blok begint te mijnen."] +name = "Haastige Graver" +description = "Dit versnelt het graafproces, met HAAST!" +lore1 = "Verkrijg Haast tijdens het graven" +lore2 = "x niveaus haast wanneer je ELK blok begint te mijnen." +lore = ["Verkrijg Haast tijdens het graven", "x niveaus haast wanneer je ELK blok begint te mijnen."] [excavation.spelunker] - name = "Superziende Spelunker!" - description = "Zie ertsen met je ogen, dwars door de grond!" - lore1 = "Erts in je nevenhand, Glowberries in je hoofdhand, en Sluip!" - lore2 = "Blok Bereik: " - lore3 = "Verbruikt Glowberry bij gebruik" - lore = ["Erts in je nevenhand, Glowberries in je hoofdhand, en Sluip!", "Blok Bereik: ", "Verbruikt Glowberry bij gebruik"] +name = "Superziende Spelunker!" +description = "Zie ertsen met je ogen, dwars door de grond!" +lore1 = "Erts in je nevenhand, Glowberries in je hoofdhand, en Sluip!" +lore2 = "Blok Bereik: " +lore3 = "Verbruikt Glowberry bij gebruik" +lore = ["Erts in je nevenhand, Glowberries in je hoofdhand, en Sluip!", "Blok Bereik: ", "Verbruikt Glowberry bij gebruik"] [excavation.drop_to_inventory] - name = "Schop Drop-naar-Inventaris" +name = "Schop Drop-naar-Inventaris" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Tackle's overontworpen weelderige zakmes" - lore1 = "Waarschijnlijk de krachtigste van velen, hiermee kun je" - lore2 = "dynamisch gereedschap samenvoegen en wisselen, op basis van je behoeften." - lore3 = "Om samen te voegen, shift-klik een item op een ander in je inventaris." - lore4 = "Om gereedschap los te koppelen, sluip en drop het item, en het wordt gedemonteerd." - lore5 = "Je kunt gereedschap in dit zakmes niet breken, maar kapot gereedschap kun je niet gebruiken" - lore6 = "totaal samen te voegen items." - lore7 = "Je kunt vijf of zes gereedschappen gebruiken, of gewoon een!" - lore = ["Waarschijnlijk de krachtigste van velen, hiermee kun je", "dynamisch gereedschap samenvoegen en wisselen, op basis van je behoeften.", "Om samen te voegen, shift-klik een item op een ander in je inventaris.", "Om gereedschap los te koppelen, sluip en drop het item, en het wordt gedemonteerd.", "Je kunt gereedschap in dit zakmes niet breken, maar kapot gereedschap kun je niet gebruiken", "totaal samen te voegen items.", "Je kunt vijf of zes gereedschappen gebruiken, of gewoon een!"] +name = "OMNI - T.O.O.L." +description = "Tackle's overontworpen weelderige zakmes" +lore1 = "Waarschijnlijk de krachtigste van velen, hiermee kun je" +lore2 = "dynamisch gereedschap samenvoegen en wisselen, op basis van je behoeften." +lore3 = "Om samen te voegen, shift-klik een item op een ander in je inventaris." +lore4 = "Om gereedschap los te koppelen, sluip en drop het item, en het wordt gedemonteerd." +lore5 = "Je kunt gereedschap in dit zakmes niet breken, maar kapot gereedschap kun je niet gebruiken" +lore6 = "totaal samen te voegen items." +lore7 = "Je kunt vijf of zes gereedschappen gebruiken, of gewoon een!" +lore = ["Waarschijnlijk de krachtigste van velen, hiermee kun je", "dynamisch gereedschap samenvoegen en wisselen, op basis van je behoeften.", "Om samen te voegen, shift-klik een item op een ander in je inventaris.", "Om gereedschap los te koppelen, sluip en drop het item, en het wordt gedemonteerd.", "Je kunt gereedschap in dit zakmes niet breken, maar kapot gereedschap kun je niet gebruiken", "totaal samen te voegen items.", "Je kunt vijf of zes gereedschappen gebruiken, of gewoon een!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Groeiaura" - description = "Laat de natuur om je heen groeien in een aura" - lore1 = "Blokken Radius" - lore2 = "Groeiaura Sterkte" - lore3 = "Voedselkosten" - lore = ["Blokken Radius", "Groeiaura Sterkte", "Voedselkosten"] +name = "Groeiaura" +description = "Laat de natuur om je heen groeien in een aura" +lore1 = "Blokken Radius" +lore2 = "Groeiaura Sterkte" +lore3 = "Voedselkosten" +lore = ["Blokken Radius", "Groeiaura Sterkte", "Voedselkosten"] [herbalism.hippo] - name = "Kruidkundige's Nijlpaard" - description = "Voedsel consumeren geeft je meer verzadiging" - lore1 = "Voedsel) extra verzadigingspunten bij consumptie" - lore = ["Voedsel) extra verzadigingspunten bij consumptie"] +name = "Kruidkundige's Nijlpaard" +description = "Voedsel consumeren geeft je meer verzadiging" +lore1 = "Voedsel) extra verzadigingspunten bij consumptie" +lore = ["Voedsel) extra verzadigingspunten bij consumptie"] [herbalism.myconid] - name = "Kruidkundige's Myconide" - description = "Geeft je de mogelijkheid om Mycelium te maken" - lore1 = "Elke aarde en een bruine & rode paddenstoel maken Mycelium." - lore = ["Elke aarde en een bruine & rode paddenstoel maken Mycelium."] +name = "Kruidkundige's Myconide" +description = "Geeft je de mogelijkheid om Mycelium te maken" +lore1 = "Elke aarde en een bruine & rode paddenstoel maken Mycelium." +lore = ["Elke aarde en een bruine & rode paddenstoel maken Mycelium."] [herbalism.terralid] - name = "Kruidkundige's Terralide" - description = "Geeft je de mogelijkheid om Grasblokken te maken" - lore1 = "Drie Zaden boven 3 Aarde maken 3 Grasblokken." - lore = ["Drie Zaden boven 3 Aarde maken 3 Grasblokken."] +name = "Kruidkundige's Terralide" +description = "Geeft je de mogelijkheid om Grasblokken te maken" +lore1 = "Drie Zaden boven 3 Aarde maken 3 Grasblokken." +lore = ["Drie Zaden boven 3 Aarde maken 3 Grasblokken."] [herbalism.cobweb] - name = "Webmaker" - description = "Geeft je de mogelijkheid om spinnenwebben te maken in een Werktafel" - lore1 = "Negen touw maakt een spinnenweb." - lore = ["Negen touw maakt een spinnenweb."] +name = "Webmaker" +description = "Geeft je de mogelijkheid om spinnenwebben te maken in een Werktafel" +lore1 = "Negen touw maakt een spinnenweb." +lore = ["Negen touw maakt een spinnenweb."] [herbalism.mushroom_blocks] - name = "Paddenstoelenmaker" - description = "Geeft je de mogelijkheid om paddenstoelenblokken te maken in een Werktafel" - lore1 = "Vier paddenstoelen om een blok te maken, of een blok om een stengel te maken." - lore = ["Vier paddenstoelen om een blok te maken, of een blok om een stengel te maken."] +name = "Paddenstoelenmaker" +description = "Geeft je de mogelijkheid om paddenstoelenblokken te maken in een Werktafel" +lore1 = "Vier paddenstoelen om een blok te maken, of een blok om een stengel te maken." +lore = ["Vier paddenstoelen om een blok te maken, of een blok om een stengel te maken."] [herbalism.drop_to_inventory] - name = "Schoffel Drop-naar-Inventaris" +name = "Schoffel Drop-naar-Inventaris" [herbalism.hungry_shield] - name = "Hongerig Schild" - description = "Neem schade op je honger voordat je gezondheid wordt aangetast." - lore1 = "Weerstaan door Honger" - lore = ["Weerstaan door Honger"] +name = "Hongerig Schild" +description = "Neem schade op je honger voordat je gezondheid wordt aangetast." +lore1 = "Weerstaan door Honger" +lore = ["Weerstaan door Honger"] [herbalism.luck] - name = "Kruidkundige's Geluk" - description = "Wanneer je Gras/Bloemen breekt, heb je kans om een willekeurig item te krijgen" - lore0 = "Bloemen = Voedsel, en Gras = Zaden" - lore1 = "Kans om een item te krijgen door Bloemen te breken" - lore2 = "Kans om een item te krijgen door Gras te breken" - lore = ["Bloemen = Voedsel, en Gras = Zaden", "Kans om een item te krijgen door Bloemen te breken", "Kans om een item te krijgen door Gras te breken"] +name = "Kruidkundige's Geluk" +description = "Wanneer je Gras/Bloemen breekt, heb je kans om een willekeurig item te krijgen" +lore0 = "Bloemen = Voedsel, en Gras = Zaden" +lore1 = "Kans om een item te krijgen door Bloemen te breken" +lore2 = "Kans om een item te krijgen door Gras te breken" +lore = ["Bloemen = Voedsel, en Gras = Zaden", "Kans om een item te krijgen door Bloemen te breken", "Kans om een item te krijgen door Gras te breken"] [herbalism.replant] - name = "Oogsten & Herplanten" - description = "Rechtsklik op een gewas met een schoffel om te oogsten en herplanten." - lore1 = "Blokken Herplant Radius" - lore = ["Blokken Herplant Radius"] +name = "Oogsten & Herplanten" +description = "Rechtsklik op een gewas met een schoffel om te oogsten en herplanten." +lore1 = "Blokken Herplant Radius" +lore = ["Blokken Herplant Radius"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenaline" - description = "Doe meer schade hoe lager je gezondheid is (melee)" - lore1 = "Max Schade" - lore = ["Max Schade"] +name = "Adrenaline" +description = "Doe meer schade hoe lager je gezondheid is (melee)" +lore1 = "Max Schade" +lore = ["Max Schade"] [hunter.penalty] - name = "" - description = "" - lore1 = "Je krijgt gifstapels als je honger opraakt" - lore = ["Je krijgt gifstapels als je honger opraakt"] +name = "" +description = "" +lore1 = "Je krijgt gifstapels als je honger opraakt" +lore = ["Je krijgt gifstapels als je honger opraakt"] [hunter.drop_to_inventory] - name = "Items Drop-naar-Inventaris" - description = "Wanneer je iets doodt / een blok breekt met een zwaard worden de drops naar je inventaris geteleporteerd" - lore1 = "Telkens wanneer een item door een mob/blok wordt gedropt dat je breekt, gaat het naar je inventaris als dat past." - lore = ["Telkens wanneer een item door een mob/blok wordt gedropt dat je breekt, gaat het naar je inventaris als dat past."] +name = "Items Drop-naar-Inventaris" +description = "Wanneer je iets doodt / een blok breekt met een zwaard worden de drops naar je inventaris geteleporteerd" +lore1 = "Telkens wanneer een item door een mob/blok wordt gedropt dat je breekt, gaat het naar je inventaris als dat past." +lore = ["Telkens wanneer een item door een mob/blok wordt gedropt dat je breekt, gaat het naar je inventaris als dat past."] [hunter.invisibility] - name = "Verdwijnende Stap" - description = "Wanneer je geraakt wordt krijg je onzichtbaarheid, ten koste van honger" - lore1 = "Verkrijg passieve onzichtbaarheid wanneer je geraakt wordt" - lore2 = "x Onzichtbaarheidsstapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Onzichtbaarheidsduur" - lore = ["Verkrijg passieve onzichtbaarheid wanneer je geraakt wordt", "x Onzichtbaarheidsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Onzichtbaarheidsduur"] +name = "Verdwijnende Stap" +description = "Wanneer je geraakt wordt krijg je onzichtbaarheid, ten koste van honger" +lore1 = "Verkrijg passieve onzichtbaarheid wanneer je geraakt wordt" +lore2 = "x Onzichtbaarheidsstapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Onzichtbaarheidsduur" +lore = ["Verkrijg passieve onzichtbaarheid wanneer je geraakt wordt", "x Onzichtbaarheidsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Onzichtbaarheidsduur"] [hunter.jump_boost] - name = "Jagershoogten" - description = "Wanneer je geraakt wordt krijg je springboost, ten koste van honger" - lore1 = "Verkrijg passieve springboost wanneer je geraakt wordt" - lore2 = "x Springbooststapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Springboost stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passieve springboost wanneer je geraakt wordt", "x Springbooststapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Springboost stapelvermenigvuldiger, niet duur."] +name = "Jagershoogten" +description = "Wanneer je geraakt wordt krijg je springboost, ten koste van honger" +lore1 = "Verkrijg passieve springboost wanneer je geraakt wordt" +lore2 = "x Springbooststapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Springboost stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passieve springboost wanneer je geraakt wordt", "x Springbooststapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Springboost stapelvermenigvuldiger, niet duur."] [hunter.luck] - name = "Jagersgeluk" - description = "Wanneer je geraakt wordt krijg je geluk, ten koste van honger" - lore1 = "Verkrijg passief geluk wanneer je geraakt wordt" - lore2 = "x Gelukstapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Geluk stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passief geluk wanneer je geraakt wordt", "x Gelukstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Geluk stapelvermenigvuldiger, niet duur."] +name = "Jagersgeluk" +description = "Wanneer je geraakt wordt krijg je geluk, ten koste van honger" +lore1 = "Verkrijg passief geluk wanneer je geraakt wordt" +lore2 = "x Gelukstapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Geluk stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passief geluk wanneer je geraakt wordt", "x Gelukstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Geluk stapelvermenigvuldiger, niet duur."] [hunter.regen] - name = "Jagersregeneratie" - description = "Wanneer je geraakt wordt krijg je regeneratie, ten koste van honger" - lore1 = "Verkrijg passieve regeneratie wanneer je geraakt wordt" - lore2 = "x Regeneratiestapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Regeneratie stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passieve regeneratie wanneer je geraakt wordt", "x Regeneratiestapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Regeneratie stapelvermenigvuldiger, niet duur."] +name = "Jagersregeneratie" +description = "Wanneer je geraakt wordt krijg je regeneratie, ten koste van honger" +lore1 = "Verkrijg passieve regeneratie wanneer je geraakt wordt" +lore2 = "x Regeneratiestapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Regeneratie stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passieve regeneratie wanneer je geraakt wordt", "x Regeneratiestapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Regeneratie stapelvermenigvuldiger, niet duur."] [hunter.resistance] - name = "Jagersweerstand" - description = "Wanneer je geraakt wordt krijg je weerstand, ten koste van honger" - lore1 = "Verkrijg passieve weerstand wanneer je geraakt wordt" - lore2 = "x Weerstandsstapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Weerstand stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passieve weerstand wanneer je geraakt wordt", "x Weerstandsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Weerstand stapelvermenigvuldiger, niet duur."] +name = "Jagersweerstand" +description = "Wanneer je geraakt wordt krijg je weerstand, ten koste van honger" +lore1 = "Verkrijg passieve weerstand wanneer je geraakt wordt" +lore2 = "x Weerstandsstapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Weerstand stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passieve weerstand wanneer je geraakt wordt", "x Weerstandsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Weerstand stapelvermenigvuldiger, niet duur."] [hunter.speed] - name = "Jagerssnelheid" - description = "Wanneer je geraakt wordt krijg je snelheid, ten koste van honger" - lore1 = "Verkrijg passieve snelheid wanneer je geraakt wordt" - lore2 = "x Snelheidsstapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Snelheid stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passieve snelheid wanneer je geraakt wordt", "x Snelheidsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Snelheid stapelvermenigvuldiger, niet duur."] +name = "Jagerssnelheid" +description = "Wanneer je geraakt wordt krijg je snelheid, ten koste van honger" +lore1 = "Verkrijg passieve snelheid wanneer je geraakt wordt" +lore2 = "x Snelheidsstapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Snelheid stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passieve snelheid wanneer je geraakt wordt", "x Snelheidsstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Snelheid stapelvermenigvuldiger, niet duur."] [hunter.strength] - name = "Jagerskracht" - description = "Wanneer je geraakt wordt krijg je kracht, ten koste van honger" - lore1 = "Verkrijg passieve kracht wanneer je geraakt wordt" - lore2 = "x Krachtstapels voor 3 seconden bij treffer" - lore3 = "x Stapelende honger" - lore4 = "Honger stapelduur en vermenigvuldiger." - lore5 = "Kracht stapelvermenigvuldiger, niet duur." - lore = ["Verkrijg passieve kracht wanneer je geraakt wordt", "x Krachtstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Kracht stapelvermenigvuldiger, niet duur."] +name = "Jagerskracht" +description = "Wanneer je geraakt wordt krijg je kracht, ten koste van honger" +lore1 = "Verkrijg passieve kracht wanneer je geraakt wordt" +lore2 = "x Krachtstapels voor 3 seconden bij treffer" +lore3 = "x Stapelende honger" +lore4 = "Honger stapelduur en vermenigvuldiger." +lore5 = "Kracht stapelvermenigvuldiger, niet duur." +lore = ["Verkrijg passieve kracht wanneer je geraakt wordt", "x Krachtstapels voor 3 seconden bij treffer", "x Stapelende honger", "Honger stapelduur en vermenigvuldiger.", "Kracht stapelvermenigvuldiger, niet duur."] # nether [nether] [nether.skull_toss] - name = "Witherschedel Worp" - description1 = "Laat je innerlijke Wither los door" - description2 = "iemands" - description3 = "hoofd te gebruiken." - lore1 = "Seconden cooldown tussen schedelworpen." - lore2 = "Witherschedel gebruiken: Gooi een " - lore3 = "Witherschedel" - lore4 = "die explodeert bij impact." - lore = ["Seconden cooldown tussen schedelworpen.", "Witherschedel gebruiken: Gooi een ", "Witherschedel", "die explodeert bij impact."] +name = "Witherschedel Worp" +description1 = "Laat je innerlijke Wither los door" +description2 = "iemands" +description3 = "hoofd te gebruiken." +lore1 = "Seconden cooldown tussen schedelworpen." +lore2 = "Witherschedel gebruiken: Gooi een " +lore3 = "Witherschedel" +lore4 = "die explodeert bij impact." +lore = ["Seconden cooldown tussen schedelworpen.", "Witherschedel gebruiken: Gooi een ", "Witherschedel", "die explodeert bij impact."] [nether.wither_resist] - name = "Witherweerstand" - description = "Weerstaat withering door de kracht van Netherite." - lore1 = "kans om withering te negeren (per stuk)." - lore2 = "Passief: Het dragen van Netherite Pantser heeft een kans om " - lore3 = "withering te negeren." - lore = ["kans om withering te negeren (per stuk).", "Passief: Het dragen van Netherite Pantser heeft een kans om ", "withering te negeren."] +name = "Witherweerstand" +description = "Weerstaat withering door de kracht van Netherite." +lore1 = "kans om withering te negeren (per stuk)." +lore2 = "Passief: Het dragen van Netherite Pantser heeft een kans om " +lore3 = "withering te negeren." +lore = ["kans om withering te negeren (per stuk).", "Passief: Het dragen van Netherite Pantser heeft een kans om ", "withering te negeren."] [nether.fire_resist] - name = "Vuurweerstand" - description = "Weerstaat vuur door je huid te verharden." - lore1 = "kans om het brandeffect te negeren!" - lore = ["kans om het brandeffect te negeren!"] +name = "Vuurweerstand" +description = "Weerstaat vuur door je huid te verharden." +lore1 = "kans om het brandeffect te negeren!" +lore = ["kans om het brandeffect te negeren!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Automatisch Smelten" - description = "Hiermee kun je gedolven vanilla ertsen automatisch smelten" - lore1 = "Ertsen die gesmolten kunnen worden, worden automatisch gesmolten" - lore2 = "% kans op een extra" - lore = ["Ertsen die gesmolten kunnen worden, worden automatisch gesmolten", "% kans op een extra"] +name = "Automatisch Smelten" +description = "Hiermee kun je gedolven vanilla ertsen automatisch smelten" +lore1 = "Ertsen die gesmolten kunnen worden, worden automatisch gesmolten" +lore2 = "% kans op een extra" +lore = ["Ertsen die gesmolten kunnen worden, worden automatisch gesmolten", "% kans op een extra"] [pickaxe.chisel] - name = "Ertsbeitel" - description = "Rechtsklik op ertsen om er meer erts uit te beitelen, tegen hoge duurzaamheidskosten." - lore1 = "Kans om te Droppen" - lore2 = "Gereedschapsslijtage" - lore = ["Kans om te Droppen", "Gereedschapsslijtage"] +name = "Ertsbeitel" +description = "Rechtsklik op ertsen om er meer erts uit te beitelen, tegen hoge duurzaamheidskosten." +lore1 = "Kans om te Droppen" +lore2 = "Gereedschapsslijtage" +lore = ["Kans om te Droppen", "Gereedschapsslijtage"] [pickaxe.drop_to_inventory] - name = "Pikhouweel Drop-naar-Inventaris" - description = "Wanneer je een blok breekt wordt het item naar je inventaris geteleporteerd" - lore1 = "Telkens wanneer een item uit een gebroken blok valt, gaat het naar je inventaris als dat past." - lore = ["Telkens wanneer een item uit een gebroken blok valt, gaat het naar je inventaris als dat past."] +name = "Pikhouweel Drop-naar-Inventaris" +description = "Wanneer je een blok breekt wordt het item naar je inventaris geteleporteerd" +lore1 = "Telkens wanneer een item uit een gebroken blok valt, gaat het naar je inventaris als dat past." +lore = ["Telkens wanneer een item uit een gebroken blok valt, gaat het naar je inventaris als dat past."] [pickaxe.silk_spawner] - name = "Pikhouweel Silk-Spawner" - description = "Zorgt dat spawners droppen wanneer ze gebroken worden" - lore1 = "Maakt spawners breekbaar met silk touch." - lore2 = "Maakt spawners breekbaar terwijl je sluipt." - lore = ["Maakt spawners breekbaar met silk touch.", "Maakt spawners breekbaar terwijl je sluipt."] +name = "Pikhouweel Silk-Spawner" +description = "Zorgt dat spawners droppen wanneer ze gebroken worden" +lore1 = "Maakt spawners breekbaar met silk touch." +lore2 = "Maakt spawners breekbaar terwijl je sluipt." +lore = ["Maakt spawners breekbaar met silk touch.", "Maakt spawners breekbaar terwijl je sluipt."] [pickaxe.vein_miner] - name = "Adermijnbouw" - description = "Hiermee kun je blokken breken in een ader/cluster van vanilla ertsen" - lore1 = "Sluip en mijn ERTSEN" - lore2 = "bereik van adermijnbouw" - lore3 = "Deze vaardigheid groepeert NIET alle drops bij elkaar!" - lore = ["Sluip en mijn ERTSEN", "bereik van adermijnbouw", "Deze vaardigheid groepeert NIET alle drops bij elkaar!"] +name = "Adermijnbouw" +description = "Hiermee kun je blokken breken in een ader/cluster van vanilla ertsen" +lore1 = "Sluip en mijn ERTSEN" +lore2 = "bereik van adermijnbouw" +lore3 = "Deze vaardigheid groepeert NIET alle drops bij elkaar!" +lore = ["Sluip en mijn ERTSEN", "bereik van adermijnbouw", "Deze vaardigheid groepeert NIET alle drops bij elkaar!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Pijlherstel" - description = "Herstel pijlen nadat je een vijand hebt gedood." - lore1 = "Kans om pijlen te herstellen bij treffer/dood" - lore2 = "Kans: " - lore = ["Kans om pijlen te herstellen bij treffer/dood", "Kans: "] +name = "Pijlherstel" +description = "Herstel pijlen nadat je een vijand hebt gedood." +lore1 = "Kans om pijlen te herstellen bij treffer/dood" +lore2 = "Kans: " +lore = ["Kans om pijlen te herstellen bij treffer/dood", "Kans: "] [ranged.web_shot] - name = "Webstrik" - description = "Omring je doelwit met spinnenwebben wanneer je ze raakt!" - lore1 = "8 spinnenwebben rond een sneeuwbal, en gooi!" - lore2 = "seconden in een kooi, ongeveer." - lore = ["8 spinnenwebben rond een sneeuwbal, en gooi!", "seconden in een kooi, ongeveer."] +name = "Webstrik" +description = "Omring je doelwit met spinnenwebben wanneer je ze raakt!" +lore1 = "8 spinnenwebben rond een sneeuwbal, en gooi!" +lore2 = "seconden in een kooi, ongeveer." +lore = ["8 spinnenwebben rond een sneeuwbal, en gooi!", "seconden in een kooi, ongeveer."] [ranged.force_shot] - name = "Krachtschot" - description = "Schiet projectielen verder en sneller!" - advancementname = "Lange Afstandsschot" - advancementlore = "Land een schot van meer dan 30 blokken afstand!" - lore1 = "Projectielsnelheid" - lore = ["Projectielsnelheid"] +name = "Krachtschot" +description = "Schiet projectielen verder en sneller!" +advancementname = "Lange Afstandsschot" +advancementlore = "Land een schot van meer dan 30 blokken afstand!" +lore1 = "Projectielsnelheid" +lore = ["Projectielsnelheid"] [ranged.lunge_shot] - name = "Uitvalschot" - description = "Terwijl je valt gooien je pijlen je in een willekeurige richting" - lore1 = "Willekeurige Burst Snelheid" - lore = ["Willekeurige Burst Snelheid"] +name = "Uitvalschot" +description = "Terwijl je valt gooien je pijlen je in een willekeurige richting" +lore1 = "Willekeurige Burst Snelheid" +lore = ["Willekeurige Burst Snelheid"] [ranged.arrow_piercing] - name = "Pijl Doorboring" - description = "Voegt doorboring toe aan projectielen! Schiet dwars door dingen!" - lore1 = "Doorboorde Doelen" - lore = ["Doorboorde Doelen"] +name = "Pijl Doorboring" +description = "Voegt doorboring toe aan projectielen! Schiet dwars door dingen!" +lore1 = "Doorboorde Doelen" +lore = ["Doorboorde Doelen"] # rift [rift] [rift.remote_access] - name = "Toegang op Afstand" - description = "Trek vanuit de leegte en open een gemarkeerde container." - lore1 = "Enderparel + Kompas = Reliekschrijn Portkey" - lore2 = "Met dit item kun je containers op afstand openen" - lore3 = "Eenmaal gemaakt, bekijk het item om het gebruik te zien" - notcontainer = "Dat is geen container" - lore = ["Enderparel + Kompas = Reliekschrijn Portkey", "Met dit item kun je containers op afstand openen", "Eenmaal gemaakt, bekijk het item om het gebruik te zien"] +name = "Toegang op Afstand" +description = "Trek vanuit de leegte en open een gemarkeerde container." +lore1 = "Enderparel + Kompas = Reliekschrijn Portkey" +lore2 = "Met dit item kun je containers op afstand openen" +lore3 = "Eenmaal gemaakt, bekijk het item om het gebruik te zien" +notcontainer = "Dat is geen container" +lore = ["Enderparel + Kompas = Reliekschrijn Portkey", "Met dit item kun je containers op afstand openen", "Eenmaal gemaakt, bekijk het item om het gebruik te zien"] [rift.blink] - name = "Rift Flits" - description = "Korte afstand directe teleportatie, slechts een oogwenk verwijderd!" - lore1 = "Blokken per flits (2x verticaal)" - lore2 = "Tijdens het sprinten: Dubbeltik op Springen om te " - lore3 = "Flitsen" - lore = ["Blokken per flits (2x verticaal)", "Tijdens het sprinten: Dubbeltik op Springen om te ", "Flitsen"] +name = "Rift Flits" +description = "Korte afstand directe teleportatie, slechts een oogwenk verwijderd!" +lore1 = "Blokken per flits (2x verticaal)" +lore2 = "Tijdens het sprinten: Dubbeltik op Springen om te " +lore3 = "Flitsen" +lore = ["Blokken per flits (2x verticaal)", "Tijdens het sprinten: Dubbeltik op Springen om te ", "Flitsen"] [rift.chest] - name = "Gemakkelijke Enderkist" - description = "Open een enderkist door er met links op te klikken in je hand." - lore1 = "Klik op een Enderkist in je hand om te openen (plaats hem gewoon niet)" - lore = ["Klik op een Enderkist in je hand om te openen (plaats hem gewoon niet)"] +name = "Gemakkelijke Enderkist" +description = "Open een enderkist door er met links op te klikken in je hand." +lore1 = "Klik op een Enderkist in je hand om te openen (plaats hem gewoon niet)" +lore = ["Klik op een Enderkist in je hand om te openen (plaats hem gewoon niet)"] [rift.descent] - name = "Anti-Levitatie" - description = "Ben je het zat om vast te zitten in de lucht? Dit is de vaardigheid voor jou!" - lore1 = "Sluip gewoon om af te dalen, en je valt langzamer dan normaal!" - lore2 = "Cooldown:" - lore = ["Sluip gewoon om af te dalen, en je valt langzamer dan normaal!", "Cooldown:"] +name = "Anti-Levitatie" +description = "Ben je het zat om vast te zitten in de lucht? Dit is de vaardigheid voor jou!" +lore1 = "Sluip gewoon om af te dalen, en je valt langzamer dan normaal!" +lore2 = "Cooldown:" +lore = ["Sluip gewoon om af te dalen, en je valt langzamer dan normaal!", "Cooldown:"] [rift.gate] - name = "Rift Poort" - description = "Teleporteer naar een gemarkeerde locatie." - lore1 = "KNUTSELEN: Smaragd + Amethist Scherf + Enderparel" - lore2 = "Lees voor gebruik!" - lore3 = "5s vertraging, " - lore4 = "je kunt sterven tijdens deze animatie" - lore = ["KNUTSELEN: Smaragd + Amethist Scherf + Enderparel", "Lees voor gebruik!", "5s vertraging, ", "je kunt sterven tijdens deze animatie"] +name = "Rift Poort" +description = "Teleporteer naar een gemarkeerde locatie." +lore1 = "KNUTSELEN: Smaragd + Amethist Scherf + Enderparel" +lore2 = "Lees voor gebruik!" +lore3 = "5s vertraging, " +lore4 = "je kunt sterven tijdens deze animatie" +lore = ["KNUTSELEN: Smaragd + Amethist Scherf + Enderparel", "Lees voor gebruik!", "5s vertraging, ", "je kunt sterven tijdens deze animatie"] [rift.resist] - name = "Rift Weerstand" - description = "Verkrijg weerstand bij het gebruik van Ender-items & vaardigheden" - lore1 = "+ Passief: Biedt weerstand wanneer je rift-vaardigheden of Ender-items gebruikt" - lore2 = "NIET inclusief draagbare Enderkist, alleen dingen die je kunt consumeren" - lore = ["+ Passief: Biedt weerstand wanneer je rift-vaardigheden of Ender-items gebruikt", "NIET inclusief draagbare Enderkist, alleen dingen die je kunt consumeren"] +name = "Rift Weerstand" +description = "Verkrijg weerstand bij het gebruik van Ender-items & vaardigheden" +lore1 = "+ Passief: Biedt weerstand wanneer je rift-vaardigheden of Ender-items gebruikt" +lore2 = "NIET inclusief draagbare Enderkist, alleen dingen die je kunt consumeren" +lore = ["+ Passief: Biedt weerstand wanneer je rift-vaardigheden of Ender-items gebruikt", "NIET inclusief draagbare Enderkist, alleen dingen die je kunt consumeren"] [rift.visage] - name = "Rift Gelaat" - description = "Voorkomt dat Endermen agressief worden als je Enderparels in je inventaris hebt." - lore1 = "Endermen worden niet agressief als je Enderparels in je inventaris hebt." - lore = ["Endermen worden niet agressief als je Enderparels in je inventaris hebt."] +name = "Rift Gelaat" +description = "Voorkomt dat Endermen agressief worden als je Enderparels in je inventaris hebt." +lore1 = "Endermen worden niet agressief als je Enderparels in je inventaris hebt." +lore = ["Endermen worden niet agressief als je Enderparels in je inventaris hebt."] # seaborn [seaborn] [seaborn.oxygen] - name = "Organische Zuurstoftank" - description = "Houd meer zuurstof vast in je kleine longen!" - lore1 = "Zuurstofcapaciteit Verhoging" - lore = ["Zuurstofcapaciteit Verhoging"] +name = "Organische Zuurstoftank" +description = "Houd meer zuurstof vast in je kleine longen!" +lore1 = "Zuurstofcapaciteit Verhoging" +lore = ["Zuurstofcapaciteit Verhoging"] [seaborn.fishers_fantasy] - name = "Vissersfantasie" - description = "Verdien meer XP van vissen en vang meer vis!" - lore1 = "Voor elk niveau is er een kans om meer XP en vis te krijgen!" - lore = ["Voor elk niveau is er een kans om meer XP en vis te krijgen!"] +name = "Vissersfantasie" +description = "Verdien meer XP van vissen en vang meer vis!" +lore1 = "Voor elk niveau is er een kans om meer XP en vis te krijgen!" +lore = ["Voor elk niveau is er een kans om meer XP en vis te krijgen!"] [seaborn.haste] - name = "Schildpadmijnwerker" - description = "Tijdens het mijnen onder water krijg je haast!" - lore1 = "Haast 3 wordt onder water toegepast tijdens het mijnen (stapelt met AquaAffinity) nadat je waterademeffect is uitgewerkt!" - lore = ["Haast 3 wordt onder water toegepast tijdens het mijnen (stapelt met AquaAffinity) nadat je waterademeffect is uitgewerkt!"] +name = "Schildpadmijnwerker" +description = "Tijdens het mijnen onder water krijg je haast!" +lore1 = "Haast 3 wordt onder water toegepast tijdens het mijnen (stapelt met AquaAffinity) nadat je waterademeffect is uitgewerkt!" +lore = ["Haast 3 wordt onder water toegepast tijdens het mijnen (stapelt met AquaAffinity) nadat je waterademeffect is uitgewerkt!"] [seaborn.night_vision] - name = "Schildpadzicht" - description = "Onder water krijg je nachtzicht" - lore1 = "Verkrijg simpelweg nachtzicht terwijl je onder water bent nadat je waterademeffect is uitgewerkt!" - lore = ["Verkrijg simpelweg nachtzicht terwijl je onder water bent nadat je waterademeffect is uitgewerkt!"] +name = "Schildpadzicht" +description = "Onder water krijg je nachtzicht" +lore1 = "Verkrijg simpelweg nachtzicht terwijl je onder water bent nadat je waterademeffect is uitgewerkt!" +lore = ["Verkrijg simpelweg nachtzicht terwijl je onder water bent nadat je waterademeffect is uitgewerkt!"] [seaborn.dolphin_grace] - name = "Dolfijnengratie" - description = "Zwem als een dolfijn, zonder de dolfijnen" - lore1 = "+ Passief: verkrijg " - lore2 = "x snelheid (dolfijnengratie)" - lore3 = "Duitse precisie-ingenieur- wacht, dat klopt niet... Niet compatibel met Depth Strider" - lore = ["+ Passief: verkrijg ", "x snelheid (dolfijnengratie)", "Duitse precisie-ingenieur- wacht, dat klopt niet... Niet compatibel met Depth Strider"] +name = "Dolfijnengratie" +description = "Zwem als een dolfijn, zonder de dolfijnen" +lore1 = "+ Passief: verkrijg " +lore2 = "x snelheid (dolfijnengratie)" +lore3 = "Duitse precisie-ingenieur- wacht, dat klopt niet... Niet compatibel met Depth Strider" +lore = ["+ Passief: verkrijg ", "x snelheid (dolfijnengratie)", "Duitse precisie-ingenieur- wacht, dat klopt niet... Niet compatibel met Depth Strider"] # stealth [stealth] [stealth.ghost_armor] - name = "Geestenpantser" - description = "Langzaam opbouwend pantser wanneer je geen schade ontvangt, duurt 1 treffer" - lore1 = "Max Pantser" - lore2 = "Snelheid" - lore = ["Max Pantser", "Snelheid"] +name = "Geestenpantser" +description = "Langzaam opbouwend pantser wanneer je geen schade ontvangt, duurt 1 treffer" +lore1 = "Max Pantser" +lore2 = "Snelheid" +lore = ["Max Pantser", "Snelheid"] [stealth.night_vision] - name = "Stealthzicht" - description = "Verkrijg nachtzicht terwijl je sluipt" - lore1 = "Verkrijg een burst van " - lore2 = "nachtzicht" - lore3 = "terwijl je sluipt" - lore = ["Verkrijg een burst van ", "nachtzicht", "terwijl je sluipt"] +name = "Stealthzicht" +description = "Verkrijg nachtzicht terwijl je sluipt" +lore1 = "Verkrijg een burst van " +lore2 = "nachtzicht" +lore3 = "terwijl je sluipt" +lore = ["Verkrijg een burst van ", "nachtzicht", "terwijl je sluipt"] [stealth.snatch] - name = "Voorwerp Grijpen" - description = "Grijp gevallen voorwerpen direct terwijl je sluipt!" - lore1 = "Grijpradius" - lore = ["Grijpradius"] +name = "Voorwerp Grijpen" +description = "Grijp gevallen voorwerpen direct terwijl je sluipt!" +lore1 = "Grijpradius" +lore = ["Grijpradius"] [stealth.speed] - name = "Sluipsnelheid" - description = "Verkrijg snelheid terwijl je sluipt" - lore1 = "Sluipsnelheid" - lore = ["Sluipsnelheid"] +name = "Sluipsnelheid" +description = "Verkrijg snelheid terwijl je sluipt" +lore1 = "Sluipsnelheid" +lore = ["Sluipsnelheid"] [stealth.ender_veil] - name = "Endersluier" - description = "Geen pompoenen meer nodig om Enderman-aanvallen te voorkomen" - lore1 = "Voorkom Enderman-aanvallen terwijl je sluipt" - lore2 = "Voorkom alle Enderman-aanvallen" - lore = ["Voorkom Enderman-aanvallen terwijl je sluipt", "Voorkom alle Enderman-aanvallen"] +name = "Endersluier" +description = "Geen pompoenen meer nodig om Enderman-aanvallen te voorkomen" +lore1 = "Voorkom Enderman-aanvallen terwijl je sluipt" +lore2 = "Voorkom alle Enderman-aanvallen" +lore = ["Voorkom Enderman-aanvallen terwijl je sluipt", "Voorkom alle Enderman-aanvallen"] # sword [sword] [sword.machete] - name = "Machete" - description = "Snijd moeiteloos door gebladerte!" - lore1 = "Snijradius" - lore2 = "Hak-Cooldown" - lore3 = "Gereedschapsslijtage" - lore = ["Snijradius", "Hak-Cooldown", "Gereedschapsslijtage"] +name = "Machete" +description = "Snijd moeiteloos door gebladerte!" +lore1 = "Snijradius" +lore2 = "Hak-Cooldown" +lore3 = "Gereedschapsslijtage" +lore = ["Snijradius", "Hak-Cooldown", "Gereedschapsslijtage"] [sword.bloody_blade] - name = "Bloederig Zwaard" - description = "Slagen met je zwaard veroorzaken bloeding!" - lore1 = "Een levend wezen raken met je zwaard veroorzaakt bloeding" - lore2 = "Bloedingsduur" - lore3 = "Bloedings-Cooldown" - lore = ["Een levend wezen raken met je zwaard veroorzaakt bloeding", "Bloedingsduur", "Bloedings-Cooldown"] +name = "Bloederig Zwaard" +description = "Slagen met je zwaard veroorzaken bloeding!" +lore1 = "Een levend wezen raken met je zwaard veroorzaakt bloeding" +lore2 = "Bloedingsduur" +lore3 = "Bloedings-Cooldown" +lore = ["Een levend wezen raken met je zwaard veroorzaakt bloeding", "Bloedingsduur", "Bloedings-Cooldown"] [sword.poisoned_blade] - name = "Vergiftigd Zwaard" - description = "Slagen met je zwaard veroorzaken vergiftiging!" - lore1 = "Een levend wezen raken met je zwaard veroorzaakt vergiftiging" - lore2 = "Vergiftigingsduur" - lore3 = "Vergiftigings-Cooldown" - lore = ["Een levend wezen raken met je zwaard veroorzaakt vergiftiging", "Vergiftigingsduur", "Vergiftigings-Cooldown"] +name = "Vergiftigd Zwaard" +description = "Slagen met je zwaard veroorzaken vergiftiging!" +lore1 = "Een levend wezen raken met je zwaard veroorzaakt vergiftiging" +lore2 = "Vergiftigingsduur" +lore3 = "Vergiftigings-Cooldown" +lore = ["Een levend wezen raken met je zwaard veroorzaakt vergiftiging", "Vergiftigingsduur", "Vergiftigings-Cooldown"] # taming [taming] [taming.damage] - name = "Temschade" - description = "Verhoog de schade die je getemde dieren toebrengen." - lore1 = "Verhoogde Schade" - lore = ["Verhoogde Schade"] +name = "Temschade" +description = "Verhoog de schade die je getemde dieren toebrengen." +lore1 = "Verhoogde Schade" +lore = ["Verhoogde Schade"] [taming.health] - name = "Temgezondheid" - description = "Verhoog de gezondheid van je getemde dieren." - lore1 = "Verhoogde Gezondheid" - lore = ["Verhoogde Gezondheid"] +name = "Temgezondheid" +description = "Verhoog de gezondheid van je getemde dieren." +lore1 = "Verhoogde Gezondheid" +lore = ["Verhoogde Gezondheid"] [taming.regeneration] - name = "Temregeneratie" - description = "Verhoog de regeneratie van je getemde dieren." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Temregeneratie" +description = "Verhoog de regeneratie van je getemde dieren." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Doornen" - description = "Weerkaats schade terug naar je aanvaller!" - lore1 = "Schade teruggeslagen bij treffer" - lore = ["Schade teruggeslagen bij treffer"] +name = "Doornen" +description = "Weerkaats schade terug naar je aanvaller!" +lore1 = "Schade teruggeslagen bij treffer" +lore = ["Schade teruggeslagen bij treffer"] [tragoul.globe] - name = "Bol van Pijn" - description = "Verdeel de schade die je toebrengt op basis van het aantal vijanden om je heen!" - lore1 = "Hoe meer vijanden om je heen, hoe minder schade je aan elk van hen toebrengt" - lore2 = "Bereik: " - lore3 = "Toegevoegde Schade aan alle Entiteiten: " - lore = ["Hoe meer vijanden om je heen, hoe minder schade je aan elk van hen toebrengt", "Bereik: ", "Toegevoegde Schade aan alle Entiteiten: "] +name = "Bol van Pijn" +description = "Verdeel de schade die je toebrengt op basis van het aantal vijanden om je heen!" +lore1 = "Hoe meer vijanden om je heen, hoe minder schade je aan elk van hen toebrengt" +lore2 = "Bereik: " +lore3 = "Toegevoegde Schade aan alle Entiteiten: " +lore = ["Hoe meer vijanden om je heen, hoe minder schade je aan elk van hen toebrengt", "Bereik: ", "Toegevoegde Schade aan alle Entiteiten: "] [tragoul.healing] - name = "Wil van Pijn" - description = "Krijg gezondheid terug op basis van de schade die je toebrengt!" - lore1 = "Dingen pijn doen heeft nog nooit zo goed gevoeld! Genees van toegebrachte schade" - lore2 = "Er is een schadevenster van 3 seconden voor genezing en een cooldown van 1 seconde " - lore3 = "Genezing Per Schadepercentage: " - lore = ["Dingen pijn doen heeft nog nooit zo goed gevoeld! Genees van toegebrachte schade", "Er is een schadevenster van 3 seconden voor genezing en een cooldown van 1 seconde ", "Genezing Per Schadepercentage: "] +name = "Wil van Pijn" +description = "Krijg gezondheid terug op basis van de schade die je toebrengt!" +lore1 = "Dingen pijn doen heeft nog nooit zo goed gevoeld! Genees van toegebrachte schade" +lore2 = "Er is een schadevenster van 3 seconden voor genezing en een cooldown van 1 seconde " +lore3 = "Genezing Per Schadepercentage: " +lore = ["Dingen pijn doen heeft nog nooit zo goed gevoeld! Genees van toegebrachte schade", "Er is een schadevenster van 3 seconden voor genezing en een cooldown van 1 seconde ", "Genezing Per Schadepercentage: "] [tragoul.lance] - name = "Lijklansen" - description = "Een vijand doden of een vaardigheid die een vijand doodt, spawnt een lans die schade toebrengt aan een nabije vijand!" - lore1 = "Lansen zoeken vanuit alles wat je doodt, EN als deze vaardigheid een vijand doodt." - lore2 = "Offer een deel van je leven om de lansen te maken (dit kan je doden)" - lore3 = "Max Lansen: 1 + " - lore = ["Lansen zoeken vanuit alles wat je doodt, EN als deze vaardigheid een vijand doodt.", "Offer een deel van je leven om de lansen te maken (dit kan je doden)", "Max Lansen: 1 + "] +name = "Lijklansen" +description = "Een vijand doden of een vaardigheid die een vijand doodt, spawnt een lans die schade toebrengt aan een nabije vijand!" +lore1 = "Lansen zoeken vanuit alles wat je doodt, EN als deze vaardigheid een vijand doodt." +lore2 = "Offer een deel van je leven om de lansen te maken (dit kan je doden)" +lore3 = "Max Lansen: 1 + " +lore = ["Lansen zoeken vanuit alles wat je doodt, EN als deze vaardigheid een vijand doodt.", "Offer een deel van je leven om de lansen te maken (dit kan je doden)", "Max Lansen: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Glazen Kanon" - description = "Bonus ongewapende schade hoe lager je pantserwaarde is" - lore1 = "x Schade bij 0 pantser" - lore2 = "Bonusschade Per Niveau" - lore = ["x Schade bij 0 pantser", "Bonusschade Per Niveau"] +name = "Glazen Kanon" +description = "Bonus ongewapende schade hoe lager je pantserwaarde is" +lore1 = "x Schade bij 0 pantser" +lore2 = "Bonusschade Per Niveau" +lore = ["x Schade bij 0 pantser", "Bonusschade Per Niveau"] [unarmed.power] - name = "Ongewapende Kracht" - description = "Verbeterde ongewapende schade" - lore1 = "Schade" - lore = ["Schade"] +name = "Ongewapende Kracht" +description = "Verbeterde ongewapende schade" +lore1 = "Schade" +lore = ["Schade"] [unarmed.sucker_punch] - name = "Sprintstoot" - description = "Sprintstoten, maar dodelijker." - lore1 = "Schade" - lore2 = "Schade neemt toe met je snelheid tijdens het stoten" - lore = ["Schade", "Schade neemt toe met je snelheid tijdens het stoten"] +name = "Sprintstoot" +description = "Sprintstoten, maar dodelijker." +lore1 = "Schade" +lore2 = "Schade neemt toe met je snelheid tijdens het stoten" +lore = ["Schade", "Schade neemt toe met je snelheid tijdens het stoten"] diff --git a/src/main/resources/pl_PL.toml b/src/main/resources/pl_PL.toml index de879ca17..69db20751 100644 --- a/src/main/resources/pl_PL.toml +++ b/src/main/resources/pl_PL.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Ruszaj sie!" - description = "Przejdz ponad 1 kilometr (1000 blokow)" +title = "Ruszaj sie!" +description = "Przejdz ponad 1 kilometr (1000 blokow)" [advancement.challenge_sprint_5k] - title = "Przebieg 5K!" - description = "Przejdz ponad 5 kilometrow (5000 blokow)" +title = "Przebieg 5K!" +description = "Przejdz ponad 5 kilometrow (5000 blokow)" [advancement.challenge_sprint_50k] - title = "Przesmiglaj 50K!" - description = "Przejdz ponad 50 kilometrow (50 000 blokow)" +title = "Przesmiglaj 50K!" +description = "Przejdz ponad 50 kilometrow (50 000 blokow)" [advancement.challenge_sprint_500k] - title = "Przemierz Wszechswiat!!" - description = "Przejdz ponad 500 kilometrow (500 000 blokow)" +title = "Przemierz Wszechswiat!!" +description = "Przejdz ponad 500 kilometrow (500 000 blokow)" [advancement.challenge_sprint_marathon] - title = "Przebieg (doslownego) Maratonu!" - description = "Przebiegnij ponad 42 195 blokow!" +title = "Przebieg (doslownego) Maratonu!" +description = "Przebiegnij ponad 42 195 blokow!" [advancement.challenge_place_1k] - title = "Poczatkujacy Budowniczy!" - description = "Umiesc 1000 blokow" +title = "Poczatkujacy Budowniczy!" +description = "Umiesc 1000 blokow" [advancement.challenge_place_5k] - title = "Sredniozaawansowany Budowniczy!" - description = "Umiesc 5000 blokow" +title = "Sredniozaawansowany Budowniczy!" +description = "Umiesc 5000 blokow" [advancement.challenge_place_50k] - title = "Zaawansowany Budowniczy!" - description = "Umiesc 50 000 blokow" +title = "Zaawansowany Budowniczy!" +description = "Umiesc 50 000 blokow" [advancement.challenge_place_500k] - title = "Mistrz Budowniczy!" - description = "Umiesc 500 000 blokow" +title = "Mistrz Budowniczy!" +description = "Umiesc 500 000 blokow" [advancement.challenge_place_5m] - title = "Akolita Symetrii!" - description = "RZECZYWISTOSC TO TWOJ PLAC ZABAW! (5 milionow blokow)" +title = "Akolita Symetrii!" +description = "RZECZYWISTOSC TO TWOJ PLAC ZABAW! (5 milionow blokow)" [advancement.challenge_chop_1k] - title = "Poczatkujacy Drwal!" - description = "Wytnij 1000 blokow" +title = "Poczatkujacy Drwal!" +description = "Wytnij 1000 blokow" [advancement.challenge_chop_5k] - title = "Sredniozaawansowany Drwal!" - description = "Wytnij 5000 blokow" +title = "Sredniozaawansowany Drwal!" +description = "Wytnij 5000 blokow" [advancement.challenge_chop_50k] - title = "Zaawansowany Drwal!" - description = "Wytnij 50 000 blokow" +title = "Zaawansowany Drwal!" +description = "Wytnij 50 000 blokow" [advancement.challenge_chop_500k] - title = "Mistrz Drwal!" - description = "Wytnij 500 000 blokow" +title = "Mistrz Drwal!" +description = "Wytnij 500 000 blokow" [advancement.challenge_chop_5m] - title = "Jackson Piesek" - description = "Najlepszy dobry chlopak! (5 milionow blokow)" +title = "Jackson Piesek" +description = "Najlepszy dobry chlopak! (5 milionow blokow)" [advancement.challenge_block_1k] - title = "Ledwo Blokujesz!" - description = "Zablokuj 1000 trafien" +title = "Ledwo Blokujesz!" +description = "Zablokuj 1000 trafien" [advancement.challenge_block_5k] - title = "Blokowanie jest Fajne!" - description = "Zablokuj 5000 trafien" +title = "Blokowanie jest Fajne!" +description = "Zablokuj 5000 trafien" [advancement.challenge_block_50k] - title = "Blokowanie to moje zycie!" - description = "Zablokuj 50 000 trafien" +title = "Blokowanie to moje zycie!" +description = "Zablokuj 50 000 trafien" [advancement.challenge_block_500k] - title = "Blokowanie to moje Powolanie!" - description = "Zablokuj 500 000 trafien" +title = "Blokowanie to moje Powolanie!" +description = "Zablokuj 500 000 trafien" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Zablokuj 5 000 000 trafien" +title = "Die Hand Die Verletzt" +description = "Zablokuj 5 000 000 trafien" [advancement.challenge_brew_1k] - title = "Poczatkujacy Alchemik!" - description = "Wypij 1000 mikstur" +title = "Poczatkujacy Alchemik!" +description = "Wypij 1000 mikstur" [advancement.challenge_brew_5k] - title = "Sredniozaawansowany Alchemik!" - description = "Wypij 5000 mikstur" +title = "Sredniozaawansowany Alchemik!" +description = "Wypij 5000 mikstur" [advancement.challenge_brew_50k] - title = "Zaawansowany Alchemik!" - description = "Wypij 50 000 mikstur" +title = "Zaawansowany Alchemik!" +description = "Wypij 50 000 mikstur" [advancement.challenge_brew_500k] - title = "Mistrz Alchemik!" - description = "Wypij 500 000 mikstur" +title = "Mistrz Alchemik!" +description = "Wypij 500 000 mikstur" [advancement.challenge_brew_5m] - title = "Alchemik" - description = "Wypij 5 000 000 mikstur" +title = "Alchemik" +description = "Wypij 5 000 000 mikstur" [advancement.challenge_brewsplash_1k] - title = "Poczatkujacy Rozpryskiwacz Mikstur!" - description = "Rozpryskaj 1000 mikstur" +title = "Poczatkujacy Rozpryskiwacz Mikstur!" +description = "Rozpryskaj 1000 mikstur" [advancement.challenge_brewsplash_5k] - title = "Sredniozaawansowany Rozpryskiwacz Mikstur!" - description = "Rozpryskaj 5000 mikstur" +title = "Sredniozaawansowany Rozpryskiwacz Mikstur!" +description = "Rozpryskaj 5000 mikstur" [advancement.challenge_brewsplash_50k] - title = "Zaawansowany Rozpryskiwacz Mikstur!" - description = "Rozpryskaj 50 000 mikstur" +title = "Zaawansowany Rozpryskiwacz Mikstur!" +description = "Rozpryskaj 50 000 mikstur" [advancement.challenge_brewsplash_500k] - title = "Mistrz Rozpryskiwania Mikstur!" - description = "Rozpryskaj 500 000 mikstur" +title = "Mistrz Rozpryskiwania Mikstur!" +description = "Rozpryskaj 500 000 mikstur" [advancement.challenge_brewsplash_5m] - title = "Mistrz Splasha" - description = "Rozpryskaj 5 000 000 mikstur" +title = "Mistrz Splasha" +description = "Rozpryskaj 5 000 000 mikstur" [advancement.challenge_craft_1k] - title = "Sprytny Rzemieslnik!" - description = "Wytworz 1000 przedmiotow" +title = "Sprytny Rzemieslnik!" +description = "Wytworz 1000 przedmiotow" [advancement.challenge_craft_5k] - title = "Zrzedliwy Rzemieslnik!" - description = "Wytworz 5000 przedmiotow" +title = "Zrzedliwy Rzemieslnik!" +description = "Wytworz 5000 przedmiotow" [advancement.challenge_craft_50k] - title = "Pokorny Rzemieslnik!" - description = "Wytworz 50 000 przedmiotow" +title = "Pokorny Rzemieslnik!" +description = "Wytworz 50 000 przedmiotow" [advancement.challenge_craft_500k] - title = "Kakofoniczny Rzemieslnik!" - description = "Wytworz 500 000 przedmiotow" +title = "Kakofoniczny Rzemieslnik!" +description = "Wytworz 500 000 przedmiotow" [advancement.challenge_craft_5m] - title = "Kleskowy McCraftface" - description = "Wytworz 5 000 000 przedmiotow" +title = "Kleskowy McCraftface" +description = "Wytworz 5 000 000 przedmiotow" [advancement.challenge_enchant_1k] - title = "Poczatkujacy Zaklinacz!" - description = "Zaczaruj 1000 przedmiotow" +title = "Poczatkujacy Zaklinacz!" +description = "Zaczaruj 1000 przedmiotow" [advancement.challenge_enchant_5k] - title = "Sredniozaawansowany Zaklinacz!" - description = "Zaczaruj 5000 przedmiotow" +title = "Sredniozaawansowany Zaklinacz!" +description = "Zaczaruj 5000 przedmiotow" [advancement.challenge_enchant_50k] - title = "Zaawansowany Zaklinacz!" - description = "Zaczaruj 50 000 przedmiotow" +title = "Zaawansowany Zaklinacz!" +description = "Zaczaruj 50 000 przedmiotow" [advancement.challenge_enchant_500k] - title = "Mistrz Zaklinacz!" - description = "Zaczaruj 500 000 przedmiotow" +title = "Mistrz Zaklinacz!" +description = "Zaczaruj 500 000 przedmiotow" [advancement.challenge_enchant_5m] - title = "Enigmatyczny Zaklinacz" - description = "Zaczaruj 5 000 000 przedmiotow" +title = "Enigmatyczny Zaklinacz" +description = "Zaczaruj 5 000 000 przedmiotow" [advancement.challenge_excavate_1k] - title = "Poczatkujacy Kopacz!" - description = "Wykop 1000 blokow" +title = "Poczatkujacy Kopacz!" +description = "Wykop 1000 blokow" [advancement.challenge_excavate_5k] - title = "Sredniozaawansowany Kopacz!" - description = "Wykop 5000 blokow" +title = "Sredniozaawansowany Kopacz!" +description = "Wykop 5000 blokow" [advancement.challenge_excavate_50k] - title = "Zaawansowany Kopacz!" - description = "Wykop 50 000 blokow" +title = "Zaawansowany Kopacz!" +description = "Wykop 50 000 blokow" [advancement.challenge_excavate_500k] - title = "Mistrz Kopacz!" - description = "Wykop 500 000 blokow" +title = "Mistrz Kopacz!" +description = "Wykop 500 000 blokow" [advancement.challenge_excavate_5m] - title = "Enigmatyczny Kopacz" - description = "Wykop 5 000 000 blokow" +title = "Enigmatyczny Kopacz" +description = "Wykop 5 000 000 blokow" [advancement.horrible_person] - title = "Jestes okropna osoba" - description = "Niepojete, naprawde" +title = "Jestes okropna osoba" +description = "Niepojete, naprawde" [advancement.challenge_turtle_egg_smasher] - title = "Rozbijacz Jaj Zolwi!" - description = "Zniszcz 100 jaj zolwia" +title = "Rozbijacz Jaj Zolwi!" +description = "Zniszcz 100 jaj zolwia" [advancement.challenge_turtle_egg_annihilator] - title = "Anihilator Jaj Zolwi!" - description = "Zniszcz 500 jaj zolwia" +title = "Anihilator Jaj Zolwi!" +description = "Zniszcz 500 jaj zolwia" [advancement.challenge_novice_hunter] - title = "Poczatkujacy Lowca!" - description = "Zabij 100 istot" +title = "Poczatkujacy Lowca!" +description = "Zabij 100 istot" [advancement.challenge_intermediate_hunter] - title = "Sredniozaawansowany Lowca!" - description = "Zabij 500 istot" +title = "Sredniozaawansowany Lowca!" +description = "Zabij 500 istot" [advancement.challenge_advanced_hunter] - title = "Zaawansowany Lowca!" - description = "Zabij 5000 istot" +title = "Zaawansowany Lowca!" +description = "Zabij 5000 istot" [advancement.challenge_creeper_conqueror] - title = "Pogromca Creeperow!" - description = "Zabij 50 creeperow" +title = "Pogromca Creeperow!" +description = "Zabij 50 creeperow" [advancement.challenge_creeper_annihilator] - title = "Anihilator Creeperow!" - description = "Zabij 200 creeperow" +title = "Anihilator Creeperow!" +description = "Zabij 200 creeperow" [advancement.challenge_pickaxe_1k] - title = "Poczatkujacy Gornik" - description = "Zniszcz 1000 blokow" +title = "Poczatkujacy Gornik" +description = "Zniszcz 1000 blokow" [advancement.challenge_pickaxe_5k] - title = "Wykwalifikowany Gornik" - description = "Zniszcz 5000 blokow" +title = "Wykwalifikowany Gornik" +description = "Zniszcz 5000 blokow" [advancement.challenge_pickaxe_50k] - title = "Ekspert Gornik" - description = "Zniszcz 50 000 blokow" +title = "Ekspert Gornik" +description = "Zniszcz 50 000 blokow" [advancement.challenge_pickaxe_500k] - title = "Mistrz Gornik" - description = "Zniszcz 500 000 blokow" +title = "Mistrz Gornik" +description = "Zniszcz 500 000 blokow" [advancement.challenge_pickaxe_5m] - title = "Legendarny Gornik" - description = "Zniszcz 5 000 000 blokow" +title = "Legendarny Gornik" +description = "Zniszcz 5 000 000 blokow" [advancement.challenge_eat_100] - title = "Tyle do jedzenia!" - description = "Zjedz ponad 100 przedmiotow!" +title = "Tyle do jedzenia!" +description = "Zjedz ponad 100 przedmiotow!" [advancement.challenge_eat_1000] - title = "Nienasycony Glod!" - description = "Zjedz ponad 1000 przedmiotow!" +title = "Nienasycony Glod!" +description = "Zjedz ponad 1000 przedmiotow!" [advancement.challenge_eat_10000] - title = "WIECZNY GLOD!" - description = "Zjedz ponad 10 000 przedmiotow!" +title = "WIECZNY GLOD!" +description = "Zjedz ponad 10 000 przedmiotow!" [advancement.challenge_harvest_100] - title = "Pelne Zniwa" - description = "Zbierz ponad 100 upraw!" +title = "Pelne Zniwa" +description = "Zbierz ponad 100 upraw!" [advancement.challenge_harvest_1000] - title = "Wielkie Zniwa" - description = "Zbierz ponad 1000 upraw!" +title = "Wielkie Zniwa" +description = "Zbierz ponad 1000 upraw!" [advancement.challenge_swim_1nm] - title = "Ludzki Okret Podwodny!" - description = "Preplyn 1 Mile Morska (1852 bloki)" +title = "Ludzki Okret Podwodny!" +description = "Preplyn 1 Mile Morska (1852 bloki)" [advancement.challenge_sneak_1k] - title = "Bol Kolan" - description = "Skradaj sie ponad kilometr (1000 blokow)" +title = "Bol Kolan" +description = "Skradaj sie ponad kilometr (1000 blokow)" [advancement.challenge_sneak_5k] - title = "Wedrowiec Cieni" - description = "Skradaj sie ponad 5000 blokow" +title = "Wedrowiec Cieni" +description = "Skradaj sie ponad 5000 blokow" [advancement.challenge_sneak_20k] - title = "Duch" - description = "Skradaj sie ponad 20 000 blokow" +title = "Duch" +description = "Skradaj sie ponad 20 000 blokow" [advancement.challenge_swim_5k] - title = "Gleboki Nurek" - description = "Preplyn ponad 5000 blokow" +title = "Gleboki Nurek" +description = "Preplyn ponad 5000 blokow" [advancement.challenge_swim_20k] - title = "Wybranik Posejdona" - description = "Preplyn ponad 20 000 blokow" +title = "Wybranik Posejdona" +description = "Preplyn ponad 20 000 blokow" [advancement.challenge_sword_100] - title = "Pierwsza Krew" - description = "Zadaj 100 ciosow mieczem" +title = "Pierwsza Krew" +description = "Zadaj 100 ciosow mieczem" [advancement.challenge_sword_1k] - title = "Taniec Ostrzy" - description = "Zadaj 1000 ciosow mieczem" +title = "Taniec Ostrzy" +description = "Zadaj 1000 ciosow mieczem" [advancement.challenge_sword_10k] - title = "Tysiac Ciec" - description = "Zadaj 10 000 ciosow mieczem" +title = "Tysiac Ciec" +description = "Zadaj 10 000 ciosow mieczem" [advancement.challenge_unarmed_100] - title = "Barowy Zawadiaka" - description = "Zadaj 100 ciosow golymi rekami" +title = "Barowy Zawadiaka" +description = "Zadaj 100 ciosow golymi rekami" [advancement.challenge_unarmed_1k] - title = "Zelazne Piesci" - description = "Zadaj 1000 ciosow golymi rekami" +title = "Zelazne Piesci" +description = "Zadaj 1000 ciosow golymi rekami" [advancement.challenge_unarmed_10k] - title = "Jeden Cios" - description = "Zadaj 10 000 ciosow golymi rekami" +title = "Jeden Cios" +description = "Zadaj 10 000 ciosow golymi rekami" [advancement.challenge_trag_1k] - title = "Cena Krwi" - description = "Otrzymaj 1000 obrazen" +title = "Cena Krwi" +description = "Otrzymaj 1000 obrazen" [advancement.challenge_trag_10k] - title = "Szkarlatna Fala" - description = "Otrzymaj 10 000 obrazen" +title = "Szkarlatna Fala" +description = "Otrzymaj 10 000 obrazen" [advancement.challenge_trag_100k] - title = "Awatar Cierpienia" - description = "Otrzymaj 100 000 obrazen" +title = "Awatar Cierpienia" +description = "Otrzymaj 100 000 obrazen" [advancement.challenge_ranged_100] - title = "Strzelnica" - description = "Wystrzel 100 pociskow" +title = "Strzelnica" +description = "Wystrzel 100 pociskow" [advancement.challenge_ranged_1k] - title = "Sokole Oko" - description = "Wystrzel 1000 pociskow" +title = "Sokole Oko" +description = "Wystrzel 1000 pociskow" [advancement.challenge_ranged_10k] - title = "Burza Strzal" - description = "Wystrzel 10 000 pociskow" +title = "Burza Strzal" +description = "Wystrzel 10 000 pociskow" [advancement.challenge_chronos_1h] - title = "Tik Tak" - description = "Spedz 1 godzine online" +title = "Tik Tak" +description = "Spedz 1 godzine online" [advancement.challenge_chronos_24h] - title = "Piaski Czasu" - description = "Spedz 24 godziny online" +title = "Piaski Czasu" +description = "Spedz 24 godziny online" [advancement.challenge_chronos_168h] - title = "Ponadczasowy" - description = "Spedz 168 godzin (1 tydzien) online" +title = "Ponadczasowy" +description = "Spedz 168 godzin (1 tydzien) online" [advancement.challenge_nether_50] - title = "Straznik Piekiel" - description = "Zabij 50 stworzen z Netheru" +title = "Straznik Piekiel" +description = "Zabij 50 stworzen z Netheru" [advancement.challenge_nether_500] - title = "Straznik Otchlani" - description = "Zabij 500 stworzen z Netheru" +title = "Straznik Otchlani" +description = "Zabij 500 stworzen z Netheru" [advancement.challenge_nether_5k] - title = "Wladca Netheru" - description = "Zabij 5000 stworzen z Netheru" +title = "Wladca Netheru" +description = "Zabij 5000 stworzen z Netheru" [advancement.challenge_rift_50] - title = "Anomalia Przestrzenna" - description = "Teleportuj sie 50 razy" +title = "Anomalia Przestrzenna" +description = "Teleportuj sie 50 razy" [advancement.challenge_rift_500] - title = "Wedrowiec Pustki" - description = "Teleportuj sie 500 razy" +title = "Wedrowiec Pustki" +description = "Teleportuj sie 500 razy" [advancement.challenge_rift_5k] - title = "Miedzy Swiatami" - description = "Teleportuj sie 5000 razy" +title = "Miedzy Swiatami" +description = "Teleportuj sie 5000 razy" [advancement.challenge_taming_10] - title = "Zaklinacz Zwierzat" - description = "Rozmnoz 10 zwierzat" +title = "Zaklinacz Zwierzat" +description = "Rozmnoz 10 zwierzat" [advancement.challenge_taming_50] - title = "Przywodca Stada" - description = "Rozmnoz 50 zwierzat" +title = "Przywodca Stada" +description = "Rozmnoz 50 zwierzat" [advancement.challenge_taming_500] - title = "Wladca Bestii" - description = "Rozmnoz 500 zwierzat" +title = "Wladca Bestii" +description = "Rozmnoz 500 zwierzat" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Demon Predkosci" - description = "Przebiegaj ponad 5 Kilometrow (5,000 blokow)" +title = "Demon Predkosci" +description = "Przebiegaj ponad 5 Kilometrow (5,000 blokow)" [advancement.challenge_sprint_dist_50k] - title = "Blyskawiczne Nogi" - description = "Przebiegaj ponad 50 Kilometrow (50,000 blokow)" +title = "Blyskawiczne Nogi" +description = "Przebiegaj ponad 50 Kilometrow (50,000 blokow)" [advancement.challenge_agility_swim_1k] - title = "Biegacz po Wodzie" - description = "Przeplyn ponad 1 Kilometr (1,000 blokow)" +title = "Biegacz po Wodzie" +description = "Przeplyn ponad 1 Kilometr (1,000 blokow)" [advancement.challenge_agility_swim_10k] - title = "Wodny Podroznik" - description = "Przeplyn ponad 10 Kilometrow (10,000 blokow)" +title = "Wodny Podroznik" +description = "Przeplyn ponad 10 Kilometrow (10,000 blokow)" [advancement.challenge_fly_1k] - title = "Tancerz Nieba" - description = "Przeleć ponad 1 Kilometr (1,000 blokow)" +title = "Tancerz Nieba" +description = "Przeleć ponad 1 Kilometr (1,000 blokow)" [advancement.challenge_fly_10k] - title = "Jezdiec Wiatru" - description = "Przeleć ponad 10 Kilometrow (10,000 blokow)" +title = "Jezdiec Wiatru" +description = "Przeleć ponad 10 Kilometrow (10,000 blokow)" [advancement.challenge_agility_sneak_500] - title = "Ciche Kroki" - description = "Przekradnij sie ponad 500 blokow" +title = "Ciche Kroki" +description = "Przekradnij sie ponad 500 blokow" [advancement.challenge_agility_sneak_5k] - title = "Widmowe Kroki" - description = "Przekradnij sie ponad 5 Kilometrow (5,000 blokow)" +title = "Widmowe Kroki" +description = "Przekradnij sie ponad 5 Kilometrow (5,000 blokow)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Ekipa Rozborkowa" - description = "Zniszcz 500 blokow" +title = "Ekipa Rozborkowa" +description = "Zniszcz 500 blokow" [advancement.challenge_demolish_5k] - title = "Kula Burzaca" - description = "Zniszcz 5,000 blokow" +title = "Kula Burzaca" +description = "Zniszcz 5,000 blokow" [advancement.challenge_value_placed_10k] - title = "Wartosciowy Budowniczy" - description = "Ustaw bloki o wartosci 10,000" +title = "Wartosciowy Budowniczy" +description = "Ustaw bloki o wartosci 10,000" [advancement.challenge_value_placed_100k] - title = "Mistrz Architektury" - description = "Ustaw bloki o wartosci 100,000" +title = "Mistrz Architektury" +description = "Ustaw bloki o wartosci 100,000" [advancement.challenge_demolish_val_5k] - title = "Ekspert Odzysku" - description = "Odzyskaj 5,000 wartosci blokow z rozbiórki" +title = "Ekspert Odzysku" +description = "Odzyskaj 5,000 wartosci blokow z rozbiórki" [advancement.challenge_demolish_val_50k] - title = "Calkowita Dekonstrukcja" - description = "Odzyskaj 50,000 wartosci blokow z rozbiórki" +title = "Calkowita Dekonstrukcja" +description = "Odzyskaj 50,000 wartosci blokow z rozbiórki" [advancement.challenge_high_build_100] - title = "Budowniczy Nieba" - description = "Ustaw 100 blokow powyzej Y=128" +title = "Budowniczy Nieba" +description = "Ustaw 100 blokow powyzej Y=128" [advancement.challenge_high_build_1k] - title = "Architekt Chmur" - description = "Ustaw 1,000 blokow powyzej Y=128" +title = "Architekt Chmur" +description = "Ustaw 1,000 blokow powyzej Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Drwal" - description = "Zamachnij sie toporem 500 razy" +title = "Drwal" +description = "Zamachnij sie toporem 500 razy" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Zamachnij sie toporem 5,000 razy" +title = "Berserker" +description = "Zamachnij sie toporem 5,000 razy" [advancement.challenge_axe_damage_1k] - title = "Tasak" - description = "Zadaj 1,000 obrazen toporem" +title = "Tasak" +description = "Zadaj 1,000 obrazen toporem" [advancement.challenge_axe_damage_10k] - title = "Topor Kata" - description = "Zadaj 10,000 obrazen toporem" +title = "Topor Kata" +description = "Zadaj 10,000 obrazen toporem" [advancement.challenge_axe_value_5k] - title = "Handlarz Drewnem" - description = "Zbierz drewno o wartosci 5,000" +title = "Handlarz Drewnem" +description = "Zbierz drewno o wartosci 5,000" [advancement.challenge_axe_value_50k] - title = "Baron Tartaku" - description = "Zbierz drewno o wartosci 50,000" +title = "Baron Tartaku" +description = "Zbierz drewno o wartosci 50,000" [advancement.challenge_leaves_500] - title = "Dmuchawa do Lisci" - description = "Usun 500 blokow lisci toporem" +title = "Dmuchawa do Lisci" +description = "Usun 500 blokow lisci toporem" [advancement.challenge_leaves_5k] - title = "Defoliator" - description = "Usun 5,000 blokow lisci toporem" +title = "Defoliator" +description = "Usun 5,000 blokow lisci toporem" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Absorber Obrazen" - description = "Zablokuj 1,000 obrazen tarcza" +title = "Absorber Obrazen" +description = "Zablokuj 1,000 obrazen tarcza" [advancement.challenge_block_dmg_10k] - title = "Zywa Tarcza" - description = "Zablokuj 10,000 obrazen tarcza" +title = "Zywa Tarcza" +description = "Zablokuj 10,000 obrazen tarcza" [advancement.challenge_block_proj_100] - title = "Odbijacz Strzal" - description = "Zablokuj 100 pociskow tarcza" +title = "Odbijacz Strzal" +description = "Zablokuj 100 pociskow tarcza" [advancement.challenge_block_proj_1k] - title = "Tarcza Przeciwpociskowa" - description = "Zablokuj 1,000 pociskow tarcza" +title = "Tarcza Przeciwpociskowa" +description = "Zablokuj 1,000 pociskow tarcza" [advancement.challenge_block_melee_500] - title = "Mistrz Parowania" - description = "Zablokuj 500 atakow wrecz tarcza" +title = "Mistrz Parowania" +description = "Zablokuj 500 atakow wrecz tarcza" [advancement.challenge_block_melee_5k] - title = "Zelazna Twierdza" - description = "Zablokuj 5,000 atakow wrecz tarcza" +title = "Zelazna Twierdza" +description = "Zablokuj 5,000 atakow wrecz tarcza" [advancement.challenge_block_heavy_50] - title = "Czolg" - description = "Zablokuj 50 ciezkich atakow (powyzej 5 obrazen)" +title = "Czolg" +description = "Zablokuj 50 ciezkich atakow (powyzej 5 obrazen)" [advancement.challenge_block_heavy_500] - title = "Nieruchomy Obiekt" - description = "Zablokuj 500 ciezkich atakow (powyzej 5 obrazen)" +title = "Nieruchomy Obiekt" +description = "Zablokuj 500 ciezkich atakow (powyzej 5 obrazen)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Warsztat Piwowara" - description = "Postaw 10 stojakow do warzenia" +title = "Warsztat Piwowara" +description = "Postaw 10 stojakow do warzenia" [advancement.challenge_brew_stands_50] - title = "Fabryka Mikstur" - description = "Postaw 50 stojakow do warzenia" +title = "Fabryka Mikstur" +description = "Postaw 50 stojakow do warzenia" [advancement.challenge_brew_strong_25] - title = "Mocny Napar" - description = "Wypij 25 ulepszonych mikstur" +title = "Mocny Napar" +description = "Wypij 25 ulepszonych mikstur" [advancement.challenge_brew_strong_250] - title = "Maksymalna Moc" - description = "Wypij 250 ulepszonych mikstur" +title = "Maksymalna Moc" +description = "Wypij 250 ulepszonych mikstur" [advancement.challenge_brew_splash_hits_50] - title = "Strefa Rozbryzgu" - description = "Traf 50 istot miksturami rozbryzgowymi" +title = "Strefa Rozbryzgu" +description = "Traf 50 istot miksturami rozbryzgowymi" [advancement.challenge_brew_splash_hits_500] - title = "Doktor Zarazy" - description = "Traf 500 istot miksturami rozbryzgowymi" +title = "Doktor Zarazy" +description = "Traf 500 istot miksturami rozbryzgowymi" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Niespokojny" - description = "Przebadz 1 Kilometr bedac aktywnym" +title = "Niespokojny" +description = "Przebadz 1 Kilometr bedac aktywnym" [advancement.challenge_active_dist_10k] - title = "Tropiciel" - description = "Przebadz 10 Kilometrow bedac aktywnym" +title = "Tropiciel" +description = "Przebadz 10 Kilometrow bedac aktywnym" [advancement.challenge_active_dist_100k] - title = "Wieczny Ruch" - description = "Przebadz 100 Kilometrow bedac aktywnym" +title = "Wieczny Ruch" +description = "Przebadz 100 Kilometrow bedac aktywnym" [advancement.challenge_beds_10] - title = "Ranny Ptaszek" - description = "Przespij sie w lozku 10 razy" +title = "Ranny Ptaszek" +description = "Przespij sie w lozku 10 razy" [advancement.challenge_beds_100] - title = "Pomijacz Czasu" - description = "Przespij sie w lozku 100 razy" +title = "Pomijacz Czasu" +description = "Przespij sie w lozku 100 razy" [advancement.challenge_chronos_tp_50] - title = "Przesuniecie Czasowe" - description = "Teleportuj sie 50 razy" +title = "Przesuniecie Czasowe" +description = "Teleportuj sie 50 razy" [advancement.challenge_chronos_tp_500] - title = "Zakrzywienie Czasu" - description = "Teleportuj sie 500 razy" +title = "Zakrzywienie Czasu" +description = "Teleportuj sie 500 razy" [advancement.challenge_chronos_deaths_10] - title = "Smiertelnik" - description = "Zgin 10 razy" +title = "Smiertelnik" +description = "Zgin 10 razy" [advancement.challenge_chronos_deaths_100] - title = "Pogromca Smierci" - description = "Zgin 100 razy" +title = "Pogromca Smierci" +description = "Zgin 100 razy" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Wartosc Rzemiosla" - description = "Wytwórz przedmioty o lacznej wartosci 10,000" +title = "Wartosc Rzemiosla" +description = "Wytwórz przedmioty o lacznej wartosci 10,000" [advancement.challenge_craft_value_100k] - title = "Rzemielnik" - description = "Wytwórz przedmioty o lacznej wartosci 100,000" +title = "Rzemielnik" +description = "Wytwórz przedmioty o lacznej wartosci 100,000" [advancement.challenge_craft_tools_25] - title = "Kowal Narzedzi" - description = "Wytwórz 25 narzedzi" +title = "Kowal Narzedzi" +description = "Wytwórz 25 narzedzi" [advancement.challenge_craft_tools_250] - title = "Mistrz Kuznicy" - description = "Wytwórz 250 narzedzi" +title = "Mistrz Kuznicy" +description = "Wytwórz 250 narzedzi" [advancement.challenge_craft_armor_25] - title = "Platnerz" - description = "Wytwórz 25 czesci zbroi" +title = "Platnerz" +description = "Wytwórz 25 czesci zbroi" [advancement.challenge_craft_armor_250] - title = "Mistrz Zbrojmistrz" - description = "Wytwórz 250 czesci zbroi" +title = "Mistrz Zbrojmistrz" +description = "Wytwórz 250 czesci zbroi" [advancement.challenge_craft_mass_25k] - title = "Masowa Produkcja" - description = "Wytwórz 25,000 przedmiotow" +title = "Masowa Produkcja" +description = "Wytwórz 25,000 przedmiotow" [advancement.challenge_craft_mass_250k] - title = "Rewolucja Przemyslowa" - description = "Wytwórz 250,000 przedmiotow" +title = "Rewolucja Przemyslowa" +description = "Wytwórz 250,000 przedmiotow" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Kolekcjoner" - description = "Odkryj 50 unikalnych przedmiotow" +title = "Kolekcjoner" +description = "Odkryj 50 unikalnych przedmiotow" [advancement.challenge_discover_items_250] - title = "Katalogista" - description = "Odkryj 250 unikalnych przedmiotow" +title = "Katalogista" +description = "Odkryj 250 unikalnych przedmiotow" [advancement.challenge_discover_blocks_50] - title = "Geodeta" - description = "Odkryj 50 unikalnych blokow" +title = "Geodeta" +description = "Odkryj 50 unikalnych blokow" [advancement.challenge_discover_blocks_250] - title = "Geolog" - description = "Odkryj 250 unikalnych blokow" +title = "Geolog" +description = "Odkryj 250 unikalnych blokow" [advancement.challenge_discover_mobs_25] - title = "Obserwator" - description = "Odkryj 25 unikalnych mobow" +title = "Obserwator" +description = "Odkryj 25 unikalnych mobow" [advancement.challenge_discover_mobs_75] - title = "Przyrodnik" - description = "Odkryj 75 unikalnych mobow" +title = "Przyrodnik" +description = "Odkryj 75 unikalnych mobow" [advancement.challenge_discover_biomes_10] - title = "Wedrowiec" - description = "Odkryj 10 unikalnych biomow" +title = "Wedrowiec" +description = "Odkryj 10 unikalnych biomow" [advancement.challenge_discover_biomes_40] - title = "Obiezyswiat" - description = "Odkryj 40 unikalnych biomow" +title = "Obiezyswiat" +description = "Odkryj 40 unikalnych biomow" [advancement.challenge_discover_foods_10] - title = "Smakosz" - description = "Odkryj 10 unikalnych potraw" +title = "Smakosz" +description = "Odkryj 10 unikalnych potraw" [advancement.challenge_discover_foods_30] - title = "Mistrz Kuchni" - description = "Odkryj 30 unikalnych potraw" +title = "Mistrz Kuchni" +description = "Odkryj 30 unikalnych potraw" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Tkacz Mocy" - description = "Zgromadz 100 mocy zaklinania" +title = "Tkacz Mocy" +description = "Zgromadz 100 mocy zaklinania" [advancement.challenge_enchant_power_1k] - title = "Mistrz Magii" - description = "Zgromadz 1,000 mocy zaklinania" +title = "Mistrz Magii" +description = "Zgromadz 1,000 mocy zaklinania" [advancement.challenge_enchant_levels_1k] - title = "Wydawca Poziomow" - description = "Wydaj 1,000 poziomow doswiadczenia na zaklinanie" +title = "Wydawca Poziomow" +description = "Wydaj 1,000 poziomow doswiadczenia na zaklinanie" [advancement.challenge_enchant_levels_10k] - title = "Pochłaniacz XP" - description = "Wydaj 10,000 poziomow doswiadczenia na zaklinanie" +title = "Pochłaniacz XP" +description = "Wydaj 10,000 poziomow doswiadczenia na zaklinanie" [advancement.challenge_enchant_high_25] - title = "Wysokie Stawki" - description = "Wykonaj 25 zaklinań maksymalnego poziomu" +title = "Wysokie Stawki" +description = "Wykonaj 25 zaklinań maksymalnego poziomu" [advancement.challenge_enchant_high_250] - title = "Legendarny Zaklinacz" - description = "Wykonaj 250 zaklinań maksymalnego poziomu" +title = "Legendarny Zaklinacz" +description = "Wykonaj 250 zaklinań maksymalnego poziomu" [advancement.challenge_enchant_total_500] - title = "Palacz Poziomow" - description = "Wydaj 500 łącznych poziomow na wszystkie zaklecia" +title = "Palacz Poziomow" +description = "Wydaj 500 łącznych poziomow na wszystkie zaklecia" [advancement.challenge_enchant_total_5k] - title = "Magiczna Inwestycja" - description = "Wydaj 5,000 łącznych poziomow na wszystkie zaklecia" +title = "Magiczna Inwestycja" +description = "Wydaj 5,000 łącznych poziomow na wszystkie zaklecia" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Kopacz" - description = "Zamachnij sie łopata 500 razy" +title = "Kopacz" +description = "Zamachnij sie łopata 500 razy" [advancement.challenge_dig_swing_5k] - title = "Koparka" - description = "Zamachnij sie łopata 5,000 razy" +title = "Koparka" +description = "Zamachnij sie łopata 5,000 razy" [advancement.challenge_dig_damage_1k] - title = "Rycerz Łopaty" - description = "Zadaj 1,000 obrazen łopata" +title = "Rycerz Łopaty" +description = "Zadaj 1,000 obrazen łopata" [advancement.challenge_dig_damage_10k] - title = "Mistrz Łopaty" - description = "Zadaj 10,000 obrazen łopata" +title = "Mistrz Łopaty" +description = "Zadaj 10,000 obrazen łopata" [advancement.challenge_dig_value_5k] - title = "Handlarz Ziemia" - description = "Wykop bloki o wartosci 5,000" +title = "Handlarz Ziemia" +description = "Wykop bloki o wartosci 5,000" [advancement.challenge_dig_value_50k] - title = "Baron Ziemi" - description = "Wykop bloki o wartosci 50,000" +title = "Baron Ziemi" +description = "Wykop bloki o wartosci 50,000" [advancement.challenge_dig_gravel_500] - title = "Młynarz Zwiru" - description = "Wykop 500 blokow zwiru, piasku lub gliny" +title = "Młynarz Zwiru" +description = "Wykop 500 blokow zwiru, piasku lub gliny" [advancement.challenge_dig_gravel_5k] - title = "Przesiewacz Piasku" - description = "Wykop 5,000 blokow zwiru, piasku lub gliny" +title = "Przesiewacz Piasku" +description = "Wykop 5,000 blokow zwiru, piasku lub gliny" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Siewca" - description = "Posadz 100 roslin" +title = "Siewca" +description = "Posadz 100 roslin" [advancement.challenge_plant_1k] - title = "Zielony Kciuk" - description = "Posadz 1,000 roslin" +title = "Zielony Kciuk" +description = "Posadz 1,000 roslin" [advancement.challenge_plant_5k] - title = "Baron Rolnictwa" - description = "Posadz 5,000 roslin" +title = "Baron Rolnictwa" +description = "Posadz 5,000 roslin" [advancement.challenge_compost_50] - title = "Recykler" - description = "Kompostuj 50 przedmiotow" +title = "Recykler" +description = "Kompostuj 50 przedmiotow" [advancement.challenge_compost_500] - title = "Wzbogacacz Gleby" - description = "Kompostuj 500 przedmiotow" +title = "Wzbogacacz Gleby" +description = "Kompostuj 500 przedmiotow" [advancement.challenge_shear_50] - title = "Strzygacz" - description = "Ostrzyż 50 istot" +title = "Strzygacz" +description = "Ostrzyż 50 istot" [advancement.challenge_shear_250] - title = "Mistrz Stada" - description = "Ostrzyż 250 istot" +title = "Mistrz Stada" +description = "Ostrzyż 250 istot" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Zabójca" - description = "Zabij 500 stworzen" +title = "Zabójca" +description = "Zabij 500 stworzen" [advancement.challenge_kills_5k] - title = "Kat" - description = "Zabij 5,000 stworzen" +title = "Kat" +description = "Zabij 5,000 stworzen" [advancement.challenge_boss_1] - title = "Pogromca Bossow" - description = "Zabij bossa" +title = "Pogromca Bossow" +description = "Zabij bossa" [advancement.challenge_boss_10] - title = "Pogromca Legend" - description = "Zabij 10 bossow" +title = "Pogromca Legend" +description = "Zabij 10 bossow" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Zwiedły" - description = "Przetrwaj 500 obrazen od witheringu" +title = "Zwiedły" +description = "Przetrwaj 500 obrazen od witheringu" [advancement.challenge_wither_dmg_5k] - title = "Ocalały z Zarazy" - description = "Przetrwaj 5,000 obrazen od witheringu" +title = "Ocalały z Zarazy" +description = "Przetrwaj 5,000 obrazen od witheringu" [advancement.challenge_wither_skel_25] - title = "Zbieracz Kosci" - description = "Zabij 25 witherowych szkieletow" +title = "Zbieracz Kosci" +description = "Zabij 25 witherowych szkieletow" [advancement.challenge_wither_skel_250] - title = "Plaga Szkieletow" - description = "Zabij 250 witherowych szkieletow" +title = "Plaga Szkieletow" +description = "Zabij 250 witherowych szkieletow" [advancement.challenge_wither_boss_1] - title = "Zabójca Withera" - description = "Pokonaj Withera" +title = "Zabójca Withera" +description = "Pokonaj Withera" [advancement.challenge_wither_boss_10] - title = "Władca Netheru" - description = "Pokonaj Withera 10 razy" +title = "Władca Netheru" +description = "Pokonaj Withera 10 razy" [advancement.challenge_roses_10] - title = "Ogrodnik Smierci" - description = "Zniszcz 10 witherowych roz" +title = "Ogrodnik Smierci" +description = "Zniszcz 10 witherowych roz" [advancement.challenge_roses_100] - title = "Zbieracz Zarazy" - description = "Zniszcz 100 witherowych roz" +title = "Zbieracz Zarazy" +description = "Zniszcz 100 witherowych roz" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Ramie Gornika" - description = "Zamachnij sie kilofem 500 razy" +title = "Ramie Gornika" +description = "Zamachnij sie kilofem 500 razy" [advancement.challenge_pick_swing_5k] - title = "Budowniczy Tuneli" - description = "Zamachnij sie kilofem 5,000 razy" +title = "Budowniczy Tuneli" +description = "Zamachnij sie kilofem 5,000 razy" [advancement.challenge_pick_damage_1k] - title = "Wojownik Kilofa" - description = "Zadaj 1,000 obrazen kilofem" +title = "Wojownik Kilofa" +description = "Zadaj 1,000 obrazen kilofem" [advancement.challenge_pick_damage_10k] - title = "Bojowy Kilof" - description = "Zadaj 10,000 obrazen kilofem" +title = "Bojowy Kilof" +description = "Zadaj 10,000 obrazen kilofem" [advancement.challenge_pick_value_5k] - title = "Poszukiwacz Klejnotow" - description = "Wydobadz bloki o wartosci 5,000" +title = "Poszukiwacz Klejnotow" +description = "Wydobadz bloki o wartosci 5,000" [advancement.challenge_pick_value_50k] - title = "Baron Rud" - description = "Wydobadz bloki o wartosci 50,000" +title = "Baron Rud" +description = "Wydobadz bloki o wartosci 50,000" [advancement.challenge_pick_ores_500] - title = "Poszukiwacz" - description = "Wydobadz 500 blokow rudy" +title = "Poszukiwacz" +description = "Wydobadz 500 blokow rudy" [advancement.challenge_pick_ores_5k] - title = "Mistrz Gornik" - description = "Wydobadz 5,000 blokow rudy" +title = "Mistrz Gornik" +description = "Wydobadz 5,000 blokow rudy" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Celny Strzelec" - description = "Zadaj 1,000 obrazen dystansowych" +title = "Celny Strzelec" +description = "Zadaj 1,000 obrazen dystansowych" [advancement.challenge_ranged_dmg_10k] - title = "Smiercionosny Łucznik" - description = "Zadaj 10,000 obrazen dystansowych" +title = "Smiercionosny Łucznik" +description = "Zadaj 10,000 obrazen dystansowych" [advancement.challenge_ranged_dist_5k] - title = "Daleki Zasieg" - description = "Wystrzel pociski na łaczny dystans 5,000 blokow" +title = "Daleki Zasieg" +description = "Wystrzel pociski na łaczny dystans 5,000 blokow" [advancement.challenge_ranged_dist_50k] - title = "Strzelec na Mile" - description = "Wystrzel pociski na łaczny dystans 50,000 blokow" +title = "Strzelec na Mile" +description = "Wystrzel pociski na łaczny dystans 50,000 blokow" [advancement.challenge_ranged_kills_50] - title = "Łucznik" - description = "Zabij 50 mobow bronia dystansowa" +title = "Łucznik" +description = "Zabij 50 mobow bronia dystansowa" [advancement.challenge_ranged_kills_500] - title = "Mistrz Łucznik" - description = "Zabij 500 mobow bronia dystansowa" +title = "Mistrz Łucznik" +description = "Zabij 500 mobow bronia dystansowa" [advancement.challenge_longshot_25] - title = "Snajper" - description = "Traf 25 strzałow dalekiego zasiegu (ponad 30 blokow)" +title = "Snajper" +description = "Traf 25 strzałow dalekiego zasiegu (ponad 30 blokow)" [advancement.challenge_longshot_250] - title = "Orle Oko" - description = "Traf 250 strzałow dalekiego zasiegu (ponad 30 blokow)" +title = "Orle Oko" +description = "Traf 250 strzałow dalekiego zasiegu (ponad 30 blokow)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Rzucacz Pereł" - description = "Rzuc 50 pereł Endu" +title = "Rzucacz Pereł" +description = "Rzuc 50 pereł Endu" [advancement.challenge_rift_pearls_500] - title = "Maniak Teleportacji" - description = "Rzuc 500 pereł Endu" +title = "Maniak Teleportacji" +description = "Rzuc 500 pereł Endu" [advancement.challenge_rift_enderman_50] - title = "Łowca Endermanow" - description = "Zabij 50 endermanow" +title = "Łowca Endermanow" +description = "Zabij 50 endermanow" [advancement.challenge_rift_enderman_500] - title = "Tropiciel Pustki" - description = "Zabij 500 endermanow" +title = "Tropiciel Pustki" +description = "Zabij 500 endermanow" [advancement.challenge_rift_dragon_500] - title = "Pogromca Smoka" - description = "Zadaj 500 obrazen Smokowi Endu" +title = "Pogromca Smoka" +description = "Zadaj 500 obrazen Smokowi Endu" [advancement.challenge_rift_dragon_5k] - title = "Smocza Zmora" - description = "Zadaj 5,000 obrazen Smokowi Endu" +title = "Smocza Zmora" +description = "Zadaj 5,000 obrazen Smokowi Endu" [advancement.challenge_rift_crystal_10] - title = "Łamacz Kryształow" - description = "Zniszcz 10 kryształow Endu" +title = "Łamacz Kryształow" +description = "Zniszcz 10 kryształow Endu" [advancement.challenge_rift_crystal_100] - title = "Burzyciel Endu" - description = "Zniszcz 100 kryształow Endu" +title = "Burzyciel Endu" +description = "Zniszcz 100 kryształow Endu" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Wedkarz" - description = "Złow 25 ryb" +title = "Wedkarz" +description = "Złow 25 ryb" [advancement.challenge_fish_250] - title = "Mistrz Rybak" - description = "Złow 250 ryb" +title = "Mistrz Rybak" +description = "Złow 250 ryb" [advancement.challenge_drowned_25] - title = "Łowca Utopcow" - description = "Zabij 25 utopcow" +title = "Łowca Utopcow" +description = "Zabij 25 utopcow" [advancement.challenge_drowned_250] - title = "Oczyszczacz Oceanu" - description = "Zabij 250 utopcow" +title = "Oczyszczacz Oceanu" +description = "Zabij 250 utopcow" [advancement.challenge_guardian_10] - title = "Zabójca Straznikow" - description = "Zabij 10 straznikow" +title = "Zabójca Straznikow" +description = "Zabij 10 straznikow" [advancement.challenge_guardian_100] - title = "Łupiezca Swiatyn" - description = "Zabij 100 straznikow" +title = "Łupiezca Swiatyn" +description = "Zabij 100 straznikow" [advancement.challenge_underwater_blocks_100] - title = "Podwodny Gornik" - description = "Zniszcz 100 blokow pod woda" +title = "Podwodny Gornik" +description = "Zniszcz 100 blokow pod woda" [advancement.challenge_underwater_blocks_1k] - title = "Inzynier Wodny" - description = "Zniszcz 1,000 blokow pod woda" +title = "Inzynier Wodny" +description = "Zniszcz 1,000 blokow pod woda" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Nozownik" - description = "Zadaj 500 obrazen podczas skradania" +title = "Nozownik" +description = "Zadaj 500 obrazen podczas skradania" [advancement.challenge_stealth_dmg_5k] - title = "Cichy Zabójca" - description = "Zadaj 5,000 obrazen podczas skradania" +title = "Cichy Zabójca" +description = "Zadaj 5,000 obrazen podczas skradania" [advancement.challenge_stealth_kills_10] - title = "Skrytobójca" - description = "Zabij 10 mobow podczas skradania" +title = "Skrytobójca" +description = "Zabij 10 mobow podczas skradania" [advancement.challenge_stealth_kills_100] - title = "Zniwiarz Cieni" - description = "Zabij 100 mobow podczas skradania" +title = "Zniwiarz Cieni" +description = "Zabij 100 mobow podczas skradania" [advancement.challenge_stealth_time_1h] - title = "Cierpliwy" - description = "Spedz 1 godzine skradajac sie (3,600 sekund)" +title = "Cierpliwy" +description = "Spedz 1 godzine skradajac sie (3,600 sekund)" [advancement.challenge_stealth_time_10h] - title = "Mistrz Cieni" - description = "Spedz 10 godzin skradajac sie (36,000 sekund)" +title = "Mistrz Cieni" +description = "Spedz 10 godzin skradajac sie (36,000 sekund)" [advancement.challenge_stealth_arrows_50] - title = "Cichy Łucznik" - description = "Wystrzel 50 strzal podczas skradania" +title = "Cichy Łucznik" +description = "Wystrzel 50 strzal podczas skradania" [advancement.challenge_stealth_arrows_500] - title = "Widmowy Łucznik" - description = "Wystrzel 500 strzal podczas skradania" +title = "Widmowy Łucznik" +description = "Wystrzel 500 strzal podczas skradania" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Uczen Ostrza" - description = "Zadaj 1,000 obrazen mieczem" +title = "Uczen Ostrza" +description = "Zadaj 1,000 obrazen mieczem" [advancement.challenge_sword_dmg_10k] - title = "Szermierz" - description = "Zadaj 10,000 obrazen mieczem" +title = "Szermierz" +description = "Zadaj 10,000 obrazen mieczem" [advancement.challenge_sword_kills_50] - title = "Pojedynkowicz" - description = "Zabij 50 mobow mieczem" +title = "Pojedynkowicz" +description = "Zabij 50 mobow mieczem" [advancement.challenge_sword_kills_500] - title = "Gladiator" - description = "Zabij 500 mobow mieczem" +title = "Gladiator" +description = "Zabij 500 mobow mieczem" [advancement.challenge_sword_crit_50] - title = "Krytyczny Cios" - description = "Wyląduj 50 trafien krytycznych mieczem" +title = "Krytyczny Cios" +description = "Wyląduj 50 trafien krytycznych mieczem" [advancement.challenge_sword_crit_500] - title = "Mistrz Precyzji" - description = "Wyląduj 500 trafien krytycznych mieczem" +title = "Mistrz Precyzji" +description = "Wyląduj 500 trafien krytycznych mieczem" [advancement.challenge_sword_heavy_25] - title = "Ciezki Cios" - description = "Wyląduj 25 ciezkich ciosow mieczem (powyzej 8 obrazen)" +title = "Ciezki Cios" +description = "Wyląduj 25 ciezkich ciosow mieczem (powyzej 8 obrazen)" [advancement.challenge_sword_heavy_250] - title = "Niszczycielski Cios" - description = "Wyląduj 250 ciezkich ciosow mieczem (powyzej 8 obrazen)" +title = "Niszczycielski Cios" +description = "Wyląduj 250 ciezkich ciosow mieczem (powyzej 8 obrazen)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Trener Bestii" - description = "Twoje zwierzeta zadaja łacznie 500 obrazen" +title = "Trener Bestii" +description = "Twoje zwierzeta zadaja łacznie 500 obrazen" [advancement.challenge_pet_dmg_5k] - title = "Mistrz Wojny" - description = "Twoje zwierzeta zadaja łacznie 5,000 obrazen" +title = "Mistrz Wojny" +description = "Twoje zwierzeta zadaja łacznie 5,000 obrazen" [advancement.challenge_tamed_10] - title = "Przyjaciel Zwierzat" - description = "Oswój 10 zwierzat" +title = "Przyjaciel Zwierzat" +description = "Oswój 10 zwierzat" [advancement.challenge_tamed_100] - title = "Opiekun Zoo" - description = "Oswój 100 zwierzat" +title = "Opiekun Zoo" +description = "Oswój 100 zwierzat" [advancement.challenge_pet_kills_25] - title = "Taktyka Stada" - description = "Twoje zwierzeta zabijaja 25 stworzen" +title = "Taktyka Stada" +description = "Twoje zwierzeta zabijaja 25 stworzen" [advancement.challenge_pet_kills_250] - title = "Alfa Dowódca" - description = "Twoje zwierzeta zabijaja 250 stworzen" +title = "Alfa Dowódca" +description = "Twoje zwierzeta zabijaja 250 stworzen" [advancement.challenge_taming_2500] - title = "Ekspert Hodowli" - description = "Rozmnóz 2,500 zwierzat" +title = "Ekspert Hodowli" +description = "Rozmnóz 2,500 zwierzat" [advancement.challenge_taming_25k] - title = "Mistrz Genetyki" - description = "Rozmnóz 25,000 zwierzat" +title = "Mistrz Genetyki" +description = "Rozmnóz 25,000 zwierzat" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Worek Treningowy" - description = "Otrzymaj 500 trafien" +title = "Worek Treningowy" +description = "Otrzymaj 500 trafien" [advancement.challenge_trag_hits_5k] - title = "Żarłok Kar" - description = "Otrzymaj 5,000 trafien" +title = "Żarłok Kar" +description = "Otrzymaj 5,000 trafien" [advancement.challenge_trag_deaths_10] - title = "Dziewiec Życ" - description = "Zgin 10 razy" +title = "Dziewiec Życ" +description = "Zgin 10 razy" [advancement.challenge_trag_deaths_100] - title = "Powracajacy Koszmar" - description = "Zgin 100 razy" +title = "Powracajacy Koszmar" +description = "Zgin 100 razy" [advancement.challenge_trag_fire_500] - title = "Ofiara Ognia" - description = "Przetrwaj 500 obrazen od ognia" +title = "Ofiara Ognia" +description = "Przetrwaj 500 obrazen od ognia" [advancement.challenge_trag_fire_5k] - title = "Feniks" - description = "Przetrwaj 5,000 obrazen od ognia" +title = "Feniks" +description = "Przetrwaj 5,000 obrazen od ognia" [advancement.challenge_trag_fall_500] - title = "Test Grawitacji" - description = "Przetrwaj 500 obrazen od upadku" +title = "Test Grawitacji" +description = "Przetrwaj 500 obrazen od upadku" [advancement.challenge_trag_fall_5k] - title = "Predkosc Graniczna" - description = "Przetrwaj 5,000 obrazen od upadku" +title = "Predkosc Graniczna" +description = "Przetrwaj 5,000 obrazen od upadku" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Osiłek" - description = "Zadaj 1,000 obrazen gołymi piesciami" +title = "Osiłek" +description = "Zadaj 1,000 obrazen gołymi piesciami" [advancement.challenge_unarmed_dmg_10k] - title = "Mistrz Sztuk Walki" - description = "Zadaj 10,000 obrazen gołymi piesciami" +title = "Mistrz Sztuk Walki" +description = "Zadaj 10,000 obrazen gołymi piesciami" [advancement.challenge_unarmed_kills_25] - title = "Goła Pięsc" - description = "Zabij 25 mobow gołymi piesciami" +title = "Goła Pięsc" +description = "Zabij 25 mobow gołymi piesciami" [advancement.challenge_unarmed_kills_250] - title = "Piesc Legendy" - description = "Zabij 250 mobow gołymi piesciami" +title = "Piesc Legendy" +description = "Zabij 250 mobow gołymi piesciami" [advancement.challenge_unarmed_crit_25] - title = "Krytyczny Cios Pięscia" - description = "Wyląduj 25 trafien krytycznych gołymi piesciami" +title = "Krytyczny Cios Pięscia" +description = "Wyląduj 25 trafien krytycznych gołymi piesciami" [advancement.challenge_unarmed_crit_250] - title = "Precyzyjna Piesc" - description = "Wyląduj 250 trafien krytycznych gołymi piesciami" +title = "Precyzyjna Piesc" +description = "Wyląduj 250 trafien krytycznych gołymi piesciami" [advancement.challenge_unarmed_heavy_25] - title = "Mocny Cios" - description = "Wyląduj 25 ciezkich ciosow gołymi piesciami (powyzej 6 obrazen)" +title = "Mocny Cios" +description = "Wyląduj 25 ciezkich ciosow gołymi piesciami (powyzej 6 obrazen)" [advancement.challenge_unarmed_heavy_250] - title = "Krol Nokautow" - description = "Wyląduj 250 ciezkich ciosow gołymi piesciami (powyzej 6 obrazen)" +title = "Krol Nokautow" +description = "Wyląduj 250 ciezkich ciosow gołymi piesciami (powyzej 6 obrazen)" # items [items] [items.bound_ender_peral] - name = "Relikwiarzowy Switoklin" - usage1 = "Shift + Lewy Przycisk Myszy aby przywiazac" - usage2 = "Prawy Przycisk Myszy aby uzyskac dostep do przywiazanego ekwipunku" +name = "Relikwiarzowy Switoklin" +usage1 = "Shift + Lewy Przycisk Myszy aby przywiazac" +usage2 = "Prawy Przycisk Myszy aby uzyskac dostep do przywiazanego ekwipunku" [items.bound_eye_of_ender] - name = "Kotwica Okularowa" - usage1 = "Prawy Przycisk Myszy aby zuzyc i teleportowac sie do przywiazanej lokalizacji" - usage2 = "Shift + Lewy Przycisk Myszy aby przywiazac do bloku" +name = "Kotwica Okularowa" +usage1 = "Prawy Przycisk Myszy aby zuzyc i teleportowac sie do przywiazanej lokalizacji" +usage2 = "Shift + Lewy Przycisk Myszy aby przywiazac do bloku" [items.bound_redstone_torch] - name = "Pilot Redstone" - usage1 = "Prawy Przycisk Myszy aby wytworzyc impuls Redstone 1-Tick" - usage2 = "Shift + Lewy Przycisk Myszy na bloku 'Cel' aby przywiazac" +name = "Pilot Redstone" +usage1 = "Prawy Przycisk Myszy aby wytworzyc impuls Redstone 1-Tick" +usage2 = "Shift + Lewy Przycisk Myszy na bloku 'Cel' aby przywiazac" [items.bound_snowball] - name = "Pajeczyna Pulapka!" - usage1 = "Rzuc aby stworzyc tymczasowa pulapke z pajeczyn w danym miejscu" +name = "Pajeczyna Pulapka!" +usage1 = "Rzuc aby stworzyc tymczasowa pulapke z pajeczyn w danym miejscu" [items.chrono_time_bottle] - name = "Czas w Butelce" - usage1 = "Pasywnie gromadzi czas bedac w ekwipunku" - usage2 = "Kliknij prawym przyciskiem myszy na bloki czasowe lub mlode zwierzeta aby wydac zgromadzony czas" - stored = "Zgromadzony Czas" +name = "Czas w Butelce" +usage1 = "Pasywnie gromadzi czas bedac w ekwipunku" +usage2 = "Kliknij prawym przyciskiem myszy na bloki czasowe lub mlode zwierzeta aby wydac zgromadzony czas" +stored = "Zgromadzony Czas" [items.chrono_time_bomb] - name = "Bomba Czasowa" - usage1 = "Kliknij prawym przyciskiem myszy aby wystrzelec bolid chrono tworzacy pole temporalne" +name = "Bomba Czasowa" +usage1 = "Kliknij prawym przyciskiem myszy aby wystrzelec bolid chrono tworzacy pole temporalne" [items.elevator_block] - name = "Blok Windy" - usage1 = "Skocz aby teleportowac sie w gore" - usage2 = "Kucnij aby teleportowac sie w dol" - usage3 = "Minimum 2 bloki powietrza miedzy windami" +name = "Blok Windy" +usage1 = "Skocz aby teleportowac sie w gore" +usage2 = "Kucnij aby teleportowac sie w dol" +usage3 = "Minimum 2 bloki powietrza miedzy windami" # snippets [snippets] [snippets.gui] - level = "Poziom" - knowledge = "wiedza" - power_used = "Uzyta Moc" - not_learned = "Nie Nauczone" - xp = "PD do" - welcome = "Witaj!" - welcome_back = "Witaj ponownie!" - xp_bonus_for_time = "PD za" - max_ability_power = "Maksymalna Moc Umiejetnosci" - unlock_this_by_clicking = "Odblokuj to klikajac Prawym Przyciskiem Myszy: " - back = "Wstecz" - unlearn_all = "Oducz sie wszystkiego" - unlearned_all = "Wszystko oduczone" +level = "Poziom" +knowledge = "wiedza" +power_used = "Uzyta Moc" +not_learned = "Nie Nauczone" +xp = "PD do" +welcome = "Witaj!" +welcome_back = "Witaj ponownie!" +xp_bonus_for_time = "PD za" +max_ability_power = "Maksymalna Moc Umiejetnosci" +unlock_this_by_clicking = "Odblokuj to klikajac Prawym Przyciskiem Myszy: " +back = "Wstecz" +unlearn_all = "Oducz sie wszystkiego" +unlearned_all = "Wszystko oduczone" [snippets.adapt_menu] - may_not_unlearn = "NIE MOZESZ SIE ODUCZYC" - may_unlearn = "MOZESZ SIE UCZYC/ODUCZYC" - knowledge_cost = "Koszt Wiedzy" - knowledge_available = "Dostepna Wiedza" - already_learned = "Juz Nauczone" - unlearn_refund = "Kliknij aby Oduczyc sie i Odzyskac" - no_refunds = "HARDCORE, ZWROTY WYLACZONE" - knowledge = "wiedza" - click_learn = "Kliknij aby sie Nauczyc" - no_knowledge = "(Nie masz zadnej Wiedzy)" - you_only_have = "Masz tylko" - how_to_level_up = "Rozwijaj umiejetnosci, aby zwiekszyc maksymalna moc." - not_enough_power = "Za malo mocy! Kazdy Poziom Umiejetnosci kosztuje 1 moc." - power = "moc" - power_drain = "Zuzycie Mocy" - learned = "Nauczone " - unlearned = "Oduczone " - activator_block = "Polka na ksiazki" +may_not_unlearn = "NIE MOZESZ SIE ODUCZYC" +may_unlearn = "MOZESZ SIE UCZYC/ODUCZYC" +knowledge_cost = "Koszt Wiedzy" +knowledge_available = "Dostepna Wiedza" +already_learned = "Juz Nauczone" +unlearn_refund = "Kliknij aby Oduczyc sie i Odzyskac" +no_refunds = "HARDCORE, ZWROTY WYLACZONE" +knowledge = "wiedza" +click_learn = "Kliknij aby sie Nauczyc" +no_knowledge = "(Nie masz zadnej Wiedzy)" +you_only_have = "Masz tylko" +how_to_level_up = "Rozwijaj umiejetnosci, aby zwiekszyc maksymalna moc." +not_enough_power = "Za malo mocy! Kazdy Poziom Umiejetnosci kosztuje 1 moc." +power = "moc" +power_drain = "Zuzycie Mocy" +learned = "Nauczone " +unlearned = "Oduczone " +activator_block = "Polka na ksiazki" [snippets.knowledge_orb] - contains = "zawiera" - knowledge = "wiedzy" - rightclick = "Kliknij Prawym Przyciskiem" - togainknowledge = "aby zdobyc te wiedze" - knowledge_orb = "Kula Wiedzy" +contains = "zawiera" +knowledge = "wiedzy" +rightclick = "Kliknij Prawym Przyciskiem" +togainknowledge = "aby zdobyc te wiedze" +knowledge_orb = "Kula Wiedzy" [snippets.experience_orb] - contains = "zawiera" - xp = "Doswiadczenia" - rightclick = "Kliknij Prawym Przyciskiem" - togainxp = "aby zdobyc to doswiadczenie" - xporb = "Kula Doswiadczenia" +contains = "zawiera" +xp = "Doswiadczenia" +rightclick = "Kliknij Prawym Przyciskiem" +togainxp = "aby zdobyc to doswiadczenie" +xporb = "Kula Doswiadczenia" # skill [skill] [skill.agility] - name = "Zwinnosc" - icon = "⇉" - description = "Zwinnosc to umiejetnosc szybkiego i plynnego poruszania sie w obliczu przeszkod." +name = "Zwinnosc" +icon = "⇉" +description = "Zwinnosc to umiejetnosc szybkiego i plynnego poruszania sie w obliczu przeszkod." [skill.architect] - name = "Architekt" - icon = "⬧" - description = "Struktury sa budulcem swiata. Rzeczywistosc jest w Twoich rekach i pod Twoja kontrola." +name = "Architekt" +icon = "⬧" +description = "Struktury sa budulcem swiata. Rzeczywistosc jest w Twoich rekach i pod Twoja kontrola." [skill.axes] - name = "Siekiery" - icon = "🪓" - description1 = "Po co scinac drzewa, skoro mozna rabac " - description2 = "rzeczy" - description3 = "zamiast tego, ten sam efekt koncowy!" +name = "Siekiery" +icon = "🪓" +description1 = "Po co scinac drzewa, skoro mozna rabac " +description2 = "rzeczy" +description3 = "zamiast tego, ten sam efekt koncowy!" [skill.brewing] - name = "Browarnictwo" - icon = "❦" - description = "Podwojny Babelek, Potrojny Babelek, Poczworny Babelek- Nadal nie moge wlac tej mikstury do kotla" +name = "Browarnictwo" +icon = "❦" +description = "Podwojny Babelek, Potrojny Babelek, Poczworny Babelek- Nadal nie moge wlac tej mikstury do kotla" [skill.blocking] - name = "Blokowanie" - icon = "🛡" - description = "Kije i kamienie nie zlamia Ci kosci, ale tarcza owszem." +name = "Blokowanie" +icon = "🛡" +description = "Kije i kamienie nie zlamia Ci kosci, ale tarcza owszem." [skill.crafting] - name = "Rzemioslo" - icon = "⌂" - description = "Skoro nie ma juz czesci do ulozenia, czemu nie zrobic kolejnej?" +name = "Rzemioslo" +icon = "⌂" +description = "Skoro nie ma juz czesci do ulozenia, czemu nie zrobic kolejnej?" [skill.discovery] - name = "Odkrycie" - icon = "⚛" - description = "Gdy twoja percepcja sie rozszerza, twoj umysl rozplatuje sie, by odkryc to, czego nie odkryles." +name = "Odkrycie" +icon = "⚛" +description = "Gdy twoja percepcja sie rozszerza, twoj umysl rozplatuje sie, by odkryc to, czego nie odkryles." [skill.enchanting] - name = "Zaklecia" - icon = "✠" - description = "O czym ty mowisz? Proroctwa, wizje, przesadny belkot?" +name = "Zaklecia" +icon = "✠" +description = "O czym ty mowisz? Proroctwa, wizje, przesadny belkot?" [skill.excavation] - name = "Wykopywanie" - icon = "ᛳ" - description = "Kopie kopie doleczek..." +name = "Wykopywanie" +icon = "ᛳ" +description = "Kopie kopie doleczek..." [skill.herbalism] - name = "Zielarstwo" - icon = "⚘" - description = "Nie moge znalezc zadnych roslin, ale moge znalezc nasiona i- czy to... Ziele?" +name = "Zielarstwo" +icon = "⚘" +description = "Nie moge znalezc zadnych roslin, ale moge znalezc nasiona i- czy to... Ziele?" [skill.hunter] - name = "Lowca" - icon = "☠" - description = "W polowaniu liczy sie podroz, nie wynik." +name = "Lowca" +icon = "☠" +description = "W polowaniu liczy sie podroz, nie wynik." [skill.nether] - name = "Nether" - icon = "₪" - description = "Z glebin samego Netheru." +name = "Nether" +icon = "₪" +description = "Z glebin samego Netheru." [skill.pickaxe] - name = "Kilof" - icon = "⛏" - description = "Krasnoludy sa gornikami, ale ja tez sie czegos nauczylem. JESTEM SZWEDEM" +name = "Kilof" +icon = "⛏" +description = "Krasnoludy sa gornikami, ale ja tez sie czegos nauczylem. JESTEM SZWEDEM" [skill.ranged] - name = "Dystansowy" - icon = "🏹" - description = "Dystans to klucz do zwyciestwa i klucz do przetrwania." +name = "Dystansowy" +icon = "🏹" +description = "Dystans to klucz do zwyciestwa i klucz do przetrwania." [skill.rift] - name = "Szczelina" - icon = "❍" - description = "Szczelina to zraca uprza, ale ty ja opanowalec." +name = "Szczelina" +icon = "❍" +description = "Szczelina to zraca uprza, ale ty ja opanowalec." [skill.seaborne] - name = "Morski" - icon = "🎣" - description = "Dzieki tej umiejetnosci mozesz obudzic cuda wody." +name = "Morski" +icon = "🎣" +description = "Dzieki tej umiejetnosci mozesz obudzic cuda wody." [skill.stealth] - name = "Skradanie" - icon = "☯" - description = "Sztuka niewidzialnosci. Krocz w cieniach." +name = "Skradanie" +icon = "☯" +description = "Sztuka niewidzialnosci. Krocz w cieniach." [skill.swords] - name = "Miecze" - icon = "⚔" - description = "Moca Szarego Kamienia!" +name = "Miecze" +icon = "⚔" +description = "Moca Szarego Kamienia!" [skill.taming] - name = "Oswajanie" - icon = "♥" - description = "Papugi i pszczoly... a ty?" +name = "Oswajanie" +icon = "♥" +description = "Papugi i pszczoly... a ty?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Krew plynie zylami wszechswiata. Scisnieta przez twoje dlonie." +name = "TragOul" +icon = "🗡" +description = "Krew plynie zylami wszechswiata. Scisnieta przez twoje dlonie." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Nakrec Zegar wszechswiata, poczuj przeplyw. Zlam zegar, stac sie nim." +name = "Chronos" +icon = "🕒" +description = "Nakrec Zegar wszechswiata, poczuj przeplyw. Zlam zegar, stac sie nim." [skill.unarmed] - name = "Walka Wreczna" - icon = "»" - description = "Bez broni nie znaczy bez sily." +name = "Walka Wreczna" +icon = "»" +description = "Bez broni nie znaczy bez sily." # agility [agility] [agility.armor_up] - name = "Pancerz-Up" - description = "Zdobywaj wiecej pancerza im dluzej sprintujesz!" - lore1 = "Maksymalny Pancerz" - lore2 = "Czas Narastania Pancerza" - lore = ["Maksymalny Pancerz", "Czas Narastania Pancerza"] +name = "Pancerz-Up" +description = "Zdobywaj wiecej pancerza im dluzej sprintujesz!" +lore1 = "Maksymalny Pancerz" +lore2 = "Czas Narastania Pancerza" +lore = ["Maksymalny Pancerz", "Czas Narastania Pancerza"] [agility.ladder_slide] - name = "Zjazd po Drabinie" - description = "Wspinaj sie i zesliguj po drabinach znacznie szybciej w obu kierunkach." - lore1 = "Mnoznik predkosci na drabinie" - lore2 = "Predkosc szybkiego zjazdu" - lore = ["Mnoznik predkosci na drabinie", "Predkosc szybkiego zjazdu"] +name = "Zjazd po Drabinie" +description = "Wspinaj sie i zesliguj po drabinach znacznie szybciej w obu kierunkach." +lore1 = "Mnoznik predkosci na drabinie" +lore2 = "Predkosc szybkiego zjazdu" +lore = ["Mnoznik predkosci na drabinie", "Predkosc szybkiego zjazdu"] [agility.super_jump] - name = "Super Skok" - description = "Wyjatkowa Przewaga Wysokosci." - lore1 = "Maksymalna Wysokosc Skoku" - lore2 = "Kucnij + Skok aby wykonac Super Skok!" - lore = ["Maksymalna Wysokosc Skoku", "Kucnij + Skok aby wykonac Super Skok!"] +name = "Super Skok" +description = "Wyjatkowa Przewaga Wysokosci." +lore1 = "Maksymalna Wysokosc Skoku" +lore2 = "Kucnij + Skok aby wykonac Super Skok!" +lore = ["Maksymalna Wysokosc Skoku", "Kucnij + Skok aby wykonac Super Skok!"] [agility.wall_jump] - name = "Skok od Sciany" - description = "Przytrzymaj Shift w powietrzu przy scianie aby sie przyczepic i odskoczycz!" - lore1 = "Maksymalna Ilosc Skokow" - lore2 = "Wysokosc Skoku" - lore = ["Maksymalna Ilosc Skokow", "Wysokosc Skoku"] +name = "Skok od Sciany" +description = "Przytrzymaj Shift w powietrzu przy scianie aby sie przyczepic i odskoczycz!" +lore1 = "Maksymalna Ilosc Skokow" +lore2 = "Wysokosc Skoku" +lore = ["Maksymalna Ilosc Skokow", "Wysokosc Skoku"] [agility.wind_up] - name = "Rozkrecanie" - description = "Nabieraj predkosci im dluzej biegniesz!" - lore1 = "Maksymalna Predkosc" - lore2 = "Czas Rozkrecania" - lore = ["Maksymalna Predkosc", "Czas Rozkrecania"] +name = "Rozkrecanie" +description = "Nabieraj predkosci im dluzej biegniesz!" +lore1 = "Maksymalna Predkosc" +lore2 = "Czas Rozkrecania" +lore = ["Maksymalna Predkosc", "Czas Rozkrecania"] # architect [architect] [architect.elevator] - name = "Winda" - description = "Pozwala zbudowac winde do szybkiego teleportowania sie w pionie!" - lore1 = "Odblokowuje przepis na winde: X=WELNA, Y=PERLA ENDERA" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Odblokowuje przepis na winde: X=WELNA, Y=PERLA ENDERA", "XXX", "XYX", "XXX"] +name = "Winda" +description = "Pozwala zbudowac winde do szybkiego teleportowania sie w pionie!" +lore1 = "Odblokowuje przepis na winde: X=WELNA, Y=PERLA ENDERA" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Odblokowuje przepis na winde: X=WELNA, Y=PERLA ENDERA", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Magiczny Fundament" - description = "Pozwala na kucniecie i umieszczenie tymczasowego fundamentu pod soba!" - lore1 = "Magicznie stworz: " - lore2 = "Blokow pod soba!" - lore = ["Magicznie stworz: ", "Blokow pod soba!"] +name = "Magiczny Fundament" +description = "Pozwala na kucniecie i umieszczenie tymczasowego fundamentu pod soba!" +lore1 = "Magicznie stworz: " +lore2 = "Blokow pod soba!" +lore = ["Magicznie stworz: ", "Blokow pod soba!"] [architect.glass] - name = "Jedwabny Dotyk Szkla" - description = "Zapobiega utracie blokow szklanych kiedy rozbijasz je gola reka!" - lore1 = "Twoje rece zyskuja Jedwabny Dotyk dla Szkla" - lore = ["Twoje rece zyskuja Jedwabny Dotyk dla Szkla"] +name = "Jedwabny Dotyk Szkla" +description = "Zapobiega utracie blokow szklanych kiedy rozbijasz je gola reka!" +lore1 = "Twoje rece zyskuja Jedwabny Dotyk dla Szkla" +lore = ["Twoje rece zyskuja Jedwabny Dotyk dla Szkla"] [architect.wireless_redstone] - name = "Pilot Redstone" - description = "Pozwala uzyc pochodni z redstone do zdalnego przelaczania redstone!" - lore1 = "Cel + Pochodnia Redstone + Perla Endera = 1 Pilot Redstone" - lore = ["Cel + Pochodnia Redstone + Perla Endera = 1 Pilot Redstone"] +name = "Pilot Redstone" +description = "Pozwala uzyc pochodni z redstone do zdalnego przelaczania redstone!" +lore1 = "Cel + Pochodnia Redstone + Perla Endera = 1 Pilot Redstone" +lore = ["Cel + Pochodnia Redstone + Perla Endera = 1 Pilot Redstone"] [architect.placement] - name = "Rozdzka Budowniczego" - description = "Pozwala umieszczac wiele blokow naraz. Aby aktywowac: kucnij i trzymaj blok pasujacy do bloku na ktory patrzysz, nastepnie postaw! Pamietaj, moze byc konieczne lekkie przesuniecie sie!" - lore1 = "Potrzebujesz" - lore2 = "blokow w rece aby to postawic" - lore3 = "Materialowa Rozdzka Budowniczego" - lore = ["Potrzebujesz", "blokow w rece aby to postawic", "Materialowa Rozdzka Budowniczego"] +name = "Rozdzka Budowniczego" +description = "Pozwala umieszczac wiele blokow naraz. Aby aktywowac: kucnij i trzymaj blok pasujacy do bloku na ktory patrzysz, nastepnie postaw! Pamietaj, moze byc konieczne lekkie przesuniecie sie!" +lore1 = "Potrzebujesz" +lore2 = "blokow w rece aby to postawic" +lore3 = "Materialowa Rozdzka Budowniczego" +lore = ["Potrzebujesz", "blokow w rece aby to postawic", "Materialowa Rozdzka Budowniczego"] # axe [axe] [axe.chop] - name = "Ciecie Siekiera" - description = "Scinaj drzewa klikajac prawym przyciskiem myszy na dolny klode!" - lore1 = "Blokow na Ciecie" - lore2 = "Czas Odnowienia Ciecia" - lore3 = "Zuzycie Narzedzia" - lore = ["Blokow na Ciecie", "Czas Odnowienia Ciecia", "Zuzycie Narzedzia"] +name = "Ciecie Siekiera" +description = "Scinaj drzewa klikajac prawym przyciskiem myszy na dolny klode!" +lore1 = "Blokow na Ciecie" +lore2 = "Czas Odnowienia Ciecia" +lore3 = "Zuzycie Narzedzia" +lore = ["Blokow na Ciecie", "Czas Odnowienia Ciecia", "Zuzycie Narzedzia"] [axe.log_swap] - name = "Wymiennik Klod Lucy" - description = "Zmien rodzaj klod na Stole Rzemicslniczym!" - lore1 = "8 Klod dowolnego rodzaju + 1 sadzonka = 8 klod typu sadzonki" - lore = ["8 Klod dowolnego rodzaju + 1 sadzonka = 8 klod typu sadzonki"] +name = "Wymiennik Klod Lucy" +description = "Zmien rodzaj klod na Stole Rzemicslniczym!" +lore1 = "8 Klod dowolnego rodzaju + 1 sadzonka = 8 klod typu sadzonki" +lore = ["8 Klod dowolnego rodzaju + 1 sadzonka = 8 klod typu sadzonki"] [axe.drop_to_inventory] - name = "Siekiera Zrzut-Do-Ekwipunku" +name = "Siekiera Zrzut-Do-Ekwipunku" [axe.ground_smash] - name = "Uderzenie w Ziemie Siekiera" - description = "Skocz, a potem kucnij i zmiazdzh wszystkich poblizskich wrogow." - lore1 = "Obrazenia" - lore2 = "Zasieg Blokow" - lore3 = "Sila" - lore4 = "Czas Odnowienia Uderzenia" - lore = ["Obrazenia", "Zasieg Blokow", "Sila", "Czas Odnowienia Uderzenia"] +name = "Uderzenie w Ziemie Siekiera" +description = "Skocz, a potem kucnij i zmiazdzh wszystkich poblizskich wrogow." +lore1 = "Obrazenia" +lore2 = "Zasieg Blokow" +lore3 = "Sila" +lore4 = "Czas Odnowienia Uderzenia" +lore = ["Obrazenia", "Zasieg Blokow", "Sila", "Czas Odnowienia Uderzenia"] [axe.leaf_miner] - name = "Gornik Lisci" - description = "Pozwala niszczyc duze ilosci lisci naraz!" - lore1 = "Kucnij i niszcz LISCIE" - lore2 = "zasieg niszczenia lisci" - lore3 = "Nie otrzymasz dropu z lisci (Zapobieganie Naduzywaniu)" - lore = ["Kucnij i niszcz LISCIE", "zasieg niszczenia lisci", "Nie otrzymasz dropu z lisci (Zapobieganie Naduzywaniu)"] +name = "Gornik Lisci" +description = "Pozwala niszczyc duze ilosci lisci naraz!" +lore1 = "Kucnij i niszcz LISCIE" +lore2 = "zasieg niszczenia lisci" +lore3 = "Nie otrzymasz dropu z lisci (Zapobieganie Naduzywaniu)" +lore = ["Kucnij i niszcz LISCIE", "zasieg niszczenia lisci", "Nie otrzymasz dropu z lisci (Zapobieganie Naduzywaniu)"] [axe.wood_miner] - name = "Gornik Drewna" - description = "Pozwala niszczyc duze ilosci drewna naraz!" - lore1 = "Kucnij i niszcz DREWNO/KLODY (Nie Deski)" - lore2 = "zasieg niszczenia drewna" - lore3 = "Dziala ze Zrzutem do Ekwipunku" - lore = ["Kucnij i niszcz DREWNO/KLODY (Nie Deski)", "zasieg niszczenia drewna", "Dziala ze Zrzutem do Ekwipunku"] +name = "Gornik Drewna" +description = "Pozwala niszczyc duze ilosci drewna naraz!" +lore1 = "Kucnij i niszcz DREWNO/KLODY (Nie Deski)" +lore2 = "zasieg niszczenia drewna" +lore3 = "Dziala ze Zrzutem do Ekwipunku" +lore = ["Kucnij i niszcz DREWNO/KLODY (Nie Deski)", "zasieg niszczenia drewna", "Dziala ze Zrzutem do Ekwipunku"] # brewing [brewing] [brewing.lingering] - name = "Dlugotrwaly Napar" - description = "Uwarzone mikstury trwaja dluzej!" - lore1 = "Czas Trwania" - lore2 = "Czas Trwania" - lore = ["Czas Trwania", "Czas Trwania"] +name = "Dlugotrwaly Napar" +description = "Uwarzone mikstury trwaja dluzej!" +lore1 = "Czas Trwania" +lore2 = "Czas Trwania" +lore = ["Czas Trwania", "Czas Trwania"] [brewing.super_heated] - name = "Super Podgrzany Napar" - description = "Stojaki alchemiczne dzialaja szybciej im sa goretsze." - lore1 = "Za Dotykajacy Blok Ognia" - lore2 = "Za Dotykajacy Blok Lawy" - lore = ["Za Dotykajacy Blok Ognia", "Za Dotykajacy Blok Lawy"] +name = "Super Podgrzany Napar" +description = "Stojaki alchemiczne dzialaja szybciej im sa goretsze." +lore1 = "Za Dotykajacy Blok Ognia" +lore2 = "Za Dotykajacy Blok Lawy" +lore = ["Za Dotykajacy Blok Ognia", "Za Dotykajacy Blok Lawy"] [brewing.darkness] - name = "Ciemnosc w Butelce" - description = "Nie wiem po co ci to, ale prosze bardzo!" - lore1 = "Mikstura Noktowizji + Czarny Beton = Mikstura Ciemnosci (30 sekund)" - lore2 = "Nalezy zauwazyc, ze uniemozliwia to sprintowanie!" - lore = ["Mikstura Noktowizji + Czarny Beton = Mikstura Ciemnosci (30 sekund)", "Nalezy zauwazyc, ze uniemozliwia to sprintowanie!"] +name = "Ciemnosc w Butelce" +description = "Nie wiem po co ci to, ale prosze bardzo!" +lore1 = "Mikstura Noktowizji + Czarny Beton = Mikstura Ciemnosci (30 sekund)" +lore2 = "Nalezy zauwazyc, ze uniemozliwia to sprintowanie!" +lore = ["Mikstura Noktowizji + Czarny Beton = Mikstura Ciemnosci (30 sekund)", "Nalezy zauwazyc, ze uniemozliwia to sprintowanie!"] [brewing.haste] - name = "Pospiech w Butelce" - description = "Kiedy Wydajnosc to za malo" - lore1 = "Mikstura Szybkosci + Odlamek Ametystu = Mikstura Pospiechu (60 sekund)" - lore2 = "Mikstura Szybkosci + Blok Ametystu = Mikstura Pospiechu-2 (30 sekund)" - lore = ["Mikstura Szybkosci + Odlamek Ametystu = Mikstura Pospiechu (60 sekund)", "Mikstura Szybkosci + Blok Ametystu = Mikstura Pospiechu-2 (30 sekund)"] +name = "Pospiech w Butelce" +description = "Kiedy Wydajnosc to za malo" +lore1 = "Mikstura Szybkosci + Odlamek Ametystu = Mikstura Pospiechu (60 sekund)" +lore2 = "Mikstura Szybkosci + Blok Ametystu = Mikstura Pospiechu-2 (30 sekund)" +lore = ["Mikstura Szybkosci + Odlamek Ametystu = Mikstura Pospiechu (60 sekund)", "Mikstura Szybkosci + Blok Ametystu = Mikstura Pospiechu-2 (30 sekund)"] [brewing.absorption] - name = "Absorpcja w Butelce" - description = "Wzmocnij cialo!" - lore1 = "Natychmiastowe Leczenie + Kwarc = Mikstura Absorpcji (60 sekund)" - lore2 = "Natychmiastowe Leczenie + Blok Kwarcu = Mikstura Absorpcji-2 (30 sekund)" - lore = ["Natychmiastowe Leczenie + Kwarc = Mikstura Absorpcji (60 sekund)", "Natychmiastowe Leczenie + Blok Kwarcu = Mikstura Absorpcji-2 (30 sekund)"] +name = "Absorpcja w Butelce" +description = "Wzmocnij cialo!" +lore1 = "Natychmiastowe Leczenie + Kwarc = Mikstura Absorpcji (60 sekund)" +lore2 = "Natychmiastowe Leczenie + Blok Kwarcu = Mikstura Absorpcji-2 (30 sekund)" +lore = ["Natychmiastowe Leczenie + Kwarc = Mikstura Absorpcji (60 sekund)", "Natychmiastowe Leczenie + Blok Kwarcu = Mikstura Absorpcji-2 (30 sekund)"] [brewing.fatigue] - name = "Zmeczenie w Butelce" - description = "Oslab cialo!" - lore1 = "Mikstura Slabosci + Kula Sluzu = Mikstura Zmeczenia (30 sekund)" - lore2 = "Mikstura Slabosci + Blok Sluzu = Mikstura Zmeczenia-2 (15 sekund)" - lore = ["Mikstura Slabosci + Kula Sluzu = Mikstura Zmeczenia (30 sekund)", "Mikstura Slabosci + Blok Sluzu = Mikstura Zmeczenia-2 (15 sekund)"] +name = "Zmeczenie w Butelce" +description = "Oslab cialo!" +lore1 = "Mikstura Slabosci + Kula Sluzu = Mikstura Zmeczenia (30 sekund)" +lore2 = "Mikstura Slabosci + Blok Sluzu = Mikstura Zmeczenia-2 (15 sekund)" +lore = ["Mikstura Slabosci + Kula Sluzu = Mikstura Zmeczenia (30 sekund)", "Mikstura Slabosci + Blok Sluzu = Mikstura Zmeczenia-2 (15 sekund)"] [brewing.hunger] - name = "Glod w Butelce" - description = "Nakarm Nienasyconego!" - lore1 = "Niezreczna Mikstura + Zgnile Mieso = Mikstura Glodu (30 sekund)" - lore2 = "Mikstura Slabosci + Zgnile Mieso = Mikstura Glodu-3 (15 sekund)" - lore = ["Niezreczna Mikstura + Zgnile Mieso = Mikstura Glodu (30 sekund)", "Mikstura Slabosci + Zgnile Mieso = Mikstura Glodu-3 (15 sekund)"] +name = "Glod w Butelce" +description = "Nakarm Nienasyconego!" +lore1 = "Niezreczna Mikstura + Zgnile Mieso = Mikstura Glodu (30 sekund)" +lore2 = "Mikstura Slabosci + Zgnile Mieso = Mikstura Glodu-3 (15 sekund)" +lore = ["Niezreczna Mikstura + Zgnile Mieso = Mikstura Glodu (30 sekund)", "Mikstura Slabosci + Zgnile Mieso = Mikstura Glodu-3 (15 sekund)"] [brewing.nausea] - name = "Mdlosci w Butelce" - description = "Robi mi sie od ciebie niedobrze!" - lore1 = "Niezreczna Mikstura + Brazowy Grzyb = Mikstura Mdlosci (16 sekund)" - lore2 = "Niezreczna Mikstura + Szkarlatny Grzyb = Mikstura Mdlosci-2 (8 sekund)" - lore = ["Niezreczna Mikstura + Brazowy Grzyb = Mikstura Mdlosci (16 sekund)", "Niezreczna Mikstura + Szkarlatny Grzyb = Mikstura Mdlosci-2 (8 sekund)"] +name = "Mdlosci w Butelce" +description = "Robi mi sie od ciebie niedobrze!" +lore1 = "Niezreczna Mikstura + Brazowy Grzyb = Mikstura Mdlosci (16 sekund)" +lore2 = "Niezreczna Mikstura + Szkarlatny Grzyb = Mikstura Mdlosci-2 (8 sekund)" +lore = ["Niezreczna Mikstura + Brazowy Grzyb = Mikstura Mdlosci (16 sekund)", "Niezreczna Mikstura + Szkarlatny Grzyb = Mikstura Mdlosci-2 (8 sekund)"] [brewing.blindness] - name = "Slepota w Butelce" - description = "Jestes okropna osoba..." - lore1 = "Niezreczna Mikstura + Worek z Atramentem = Mikstura Slepoty (30 sekund)" - lore2 = "Niezreczna Mikstura + Swiecacy Worek z Atramentem = Mikstura Slepoty-2 (15 sekund)" - lore = ["Niezreczna Mikstura + Worek z Atramentem = Mikstura Slepoty (30 sekund)", "Niezreczna Mikstura + Swiecacy Worek z Atramentem = Mikstura Slepoty-2 (15 sekund)"] +name = "Slepota w Butelce" +description = "Jestes okropna osoba..." +lore1 = "Niezreczna Mikstura + Worek z Atramentem = Mikstura Slepoty (30 sekund)" +lore2 = "Niezreczna Mikstura + Swiecacy Worek z Atramentem = Mikstura Slepoty-2 (15 sekund)" +lore = ["Niezreczna Mikstura + Worek z Atramentem = Mikstura Slepoty (30 sekund)", "Niezreczna Mikstura + Swiecacy Worek z Atramentem = Mikstura Slepoty-2 (15 sekund)"] [brewing.resistance] - name = "Odpornosc w Butelce" - description = "Fortyfikacja w najlepszym wydaniu!" - lore1 = "Niezreczna Mikstura + Sztabka Zelaza = Mikstura Odpornosci (60 sekund)" - lore2 = "Niezreczna Mikstura + Blok Zelaza = Mikstura Odpornosci-2 (30 sekund)" - lore = ["Niezreczna Mikstura + Sztabka Zelaza = Mikstura Odpornosci (60 sekund)", "Niezreczna Mikstura + Blok Zelaza = Mikstura Odpornosci-2 (30 sekund)"] +name = "Odpornosc w Butelce" +description = "Fortyfikacja w najlepszym wydaniu!" +lore1 = "Niezreczna Mikstura + Sztabka Zelaza = Mikstura Odpornosci (60 sekund)" +lore2 = "Niezreczna Mikstura + Blok Zelaza = Mikstura Odpornosci-2 (30 sekund)" +lore = ["Niezreczna Mikstura + Sztabka Zelaza = Mikstura Odpornosci (60 sekund)", "Niezreczna Mikstura + Blok Zelaza = Mikstura Odpornosci-2 (30 sekund)"] [brewing.health_boost] - name = "Zycie w Butelce" - description = "Kiedy maksymalne zdrowie to za malo..." - lore1 = "Mikstura Natychmiastowego Leczenia + Zlote Jablko = Mikstura Wzmocnienia Zdrowia (120 sekund)" - lore2 = "Mikstura Natychmiastowego Leczenia + Zaczarowane Zlote Jablko = Mikstura Wzmocnienia Zdrowia-2 (120 sekund)" - lore = ["Mikstura Natychmiastowego Leczenia + Zlote Jablko = Mikstura Wzmocnienia Zdrowia (120 sekund)", "Mikstura Natychmiastowego Leczenia + Zaczarowane Zlote Jablko = Mikstura Wzmocnienia Zdrowia-2 (120 sekund)"] +name = "Zycie w Butelce" +description = "Kiedy maksymalne zdrowie to za malo..." +lore1 = "Mikstura Natychmiastowego Leczenia + Zlote Jablko = Mikstura Wzmocnienia Zdrowia (120 sekund)" +lore2 = "Mikstura Natychmiastowego Leczenia + Zaczarowane Zlote Jablko = Mikstura Wzmocnienia Zdrowia-2 (120 sekund)" +lore = ["Mikstura Natychmiastowego Leczenia + Zlote Jablko = Mikstura Wzmocnienia Zdrowia (120 sekund)", "Mikstura Natychmiastowego Leczenia + Zaczarowane Zlote Jablko = Mikstura Wzmocnienia Zdrowia-2 (120 sekund)"] [brewing.decay] - name = "Rozklad w Butelce" - description = "Kto by pomyslal, ze Detrytus bedzie tak przydatny?" - lore1 = "Mikstura Slabosci + Trujacy Ziemniak = Mikstura Wiecenia (16 sekund)" - lore2 = "Mikstura Slabosci + Szkarlatne Korzenie = Mikstura Wiecenia-2 (8 sekund)" - lore = ["Mikstura Slabosci + Trujacy Ziemniak = Mikstura Wiecenia (16 sekund)", "Mikstura Slabosci + Szkarlatne Korzenie = Mikstura Wiecenia-2 (8 sekund)"] +name = "Rozklad w Butelce" +description = "Kto by pomyslal, ze Detrytus bedzie tak przydatny?" +lore1 = "Mikstura Slabosci + Trujacy Ziemniak = Mikstura Wiecenia (16 sekund)" +lore2 = "Mikstura Slabosci + Szkarlatne Korzenie = Mikstura Wiecenia-2 (8 sekund)" +lore = ["Mikstura Slabosci + Trujacy Ziemniak = Mikstura Wiecenia (16 sekund)", "Mikstura Slabosci + Szkarlatne Korzenie = Mikstura Wiecenia-2 (8 sekund)"] [brewing.saturation] - name = "Nasycenie w Butelce" - description = "Wiesz co... nawet nie jestem glodny..." - lore1 = "Mikstura Regeneracji + Pieczony Ziemniak = Mikstura Nasycenia" - lore2 = "Mikstura Regeneracji + Bela Siana = Mikstura Nasycenia-2" - lore = ["Mikstura Regeneracji + Pieczony Ziemniak = Mikstura Nasycenia", "Mikstura Regeneracji + Bela Siana = Mikstura Nasycenia-2"] +name = "Nasycenie w Butelce" +description = "Wiesz co... nawet nie jestem glodny..." +lore1 = "Mikstura Regeneracji + Pieczony Ziemniak = Mikstura Nasycenia" +lore2 = "Mikstura Regeneracji + Bela Siana = Mikstura Nasycenia-2" +lore = ["Mikstura Regeneracji + Pieczony Ziemniak = Mikstura Nasycenia", "Mikstura Regeneracji + Bela Siana = Mikstura Nasycenia-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Lancuchy Mefistofelesa" - description = "Pozwala tworzyc zbroje kolczuga" - lore1 = "Przepis jest taki sam jak na kazda inna zbroje, ale z brylek zelaza" - lore = ["Przepis jest taki sam jak na kazda inna zbroje, ale z brylek zelaza"] +name = "Lancuchy Mefistofelesa" +description = "Pozwala tworzyc zbroje kolczuga" +lore1 = "Przepis jest taki sam jak na kazda inna zbroje, ale z brylek zelaza" +lore = ["Przepis jest taki sam jak na kazda inna zbroje, ale z brylek zelaza"] [blocking.horse_armorer] - name = "Wytwarzalna Zbroja Konia" - description = "Pozwala tworzyc zbroje konia" - lore1 = "Otocz siodlo materialem, ktorego chcesz uzyc do wytworzenia zbroi" - lore = ["Otocz siodlo materialem, ktorego chcesz uzyc do wytworzenia zbroi"] +name = "Wytwarzalna Zbroja Konia" +description = "Pozwala tworzyc zbroje konia" +lore1 = "Otocz siodlo materialem, ktorego chcesz uzyc do wytworzenia zbroi" +lore = ["Otocz siodlo materialem, ktorego chcesz uzyc do wytworzenia zbroi"] [blocking.saddle_crafter] - name = "Wytwarzalne Siodlo" - description = "Wytworz siodlo ze skory" - lore1 = "Przepis: 5 Skora:" - lore = ["Przepis: 5 Skora:"] +name = "Wytwarzalne Siodlo" +description = "Wytworz siodlo ze skory" +lore1 = "Przepis: 5 Skora:" +lore = ["Przepis: 5 Skora:"] [blocking.multi_armor] - name = "Multi-Pancerz" - description = "Wiaz Elytry ze zbroja" - lore1 = "To niesamowita umiejetnosc do podrozowania." - lore2 = "Dynamicznie lacz i zmieniaj Zbroje/Elytry w locie!" - lore3 = "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku." - lore4 = "Aby rozdzielic zbroje, upusc przedmiot z Shiftem, a rozlozy sie." - lore5 = "Jesli twoj Multi-Pancerz zostanie zniszczony, stracisz wszystkie przedmioty w nim." - lore6 = "Nie potrzebuje zbroi, to mnie rozczarowuje..." - lore = ["To niesamowita umiejetnosc do podrozowania.", "Dynamicznie lacz i zmieniaj Zbroje/Elytry w locie!", "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku.", "Aby rozdzielic zbroje, upusc przedmiot z Shiftem, a rozlozy sie.", "Jesli twoj Multi-Pancerz zostanie zniszczony, stracisz wszystkie przedmioty w nim.", "Nie potrzebuje zbroi, to mnie rozczarowuje..."] +name = "Multi-Pancerz" +description = "Wiaz Elytry ze zbroja" +lore1 = "To niesamowita umiejetnosc do podrozowania." +lore2 = "Dynamicznie lacz i zmieniaj Zbroje/Elytry w locie!" +lore3 = "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku." +lore4 = "Aby rozdzielic zbroje, upusc przedmiot z Shiftem, a rozlozy sie." +lore5 = "Jesli twoj Multi-Pancerz zostanie zniszczony, stracisz wszystkie przedmioty w nim." +lore6 = "Nie potrzebuje zbroi, to mnie rozczarowuje..." +lore = ["To niesamowita umiejetnosc do podrozowania.", "Dynamicznie lacz i zmieniaj Zbroje/Elytry w locie!", "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku.", "Aby rozdzielic zbroje, upusc przedmiot z Shiftem, a rozlozy sie.", "Jesli twoj Multi-Pancerz zostanie zniszczony, stracisz wszystkie przedmioty w nim.", "Nie potrzebuje zbroi, to mnie rozczarowuje..."] # crafting [crafting] [crafting.deconstruction] - name = "Dekonstrukcja" - description = "Rozloz bloki i przedmioty na odzyskiwalne elementy bazowe!" - lore1 = "Upusc dowolny przedmiot na ziemie." - lore2 = "Nastepnie kucnij i kliknij prawym przyciskiem nozycami" - lore = ["Upusc dowolny przedmiot na ziemie.", "Nastepnie kucnij i kliknij prawym przyciskiem nozycami"] +name = "Dekonstrukcja" +description = "Rozloz bloki i przedmioty na odzyskiwalne elementy bazowe!" +lore1 = "Upusc dowolny przedmiot na ziemie." +lore2 = "Nastepnie kucnij i kliknij prawym przyciskiem nozycami" +lore = ["Upusc dowolny przedmiot na ziemie.", "Nastepnie kucnij i kliknij prawym przyciskiem nozycami"] [crafting.xp] - name = "PD z Rzemiosla" - description = "Zdobywaj pasywne PD podczas wytwarzania" - lore1 = "Zdobywaj PD podczas wytwarzania" - lore = ["Zdobywaj PD podczas wytwarzania"] +name = "PD z Rzemiosla" +description = "Zdobywaj pasywne PD podczas wytwarzania" +lore1 = "Zdobywaj PD podczas wytwarzania" +lore = ["Zdobywaj PD podczas wytwarzania"] [crafting.reconstruction] - name = "Rekonstrukcja Rud" - description = "Przerabiaj rudy z ich podstawowych skladnikow!" - lore1 = "8 Dropu i 1 Nosnik = 1 Ruda (bezformowe)" - lore2 = "Dropy musza byc przetopione (jesli dotyczy)" - lore3 = "Nie obejmuje: Zlomu, Kwarcu, Szmaragdow itp..." - lore4 = "Nosnik = Otoczenie. np.: Kamien, Netherrack, Lupek" - lore = ["8 Dropu i 1 Nosnik = 1 Ruda (bezformowe)", "Dropy musza byc przetopione (jesli dotyczy)", "Nie obejmuje: Zlomu, Kwarcu, Szmaragdow itp...", "Nosnik = Otoczenie. np.: Kamien, Netherrack, Lupek"] +name = "Rekonstrukcja Rud" +description = "Przerabiaj rudy z ich podstawowych skladnikow!" +lore1 = "8 Dropu i 1 Nosnik = 1 Ruda (bezformowe)" +lore2 = "Dropy musza byc przetopione (jesli dotyczy)" +lore3 = "Nie obejmuje: Zlomu, Kwarcu, Szmaragdow itp..." +lore4 = "Nosnik = Otoczenie. np.: Kamien, Netherrack, Lupek" +lore = ["8 Dropu i 1 Nosnik = 1 Ruda (bezformowe)", "Dropy musza byc przetopione (jesli dotyczy)", "Nie obejmuje: Zlomu, Kwarcu, Szmaragdow itp...", "Nosnik = Otoczenie. np.: Kamien, Netherrack, Lupek"] [crafting.leather] - name = "Wytwarzalna Skora" - description = "Wytworz Skore ze Zgnilego Miesa" - lore1 = "Po prostu wrzuc to (zgnile mieso) na ognisko!" - lore = ["Po prostu wrzuc to (zgnile mieso) na ognisko!"] +name = "Wytwarzalna Skora" +description = "Wytworz Skore ze Zgnilego Miesa" +lore1 = "Po prostu wrzuc to (zgnile mieso) na ognisko!" +lore = ["Po prostu wrzuc to (zgnile mieso) na ognisko!"] [crafting.backpacks] - name = "Plecaki Boutiliera!" - description = "Wprowadza do gry pakiet Mojang!" - lore1 = "Musisz byc w trybie Przetrwania aby tego uzywac" - lore2 = "XLX : Skora, Smycz, Skora" - lore3 = "XSX : Skora, Skrzynka Barrelowa, Skora" - lore4 = "XCX : Skora, Skrzynia, Skora" - lore = ["Musisz byc w trybie Przetrwania aby tego uzywac", "XLX : Skora, Smycz, Skora", "XSX : Skora, Skrzynka Barrelowa, Skora", "XCX : Skora, Skrzynia, Skora"] +name = "Plecaki Boutiliera!" +description = "Wprowadza do gry pakiet Mojang!" +lore1 = "Musisz byc w trybie Przetrwania aby tego uzywac" +lore2 = "XLX : Skora, Smycz, Skora" +lore3 = "XSX : Skora, Skrzynka Barrelowa, Skora" +lore4 = "XCX : Skora, Skrzynia, Skora" +lore = ["Musisz byc w trybie Przetrwania aby tego uzywac", "XLX : Skora, Smycz, Skora", "XSX : Skora, Skrzynka Barrelowa, Skora", "XCX : Skora, Skrzynia, Skora"] [crafting.stations] - name = "Przenosne Stoly!" - description = "Uzyj stolu prosto z dloni!" - lore2 = "WSZELKIE PRZEDMIOTY ZAPOMNIANE W STOLE PO ZAMKNIECIU SA STRACONE NA ZAWSZE!" - lore3 = "Prawidlowe stoly: Kowadlo, Stol Rzemicslniczy, Szlifierka, Kartografia, Kamieniarka, Krosno" - lore = ["WSZELKIE PRZEDMIOTY ZAPOMNIANE W STOLE PO ZAMKNIECIU SA STRACONE NA ZAWSZE!", "Prawidlowe stoly: Kowadlo, Stol Rzemicslniczy, Szlifierka, Kartografia, Kamieniarka, Krosno"] +name = "Przenosne Stoly!" +description = "Uzyj stolu prosto z dloni!" +lore2 = "WSZELKIE PRZEDMIOTY ZAPOMNIANE W STOLE PO ZAMKNIECIU SA STRACONE NA ZAWSZE!" +lore3 = "Prawidlowe stoly: Kowadlo, Stol Rzemicslniczy, Szlifierka, Kartografia, Kamieniarka, Krosno" +lore = ["WSZELKIE PRZEDMIOTY ZAPOMNIANE W STOLE PO ZAMKNIECIU SA STRACONE NA ZAWSZE!", "Prawidlowe stoly: Kowadlo, Stol Rzemicslniczy, Szlifierka, Kartografia, Kamieniarka, Krosno"] [crafting.skulls] - name = "Wytwarzalne Czaszki!" - description = "Uzywajac materialow mozesz tworzyc czaszki mobow!" - lore1 = "Otocz Blok Kosci nastepujacymi aby uzyskac czaszke:" - lore2 = "Zombie: Zgnile Mieso" - lore3 = "Szkielet: Kosc" - lore4 = "Creeper: Proch" - lore5 = "Wither: Cegla Netheru" - lore6 = "Smok: Oddech Smoka" - lore = ["Otocz Blok Kosci nastepujacymi aby uzyskac czaszke:", "Zombie: Zgnile Mieso", "Szkielet: Kosc", "Creeper: Proch", "Wither: Cegla Netheru", "Smok: Oddech Smoka"] +name = "Wytwarzalne Czaszki!" +description = "Uzywajac materialow mozesz tworzyc czaszki mobow!" +lore1 = "Otocz Blok Kosci nastepujacymi aby uzyskac czaszke:" +lore2 = "Zombie: Zgnile Mieso" +lore3 = "Szkielet: Kosc" +lore4 = "Creeper: Proch" +lore5 = "Wither: Cegla Netheru" +lore6 = "Smok: Oddech Smoka" +lore = ["Otocz Blok Kosci nastepujacymi aby uzyskac czaszke:", "Zombie: Zgnile Mieso", "Szkielet: Kosc", "Creeper: Proch", "Wither: Cegla Netheru", "Smok: Oddech Smoka"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Czas w Butelce" - description = "Nos butelke temporalna, ktora gromadzi czas i wydawaj go na przyspieszanie blokow czasowych, upraw i dorastajaych istot, takich jak mlode zwierzeta. Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka." - lore1 = "Zgromadzone sekundy ladowane co tick" - lore2 = "Przyspieszenie czasu na zgromadzona sekunde" - lore3 = "Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka" - lore = ["Zgromadzone sekundy ladowane co tick", "Przyspieszenie czasu na zgromadzona sekunde", "Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka"] +name = "Czas w Butelce" +description = "Nos butelke temporalna, ktora gromadzi czas i wydawaj go na przyspieszanie blokow czasowych, upraw i dorastajaych istot, takich jak mlode zwierzeta. Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka." +lore1 = "Zgromadzone sekundy ladowane co tick" +lore2 = "Przyspieszenie czasu na zgromadzona sekunde" +lore3 = "Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka" +lore = ["Zgromadzone sekundy ladowane co tick", "Przyspieszenie czasu na zgromadzona sekunde", "Przepis (Bezformowy): Mikstura Szybkosci + Zegar + Szklana Butelka"] [chronos.aberrant_touch] - name = "Aberracyjny Dotyk" - description = "Ataki w zwarciu nakladaja kumulujace spowolnienie kosztem glodu, z ograniczeniami PvP, i unieruchamiaja cele przy 5 kumulacjach." - lore1 = "Ataki w zwarciu nakladaja kumulujace spowolnienie" - lore2 = "Limit czasu trwania spowolnienia PvE" - lore3 = "Limit poziomu spowolnienia PvP" - lore = ["Ataki w zwarciu nakladaja kumulujace spowolnienie", "Limit czasu trwania spowolnienia PvE", "Limit poziomu spowolnienia PvP"] +name = "Aberracyjny Dotyk" +description = "Ataki w zwarciu nakladaja kumulujace spowolnienie kosztem glodu, z ograniczeniami PvP, i unieruchamiaja cele przy 5 kumulacjach." +lore1 = "Ataki w zwarciu nakladaja kumulujace spowolnienie" +lore2 = "Limit czasu trwania spowolnienia PvE" +lore3 = "Limit poziomu spowolnienia PvP" +lore = ["Ataki w zwarciu nakladaja kumulujace spowolnienie", "Limit czasu trwania spowolnienia PvE", "Limit poziomu spowolnienia PvP"] [chronos.instant_recall] - name = "Natychmiastowe Przywolanie" - description = "Kliknij lewym lub prawym przyciskiem z zegarem w rece aby cofnac sie do niedawnego stanu ze przywroconym zdrowiem i glodem." - lore1 = "Czas cofniecia" - lore2 = "Czas odnowienia" - lore3 = "Brak cofania ekwipunku" - lore = ["Czas cofniecia", "Czas odnowienia", "Brak cofania ekwipunku"] +name = "Natychmiastowe Przywolanie" +description = "Kliknij lewym lub prawym przyciskiem z zegarem w rece aby cofnac sie do niedawnego stanu ze przywroconym zdrowiem i glodem." +lore1 = "Czas cofniecia" +lore2 = "Czas odnowienia" +lore3 = "Brak cofania ekwipunku" +lore = ["Czas cofniecia", "Czas odnowienia", "Brak cofania ekwipunku"] [chronos.time_bomb] - name = "Bomba Czasowa" - description = "Rzuc wytworzona bombe chrono, ktora tworzy pole temporalne, spowalnia istoty i zamraza pociski." - lore1 = "Zasieg pola temporalnego" - lore2 = "Czas trwania pola temporalnego" - lore3 = "Czas odnowienia bomby" - lore4 = "Przepis (Bezformowy): Zegar + Sniezka + Diament + Piasek" - lore = ["Zasieg pola temporalnego", "Czas trwania pola temporalnego", "Czas odnowienia bomby", "Przepis (Bezformowy): Zegar + Sniezka + Diament + Piasek"] +name = "Bomba Czasowa" +description = "Rzuc wytworzona bombe chrono, ktora tworzy pole temporalne, spowalnia istoty i zamraza pociski." +lore1 = "Zasieg pola temporalnego" +lore2 = "Czas trwania pola temporalnego" +lore3 = "Czas odnowienia bomby" +lore4 = "Przepis (Bezformowy): Zegar + Sniezka + Diament + Piasek" +lore = ["Zasieg pola temporalnego", "Czas trwania pola temporalnego", "Czas odnowienia bomby", "Przepis (Bezformowy): Zegar + Sniezka + Diament + Piasek"] # discovery [discovery] [discovery.armor] - name = "Pancerz Swiata" - description = "Pasywny pancerz zalezny od twardosci poblizskich blokow." - lore1 = "Pasywny Pancerz" - lore2 = "Na podstawie twardosci poblizskich blokow" - lore3 = "Sila Pancerza:" - lore = ["Pasywny Pancerz", "Na podstawie twardosci poblizskich blokow", "Sila Pancerza:"] +name = "Pancerz Swiata" +description = "Pasywny pancerz zalezny od twardosci poblizskich blokow." +lore1 = "Pasywny Pancerz" +lore2 = "Na podstawie twardosci poblizskich blokow" +lore3 = "Sila Pancerza:" +lore = ["Pasywny Pancerz", "Na podstawie twardosci poblizskich blokow", "Sila Pancerza:"] [discovery.unity] - name = "Eksperymentalna Jednosc" - description = "Zbieranie Kul Doswiadczenia dodaje PD do losowych umiejetnosci." - lore1 = "PD " - lore2 = "Za Kule" - lore = ["PD ", "Za Kule"] +name = "Eksperymentalna Jednosc" +description = "Zbieranie Kul Doswiadczenia dodaje PD do losowych umiejetnosci." +lore1 = "PD " +lore2 = "Za Kule" +lore = ["PD ", "Za Kule"] [discovery.resist] - name = "Eksperymentalna Odpornosc" - description = "Zuzyj doswiadczenie aby zlagodzic obrazenia, ale tylko gdy trafienie zrzuciloby cie ponizej 5 serc lub by cie zabilo." - lore0 = "Aktywuje sie tylko przy krytycznym zdrowiu (<= 5 serc) raz na 15 sekund" - lore1 = " Zmniejszone Obrazenia" - lore2 = "wyczerpane doswiadczenie" - lore = ["Aktywuje sie tylko przy krytycznym zdrowiu (<= 5 serc) raz na 15 sekund", " Zmniejszone Obrazenia", "wyczerpane doswiadczenie"] +name = "Eksperymentalna Odpornosc" +description = "Zuzyj doswiadczenie aby zlagodzic obrazenia, ale tylko gdy trafienie zrzuciloby cie ponizej 5 serc lub by cie zabilo." +lore0 = "Aktywuje sie tylko przy krytycznym zdrowiu (<= 5 serc) raz na 15 sekund" +lore1 = " Zmniejszone Obrazenia" +lore2 = "wyczerpane doswiadczenie" +lore = ["Aktywuje sie tylko przy krytycznym zdrowiu (<= 5 serc) raz na 15 sekund", " Zmniejszone Obrazenia", "wyczerpane doswiadczenie"] [discovery.villager] - name = "Atrakcja Osadnikow" - description = "Pozwala na uzyskanie lepszych transakcji z mieszkancami wioski!" - lore1 = "Zuzywa PD za kazda interakcje z osadnikami" - lore2 = "Szansa na zuzycie PD i ulepszenie handlu za interakcje" - lore3 = "wymagane zuzycie PD za interakcje" - lore = ["Zuzywa PD za kazda interakcje z osadnikami", "Szansa na zuzycie PD i ulepszenie handlu za interakcje", "wymagane zuzycie PD za interakcje"] +name = "Atrakcja Osadnikow" +description = "Pozwala na uzyskanie lepszych transakcji z mieszkancami wioski!" +lore1 = "Zuzywa PD za kazda interakcje z osadnikami" +lore2 = "Szansa na zuzycie PD i ulepszenie handlu za interakcje" +lore3 = "wymagane zuzycie PD za interakcje" +lore = ["Zuzywa PD za kazda interakcje z osadnikami", "Szansa na zuzycie PD i ulepszenie handlu za interakcje", "wymagane zuzycie PD za interakcje"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Zwrot Lazurytu" - description = "Kosztem 1 dodatkowego poziomu PD, masz szanse na darmowy lazuryt w zamian" - lore1 = "Za kazdy poziom zwieksza koszt zaklecia o 1, ale moze zwrocic do 3 Lazurytu" - lore = ["Za kazdy poziom zwieksza koszt zaklecia o 1, ale moze zwrocic do 3 Lazurytu"] +name = "Zwrot Lazurytu" +description = "Kosztem 1 dodatkowego poziomu PD, masz szanse na darmowy lazuryt w zamian" +lore1 = "Za kazdy poziom zwieksza koszt zaklecia o 1, ale moze zwrocic do 3 Lazurytu" +lore = ["Za kazdy poziom zwieksza koszt zaklecia o 1, ale moze zwrocic do 3 Lazurytu"] [enchanting.quick_enchant] - name = "Szybkie Zaklecie Kliknieciem" - description = "Zaklinaj przedmioty klikajac ksiazkami zaklec bezposrednio na nich." - lore1 = "Maks. Polaczone Poziomy" - lore2 = "Nie mozna zaklinac przedmiotu z wiecej niz " - lore3 = "mocy" - lore = ["Maks. Polaczone Poziomy", "Nie mozna zaklinac przedmiotu z wiecej niz ", "mocy"] +name = "Szybkie Zaklecie Kliknieciem" +description = "Zaklinaj przedmioty klikajac ksiazkami zaklec bezposrednio na nich." +lore1 = "Maks. Polaczone Poziomy" +lore2 = "Nie mozna zaklinac przedmiotu z wiecej niz " +lore3 = "mocy" +lore = ["Maks. Polaczone Poziomy", "Nie mozna zaklinac przedmiotu z wiecej niz ", "mocy"] [enchanting.return] - name = "Zwrot PD" - description = "PD z zaklec jest ci zwracane gdy zaklinasz przedmiot." - lore1 = "Wydane doswiadczenie ma szanse zostac zwrocone gdy zaklinasz przedmiot" - lore2 = "Doswiadczenie za Zaklecie" - lore = ["Wydane doswiadczenie ma szanse zostac zwrocone gdy zaklinasz przedmiot", "Doswiadczenie za Zaklecie"] +name = "Zwrot PD" +description = "PD z zaklec jest ci zwracane gdy zaklinasz przedmiot." +lore1 = "Wydane doswiadczenie ma szanse zostac zwrocone gdy zaklinasz przedmiot" +lore2 = "Doswiadczenie za Zaklecie" +lore = ["Wydane doswiadczenie ma szanse zostac zwrocone gdy zaklinasz przedmiot", "Doswiadczenie za Zaklecie"] # excavation [excavation] [excavation.haste] - name = "Pospieszny Kopacz" - description = "Przyspiesza kopanie dzieki POSPIECOWI!" - lore1 = "Zyskaj pospiech podczas kopania" - lore2 = "x Poziomow pospiechu gdy zaczniesz niszczyc DOWOLNY blok." - lore = ["Zyskaj pospiech podczas kopania", "x Poziomow pospiechu gdy zaczniesz niszczyc DOWOLNY blok."] +name = "Pospieszny Kopacz" +description = "Przyspiesza kopanie dzieki POSPIECOWI!" +lore1 = "Zyskaj pospiech podczas kopania" +lore2 = "x Poziomow pospiechu gdy zaczniesz niszczyc DOWOLNY blok." +lore = ["Zyskaj pospiech podczas kopania", "x Poziomow pospiechu gdy zaczniesz niszczyc DOWOLNY blok."] [excavation.spelunker] - name = "Super-Widzacy Grotolaz!" - description = "Zobacz rudy na wlasne oczy, ale przez ziemie!" - lore1 = "Ruda w drugiej rece, Swiecace Jagody w glownej rece i Kucnij!" - lore2 = "Zasieg Blokow: " - lore3 = "Zuzywa Swiecace Jagody przy uzyciu" - lore = ["Ruda w drugiej rece, Swiecace Jagody w glownej rece i Kucnij!", "Zasieg Blokow: ", "Zuzywa Swiecace Jagody przy uzyciu"] +name = "Super-Widzacy Grotolaz!" +description = "Zobacz rudy na wlasne oczy, ale przez ziemie!" +lore1 = "Ruda w drugiej rece, Swiecace Jagody w glownej rece i Kucnij!" +lore2 = "Zasieg Blokow: " +lore3 = "Zuzywa Swiecace Jagody przy uzyciu" +lore = ["Ruda w drugiej rece, Swiecace Jagody w glownej rece i Kucnij!", "Zasieg Blokow: ", "Zuzywa Swiecace Jagody przy uzyciu"] [excavation.drop_to_inventory] - name = "Lopata Zrzut-Do-Ekwipunku" +name = "Lopata Zrzut-Do-Ekwipunku" [excavation.omni_tool] - name = "OMNI - N.A.R.Z." - description = "Przesadnie zaprojektowany bogaty Multitool Tackle'a" - lore1 = "Prawdopodobnie najpotezniejszy z wielu, pozwala ci" - lore2 = "dynamicznie laczyc i zmieniac narzedzia w locie, wedlug potrzeb." - lore3 = "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku." - lore4 = "Aby rozdzielic narzedzia, upusc przedmiot z Shiftem, a zostanie zdemontowany." - lore5 = "Nie mozesz zniszczyc narzedzi w tym multitoolu ale nie mozesz uzywac zepsutych narzedzi" - lore6 = "laczna ilosc przedmiotow do polaczenia." - lore7 = "Mozesz uzyc pieciu albo szesciu narzedzi, albo tylko jednego!" - lore = ["Prawdopodobnie najpotezniejszy z wielu, pozwala ci", "dynamicznie laczyc i zmieniac narzedzia w locie, wedlug potrzeb.", "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku.", "Aby rozdzielic narzedzia, upusc przedmiot z Shiftem, a zostanie zdemontowany.", "Nie mozesz zniszczyc narzedzi w tym multitoolu ale nie mozesz uzywac zepsutych narzedzi", "laczna ilosc przedmiotow do polaczenia.", "Mozesz uzyc pieciu albo szesciu narzedzi, albo tylko jednego!"] +name = "OMNI - N.A.R.Z." +description = "Przesadnie zaprojektowany bogaty Multitool Tackle'a" +lore1 = "Prawdopodobnie najpotezniejszy z wielu, pozwala ci" +lore2 = "dynamicznie laczyc i zmieniac narzedzia w locie, wedlug potrzeb." +lore3 = "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku." +lore4 = "Aby rozdzielic narzedzia, upusc przedmiot z Shiftem, a zostanie zdemontowany." +lore5 = "Nie mozesz zniszczyc narzedzi w tym multitoolu ale nie mozesz uzywac zepsutych narzedzi" +lore6 = "laczna ilosc przedmiotow do polaczenia." +lore7 = "Mozesz uzyc pieciu albo szesciu narzedzi, albo tylko jednego!" +lore = ["Prawdopodobnie najpotezniejszy z wielu, pozwala ci", "dynamicznie laczyc i zmieniac narzedzia w locie, wedlug potrzeb.", "Aby polaczyc, kliknij z Shiftem na przedmiot nad innym w ekwipunku.", "Aby rozdzielic narzedzia, upusc przedmiot z Shiftem, a zostanie zdemontowany.", "Nie mozesz zniszczyc narzedzi w tym multitoolu ale nie mozesz uzywac zepsutych narzedzi", "laczna ilosc przedmiotow do polaczenia.", "Mozesz uzyc pieciu albo szesciu narzedzi, albo tylko jednego!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Aura Wzrostu" - description = "Rozwijaj nature wokol siebie w aurze" - lore1 = "Zasieg Blokow" - lore2 = "Sila Aury Wzrostu" - lore3 = "Koszt Jedzenia" - lore = ["Zasieg Blokow", "Sila Aury Wzrostu", "Koszt Jedzenia"] +name = "Aura Wzrostu" +description = "Rozwijaj nature wokol siebie w aurze" +lore1 = "Zasieg Blokow" +lore2 = "Sila Aury Wzrostu" +lore3 = "Koszt Jedzenia" +lore = ["Zasieg Blokow", "Sila Aury Wzrostu", "Koszt Jedzenia"] [herbalism.hippo] - name = "Hipopotam Zielarza" - description = "Spozywanie jedzenia daje wiecej nasycenia" - lore1 = "Jedzenie) dodatkowe punkty nasycenia przy spozyciu" - lore = ["Jedzenie) dodatkowe punkty nasycenia przy spozyciu"] +name = "Hipopotam Zielarza" +description = "Spozywanie jedzenia daje wiecej nasycenia" +lore1 = "Jedzenie) dodatkowe punkty nasycenia przy spozyciu" +lore = ["Jedzenie) dodatkowe punkty nasycenia przy spozyciu"] [herbalism.myconid] - name = "Mykonid Zielarza" - description = "Daje mozliwosc wytwarzania Grzybni" - lore1 = "Dowolna Ziemia + Brazowy i Czerwony Grzyb wytworzya Grzybnie." - lore = ["Dowolna Ziemia + Brazowy i Czerwony Grzyb wytworzya Grzybnie."] +name = "Mykonid Zielarza" +description = "Daje mozliwosc wytwarzania Grzybni" +lore1 = "Dowolna Ziemia + Brazowy i Czerwony Grzyb wytworzya Grzybnie." +lore = ["Dowolna Ziemia + Brazowy i Czerwony Grzyb wytworzya Grzybnie."] [herbalism.terralid] - name = "Terralid Zielarza" - description = "Daje mozliwosc wytwarzania Blokow Trawy" - lore1 = "Trzy Nasiona nad 3 Ziemia wytworzya 3 Bloki Trawy." - lore = ["Trzy Nasiona nad 3 Ziemia wytworzya 3 Bloki Trawy."] +name = "Terralid Zielarza" +description = "Daje mozliwosc wytwarzania Blokow Trawy" +lore1 = "Trzy Nasiona nad 3 Ziemia wytworzya 3 Bloki Trawy." +lore = ["Trzy Nasiona nad 3 Ziemia wytworzya 3 Bloki Trawy."] [herbalism.cobweb] - name = "Tworca Pajeczyn" - description = "Daje mozliwosc wytwarzania Pajeczyn na Stole Rzemicslniczym" - lore1 = "Dziewiec Nitek wytworzy Pajeczyne." - lore = ["Dziewiec Nitek wytworzy Pajeczyne."] +name = "Tworca Pajeczyn" +description = "Daje mozliwosc wytwarzania Pajeczyn na Stole Rzemicslniczym" +lore1 = "Dziewiec Nitek wytworzy Pajeczyne." +lore = ["Dziewiec Nitek wytworzy Pajeczyne."] [herbalism.mushroom_blocks] - name = "Grzybiarz" - description = "Daje mozliwosc wytwarzania Blokow Grzybowych na Stole Rzemicslniczym" - lore1 = "Cztery Grzyby zrobia blok, albo blok zrobi lodyge." - lore = ["Cztery Grzyby zrobia blok, albo blok zrobi lodyge."] +name = "Grzybiarz" +description = "Daje mozliwosc wytwarzania Blokow Grzybowych na Stole Rzemicslniczym" +lore1 = "Cztery Grzyby zrobia blok, albo blok zrobi lodyge." +lore = ["Cztery Grzyby zrobia blok, albo blok zrobi lodyge."] [herbalism.drop_to_inventory] - name = "Motyka Zrzut-Do-Ekwipunku" +name = "Motyka Zrzut-Do-Ekwipunku" [herbalism.hungry_shield] - name = "Glodna Tarcza" - description = "Obrazenia odejmuja sie od glodu zanim od zdrowia." - lore1 = "Odparte przez Glod" - lore = ["Odparte przez Glod"] +name = "Glodna Tarcza" +description = "Obrazenia odejmuja sie od glodu zanim od zdrowia." +lore1 = "Odparte przez Glod" +lore = ["Odparte przez Glod"] [herbalism.luck] - name = "Szczescie Zielarza" - description = "Gdy niszczysz Trawe/Kwiaty, masz szanse na losowy przedmiot" - lore0 = "Kwiaty = Jedzenie, a Trawa = Nasiona" - lore1 = "Szansa na zdobycie przedmiotu z niszczenia Kwiatow" - lore2 = "Szansa na zdobycie przedmiotu z niszczenia Trawy" - lore = ["Kwiaty = Jedzenie, a Trawa = Nasiona", "Szansa na zdobycie przedmiotu z niszczenia Kwiatow", "Szansa na zdobycie przedmiotu z niszczenia Trawy"] +name = "Szczescie Zielarza" +description = "Gdy niszczysz Trawe/Kwiaty, masz szanse na losowy przedmiot" +lore0 = "Kwiaty = Jedzenie, a Trawa = Nasiona" +lore1 = "Szansa na zdobycie przedmiotu z niszczenia Kwiatow" +lore2 = "Szansa na zdobycie przedmiotu z niszczenia Trawy" +lore = ["Kwiaty = Jedzenie, a Trawa = Nasiona", "Szansa na zdobycie przedmiotu z niszczenia Kwiatow", "Szansa na zdobycie przedmiotu z niszczenia Trawy"] [herbalism.replant] - name = "Zbior i Przesadzanie" - description = "Kliknij prawym przyciskiem na uprawe z motyka aby zebrac i przesadzic." - lore1 = "Zasieg Przesadzania Blokow" - lore = ["Zasieg Przesadzania Blokow"] +name = "Zbior i Przesadzanie" +description = "Kliknij prawym przyciskiem na uprawe z motyka aby zebrac i przesadzic." +lore1 = "Zasieg Przesadzania Blokow" +lore = ["Zasieg Przesadzania Blokow"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalina" - description = "Zadawaj wiecej obrazen im mniej masz zdrowia (Walka Wreczna)" - lore1 = "Maksymalne Obrazenia" - lore = ["Maksymalne Obrazenia"] +name = "Adrenalina" +description = "Zadawaj wiecej obrazen im mniej masz zdrowia (Walka Wreczna)" +lore1 = "Maksymalne Obrazenia" +lore = ["Maksymalne Obrazenia"] [hunter.penalty] - name = "" - description = "" - lore1 = "Otrzymasz kumulacje Trucizny jesli zabraknie ci glodu" - lore = ["Otrzymasz kumulacje Trucizny jesli zabraknie ci glodu"] +name = "" +description = "" +lore1 = "Otrzymasz kumulacje Trucizny jesli zabraknie ci glodu" +lore = ["Otrzymasz kumulacje Trucizny jesli zabraknie ci glodu"] [hunter.drop_to_inventory] - name = "Przedmioty Zrzut-Do-Ekwipunku" - description = "Gdy cos zabijesz / zniszczysz blok mieczem, drop teleportuje sie do twojego ekwipunku" - lore1 = "Za kazdym razem gdy przedmiot wypada z moba/bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe." - lore = ["Za kazdym razem gdy przedmiot wypada z moba/bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe."] +name = "Przedmioty Zrzut-Do-Ekwipunku" +description = "Gdy cos zabijesz / zniszczysz blok mieczem, drop teleportuje sie do twojego ekwipunku" +lore1 = "Za kazdym razem gdy przedmiot wypada z moba/bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe." +lore = ["Za kazdym razem gdy przedmiot wypada z moba/bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe."] [hunter.invisibility] - name = "Znikajacy Krok" - description = "Gdy zostaniesz trafiony zyskujesz niewidzialnosc, kosztem glodu" - lore1 = "Zyskaj pasywna niewidzialnosc po trafieniu" - lore2 = "x Kumulacje Niewidzialnosci na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Czas trwania niewidzialnosci" - lore = ["Zyskaj pasywna niewidzialnosc po trafieniu", "x Kumulacje Niewidzialnosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Czas trwania niewidzialnosci"] +name = "Znikajacy Krok" +description = "Gdy zostaniesz trafiony zyskujesz niewidzialnosc, kosztem glodu" +lore1 = "Zyskaj pasywna niewidzialnosc po trafieniu" +lore2 = "x Kumulacje Niewidzialnosci na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Czas trwania niewidzialnosci" +lore = ["Zyskaj pasywna niewidzialnosc po trafieniu", "x Kumulacje Niewidzialnosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Czas trwania niewidzialnosci"] [hunter.jump_boost] - name = "Wyskoki Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz wzmocnienie skoku, kosztem glodu" - lore1 = "Zyskaj pasywne wzmocnienie skoku po trafieniu" - lore2 = "x Kumulacje Wzmocnienia Skoku na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji Wzmocnienia Skoku, nie czas trwania." - lore = ["Zyskaj pasywne wzmocnienie skoku po trafieniu", "x Kumulacje Wzmocnienia Skoku na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji Wzmocnienia Skoku, nie czas trwania."] +name = "Wyskoki Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz wzmocnienie skoku, kosztem glodu" +lore1 = "Zyskaj pasywne wzmocnienie skoku po trafieniu" +lore2 = "x Kumulacje Wzmocnienia Skoku na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji Wzmocnienia Skoku, nie czas trwania." +lore = ["Zyskaj pasywne wzmocnienie skoku po trafieniu", "x Kumulacje Wzmocnienia Skoku na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji Wzmocnienia Skoku, nie czas trwania."] [hunter.luck] - name = "Szczescie Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz szczescie, kosztem glodu" - lore1 = "Zyskaj pasywne szczescie po trafieniu" - lore2 = "x Kumulacje Szczescia na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji szczescia, nie czas trwania." - lore = ["Zyskaj pasywne szczescie po trafieniu", "x Kumulacje Szczescia na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji szczescia, nie czas trwania."] +name = "Szczescie Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz szczescie, kosztem glodu" +lore1 = "Zyskaj pasywne szczescie po trafieniu" +lore2 = "x Kumulacje Szczescia na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji szczescia, nie czas trwania." +lore = ["Zyskaj pasywne szczescie po trafieniu", "x Kumulacje Szczescia na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji szczescia, nie czas trwania."] [hunter.regen] - name = "Regeneracja Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz regeneracje, kosztem glodu" - lore1 = "Zyskaj pasywna regeneracje po trafieniu" - lore2 = "x Kumulacje Regeneracji na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji regeneracji, nie czas trwania." - lore = ["Zyskaj pasywna regeneracje po trafieniu", "x Kumulacje Regeneracji na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji regeneracji, nie czas trwania."] +name = "Regeneracja Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz regeneracje, kosztem glodu" +lore1 = "Zyskaj pasywna regeneracje po trafieniu" +lore2 = "x Kumulacje Regeneracji na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji regeneracji, nie czas trwania." +lore = ["Zyskaj pasywna regeneracje po trafieniu", "x Kumulacje Regeneracji na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji regeneracji, nie czas trwania."] [hunter.resistance] - name = "Odpornosc Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz odpornosc, kosztem glodu" - lore1 = "Zyskaj pasywna odpornosc po trafieniu" - lore2 = "x Kumulacje Odpornosci na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji odpornosci, nie czas trwania." - lore = ["Zyskaj pasywna odpornosc po trafieniu", "x Kumulacje Odpornosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji odpornosci, nie czas trwania."] +name = "Odpornosc Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz odpornosc, kosztem glodu" +lore1 = "Zyskaj pasywna odpornosc po trafieniu" +lore2 = "x Kumulacje Odpornosci na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji odpornosci, nie czas trwania." +lore = ["Zyskaj pasywna odpornosc po trafieniu", "x Kumulacje Odpornosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji odpornosci, nie czas trwania."] [hunter.speed] - name = "Szybkosc Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz szybkosc, kosztem glodu" - lore1 = "Zyskaj pasywna szybkosc po trafieniu" - lore2 = "x Kumulacje Szybkosci na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji szybkosci, nie czas trwania." - lore = ["Zyskaj pasywna szybkosc po trafieniu", "x Kumulacje Szybkosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji szybkosci, nie czas trwania."] +name = "Szybkosc Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz szybkosc, kosztem glodu" +lore1 = "Zyskaj pasywna szybkosc po trafieniu" +lore2 = "x Kumulacje Szybkosci na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji szybkosci, nie czas trwania." +lore = ["Zyskaj pasywna szybkosc po trafieniu", "x Kumulacje Szybkosci na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji szybkosci, nie czas trwania."] [hunter.strength] - name = "Sila Lowcy" - description = "Gdy zostaniesz trafiony zyskujesz sile, kosztem glodu" - lore1 = "Zyskaj pasywna sile po trafieniu" - lore2 = "x Kumulacje Sily na 3 sekundy po trafieniu" - lore3 = "x Kumulowany glod" - lore4 = "Czas trwania i mnoznik kumulacji glodu." - lore5 = "Mnoznik kumulacji sily, nie czas trwania." - lore = ["Zyskaj pasywna sile po trafieniu", "x Kumulacje Sily na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji sily, nie czas trwania."] +name = "Sila Lowcy" +description = "Gdy zostaniesz trafiony zyskujesz sile, kosztem glodu" +lore1 = "Zyskaj pasywna sile po trafieniu" +lore2 = "x Kumulacje Sily na 3 sekundy po trafieniu" +lore3 = "x Kumulowany glod" +lore4 = "Czas trwania i mnoznik kumulacji glodu." +lore5 = "Mnoznik kumulacji sily, nie czas trwania." +lore = ["Zyskaj pasywna sile po trafieniu", "x Kumulacje Sily na 3 sekundy po trafieniu", "x Kumulowany glod", "Czas trwania i mnoznik kumulacji glodu.", "Mnoznik kumulacji sily, nie czas trwania."] # nether [nether] [nether.skull_toss] - name = "Rzut Czaszka Withera" - description1 = "Uwolnij swego wewnetrznego Withera uzywajac" - description2 = "czyjejs" - description3 = "glowy." - lore1 = "Sekundy odnowienia miedzy rzutami czaszka." - lore2 = "Uzywajac Czaszki Withera: Rzuc " - lore3 = "Czaszke Withera" - lore4 = "eksplodujaca przy uderzeniu." - lore = ["Sekundy odnowienia miedzy rzutami czaszka.", "Uzywajac Czaszki Withera: Rzuc ", "Czaszke Withera", "eksplodujaca przy uderzeniu."] +name = "Rzut Czaszka Withera" +description1 = "Uwolnij swego wewnetrznego Withera uzywajac" +description2 = "czyjejs" +description3 = "glowy." +lore1 = "Sekundy odnowienia miedzy rzutami czaszka." +lore2 = "Uzywajac Czaszki Withera: Rzuc " +lore3 = "Czaszke Withera" +lore4 = "eksplodujaca przy uderzeniu." +lore = ["Sekundy odnowienia miedzy rzutami czaszka.", "Uzywajac Czaszki Withera: Rzuc ", "Czaszke Withera", "eksplodujaca przy uderzeniu."] [nether.wither_resist] - name = "Odpornosc na Wiecenie" - description = "Odpiera wiecenie dzieki mocy Netheritu." - lore1 = "szansa na zanegowanie wiecenia (za czesc)." - lore2 = "Pasywne: Noszenie Zbroi z Netheritu ma szanse zanegowac " - lore3 = "wiecenie." - lore = ["szansa na zanegowanie wiecenia (za czesc).", "Pasywne: Noszenie Zbroi z Netheritu ma szanse zanegowac ", "wiecenie."] +name = "Odpornosc na Wiecenie" +description = "Odpiera wiecenie dzieki mocy Netheritu." +lore1 = "szansa na zanegowanie wiecenia (za czesc)." +lore2 = "Pasywne: Noszenie Zbroi z Netheritu ma szanse zanegowac " +lore3 = "wiecenie." +lore = ["szansa na zanegowanie wiecenia (za czesc).", "Pasywne: Noszenie Zbroi z Netheritu ma szanse zanegowac ", "wiecenie."] [nether.fire_resist] - name = "Odpornosc na Ogien" - description = "Odpiera ogien utwardzajac twoja skore." - lore1 = "szansa na zanegowanie efektu palenia!" - lore = ["szansa na zanegowanie efektu palenia!"] +name = "Odpornosc na Ogien" +description = "Odpiera ogien utwardzajac twoja skore." +lore1 = "szansa na zanegowanie efektu palenia!" +lore = ["szansa na zanegowanie efektu palenia!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Autotopienie" - description = "Pozwala automatycznie wytapiac wydobywane standardowe rudy" - lore1 = "Rudy ktore mozna przetopic sa przetapiane automatycznie" - lore2 = "% szansy na dodatkowy" - lore = ["Rudy ktore mozna przetopic sa przetapiane automatycznie", "% szansy na dodatkowy"] +name = "Autotopienie" +description = "Pozwala automatycznie wytapiac wydobywane standardowe rudy" +lore1 = "Rudy ktore mozna przetopic sa przetapiane automatycznie" +lore2 = "% szansy na dodatkowy" +lore = ["Rudy ktore mozna przetopic sa przetapiane automatycznie", "% szansy na dodatkowy"] [pickaxe.chisel] - name = "Dluto do Rud" - description = "Kliknij prawym przyciskiem na rudy aby wydlutac z nich wiecej, kosztem duzej wytrzymalosci." - lore1 = "Szansa na Drop" - lore2 = "Zuzycie Narzedzia" - lore = ["Szansa na Drop", "Zuzycie Narzedzia"] +name = "Dluto do Rud" +description = "Kliknij prawym przyciskiem na rudy aby wydlutac z nich wiecej, kosztem duzej wytrzymalosci." +lore1 = "Szansa na Drop" +lore2 = "Zuzycie Narzedzia" +lore = ["Szansa na Drop", "Zuzycie Narzedzia"] [pickaxe.drop_to_inventory] - name = "Kilof Zrzut-Do-Ekwipunku" - description = "Gdy zniszczysz blok, przedmiot teleportuje sie do twojego ekwipunku" - lore1 = "Za kazdym razem gdy przedmiot wypada z bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe." - lore = ["Za kazdym razem gdy przedmiot wypada z bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe."] +name = "Kilof Zrzut-Do-Ekwipunku" +description = "Gdy zniszczysz blok, przedmiot teleportuje sie do twojego ekwipunku" +lore1 = "Za kazdym razem gdy przedmiot wypada z bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe." +lore = ["Za kazdym razem gdy przedmiot wypada z bloku ktory niszczysz, trafia do twojego ekwipunku jesli to mozliwe."] [pickaxe.silk_spawner] - name = "Kilof Jedwabny-Spawner" - description = "Spawnery wypadaja gdy sa niszczone" - lore1 = "Spawnery mozna niszczyc z Jedwabnym Dotykiem." - lore2 = "Spawnery mozna niszczyc podczas kucania." - lore = ["Spawnery mozna niszczyc z Jedwabnym Dotykiem.", "Spawnery mozna niszczyc podczas kucania."] +name = "Kilof Jedwabny-Spawner" +description = "Spawnery wypadaja gdy sa niszczone" +lore1 = "Spawnery mozna niszczyc z Jedwabnym Dotykiem." +lore2 = "Spawnery mozna niszczyc podczas kucania." +lore = ["Spawnery mozna niszczyc z Jedwabnym Dotykiem.", "Spawnery mozna niszczyc podczas kucania."] [pickaxe.vein_miner] - name = "Gornik Zyl" - description = "Pozwala niszczyc bloki w Zyle/Skupisku standardowych rud" - lore1 = "Kucnij i niszcz RUDY" - lore2 = "zasieg wydobycia zylowego" - lore3 = "Ta umiejetnosc NIE grupuje wszystkich dropow razem!" - lore = ["Kucnij i niszcz RUDY", "zasieg wydobycia zylowego", "Ta umiejetnosc NIE grupuje wszystkich dropow razem!"] +name = "Gornik Zyl" +description = "Pozwala niszczyc bloki w Zyle/Skupisku standardowych rud" +lore1 = "Kucnij i niszcz RUDY" +lore2 = "zasieg wydobycia zylowego" +lore3 = "Ta umiejetnosc NIE grupuje wszystkich dropow razem!" +lore = ["Kucnij i niszcz RUDY", "zasieg wydobycia zylowego", "Ta umiejetnosc NIE grupuje wszystkich dropow razem!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Odzyskiwanie Strzal" - description = "Odzyskuj strzaly po zabiciu wroga." - lore1 = "Szansa na odzyskanie strzal po trafieniu/zabiciu" - lore2 = "Szansa: " - lore = ["Szansa na odzyskanie strzal po trafieniu/zabiciu", "Szansa: "] +name = "Odzyskiwanie Strzal" +description = "Odzyskuj strzaly po zabiciu wroga." +lore1 = "Szansa na odzyskanie strzal po trafieniu/zabiciu" +lore2 = "Szansa: " +lore = ["Szansa na odzyskanie strzal po trafieniu/zabiciu", "Szansa: "] [ranged.web_shot] - name = "Pajeczyna Pulapka" - description = "Otocz cel pajeczyna gdy go trafisz!" - lore1 = "8 Pajeczyn wokol Sniezki i rzuc!" - lore2 = "sekundy klatki, mniej wiecej." - lore = ["8 Pajeczyn wokol Sniezki i rzuc!", "sekundy klatki, mniej wiecej."] +name = "Pajeczyna Pulapka" +description = "Otocz cel pajeczyna gdy go trafisz!" +lore1 = "8 Pajeczyn wokol Sniezki i rzuc!" +lore2 = "sekundy klatki, mniej wiecej." +lore = ["8 Pajeczyn wokol Sniezki i rzuc!", "sekundy klatki, mniej wiecej."] [ranged.force_shot] - name = "Silowy Strzal" - description = "Strzelaj pociskami dalej i szybciej!" - advancementname = "Dlugi Strzal" - advancementlore = "Traf strzalem z ponad 30 blokow!" - lore1 = "Predkosc Pocisku" - lore = ["Predkosc Pocisku"] +name = "Silowy Strzal" +description = "Strzelaj pociskami dalej i szybciej!" +advancementname = "Dlugi Strzal" +advancementlore = "Traf strzalem z ponad 30 blokow!" +lore1 = "Predkosc Pocisku" +lore = ["Predkosc Pocisku"] [ranged.lunge_shot] - name = "Strzal z Wyskoku" - description = "Podczas spadania strzaly rzucaja cie w losowym kierunku" - lore1 = "Losowa Predkosc Zrywu" - lore = ["Losowa Predkosc Zrywu"] +name = "Strzal z Wyskoku" +description = "Podczas spadania strzaly rzucaja cie w losowym kierunku" +lore1 = "Losowa Predkosc Zrywu" +lore = ["Losowa Predkosc Zrywu"] [ranged.arrow_piercing] - name = "Przeszywanie Strzalami" - description = "Dodaje przeszywanie do pociskow! Strzelaj przez przeszkody!" - lore1 = "Przeszyte Cele" - lore = ["Przeszyte Cele"] +name = "Przeszywanie Strzalami" +description = "Dodaje przeszywanie do pociskow! Strzelaj przez przeszkody!" +lore1 = "Przeszyte Cele" +lore = ["Przeszyte Cele"] # rift [rift] [rift.remote_access] - name = "Zdalny Dostep" - description = "Siegnij przez pustke i dostac sie do oznaczonego pojemnika." - lore1 = "Perla Endera + Kompas = Relikwiarzowy Switoklin" - lore2 = "Ten przedmiot pozwala na zdalny dostep do pojemnikow" - lore3 = "Po wytworzeniu spjorz na przedmiot aby zobaczyc uzycie" - notcontainer = "To nie jest pojemnik" - lore = ["Perla Endera + Kompas = Relikwiarzowy Switoklin", "Ten przedmiot pozwala na zdalny dostep do pojemnikow", "Po wytworzeniu spjorz na przedmiot aby zobaczyc uzycie"] +name = "Zdalny Dostep" +description = "Siegnij przez pustke i dostac sie do oznaczonego pojemnika." +lore1 = "Perla Endera + Kompas = Relikwiarzowy Switoklin" +lore2 = "Ten przedmiot pozwala na zdalny dostep do pojemnikow" +lore3 = "Po wytworzeniu spjorz na przedmiot aby zobaczyc uzycie" +notcontainer = "To nie jest pojemnik" +lore = ["Perla Endera + Kompas = Relikwiarzowy Switoklin", "Ten przedmiot pozwala na zdalny dostep do pojemnikow", "Po wytworzeniu spjorz na przedmiot aby zobaczyc uzycie"] [rift.blink] - name = "Mrgniecie Szczeliny" - description = "Natychmiastowa teleportacja krotkiego zasiegu, jedno mrgniecie!" - lore1 = "Blokow na mrgniecie (2x w pionie)" - lore2 = "Podczas Sprintowania: Podwojnie wcisnij Skok aby " - lore3 = "Mrgnac" - lore = ["Blokow na mrgniecie (2x w pionie)", "Podczas Sprintowania: Podwojnie wcisnij Skok aby ", "Mrgnac"] +name = "Mrgniecie Szczeliny" +description = "Natychmiastowa teleportacja krotkiego zasiegu, jedno mrgniecie!" +lore1 = "Blokow na mrgniecie (2x w pionie)" +lore2 = "Podczas Sprintowania: Podwojnie wcisnij Skok aby " +lore3 = "Mrgnac" +lore = ["Blokow na mrgniecie (2x w pionie)", "Podczas Sprintowania: Podwojnie wcisnij Skok aby ", "Mrgnac"] [rift.chest] - name = "Latwa Enderchest" - description = "Otworz enderchest klikajac na nia lewym przyciskiem w rece." - lore1 = "Kliknij Enderchest w rece aby otworzyc (Tylko jej nie stawiaj)" - lore = ["Kliknij Enderchest w rece aby otworzyc (Tylko jej nie stawiaj)"] +name = "Latwa Enderchest" +description = "Otworz enderchest klikajac na nia lewym przyciskiem w rece." +lore1 = "Kliknij Enderchest w rece aby otworzyc (Tylko jej nie stawiaj)" +lore = ["Kliknij Enderchest w rece aby otworzyc (Tylko jej nie stawiaj)"] [rift.descent] - name = "Anty-Lewitacja" - description = "Masz dosc utknieccia w powietrzu? Ta umiejetnosc jest dla ciebie!" - lore1 = "Po prostu kucnij aby opadac, a bedziesz spadac wolniej niz normalnie!" - lore2 = "Czas odnowienia:" - lore = ["Po prostu kucnij aby opadac, a bedziesz spadac wolniej niz normalnie!", "Czas odnowienia:"] +name = "Anty-Lewitacja" +description = "Masz dosc utknieccia w powietrzu? Ta umiejetnosc jest dla ciebie!" +lore1 = "Po prostu kucnij aby opadac, a bedziesz spadac wolniej niz normalnie!" +lore2 = "Czas odnowienia:" +lore = ["Po prostu kucnij aby opadac, a bedziesz spadac wolniej niz normalnie!", "Czas odnowienia:"] [rift.gate] - name = "Brama Szczeliny" - description = "Teleportuj sie do oznaczonej lokalizacji." - lore1 = "WYTWARZANIE: Szmaragd + Odlamek Ametystu + Perla Endera" - lore2 = "Przeczytaj przed uzyciem!" - lore3 = "5 sek. opoznienia, " - lore4 = "mozesz umrzec podczas tej animacji" - lore = ["WYTWARZANIE: Szmaragd + Odlamek Ametystu + Perla Endera", "Przeczytaj przed uzyciem!", "5 sek. opoznienia, ", "mozesz umrzec podczas tej animacji"] +name = "Brama Szczeliny" +description = "Teleportuj sie do oznaczonej lokalizacji." +lore1 = "WYTWARZANIE: Szmaragd + Odlamek Ametystu + Perla Endera" +lore2 = "Przeczytaj przed uzyciem!" +lore3 = "5 sek. opoznienia, " +lore4 = "mozesz umrzec podczas tej animacji" +lore = ["WYTWARZANIE: Szmaragd + Odlamek Ametystu + Perla Endera", "Przeczytaj przed uzyciem!", "5 sek. opoznienia, ", "mozesz umrzec podczas tej animacji"] [rift.resist] - name = "Odpornosc Szczeliny" - description = "Zyskaj Odpornosc gdy uzywasz przedmiotow i zdolnosci Endera" - lore1 = "+ Pasywne: Zapewnia odpornosc gdy uzywasz zdolnosci Szczeliny lub Przedmiotow Endera" - lore2 = "NIE wliczajac przenosnej Enderchest, tylko rzeczy ktore mozesz zuzyc" - lore = ["+ Pasywne: Zapewnia odpornosc gdy uzywasz zdolnosci Szczeliny lub Przedmiotow Endera", "NIE wliczajac przenosnej Enderchest, tylko rzeczy ktore mozesz zuzyc"] +name = "Odpornosc Szczeliny" +description = "Zyskaj Odpornosc gdy uzywasz przedmiotow i zdolnosci Endera" +lore1 = "+ Pasywne: Zapewnia odpornosc gdy uzywasz zdolnosci Szczeliny lub Przedmiotow Endera" +lore2 = "NIE wliczajac przenosnej Enderchest, tylko rzeczy ktore mozesz zuzyc" +lore = ["+ Pasywne: Zapewnia odpornosc gdy uzywasz zdolnosci Szczeliny lub Przedmiotow Endera", "NIE wliczajac przenosnej Enderchest, tylko rzeczy ktore mozesz zuzyc"] [rift.visage] - name = "Oblicze Szczeliny" - description = "Zapobiega agresji Endermanow jesli masz Perly Endera w ekwipunku." - lore1 = "Endermany nie stana sie agresywne jesli masz Perly Endera w ekwipunku." - lore = ["Endermany nie stana sie agresywne jesli masz Perly Endera w ekwipunku."] +name = "Oblicze Szczeliny" +description = "Zapobiega agresji Endermanow jesli masz Perly Endera w ekwipunku." +lore1 = "Endermany nie stana sie agresywne jesli masz Perly Endera w ekwipunku." +lore = ["Endermany nie stana sie agresywne jesli masz Perly Endera w ekwipunku."] # seaborn [seaborn] [seaborn.oxygen] - name = "Organiczny Zbiornik Tlenu" - description = "Przechowuj wiecej tlenu w swoich malych plucach!" - lore1 = "Zwiekszenie Pojemnosci Tlenowej" - lore = ["Zwiekszenie Pojemnosci Tlenowej"] +name = "Organiczny Zbiornik Tlenu" +description = "Przechowuj wiecej tlenu w swoich malych plucach!" +lore1 = "Zwiekszenie Pojemnosci Tlenowej" +lore = ["Zwiekszenie Pojemnosci Tlenowej"] [seaborn.fishers_fantasy] - name = "Fantazja Rybaka" - description = "Zdobywaj wiecej PD z lowienia i lapaj wiecej ryb!" - lore1 = "Za kazdy poziom jest szansa na wiecej PD i wiecej Ryb!" - lore = ["Za kazdy poziom jest szansa na wiecej PD i wiecej Ryb!"] +name = "Fantazja Rybaka" +description = "Zdobywaj wiecej PD z lowienia i lapaj wiecej ryb!" +lore1 = "Za kazdy poziom jest szansa na wiecej PD i wiecej Ryb!" +lore = ["Za kazdy poziom jest szansa na wiecej PD i wiecej Ryb!"] [seaborn.haste] - name = "Zolwi Gornik" - description = "Podczas kopania pod woda zyskujesz pospiech!" - lore1 = "Pospiech 3 jest nakladany pod woda podczas kopania (kumuluje sie z AquaAffinity) po zakonczeniu efektu oddychania pod woda!" - lore = ["Pospiech 3 jest nakladany pod woda podczas kopania (kumuluje sie z AquaAffinity) po zakonczeniu efektu oddychania pod woda!"] +name = "Zolwi Gornik" +description = "Podczas kopania pod woda zyskujesz pospiech!" +lore1 = "Pospiech 3 jest nakladany pod woda podczas kopania (kumuluje sie z AquaAffinity) po zakonczeniu efektu oddychania pod woda!" +lore = ["Pospiech 3 jest nakladany pod woda podczas kopania (kumuluje sie z AquaAffinity) po zakonczeniu efektu oddychania pod woda!"] [seaborn.night_vision] - name = "Wizja Zolwia" - description = "Pod woda zyskujesz Noktowizje" - lore1 = "Po prostu zyskaj Noktowizje pod woda po zakonczeniu efektu oddychania pod woda!" - lore = ["Po prostu zyskaj Noktowizje pod woda po zakonczeniu efektu oddychania pod woda!"] +name = "Wizja Zolwia" +description = "Pod woda zyskujesz Noktowizje" +lore1 = "Po prostu zyskaj Noktowizje pod woda po zakonczeniu efektu oddychania pod woda!" +lore = ["Po prostu zyskaj Noktowizje pod woda po zakonczeniu efektu oddychania pod woda!"] [seaborn.dolphin_grace] - name = "Laska Delfina" - description = "Plywaj jak delfin, bez delfinow" - lore1 = "+ Pasywne: zyskaj " - lore2 = "x predkosc (laska delfina)" - lore3 = "precyzyjna niemiecka inzynier- czekaj, to nie tak... Nie kompatybilne z Glebinowym Krokiem" - lore = ["+ Pasywne: zyskaj ", "x predkosc (laska delfina)", "precyzyjna niemiecka inzynier- czekaj, to nie tak... Nie kompatybilne z Glebinowym Krokiem"] +name = "Laska Delfina" +description = "Plywaj jak delfin, bez delfinow" +lore1 = "+ Pasywne: zyskaj " +lore2 = "x predkosc (laska delfina)" +lore3 = "precyzyjna niemiecka inzynier- czekaj, to nie tak... Nie kompatybilne z Glebinowym Krokiem" +lore = ["+ Pasywne: zyskaj ", "x predkosc (laska delfina)", "precyzyjna niemiecka inzynier- czekaj, to nie tak... Nie kompatybilne z Glebinowym Krokiem"] # stealth [stealth] [stealth.ghost_armor] - name = "Zbroja Ducha" - description = "Wolno narastajacy pancerz gdy nie otrzymujesz obrazen, wytrzymuje 1 trafienie" - lore1 = "Maksymalny Pancerz" - lore2 = "Szybkosc" - lore = ["Maksymalny Pancerz", "Szybkosc"] +name = "Zbroja Ducha" +description = "Wolno narastajacy pancerz gdy nie otrzymujesz obrazen, wytrzymuje 1 trafienie" +lore1 = "Maksymalny Pancerz" +lore2 = "Szybkosc" +lore = ["Maksymalny Pancerz", "Szybkosc"] [stealth.night_vision] - name = "Wizja Skradania" - description = "Zyskaj noktowizje podczas kucania" - lore1 = "Zyskaj porcje " - lore2 = "noktowizji" - lore3 = "podczas kucania" - lore = ["Zyskaj porcje ", "noktowizji", "podczas kucania"] +name = "Wizja Skradania" +description = "Zyskaj noktowizje podczas kucania" +lore1 = "Zyskaj porcje " +lore2 = "noktowizji" +lore3 = "podczas kucania" +lore = ["Zyskaj porcje ", "noktowizji", "podczas kucania"] [stealth.snatch] - name = "Chwytanie Przedmiotow" - description = "Chwytaj Upuszczone Przedmioty natychmiast podczas kucania!" - lore1 = "Zasieg Chwytania" - lore = ["Zasieg Chwytania"] +name = "Chwytanie Przedmiotow" +description = "Chwytaj Upuszczone Przedmioty natychmiast podczas kucania!" +lore1 = "Zasieg Chwytania" +lore = ["Zasieg Chwytania"] [stealth.speed] - name = "Szybkosc Skradania" - description = "Zyskaj szybkosc podczas kucania" - lore1 = "Szybkosc Kucania" - lore = ["Szybkosc Kucania"] +name = "Szybkosc Skradania" +description = "Zyskaj szybkosc podczas kucania" +lore1 = "Szybkosc Kucania" +lore = ["Szybkosc Kucania"] [stealth.ender_veil] - name = "Zaslona Endera" - description = "Koniec z dyniami aby zapobiec atakom Endermanow" - lore1 = "Zapobiega atakom Endermanow podczas kucania" - lore2 = "Zapobiega wszystkim atakom Endermanow" - lore = ["Zapobiega atakom Endermanow podczas kucania", "Zapobiega wszystkim atakom Endermanow"] +name = "Zaslona Endera" +description = "Koniec z dyniami aby zapobiec atakom Endermanow" +lore1 = "Zapobiega atakom Endermanow podczas kucania" +lore2 = "Zapobiega wszystkim atakom Endermanow" +lore = ["Zapobiega atakom Endermanow podczas kucania", "Zapobiega wszystkim atakom Endermanow"] # sword [sword] [sword.machete] - name = "Maczeta" - description = "Przedzieraj sie przez roslinnosc z latwoscia!" - lore1 = "Zasieg Ciecia" - lore2 = "Czas Odnowienia Ciecia" - lore3 = "Zuzycie Narzedzia" - lore = ["Zasieg Ciecia", "Czas Odnowienia Ciecia", "Zuzycie Narzedzia"] +name = "Maczeta" +description = "Przedzieraj sie przez roslinnosc z latwoscia!" +lore1 = "Zasieg Ciecia" +lore2 = "Czas Odnowienia Ciecia" +lore3 = "Zuzycie Narzedzia" +lore = ["Zasieg Ciecia", "Czas Odnowienia Ciecia", "Zuzycie Narzedzia"] [sword.bloody_blade] - name = "Krwawe Ostrze" - description = "Uderzenia mieczem powoduja Krwawienie!" - lore1 = "Trafienie zywej istoty mieczem powoduje Krwawienie" - lore2 = "Czas Trwania Krwawienia" - lore3 = "Czas Odnowienia Krwawienia" - lore = ["Trafienie zywej istoty mieczem powoduje Krwawienie", "Czas Trwania Krwawienia", "Czas Odnowienia Krwawienia"] +name = "Krwawe Ostrze" +description = "Uderzenia mieczem powoduja Krwawienie!" +lore1 = "Trafienie zywej istoty mieczem powoduje Krwawienie" +lore2 = "Czas Trwania Krwawienia" +lore3 = "Czas Odnowienia Krwawienia" +lore = ["Trafienie zywej istoty mieczem powoduje Krwawienie", "Czas Trwania Krwawienia", "Czas Odnowienia Krwawienia"] [sword.poisoned_blade] - name = "Zatrute Ostrze" - description = "Uderzenia mieczem powoduja Truizne!" - lore1 = "Trafienie zywej istoty mieczem powoduje Truizne" - lore2 = "Czas Trwania Trucizny" - lore3 = "Czas Odnowienia Trucizny" - lore = ["Trafienie zywej istoty mieczem powoduje Truizne", "Czas Trwania Trucizny", "Czas Odnowienia Trucizny"] +name = "Zatrute Ostrze" +description = "Uderzenia mieczem powoduja Truizne!" +lore1 = "Trafienie zywej istoty mieczem powoduje Truizne" +lore2 = "Czas Trwania Trucizny" +lore3 = "Czas Odnowienia Trucizny" +lore = ["Trafienie zywej istoty mieczem powoduje Truizne", "Czas Trwania Trucizny", "Czas Odnowienia Trucizny"] # taming [taming] [taming.damage] - name = "Obrazenia Oswojonych" - description = "Zwieksz obrazenia zadawane przez oswojone zwierzeta." - lore1 = "Zwiekszone Obrazenia" - lore = ["Zwiekszone Obrazenia"] +name = "Obrazenia Oswojonych" +description = "Zwieksz obrazenia zadawane przez oswojone zwierzeta." +lore1 = "Zwiekszone Obrazenia" +lore = ["Zwiekszone Obrazenia"] [taming.health] - name = "Zdrowie Oswojonych" - description = "Zwieksz zdrowie swoich oswojonych zwierzat." - lore1 = "Zwiekszone Zdrowie" - lore = ["Zwiekszone Zdrowie"] +name = "Zdrowie Oswojonych" +description = "Zwieksz zdrowie swoich oswojonych zwierzat." +lore1 = "Zwiekszone Zdrowie" +lore = ["Zwiekszone Zdrowie"] [taming.regeneration] - name = "Regeneracja Oswojonych" - description = "Zwieksz regeneracje swoich oswojonych zwierzat." - lore1 = "PZ/s" - lore = ["PZ/s"] +name = "Regeneracja Oswojonych" +description = "Zwieksz regeneracje swoich oswojonych zwierzat." +lore1 = "PZ/s" +lore = ["PZ/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Ciernie" - description = "Odbij obrazenia z powrotem na atakujacego!" - lore1 = "Obrazenia odwetowe po trafieniu" - lore = ["Obrazenia odwetowe po trafieniu"] +name = "Ciernie" +description = "Odbij obrazenia z powrotem na atakujacego!" +lore1 = "Obrazenia odwetowe po trafieniu" +lore = ["Obrazenia odwetowe po trafieniu"] [tragoul.globe] - name = "Kula Bolu" - description = "Podziel zadawane obrazenia na podstawie liczby wrogow wokol ciebie!" - lore1 = "Im wiecej wrogow wokol ciebie, tym mniej obrazen zadasz kazdemu" - lore2 = "Zasieg: " - lore3 = "Dodane Obrazenia dla wszystkich Istot: " - lore = ["Im wiecej wrogow wokol ciebie, tym mniej obrazen zadasz kazdemu", "Zasieg: ", "Dodane Obrazenia dla wszystkich Istot: "] +name = "Kula Bolu" +description = "Podziel zadawane obrazenia na podstawie liczby wrogow wokol ciebie!" +lore1 = "Im wiecej wrogow wokol ciebie, tym mniej obrazen zadasz kazdemu" +lore2 = "Zasieg: " +lore3 = "Dodane Obrazenia dla wszystkich Istot: " +lore = ["Im wiecej wrogow wokol ciebie, tym mniej obrazen zadasz kazdemu", "Zasieg: ", "Dodane Obrazenia dla wszystkich Istot: "] [tragoul.healing] - name = "Wola Bolu" - description = "Odzyskaj zdrowie na podstawie zadawanych obrazen!" - lore1 = "Krzywdzenie jeszcze nigdy nie bylo tak przyjemne! Lecz sie z Zadanych Obrazen" - lore2 = "Jest 3-sekundowe okno obrazen na leczenie i 1-sekundowy czas odnowienia " - lore3 = "Procent Leczenia za Obrazenia: " - lore = ["Krzywdzenie jeszcze nigdy nie bylo tak przyjemne! Lecz sie z Zadanych Obrazen", "Jest 3-sekundowe okno obrazen na leczenie i 1-sekundowy czas odnowienia ", "Procent Leczenia za Obrazenia: "] +name = "Wola Bolu" +description = "Odzyskaj zdrowie na podstawie zadawanych obrazen!" +lore1 = "Krzywdzenie jeszcze nigdy nie bylo tak przyjemne! Lecz sie z Zadanych Obrazen" +lore2 = "Jest 3-sekundowe okno obrazen na leczenie i 1-sekundowy czas odnowienia " +lore3 = "Procent Leczenia za Obrazenia: " +lore = ["Krzywdzenie jeszcze nigdy nie bylo tak przyjemne! Lecz sie z Zadanych Obrazen", "Jest 3-sekundowe okno obrazen na leczenie i 1-sekundowy czas odnowienia ", "Procent Leczenia za Obrazenia: "] [tragoul.lance] - name = "Lance Trupow" - description = "Zabicie wroga lub zabojstwo przez umiejetnosc tworzy lance zadajaca obrazenia pobliskiemu wrogowi!" - lore1 = "Lance beda szukac ze wszystkiego co zabijesz ORAZ jesli ta umiejetnosc zabije wroga." - lore2 = "Poswiec czesc swojego zycia aby stworzyc lance (to moze cie zabic)" - lore3 = "Maks. Lanc: 1 + " - lore = ["Lance beda szukac ze wszystkiego co zabijesz ORAZ jesli ta umiejetnosc zabije wroga.", "Poswiec czesc swojego zycia aby stworzyc lance (to moze cie zabic)", "Maks. Lanc: 1 + "] +name = "Lance Trupow" +description = "Zabicie wroga lub zabojstwo przez umiejetnosc tworzy lance zadajaca obrazenia pobliskiemu wrogowi!" +lore1 = "Lance beda szukac ze wszystkiego co zabijesz ORAZ jesli ta umiejetnosc zabije wroga." +lore2 = "Poswiec czesc swojego zycia aby stworzyc lance (to moze cie zabic)" +lore3 = "Maks. Lanc: 1 + " +lore = ["Lance beda szukac ze wszystkiego co zabijesz ORAZ jesli ta umiejetnosc zabije wroga.", "Poswiec czesc swojego zycia aby stworzyc lance (to moze cie zabic)", "Maks. Lanc: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Szklane Dzialo" - description = "Dodatkowe obrazenia wreczna im nizsza wartosc twojego pancerza" - lore1 = "x Obrazenia przy 0 pancerza" - lore2 = "Dodatkowe Obrazenia za Poziom" - lore = ["x Obrazenia przy 0 pancerza", "Dodatkowe Obrazenia za Poziom"] +name = "Szklane Dzialo" +description = "Dodatkowe obrazenia wreczna im nizsza wartosc twojego pancerza" +lore1 = "x Obrazenia przy 0 pancerza" +lore2 = "Dodatkowe Obrazenia za Poziom" +lore = ["x Obrazenia przy 0 pancerza", "Dodatkowe Obrazenia za Poziom"] [unarmed.power] - name = "Moc Walki Wreczna" - description = "Ulepszone obrazenia wreczna" - lore1 = "Obrazenia" - lore = ["Obrazenia"] +name = "Moc Walki Wreczna" +description = "Ulepszone obrazenia wreczna" +lore1 = "Obrazenia" +lore = ["Obrazenia"] [unarmed.sucker_punch] - name = "Cios z Zaskoczenia" - description = "Sprintowe ciosy, ale bardziej smiercionosne." - lore1 = "Obrazenia" - lore2 = "Obrazenia rosna wraz z szybkoscia podczas ciosow" - lore = ["Obrazenia", "Obrazenia rosna wraz z szybkoscia podczas ciosow"] +name = "Cios z Zaskoczenia" +description = "Sprintowe ciosy, ale bardziej smiercionosne." +lore1 = "Obrazenia" +lore2 = "Obrazenia rosna wraz z szybkoscia podczas ciosow" +lore = ["Obrazenia", "Obrazenia rosna wraz z szybkoscia podczas ciosow"] diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4a6310fd0..b68c1e9b8 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -15,7 +15,7 @@ softdepend: - Residence commands: - adapt: {} + adapt: { } permissions: adapt.idontknowwhatimdoingiswear: diff --git a/src/main/resources/pt_PT.toml b/src/main/resources/pt_PT.toml index 1398b9e14..cfc16a177 100644 --- a/src/main/resources/pt_PT.toml +++ b/src/main/resources/pt_PT.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Tem que se mexer!" - description = "Caminhe mais de 1 quilometro (1.000 blocos)" +title = "Tem que se mexer!" +description = "Caminhe mais de 1 quilometro (1.000 blocos)" [advancement.challenge_sprint_5k] - title = "Corra 5K!" - description = "Caminhe mais de 5 quilometros (5.000 blocos)" +title = "Corra 5K!" +description = "Caminhe mais de 5 quilometros (5.000 blocos)" [advancement.challenge_sprint_50k] - title = "Zoom de 50K!" - description = "Caminhe mais de 50 quilometros (50.000 blocos)" +title = "Zoom de 50K!" +description = "Caminhe mais de 50 quilometros (50.000 blocos)" [advancement.challenge_sprint_500k] - title = "Atravesse o Universo!!" - description = "Caminhe mais de 500 quilometros (500.000 blocos)" +title = "Atravesse o Universo!!" +description = "Caminhe mais de 500 quilometros (500.000 blocos)" [advancement.challenge_sprint_marathon] - title = "Corra uma maratona (literal)!" - description = "Corra mais de 42.195 blocos!" +title = "Corra uma maratona (literal)!" +description = "Corra mais de 42.195 blocos!" [advancement.challenge_place_1k] - title = "Construtor Iniciante!" - description = "Coloque 1.000 Blocos" +title = "Construtor Iniciante!" +description = "Coloque 1.000 Blocos" [advancement.challenge_place_5k] - title = "Construtor Intermediario!" - description = "Coloque 5.000 Blocos" +title = "Construtor Intermediario!" +description = "Coloque 5.000 Blocos" [advancement.challenge_place_50k] - title = "Construtor Avancado!" - description = "Coloque 50.000 Blocos" +title = "Construtor Avancado!" +description = "Coloque 50.000 Blocos" [advancement.challenge_place_500k] - title = "Mestre Construtor!" - description = "Coloque 500.000 Blocos" +title = "Mestre Construtor!" +description = "Coloque 500.000 Blocos" [advancement.challenge_place_5m] - title = "Acolito da Simetria!" - description = "A REALIDADE E O SEU PARQUE! (5 Milhoes de Blocos)" +title = "Acolito da Simetria!" +description = "A REALIDADE E O SEU PARQUE! (5 Milhoes de Blocos)" [advancement.challenge_chop_1k] - title = "Lenhador Iniciante!" - description = "Corte 1.000 Blocos" +title = "Lenhador Iniciante!" +description = "Corte 1.000 Blocos" [advancement.challenge_chop_5k] - title = "Lenhador Intermediario!" - description = "Corte 5.000 Blocos" +title = "Lenhador Intermediario!" +description = "Corte 5.000 Blocos" [advancement.challenge_chop_50k] - title = "Lenhador Avancado!" - description = "Corte 50.000 Blocos" +title = "Lenhador Avancado!" +description = "Corte 50.000 Blocos" [advancement.challenge_chop_500k] - title = "Mestre Lenhador!" - description = "Corte 500.000 Blocos" +title = "Mestre Lenhador!" +description = "Corte 500.000 Blocos" [advancement.challenge_chop_5m] - title = "Jackson, o Cao" - description = "O melhor bom menino! (5 Milhoes de Blocos)" +title = "Jackson, o Cao" +description = "O melhor bom menino! (5 Milhoes de Blocos)" [advancement.challenge_block_1k] - title = "Mal Bloqueando!" - description = "Bloqueie 1000 Golpes" +title = "Mal Bloqueando!" +description = "Bloqueie 1000 Golpes" [advancement.challenge_block_5k] - title = "Bloquear e Divertido!" - description = "Bloqueie 5000 Golpes" +title = "Bloquear e Divertido!" +description = "Bloqueie 5000 Golpes" [advancement.challenge_block_50k] - title = "Bloquear e a minha vida!" - description = "Bloqueie 50.000 Golpes" +title = "Bloquear e a minha vida!" +description = "Bloqueie 50.000 Golpes" [advancement.challenge_block_500k] - title = "Bloquear e o meu Proposito!" - description = "Bloqueie 500.000 Golpes" +title = "Bloquear e o meu Proposito!" +description = "Bloqueie 500.000 Golpes" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Bloqueie 5.000.000 Golpes" +title = "Die Hand Die Verletzt" +description = "Bloqueie 5.000.000 Golpes" [advancement.challenge_brew_1k] - title = "Alquimista Iniciante!" - description = "Consuma 1000 Pocoes" +title = "Alquimista Iniciante!" +description = "Consuma 1000 Pocoes" [advancement.challenge_brew_5k] - title = "Alquimista Intermediario!" - description = "Consuma 5000 Pocoes" +title = "Alquimista Intermediario!" +description = "Consuma 5000 Pocoes" [advancement.challenge_brew_50k] - title = "Alquimista Avancado!" - description = "Consuma 50.000 Pocoes" +title = "Alquimista Avancado!" +description = "Consuma 50.000 Pocoes" [advancement.challenge_brew_500k] - title = "Mestre Alquimista!" - description = "Consuma 500.000 Pocoes" +title = "Mestre Alquimista!" +description = "Consuma 500.000 Pocoes" [advancement.challenge_brew_5m] - title = "O Alquimista" - description = "Consuma 5.000.000 Pocoes" +title = "O Alquimista" +description = "Consuma 5.000.000 Pocoes" [advancement.challenge_brewsplash_1k] - title = "Lancador de Pocoes Iniciante!" - description = "Lance 1000 Pocoes" +title = "Lancador de Pocoes Iniciante!" +description = "Lance 1000 Pocoes" [advancement.challenge_brewsplash_5k] - title = "Lancador de Pocoes Intermediario!" - description = "Lance 5000 Pocoes" +title = "Lancador de Pocoes Intermediario!" +description = "Lance 5000 Pocoes" [advancement.challenge_brewsplash_50k] - title = "Lancador de Pocoes Avancado!" - description = "Lance 50.000 Pocoes" +title = "Lancador de Pocoes Avancado!" +description = "Lance 50.000 Pocoes" [advancement.challenge_brewsplash_500k] - title = "Mestre Lancador de Pocoes!" - description = "Lance 500.000 Pocoes" +title = "Mestre Lancador de Pocoes!" +description = "Lance 500.000 Pocoes" [advancement.challenge_brewsplash_5m] - title = "O Mestre do Respingo" - description = "Lance 5.000.000 Pocoes" +title = "O Mestre do Respingo" +description = "Lance 5.000.000 Pocoes" [advancement.challenge_craft_1k] - title = "Artesao Astuto!" - description = "Fabrique 1000 Itens" +title = "Artesao Astuto!" +description = "Fabrique 1000 Itens" [advancement.challenge_craft_5k] - title = "Artesao Rabugento!" - description = "Fabrique 5000 Itens" +title = "Artesao Rabugento!" +description = "Fabrique 5000 Itens" [advancement.challenge_craft_50k] - title = "Artesao Servil!" - description = "Fabrique 50.000 Itens" +title = "Artesao Servil!" +description = "Fabrique 50.000 Itens" [advancement.challenge_craft_500k] - title = "Artesao Cacofonico!" - description = "Fabrique 500.000 Itens" +title = "Artesao Cacofonico!" +description = "Fabrique 500.000 Itens" [advancement.challenge_craft_5m] - title = "Calamitoso McCraftface" - description = "Fabrique 5.000.000 Itens" +title = "Calamitoso McCraftface" +description = "Fabrique 5.000.000 Itens" [advancement.challenge_enchant_1k] - title = "Encantador Iniciante!" - description = "Encante 1000 Itens" +title = "Encantador Iniciante!" +description = "Encante 1000 Itens" [advancement.challenge_enchant_5k] - title = "Encantador Intermediario!" - description = "Encante 5000 Itens" +title = "Encantador Intermediario!" +description = "Encante 5000 Itens" [advancement.challenge_enchant_50k] - title = "Encantador Avancado!" - description = "Encante 50.000 Itens" +title = "Encantador Avancado!" +description = "Encante 50.000 Itens" [advancement.challenge_enchant_500k] - title = "Mestre Encantador!" - description = "Encante 500.000 Itens" +title = "Mestre Encantador!" +description = "Encante 500.000 Itens" [advancement.challenge_enchant_5m] - title = "Encantador Enigmatico" - description = "Encante 5.000.000 Itens" +title = "Encantador Enigmatico" +description = "Encante 5.000.000 Itens" [advancement.challenge_excavate_1k] - title = "Escavador Entusiasmado!" - description = "Escave 1000 Blocos" +title = "Escavador Entusiasmado!" +description = "Escave 1000 Blocos" [advancement.challenge_excavate_5k] - title = "Escavador Intermediario!" - description = "Escave 5000 Blocos" +title = "Escavador Intermediario!" +description = "Escave 5000 Blocos" [advancement.challenge_excavate_50k] - title = "Escavador Avancado!" - description = "Escave 50.000 Blocos" +title = "Escavador Avancado!" +description = "Escave 50.000 Blocos" [advancement.challenge_excavate_500k] - title = "Mestre Escavador!" - description = "Escave 500.000 Blocos" +title = "Mestre Escavador!" +description = "Escave 500.000 Blocos" [advancement.challenge_excavate_5m] - title = "Escavador Enigmatico" - description = "Escave 5.000.000 Blocos" +title = "Escavador Enigmatico" +description = "Escave 5.000.000 Blocos" [advancement.horrible_person] - title = "Voce e uma Pessoa Horrivel" - description = "Insondavel, realmente" +title = "Voce e uma Pessoa Horrivel" +description = "Insondavel, realmente" [advancement.challenge_turtle_egg_smasher] - title = "Esmagador de Ovos de Tartaruga!" - description = "Quebre 100 ovos de tartaruga" +title = "Esmagador de Ovos de Tartaruga!" +description = "Quebre 100 ovos de tartaruga" [advancement.challenge_turtle_egg_annihilator] - title = "Aniquilador de Ovos de Tartaruga!" - description = "Quebre 500 ovos de tartaruga" +title = "Aniquilador de Ovos de Tartaruga!" +description = "Quebre 500 ovos de tartaruga" [advancement.challenge_novice_hunter] - title = "Cacador Iniciante!" - description = "Mate 100 entidades" +title = "Cacador Iniciante!" +description = "Mate 100 entidades" [advancement.challenge_intermediate_hunter] - title = "Cacador Intermediario!" - description = "Mate 500 entidades" +title = "Cacador Intermediario!" +description = "Mate 500 entidades" [advancement.challenge_advanced_hunter] - title = "Cacador Avancado!" - description = "Mate 5000 entidades" +title = "Cacador Avancado!" +description = "Mate 5000 entidades" [advancement.challenge_creeper_conqueror] - title = "Conquistador de Creepers!" - description = "Mate 50 creepers" +title = "Conquistador de Creepers!" +description = "Mate 50 creepers" [advancement.challenge_creeper_annihilator] - title = "Aniquilador de Creepers!" - description = "Mate 200 creepers" +title = "Aniquilador de Creepers!" +description = "Mate 200 creepers" [advancement.challenge_pickaxe_1k] - title = "Mineiro Iniciante" - description = "Quebre 1000 Blocos" +title = "Mineiro Iniciante" +description = "Quebre 1000 Blocos" [advancement.challenge_pickaxe_5k] - title = "Mineiro Habilidoso" - description = "Quebre 5000 Blocos" +title = "Mineiro Habilidoso" +description = "Quebre 5000 Blocos" [advancement.challenge_pickaxe_50k] - title = "Mineiro Especialista" - description = "Quebre 50.000 Blocos" +title = "Mineiro Especialista" +description = "Quebre 50.000 Blocos" [advancement.challenge_pickaxe_500k] - title = "Mestre Mineiro" - description = "Quebre 500.000 Blocos" +title = "Mestre Mineiro" +description = "Quebre 500.000 Blocos" [advancement.challenge_pickaxe_5m] - title = "Mineiro Lendario" - description = "Quebre 5.000.000 Blocos" +title = "Mineiro Lendario" +description = "Quebre 5.000.000 Blocos" [advancement.challenge_eat_100] - title = "Tanta coisa para comer!" - description = "Coma mais de 100 Itens!" +title = "Tanta coisa para comer!" +description = "Coma mais de 100 Itens!" [advancement.challenge_eat_1000] - title = "Fome Insaciavel!" - description = "Coma mais de 1.000 Itens!" +title = "Fome Insaciavel!" +description = "Coma mais de 1.000 Itens!" [advancement.challenge_eat_10000] - title = "FOME ETERNA!" - description = "Coma mais de 10.000 Itens!" +title = "FOME ETERNA!" +description = "Coma mais de 10.000 Itens!" [advancement.challenge_harvest_100] - title = "Colheita Completa" - description = "Colha mais de 100 cultivos!" +title = "Colheita Completa" +description = "Colha mais de 100 cultivos!" [advancement.challenge_harvest_1000] - title = "Grande Colheita" - description = "Colha mais de 1.000 cultivos!" +title = "Grande Colheita" +description = "Colha mais de 1.000 cultivos!" [advancement.challenge_swim_1nm] - title = "Submarino Humano!" - description = "Nade 1 Milha Nautica (1.852 blocos)" +title = "Submarino Humano!" +description = "Nade 1 Milha Nautica (1.852 blocos)" [advancement.challenge_sneak_1k] - title = "Dor nos Joelhos" - description = "Agache-se por mais de um quilometro (1.000 blocos)" +title = "Dor nos Joelhos" +description = "Agache-se por mais de um quilometro (1.000 blocos)" [advancement.challenge_sneak_5k] - title = "Caminhante das Sombras" - description = "Agache-se por mais de 5.000 blocos" +title = "Caminhante das Sombras" +description = "Agache-se por mais de 5.000 blocos" [advancement.challenge_sneak_20k] - title = "Fantasma" - description = "Agache-se por mais de 20.000 blocos" +title = "Fantasma" +description = "Agache-se por mais de 20.000 blocos" [advancement.challenge_swim_5k] - title = "Mergulhador Profundo" - description = "Nade mais de 5.000 blocos" +title = "Mergulhador Profundo" +description = "Nade mais de 5.000 blocos" [advancement.challenge_swim_20k] - title = "Escolhido de Poseidon" - description = "Nade mais de 20.000 blocos" +title = "Escolhido de Poseidon" +description = "Nade mais de 20.000 blocos" [advancement.challenge_sword_100] - title = "Primeiro Sangue" - description = "Acerte 100 golpes com uma espada" +title = "Primeiro Sangue" +description = "Acerte 100 golpes com uma espada" [advancement.challenge_sword_1k] - title = "Dancarina das Laminas" - description = "Acerte 1.000 golpes com uma espada" +title = "Dancarina das Laminas" +description = "Acerte 1.000 golpes com uma espada" [advancement.challenge_sword_10k] - title = "Mil Cortes" - description = "Acerte 10.000 golpes com uma espada" +title = "Mil Cortes" +description = "Acerte 10.000 golpes com uma espada" [advancement.challenge_unarmed_100] - title = "Brigao de Bar" - description = "Acerte 100 golpes desarmado" +title = "Brigao de Bar" +description = "Acerte 100 golpes desarmado" [advancement.challenge_unarmed_1k] - title = "Punhos de Ferro" - description = "Acerte 1.000 golpes desarmado" +title = "Punhos de Ferro" +description = "Acerte 1.000 golpes desarmado" [advancement.challenge_unarmed_10k] - title = "Um Soco" - description = "Acerte 10.000 golpes desarmado" +title = "Um Soco" +description = "Acerte 10.000 golpes desarmado" [advancement.challenge_trag_1k] - title = "Preco de Sangue" - description = "Receba 1.000 de dano" +title = "Preco de Sangue" +description = "Receba 1.000 de dano" [advancement.challenge_trag_10k] - title = "Mare Vermelha" - description = "Receba 10.000 de dano" +title = "Mare Vermelha" +description = "Receba 10.000 de dano" [advancement.challenge_trag_100k] - title = "Avatar do Sofrimento" - description = "Receba 100.000 de dano" +title = "Avatar do Sofrimento" +description = "Receba 100.000 de dano" [advancement.challenge_ranged_100] - title = "Pratica de Tiro" - description = "Dispare 100 projeteis" +title = "Pratica de Tiro" +description = "Dispare 100 projeteis" [advancement.challenge_ranged_1k] - title = "Olho de Falcao" - description = "Dispare 1.000 projeteis" +title = "Olho de Falcao" +description = "Dispare 1.000 projeteis" [advancement.challenge_ranged_10k] - title = "Tempestade de Flechas" - description = "Dispare 10.000 projeteis" +title = "Tempestade de Flechas" +description = "Dispare 10.000 projeteis" [advancement.challenge_chronos_1h] - title = "Tic-Tac" - description = "Passe 1 hora online" +title = "Tic-Tac" +description = "Passe 1 hora online" [advancement.challenge_chronos_24h] - title = "Areias do Tempo" - description = "Passe 24 horas online" +title = "Areias do Tempo" +description = "Passe 24 horas online" [advancement.challenge_chronos_168h] - title = "Eterno" - description = "Passe 168 horas (1 semana) online" +title = "Eterno" +description = "Passe 168 horas (1 semana) online" [advancement.challenge_nether_50] - title = "Guardiao do Inferno" - description = "Mate 50 criaturas do Nether" +title = "Guardiao do Inferno" +description = "Mate 50 criaturas do Nether" [advancement.challenge_nether_500] - title = "Sentinela Abissal" - description = "Mate 500 criaturas do Nether" +title = "Sentinela Abissal" +description = "Mate 500 criaturas do Nether" [advancement.challenge_nether_5k] - title = "Senhor do Nether" - description = "Mate 5.000 criaturas do Nether" +title = "Senhor do Nether" +description = "Mate 5.000 criaturas do Nether" [advancement.challenge_rift_50] - title = "Anomalia Espacial" - description = "Teleporte-se 50 vezes" +title = "Anomalia Espacial" +description = "Teleporte-se 50 vezes" [advancement.challenge_rift_500] - title = "Caminhante do Vazio" - description = "Teleporte-se 500 vezes" +title = "Caminhante do Vazio" +description = "Teleporte-se 500 vezes" [advancement.challenge_rift_5k] - title = "Entre Mundos" - description = "Teleporte-se 5.000 vezes" +title = "Entre Mundos" +description = "Teleporte-se 5.000 vezes" [advancement.challenge_taming_10] - title = "Encantador de Animais" - description = "Crie 10 animais" +title = "Encantador de Animais" +description = "Crie 10 animais" [advancement.challenge_taming_50] - title = "Lider da Matilha" - description = "Crie 50 animais" +title = "Lider da Matilha" +description = "Crie 50 animais" [advancement.challenge_taming_500] - title = "Mestre das Feras" - description = "Crie 500 animais" +title = "Mestre das Feras" +description = "Crie 500 animais" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Demonio da Velocidade" - description = "Correr mais de 5 Quilometros (5,000 blocos)" +title = "Demonio da Velocidade" +description = "Correr mais de 5 Quilometros (5,000 blocos)" [advancement.challenge_sprint_dist_50k] - title = "Pernas Relampago" - description = "Correr mais de 50 Quilometros (50,000 blocos)" +title = "Pernas Relampago" +description = "Correr mais de 50 Quilometros (50,000 blocos)" [advancement.challenge_agility_swim_1k] - title = "Caminhante das Aguas" - description = "Nadar mais de 1 Quilometro (1,000 blocos)" +title = "Caminhante das Aguas" +description = "Nadar mais de 1 Quilometro (1,000 blocos)" [advancement.challenge_agility_swim_10k] - title = "Viajante Aquatico" - description = "Nadar mais de 10 Quilometros (10,000 blocos)" +title = "Viajante Aquatico" +description = "Nadar mais de 10 Quilometros (10,000 blocos)" [advancement.challenge_fly_1k] - title = "Dancarino Celeste" - description = "Voar mais de 1 Quilometro (1,000 blocos)" +title = "Dancarino Celeste" +description = "Voar mais de 1 Quilometro (1,000 blocos)" [advancement.challenge_fly_10k] - title = "Cavaleiro do Vento" - description = "Voar mais de 10 Quilometros (10,000 blocos)" +title = "Cavaleiro do Vento" +description = "Voar mais de 10 Quilometros (10,000 blocos)" [advancement.challenge_agility_sneak_500] - title = "Passos Silenciosos" - description = "Agachar-se por mais de 500 blocos" +title = "Passos Silenciosos" +description = "Agachar-se por mais de 500 blocos" [advancement.challenge_agility_sneak_5k] - title = "Passos Fantasma" - description = "Agachar-se por mais de 5 Quilometros (5,000 blocos)" +title = "Passos Fantasma" +description = "Agachar-se por mais de 5 Quilometros (5,000 blocos)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Equipa de Demolicao" - description = "Destruir 500 blocos" +title = "Equipa de Demolicao" +description = "Destruir 500 blocos" [advancement.challenge_demolish_5k] - title = "Bola de Demolicao" - description = "Destruir 5,000 blocos" +title = "Bola de Demolicao" +description = "Destruir 5,000 blocos" [advancement.challenge_value_placed_10k] - title = "Construtor Valioso" - description = "Colocar blocos no valor de 10,000" +title = "Construtor Valioso" +description = "Colocar blocos no valor de 10,000" [advancement.challenge_value_placed_100k] - title = "Mestre Arquiteto" - description = "Colocar blocos no valor de 100,000" +title = "Mestre Arquiteto" +description = "Colocar blocos no valor de 100,000" [advancement.challenge_demolish_val_5k] - title = "Especialista em Salvamento" - description = "Recuperar 5,000 de valor em demolicao" +title = "Especialista em Salvamento" +description = "Recuperar 5,000 de valor em demolicao" [advancement.challenge_demolish_val_50k] - title = "Desconstrucao Total" - description = "Recuperar 50,000 de valor em demolicao" +title = "Desconstrucao Total" +description = "Recuperar 50,000 de valor em demolicao" [advancement.challenge_high_build_100] - title = "Construtor Celeste" - description = "Colocar 100 blocos acima de Y=128" +title = "Construtor Celeste" +description = "Colocar 100 blocos acima de Y=128" [advancement.challenge_high_build_1k] - title = "Arquiteto das Nuvens" - description = "Colocar 1,000 blocos acima de Y=128" +title = "Arquiteto das Nuvens" +description = "Colocar 1,000 blocos acima de Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Lenhador" - description = "Balancear o machado 500 vezes" +title = "Lenhador" +description = "Balancear o machado 500 vezes" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Balancear o machado 5,000 vezes" +title = "Berserker" +description = "Balancear o machado 5,000 vezes" [advancement.challenge_axe_damage_1k] - title = "Cortador" - description = "Causar 1,000 de dano com machados" +title = "Cortador" +description = "Causar 1,000 de dano com machados" [advancement.challenge_axe_damage_10k] - title = "Machado do Carrasco" - description = "Causar 10,000 de dano com machados" +title = "Machado do Carrasco" +description = "Causar 10,000 de dano com machados" [advancement.challenge_axe_value_5k] - title = "Comerciante de Madeira" - description = "Colher 5,000 de valor em madeira" +title = "Comerciante de Madeira" +description = "Colher 5,000 de valor em madeira" [advancement.challenge_axe_value_50k] - title = "Barao da Madeira" - description = "Colher 50,000 de valor em madeira" +title = "Barao da Madeira" +description = "Colher 50,000 de valor em madeira" [advancement.challenge_leaves_500] - title = "Soprador de Folhas" - description = "Limpar 500 blocos de folhas com um machado" +title = "Soprador de Folhas" +description = "Limpar 500 blocos de folhas com um machado" [advancement.challenge_leaves_5k] - title = "Desfolhador" - description = "Limpar 5,000 blocos de folhas com um machado" +title = "Desfolhador" +description = "Limpar 5,000 blocos de folhas com um machado" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Absorvedor de Dano" - description = "Bloquear 1,000 de dano com um escudo" +title = "Absorvedor de Dano" +description = "Bloquear 1,000 de dano com um escudo" [advancement.challenge_block_dmg_10k] - title = "Escudo Humano" - description = "Bloquear 10,000 de dano com um escudo" +title = "Escudo Humano" +description = "Bloquear 10,000 de dano com um escudo" [advancement.challenge_block_proj_100] - title = "Deflector de Flechas" - description = "Bloquear 100 projeteis com um escudo" +title = "Deflector de Flechas" +description = "Bloquear 100 projeteis com um escudo" [advancement.challenge_block_proj_1k] - title = "Escudo Anti-Projeteis" - description = "Bloquear 1,000 projeteis com um escudo" +title = "Escudo Anti-Projeteis" +description = "Bloquear 1,000 projeteis com um escudo" [advancement.challenge_block_melee_500] - title = "Mestre da Defesa" - description = "Bloquear 500 ataques corpo a corpo com um escudo" +title = "Mestre da Defesa" +description = "Bloquear 500 ataques corpo a corpo com um escudo" [advancement.challenge_block_melee_5k] - title = "Fortaleza de Ferro" - description = "Bloquear 5,000 ataques corpo a corpo com um escudo" +title = "Fortaleza de Ferro" +description = "Bloquear 5,000 ataques corpo a corpo com um escudo" [advancement.challenge_block_heavy_50] - title = "Tanque" - description = "Bloquear 50 ataques pesados (mais de 5 de dano)" +title = "Tanque" +description = "Bloquear 50 ataques pesados (mais de 5 de dano)" [advancement.challenge_block_heavy_500] - title = "Objeto Imovel" - description = "Bloquear 500 ataques pesados (mais de 5 de dano)" +title = "Objeto Imovel" +description = "Bloquear 500 ataques pesados (mais de 5 de dano)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Preparacao do Alquimista" - description = "Colocar 10 suportes de pocoes" +title = "Preparacao do Alquimista" +description = "Colocar 10 suportes de pocoes" [advancement.challenge_brew_stands_50] - title = "Fabrica de Pocoes" - description = "Colocar 50 suportes de pocoes" +title = "Fabrica de Pocoes" +description = "Colocar 50 suportes de pocoes" [advancement.challenge_brew_strong_25] - title = "Pocao Forte" - description = "Consumir 25 pocoes melhoradas" +title = "Pocao Forte" +description = "Consumir 25 pocoes melhoradas" [advancement.challenge_brew_strong_250] - title = "Potencia Maxima" - description = "Consumir 250 pocoes melhoradas" +title = "Potencia Maxima" +description = "Consumir 250 pocoes melhoradas" [advancement.challenge_brew_splash_hits_50] - title = "Zona de Impacto" - description = "Acertar 50 entidades com pocoes de arremesso" +title = "Zona de Impacto" +description = "Acertar 50 entidades com pocoes de arremesso" [advancement.challenge_brew_splash_hits_500] - title = "Medico da Peste" - description = "Acertar 500 entidades com pocoes de arremesso" +title = "Medico da Peste" +description = "Acertar 500 entidades com pocoes de arremesso" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Inquieto" - description = "Viajar 1 Quilometro enquanto ativo" +title = "Inquieto" +description = "Viajar 1 Quilometro enquanto ativo" [advancement.challenge_active_dist_10k] - title = "Desbravador" - description = "Viajar 10 Quilometros enquanto ativo" +title = "Desbravador" +description = "Viajar 10 Quilometros enquanto ativo" [advancement.challenge_active_dist_100k] - title = "Movimento Perpetuo" - description = "Viajar 100 Quilometros enquanto ativo" +title = "Movimento Perpetuo" +description = "Viajar 100 Quilometros enquanto ativo" [advancement.challenge_beds_10] - title = "Madrugador" - description = "Dormir numa cama 10 vezes" +title = "Madrugador" +description = "Dormir numa cama 10 vezes" [advancement.challenge_beds_100] - title = "Saltador do Tempo" - description = "Dormir numa cama 100 vezes" +title = "Saltador do Tempo" +description = "Dormir numa cama 100 vezes" [advancement.challenge_chronos_tp_50] - title = "Mudanca Temporal" - description = "Teletransportar-se 50 vezes" +title = "Mudanca Temporal" +description = "Teletransportar-se 50 vezes" [advancement.challenge_chronos_tp_500] - title = "Distorcao Temporal" - description = "Teletransportar-se 500 vezes" +title = "Distorcao Temporal" +description = "Teletransportar-se 500 vezes" [advancement.challenge_chronos_deaths_10] - title = "Mortal" - description = "Morrer 10 vezes" +title = "Mortal" +description = "Morrer 10 vezes" [advancement.challenge_chronos_deaths_100] - title = "Desafiador da Morte" - description = "Morrer 100 vezes" +title = "Desafiador da Morte" +description = "Morrer 100 vezes" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Valor Artesanal" - description = "Fabricar itens no valor total de 10,000" +title = "Valor Artesanal" +description = "Fabricar itens no valor total de 10,000" [advancement.challenge_craft_value_100k] - title = "Artesao" - description = "Fabricar itens no valor total de 100,000" +title = "Artesao" +description = "Fabricar itens no valor total de 100,000" [advancement.challenge_craft_tools_25] - title = "Ferreiro de Ferramentas" - description = "Fabricar 25 ferramentas" +title = "Ferreiro de Ferramentas" +description = "Fabricar 25 ferramentas" [advancement.challenge_craft_tools_250] - title = "Mestre Forjador" - description = "Fabricar 250 ferramentas" +title = "Mestre Forjador" +description = "Fabricar 250 ferramentas" [advancement.challenge_craft_armor_25] - title = "Ferreiro de Armaduras" - description = "Fabricar 25 pecas de armadura" +title = "Ferreiro de Armaduras" +description = "Fabricar 25 pecas de armadura" [advancement.challenge_craft_armor_250] - title = "Mestre Armeiro" - description = "Fabricar 250 pecas de armadura" +title = "Mestre Armeiro" +description = "Fabricar 250 pecas de armadura" [advancement.challenge_craft_mass_25k] - title = "Producao em Massa" - description = "Fabricar 25,000 itens" +title = "Producao em Massa" +description = "Fabricar 25,000 itens" [advancement.challenge_craft_mass_250k] - title = "Revolucao Industrial" - description = "Fabricar 250,000 itens" +title = "Revolucao Industrial" +description = "Fabricar 250,000 itens" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Colecionador" - description = "Descobrir 50 itens unicos" +title = "Colecionador" +description = "Descobrir 50 itens unicos" [advancement.challenge_discover_items_250] - title = "Catalogador" - description = "Descobrir 250 itens unicos" +title = "Catalogador" +description = "Descobrir 250 itens unicos" [advancement.challenge_discover_blocks_50] - title = "Topografo" - description = "Descobrir 50 blocos unicos" +title = "Topografo" +description = "Descobrir 50 blocos unicos" [advancement.challenge_discover_blocks_250] - title = "Geologo" - description = "Descobrir 250 blocos unicos" +title = "Geologo" +description = "Descobrir 250 blocos unicos" [advancement.challenge_discover_mobs_25] - title = "Observador" - description = "Descobrir 25 mobs unicos" +title = "Observador" +description = "Descobrir 25 mobs unicos" [advancement.challenge_discover_mobs_75] - title = "Naturalista" - description = "Descobrir 75 mobs unicos" +title = "Naturalista" +description = "Descobrir 75 mobs unicos" [advancement.challenge_discover_biomes_10] - title = "Andarilho" - description = "Descobrir 10 biomas unicos" +title = "Andarilho" +description = "Descobrir 10 biomas unicos" [advancement.challenge_discover_biomes_40] - title = "Viajante do Mundo" - description = "Descobrir 40 biomas unicos" +title = "Viajante do Mundo" +description = "Descobrir 40 biomas unicos" [advancement.challenge_discover_foods_10] - title = "Gastronomista" - description = "Descobrir 10 alimentos unicos" +title = "Gastronomista" +description = "Descobrir 10 alimentos unicos" [advancement.challenge_discover_foods_30] - title = "Mestre Culinario" - description = "Descobrir 30 alimentos unicos" +title = "Mestre Culinario" +description = "Descobrir 30 alimentos unicos" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Tecelao de Poder" - description = "Acumular 100 de poder de encantamento" +title = "Tecelao de Poder" +description = "Acumular 100 de poder de encantamento" [advancement.challenge_enchant_power_1k] - title = "Mestre Arcano" - description = "Acumular 1,000 de poder de encantamento" +title = "Mestre Arcano" +description = "Acumular 1,000 de poder de encantamento" [advancement.challenge_enchant_levels_1k] - title = "Gastador de Niveis" - description = "Gastar 1,000 niveis de experiencia em encantamentos" +title = "Gastador de Niveis" +description = "Gastar 1,000 niveis de experiencia em encantamentos" [advancement.challenge_enchant_levels_10k] - title = "Poco de Experiencia" - description = "Gastar 10,000 niveis de experiencia em encantamentos" +title = "Poco de Experiencia" +description = "Gastar 10,000 niveis de experiencia em encantamentos" [advancement.challenge_enchant_high_25] - title = "Alto Risco" - description = "Realizar 25 encantamentos de nivel maximo" +title = "Alto Risco" +description = "Realizar 25 encantamentos de nivel maximo" [advancement.challenge_enchant_high_250] - title = "Encantador Lendario" - description = "Realizar 250 encantamentos de nivel maximo" +title = "Encantador Lendario" +description = "Realizar 250 encantamentos de nivel maximo" [advancement.challenge_enchant_total_500] - title = "Queimador de Niveis" - description = "Gastar 500 niveis totais em todos os encantamentos" +title = "Queimador de Niveis" +description = "Gastar 500 niveis totais em todos os encantamentos" [advancement.challenge_enchant_total_5k] - title = "Investimento Arcano" - description = "Gastar 5,000 niveis totais em todos os encantamentos" +title = "Investimento Arcano" +description = "Gastar 5,000 niveis totais em todos os encantamentos" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Escavador" - description = "Balancear a pa 500 vezes" +title = "Escavador" +description = "Balancear a pa 500 vezes" [advancement.challenge_dig_swing_5k] - title = "Escavadeira" - description = "Balancear a pa 5,000 vezes" +title = "Escavadeira" +description = "Balancear a pa 5,000 vezes" [advancement.challenge_dig_damage_1k] - title = "Cavaleiro da Pa" - description = "Causar 1,000 de dano com uma pa" +title = "Cavaleiro da Pa" +description = "Causar 1,000 de dano com uma pa" [advancement.challenge_dig_damage_10k] - title = "Mestre da Pa" - description = "Causar 10,000 de dano com uma pa" +title = "Mestre da Pa" +description = "Causar 10,000 de dano com uma pa" [advancement.challenge_dig_value_5k] - title = "Comerciante de Terra" - description = "Escavar 5,000 de valor em blocos" +title = "Comerciante de Terra" +description = "Escavar 5,000 de valor em blocos" [advancement.challenge_dig_value_50k] - title = "Barao da Terra" - description = "Escavar 50,000 de valor em blocos" +title = "Barao da Terra" +description = "Escavar 50,000 de valor em blocos" [advancement.challenge_dig_gravel_500] - title = "Triturador de Cascalho" - description = "Cavar 500 blocos de cascalho, areia ou argila" +title = "Triturador de Cascalho" +description = "Cavar 500 blocos de cascalho, areia ou argila" [advancement.challenge_dig_gravel_5k] - title = "Peneirador de Areia" - description = "Cavar 5,000 blocos de cascalho, areia ou argila" +title = "Peneirador de Areia" +description = "Cavar 5,000 blocos de cascalho, areia ou argila" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Semeador" - description = "Plantar 100 cultivos" +title = "Semeador" +description = "Plantar 100 cultivos" [advancement.challenge_plant_1k] - title = "Mao Verde" - description = "Plantar 1,000 cultivos" +title = "Mao Verde" +description = "Plantar 1,000 cultivos" [advancement.challenge_plant_5k] - title = "Barao Agricola" - description = "Plantar 5,000 cultivos" +title = "Barao Agricola" +description = "Plantar 5,000 cultivos" [advancement.challenge_compost_50] - title = "Reciclador" - description = "Compostar 50 itens" +title = "Reciclador" +description = "Compostar 50 itens" [advancement.challenge_compost_500] - title = "Enriquecedor de Solo" - description = "Compostar 500 itens" +title = "Enriquecedor de Solo" +description = "Compostar 500 itens" [advancement.challenge_shear_50] - title = "Tosquiador" - description = "Tosquiar 50 entidades" +title = "Tosquiador" +description = "Tosquiar 50 entidades" [advancement.challenge_shear_250] - title = "Mestre do Rebanho" - description = "Tosquiar 250 entidades" +title = "Mestre do Rebanho" +description = "Tosquiar 250 entidades" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Cacador" - description = "Abater 500 criaturas" +title = "Cacador" +description = "Abater 500 criaturas" [advancement.challenge_kills_5k] - title = "Carrasco" - description = "Abater 5,000 criaturas" +title = "Carrasco" +description = "Abater 5,000 criaturas" [advancement.challenge_boss_1] - title = "Desafiador de Chefes" - description = "Abater um mob chefe" +title = "Desafiador de Chefes" +description = "Abater um mob chefe" [advancement.challenge_boss_10] - title = "Matador de Lendas" - description = "Abater 10 mobs chefes" +title = "Matador de Lendas" +description = "Abater 10 mobs chefes" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Murcho" - description = "Suportar 500 de dano de wither" +title = "Murcho" +description = "Suportar 500 de dano de wither" [advancement.challenge_wither_dmg_5k] - title = "Sobrevivente da Praga" - description = "Suportar 5,000 de dano de wither" +title = "Sobrevivente da Praga" +description = "Suportar 5,000 de dano de wither" [advancement.challenge_wither_skel_25] - title = "Colecionador de Ossos" - description = "Abater 25 wither skeletons" +title = "Colecionador de Ossos" +description = "Abater 25 wither skeletons" [advancement.challenge_wither_skel_250] - title = "Flagelo dos Esqueletos" - description = "Abater 250 wither skeletons" +title = "Flagelo dos Esqueletos" +description = "Abater 250 wither skeletons" [advancement.challenge_wither_boss_1] - title = "Destruidor do Wither" - description = "Derrotar o Wither" +title = "Destruidor do Wither" +description = "Derrotar o Wither" [advancement.challenge_wither_boss_10] - title = "Dominador do Nether" - description = "Derrotar o Wither 10 vezes" +title = "Dominador do Nether" +description = "Derrotar o Wither 10 vezes" [advancement.challenge_roses_10] - title = "Jardineiro da Morte" - description = "Partir 10 rosas do wither" +title = "Jardineiro da Morte" +description = "Partir 10 rosas do wither" [advancement.challenge_roses_100] - title = "Colecionador da Praga" - description = "Partir 100 rosas do wither" +title = "Colecionador da Praga" +description = "Partir 100 rosas do wither" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Braco de Mineiro" - description = "Balancear a picareta 500 vezes" +title = "Braco de Mineiro" +description = "Balancear a picareta 500 vezes" [advancement.challenge_pick_swing_5k] - title = "Abridor de Tuneis" - description = "Balancear a picareta 5,000 vezes" +title = "Abridor de Tuneis" +description = "Balancear a picareta 5,000 vezes" [advancement.challenge_pick_damage_1k] - title = "Lutador de Picareta" - description = "Causar 1,000 de dano com uma picareta" +title = "Lutador de Picareta" +description = "Causar 1,000 de dano com uma picareta" [advancement.challenge_pick_damage_10k] - title = "Picareta de Guerra" - description = "Causar 10,000 de dano com uma picareta" +title = "Picareta de Guerra" +description = "Causar 10,000 de dano com uma picareta" [advancement.challenge_pick_value_5k] - title = "Descobridor de Gemas" - description = "Minerar 5,000 de valor em blocos" +title = "Descobridor de Gemas" +description = "Minerar 5,000 de valor em blocos" [advancement.challenge_pick_value_50k] - title = "Barao dos Minerios" - description = "Minerar 50,000 de valor em blocos" +title = "Barao dos Minerios" +description = "Minerar 50,000 de valor em blocos" [advancement.challenge_pick_ores_500] - title = "Garimpeiro" - description = "Minerar 500 blocos de minerio" +title = "Garimpeiro" +description = "Minerar 500 blocos de minerio" [advancement.challenge_pick_ores_5k] - title = "Mestre Mineiro" - description = "Minerar 5,000 blocos de minerio" +title = "Mestre Mineiro" +description = "Minerar 5,000 blocos de minerio" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Atirador Certeiro" - description = "Causar 1,000 de dano a distancia" +title = "Atirador Certeiro" +description = "Causar 1,000 de dano a distancia" [advancement.challenge_ranged_dmg_10k] - title = "Arqueiro Letal" - description = "Causar 10,000 de dano a distancia" +title = "Arqueiro Letal" +description = "Causar 10,000 de dano a distancia" [advancement.challenge_ranged_dist_5k] - title = "Longo Alcance" - description = "Disparar projeteis cobrindo 5,000 blocos de distancia total" +title = "Longo Alcance" +description = "Disparar projeteis cobrindo 5,000 blocos de distancia total" [advancement.challenge_ranged_dist_50k] - title = "Atirador Quilometrico" - description = "Disparar projeteis cobrindo 50,000 blocos de distancia total" +title = "Atirador Quilometrico" +description = "Disparar projeteis cobrindo 50,000 blocos de distancia total" [advancement.challenge_ranged_kills_50] - title = "Arqueiro" - description = "Matar 50 mobs com armas de longo alcance" +title = "Arqueiro" +description = "Matar 50 mobs com armas de longo alcance" [advancement.challenge_ranged_kills_500] - title = "Mestre Arqueiro" - description = "Matar 500 mobs com armas de longo alcance" +title = "Mestre Arqueiro" +description = "Matar 500 mobs com armas de longo alcance" [advancement.challenge_longshot_25] - title = "Franco-atirador" - description = "Acertar 25 tiros de longa distancia (mais de 30 blocos)" +title = "Franco-atirador" +description = "Acertar 25 tiros de longa distancia (mais de 30 blocos)" [advancement.challenge_longshot_250] - title = "Olho de Aguia" - description = "Acertar 250 tiros de longa distancia (mais de 30 blocos)" +title = "Olho de Aguia" +description = "Acertar 250 tiros de longa distancia (mais de 30 blocos)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Lancador de Perolas" - description = "Lancar 50 perolas do ender" +title = "Lancador de Perolas" +description = "Lancar 50 perolas do ender" [advancement.challenge_rift_pearls_500] - title = "Viciado em Teletransporte" - description = "Lancar 500 perolas do ender" +title = "Viciado em Teletransporte" +description = "Lancar 500 perolas do ender" [advancement.challenge_rift_enderman_50] - title = "Cacador de Endermen" - description = "Abater 50 endermen" +title = "Cacador de Endermen" +description = "Abater 50 endermen" [advancement.challenge_rift_enderman_500] - title = "Perseguidor do Vazio" - description = "Abater 500 endermen" +title = "Perseguidor do Vazio" +description = "Abater 500 endermen" [advancement.challenge_rift_dragon_500] - title = "Lutador de Dragoes" - description = "Causar 500 de dano ao Ender Dragon" +title = "Lutador de Dragoes" +description = "Causar 500 de dano ao Ender Dragon" [advancement.challenge_rift_dragon_5k] - title = "Flagelo dos Dragoes" - description = "Causar 5,000 de dano ao Ender Dragon" +title = "Flagelo dos Dragoes" +description = "Causar 5,000 de dano ao Ender Dragon" [advancement.challenge_rift_crystal_10] - title = "Quebrador de Cristais" - description = "Destruir 10 cristais do end" +title = "Quebrador de Cristais" +description = "Destruir 10 cristais do end" [advancement.challenge_rift_crystal_100] - title = "Demolidor do End" - description = "Destruir 100 cristais do end" +title = "Demolidor do End" +description = "Destruir 100 cristais do end" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Pescador" - description = "Pescar 25 peixes" +title = "Pescador" +description = "Pescar 25 peixes" [advancement.challenge_fish_250] - title = "Mestre Pescador" - description = "Pescar 250 peixes" +title = "Mestre Pescador" +description = "Pescar 250 peixes" [advancement.challenge_drowned_25] - title = "Cacador de Afogados" - description = "Abater 25 afogados" +title = "Cacador de Afogados" +description = "Abater 25 afogados" [advancement.challenge_drowned_250] - title = "Purificador do Oceano" - description = "Abater 250 afogados" +title = "Purificador do Oceano" +description = "Abater 250 afogados" [advancement.challenge_guardian_10] - title = "Destruidor de Guardioes" - description = "Abater 10 guardioes" +title = "Destruidor de Guardioes" +description = "Abater 10 guardioes" [advancement.challenge_guardian_100] - title = "Saqueador de Templos" - description = "Abater 100 guardioes" +title = "Saqueador de Templos" +description = "Abater 100 guardioes" [advancement.challenge_underwater_blocks_100] - title = "Mineiro Submarino" - description = "Quebrar 100 blocos debaixo de agua" +title = "Mineiro Submarino" +description = "Quebrar 100 blocos debaixo de agua" [advancement.challenge_underwater_blocks_1k] - title = "Engenheiro Aquatico" - description = "Quebrar 1,000 blocos debaixo de agua" +title = "Engenheiro Aquatico" +description = "Quebrar 1,000 blocos debaixo de agua" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Punhalada nas Costas" - description = "Causar 500 de dano enquanto agachado" +title = "Punhalada nas Costas" +description = "Causar 500 de dano enquanto agachado" [advancement.challenge_stealth_dmg_5k] - title = "Assassino Silencioso" - description = "Causar 5,000 de dano enquanto agachado" +title = "Assassino Silencioso" +description = "Causar 5,000 de dano enquanto agachado" [advancement.challenge_stealth_kills_10] - title = "Assassino" - description = "Matar 10 mobs enquanto agachado" +title = "Assassino" +description = "Matar 10 mobs enquanto agachado" [advancement.challenge_stealth_kills_100] - title = "Ceifador das Sombras" - description = "Matar 100 mobs enquanto agachado" +title = "Ceifador das Sombras" +description = "Matar 100 mobs enquanto agachado" [advancement.challenge_stealth_time_1h] - title = "Paciente" - description = "Passar 1 hora agachado (3,600 segundos)" +title = "Paciente" +description = "Passar 1 hora agachado (3,600 segundos)" [advancement.challenge_stealth_time_10h] - title = "Mestre das Sombras" - description = "Passar 10 horas agachado (36,000 segundos)" +title = "Mestre das Sombras" +description = "Passar 10 horas agachado (36,000 segundos)" [advancement.challenge_stealth_arrows_50] - title = "Arqueiro Silencioso" - description = "Disparar 50 flechas enquanto agachado" +title = "Arqueiro Silencioso" +description = "Disparar 50 flechas enquanto agachado" [advancement.challenge_stealth_arrows_500] - title = "Arqueiro Fantasma" - description = "Disparar 500 flechas enquanto agachado" +title = "Arqueiro Fantasma" +description = "Disparar 500 flechas enquanto agachado" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Aprendiz de Espada" - description = "Causar 1,000 de dano com espadas" +title = "Aprendiz de Espada" +description = "Causar 1,000 de dano com espadas" [advancement.challenge_sword_dmg_10k] - title = "Espadachim" - description = "Causar 10,000 de dano com espadas" +title = "Espadachim" +description = "Causar 10,000 de dano com espadas" [advancement.challenge_sword_kills_50] - title = "Duelista" - description = "Matar 50 mobs com espadas" +title = "Duelista" +description = "Matar 50 mobs com espadas" [advancement.challenge_sword_kills_500] - title = "Gladiador" - description = "Matar 500 mobs com espadas" +title = "Gladiador" +description = "Matar 500 mobs com espadas" [advancement.challenge_sword_crit_50] - title = "Golpe Critico" - description = "Acertar 50 golpes criticos com espadas" +title = "Golpe Critico" +description = "Acertar 50 golpes criticos com espadas" [advancement.challenge_sword_crit_500] - title = "Mestre da Precisao" - description = "Acertar 500 golpes criticos com espadas" +title = "Mestre da Precisao" +description = "Acertar 500 golpes criticos com espadas" [advancement.challenge_sword_heavy_25] - title = "Golpe Pesado" - description = "Acertar 25 golpes pesados com espadas (mais de 8 de dano)" +title = "Golpe Pesado" +description = "Acertar 25 golpes pesados com espadas (mais de 8 de dano)" [advancement.challenge_sword_heavy_250] - title = "Golpe Devastador" - description = "Acertar 250 golpes pesados com espadas (mais de 8 de dano)" +title = "Golpe Devastador" +description = "Acertar 250 golpes pesados com espadas (mais de 8 de dano)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Treinador de Feras" - description = "Os seus animais causam 500 de dano total" +title = "Treinador de Feras" +description = "Os seus animais causam 500 de dano total" [advancement.challenge_pet_dmg_5k] - title = "Mestre de Guerra" - description = "Os seus animais causam 5,000 de dano total" +title = "Mestre de Guerra" +description = "Os seus animais causam 5,000 de dano total" [advancement.challenge_tamed_10] - title = "Amigo dos Animais" - description = "Domesticar 10 animais" +title = "Amigo dos Animais" +description = "Domesticar 10 animais" [advancement.challenge_tamed_100] - title = "Tratador do Zoo" - description = "Domesticar 100 animais" +title = "Tratador do Zoo" +description = "Domesticar 100 animais" [advancement.challenge_pet_kills_25] - title = "Taticas de Matilha" - description = "Os seus animais abatem 25 criaturas" +title = "Taticas de Matilha" +description = "Os seus animais abatem 25 criaturas" [advancement.challenge_pet_kills_250] - title = "Comandante Alfa" - description = "Os seus animais abatem 250 criaturas" +title = "Comandante Alfa" +description = "Os seus animais abatem 250 criaturas" [advancement.challenge_taming_2500] - title = "Especialista em Criacao" - description = "Criar 2,500 animais" +title = "Especialista em Criacao" +description = "Criar 2,500 animais" [advancement.challenge_taming_25k] - title = "Mestre Geneticista" - description = "Criar 25,000 animais" +title = "Mestre Geneticista" +description = "Criar 25,000 animais" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Saco de Pancadas" - description = "Receber 500 golpes" +title = "Saco de Pancadas" +description = "Receber 500 golpes" [advancement.challenge_trag_hits_5k] - title = "Glutao por Castigo" - description = "Receber 5,000 golpes" +title = "Glutao por Castigo" +description = "Receber 5,000 golpes" [advancement.challenge_trag_deaths_10] - title = "Sete Vidas" - description = "Morrer 10 vezes" +title = "Sete Vidas" +description = "Morrer 10 vezes" [advancement.challenge_trag_deaths_100] - title = "Pesadelo Recorrente" - description = "Morrer 100 vezes" +title = "Pesadelo Recorrente" +description = "Morrer 100 vezes" [advancement.challenge_trag_fire_500] - title = "Vitima das Chamas" - description = "Suportar 500 de dano de fogo" +title = "Vitima das Chamas" +description = "Suportar 500 de dano de fogo" [advancement.challenge_trag_fire_5k] - title = "Fenix" - description = "Suportar 5,000 de dano de fogo" +title = "Fenix" +description = "Suportar 5,000 de dano de fogo" [advancement.challenge_trag_fall_500] - title = "Teste de Gravidade" - description = "Suportar 500 de dano de queda" +title = "Teste de Gravidade" +description = "Suportar 500 de dano de queda" [advancement.challenge_trag_fall_5k] - title = "Velocidade Terminal" - description = "Suportar 5,000 de dano de queda" +title = "Velocidade Terminal" +description = "Suportar 5,000 de dano de queda" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Brigao" - description = "Causar 1,000 de dano com os punhos" +title = "Brigao" +description = "Causar 1,000 de dano com os punhos" [advancement.challenge_unarmed_dmg_10k] - title = "Artista Marcial" - description = "Causar 10,000 de dano com os punhos" +title = "Artista Marcial" +description = "Causar 10,000 de dano com os punhos" [advancement.challenge_unarmed_kills_25] - title = "Punhos de Ferro" - description = "Matar 25 mobs com os punhos" +title = "Punhos de Ferro" +description = "Matar 25 mobs com os punhos" [advancement.challenge_unarmed_kills_250] - title = "Punho Lendario" - description = "Matar 250 mobs com os punhos" +title = "Punho Lendario" +description = "Matar 250 mobs com os punhos" [advancement.challenge_unarmed_crit_25] - title = "Soco Critico" - description = "Acertar 25 golpes criticos com os punhos" +title = "Soco Critico" +description = "Acertar 25 golpes criticos com os punhos" [advancement.challenge_unarmed_crit_250] - title = "Punho Preciso" - description = "Acertar 250 golpes criticos com os punhos" +title = "Punho Preciso" +description = "Acertar 250 golpes criticos com os punhos" [advancement.challenge_unarmed_heavy_25] - title = "Soco Potente" - description = "Acertar 25 golpes pesados com os punhos (mais de 6 de dano)" +title = "Soco Potente" +description = "Acertar 25 golpes pesados com os punhos (mais de 6 de dano)" [advancement.challenge_unarmed_heavy_250] - title = "Rei do Nocaute" - description = "Acertar 250 golpes pesados com os punhos (mais de 6 de dano)" +title = "Rei do Nocaute" +description = "Acertar 250 golpes pesados com os punhos (mais de 6 de dano)" # items [items] [items.bound_ender_peral] - name = "Chave Portal do Relicario" - usage1 = "Shift + Clique Esquerdo para vincular" - usage2 = "Clique Direito para aceder ao Inventario vinculado" +name = "Chave Portal do Relicario" +usage1 = "Shift + Clique Esquerdo para vincular" +usage2 = "Clique Direito para aceder ao Inventario vinculado" [items.bound_eye_of_ender] - name = "Ancora Ocular" - usage1 = "Clique Direito para consumir e teletransportar para o local vinculado" - usage2 = "Shift + Clique Esquerdo para vincular a um bloco" +name = "Ancora Ocular" +usage1 = "Clique Direito para consumir e teletransportar para o local vinculado" +usage2 = "Shift + Clique Esquerdo para vincular a um bloco" [items.bound_redstone_torch] - name = "Controlo Remoto de Redstone" - usage1 = "Clique Direito para criar um pulso Redstone de 1 Tick" - usage2 = "Shift + Clique Esquerdo num bloco 'Alvo' para vincular" +name = "Controlo Remoto de Redstone" +usage1 = "Clique Direito para criar um pulso Redstone de 1 Tick" +usage2 = "Shift + Clique Esquerdo num bloco 'Alvo' para vincular" [items.bound_snowball] - name = "Armadilha de Teia!" - usage1 = "Atire para criar uma armadilha de teia temporaria no local" +name = "Armadilha de Teia!" +usage1 = "Atire para criar uma armadilha de teia temporaria no local" [items.chrono_time_bottle] - name = "Tempo Numa Garrafa" - usage1 = "Armazena tempo passivamente enquanto esta no seu inventario" - usage2 = "Clique direito em blocos temporais ou animais bebes para gastar tempo armazenado" - stored = "Tempo Armazenado" +name = "Tempo Numa Garrafa" +usage1 = "Armazena tempo passivamente enquanto esta no seu inventario" +usage2 = "Clique direito em blocos temporais ou animais bebes para gastar tempo armazenado" +stored = "Tempo Armazenado" [items.chrono_time_bomb] - name = "Bomba Temporal" - usage1 = "Clique direito para lancar um projetil crono que cria um campo temporal" +name = "Bomba Temporal" +usage1 = "Clique direito para lancar um projetil crono que cria um campo temporal" [items.elevator_block] - name = "Bloco Elevador" - usage1 = "Salte para teletransportar para cima" - usage2 = "Agache para teletransportar para baixo" - usage3 = "Minimo de 2 blocos de ar entre os elevadores" +name = "Bloco Elevador" +usage1 = "Salte para teletransportar para cima" +usage2 = "Agache para teletransportar para baixo" +usage3 = "Minimo de 2 blocos de ar entre os elevadores" # snippets [snippets] [snippets.gui] - level = "Nivel" - knowledge = "conhecimento" - power_used = "Energia Utilizada" - not_learned = "Nao Aprendido" - xp = "XP para" - welcome = "Bem-vindo!" - welcome_back = "Bem-vindo de volta!" - xp_bonus_for_time = "XP para" - max_ability_power = "Poder Maximo de Habilidade" - unlock_this_by_clicking = "Desbloqueie isto com Clique Direito: " - back = "Voltar" - unlearn_all = "Desaprender tudo" - unlearned_all = "Tudo desaprendido" +level = "Nivel" +knowledge = "conhecimento" +power_used = "Energia Utilizada" +not_learned = "Nao Aprendido" +xp = "XP para" +welcome = "Bem-vindo!" +welcome_back = "Bem-vindo de volta!" +xp_bonus_for_time = "XP para" +max_ability_power = "Poder Maximo de Habilidade" +unlock_this_by_clicking = "Desbloqueie isto com Clique Direito: " +back = "Voltar" +unlearn_all = "Desaprender tudo" +unlearned_all = "Tudo desaprendido" [snippets.adapt_menu] - may_not_unlearn = "NAO PODE DESAPRENDER" - may_unlearn = "PODE APRENDER/DESAPRENDER" - knowledge_cost = "Custo de Conhecimento" - knowledge_available = "Conhecimento Disponivel" - already_learned = "Ja Aprendido" - unlearn_refund = "Clique para Desaprender e Reembolsar" - no_refunds = "HARDCORE, REEMBOLSOS DESATIVADOS" - knowledge = "conhecimento" - click_learn = "Clique para Aprender" - no_knowledge = "(Nao tens nenhum Conhecimento)" - you_only_have = "So tens" - how_to_level_up = "Sobe o nivel das habilidades para aumentar o teu poder maximo." - not_enough_power = "Poder insuficiente! Cada Nivel de Habilidade custa 1 poder." - power = "poder" - power_drain = "Consumo de Poder" - learned = "Aprendido " - unlearned = "Desaprendido " - activator_block = "Estante de Livros" +may_not_unlearn = "NAO PODE DESAPRENDER" +may_unlearn = "PODE APRENDER/DESAPRENDER" +knowledge_cost = "Custo de Conhecimento" +knowledge_available = "Conhecimento Disponivel" +already_learned = "Ja Aprendido" +unlearn_refund = "Clique para Desaprender e Reembolsar" +no_refunds = "HARDCORE, REEMBOLSOS DESATIVADOS" +knowledge = "conhecimento" +click_learn = "Clique para Aprender" +no_knowledge = "(Nao tens nenhum Conhecimento)" +you_only_have = "So tens" +how_to_level_up = "Sobe o nivel das habilidades para aumentar o teu poder maximo." +not_enough_power = "Poder insuficiente! Cada Nivel de Habilidade custa 1 poder." +power = "poder" +power_drain = "Consumo de Poder" +learned = "Aprendido " +unlearned = "Desaprendido " +activator_block = "Estante de Livros" [snippets.knowledge_orb] - contains = "contem" - knowledge = "conhecimento" - rightclick = "Clique Direito" - togainknowledge = "para obter este conhecimento" - knowledge_orb = "Orbe de Conhecimento" +contains = "contem" +knowledge = "conhecimento" +rightclick = "Clique Direito" +togainknowledge = "para obter este conhecimento" +knowledge_orb = "Orbe de Conhecimento" [snippets.experience_orb] - contains = "contem" - xp = "Experiencia" - rightclick = "Clique Direito" - togainxp = "para ganhar esta experiencia" - xporb = "Orbe de Experiencia" +contains = "contem" +xp = "Experiencia" +rightclick = "Clique Direito" +togainxp = "para ganhar esta experiencia" +xporb = "Orbe de Experiencia" # skill [skill] [skill.agility] - name = "Agilidade" - icon = "⇉" - description = "Agilidade e a capacidade de se mover rapida e fluidamente perante obstaculos." +name = "Agilidade" +icon = "⇉" +description = "Agilidade e a capacidade de se mover rapida e fluidamente perante obstaculos." [skill.architect] - name = "Arquiteto" - icon = "⬧" - description = "As estruturas sao os blocos de construcao do mundo. A realidade esta nas tuas maos, tua para controlar." +name = "Arquiteto" +icon = "⬧" +description = "As estruturas sao os blocos de construcao do mundo. A realidade esta nas tuas maos, tua para controlar." [skill.axes] - name = "Machados" - icon = "🪓" - description1 = "Porque cortar arvores, quando podes cortar " - description2 = "coisas" - description3 = "em vez disso, o mesmo resultado final!" +name = "Machados" +icon = "🪓" +description1 = "Porque cortar arvores, quando podes cortar " +description2 = "coisas" +description3 = "em vez disso, o mesmo resultado final!" [skill.brewing] - name = "Alquimia" - icon = "❦" - description = "Bolha Dupla, Bolha Tripla, Bolha Quadrupla - ainda nao consigo colocar esta pocao num caldeirao" +name = "Alquimia" +icon = "❦" +description = "Bolha Dupla, Bolha Tripla, Bolha Quadrupla - ainda nao consigo colocar esta pocao num caldeirao" [skill.blocking] - name = "Bloqueio" - icon = "🛡" - description = "Paus e pedras nao vao partir os teus ossos, mas um escudo sim." +name = "Bloqueio" +icon = "🛡" +description = "Paus e pedras nao vao partir os teus ossos, mas um escudo sim." [skill.crafting] - name = "Fabrico" - icon = "⌂" - description = "Sem mais pecas para colocar, porque nao fazer outra?" +name = "Fabrico" +icon = "⌂" +description = "Sem mais pecas para colocar, porque nao fazer outra?" [skill.discovery] - name = "Descoberta" - icon = "⚛" - description = "A medida que a tua percepcao se expande, a tua mente desvenda aquilo que nao conhecias." +name = "Descoberta" +icon = "⚛" +description = "A medida que a tua percepcao se expande, a tua mente desvenda aquilo que nao conhecias." [skill.enchanting] - name = "Encantamento" - icon = "✠" - description = "De que estas a falar? Profecias, visoes, tretas supersticiosas?" +name = "Encantamento" +icon = "✠" +description = "De que estas a falar? Profecias, visoes, tretas supersticiosas?" [skill.excavation] - name = "Escavacao" - icon = "ᛳ" - description = "Cava, cava o buraco..." +name = "Escavacao" +icon = "ᛳ" +description = "Cava, cava o buraco..." [skill.herbalism] - name = "Herbalismo" - icon = "⚘" - description = "Nao encontro plantas nenhumas, mas encontro sementes e - isso e... Erva?" +name = "Herbalismo" +icon = "⚘" +description = "Nao encontro plantas nenhumas, mas encontro sementes e - isso e... Erva?" [skill.hunter] - name = "Cacador" - icon = "☠" - description = "A caca e sobre a jornada, nao o resultado." +name = "Cacador" +icon = "☠" +description = "A caca e sobre a jornada, nao o resultado." [skill.nether] - name = "Nether" - icon = "₪" - description = "Das profundezas do proprio Nether." +name = "Nether" +icon = "₪" +description = "Das profundezas do proprio Nether." [skill.pickaxe] - name = "Picareta" - icon = "⛏" - description = "Os anoes sao os mineiros, mas aprendi uma coisa ou duas no meu tempo. SOU SUECO" +name = "Picareta" +icon = "⛏" +description = "Os anoes sao os mineiros, mas aprendi uma coisa ou duas no meu tempo. SOU SUECO" [skill.ranged] - name = "Distancia" - icon = "🏹" - description = "A distancia e a chave para a vitoria, e a chave para a sobrevivencia." +name = "Distancia" +icon = "🏹" +description = "A distancia e a chave para a vitoria, e a chave para a sobrevivencia." [skill.rift] - name = "Fenda" - icon = "❍" - description = "A Fenda e um arnes caustico, mas tu dominaste o arnes." +name = "Fenda" +icon = "❍" +description = "A Fenda e um arnes caustico, mas tu dominaste o arnes." [skill.seaborne] - name = "Maritimo" - icon = "🎣" - description = "Com esta habilidade, podes dominar as maravilhas da agua." +name = "Maritimo" +icon = "🎣" +description = "Com esta habilidade, podes dominar as maravilhas da agua." [skill.stealth] - name = "Furtividade" - icon = "☯" - description = "A arte do invisivel. Caminha nas sombras." +name = "Furtividade" +icon = "☯" +description = "A arte do invisivel. Caminha nas sombras." [skill.swords] - name = "Espadas" - icon = "⚔" - description = "Pelo poder de GreyStone!" +name = "Espadas" +icon = "⚔" +description = "Pelo poder de GreyStone!" [skill.taming] - name = "Domesticacao" - icon = "♥" - description = "Os papagaios e as abelhas... e tu?" +name = "Domesticacao" +icon = "♥" +description = "Os papagaios e as abelhas... e tu?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "O sangue flui pelas veias do universo. Constrangido pelas tuas maos." +name = "TragOul" +icon = "🗡" +description = "O sangue flui pelas veias do universo. Constrangido pelas tuas maos." [skill.chronos] - name = "Cronos" - icon = "🕒" - description = "Da corda ao Relogio do universo, experimenta o fluxo. Parte o relogio, torna-te nele." +name = "Cronos" +icon = "🕒" +description = "Da corda ao Relogio do universo, experimenta o fluxo. Parte o relogio, torna-te nele." [skill.unarmed] - name = "Desarmado" - icon = "»" - description = "Sem arma nao significa sem forca." +name = "Desarmado" +icon = "»" +description = "Sem arma nao significa sem forca." # agility [agility] [agility.armor_up] - name = "Armadura Crescente" - description = "Ganha mais armadura quanto mais tempo correres!" - lore1 = "Armadura Maxima" - lore2 = "Tempo de Armadura" - lore = ["Armadura Maxima", "Tempo de Armadura"] +name = "Armadura Crescente" +description = "Ganha mais armadura quanto mais tempo correres!" +lore1 = "Armadura Maxima" +lore2 = "Tempo de Armadura" +lore = ["Armadura Maxima", "Tempo de Armadura"] [agility.ladder_slide] - name = "Deslizar na Escada" - description = "Sobe e desce escadas muito mais rapido em ambas as direcoes." - lore1 = "Multiplicador de velocidade na escada" - lore2 = "Velocidade de descida rapida" - lore = ["Multiplicador de velocidade na escada", "Velocidade de descida rapida"] +name = "Deslizar na Escada" +description = "Sobe e desce escadas muito mais rapido em ambas as direcoes." +lore1 = "Multiplicador de velocidade na escada" +lore2 = "Velocidade de descida rapida" +lore = ["Multiplicador de velocidade na escada", "Velocidade de descida rapida"] [agility.super_jump] - name = "Super Salto" - description = "Vantagem de Altura Excecional." - lore1 = "Altura Maxima de Salto" - lore2 = "Agachar + Saltar para Super Salto!" - lore = ["Altura Maxima de Salto", "Agachar + Saltar para Super Salto!"] +name = "Super Salto" +description = "Vantagem de Altura Excecional." +lore1 = "Altura Maxima de Salto" +lore2 = "Agachar + Saltar para Super Salto!" +lore = ["Altura Maxima de Salto", "Agachar + Saltar para Super Salto!"] [agility.wall_jump] - name = "Salto na Parede" - description = "Segura shift enquanto no ar junto a uma parede para te agarrares e saltares!" - lore1 = "Saltos Maximos" - lore2 = "Altura do Salto" - lore = ["Saltos Maximos", "Altura do Salto"] +name = "Salto na Parede" +description = "Segura shift enquanto no ar junto a uma parede para te agarrares e saltares!" +lore1 = "Saltos Maximos" +lore2 = "Altura do Salto" +lore = ["Saltos Maximos", "Altura do Salto"] [agility.wind_up] - name = "Aceleracao" - description = "Fica mais rapido quanto mais tempo correres!" - lore1 = "Velocidade Maxima" - lore2 = "Tempo de Aceleracao" - lore = ["Velocidade Maxima", "Tempo de Aceleracao"] +name = "Aceleracao" +description = "Fica mais rapido quanto mais tempo correres!" +lore1 = "Velocidade Maxima" +lore2 = "Tempo de Aceleracao" +lore = ["Velocidade Maxima", "Tempo de Aceleracao"] # architect [architect] [architect.elevator] - name = "Elevador" - description = "Permite-te construir um elevador para teletransportar verticalmente com rapidez!" - lore1 = "Desbloqueia receita do elevador: X=LA, Y=PEROLA DO ENDER" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Desbloqueia receita do elevador: X=LA, Y=PEROLA DO ENDER", "XXX", "XYX", "XXX"] +name = "Elevador" +description = "Permite-te construir um elevador para teletransportar verticalmente com rapidez!" +lore1 = "Desbloqueia receita do elevador: X=LA, Y=PEROLA DO ENDER" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Desbloqueia receita do elevador: X=LA, Y=PEROLA DO ENDER", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Fundacao Magica" - description = "Permite-te agachar e colocar uma fundacao temporaria debaixo de ti!" - lore1 = "Cria magicamente: " - lore2 = "Blocos debaixo de ti!" - lore = ["Cria magicamente: ", "Blocos debaixo de ti!"] +name = "Fundacao Magica" +description = "Permite-te agachar e colocar uma fundacao temporaria debaixo de ti!" +lore1 = "Cria magicamente: " +lore2 = "Blocos debaixo de ti!" +lore = ["Cria magicamente: ", "Blocos debaixo de ti!"] [architect.glass] - name = "Vidro com Toque de Seda" - description = "Permite-te essencialmente evitar a perda de blocos de vidro quando os partes com a mao vazia!" - lore1 = "As tuas maos ganham toque de seda para Vidro" - lore = ["As tuas maos ganham toque de seda para Vidro"] +name = "Vidro com Toque de Seda" +description = "Permite-te essencialmente evitar a perda de blocos de vidro quando os partes com a mao vazia!" +lore1 = "As tuas maos ganham toque de seda para Vidro" +lore = ["As tuas maos ganham toque de seda para Vidro"] [architect.wireless_redstone] - name = "Controlo Remoto de Redstone" - description = "Permite-te usar uma tocha de redstone para alternar redstone, remotamente!" - lore1 = "Alvo + Tocha de Redstone + Perola do Ender = 1 Controlo Remoto de Redstone" - lore = ["Alvo + Tocha de Redstone + Perola do Ender = 1 Controlo Remoto de Redstone"] +name = "Controlo Remoto de Redstone" +description = "Permite-te usar uma tocha de redstone para alternar redstone, remotamente!" +lore1 = "Alvo + Tocha de Redstone + Perola do Ender = 1 Controlo Remoto de Redstone" +lore = ["Alvo + Tocha de Redstone + Perola do Ender = 1 Controlo Remoto de Redstone"] [architect.placement] - name = "Varinha de Construtor" - description = "Permite-te colocar multiplos blocos de uma vez. Para ativar, agacha-te e segura um bloco que corresponda ao bloco para o qual estas a olhar e coloca! Tem em conta que podes precisar de te mover um pouco para acionar a delimitacao!" - lore1 = "Precisas de" - lore2 = "blocos na tua mao para colocar isto" - lore3 = "Uma Varinha de Construtor de Material" - lore = ["Precisas de", "blocos na tua mao para colocar isto", "Uma Varinha de Construtor de Material"] +name = "Varinha de Construtor" +description = "Permite-te colocar multiplos blocos de uma vez. Para ativar, agacha-te e segura um bloco que corresponda ao bloco para o qual estas a olhar e coloca! Tem em conta que podes precisar de te mover um pouco para acionar a delimitacao!" +lore1 = "Precisas de" +lore2 = "blocos na tua mao para colocar isto" +lore3 = "Uma Varinha de Construtor de Material" +lore = ["Precisas de", "blocos na tua mao para colocar isto", "Uma Varinha de Construtor de Material"] # axe [axe] [axe.chop] - name = "Corte de Machado" - description = "Corta arvores clicando com o botao direito no tronco base!" - lore1 = "Blocos Por Corte" - lore2 = "Tempo de Espera do Corte" - lore3 = "Desgaste da Ferramenta" - lore = ["Blocos Por Corte", "Tempo de Espera do Corte", "Desgaste da Ferramenta"] +name = "Corte de Machado" +description = "Corta arvores clicando com o botao direito no tronco base!" +lore1 = "Blocos Por Corte" +lore2 = "Tempo de Espera do Corte" +lore3 = "Desgaste da Ferramenta" +lore = ["Blocos Por Corte", "Tempo de Espera do Corte", "Desgaste da Ferramenta"] [axe.log_swap] - name = "Troca de Troncos da Lucy" - description = "Muda o tipo de troncos numa Mesa de Fabrico!" - lore1 = "8 Troncos de qualquer tipo + 1 muda = 8 troncos do tipo da muda" - lore = ["8 Troncos de qualquer tipo + 1 muda = 8 troncos do tipo da muda"] +name = "Troca de Troncos da Lucy" +description = "Muda o tipo de troncos numa Mesa de Fabrico!" +lore1 = "8 Troncos de qualquer tipo + 1 muda = 8 troncos do tipo da muda" +lore = ["8 Troncos de qualquer tipo + 1 muda = 8 troncos do tipo da muda"] [axe.drop_to_inventory] - name = "Machado Direto-Para-Inventario" +name = "Machado Direto-Para-Inventario" [axe.ground_smash] - name = "Esmagar o Chao com Machado" - description = "Salta, depois agacha-te e esmaga todos os inimigos proximos." - lore1 = "Dano" - lore2 = "Raio de Blocos" - lore3 = "Forca" - lore4 = "Tempo de Espera do Esmagar" - lore = ["Dano", "Raio de Blocos", "Forca", "Tempo de Espera do Esmagar"] +name = "Esmagar o Chao com Machado" +description = "Salta, depois agacha-te e esmaga todos os inimigos proximos." +lore1 = "Dano" +lore2 = "Raio de Blocos" +lore3 = "Forca" +lore4 = "Tempo de Espera do Esmagar" +lore = ["Dano", "Raio de Blocos", "Forca", "Tempo de Espera do Esmagar"] [axe.leaf_miner] - name = "Mineiro de Folhas" - description = "Permite-te partir folhas em massa de uma so vez!" - lore1 = "Agacha e minera FOLHAS" - lore2 = "alcance da mineracao de folhas" - lore3 = "Nao recebes os drops das folhas (Prevencao de Exploits)" - lore = ["Agacha e minera FOLHAS", "alcance da mineracao de folhas", "Nao recebes os drops das folhas (Prevencao de Exploits)"] +name = "Mineiro de Folhas" +description = "Permite-te partir folhas em massa de uma so vez!" +lore1 = "Agacha e minera FOLHAS" +lore2 = "alcance da mineracao de folhas" +lore3 = "Nao recebes os drops das folhas (Prevencao de Exploits)" +lore = ["Agacha e minera FOLHAS", "alcance da mineracao de folhas", "Nao recebes os drops das folhas (Prevencao de Exploits)"] [axe.wood_miner] - name = "Mineiro de Madeira" - description = "Permite-te partir madeira em massa de uma so vez!" - lore1 = "Agacha e minera MADEIRA/TRONCOS (Nao Tabuas)" - lore2 = "alcance da mineracao de madeira" - lore3 = "Funciona com Direto-Para-Inventario" - lore = ["Agacha e minera MADEIRA/TRONCOS (Nao Tabuas)", "alcance da mineracao de madeira", "Funciona com Direto-Para-Inventario"] +name = "Mineiro de Madeira" +description = "Permite-te partir madeira em massa de uma so vez!" +lore1 = "Agacha e minera MADEIRA/TRONCOS (Nao Tabuas)" +lore2 = "alcance da mineracao de madeira" +lore3 = "Funciona com Direto-Para-Inventario" +lore = ["Agacha e minera MADEIRA/TRONCOS (Nao Tabuas)", "alcance da mineracao de madeira", "Funciona com Direto-Para-Inventario"] # brewing [brewing] [brewing.lingering] - name = "Pocao Prolongada" - description = "Pocoes fabricadas duram mais tempo!" - lore1 = "Duracao" - lore2 = "Duracao" - lore = ["Duracao", "Duracao"] +name = "Pocao Prolongada" +description = "Pocoes fabricadas duram mais tempo!" +lore1 = "Duracao" +lore2 = "Duracao" +lore = ["Duracao", "Duracao"] [brewing.super_heated] - name = "Pocao Super Aquecida" - description = "Suportes de pocoes funcionam mais rapido quanto mais quentes estiverem." - lore1 = "Por Bloco de Fogo a Tocar" - lore2 = "Por Bloco de Lava a Tocar" - lore = ["Por Bloco de Fogo a Tocar", "Por Bloco de Lava a Tocar"] +name = "Pocao Super Aquecida" +description = "Suportes de pocoes funcionam mais rapido quanto mais quentes estiverem." +lore1 = "Por Bloco de Fogo a Tocar" +lore2 = "Por Bloco de Lava a Tocar" +lore = ["Por Bloco de Fogo a Tocar", "Por Bloco de Lava a Tocar"] [brewing.darkness] - name = "Escuridao Engarrafada" - description = "Nao sei porque precisas disto, mas aqui tens!" - lore1 = "Pocao de Visao Noturna + Betao Preto = Pocao de Escuridao (30 segundos)" - lore2 = "Nota que isto impede o utilizador de correr!" - lore = ["Pocao de Visao Noturna + Betao Preto = Pocao de Escuridao (30 segundos)", "Nota que isto impede o utilizador de correr!"] +name = "Escuridao Engarrafada" +description = "Nao sei porque precisas disto, mas aqui tens!" +lore1 = "Pocao de Visao Noturna + Betao Preto = Pocao de Escuridao (30 segundos)" +lore2 = "Nota que isto impede o utilizador de correr!" +lore = ["Pocao de Visao Noturna + Betao Preto = Pocao de Escuridao (30 segundos)", "Nota que isto impede o utilizador de correr!"] [brewing.haste] - name = "Pressa Engarrafada" - description = "Quando a Eficiencia nao e suficiente" - lore1 = "Pocao de Velocidade + Fragmento de Ametista = Pocao de Pressa (60 segundos)" - lore2 = "Pocao de Velocidade + Bloco de Ametista = Pocao de Pressa-2 (30 segundos)" - lore = ["Pocao de Velocidade + Fragmento de Ametista = Pocao de Pressa (60 segundos)", "Pocao de Velocidade + Bloco de Ametista = Pocao de Pressa-2 (30 segundos)"] +name = "Pressa Engarrafada" +description = "Quando a Eficiencia nao e suficiente" +lore1 = "Pocao de Velocidade + Fragmento de Ametista = Pocao de Pressa (60 segundos)" +lore2 = "Pocao de Velocidade + Bloco de Ametista = Pocao de Pressa-2 (30 segundos)" +lore = ["Pocao de Velocidade + Fragmento de Ametista = Pocao de Pressa (60 segundos)", "Pocao de Velocidade + Bloco de Ametista = Pocao de Pressa-2 (30 segundos)"] [brewing.absorption] - name = "Absorcao Engarrafada" - description = "Endurece o corpo!" - lore1 = "Cura Instantanea + Quartzo = Pocao de Absorcao (60 segundos)" - lore2 = "Cura Instantanea + Bloco de Quartzo = Pocao de Absorcao-2 (30 segundos)" - lore = ["Cura Instantanea + Quartzo = Pocao de Absorcao (60 segundos)", "Cura Instantanea + Bloco de Quartzo = Pocao de Absorcao-2 (30 segundos)"] +name = "Absorcao Engarrafada" +description = "Endurece o corpo!" +lore1 = "Cura Instantanea + Quartzo = Pocao de Absorcao (60 segundos)" +lore2 = "Cura Instantanea + Bloco de Quartzo = Pocao de Absorcao-2 (30 segundos)" +lore = ["Cura Instantanea + Quartzo = Pocao de Absorcao (60 segundos)", "Cura Instantanea + Bloco de Quartzo = Pocao de Absorcao-2 (30 segundos)"] [brewing.fatigue] - name = "Fadiga Engarrafada" - description = "Enfraquece o corpo!" - lore1 = "Pocao de Fraqueza + Bola de Slime = Pocao de Fadiga (30 segundos)" - lore2 = "Pocao de Fraqueza + Bloco de Slime = Pocao de Fadiga-2 (15 segundos)" - lore = ["Pocao de Fraqueza + Bola de Slime = Pocao de Fadiga (30 segundos)", "Pocao de Fraqueza + Bloco de Slime = Pocao de Fadiga-2 (15 segundos)"] +name = "Fadiga Engarrafada" +description = "Enfraquece o corpo!" +lore1 = "Pocao de Fraqueza + Bola de Slime = Pocao de Fadiga (30 segundos)" +lore2 = "Pocao de Fraqueza + Bloco de Slime = Pocao de Fadiga-2 (15 segundos)" +lore = ["Pocao de Fraqueza + Bola de Slime = Pocao de Fadiga (30 segundos)", "Pocao de Fraqueza + Bloco de Slime = Pocao de Fadiga-2 (15 segundos)"] [brewing.hunger] - name = "Fome Engarrafada" - description = "Alimenta o Insaciavel!" - lore1 = "Pocao Estranha + Carne Podre = Pocao de Fome (30 segundos)" - lore2 = "Pocao de Fraqueza + Carne Podre = Pocao de Fome-3 (15 segundos)" - lore = ["Pocao Estranha + Carne Podre = Pocao de Fome (30 segundos)", "Pocao de Fraqueza + Carne Podre = Pocao de Fome-3 (15 segundos)"] +name = "Fome Engarrafada" +description = "Alimenta o Insaciavel!" +lore1 = "Pocao Estranha + Carne Podre = Pocao de Fome (30 segundos)" +lore2 = "Pocao de Fraqueza + Carne Podre = Pocao de Fome-3 (15 segundos)" +lore = ["Pocao Estranha + Carne Podre = Pocao de Fome (30 segundos)", "Pocao de Fraqueza + Carne Podre = Pocao de Fome-3 (15 segundos)"] [brewing.nausea] - name = "Nausea Engarrafada" - description = "Voce me da enjoo!" - lore1 = "Pocao Estranha + Cogumelo Castanho = Pocao de Nausea (16 segundos)" - lore2 = "Pocao Estranha + Fungo Carmesim = Pocao de Nausea-2 (8 segundos)" - lore = ["Pocao Estranha + Cogumelo Castanho = Pocao de Nausea (16 segundos)", "Pocao Estranha + Fungo Carmesim = Pocao de Nausea-2 (8 segundos)"] +name = "Nausea Engarrafada" +description = "Voce me da enjoo!" +lore1 = "Pocao Estranha + Cogumelo Castanho = Pocao de Nausea (16 segundos)" +lore2 = "Pocao Estranha + Fungo Carmesim = Pocao de Nausea-2 (8 segundos)" +lore = ["Pocao Estranha + Cogumelo Castanho = Pocao de Nausea (16 segundos)", "Pocao Estranha + Fungo Carmesim = Pocao de Nausea-2 (8 segundos)"] [brewing.blindness] - name = "Cegueira Engarrafada" - description = "Es uma pessoa horrivel..." - lore1 = "Pocao Estranha + Saco de Tinta = Pocao de Cegueira (30 segundos)" - lore2 = "Pocao Estranha + Saco de Tinta Brilhante = Pocao de Cegueira-2 (15 segundos)" - lore = ["Pocao Estranha + Saco de Tinta = Pocao de Cegueira (30 segundos)", "Pocao Estranha + Saco de Tinta Brilhante = Pocao de Cegueira-2 (15 segundos)"] +name = "Cegueira Engarrafada" +description = "Es uma pessoa horrivel..." +lore1 = "Pocao Estranha + Saco de Tinta = Pocao de Cegueira (30 segundos)" +lore2 = "Pocao Estranha + Saco de Tinta Brilhante = Pocao de Cegueira-2 (15 segundos)" +lore = ["Pocao Estranha + Saco de Tinta = Pocao de Cegueira (30 segundos)", "Pocao Estranha + Saco de Tinta Brilhante = Pocao de Cegueira-2 (15 segundos)"] [brewing.resistance] - name = "Resistencia Engarrafada" - description = "Fortificacao no seu melhor!" - lore1 = "Pocao Estranha + Lingote de Ferro = Pocao de Resistencia (60 segundos)" - lore2 = "Pocao Estranha + Bloco de Ferro = Pocao de Resistencia-2 (30 segundos)" - lore = ["Pocao Estranha + Lingote de Ferro = Pocao de Resistencia (60 segundos)", "Pocao Estranha + Bloco de Ferro = Pocao de Resistencia-2 (30 segundos)"] +name = "Resistencia Engarrafada" +description = "Fortificacao no seu melhor!" +lore1 = "Pocao Estranha + Lingote de Ferro = Pocao de Resistencia (60 segundos)" +lore2 = "Pocao Estranha + Bloco de Ferro = Pocao de Resistencia-2 (30 segundos)" +lore = ["Pocao Estranha + Lingote de Ferro = Pocao de Resistencia (60 segundos)", "Pocao Estranha + Bloco de Ferro = Pocao de Resistencia-2 (30 segundos)"] [brewing.health_boost] - name = "Vida Engarrafada" - description = "Quando a saude maxima nao e suficiente..." - lore1 = "Pocao de Cura Instantanea + Maca Dourada = Pocao de Aumento de Saude (120 segundos)" - lore2 = "Pocao de Cura Instantanea + Maca Dourada Encantada = Pocao de Aumento de Saude-2 (120 segundos)" - lore = ["Pocao de Cura Instantanea + Maca Dourada = Pocao de Aumento de Saude (120 segundos)", "Pocao de Cura Instantanea + Maca Dourada Encantada = Pocao de Aumento de Saude-2 (120 segundos)"] +name = "Vida Engarrafada" +description = "Quando a saude maxima nao e suficiente..." +lore1 = "Pocao de Cura Instantanea + Maca Dourada = Pocao de Aumento de Saude (120 segundos)" +lore2 = "Pocao de Cura Instantanea + Maca Dourada Encantada = Pocao de Aumento de Saude-2 (120 segundos)" +lore = ["Pocao de Cura Instantanea + Maca Dourada = Pocao de Aumento de Saude (120 segundos)", "Pocao de Cura Instantanea + Maca Dourada Encantada = Pocao de Aumento de Saude-2 (120 segundos)"] [brewing.decay] - name = "Decomposicao Engarrafada" - description = "Quem diria que detritos seriam tao uteis?" - lore1 = "Pocao de Fraqueza + Batata Venenosa = Pocao de Wither (16 segundos)" - lore2 = "Pocao de Fraqueza + Raizes Carmesim = Pocao de Wither-2 (8 segundos)" - lore = ["Pocao de Fraqueza + Batata Venenosa = Pocao de Wither (16 segundos)", "Pocao de Fraqueza + Raizes Carmesim = Pocao de Wither-2 (8 segundos)"] +name = "Decomposicao Engarrafada" +description = "Quem diria que detritos seriam tao uteis?" +lore1 = "Pocao de Fraqueza + Batata Venenosa = Pocao de Wither (16 segundos)" +lore2 = "Pocao de Fraqueza + Raizes Carmesim = Pocao de Wither-2 (8 segundos)" +lore = ["Pocao de Fraqueza + Batata Venenosa = Pocao de Wither (16 segundos)", "Pocao de Fraqueza + Raizes Carmesim = Pocao de Wither-2 (8 segundos)"] [brewing.saturation] - name = "Saturacao Engarrafada" - description = "Sabes que... Nem estou com fome..." - lore1 = "Pocao de Regeneracao + Batata Assada = Pocao de Saturacao" - lore2 = "Pocao de Regeneracao + Fardo de Feno = Pocao de Saturacao-2" - lore = ["Pocao de Regeneracao + Batata Assada = Pocao de Saturacao", "Pocao de Regeneracao + Fardo de Feno = Pocao de Saturacao-2"] +name = "Saturacao Engarrafada" +description = "Sabes que... Nem estou com fome..." +lore1 = "Pocao de Regeneracao + Batata Assada = Pocao de Saturacao" +lore2 = "Pocao de Regeneracao + Fardo de Feno = Pocao de Saturacao-2" +lore = ["Pocao de Regeneracao + Batata Assada = Pocao de Saturacao", "Pocao de Regeneracao + Fardo de Feno = Pocao de Saturacao-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Correntes de Mefistofeles" - description = "Permite-te fabricar Armadura de Malha" - lore1 = "A receita de fabrico e igual a qualquer outra, mas com pepitas de ferro em vez disso" - lore = ["A receita de fabrico e igual a qualquer outra, mas com pepitas de ferro em vez disso"] +name = "Correntes de Mefistofeles" +description = "Permite-te fabricar Armadura de Malha" +lore1 = "A receita de fabrico e igual a qualquer outra, mas com pepitas de ferro em vez disso" +lore = ["A receita de fabrico e igual a qualquer outra, mas com pepitas de ferro em vez disso"] [blocking.horse_armorer] - name = "Armadura de Cavalo Fabricavel" - description = "Permite-te fabricar Armadura de Cavalo" - lore1 = "Rodeia uma sela com o material que queres usar para fabricar a armadura" - lore = ["Rodeia uma sela com o material que queres usar para fabricar a armadura"] +name = "Armadura de Cavalo Fabricavel" +description = "Permite-te fabricar Armadura de Cavalo" +lore1 = "Rodeia uma sela com o material que queres usar para fabricar a armadura" +lore = ["Rodeia uma sela com o material que queres usar para fabricar a armadura"] [blocking.saddle_crafter] - name = "Sela Fabricavel" - description = "Fabrica uma Sela com Couro" - lore1 = "Receita: 5 Couro:" - lore = ["Receita: 5 Couro:"] +name = "Sela Fabricavel" +description = "Fabrica uma Sela com Couro" +lore1 = "Receita: 5 Couro:" +lore = ["Receita: 5 Couro:"] [blocking.multi_armor] - name = "Multi-Armadura" - description = "Liga Elytras a Armadura" - lore1 = "Esta e uma habilidade incrivel para viajar." - lore2 = "Funde e altera dinamicamente Armadura/Elytra em tempo real!" - lore3 = "Para fundir, clica com shift num item sobre outro no teu inventario." - lore4 = "Para desvincular Armadura, larga o item agachado e ele sera desmontado." - lore5 = "Se a tua MultiArmadura for destruida, perdes todos os itens nela." - lore6 = "Nao preciso de armadura, isso decepciona-me..." - lore = ["Esta e uma habilidade incrivel para viajar.", "Funde e altera dinamicamente Armadura/Elytra em tempo real!", "Para fundir, clica com shift num item sobre outro no teu inventario.", "Para desvincular Armadura, larga o item agachado e ele sera desmontado.", "Se a tua MultiArmadura for destruida, perdes todos os itens nela.", "Nao preciso de armadura, isso decepciona-me..."] +name = "Multi-Armadura" +description = "Liga Elytras a Armadura" +lore1 = "Esta e uma habilidade incrivel para viajar." +lore2 = "Funde e altera dinamicamente Armadura/Elytra em tempo real!" +lore3 = "Para fundir, clica com shift num item sobre outro no teu inventario." +lore4 = "Para desvincular Armadura, larga o item agachado e ele sera desmontado." +lore5 = "Se a tua MultiArmadura for destruida, perdes todos os itens nela." +lore6 = "Nao preciso de armadura, isso decepciona-me..." +lore = ["Esta e uma habilidade incrivel para viajar.", "Funde e altera dinamicamente Armadura/Elytra em tempo real!", "Para fundir, clica com shift num item sobre outro no teu inventario.", "Para desvincular Armadura, larga o item agachado e ele sera desmontado.", "Se a tua MultiArmadura for destruida, perdes todos os itens nela.", "Nao preciso de armadura, isso decepciona-me..."] # crafting [crafting] [crafting.deconstruction] - name = "Desconstrucao" - description = "Desconstroi blocos e itens em componentes base recuperaveis!" - lore1 = "Larga qualquer item no chao." - lore2 = "Depois, agacha-te e clica direito com Tesouras" - lore = ["Larga qualquer item no chao.", "Depois, agacha-te e clica direito com Tesouras"] +name = "Desconstrucao" +description = "Desconstroi blocos e itens em componentes base recuperaveis!" +lore1 = "Larga qualquer item no chao." +lore2 = "Depois, agacha-te e clica direito com Tesouras" +lore = ["Larga qualquer item no chao.", "Depois, agacha-te e clica direito com Tesouras"] [crafting.xp] - name = "XP de Fabrico" - description = "Ganha XP passivo ao fabricar" - lore1 = "Ganha XP ao fabricar" - lore = ["Ganha XP ao fabricar"] +name = "XP de Fabrico" +description = "Ganha XP passivo ao fabricar" +lore1 = "Ganha XP ao fabricar" +lore = ["Ganha XP ao fabricar"] [crafting.reconstruction] - name = "Reconstrucao de Minerio" - description = "Refabrica minerios a partir dos seus componentes base!" - lore1 = "8 dos Drops e 1 Hospedeiro = 1 Minerio (sem forma)" - lore2 = "Os drops devem ser fundidos (se aplicavel)" - lore3 = "Nao inclui: Sucatas, Quartzo, e Esmeraldas etc..." - lore4 = "Hospedeiro = Revestimento. ex: Pedra, Netherrack, Ardosia Profunda" - lore = ["8 dos Drops e 1 Hospedeiro = 1 Minerio (sem forma)", "Os drops devem ser fundidos (se aplicavel)", "Nao inclui: Sucatas, Quartzo, e Esmeraldas etc...", "Hospedeiro = Revestimento. ex: Pedra, Netherrack, Ardosia Profunda"] +name = "Reconstrucao de Minerio" +description = "Refabrica minerios a partir dos seus componentes base!" +lore1 = "8 dos Drops e 1 Hospedeiro = 1 Minerio (sem forma)" +lore2 = "Os drops devem ser fundidos (se aplicavel)" +lore3 = "Nao inclui: Sucatas, Quartzo, e Esmeraldas etc..." +lore4 = "Hospedeiro = Revestimento. ex: Pedra, Netherrack, Ardosia Profunda" +lore = ["8 dos Drops e 1 Hospedeiro = 1 Minerio (sem forma)", "Os drops devem ser fundidos (se aplicavel)", "Nao inclui: Sucatas, Quartzo, e Esmeraldas etc...", "Hospedeiro = Revestimento. ex: Pedra, Netherrack, Ardosia Profunda"] [crafting.leather] - name = "Couro Fabricavel" - description = "Fabrica Couro a partir de Carne Podre" - lore1 = "Basta atirar (carne podre) para a fogueira!" - lore = ["Basta atirar (carne podre) para a fogueira!"] +name = "Couro Fabricavel" +description = "Fabrica Couro a partir de Carne Podre" +lore1 = "Basta atirar (carne podre) para a fogueira!" +lore = ["Basta atirar (carne podre) para a fogueira!"] [crafting.backpacks] - name = "Mochilas de Boutilier!" - description = "Isto simplesmente traz o Pacote do Mojang para o jogo!" - lore1 = "Precisas de estar em Sobrevivencia para usar isto" - lore2 = "XLX : Couro, Trela, Couro" - lore3 = "XSX : Couro, Caixa de Barril, Couro" - lore4 = "XCX : Couro, Bau, Couro" - lore = ["Precisas de estar em Sobrevivencia para usar isto", "XLX : Couro, Trela, Couro", "XSX : Couro, Caixa de Barril, Couro", "XCX : Couro, Bau, Couro"] +name = "Mochilas de Boutilier!" +description = "Isto simplesmente traz o Pacote do Mojang para o jogo!" +lore1 = "Precisas de estar em Sobrevivencia para usar isto" +lore2 = "XLX : Couro, Trela, Couro" +lore3 = "XSX : Couro, Caixa de Barril, Couro" +lore4 = "XCX : Couro, Bau, Couro" +lore = ["Precisas de estar em Sobrevivencia para usar isto", "XLX : Couro, Trela, Couro", "XSX : Couro, Caixa de Barril, Couro", "XCX : Couro, Bau, Couro"] [crafting.stations] - name = "Bancadas Portateis!" - description = "Usa uma bancada na palma da tua mao!" - lore2 = "QUAISQUER ITENS QUE ESQUECAS NA BANCADA AO FECHAR SAO PERDIDOS PARA SEMPRE!" - lore3 = "Bancadas validas: Bigorna, Fabrico, Rebolo, Cartografia, Corta-Pedras, Tear" - lore = ["QUAISQUER ITENS QUE ESQUECAS NA BANCADA AO FECHAR SAO PERDIDOS PARA SEMPRE!", "Bancadas validas: Bigorna, Fabrico, Rebolo, Cartografia, Corta-Pedras, Tear"] +name = "Bancadas Portateis!" +description = "Usa uma bancada na palma da tua mao!" +lore2 = "QUAISQUER ITENS QUE ESQUECAS NA BANCADA AO FECHAR SAO PERDIDOS PARA SEMPRE!" +lore3 = "Bancadas validas: Bigorna, Fabrico, Rebolo, Cartografia, Corta-Pedras, Tear" +lore = ["QUAISQUER ITENS QUE ESQUECAS NA BANCADA AO FECHAR SAO PERDIDOS PARA SEMPRE!", "Bancadas validas: Bigorna, Fabrico, Rebolo, Cartografia, Corta-Pedras, Tear"] [crafting.skulls] - name = "Cranios Fabricaveis!" - description = "Usando Materiais podes Fabricar Cranios de Mobs!" - lore1 = "Rodeia um Bloco de Osso com o seguinte para obter um cranio:" - lore2 = "Zombie: Carne Podre" - lore3 = "Esqueleto: Osso" - lore4 = "Creeper: Polvora" - lore5 = "Wither: Tijolo do Nether" - lore6 = "Dragao: Sopro do Dragao" - lore = ["Rodeia um Bloco de Osso com o seguinte para obter um cranio:", "Zombie: Carne Podre", "Esqueleto: Osso", "Creeper: Polvora", "Wither: Tijolo do Nether", "Dragao: Sopro do Dragao"] +name = "Cranios Fabricaveis!" +description = "Usando Materiais podes Fabricar Cranios de Mobs!" +lore1 = "Rodeia um Bloco de Osso com o seguinte para obter um cranio:" +lore2 = "Zombie: Carne Podre" +lore3 = "Esqueleto: Osso" +lore4 = "Creeper: Polvora" +lore5 = "Wither: Tijolo do Nether" +lore6 = "Dragao: Sopro do Dragao" +lore = ["Rodeia um Bloco de Osso com o seguinte para obter um cranio:", "Zombie: Carne Podre", "Esqueleto: Osso", "Creeper: Polvora", "Wither: Tijolo do Nether", "Dragao: Sopro do Dragao"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Tempo Numa Garrafa" - description = "Carrega uma garrafa temporal que armazena tempo e gasta-o para acelerar blocos temporizados, cultivos e entidades envelheciveis como animais bebes. Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro." - lore1 = "Segundos armazenados carregados por tick" - lore2 = "Aceleracao temporal por segundo armazenado" - lore3 = "Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro" - lore = ["Segundos armazenados carregados por tick", "Aceleracao temporal por segundo armazenado", "Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro"] +name = "Tempo Numa Garrafa" +description = "Carrega uma garrafa temporal que armazena tempo e gasta-o para acelerar blocos temporizados, cultivos e entidades envelheciveis como animais bebes. Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro." +lore1 = "Segundos armazenados carregados por tick" +lore2 = "Aceleracao temporal por segundo armazenado" +lore3 = "Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro" +lore = ["Segundos armazenados carregados por tick", "Aceleracao temporal por segundo armazenado", "Receita (Sem Forma): Pocao de Rapidez + Relogio + Garrafa de Vidro"] [chronos.aberrant_touch] - name = "Toque Aberrante" - description = "Ataques corpo a corpo aplicam lentidao acumulativa a custo de fome, com limites estritos em PvP, e enraizam alvos a 5 acumulacoes." - lore1 = "Ataques corpo a corpo aplicam lentidao acumulativa" - lore2 = "Limite de duracao de lentidao em PvE" - lore3 = "Limite de amplificador de lentidao em PvP" - lore = ["Ataques corpo a corpo aplicam lentidao acumulativa", "Limite de duracao de lentidao em PvE", "Limite de amplificador de lentidao em PvP"] +name = "Toque Aberrante" +description = "Ataques corpo a corpo aplicam lentidao acumulativa a custo de fome, com limites estritos em PvP, e enraizam alvos a 5 acumulacoes." +lore1 = "Ataques corpo a corpo aplicam lentidao acumulativa" +lore2 = "Limite de duracao de lentidao em PvE" +lore3 = "Limite de amplificador de lentidao em PvP" +lore = ["Ataques corpo a corpo aplicam lentidao acumulativa", "Limite de duracao de lentidao em PvE", "Limite de amplificador de lentidao em PvP"] [chronos.instant_recall] - name = "Retorno Instantaneo" - description = "Clica esquerdo ou direito com um relogio na mao para retroceder a um registo recente com saude e fome restauradas." - lore1 = "Duracao do retrocesso" - lore2 = "Tempo de espera" - lore3 = "Sem reversao de inventario" - lore = ["Duracao do retrocesso", "Tempo de espera", "Sem reversao de inventario"] +name = "Retorno Instantaneo" +description = "Clica esquerdo ou direito com um relogio na mao para retroceder a um registo recente com saude e fome restauradas." +lore1 = "Duracao do retrocesso" +lore2 = "Tempo de espera" +lore3 = "Sem reversao de inventario" +lore = ["Duracao do retrocesso", "Tempo de espera", "Sem reversao de inventario"] [chronos.time_bomb] - name = "Bomba Temporal" - description = "Lanca uma bomba crono fabricada que cria um campo temporal, abranda entidades e congela projeteis." - lore1 = "Raio do campo temporal" - lore2 = "Duracao do campo temporal" - lore3 = "Tempo de espera da bomba" - lore4 = "Receita (Sem Forma): Relogio + Bola de Neve + Diamante + Areia" - lore = ["Raio do campo temporal", "Duracao do campo temporal", "Tempo de espera da bomba", "Receita (Sem Forma): Relogio + Bola de Neve + Diamante + Areia"] +name = "Bomba Temporal" +description = "Lanca uma bomba crono fabricada que cria um campo temporal, abranda entidades e congela projeteis." +lore1 = "Raio do campo temporal" +lore2 = "Duracao do campo temporal" +lore3 = "Tempo de espera da bomba" +lore4 = "Receita (Sem Forma): Relogio + Bola de Neve + Diamante + Areia" +lore = ["Raio do campo temporal", "Duracao do campo temporal", "Tempo de espera da bomba", "Receita (Sem Forma): Relogio + Bola de Neve + Diamante + Areia"] # discovery [discovery] [discovery.armor] - name = "Armadura do Mundo" - description = "Armadura passiva dependendo da dureza dos blocos proximos." - lore1 = "Armadura Passiva" - lore2 = "Baseada na dureza dos blocos proximos" - lore3 = "Forca da Armadura:" - lore = ["Armadura Passiva", "Baseada na dureza dos blocos proximos", "Forca da Armadura:"] +name = "Armadura do Mundo" +description = "Armadura passiva dependendo da dureza dos blocos proximos." +lore1 = "Armadura Passiva" +lore2 = "Baseada na dureza dos blocos proximos" +lore3 = "Forca da Armadura:" +lore = ["Armadura Passiva", "Baseada na dureza dos blocos proximos", "Forca da Armadura:"] [discovery.unity] - name = "Unidade Experimental" - description = "Recolher Orbes de Experiencia adiciona XP a habilidades aleatorias." - lore1 = "XP " - lore2 = "Por Orbe" - lore = ["XP ", "Por Orbe"] +name = "Unidade Experimental" +description = "Recolher Orbes de Experiencia adiciona XP a habilidades aleatorias." +lore1 = "XP " +lore2 = "Por Orbe" +lore = ["XP ", "Por Orbe"] [discovery.resist] - name = "Resistencia Experimental" - description = "Consome experiencia para mitigar dano apenas quando um golpe te deixaria abaixo de 5 coracoes ou te mataria." - lore0 = "Ativa apenas em saude critica (<= 5 coracoes) uma vez a cada 15 segundos" - lore1 = " Dano Reduzido" - lore2 = "experiencia drenada" - lore = ["Ativa apenas em saude critica (<= 5 coracoes) uma vez a cada 15 segundos", " Dano Reduzido", "experiencia drenada"] +name = "Resistencia Experimental" +description = "Consome experiencia para mitigar dano apenas quando um golpe te deixaria abaixo de 5 coracoes ou te mataria." +lore0 = "Ativa apenas em saude critica (<= 5 coracoes) uma vez a cada 15 segundos" +lore1 = " Dano Reduzido" +lore2 = "experiencia drenada" +lore = ["Ativa apenas em saude critica (<= 5 coracoes) uma vez a cada 15 segundos", " Dano Reduzido", "experiencia drenada"] [discovery.villager] - name = "Atracao de Aldeao" - description = "Permite-te obter melhores trocas com aldeoes!" - lore1 = "Isto consome XP por interacao com Aldeoes" - lore2 = "Chance por interacao de consumir XP e melhorar trocas" - lore3 = "drenagem de XP necessaria por Interacao" - lore = ["Isto consome XP por interacao com Aldeoes", "Chance por interacao de consumir XP e melhorar trocas", "drenagem de XP necessaria por Interacao"] +name = "Atracao de Aldeao" +description = "Permite-te obter melhores trocas com aldeoes!" +lore1 = "Isto consome XP por interacao com Aldeoes" +lore2 = "Chance por interacao de consumir XP e melhorar trocas" +lore3 = "drenagem de XP necessaria por Interacao" +lore = ["Isto consome XP por interacao com Aldeoes", "Chance por interacao de consumir XP e melhorar trocas", "drenagem de XP necessaria por Interacao"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Retorno de Lapis" - description = "Ao custo de mais 1 nivel de XP, tem chance de te dar lapis gratis em troca" - lore1 = "Por cada nivel, aumenta o custo de encantamento em 1, mas pode devolver ate 3 Lapis" - lore = ["Por cada nivel, aumenta o custo de encantamento em 1, mas pode devolver ate 3 Lapis"] +name = "Retorno de Lapis" +description = "Ao custo de mais 1 nivel de XP, tem chance de te dar lapis gratis em troca" +lore1 = "Por cada nivel, aumenta o custo de encantamento em 1, mas pode devolver ate 3 Lapis" +lore = ["Por cada nivel, aumenta o custo de encantamento em 1, mas pode devolver ate 3 Lapis"] [enchanting.quick_enchant] - name = "Encantamento Rapido" - description = "Encanta itens clicando livros de encantamento diretamente neles." - lore1 = "Niveis Maximos Combinados" - lore2 = "Nao e possivel encantar um item com mais de " - lore3 = "potencia" - lore = ["Niveis Maximos Combinados", "Nao e possivel encantar um item com mais de ", "potencia"] +name = "Encantamento Rapido" +description = "Encanta itens clicando livros de encantamento diretamente neles." +lore1 = "Niveis Maximos Combinados" +lore2 = "Nao e possivel encantar um item com mais de " +lore3 = "potencia" +lore = ["Niveis Maximos Combinados", "Nao e possivel encantar um item com mais de ", "potencia"] [enchanting.return] - name = "Retorno de XP" - description = "XP de encantamento e devolvido quando encantas um item." - lore1 = "Experiencia gasta tem chance de ser reembolsada quando encantas um item" - lore2 = "Experiencia por Encantamento" - lore = ["Experiencia gasta tem chance de ser reembolsada quando encantas um item", "Experiencia por Encantamento"] +name = "Retorno de XP" +description = "XP de encantamento e devolvido quando encantas um item." +lore1 = "Experiencia gasta tem chance de ser reembolsada quando encantas um item" +lore2 = "Experiencia por Encantamento" +lore = ["Experiencia gasta tem chance de ser reembolsada quando encantas um item", "Experiencia por Encantamento"] # excavation [excavation] [excavation.haste] - name = "Escavador Apressado" - description = "Isto acelera o processo de escavacao, com PRESSA!" - lore1 = "Ganha Pressa durante a escavacao" - lore2 = "x Niveis de pressa quando comecas a minerar QUALQUER bloco." - lore = ["Ganha Pressa durante a escavacao", "x Niveis de pressa quando comecas a minerar QUALQUER bloco."] +name = "Escavador Apressado" +description = "Isto acelera o processo de escavacao, com PRESSA!" +lore1 = "Ganha Pressa durante a escavacao" +lore2 = "x Niveis de pressa quando comecas a minerar QUALQUER bloco." +lore = ["Ganha Pressa durante a escavacao", "x Niveis de pressa quando comecas a minerar QUALQUER bloco."] [excavation.spelunker] - name = "Espeleologista Supervidente!" - description = "Ve Minerios com os teus olhos, mas atraves do chao!" - lore1 = "Minerio na mao secundaria, Bagas Luminosas na mao principal, e Agacha!" - lore2 = "Alcance de Blocos: " - lore3 = "Consome Baga Luminosa ao usar" - lore = ["Minerio na mao secundaria, Bagas Luminosas na mao principal, e Agacha!", "Alcance de Blocos: ", "Consome Baga Luminosa ao usar"] +name = "Espeleologista Supervidente!" +description = "Ve Minerios com os teus olhos, mas atraves do chao!" +lore1 = "Minerio na mao secundaria, Bagas Luminosas na mao principal, e Agacha!" +lore2 = "Alcance de Blocos: " +lore3 = "Consome Baga Luminosa ao usar" +lore = ["Minerio na mao secundaria, Bagas Luminosas na mao principal, e Agacha!", "Alcance de Blocos: ", "Consome Baga Luminosa ao usar"] [excavation.drop_to_inventory] - name = "Pa Direto-Para-Inventario" +name = "Pa Direto-Para-Inventario" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "O opulento canivete suico de Tackle" - lore1 = "Provavelmente a mais poderosa de muitas permite-te" - lore2 = "fundir e trocar ferramentas dinamicamente em tempo real, com base nas tuas necessidades." - lore3 = "Para fundir, clica com shift num item sobre outro no teu inventario." - lore4 = "Para desvincular ferramentas, larga o item agachado e ele sera desmontado." - lore5 = "Nao podes partir ferramentas neste canivete mas nao podes usar ferramentas partidas" - lore6 = "total de itens fundiveis." - lore7 = "Podes usar cinco ou seis ferramentas, ou apenas uma!" - lore = ["Provavelmente a mais poderosa de muitas permite-te", "fundir e trocar ferramentas dinamicamente em tempo real, com base nas tuas necessidades.", "Para fundir, clica com shift num item sobre outro no teu inventario.", "Para desvincular ferramentas, larga o item agachado e ele sera desmontado.", "Nao podes partir ferramentas neste canivete mas nao podes usar ferramentas partidas", "total de itens fundiveis.", "Podes usar cinco ou seis ferramentas, ou apenas uma!"] +name = "OMNI - T.O.O.L." +description = "O opulento canivete suico de Tackle" +lore1 = "Provavelmente a mais poderosa de muitas permite-te" +lore2 = "fundir e trocar ferramentas dinamicamente em tempo real, com base nas tuas necessidades." +lore3 = "Para fundir, clica com shift num item sobre outro no teu inventario." +lore4 = "Para desvincular ferramentas, larga o item agachado e ele sera desmontado." +lore5 = "Nao podes partir ferramentas neste canivete mas nao podes usar ferramentas partidas" +lore6 = "total de itens fundiveis." +lore7 = "Podes usar cinco ou seis ferramentas, ou apenas uma!" +lore = ["Provavelmente a mais poderosa de muitas permite-te", "fundir e trocar ferramentas dinamicamente em tempo real, com base nas tuas necessidades.", "Para fundir, clica com shift num item sobre outro no teu inventario.", "Para desvincular ferramentas, larga o item agachado e ele sera desmontado.", "Nao podes partir ferramentas neste canivete mas nao podes usar ferramentas partidas", "total de itens fundiveis.", "Podes usar cinco ou seis ferramentas, ou apenas uma!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Aura de Crescimento" - description = "Faz crescer a natureza a tua volta numa aura" - lore1 = "Raio de Blocos" - lore2 = "Forca da Aura de Crescimento" - lore3 = "Custo de Comida" - lore = ["Raio de Blocos", "Forca da Aura de Crescimento", "Custo de Comida"] +name = "Aura de Crescimento" +description = "Faz crescer a natureza a tua volta numa aura" +lore1 = "Raio de Blocos" +lore2 = "Forca da Aura de Crescimento" +lore3 = "Custo de Comida" +lore = ["Raio de Blocos", "Forca da Aura de Crescimento", "Custo de Comida"] [herbalism.hippo] - name = "Hipopotamo do Herbalista" - description = "Consumir comida da-te mais saturacao" - lore1 = "Comida) pontos de saturacao adicionais ao consumir" - lore = ["Comida) pontos de saturacao adicionais ao consumir"] +name = "Hipopotamo do Herbalista" +description = "Consumir comida da-te mais saturacao" +lore1 = "Comida) pontos de saturacao adicionais ao consumir" +lore = ["Comida) pontos de saturacao adicionais ao consumir"] [herbalism.myconid] - name = "Miconide do Herbalista" - description = "Da-te a capacidade de fabricar Micelio" - lore1 = "Qualquer Terra e um Cogumelo Castanho e Vermelho fabricam Micelio." - lore = ["Qualquer Terra e um Cogumelo Castanho e Vermelho fabricam Micelio."] +name = "Miconide do Herbalista" +description = "Da-te a capacidade de fabricar Micelio" +lore1 = "Qualquer Terra e um Cogumelo Castanho e Vermelho fabricam Micelio." +lore = ["Qualquer Terra e um Cogumelo Castanho e Vermelho fabricam Micelio."] [herbalism.terralid] - name = "Terralide do Herbalista" - description = "Da-te a capacidade de fabricar Blocos de Relva" - lore1 = "Tres Sementes, sobre 3 Terra, fabricam 3 Blocos de Relva." - lore = ["Tres Sementes, sobre 3 Terra, fabricam 3 Blocos de Relva."] +name = "Terralide do Herbalista" +description = "Da-te a capacidade de fabricar Blocos de Relva" +lore1 = "Tres Sementes, sobre 3 Terra, fabricam 3 Blocos de Relva." +lore = ["Tres Sementes, sobre 3 Terra, fabricam 3 Blocos de Relva."] [herbalism.cobweb] - name = "Criador de Teias" - description = "Da-te a capacidade de fabricar Teias de Aranha numa Mesa de Fabrico" - lore1 = "Nove Fios fabricam uma Teia de Aranha." - lore = ["Nove Fios fabricam uma Teia de Aranha."] +name = "Criador de Teias" +description = "Da-te a capacidade de fabricar Teias de Aranha numa Mesa de Fabrico" +lore1 = "Nove Fios fabricam uma Teia de Aranha." +lore = ["Nove Fios fabricam uma Teia de Aranha."] [herbalism.mushroom_blocks] - name = "Fabricante de Cogumelos" - description = "Da-te a capacidade de fabricar Blocos de Cogumelo numa Mesa de Fabrico" - lore1 = "Quatro Cogumelos para fazer um bloco, ou um bloco para fazer um caule." - lore = ["Quatro Cogumelos para fazer um bloco, ou um bloco para fazer um caule."] +name = "Fabricante de Cogumelos" +description = "Da-te a capacidade de fabricar Blocos de Cogumelo numa Mesa de Fabrico" +lore1 = "Quatro Cogumelos para fazer um bloco, ou um bloco para fazer um caule." +lore = ["Quatro Cogumelos para fazer um bloco, ou um bloco para fazer um caule."] [herbalism.drop_to_inventory] - name = "Enxada Direto-Para-Inventario" +name = "Enxada Direto-Para-Inventario" [herbalism.hungry_shield] - name = "Escudo Faminto" - description = "Recebe dano na fome antes da saude." - lore1 = "Resistido pela Fome" - lore = ["Resistido pela Fome"] +name = "Escudo Faminto" +description = "Recebe dano na fome antes da saude." +lore1 = "Resistido pela Fome" +lore = ["Resistido pela Fome"] [herbalism.luck] - name = "Sorte do Herbalista" - description = "Ao partires Relva/Flores, tens chance de obter um item aleatorio" - lore0 = "Flores = Comida, e Relva = Sementes" - lore1 = "Chance de obter um item ao partir Flores" - lore2 = "Chance de obter um item ao partir Relva" - lore = ["Flores = Comida, e Relva = Sementes", "Chance de obter um item ao partir Flores", "Chance de obter um item ao partir Relva"] +name = "Sorte do Herbalista" +description = "Ao partires Relva/Flores, tens chance de obter um item aleatorio" +lore0 = "Flores = Comida, e Relva = Sementes" +lore1 = "Chance de obter um item ao partir Flores" +lore2 = "Chance de obter um item ao partir Relva" +lore = ["Flores = Comida, e Relva = Sementes", "Chance de obter um item ao partir Flores", "Chance de obter um item ao partir Relva"] [herbalism.replant] - name = "Colheita e Replantacao" - description = "Clica direito numa colheita com uma enxada para colher e replantar." - lore1 = "Raio de Blocos de Replantacao" - lore = ["Raio de Blocos de Replantacao"] +name = "Colheita e Replantacao" +description = "Clica direito numa colheita com uma enxada para colher e replantar." +lore1 = "Raio de Blocos de Replantacao" +lore = ["Raio de Blocos de Replantacao"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalina" - description = "Causa mais dano quanto menos saude tiveres (Corpo a corpo)" - lore1 = "Dano Maximo" - lore = ["Dano Maximo"] +name = "Adrenalina" +description = "Causa mais dano quanto menos saude tiveres (Corpo a corpo)" +lore1 = "Dano Maximo" +lore = ["Dano Maximo"] [hunter.penalty] - name = "" - description = "" - lore1 = "Ganhas acumulacoes de Veneno se ficares sem fome" - lore = ["Ganhas acumulacoes de Veneno se ficares sem fome"] +name = "" +description = "" +lore1 = "Ganhas acumulacoes de Veneno se ficares sem fome" +lore = ["Ganhas acumulacoes de Veneno se ficares sem fome"] [hunter.drop_to_inventory] - name = "Itens Direto-Para-Inventario" - description = "Quando matas algo / Partes um bloco com uma espada teleporta os drops para o teu inventario" - lore1 = "Sempre que um item cai de um mob/bloco que partes, vai para o teu inventario se possivel." - lore = ["Sempre que um item cai de um mob/bloco que partes, vai para o teu inventario se possivel."] +name = "Itens Direto-Para-Inventario" +description = "Quando matas algo / Partes um bloco com uma espada teleporta os drops para o teu inventario" +lore1 = "Sempre que um item cai de um mob/bloco que partes, vai para o teu inventario se possivel." +lore = ["Sempre que um item cai de um mob/bloco que partes, vai para o teu inventario se possivel."] [hunter.invisibility] - name = "Passo Fantasma" - description = "Quando es atingido ganhas invisibilidade, a custo de fome" - lore1 = "Ganha invisibilidade passiva ao ser atingido" - lore2 = "x Acumulacoes de Invisibilidade por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Duracao da invisibilidade" - lore = ["Ganha invisibilidade passiva ao ser atingido", "x Acumulacoes de Invisibilidade por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Duracao da invisibilidade"] +name = "Passo Fantasma" +description = "Quando es atingido ganhas invisibilidade, a custo de fome" +lore1 = "Ganha invisibilidade passiva ao ser atingido" +lore2 = "x Acumulacoes de Invisibilidade por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Duracao da invisibilidade" +lore = ["Ganha invisibilidade passiva ao ser atingido", "x Acumulacoes de Invisibilidade por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Duracao da invisibilidade"] [hunter.jump_boost] - name = "Alturas do Cacador" - description = "Quando es atingido ganhas impulso de salto, a custo de fome" - lore1 = "Ganha impulso de salto passivo ao ser atingido" - lore2 = "x Acumulacoes de Impulso de Salto por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Impulso de Salto, nao duracao." - lore = ["Ganha impulso de salto passivo ao ser atingido", "x Acumulacoes de Impulso de Salto por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Impulso de Salto, nao duracao."] +name = "Alturas do Cacador" +description = "Quando es atingido ganhas impulso de salto, a custo de fome" +lore1 = "Ganha impulso de salto passivo ao ser atingido" +lore2 = "x Acumulacoes de Impulso de Salto por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Impulso de Salto, nao duracao." +lore = ["Ganha impulso de salto passivo ao ser atingido", "x Acumulacoes de Impulso de Salto por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Impulso de Salto, nao duracao."] [hunter.luck] - name = "Sorte do Cacador" - description = "Quando es atingido ganhas sorte, a custo de fome" - lore1 = "Ganha sorte passiva ao ser atingido" - lore2 = "x Acumulacoes de Sorte por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Sorte, nao duracao." - lore = ["Ganha sorte passiva ao ser atingido", "x Acumulacoes de Sorte por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Sorte, nao duracao."] +name = "Sorte do Cacador" +description = "Quando es atingido ganhas sorte, a custo de fome" +lore1 = "Ganha sorte passiva ao ser atingido" +lore2 = "x Acumulacoes de Sorte por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Sorte, nao duracao." +lore = ["Ganha sorte passiva ao ser atingido", "x Acumulacoes de Sorte por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Sorte, nao duracao."] [hunter.regen] - name = "Regeneracao do Cacador" - description = "Quando es atingido ganhas regeneracao, a custo de fome" - lore1 = "Ganha regeneracao passiva ao ser atingido" - lore2 = "x Acumulacoes de Regeneracao por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Regeneracao, nao duracao." - lore = ["Ganha regeneracao passiva ao ser atingido", "x Acumulacoes de Regeneracao por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Regeneracao, nao duracao."] +name = "Regeneracao do Cacador" +description = "Quando es atingido ganhas regeneracao, a custo de fome" +lore1 = "Ganha regeneracao passiva ao ser atingido" +lore2 = "x Acumulacoes de Regeneracao por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Regeneracao, nao duracao." +lore = ["Ganha regeneracao passiva ao ser atingido", "x Acumulacoes de Regeneracao por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Regeneracao, nao duracao."] [hunter.resistance] - name = "Resistencia do Cacador" - description = "Quando es atingido ganhas resistencia, a custo de fome" - lore1 = "Ganha resistencia passiva ao ser atingido" - lore2 = "x Acumulacoes de Resistencia por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Resistencia, nao duracao." - lore = ["Ganha resistencia passiva ao ser atingido", "x Acumulacoes de Resistencia por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Resistencia, nao duracao."] +name = "Resistencia do Cacador" +description = "Quando es atingido ganhas resistencia, a custo de fome" +lore1 = "Ganha resistencia passiva ao ser atingido" +lore2 = "x Acumulacoes de Resistencia por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Resistencia, nao duracao." +lore = ["Ganha resistencia passiva ao ser atingido", "x Acumulacoes de Resistencia por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Resistencia, nao duracao."] [hunter.speed] - name = "Velocidade do Cacador" - description = "Quando es atingido ganhas velocidade, a custo de fome" - lore1 = "Ganha velocidade passiva ao ser atingido" - lore2 = "x Acumulacoes de Velocidade por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Velocidade, nao duracao." - lore = ["Ganha velocidade passiva ao ser atingido", "x Acumulacoes de Velocidade por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Velocidade, nao duracao."] +name = "Velocidade do Cacador" +description = "Quando es atingido ganhas velocidade, a custo de fome" +lore1 = "Ganha velocidade passiva ao ser atingido" +lore2 = "x Acumulacoes de Velocidade por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Velocidade, nao duracao." +lore = ["Ganha velocidade passiva ao ser atingido", "x Acumulacoes de Velocidade por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Velocidade, nao duracao."] [hunter.strength] - name = "Forca do Cacador" - description = "Quando es atingido ganhas forca, a custo de fome" - lore1 = "Ganha forca passiva ao ser atingido" - lore2 = "x Acumulacoes de Forca por 3 segundos ao ser atingido" - lore3 = "x Fome acumulativa" - lore4 = "Duracao e multiplicador da fome acumulativa." - lore5 = "Multiplicador de acumulacoes de Forca, nao duracao." - lore = ["Ganha forca passiva ao ser atingido", "x Acumulacoes de Forca por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Forca, nao duracao."] +name = "Forca do Cacador" +description = "Quando es atingido ganhas forca, a custo de fome" +lore1 = "Ganha forca passiva ao ser atingido" +lore2 = "x Acumulacoes de Forca por 3 segundos ao ser atingido" +lore3 = "x Fome acumulativa" +lore4 = "Duracao e multiplicador da fome acumulativa." +lore5 = "Multiplicador de acumulacoes de Forca, nao duracao." +lore = ["Ganha forca passiva ao ser atingido", "x Acumulacoes de Forca por 3 segundos ao ser atingido", "x Fome acumulativa", "Duracao e multiplicador da fome acumulativa.", "Multiplicador de acumulacoes de Forca, nao duracao."] # nether [nether] [nether.skull_toss] - name = "Arremesso de Cranio Wither" - description1 = "Liberta o teu Wither interior usando" - description2 = "a cabeca de" - description3 = "alguem." - lore1 = "Segundos de tempo de espera entre arremessos de cranio." - lore2 = "Usando Cranio Wither: Lanca um " - lore3 = "Cranio Wither" - lore4 = "que explode ao impacto." - lore = ["Segundos de tempo de espera entre arremessos de cranio.", "Usando Cranio Wither: Lanca um ", "Cranio Wither", "que explode ao impacto."] +name = "Arremesso de Cranio Wither" +description1 = "Liberta o teu Wither interior usando" +description2 = "a cabeca de" +description3 = "alguem." +lore1 = "Segundos de tempo de espera entre arremessos de cranio." +lore2 = "Usando Cranio Wither: Lanca um " +lore3 = "Cranio Wither" +lore4 = "que explode ao impacto." +lore = ["Segundos de tempo de espera entre arremessos de cranio.", "Usando Cranio Wither: Lanca um ", "Cranio Wither", "que explode ao impacto."] [nether.wither_resist] - name = "Resistencia ao Wither" - description = "Resiste ao wither atraves do poder da Netherite." - lore1 = "chance de negar o wither (por peca)." - lore2 = "Passivo: Usar Armadura de Netherite tem chance de negar " - lore3 = "o wither." - lore = ["chance de negar o wither (por peca).", "Passivo: Usar Armadura de Netherite tem chance de negar ", "o wither."] +name = "Resistencia ao Wither" +description = "Resiste ao wither atraves do poder da Netherite." +lore1 = "chance de negar o wither (por peca)." +lore2 = "Passivo: Usar Armadura de Netherite tem chance de negar " +lore3 = "o wither." +lore = ["chance de negar o wither (por peca).", "Passivo: Usar Armadura de Netherite tem chance de negar ", "o wither."] [nether.fire_resist] - name = "Resistencia ao Fogo" - description = "Resiste ao fogo endurecendo a tua pele." - lore1 = "chance de negar o efeito de queimadura!" - lore = ["chance de negar o efeito de queimadura!"] +name = "Resistencia ao Fogo" +description = "Resiste ao fogo endurecendo a tua pele." +lore1 = "chance de negar o efeito de queimadura!" +lore = ["chance de negar o efeito de queimadura!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Fundicao Automatica" - description = "Permite-te fundir minerios Vanilla extraidos" - lore1 = "Minerios que podem ser fundidos sao fundidos automaticamente" - lore2 = "% de chance de um extra" - lore = ["Minerios que podem ser fundidos sao fundidos automaticamente", "% de chance de um extra"] +name = "Fundicao Automatica" +description = "Permite-te fundir minerios Vanilla extraidos" +lore1 = "Minerios que podem ser fundidos sao fundidos automaticamente" +lore2 = "% de chance de um extra" +lore = ["Minerios que podem ser fundidos sao fundidos automaticamente", "% de chance de um extra"] [pickaxe.chisel] - name = "Cinzel de Minerio" - description = "Clica direito em Minerios para Cinzelar mais minerio deles, a um custo severo de durabilidade." - lore1 = "Chance de Drop" - lore2 = "Desgaste da Ferramenta" - lore = ["Chance de Drop", "Desgaste da Ferramenta"] +name = "Cinzel de Minerio" +description = "Clica direito em Minerios para Cinzelar mais minerio deles, a um custo severo de durabilidade." +lore1 = "Chance de Drop" +lore2 = "Desgaste da Ferramenta" +lore = ["Chance de Drop", "Desgaste da Ferramenta"] [pickaxe.drop_to_inventory] - name = "Picareta Direto-Para-Inventario" - description = "Quando partes um bloco, teleporta o item para o teu inventario" - lore1 = "Sempre que um item cai de um bloco que partes, vai para o teu inventario se possivel." - lore = ["Sempre que um item cai de um bloco que partes, vai para o teu inventario se possivel."] +name = "Picareta Direto-Para-Inventario" +description = "Quando partes um bloco, teleporta o item para o teu inventario" +lore1 = "Sempre que um item cai de um bloco que partes, vai para o teu inventario se possivel." +lore = ["Sempre que um item cai de um bloco que partes, vai para o teu inventario se possivel."] [pickaxe.silk_spawner] - name = "Picareta Silk-Spawner" - description = "Faz Spawners caírem quando partidos" - lore1 = "Torna Spawners quebraveis com toque de seda." - lore2 = "Torna Spawners quebraveis enquanto agachado." - lore = ["Torna Spawners quebraveis com toque de seda.", "Torna Spawners quebraveis enquanto agachado."] +name = "Picareta Silk-Spawner" +description = "Faz Spawners caírem quando partidos" +lore1 = "Torna Spawners quebraveis com toque de seda." +lore2 = "Torna Spawners quebraveis enquanto agachado." +lore = ["Torna Spawners quebraveis com toque de seda.", "Torna Spawners quebraveis enquanto agachado."] [pickaxe.vein_miner] - name = "Mineiro de Veios" - description = "Permite-te partir blocos num Veio/Grupo de minerios Vanilla" - lore1 = "Agacha e minera MINERIOS" - lore2 = "alcance da mineracao de veios" - lore3 = "Esta habilidade NAO agrupa todos os drops juntos!" - lore = ["Agacha e minera MINERIOS", "alcance da mineracao de veios", "Esta habilidade NAO agrupa todos os drops juntos!"] +name = "Mineiro de Veios" +description = "Permite-te partir blocos num Veio/Grupo de minerios Vanilla" +lore1 = "Agacha e minera MINERIOS" +lore2 = "alcance da mineracao de veios" +lore3 = "Esta habilidade NAO agrupa todos os drops juntos!" +lore = ["Agacha e minera MINERIOS", "alcance da mineracao de veios", "Esta habilidade NAO agrupa todos os drops juntos!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Recuperacao de Flechas" - description = "Recupera Flechas depois de matares um inimigo." - lore1 = "Chance de Recuperar Flechas ao Acertar/Matar" - lore2 = "Chance: " - lore = ["Chance de Recuperar Flechas ao Acertar/Matar", "Chance: "] +name = "Recuperacao de Flechas" +description = "Recupera Flechas depois de matares um inimigo." +lore1 = "Chance de Recuperar Flechas ao Acertar/Matar" +lore2 = "Chance: " +lore = ["Chance de Recuperar Flechas ao Acertar/Matar", "Chance: "] [ranged.web_shot] - name = "Armadilha de Teia" - description = "Rodeia o teu alvo com teias de aranha quando o acertas!" - lore1 = "8 Teias de Aranha a volta de uma Bola de Neve, e atira!" - lore2 = "segundos de gaiola, aproximadamente." - lore = ["8 Teias de Aranha a volta de uma Bola de Neve, e atira!", "segundos de gaiola, aproximadamente."] +name = "Armadilha de Teia" +description = "Rodeia o teu alvo com teias de aranha quando o acertas!" +lore1 = "8 Teias de Aranha a volta de uma Bola de Neve, e atira!" +lore2 = "segundos de gaiola, aproximadamente." +lore = ["8 Teias de Aranha a volta de uma Bola de Neve, e atira!", "segundos de gaiola, aproximadamente."] [ranged.force_shot] - name = "Tiro Forcado" - description = "Dispara projeteis mais longe, mais rapido!" - advancementname = "Tiro Longo" - advancementlore = "Acerta um tiro a mais de 30 blocos de distancia!" - lore1 = "Velocidade do Projetil" - lore = ["Velocidade do Projetil"] +name = "Tiro Forcado" +description = "Dispara projeteis mais longe, mais rapido!" +advancementname = "Tiro Longo" +advancementlore = "Acerta um tiro a mais de 30 blocos de distancia!" +lore1 = "Velocidade do Projetil" +lore = ["Velocidade do Projetil"] [ranged.lunge_shot] - name = "Tiro de Estocada" - description = "Enquanto cais, as tuas flechas lancam-te numa direcao aleatoria" - lore1 = "Velocidade de Rajada Aleatoria" - lore = ["Velocidade de Rajada Aleatoria"] +name = "Tiro de Estocada" +description = "Enquanto cais, as tuas flechas lancam-te numa direcao aleatoria" +lore1 = "Velocidade de Rajada Aleatoria" +lore = ["Velocidade de Rajada Aleatoria"] [ranged.arrow_piercing] - name = "Perfuracao de Flechas" - description = "Adiciona Perfuracao a projeteis! Dispara atraves de coisas!" - lore1 = "Alvos Perfurados" - lore = ["Alvos Perfurados"] +name = "Perfuracao de Flechas" +description = "Adiciona Perfuracao a projeteis! Dispara atraves de coisas!" +lore1 = "Alvos Perfurados" +lore = ["Alvos Perfurados"] # rift [rift] [rift.remote_access] - name = "Acesso Remoto" - description = "Puxa do vazio e acede a um contentor marcado." - lore1 = "Perola do Ender + Bussola = Chave Portal do Relicario" - lore2 = "Este item permite-te aceder a contentores remotamente" - lore3 = "Depois de fabricar, olha para o item para ver como usar" - notcontainer = "Isso nao e um contentor" - lore = ["Perola do Ender + Bussola = Chave Portal do Relicario", "Este item permite-te aceder a contentores remotamente", "Depois de fabricar, olha para o item para ver como usar"] +name = "Acesso Remoto" +description = "Puxa do vazio e acede a um contentor marcado." +lore1 = "Perola do Ender + Bussola = Chave Portal do Relicario" +lore2 = "Este item permite-te aceder a contentores remotamente" +lore3 = "Depois de fabricar, olha para o item para ver como usar" +notcontainer = "Isso nao e um contentor" +lore = ["Perola do Ender + Bussola = Chave Portal do Relicario", "Este item permite-te aceder a contentores remotamente", "Depois de fabricar, olha para o item para ver como usar"] [rift.blink] - name = "Piscar da Fenda" - description = "Teletransporte instantaneo de curto alcance, apenas um piscar de olhos!" - lore1 = "Blocos no piscar (2x Vertical)" - lore2 = "Enquanto a Correr: Toca duas vezes em Saltar para " - lore3 = "Piscar" - lore = ["Blocos no piscar (2x Vertical)", "Enquanto a Correr: Toca duas vezes em Saltar para ", "Piscar"] +name = "Piscar da Fenda" +description = "Teletransporte instantaneo de curto alcance, apenas um piscar de olhos!" +lore1 = "Blocos no piscar (2x Vertical)" +lore2 = "Enquanto a Correr: Toca duas vezes em Saltar para " +lore3 = "Piscar" +lore = ["Blocos no piscar (2x Vertical)", "Enquanto a Correr: Toca duas vezes em Saltar para ", "Piscar"] [rift.chest] - name = "Bau do Ender Facil" - description = "Abre um bau do ender clicando nele com o botao esquerdo na tua mao." - lore1 = "Clica num Bau do Ender na tua mao para abrir (Nao o coloques)" - lore = ["Clica num Bau do Ender na tua mao para abrir (Nao o coloques)"] +name = "Bau do Ender Facil" +description = "Abre um bau do ender clicando nele com o botao esquerdo na tua mao." +lore1 = "Clica num Bau do Ender na tua mao para abrir (Nao o coloques)" +lore = ["Clica num Bau do Ender na tua mao para abrir (Nao o coloques)"] [rift.descent] - name = "Anti-Levitacao" - description = "Estas cansado de ficar preso no ar? Esta e a habilidade para ti!" - lore1 = "Basta agachares para descer, e vais cair a uma velocidade menor que o normal!" - lore2 = "Tempo de espera:" - lore = ["Basta agachares para descer, e vais cair a uma velocidade menor que o normal!", "Tempo de espera:"] +name = "Anti-Levitacao" +description = "Estas cansado de ficar preso no ar? Esta e a habilidade para ti!" +lore1 = "Basta agachares para descer, e vais cair a uma velocidade menor que o normal!" +lore2 = "Tempo de espera:" +lore = ["Basta agachares para descer, e vais cair a uma velocidade menor que o normal!", "Tempo de espera:"] [rift.gate] - name = "Portal da Fenda" - description = "Teletransporta para um local marcado." - lore1 = "FABRICO: Esmeralda + Fragmento de Ametista + Perola do Ender" - lore2 = "Le antes de usar!" - lore3 = "5s de atraso, " - lore4 = "podes morrer enquanto estiveres nesta animacao" - lore = ["FABRICO: Esmeralda + Fragmento de Ametista + Perola do Ender", "Le antes de usar!", "5s de atraso, ", "podes morrer enquanto estiveres nesta animacao"] +name = "Portal da Fenda" +description = "Teletransporta para um local marcado." +lore1 = "FABRICO: Esmeralda + Fragmento de Ametista + Perola do Ender" +lore2 = "Le antes de usar!" +lore3 = "5s de atraso, " +lore4 = "podes morrer enquanto estiveres nesta animacao" +lore = ["FABRICO: Esmeralda + Fragmento de Ametista + Perola do Ender", "Le antes de usar!", "5s de atraso, ", "podes morrer enquanto estiveres nesta animacao"] [rift.resist] - name = "Resistencia da Fenda" - description = "Ganha Resistencia ao usar Itens e Habilidades do Ender" - lore1 = "+ Passivo: Fornece resistencia quando usas habilidades da fenda, ou Itens do Ender" - lore2 = "NAO incluindo Bau do Ender Portatil, apenas coisas que podes Consumir" - lore = ["+ Passivo: Fornece resistencia quando usas habilidades da fenda, ou Itens do Ender", "NAO incluindo Bau do Ender Portatil, apenas coisas que podes Consumir"] +name = "Resistencia da Fenda" +description = "Ganha Resistencia ao usar Itens e Habilidades do Ender" +lore1 = "+ Passivo: Fornece resistencia quando usas habilidades da fenda, ou Itens do Ender" +lore2 = "NAO incluindo Bau do Ender Portatil, apenas coisas que podes Consumir" +lore = ["+ Passivo: Fornece resistencia quando usas habilidades da fenda, ou Itens do Ender", "NAO incluindo Bau do Ender Portatil, apenas coisas que podes Consumir"] [rift.visage] - name = "Visagem da Fenda" - description = "Impede Endermen de ficarem agressivos se tiveres Perolas do Ender no teu inventario." - lore1 = "Endermen nao ficam agressivos se tiveres Perolas do Ender no teu inventario." - lore = ["Endermen nao ficam agressivos se tiveres Perolas do Ender no teu inventario."] +name = "Visagem da Fenda" +description = "Impede Endermen de ficarem agressivos se tiveres Perolas do Ender no teu inventario." +lore1 = "Endermen nao ficam agressivos se tiveres Perolas do Ender no teu inventario." +lore = ["Endermen nao ficam agressivos se tiveres Perolas do Ender no teu inventario."] # seaborn [seaborn] [seaborn.oxygen] - name = "Tanque de Oxigenio Organico" - description = "Mantem mais oxigenio nos teus pequenos pulmoes!" - lore1 = "Aumento da Capacidade de Oxigenio" - lore = ["Aumento da Capacidade de Oxigenio"] +name = "Tanque de Oxigenio Organico" +description = "Mantem mais oxigenio nos teus pequenos pulmoes!" +lore1 = "Aumento da Capacidade de Oxigenio" +lore = ["Aumento da Capacidade de Oxigenio"] [seaborn.fishers_fantasy] - name = "Fantasia do Pescador" - description = "Ganha mais XP ao pescar e apanha mais peixe!" - lore1 = "Por cada nivel ha uma chance de obter mais XP e Peixe!" - lore = ["Por cada nivel ha uma chance de obter mais XP e Peixe!"] +name = "Fantasia do Pescador" +description = "Ganha mais XP ao pescar e apanha mais peixe!" +lore1 = "Por cada nivel ha uma chance de obter mais XP e Peixe!" +lore = ["Por cada nivel ha uma chance de obter mais XP e Peixe!"] [seaborn.haste] - name = "Tartaruga Mineira" - description = "Ao minerar debaixo de agua ganhas pressa!" - lore1 = "Pressa 3 e aplicada debaixo de agua enquanto mineras (acumula com AquaAffinity) depois do efeito de respiracao aquatica acabar!" - lore = ["Pressa 3 e aplicada debaixo de agua enquanto mineras (acumula com AquaAffinity) depois do efeito de respiracao aquatica acabar!"] +name = "Tartaruga Mineira" +description = "Ao minerar debaixo de agua ganhas pressa!" +lore1 = "Pressa 3 e aplicada debaixo de agua enquanto mineras (acumula com AquaAffinity) depois do efeito de respiracao aquatica acabar!" +lore = ["Pressa 3 e aplicada debaixo de agua enquanto mineras (acumula com AquaAffinity) depois do efeito de respiracao aquatica acabar!"] [seaborn.night_vision] - name = "Visao da Tartaruga" - description = "Enquanto debaixo de agua, ganhas Visao Noturna" - lore1 = "Simplesmente ganha Visao Noturna enquanto debaixo de agua depois do efeito de respiracao aquatica acabar!" - lore = ["Simplesmente ganha Visao Noturna enquanto debaixo de agua depois do efeito de respiracao aquatica acabar!"] +name = "Visao da Tartaruga" +description = "Enquanto debaixo de agua, ganhas Visao Noturna" +lore1 = "Simplesmente ganha Visao Noturna enquanto debaixo de agua depois do efeito de respiracao aquatica acabar!" +lore = ["Simplesmente ganha Visao Noturna enquanto debaixo de agua depois do efeito de respiracao aquatica acabar!"] [seaborn.dolphin_grace] - name = "Graca do Golfinho" - description = "Nada como um golfinho, sem os golfinhos" - lore1 = "+ Passivo: ganha " - lore2 = "x velocidade (graca do golfinho)" - lore3 = "engenharia alema de precisao- espera, isso nao esta certo... Nao Compativel com Profundidade" - lore = ["+ Passivo: ganha ", "x velocidade (graca do golfinho)", "engenharia alema de precisao- espera, isso nao esta certo... Nao Compativel com Profundidade"] +name = "Graca do Golfinho" +description = "Nada como um golfinho, sem os golfinhos" +lore1 = "+ Passivo: ganha " +lore2 = "x velocidade (graca do golfinho)" +lore3 = "engenharia alema de precisao- espera, isso nao esta certo... Nao Compativel com Profundidade" +lore = ["+ Passivo: ganha ", "x velocidade (graca do golfinho)", "engenharia alema de precisao- espera, isso nao esta certo... Nao Compativel com Profundidade"] # stealth [stealth] [stealth.ghost_armor] - name = "Armadura Fantasma" - description = "Armadura que se constroi lentamente quando nao recebes dano, dura 1 golpe" - lore1 = "Armadura Maxima" - lore2 = "Velocidade" - lore = ["Armadura Maxima", "Velocidade"] +name = "Armadura Fantasma" +description = "Armadura que se constroi lentamente quando nao recebes dano, dura 1 golpe" +lore1 = "Armadura Maxima" +lore2 = "Velocidade" +lore = ["Armadura Maxima", "Velocidade"] [stealth.night_vision] - name = "Visao Furtiva" - description = "Ganha visao noturna enquanto agachado" - lore1 = "Ganha um surto de " - lore2 = "visao noturna" - lore3 = "enquanto agachado" - lore = ["Ganha um surto de ", "visao noturna", "enquanto agachado"] +name = "Visao Furtiva" +description = "Ganha visao noturna enquanto agachado" +lore1 = "Ganha um surto de " +lore2 = "visao noturna" +lore3 = "enquanto agachado" +lore = ["Ganha um surto de ", "visao noturna", "enquanto agachado"] [stealth.snatch] - name = "Apanhar Itens" - description = "Apanha itens largados instantaneamente enquanto agachado!" - lore1 = "Raio de Apanhar" - lore = ["Raio de Apanhar"] +name = "Apanhar Itens" +description = "Apanha itens largados instantaneamente enquanto agachado!" +lore1 = "Raio de Apanhar" +lore = ["Raio de Apanhar"] [stealth.speed] - name = "Velocidade Furtiva" - description = "Ganha velocidade enquanto agachado" - lore1 = "Velocidade ao Agachar" - lore = ["Velocidade ao Agachar"] +name = "Velocidade Furtiva" +description = "Ganha velocidade enquanto agachado" +lore1 = "Velocidade ao Agachar" +lore = ["Velocidade ao Agachar"] [stealth.ender_veil] - name = "Veu do Ender" - description = "Sem mais Aboboras para prevenir ataques de Endermen" - lore1 = "Previne ataques de endermen enquanto agachado" - lore2 = "Previne todos os ataques de endermen" - lore = ["Previne ataques de endermen enquanto agachado", "Previne todos os ataques de endermen"] +name = "Veu do Ender" +description = "Sem mais Aboboras para prevenir ataques de Endermen" +lore1 = "Previne ataques de endermen enquanto agachado" +lore2 = "Previne todos os ataques de endermen" +lore = ["Previne ataques de endermen enquanto agachado", "Previne todos os ataques de endermen"] # sword [sword] [sword.machete] - name = "Machete" - description = "Corta folhagem com facilidade!" - lore1 = "Raio de Corte" - lore2 = "Tempo de Espera do Corte" - lore3 = "Desgaste da Ferramenta" - lore = ["Raio de Corte", "Tempo de Espera do Corte", "Desgaste da Ferramenta"] +name = "Machete" +description = "Corta folhagem com facilidade!" +lore1 = "Raio de Corte" +lore2 = "Tempo de Espera do Corte" +lore3 = "Desgaste da Ferramenta" +lore = ["Raio de Corte", "Tempo de Espera do Corte", "Desgaste da Ferramenta"] [sword.bloody_blade] - name = "Lamina Sangrenta" - description = "Golpes com a tua espada causam Sangramento!" - lore1 = "Atingir uma entidade viva com a tua Espada causa Sangramento" - lore2 = "Duracao do Sangramento" - lore3 = "Tempo de Espera do Sangramento" - lore = ["Atingir uma entidade viva com a tua Espada causa Sangramento", "Duracao do Sangramento", "Tempo de Espera do Sangramento"] +name = "Lamina Sangrenta" +description = "Golpes com a tua espada causam Sangramento!" +lore1 = "Atingir uma entidade viva com a tua Espada causa Sangramento" +lore2 = "Duracao do Sangramento" +lore3 = "Tempo de Espera do Sangramento" +lore = ["Atingir uma entidade viva com a tua Espada causa Sangramento", "Duracao do Sangramento", "Tempo de Espera do Sangramento"] [sword.poisoned_blade] - name = "Lamina Envenenada" - description = "Golpes com a tua espada causam Veneno!" - lore1 = "Atingir uma entidade viva com a tua Espada causa Veneno" - lore2 = "Duracao do Veneno" - lore3 = "Tempo de Espera do Veneno" - lore = ["Atingir uma entidade viva com a tua Espada causa Veneno", "Duracao do Veneno", "Tempo de Espera do Veneno"] +name = "Lamina Envenenada" +description = "Golpes com a tua espada causam Veneno!" +lore1 = "Atingir uma entidade viva com a tua Espada causa Veneno" +lore2 = "Duracao do Veneno" +lore3 = "Tempo de Espera do Veneno" +lore = ["Atingir uma entidade viva com a tua Espada causa Veneno", "Duracao do Veneno", "Tempo de Espera do Veneno"] # taming [taming] [taming.damage] - name = "Dano de Domesticado" - description = "Aumenta o dano causado pelo teu animal domesticado." - lore1 = "Dano Aumentado" - lore = ["Dano Aumentado"] +name = "Dano de Domesticado" +description = "Aumenta o dano causado pelo teu animal domesticado." +lore1 = "Dano Aumentado" +lore = ["Dano Aumentado"] [taming.health] - name = "Saude de Domesticado" - description = "Aumenta a saude do teu animal domesticado." - lore1 = "Saude Aumentada" - lore = ["Saude Aumentada"] +name = "Saude de Domesticado" +description = "Aumenta a saude do teu animal domesticado." +lore1 = "Saude Aumentada" +lore = ["Saude Aumentada"] [taming.regeneration] - name = "Regeneracao de Domesticado" - description = "Aumenta a regeneracao do teu animal domesticado." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Regeneracao de Domesticado" +description = "Aumenta a regeneracao do teu animal domesticado." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Espinhos" - description = "Reflete dano de volta ao teu atacante!" - lore1 = "Dano retaliado quando atingido" - lore = ["Dano retaliado quando atingido"] +name = "Espinhos" +description = "Reflete dano de volta ao teu atacante!" +lore1 = "Dano retaliado quando atingido" +lore = ["Dano retaliado quando atingido"] [tragoul.globe] - name = "Globo de Dor" - description = "Divide o Dano que causas baseado no numero de inimigos a tua volta!" - lore1 = "Quanto mais inimigos a tua volta, menos dano causas a cada um" - lore2 = "Alcance: " - lore3 = "Dano Adicionado a todas as Entidades: " - lore = ["Quanto mais inimigos a tua volta, menos dano causas a cada um", "Alcance: ", "Dano Adicionado a todas as Entidades: "] +name = "Globo de Dor" +description = "Divide o Dano que causas baseado no numero de inimigos a tua volta!" +lore1 = "Quanto mais inimigos a tua volta, menos dano causas a cada um" +lore2 = "Alcance: " +lore3 = "Dano Adicionado a todas as Entidades: " +lore = ["Quanto mais inimigos a tua volta, menos dano causas a cada um", "Alcance: ", "Dano Adicionado a todas as Entidades: "] [tragoul.healing] - name = "Vontade da Dor" - description = "Recupera saude baseado no dano que causas!" - lore1 = "Magoar coisas nunca soube tao bem! Cura com o Dano Causado" - lore2 = "Ha uma janela de dano de 3 Segundos, para cura e um Tempo de Espera de 1 Segundo " - lore3 = "Cura Por Percentagem de Dano: " - lore = ["Magoar coisas nunca soube tao bem! Cura com o Dano Causado", "Ha uma janela de dano de 3 Segundos, para cura e um Tempo de Espera de 1 Segundo ", "Cura Por Percentagem de Dano: "] +name = "Vontade da Dor" +description = "Recupera saude baseado no dano que causas!" +lore1 = "Magoar coisas nunca soube tao bem! Cura com o Dano Causado" +lore2 = "Ha uma janela de dano de 3 Segundos, para cura e um Tempo de Espera de 1 Segundo " +lore3 = "Cura Por Percentagem de Dano: " +lore = ["Magoar coisas nunca soube tao bem! Cura com o Dano Causado", "Ha uma janela de dano de 3 Segundos, para cura e um Tempo de Espera de 1 Segundo ", "Cura Por Percentagem de Dano: "] [tragoul.lance] - name = "Lancas Cadavericas" - description = "Matar um inimigo ou uma habilidade matar um inimigo gera uma lanca que causa dano a um inimigo proximo!" - lore1 = "As Lancas procuram a partir de tudo o que matas, E se esta habilidade matar um inimigo." - lore2 = "Sacrifica uma porcao da tua vida para criar as lancas (isto pode matar-te)" - lore3 = "Lancas Maximas: 1 + " - lore = ["As Lancas procuram a partir de tudo o que matas, E se esta habilidade matar um inimigo.", "Sacrifica uma porcao da tua vida para criar as lancas (isto pode matar-te)", "Lancas Maximas: 1 + "] +name = "Lancas Cadavericas" +description = "Matar um inimigo ou uma habilidade matar um inimigo gera uma lanca que causa dano a um inimigo proximo!" +lore1 = "As Lancas procuram a partir de tudo o que matas, E se esta habilidade matar um inimigo." +lore2 = "Sacrifica uma porcao da tua vida para criar as lancas (isto pode matar-te)" +lore3 = "Lancas Maximas: 1 + " +lore = ["As Lancas procuram a partir de tudo o que matas, E se esta habilidade matar um inimigo.", "Sacrifica uma porcao da tua vida para criar as lancas (isto pode matar-te)", "Lancas Maximas: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Canhao de Vidro" - description = "Bonus de Dano Desarmado quanto menor for o teu valor de armadura" - lore1 = "x Dano com 0 de armadura" - lore2 = "Dano Bonus PorNivel" - lore = ["x Dano com 0 de armadura", "Dano Bonus PorNivel"] +name = "Canhao de Vidro" +description = "Bonus de Dano Desarmado quanto menor for o teu valor de armadura" +lore1 = "x Dano com 0 de armadura" +lore2 = "Dano Bonus PorNivel" +lore = ["x Dano com 0 de armadura", "Dano Bonus PorNivel"] [unarmed.power] - name = "Poder Desarmado" - description = "Dano Desarmado Melhorado" - lore1 = "Dano" - lore = ["Dano"] +name = "Poder Desarmado" +description = "Dano Desarmado Melhorado" +lore1 = "Dano" +lore = ["Dano"] [unarmed.sucker_punch] - name = "Soco Surpresa" - description = "Socos a correr, mas mais mortiferos." - lore1 = "Dano" - lore2 = "O dano aumenta com a tua velocidade enquanto socas" - lore = ["Dano", "O dano aumenta com a tua velocidade enquanto socas"] +name = "Soco Surpresa" +description = "Socos a correr, mas mais mortiferos." +lore1 = "Dano" +lore2 = "O dano aumenta com a tua velocidade enquanto socas" +lore = ["Dano", "O dano aumenta com a tua velocidade enquanto socas"] diff --git a/src/main/resources/ru_RU.toml b/src/main/resources/ru_RU.toml index a1a84dadf..8adee7f6c 100644 --- a/src/main/resources/ru_RU.toml +++ b/src/main/resources/ru_RU.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Нужно двигаться!" - description = "Пройдите более 1 километра (1 000 блоков)" +title = "Нужно двигаться!" +description = "Пройдите более 1 километра (1 000 блоков)" [advancement.challenge_sprint_5k] - title = "Пробегите 5 км!" - description = "Пройдите более 5 километров (5 000 блоков)" +title = "Пробегите 5 км!" +description = "Пройдите более 5 километров (5 000 блоков)" [advancement.challenge_sprint_50k] - title = "Промчитесь 50 км!" - description = "Пройдите более 50 километров (50 000 блоков)" +title = "Промчитесь 50 км!" +description = "Пройдите более 50 километров (50 000 блоков)" [advancement.challenge_sprint_500k] - title = "Путешествие по Вселенной!!" - description = "Пройдите более 500 километров (500 000 блоков)" +title = "Путешествие по Вселенной!!" +description = "Пройдите более 500 километров (500 000 блоков)" [advancement.challenge_sprint_marathon] - title = "Пробегите (буквально) марафон!" - description = "Пробегите более 42 195 блоков!" +title = "Пробегите (буквально) марафон!" +description = "Пробегите более 42 195 блоков!" [advancement.challenge_place_1k] - title = "Начинающий строитель!" - description = "Разместите 1 000 блоков" +title = "Начинающий строитель!" +description = "Разместите 1 000 блоков" [advancement.challenge_place_5k] - title = "Продвинутый строитель!" - description = "Разместите 5 000 блоков" +title = "Продвинутый строитель!" +description = "Разместите 5 000 блоков" [advancement.challenge_place_50k] - title = "Опытный строитель!" - description = "Разместите 50 000 блоков" +title = "Опытный строитель!" +description = "Разместите 50 000 блоков" [advancement.challenge_place_500k] - title = "Мастер-строитель!" - description = "Разместите 500 000 блоков" +title = "Мастер-строитель!" +description = "Разместите 500 000 блоков" [advancement.challenge_place_5m] - title = "Аколит Симметрии!" - description = "РЕАЛЬНОСТЬ -- ВАША ИГРОВАЯ ПЛОЩАДКА! (5 миллионов блоков)" +title = "Аколит Симметрии!" +description = "РЕАЛЬНОСТЬ -- ВАША ИГРОВАЯ ПЛОЩАДКА! (5 миллионов блоков)" [advancement.challenge_chop_1k] - title = "Начинающий лесоруб!" - description = "Срубите 1 000 блоков" +title = "Начинающий лесоруб!" +description = "Срубите 1 000 блоков" [advancement.challenge_chop_5k] - title = "Продвинутый лесоруб!" - description = "Срубите 5 000 блоков" +title = "Продвинутый лесоруб!" +description = "Срубите 5 000 блоков" [advancement.challenge_chop_50k] - title = "Опытный лесоруб!" - description = "Срубите 50 000 блоков" +title = "Опытный лесоруб!" +description = "Срубите 50 000 блоков" [advancement.challenge_chop_500k] - title = "Мастер-лесоруб!" - description = "Срубите 500 000 блоков" +title = "Мастер-лесоруб!" +description = "Срубите 500 000 блоков" [advancement.challenge_chop_5m] - title = "Пёс Джексон" - description = "Самый лучший, добрый мальчик! (5 миллионов блоков)" +title = "Пёс Джексон" +description = "Самый лучший, добрый мальчик! (5 миллионов блоков)" [advancement.challenge_block_1k] - title = "Едва блокирую!" - description = "Заблокируйте 1 000 ударов" +title = "Едва блокирую!" +description = "Заблокируйте 1 000 ударов" [advancement.challenge_block_5k] - title = "Блокировать -- это весело!" - description = "Заблокируйте 5 000 ударов" +title = "Блокировать -- это весело!" +description = "Заблокируйте 5 000 ударов" [advancement.challenge_block_50k] - title = "Блокирование -- моя жизнь!" - description = "Заблокируйте 50 000 ударов" +title = "Блокирование -- моя жизнь!" +description = "Заблокируйте 50 000 ударов" [advancement.challenge_block_500k] - title = "Блокирование -- моё предназначение!" - description = "Заблокируйте 500 000 ударов" +title = "Блокирование -- моё предназначение!" +description = "Заблокируйте 500 000 ударов" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Заблокируйте 5 000 000 ударов" +title = "Die Hand Die Verletzt" +description = "Заблокируйте 5 000 000 ударов" [advancement.challenge_brew_1k] - title = "Начинающий алхимик!" - description = "Выпейте 1 000 зелий" +title = "Начинающий алхимик!" +description = "Выпейте 1 000 зелий" [advancement.challenge_brew_5k] - title = "Продвинутый алхимик!" - description = "Выпейте 5 000 зелий" +title = "Продвинутый алхимик!" +description = "Выпейте 5 000 зелий" [advancement.challenge_brew_50k] - title = "Опытный алхимик!" - description = "Выпейте 50 000 зелий" +title = "Опытный алхимик!" +description = "Выпейте 50 000 зелий" [advancement.challenge_brew_500k] - title = "Мастер-алхимик!" - description = "Выпейте 500 000 зелий" +title = "Мастер-алхимик!" +description = "Выпейте 500 000 зелий" [advancement.challenge_brew_5m] - title = "Великий Алхимик" - description = "Выпейте 5 000 000 зелий" +title = "Великий Алхимик" +description = "Выпейте 5 000 000 зелий" [advancement.challenge_brewsplash_1k] - title = "Начинающий зельевар!" - description = "Выплесните 1 000 зелий" +title = "Начинающий зельевар!" +description = "Выплесните 1 000 зелий" [advancement.challenge_brewsplash_5k] - title = "Продвинутый зельевар!" - description = "Выплесните 5 000 зелий" +title = "Продвинутый зельевар!" +description = "Выплесните 5 000 зелий" [advancement.challenge_brewsplash_50k] - title = "Опытный зельевар!" - description = "Выплесните 50 000 зелий" +title = "Опытный зельевар!" +description = "Выплесните 50 000 зелий" [advancement.challenge_brewsplash_500k] - title = "Мастер-зельевар!" - description = "Выплесните 500 000 зелий" +title = "Мастер-зельевар!" +description = "Выплесните 500 000 зелий" [advancement.challenge_brewsplash_5m] - title = "Брызгомейстер" - description = "Выплесните 5 000 000 зелий" +title = "Брызгомейстер" +description = "Выплесните 5 000 000 зелий" [advancement.challenge_craft_1k] - title = "Ловкий ремесленник!" - description = "Создайте 1 000 предметов" +title = "Ловкий ремесленник!" +description = "Создайте 1 000 предметов" [advancement.challenge_craft_5k] - title = "Сварливый ремесленник!" - description = "Создайте 5 000 предметов" +title = "Сварливый ремесленник!" +description = "Создайте 5 000 предметов" [advancement.challenge_craft_50k] - title = "Усердный ремесленник!" - description = "Создайте 50 000 предметов" +title = "Усердный ремесленник!" +description = "Создайте 50 000 предметов" [advancement.challenge_craft_500k] - title = "Неугомонный ремесленник!" - description = "Создайте 500 000 предметов" +title = "Неугомонный ремесленник!" +description = "Создайте 500 000 предметов" [advancement.challenge_craft_5m] - title = "Катастрофический Маккрафтфейс" - description = "Создайте 5 000 000 предметов" +title = "Катастрофический Маккрафтфейс" +description = "Создайте 5 000 000 предметов" [advancement.challenge_enchant_1k] - title = "Начинающий зачарователь!" - description = "Зачаруйте 1 000 предметов" +title = "Начинающий зачарователь!" +description = "Зачаруйте 1 000 предметов" [advancement.challenge_enchant_5k] - title = "Продвинутый зачарователь!" - description = "Зачаруйте 5 000 предметов" +title = "Продвинутый зачарователь!" +description = "Зачаруйте 5 000 предметов" [advancement.challenge_enchant_50k] - title = "Опытный зачарователь!" - description = "Зачаруйте 50 000 предметов" +title = "Опытный зачарователь!" +description = "Зачаруйте 50 000 предметов" [advancement.challenge_enchant_500k] - title = "Мастер-зачарователь!" - description = "Зачаруйте 500 000 предметов" +title = "Мастер-зачарователь!" +description = "Зачаруйте 500 000 предметов" [advancement.challenge_enchant_5m] - title = "Загадочный зачарователь" - description = "Зачаруйте 5 000 000 предметов" +title = "Загадочный зачарователь" +description = "Зачаруйте 5 000 000 предметов" [advancement.challenge_excavate_1k] - title = "Начинающий копатель!" - description = "Вскопайте 1 000 блоков" +title = "Начинающий копатель!" +description = "Вскопайте 1 000 блоков" [advancement.challenge_excavate_5k] - title = "Продвинутый копатель!" - description = "Вскопайте 5 000 блоков" +title = "Продвинутый копатель!" +description = "Вскопайте 5 000 блоков" [advancement.challenge_excavate_50k] - title = "Опытный копатель!" - description = "Вскопайте 50 000 блоков" +title = "Опытный копатель!" +description = "Вскопайте 50 000 блоков" [advancement.challenge_excavate_500k] - title = "Мастер-копатель!" - description = "Вскопайте 500 000 блоков" +title = "Мастер-копатель!" +description = "Вскопайте 500 000 блоков" [advancement.challenge_excavate_5m] - title = "Загадочный копатель" - description = "Вскопайте 5 000 000 блоков" +title = "Загадочный копатель" +description = "Вскопайте 5 000 000 блоков" [advancement.horrible_person] - title = "Ты ужасный человек" - description = "Непостижимо, правда" +title = "Ты ужасный человек" +description = "Непостижимо, правда" [advancement.challenge_turtle_egg_smasher] - title = "Крушитель черепашьих яиц!" - description = "Разбейте 100 черепашьих яиц" +title = "Крушитель черепашьих яиц!" +description = "Разбейте 100 черепашьих яиц" [advancement.challenge_turtle_egg_annihilator] - title = "Уничтожитель черепашьих яиц!" - description = "Разбейте 500 черепашьих яиц" +title = "Уничтожитель черепашьих яиц!" +description = "Разбейте 500 черепашьих яиц" [advancement.challenge_novice_hunter] - title = "Начинающий охотник!" - description = "Убейте 100 существ" +title = "Начинающий охотник!" +description = "Убейте 100 существ" [advancement.challenge_intermediate_hunter] - title = "Продвинутый охотник!" - description = "Убейте 500 существ" +title = "Продвинутый охотник!" +description = "Убейте 500 существ" [advancement.challenge_advanced_hunter] - title = "Опытный охотник!" - description = "Убейте 5 000 существ" +title = "Опытный охотник!" +description = "Убейте 5 000 существ" [advancement.challenge_creeper_conqueror] - title = "Покоритель криперов!" - description = "Убейте 50 криперов" +title = "Покоритель криперов!" +description = "Убейте 50 криперов" [advancement.challenge_creeper_annihilator] - title = "Уничтожитель криперов!" - description = "Убейте 200 криперов" +title = "Уничтожитель криперов!" +description = "Убейте 200 криперов" [advancement.challenge_pickaxe_1k] - title = "Начинающий шахтёр" - description = "Сломайте 1 000 блоков" +title = "Начинающий шахтёр" +description = "Сломайте 1 000 блоков" [advancement.challenge_pickaxe_5k] - title = "Опытный шахтёр" - description = "Сломайте 5 000 блоков" +title = "Опытный шахтёр" +description = "Сломайте 5 000 блоков" [advancement.challenge_pickaxe_50k] - title = "Шахтёр-эксперт" - description = "Сломайте 50 000 блоков" +title = "Шахтёр-эксперт" +description = "Сломайте 50 000 блоков" [advancement.challenge_pickaxe_500k] - title = "Мастер-шахтёр" - description = "Сломайте 500 000 блоков" +title = "Мастер-шахтёр" +description = "Сломайте 500 000 блоков" [advancement.challenge_pickaxe_5m] - title = "Легендарный шахтёр" - description = "Сломайте 5 000 000 блоков" +title = "Легендарный шахтёр" +description = "Сломайте 5 000 000 блоков" [advancement.challenge_eat_100] - title = "Сколько всего вкусного!" - description = "Съешьте более 100 предметов!" +title = "Сколько всего вкусного!" +description = "Съешьте более 100 предметов!" [advancement.challenge_eat_1000] - title = "Неутолимый голод!" - description = "Съешьте более 1 000 предметов!" +title = "Неутолимый голод!" +description = "Съешьте более 1 000 предметов!" [advancement.challenge_eat_10000] - title = "ВЕЧНЫЙ ГОЛОД!" - description = "Съешьте более 10 000 предметов!" +title = "ВЕЧНЫЙ ГОЛОД!" +description = "Съешьте более 10 000 предметов!" [advancement.challenge_harvest_100] - title = "Полный урожай" - description = "Соберите более 100 культур!" +title = "Полный урожай" +description = "Соберите более 100 культур!" [advancement.challenge_harvest_1000] - title = "Великий урожай" - description = "Соберите более 1 000 культур!" +title = "Великий урожай" +description = "Соберите более 1 000 культур!" [advancement.challenge_swim_1nm] - title = "Человек-подлодка!" - description = "Проплывите 1 морскую милю (1 852 блока)" +title = "Человек-подлодка!" +description = "Проплывите 1 морскую милю (1 852 блока)" [advancement.challenge_sneak_1k] - title = "Больные колени" - description = "Прокрадитесь более километра (1 000 блоков)" +title = "Больные колени" +description = "Прокрадитесь более километра (1 000 блоков)" [advancement.challenge_sneak_5k] - title = "Ходок Теней" - description = "Прокрадитесь более 5 000 блоков" +title = "Ходок Теней" +description = "Прокрадитесь более 5 000 блоков" [advancement.challenge_sneak_20k] - title = "Призрак" - description = "Прокрадитесь более 20 000 блоков" +title = "Призрак" +description = "Прокрадитесь более 20 000 блоков" [advancement.challenge_swim_5k] - title = "Глубоководный Ныряльщик" - description = "Проплывите более 5 000 блоков" +title = "Глубоководный Ныряльщик" +description = "Проплывите более 5 000 блоков" [advancement.challenge_swim_20k] - title = "Избранник Посейдона" - description = "Проплывите более 20 000 блоков" +title = "Избранник Посейдона" +description = "Проплывите более 20 000 блоков" [advancement.challenge_sword_100] - title = "Первая Кровь" - description = "Нанесите 100 ударов мечом" +title = "Первая Кровь" +description = "Нанесите 100 ударов мечом" [advancement.challenge_sword_1k] - title = "Танцор Клинка" - description = "Нанесите 1 000 ударов мечом" +title = "Танцор Клинка" +description = "Нанесите 1 000 ударов мечом" [advancement.challenge_sword_10k] - title = "Тысяча Порезов" - description = "Нанесите 10 000 ударов мечом" +title = "Тысяча Порезов" +description = "Нанесите 10 000 ударов мечом" [advancement.challenge_unarmed_100] - title = "Барный Драчун" - description = "Нанесите 100 ударов голыми руками" +title = "Барный Драчун" +description = "Нанесите 100 ударов голыми руками" [advancement.challenge_unarmed_1k] - title = "Железные Кулаки" - description = "Нанесите 1 000 ударов голыми руками" +title = "Железные Кулаки" +description = "Нанесите 1 000 ударов голыми руками" [advancement.challenge_unarmed_10k] - title = "Один Удар" - description = "Нанесите 10 000 ударов голыми руками" +title = "Один Удар" +description = "Нанесите 10 000 ударов голыми руками" [advancement.challenge_trag_1k] - title = "Кровавая Цена" - description = "Получите 1 000 урона" +title = "Кровавая Цена" +description = "Получите 1 000 урона" [advancement.challenge_trag_10k] - title = "Багровый Прилив" - description = "Получите 10 000 урона" +title = "Багровый Прилив" +description = "Получите 10 000 урона" [advancement.challenge_trag_100k] - title = "Аватар Страдания" - description = "Получите 100 000 урона" +title = "Аватар Страдания" +description = "Получите 100 000 урона" [advancement.challenge_ranged_100] - title = "Учебная Стрельба" - description = "Выпустите 100 снарядов" +title = "Учебная Стрельба" +description = "Выпустите 100 снарядов" [advancement.challenge_ranged_1k] - title = "Соколиный Глаз" - description = "Выпустите 1 000 снарядов" +title = "Соколиный Глаз" +description = "Выпустите 1 000 снарядов" [advancement.challenge_ranged_10k] - title = "Буря Стрел" - description = "Выпустите 10 000 снарядов" +title = "Буря Стрел" +description = "Выпустите 10 000 снарядов" [advancement.challenge_chronos_1h] - title = "Тик-Так" - description = "Проведите 1 час в сети" +title = "Тик-Так" +description = "Проведите 1 час в сети" [advancement.challenge_chronos_24h] - title = "Пески Времени" - description = "Проведите 24 часа в сети" +title = "Пески Времени" +description = "Проведите 24 часа в сети" [advancement.challenge_chronos_168h] - title = "Вне Времени" - description = "Проведите 168 часов (1 неделю) в сети" +title = "Вне Времени" +description = "Проведите 168 часов (1 неделю) в сети" [advancement.challenge_nether_50] - title = "Привратник Ада" - description = "Убейте 50 существ Нижнего мира" +title = "Привратник Ада" +description = "Убейте 50 существ Нижнего мира" [advancement.challenge_nether_500] - title = "Страж Бездны" - description = "Убейте 500 существ Нижнего мира" +title = "Страж Бездны" +description = "Убейте 500 существ Нижнего мира" [advancement.challenge_nether_5k] - title = "Повелитель Нижнего Мира" - description = "Убейте 5 000 существ Нижнего мира" +title = "Повелитель Нижнего Мира" +description = "Убейте 5 000 существ Нижнего мира" [advancement.challenge_rift_50] - title = "Пространственная Аномалия" - description = "Телепортируйтесь 50 раз" +title = "Пространственная Аномалия" +description = "Телепортируйтесь 50 раз" [advancement.challenge_rift_500] - title = "Ходок Пустоты" - description = "Телепортируйтесь 500 раз" +title = "Ходок Пустоты" +description = "Телепортируйтесь 500 раз" [advancement.challenge_rift_5k] - title = "Между Мирами" - description = "Телепортируйтесь 5 000 раз" +title = "Между Мирами" +description = "Телепортируйтесь 5 000 раз" [advancement.challenge_taming_10] - title = "Шептун Зверей" - description = "Разведите 10 животных" +title = "Шептун Зверей" +description = "Разведите 10 животных" [advancement.challenge_taming_50] - title = "Вожак Стаи" - description = "Разведите 50 животных" +title = "Вожак Стаи" +description = "Разведите 50 животных" [advancement.challenge_taming_500] - title = "Повелитель Зверей" - description = "Разведите 500 животных" +title = "Повелитель Зверей" +description = "Разведите 500 животных" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Демон Скорости" - description = "Пробегите более 5 километров (5,000 блоков)" +title = "Демон Скорости" +description = "Пробегите более 5 километров (5,000 блоков)" [advancement.challenge_sprint_dist_50k] - title = "Молниеносные Ноги" - description = "Пробегите более 50 километров (50,000 блоков)" +title = "Молниеносные Ноги" +description = "Пробегите более 50 километров (50,000 блоков)" [advancement.challenge_agility_swim_1k] - title = "Водомерка" - description = "Проплывите более 1 километра (1,000 блоков)" +title = "Водомерка" +description = "Проплывите более 1 километра (1,000 блоков)" [advancement.challenge_agility_swim_10k] - title = "Морской Странник" - description = "Проплывите более 10 километров (10,000 блоков)" +title = "Морской Странник" +description = "Проплывите более 10 километров (10,000 блоков)" [advancement.challenge_fly_1k] - title = "Танцор Небес" - description = "Пролетите более 1 километра (1,000 блоков)" +title = "Танцор Небес" +description = "Пролетите более 1 километра (1,000 блоков)" [advancement.challenge_fly_10k] - title = "Наездник Ветра" - description = "Пролетите более 10 километров (10,000 блоков)" +title = "Наездник Ветра" +description = "Пролетите более 10 километров (10,000 блоков)" [advancement.challenge_agility_sneak_500] - title = "Тихие Шаги" - description = "Прокрадитесь более 500 блоков" +title = "Тихие Шаги" +description = "Прокрадитесь более 500 блоков" [advancement.challenge_agility_sneak_5k] - title = "Призрачная Поступь" - description = "Прокрадитесь более 5 километров (5,000 блоков)" +title = "Призрачная Поступь" +description = "Прокрадитесь более 5 километров (5,000 блоков)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Бригада Сноса" - description = "Сломайте 500 блоков" +title = "Бригада Сноса" +description = "Сломайте 500 блоков" [advancement.challenge_demolish_5k] - title = "Шар-Разрушитель" - description = "Сломайте 5,000 блоков" +title = "Шар-Разрушитель" +description = "Сломайте 5,000 блоков" [advancement.challenge_value_placed_10k] - title = "Ценный Строитель" - description = "Разместите блоки стоимостью 10,000" +title = "Ценный Строитель" +description = "Разместите блоки стоимостью 10,000" [advancement.challenge_value_placed_100k] - title = "Мастер Архитектор" - description = "Разместите блоки стоимостью 100,000" +title = "Мастер Архитектор" +description = "Разместите блоки стоимостью 100,000" [advancement.challenge_demolish_val_5k] - title = "Специалист по Утилизации" - description = "Утилизируйте блоки стоимостью 5,000 при сносе" +title = "Специалист по Утилизации" +description = "Утилизируйте блоки стоимостью 5,000 при сносе" [advancement.challenge_demolish_val_50k] - title = "Полная Деконструкция" - description = "Утилизируйте блоки стоимостью 50,000 при сносе" +title = "Полная Деконструкция" +description = "Утилизируйте блоки стоимостью 50,000 при сносе" [advancement.challenge_high_build_100] - title = "Высотный Строитель" - description = "Разместите 100 блоков выше Y=128" +title = "Высотный Строитель" +description = "Разместите 100 блоков выше Y=128" [advancement.challenge_high_build_1k] - title = "Облачный Архитектор" - description = "Разместите 1,000 блоков выше Y=128" +title = "Облачный Архитектор" +description = "Разместите 1,000 блоков выше Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Дровосек" - description = "Взмахните топором 500 раз" +title = "Дровосек" +description = "Взмахните топором 500 раз" [advancement.challenge_axe_swing_5k] - title = "Берсерк" - description = "Взмахните топором 5,000 раз" +title = "Берсерк" +description = "Взмахните топором 5,000 раз" [advancement.challenge_axe_damage_1k] - title = "Рассекатель" - description = "Нанесите 1,000 урона топорами" +title = "Рассекатель" +description = "Нанесите 1,000 урона топорами" [advancement.challenge_axe_damage_10k] - title = "Топор Палача" - description = "Нанесите 10,000 урона топорами" +title = "Топор Палача" +description = "Нанесите 10,000 урона топорами" [advancement.challenge_axe_value_5k] - title = "Торговец Древесиной" - description = "Нарубите древесины стоимостью 5,000" +title = "Торговец Древесиной" +description = "Нарубите древесины стоимостью 5,000" [advancement.challenge_axe_value_50k] - title = "Лесной Барон" - description = "Нарубите древесины стоимостью 50,000" +title = "Лесной Барон" +description = "Нарубите древесины стоимостью 50,000" [advancement.challenge_leaves_500] - title = "Воздуходув" - description = "Срубите 500 блоков листвы топором" +title = "Воздуходув" +description = "Срубите 500 блоков листвы топором" [advancement.challenge_leaves_5k] - title = "Листобой" - description = "Срубите 5,000 блоков листвы топором" +title = "Листобой" +description = "Срубите 5,000 блоков листвы топором" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Поглотитель Урона" - description = "Заблокируйте 1,000 урона щитом" +title = "Поглотитель Урона" +description = "Заблокируйте 1,000 урона щитом" [advancement.challenge_block_dmg_10k] - title = "Живой Щит" - description = "Заблокируйте 10,000 урона щитом" +title = "Живой Щит" +description = "Заблокируйте 10,000 урона щитом" [advancement.challenge_block_proj_100] - title = "Отражатель Стрел" - description = "Заблокируйте 100 снарядов щитом" +title = "Отражатель Стрел" +description = "Заблокируйте 100 снарядов щитом" [advancement.challenge_block_proj_1k] - title = "Снарядный Щит" - description = "Заблокируйте 1,000 снарядов щитом" +title = "Снарядный Щит" +description = "Заблокируйте 1,000 снарядов щитом" [advancement.challenge_block_melee_500] - title = "Мастер Парирования" - description = "Заблокируйте 500 атак ближнего боя щитом" +title = "Мастер Парирования" +description = "Заблокируйте 500 атак ближнего боя щитом" [advancement.challenge_block_melee_5k] - title = "Железная Крепость" - description = "Заблокируйте 5,000 атак ближнего боя щитом" +title = "Железная Крепость" +description = "Заблокируйте 5,000 атак ближнего боя щитом" [advancement.challenge_block_heavy_50] - title = "Танк" - description = "Заблокируйте 50 тяжёлых атак (свыше 5 урона)" +title = "Танк" +description = "Заблокируйте 50 тяжёлых атак (свыше 5 урона)" [advancement.challenge_block_heavy_500] - title = "Недвижимый Объект" - description = "Заблокируйте 500 тяжёлых атак (свыше 5 урона)" +title = "Недвижимый Объект" +description = "Заблокируйте 500 тяжёлых атак (свыше 5 урона)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Пивоварня" - description = "Установите 10 варочных стоек" +title = "Пивоварня" +description = "Установите 10 варочных стоек" [advancement.challenge_brew_stands_50] - title = "Фабрика Зелий" - description = "Установите 50 варочных стоек" +title = "Фабрика Зелий" +description = "Установите 50 варочных стоек" [advancement.challenge_brew_strong_25] - title = "Крепкое Зелье" - description = "Выпейте 25 усиленных зелий" +title = "Крепкое Зелье" +description = "Выпейте 25 усиленных зелий" [advancement.challenge_brew_strong_250] - title = "Максимальная Мощь" - description = "Выпейте 250 усиленных зелий" +title = "Максимальная Мощь" +description = "Выпейте 250 усиленных зелий" [advancement.challenge_brew_splash_hits_50] - title = "Зона Брызг" - description = "Попадите в 50 существ взрывными зельями" +title = "Зона Брызг" +description = "Попадите в 50 существ взрывными зельями" [advancement.challenge_brew_splash_hits_500] - title = "Чумной Доктор" - description = "Попадите в 500 существ взрывными зельями" +title = "Чумной Доктор" +description = "Попадите в 500 существ взрывными зельями" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Неугомонный" - description = "Пройдите 1 километр в активном состоянии" +title = "Неугомонный" +description = "Пройдите 1 километр в активном состоянии" [advancement.challenge_active_dist_10k] - title = "Следопыт" - description = "Пройдите 10 километров в активном состоянии" +title = "Следопыт" +description = "Пройдите 10 километров в активном состоянии" [advancement.challenge_active_dist_100k] - title = "Вечное Движение" - description = "Пройдите 100 километров в активном состоянии" +title = "Вечное Движение" +description = "Пройдите 100 километров в активном состоянии" [advancement.challenge_beds_10] - title = "Ранняя Пташка" - description = "Поспите в кровати 10 раз" +title = "Ранняя Пташка" +description = "Поспите в кровати 10 раз" [advancement.challenge_beds_100] - title = "Прыгун во Времени" - description = "Поспите в кровати 100 раз" +title = "Прыгун во Времени" +description = "Поспите в кровати 100 раз" [advancement.challenge_chronos_tp_50] - title = "Временной Сдвиг" - description = "Телепортируйтесь 50 раз" +title = "Временной Сдвиг" +description = "Телепортируйтесь 50 раз" [advancement.challenge_chronos_tp_500] - title = "Искривление Времени" - description = "Телепортируйтесь 500 раз" +title = "Искривление Времени" +description = "Телепортируйтесь 500 раз" [advancement.challenge_chronos_deaths_10] - title = "Смертный" - description = "Умрите 10 раз" +title = "Смертный" +description = "Умрите 10 раз" [advancement.challenge_chronos_deaths_100] - title = "Бросивший Вызов Смерти" - description = "Умрите 100 раз" +title = "Бросивший Вызов Смерти" +description = "Умрите 100 раз" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Ценный Крафт" - description = "Скрафтите предметы общей стоимостью 10,000" +title = "Ценный Крафт" +description = "Скрафтите предметы общей стоимостью 10,000" [advancement.challenge_craft_value_100k] - title = "Ремесленник" - description = "Скрафтите предметы общей стоимостью 100,000" +title = "Ремесленник" +description = "Скрафтите предметы общей стоимостью 100,000" [advancement.challenge_craft_tools_25] - title = "Инструментальщик" - description = "Скрафтите 25 инструментов" +title = "Инструментальщик" +description = "Скрафтите 25 инструментов" [advancement.challenge_craft_tools_250] - title = "Мастер Ковки" - description = "Скрафтите 250 инструментов" +title = "Мастер Ковки" +description = "Скрафтите 250 инструментов" [advancement.challenge_craft_armor_25] - title = "Бронник" - description = "Скрафтите 25 единиц брони" +title = "Бронник" +description = "Скрафтите 25 единиц брони" [advancement.challenge_craft_armor_250] - title = "Мастер Бронник" - description = "Скрафтите 250 единиц брони" +title = "Мастер Бронник" +description = "Скрафтите 250 единиц брони" [advancement.challenge_craft_mass_25k] - title = "Массовое Производство" - description = "Скрафтите 25,000 предметов" +title = "Массовое Производство" +description = "Скрафтите 25,000 предметов" [advancement.challenge_craft_mass_250k] - title = "Промышленная Революция" - description = "Скрафтите 250,000 предметов" +title = "Промышленная Революция" +description = "Скрафтите 250,000 предметов" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Собиратель" - description = "Откройте 50 уникальных предметов" +title = "Собиратель" +description = "Откройте 50 уникальных предметов" [advancement.challenge_discover_items_250] - title = "Каталогизатор" - description = "Откройте 250 уникальных предметов" +title = "Каталогизатор" +description = "Откройте 250 уникальных предметов" [advancement.challenge_discover_blocks_50] - title = "Исследователь" - description = "Откройте 50 уникальных блоков" +title = "Исследователь" +description = "Откройте 50 уникальных блоков" [advancement.challenge_discover_blocks_250] - title = "Геолог" - description = "Откройте 250 уникальных блоков" +title = "Геолог" +description = "Откройте 250 уникальных блоков" [advancement.challenge_discover_mobs_25] - title = "Наблюдатель" - description = "Откройте 25 уникальных мобов" +title = "Наблюдатель" +description = "Откройте 25 уникальных мобов" [advancement.challenge_discover_mobs_75] - title = "Натуралист" - description = "Откройте 75 уникальных мобов" +title = "Натуралист" +description = "Откройте 75 уникальных мобов" [advancement.challenge_discover_biomes_10] - title = "Странник" - description = "Откройте 10 уникальных биомов" +title = "Странник" +description = "Откройте 10 уникальных биомов" [advancement.challenge_discover_biomes_40] - title = "Путешественник" - description = "Откройте 40 уникальных биомов" +title = "Путешественник" +description = "Откройте 40 уникальных биомов" [advancement.challenge_discover_foods_10] - title = "Гурман" - description = "Откройте 10 уникальных блюд" +title = "Гурман" +description = "Откройте 10 уникальных блюд" [advancement.challenge_discover_foods_30] - title = "Мастер Кулинарии" - description = "Откройте 30 уникальных блюд" +title = "Мастер Кулинарии" +description = "Откройте 30 уникальных блюд" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Ткач Силы" - description = "Накопите 100 силы зачарования" +title = "Ткач Силы" +description = "Накопите 100 силы зачарования" [advancement.challenge_enchant_power_1k] - title = "Магистр Тайных Сил" - description = "Накопите 1,000 силы зачарования" +title = "Магистр Тайных Сил" +description = "Накопите 1,000 силы зачарования" [advancement.challenge_enchant_levels_1k] - title = "Расточитель Уровней" - description = "Потратьте 1,000 уровней опыта на зачарование" +title = "Расточитель Уровней" +description = "Потратьте 1,000 уровней опыта на зачарование" [advancement.challenge_enchant_levels_10k] - title = "Поглотитель Опыта" - description = "Потратьте 10,000 уровней опыта на зачарование" +title = "Поглотитель Опыта" +description = "Потратьте 10,000 уровней опыта на зачарование" [advancement.challenge_enchant_high_25] - title = "По-Крупному" - description = "Выполните 25 зачарований максимального уровня" +title = "По-Крупному" +description = "Выполните 25 зачарований максимального уровня" [advancement.challenge_enchant_high_250] - title = "Легендарный Зачарователь" - description = "Выполните 250 зачарований максимального уровня" +title = "Легендарный Зачарователь" +description = "Выполните 250 зачарований максимального уровня" [advancement.challenge_enchant_total_500] - title = "Сжигатель Уровней" - description = "Потратьте 500 уровней на все зачарования" +title = "Сжигатель Уровней" +description = "Потратьте 500 уровней на все зачарования" [advancement.challenge_enchant_total_5k] - title = "Магические Вложения" - description = "Потратьте 5,000 уровней на все зачарования" +title = "Магические Вложения" +description = "Потратьте 5,000 уровней на все зачарования" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Копатель" - description = "Взмахните лопатой 500 раз" +title = "Копатель" +description = "Взмахните лопатой 500 раз" [advancement.challenge_dig_swing_5k] - title = "Экскаватор" - description = "Взмахните лопатой 5,000 раз" +title = "Экскаватор" +description = "Взмахните лопатой 5,000 раз" [advancement.challenge_dig_damage_1k] - title = "Рыцарь Лопаты" - description = "Нанесите 1,000 урона лопатой" +title = "Рыцарь Лопаты" +description = "Нанесите 1,000 урона лопатой" [advancement.challenge_dig_damage_10k] - title = "Мастер Лопаты" - description = "Нанесите 10,000 урона лопатой" +title = "Мастер Лопаты" +description = "Нанесите 10,000 урона лопатой" [advancement.challenge_dig_value_5k] - title = "Торговец Грунтом" - description = "Выкопайте блоки стоимостью 5,000" +title = "Торговец Грунтом" +description = "Выкопайте блоки стоимостью 5,000" [advancement.challenge_dig_value_50k] - title = "Земляной Барон" - description = "Выкопайте блоки стоимостью 50,000" +title = "Земляной Барон" +description = "Выкопайте блоки стоимостью 50,000" [advancement.challenge_dig_gravel_500] - title = "Гравийный Дробильщик" - description = "Выкопайте 500 блоков гравия, песка или глины" +title = "Гравийный Дробильщик" +description = "Выкопайте 500 блоков гравия, песка или глины" [advancement.challenge_dig_gravel_5k] - title = "Просеиватель Песка" - description = "Выкопайте 5,000 блоков гравия, песка или глины" +title = "Просеиватель Песка" +description = "Выкопайте 5,000 блоков гравия, песка или глины" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Сеятель" - description = "Посадите 100 культур" +title = "Сеятель" +description = "Посадите 100 культур" [advancement.challenge_plant_1k] - title = "Зелёный Палец" - description = "Посадите 1,000 культур" +title = "Зелёный Палец" +description = "Посадите 1,000 культур" [advancement.challenge_plant_5k] - title = "Аграрный Барон" - description = "Посадите 5,000 культур" +title = "Аграрный Барон" +description = "Посадите 5,000 культур" [advancement.challenge_compost_50] - title = "Переработчик" - description = "Компостируйте 50 предметов" +title = "Переработчик" +description = "Компостируйте 50 предметов" [advancement.challenge_compost_500] - title = "Обогатитель Почвы" - description = "Компостируйте 500 предметов" +title = "Обогатитель Почвы" +description = "Компостируйте 500 предметов" [advancement.challenge_shear_50] - title = "Стригаль" - description = "Остригите 50 существ" +title = "Стригаль" +description = "Остригите 50 существ" [advancement.challenge_shear_250] - title = "Мастер Стада" - description = "Остригите 250 существ" +title = "Мастер Стада" +description = "Остригите 250 существ" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Истребитель" - description = "Уничтожьте 500 существ" +title = "Истребитель" +description = "Уничтожьте 500 существ" [advancement.challenge_kills_5k] - title = "Палач" - description = "Уничтожьте 5,000 существ" +title = "Палач" +description = "Уничтожьте 5,000 существ" [advancement.challenge_boss_1] - title = "Претендент на Босса" - description = "Убейте босс-моба" +title = "Претендент на Босса" +description = "Убейте босс-моба" [advancement.challenge_boss_10] - title = "Убийца Легенд" - description = "Убейте 10 босс-мобов" +title = "Убийца Легенд" +description = "Убейте 10 босс-мобов" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Иссушённый" - description = "Перенесите 500 урона от иссушения" +title = "Иссушённый" +description = "Перенесите 500 урона от иссушения" [advancement.challenge_wither_dmg_5k] - title = "Выживший Среди Скверны" - description = "Перенесите 5,000 урона от иссушения" +title = "Выживший Среди Скверны" +description = "Перенесите 5,000 урона от иссушения" [advancement.challenge_wither_skel_25] - title = "Собиратель Костей" - description = "Убейте 25 скелетов-иссушителей" +title = "Собиратель Костей" +description = "Убейте 25 скелетов-иссушителей" [advancement.challenge_wither_skel_250] - title = "Бич Скелетов" - description = "Убейте 250 скелетов-иссушителей" +title = "Бич Скелетов" +description = "Убейте 250 скелетов-иссушителей" [advancement.challenge_wither_boss_1] - title = "Победитель Визера" - description = "Победите Визера" +title = "Победитель Визера" +description = "Победите Визера" [advancement.challenge_wither_boss_10] - title = "Властелин Незера" - description = "Победите Визера 10 раз" +title = "Властелин Незера" +description = "Победите Визера 10 раз" [advancement.challenge_roses_10] - title = "Садовник Смерти" - description = "Сломайте 10 роз иссушения" +title = "Садовник Смерти" +description = "Сломайте 10 роз иссушения" [advancement.challenge_roses_100] - title = "Собиратель Скверны" - description = "Сломайте 100 роз иссушения" +title = "Собиратель Скверны" +description = "Сломайте 100 роз иссушения" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Рука Шахтёра" - description = "Взмахните киркой 500 раз" +title = "Рука Шахтёра" +description = "Взмахните киркой 500 раз" [advancement.challenge_pick_swing_5k] - title = "Строитель Туннелей" - description = "Взмахните киркой 5,000 раз" +title = "Строитель Туннелей" +description = "Взмахните киркой 5,000 раз" [advancement.challenge_pick_damage_1k] - title = "Боевая Кирка" - description = "Нанесите 1,000 урона киркой" +title = "Боевая Кирка" +description = "Нанесите 1,000 урона киркой" [advancement.challenge_pick_damage_10k] - title = "Военный Клевец" - description = "Нанесите 10,000 урона киркой" +title = "Военный Клевец" +description = "Нанесите 10,000 урона киркой" [advancement.challenge_pick_value_5k] - title = "Искатель Самоцветов" - description = "Добудьте блоки стоимостью 5,000" +title = "Искатель Самоцветов" +description = "Добудьте блоки стоимостью 5,000" [advancement.challenge_pick_value_50k] - title = "Рудный Барон" - description = "Добудьте блоки стоимостью 50,000" +title = "Рудный Барон" +description = "Добудьте блоки стоимостью 50,000" [advancement.challenge_pick_ores_500] - title = "Старатель" - description = "Добудьте 500 рудных блоков" +title = "Старатель" +description = "Добудьте 500 рудных блоков" [advancement.challenge_pick_ores_5k] - title = "Мастер Шахтёр" - description = "Добудьте 5,000 рудных блоков" +title = "Мастер Шахтёр" +description = "Добудьте 5,000 рудных блоков" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Меткий Стрелок" - description = "Нанесите 1,000 дальнего урона" +title = "Меткий Стрелок" +description = "Нанесите 1,000 дальнего урона" [advancement.challenge_ranged_dmg_10k] - title = "Смертоносный Лучник" - description = "Нанесите 10,000 дальнего урона" +title = "Смертоносный Лучник" +description = "Нанесите 10,000 дальнего урона" [advancement.challenge_ranged_dist_5k] - title = "Дальнобойный" - description = "Выпустите снаряды на общую дистанцию 5,000 блоков" +title = "Дальнобойный" +description = "Выпустите снаряды на общую дистанцию 5,000 блоков" [advancement.challenge_ranged_dist_50k] - title = "Километровый Выстрел" - description = "Выпустите снаряды на общую дистанцию 50,000 блоков" +title = "Километровый Выстрел" +description = "Выпустите снаряды на общую дистанцию 50,000 блоков" [advancement.challenge_ranged_kills_50] - title = "Лучник" - description = "Убейте 50 мобов дальнобойным оружием" +title = "Лучник" +description = "Убейте 50 мобов дальнобойным оружием" [advancement.challenge_ranged_kills_500] - title = "Мастер Лучник" - description = "Убейте 500 мобов дальнобойным оружием" +title = "Мастер Лучник" +description = "Убейте 500 мобов дальнобойным оружием" [advancement.challenge_longshot_25] - title = "Снайпер" - description = "Совершите 25 дальних попаданий (свыше 30 блоков)" +title = "Снайпер" +description = "Совершите 25 дальних попаданий (свыше 30 блоков)" [advancement.challenge_longshot_250] - title = "Орлиный Глаз" - description = "Совершите 250 дальних попаданий (свыше 30 блоков)" +title = "Орлиный Глаз" +description = "Совершите 250 дальних попаданий (свыше 30 блоков)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Метатель Жемчуга" - description = "Бросьте 50 жемчужин Края" +title = "Метатель Жемчуга" +description = "Бросьте 50 жемчужин Края" [advancement.challenge_rift_pearls_500] - title = "Телепорт-Маньяк" - description = "Бросьте 500 жемчужин Края" +title = "Телепорт-Маньяк" +description = "Бросьте 500 жемчужин Края" [advancement.challenge_rift_enderman_50] - title = "Охотник на Эндерменов" - description = "Убейте 50 эндерменов" +title = "Охотник на Эндерменов" +description = "Убейте 50 эндерменов" [advancement.challenge_rift_enderman_500] - title = "Преследователь Пустоты" - description = "Убейте 500 эндерменов" +title = "Преследователь Пустоты" +description = "Убейте 500 эндерменов" [advancement.challenge_rift_dragon_500] - title = "Боец с Драконом" - description = "Нанесите 500 урона Дракону Края" +title = "Боец с Драконом" +description = "Нанесите 500 урона Дракону Края" [advancement.challenge_rift_dragon_5k] - title = "Драконоборец" - description = "Нанесите 5,000 урона Дракону Края" +title = "Драконоборец" +description = "Нанесите 5,000 урона Дракону Края" [advancement.challenge_rift_crystal_10] - title = "Разрушитель Кристаллов" - description = "Уничтожьте 10 кристаллов Края" +title = "Разрушитель Кристаллов" +description = "Уничтожьте 10 кристаллов Края" [advancement.challenge_rift_crystal_100] - title = "Демонтажник Края" - description = "Уничтожьте 100 кристаллов Края" +title = "Демонтажник Края" +description = "Уничтожьте 100 кристаллов Края" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Рыболов" - description = "Поймайте 25 рыб" +title = "Рыболов" +description = "Поймайте 25 рыб" [advancement.challenge_fish_250] - title = "Мастер Рыбалки" - description = "Поймайте 250 рыб" +title = "Мастер Рыбалки" +description = "Поймайте 250 рыб" [advancement.challenge_drowned_25] - title = "Охотник на Утопленников" - description = "Убейте 25 утопленников" +title = "Охотник на Утопленников" +description = "Убейте 25 утопленников" [advancement.challenge_drowned_250] - title = "Чистильщик Океана" - description = "Убейте 250 утопленников" +title = "Чистильщик Океана" +description = "Убейте 250 утопленников" [advancement.challenge_guardian_10] - title = "Убийца Стражей" - description = "Убейте 10 стражей" +title = "Убийца Стражей" +description = "Убейте 10 стражей" [advancement.challenge_guardian_100] - title = "Расхититель Храмов" - description = "Убейте 100 стражей" +title = "Расхититель Храмов" +description = "Убейте 100 стражей" [advancement.challenge_underwater_blocks_100] - title = "Подводный Шахтёр" - description = "Сломайте 100 блоков под водой" +title = "Подводный Шахтёр" +description = "Сломайте 100 блоков под водой" [advancement.challenge_underwater_blocks_1k] - title = "Подводный Инженер" - description = "Сломайте 1,000 блоков под водой" +title = "Подводный Инженер" +description = "Сломайте 1,000 блоков под водой" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Удар в Спину" - description = "Нанесите 500 урона крадучись" +title = "Удар в Спину" +description = "Нанесите 500 урона крадучись" [advancement.challenge_stealth_dmg_5k] - title = "Бесшумный Убийца" - description = "Нанесите 5,000 урона крадучись" +title = "Бесшумный Убийца" +description = "Нанесите 5,000 урона крадучись" [advancement.challenge_stealth_kills_10] - title = "Ассасин" - description = "Убейте 10 мобов крадучись" +title = "Ассасин" +description = "Убейте 10 мобов крадучись" [advancement.challenge_stealth_kills_100] - title = "Теневой Жнец" - description = "Убейте 100 мобов крадучись" +title = "Теневой Жнец" +description = "Убейте 100 мобов крадучись" [advancement.challenge_stealth_time_1h] - title = "Терпеливый" - description = "Проведите 1 час крадучись (3,600 секунд)" +title = "Терпеливый" +description = "Проведите 1 час крадучись (3,600 секунд)" [advancement.challenge_stealth_time_10h] - title = "Повелитель Теней" - description = "Проведите 10 часов крадучись (36,000 секунд)" +title = "Повелитель Теней" +description = "Проведите 10 часов крадучись (36,000 секунд)" [advancement.challenge_stealth_arrows_50] - title = "Тихий Лучник" - description = "Выпустите 50 стрел крадучись" +title = "Тихий Лучник" +description = "Выпустите 50 стрел крадучись" [advancement.challenge_stealth_arrows_500] - title = "Призрачный Стрелок" - description = "Выпустите 500 стрел крадучись" +title = "Призрачный Стрелок" +description = "Выпустите 500 стрел крадучись" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Ученик Меча" - description = "Нанесите 1,000 урона мечами" +title = "Ученик Меча" +description = "Нанесите 1,000 урона мечами" [advancement.challenge_sword_dmg_10k] - title = "Фехтовальщик" - description = "Нанесите 10,000 урона мечами" +title = "Фехтовальщик" +description = "Нанесите 10,000 урона мечами" [advancement.challenge_sword_kills_50] - title = "Дуэлянт" - description = "Убейте 50 мобов мечами" +title = "Дуэлянт" +description = "Убейте 50 мобов мечами" [advancement.challenge_sword_kills_500] - title = "Гладиатор" - description = "Убейте 500 мобов мечами" +title = "Гладиатор" +description = "Убейте 500 мобов мечами" [advancement.challenge_sword_crit_50] - title = "Критический Удар" - description = "Нанесите 50 критических ударов мечами" +title = "Критический Удар" +description = "Нанесите 50 критических ударов мечами" [advancement.challenge_sword_crit_500] - title = "Мастер Точности" - description = "Нанесите 500 критических ударов мечами" +title = "Мастер Точности" +description = "Нанесите 500 критических ударов мечами" [advancement.challenge_sword_heavy_25] - title = "Тяжёлый Удар" - description = "Нанесите 25 тяжёлых ударов мечами (свыше 8 урона)" +title = "Тяжёлый Удар" +description = "Нанесите 25 тяжёлых ударов мечами (свыше 8 урона)" [advancement.challenge_sword_heavy_250] - title = "Сокрушительный Удар" - description = "Нанесите 250 тяжёлых ударов мечами (свыше 8 урона)" +title = "Сокрушительный Удар" +description = "Нанесите 250 тяжёлых ударов мечами (свыше 8 урона)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Дрессировщик Зверей" - description = "Ваши питомцы наносят 500 общего урона" +title = "Дрессировщик Зверей" +description = "Ваши питомцы наносят 500 общего урона" [advancement.challenge_pet_dmg_5k] - title = "Военачальник" - description = "Ваши питомцы наносят 5,000 общего урона" +title = "Военачальник" +description = "Ваши питомцы наносят 5,000 общего урона" [advancement.challenge_tamed_10] - title = "Друг Животных" - description = "Приручите 10 животных" +title = "Друг Животных" +description = "Приручите 10 животных" [advancement.challenge_tamed_100] - title = "Смотритель Зоопарка" - description = "Приручите 100 животных" +title = "Смотритель Зоопарка" +description = "Приручите 100 животных" [advancement.challenge_pet_kills_25] - title = "Стайная Тактика" - description = "Ваши питомцы уничтожат 25 существ" +title = "Стайная Тактика" +description = "Ваши питомцы уничтожат 25 существ" [advancement.challenge_pet_kills_250] - title = "Командир Стаи" - description = "Ваши питомцы уничтожат 250 существ" +title = "Командир Стаи" +description = "Ваши питомцы уничтожат 250 существ" [advancement.challenge_taming_2500] - title = "Эксперт по Разведению" - description = "Разведите 2,500 животных" +title = "Эксперт по Разведению" +description = "Разведите 2,500 животных" [advancement.challenge_taming_25k] - title = "Мастер Генетики" - description = "Разведите 25,000 животных" +title = "Мастер Генетики" +description = "Разведите 25,000 животных" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Боксёрская Груша" - description = "Получите 500 ударов" +title = "Боксёрская Груша" +description = "Получите 500 ударов" [advancement.challenge_trag_hits_5k] - title = "Любитель Боли" - description = "Получите 5,000 ударов" +title = "Любитель Боли" +description = "Получите 5,000 ударов" [advancement.challenge_trag_deaths_10] - title = "Девять Жизней" - description = "Умрите 10 раз" +title = "Девять Жизней" +description = "Умрите 10 раз" [advancement.challenge_trag_deaths_100] - title = "Повторяющийся Кошмар" - description = "Умрите 100 раз" +title = "Повторяющийся Кошмар" +description = "Умрите 100 раз" [advancement.challenge_trag_fire_500] - title = "Жертва Огня" - description = "Перенесите 500 урона от огня" +title = "Жертва Огня" +description = "Перенесите 500 урона от огня" [advancement.challenge_trag_fire_5k] - title = "Феникс" - description = "Перенесите 5,000 урона от огня" +title = "Феникс" +description = "Перенесите 5,000 урона от огня" [advancement.challenge_trag_fall_500] - title = "Проверка Гравитации" - description = "Перенесите 500 урона от падения" +title = "Проверка Гравитации" +description = "Перенесите 500 урона от падения" [advancement.challenge_trag_fall_5k] - title = "Предельная Скорость" - description = "Перенесите 5,000 урона от падения" +title = "Предельная Скорость" +description = "Перенесите 5,000 урона от падения" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Драчун" - description = "Нанесите 1,000 урона голыми кулаками" +title = "Драчун" +description = "Нанесите 1,000 урона голыми кулаками" [advancement.challenge_unarmed_dmg_10k] - title = "Мастер Боевых Искусств" - description = "Нанесите 10,000 урона голыми кулаками" +title = "Мастер Боевых Искусств" +description = "Нанесите 10,000 урона голыми кулаками" [advancement.challenge_unarmed_kills_25] - title = "Голые Кулаки" - description = "Убейте 25 мобов голыми кулаками" +title = "Голые Кулаки" +description = "Убейте 25 мобов голыми кулаками" [advancement.challenge_unarmed_kills_250] - title = "Кулак Легенды" - description = "Убейте 250 мобов голыми кулаками" +title = "Кулак Легенды" +description = "Убейте 250 мобов голыми кулаками" [advancement.challenge_unarmed_crit_25] - title = "Критический Кулак" - description = "Нанесите 25 критических ударов голыми кулаками" +title = "Критический Кулак" +description = "Нанесите 25 критических ударов голыми кулаками" [advancement.challenge_unarmed_crit_250] - title = "Точный Кулак" - description = "Нанесите 250 критических ударов голыми кулаками" +title = "Точный Кулак" +description = "Нанесите 250 критических ударов голыми кулаками" [advancement.challenge_unarmed_heavy_25] - title = "Мощный Удар" - description = "Нанесите 25 тяжёлых ударов голыми кулаками (свыше 6 урона)" +title = "Мощный Удар" +description = "Нанесите 25 тяжёлых ударов голыми кулаками (свыше 6 урона)" [advancement.challenge_unarmed_heavy_250] - title = "Король Нокаутов" - description = "Нанесите 250 тяжёлых ударов голыми кулаками (свыше 6 урона)" +title = "Король Нокаутов" +description = "Нанесите 250 тяжёлых ударов голыми кулаками (свыше 6 урона)" # items [items] [items.bound_ender_peral] - name = "Портключ Реликвария" - usage1 = "Shift + ЛКМ для привязки" - usage2 = "ПКМ для доступа к привязанному инвентарю" +name = "Портключ Реликвария" +usage1 = "Shift + ЛКМ для привязки" +usage2 = "ПКМ для доступа к привязанному инвентарю" [items.bound_eye_of_ender] - name = "Зрительный якорь" - usage1 = "ПКМ, чтобы использовать и телепортироваться к привязанному месту" - usage2 = "Shift + ЛКМ для привязки к блоку" +name = "Зрительный якорь" +usage1 = "ПКМ, чтобы использовать и телепортироваться к привязанному месту" +usage2 = "Shift + ЛКМ для привязки к блоку" [items.bound_redstone_torch] - name = "Пульт редстоуна" - usage1 = "ПКМ для создания 1-тикового импульса редстоуна" - usage2 = "Shift + ЛКМ по целевому блоку для привязки" +name = "Пульт редстоуна" +usage1 = "ПКМ для создания 1-тикового импульса редстоуна" +usage2 = "Shift + ЛКМ по целевому блоку для привязки" [items.bound_snowball] - name = "Паутинная ловушка!" - usage1 = "Бросьте для создания временной паутинной ловушки" +name = "Паутинная ловушка!" +usage1 = "Бросьте для создания временной паутинной ловушки" [items.chrono_time_bottle] - name = "Время в бутылке" - usage1 = "Пассивно накапливает время, пока находится в инвентаре" - usage2 = "ПКМ по тикающим блокам или детёнышам, чтобы потратить накопленное время" - stored = "Накопленное время" +name = "Время в бутылке" +usage1 = "Пассивно накапливает время, пока находится в инвентаре" +usage2 = "ПКМ по тикающим блокам или детёнышам, чтобы потратить накопленное время" +stored = "Накопленное время" [items.chrono_time_bomb] - name = "Хронобомба" - usage1 = "ПКМ для запуска хроноснаряда, создающего временное поле" +name = "Хронобомба" +usage1 = "ПКМ для запуска хроноснаряда, создающего временное поле" [items.elevator_block] - name = "Блок лифта" - usage1 = "Прыжок для телепортации вверх" - usage2 = "Присесть для телепортации вниз" - usage3 = "Минимум 2 воздушных блока между лифтами" +name = "Блок лифта" +usage1 = "Прыжок для телепортации вверх" +usage2 = "Присесть для телепортации вниз" +usage3 = "Минимум 2 воздушных блока между лифтами" # snippets [snippets] [snippets.gui] - level = "Уровень" - knowledge = "знаний" - power_used = "Энергии использовано" - not_learned = "Не изучено" - xp = "Опыта до" - welcome = "Добро пожаловать!" - welcome_back = "С возвращением!" - xp_bonus_for_time = "Опыта за" - max_ability_power = "Максимальная сила способностей" - unlock_this_by_clicking = "Разблокируйте с помощью ПКМ: " - back = "Назад" - unlearn_all = "Забыть всё" - unlearned_all = "Всё забыто" +level = "Уровень" +knowledge = "знаний" +power_used = "Энергии использовано" +not_learned = "Не изучено" +xp = "Опыта до" +welcome = "Добро пожаловать!" +welcome_back = "С возвращением!" +xp_bonus_for_time = "Опыта за" +max_ability_power = "Максимальная сила способностей" +unlock_this_by_clicking = "Разблокируйте с помощью ПКМ: " +back = "Назад" +unlearn_all = "Забыть всё" +unlearned_all = "Всё забыто" [snippets.adapt_menu] - may_not_unlearn = "ВЫ НЕ МОЖЕТЕ ЗАБЫТЬ" - may_unlearn = "ВЫ МОЖЕТЕ ИЗУЧИТЬ/ЗАБЫТЬ" - knowledge_cost = "Стоимость знаний" - knowledge_available = "Доступно знаний" - already_learned = "Уже изучено" - unlearn_refund = "Нажмите, чтобы забыть и вернуть" - no_refunds = "ХАРДКОР, ВОЗВРАТ ОТКЛЮЧЁН" - knowledge = "знаний" - click_learn = "Нажмите, чтобы изучить" - no_knowledge = "(У вас нет знаний)" - you_only_have = "У вас только" - how_to_level_up = "Повышайте уровень навыков, чтобы увеличить максимальную энергию." - not_enough_power = "Недостаточно энергии! Каждый уровень способности стоит 1 энергию." - power = "энергия" - power_drain = "Расход энергии" - learned = "Изучено " - unlearned = "Забыто " - activator_block = "Книжная полка" +may_not_unlearn = "ВЫ НЕ МОЖЕТЕ ЗАБЫТЬ" +may_unlearn = "ВЫ МОЖЕТЕ ИЗУЧИТЬ/ЗАБЫТЬ" +knowledge_cost = "Стоимость знаний" +knowledge_available = "Доступно знаний" +already_learned = "Уже изучено" +unlearn_refund = "Нажмите, чтобы забыть и вернуть" +no_refunds = "ХАРДКОР, ВОЗВРАТ ОТКЛЮЧЁН" +knowledge = "знаний" +click_learn = "Нажмите, чтобы изучить" +no_knowledge = "(У вас нет знаний)" +you_only_have = "У вас только" +how_to_level_up = "Повышайте уровень навыков, чтобы увеличить максимальную энергию." +not_enough_power = "Недостаточно энергии! Каждый уровень способности стоит 1 энергию." +power = "энергия" +power_drain = "Расход энергии" +learned = "Изучено " +unlearned = "Забыто " +activator_block = "Книжная полка" [snippets.knowledge_orb] - contains = "содержит" - knowledge = "знаний" - rightclick = "ПКМ" - togainknowledge = "чтобы получить эти знания" - knowledge_orb = "Сфера знаний" +contains = "содержит" +knowledge = "знаний" +rightclick = "ПКМ" +togainknowledge = "чтобы получить эти знания" +knowledge_orb = "Сфера знаний" [snippets.experience_orb] - contains = "содержит" - xp = "Опыта" - rightclick = "ПКМ" - togainxp = "чтобы получить этот опыт" - xporb = "Сфера опыта" +contains = "содержит" +xp = "Опыта" +rightclick = "ПКМ" +togainxp = "чтобы получить этот опыт" +xporb = "Сфера опыта" # skill [skill] [skill.agility] - name = "Ловкость" - icon = "⇉" - description = "Ловкость -- это способность быстро и плавно передвигаться, преодолевая препятствия." +name = "Ловкость" +icon = "⇉" +description = "Ловкость -- это способность быстро и плавно передвигаться, преодолевая препятствия." [skill.architect] - name = "Архитектура" - icon = "⬧" - description = "Структуры -- строительные блоки мира. Реальность в ваших руках, вам ею управлять." +name = "Архитектура" +icon = "⬧" +description = "Структуры -- строительные блоки мира. Реальность в ваших руках, вам ею управлять." [skill.axes] - name = "Топоры" - icon = "🪓" - description1 = "Зачем рубить деревья, когда можно рубить " - description2 = "вещи" - description3 = " -- результат тот же!" +name = "Топоры" +icon = "🪓" +description1 = "Зачем рубить деревья, когда можно рубить " +description2 = "вещи" +description3 = " -- результат тот же!" [skill.brewing] - name = "Зельеварение" - icon = "❦" - description = "Двойной пузырёк, тройной пузырёк, четверной пузырёк -- а зелье в котёл так и не лезет" +name = "Зельеварение" +icon = "❦" +description = "Двойной пузырёк, тройной пузырёк, четверной пузырёк -- а зелье в котёл так и не лезет" [skill.blocking] - name = "Блокирование" - icon = "🛡" - description = "Палки и камни костей не сломают, а вот щит -- запросто." +name = "Блокирование" +icon = "🛡" +description = "Палки и камни костей не сломают, а вот щит -- запросто." [skill.crafting] - name = "Ремесло" - icon = "⌂" - description = "Когда деталей больше не осталось, почему бы не сделать ещё?" +name = "Ремесло" +icon = "⌂" +description = "Когда деталей больше не осталось, почему бы не сделать ещё?" [skill.discovery] - name = "Исследование" - icon = "⚛" - description = "По мере расширения восприятия ваш разум раскрывается, обнаруживая то, чего вы не замечали." +name = "Исследование" +icon = "⚛" +description = "По мере расширения восприятия ваш разум раскрывается, обнаруживая то, чего вы не замечали." [skill.enchanting] - name = "Зачарование" - icon = "♰" - description = "О чём ты говоришь? Пророчества, видения, суеверная болтовня?" +name = "Зачарование" +icon = "♰" +description = "О чём ты говоришь? Пророчества, видения, суеверная болтовня?" [skill.excavation] - name = "Раскопки" - icon = "ᛳ" - description = "Копай, копай яму..." +name = "Раскопки" +icon = "ᛳ" +description = "Копай, копай яму..." [skill.herbalism] - name = "Травничество" - icon = "⚘" - description = "Не могу найти растения, но нашёл несколько семян и... это что... Травка?" +name = "Травничество" +icon = "⚘" +description = "Не могу найти растения, но нашёл несколько семян и... это что... Травка?" [skill.hunter] - name = "Охота" - icon = "☠" - description = "Охота -- это путешествие, а не результат." +name = "Охота" +icon = "☠" +description = "Охота -- это путешествие, а не результат." [skill.nether] - name = "Нижний мир" - icon = "₪" - description = "Из самых глубин Нижнего мира." +name = "Нижний мир" +icon = "₪" +description = "Из самых глубин Нижнего мира." [skill.pickaxe] - name = "Кирки" - icon = "⛏" - description = "Гномы -- шахтёры, но я тоже кое-чему научился. Я ШВЕД" +name = "Кирки" +icon = "⛏" +description = "Гномы -- шахтёры, но я тоже кое-чему научился. Я ШВЕД" [skill.ranged] - name = "Дальний бой" - icon = "🏹" - description = "Расстояние -- ключ к победе и ключ к выживанию." +name = "Дальний бой" +icon = "🏹" +description = "Расстояние -- ключ к победе и ключ к выживанию." [skill.rift] - name = "Разлом" - icon = "❍" - description = "Разлом -- опасная упряжь, но вы обуздали упряжь." +name = "Разлом" +icon = "❍" +description = "Разлом -- опасная упряжь, но вы обуздали упряжь." [skill.seaborne] - name = "Дитя моря" - icon = "🎣" - description = "С этим навыком вы повелеваете чудесами водной стихии." +name = "Дитя моря" +icon = "🎣" +description = "С этим навыком вы повелеваете чудесами водной стихии." [skill.stealth] - name = "Скрытность" - icon = "☯" - description = "Искусство невидимого. Ступайте в тенях." +name = "Скрытность" +icon = "☯" +description = "Искусство невидимого. Ступайте в тенях." [skill.swords] - name = "Мечи" - icon = "⚔" - description = "Силой Серого Камня!" +name = "Мечи" +icon = "⚔" +description = "Силой Серого Камня!" [skill.taming] - name = "Приручение" - icon = "♥" - description = "Попугаи и пчёлы... а вы?" +name = "Приручение" +icon = "♥" +description = "Попугаи и пчёлы... а вы?" [skill.tragoul] - name = "Трагул" - icon = "🗡" - description = "Кровь течёт по венам вселенной. Сжатая вашими руками." +name = "Трагул" +icon = "🗡" +description = "Кровь течёт по венам вселенной. Сжатая вашими руками." [skill.chronos] - name = "Хронос" - icon = "🕒" - description = "Заведите часы вселенной, ощутите поток. Сломайте часы, станьте ими." +name = "Хронос" +icon = "🕒" +description = "Заведите часы вселенной, ощутите поток. Сломайте часы, станьте ими." [skill.unarmed] - name = "Без оружия" - icon = "»" - description = "Без оружия -- не значит без силы." +name = "Без оружия" +icon = "»" +description = "Без оружия -- не значит без силы." # agility [agility] [agility.armor_up] - name = "Укрепление брони" - description = "Чем дольше бежите, тем больше брони!" - lore1 = "Максимум брони" - lore2 = "Время набора брони" - lore = ["Максимум брони", "Время набора брони"] +name = "Укрепление брони" +description = "Чем дольше бежите, тем больше брони!" +lore1 = "Максимум брони" +lore2 = "Время набора брони" +lore = ["Максимум брони", "Время набора брони"] [agility.ladder_slide] - name = "Скольжение по лестнице" - description = "Поднимайтесь и скользите по лестницам гораздо быстрее в обоих направлениях." - lore1 = "Множитель скорости на лестнице" - lore2 = "Скорость быстрого спуска" - lore = ["Множитель скорости на лестнице", "Скорость быстрого спуска"] +name = "Скольжение по лестнице" +description = "Поднимайтесь и скользите по лестницам гораздо быстрее в обоих направлениях." +lore1 = "Множитель скорости на лестнице" +lore2 = "Скорость быстрого спуска" +lore = ["Множитель скорости на лестнице", "Скорость быстрого спуска"] [agility.super_jump] - name = "Суперпрыжок" - description = "Исключительное преимущество в высоте." - lore1 = "Максимальная высота прыжка" - lore2 = "Shift + Прыжок для суперпрыжка!" - lore = ["Максимальная высота прыжка", "Shift + Прыжок для суперпрыжка!"] +name = "Суперпрыжок" +description = "Исключительное преимущество в высоте." +lore1 = "Максимальная высота прыжка" +lore2 = "Shift + Прыжок для суперпрыжка!" +lore = ["Максимальная высота прыжка", "Shift + Прыжок для суперпрыжка!"] [agility.wall_jump] - name = "Прыжок от стены" - description = "Удерживайте Shift в воздухе у стены, чтобы зацепиться и прыгнуть!" - lore1 = "Максимум прыжков" - lore2 = "Высота прыжка" - lore = ["Максимум прыжков", "Высота прыжка"] +name = "Прыжок от стены" +description = "Удерживайте Shift в воздухе у стены, чтобы зацепиться и прыгнуть!" +lore1 = "Максимум прыжков" +lore2 = "Высота прыжка" +lore = ["Максимум прыжков", "Высота прыжка"] [agility.wind_up] - name = "Разгон" - description = "Чем дольше бежите, тем быстрее становитесь!" - lore1 = "Максимальная скорость" - lore2 = "Время разгона" - lore = ["Максимальная скорость", "Время разгона"] +name = "Разгон" +description = "Чем дольше бежите, тем быстрее становитесь!" +lore1 = "Максимальная скорость" +lore2 = "Время разгона" +lore = ["Максимальная скорость", "Время разгона"] # architect [architect] [architect.elevator] - name = "Лифт" - description = "Позволяет построить лифт для быстрой вертикальной телепортации!" - lore1 = "Открывает рецепт лифта: X=ШЕРСТЬ, Y=ЭНДЕР-ЖЕМЧУГ" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Открывает рецепт лифта: X=ШЕРСТЬ, Y=ЭНДЕР-ЖЕМЧУГ", "XXX", "XYX", "XXX"] +name = "Лифт" +description = "Позволяет построить лифт для быстрой вертикальной телепортации!" +lore1 = "Открывает рецепт лифта: X=ШЕРСТЬ, Y=ЭНДЕР-ЖЕМЧУГ" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Открывает рецепт лифта: X=ШЕРСТЬ, Y=ЭНДЕР-ЖЕМЧУГ", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Магический фундамент" - description = "Позволяет создавать временный фундамент под собой при приседании!" - lore1 = "Магически создать: " - lore2 = "блоков под собой!" - lore = ["Магически создать: ", "блоков под собой!"] +name = "Магический фундамент" +description = "Позволяет создавать временный фундамент под собой при приседании!" +lore1 = "Магически создать: " +lore2 = "блоков под собой!" +lore = ["Магически создать: ", "блоков под собой!"] [architect.glass] - name = "Шёлковое касание: стекло" - description = "Позволяет предотвратить потерю стеклянных блоков, когда вы ломаете их пустой рукой!" - lore1 = "Ваши руки получают Шёлковое касание для стекла" - lore = ["Ваши руки получают Шёлковое касание для стекла"] +name = "Шёлковое касание: стекло" +description = "Позволяет предотвратить потерю стеклянных блоков, когда вы ломаете их пустой рукой!" +lore1 = "Ваши руки получают Шёлковое касание для стекла" +lore = ["Ваши руки получают Шёлковое касание для стекла"] [architect.wireless_redstone] - name = "Пульт редстоуна" - description = "Позволяет переключать редстоун-сигнал дистанционно с помощью редстоунового факела!" - lore1 = "Мишень + Редстоуновый факел + Эндер-жемчуг = 1 пульт редстоуна" - lore = ["Мишень + Редстоуновый факел + Эндер-жемчуг = 1 пульт редстоуна"] +name = "Пульт редстоуна" +description = "Позволяет переключать редстоун-сигнал дистанционно с помощью редстоунового факела!" +lore1 = "Мишень + Редстоуновый факел + Эндер-жемчуг = 1 пульт редстоуна" +lore = ["Мишень + Редстоуновый факел + Эндер-жемчуг = 1 пульт редстоуна"] [architect.placement] - name = "Жезл строителя" - description = "Позволяет размещать несколько блоков одновременно! Для активации присядьте и держите блок, совпадающий с тем, на который вы смотрите, и ставьте! Возможно, придётся чуть сдвинуться!" - lore1 = "Вам нужно" - lore2 = "блоков в руке, чтобы разместить это" - lore3 = "Жезл строителя из материала" - lore = ["Вам нужно", "блоков в руке, чтобы разместить это", "Жезл строителя из материала"] +name = "Жезл строителя" +description = "Позволяет размещать несколько блоков одновременно! Для активации присядьте и держите блок, совпадающий с тем, на который вы смотрите, и ставьте! Возможно, придётся чуть сдвинуться!" +lore1 = "Вам нужно" +lore2 = "блоков в руке, чтобы разместить это" +lore3 = "Жезл строителя из материала" +lore = ["Вам нужно", "блоков в руке, чтобы разместить это", "Жезл строителя из материала"] # axe [axe] [axe.chop] - name = "Рубка топором" - description = "Срубайте деревья, нажав ПКМ по нижнему бревну!" - lore1 = "Блоков за рубку" - lore2 = "Перезарядка рубки" - lore3 = "Износ инструмента" - lore = ["Блоков за рубку", "Перезарядка рубки", "Износ инструмента"] +name = "Рубка топором" +description = "Срубайте деревья, нажав ПКМ по нижнему бревну!" +lore1 = "Блоков за рубку" +lore2 = "Перезарядка рубки" +lore3 = "Износ инструмента" +lore = ["Блоков за рубку", "Перезарядка рубки", "Износ инструмента"] [axe.log_swap] - name = "Обменник брёвен Люси" - description = "Меняйте тип брёвен на верстаке!" - lore1 = "8 брёвен любого вида + 1 саженец = 8 брёвен типа саженца" - lore = ["8 брёвен любого вида + 1 саженец = 8 брёвен типа саженца"] +name = "Обменник брёвен Люси" +description = "Меняйте тип брёвен на верстаке!" +lore1 = "8 брёвен любого вида + 1 саженец = 8 брёвен типа саженца" +lore = ["8 брёвен любого вида + 1 саженец = 8 брёвен типа саженца"] [axe.drop_to_inventory] - name = "Автосбор с топора" +name = "Автосбор с топора" [axe.ground_smash] - name = "Удар топором по земле" - description = "Прыгните, затем присядьте и ударьте всех ближайших врагов." - lore1 = "Урон" - lore2 = "Радиус в блоках" - lore3 = "Сила" - lore4 = "Перезарядка удара" - lore = ["Урон", "Радиус в блоках", "Сила", "Перезарядка удара"] +name = "Удар топором по земле" +description = "Прыгните, затем присядьте и ударьте всех ближайших врагов." +lore1 = "Урон" +lore2 = "Радиус в блоках" +lore3 = "Сила" +lore4 = "Перезарядка удара" +lore = ["Урон", "Радиус в блоках", "Сила", "Перезарядка удара"] [axe.leaf_miner] - name = "Сборщик листвы" - description = "Позволяет ломать массу листвы сразу!" - lore1 = "Присядьте и рубите ЛИСТВУ" - lore2 = "Радиус сбора листвы" - lore3 = "Вы не получите дроп с листвы (защита от эксплойтов)" - lore = ["Присядьте и рубите ЛИСТВУ", "Радиус сбора листвы", "Вы не получите дроп с листвы (защита от эксплойтов)"] +name = "Сборщик листвы" +description = "Позволяет ломать массу листвы сразу!" +lore1 = "Присядьте и рубите ЛИСТВУ" +lore2 = "Радиус сбора листвы" +lore3 = "Вы не получите дроп с листвы (защита от эксплойтов)" +lore = ["Присядьте и рубите ЛИСТВУ", "Радиус сбора листвы", "Вы не получите дроп с листвы (защита от эксплойтов)"] [axe.wood_miner] - name = "Лесоруб" - description = "Позволяет ломать массу древесины сразу!" - lore1 = "Присядьте и рубите ДЕРЕВО/БРЁВНА (не доски)" - lore2 = "Радиус рубки древесины" - lore3 = "Работает с автосбором в инвентарь" - lore = ["Присядьте и рубите ДЕРЕВО/БРЁВНА (не доски)", "Радиус рубки древесины", "Работает с автосбором в инвентарь"] +name = "Лесоруб" +description = "Позволяет ломать массу древесины сразу!" +lore1 = "Присядьте и рубите ДЕРЕВО/БРЁВНА (не доски)" +lore2 = "Радиус рубки древесины" +lore3 = "Работает с автосбором в инвентарь" +lore = ["Присядьте и рубите ДЕРЕВО/БРЁВНА (не доски)", "Радиус рубки древесины", "Работает с автосбором в инвентарь"] # brewing [brewing] [brewing.lingering] - name = "Стойкое зелье" - description = "Сваренные зелья действуют дольше!" - lore1 = "Длительность" - lore2 = "Длительность" - lore = ["Длительность", "Длительность"] +name = "Стойкое зелье" +description = "Сваренные зелья действуют дольше!" +lore1 = "Длительность" +lore2 = "Длительность" +lore = ["Длительность", "Длительность"] [brewing.super_heated] - name = "Сверхнагретое зелье" - description = "Варочные стойки работают быстрее, чем горячее вокруг них." - lore1 = "За каждый касающийся блок огня" - lore2 = "За каждый касающийся блок лавы" - lore = ["За каждый касающийся блок огня", "За каждый касающийся блок лавы"] +name = "Сверхнагретое зелье" +description = "Варочные стойки работают быстрее, чем горячее вокруг них." +lore1 = "За каждый касающийся блок огня" +lore2 = "За каждый касающийся блок лавы" +lore = ["За каждый касающийся блок огня", "За каждый касающийся блок лавы"] [brewing.darkness] - name = "Тьма в бутылке" - description = "Не знаю, зачем вам это, но вот, держите!" - lore1 = "Зелье ночного зрения + Чёрный бетон = Зелье тьмы (30 секунд)" - lore2 = "Следует отметить, что это не позволяет бегать!" - lore = ["Зелье ночного зрения + Чёрный бетон = Зелье тьмы (30 секунд)", "Следует отметить, что это не позволяет бегать!"] +name = "Тьма в бутылке" +description = "Не знаю, зачем вам это, но вот, держите!" +lore1 = "Зелье ночного зрения + Чёрный бетон = Зелье тьмы (30 секунд)" +lore2 = "Следует отметить, что это не позволяет бегать!" +lore = ["Зелье ночного зрения + Чёрный бетон = Зелье тьмы (30 секунд)", "Следует отметить, что это не позволяет бегать!"] [brewing.haste] - name = "Спешка в бутылке" - description = "Когда эффективности недостаточно" - lore1 = "Зелье скорости + Осколок аметиста = Зелье спешки (60 секунд)" - lore2 = "Зелье скорости + Блок аметиста = Зелье спешки II (30 секунд)" - lore = ["Зелье скорости + Осколок аметиста = Зелье спешки (60 секунд)", "Зелье скорости + Блок аметиста = Зелье спешки II (30 секунд)"] +name = "Спешка в бутылке" +description = "Когда эффективности недостаточно" +lore1 = "Зелье скорости + Осколок аметиста = Зелье спешки (60 секунд)" +lore2 = "Зелье скорости + Блок аметиста = Зелье спешки II (30 секунд)" +lore = ["Зелье скорости + Осколок аметиста = Зелье спешки (60 секунд)", "Зелье скорости + Блок аметиста = Зелье спешки II (30 секунд)"] [brewing.absorption] - name = "Поглощение в бутылке" - description = "Закали тело!" - lore1 = "Зелье исцеления + Кварц = Зелье поглощения (60 секунд)" - lore2 = "Зелье исцеления + Кварцевый блок = Зелье поглощения II (30 секунд)" - lore = ["Зелье исцеления + Кварц = Зелье поглощения (60 секунд)", "Зелье исцеления + Кварцевый блок = Зелье поглощения II (30 секунд)"] +name = "Поглощение в бутылке" +description = "Закали тело!" +lore1 = "Зелье исцеления + Кварц = Зелье поглощения (60 секунд)" +lore2 = "Зелье исцеления + Кварцевый блок = Зелье поглощения II (30 секунд)" +lore = ["Зелье исцеления + Кварц = Зелье поглощения (60 секунд)", "Зелье исцеления + Кварцевый блок = Зелье поглощения II (30 секунд)"] [brewing.fatigue] - name = "Усталость в бутылке" - description = "Ослабь тело!" - lore1 = "Зелье слабости + Сгусток слизи = Зелье усталости (30 секунд)" - lore2 = "Зелье слабости + Блок слизи = Зелье усталости II (15 секунд)" - lore = ["Зелье слабости + Сгусток слизи = Зелье усталости (30 секунд)", "Зелье слабости + Блок слизи = Зелье усталости II (15 секунд)"] +name = "Усталость в бутылке" +description = "Ослабь тело!" +lore1 = "Зелье слабости + Сгусток слизи = Зелье усталости (30 секунд)" +lore2 = "Зелье слабости + Блок слизи = Зелье усталости II (15 секунд)" +lore = ["Зелье слабости + Сгусток слизи = Зелье усталости (30 секунд)", "Зелье слабости + Блок слизи = Зелье усталости II (15 секунд)"] [brewing.hunger] - name = "Голод в бутылке" - description = "Накорми ненасытного!" - lore1 = "Мутное зелье + Гнилая плоть = Зелье голода (30 секунд)" - lore2 = "Зелье слабости + Гнилая плоть = Зелье голода III (15 секунд)" - lore = ["Мутное зелье + Гнилая плоть = Зелье голода (30 секунд)", "Зелье слабости + Гнилая плоть = Зелье голода III (15 секунд)"] +name = "Голод в бутылке" +description = "Накорми ненасытного!" +lore1 = "Мутное зелье + Гнилая плоть = Зелье голода (30 секунд)" +lore2 = "Зелье слабости + Гнилая плоть = Зелье голода III (15 секунд)" +lore = ["Мутное зелье + Гнилая плоть = Зелье голода (30 секунд)", "Зелье слабости + Гнилая плоть = Зелье голода III (15 секунд)"] [brewing.nausea] - name = "Тошнота в бутылке" - description = "Меня от тебя тошнит!" - lore1 = "Мутное зелье + Коричневый гриб = Зелье тошноты (16 секунд)" - lore2 = "Мутное зелье + Багровый гриб = Зелье тошноты II (8 секунд)" - lore = ["Мутное зелье + Коричневый гриб = Зелье тошноты (16 секунд)", "Мутное зелье + Багровый гриб = Зелье тошноты II (8 секунд)"] +name = "Тошнота в бутылке" +description = "Меня от тебя тошнит!" +lore1 = "Мутное зелье + Коричневый гриб = Зелье тошноты (16 секунд)" +lore2 = "Мутное зелье + Багровый гриб = Зелье тошноты II (8 секунд)" +lore = ["Мутное зелье + Коричневый гриб = Зелье тошноты (16 секунд)", "Мутное зелье + Багровый гриб = Зелье тошноты II (8 секунд)"] [brewing.blindness] - name = "Слепота в бутылке" - description = "Ты ужасный человек..." - lore1 = "Мутное зелье + Чернильный мешок = Зелье слепоты (30 секунд)" - lore2 = "Мутное зелье + Светящийся чернильный мешок = Зелье слепоты II (15 секунд)" - lore = ["Мутное зелье + Чернильный мешок = Зелье слепоты (30 секунд)", "Мутное зелье + Светящийся чернильный мешок = Зелье слепоты II (15 секунд)"] +name = "Слепота в бутылке" +description = "Ты ужасный человек..." +lore1 = "Мутное зелье + Чернильный мешок = Зелье слепоты (30 секунд)" +lore2 = "Мутное зелье + Светящийся чернильный мешок = Зелье слепоты II (15 секунд)" +lore = ["Мутное зелье + Чернильный мешок = Зелье слепоты (30 секунд)", "Мутное зелье + Светящийся чернильный мешок = Зелье слепоты II (15 секунд)"] [brewing.resistance] - name = "Сопротивление в бутылке" - description = "Укрепление в лучшем виде!" - lore1 = "Мутное зелье + Железный слиток = Зелье сопротивления (60 секунд)" - lore2 = "Мутное зелье + Железный блок = Зелье сопротивления II (30 секунд)" - lore = ["Мутное зелье + Железный слиток = Зелье сопротивления (60 секунд)", "Мутное зелье + Железный блок = Зелье сопротивления II (30 секунд)"] +name = "Сопротивление в бутылке" +description = "Укрепление в лучшем виде!" +lore1 = "Мутное зелье + Железный слиток = Зелье сопротивления (60 секунд)" +lore2 = "Мутное зелье + Железный блок = Зелье сопротивления II (30 секунд)" +lore = ["Мутное зелье + Железный слиток = Зелье сопротивления (60 секунд)", "Мутное зелье + Железный блок = Зелье сопротивления II (30 секунд)"] [brewing.health_boost] - name = "Жизнь в бутылке" - description = "Когда максимального здоровья недостаточно..." - lore1 = "Зелье исцеления + Золотое яблоко = Зелье усиления здоровья (120 секунд)" - lore2 = "Зелье исцеления + Зачарованное золотое яблоко = Зелье усиления здоровья II (120 секунд)" - lore = ["Зелье исцеления + Золотое яблоко = Зелье усиления здоровья (120 секунд)", "Зелье исцеления + Зачарованное золотое яблоко = Зелье усиления здоровья II (120 секунд)"] +name = "Жизнь в бутылке" +description = "Когда максимального здоровья недостаточно..." +lore1 = "Зелье исцеления + Золотое яблоко = Зелье усиления здоровья (120 секунд)" +lore2 = "Зелье исцеления + Зачарованное золотое яблоко = Зелье усиления здоровья II (120 секунд)" +lore = ["Зелье исцеления + Золотое яблоко = Зелье усиления здоровья (120 секунд)", "Зелье исцеления + Зачарованное золотое яблоко = Зелье усиления здоровья II (120 секунд)"] [brewing.decay] - name = "Распад в бутылке" - description = "Кто бы мог подумать, что Детрит окажется таким полезным?" - lore1 = "Зелье слабости + Ядовитый картофель = Зелье иссушения (16 секунд)" - lore2 = "Зелье слабости + Багровые корни = Зелье иссушения II (8 секунд)" - lore = ["Зелье слабости + Ядовитый картофель = Зелье иссушения (16 секунд)", "Зелье слабости + Багровые корни = Зелье иссушения II (8 секунд)"] +name = "Распад в бутылке" +description = "Кто бы мог подумать, что Детрит окажется таким полезным?" +lore1 = "Зелье слабости + Ядовитый картофель = Зелье иссушения (16 секунд)" +lore2 = "Зелье слабости + Багровые корни = Зелье иссушения II (8 секунд)" +lore = ["Зелье слабости + Ядовитый картофель = Зелье иссушения (16 секунд)", "Зелье слабости + Багровые корни = Зелье иссушения II (8 секунд)"] [brewing.saturation] - name = "Насыщение в бутылке" - description = "Знаешь... я даже не голоден..." - lore1 = "Зелье регенерации + Печёный картофель = Зелье насыщения" - lore2 = "Зелье регенерации + Сноп сена = Зелье насыщения II" - lore = ["Зелье регенерации + Печёный картофель = Зелье насыщения", "Зелье регенерации + Сноп сена = Зелье насыщения II"] +name = "Насыщение в бутылке" +description = "Знаешь... я даже не голоден..." +lore1 = "Зелье регенерации + Печёный картофель = Зелье насыщения" +lore2 = "Зелье регенерации + Сноп сена = Зелье насыщения II" +lore = ["Зелье регенерации + Печёный картофель = Зелье насыщения", "Зелье регенерации + Сноп сена = Зелье насыщения II"] # blocking [blocking] [blocking.chain_armorer] - name = "Цепи Мефистофеля" - description = "Позволяет создавать кольчужную броню" - lore1 = "Рецепт крафта такой же, как и для любой другой брони, но с железными самородками" - lore = ["Рецепт крафта такой же, как и для любой другой брони, но с железными самородками"] +name = "Цепи Мефистофеля" +description = "Позволяет создавать кольчужную броню" +lore1 = "Рецепт крафта такой же, как и для любой другой брони, но с железными самородками" +lore = ["Рецепт крафта такой же, как и для любой другой брони, но с железными самородками"] [blocking.horse_armorer] - name = "Крафт конской брони" - description = "Позволяет создавать конскую броню" - lore1 = "Окружите седло материалом, из которого хотите изготовить броню" - lore = ["Окружите седло материалом, из которого хотите изготовить броню"] +name = "Крафт конской брони" +description = "Позволяет создавать конскую броню" +lore1 = "Окружите седло материалом, из которого хотите изготовить броню" +lore = ["Окружите седло материалом, из которого хотите изготовить броню"] [blocking.saddle_crafter] - name = "Крафт седла" - description = "Изготовьте седло из кожи" - lore1 = "Рецепт: 5 кожи:" - lore = ["Рецепт: 5 кожи:"] +name = "Крафт седла" +description = "Изготовьте седло из кожи" +lore1 = "Рецепт: 5 кожи:" +lore = ["Рецепт: 5 кожи:"] [blocking.multi_armor] - name = "Мультиброня" - description = "Привяжите элитры к броне" - lore1 = "Невероятный навык для путешествий." - lore2 = "Динамически объединяйте и меняйте броню/элитры на лету!" - lore3 = "Для объединения: Shift + клик по предмету поверх другого в инвентаре." - lore4 = "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран." - lore5 = "Если ваша мультиброня уничтожена, вы потеряете все предметы в ней." - lore6 = "Мне не нужна броня, она меня разочаровывает..." - lore = ["Невероятный навык для путешествий.", "Динамически объединяйте и меняйте броню/элитры на лету!", "Для объединения: Shift + клик по предмету поверх другого в инвентаре.", "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран.", "Если ваша мультиброня уничтожена, вы потеряете все предметы в ней.", "Мне не нужна броня, она меня разочаровывает..."] +name = "Мультиброня" +description = "Привяжите элитры к броне" +lore1 = "Невероятный навык для путешествий." +lore2 = "Динамически объединяйте и меняйте броню/элитры на лету!" +lore3 = "Для объединения: Shift + клик по предмету поверх другого в инвентаре." +lore4 = "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран." +lore5 = "Если ваша мультиброня уничтожена, вы потеряете все предметы в ней." +lore6 = "Мне не нужна броня, она меня разочаровывает..." +lore = ["Невероятный навык для путешествий.", "Динамически объединяйте и меняйте броню/элитры на лету!", "Для объединения: Shift + клик по предмету поверх другого в инвентаре.", "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран.", "Если ваша мультиброня уничтожена, вы потеряете все предметы в ней.", "Мне не нужна броня, она меня разочаровывает..."] # crafting [crafting] [crafting.deconstruction] - name = "Деконструкция" - description = "Разбирайте блоки и предметы на базовые компоненты!" - lore1 = "Бросьте любой предмет на землю." - lore2 = "Затем присядьте и нажмите ПКМ ножницами" - lore = ["Бросьте любой предмет на землю.", "Затем присядьте и нажмите ПКМ ножницами"] +name = "Деконструкция" +description = "Разбирайте блоки и предметы на базовые компоненты!" +lore1 = "Бросьте любой предмет на землю." +lore2 = "Затем присядьте и нажмите ПКМ ножницами" +lore = ["Бросьте любой предмет на землю.", "Затем присядьте и нажмите ПКМ ножницами"] [crafting.xp] - name = "Опыт за крафт" - description = "Получайте пассивный опыт при крафте" - lore1 = "Получайте опыт при крафте" - lore = ["Получайте опыт при крафте"] +name = "Опыт за крафт" +description = "Получайте пассивный опыт при крафте" +lore1 = "Получайте опыт при крафте" +lore = ["Получайте опыт при крафте"] [crafting.reconstruction] - name = "Реконструкция руды" - description = "Воссоздавайте руды из их базовых компонентов!" - lore1 = "8 единиц дропа и 1 основа = 1 руда (бесформенный рецепт)" - lore2 = "Дроп должен быть переплавлен (если применимо)" - lore3 = "Не включает: обломки, кварц, изумруды и т.д." - lore4 = "Основа = оболочка, т.е.: камень, незеррак, глубинный сланец" - lore = ["8 единиц дропа и 1 основа = 1 руда (бесформенный рецепт)", "Дроп должен быть переплавлен (если применимо)", "Не включает: обломки, кварц, изумруды и т.д.", "Основа = оболочка, т.е.: камень, незеррак, глубинный сланец"] +name = "Реконструкция руды" +description = "Воссоздавайте руды из их базовых компонентов!" +lore1 = "8 единиц дропа и 1 основа = 1 руда (бесформенный рецепт)" +lore2 = "Дроп должен быть переплавлен (если применимо)" +lore3 = "Не включает: обломки, кварц, изумруды и т.д." +lore4 = "Основа = оболочка, т.е.: камень, незеррак, глубинный сланец" +lore = ["8 единиц дропа и 1 основа = 1 руда (бесформенный рецепт)", "Дроп должен быть переплавлен (если применимо)", "Не включает: обломки, кварц, изумруды и т.д.", "Основа = оболочка, т.е.: камень, незеррак, глубинный сланец"] [crafting.leather] - name = "Крафт кожи" - description = "Изготавливайте кожу из гнилой плоти" - lore1 = "Просто положите гнилую плоть на костёр!" - lore = ["Просто положите гнилую плоть на костёр!"] +name = "Крафт кожи" +description = "Изготавливайте кожу из гнилой плоти" +lore1 = "Просто положите гнилую плоть на костёр!" +lore = ["Просто положите гнилую плоть на костёр!"] [crafting.backpacks] - name = "Рюкзаки Бутильера!" - description = "Мешки Mojang теперь в игре!" - lore1 = "Вы должны быть в режиме выживания" - lore2 = "XLX : Кожа, Поводок, Кожа" - lore3 = "XSX : Кожа, Бочка, Кожа" - lore4 = "XCX : Кожа, Сундук, Кожа" - lore = ["Вы должны быть в режиме выживания", "XLX : Кожа, Поводок, Кожа", "XSX : Кожа, Бочка, Кожа", "XCX : Кожа, Сундук, Кожа"] +name = "Рюкзаки Бутильера!" +description = "Мешки Mojang теперь в игре!" +lore1 = "Вы должны быть в режиме выживания" +lore2 = "XLX : Кожа, Поводок, Кожа" +lore3 = "XSX : Кожа, Бочка, Кожа" +lore4 = "XCX : Кожа, Сундук, Кожа" +lore = ["Вы должны быть в режиме выживания", "XLX : Кожа, Поводок, Кожа", "XSX : Кожа, Бочка, Кожа", "XCX : Кожа, Сундук, Кожа"] [crafting.stations] - name = "Портативные столы!" - description = "Используйте верстак прямо в руке!" - lore2 = "ВСЕ ПРЕДМЕТЫ, ЗАБЫТЫЕ В СТОЛЕ ПРИ ЗАКРЫТИИ, БУДУТ ПОТЕРЯНЫ НАВСЕГДА!" - lore3 = "Доступные столы: Наковальня, Верстак, Точило, Стол картографа, Камнерез, Ткацкий станок" - lore = ["ВСЕ ПРЕДМЕТЫ, ЗАБЫТЫЕ В СТОЛЕ ПРИ ЗАКРЫТИИ, БУДУТ ПОТЕРЯНЫ НАВСЕГДА!", "Доступные столы: Наковальня, Верстак, Точило, Стол картографа, Камнерез, Ткацкий станок"] +name = "Портативные столы!" +description = "Используйте верстак прямо в руке!" +lore2 = "ВСЕ ПРЕДМЕТЫ, ЗАБЫТЫЕ В СТОЛЕ ПРИ ЗАКРЫТИИ, БУДУТ ПОТЕРЯНЫ НАВСЕГДА!" +lore3 = "Доступные столы: Наковальня, Верстак, Точило, Стол картографа, Камнерез, Ткацкий станок" +lore = ["ВСЕ ПРЕДМЕТЫ, ЗАБЫТЫЕ В СТОЛЕ ПРИ ЗАКРЫТИИ, БУДУТ ПОТЕРЯНЫ НАВСЕГДА!", "Доступные столы: Наковальня, Верстак, Точило, Стол картографа, Камнерез, Ткацкий станок"] [crafting.skulls] - name = "Крафт черепов!" - description = "Используя материалы, вы можете создавать черепа мобов!" - lore1 = "Окружите костяной блок следующим, чтобы получить череп:" - lore2 = "Зомби: Гнилая плоть" - lore3 = "Скелет: Кость" - lore4 = "Крипер: Порох" - lore5 = "Иссушитель: Незеритовый кирпич" - lore6 = "Дракон: Дыхание дракона" - lore = ["Окружите костяной блок следующим, чтобы получить череп:", "Зомби: Гнилая плоть", "Скелет: Кость", "Крипер: Порох", "Иссушитель: Незеритовый кирпич", "Дракон: Дыхание дракона"] +name = "Крафт черепов!" +description = "Используя материалы, вы можете создавать черепа мобов!" +lore1 = "Окружите костяной блок следующим, чтобы получить череп:" +lore2 = "Зомби: Гнилая плоть" +lore3 = "Скелет: Кость" +lore4 = "Крипер: Порох" +lore5 = "Иссушитель: Незеритовый кирпич" +lore6 = "Дракон: Дыхание дракона" +lore = ["Окружите костяной блок следующим, чтобы получить череп:", "Зомби: Гнилая плоть", "Скелет: Кость", "Крипер: Порох", "Иссушитель: Незеритовый кирпич", "Дракон: Дыхание дракона"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Время в бутылке" - description = "Носите временную бутылку, которая накапливает время. Тратьте его на ускорение тикающих блоков, растений и детёнышей животных. Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка." - lore1 = "Секунд заряжается за тик" - lore2 = "Ускорение времени за накопленную секунду" - lore3 = "Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка" - lore = ["Секунд заряжается за тик", "Ускорение времени за накопленную секунду", "Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка"] +name = "Время в бутылке" +description = "Носите временную бутылку, которая накапливает время. Тратьте его на ускорение тикающих блоков, растений и детёнышей животных. Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка." +lore1 = "Секунд заряжается за тик" +lore2 = "Ускорение времени за накопленную секунду" +lore3 = "Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка" +lore = ["Секунд заряжается за тик", "Ускорение времени за накопленную секунду", "Рецепт (бесформенный): Зелье скорости + Часы + Стеклянная бутылка"] [chronos.aberrant_touch] - name = "Аберрантное касание" - description = "Атаки ближнего боя накладывают накапливающееся замедление за счёт голода, с ограничениями для PvP, и обездвиживают цель при 5 стаках." - lore1 = "Атаки ближнего боя накладывают замедление" - lore2 = "Ограничение длительности замедления для PvE" - lore3 = "Ограничение силы замедления для PvP" - lore = ["Атаки ближнего боя накладывают замедление", "Ограничение длительности замедления для PvE", "Ограничение силы замедления для PvP"] +name = "Аберрантное касание" +description = "Атаки ближнего боя накладывают накапливающееся замедление за счёт голода, с ограничениями для PvP, и обездвиживают цель при 5 стаках." +lore1 = "Атаки ближнего боя накладывают замедление" +lore2 = "Ограничение длительности замедления для PvE" +lore3 = "Ограничение силы замедления для PvP" +lore = ["Атаки ближнего боя накладывают замедление", "Ограничение длительности замедления для PvE", "Ограничение силы замедления для PvP"] [chronos.instant_recall] - name = "Мгновенный откат" - description = "Нажмите ЛКМ или ПКМ с часами в руке, чтобы перемотать время к недавнему снимку с восстановлением здоровья и голода." - lore1 = "Длительность перемотки" - lore2 = "Перезарядка" - lore3 = "Инвентарь не откатывается" - lore = ["Длительность перемотки", "Перезарядка", "Инвентарь не откатывается"] +name = "Мгновенный откат" +description = "Нажмите ЛКМ или ПКМ с часами в руке, чтобы перемотать время к недавнему снимку с восстановлением здоровья и голода." +lore1 = "Длительность перемотки" +lore2 = "Перезарядка" +lore3 = "Инвентарь не откатывается" +lore = ["Длительность перемотки", "Перезарядка", "Инвентарь не откатывается"] [chronos.time_bomb] - name = "Хронобомба" - description = "Бросьте скрафченную хронобомбу, которая создаёт временное поле, замедляет существ и замораживает снаряды." - lore1 = "Радиус временного поля" - lore2 = "Длительность временного поля" - lore3 = "Перезарядка бомбы" - lore4 = "Рецепт (бесформенный): Часы + Снежок + Алмаз + Песок" - lore = ["Радиус временного поля", "Длительность временного поля", "Перезарядка бомбы", "Рецепт (бесформенный): Часы + Снежок + Алмаз + Песок"] +name = "Хронобомба" +description = "Бросьте скрафченную хронобомбу, которая создаёт временное поле, замедляет существ и замораживает снаряды." +lore1 = "Радиус временного поля" +lore2 = "Длительность временного поля" +lore3 = "Перезарядка бомбы" +lore4 = "Рецепт (бесформенный): Часы + Снежок + Алмаз + Песок" +lore = ["Радиус временного поля", "Длительность временного поля", "Перезарядка бомбы", "Рецепт (бесформенный): Часы + Снежок + Алмаз + Песок"] # discovery [discovery] [discovery.armor] - name = "Мировая броня" - description = "Пассивная броня в зависимости от твёрдости ближайших блоков." - lore1 = "Пассивная броня" - lore2 = "На основе твёрдости ближайших блоков" - lore3 = "Сила брони:" - lore = ["Пассивная броня", "На основе твёрдости ближайших блоков", "Сила брони:"] +name = "Мировая броня" +description = "Пассивная броня в зависимости от твёрдости ближайших блоков." +lore1 = "Пассивная броня" +lore2 = "На основе твёрдости ближайших блоков" +lore3 = "Сила брони:" +lore = ["Пассивная броня", "На основе твёрдости ближайших блоков", "Сила брони:"] [discovery.unity] - name = "Экспериментальное единство" - description = "Сбор сфер опыта добавляет опыт к случайным навыкам." - lore1 = "Опыта " - lore2 = "за сферу" - lore = ["Опыта ", "за сферу"] +name = "Экспериментальное единство" +description = "Сбор сфер опыта добавляет опыт к случайным навыкам." +lore1 = "Опыта " +lore2 = "за сферу" +lore = ["Опыта ", "за сферу"] [discovery.resist] - name = "Экспериментальное сопротивление" - description = "Тратьте опыт для снижения урона, только когда удар может уронить вас ниже 5 сердец или убить." - lore0 = "Срабатывает только при критическом здоровье (<= 5 сердец) раз в 15 секунд" - lore1 = " снижения урона" - lore2 = "опыта потрачено" - lore = ["Срабатывает только при критическом здоровье (<= 5 сердец) раз в 15 секунд", " снижения урона", "опыта потрачено"] +name = "Экспериментальное сопротивление" +description = "Тратьте опыт для снижения урона, только когда удар может уронить вас ниже 5 сердец или убить." +lore0 = "Срабатывает только при критическом здоровье (<= 5 сердец) раз в 15 секунд" +lore1 = " снижения урона" +lore2 = "опыта потрачено" +lore = ["Срабатывает только при критическом здоровье (<= 5 сердец) раз в 15 секунд", " снижения урона", "опыта потрачено"] [discovery.villager] - name = "Привлекательность для жителей" - description = "Позволяет получать лучшие сделки у жителей!" - lore1 = "Тратится опыт за каждое взаимодействие с жителями" - lore2 = "Шанс за взаимодействие потратить опыт и улучшить торговлю" - lore3 = "Необходимый расход опыта за взаимодействие" - lore = ["Тратится опыт за каждое взаимодействие с жителями", "Шанс за взаимодействие потратить опыт и улучшить торговлю", "Необходимый расход опыта за взаимодействие"] +name = "Привлекательность для жителей" +description = "Позволяет получать лучшие сделки у жителей!" +lore1 = "Тратится опыт за каждое взаимодействие с жителями" +lore2 = "Шанс за взаимодействие потратить опыт и улучшить торговлю" +lore3 = "Необходимый расход опыта за взаимодействие" +lore = ["Тратится опыт за каждое взаимодействие с жителями", "Шанс за взаимодействие потратить опыт и улучшить торговлю", "Необходимый расход опыта за взаимодействие"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Возврат лазурита" - description = "За 1 дополнительный уровень опыта есть шанс получить бесплатный лазурит" - lore1 = "За каждый уровень увеличивается стоимость зачарования на 1, но может вернуться до 3 лазурита" - lore = ["За каждый уровень увеличивается стоимость зачарования на 1, но может вернуться до 3 лазурита"] +name = "Возврат лазурита" +description = "За 1 дополнительный уровень опыта есть шанс получить бесплатный лазурит" +lore1 = "За каждый уровень увеличивается стоимость зачарования на 1, но может вернуться до 3 лазурита" +lore = ["За каждый уровень увеличивается стоимость зачарования на 1, но может вернуться до 3 лазурита"] [enchanting.quick_enchant] - name = "Быстрое зачарование" - description = "Зачаровывайте предметы, нажимая книгами зачарований прямо по ним." - lore1 = "Максимум комбинированных уровней" - lore2 = "Нельзя зачаровать предмет с более чем " - lore3 = "силы" - lore = ["Максимум комбинированных уровней", "Нельзя зачаровать предмет с более чем ", "силы"] +name = "Быстрое зачарование" +description = "Зачаровывайте предметы, нажимая книгами зачарований прямо по ним." +lore1 = "Максимум комбинированных уровней" +lore2 = "Нельзя зачаровать предмет с более чем " +lore3 = "силы" +lore = ["Максимум комбинированных уровней", "Нельзя зачаровать предмет с более чем ", "силы"] [enchanting.return] - name = "Возврат опыта" - description = "Опыт зачарования возвращается вам при зачаровании предмета." - lore1 = "Потраченный опыт имеет шанс вернуться при зачаровании предмета" - lore2 = "Опыта за зачарование" - lore = ["Потраченный опыт имеет шанс вернуться при зачаровании предмета", "Опыта за зачарование"] +name = "Возврат опыта" +description = "Опыт зачарования возвращается вам при зачаровании предмета." +lore1 = "Потраченный опыт имеет шанс вернуться при зачаровании предмета" +lore2 = "Опыта за зачарование" +lore = ["Потраченный опыт имеет шанс вернуться при зачаровании предмета", "Опыта за зачарование"] # excavation [excavation] [excavation.haste] - name = "Торопливый копатель" - description = "Ускоряет процесс раскопок с помощью Спешки!" - lore1 = "Получайте Спешку при раскопках" - lore2 = "x уровней Спешки при начале добычи ЛЮБОГО блока." - lore = ["Получайте Спешку при раскопках", "x уровней Спешки при начале добычи ЛЮБОГО блока."] +name = "Торопливый копатель" +description = "Ускоряет процесс раскопок с помощью Спешки!" +lore1 = "Получайте Спешку при раскопках" +lore2 = "x уровней Спешки при начале добычи ЛЮБОГО блока." +lore = ["Получайте Спешку при раскопках", "x уровней Спешки при начале добычи ЛЮБОГО блока."] [excavation.spelunker] - name = "Суперзрячий спелеолог!" - description = "Видьте руды сквозь землю!" - lore1 = "Руда во второй руке, светящиеся ягоды в основной, и присядьте!" - lore2 = "Радиус в блоках: " - lore3 = "Расходует светящуюся ягоду при использовании" - lore = ["Руда во второй руке, светящиеся ягоды в основной, и присядьте!", "Радиус в блоках: ", "Расходует светящуюся ягоду при использовании"] +name = "Суперзрячий спелеолог!" +description = "Видьте руды сквозь землю!" +lore1 = "Руда во второй руке, светящиеся ягоды в основной, и присядьте!" +lore2 = "Радиус в блоках: " +lore3 = "Расходует светящуюся ягоду при использовании" +lore = ["Руда во второй руке, светящиеся ягоды в основной, и присядьте!", "Радиус в блоках: ", "Расходует светящуюся ягоду при использовании"] [excavation.drop_to_inventory] - name = "Автосбор с лопаты" +name = "Автосбор с лопаты" [excavation.omni_tool] - name = "МУЛЬТИ-И.Н.С.Т.Р.У.М.Е.Н.Т" - description = "Переусложнённый роскошный мультитул Такла" - lore1 = "Пожалуй, самый мощный навык, позволяющий" - lore2 = "динамически объединять и менять инструменты на лету по необходимости." - lore3 = "Для объединения: Shift + клик по предмету поверх другого в инвентаре." - lore4 = "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран." - lore5 = "Нельзя сломать инструменты в мультитуле, но нельзя использовать сломанные" - lore6 = "всего объединяемых предметов." - lore7 = "Можно использовать пять-шесть инструментов, а можно всего один!" - lore = ["Пожалуй, самый мощный навык, позволяющий", "динамически объединять и менять инструменты на лету по необходимости.", "Для объединения: Shift + клик по предмету поверх другого в инвентаре.", "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран.", "Нельзя сломать инструменты в мультитуле, но нельзя использовать сломанные", "всего объединяемых предметов.", "Можно использовать пять-шесть инструментов, а можно всего один!"] +name = "МУЛЬТИ-И.Н.С.Т.Р.У.М.Е.Н.Т" +description = "Переусложнённый роскошный мультитул Такла" +lore1 = "Пожалуй, самый мощный навык, позволяющий" +lore2 = "динамически объединять и менять инструменты на лету по необходимости." +lore3 = "Для объединения: Shift + клик по предмету поверх другого в инвентаре." +lore4 = "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран." +lore5 = "Нельзя сломать инструменты в мультитуле, но нельзя использовать сломанные" +lore6 = "всего объединяемых предметов." +lore7 = "Можно использовать пять-шесть инструментов, а можно всего один!" +lore = ["Пожалуй, самый мощный навык, позволяющий", "динамически объединять и менять инструменты на лету по необходимости.", "Для объединения: Shift + клик по предмету поверх другого в инвентаре.", "Для разборки: бросьте предмет с зажатым Shift, и он будет разобран.", "Нельзя сломать инструменты в мультитуле, но нельзя использовать сломанные", "всего объединяемых предметов.", "Можно использовать пять-шесть инструментов, а можно всего один!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Аура роста" - description = "Выращивайте природу вокруг себя аурой" - lore1 = "Радиус в блоках" - lore2 = "Сила ауры роста" - lore3 = "Расход еды" - lore = ["Радиус в блоках", "Сила ауры роста", "Расход еды"] +name = "Аура роста" +description = "Выращивайте природу вокруг себя аурой" +lore1 = "Радиус в блоках" +lore2 = "Сила ауры роста" +lore3 = "Расход еды" +lore = ["Радиус в блоках", "Сила ауры роста", "Расход еды"] [herbalism.hippo] - name = "Бегемот травника" - description = "Употребление пищи даёт больше насыщения" - lore1 = "Еда) дополнительные очки насыщения при употреблении" - lore = ["Еда) дополнительные очки насыщения при употреблении"] +name = "Бегемот травника" +description = "Употребление пищи даёт больше насыщения" +lore1 = "Еда) дополнительные очки насыщения при употреблении" +lore = ["Еда) дополнительные очки насыщения при употреблении"] [herbalism.myconid] - name = "Миконид травника" - description = "Даёт возможность создавать мицелий" - lore1 = "Любая земля + коричневый и красный гриб = мицелий." - lore = ["Любая земля + коричневый и красный гриб = мицелий."] +name = "Миконид травника" +description = "Даёт возможность создавать мицелий" +lore1 = "Любая земля + коричневый и красный гриб = мицелий." +lore = ["Любая земля + коричневый и красный гриб = мицелий."] [herbalism.terralid] - name = "Терралид травника" - description = "Даёт возможность создавать блоки дёрна" - lore1 = "Три семечка над 3 блоками земли = 3 блока дёрна." - lore = ["Три семечка над 3 блоками земли = 3 блока дёрна."] +name = "Терралид травника" +description = "Даёт возможность создавать блоки дёрна" +lore1 = "Три семечка над 3 блоками земли = 3 блока дёрна." +lore = ["Три семечка над 3 блоками земли = 3 блока дёрна."] [herbalism.cobweb] - name = "Плетёльщик паутины" - description = "Даёт возможность создавать паутину на верстаке" - lore1 = "Девять нитей = одна паутина." - lore = ["Девять нитей = одна паутина."] +name = "Плетёльщик паутины" +description = "Даёт возможность создавать паутину на верстаке" +lore1 = "Девять нитей = одна паутина." +lore = ["Девять нитей = одна паутина."] [herbalism.mushroom_blocks] - name = "Грибник" - description = "Даёт возможность создавать грибные блоки на верстаке" - lore1 = "Четыре гриба = блок, или блок = стебель." - lore = ["Четыре гриба = блок, или блок = стебель."] +name = "Грибник" +description = "Даёт возможность создавать грибные блоки на верстаке" +lore1 = "Четыре гриба = блок, или блок = стебель." +lore = ["Четыре гриба = блок, или блок = стебель."] [herbalism.drop_to_inventory] - name = "Автосбор с мотыги" +name = "Автосбор с мотыги" [herbalism.hungry_shield] - name = "Голодный щит" - description = "Получайте урон в голод, а не в здоровье." - lore1 = "Сопротивление за счёт голода" - lore = ["Сопротивление за счёт голода"] +name = "Голодный щит" +description = "Получайте урон в голод, а не в здоровье." +lore1 = "Сопротивление за счёт голода" +lore = ["Сопротивление за счёт голода"] [herbalism.luck] - name = "Удача травника" - description = "При разрушении травы/цветов есть шанс получить случайный предмет" - lore0 = "Цветы = еда, Трава = семена" - lore1 = "Шанс получить предмет из цветов" - lore2 = "Шанс получить предмет из травы" - lore = ["Цветы = еда, Трава = семена", "Шанс получить предмет из цветов", "Шанс получить предмет из травы"] +name = "Удача травника" +description = "При разрушении травы/цветов есть шанс получить случайный предмет" +lore0 = "Цветы = еда, Трава = семена" +lore1 = "Шанс получить предмет из цветов" +lore2 = "Шанс получить предмет из травы" +lore = ["Цветы = еда, Трава = семена", "Шанс получить предмет из цветов", "Шанс получить предмет из травы"] [herbalism.replant] - name = "Сбор и пересадка" - description = "Нажмите ПКМ мотыгой по культуре, чтобы собрать и пересадить." - lore1 = "Радиус пересадки в блоках" - lore = ["Радиус пересадки в блоках"] +name = "Сбор и пересадка" +description = "Нажмите ПКМ мотыгой по культуре, чтобы собрать и пересадить." +lore1 = "Радиус пересадки в блоках" +lore = ["Радиус пересадки в блоках"] # hunter [hunter] [hunter.adrenaline] - name = "Адреналин" - description = "Наносите больше урона, чем ниже ваше здоровье (ближний бой)" - lore1 = "Максимальный урон" - lore = ["Максимальный урон"] +name = "Адреналин" +description = "Наносите больше урона, чем ниже ваше здоровье (ближний бой)" +lore1 = "Максимальный урон" +lore = ["Максимальный урон"] [hunter.penalty] - name = "" - description = "" - lore1 = "Вы получите отравление, если у вас закончится голод" - lore = ["Вы получите отравление, если у вас закончится голод"] +name = "" +description = "" +lore1 = "Вы получите отравление, если у вас закончится голод" +lore = ["Вы получите отравление, если у вас закончится голод"] [hunter.drop_to_inventory] - name = "Автосбор предметов" - description = "При убийстве или разрушении блока мечом дроп телепортируется в ваш инвентарь" - lore1 = "Когда предмет выпадает из моба/блока, он попадает в ваш инвентарь, если возможно." - lore = ["Когда предмет выпадает из моба/блока, он попадает в ваш инвентарь, если возможно."] +name = "Автосбор предметов" +description = "При убийстве или разрушении блока мечом дроп телепортируется в ваш инвентарь" +lore1 = "Когда предмет выпадает из моба/блока, он попадает в ваш инвентарь, если возможно." +lore = ["Когда предмет выпадает из моба/блока, он попадает в ваш инвентарь, если возможно."] [hunter.invisibility] - name = "Исчезающий шаг" - description = "При получении удара вы получаете невидимость ценой голода" - lore1 = "Пассивная невидимость при получении удара" - lore2 = "x уровней невидимости на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Длительность невидимости" - lore = ["Пассивная невидимость при получении удара", "x уровней невидимости на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Длительность невидимости"] +name = "Исчезающий шаг" +description = "При получении удара вы получаете невидимость ценой голода" +lore1 = "Пассивная невидимость при получении удара" +lore2 = "x уровней невидимости на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Длительность невидимости" +lore = ["Пассивная невидимость при получении удара", "x уровней невидимости на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Длительность невидимости"] [hunter.jump_boost] - name = "Высоты охотника" - description = "При получении удара вы получаете усиление прыжка ценой голода" - lore1 = "Пассивное усиление прыжка при получении удара" - lore2 = "x уровней усиления прыжка на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель усиления прыжка, не длительность." - lore = ["Пассивное усиление прыжка при получении удара", "x уровней усиления прыжка на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель усиления прыжка, не длительность."] +name = "Высоты охотника" +description = "При получении удара вы получаете усиление прыжка ценой голода" +lore1 = "Пассивное усиление прыжка при получении удара" +lore2 = "x уровней усиления прыжка на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель усиления прыжка, не длительность." +lore = ["Пассивное усиление прыжка при получении удара", "x уровней усиления прыжка на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель усиления прыжка, не длительность."] [hunter.luck] - name = "Удача охотника" - description = "При получении удара вы получаете удачу ценой голода" - lore1 = "Пассивная удача при получении удара" - lore2 = "x уровней удачи на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель удачи, не длительность." - lore = ["Пассивная удача при получении удара", "x уровней удачи на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель удачи, не длительность."] +name = "Удача охотника" +description = "При получении удара вы получаете удачу ценой голода" +lore1 = "Пассивная удача при получении удара" +lore2 = "x уровней удачи на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель удачи, не длительность." +lore = ["Пассивная удача при получении удара", "x уровней удачи на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель удачи, не длительность."] [hunter.regen] - name = "Регенерация охотника" - description = "При получении удара вы получаете регенерацию ценой голода" - lore1 = "Пассивная регенерация при получении удара" - lore2 = "x уровней регенерации на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель регенерации, не длительность." - lore = ["Пассивная регенерация при получении удара", "x уровней регенерации на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель регенерации, не длительность."] +name = "Регенерация охотника" +description = "При получении удара вы получаете регенерацию ценой голода" +lore1 = "Пассивная регенерация при получении удара" +lore2 = "x уровней регенерации на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель регенерации, не длительность." +lore = ["Пассивная регенерация при получении удара", "x уровней регенерации на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель регенерации, не длительность."] [hunter.resistance] - name = "Сопротивление охотника" - description = "При получении удара вы получаете сопротивление ценой голода" - lore1 = "Пассивное сопротивление при получении удара" - lore2 = "x уровней сопротивления на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель сопротивления, не длительность." - lore = ["Пассивное сопротивление при получении удара", "x уровней сопротивления на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель сопротивления, не длительность."] +name = "Сопротивление охотника" +description = "При получении удара вы получаете сопротивление ценой голода" +lore1 = "Пассивное сопротивление при получении удара" +lore2 = "x уровней сопротивления на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель сопротивления, не длительность." +lore = ["Пассивное сопротивление при получении удара", "x уровней сопротивления на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель сопротивления, не длительность."] [hunter.speed] - name = "Скорость охотника" - description = "При получении удара вы получаете скорость ценой голода" - lore1 = "Пассивная скорость при получении удара" - lore2 = "x уровней скорости на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель скорости, не длительность." - lore = ["Пассивная скорость при получении удара", "x уровней скорости на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель скорости, не длительность."] +name = "Скорость охотника" +description = "При получении удара вы получаете скорость ценой голода" +lore1 = "Пассивная скорость при получении удара" +lore2 = "x уровней скорости на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель скорости, не длительность." +lore = ["Пассивная скорость при получении удара", "x уровней скорости на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель скорости, не длительность."] [hunter.strength] - name = "Сила охотника" - description = "При получении удара вы получаете силу ценой голода" - lore1 = "Пассивная сила при получении удара" - lore2 = "x уровней силы на 3 секунды при ударе" - lore3 = "x накопление голода" - lore4 = "Длительность и множитель голода." - lore5 = "Множитель силы, не длительность." - lore = ["Пассивная сила при получении удара", "x уровней силы на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель силы, не длительность."] +name = "Сила охотника" +description = "При получении удара вы получаете силу ценой голода" +lore1 = "Пассивная сила при получении удара" +lore2 = "x уровней силы на 3 секунды при ударе" +lore3 = "x накопление голода" +lore4 = "Длительность и множитель голода." +lore5 = "Множитель силы, не длительность." +lore = ["Пассивная сила при получении удара", "x уровней силы на 3 секунды при ударе", "x накопление голода", "Длительность и множитель голода.", "Множитель силы, не длительность."] # nether [nether] [nether.skull_toss] - name = "Бросок черепа иссушителя" - description1 = "Выпустите своего внутреннего Иссушителя, используя" - description2 = "чью-то" - description3 = "голову." - lore1 = "Секунд перезарядки между бросками черепов." - lore2 = "С черепом иссушителя: бросьте " - lore3 = "Череп иссушителя" - lore4 = "взрывающийся при попадании." - lore = ["Секунд перезарядки между бросками черепов.", "С черепом иссушителя: бросьте ", "Череп иссушителя", "взрывающийся при попадании."] +name = "Бросок черепа иссушителя" +description1 = "Выпустите своего внутреннего Иссушителя, используя" +description2 = "чью-то" +description3 = "голову." +lore1 = "Секунд перезарядки между бросками черепов." +lore2 = "С черепом иссушителя: бросьте " +lore3 = "Череп иссушителя" +lore4 = "взрывающийся при попадании." +lore = ["Секунд перезарядки между бросками черепов.", "С черепом иссушителя: бросьте ", "Череп иссушителя", "взрывающийся при попадании."] [nether.wither_resist] - name = "Сопротивление иссушению" - description = "Противостоит иссушению силой незерита." - lore1 = "шанс отменить иссушение (за каждую деталь)." - lore2 = "Пассив: Незеритовая броня даёт шанс отменить " - lore3 = "иссушение." - lore = ["шанс отменить иссушение (за каждую деталь).", "Пассив: Незеритовая броня даёт шанс отменить ", "иссушение."] +name = "Сопротивление иссушению" +description = "Противостоит иссушению силой незерита." +lore1 = "шанс отменить иссушение (за каждую деталь)." +lore2 = "Пассив: Незеритовая броня даёт шанс отменить " +lore3 = "иссушение." +lore = ["шанс отменить иссушение (за каждую деталь).", "Пассив: Незеритовая броня даёт шанс отменить ", "иссушение."] [nether.fire_resist] - name = "Огнестойкость" - description = "Противостоит огню, закаляя кожу." - lore1 = "шанс отменить эффект горения!" - lore = ["шанс отменить эффект горения!"] +name = "Огнестойкость" +description = "Противостоит огню, закаляя кожу." +lore1 = "шанс отменить эффект горения!" +lore = ["шанс отменить эффект горения!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Автоплавка" - description = "Позволяет автоматически переплавлять стандартные руды" - lore1 = "Руды, которые можно переплавить, переплавляются автоматически" - lore2 = "% шанс на дополнительный" - lore = ["Руды, которые можно переплавить, переплавляются автоматически", "% шанс на дополнительный"] +name = "Автоплавка" +description = "Позволяет автоматически переплавлять стандартные руды" +lore1 = "Руды, которые можно переплавить, переплавляются автоматически" +lore2 = "% шанс на дополнительный" +lore = ["Руды, которые можно переплавить, переплавляются автоматически", "% шанс на дополнительный"] [pickaxe.chisel] - name = "Резец для руды" - description = "Нажмите ПКМ по руде, чтобы выбить из неё больше ресурсов, за счёт значительного износа инструмента." - lore1 = "Шанс выпадения" - lore2 = "Износ инструмента" - lore = ["Шанс выпадения", "Износ инструмента"] +name = "Резец для руды" +description = "Нажмите ПКМ по руде, чтобы выбить из неё больше ресурсов, за счёт значительного износа инструмента." +lore1 = "Шанс выпадения" +lore2 = "Износ инструмента" +lore = ["Шанс выпадения", "Износ инструмента"] [pickaxe.drop_to_inventory] - name = "Автосбор с кирки" - description = "При разрушении блока предмет телепортируется в ваш инвентарь" - lore1 = "Когда предмет выпадает из разрушенного блока, он попадает в ваш инвентарь, если возможно." - lore = ["Когда предмет выпадает из разрушенного блока, он попадает в ваш инвентарь, если возможно."] +name = "Автосбор с кирки" +description = "При разрушении блока предмет телепортируется в ваш инвентарь" +lore1 = "Когда предмет выпадает из разрушенного блока, он попадает в ваш инвентарь, если возможно." +lore = ["Когда предмет выпадает из разрушенного блока, он попадает в ваш инвентарь, если возможно."] [pickaxe.silk_spawner] - name = "Шёлковый спаунер" - description = "Спаунеры выпадают при разрушении" - lore1 = "Спаунеры можно добывать с помощью Шёлкового касания." - lore2 = "Спаунеры можно добывать при приседании." - lore = ["Спаунеры можно добывать с помощью Шёлкового касания.", "Спаунеры можно добывать при приседании."] +name = "Шёлковый спаунер" +description = "Спаунеры выпадают при разрушении" +lore1 = "Спаунеры можно добывать с помощью Шёлкового касания." +lore2 = "Спаунеры можно добывать при приседании." +lore = ["Спаунеры можно добывать с помощью Шёлкового касания.", "Спаунеры можно добывать при приседании."] [pickaxe.vein_miner] - name = "Жильный шахтёр" - description = "Позволяет разом добыть жилу стандартных руд" - lore1 = "Присядьте и добывайте РУДЫ" - lore2 = "Радиус жильной добычи" - lore3 = "Этот навык НЕ собирает весь дроп вместе!" - lore = ["Присядьте и добывайте РУДЫ", "Радиус жильной добычи", "Этот навык НЕ собирает весь дроп вместе!"] +name = "Жильный шахтёр" +description = "Позволяет разом добыть жилу стандартных руд" +lore1 = "Присядьте и добывайте РУДЫ" +lore2 = "Радиус жильной добычи" +lore3 = "Этот навык НЕ собирает весь дроп вместе!" +lore = ["Присядьте и добывайте РУДЫ", "Радиус жильной добычи", "Этот навык НЕ собирает весь дроп вместе!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Возврат стрел" - description = "Возвращайте стрелы после убийства врага." - lore1 = "Шанс вернуть стрелы при попадании/убийстве" - lore2 = "Шанс: " - lore = ["Шанс вернуть стрелы при попадании/убийстве", "Шанс: "] +name = "Возврат стрел" +description = "Возвращайте стрелы после убийства врага." +lore1 = "Шанс вернуть стрелы при попадании/убийстве" +lore2 = "Шанс: " +lore = ["Шанс вернуть стрелы при попадании/убийстве", "Шанс: "] [ranged.web_shot] - name = "Паутинная ловушка" - description = "Окружите цель паутиной при попадании!" - lore1 = "8 паутин вокруг снежка, и бросайте!" - lore2 = "секунд в клетке, примерно." - lore = ["8 паутин вокруг снежка, и бросайте!", "секунд в клетке, примерно."] +name = "Паутинная ловушка" +description = "Окружите цель паутиной при попадании!" +lore1 = "8 паутин вокруг снежка, и бросайте!" +lore2 = "секунд в клетке, примерно." +lore = ["8 паутин вокруг снежка, и бросайте!", "секунд в клетке, примерно."] [ranged.force_shot] - name = "Усиленный выстрел" - description = "Стреляйте снарядами дальше и быстрее!" - advancementname = "Дальний выстрел" - advancementlore = "Попадите с расстояния более 30 блоков!" - lore1 = "Скорость снаряда" - lore = ["Скорость снаряда"] +name = "Усиленный выстрел" +description = "Стреляйте снарядами дальше и быстрее!" +advancementname = "Дальний выстрел" +advancementlore = "Попадите с расстояния более 30 блоков!" +lore1 = "Скорость снаряда" +lore = ["Скорость снаряда"] [ranged.lunge_shot] - name = "Выстрел с выпадом" - description = "Во время падения ваши стрелы отбрасывают вас в случайном направлении" - lore1 = "Случайная скорость рывка" - lore = ["Случайная скорость рывка"] +name = "Выстрел с выпадом" +description = "Во время падения ваши стрелы отбрасывают вас в случайном направлении" +lore1 = "Случайная скорость рывка" +lore = ["Случайная скорость рывка"] [ranged.arrow_piercing] - name = "Пронзающая стрела" - description = "Добавляет пронзание снарядам! Стреляйте сквозь цели!" - lore1 = "Пронзённых целей" - lore = ["Пронзённых целей"] +name = "Пронзающая стрела" +description = "Добавляет пронзание снарядам! Стреляйте сквозь цели!" +lore1 = "Пронзённых целей" +lore = ["Пронзённых целей"] # rift [rift] [rift.remote_access] - name = "Удалённый доступ" - description = "Потяните из пустоты и получите доступ к отмеченному контейнеру." - lore1 = "Эндер-жемчуг + Компас = Портключ Реликвария" - lore2 = "Этот предмет позволяет удалённо открывать контейнеры" - lore3 = "После крафта посмотрите на предмет для инструкции" - notcontainer = "Это не контейнер" - lore = ["Эндер-жемчуг + Компас = Портключ Реликвария", "Этот предмет позволяет удалённо открывать контейнеры", "После крафта посмотрите на предмет для инструкции"] +name = "Удалённый доступ" +description = "Потяните из пустоты и получите доступ к отмеченному контейнеру." +lore1 = "Эндер-жемчуг + Компас = Портключ Реликвария" +lore2 = "Этот предмет позволяет удалённо открывать контейнеры" +lore3 = "После крафта посмотрите на предмет для инструкции" +notcontainer = "Это не контейнер" +lore = ["Эндер-жемчуг + Компас = Портключ Реликвария", "Этот предмет позволяет удалённо открывать контейнеры", "После крафта посмотрите на предмет для инструкции"] [rift.blink] - name = "Мерцание Разлома" - description = "Мгновенная телепортация на короткое расстояние, одним мерцанием!" - lore1 = "Блоков за мерцание (2x по вертикали)" - lore2 = "Во время бега: дважды нажмите Прыжок, чтобы " - lore3 = "мерцнуть" - lore = ["Блоков за мерцание (2x по вертикали)", "Во время бега: дважды нажмите Прыжок, чтобы ", "мерцнуть"] +name = "Мерцание Разлома" +description = "Мгновенная телепортация на короткое расстояние, одним мерцанием!" +lore1 = "Блоков за мерцание (2x по вертикали)" +lore2 = "Во время бега: дважды нажмите Прыжок, чтобы " +lore3 = "мерцнуть" +lore = ["Блоков за мерцание (2x по вертикали)", "Во время бега: дважды нажмите Прыжок, чтобы ", "мерцнуть"] [rift.chest] - name = "Портативный эндер-сундук" - description = "Откройте эндер-сундук, нажав ЛКМ по нему в руке." - lore1 = "Нажмите на эндер-сундук в руке, чтобы открыть (только не ставьте его)" - lore = ["Нажмите на эндер-сундук в руке, чтобы открыть (только не ставьте его)"] +name = "Портативный эндер-сундук" +description = "Откройте эндер-сундук, нажав ЛКМ по нему в руке." +lore1 = "Нажмите на эндер-сундук в руке, чтобы открыть (только не ставьте его)" +lore = ["Нажмите на эндер-сундук в руке, чтобы открыть (только не ставьте его)"] [rift.descent] - name = "Антилевитация" - description = "Устали зависать в воздухе? Этот навык для вас!" - lore1 = "Просто присядьте, чтобы спуститься, и вы будете падать медленнее обычного!" - lore2 = "Перезарядка:" - lore = ["Просто присядьте, чтобы спуститься, и вы будете падать медленнее обычного!", "Перезарядка:"] +name = "Антилевитация" +description = "Устали зависать в воздухе? Этот навык для вас!" +lore1 = "Просто присядьте, чтобы спуститься, и вы будете падать медленнее обычного!" +lore2 = "Перезарядка:" +lore = ["Просто присядьте, чтобы спуститься, и вы будете падать медленнее обычного!", "Перезарядка:"] [rift.gate] - name = "Врата Разлома" - description = "Телепортируйтесь к отмеченному месту." - lore1 = "КРАФТ: Изумруд + Осколок аметиста + Эндер-жемчуг" - lore2 = "Прочтите перед использованием!" - lore3 = "Задержка 5 секунд, " - lore4 = "вы можете погибнуть во время этой анимации" - lore = ["КРАФТ: Изумруд + Осколок аметиста + Эндер-жемчуг", "Прочтите перед использованием!", "Задержка 5 секунд, ", "вы можете погибнуть во время этой анимации"] +name = "Врата Разлома" +description = "Телепортируйтесь к отмеченному месту." +lore1 = "КРАФТ: Изумруд + Осколок аметиста + Эндер-жемчуг" +lore2 = "Прочтите перед использованием!" +lore3 = "Задержка 5 секунд, " +lore4 = "вы можете погибнуть во время этой анимации" +lore = ["КРАФТ: Изумруд + Осколок аметиста + Эндер-жемчуг", "Прочтите перед использованием!", "Задержка 5 секунд, ", "вы можете погибнуть во время этой анимации"] [rift.resist] - name = "Сопротивление Разлома" - description = "Получайте Сопротивление при использовании эндер-предметов и способностей" - lore1 = "+ Пассив: даёт сопротивление при использовании способностей Разлома или эндер-предметов" - lore2 = "НЕ включает портативный эндер-сундук, только расходуемые предметы" - lore = ["+ Пассив: даёт сопротивление при использовании способностей Разлома или эндер-предметов", "НЕ включает портативный эндер-сундук, только расходуемые предметы"] +name = "Сопротивление Разлома" +description = "Получайте Сопротивление при использовании эндер-предметов и способностей" +lore1 = "+ Пассив: даёт сопротивление при использовании способностей Разлома или эндер-предметов" +lore2 = "НЕ включает портативный эндер-сундук, только расходуемые предметы" +lore = ["+ Пассив: даёт сопротивление при использовании способностей Разлома или эндер-предметов", "НЕ включает портативный эндер-сундук, только расходуемые предметы"] [rift.visage] - name = "Облик Разлома" - description = "Эндермены не становятся агрессивными, если у вас в инвентаре есть эндер-жемчуг." - lore1 = "Эндермены не будут агрессивными, если у вас в инвентаре есть эндер-жемчуг." - lore = ["Эндермены не будут агрессивными, если у вас в инвентаре есть эндер-жемчуг."] +name = "Облик Разлома" +description = "Эндермены не становятся агрессивными, если у вас в инвентаре есть эндер-жемчуг." +lore1 = "Эндермены не будут агрессивными, если у вас в инвентаре есть эндер-жемчуг." +lore = ["Эндермены не будут агрессивными, если у вас в инвентаре есть эндер-жемчуг."] # seaborn [seaborn] [seaborn.oxygen] - name = "Органический кислородный баллон" - description = "Удерживайте больше кислорода в своих маленьких лёгких!" - lore1 = "Увеличение запаса кислорода" - lore = ["Увеличение запаса кислорода"] +name = "Органический кислородный баллон" +description = "Удерживайте больше кислорода в своих маленьких лёгких!" +lore1 = "Увеличение запаса кислорода" +lore = ["Увеличение запаса кислорода"] [seaborn.fishers_fantasy] - name = "Мечта рыбака" - description = "Получайте больше опыта от рыбалки и ловите больше рыбы!" - lore1 = "С каждым уровнем растёт шанс получить больше опыта и рыбы!" - lore = ["С каждым уровнем растёт шанс получить больше опыта и рыбы!"] +name = "Мечта рыбака" +description = "Получайте больше опыта от рыбалки и ловите больше рыбы!" +lore1 = "С каждым уровнем растёт шанс получить больше опыта и рыбы!" +lore = ["С каждым уровнем растёт шанс получить больше опыта и рыбы!"] [seaborn.haste] - name = "Черепаший шахтёр" - description = "При добыче под водой вы получаете Спешку!" - lore1 = "Спешка III применяется под водой при добыче (суммируется с Подводником) после окончания эффекта подводного дыхания!" - lore = ["Спешка III применяется под водой при добыче (суммируется с Подводником) после окончания эффекта подводного дыхания!"] +name = "Черепаший шахтёр" +description = "При добыче под водой вы получаете Спешку!" +lore1 = "Спешка III применяется под водой при добыче (суммируется с Подводником) после окончания эффекта подводного дыхания!" +lore = ["Спешка III применяется под водой при добыче (суммируется с Подводником) после окончания эффекта подводного дыхания!"] [seaborn.night_vision] - name = "Зрение черепахи" - description = "Под водой вы получаете ночное зрение" - lore1 = "Просто получайте ночное зрение под водой после окончания эффекта подводного дыхания!" - lore = ["Просто получайте ночное зрение под водой после окончания эффекта подводного дыхания!"] +name = "Зрение черепахи" +description = "Под водой вы получаете ночное зрение" +lore1 = "Просто получайте ночное зрение под водой после окончания эффекта подводного дыхания!" +lore = ["Просто получайте ночное зрение под водой после окончания эффекта подводного дыхания!"] [seaborn.dolphin_grace] - name = "Грация дельфина" - description = "Плавайте как дельфин, без самих дельфинов" - lore1 = "+ Пассив: получите " - lore2 = "x скорости (грация дельфина)" - lore3 = "Точная немецкая инженер... стоп, что-то не то... Не совместимо с Покорителем глубин" - lore = ["+ Пассив: получите ", "x скорости (грация дельфина)", "Точная немецкая инженер... стоп, что-то не то... Не совместимо с Покорителем глубин"] +name = "Грация дельфина" +description = "Плавайте как дельфин, без самих дельфинов" +lore1 = "+ Пассив: получите " +lore2 = "x скорости (грация дельфина)" +lore3 = "Точная немецкая инженер... стоп, что-то не то... Не совместимо с Покорителем глубин" +lore = ["+ Пассив: получите ", "x скорости (грация дельфина)", "Точная немецкая инженер... стоп, что-то не то... Не совместимо с Покорителем глубин"] # stealth [stealth] [stealth.ghost_armor] - name = "Призрачная броня" - description = "Медленно растущая броня, когда вы не получаете урон. Исчезает после 1 удара" - lore1 = "Максимум брони" - lore2 = "Скорость" - lore = ["Максимум брони", "Скорость"] +name = "Призрачная броня" +description = "Медленно растущая броня, когда вы не получаете урон. Исчезает после 1 удара" +lore1 = "Максимум брони" +lore2 = "Скорость" +lore = ["Максимум брони", "Скорость"] [stealth.night_vision] - name = "Скрытное зрение" - description = "Получайте ночное зрение при приседании" - lore1 = "Получите всплеск " - lore2 = "ночного зрения" - lore3 = "при приседании" - lore = ["Получите всплеск ", "ночного зрения", "при приседании"] +name = "Скрытное зрение" +description = "Получайте ночное зрение при приседании" +lore1 = "Получите всплеск " +lore2 = "ночного зрения" +lore3 = "при приседании" +lore = ["Получите всплеск ", "ночного зрения", "при приседании"] [stealth.snatch] - name = "Хватка" - description = "Мгновенно подбирайте выброшенные предметы при приседании!" - lore1 = "Радиус хватки" - lore = ["Радиус хватки"] +name = "Хватка" +description = "Мгновенно подбирайте выброшенные предметы при приседании!" +lore1 = "Радиус хватки" +lore = ["Радиус хватки"] [stealth.speed] - name = "Скорость в приседе" - description = "Получайте ускорение при приседании" - lore1 = "Скорость приседания" - lore = ["Скорость приседания"] +name = "Скорость в приседе" +description = "Получайте ускорение при приседании" +lore1 = "Скорость приседания" +lore = ["Скорость приседания"] [stealth.ender_veil] - name = "Завеса Края" - description = "Больше не нужны тыквы для защиты от эндерменов" - lore1 = "Защита от эндерменов при приседании" - lore2 = "Полная защита от всех эндерменов" - lore = ["Защита от эндерменов при приседании", "Полная защита от всех эндерменов"] +name = "Завеса Края" +description = "Больше не нужны тыквы для защиты от эндерменов" +lore1 = "Защита от эндерменов при приседании" +lore2 = "Полная защита от всех эндерменов" +lore = ["Защита от эндерменов при приседании", "Полная защита от всех эндерменов"] # sword [sword] [sword.machete] - name = "Мачете" - description = "С лёгкостью прорубайте листву!" - lore1 = "Радиус удара" - lore2 = "Перезарядка удара" - lore3 = "Износ инструмента" - lore = ["Радиус удара", "Перезарядка удара", "Износ инструмента"] +name = "Мачете" +description = "С лёгкостью прорубайте листву!" +lore1 = "Радиус удара" +lore2 = "Перезарядка удара" +lore3 = "Износ инструмента" +lore = ["Радиус удара", "Перезарядка удара", "Износ инструмента"] [sword.bloody_blade] - name = "Кровавый клинок" - description = "Удары мечом вызывают кровотечение!" - lore1 = "Удар мечом по живому существу вызывает кровотечение" - lore2 = "Длительность кровотечения" - lore3 = "Перезарядка кровотечения" - lore = ["Удар мечом по живому существу вызывает кровотечение", "Длительность кровотечения", "Перезарядка кровотечения"] +name = "Кровавый клинок" +description = "Удары мечом вызывают кровотечение!" +lore1 = "Удар мечом по живому существу вызывает кровотечение" +lore2 = "Длительность кровотечения" +lore3 = "Перезарядка кровотечения" +lore = ["Удар мечом по живому существу вызывает кровотечение", "Длительность кровотечения", "Перезарядка кровотечения"] [sword.poisoned_blade] - name = "Отравленный клинок" - description = "Удары мечом вызывают отравление!" - lore1 = "Удар мечом по живому существу вызывает отравление" - lore2 = "Длительность отравления" - lore3 = "Перезарядка отравления" - lore = ["Удар мечом по живому существу вызывает отравление", "Длительность отравления", "Перезарядка отравления"] +name = "Отравленный клинок" +description = "Удары мечом вызывают отравление!" +lore1 = "Удар мечом по живому существу вызывает отравление" +lore2 = "Длительность отравления" +lore3 = "Перезарядка отравления" +lore = ["Удар мечом по живому существу вызывает отравление", "Длительность отравления", "Перезарядка отравления"] # taming [taming] [taming.damage] - name = "Урон питомца" - description = "Увеличьте урон, наносимый прирученными животными." - lore1 = "Увеличение урона" - lore = ["Увеличение урона"] +name = "Урон питомца" +description = "Увеличьте урон, наносимый прирученными животными." +lore1 = "Увеличение урона" +lore = ["Увеличение урона"] [taming.health] - name = "Здоровье питомца" - description = "Увеличьте здоровье прирученных животных." - lore1 = "Увеличение здоровья" - lore = ["Увеличение здоровья"] +name = "Здоровье питомца" +description = "Увеличьте здоровье прирученных животных." +lore1 = "Увеличение здоровья" +lore = ["Увеличение здоровья"] [taming.regeneration] - name = "Регенерация питомца" - description = "Увеличьте регенерацию прирученных животных." - lore1 = "ХП/с" - lore = ["ХП/с"] +name = "Регенерация питомца" +description = "Увеличьте регенерацию прирученных животных." +lore1 = "ХП/с" +lore = ["ХП/с"] # tragoul [tragoul] [tragoul.thorns] - name = "Шипы" - description = "Отражайте урон обратно нападающему!" - lore1 = "Отражённый урон при получении удара" - lore = ["Отражённый урон при получении удара"] +name = "Шипы" +description = "Отражайте урон обратно нападающему!" +lore1 = "Отражённый урон при получении удара" +lore = ["Отражённый урон при получении удара"] [tragoul.globe] - name = "Сфера боли" - description = "Разделите наносимый урон по количеству врагов вокруг вас!" - lore1 = "Чем больше врагов вокруг, тем меньше урона по каждому" - lore2 = "Радиус: " - lore3 = "Добавленный урон всем существам: " - lore = ["Чем больше врагов вокруг, тем меньше урона по каждому", "Радиус: ", "Добавленный урон всем существам: "] +name = "Сфера боли" +description = "Разделите наносимый урон по количеству врагов вокруг вас!" +lore1 = "Чем больше врагов вокруг, тем меньше урона по каждому" +lore2 = "Радиус: " +lore3 = "Добавленный урон всем существам: " +lore = ["Чем больше врагов вокруг, тем меньше урона по каждому", "Радиус: ", "Добавленный урон всем существам: "] [tragoul.healing] - name = "Воля боли" - description = "Восстанавливайте здоровье в зависимости от нанесённого урона!" - lore1 = "Причинять вред ещё никогда не было так приятно! Исцеление от нанесённого урона" - lore2 = "3-секундное окно для урона и исцеления, затем 1 секунда перезарядки " - lore3 = "Исцеление за процент урона: " - lore = ["Причинять вред ещё никогда не было так приятно! Исцеление от нанесённого урона", "3-секундное окно для урона и исцеления, затем 1 секунда перезарядки ", "Исцеление за процент урона: "] +name = "Воля боли" +description = "Восстанавливайте здоровье в зависимости от нанесённого урона!" +lore1 = "Причинять вред ещё никогда не было так приятно! Исцеление от нанесённого урона" +lore2 = "3-секундное окно для урона и исцеления, затем 1 секунда перезарядки " +lore3 = "Исцеление за процент урона: " +lore = ["Причинять вред ещё никогда не было так приятно! Исцеление от нанесённого урона", "3-секундное окно для урона и исцеления, затем 1 секунда перезарядки ", "Исцеление за процент урона: "] [tragoul.lance] - name = "Трупные копья" - description = "Убийство врага или убийство способностью создаёт копьё, поражающее ближайшего врага!" - lore1 = "Копья вылетают из всего, что вы убьёте, И если эта способность убьёт врага." - lore2 = "Пожертвуйте частью здоровья для создания копий (это может убить вас)" - lore3 = "Максимум копий: 1 + " - lore = ["Копья вылетают из всего, что вы убьёте, И если эта способность убьёт врага.", "Пожертвуйте частью здоровья для создания копий (это может убить вас)", "Максимум копий: 1 + "] +name = "Трупные копья" +description = "Убийство врага или убийство способностью создаёт копьё, поражающее ближайшего врага!" +lore1 = "Копья вылетают из всего, что вы убьёте, И если эта способность убьёт врага." +lore2 = "Пожертвуйте частью здоровья для создания копий (это может убить вас)" +lore3 = "Максимум копий: 1 + " +lore = ["Копья вылетают из всего, что вы убьёте, И если эта способность убьёт врага.", "Пожертвуйте частью здоровья для создания копий (это может убить вас)", "Максимум копий: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Стеклянная пушка" - description = "Бонусный урон без оружия, чем ниже значение брони" - lore1 = "x урона при 0 брони" - lore2 = "Бонусный урон за уровень" - lore = ["x урона при 0 брони", "Бонусный урон за уровень"] +name = "Стеклянная пушка" +description = "Бонусный урон без оружия, чем ниже значение брони" +lore1 = "x урона при 0 брони" +lore2 = "Бонусный урон за уровень" +lore = ["x урона при 0 брони", "Бонусный урон за уровень"] [unarmed.power] - name = "Сила без оружия" - description = "Увеличенный урон без оружия" - lore1 = "Урон" - lore = ["Урон"] +name = "Сила без оружия" +description = "Увеличенный урон без оружия" +lore1 = "Урон" +lore = ["Урон"] [unarmed.sucker_punch] - name = "Удар исподтишка" - description = "Удары на бегу, но более смертоносные." - lore1 = "Урон" - lore2 = "Урон увеличивается со скоростью при ударе" - lore = ["Урон", "Урон увеличивается со скоростью при ударе"] +name = "Удар исподтишка" +description = "Удары на бегу, но более смертоносные." +lore1 = "Урон" +lore2 = "Урон увеличивается со скоростью при ударе" +lore = ["Урон", "Урон увеличивается со скоростью при ударе"] diff --git a/src/main/resources/tr_TR.toml b/src/main/resources/tr_TR.toml index 82255210b..4c7065ac1 100644 --- a/src/main/resources/tr_TR.toml +++ b/src/main/resources/tr_TR.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Hareket Etmeliyim!" - description = "1 Kilometre yuru (1.000 blok)" +title = "Hareket Etmeliyim!" +description = "1 Kilometre yuru (1.000 blok)" [advancement.challenge_sprint_5k] - title = "5K Kosu Yap!" - description = "5 Kilometre yuru (5.000 blok)" +title = "5K Kosu Yap!" +description = "5 Kilometre yuru (5.000 blok)" [advancement.challenge_sprint_50k] - title = "Ucarcasina 50K!" - description = "50 Kilometre yuru (50.000 blok)" +title = "Ucarcasina 50K!" +description = "50 Kilometre yuru (50.000 blok)" [advancement.challenge_sprint_500k] - title = "Evreni Dolas!!" - description = "500 Kilometre yuru (500.000 blok)" +title = "Evreni Dolas!!" +description = "500 Kilometre yuru (500.000 blok)" [advancement.challenge_sprint_marathon] - title = "Gercek Bir Maraton Kos!" - description = "42.195 Blok kosar adim at!" +title = "Gercek Bir Maraton Kos!" +description = "42.195 Blok kosar adim at!" [advancement.challenge_place_1k] - title = "Acemi Insaatci!" - description = "1.000 Blok Yerlestir" +title = "Acemi Insaatci!" +description = "1.000 Blok Yerlestir" [advancement.challenge_place_5k] - title = "Orta Duzey Insaatci!" - description = "5.000 Blok Yerlestir" +title = "Orta Duzey Insaatci!" +description = "5.000 Blok Yerlestir" [advancement.challenge_place_50k] - title = "Ileri Duzey Insaatci!" - description = "50.000 Blok Yerlestir" +title = "Ileri Duzey Insaatci!" +description = "50.000 Blok Yerlestir" [advancement.challenge_place_500k] - title = "Usta Insaatci!" - description = "500.000 Blok Yerlestir" +title = "Usta Insaatci!" +description = "500.000 Blok Yerlestir" [advancement.challenge_place_5m] - title = "Simetrinin Ciragi!" - description = "GERCEKLIK SENIN OYUN ALANIN! (5 Milyon Blok)" +title = "Simetrinin Ciragi!" +description = "GERCEKLIK SENIN OYUN ALANIN! (5 Milyon Blok)" [advancement.challenge_chop_1k] - title = "Acemi Oduncu!" - description = "1.000 Blok Kes" +title = "Acemi Oduncu!" +description = "1.000 Blok Kes" [advancement.challenge_chop_5k] - title = "Orta Duzey Oduncu!" - description = "5.000 Blok Kes" +title = "Orta Duzey Oduncu!" +description = "5.000 Blok Kes" [advancement.challenge_chop_50k] - title = "Ileri Duzey Oduncu!" - description = "50.000 Blok Kes" +title = "Ileri Duzey Oduncu!" +description = "50.000 Blok Kes" [advancement.challenge_chop_500k] - title = "Usta Oduncu!" - description = "500.000 Blok Kes" +title = "Usta Oduncu!" +description = "500.000 Blok Kes" [advancement.challenge_chop_5m] - title = "Kopek Jackson" - description = "En iyi uslu cocuk! (5 Milyon Blok)" +title = "Kopek Jackson" +description = "En iyi uslu cocuk! (5 Milyon Blok)" [advancement.challenge_block_1k] - title = "Zorlukla Engelleme!" - description = "1000 Vurusu Engelle" +title = "Zorlukla Engelleme!" +description = "1000 Vurusu Engelle" [advancement.challenge_block_5k] - title = "Engellemek Eglencelidir!" - description = "5000 Vurusu Engelle" +title = "Engellemek Eglencelidir!" +description = "5000 Vurusu Engelle" [advancement.challenge_block_50k] - title = "Engellemek benim Hayatim!" - description = "50.000 Vurusu Engelle" +title = "Engellemek benim Hayatim!" +description = "50.000 Vurusu Engelle" [advancement.challenge_block_500k] - title = "Engellemek benim Amacim!" - description = "500.000 Vurusu Engelle" +title = "Engellemek benim Amacim!" +description = "500.000 Vurusu Engelle" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "5.000.000 Vurusu Engelle" +title = "Die Hand Die Verletzt" +description = "5.000.000 Vurusu Engelle" [advancement.challenge_brew_1k] - title = "Acemi Simyaci!" - description = "1000 Iksir Tuket" +title = "Acemi Simyaci!" +description = "1000 Iksir Tuket" [advancement.challenge_brew_5k] - title = "Orta Duzey Simyaci!" - description = "5000 Iksir Tuket" +title = "Orta Duzey Simyaci!" +description = "5000 Iksir Tuket" [advancement.challenge_brew_50k] - title = "Ileri Duzey Simyaci!" - description = "50.000 Iksir Tuket" +title = "Ileri Duzey Simyaci!" +description = "50.000 Iksir Tuket" [advancement.challenge_brew_500k] - title = "Usta Simyaci!" - description = "500.000 Iksir Tuket" +title = "Usta Simyaci!" +description = "500.000 Iksir Tuket" [advancement.challenge_brew_5m] - title = "Essiz Simyaci" - description = "5.000.000 Iksir Tuket" +title = "Essiz Simyaci" +description = "5.000.000 Iksir Tuket" [advancement.challenge_brewsplash_1k] - title = "Acemi Iksir-Patlatici!" - description = "1000 Iksir Patlat" +title = "Acemi Iksir-Patlatici!" +description = "1000 Iksir Patlat" [advancement.challenge_brewsplash_5k] - title = "Orta Duzey Iksir-Patlatici!" - description = "5000 Iksir Patlat" +title = "Orta Duzey Iksir-Patlatici!" +description = "5000 Iksir Patlat" [advancement.challenge_brewsplash_50k] - title = "Ileri Duzey Iksir-Patlatici!" - description = "50.000 Iksir Patlat" +title = "Ileri Duzey Iksir-Patlatici!" +description = "50.000 Iksir Patlat" [advancement.challenge_brewsplash_500k] - title = "Usta Iksir-Patlatici!" - description = "500.000 Iksir Patlat" +title = "Usta Iksir-Patlatici!" +description = "500.000 Iksir Patlat" [advancement.challenge_brewsplash_5m] - title = "Iksir Patlatma Ustadi" - description = "5.000.000 Iksir Patlat" +title = "Iksir Patlatma Ustadi" +description = "5.000.000 Iksir Patlat" [advancement.challenge_craft_1k] - title = "Kurnaz Zanaatkar!" - description = "1000 Esya Uret" +title = "Kurnaz Zanaatkar!" +description = "1000 Esya Uret" [advancement.challenge_craft_5k] - title = "Huysuz Zanaatkar!" - description = "5000 Esya Uret" +title = "Huysuz Zanaatkar!" +description = "5000 Esya Uret" [advancement.challenge_craft_50k] - title = "Calisan Zanaatkar!" - description = "50.000 Esya Uret" +title = "Calisan Zanaatkar!" +description = "50.000 Esya Uret" [advancement.challenge_craft_500k] - title = "Kakofonik Zanaatkar!" - description = "500.000 Esya Uret" +title = "Kakofonik Zanaatkar!" +description = "500.000 Esya Uret" [advancement.challenge_craft_5m] - title = "Felaketzede Ureticiyuz" - description = "5.000.000 Esya Uret" +title = "Felaketzede Ureticiyuz" +description = "5.000.000 Esya Uret" [advancement.challenge_enchant_1k] - title = "Acemi Buyucu!" - description = "1000 Esya Buyule" +title = "Acemi Buyucu!" +description = "1000 Esya Buyule" [advancement.challenge_enchant_5k] - title = "Orta Duzey Buyucu!" - description = "5000 Esya Buyule" +title = "Orta Duzey Buyucu!" +description = "5000 Esya Buyule" [advancement.challenge_enchant_50k] - title = "Ileri Duzey Buyucu!" - description = "50.000 Esya Buyule" +title = "Ileri Duzey Buyucu!" +description = "50.000 Esya Buyule" [advancement.challenge_enchant_500k] - title = "Usta Buyucu!" - description = "500.000 Esya Buyule" +title = "Usta Buyucu!" +description = "500.000 Esya Buyule" [advancement.challenge_enchant_5m] - title = "Esrarengiz Buyucu" - description = "5.000.000 Esya Buyule" +title = "Esrarengiz Buyucu" +description = "5.000.000 Esya Buyule" [advancement.challenge_excavate_1k] - title = "Hevesli Kazici!" - description = "1000 Blok Kaz" +title = "Hevesli Kazici!" +description = "1000 Blok Kaz" [advancement.challenge_excavate_5k] - title = "Orta Duzey Kazici!" - description = "5000 Blok Kaz" +title = "Orta Duzey Kazici!" +description = "5000 Blok Kaz" [advancement.challenge_excavate_50k] - title = "Ileri Duzey Kazici!" - description = "50.000 Blok Kaz" +title = "Ileri Duzey Kazici!" +description = "50.000 Blok Kaz" [advancement.challenge_excavate_500k] - title = "Usta Kazici!" - description = "500.000 Blok Kaz" +title = "Usta Kazici!" +description = "500.000 Blok Kaz" [advancement.challenge_excavate_5m] - title = "Esrarengiz Kazici" - description = "5.000.000 Blok Kaz" +title = "Esrarengiz Kazici" +description = "5.000.000 Blok Kaz" [advancement.horrible_person] - title = "Sen Korkunc bir Insansin" - description = "Akilalmaz, gercekten" +title = "Sen Korkunc bir Insansin" +description = "Akilalmaz, gercekten" [advancement.challenge_turtle_egg_smasher] - title = "Kaplumbaga Yumurtasi Ezici!" - description = "100 kaplumbaga yumurtasi kir" +title = "Kaplumbaga Yumurtasi Ezici!" +description = "100 kaplumbaga yumurtasi kir" [advancement.challenge_turtle_egg_annihilator] - title = "Kaplumbaga Yumurtasi Yok Edici!" - description = "500 kaplumbaga yumurtasi kir" +title = "Kaplumbaga Yumurtasi Yok Edici!" +description = "500 kaplumbaga yumurtasi kir" [advancement.challenge_novice_hunter] - title = "Acemi Avci!" - description = "100 varlik oldur" +title = "Acemi Avci!" +description = "100 varlik oldur" [advancement.challenge_intermediate_hunter] - title = "Orta Duzey Avci!" - description = "500 varlik oldur" +title = "Orta Duzey Avci!" +description = "500 varlik oldur" [advancement.challenge_advanced_hunter] - title = "Ileri Duzey Avci!" - description = "5000 varlik oldur" +title = "Ileri Duzey Avci!" +description = "5000 varlik oldur" [advancement.challenge_creeper_conqueror] - title = "Creeper Fatihi!" - description = "50 creeper oldur" +title = "Creeper Fatihi!" +description = "50 creeper oldur" [advancement.challenge_creeper_annihilator] - title = "Creeper Yok Edici!" - description = "200 creeper oldur" +title = "Creeper Yok Edici!" +description = "200 creeper oldur" [advancement.challenge_pickaxe_1k] - title = "Acemi Madenci" - description = "1000 Blok Kir" +title = "Acemi Madenci" +description = "1000 Blok Kir" [advancement.challenge_pickaxe_5k] - title = "Yetenekli Madenci" - description = "5000 Blok Kir" +title = "Yetenekli Madenci" +description = "5000 Blok Kir" [advancement.challenge_pickaxe_50k] - title = "Uzman Madenci" - description = "50.000 Blok Kir" +title = "Uzman Madenci" +description = "50.000 Blok Kir" [advancement.challenge_pickaxe_500k] - title = "Usta Madenci" - description = "500.000 Blok Kir" +title = "Usta Madenci" +description = "500.000 Blok Kir" [advancement.challenge_pickaxe_5m] - title = "Efsanevi Madenci" - description = "5.000.000 Blok Kir" +title = "Efsanevi Madenci" +description = "5.000.000 Blok Kir" [advancement.challenge_eat_100] - title = "Ne cok yiyecek!" - description = "100'den fazla Esya Ye!" +title = "Ne cok yiyecek!" +description = "100'den fazla Esya Ye!" [advancement.challenge_eat_1000] - title = "Doyurulamaz Aclik!" - description = "1.000'den fazla Esya Ye!" +title = "Doyurulamaz Aclik!" +description = "1.000'den fazla Esya Ye!" [advancement.challenge_eat_10000] - title = "SONSUZ ACLIK!" - description = "10.000'den fazla Esya Ye!" +title = "SONSUZ ACLIK!" +description = "10.000'den fazla Esya Ye!" [advancement.challenge_harvest_100] - title = "Dolu Hasat" - description = "100'den fazla ekin has!" +title = "Dolu Hasat" +description = "100'den fazla ekin has!" [advancement.challenge_harvest_1000] - title = "Buyuk Hasat" - description = "1.000'den fazla ekin has!" +title = "Buyuk Hasat" +description = "1.000'den fazla ekin has!" [advancement.challenge_swim_1nm] - title = "Insan Denizaltisi!" - description = "1 Deniz Mili yuz (1.852 blok)" +title = "Insan Denizaltisi!" +description = "1 Deniz Mili yuz (1.852 blok)" [advancement.challenge_sneak_1k] - title = "Diz Agrisi" - description = "Bir kilometreden fazla gizlen (1.000 blok)" +title = "Diz Agrisi" +description = "Bir kilometreden fazla gizlen (1.000 blok)" [advancement.challenge_sneak_5k] - title = "Golge Yolcusu" - description = "5.000 bloktan fazla gizlen" +title = "Golge Yolcusu" +description = "5.000 bloktan fazla gizlen" [advancement.challenge_sneak_20k] - title = "Hayalet" - description = "20.000 bloktan fazla gizlen" +title = "Hayalet" +description = "20.000 bloktan fazla gizlen" [advancement.challenge_swim_5k] - title = "Derin Dalgic" - description = "5.000 bloktan fazla yuz" +title = "Derin Dalgic" +description = "5.000 bloktan fazla yuz" [advancement.challenge_swim_20k] - title = "Poseidon'un Secilmisi" - description = "20.000 bloktan fazla yuz" +title = "Poseidon'un Secilmisi" +description = "20.000 bloktan fazla yuz" [advancement.challenge_sword_100] - title = "Ilk Kan" - description = "Kilicla 100 vurus yap" +title = "Ilk Kan" +description = "Kilicla 100 vurus yap" [advancement.challenge_sword_1k] - title = "Kilic Dansisi" - description = "Kilicla 1.000 vurus yap" +title = "Kilic Dansisi" +description = "Kilicla 1.000 vurus yap" [advancement.challenge_sword_10k] - title = "Bin Kesik" - description = "Kilicla 10.000 vurus yap" +title = "Bin Kesik" +description = "Kilicla 10.000 vurus yap" [advancement.challenge_unarmed_100] - title = "Bar Kavgacisi" - description = "Silahsiz 100 vurus yap" +title = "Bar Kavgacisi" +description = "Silahsiz 100 vurus yap" [advancement.challenge_unarmed_1k] - title = "Demir Yumruklar" - description = "Silahsiz 1.000 vurus yap" +title = "Demir Yumruklar" +description = "Silahsiz 1.000 vurus yap" [advancement.challenge_unarmed_10k] - title = "Tek Yumruk" - description = "Silahsiz 10.000 vurus yap" +title = "Tek Yumruk" +description = "Silahsiz 10.000 vurus yap" [advancement.challenge_trag_1k] - title = "Kan Bedeli" - description = "1.000 hasar al" +title = "Kan Bedeli" +description = "1.000 hasar al" [advancement.challenge_trag_10k] - title = "Kizil Dalga" - description = "10.000 hasar al" +title = "Kizil Dalga" +description = "10.000 hasar al" [advancement.challenge_trag_100k] - title = "Acinin Avatari" - description = "100.000 hasar al" +title = "Acinin Avatari" +description = "100.000 hasar al" [advancement.challenge_ranged_100] - title = "Hedef Talimi" - description = "100 mermi firla" +title = "Hedef Talimi" +description = "100 mermi firla" [advancement.challenge_ranged_1k] - title = "Sahin Gozu" - description = "1.000 mermi firla" +title = "Sahin Gozu" +description = "1.000 mermi firla" [advancement.challenge_ranged_10k] - title = "Ok Firtinasi" - description = "10.000 mermi firla" +title = "Ok Firtinasi" +description = "10.000 mermi firla" [advancement.challenge_chronos_1h] - title = "Tik Tak" - description = "1 saat cevrimici ol" +title = "Tik Tak" +description = "1 saat cevrimici ol" [advancement.challenge_chronos_24h] - title = "Zamanin Kumlari" - description = "24 saat cevrimici ol" +title = "Zamanin Kumlari" +description = "24 saat cevrimici ol" [advancement.challenge_chronos_168h] - title = "Zamansiz" - description = "168 saat (1 hafta) cevrimici ol" +title = "Zamansiz" +description = "168 saat (1 hafta) cevrimici ol" [advancement.challenge_nether_50] - title = "Cehennem Kapicisi" - description = "50 nether yaratigi oldur" +title = "Cehennem Kapicisi" +description = "50 nether yaratigi oldur" [advancement.challenge_nether_500] - title = "Ucurum Muhafizi" - description = "500 nether yaratigi oldur" +title = "Ucurum Muhafizi" +description = "500 nether yaratigi oldur" [advancement.challenge_nether_5k] - title = "Nether'in Efendisi" - description = "5.000 nether yaratigi oldur" +title = "Nether'in Efendisi" +description = "5.000 nether yaratigi oldur" [advancement.challenge_rift_50] - title = "Uzaysal Anomali" - description = "50 kez isinlan" +title = "Uzaysal Anomali" +description = "50 kez isinlan" [advancement.challenge_rift_500] - title = "Bosluk Gezgini" - description = "500 kez isinlan" +title = "Bosluk Gezgini" +description = "500 kez isinlan" [advancement.challenge_rift_5k] - title = "Dusler Arasi" - description = "5.000 kez isinlan" +title = "Dusler Arasi" +description = "5.000 kez isinlan" [advancement.challenge_taming_10] - title = "Hayvan Fisildacisi" - description = "10 hayvan cogalt" +title = "Hayvan Fisildacisi" +description = "10 hayvan cogalt" [advancement.challenge_taming_50] - title = "Suru Lideri" - description = "50 hayvan cogalt" +title = "Suru Lideri" +description = "50 hayvan cogalt" [advancement.challenge_taming_500] - title = "Canavar Ustasi" - description = "500 hayvan cogalt" +title = "Canavar Ustasi" +description = "500 hayvan cogalt" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Hiz Iblisi" - description = "5 Kilometreden fazla kos (5,000 blok)" +title = "Hiz Iblisi" +description = "5 Kilometreden fazla kos (5,000 blok)" [advancement.challenge_sprint_dist_50k] - title = "Yildirim Bacaklar" - description = "50 Kilometreden fazla kos (50,000 blok)" +title = "Yildirim Bacaklar" +description = "50 Kilometreden fazla kos (50,000 blok)" [advancement.challenge_agility_swim_1k] - title = "Su Yuruyucusu" - description = "1 Kilometreden fazla yuz (1,000 blok)" +title = "Su Yuruyucusu" +description = "1 Kilometreden fazla yuz (1,000 blok)" [advancement.challenge_agility_swim_10k] - title = "Deniz Gezgini" - description = "10 Kilometreden fazla yuz (10,000 blok)" +title = "Deniz Gezgini" +description = "10 Kilometreden fazla yuz (10,000 blok)" [advancement.challenge_fly_1k] - title = "Gok Dansci" - description = "1 Kilometreden fazla uc (1,000 blok)" +title = "Gok Dansci" +description = "1 Kilometreden fazla uc (1,000 blok)" [advancement.challenge_fly_10k] - title = "Ruzgar Binicisi" - description = "10 Kilometreden fazla uc (10,000 blok)" +title = "Ruzgar Binicisi" +description = "10 Kilometreden fazla uc (10,000 blok)" [advancement.challenge_agility_sneak_500] - title = "Sessiz Adimlar" - description = "500 bloktan fazla sinsi yuru" +title = "Sessiz Adimlar" +description = "500 bloktan fazla sinsi yuru" [advancement.challenge_agility_sneak_5k] - title = "Hayalet Adimlar" - description = "5 Kilometreden fazla sinsi yuru (5,000 blok)" +title = "Hayalet Adimlar" +description = "5 Kilometreden fazla sinsi yuru (5,000 blok)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Yikim Ekibi" - description = "500 blok kir" +title = "Yikim Ekibi" +description = "500 blok kir" [advancement.challenge_demolish_5k] - title = "Yikim Topu" - description = "5,000 blok kir" +title = "Yikim Topu" +description = "5,000 blok kir" [advancement.challenge_value_placed_10k] - title = "Degerli Insaatci" - description = "10,000 deger karsiligi blok yerlestir" +title = "Degerli Insaatci" +description = "10,000 deger karsiligi blok yerlestir" [advancement.challenge_value_placed_100k] - title = "Usta Mimar" - description = "100,000 deger karsiligi blok yerlestir" +title = "Usta Mimar" +description = "100,000 deger karsiligi blok yerlestir" [advancement.challenge_demolish_val_5k] - title = "Kurtarma Uzmani" - description = "Yikimdan 5,000 blok degeri kurtar" +title = "Kurtarma Uzmani" +description = "Yikimdan 5,000 blok degeri kurtar" [advancement.challenge_demolish_val_50k] - title = "Tam Sokuluş" - description = "Yikimdan 50,000 blok degeri kurtar" +title = "Tam Sokuluş" +description = "Yikimdan 50,000 blok degeri kurtar" [advancement.challenge_high_build_100] - title = "Gok Insaatcisi" - description = "Y=128 ustune 100 blok yerlestir" +title = "Gok Insaatcisi" +description = "Y=128 ustune 100 blok yerlestir" [advancement.challenge_high_build_1k] - title = "Bulut Mimari" - description = "Y=128 ustune 1,000 blok yerlestir" +title = "Bulut Mimari" +description = "Y=128 ustune 1,000 blok yerlestir" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Balta Sallayici" - description = "Baltayi 500 kez salla" +title = "Balta Sallayici" +description = "Baltayi 500 kez salla" [advancement.challenge_axe_swing_5k] - title = "Berserker" - description = "Baltayi 5,000 kez salla" +title = "Berserker" +description = "Baltayi 5,000 kez salla" [advancement.challenge_axe_damage_1k] - title = "Kesici" - description = "Balta ile 1,000 hasar ver" +title = "Kesici" +description = "Balta ile 1,000 hasar ver" [advancement.challenge_axe_damage_10k] - title = "Celladın Baltasi" - description = "Balta ile 10,000 hasar ver" +title = "Celladın Baltasi" +description = "Balta ile 10,000 hasar ver" [advancement.challenge_axe_value_5k] - title = "Kereste Tuccari" - description = "5,000 degerinde odun topla" +title = "Kereste Tuccari" +description = "5,000 degerinde odun topla" [advancement.challenge_axe_value_50k] - title = "Kereste Baronu" - description = "50,000 degerinde odun topla" +title = "Kereste Baronu" +description = "50,000 degerinde odun topla" [advancement.challenge_leaves_500] - title = "Yaprak Ufleyici" - description = "Balta ile 500 yaprak bloku temizle" +title = "Yaprak Ufleyici" +description = "Balta ile 500 yaprak bloku temizle" [advancement.challenge_leaves_5k] - title = "Yaprak Dorucu" - description = "Balta ile 5,000 yaprak bloku temizle" +title = "Yaprak Dorucu" +description = "Balta ile 5,000 yaprak bloku temizle" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Hasar Emici" - description = "Kalkan ile 1,000 hasar engelle" +title = "Hasar Emici" +description = "Kalkan ile 1,000 hasar engelle" [advancement.challenge_block_dmg_10k] - title = "Canli Kalkan" - description = "Kalkan ile 10,000 hasar engelle" +title = "Canli Kalkan" +description = "Kalkan ile 10,000 hasar engelle" [advancement.challenge_block_proj_100] - title = "Ok Deflektoru" - description = "Kalkan ile 100 mermi engelle" +title = "Ok Deflektoru" +description = "Kalkan ile 100 mermi engelle" [advancement.challenge_block_proj_1k] - title = "Mermi Kalkani" - description = "Kalkan ile 1,000 mermi engelle" +title = "Mermi Kalkani" +description = "Kalkan ile 1,000 mermi engelle" [advancement.challenge_block_melee_500] - title = "Pariş Ustasi" - description = "Kalkan ile 500 yakin dovus saldirisi engelle" +title = "Pariş Ustasi" +description = "Kalkan ile 500 yakin dovus saldirisi engelle" [advancement.challenge_block_melee_5k] - title = "Demir Kale" - description = "Kalkan ile 5,000 yakin dovus saldirisi engelle" +title = "Demir Kale" +description = "Kalkan ile 5,000 yakin dovus saldirisi engelle" [advancement.challenge_block_heavy_50] - title = "Tank" - description = "50 agir saldiri engelle (5 hasardan fazla)" +title = "Tank" +description = "50 agir saldiri engelle (5 hasardan fazla)" [advancement.challenge_block_heavy_500] - title = "Sarsilmaz Nesne" - description = "500 agir saldiri engelle (5 hasardan fazla)" +title = "Sarsilmaz Nesne" +description = "500 agir saldiri engelle (5 hasardan fazla)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "İksir Atolyesi" - description = "10 iksir tezgahi yerlestir" +title = "İksir Atolyesi" +description = "10 iksir tezgahi yerlestir" [advancement.challenge_brew_stands_50] - title = "İksir Fabrikasi" - description = "50 iksir tezgahi yerlestir" +title = "İksir Fabrikasi" +description = "50 iksir tezgahi yerlestir" [advancement.challenge_brew_strong_25] - title = "Guclu Demleme" - description = "25 guclendirilmis iksir ic" +title = "Guclu Demleme" +description = "25 guclendirilmis iksir ic" [advancement.challenge_brew_strong_250] - title = "Maksimum Guc" - description = "250 guclendirilmis iksir ic" +title = "Maksimum Guc" +description = "250 guclendirilmis iksir ic" [advancement.challenge_brew_splash_hits_50] - title = "Sicrama Bolgesi" - description = "Sicrama iksiri ile 50 varliga vur" +title = "Sicrama Bolgesi" +description = "Sicrama iksiri ile 50 varliga vur" [advancement.challenge_brew_splash_hits_500] - title = "Veba Doktoru" - description = "Sicrama iksiri ile 500 varliga vur" +title = "Veba Doktoru" +description = "Sicrama iksiri ile 500 varliga vur" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Huzursuz" - description = "Aktifken 1 Kilometre yol kat" +title = "Huzursuz" +description = "Aktifken 1 Kilometre yol kat" [advancement.challenge_active_dist_10k] - title = "Yol Bulucu" - description = "Aktifken 10 Kilometre yol kat" +title = "Yol Bulucu" +description = "Aktifken 10 Kilometre yol kat" [advancement.challenge_active_dist_100k] - title = "Surekli Hareket" - description = "Aktifken 100 Kilometre yol kat" +title = "Surekli Hareket" +description = "Aktifken 100 Kilometre yol kat" [advancement.challenge_beds_10] - title = "Erken Kalkan" - description = "10 kez yatakta uyu" +title = "Erken Kalkan" +description = "10 kez yatakta uyu" [advancement.challenge_beds_100] - title = "Zaman Atlayici" - description = "100 kez yatakta uyu" +title = "Zaman Atlayici" +description = "100 kez yatakta uyu" [advancement.challenge_chronos_tp_50] - title = "Zamansal Kayma" - description = "50 kez isinlan" +title = "Zamansal Kayma" +description = "50 kez isinlan" [advancement.challenge_chronos_tp_500] - title = "Zaman Bukulmesi" - description = "500 kez isinlan" +title = "Zaman Bukulmesi" +description = "500 kez isinlan" [advancement.challenge_chronos_deaths_10] - title = "Olumlu" - description = "10 kez ol" +title = "Olumlu" +description = "10 kez ol" [advancement.challenge_chronos_deaths_100] - title = "Olume Meydan Okuyan" - description = "100 kez ol" +title = "Olume Meydan Okuyan" +description = "100 kez ol" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Zanaat Degeri" - description = "Toplam 10,000 degerinde esya imal et" +title = "Zanaat Degeri" +description = "Toplam 10,000 degerinde esya imal et" [advancement.challenge_craft_value_100k] - title = "Usta Zanaatkar" - description = "Toplam 100,000 degerinde esya imal et" +title = "Usta Zanaatkar" +description = "Toplam 100,000 degerinde esya imal et" [advancement.challenge_craft_tools_25] - title = "Alet Ustasi" - description = "25 alet imal et" +title = "Alet Ustasi" +description = "25 alet imal et" [advancement.challenge_craft_tools_250] - title = "Demirci Ustasi" - description = "250 alet imal et" +title = "Demirci Ustasi" +description = "250 alet imal et" [advancement.challenge_craft_armor_25] - title = "Zirhci" - description = "25 parca zirh imal et" +title = "Zirhci" +description = "25 parca zirh imal et" [advancement.challenge_craft_armor_250] - title = "Usta Zirhci" - description = "250 parca zirh imal et" +title = "Usta Zirhci" +description = "250 parca zirh imal et" [advancement.challenge_craft_mass_25k] - title = "Seri Uretici" - description = "25,000 esya imal et" +title = "Seri Uretici" +description = "25,000 esya imal et" [advancement.challenge_craft_mass_250k] - title = "Endustri Devrimi" - description = "250,000 esya imal et" +title = "Endustri Devrimi" +description = "250,000 esya imal et" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Koleksiyoncu" - description = "50 benzersiz esya kesfet" +title = "Koleksiyoncu" +description = "50 benzersiz esya kesfet" [advancement.challenge_discover_items_250] - title = "Katalogcu" - description = "250 benzersiz esya kesfet" +title = "Katalogcu" +description = "250 benzersiz esya kesfet" [advancement.challenge_discover_blocks_50] - title = "Arastirmaci" - description = "50 benzersiz blok kesfet" +title = "Arastirmaci" +description = "50 benzersiz blok kesfet" [advancement.challenge_discover_blocks_250] - title = "Jeolog" - description = "250 benzersiz blok kesfet" +title = "Jeolog" +description = "250 benzersiz blok kesfet" [advancement.challenge_discover_mobs_25] - title = "Gozlemci" - description = "25 benzersiz yaratik kesfet" +title = "Gozlemci" +description = "25 benzersiz yaratik kesfet" [advancement.challenge_discover_mobs_75] - title = "Doga Bilimci" - description = "75 benzersiz yaratik kesfet" +title = "Doga Bilimci" +description = "75 benzersiz yaratik kesfet" [advancement.challenge_discover_biomes_10] - title = "Gezgin" - description = "10 benzersiz biyom kesfet" +title = "Gezgin" +description = "10 benzersiz biyom kesfet" [advancement.challenge_discover_biomes_40] - title = "Dunya Gezgini" - description = "40 benzersiz biyom kesfet" +title = "Dunya Gezgini" +description = "40 benzersiz biyom kesfet" [advancement.challenge_discover_foods_10] - title = "Gurmand" - description = "10 benzersiz yiyecek kesfet" +title = "Gurmand" +description = "10 benzersiz yiyecek kesfet" [advancement.challenge_discover_foods_30] - title = "Mutfak Ustasi" - description = "30 benzersiz yiyecek kesfet" +title = "Mutfak Ustasi" +description = "30 benzersiz yiyecek kesfet" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Guc Dokucu" - description = "100 buyuleme gucu biriktir" +title = "Guc Dokucu" +description = "100 buyuleme gucu biriktir" [advancement.challenge_enchant_power_1k] - title = "Buyu Ustasi" - description = "1,000 buyuleme gucu biriktir" +title = "Buyu Ustasi" +description = "1,000 buyuleme gucu biriktir" [advancement.challenge_enchant_levels_1k] - title = "Seviye Harcayici" - description = "Buyulemeye 1,000 tecrube seviyesi harca" +title = "Seviye Harcayici" +description = "Buyulemeye 1,000 tecrube seviyesi harca" [advancement.challenge_enchant_levels_10k] - title = "XP Yutagi" - description = "Buyulemeye 10,000 tecrube seviyesi harca" +title = "XP Yutagi" +description = "Buyulemeye 10,000 tecrube seviyesi harca" [advancement.challenge_enchant_high_25] - title = "Yuksek Bahisci" - description = "25 maksimum seviye buyuleme yap" +title = "Yuksek Bahisci" +description = "25 maksimum seviye buyuleme yap" [advancement.challenge_enchant_high_250] - title = "Efsanevi Buyucu" - description = "250 maksimum seviye buyuleme yap" +title = "Efsanevi Buyucu" +description = "250 maksimum seviye buyuleme yap" [advancement.challenge_enchant_total_500] - title = "Seviye Yakici" - description = "Tum buyulemelere toplam 500 seviye harca" +title = "Seviye Yakici" +description = "Tum buyulemelere toplam 500 seviye harca" [advancement.challenge_enchant_total_5k] - title = "Mistik Yatirim" - description = "Tum buyulemelere toplam 5,000 seviye harca" +title = "Mistik Yatirim" +description = "Tum buyulemelere toplam 5,000 seviye harca" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Kazici" - description = "Kuregi 500 kez salla" +title = "Kazici" +description = "Kuregi 500 kez salla" [advancement.challenge_dig_swing_5k] - title = "Hafriyatci" - description = "Kuregi 5,000 kez salla" +title = "Hafriyatci" +description = "Kuregi 5,000 kez salla" [advancement.challenge_dig_damage_1k] - title = "Kurek Savascisi" - description = "Kurekle 1,000 hasar ver" +title = "Kurek Savascisi" +description = "Kurekle 1,000 hasar ver" [advancement.challenge_dig_damage_10k] - title = "Kurek Ustasi" - description = "Kurekle 10,000 hasar ver" +title = "Kurek Ustasi" +description = "Kurekle 10,000 hasar ver" [advancement.challenge_dig_value_5k] - title = "Toprak Tuccari" - description = "5,000 degerinde blok kaz" +title = "Toprak Tuccari" +description = "5,000 degerinde blok kaz" [advancement.challenge_dig_value_50k] - title = "Toprak Baronu" - description = "50,000 degerinde blok kaz" +title = "Toprak Baronu" +description = "50,000 degerinde blok kaz" [advancement.challenge_dig_gravel_500] - title = "Cakil Ogutucusu" - description = "500 cakil, kum veya kil bloku kaz" +title = "Cakil Ogutucusu" +description = "500 cakil, kum veya kil bloku kaz" [advancement.challenge_dig_gravel_5k] - title = "Kum Elekcisi" - description = "5,000 cakil, kum veya kil bloku kaz" +title = "Kum Elekcisi" +description = "5,000 cakil, kum veya kil bloku kaz" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Tohum Ekici" - description = "100 ekin ek" +title = "Tohum Ekici" +description = "100 ekin ek" [advancement.challenge_plant_1k] - title = "Yesil Parmak" - description = "1,000 ekin ek" +title = "Yesil Parmak" +description = "1,000 ekin ek" [advancement.challenge_plant_5k] - title = "Tarim Baronu" - description = "5,000 ekin ek" +title = "Tarim Baronu" +description = "5,000 ekin ek" [advancement.challenge_compost_50] - title = "Geri Donusumcu" - description = "50 esya kompostla" +title = "Geri Donusumcu" +description = "50 esya kompostla" [advancement.challenge_compost_500] - title = "Toprak Zenginlestirici" - description = "500 esya kompostla" +title = "Toprak Zenginlestirici" +description = "500 esya kompostla" [advancement.challenge_shear_50] - title = "Kirkici" - description = "50 varlik kirk" +title = "Kirkici" +description = "50 varlik kirk" [advancement.challenge_shear_250] - title = "Suru Ustasi" - description = "250 varlik kirk" +title = "Suru Ustasi" +description = "250 varlik kirk" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Katil" - description = "500 yaratik oldur" +title = "Katil" +description = "500 yaratik oldur" [advancement.challenge_kills_5k] - title = "Cellat" - description = "5,000 yaratik oldur" +title = "Cellat" +description = "5,000 yaratik oldur" [advancement.challenge_boss_1] - title = "Patron Avcisi" - description = "Bir patron yaratigi oldur" +title = "Patron Avcisi" +description = "Bir patron yaratigi oldur" [advancement.challenge_boss_10] - title = "Efsane Avcisi" - description = "10 patron yaratik oldur" +title = "Efsane Avcisi" +description = "10 patron yaratik oldur" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Soldurulmus" - description = "500 solma hasari al" +title = "Soldurulmus" +description = "500 solma hasari al" [advancement.challenge_wither_dmg_5k] - title = "Bela Hayatta Kalan" - description = "5,000 solma hasari al" +title = "Bela Hayatta Kalan" +description = "5,000 solma hasari al" [advancement.challenge_wither_skel_25] - title = "Kemik Toplayici" - description = "25 wither iskeleti oldur" +title = "Kemik Toplayici" +description = "25 wither iskeleti oldur" [advancement.challenge_wither_skel_250] - title = "İskelet Belasi" - description = "250 wither iskeleti oldur" +title = "İskelet Belasi" +description = "250 wither iskeleti oldur" [advancement.challenge_wither_boss_1] - title = "Wither Avcisi" - description = "Wither'i yen" +title = "Wither Avcisi" +description = "Wither'i yen" [advancement.challenge_wither_boss_10] - title = "Nether Hakimi" - description = "Wither'i 10 kez yen" +title = "Nether Hakimi" +description = "Wither'i 10 kez yen" [advancement.challenge_roses_10] - title = "Olum Bahcivani" - description = "10 wither gulu kir" +title = "Olum Bahcivani" +description = "10 wither gulu kir" [advancement.challenge_roses_100] - title = "Bela Toplayicisi" - description = "100 wither gulu kir" +title = "Bela Toplayicisi" +description = "100 wither gulu kir" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Madenci Kolu" - description = "Kazmayi 500 kez salla" +title = "Madenci Kolu" +description = "Kazmayi 500 kez salla" [advancement.challenge_pick_swing_5k] - title = "Tunel Yapici" - description = "Kazmayi 5,000 kez salla" +title = "Tunel Yapici" +description = "Kazmayi 5,000 kez salla" [advancement.challenge_pick_damage_1k] - title = "Kazma Dovuscusu" - description = "Kazma ile 1,000 hasar ver" +title = "Kazma Dovuscusu" +description = "Kazma ile 1,000 hasar ver" [advancement.challenge_pick_damage_10k] - title = "Savas Kazmasi" - description = "Kazma ile 10,000 hasar ver" +title = "Savas Kazmasi" +description = "Kazma ile 10,000 hasar ver" [advancement.challenge_pick_value_5k] - title = "Mucevher Bulucu" - description = "5,000 degerinde blok kaz" +title = "Mucevher Bulucu" +description = "5,000 degerinde blok kaz" [advancement.challenge_pick_value_50k] - title = "Cevher Baronu" - description = "50,000 degerinde blok kaz" +title = "Cevher Baronu" +description = "50,000 degerinde blok kaz" [advancement.challenge_pick_ores_500] - title = "Arastirmaci" - description = "500 cevher bloku kaz" +title = "Arastirmaci" +description = "500 cevher bloku kaz" [advancement.challenge_pick_ores_5k] - title = "Usta Madenci" - description = "5,000 cevher bloku kaz" +title = "Usta Madenci" +description = "5,000 cevher bloku kaz" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Keskin Nisanci" - description = "1,000 uzak mesafe hasari ver" +title = "Keskin Nisanci" +description = "1,000 uzak mesafe hasari ver" [advancement.challenge_ranged_dmg_10k] - title = "Olumcul Okcu" - description = "10,000 uzak mesafe hasari ver" +title = "Olumcul Okcu" +description = "10,000 uzak mesafe hasari ver" [advancement.challenge_ranged_dist_5k] - title = "Uzun Menzil" - description = "Toplam 5,000 blok mesafe kapsayan mermiler at" +title = "Uzun Menzil" +description = "Toplam 5,000 blok mesafe kapsayan mermiler at" [advancement.challenge_ranged_dist_50k] - title = "Mil Atıcisi" - description = "Toplam 50,000 blok mesafe kapsayan mermiler at" +title = "Mil Atıcisi" +description = "Toplam 50,000 blok mesafe kapsayan mermiler at" [advancement.challenge_ranged_kills_50] - title = "Okcu" - description = "Uzak mesafe silahlariyla 50 yaratik oldur" +title = "Okcu" +description = "Uzak mesafe silahlariyla 50 yaratik oldur" [advancement.challenge_ranged_kills_500] - title = "Usta Okcu" - description = "Uzak mesafe silahlariyla 500 yaratik oldur" +title = "Usta Okcu" +description = "Uzak mesafe silahlariyla 500 yaratik oldur" [advancement.challenge_longshot_25] - title = "Keskin Nisanci" - description = "25 uzun mesafe atisi isabet ettir (30 bloktan fazla)" +title = "Keskin Nisanci" +description = "25 uzun mesafe atisi isabet ettir (30 bloktan fazla)" [advancement.challenge_longshot_250] - title = "Kartal Gozu" - description = "250 uzun mesafe atisi isabet ettir (30 bloktan fazla)" +title = "Kartal Gozu" +description = "250 uzun mesafe atisi isabet ettir (30 bloktan fazla)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "İnci Atici" - description = "50 ender incisi at" +title = "İnci Atici" +description = "50 ender incisi at" [advancement.challenge_rift_pearls_500] - title = "Isinlanma Bagimlisi" - description = "500 ender incisi at" +title = "Isinlanma Bagimlisi" +description = "500 ender incisi at" [advancement.challenge_rift_enderman_50] - title = "Enderman Avcisi" - description = "50 enderman oldur" +title = "Enderman Avcisi" +description = "50 enderman oldur" [advancement.challenge_rift_enderman_500] - title = "Bosluk Takipcisi" - description = "500 enderman oldur" +title = "Bosluk Takipcisi" +description = "500 enderman oldur" [advancement.challenge_rift_dragon_500] - title = "Ejderha Dovuscusu" - description = "Ender Ejderhasi'na 500 hasar ver" +title = "Ejderha Dovuscusu" +description = "Ender Ejderhasi'na 500 hasar ver" [advancement.challenge_rift_dragon_5k] - title = "Ejderha Belasi" - description = "Ender Ejderhasi'na 5,000 hasar ver" +title = "Ejderha Belasi" +description = "Ender Ejderhasi'na 5,000 hasar ver" [advancement.challenge_rift_crystal_10] - title = "Kristal Kirici" - description = "10 ender kristali yok et" +title = "Kristal Kirici" +description = "10 ender kristali yok et" [advancement.challenge_rift_crystal_100] - title = "Son Yikici" - description = "100 ender kristali yok et" +title = "Son Yikici" +description = "100 ender kristali yok et" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Balikci" - description = "25 balik yakala" +title = "Balikci" +description = "25 balik yakala" [advancement.challenge_fish_250] - title = "Usta Balikci" - description = "250 balik yakala" +title = "Usta Balikci" +description = "250 balik yakala" [advancement.challenge_drowned_25] - title = "Bogulmus Avcisi" - description = "25 bogulmus oldur" +title = "Bogulmus Avcisi" +description = "25 bogulmus oldur" [advancement.challenge_drowned_250] - title = "Okyanus Temizleyici" - description = "250 bogulmus oldur" +title = "Okyanus Temizleyici" +description = "250 bogulmus oldur" [advancement.challenge_guardian_10] - title = "Koruyucu Avcisi" - description = "10 koruyucu oldur" +title = "Koruyucu Avcisi" +description = "10 koruyucu oldur" [advancement.challenge_guardian_100] - title = "Tapınak Yakmacisi" - description = "100 koruyucu oldur" +title = "Tapınak Yakmacisi" +description = "100 koruyucu oldur" [advancement.challenge_underwater_blocks_100] - title = "Sualti Madencisi" - description = "Su altinda 100 blok kir" +title = "Sualti Madencisi" +description = "Su altinda 100 blok kir" [advancement.challenge_underwater_blocks_1k] - title = "Sualti Muhendisi" - description = "Su altinda 1,000 blok kir" +title = "Sualti Muhendisi" +description = "Su altinda 1,000 blok kir" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Sirt Biçaklayici" - description = "Sinsileyken 500 hasar ver" +title = "Sirt Biçaklayici" +description = "Sinsileyken 500 hasar ver" [advancement.challenge_stealth_dmg_5k] - title = "Sessiz Katil" - description = "Sinsileyken 5,000 hasar ver" +title = "Sessiz Katil" +description = "Sinsileyken 5,000 hasar ver" [advancement.challenge_stealth_kills_10] - title = "Suikastci" - description = "Sinsileyken 10 yaratik oldur" +title = "Suikastci" +description = "Sinsileyken 10 yaratik oldur" [advancement.challenge_stealth_kills_100] - title = "Golge Bicakçisi" - description = "Sinsileyken 100 yaratik oldur" +title = "Golge Bicakçisi" +description = "Sinsileyken 100 yaratik oldur" [advancement.challenge_stealth_time_1h] - title = "Sabırli" - description = "1 saat sinsi yuru (3,600 saniye)" +title = "Sabırli" +description = "1 saat sinsi yuru (3,600 saniye)" [advancement.challenge_stealth_time_10h] - title = "Golgelerin Efendisi" - description = "10 saat sinsi yuru (36,000 saniye)" +title = "Golgelerin Efendisi" +description = "10 saat sinsi yuru (36,000 saniye)" [advancement.challenge_stealth_arrows_50] - title = "Sessiz Okcu" - description = "Sinsileyken 50 ok at" +title = "Sessiz Okcu" +description = "Sinsileyken 50 ok at" [advancement.challenge_stealth_arrows_500] - title = "Hayalet Okcu" - description = "Sinsileyken 500 ok at" +title = "Hayalet Okcu" +description = "Sinsileyken 500 ok at" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Kilic Ciragi" - description = "Kılıcla 1,000 hasar ver" +title = "Kilic Ciragi" +description = "Kılıcla 1,000 hasar ver" [advancement.challenge_sword_dmg_10k] - title = "Kilicçi" - description = "Kılıcla 10,000 hasar ver" +title = "Kilicçi" +description = "Kılıcla 10,000 hasar ver" [advancement.challenge_sword_kills_50] - title = "Duellocu" - description = "Kılıcla 50 yaratik oldur" +title = "Duellocu" +description = "Kılıcla 50 yaratik oldur" [advancement.challenge_sword_kills_500] - title = "Gladyator" - description = "Kılıcla 500 yaratik oldur" +title = "Gladyator" +description = "Kılıcla 500 yaratik oldur" [advancement.challenge_sword_crit_50] - title = "Kritik Vurucu" - description = "Kılıcla 50 kritik vurus yap" +title = "Kritik Vurucu" +description = "Kılıcla 50 kritik vurus yap" [advancement.challenge_sword_crit_500] - title = "Hassasiyet Ustasi" - description = "Kılıcla 500 kritik vurus yap" +title = "Hassasiyet Ustasi" +description = "Kılıcla 500 kritik vurus yap" [advancement.challenge_sword_heavy_25] - title = "Agir Vurucu" - description = "Kılıcla 25 agir vurus yap (8 hasardan fazla)" +title = "Agir Vurucu" +description = "Kılıcla 25 agir vurus yap (8 hasardan fazla)" [advancement.challenge_sword_heavy_250] - title = "Yikici Darbe" - description = "Kılıcla 250 agir vurus yap (8 hasardan fazla)" +title = "Yikici Darbe" +description = "Kılıcla 250 agir vurus yap (8 hasardan fazla)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Canavar Egitici" - description = "Evcil hayvanlarin toplam 500 hasar versin" +title = "Canavar Egitici" +description = "Evcil hayvanlarin toplam 500 hasar versin" [advancement.challenge_pet_dmg_5k] - title = "Savas Ustasi" - description = "Evcil hayvanlarin toplam 5,000 hasar versin" +title = "Savas Ustasi" +description = "Evcil hayvanlarin toplam 5,000 hasar versin" [advancement.challenge_tamed_10] - title = "Hayvan Dostu" - description = "10 hayvan ehillestir" +title = "Hayvan Dostu" +description = "10 hayvan ehillestir" [advancement.challenge_tamed_100] - title = "Hayvanat Bahcesi Bakicisi" - description = "100 hayvan ehillestir" +title = "Hayvanat Bahcesi Bakicisi" +description = "100 hayvan ehillestir" [advancement.challenge_pet_kills_25] - title = "Suru Taktigi" - description = "Evcil hayvanlarin 25 yaratik oldursun" +title = "Suru Taktigi" +description = "Evcil hayvanlarin 25 yaratik oldursun" [advancement.challenge_pet_kills_250] - title = "Alfa Komutan" - description = "Evcil hayvanlarin 250 yaratik oldursun" +title = "Alfa Komutan" +description = "Evcil hayvanlarin 250 yaratik oldursun" [advancement.challenge_taming_2500] - title = "Uretim Uzmani" - description = "2,500 hayvan cogalt" +title = "Uretim Uzmani" +description = "2,500 hayvan cogalt" [advancement.challenge_taming_25k] - title = "Genetik Ustasi" - description = "25,000 hayvan cogalt" +title = "Genetik Ustasi" +description = "25,000 hayvan cogalt" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Kum Torbasi" - description = "500 darbe al" +title = "Kum Torbasi" +description = "500 darbe al" [advancement.challenge_trag_hits_5k] - title = "Ceza Aşigi" - description = "5,000 darbe al" +title = "Ceza Aşigi" +description = "5,000 darbe al" [advancement.challenge_trag_deaths_10] - title = "Dokuz Canli" - description = "10 kez ol" +title = "Dokuz Canli" +description = "10 kez ol" [advancement.challenge_trag_deaths_100] - title = "Tekrarlayan Kabus" - description = "100 kez ol" +title = "Tekrarlayan Kabus" +description = "100 kez ol" [advancement.challenge_trag_fire_500] - title = "Yanik Kurbani" - description = "500 ates hasari al" +title = "Yanik Kurbani" +description = "500 ates hasari al" [advancement.challenge_trag_fire_5k] - title = "Anka Kusu" - description = "5,000 ates hasari al" +title = "Anka Kusu" +description = "5,000 ates hasari al" [advancement.challenge_trag_fall_500] - title = "Yercekimi Testi" - description = "500 dusme hasari al" +title = "Yercekimi Testi" +description = "500 dusme hasari al" [advancement.challenge_trag_fall_5k] - title = "Son Hiz" - description = "5,000 dusme hasari al" +title = "Son Hiz" +description = "5,000 dusme hasari al" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Kavgaci" - description = "Ciplan yumruklarla 1,000 hasar ver" +title = "Kavgaci" +description = "Ciplan yumruklarla 1,000 hasar ver" [advancement.challenge_unarmed_dmg_10k] - title = "Dovus Sanatlari Ustasi" - description = "Ciplan yumruklarla 10,000 hasar ver" +title = "Dovus Sanatlari Ustasi" +description = "Ciplan yumruklarla 10,000 hasar ver" [advancement.challenge_unarmed_kills_25] - title = "Ciplan Yumruk" - description = "Ciplan yumruklarla 25 yaratik oldur" +title = "Ciplan Yumruk" +description = "Ciplan yumruklarla 25 yaratik oldur" [advancement.challenge_unarmed_kills_250] - title = "Efsane Yumruk" - description = "Ciplan yumruklarla 250 yaratik oldur" +title = "Efsane Yumruk" +description = "Ciplan yumruklarla 250 yaratik oldur" [advancement.challenge_unarmed_crit_25] - title = "Kritik Yumruk" - description = "Ciplan yumruklarla 25 kritik vurus yap" +title = "Kritik Yumruk" +description = "Ciplan yumruklarla 25 kritik vurus yap" [advancement.challenge_unarmed_crit_250] - title = "Hassas Yumruk" - description = "Ciplan yumruklarla 250 kritik vurus yap" +title = "Hassas Yumruk" +description = "Ciplan yumruklarla 250 kritik vurus yap" [advancement.challenge_unarmed_heavy_25] - title = "Guclu Yumruk" - description = "Ciplan yumruklarla 25 agir vurus yap (6 hasardan fazla)" +title = "Guclu Yumruk" +description = "Ciplan yumruklarla 25 agir vurus yap (6 hasardan fazla)" [advancement.challenge_unarmed_heavy_250] - title = "Nakaut Krali" - description = "Ciplan yumruklarla 250 agir vurus yap (6 hasardan fazla)" +title = "Nakaut Krali" +description = "Ciplan yumruklarla 250 agir vurus yap (6 hasardan fazla)" # items [items] [items.bound_ender_peral] - name = "Emanet Anahtari" - usage1 = "Baglamak icin Shift + Sol Tiklama" - usage2 = "Bagli Envantere erismek icin Sag Tiklayin" +name = "Emanet Anahtari" +usage1 = "Baglamak icin Shift + Sol Tiklama" +usage2 = "Bagli Envantere erismek icin Sag Tiklayin" [items.bound_eye_of_ender] - name = "Okuler Capa" - usage1 = "Tuketmek ve bagli konuma isinlanmak icin Sag Tiklayin" - usage2 = "Bir bloga baglamak icin Shift + Sol Tiklama" +name = "Okuler Capa" +usage1 = "Tuketmek ve bagli konuma isinlanmak icin Sag Tiklayin" +usage2 = "Bir bloga baglamak icin Shift + Sol Tiklama" [items.bound_redstone_torch] - name = "Redstone Kumandasi" - usage1 = "1-Tick Redstone darbesi olusturmak icin Sag Tiklayin" - usage2 = "Baglamak icin bir 'Hedef' Blok uzerinde Shift + Sol Tiklama" +name = "Redstone Kumandasi" +usage1 = "1-Tick Redstone darbesi olusturmak icin Sag Tiklayin" +usage2 = "Baglamak icin bir 'Hedef' Blok uzerinde Shift + Sol Tiklama" [items.bound_snowball] - name = "Ag Tuzagi!" - usage1 = "Konumda gecici bir ag tuzagi olusturmak icin atin" +name = "Ag Tuzagi!" +usage1 = "Konumda gecici bir ag tuzagi olusturmak icin atin" [items.chrono_time_bottle] - name = "Sisedeki Zaman" - usage1 = "Envanterinizdeyken pasif olarak zaman depolar" - usage2 = "Zamanli bloklara veya yavru hayvanlara sag tiklayarak depolanan zamani harcar" - stored = "Depolanmis Zaman" +name = "Sisedeki Zaman" +usage1 = "Envanterinizdeyken pasif olarak zaman depolar" +usage2 = "Zamanli bloklara veya yavru hayvanlara sag tiklayarak depolanan zamani harcar" +stored = "Depolanmis Zaman" [items.chrono_time_bomb] - name = "Zaman Bombasi" - usage1 = "Zamansal bir alan yaratan krono mermisi firlatmak icin sag tiklayin" +name = "Zaman Bombasi" +usage1 = "Zamansal bir alan yaratan krono mermisi firlatmak icin sag tiklayin" [items.elevator_block] - name = "Asansor Blogu" - usage1 = "Yukari isinlanmak icin ziplayin" - usage2 = "Asagi isinlanmak icin egilin" - usage3 = "Asansorler arasinda en az 2 hava blogu olmalidir" +name = "Asansor Blogu" +usage1 = "Yukari isinlanmak icin ziplayin" +usage2 = "Asagi isinlanmak icin egilin" +usage3 = "Asansorler arasinda en az 2 hava blogu olmalidir" # snippets [snippets] [snippets.gui] - level = "Seviye" - knowledge = "bilgi" - power_used = "Kullanilan Guc" - not_learned = "Ogrenilmedi" - xp = "XP'ye" - welcome = "Hos geldin!" - welcome_back = "Tekrar hosgeldiniz!" - xp_bonus_for_time = "XP icin" - max_ability_power = "Maksimum Yetenek Gucu" - unlock_this_by_clicking = "Sag Tiklayarak bunun kilidini acin: " - back = "Geri" - unlearn_all = "Tumunu unuttur" - unlearned_all = "Hepsi unutturuldu" +level = "Seviye" +knowledge = "bilgi" +power_used = "Kullanilan Guc" +not_learned = "Ogrenilmedi" +xp = "XP'ye" +welcome = "Hos geldin!" +welcome_back = "Tekrar hosgeldiniz!" +xp_bonus_for_time = "XP icin" +max_ability_power = "Maksimum Yetenek Gucu" +unlock_this_by_clicking = "Sag Tiklayarak bunun kilidini acin: " +back = "Geri" +unlearn_all = "Tumunu unuttur" +unlearned_all = "Hepsi unutturuldu" [snippets.adapt_menu] - may_not_unlearn = "OGRENMEYI GERI ALAMAZSINIZ" - may_unlearn = "OGRENEBiLiR/UNUTTURABILIRSINIZ" - knowledge_cost = "Bilgi Maliyeti" - knowledge_available = "Mevcut Bilgi" - already_learned = "Zaten Ogrenildi" - unlearn_refund = "Unutturmak ve Geri Almak Icin Tiklayin" - no_refunds = "HARDCORE, GERI ODEME ENGELLI" - knowledge = "bilgi" - click_learn = "Ogrenmek icin tiklayin" - no_knowledge = "(Hic bilginiz yok)" - you_only_have = "Sadece sahipsiniz" - how_to_level_up = "Maksimum gucunuzu artirmak icin becerilerinizi yukseltin." - not_enough_power = "Yetersiz guc! Her Yetenek Seviyesi 1 guce mal olur." - power = "guc" - power_drain = "Guc Tukenmesi" - learned = "Ogrenildi " - unlearned = "Unutturuldu " - activator_block = "Kitaplik" +may_not_unlearn = "OGRENMEYI GERI ALAMAZSINIZ" +may_unlearn = "OGRENEBiLiR/UNUTTURABILIRSINIZ" +knowledge_cost = "Bilgi Maliyeti" +knowledge_available = "Mevcut Bilgi" +already_learned = "Zaten Ogrenildi" +unlearn_refund = "Unutturmak ve Geri Almak Icin Tiklayin" +no_refunds = "HARDCORE, GERI ODEME ENGELLI" +knowledge = "bilgi" +click_learn = "Ogrenmek icin tiklayin" +no_knowledge = "(Hic bilginiz yok)" +you_only_have = "Sadece sahipsiniz" +how_to_level_up = "Maksimum gucunuzu artirmak icin becerilerinizi yukseltin." +not_enough_power = "Yetersiz guc! Her Yetenek Seviyesi 1 guce mal olur." +power = "guc" +power_drain = "Guc Tukenmesi" +learned = "Ogrenildi " +unlearned = "Unutturuldu " +activator_block = "Kitaplik" [snippets.knowledge_orb] - contains = "icerir" - knowledge = "bilgi" - rightclick = "Sag Tik" - togainknowledge = "bu bilgiyi kazanmak icin" - knowledge_orb = "Bilgi Kuresi" +contains = "icerir" +knowledge = "bilgi" +rightclick = "Sag Tik" +togainknowledge = "bu bilgiyi kazanmak icin" +knowledge_orb = "Bilgi Kuresi" [snippets.experience_orb] - contains = "icerir" - xp = "Deneyim" - rightclick = "Sag Tik" - togainxp = "bu deneyimi kazanmak icin" - xporb = "Deneyim Kuresi" +contains = "icerir" +xp = "Deneyim" +rightclick = "Sag Tik" +togainxp = "bu deneyimi kazanmak icin" +xporb = "Deneyim Kuresi" # skill [skill] [skill.agility] - name = "Ceviklik" - icon = "⇉" - description = "Ceviklik, engeller karsisinda hizli ve akici hareket etme yetenegidir." +name = "Ceviklik" +icon = "⇉" +description = "Ceviklik, engeller karsisinda hizli ve akici hareket etme yetenegidir." [skill.architect] - name = "Mimar" - icon = "⬧" - description = "Yapilar dunyanin yapi taslaridir. Gerceklik sizin elinizde, kontrol etmek sizin elinizde." +name = "Mimar" +icon = "⬧" +description = "Yapilar dunyanin yapi taslaridir. Gerceklik sizin elinizde, kontrol etmek sizin elinizde." [skill.axes] - name = "Baltalar" - icon = "🪓" - description1 = "Agaclari kesmek varken neden " - description2 = "seyleri" - description3 = "kesesin ki, ayni sonuc!" +name = "Baltalar" +icon = "🪓" +description1 = "Agaclari kesmek varken neden " +description2 = "seyleri" +description3 = "kesesin ki, ayni sonuc!" [skill.brewing] - name = "Demleme" - icon = "❦" - description = "Cift Balon, Uclu Balon, Dortlu Balon- Bu iksiri hala bir kazana koyamiyorum" +name = "Demleme" +icon = "❦" +description = "Cift Balon, Uclu Balon, Dortlu Balon- Bu iksiri hala bir kazana koyamiyorum" [skill.blocking] - name = "Engelleme" - icon = "🛡" - description = "Sopalar ve taslar kemiklerini kirmaz, Ama bir kalkan kirar." +name = "Engelleme" +icon = "🛡" +description = "Sopalar ve taslar kemiklerini kirmaz, Ama bir kalkan kirar." [skill.crafting] - name = "Uretim" - icon = "⌂" - description = "Yerlestirilecek parca kalmayinca, neden bir tane daha yapmayasiniz?" +name = "Uretim" +icon = "⌂" +description = "Yerlestirilecek parca kalmayinca, neden bir tane daha yapmayasiniz?" [skill.discovery] - name = "Kesif" - icon = "⚛" - description = "Alginiz genisledikce, zihniniz kesfetmediginizi kesfetmek icin cozulur." +name = "Kesif" +icon = "⚛" +description = "Alginiz genisledikce, zihniniz kesfetmediginizi kesfetmek icin cozulur." [skill.enchanting] - name = "Buyuleme" - icon = "✠" - description = "Neden bahsediyorsun? Kehanetler, vizyonlar, batil inancli gevezelik mi?" +name = "Buyuleme" +icon = "✠" +description = "Neden bahsediyorsun? Kehanetler, vizyonlar, batil inancli gevezelik mi?" [skill.excavation] - name = "Kazi" - icon = "ᛳ" - description = "Kaz Kaz Cukur..." +name = "Kazi" +icon = "ᛳ" +description = "Kaz Kaz Cukur..." [skill.herbalism] - name = "Bitkicilik" - icon = "⚘" - description = "Bitki bulamiyorum ama biraz tohum bulabiliyorum ve- bu... Ot mu?" +name = "Bitkicilik" +icon = "⚘" +description = "Bitki bulamiyorum ama biraz tohum bulabiliyorum ve- bu... Ot mu?" [skill.hunter] - name = "Avci" - icon = "☠" - description = "Avcilik sonucla degil yolculukla ilgilidir." +name = "Avci" +icon = "☠" +description = "Avcilik sonucla degil yolculukla ilgilidir." [skill.nether] - name = "Nether" - icon = "₪" - description = "Nether'in derinliklerinden." +name = "Nether" +icon = "₪" +description = "Nether'in derinliklerinden." [skill.pickaxe] - name = "Kazma" - icon = "⛏" - description = "Cuceler madencidir ama ben de zamanimda bir iki sey ogrendim. BEN ISVECLIYIM" +name = "Kazma" +icon = "⛏" +description = "Cuceler madencidir ama ben de zamanimda bir iki sey ogrendim. BEN ISVECLIYIM" [skill.ranged] - name = "Menzilli" - icon = "🏹" - description = "Mesafe zaferin anahtaridir ve hayatta kalmanin anahtaridir." +name = "Menzilli" +icon = "🏹" +description = "Mesafe zaferin anahtaridir ve hayatta kalmanin anahtaridir." [skill.rift] - name = "Yarik" - icon = "❍" - description = "Yarik, yakici bir kosumdir ama siz kosumu kullanmayi basardiniz." +name = "Yarik" +icon = "❍" +description = "Yarik, yakici bir kosumdir ama siz kosumu kullanmayi basardiniz." [skill.seaborne] - name = "Denizci" - icon = "🎣" - description = "Bu beceri ile suyun harikalarini yonetebilirsiniz." +name = "Denizci" +icon = "🎣" +description = "Bu beceri ile suyun harikalarini yonetebilirsiniz." [skill.stealth] - name = "Gizlilik" - icon = "☯" - description = "Gorunmezlik sanati. Golgelerde yuru." +name = "Gizlilik" +icon = "☯" +description = "Gorunmezlik sanati. Golgelerde yuru." [skill.swords] - name = "Kiliclar" - icon = "⚔" - description = "GreyStone'un gucu adina!" +name = "Kiliclar" +icon = "⚔" +description = "GreyStone'un gucu adina!" [skill.taming] - name = "Evcillestirme" - icon = "♥" - description = "Papganlar ve arilar... ya sen?" +name = "Evcillestirme" +icon = "♥" +description = "Papganlar ve arilar... ya sen?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Kan evrenin damarlarinda akar. Ellerinle kisitlanmis." +name = "TragOul" +icon = "🗡" +description = "Kan evrenin damarlarinda akar. Ellerinle kisitlanmis." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Evrenin Saatini kur, akisi deneyimle. Saati kir, saat ol." +name = "Chronos" +icon = "🕒" +description = "Evrenin Saatini kur, akisi deneyimle. Saati kir, saat ol." [skill.unarmed] - name = "Silahsiz" - icon = "»" - description = "Silahsiz olmak gucsuz olmak degildir." +name = "Silahsiz" +icon = "»" +description = "Silahsiz olmak gucsuz olmak degildir." # agility [agility] [agility.armor_up] - name = "Zirhlan" - description = "Ne kadar uzun sure kosarsan o kadar cok zirh al!" - lore1 = "Maksimum Zirh" - lore2 = "Zirhlanma Suresi" - lore = ["Maksimum Zirh", "Zirhlanma Suresi"] +name = "Zirhlan" +description = "Ne kadar uzun sure kosarsan o kadar cok zirh al!" +lore1 = "Maksimum Zirh" +lore2 = "Zirhlanma Suresi" +lore = ["Maksimum Zirh", "Zirhlanma Suresi"] [agility.ladder_slide] - name = "Merdiven Kayisi" - description = "Merdivenleri her iki yonde de cok daha hizli tirmanin ve kayarak inin." - lore1 = "Merdiven hiz carpani" - lore2 = "Hizli inis hizi" - lore = ["Merdiven hiz carpani", "Hizli inis hizi"] +name = "Merdiven Kayisi" +description = "Merdivenleri her iki yonde de cok daha hizli tirmanin ve kayarak inin." +lore1 = "Merdiven hiz carpani" +lore2 = "Hizli inis hizi" +lore = ["Merdiven hiz carpani", "Hizli inis hizi"] [agility.super_jump] - name = "Super Ziplama" - description = "Olaganustu Yukseklik Avantaji." - lore1 = "Maksimum Atlama Yuksekligi" - lore2 = "Egilme + Ziplama ile Super Zipla!" - lore = ["Maksimum Atlama Yuksekligi", "Egilme + Ziplama ile Super Zipla!"] +name = "Super Ziplama" +description = "Olaganustu Yukseklik Avantaji." +lore1 = "Maksimum Atlama Yuksekligi" +lore2 = "Egilme + Ziplama ile Super Zipla!" +lore = ["Maksimum Atlama Yuksekligi", "Egilme + Ziplama ile Super Zipla!"] [agility.wall_jump] - name = "Duvar Atlayisi" - description = "Havadayken bir duvara karsi shift basili tutarak duvara tutun ve ziplayin!" - lore1 = "Maksimum Atlama" - lore2 = "Atlama Yuksekligi" - lore = ["Maksimum Atlama", "Atlama Yuksekligi"] +name = "Duvar Atlayisi" +description = "Havadayken bir duvara karsi shift basili tutarak duvara tutun ve ziplayin!" +lore1 = "Maksimum Atlama" +lore2 = "Atlama Yuksekligi" +lore = ["Maksimum Atlama", "Atlama Yuksekligi"] [agility.wind_up] - name = "Hizlanma" - description = "Ne kadar uzun kosarsan o kadar hizli ol!" - lore1 = "Maksimum Hiz" - lore2 = "Hizlanma Suresi" - lore = ["Maksimum Hiz", "Hizlanma Suresi"] +name = "Hizlanma" +description = "Ne kadar uzun kosarsan o kadar hizli ol!" +lore1 = "Maksimum Hiz" +lore2 = "Hizlanma Suresi" +lore = ["Maksimum Hiz", "Hizlanma Suresi"] # architect [architect] [architect.elevator] - name = "Asansor" - description = "Dikey olarak hizla isinlanmak icin bir asansor insa etmenizi saglar!" - lore1 = "Asansor tarifi: X=YUN, Y=ENDER INCISI" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Asansor tarifi: X=YUN, Y=ENDER INCISI", "XXX", "XYX", "XXX"] +name = "Asansor" +description = "Dikey olarak hizla isinlanmak icin bir asansor insa etmenizi saglar!" +lore1 = "Asansor tarifi: X=YUN, Y=ENDER INCISI" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Asansor tarifi: X=YUN, Y=ENDER INCISI", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Sihirli Temel" - description = "Egilip altiniza gecici bir temel olusturmanizi saglar!" - lore1 = "Sihirli bir sekilde olusturun: " - lore2 = "Altinizdaki bloklar!" - lore = ["Sihirli bir sekilde olusturun: ", "Altinizdaki bloklar!"] +name = "Sihirli Temel" +description = "Egilip altiniza gecici bir temel olusturmanizi saglar!" +lore1 = "Sihirli bir sekilde olusturun: " +lore2 = "Altinizdaki bloklar!" +lore = ["Sihirli bir sekilde olusturun: ", "Altinizdaki bloklar!"] [architect.glass] - name = "Ipek Dokunuslu Cam" - description = "Cam bloklari bos elinizle kirdiginizda kaybolmasini onlemenizi saglar!" - lore1 = "Elleriniz Cam icin ipek dokunusu kazanir" - lore = ["Elleriniz Cam icin ipek dokunusu kazanir"] +name = "Ipek Dokunuslu Cam" +description = "Cam bloklari bos elinizle kirdiginizda kaybolmasini onlemenizi saglar!" +lore1 = "Elleriniz Cam icin ipek dokunusu kazanir" +lore = ["Elleriniz Cam icin ipek dokunusu kazanir"] [architect.wireless_redstone] - name = "Redstone Kumandasi" - description = "Redstone'u uzaktan degistirmek icin bir redstone mesalesi kullanmanizi saglar!" - lore1 = "Hedef + Redstone Mesalesi + Ender Incisi = 1 Redstone Kumandasi" - lore = ["Hedef + Redstone Mesalesi + Ender Incisi = 1 Redstone Kumandasi"] +name = "Redstone Kumandasi" +description = "Redstone'u uzaktan degistirmek icin bir redstone mesalesi kullanmanizi saglar!" +lore1 = "Hedef + Redstone Mesalesi + Ender Incisi = 1 Redstone Kumandasi" +lore = ["Hedef + Redstone Mesalesi + Ender Incisi = 1 Redstone Kumandasi"] [architect.placement] - name = "Insaatci Asasi" - description = "Egilme modunda ayni anda birden fazla blok yerlestirmenizi saglar. Baktiginiz blokla esesen bir blok tutun ve yerlestirin! Kutulari sinirlamak icin biraz hareket etmeniz gerekebilir!" - lore1 = "Ihtiyaciniz var" - lore2 = "bunu yerlestirmek icin elinizde bloklar" - lore3 = "Malzeme Insaatci Asasi" - lore = ["Ihtiyaciniz var", "bunu yerlestirmek icin elinizde bloklar", "Malzeme Insaatci Asasi"] +name = "Insaatci Asasi" +description = "Egilme modunda ayni anda birden fazla blok yerlestirmenizi saglar. Baktiginiz blokla esesen bir blok tutun ve yerlestirin! Kutulari sinirlamak icin biraz hareket etmeniz gerekebilir!" +lore1 = "Ihtiyaciniz var" +lore2 = "bunu yerlestirmek icin elinizde bloklar" +lore3 = "Malzeme Insaatci Asasi" +lore = ["Ihtiyaciniz var", "bunu yerlestirmek icin elinizde bloklar", "Malzeme Insaatci Asasi"] # axe [axe] [axe.chop] - name = "Balta Kesimi" - description = "Taban kutugune sag tiklayarak agaclari kesin!" - lore1 = "Kesim Basina Blok" - lore2 = "Kesim Bekleme Suresi" - lore3 = "Alet Asinmasi" - lore = ["Kesim Basina Blok", "Kesim Bekleme Suresi", "Alet Asinmasi"] +name = "Balta Kesimi" +description = "Taban kutugune sag tiklayarak agaclari kesin!" +lore1 = "Kesim Basina Blok" +lore2 = "Kesim Bekleme Suresi" +lore3 = "Alet Asinmasi" +lore = ["Kesim Basina Blok", "Kesim Bekleme Suresi", "Alet Asinmasi"] [axe.log_swap] - name = "Lucy'nin Kutuk Degistiricisi" - description = "Uretim Masasinda kutuklerin turunu degistirin!" - lore1 = "8 herhangi turde kutuk + 1 fidan = 8 fidan turunde kutuk" - lore = ["8 herhangi turde kutuk + 1 fidan = 8 fidan turunde kutuk"] +name = "Lucy'nin Kutuk Degistiricisi" +description = "Uretim Masasinda kutuklerin turunu degistirin!" +lore1 = "8 herhangi turde kutuk + 1 fidan = 8 fidan turunde kutuk" +lore = ["8 herhangi turde kutuk + 1 fidan = 8 fidan turunde kutuk"] [axe.drop_to_inventory] - name = "Balta Envantere Dusurme" +name = "Balta Envantere Dusurme" [axe.ground_smash] - name = "Balta Yer Ezmesi" - description = "Ziplayin, sonra egilin ve yakindaki tum dusmanlari parcalayin." - lore1 = "Hasar" - lore2 = "Blok Yaricapi" - lore3 = "Kuvvet" - lore4 = "Ezme Bekleme Suresi" - lore = ["Hasar", "Blok Yaricapi", "Kuvvet", "Ezme Bekleme Suresi"] +name = "Balta Yer Ezmesi" +description = "Ziplayin, sonra egilin ve yakindaki tum dusmanlari parcalayin." +lore1 = "Hasar" +lore2 = "Blok Yaricapi" +lore3 = "Kuvvet" +lore4 = "Ezme Bekleme Suresi" +lore = ["Hasar", "Blok Yaricapi", "Kuvvet", "Ezme Bekleme Suresi"] [axe.leaf_miner] - name = "Yaprak Madencisi" - description = "Toplu yapraklari bir seferde kirmanizi saglar!" - lore1 = "Egilin ve YAPRAK kazin" - lore2 = "yaprak madenciligi menzili" - lore3 = "Yapraklardan dusme elde edemeyeceksiniz (Istismar Onleme)" - lore = ["Egilin ve YAPRAK kazin", "yaprak madenciligi menzili", "Yapraklardan dusme elde edemeyeceksiniz (Istismar Onleme)"] +name = "Yaprak Madencisi" +description = "Toplu yapraklari bir seferde kirmanizi saglar!" +lore1 = "Egilin ve YAPRAK kazin" +lore2 = "yaprak madenciligi menzili" +lore3 = "Yapraklardan dusme elde edemeyeceksiniz (Istismar Onleme)" +lore = ["Egilin ve YAPRAK kazin", "yaprak madenciligi menzili", "Yapraklardan dusme elde edemeyeceksiniz (Istismar Onleme)"] [axe.wood_miner] - name = "Odun Madencisi" - description = "Toplu ahsabi bir seferde kirmanizi saglar!" - lore1 = "Egilin ve AHSAP/KUTUK (Tahta Degil) kazin" - lore2 = "odun madenciligi menzili" - lore3 = "Envantere dusurme ile calisir" - lore = ["Egilin ve AHSAP/KUTUK (Tahta Degil) kazin", "odun madenciligi menzili", "Envantere dusurme ile calisir"] +name = "Odun Madencisi" +description = "Toplu ahsabi bir seferde kirmanizi saglar!" +lore1 = "Egilin ve AHSAP/KUTUK (Tahta Degil) kazin" +lore2 = "odun madenciligi menzili" +lore3 = "Envantere dusurme ile calisir" +lore = ["Egilin ve AHSAP/KUTUK (Tahta Degil) kazin", "odun madenciligi menzili", "Envantere dusurme ile calisir"] # brewing [brewing] [brewing.lingering] - name = "Kalici Demleme" - description = "Demlenmis iksirler daha uzun surer!" - lore1 = "Sure" - lore2 = "Sure" - lore = ["Sure", "Sure"] +name = "Kalici Demleme" +description = "Demlenmis iksirler daha uzun surer!" +lore1 = "Sure" +lore2 = "Sure" +lore = ["Sure", "Sure"] [brewing.super_heated] - name = "Super Isitilmis Demleme" - description = "Demleme tezgahlari ne kadar sicaksa o kadar hizli calisir." - lore1 = "Dokunan Ates Blogu Basina" - lore2 = "Dokunan Lav Blogu Basina" - lore = ["Dokunan Ates Blogu Basina", "Dokunan Lav Blogu Basina"] +name = "Super Isitilmis Demleme" +description = "Demleme tezgahlari ne kadar sicaksa o kadar hizli calisir." +lore1 = "Dokunan Ates Blogu Basina" +lore2 = "Dokunan Lav Blogu Basina" +lore = ["Dokunan Ates Blogu Basina", "Dokunan Lav Blogu Basina"] [brewing.darkness] - name = "Siselenmiş Karanlik" - description = "Buna neden ihtiyacin oldugundan emin degilim ama buyur!" - lore1 = "Gece Gorusu Iksiri + Siyah Beton = Karanlik Iksiri (30 saniye)" - lore2 = "Kullanicinin kosamayacagini unutmayin!" - lore = ["Gece Gorusu Iksiri + Siyah Beton = Karanlik Iksiri (30 saniye)", "Kullanicinin kosamayacagini unutmayin!"] +name = "Siselenmiş Karanlik" +description = "Buna neden ihtiyacin oldugundan emin degilim ama buyur!" +lore1 = "Gece Gorusu Iksiri + Siyah Beton = Karanlik Iksiri (30 saniye)" +lore2 = "Kullanicinin kosamayacagini unutmayin!" +lore = ["Gece Gorusu Iksiri + Siyah Beton = Karanlik Iksiri (30 saniye)", "Kullanicinin kosamayacagini unutmayin!"] [brewing.haste] - name = "Siselenmiş Acele" - description = "Verimlilik yeterli olmadiginda" - lore1 = "Hiz Iksiri + Ametist Parcasi = Acele Iksiri (60 saniye)" - lore2 = "Hiz Iksiri + Ametist Bloku = Acele Iksiri-2 (30 saniye)" - lore = ["Hiz Iksiri + Ametist Parcasi = Acele Iksiri (60 saniye)", "Hiz Iksiri + Ametist Bloku = Acele Iksiri-2 (30 saniye)"] +name = "Siselenmiş Acele" +description = "Verimlilik yeterli olmadiginda" +lore1 = "Hiz Iksiri + Ametist Parcasi = Acele Iksiri (60 saniye)" +lore2 = "Hiz Iksiri + Ametist Bloku = Acele Iksiri-2 (30 saniye)" +lore = ["Hiz Iksiri + Ametist Parcasi = Acele Iksiri (60 saniye)", "Hiz Iksiri + Ametist Bloku = Acele Iksiri-2 (30 saniye)"] [brewing.absorption] - name = "Siselenmiş Emilim" - description = "Vuçudu sertlestirin!" - lore1 = "Aninda Iyilesme + Kuvars = Emilim Iksiri (60 saniye)" - lore2 = "Aninda Iyilesme + Kuvars Bloku = Emilim Iksiri-2 (30 saniye)" - lore = ["Aninda Iyilesme + Kuvars = Emilim Iksiri (60 saniye)", "Aninda Iyilesme + Kuvars Bloku = Emilim Iksiri-2 (30 saniye)"] +name = "Siselenmiş Emilim" +description = "Vuçudu sertlestirin!" +lore1 = "Aninda Iyilesme + Kuvars = Emilim Iksiri (60 saniye)" +lore2 = "Aninda Iyilesme + Kuvars Bloku = Emilim Iksiri-2 (30 saniye)" +lore = ["Aninda Iyilesme + Kuvars = Emilim Iksiri (60 saniye)", "Aninda Iyilesme + Kuvars Bloku = Emilim Iksiri-2 (30 saniye)"] [brewing.fatigue] - name = "Siselenmiş Yorgunluk" - description = "Vuçudu zayiflatin!" - lore1 = "Zayiflik Iksiri + Balcik Topu = Yorgunluk Iksiri (30 saniye)" - lore2 = "Zayiflik Iksiri + Balcik Bloku = Yorgunluk Iksiri-2 (15 saniye)" - lore = ["Zayiflik Iksiri + Balcik Topu = Yorgunluk Iksiri (30 saniye)", "Zayiflik Iksiri + Balcik Bloku = Yorgunluk Iksiri-2 (15 saniye)"] +name = "Siselenmiş Yorgunluk" +description = "Vuçudu zayiflatin!" +lore1 = "Zayiflik Iksiri + Balcik Topu = Yorgunluk Iksiri (30 saniye)" +lore2 = "Zayiflik Iksiri + Balcik Bloku = Yorgunluk Iksiri-2 (15 saniye)" +lore = ["Zayiflik Iksiri + Balcik Topu = Yorgunluk Iksiri (30 saniye)", "Zayiflik Iksiri + Balcik Bloku = Yorgunluk Iksiri-2 (15 saniye)"] [brewing.hunger] - name = "Siselenmiş Aclik" - description = "Doyumsuzlari besleyin!" - lore1 = "Garip Iksir + Curuk Et = Aclik Iksiri (30 saniye)" - lore2 = "Zayiflik Iksiri + Curuk Et = Aclik Iksiri-3 (15 saniye)" - lore = ["Garip Iksir + Curuk Et = Aclik Iksiri (30 saniye)", "Zayiflik Iksiri + Curuk Et = Aclik Iksiri-3 (15 saniye)"] +name = "Siselenmiş Aclik" +description = "Doyumsuzlari besleyin!" +lore1 = "Garip Iksir + Curuk Et = Aclik Iksiri (30 saniye)" +lore2 = "Zayiflik Iksiri + Curuk Et = Aclik Iksiri-3 (15 saniye)" +lore = ["Garip Iksir + Curuk Et = Aclik Iksiri (30 saniye)", "Zayiflik Iksiri + Curuk Et = Aclik Iksiri-3 (15 saniye)"] [brewing.nausea] - name = "Siselenmiş Bulanti" - description = "Midem bulaniyor senden!" - lore1 = "Garip Iksir + Kahverengi Mantar = Bulanti Iksiri (16 saniye)" - lore2 = "Garip Iksir + Kizil Mantar = Bulanti Iksiri-2 (8 saniye)" - lore = ["Garip Iksir + Kahverengi Mantar = Bulanti Iksiri (16 saniye)", "Garip Iksir + Kizil Mantar = Bulanti Iksiri-2 (8 saniye)"] +name = "Siselenmiş Bulanti" +description = "Midem bulaniyor senden!" +lore1 = "Garip Iksir + Kahverengi Mantar = Bulanti Iksiri (16 saniye)" +lore2 = "Garip Iksir + Kizil Mantar = Bulanti Iksiri-2 (8 saniye)" +lore = ["Garip Iksir + Kahverengi Mantar = Bulanti Iksiri (16 saniye)", "Garip Iksir + Kizil Mantar = Bulanti Iksiri-2 (8 saniye)"] [brewing.blindness] - name = "Siselenmiş Korluk" - description = "Korkunc bir insansin..." - lore1 = "Garip Iksir + Murekkep Kesesi = Korluk Iksiri (30 saniye)" - lore2 = "Garip Iksir + Parlayan Murekkep Kesesi = Korluk Iksiri-2 (15 saniye)" - lore = ["Garip Iksir + Murekkep Kesesi = Korluk Iksiri (30 saniye)", "Garip Iksir + Parlayan Murekkep Kesesi = Korluk Iksiri-2 (15 saniye)"] +name = "Siselenmiş Korluk" +description = "Korkunc bir insansin..." +lore1 = "Garip Iksir + Murekkep Kesesi = Korluk Iksiri (30 saniye)" +lore2 = "Garip Iksir + Parlayan Murekkep Kesesi = Korluk Iksiri-2 (15 saniye)" +lore = ["Garip Iksir + Murekkep Kesesi = Korluk Iksiri (30 saniye)", "Garip Iksir + Parlayan Murekkep Kesesi = Korluk Iksiri-2 (15 saniye)"] [brewing.resistance] - name = "Siselenmiş Direnc" - description = "En iyi sekilde guclendirme!" - lore1 = "Garip Iksir + Demir Kulce = Direnc Iksiri (60 saniye)" - lore2 = "Garip Iksir + Demir Blok = Direnc Iksiri-2 (30 saniye)" - lore = ["Garip Iksir + Demir Kulce = Direnc Iksiri (60 saniye)", "Garip Iksir + Demir Blok = Direnc Iksiri-2 (30 saniye)"] +name = "Siselenmiş Direnc" +description = "En iyi sekilde guclendirme!" +lore1 = "Garip Iksir + Demir Kulce = Direnc Iksiri (60 saniye)" +lore2 = "Garip Iksir + Demir Blok = Direnc Iksiri-2 (30 saniye)" +lore = ["Garip Iksir + Demir Kulce = Direnc Iksiri (60 saniye)", "Garip Iksir + Demir Blok = Direnc Iksiri-2 (30 saniye)"] [brewing.health_boost] - name = "Siselenmiş Yasam" - description = "Maksimum saglik yeterli olmadiginda..." - lore1 = "Aninda-Iyilestirme Iksiri + Altin Elma = Saglik Artisi Iksiri (120 saniye)" - lore2 = "Aninda-Iyilestirme Iksiri + Buyulu Altin Elma = Saglik Artisi Iksiri-2 (120 saniye)" - lore = ["Aninda-Iyilestirme Iksiri + Altin Elma = Saglik Artisi Iksiri (120 saniye)", "Aninda-Iyilestirme Iksiri + Buyulu Altin Elma = Saglik Artisi Iksiri-2 (120 saniye)"] +name = "Siselenmiş Yasam" +description = "Maksimum saglik yeterli olmadiginda..." +lore1 = "Aninda-Iyilestirme Iksiri + Altin Elma = Saglik Artisi Iksiri (120 saniye)" +lore2 = "Aninda-Iyilestirme Iksiri + Buyulu Altin Elma = Saglik Artisi Iksiri-2 (120 saniye)" +lore = ["Aninda-Iyilestirme Iksiri + Altin Elma = Saglik Artisi Iksiri (120 saniye)", "Aninda-Iyilestirme Iksiri + Buyulu Altin Elma = Saglik Artisi Iksiri-2 (120 saniye)"] [brewing.decay] - name = "Siselenmiş Curume" - description = "Curumenin bu kadar faydali olacagini kim bilebilirdi?" - lore1 = "Zayiflik Iksiri + Zehirli Patates = Solma Iksiri (16 saniye)" - lore2 = "Zayiflik Iksiri + Kizil Kokler = Solma Iksiri-2 (8 saniye)" - lore = ["Zayiflik Iksiri + Zehirli Patates = Solma Iksiri (16 saniye)", "Zayiflik Iksiri + Kizil Kokler = Solma Iksiri-2 (8 saniye)"] +name = "Siselenmiş Curume" +description = "Curumenin bu kadar faydali olacagini kim bilebilirdi?" +lore1 = "Zayiflik Iksiri + Zehirli Patates = Solma Iksiri (16 saniye)" +lore2 = "Zayiflik Iksiri + Kizil Kokler = Solma Iksiri-2 (8 saniye)" +lore = ["Zayiflik Iksiri + Zehirli Patates = Solma Iksiri (16 saniye)", "Zayiflik Iksiri + Kizil Kokler = Solma Iksiri-2 (8 saniye)"] [brewing.saturation] - name = "Siselenmiş Doygunluk" - description = "Biliyor musun... Ac bile degilim..." - lore1 = "Yenilenme Iksiri + Firinda Patates = Doygunluk Iksiri" - lore2 = "Yenilenme Iksiri + Saman Balyasi = Doygunluk Iksiri-2" - lore = ["Yenilenme Iksiri + Firinda Patates = Doygunluk Iksiri", "Yenilenme Iksiri + Saman Balyasi = Doygunluk Iksiri-2"] +name = "Siselenmiş Doygunluk" +description = "Biliyor musun... Ac bile degilim..." +lore1 = "Yenilenme Iksiri + Firinda Patates = Doygunluk Iksiri" +lore2 = "Yenilenme Iksiri + Saman Balyasi = Doygunluk Iksiri-2" +lore = ["Yenilenme Iksiri + Firinda Patates = Doygunluk Iksiri", "Yenilenme Iksiri + Saman Balyasi = Doygunluk Iksiri-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Mephistopheles Zincirleri" - description = "Zincir Zirh uretmenizi saglar" - lore1 = "Uretim tarifi digerlerinin aynisidir, ancak demir kulce yerine demir parcasi kullanilir" - lore = ["Uretim tarifi digerlerinin aynisidir, ancak demir kulce yerine demir parcasi kullanilir"] +name = "Mephistopheles Zincirleri" +description = "Zincir Zirh uretmenizi saglar" +lore1 = "Uretim tarifi digerlerinin aynisidir, ancak demir kulce yerine demir parcasi kullanilir" +lore = ["Uretim tarifi digerlerinin aynisidir, ancak demir kulce yerine demir parcasi kullanilir"] [blocking.horse_armorer] - name = "Uretilebilir At Zirhi" - description = "At Zirhi uretmenizi saglar" - lore1 = "Zirhi uretmek istediginiz malzemeyle bir eyeri cevreleyin" - lore = ["Zirhi uretmek istediginiz malzemeyle bir eyeri cevreleyin"] +name = "Uretilebilir At Zirhi" +description = "At Zirhi uretmenizi saglar" +lore1 = "Zirhi uretmek istediginiz malzemeyle bir eyeri cevreleyin" +lore = ["Zirhi uretmek istediginiz malzemeyle bir eyeri cevreleyin"] [blocking.saddle_crafter] - name = "Uretilebilir Eyer" - description = "Deri ile Eyer Uret" - lore1 = "Tarif: 5 Deri:" - lore = ["Tarif: 5 Deri:"] +name = "Uretilebilir Eyer" +description = "Deri ile Eyer Uret" +lore1 = "Tarif: 5 Deri:" +lore = ["Tarif: 5 Deri:"] [blocking.multi_armor] - name = "Coklu Zirh" - description = "Elytra'lari Zirha Baglayin" - lore1 = "Seyahat icin muhtesem bir beceri." - lore2 = "Zirh/Elytra'yi aninda dinamik olarak birlestirin ve degistirin!" - lore3 = "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayarak koyun." - lore4 = "Zirhi cozmek icin esyayi Egilip-Birakin, parcalanacaktir." - lore5 = "Coklu Zirhiniz yok edilirse, icindeki tum esyalari kaybedersiniz." - lore6 = "Zirha ihtiyacim yok, beni hayal kirikligina ugratiyor..." - lore = ["Seyahat icin muhtesem bir beceri.", "Zirh/Elytra'yi aninda dinamik olarak birlestirin ve degistirin!", "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayarak koyun.", "Zirhi cozmek icin esyayi Egilip-Birakin, parcalanacaktir.", "Coklu Zirhiniz yok edilirse, icindeki tum esyalari kaybedersiniz.", "Zirha ihtiyacim yok, beni hayal kirikligina ugratiyor..."] +name = "Coklu Zirh" +description = "Elytra'lari Zirha Baglayin" +lore1 = "Seyahat icin muhtesem bir beceri." +lore2 = "Zirh/Elytra'yi aninda dinamik olarak birlestirin ve degistirin!" +lore3 = "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayarak koyun." +lore4 = "Zirhi cozmek icin esyayi Egilip-Birakin, parcalanacaktir." +lore5 = "Coklu Zirhiniz yok edilirse, icindeki tum esyalari kaybedersiniz." +lore6 = "Zirha ihtiyacim yok, beni hayal kirikligina ugratiyor..." +lore = ["Seyahat icin muhtesem bir beceri.", "Zirh/Elytra'yi aninda dinamik olarak birlestirin ve degistirin!", "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayarak koyun.", "Zirhi cozmek icin esyayi Egilip-Birakin, parcalanacaktir.", "Coklu Zirhiniz yok edilirse, icindeki tum esyalari kaybedersiniz.", "Zirha ihtiyacim yok, beni hayal kirikligina ugratiyor..."] # crafting [crafting] [crafting.deconstruction] - name = "Yapi Sokum" - description = "Bloklari ve esyalari kurtarilabilir temel bilesenlere donusturun!" - lore1 = "Herhangi bir esyayi yere birakin." - lore2 = "Sonra, Egilip Makasla Sag Tiklayin" - lore = ["Herhangi bir esyayi yere birakin.", "Sonra, Egilip Makasla Sag Tiklayin"] +name = "Yapi Sokum" +description = "Bloklari ve esyalari kurtarilabilir temel bilesenlere donusturun!" +lore1 = "Herhangi bir esyayi yere birakin." +lore2 = "Sonra, Egilip Makasla Sag Tiklayin" +lore = ["Herhangi bir esyayi yere birakin.", "Sonra, Egilip Makasla Sag Tiklayin"] [crafting.xp] - name = "Uretim XP'si" - description = "Uretirken pasif XP kazanin" - lore1 = "Uretirken XP kazanin" - lore = ["Uretirken XP kazanin"] +name = "Uretim XP'si" +description = "Uretirken pasif XP kazanin" +lore1 = "Uretirken XP kazanin" +lore = ["Uretirken XP kazanin"] [crafting.reconstruction] - name = "Cevher Yeniden Insasi" - description = "Cevherleri temel bilesenlerinden yeniden uretin!" - lore1 = "8 Dusme ve 1 Ana Bilgisayar = 1 Cevher (sekillsiz)" - lore2 = "Dusmeler eritilmelidir (varsa)" - lore3 = "Dahil degil: Hurdalar, Kuvarslar ve Zumurutler vb..." - lore4 = "Ana Bilgisayar = Kaplama. orn: Tas, Netherrack, Deepslate" - lore = ["8 Dusme ve 1 Ana Bilgisayar = 1 Cevher (sekillsiz)", "Dusmeler eritilmelidir (varsa)", "Dahil degil: Hurdalar, Kuvarslar ve Zumurutler vb...", "Ana Bilgisayar = Kaplama. orn: Tas, Netherrack, Deepslate"] +name = "Cevher Yeniden Insasi" +description = "Cevherleri temel bilesenlerinden yeniden uretin!" +lore1 = "8 Dusme ve 1 Ana Bilgisayar = 1 Cevher (sekillsiz)" +lore2 = "Dusmeler eritilmelidir (varsa)" +lore3 = "Dahil degil: Hurdalar, Kuvarslar ve Zumurutler vb..." +lore4 = "Ana Bilgisayar = Kaplama. orn: Tas, Netherrack, Deepslate" +lore = ["8 Dusme ve 1 Ana Bilgisayar = 1 Cevher (sekillsiz)", "Dusmeler eritilmelidir (varsa)", "Dahil degil: Hurdalar, Kuvarslar ve Zumurutler vb...", "Ana Bilgisayar = Kaplama. orn: Tas, Netherrack, Deepslate"] [crafting.leather] - name = "Uretilebilir Deri" - description = "Curuk Etten Deri Uret" - lore1 = "Sadece onu (curuk eti) kamp atesine atin!" - lore = ["Sadece onu (curuk eti) kamp atesine atin!"] +name = "Uretilebilir Deri" +description = "Curuk Etten Deri Uret" +lore1 = "Sadece onu (curuk eti) kamp atesine atin!" +lore = ["Sadece onu (curuk eti) kamp atesine atin!"] [crafting.backpacks] - name = "Boutilier'in Sirt Cantalari!" - description = "Bu sadece Mojang Paketini oyuna getiriyor!" - lore1 = "Bunu kullanmak icin Hayatta Kalma modunda olmalisiniz" - lore2 = "XLX : Deri, Tasma, Deri" - lore3 = "XSX : Deri, Fici, Deri" - lore4 = "XCX : Deri, Sandik, Deri" - lore = ["Bunu kullanmak icin Hayatta Kalma modunda olmalisiniz", "XLX : Deri, Tasma, Deri", "XSX : Deri, Fici, Deri", "XCX : Deri, Sandik, Deri"] +name = "Boutilier'in Sirt Cantalari!" +description = "Bu sadece Mojang Paketini oyuna getiriyor!" +lore1 = "Bunu kullanmak icin Hayatta Kalma modunda olmalisiniz" +lore2 = "XLX : Deri, Tasma, Deri" +lore3 = "XSX : Deri, Fici, Deri" +lore4 = "XCX : Deri, Sandik, Deri" +lore = ["Bunu kullanmak icin Hayatta Kalma modunda olmalisiniz", "XLX : Deri, Tasma, Deri", "XSX : Deri, Fici, Deri", "XCX : Deri, Sandik, Deri"] [crafting.stations] - name = "Portatif Masalar!" - description = "Avucunuzun icinde bir masa kullanin!" - lore2 = "KAPATTIGINIZDA MASA ICINDE UNUTTUGUNUZ ESYALAR SONSUZA KADAR KAYBOLUR!" - lore3 = "Gecerli masalar: Ors, Uretim, Bileme Tasi, Haritacilik, Tas Kesici, Dokuma Tezgahi" - lore = ["KAPATTIGINIZDA MASA ICINDE UNUTTUGUNUZ ESYALAR SONSUZA KADAR KAYBOLUR!", "Gecerli masalar: Ors, Uretim, Bileme Tasi, Haritacilik, Tas Kesici, Dokuma Tezgahi"] +name = "Portatif Masalar!" +description = "Avucunuzun icinde bir masa kullanin!" +lore2 = "KAPATTIGINIZDA MASA ICINDE UNUTTUGUNUZ ESYALAR SONSUZA KADAR KAYBOLUR!" +lore3 = "Gecerli masalar: Ors, Uretim, Bileme Tasi, Haritacilik, Tas Kesici, Dokuma Tezgahi" +lore = ["KAPATTIGINIZDA MASA ICINDE UNUTTUGUNUZ ESYALAR SONSUZA KADAR KAYBOLUR!", "Gecerli masalar: Ors, Uretim, Bileme Tasi, Haritacilik, Tas Kesici, Dokuma Tezgahi"] [crafting.skulls] - name = "Uretilebilir Kafataslari!" - description = "Malzemeler kullanarak Yaratik Kafataslari Uretebilirsiniz!" - lore1 = "Bir kafatasi elde etmek icin bir Kemik Blogunu asagidakilerle cevreleyin:" - lore2 = "Zombi: Curuk Et" - lore3 = "Iskelet: Kemik" - lore4 = "Creeper: Barut" - lore5 = "Wither: Nether Tuglasi" - lore6 = "Ejderha: Ejderha Nefesi" - lore = ["Bir kafatasi elde etmek icin bir Kemik Blogunu asagidakilerle cevreleyin:", "Zombi: Curuk Et", "Iskelet: Kemik", "Creeper: Barut", "Wither: Nether Tuglasi", "Ejderha: Ejderha Nefesi"] +name = "Uretilebilir Kafataslari!" +description = "Malzemeler kullanarak Yaratik Kafataslari Uretebilirsiniz!" +lore1 = "Bir kafatasi elde etmek icin bir Kemik Blogunu asagidakilerle cevreleyin:" +lore2 = "Zombi: Curuk Et" +lore3 = "Iskelet: Kemik" +lore4 = "Creeper: Barut" +lore5 = "Wither: Nether Tuglasi" +lore6 = "Ejderha: Ejderha Nefesi" +lore = ["Bir kafatasi elde etmek icin bir Kemik Blogunu asagidakilerle cevreleyin:", "Zombi: Curuk Et", "Iskelet: Kemik", "Creeper: Barut", "Wither: Nether Tuglasi", "Ejderha: Ejderha Nefesi"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Sisedeki Zaman" - description = "Zaman depolayan ve zamanli bloklari, buyuyebilir bitkileri ve yavru hayvanlar gibi Yaslanabilir varliklari hizlandirmak icin harcayabileceginiz zamansal bir sise tasiyin. Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise." - lore1 = "Her tikte sarj edilen saniye" - lore2 = "Depolanan saniye basina zaman hizlandirmasi" - lore3 = "Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise" - lore = ["Her tikte sarj edilen saniye", "Depolanan saniye basina zaman hizlandirmasi", "Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise"] +name = "Sisedeki Zaman" +description = "Zaman depolayan ve zamanli bloklari, buyuyebilir bitkileri ve yavru hayvanlar gibi Yaslanabilir varliklari hizlandirmak icin harcayabileceginiz zamansal bir sise tasiyin. Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise." +lore1 = "Her tikte sarj edilen saniye" +lore2 = "Depolanan saniye basina zaman hizlandirmasi" +lore3 = "Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise" +lore = ["Her tikte sarj edilen saniye", "Depolanan saniye basina zaman hizlandirmasi", "Tarif (Sekillsiz): Hizlilik Iksiri + Saat + Cam Sise"] [chronos.aberrant_touch] - name = "Sapkin Dokunuş" - description = "Yakin dövüs saldiriları, açlik pahasina yigilan yavaslatma uygular, sıki PvP sinirlariyla ve 5 yiginlamada hedefleri kökler." - lore1 = "Yakin dovus saldirilari yigilan yavaslatma uygular" - lore2 = "PvE yavaslatma suresi siniri" - lore3 = "PvP yavaslatma guclendirici siniri" - lore = ["Yakin dovus saldirilari yigilan yavaslatma uygular", "PvE yavaslatma suresi siniri", "PvP yavaslatma guclendirici siniri"] +name = "Sapkin Dokunuş" +description = "Yakin dövüs saldiriları, açlik pahasina yigilan yavaslatma uygular, sıki PvP sinirlariyla ve 5 yiginlamada hedefleri kökler." +lore1 = "Yakin dovus saldirilari yigilan yavaslatma uygular" +lore2 = "PvE yavaslatma suresi siniri" +lore3 = "PvP yavaslatma guclendirici siniri" +lore = ["Yakin dovus saldirilari yigilan yavaslatma uygular", "PvE yavaslatma suresi siniri", "PvP yavaslatma guclendirici siniri"] [chronos.instant_recall] - name = "Aninda Geri Cagirma" - description = "Elde saat tutarken sol veya sag tiklayarak saglik ve aclik geri yuklenmis sekilde yakin bir ana geri sarın." - lore1 = "Geri sarim suresi" - lore2 = "Bekleme suresi" - lore3 = "Envanter geri alinmaz" - lore = ["Geri sarim suresi", "Bekleme suresi", "Envanter geri alinmaz"] +name = "Aninda Geri Cagirma" +description = "Elde saat tutarken sol veya sag tiklayarak saglik ve aclik geri yuklenmis sekilde yakin bir ana geri sarın." +lore1 = "Geri sarim suresi" +lore2 = "Bekleme suresi" +lore3 = "Envanter geri alinmaz" +lore = ["Geri sarim suresi", "Bekleme suresi", "Envanter geri alinmaz"] [chronos.time_bomb] - name = "Zaman Bombasi" - description = "Zamansal bir alan yaratan, varliklari yavalatan ve mermileleri donduran üretilmiş bir krono bombasi firlatın." - lore1 = "Zamansal alan yaricapi" - lore2 = "Zamansal alan suresi" - lore3 = "Bomba bekleme suresi" - lore4 = "Tarif (Sekillsiz): Saat + Kartopu + Elmas + Kum" - lore = ["Zamansal alan yaricapi", "Zamansal alan suresi", "Bomba bekleme suresi", "Tarif (Sekillsiz): Saat + Kartopu + Elmas + Kum"] +name = "Zaman Bombasi" +description = "Zamansal bir alan yaratan, varliklari yavalatan ve mermileleri donduran üretilmiş bir krono bombasi firlatın." +lore1 = "Zamansal alan yaricapi" +lore2 = "Zamansal alan suresi" +lore3 = "Bomba bekleme suresi" +lore4 = "Tarif (Sekillsiz): Saat + Kartopu + Elmas + Kum" +lore = ["Zamansal alan yaricapi", "Zamansal alan suresi", "Bomba bekleme suresi", "Tarif (Sekillsiz): Saat + Kartopu + Elmas + Kum"] # discovery [discovery] [discovery.armor] - name = "Dunya Zirhi" - description = "Yakindaki blok sertligine bagli olarak pasif zirh." - lore1 = "Pasif Zirh" - lore2 = "Yakindaki blok sertligine gore" - lore3 = "Zirh Gucu:" - lore = ["Pasif Zirh", "Yakindaki blok sertligine gore", "Zirh Gucu:"] +name = "Dunya Zirhi" +description = "Yakindaki blok sertligine bagli olarak pasif zirh." +lore1 = "Pasif Zirh" +lore2 = "Yakindaki blok sertligine gore" +lore3 = "Zirh Gucu:" +lore = ["Pasif Zirh", "Yakindaki blok sertligine gore", "Zirh Gucu:"] [discovery.unity] - name = "Deneysel Birlik" - description = "Deneyim Kureleri toplamak rastgele becerilere XP ekler." - lore1 = "XP " - lore2 = "Kure Basina" - lore = ["XP ", "Kure Basina"] +name = "Deneysel Birlik" +description = "Deneyim Kureleri toplamak rastgele becerilere XP ekler." +lore1 = "XP " +lore2 = "Kure Basina" +lore = ["XP ", "Kure Basina"] [discovery.resist] - name = "Deneysel Direnc" - description = "Bir vurus sizi 5 kalbin altina dusurduğünde veya öldüreceğinde hasari azaltmak icin deneyim tuketin." - lore0 = "Yalnizca kritik saglikta (<= 5 kalp) 15 saniyede bir tetiklenir" - lore1 = " Azaltilmis Hasar" - lore2 = "deneyim tukendi" - lore = ["Yalnizca kritik saglikta (<= 5 kalp) 15 saniyede bir tetiklenir", " Azaltilmis Hasar", "deneyim tukendi"] +name = "Deneysel Direnc" +description = "Bir vurus sizi 5 kalbin altina dusurduğünde veya öldüreceğinde hasari azaltmak icin deneyim tuketin." +lore0 = "Yalnizca kritik saglikta (<= 5 kalp) 15 saniyede bir tetiklenir" +lore1 = " Azaltilmis Hasar" +lore2 = "deneyim tukendi" +lore = ["Yalnizca kritik saglikta (<= 5 kalp) 15 saniyede bir tetiklenir", " Azaltilmis Hasar", "deneyim tukendi"] [discovery.villager] - name = "Koylu Cazibesi" - description = "Koylülerle daha iyi ticaret yapmanizi saglar!" - lore1 = "Bu, Koylülerle etkilesim basina XP tuketir" - lore2 = "XP tuketmek ve takasi gelistirmek icin etkilesim basina sans" - lore3 = "Etkilesim basina gereken XP tukenmesi" - lore = ["Bu, Koylülerle etkilesim basina XP tuketir", "XP tuketmek ve takasi gelistirmek icin etkilesim basina sans", "Etkilesim basina gereken XP tukenmesi"] +name = "Koylu Cazibesi" +description = "Koylülerle daha iyi ticaret yapmanizi saglar!" +lore1 = "Bu, Koylülerle etkilesim basina XP tuketir" +lore2 = "XP tuketmek ve takasi gelistirmek icin etkilesim basina sans" +lore3 = "Etkilesim basina gereken XP tukenmesi" +lore = ["Bu, Koylülerle etkilesim basina XP tuketir", "XP tuketmek ve takasi gelistirmek icin etkilesim basina sans", "Etkilesim basina gereken XP tukenmesi"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Lapis Iadesi" - description = "1 XP seviyesi daha fazla maliyetine, karsiliginda ucretsiz lapis verme sansi var" - lore1 = "Her seviye, buyuleme maliyetini 1 arttirir ama 3 Lapis'e kadar iade edebilir" - lore = ["Her seviye, buyuleme maliyetini 1 arttirir ama 3 Lapis'e kadar iade edebilir"] +name = "Lapis Iadesi" +description = "1 XP seviyesi daha fazla maliyetine, karsiliginda ucretsiz lapis verme sansi var" +lore1 = "Her seviye, buyuleme maliyetini 1 arttirir ama 3 Lapis'e kadar iade edebilir" +lore = ["Her seviye, buyuleme maliyetini 1 arttirir ama 3 Lapis'e kadar iade edebilir"] [enchanting.quick_enchant] - name = "Hizli Tikla Buyule" - description = "Esyalari dogrudan uzerlerindeki buyulu kitaplara tiklayarak buyuleyin." - lore1 = "Maksimum Birlesik Seviye" - lore2 = "Bir esyayi birden fazla buyuleyemezsiniz " - lore3 = "guc" - lore = ["Maksimum Birlesik Seviye", "Bir esyayi birden fazla buyuleyemezsiniz ", "guc"] +name = "Hizli Tikla Buyule" +description = "Esyalari dogrudan uzerlerindeki buyulu kitaplara tiklayarak buyuleyin." +lore1 = "Maksimum Birlesik Seviye" +lore2 = "Bir esyayi birden fazla buyuleyemezsiniz " +lore3 = "guc" +lore = ["Maksimum Birlesik Seviye", "Bir esyayi birden fazla buyuleyemezsiniz ", "guc"] [enchanting.return] - name = "XP Iadesi" - description = "Bir esyayi buyulediginizde Buyuleme XP'si size iade edilir." - lore1 = "Bir esyayi buyulediginizde harcanan deneyimin iade edilme sansi vardir" - lore2 = "Buyuleme Basina Deneyim" - lore = ["Bir esyayi buyulediginizde harcanan deneyimin iade edilme sansi vardir", "Buyuleme Basina Deneyim"] +name = "XP Iadesi" +description = "Bir esyayi buyulediginizde Buyuleme XP'si size iade edilir." +lore1 = "Bir esyayi buyulediginizde harcanan deneyimin iade edilme sansi vardir" +lore2 = "Buyuleme Basina Deneyim" +lore = ["Bir esyayi buyulediginizde harcanan deneyimin iade edilme sansi vardir", "Buyuleme Basina Deneyim"] # excavation [excavation] [excavation.haste] - name = "Aceleci Kazici" - description = "Kazi islemini ACELE ile hizlandirir!" - lore1 = "Kazi yaparken Acele kazanin" - lore2 = "x herhangi bir blok kazmaya basladiginizda Acele seviyeleri." - lore = ["Kazi yaparken Acele kazanin", "x herhangi bir blok kazmaya basladiginizda Acele seviyeleri."] +name = "Aceleci Kazici" +description = "Kazi islemini ACELE ile hizlandirir!" +lore1 = "Kazi yaparken Acele kazanin" +lore2 = "x herhangi bir blok kazmaya basladiginizda Acele seviyeleri." +lore = ["Kazi yaparken Acele kazanin", "x herhangi bir blok kazmaya basladiginizda Acele seviyeleri."] [excavation.spelunker] - name = "Super Goren Magara Kásifi!" - description = "Cevherleri gozlerinizle gorun, ama yerin altindan!" - lore1 = "Yan elinizde cevher, ana elinizde Parlakmeyveler ve Egilin!" - lore2 = "Blok Menzili: " - lore3 = "Kullanimda Parlakmeyve tuketir" - lore = ["Yan elinizde cevher, ana elinizde Parlakmeyveler ve Egilin!", "Blok Menzili: ", "Kullanimda Parlakmeyve tuketir"] +name = "Super Goren Magara Kásifi!" +description = "Cevherleri gozlerinizle gorun, ama yerin altindan!" +lore1 = "Yan elinizde cevher, ana elinizde Parlakmeyveler ve Egilin!" +lore2 = "Blok Menzili: " +lore3 = "Kullanimda Parlakmeyve tuketir" +lore = ["Yan elinizde cevher, ana elinizde Parlakmeyveler ve Egilin!", "Blok Menzili: ", "Kullanimda Parlakmeyve tuketir"] [excavation.drop_to_inventory] - name = "Kurek Envantere Dusurme" +name = "Kurek Envantere Dusurme" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Tackle'in asiri tasarlanmis gosterisli Leatherman'i" - lore1 = "Muhtemelen en guclulerinden biri," - lore2 = "ihtiyaclariniza gore aletleri aninda dinamik olarak birlestirin ve degistirin." - lore3 = "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayin." - lore4 = "Aletleri cozmek icin esyayi Egilip-Birakin, parcalanacaktir." - lore5 = "Bu leatherman'de aletleri kiramazsiniz ama kirik aletleri kullanamazsiniz" - lore6 = "toplam birlestirilebilir esya." - lore7 = "Bes veya alti alet kullanabilirsin ya da sadece bir tane!" - lore = ["Muhtemelen en guclulerinden biri,", "ihtiyaclariniza gore aletleri aninda dinamik olarak birlestirin ve degistirin.", "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayin.", "Aletleri cozmek icin esyayi Egilip-Birakin, parcalanacaktir.", "Bu leatherman'de aletleri kiramazsiniz ama kirik aletleri kullanamazsiniz", "toplam birlestirilebilir esya.", "Bes veya alti alet kullanabilirsin ya da sadece bir tane!"] +name = "OMNI - T.O.O.L." +description = "Tackle'in asiri tasarlanmis gosterisli Leatherman'i" +lore1 = "Muhtemelen en guclulerinden biri," +lore2 = "ihtiyaclariniza gore aletleri aninda dinamik olarak birlestirin ve degistirin." +lore3 = "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayin." +lore4 = "Aletleri cozmek icin esyayi Egilip-Birakin, parcalanacaktir." +lore5 = "Bu leatherman'de aletleri kiramazsiniz ama kirik aletleri kullanamazsiniz" +lore6 = "toplam birlestirilebilir esya." +lore7 = "Bes veya alti alet kullanabilirsin ya da sadece bir tane!" +lore = ["Muhtemelen en guclulerinden biri,", "ihtiyaclariniza gore aletleri aninda dinamik olarak birlestirin ve degistirin.", "Birlestirmek icin envanterinizde bir esyayi digerinin uzerine shift tiklayin.", "Aletleri cozmek icin esyayi Egilip-Birakin, parcalanacaktir.", "Bu leatherman'de aletleri kiramazsiniz ama kirik aletleri kullanamazsiniz", "toplam birlestirilebilir esya.", "Bes veya alti alet kullanabilirsin ya da sadece bir tane!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Buyume Aurasi" - description = "Cevrenizdeki dogayi bir aura icinde buyutun" - lore1 = "Blok Yaricapi" - lore2 = "Buyume Aurasi Gucu" - lore3 = "Yiyecek Maliyeti" - lore = ["Blok Yaricapi", "Buyume Aurasi Gucu", "Yiyecek Maliyeti"] +name = "Buyume Aurasi" +description = "Cevrenizdeki dogayi bir aura icinde buyutun" +lore1 = "Blok Yaricapi" +lore2 = "Buyume Aurasi Gucu" +lore3 = "Yiyecek Maliyeti" +lore = ["Blok Yaricapi", "Buyume Aurasi Gucu", "Yiyecek Maliyeti"] [herbalism.hippo] - name = "Bitkicinin Su Aygiri" - description = "Yiyecek tuketmek size daha fazla doygunluk saglar" - lore1 = "Yiyecek) tuketimde ek doygunluk puanlari" - lore = ["Yiyecek) tuketimde ek doygunluk puanlari"] +name = "Bitkicinin Su Aygiri" +description = "Yiyecek tuketmek size daha fazla doygunluk saglar" +lore1 = "Yiyecek) tuketimde ek doygunluk puanlari" +lore = ["Yiyecek) tuketimde ek doygunluk puanlari"] [herbalism.myconid] - name = "Bitkicinin Myconid'i" - description = "Size Miselyum yapma yetenegi verir" - lore1 = "Herhangi bir Toprak ve bir Kahverengi & Kirmizi Mantar, Miselyum uretecektir." - lore = ["Herhangi bir Toprak ve bir Kahverengi & Kirmizi Mantar, Miselyum uretecektir."] +name = "Bitkicinin Myconid'i" +description = "Size Miselyum yapma yetenegi verir" +lore1 = "Herhangi bir Toprak ve bir Kahverengi & Kirmizi Mantar, Miselyum uretecektir." +lore = ["Herhangi bir Toprak ve bir Kahverengi & Kirmizi Mantar, Miselyum uretecektir."] [herbalism.terralid] - name = "Bitkicinin Terralid'i" - description = "Size Cimenli Blok yapma yetenegi verir" - lore1 = "3 Toprak uzerinde Uc Tohum, 3 Cimenli Blok uretecektir." - lore = ["3 Toprak uzerinde Uc Tohum, 3 Cimenli Blok uretecektir."] +name = "Bitkicinin Terralid'i" +description = "Size Cimenli Blok yapma yetenegi verir" +lore1 = "3 Toprak uzerinde Uc Tohum, 3 Cimenli Blok uretecektir." +lore = ["3 Toprak uzerinde Uc Tohum, 3 Cimenli Blok uretecektir."] [herbalism.cobweb] - name = "Ag Oluşturucu" - description = "Uretim Masasinda Orumcek Agi yapma yetenegi verir" - lore1 = "Dokuz Iplik, bir Orumcek Agi yapacaktir." - lore = ["Dokuz Iplik, bir Orumcek Agi yapacaktir."] +name = "Ag Oluşturucu" +description = "Uretim Masasinda Orumcek Agi yapma yetenegi verir" +lore1 = "Dokuz Iplik, bir Orumcek Agi yapacaktir." +lore = ["Dokuz Iplik, bir Orumcek Agi yapacaktir."] [herbalism.mushroom_blocks] - name = "Mantar Yapici" - description = "Uretim Masasinda Mantar Bloklari yapma yetenegi verir" - lore1 = "Bir blok yapmak icin dort Mantar veya bir govde yapmak icin bir blok." - lore = ["Bir blok yapmak icin dort Mantar veya bir govde yapmak icin bir blok."] +name = "Mantar Yapici" +description = "Uretim Masasinda Mantar Bloklari yapma yetenegi verir" +lore1 = "Bir blok yapmak icin dort Mantar veya bir govde yapmak icin bir blok." +lore = ["Bir blok yapmak icin dort Mantar veya bir govde yapmak icin bir blok."] [herbalism.drop_to_inventory] - name = "Copa Envantere Dusurme" +name = "Copa Envantere Dusurme" [herbalism.hungry_shield] - name = "Ac Kalkan" - description = "Sagliginizdan once acliginiza hasar verin." - lore1 = "Aclik tarafindan direncli" - lore = ["Aclik tarafindan direncli"] +name = "Ac Kalkan" +description = "Sagliginizdan once acliginiza hasar verin." +lore1 = "Aclik tarafindan direncli" +lore = ["Aclik tarafindan direncli"] [herbalism.luck] - name = "Bitkicinin Sansi" - description = "Cim/Cicek kirdiginizda rastgele bir esya alma sansiniz olur" - lore0 = "Cicekler = Yiyecek ve Cim = Tohumlar" - lore1 = "Cicek kirarak esya kazanma sansi" - lore2 = "Cim kirarak esya kazanma sansi" - lore = ["Cicekler = Yiyecek ve Cim = Tohumlar", "Cicek kirarak esya kazanma sansi", "Cim kirarak esya kazanma sansi"] +name = "Bitkicinin Sansi" +description = "Cim/Cicek kirdiginizda rastgele bir esya alma sansiniz olur" +lore0 = "Cicekler = Yiyecek ve Cim = Tohumlar" +lore1 = "Cicek kirarak esya kazanma sansi" +lore2 = "Cim kirarak esya kazanma sansi" +lore = ["Cicekler = Yiyecek ve Cim = Tohumlar", "Cicek kirarak esya kazanma sansi", "Cim kirarak esya kazanma sansi"] [herbalism.replant] - name = "Hasat ve Tekrar Dikim" - description = "Copa ile bir ekine sag tiklayarak hasat edin ve yeniden dikin." - lore1 = "Blok Yeniden Dikim Yaricapi" - lore = ["Blok Yeniden Dikim Yaricapi"] +name = "Hasat ve Tekrar Dikim" +description = "Copa ile bir ekine sag tiklayarak hasat edin ve yeniden dikin." +lore1 = "Blok Yeniden Dikim Yaricapi" +lore = ["Blok Yeniden Dikim Yaricapi"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenalin" - description = "Caniniz azaldikca daha fazla hasar verin (Yakin Dovus)" - lore1 = "Maksimum Hasar" - lore = ["Maksimum Hasar"] +name = "Adrenalin" +description = "Caniniz azaldikca daha fazla hasar verin (Yakin Dovus)" +lore1 = "Maksimum Hasar" +lore = ["Maksimum Hasar"] [hunter.penalty] - name = "" - description = "" - lore1 = "Acliginiz biterse Zehir yiginlari kazanacaksiniz" - lore = ["Acliginiz biterse Zehir yiginlari kazanacaksiniz"] +name = "" +description = "" +lore1 = "Acliginiz biterse Zehir yiginlari kazanacaksiniz" +lore = ["Acliginiz biterse Zehir yiginlari kazanacaksiniz"] [hunter.drop_to_inventory] - name = "Esya Envantere Dusurme" - description = "Bir seyi oldurdugunuzde / Kilicla bir blok kirdiginizda dusmeleri envanterinize isinlar" - lore1 = "Bir yaratik/bloktan bir esya dustugunde, mumkunse envanterinize girer." - lore = ["Bir yaratik/bloktan bir esya dustugunde, mumkunse envanterinize girer."] +name = "Esya Envantere Dusurme" +description = "Bir seyi oldurdugunuzde / Kilicla bir blok kirdiginizda dusmeleri envanterinize isinlar" +lore1 = "Bir yaratik/bloktan bir esya dustugunde, mumkunse envanterinize girer." +lore = ["Bir yaratik/bloktan bir esya dustugunde, mumkunse envanterinize girer."] [hunter.invisibility] - name = "Kaybolan Adim" - description = "Vuruldugunuzda aclik pahasina gorunmezlik kazanirsiniz" - lore1 = "Vuruldugunuzda pasif gorunmezlik kazanin" - lore2 = "x Gorunmezlik vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Gorunmezlik suresi" - lore = ["Vuruldugunuzda pasif gorunmezlik kazanin", "x Gorunmezlik vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Gorunmezlik suresi"] +name = "Kaybolan Adim" +description = "Vuruldugunuzda aclik pahasina gorunmezlik kazanirsiniz" +lore1 = "Vuruldugunuzda pasif gorunmezlik kazanin" +lore2 = "x Gorunmezlik vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Gorunmezlik suresi" +lore = ["Vuruldugunuzda pasif gorunmezlik kazanin", "x Gorunmezlik vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Gorunmezlik suresi"] [hunter.jump_boost] - name = "Avcinin Yukseklikleri" - description = "Vuruldugunuzda aclik pahasina ziplama gucu kazanirsiniz" - lore1 = "Vuruldugunuzda pasif ziplama gucu kazanin" - lore2 = "x Vurusda 3 saniye boyunca Ziplama Gucu birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Ziplama Gucu yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif ziplama gucu kazanin", "x Vurusda 3 saniye boyunca Ziplama Gucu birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Ziplama Gucu yiginlari carpani, sureyi degil."] +name = "Avcinin Yukseklikleri" +description = "Vuruldugunuzda aclik pahasina ziplama gucu kazanirsiniz" +lore1 = "Vuruldugunuzda pasif ziplama gucu kazanin" +lore2 = "x Vurusda 3 saniye boyunca Ziplama Gucu birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Ziplama Gucu yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif ziplama gucu kazanin", "x Vurusda 3 saniye boyunca Ziplama Gucu birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Ziplama Gucu yiginlari carpani, sureyi degil."] [hunter.luck] - name = "Avcinin Sansi" - description = "Vuruldugunuzda aclik pahasina sans kazanirsiniz" - lore1 = "Vuruldugunuzda pasif sans kazanin" - lore2 = "x Sans vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Sans yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif sans kazanin", "x Sans vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Sans yiginlari carpani, sureyi degil."] +name = "Avcinin Sansi" +description = "Vuruldugunuzda aclik pahasina sans kazanirsiniz" +lore1 = "Vuruldugunuzda pasif sans kazanin" +lore2 = "x Sans vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Sans yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif sans kazanin", "x Sans vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Sans yiginlari carpani, sureyi degil."] [hunter.regen] - name = "Avcinin Yenilenmesi" - description = "Vuruldugunuzda aclik pahasina yenilenme kazanirsiniz" - lore1 = "Vuruldugunuzda pasif yenilenme kazanin" - lore2 = "x Yenilenme vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Yenilenme yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif yenilenme kazanin", "x Yenilenme vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Yenilenme yiginlari carpani, sureyi degil."] +name = "Avcinin Yenilenmesi" +description = "Vuruldugunuzda aclik pahasina yenilenme kazanirsiniz" +lore1 = "Vuruldugunuzda pasif yenilenme kazanin" +lore2 = "x Yenilenme vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Yenilenme yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif yenilenme kazanin", "x Yenilenme vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Yenilenme yiginlari carpani, sureyi degil."] [hunter.resistance] - name = "Avcinin Direnci" - description = "Vuruldugunuzda aclik pahasina direnc kazanirsiniz" - lore1 = "Vuruldugunuzda pasif direnc kazanin" - lore2 = "x Direnc vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Direnc yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif direnc kazanin", "x Direnc vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Direnc yiginlari carpani, sureyi degil."] +name = "Avcinin Direnci" +description = "Vuruldugunuzda aclik pahasina direnc kazanirsiniz" +lore1 = "Vuruldugunuzda pasif direnc kazanin" +lore2 = "x Direnc vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Direnc yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif direnc kazanin", "x Direnc vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Direnc yiginlari carpani, sureyi degil."] [hunter.speed] - name = "Avcinin Hizi" - description = "Vuruldugunuzda aclik pahasina hiz kazanirsiniz" - lore1 = "Vuruldugunuzda pasif hiz kazanin" - lore2 = "x Hiz vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Hiz yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif hiz kazanin", "x Hiz vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Hiz yiginlari carpani, sureyi degil."] +name = "Avcinin Hizi" +description = "Vuruldugunuzda aclik pahasina hiz kazanirsiniz" +lore1 = "Vuruldugunuzda pasif hiz kazanin" +lore2 = "x Hiz vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Hiz yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif hiz kazanin", "x Hiz vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Hiz yiginlari carpani, sureyi degil."] [hunter.strength] - name = "Avcinin Gucu" - description = "Vuruldugunuzda aclik pahasina guc kazanirsiniz" - lore1 = "Vuruldugunuzda pasif guc kazanin" - lore2 = "x Guc vurusda 3 saniye boyunca birikir" - lore3 = "x Biriken aclik" - lore4 = "Aclik yiginlari suresi ve carpani." - lore5 = "Guc yiginlari carpani, sureyi degil." - lore = ["Vuruldugunuzda pasif guc kazanin", "x Guc vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Guc yiginlari carpani, sureyi degil."] +name = "Avcinin Gucu" +description = "Vuruldugunuzda aclik pahasina guc kazanirsiniz" +lore1 = "Vuruldugunuzda pasif guc kazanin" +lore2 = "x Guc vurusda 3 saniye boyunca birikir" +lore3 = "x Biriken aclik" +lore4 = "Aclik yiginlari suresi ve carpani." +lore5 = "Guc yiginlari carpani, sureyi degil." +lore = ["Vuruldugunuzda pasif guc kazanin", "x Guc vurusda 3 saniye boyunca birikir", "x Biriken aclik", "Aclik yiginlari suresi ve carpani.", "Guc yiginlari carpani, sureyi degil."] # nether [nether] [nether.skull_toss] - name = "Wither Kafatasi Firlatma" - description1 = "Kullanarak icinizdeki Wither'i aciga cikarin" - description2 = "birinin" - description3 = "kafasini." - lore1 = "Kafatasi firlatlari arasinda saniye bekleme suresi." - lore2 = "Wither Kafatasi Kullanarak: Bir " - lore3 = "Wither Kafatasi" - lore4 = "carpma aninda patlayarak firlatin." - lore = ["Kafatasi firlatlari arasinda saniye bekleme suresi.", "Wither Kafatasi Kullanarak: Bir ", "Wither Kafatasi", "carpma aninda patlayarak firlatin."] +name = "Wither Kafatasi Firlatma" +description1 = "Kullanarak icinizdeki Wither'i aciga cikarin" +description2 = "birinin" +description3 = "kafasini." +lore1 = "Kafatasi firlatlari arasinda saniye bekleme suresi." +lore2 = "Wither Kafatasi Kullanarak: Bir " +lore3 = "Wither Kafatasi" +lore4 = "carpma aninda patlayarak firlatin." +lore = ["Kafatasi firlatlari arasinda saniye bekleme suresi.", "Wither Kafatasi Kullanarak: Bir ", "Wither Kafatasi", "carpma aninda patlayarak firlatin."] [nether.wither_resist] - name = "Wither Direnci" - description = "Netherite'in gucu ile solmaya karsi direnir." - lore1 = "solmayi engeleme sansi (parca basina)." - lore2 = "Pasif: Netherite Zirhi giymek solmayi engelleme" - lore3 = "sansina sahiptir." - lore = ["solmayi engeleme sansi (parca basina).", "Pasif: Netherite Zirhi giymek solmayi engelleme", "sansina sahiptir."] +name = "Wither Direnci" +description = "Netherite'in gucu ile solmaya karsi direnir." +lore1 = "solmayi engeleme sansi (parca basina)." +lore2 = "Pasif: Netherite Zirhi giymek solmayi engelleme" +lore3 = "sansina sahiptir." +lore = ["solmayi engeleme sansi (parca basina).", "Pasif: Netherite Zirhi giymek solmayi engelleme", "sansina sahiptir."] [nether.fire_resist] - name = "Ates Direnci" - description = "Cildinizi sertlestirerek atese karsi direnir." - lore1 = "yanma etkisini ortadan kaldirma sansi!" - lore = ["yanma etkisini ortadan kaldirma sansi!"] +name = "Ates Direnci" +description = "Cildinizi sertlestirerek atese karsi direnir." +lore1 = "yanma etkisini ortadan kaldirma sansi!" +lore = ["yanma etkisini ortadan kaldirma sansi!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Otomatik Eritme" - description = "Kaziilan Vanilya cevherlerini eritmenizi saglar" - lore1 = "Eritilebilen cevherler otomatik olarak eritilir" - lore2 = "% ekstra sans" - lore = ["Eritilebilen cevherler otomatik olarak eritilir", "% ekstra sans"] +name = "Otomatik Eritme" +description = "Kaziilan Vanilya cevherlerini eritmenizi saglar" +lore1 = "Eritilebilen cevherler otomatik olarak eritilir" +lore2 = "% ekstra sans" +lore = ["Eritilebilen cevherler otomatik olarak eritilir", "% ekstra sans"] [pickaxe.chisel] - name = "Cevher Keskisi" - description = "Cevherlerden daha fazla cevher cikarmak icin Sag Tiklayin, ciddi bir dayaniklilik maliyetiyle." - lore1 = "Dusme Sansi" - lore2 = "Alet Asinmasi" - lore = ["Dusme Sansi", "Alet Asinmasi"] +name = "Cevher Keskisi" +description = "Cevherlerden daha fazla cevher cikarmak icin Sag Tiklayin, ciddi bir dayaniklilik maliyetiyle." +lore1 = "Dusme Sansi" +lore2 = "Alet Asinmasi" +lore = ["Dusme Sansi", "Alet Asinmasi"] [pickaxe.drop_to_inventory] - name = "Kazma Envantere Dusurme" - description = "Bir blogu kirdiginizda esya envanterinize isinlanir" - lore1 = "Kirdiginiz bir bloktan dusen bir esya mumkunse envanterinize girer." - lore = ["Kirdiginiz bir bloktan dusen bir esya mumkunse envanterinize girer."] +name = "Kazma Envantere Dusurme" +description = "Bir blogu kirdiginizda esya envanterinize isinlanir" +lore1 = "Kirdiginiz bir bloktan dusen bir esya mumkunse envanterinize girer." +lore = ["Kirdiginiz bir bloktan dusen bir esya mumkunse envanterinize girer."] [pickaxe.silk_spawner] - name = "Kazma Ipek-Yaratici" - description = "Spawner'larin kirildiginda dusmesini saglar" - lore1 = "Spawner'lari ipek dokunusuyla kiriabilir hale getirir." - lore2 = "Spawner'lari egilirken kiriabilir hale getirir." - lore = ["Spawner'lari ipek dokunusuyla kiriabilir hale getirir.", "Spawner'lari egilirken kiriabilir hale getirir."] +name = "Kazma Ipek-Yaratici" +description = "Spawner'larin kirildiginda dusmesini saglar" +lore1 = "Spawner'lari ipek dokunusuyla kiriabilir hale getirir." +lore2 = "Spawner'lari egilirken kiriabilir hale getirir." +lore = ["Spawner'lari ipek dokunusuyla kiriabilir hale getirir.", "Spawner'lari egilirken kiriabilir hale getirir."] [pickaxe.vein_miner] - name = "Damar Madencisi" - description = "Vanilya cevheri Damarinda/Kumesinde bloklari kirmanizi saglar" - lore1 = "Egilin ve CEVHER kazin" - lore2 = "damar madenciligi menzili" - lore3 = "Bu beceri tum dusmeleri bir araya gruplandirmaz!" - lore = ["Egilin ve CEVHER kazin", "damar madenciligi menzili", "Bu beceri tum dusmeleri bir araya gruplandirmaz!"] +name = "Damar Madencisi" +description = "Vanilya cevheri Damarinda/Kumesinde bloklari kirmanizi saglar" +lore1 = "Egilin ve CEVHER kazin" +lore2 = "damar madenciligi menzili" +lore3 = "Bu beceri tum dusmeleri bir araya gruplandirmaz!" +lore = ["Egilin ve CEVHER kazin", "damar madenciligi menzili", "Bu beceri tum dusmeleri bir araya gruplandirmaz!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Ok Kurtarma" - description = "Bir dusmani oldurdukten sonra oklari kurtarin." - lore1 = "Vurus/Oldurme'de Ok Kurtarma Sansi" - lore2 = "Sans: " - lore = ["Vurus/Oldurme'de Ok Kurtarma Sansi", "Sans: "] +name = "Ok Kurtarma" +description = "Bir dusmani oldurdukten sonra oklari kurtarin." +lore1 = "Vurus/Oldurme'de Ok Kurtarma Sansi" +lore2 = "Sans: " +lore = ["Vurus/Oldurme'de Ok Kurtarma Sansi", "Sans: "] [ranged.web_shot] - name = "Ag Tuzagi" - description = "Vurdugunuzda hedefinizin etrafini orumcek aglariyla cevreleyin!" - lore1 = "Bir Kartopunun etrafinda 8 Orumcek Agi ve firlatın!" - lore2 = "saniye kadar bir kafes, yaklasik olarak." - lore = ["Bir Kartopunun etrafinda 8 Orumcek Agi ve firlatın!", "saniye kadar bir kafes, yaklasik olarak."] +name = "Ag Tuzagi" +description = "Vurdugunuzda hedefinizin etrafini orumcek aglariyla cevreleyin!" +lore1 = "Bir Kartopunun etrafinda 8 Orumcek Agi ve firlatın!" +lore2 = "saniye kadar bir kafes, yaklasik olarak." +lore = ["Bir Kartopunun etrafinda 8 Orumcek Agi ve firlatın!", "saniye kadar bir kafes, yaklasik olarak."] [ranged.force_shot] - name = "Guclu Atis" - description = "Mermileleri daha uzaga, daha hizli atin!" - advancementname = "Uzun Atis" - advancementlore = "30 blok oteden bir atis yapin!" - lore1 = "Mermilerin Hizi" - lore = ["Mermilerin Hizi"] +name = "Guclu Atis" +description = "Mermileleri daha uzaga, daha hizli atin!" +advancementname = "Uzun Atis" +advancementlore = "30 blok oteden bir atis yapin!" +lore1 = "Mermilerin Hizi" +lore = ["Mermilerin Hizi"] [ranged.lunge_shot] - name = "Hamle Atisi" - description = "Duserken oklariniz sizi rastgele bir yone firlatir" - lore1 = "Rastgele Patlama Hizi" - lore = ["Rastgele Patlama Hizi"] +name = "Hamle Atisi" +description = "Duserken oklariniz sizi rastgele bir yone firlatir" +lore1 = "Rastgele Patlama Hizi" +lore = ["Rastgele Patlama Hizi"] [ranged.arrow_piercing] - name = "Ok Delicisi" - description = "Mermilere Delicilik ekler! Seylerin icinden atin!" - lore1 = "Delebilecegi Hedef" - lore = ["Delebilecegi Hedef"] +name = "Ok Delicisi" +description = "Mermilere Delicilik ekler! Seylerin icinden atin!" +lore1 = "Delebilecegi Hedef" +lore = ["Delebilecegi Hedef"] # rift [rift] [rift.remote_access] - name = "Uzaktan Erisim" - description = "Bosluktan cekin ve isaretli bir konteynere erisin." - lore1 = "Ender Incisi + Pusula = Emanet Anahtari" - lore2 = "Bu esya konteynerlere uzaktan erismenizi saglar" - lore3 = "Uretildikten sonra kullanimi gormek icin esyaya bakin" - notcontainer = "Bu bir konteyner degil" - lore = ["Ender Incisi + Pusula = Emanet Anahtari", "Bu esya konteynerlere uzaktan erismenizi saglar", "Uretildikten sonra kullanimi gormek icin esyaya bakin"] +name = "Uzaktan Erisim" +description = "Bosluktan cekin ve isaretli bir konteynere erisin." +lore1 = "Ender Incisi + Pusula = Emanet Anahtari" +lore2 = "Bu esya konteynerlere uzaktan erismenizi saglar" +lore3 = "Uretildikten sonra kullanimi gormek icin esyaya bakin" +notcontainer = "Bu bir konteyner degil" +lore = ["Ender Incisi + Pusula = Emanet Anahtari", "Bu esya konteynerlere uzaktan erismenizi saglar", "Uretildikten sonra kullanimi gormek icin esyaya bakin"] [rift.blink] - name = "Yarik Gozu Kirpma" - description = "Kisa menzilli aninda isinlanma, Sadece bir goz kirpimi uzaklikta!" - lore1 = "Goz kirpmada blok (2x Dikey)" - lore2 = "Kosarken: Ziplama'ya cift basin " - lore3 = "Goz Kirp" - lore = ["Goz kirpmada blok (2x Dikey)", "Kosarken: Ziplama'ya cift basin ", "Goz Kirp"] +name = "Yarik Gozu Kirpma" +description = "Kisa menzilli aninda isinlanma, Sadece bir goz kirpimi uzaklikta!" +lore1 = "Goz kirpmada blok (2x Dikey)" +lore2 = "Kosarken: Ziplama'ya cift basin " +lore3 = "Goz Kirp" +lore = ["Goz kirpmada blok (2x Dikey)", "Kosarken: Ziplama'ya cift basin ", "Goz Kirp"] [rift.chest] - name = "Kolay Ender Sandigi" - description = "Elinizde Sol tiklayarak bir ender sandigi acin." - lore1 = "Acmak icin elinizdeki Ender Sandigina tiklayin (Sadece yerlestirmeyin)" - lore = ["Acmak icin elinizdeki Ender Sandigina tiklayin (Sadece yerlestirmeyin)"] +name = "Kolay Ender Sandigi" +description = "Elinizde Sol tiklayarak bir ender sandigi acin." +lore1 = "Acmak icin elinizdeki Ender Sandigina tiklayin (Sadece yerlestirmeyin)" +lore = ["Acmak icin elinizdeki Ender Sandigina tiklayin (Sadece yerlestirmeyin)"] [rift.descent] - name = "Anti-Havaya Kalkma" - description = "Havada kalmaktan biktiniz mi? Bu sizin icin olan beceri!" - lore1 = "Inmek icin egilmeniz yeterli, normalden daha yavas duseceksiniz!" - lore2 = "Bekleme suresi:" - lore = ["Inmek icin egilmeniz yeterli, normalden daha yavas duseceksiniz!", "Bekleme suresi:"] +name = "Anti-Havaya Kalkma" +description = "Havada kalmaktan biktiniz mi? Bu sizin icin olan beceri!" +lore1 = "Inmek icin egilmeniz yeterli, normalden daha yavas duseceksiniz!" +lore2 = "Bekleme suresi:" +lore = ["Inmek icin egilmeniz yeterli, normalden daha yavas duseceksiniz!", "Bekleme suresi:"] [rift.gate] - name = "Yarik Kapisi" - description = "Isaretli bir konuma isinlanin." - lore1 = "URETIM: Zumrut + Ametist parcasi + Ender Incisi" - lore2 = "Kullanmadan once okuyun!" - lore3 = "5sn gecikme, " - lore4 = "bu animasyondayken olebilirsiniz" - lore = ["URETIM: Zumrut + Ametist parcasi + Ender Incisi", "Kullanmadan once okuyun!", "5sn gecikme, ", "bu animasyondayken olebilirsiniz"] +name = "Yarik Kapisi" +description = "Isaretli bir konuma isinlanin." +lore1 = "URETIM: Zumrut + Ametist parcasi + Ender Incisi" +lore2 = "Kullanmadan once okuyun!" +lore3 = "5sn gecikme, " +lore4 = "bu animasyondayken olebilirsiniz" +lore = ["URETIM: Zumrut + Ametist parcasi + Ender Incisi", "Kullanmadan once okuyun!", "5sn gecikme, ", "bu animasyondayken olebilirsiniz"] [rift.resist] - name = "Yarik Direnci" - description = "Ender Esyalari ve Yeteneklerini kullanirken Direnc kazanin" - lore1 = "+ Pasif: Yarik yeteneklerini veya Ender Esyalarini kullandiginizda direnc saglar" - lore2 = "Portatif Ender Sandigi DAHiL DEGiLDiR, yalnizca tuketebileceginiz seyler" - lore = ["+ Pasif: Yarik yeteneklerini veya Ender Esyalarini kullandiginizda direnc saglar", "Portatif Ender Sandigi DAHiL DEGiLDiR, yalnizca tuketebileceginiz seyler"] +name = "Yarik Direnci" +description = "Ender Esyalari ve Yeteneklerini kullanirken Direnc kazanin" +lore1 = "+ Pasif: Yarik yeteneklerini veya Ender Esyalarini kullandiginizda direnc saglar" +lore2 = "Portatif Ender Sandigi DAHiL DEGiLDiR, yalnizca tuketebileceginiz seyler" +lore = ["+ Pasif: Yarik yeteneklerini veya Ender Esyalarini kullandiginizda direnc saglar", "Portatif Ender Sandigi DAHiL DEGiLDiR, yalnizca tuketebileceginiz seyler"] [rift.visage] - name = "Yarik Gorunumu" - description = "Envanterinizde Ender Incisi varsa Endermen'lerin saldirganlasmasini engeller." - lore1 = "Envanterinizde Ender Incisi varsa Endermen'ler saldirgan olmaz." - lore = ["Envanterinizde Ender Incisi varsa Endermen'ler saldirgan olmaz."] +name = "Yarik Gorunumu" +description = "Envanterinizde Ender Incisi varsa Endermen'lerin saldirganlasmasini engeller." +lore1 = "Envanterinizde Ender Incisi varsa Endermen'ler saldirgan olmaz." +lore = ["Envanterinizde Ender Incisi varsa Endermen'ler saldirgan olmaz."] # seaborn [seaborn] [seaborn.oxygen] - name = "Organik Oksijen Tanki" - description = "Minik cigerlerinizde daha fazla oksijen tutun!" - lore1 = "Oksijen Kapasitesi Artisi" - lore = ["Oksijen Kapasitesi Artisi"] +name = "Organik Oksijen Tanki" +description = "Minik cigerlerinizde daha fazla oksijen tutun!" +lore1 = "Oksijen Kapasitesi Artisi" +lore = ["Oksijen Kapasitesi Artisi"] [seaborn.fishers_fantasy] - name = "Balcinin Hayali" - description = "Balikciliktan daha fazla XP kazanin ve daha fazla balik alin!" - lore1 = "Her seviye icin daha fazla XP ve Balik kazanma sansi var!" - lore = ["Her seviye icin daha fazla XP ve Balik kazanma sansi var!"] +name = "Balcinin Hayali" +description = "Balikciliktan daha fazla XP kazanin ve daha fazla balik alin!" +lore1 = "Her seviye icin daha fazla XP ve Balik kazanma sansi var!" +lore = ["Her seviye icin daha fazla XP ve Balik kazanma sansi var!"] [seaborn.haste] - name = "Kaplumbaga Madencisi" - description = "Su altinda madencilik yaparken acele kazanirsiniz!" - lore1 = "Su soluma etkiniz bittikten sonra su altinda madencilik yaparken Acele 3 uygulanir (AquaAffinity ile birikir)!" - lore = ["Su soluma etkiniz bittikten sonra su altinda madencilik yaparken Acele 3 uygulanir (AquaAffinity ile birikir)!"] +name = "Kaplumbaga Madencisi" +description = "Su altinda madencilik yaparken acele kazanirsiniz!" +lore1 = "Su soluma etkiniz bittikten sonra su altinda madencilik yaparken Acele 3 uygulanir (AquaAffinity ile birikir)!" +lore = ["Su soluma etkiniz bittikten sonra su altinda madencilik yaparken Acele 3 uygulanir (AquaAffinity ile birikir)!"] [seaborn.night_vision] - name = "Kaplumbaganin Gorusu" - description = "Su altindayken Gece Gorusu kazanirsiniz" - lore1 = "Su soluma etkiniz bittikten sonra su altindayken Gece Gorusu kazanin!" - lore = ["Su soluma etkiniz bittikten sonra su altindayken Gece Gorusu kazanin!"] +name = "Kaplumbaganin Gorusu" +description = "Su altindayken Gece Gorusu kazanirsiniz" +lore1 = "Su soluma etkiniz bittikten sonra su altindayken Gece Gorusu kazanin!" +lore = ["Su soluma etkiniz bittikten sonra su altindayken Gece Gorusu kazanin!"] [seaborn.dolphin_grace] - name = "Yunusun Zarfeti" - description = "Yunuslar olmadan bir yunus gibi yuzun" - lore1 = "+ Pasif: kazan " - lore2 = "x hiz (yunus zarafeti)" - lore3 = "hassas Alman muhendisligi- dur bu dogru degil... Derinlik Kosucusu ile uyumlu degil" - lore = ["+ Pasif: kazan ", "x hiz (yunus zarafeti)", "hassas Alman muhendisligi- dur bu dogru degil... Derinlik Kosucusu ile uyumlu degil"] +name = "Yunusun Zarfeti" +description = "Yunuslar olmadan bir yunus gibi yuzun" +lore1 = "+ Pasif: kazan " +lore2 = "x hiz (yunus zarafeti)" +lore3 = "hassas Alman muhendisligi- dur bu dogru degil... Derinlik Kosucusu ile uyumlu degil" +lore = ["+ Pasif: kazan ", "x hiz (yunus zarafeti)", "hassas Alman muhendisligi- dur bu dogru degil... Derinlik Kosucusu ile uyumlu degil"] # stealth [stealth] [stealth.ghost_armor] - name = "Hayaletin Zirhi" - description = "Hasar almadiginizda yavas yavas zirh birikir, 1 vurus surer" - lore1 = "Maksimum Zirh" - lore2 = "Hiz" - lore = ["Maksimum Zirh", "Hiz"] +name = "Hayaletin Zirhi" +description = "Hasar almadiginizda yavas yavas zirh birikir, 1 vurus surer" +lore1 = "Maksimum Zirh" +lore2 = "Hiz" +lore = ["Maksimum Zirh", "Hiz"] [stealth.night_vision] - name = "Gizlilik Gorusu" - description = "Egilirken gece gorusu kazanin" - lore1 = "Bir patlama kazanin " - lore2 = "gece gorusu" - lore3 = "egilirken" - lore = ["Bir patlama kazanin ", "gece gorusu", "egilirken"] +name = "Gizlilik Gorusu" +description = "Egilirken gece gorusu kazanin" +lore1 = "Bir patlama kazanin " +lore2 = "gece gorusu" +lore3 = "egilirken" +lore = ["Bir patlama kazanin ", "gece gorusu", "egilirken"] [stealth.snatch] - name = "Esya Kapma" - description = "Egilirken aninda dusen esyalari kapin!" - lore1 = "Kapma Yaricapi" - lore = ["Kapma Yaricapi"] +name = "Esya Kapma" +description = "Egilirken aninda dusen esyalari kapin!" +lore1 = "Kapma Yaricapi" +lore = ["Kapma Yaricapi"] [stealth.speed] - name = "Gizli Hiz" - description = "Egilirken hiz kazanin" - lore1 = "Egilme Hizi" - lore = ["Egilme Hizi"] +name = "Gizli Hiz" +description = "Egilirken hiz kazanin" +lore1 = "Egilme Hizi" +lore = ["Egilme Hizi"] [stealth.ender_veil] - name = "Ender Ortüsü" - description = "Enderman saldirilarina karsi artik Balkabagi takmak yok" - lore1 = "Egilirken enderman saldirilarini onleyin" - lore2 = "Tum enderman saldirilarini onleyin" - lore = ["Egilirken enderman saldirilarini onleyin", "Tum enderman saldirilarini onleyin"] +name = "Ender Ortüsü" +description = "Enderman saldirilarina karsi artik Balkabagi takmak yok" +lore1 = "Egilirken enderman saldirilarini onleyin" +lore2 = "Tum enderman saldirilarini onleyin" +lore = ["Egilirken enderman saldirilarini onleyin", "Tum enderman saldirilarini onleyin"] # sword [sword] [sword.machete] - name = "Pala" - description = "Bitki ortusunu kolayca kesin!" - lore1 = "Kesme Yaricapi" - lore2 = "Kesme Bekleme Suresi" - lore3 = "Alet Asinmasi" - lore = ["Kesme Yaricapi", "Kesme Bekleme Suresi", "Alet Asinmasi"] +name = "Pala" +description = "Bitki ortusunu kolayca kesin!" +lore1 = "Kesme Yaricapi" +lore2 = "Kesme Bekleme Suresi" +lore3 = "Alet Asinmasi" +lore = ["Kesme Yaricapi", "Kesme Bekleme Suresi", "Alet Asinmasi"] [sword.bloody_blade] - name = "Kanli Bicak" - description = "Kilic darbeleri Kanamaya neden olur!" - lore1 = "Kilicinizla canli bir varlga vurmak Kanamaya neden olur" - lore2 = "Kanama Suresi" - lore3 = "Kanama Bekleme Suresi" - lore = ["Kilicinizla canli bir varlga vurmak Kanamaya neden olur", "Kanama Suresi", "Kanama Bekleme Suresi"] +name = "Kanli Bicak" +description = "Kilic darbeleri Kanamaya neden olur!" +lore1 = "Kilicinizla canli bir varlga vurmak Kanamaya neden olur" +lore2 = "Kanama Suresi" +lore3 = "Kanama Bekleme Suresi" +lore = ["Kilicinizla canli bir varlga vurmak Kanamaya neden olur", "Kanama Suresi", "Kanama Bekleme Suresi"] [sword.poisoned_blade] - name = "Zehirli Bicak" - description = "Kilic darbeleri Zehire neden olur!" - lore1 = "Kilicinizla canli bir varliga vurmak Zehire neden olur" - lore2 = "Zehir Suresi" - lore3 = "Zehir Bekleme Suresi" - lore = ["Kilicinizla canli bir varliga vurmak Zehire neden olur", "Zehir Suresi", "Zehir Bekleme Suresi"] +name = "Zehirli Bicak" +description = "Kilic darbeleri Zehire neden olur!" +lore1 = "Kilicinizla canli bir varliga vurmak Zehire neden olur" +lore2 = "Zehir Suresi" +lore3 = "Zehir Bekleme Suresi" +lore = ["Kilicinizla canli bir varliga vurmak Zehire neden olur", "Zehir Suresi", "Zehir Bekleme Suresi"] # taming [taming] [taming.damage] - name = "Evcil Hasar" - description = "Evcillestirilmis hayvaninizin verdigi hasari artirin." - lore1 = "Arttirilmis Hasar" - lore = ["Arttirilmis Hasar"] +name = "Evcil Hasar" +description = "Evcillestirilmis hayvaninizin verdigi hasari artirin." +lore1 = "Arttirilmis Hasar" +lore = ["Arttirilmis Hasar"] [taming.health] - name = "Evcil Saglik" - description = "Evcillestirilmis hayvaninizin sagligini artirin." - lore1 = "Arttirilmis Saglik" - lore = ["Arttirilmis Saglik"] +name = "Evcil Saglik" +description = "Evcillestirilmis hayvaninizin sagligini artirin." +lore1 = "Arttirilmis Saglik" +lore = ["Arttirilmis Saglik"] [taming.regeneration] - name = "Evcil Yenilenme" - description = "Evcillestirilmis hayvaninizin yenilenmesini artirin." - lore1 = "HP/s" - lore = ["HP/s"] +name = "Evcil Yenilenme" +description = "Evcillestirilmis hayvaninizin yenilenmesini artirin." +lore1 = "HP/s" +lore = ["HP/s"] # tragoul [tragoul] [tragoul.thorns] - name = "Dikenler" - description = "Hasari saldirganiniza geri yansitin!" - lore1 = "Vuruldugunuzda misilleme edilen hasar" - lore = ["Vuruldugunuzda misilleme edilen hasar"] +name = "Dikenler" +description = "Hasari saldirganiniza geri yansitin!" +lore1 = "Vuruldugunuzda misilleme edilen hasar" +lore = ["Vuruldugunuzda misilleme edilen hasar"] [tragoul.globe] - name = "Aci Kuresi" - description = "Verdiginiz Hasari cevrenizdeki dusman sayisina gore bolun!" - lore1 = "Cevrenizdeki dusman sayisi arttikca her birine verdiginiz hasar azalir" - lore2 = "Menzil: " - lore3 = "Tum Varliklara Eklenen Hasar: " - lore = ["Cevrenizdeki dusman sayisi arttikca her birine verdiginiz hasar azalir", "Menzil: ", "Tum Varliklara Eklenen Hasar: "] +name = "Aci Kuresi" +description = "Verdiginiz Hasari cevrenizdeki dusman sayisina gore bolun!" +lore1 = "Cevrenizdeki dusman sayisi arttikca her birine verdiginiz hasar azalir" +lore2 = "Menzil: " +lore3 = "Tum Varliklara Eklenen Hasar: " +lore = ["Cevrenizdeki dusman sayisi arttikca her birine verdiginiz hasar azalir", "Menzil: ", "Tum Varliklara Eklenen Hasar: "] [tragoul.healing] - name = "Acinin Iradesi" - description = "Verdiginiz hasara gore saglik geri kazanin!" - lore1 = "Bir seylere zarar vermek hic bu kadar iyi hissettirmemisti! Verilen Hasardan Iyilesme" - lore2 = "Iyilestirme icin 3 Saniyelik bir hasar penceresi ve 1 Saniyelik bekleme suresi vardir " - lore3 = "Hasar Yuzdesine Gore Iyilestirme: " - lore = ["Bir seylere zarar vermek hic bu kadar iyi hissettirmemisti! Verilen Hasardan Iyilesme", "Iyilestirme icin 3 Saniyelik bir hasar penceresi ve 1 Saniyelik bekleme suresi vardir ", "Hasar Yuzdesine Gore Iyilestirme: "] +name = "Acinin Iradesi" +description = "Verdiginiz hasara gore saglik geri kazanin!" +lore1 = "Bir seylere zarar vermek hic bu kadar iyi hissettirmemisti! Verilen Hasardan Iyilesme" +lore2 = "Iyilestirme icin 3 Saniyelik bir hasar penceresi ve 1 Saniyelik bekleme suresi vardir " +lore3 = "Hasar Yuzdesine Gore Iyilestirme: " +lore = ["Bir seylere zarar vermek hic bu kadar iyi hissettirmemisti! Verilen Hasardan Iyilesme", "Iyilestirme icin 3 Saniyelik bir hasar penceresi ve 1 Saniyelik bekleme suresi vardir ", "Hasar Yuzdesine Gore Iyilestirme: "] [tragoul.lance] - name = "Ceset Mizraklari" - description = "Bir dusmani oldurmek veya bir yetenegin bir dusmani oldulmesi, yakindaki bir dusmana hasar veren bir mizrak olusturur!" - lore1 = "Mizraklar oldedugunuz her seyden VE bu yetenek bir dusmani oldurse cikacaktir." - lore2 = "Mizraklari yaratmak icin hayatinizin bir kismini feda edin (bu sizi oldurebilir)" - lore3 = "Maksimum Mizrak: 1 + " - lore = ["Mizraklar oldedugunuz her seyden VE bu yetenek bir dusmani oldurse cikacaktir.", "Mizraklari yaratmak icin hayatinizin bir kismini feda edin (bu sizi oldurebilir)", "Maksimum Mizrak: 1 + "] +name = "Ceset Mizraklari" +description = "Bir dusmani oldurmek veya bir yetenegin bir dusmani oldulmesi, yakindaki bir dusmana hasar veren bir mizrak olusturur!" +lore1 = "Mizraklar oldedugunuz her seyden VE bu yetenek bir dusmani oldurse cikacaktir." +lore2 = "Mizraklari yaratmak icin hayatinizin bir kismini feda edin (bu sizi oldurebilir)" +lore3 = "Maksimum Mizrak: 1 + " +lore = ["Mizraklar oldedugunuz her seyden VE bu yetenek bir dusmani oldurse cikacaktir.", "Mizraklari yaratmak icin hayatinizin bir kismini feda edin (bu sizi oldurebilir)", "Maksimum Mizrak: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Cam Top" - description = "Zirh degeriniz ne kadar dusukse o kadar fazla bonus Silahsiz Hasar" - lore1 = "x 0 zirhta hasar" - lore2 = "Seviye Basina Bonus Hasar" - lore = ["x 0 zirhta hasar", "Seviye Basina Bonus Hasar"] +name = "Cam Top" +description = "Zirh degeriniz ne kadar dusukse o kadar fazla bonus Silahsiz Hasar" +lore1 = "x 0 zirhta hasar" +lore2 = "Seviye Basina Bonus Hasar" +lore = ["x 0 zirhta hasar", "Seviye Basina Bonus Hasar"] [unarmed.power] - name = "Silahsiz Guc" - description = "Gelistirilmis Silahsiz Hasar" - lore1 = "Hasar" - lore = ["Hasar"] +name = "Silahsiz Guc" +description = "Gelistirilmis Silahsiz Hasar" +lore1 = "Hasar" +lore = ["Hasar"] [unarmed.sucker_punch] - name = "Sinsi Yumruk" - description = "Sprint yumruklari, ama daha olumcul." - lore1 = "Hasar" - lore2 = "Yumruk atarken hizinizla birlikte hasar artar" - lore = ["Hasar", "Yumruk atarken hizinizla birlikte hasar artar"] +name = "Sinsi Yumruk" +description = "Sprint yumruklari, ama daha olumcul." +lore1 = "Hasar" +lore2 = "Yumruk atarken hizinizla birlikte hasar artar" +lore = ["Hasar", "Yumruk atarken hizinizla birlikte hasar artar"] diff --git a/src/main/resources/vi_VI.toml b/src/main/resources/vi_VI.toml index 9c8d0b26d..ec4c1edd8 100644 --- a/src/main/resources/vi_VI.toml +++ b/src/main/resources/vi_VI.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "Phải Di Chuyển!" - description = "Đi bộ hơn 1 Km (1.000 khối)" +title = "Phải Di Chuyển!" +description = "Đi bộ hơn 1 Km (1.000 khối)" [advancement.challenge_sprint_5k] - title = "Chạy Nước Rút 5K!" - description = "Đi bộ hơn 5 Km (5.000 khối)" +title = "Chạy Nước Rút 5K!" +description = "Đi bộ hơn 5 Km (5.000 khối)" [advancement.challenge_sprint_50k] - title = "Phóng Nhanh 50K!" - description = "Đi bộ hơn 50 Km (50.000 khối)" +title = "Phóng Nhanh 50K!" +description = "Đi bộ hơn 50 Km (50.000 khối)" [advancement.challenge_sprint_500k] - title = "Xuyên Qua Vũ Trụ!!" - description = "Đi bộ hơn 500 Km (500.000 khối)" +title = "Xuyên Qua Vũ Trụ!!" +description = "Đi bộ hơn 500 Km (500.000 khối)" [advancement.challenge_sprint_marathon] - title = "Chạy Marathon (theo nghĩa đen)!" - description = "Chạy nước rút hơn 42.195 Khối!" +title = "Chạy Marathon (theo nghĩa đen)!" +description = "Chạy nước rút hơn 42.195 Khối!" [advancement.challenge_place_1k] - title = "Thợ Xây Tập Sự!" - description = "Đặt 1.000 Khối" +title = "Thợ Xây Tập Sự!" +description = "Đặt 1.000 Khối" [advancement.challenge_place_5k] - title = "Thợ Xây Trung Cấp!" - description = "Đặt 5.000 Khối" +title = "Thợ Xây Trung Cấp!" +description = "Đặt 5.000 Khối" [advancement.challenge_place_50k] - title = "Thợ Xây Cao Cấp!" - description = "Đặt 50.000 Khối" +title = "Thợ Xây Cao Cấp!" +description = "Đặt 50.000 Khối" [advancement.challenge_place_500k] - title = "Bậc Thầy Xây Dựng!" - description = "Đặt 500.000 Khối" +title = "Bậc Thầy Xây Dựng!" +description = "Đặt 500.000 Khối" [advancement.challenge_place_5m] - title = "Tín Đồ Của Đối Xứng!" - description = "THỰC TẠI LÀ SÂN CHƠI CỦA BẠN! (5 Triệu Khối)" +title = "Tín Đồ Của Đối Xứng!" +description = "THỰC TẠI LÀ SÂN CHƠI CỦA BẠN! (5 Triệu Khối)" [advancement.challenge_chop_1k] - title = "Tiều Phu Tập Sự!" - description = "Chặt 1.000 Khối" +title = "Tiều Phu Tập Sự!" +description = "Chặt 1.000 Khối" [advancement.challenge_chop_5k] - title = "Tiều Phu Trung Cấp!" - description = "Chặt 5.000 Khối" +title = "Tiều Phu Trung Cấp!" +description = "Chặt 5.000 Khối" [advancement.challenge_chop_50k] - title = "Tiều Phu Cao Cấp!" - description = "Chặt 50.000 Khối" +title = "Tiều Phu Cao Cấp!" +description = "Chặt 50.000 Khối" [advancement.challenge_chop_500k] - title = "Bậc Thầy Tiều Phu!" - description = "Chặt 500.000 Khối" +title = "Bậc Thầy Tiều Phu!" +description = "Chặt 500.000 Khối" [advancement.challenge_chop_5m] - title = "Chú Chó Jackson" - description = "Cậu bé ngoan nhất quả đất! (5 Triệu Khối)" +title = "Chú Chó Jackson" +description = "Cậu bé ngoan nhất quả đất! (5 Triệu Khối)" [advancement.challenge_block_1k] - title = "Mới Tập Đỡ Đòn!" - description = "Đỡ 1000 Đòn Đánh" +title = "Mới Tập Đỡ Đòn!" +description = "Đỡ 1000 Đòn Đánh" [advancement.challenge_block_5k] - title = "Đỡ Đòn Thật Vui!" - description = "Đỡ 5000 Đòn Đánh" +title = "Đỡ Đòn Thật Vui!" +description = "Đỡ 5000 Đòn Đánh" [advancement.challenge_block_50k] - title = "Đỡ Đòn Là Cuộc Đời Tôi!" - description = "Đỡ 50.000 Đòn Đánh" +title = "Đỡ Đòn Là Cuộc Đời Tôi!" +description = "Đỡ 50.000 Đòn Đánh" [advancement.challenge_block_500k] - title = "Đỡ Đòn Là Sứ Mệnh Của Tôi!" - description = "Đỡ 500.000 Đòn Đánh" +title = "Đỡ Đòn Là Sứ Mệnh Của Tôi!" +description = "Đỡ 500.000 Đòn Đánh" [advancement.challenge_block_5m] - title = "Die Hand Die Verletzt" - description = "Đỡ 5.000.000 Đòn Đánh" +title = "Die Hand Die Verletzt" +description = "Đỡ 5.000.000 Đòn Đánh" [advancement.challenge_brew_1k] - title = "Nhà Giả Kim Tập Sự!" - description = "Uống 1000 Bình Thuốc" +title = "Nhà Giả Kim Tập Sự!" +description = "Uống 1000 Bình Thuốc" [advancement.challenge_brew_5k] - title = "Nhà Giả Kim Trung Cấp!" - description = "Uống 5000 Bình Thuốc" +title = "Nhà Giả Kim Trung Cấp!" +description = "Uống 5000 Bình Thuốc" [advancement.challenge_brew_50k] - title = "Nhà Giả Kim Cao Cấp!" - description = "Uống 50.000 Bình Thuốc" +title = "Nhà Giả Kim Cao Cấp!" +description = "Uống 50.000 Bình Thuốc" [advancement.challenge_brew_500k] - title = "Bậc Thầy Giả Kim!" - description = "Uống 500.000 Bình Thuốc" +title = "Bậc Thầy Giả Kim!" +description = "Uống 500.000 Bình Thuốc" [advancement.challenge_brew_5m] - title = "Nhà Giả Kim Thuật" - description = "Uống 5.000.000 Bình Thuốc" +title = "Nhà Giả Kim Thuật" +description = "Uống 5.000.000 Bình Thuốc" [advancement.challenge_brewsplash_1k] - title = "Người Tạt Thuốc Tập Sự!" - description = "Tạt 1000 Bình Thuốc" +title = "Người Tạt Thuốc Tập Sự!" +description = "Tạt 1000 Bình Thuốc" [advancement.challenge_brewsplash_5k] - title = "Người Tạt Thuốc Trung Cấp!" - description = "Tạt 5000 Bình Thuốc" +title = "Người Tạt Thuốc Trung Cấp!" +description = "Tạt 5000 Bình Thuốc" [advancement.challenge_brewsplash_50k] - title = "Người Tạt Thuốc Cao Cấp!" - description = "Tạt 50.000 Bình Thuốc" +title = "Người Tạt Thuốc Cao Cấp!" +description = "Tạt 50.000 Bình Thuốc" [advancement.challenge_brewsplash_500k] - title = "Bậc Thầy Tạt Thuốc!" - description = "Tạt 500.000 Bình Thuốc" +title = "Bậc Thầy Tạt Thuốc!" +description = "Tạt 500.000 Bình Thuốc" [advancement.challenge_brewsplash_5m] - title = "Bậc Thầy Tạt Nước" - description = "Tạt 5.000.000 Bình Thuốc" +title = "Bậc Thầy Tạt Nước" +description = "Tạt 5.000.000 Bình Thuốc" [advancement.challenge_craft_1k] - title = "Thợ Thủ Công Khéo Tay!" - description = "Chế tạo 1000 Vật Phẩm" +title = "Thợ Thủ Công Khéo Tay!" +description = "Chế tạo 1000 Vật Phẩm" [advancement.challenge_craft_5k] - title = "Thợ Thủ Công Gắt Gỏng!" - description = "Chế tạo 5000 Vật Phẩm" +title = "Thợ Thủ Công Gắt Gỏng!" +description = "Chế tạo 5000 Vật Phẩm" [advancement.challenge_craft_50k] - title = "Thợ Thủ Công Cần Mẫn!" - description = "Chế tạo 50.000 Vật Phẩm" +title = "Thợ Thủ Công Cần Mẫn!" +description = "Chế tạo 50.000 Vật Phẩm" [advancement.challenge_craft_500k] - title = "Thợ Thủ Công Ầm Ĩ!" - description = "Chế tạo 500.000 Vật Phẩm" +title = "Thợ Thủ Công Ầm Ĩ!" +description = "Chế tạo 500.000 Vật Phẩm" [advancement.challenge_craft_5m] - title = "McCraftface Tai Họa" - description = "Chế tạo 5.000.000 Vật Phẩm" +title = "McCraftface Tai Họa" +description = "Chế tạo 5.000.000 Vật Phẩm" [advancement.challenge_enchant_1k] - title = "Phù Phép Sư Tập Sự!" - description = "Phù phép 1000 Vật Phẩm" +title = "Phù Phép Sư Tập Sự!" +description = "Phù phép 1000 Vật Phẩm" [advancement.challenge_enchant_5k] - title = "Phù Phép Sư Trung Cấp!" - description = "Phù phép 5000 Vật Phẩm" +title = "Phù Phép Sư Trung Cấp!" +description = "Phù phép 5000 Vật Phẩm" [advancement.challenge_enchant_50k] - title = "Phù Phép Sư Cao Cấp!" - description = "Phù phép 50.000 Vật Phẩm" +title = "Phù Phép Sư Cao Cấp!" +description = "Phù phép 50.000 Vật Phẩm" [advancement.challenge_enchant_500k] - title = "Bậc Thầy Phù Phép!" - description = "Phù phép 500.000 Vật Phẩm" +title = "Bậc Thầy Phù Phép!" +description = "Phù phép 500.000 Vật Phẩm" [advancement.challenge_enchant_5m] - title = "Phù Phép Sư Bí Ẩn" - description = "Phù phép 5.000.000 Vật Phẩm" +title = "Phù Phép Sư Bí Ẩn" +description = "Phù phép 5.000.000 Vật Phẩm" [advancement.challenge_excavate_1k] - title = "Thợ Đào Hào Hứng!" - description = "Đào 1000 Khối" +title = "Thợ Đào Hào Hứng!" +description = "Đào 1000 Khối" [advancement.challenge_excavate_5k] - title = "Thợ Đào Trung Cấp!" - description = "Đào 5000 Khối" +title = "Thợ Đào Trung Cấp!" +description = "Đào 5000 Khối" [advancement.challenge_excavate_50k] - title = "Thợ Đào Cao Cấp!" - description = "Đào 50.000 Khối" +title = "Thợ Đào Cao Cấp!" +description = "Đào 50.000 Khối" [advancement.challenge_excavate_500k] - title = "Bậc Thầy Đào Bới!" - description = "Đào 500.000 Khối" +title = "Bậc Thầy Đào Bới!" +description = "Đào 500.000 Khối" [advancement.challenge_excavate_5m] - title = "Thợ Đào Bí Ẩn" - description = "Đào 5.000.000 Khối" +title = "Thợ Đào Bí Ẩn" +description = "Đào 5.000.000 Khối" [advancement.horrible_person] - title = "Bạn Là Người Tệ Hại" - description = "Thật không thể tưởng tượng nổi" +title = "Bạn Là Người Tệ Hại" +description = "Thật không thể tưởng tượng nổi" [advancement.challenge_turtle_egg_smasher] - title = "Kẻ Đập Trứng Rùa!" - description = "Đập vỡ 100 quả trứng rùa" +title = "Kẻ Đập Trứng Rùa!" +description = "Đập vỡ 100 quả trứng rùa" [advancement.challenge_turtle_egg_annihilator] - title = "Kẻ Hủy Diệt Trứng Rùa!" - description = "Đập vỡ 500 quả trứng rùa" +title = "Kẻ Hủy Diệt Trứng Rùa!" +description = "Đập vỡ 500 quả trứng rùa" [advancement.challenge_novice_hunter] - title = "Thợ Săn Tập Sự!" - description = "Tiêu diệt 100 thực thể" +title = "Thợ Săn Tập Sự!" +description = "Tiêu diệt 100 thực thể" [advancement.challenge_intermediate_hunter] - title = "Thợ Săn Trung Cấp!" - description = "Tiêu diệt 500 thực thể" +title = "Thợ Săn Trung Cấp!" +description = "Tiêu diệt 500 thực thể" [advancement.challenge_advanced_hunter] - title = "Thợ Săn Cao Cấp!" - description = "Tiêu diệt 5000 thực thể" +title = "Thợ Săn Cao Cấp!" +description = "Tiêu diệt 5000 thực thể" [advancement.challenge_creeper_conqueror] - title = "Kẻ Chinh Phục Creeper!" - description = "Tiêu diệt 50 Creeper" +title = "Kẻ Chinh Phục Creeper!" +description = "Tiêu diệt 50 Creeper" [advancement.challenge_creeper_annihilator] - title = "Kẻ Hủy Diệt Creeper!" - description = "Tiêu diệt 200 Creeper" +title = "Kẻ Hủy Diệt Creeper!" +description = "Tiêu diệt 200 Creeper" [advancement.challenge_pickaxe_1k] - title = "Thợ Mỏ Tập Sự" - description = "Phá 1000 Khối" +title = "Thợ Mỏ Tập Sự" +description = "Phá 1000 Khối" [advancement.challenge_pickaxe_5k] - title = "Thợ Mỏ Lành Nghề" - description = "Phá 5000 Khối" +title = "Thợ Mỏ Lành Nghề" +description = "Phá 5000 Khối" [advancement.challenge_pickaxe_50k] - title = "Thợ Mỏ Chuyên Gia" - description = "Phá 50.000 Khối" +title = "Thợ Mỏ Chuyên Gia" +description = "Phá 50.000 Khối" [advancement.challenge_pickaxe_500k] - title = "Bậc Thầy Thợ Mỏ" - description = "Phá 500.000 Khối" +title = "Bậc Thầy Thợ Mỏ" +description = "Phá 500.000 Khối" [advancement.challenge_pickaxe_5m] - title = "Thợ Mỏ Huyền Thoại" - description = "Phá 5.000.000 Khối" +title = "Thợ Mỏ Huyền Thoại" +description = "Phá 5.000.000 Khối" [advancement.challenge_eat_100] - title = "Bao Nhiêu Thứ Để Ăn!" - description = "Ăn hơn 100 Vật Phẩm!" +title = "Bao Nhiêu Thứ Để Ăn!" +description = "Ăn hơn 100 Vật Phẩm!" [advancement.challenge_eat_1000] - title = "Cơn Đói Không Thể Dập Tắt!" - description = "Ăn hơn 1.000 Vật Phẩm!" +title = "Cơn Đói Không Thể Dập Tắt!" +description = "Ăn hơn 1.000 Vật Phẩm!" [advancement.challenge_eat_10000] - title = "CƠN ĐÓI VĨNH CỬU!" - description = "Ăn hơn 10.000 Vật Phẩm!" +title = "CƠN ĐÓI VĨNH CỬU!" +description = "Ăn hơn 10.000 Vật Phẩm!" [advancement.challenge_harvest_100] - title = "Mùa Thu Hoạch Đầy Đủ" - description = "Thu hoạch hơn 100 cây trồng!" +title = "Mùa Thu Hoạch Đầy Đủ" +description = "Thu hoạch hơn 100 cây trồng!" [advancement.challenge_harvest_1000] - title = "Mùa Thu Hoạch Vĩ Đại" - description = "Thu hoạch hơn 1.000 cây trồng!" +title = "Mùa Thu Hoạch Vĩ Đại" +description = "Thu hoạch hơn 1.000 cây trồng!" [advancement.challenge_swim_1nm] - title = "Tàu Ngầm Người!" - description = "Bơi 1 Hải Lý (1.852 khối)" +title = "Tàu Ngầm Người!" +description = "Bơi 1 Hải Lý (1.852 khối)" [advancement.challenge_sneak_1k] - title = "Đau Đầu Gối" - description = "Rón rén hơn một km (1.000 khối)" +title = "Đau Đầu Gối" +description = "Rón rén hơn một km (1.000 khối)" [advancement.challenge_sneak_5k] - title = "Kẻ Đi Trong Bóng Tối" - description = "Rón rén hơn 5.000 khối" +title = "Kẻ Đi Trong Bóng Tối" +description = "Rón rén hơn 5.000 khối" [advancement.challenge_sneak_20k] - title = "Bóng Ma" - description = "Rón rén hơn 20.000 khối" +title = "Bóng Ma" +description = "Rón rén hơn 20.000 khối" [advancement.challenge_swim_5k] - title = "Thợ Lặn Sâu" - description = "Bơi hơn 5.000 khối" +title = "Thợ Lặn Sâu" +description = "Bơi hơn 5.000 khối" [advancement.challenge_swim_20k] - title = "Người Được Poseidon Chọn" - description = "Bơi hơn 20.000 khối" +title = "Người Được Poseidon Chọn" +description = "Bơi hơn 20.000 khối" [advancement.challenge_sword_100] - title = "Giọt Máu Đầu Tiên" - description = "Chém 100 nhát bằng kiếm" +title = "Giọt Máu Đầu Tiên" +description = "Chém 100 nhát bằng kiếm" [advancement.challenge_sword_1k] - title = "Vũ Công Lưỡi Kiếm" - description = "Chém 1.000 nhát bằng kiếm" +title = "Vũ Công Lưỡi Kiếm" +description = "Chém 1.000 nhát bằng kiếm" [advancement.challenge_sword_10k] - title = "Ngàn Nhát Chém" - description = "Chém 10.000 nhát bằng kiếm" +title = "Ngàn Nhát Chém" +description = "Chém 10.000 nhát bằng kiếm" [advancement.challenge_unarmed_100] - title = "Tay Đấm Quán Rượu" - description = "Đấm 100 cú tay không" +title = "Tay Đấm Quán Rượu" +description = "Đấm 100 cú tay không" [advancement.challenge_unarmed_1k] - title = "Nắm Đấm Thép" - description = "Đấm 1.000 cú tay không" +title = "Nắm Đấm Thép" +description = "Đấm 1.000 cú tay không" [advancement.challenge_unarmed_10k] - title = "Một Cú Đấm" - description = "Đấm 10.000 cú tay không" +title = "Một Cú Đấm" +description = "Đấm 10.000 cú tay không" [advancement.challenge_trag_1k] - title = "Giá Máu" - description = "Nhận 1.000 sát thương" +title = "Giá Máu" +description = "Nhận 1.000 sát thương" [advancement.challenge_trag_10k] - title = "Thủy Triều Đỏ" - description = "Nhận 10.000 sát thương" +title = "Thủy Triều Đỏ" +description = "Nhận 10.000 sát thương" [advancement.challenge_trag_100k] - title = "Hóa Thân Của Đau Khổ" - description = "Nhận 100.000 sát thương" +title = "Hóa Thân Của Đau Khổ" +description = "Nhận 100.000 sát thương" [advancement.challenge_ranged_100] - title = "Tập Bắn" - description = "Bắn 100 đạn" +title = "Tập Bắn" +description = "Bắn 100 đạn" [advancement.challenge_ranged_1k] - title = "Mắt Đại Bàng" - description = "Bắn 1.000 đạn" +title = "Mắt Đại Bàng" +description = "Bắn 1.000 đạn" [advancement.challenge_ranged_10k] - title = "Bão Tên" - description = "Bắn 10.000 đạn" +title = "Bão Tên" +description = "Bắn 10.000 đạn" [advancement.challenge_chronos_1h] - title = "Tích Tắc" - description = "Chơi trực tuyến 1 giờ" +title = "Tích Tắc" +description = "Chơi trực tuyến 1 giờ" [advancement.challenge_chronos_24h] - title = "Cát Thời Gian" - description = "Chơi trực tuyến 24 giờ" +title = "Cát Thời Gian" +description = "Chơi trực tuyến 24 giờ" [advancement.challenge_chronos_168h] - title = "Vượt Thời Gian" - description = "Chơi trực tuyến 168 giờ (1 tuần)" +title = "Vượt Thời Gian" +description = "Chơi trực tuyến 168 giờ (1 tuần)" [advancement.challenge_nether_50] - title = "Người Gác Cổng Địa Ngục" - description = "Giết 50 sinh vật Nether" +title = "Người Gác Cổng Địa Ngục" +description = "Giết 50 sinh vật Nether" [advancement.challenge_nether_500] - title = "Lính Canh Vực Thẳm" - description = "Giết 500 sinh vật Nether" +title = "Lính Canh Vực Thẳm" +description = "Giết 500 sinh vật Nether" [advancement.challenge_nether_5k] - title = "Chúa Tể Nether" - description = "Giết 5.000 sinh vật Nether" +title = "Chúa Tể Nether" +description = "Giết 5.000 sinh vật Nether" [advancement.challenge_rift_50] - title = "Dị Thường Không Gian" - description = "Dịch chuyển 50 lần" +title = "Dị Thường Không Gian" +description = "Dịch chuyển 50 lần" [advancement.challenge_rift_500] - title = "Kẻ Đi Trong Hư Không" - description = "Dịch chuyển 500 lần" +title = "Kẻ Đi Trong Hư Không" +description = "Dịch chuyển 500 lần" [advancement.challenge_rift_5k] - title = "Giữa Các Thế Giới" - description = "Dịch chuyển 5.000 lần" +title = "Giữa Các Thế Giới" +description = "Dịch chuyển 5.000 lần" [advancement.challenge_taming_10] - title = "Người Thì Thầm Với Thú" - description = "Nhân giống 10 động vật" +title = "Người Thì Thầm Với Thú" +description = "Nhân giống 10 động vật" [advancement.challenge_taming_50] - title = "Thủ Lĩnh Bầy" - description = "Nhân giống 50 động vật" +title = "Thủ Lĩnh Bầy" +description = "Nhân giống 50 động vật" [advancement.challenge_taming_500] - title = "Chúa Tể Muông Thú" - description = "Nhân giống 500 động vật" +title = "Chúa Tể Muông Thú" +description = "Nhân giống 500 động vật" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "Ác Quỷ Tốc Độ" - description = "Chạy nước rút hơn 5 Kilômét (5,000 khối)" +title = "Ác Quỷ Tốc Độ" +description = "Chạy nước rút hơn 5 Kilômét (5,000 khối)" [advancement.challenge_sprint_dist_50k] - title = "Chân Sấm Sét" - description = "Chạy nước rút hơn 50 Kilômét (50,000 khối)" +title = "Chân Sấm Sét" +description = "Chạy nước rút hơn 50 Kilômét (50,000 khối)" [advancement.challenge_agility_swim_1k] - title = "Người Lướt Nước" - description = "Bơi hơn 1 Kilômét (1,000 khối)" +title = "Người Lướt Nước" +description = "Bơi hơn 1 Kilômét (1,000 khối)" [advancement.challenge_agility_swim_10k] - title = "Nhà Hàng Hải" - description = "Bơi hơn 10 Kilômét (10,000 khối)" +title = "Nhà Hàng Hải" +description = "Bơi hơn 10 Kilômét (10,000 khối)" [advancement.challenge_fly_1k] - title = "Vũ Công Bầu Trời" - description = "Bay hơn 1 Kilômét (1,000 khối)" +title = "Vũ Công Bầu Trời" +description = "Bay hơn 1 Kilômét (1,000 khối)" [advancement.challenge_fly_10k] - title = "Kỵ Sĩ Gió" - description = "Bay hơn 10 Kilômét (10,000 khối)" +title = "Kỵ Sĩ Gió" +description = "Bay hơn 10 Kilômét (10,000 khối)" [advancement.challenge_agility_sneak_500] - title = "Bước Chân Nhẹ" - description = "Lén lút hơn 500 khối" +title = "Bước Chân Nhẹ" +description = "Lén lút hơn 500 khối" [advancement.challenge_agility_sneak_5k] - title = "Bước Chân Ma" - description = "Lén lút hơn 5 Kilômét (5,000 khối)" +title = "Bước Chân Ma" +description = "Lén lút hơn 5 Kilômét (5,000 khối)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "Đội Phá Hủy" - description = "Phá 500 khối" +title = "Đội Phá Hủy" +description = "Phá 500 khối" [advancement.challenge_demolish_5k] - title = "Quả Cầu Phá Hủy" - description = "Phá 5,000 khối" +title = "Quả Cầu Phá Hủy" +description = "Phá 5,000 khối" [advancement.challenge_value_placed_10k] - title = "Thợ Xây Giá Trị" - description = "Đặt khối có giá trị 10,000" +title = "Thợ Xây Giá Trị" +description = "Đặt khối có giá trị 10,000" [advancement.challenge_value_placed_100k] - title = "Bậc Thầy Kiến Trúc" - description = "Đặt khối có giá trị 100,000" +title = "Bậc Thầy Kiến Trúc" +description = "Đặt khối có giá trị 100,000" [advancement.challenge_demolish_val_5k] - title = "Chuyên Gia Thu Hồi" - description = "Thu hồi 5,000 giá trị khối từ phá hủy" +title = "Chuyên Gia Thu Hồi" +description = "Thu hồi 5,000 giá trị khối từ phá hủy" [advancement.challenge_demolish_val_50k] - title = "Tháo Dỡ Hoàn Toàn" - description = "Thu hồi 50,000 giá trị khối từ phá hủy" +title = "Tháo Dỡ Hoàn Toàn" +description = "Thu hồi 50,000 giá trị khối từ phá hủy" [advancement.challenge_high_build_100] - title = "Thợ Xây Trên Cao" - description = "Đặt 100 khối trên Y=128" +title = "Thợ Xây Trên Cao" +description = "Đặt 100 khối trên Y=128" [advancement.challenge_high_build_1k] - title = "Kiến Trúc Sư Mây" - description = "Đặt 1,000 khối trên Y=128" +title = "Kiến Trúc Sư Mây" +description = "Đặt 1,000 khối trên Y=128" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "Người Vung Rìu" - description = "Vung rìu 500 lần" +title = "Người Vung Rìu" +description = "Vung rìu 500 lần" [advancement.challenge_axe_swing_5k] - title = "Cuồng Chiến Binh" - description = "Vung rìu 5,000 lần" +title = "Cuồng Chiến Binh" +description = "Vung rìu 5,000 lần" [advancement.challenge_axe_damage_1k] - title = "Người Chẻ" - description = "Gây 1,000 sát thương bằng rìu" +title = "Người Chẻ" +description = "Gây 1,000 sát thương bằng rìu" [advancement.challenge_axe_damage_10k] - title = "Rìu Đao Phủ" - description = "Gây 10,000 sát thương bằng rìu" +title = "Rìu Đao Phủ" +description = "Gây 10,000 sát thương bằng rìu" [advancement.challenge_axe_value_5k] - title = "Thương Nhân Gỗ" - description = "Thu hoạch gỗ trị giá 5,000" +title = "Thương Nhân Gỗ" +description = "Thu hoạch gỗ trị giá 5,000" [advancement.challenge_axe_value_50k] - title = "Trùm Khai Thác Gỗ" - description = "Thu hoạch gỗ trị giá 50,000" +title = "Trùm Khai Thác Gỗ" +description = "Thu hoạch gỗ trị giá 50,000" [advancement.challenge_leaves_500] - title = "Máy Thổi Lá" - description = "Dọn 500 khối lá bằng rìu" +title = "Máy Thổi Lá" +description = "Dọn 500 khối lá bằng rìu" [advancement.challenge_leaves_5k] - title = "Người Rụng Lá" - description = "Dọn 5,000 khối lá bằng rìu" +title = "Người Rụng Lá" +description = "Dọn 5,000 khối lá bằng rìu" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "Người Hấp Thụ Sát Thương" - description = "Chặn 1,000 sát thương bằng khiên" +title = "Người Hấp Thụ Sát Thương" +description = "Chặn 1,000 sát thương bằng khiên" [advancement.challenge_block_dmg_10k] - title = "Khiên Sống" - description = "Chặn 10,000 sát thương bằng khiên" +title = "Khiên Sống" +description = "Chặn 10,000 sát thương bằng khiên" [advancement.challenge_block_proj_100] - title = "Người Đỡ Tên" - description = "Chặn 100 đạn bằng khiên" +title = "Người Đỡ Tên" +description = "Chặn 100 đạn bằng khiên" [advancement.challenge_block_proj_1k] - title = "Khiên Chống Đạn" - description = "Chặn 1,000 đạn bằng khiên" +title = "Khiên Chống Đạn" +description = "Chặn 1,000 đạn bằng khiên" [advancement.challenge_block_melee_500] - title = "Bậc Thầy Đỡ Đòn" - description = "Chặn 500 đòn cận chiến bằng khiên" +title = "Bậc Thầy Đỡ Đòn" +description = "Chặn 500 đòn cận chiến bằng khiên" [advancement.challenge_block_melee_5k] - title = "Pháo Đài Sắt" - description = "Chặn 5,000 đòn cận chiến bằng khiên" +title = "Pháo Đài Sắt" +description = "Chặn 5,000 đòn cận chiến bằng khiên" [advancement.challenge_block_heavy_50] - title = "Xe Tăng" - description = "Chặn 50 đòn nặng (trên 5 sát thương)" +title = "Xe Tăng" +description = "Chặn 50 đòn nặng (trên 5 sát thương)" [advancement.challenge_block_heavy_500] - title = "Vật Bất Di" - description = "Chặn 500 đòn nặng (trên 5 sát thương)" +title = "Vật Bất Di" +description = "Chặn 500 đòn nặng (trên 5 sát thương)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "Xưởng Pha Chế" - description = "Đặt 10 giá pha chế" +title = "Xưởng Pha Chế" +description = "Đặt 10 giá pha chế" [advancement.challenge_brew_stands_50] - title = "Nhà Máy Thuốc" - description = "Đặt 50 giá pha chế" +title = "Nhà Máy Thuốc" +description = "Đặt 50 giá pha chế" [advancement.challenge_brew_strong_25] - title = "Thuốc Mạnh" - description = "Uống 25 thuốc nâng cấp" +title = "Thuốc Mạnh" +description = "Uống 25 thuốc nâng cấp" [advancement.challenge_brew_strong_250] - title = "Công Lực Tối Đa" - description = "Uống 250 thuốc nâng cấp" +title = "Công Lực Tối Đa" +description = "Uống 250 thuốc nâng cấp" [advancement.challenge_brew_splash_hits_50] - title = "Vùng Bắn Tung Tóe" - description = "Trúng 50 thực thể bằng thuốc bắn tung tóe" +title = "Vùng Bắn Tung Tóe" +description = "Trúng 50 thực thể bằng thuốc bắn tung tóe" [advancement.challenge_brew_splash_hits_500] - title = "Thầy Thuốc Dịch Bệnh" - description = "Trúng 500 thực thể bằng thuốc bắn tung tóe" +title = "Thầy Thuốc Dịch Bệnh" +description = "Trúng 500 thực thể bằng thuốc bắn tung tóe" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "Không Yên" - description = "Di chuyển 1 Kilômét khi đang hoạt động" +title = "Không Yên" +description = "Di chuyển 1 Kilômét khi đang hoạt động" [advancement.challenge_active_dist_10k] - title = "Người Dẫn Đường" - description = "Di chuyển 10 Kilômét khi đang hoạt động" +title = "Người Dẫn Đường" +description = "Di chuyển 10 Kilômét khi đang hoạt động" [advancement.challenge_active_dist_100k] - title = "Chuyển Động Vĩnh Cửu" - description = "Di chuyển 100 Kilômét khi đang hoạt động" +title = "Chuyển Động Vĩnh Cửu" +description = "Di chuyển 100 Kilômét khi đang hoạt động" [advancement.challenge_beds_10] - title = "Người Dậy Sớm" - description = "Ngủ trên giường 10 lần" +title = "Người Dậy Sớm" +description = "Ngủ trên giường 10 lần" [advancement.challenge_beds_100] - title = "Người Bỏ Qua Thời Gian" - description = "Ngủ trên giường 100 lần" +title = "Người Bỏ Qua Thời Gian" +description = "Ngủ trên giường 100 lần" [advancement.challenge_chronos_tp_50] - title = "Dịch Chuyển Thời Gian" - description = "Dịch chuyển tức thời 50 lần" +title = "Dịch Chuyển Thời Gian" +description = "Dịch chuyển tức thời 50 lần" [advancement.challenge_chronos_tp_500] - title = "Xoắn Thời Gian" - description = "Dịch chuyển tức thời 500 lần" +title = "Xoắn Thời Gian" +description = "Dịch chuyển tức thời 500 lần" [advancement.challenge_chronos_deaths_10] - title = "Phàm Nhân" - description = "Chết 10 lần" +title = "Phàm Nhân" +description = "Chết 10 lần" [advancement.challenge_chronos_deaths_100] - title = "Người Thách Thức Tử Thần" - description = "Chết 100 lần" +title = "Người Thách Thức Tử Thần" +description = "Chết 100 lần" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "Giá Trị Chế Tạo" - description = "Chế tạo vật phẩm có tổng giá trị 10,000" +title = "Giá Trị Chế Tạo" +description = "Chế tạo vật phẩm có tổng giá trị 10,000" [advancement.challenge_craft_value_100k] - title = "Nghệ Nhân" - description = "Chế tạo vật phẩm có tổng giá trị 100,000" +title = "Nghệ Nhân" +description = "Chế tạo vật phẩm có tổng giá trị 100,000" [advancement.challenge_craft_tools_25] - title = "Thợ Rèn Dụng Cụ" - description = "Chế tạo 25 dụng cụ" +title = "Thợ Rèn Dụng Cụ" +description = "Chế tạo 25 dụng cụ" [advancement.challenge_craft_tools_250] - title = "Bậc Thầy Lò Rèn" - description = "Chế tạo 250 dụng cụ" +title = "Bậc Thầy Lò Rèn" +description = "Chế tạo 250 dụng cụ" [advancement.challenge_craft_armor_25] - title = "Thợ Rèn Giáp" - description = "Chế tạo 25 mảnh giáp" +title = "Thợ Rèn Giáp" +description = "Chế tạo 25 mảnh giáp" [advancement.challenge_craft_armor_250] - title = "Bậc Thầy Giáp" - description = "Chế tạo 250 mảnh giáp" +title = "Bậc Thầy Giáp" +description = "Chế tạo 250 mảnh giáp" [advancement.challenge_craft_mass_25k] - title = "Sản Xuất Hàng Loạt" - description = "Chế tạo 25,000 vật phẩm" +title = "Sản Xuất Hàng Loạt" +description = "Chế tạo 25,000 vật phẩm" [advancement.challenge_craft_mass_250k] - title = "Cách Mạng Công Nghiệp" - description = "Chế tạo 250,000 vật phẩm" +title = "Cách Mạng Công Nghiệp" +description = "Chế tạo 250,000 vật phẩm" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "Nhà Sưu Tập" - description = "Khám phá 50 vật phẩm độc nhất" +title = "Nhà Sưu Tập" +description = "Khám phá 50 vật phẩm độc nhất" [advancement.challenge_discover_items_250] - title = "Người Lập Danh Mục" - description = "Khám phá 250 vật phẩm độc nhất" +title = "Người Lập Danh Mục" +description = "Khám phá 250 vật phẩm độc nhất" [advancement.challenge_discover_blocks_50] - title = "Nhà Khảo Sát" - description = "Khám phá 50 khối độc nhất" +title = "Nhà Khảo Sát" +description = "Khám phá 50 khối độc nhất" [advancement.challenge_discover_blocks_250] - title = "Nhà Địa Chất" - description = "Khám phá 250 khối độc nhất" +title = "Nhà Địa Chất" +description = "Khám phá 250 khối độc nhất" [advancement.challenge_discover_mobs_25] - title = "Người Quan Sát" - description = "Khám phá 25 sinh vật độc nhất" +title = "Người Quan Sát" +description = "Khám phá 25 sinh vật độc nhất" [advancement.challenge_discover_mobs_75] - title = "Nhà Tự Nhiên Học" - description = "Khám phá 75 sinh vật độc nhất" +title = "Nhà Tự Nhiên Học" +description = "Khám phá 75 sinh vật độc nhất" [advancement.challenge_discover_biomes_10] - title = "Kẻ Lang Thang" - description = "Khám phá 10 quần xã sinh vật độc nhất" +title = "Kẻ Lang Thang" +description = "Khám phá 10 quần xã sinh vật độc nhất" [advancement.challenge_discover_biomes_40] - title = "Nhà Du Hành Thế Giới" - description = "Khám phá 40 quần xã sinh vật độc nhất" +title = "Nhà Du Hành Thế Giới" +description = "Khám phá 40 quần xã sinh vật độc nhất" [advancement.challenge_discover_foods_10] - title = "Người Sành Ăn" - description = "Khám phá 10 thực phẩm độc nhất" +title = "Người Sành Ăn" +description = "Khám phá 10 thực phẩm độc nhất" [advancement.challenge_discover_foods_30] - title = "Bậc Thầy Ẩm Thực" - description = "Khám phá 30 thực phẩm độc nhất" +title = "Bậc Thầy Ẩm Thực" +description = "Khám phá 30 thực phẩm độc nhất" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "Người Dệt Sức Mạnh" - description = "Tích lũy 100 sức mạnh phù phép" +title = "Người Dệt Sức Mạnh" +description = "Tích lũy 100 sức mạnh phù phép" [advancement.challenge_enchant_power_1k] - title = "Bậc Thầy Huyền Thuật" - description = "Tích lũy 1,000 sức mạnh phù phép" +title = "Bậc Thầy Huyền Thuật" +description = "Tích lũy 1,000 sức mạnh phù phép" [advancement.challenge_enchant_levels_1k] - title = "Người Tiêu Cấp" - description = "Chi 1,000 cấp kinh nghiệm cho phù phép" +title = "Người Tiêu Cấp" +description = "Chi 1,000 cấp kinh nghiệm cho phù phép" [advancement.challenge_enchant_levels_10k] - title = "Hố Đen XP" - description = "Chi 10,000 cấp kinh nghiệm cho phù phép" +title = "Hố Đen XP" +description = "Chi 10,000 cấp kinh nghiệm cho phù phép" [advancement.challenge_enchant_high_25] - title = "Tay Chơi Lớn" - description = "Thực hiện 25 phù phép cấp tối đa" +title = "Tay Chơi Lớn" +description = "Thực hiện 25 phù phép cấp tối đa" [advancement.challenge_enchant_high_250] - title = "Phù Thủy Huyền Thoại" - description = "Thực hiện 250 phù phép cấp tối đa" +title = "Phù Thủy Huyền Thoại" +description = "Thực hiện 250 phù phép cấp tối đa" [advancement.challenge_enchant_total_500] - title = "Người Đốt Cấp" - description = "Chi tổng cộng 500 cấp cho tất cả phù phép" +title = "Người Đốt Cấp" +description = "Chi tổng cộng 500 cấp cho tất cả phù phép" [advancement.challenge_enchant_total_5k] - title = "Đầu Tư Huyền Bí" - description = "Chi tổng cộng 5,000 cấp cho tất cả phù phép" +title = "Đầu Tư Huyền Bí" +description = "Chi tổng cộng 5,000 cấp cho tất cả phù phép" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "Người Đào" - description = "Vung xẻng 500 lần" +title = "Người Đào" +description = "Vung xẻng 500 lần" [advancement.challenge_dig_swing_5k] - title = "Máy Xúc" - description = "Vung xẻng 5,000 lần" +title = "Máy Xúc" +description = "Vung xẻng 5,000 lần" [advancement.challenge_dig_damage_1k] - title = "Hiệp Sĩ Xẻng" - description = "Gây 1,000 sát thương bằng xẻng" +title = "Hiệp Sĩ Xẻng" +description = "Gây 1,000 sát thương bằng xẻng" [advancement.challenge_dig_damage_10k] - title = "Bậc Thầy Xẻng" - description = "Gây 10,000 sát thương bằng xẻng" +title = "Bậc Thầy Xẻng" +description = "Gây 10,000 sát thương bằng xẻng" [advancement.challenge_dig_value_5k] - title = "Thương Nhân Đất" - description = "Đào khối trị giá 5,000" +title = "Thương Nhân Đất" +description = "Đào khối trị giá 5,000" [advancement.challenge_dig_value_50k] - title = "Trùm Đất Đai" - description = "Đào khối trị giá 50,000" +title = "Trùm Đất Đai" +description = "Đào khối trị giá 50,000" [advancement.challenge_dig_gravel_500] - title = "Người Nghiền Sỏi" - description = "Đào 500 khối sỏi, cát hoặc đất sét" +title = "Người Nghiền Sỏi" +description = "Đào 500 khối sỏi, cát hoặc đất sét" [advancement.challenge_dig_gravel_5k] - title = "Người Sàng Cát" - description = "Đào 5,000 khối sỏi, cát hoặc đất sét" +title = "Người Sàng Cát" +description = "Đào 5,000 khối sỏi, cát hoặc đất sét" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "Người Gieo Hạt" - description = "Trồng 100 cây trồng" +title = "Người Gieo Hạt" +description = "Trồng 100 cây trồng" [advancement.challenge_plant_1k] - title = "Ngón Tay Xanh" - description = "Trồng 1,000 cây trồng" +title = "Ngón Tay Xanh" +description = "Trồng 1,000 cây trồng" [advancement.challenge_plant_5k] - title = "Trùm Nông Nghiệp" - description = "Trồng 5,000 cây trồng" +title = "Trùm Nông Nghiệp" +description = "Trồng 5,000 cây trồng" [advancement.challenge_compost_50] - title = "Người Tái Chế" - description = "Ủ phân 50 vật phẩm" +title = "Người Tái Chế" +description = "Ủ phân 50 vật phẩm" [advancement.challenge_compost_500] - title = "Người Bồi Dưỡng Đất" - description = "Ủ phân 500 vật phẩm" +title = "Người Bồi Dưỡng Đất" +description = "Ủ phân 500 vật phẩm" [advancement.challenge_shear_50] - title = "Người Xén Lông" - description = "Xén 50 thực thể" +title = "Người Xén Lông" +description = "Xén 50 thực thể" [advancement.challenge_shear_250] - title = "Chủ Đàn" - description = "Xén 250 thực thể" +title = "Chủ Đàn" +description = "Xén 250 thực thể" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "Sát Thủ" - description = "Tiêu diệt 500 sinh vật" +title = "Sát Thủ" +description = "Tiêu diệt 500 sinh vật" [advancement.challenge_kills_5k] - title = "Đao Phủ" - description = "Tiêu diệt 5,000 sinh vật" +title = "Đao Phủ" +description = "Tiêu diệt 5,000 sinh vật" [advancement.challenge_boss_1] - title = "Người Thách Đấu Boss" - description = "Tiêu diệt một boss" +title = "Người Thách Đấu Boss" +description = "Tiêu diệt một boss" [advancement.challenge_boss_10] - title = "Sát Thủ Huyền Thoại" - description = "Tiêu diệt 10 boss" +title = "Sát Thủ Huyền Thoại" +description = "Tiêu diệt 10 boss" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "Héo Tàn" - description = "Chịu đựng 500 sát thương héo tàn" +title = "Héo Tàn" +description = "Chịu đựng 500 sát thương héo tàn" [advancement.challenge_wither_dmg_5k] - title = "Người Sống Sót Dịch Bệnh" - description = "Chịu đựng 5,000 sát thương héo tàn" +title = "Người Sống Sót Dịch Bệnh" +description = "Chịu đựng 5,000 sát thương héo tàn" [advancement.challenge_wither_skel_25] - title = "Người Thu Xương" - description = "Tiêu diệt 25 bộ xương wither" +title = "Người Thu Xương" +description = "Tiêu diệt 25 bộ xương wither" [advancement.challenge_wither_skel_250] - title = "Tai Ương Bộ Xương" - description = "Tiêu diệt 250 bộ xương wither" +title = "Tai Ương Bộ Xương" +description = "Tiêu diệt 250 bộ xương wither" [advancement.challenge_wither_boss_1] - title = "Diệt Wither" - description = "Đánh bại Wither" +title = "Diệt Wither" +description = "Đánh bại Wither" [advancement.challenge_wither_boss_10] - title = "Bá Chủ Nether" - description = "Đánh bại Wither 10 lần" +title = "Bá Chủ Nether" +description = "Đánh bại Wither 10 lần" [advancement.challenge_roses_10] - title = "Người Làm Vườn Tử Thần" - description = "Phá 10 hoa wither" +title = "Người Làm Vườn Tử Thần" +description = "Phá 10 hoa wither" [advancement.challenge_roses_100] - title = "Người Thu Hoạch Dịch Bệnh" - description = "Phá 100 hoa wither" +title = "Người Thu Hoạch Dịch Bệnh" +description = "Phá 100 hoa wither" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "Cánh Tay Thợ Mỏ" - description = "Vung cuốc 500 lần" +title = "Cánh Tay Thợ Mỏ" +description = "Vung cuốc 500 lần" [advancement.challenge_pick_swing_5k] - title = "Người Đào Hầm" - description = "Vung cuốc 5,000 lần" +title = "Người Đào Hầm" +description = "Vung cuốc 5,000 lần" [advancement.challenge_pick_damage_1k] - title = "Chiến Binh Cuốc" - description = "Gây 1,000 sát thương bằng cuốc" +title = "Chiến Binh Cuốc" +description = "Gây 1,000 sát thương bằng cuốc" [advancement.challenge_pick_damage_10k] - title = "Cuốc Chiến Tranh" - description = "Gây 10,000 sát thương bằng cuốc" +title = "Cuốc Chiến Tranh" +description = "Gây 10,000 sát thương bằng cuốc" [advancement.challenge_pick_value_5k] - title = "Người Tìm Đá Quý" - description = "Đào khối trị giá 5,000" +title = "Người Tìm Đá Quý" +description = "Đào khối trị giá 5,000" [advancement.challenge_pick_value_50k] - title = "Trùm Quặng" - description = "Đào khối trị giá 50,000" +title = "Trùm Quặng" +description = "Đào khối trị giá 50,000" [advancement.challenge_pick_ores_500] - title = "Người Thăm Dò" - description = "Đào 500 khối quặng" +title = "Người Thăm Dò" +description = "Đào 500 khối quặng" [advancement.challenge_pick_ores_5k] - title = "Thợ Mỏ Bậc Thầy" - description = "Đào 5,000 khối quặng" +title = "Thợ Mỏ Bậc Thầy" +description = "Đào 5,000 khối quặng" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "Xạ Thủ Thiện Xạ" - description = "Gây 1,000 sát thương tầm xa" +title = "Xạ Thủ Thiện Xạ" +description = "Gây 1,000 sát thương tầm xa" [advancement.challenge_ranged_dmg_10k] - title = "Cung Thủ Chết Chóc" - description = "Gây 10,000 sát thương tầm xa" +title = "Cung Thủ Chết Chóc" +description = "Gây 10,000 sát thương tầm xa" [advancement.challenge_ranged_dist_5k] - title = "Tầm Xa" - description = "Bắn đạn với tổng khoảng cách 5,000 khối" +title = "Tầm Xa" +description = "Bắn đạn với tổng khoảng cách 5,000 khối" [advancement.challenge_ranged_dist_50k] - title = "Bắn Xa Cả Dặm" - description = "Bắn đạn với tổng khoảng cách 50,000 khối" +title = "Bắn Xa Cả Dặm" +description = "Bắn đạn với tổng khoảng cách 50,000 khối" [advancement.challenge_ranged_kills_50] - title = "Cung Thủ" - description = "Giết 50 sinh vật bằng vũ khí tầm xa" +title = "Cung Thủ" +description = "Giết 50 sinh vật bằng vũ khí tầm xa" [advancement.challenge_ranged_kills_500] - title = "Cung Thủ Bậc Thầy" - description = "Giết 500 sinh vật bằng vũ khí tầm xa" +title = "Cung Thủ Bậc Thầy" +description = "Giết 500 sinh vật bằng vũ khí tầm xa" [advancement.challenge_longshot_25] - title = "Xạ Thủ Bắn Tỉa" - description = "Bắn trúng 25 phát tầm xa (hơn 30 khối)" +title = "Xạ Thủ Bắn Tỉa" +description = "Bắn trúng 25 phát tầm xa (hơn 30 khối)" [advancement.challenge_longshot_250] - title = "Mắt Đại Bàng" - description = "Bắn trúng 250 phát tầm xa (hơn 30 khối)" +title = "Mắt Đại Bàng" +description = "Bắn trúng 250 phát tầm xa (hơn 30 khối)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "Người Ném Ngọc" - description = "Ném 50 ngọc ender" +title = "Người Ném Ngọc" +description = "Ném 50 ngọc ender" [advancement.challenge_rift_pearls_500] - title = "Nghiện Dịch Chuyển" - description = "Ném 500 ngọc ender" +title = "Nghiện Dịch Chuyển" +description = "Ném 500 ngọc ender" [advancement.challenge_rift_enderman_50] - title = "Thợ Săn Enderman" - description = "Tiêu diệt 50 enderman" +title = "Thợ Săn Enderman" +description = "Tiêu diệt 50 enderman" [advancement.challenge_rift_enderman_500] - title = "Kẻ Rình Rập Hư Không" - description = "Tiêu diệt 500 enderman" +title = "Kẻ Rình Rập Hư Không" +description = "Tiêu diệt 500 enderman" [advancement.challenge_rift_dragon_500] - title = "Chiến Binh Rồng" - description = "Gây 500 sát thương cho Rồng Ender" +title = "Chiến Binh Rồng" +description = "Gây 500 sát thương cho Rồng Ender" [advancement.challenge_rift_dragon_5k] - title = "Tai Ương Rồng" - description = "Gây 5,000 sát thương cho Rồng Ender" +title = "Tai Ương Rồng" +description = "Gây 5,000 sát thương cho Rồng Ender" [advancement.challenge_rift_crystal_10] - title = "Người Phá Pha Lê" - description = "Phá hủy 10 pha lê ender" +title = "Người Phá Pha Lê" +description = "Phá hủy 10 pha lê ender" [advancement.challenge_rift_crystal_100] - title = "Phá Hủy Cõi Cuối" - description = "Phá hủy 100 pha lê ender" +title = "Phá Hủy Cõi Cuối" +description = "Phá hủy 100 pha lê ender" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "Người Câu Cá" - description = "Câu 25 con cá" +title = "Người Câu Cá" +description = "Câu 25 con cá" [advancement.challenge_fish_250] - title = "Ngư Dân Bậc Thầy" - description = "Câu 250 con cá" +title = "Ngư Dân Bậc Thầy" +description = "Câu 250 con cá" [advancement.challenge_drowned_25] - title = "Thợ Săn Thây Ma Nước" - description = "Tiêu diệt 25 thây ma nước" +title = "Thợ Săn Thây Ma Nước" +description = "Tiêu diệt 25 thây ma nước" [advancement.challenge_drowned_250] - title = "Người Dọn Đại Dương" - description = "Tiêu diệt 250 thây ma nước" +title = "Người Dọn Đại Dương" +description = "Tiêu diệt 250 thây ma nước" [advancement.challenge_guardian_10] - title = "Diệt Vệ Binh" - description = "Tiêu diệt 10 vệ binh" +title = "Diệt Vệ Binh" +description = "Tiêu diệt 10 vệ binh" [advancement.challenge_guardian_100] - title = "Kẻ Cướp Đền" - description = "Tiêu diệt 100 vệ binh" +title = "Kẻ Cướp Đền" +description = "Tiêu diệt 100 vệ binh" [advancement.challenge_underwater_blocks_100] - title = "Thợ Mỏ Dưới Nước" - description = "Phá 100 khối dưới nước" +title = "Thợ Mỏ Dưới Nước" +description = "Phá 100 khối dưới nước" [advancement.challenge_underwater_blocks_1k] - title = "Kỹ Sư Biển" - description = "Phá 1,000 khối dưới nước" +title = "Kỹ Sư Biển" +description = "Phá 1,000 khối dưới nước" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "Kẻ Đâm Sau Lưng" - description = "Gây 500 sát thương khi lén lút" +title = "Kẻ Đâm Sau Lưng" +description = "Gây 500 sát thương khi lén lút" [advancement.challenge_stealth_dmg_5k] - title = "Sát Thủ Thầm Lặng" - description = "Gây 5,000 sát thương khi lén lút" +title = "Sát Thủ Thầm Lặng" +description = "Gây 5,000 sát thương khi lén lút" [advancement.challenge_stealth_kills_10] - title = "Thích Khách" - description = "Giết 10 sinh vật khi lén lút" +title = "Thích Khách" +description = "Giết 10 sinh vật khi lén lút" [advancement.challenge_stealth_kills_100] - title = "Thần Chết Bóng Tối" - description = "Giết 100 sinh vật khi lén lút" +title = "Thần Chết Bóng Tối" +description = "Giết 100 sinh vật khi lén lút" [advancement.challenge_stealth_time_1h] - title = "Kiên Nhẫn" - description = "Dành 1 giờ lén lút (3,600 giây)" +title = "Kiên Nhẫn" +description = "Dành 1 giờ lén lút (3,600 giây)" [advancement.challenge_stealth_time_10h] - title = "Bậc Thầy Bóng Tối" - description = "Dành 10 giờ lén lút (36,000 giây)" +title = "Bậc Thầy Bóng Tối" +description = "Dành 10 giờ lén lút (36,000 giây)" [advancement.challenge_stealth_arrows_50] - title = "Cung Thủ Thầm Lặng" - description = "Bắn 50 mũi tên khi lén lút" +title = "Cung Thủ Thầm Lặng" +description = "Bắn 50 mũi tên khi lén lút" [advancement.challenge_stealth_arrows_500] - title = "Cung Thủ Ma" - description = "Bắn 500 mũi tên khi lén lút" +title = "Cung Thủ Ma" +description = "Bắn 500 mũi tên khi lén lút" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "Học Viên Kiếm" - description = "Gây 1,000 sát thương bằng kiếm" +title = "Học Viên Kiếm" +description = "Gây 1,000 sát thương bằng kiếm" [advancement.challenge_sword_dmg_10k] - title = "Kiếm Sĩ" - description = "Gây 10,000 sát thương bằng kiếm" +title = "Kiếm Sĩ" +description = "Gây 10,000 sát thương bằng kiếm" [advancement.challenge_sword_kills_50] - title = "Đấu Sĩ" - description = "Giết 50 sinh vật bằng kiếm" +title = "Đấu Sĩ" +description = "Giết 50 sinh vật bằng kiếm" [advancement.challenge_sword_kills_500] - title = "Võ Sĩ Giác Đấu" - description = "Giết 500 sinh vật bằng kiếm" +title = "Võ Sĩ Giác Đấu" +description = "Giết 500 sinh vật bằng kiếm" [advancement.challenge_sword_crit_50] - title = "Đòn Chí Mạng" - description = "Đánh trúng 50 đòn chí mạng bằng kiếm" +title = "Đòn Chí Mạng" +description = "Đánh trúng 50 đòn chí mạng bằng kiếm" [advancement.challenge_sword_crit_500] - title = "Bậc Thầy Chính Xác" - description = "Đánh trúng 500 đòn chí mạng bằng kiếm" +title = "Bậc Thầy Chính Xác" +description = "Đánh trúng 500 đòn chí mạng bằng kiếm" [advancement.challenge_sword_heavy_25] - title = "Đòn Nặng" - description = "Đánh trúng 25 đòn nặng bằng kiếm (trên 8 sát thương)" +title = "Đòn Nặng" +description = "Đánh trúng 25 đòn nặng bằng kiếm (trên 8 sát thương)" [advancement.challenge_sword_heavy_250] - title = "Đòn Hủy Diệt" - description = "Đánh trúng 250 đòn nặng bằng kiếm (trên 8 sát thương)" +title = "Đòn Hủy Diệt" +description = "Đánh trúng 250 đòn nặng bằng kiếm (trên 8 sát thương)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "Huấn Luyện Thú" - description = "Thú cưng gây tổng cộng 500 sát thương" +title = "Huấn Luyện Thú" +description = "Thú cưng gây tổng cộng 500 sát thương" [advancement.challenge_pet_dmg_5k] - title = "Chủ Tướng Chiến Tranh" - description = "Thú cưng gây tổng cộng 5,000 sát thương" +title = "Chủ Tướng Chiến Tranh" +description = "Thú cưng gây tổng cộng 5,000 sát thương" [advancement.challenge_tamed_10] - title = "Bạn Của Động Vật" - description = "Thuần hóa 10 động vật" +title = "Bạn Của Động Vật" +description = "Thuần hóa 10 động vật" [advancement.challenge_tamed_100] - title = "Người Giữ Sở Thú" - description = "Thuần hóa 100 động vật" +title = "Người Giữ Sở Thú" +description = "Thuần hóa 100 động vật" [advancement.challenge_pet_kills_25] - title = "Chiến Thuật Bầy Đàn" - description = "Thú cưng tiêu diệt 25 sinh vật" +title = "Chiến Thuật Bầy Đàn" +description = "Thú cưng tiêu diệt 25 sinh vật" [advancement.challenge_pet_kills_250] - title = "Chỉ Huy Alpha" - description = "Thú cưng tiêu diệt 250 sinh vật" +title = "Chỉ Huy Alpha" +description = "Thú cưng tiêu diệt 250 sinh vật" [advancement.challenge_taming_2500] - title = "Chuyên Gia Nhân Giống" - description = "Nhân giống 2,500 động vật" +title = "Chuyên Gia Nhân Giống" +description = "Nhân giống 2,500 động vật" [advancement.challenge_taming_25k] - title = "Bậc Thầy Di Truyền" - description = "Nhân giống 25,000 động vật" +title = "Bậc Thầy Di Truyền" +description = "Nhân giống 25,000 động vật" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "Bao Cát" - description = "Nhận 500 đòn đánh" +title = "Bao Cát" +description = "Nhận 500 đòn đánh" [advancement.challenge_trag_hits_5k] - title = "Kẻ Ham Trừng Phạt" - description = "Nhận 5,000 đòn đánh" +title = "Kẻ Ham Trừng Phạt" +description = "Nhận 5,000 đòn đánh" [advancement.challenge_trag_deaths_10] - title = "Chín Mạng Sống" - description = "Chết 10 lần" +title = "Chín Mạng Sống" +description = "Chết 10 lần" [advancement.challenge_trag_deaths_100] - title = "Ác Mộng Tái Diễn" - description = "Chết 100 lần" +title = "Ác Mộng Tái Diễn" +description = "Chết 100 lần" [advancement.challenge_trag_fire_500] - title = "Nạn Nhân Lửa" - description = "Chịu đựng 500 sát thương lửa" +title = "Nạn Nhân Lửa" +description = "Chịu đựng 500 sát thương lửa" [advancement.challenge_trag_fire_5k] - title = "Phượng Hoàng" - description = "Chịu đựng 5,000 sát thương lửa" +title = "Phượng Hoàng" +description = "Chịu đựng 5,000 sát thương lửa" [advancement.challenge_trag_fall_500] - title = "Thử Trọng Lực" - description = "Chịu đựng 500 sát thương rơi" +title = "Thử Trọng Lực" +description = "Chịu đựng 500 sát thương rơi" [advancement.challenge_trag_fall_5k] - title = "Vận Tốc Giới Hạn" - description = "Chịu đựng 5,000 sát thương rơi" +title = "Vận Tốc Giới Hạn" +description = "Chịu đựng 5,000 sát thương rơi" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "Tay Đấm" - description = "Gây 1,000 sát thương bằng tay trần" +title = "Tay Đấm" +description = "Gây 1,000 sát thương bằng tay trần" [advancement.challenge_unarmed_dmg_10k] - title = "Võ Sư" - description = "Gây 10,000 sát thương bằng tay trần" +title = "Võ Sư" +description = "Gây 10,000 sát thương bằng tay trần" [advancement.challenge_unarmed_kills_25] - title = "Nắm Đấm Trần" - description = "Giết 25 sinh vật bằng tay trần" +title = "Nắm Đấm Trần" +description = "Giết 25 sinh vật bằng tay trần" [advancement.challenge_unarmed_kills_250] - title = "Nắm Đấm Huyền Thoại" - description = "Giết 250 sinh vật bằng tay trần" +title = "Nắm Đấm Huyền Thoại" +description = "Giết 250 sinh vật bằng tay trần" [advancement.challenge_unarmed_crit_25] - title = "Cú Đấm Chí Mạng" - description = "Đánh trúng 25 đòn chí mạng bằng tay trần" +title = "Cú Đấm Chí Mạng" +description = "Đánh trúng 25 đòn chí mạng bằng tay trần" [advancement.challenge_unarmed_crit_250] - title = "Nắm Đấm Chính Xác" - description = "Đánh trúng 250 đòn chí mạng bằng tay trần" +title = "Nắm Đấm Chính Xác" +description = "Đánh trúng 250 đòn chí mạng bằng tay trần" [advancement.challenge_unarmed_heavy_25] - title = "Cú Đấm Mạnh" - description = "Đánh trúng 25 đòn nặng bằng tay trần (trên 6 sát thương)" +title = "Cú Đấm Mạnh" +description = "Đánh trúng 25 đòn nặng bằng tay trần (trên 6 sát thương)" [advancement.challenge_unarmed_heavy_250] - title = "Vua Hạ Gục" - description = "Đánh trúng 250 đòn nặng bằng tay trần (trên 6 sát thương)" +title = "Vua Hạ Gục" +description = "Đánh trúng 250 đòn nặng bằng tay trần (trên 6 sát thương)" # items [items] [items.bound_ender_peral] - name = "Chìa Khóa Thánh Tích" - usage1 = "Shift + Chuột Trái để liên kết" - usage2 = "Chuột Phải để truy cập Kho Đồ đã liên kết" +name = "Chìa Khóa Thánh Tích" +usage1 = "Shift + Chuột Trái để liên kết" +usage2 = "Chuột Phải để truy cập Kho Đồ đã liên kết" [items.bound_eye_of_ender] - name = "Neo Thị Giác" - usage1 = "Chuột Phải để tiêu thụ và dịch chuyển đến vị trí đã liên kết" - usage2 = "Shift + Chuột Trái để liên kết với một khối" +name = "Neo Thị Giác" +usage1 = "Chuột Phải để tiêu thụ và dịch chuyển đến vị trí đã liên kết" +usage2 = "Shift + Chuột Trái để liên kết với một khối" [items.bound_redstone_torch] - name = "Điều Khiển Redstone Từ Xa" - usage1 = "Chuột Phải để tạo xung Redstone 1-Tick" - usage2 = "Shift + Chuột Trái vào Khối 'Mục Tiêu' để liên kết" +name = "Điều Khiển Redstone Từ Xa" +usage1 = "Chuột Phải để tạo xung Redstone 1-Tick" +usage2 = "Shift + Chuột Trái vào Khối 'Mục Tiêu' để liên kết" [items.bound_snowball] - name = "Bẫy Mạng Nhện!" - usage1 = "Ném để tạo bẫy mạng nhện tạm thời tại vị trí đó" +name = "Bẫy Mạng Nhện!" +usage1 = "Ném để tạo bẫy mạng nhện tạm thời tại vị trí đó" [items.chrono_time_bottle] - name = "Thời Gian Trong Chai" - usage1 = "Tự động tích trữ thời gian khi nằm trong túi đồ" - usage2 = "Chuột phải vào khối có bộ hẹn giờ hoặc động vật con để tiêu thụ thời gian đã lưu" - stored = "Thời Gian Tích Trữ" +name = "Thời Gian Trong Chai" +usage1 = "Tự động tích trữ thời gian khi nằm trong túi đồ" +usage2 = "Chuột phải vào khối có bộ hẹn giờ hoặc động vật con để tiêu thụ thời gian đã lưu" +stored = "Thời Gian Tích Trữ" [items.chrono_time_bomb] - name = "Bom Thời Gian" - usage1 = "Chuột phải để phóng tia thời gian tạo ra trường thời không" +name = "Bom Thời Gian" +usage1 = "Chuột phải để phóng tia thời gian tạo ra trường thời không" [items.elevator_block] - name = "Khối Thang Máy" - usage1 = "Nhảy để dịch chuyển lên" - usage2 = "Ngồi để dịch chuyển xuống" - usage3 = "Tối thiểu 2 khối trống giữa các thang máy" +name = "Khối Thang Máy" +usage1 = "Nhảy để dịch chuyển lên" +usage2 = "Ngồi để dịch chuyển xuống" +usage3 = "Tối thiểu 2 khối trống giữa các thang máy" # snippets [snippets] [snippets.gui] - level = "Cấp Độ" - knowledge = "kiến thức" - power_used = "Năng Lượng Đã Dùng" - not_learned = "Chưa Học" - xp = "XP đến" - welcome = "Chào mừng!" - welcome_back = "Chào mừng trở lại!" - xp_bonus_for_time = "XP cho" - max_ability_power = "Năng Lượng Kỹ Năng Tối Đa" - unlock_this_by_clicking = "Mở khóa bằng cách Chuột Phải: " - back = "Quay Lại" - unlearn_all = "Quên tất cả" - unlearned_all = "Đã quên tất cả" +level = "Cấp Độ" +knowledge = "kiến thức" +power_used = "Năng Lượng Đã Dùng" +not_learned = "Chưa Học" +xp = "XP đến" +welcome = "Chào mừng!" +welcome_back = "Chào mừng trở lại!" +xp_bonus_for_time = "XP cho" +max_ability_power = "Năng Lượng Kỹ Năng Tối Đa" +unlock_this_by_clicking = "Mở khóa bằng cách Chuột Phải: " +back = "Quay Lại" +unlearn_all = "Quên tất cả" +unlearned_all = "Đã quên tất cả" [snippets.adapt_menu] - may_not_unlearn = "BẠN KHÔNG THỂ QUÊN" - may_unlearn = "BẠN CÓ THỂ HỌC/QUÊN" - knowledge_cost = "Chi Phí Kiến Thức" - knowledge_available = "Kiến Thức Khả Dụng" - already_learned = "Đã Học Rồi" - unlearn_refund = "Bấm để Quên & Hoàn Lại" - no_refunds = "CHẾ ĐỘ KHÓ, KHÔNG HOÀN LẠI" - knowledge = "kiến thức" - click_learn = "Bấm để Học" - no_knowledge = "(Bạn chưa có Kiến Thức nào)" - you_only_have = "Bạn chỉ có" - how_to_level_up = "Nâng cấp kỹ năng để tăng năng lượng tối đa." - not_enough_power = "Không đủ năng lượng! Mỗi Cấp Độ Kỹ Năng tốn 1 năng lượng." - power = "năng lượng" - power_drain = "Tiêu Hao Năng Lượng" - learned = "Đã Học " - unlearned = "Đã Quên " - activator_block = "Giá Sách" +may_not_unlearn = "BẠN KHÔNG THỂ QUÊN" +may_unlearn = "BẠN CÓ THỂ HỌC/QUÊN" +knowledge_cost = "Chi Phí Kiến Thức" +knowledge_available = "Kiến Thức Khả Dụng" +already_learned = "Đã Học Rồi" +unlearn_refund = "Bấm để Quên & Hoàn Lại" +no_refunds = "CHẾ ĐỘ KHÓ, KHÔNG HOÀN LẠI" +knowledge = "kiến thức" +click_learn = "Bấm để Học" +no_knowledge = "(Bạn chưa có Kiến Thức nào)" +you_only_have = "Bạn chỉ có" +how_to_level_up = "Nâng cấp kỹ năng để tăng năng lượng tối đa." +not_enough_power = "Không đủ năng lượng! Mỗi Cấp Độ Kỹ Năng tốn 1 năng lượng." +power = "năng lượng" +power_drain = "Tiêu Hao Năng Lượng" +learned = "Đã Học " +unlearned = "Đã Quên " +activator_block = "Giá Sách" [snippets.knowledge_orb] - contains = "chứa" - knowledge = "kiến thức" - rightclick = "Chuột Phải" - togainknowledge = "để nhận kiến thức này" - knowledge_orb = "Quả Cầu Kiến Thức" +contains = "chứa" +knowledge = "kiến thức" +rightclick = "Chuột Phải" +togainknowledge = "để nhận kiến thức này" +knowledge_orb = "Quả Cầu Kiến Thức" [snippets.experience_orb] - contains = "chứa" - xp = "Kinh Nghiệm" - rightclick = "Chuột Phải" - togainxp = "để nhận kinh nghiệm này" - xporb = "Quả Cầu Kinh Nghiệm" +contains = "chứa" +xp = "Kinh Nghiệm" +rightclick = "Chuột Phải" +togainxp = "để nhận kinh nghiệm này" +xporb = "Quả Cầu Kinh Nghiệm" # skill [skill] [skill.agility] - name = "Nhanh Nhẹn" - icon = "⇉" - description = "Nhanh nhẹn là khả năng di chuyển nhanh và linh hoạt trước mọi chướng ngại vật." +name = "Nhanh Nhẹn" +icon = "⇉" +description = "Nhanh nhẹn là khả năng di chuyển nhanh và linh hoạt trước mọi chướng ngại vật." [skill.architect] - name = "Kiến Trúc Sư" - icon = "⬧" - description = "Công trình là nền tảng của thế giới. Thực tại nằm trong tay bạn, do bạn điều khiển." +name = "Kiến Trúc Sư" +icon = "⬧" +description = "Công trình là nền tảng của thế giới. Thực tại nằm trong tay bạn, do bạn điều khiển." [skill.axes] - name = "Rìu" - icon = "🪓" - description1 = "Sao phải chặt cây, khi bạn có thể chặt " - description2 = "mọi thứ" - description3 = "thay vào đó, cũng cùng kết quả thôi!" +name = "Rìu" +icon = "🪓" +description1 = "Sao phải chặt cây, khi bạn có thể chặt " +description2 = "mọi thứ" +description3 = "thay vào đó, cũng cùng kết quả thôi!" [skill.brewing] - name = "Pha Chế" - icon = "❦" - description = "Bọt Đôi, Bọt Ba, Bọt Bốn- Tôi vẫn không thể đổ thuốc này vào vạc được" +name = "Pha Chế" +icon = "❦" +description = "Bọt Đôi, Bọt Ba, Bọt Bốn- Tôi vẫn không thể đổ thuốc này vào vạc được" [skill.blocking] - name = "Phòng Thủ" - icon = "🛡" - description = "Gậy và đá không thể bẻ gãy xương bạn, Nhưng một chiếc khiên thì có thể." +name = "Phòng Thủ" +icon = "🛡" +description = "Gậy và đá không thể bẻ gãy xương bạn, Nhưng một chiếc khiên thì có thể." [skill.crafting] - name = "Chế Tạo" - icon = "⌂" - description = "Không còn mảnh nào để đặt, sao không tạo thêm một cái nữa?" +name = "Chế Tạo" +icon = "⌂" +description = "Không còn mảnh nào để đặt, sao không tạo thêm một cái nữa?" [skill.discovery] - name = "Khám Phá" - icon = "⚛" - description = "Khi nhận thức mở rộng, tâm trí bạn sẽ hé lộ những điều bạn chưa từng biết." +name = "Khám Phá" +icon = "⚛" +description = "Khi nhận thức mở rộng, tâm trí bạn sẽ hé lộ những điều bạn chưa từng biết." [skill.enchanting] - name = "Phù Phép" - icon = "♰" - description = "Bạn đang nói gì vậy? Lời tiên tri, ảo giác, mấy thứ mê tín nhảm nhí?" +name = "Phù Phép" +icon = "♰" +description = "Bạn đang nói gì vậy? Lời tiên tri, ảo giác, mấy thứ mê tín nhảm nhí?" [skill.excavation] - name = "Khai Quật" - icon = "ᛳ" - description = "Đào đào cái hố..." +name = "Khai Quật" +icon = "ᛳ" +description = "Đào đào cái hố..." [skill.herbalism] - name = "Thảo Dược Học" - icon = "⚘" - description = "Tôi không tìm thấy cây nào, nhưng tôi tìm được vài hạt giống và- đó có phải là... Cỏ dại không?" +name = "Thảo Dược Học" +icon = "⚘" +description = "Tôi không tìm thấy cây nào, nhưng tôi tìm được vài hạt giống và- đó có phải là... Cỏ dại không?" [skill.hunter] - name = "Thợ Săn" - icon = "☠" - description = "Săn bắn là về hành trình chứ không phải kết quả." +name = "Thợ Săn" +icon = "☠" +description = "Săn bắn là về hành trình chứ không phải kết quả." [skill.nether] - name = "Địa Ngục" - icon = "₪" - description = "Từ tận sâu thẳm của Địa Ngục." +name = "Địa Ngục" +icon = "₪" +description = "Từ tận sâu thẳm của Địa Ngục." [skill.pickaxe] - name = "Cuốc Chim" - icon = "⛏" - description = "Người lùn mới là thợ mỏ, nhưng tôi cũng học được đôi điều. TÔI LÀ NGƯỜI THỤY ĐIỂN" +name = "Cuốc Chim" +icon = "⛏" +description = "Người lùn mới là thợ mỏ, nhưng tôi cũng học được đôi điều. TÔI LÀ NGƯỜI THỤY ĐIỂN" [skill.ranged] - name = "Tầm Xa" - icon = "🏹" - description = "Khoảng cách là chìa khóa chiến thắng, và là chìa khóa sinh tồn." +name = "Tầm Xa" +icon = "🏹" +description = "Khoảng cách là chìa khóa chiến thắng, và là chìa khóa sinh tồn." [skill.rift] - name = "Khe Nứt" - icon = "❍" - description = "Khe Nứt là một sức mạnh khắc nghiệt, nhưng bạn đã thuần phục được nó." +name = "Khe Nứt" +icon = "❍" +description = "Khe Nứt là một sức mạnh khắc nghiệt, nhưng bạn đã thuần phục được nó." [skill.seaborne] - name = "Hải Sinh" - icon = "🎣" - description = "Với kỹ năng này, bạn có thể điều khiển những điều kỳ diệu của đại dương." +name = "Hải Sinh" +icon = "🎣" +description = "Với kỹ năng này, bạn có thể điều khiển những điều kỳ diệu của đại dương." [skill.stealth] - name = "Tàng Hình" - icon = "☯" - description = "Nghệ thuật của sự vô hình. Bước đi trong bóng tối." +name = "Tàng Hình" +icon = "☯" +description = "Nghệ thuật của sự vô hình. Bước đi trong bóng tối." [skill.swords] - name = "Kiếm" - icon = "⚔" - description = "Bằng sức mạnh của GreyStone!" +name = "Kiếm" +icon = "⚔" +description = "Bằng sức mạnh của GreyStone!" [skill.taming] - name = "Thuần Hóa" - icon = "♥" - description = "Những con vẹt và đàn ong... còn bạn thì sao?" +name = "Thuần Hóa" +icon = "♥" +description = "Những con vẹt và đàn ong... còn bạn thì sao?" [skill.tragoul] - name = "TragOul" - icon = "🗡" - description = "Máu chảy trong huyết quản của vũ trụ. Bị siết chặt bởi đôi tay bạn." +name = "TragOul" +icon = "🗡" +description = "Máu chảy trong huyết quản của vũ trụ. Bị siết chặt bởi đôi tay bạn." [skill.chronos] - name = "Chronos" - icon = "🕒" - description = "Vặn Đồng Hồ của vũ trụ, trải nghiệm dòng chảy. Đập vỡ đồng hồ, trở thành nó." +name = "Chronos" +icon = "🕒" +description = "Vặn Đồng Hồ của vũ trụ, trải nghiệm dòng chảy. Đập vỡ đồng hồ, trở thành nó." [skill.unarmed] - name = "Tay Không" - icon = "»" - description = "Không vũ khí không có nghĩa là không có sức mạnh." +name = "Tay Không" +icon = "»" +description = "Không vũ khí không có nghĩa là không có sức mạnh." # agility [agility] [agility.armor_up] - name = "Tăng Giáp" - description = "Nhận thêm giáp khi bạn chạy nước rút càng lâu!" - lore1 = "Giáp Tối Đa" - lore2 = "Thời Gian Tăng Giáp" - lore = ["Giáp Tối Đa", "Thời Gian Tăng Giáp"] +name = "Tăng Giáp" +description = "Nhận thêm giáp khi bạn chạy nước rút càng lâu!" +lore1 = "Giáp Tối Đa" +lore2 = "Thời Gian Tăng Giáp" +lore = ["Giáp Tối Đa", "Thời Gian Tăng Giáp"] [agility.ladder_slide] - name = "Trượt Thang" - description = "Leo và trượt thang nhanh hơn nhiều theo cả hai hướng." - lore1 = "Hệ số tốc độ leo thang" - lore2 = "Tốc độ trượt xuống nhanh" - lore = ["Hệ số tốc độ leo thang", "Tốc độ trượt xuống nhanh"] +name = "Trượt Thang" +description = "Leo và trượt thang nhanh hơn nhiều theo cả hai hướng." +lore1 = "Hệ số tốc độ leo thang" +lore2 = "Tốc độ trượt xuống nhanh" +lore = ["Hệ số tốc độ leo thang", "Tốc độ trượt xuống nhanh"] [agility.super_jump] - name = "Siêu Nhảy" - description = "Lợi Thế Chiều Cao Vượt Trội." - lore1 = "Chiều Cao Nhảy Tối Đa" - lore2 = "Ngồi + Nhảy để Siêu Nhảy!" - lore = ["Chiều Cao Nhảy Tối Đa", "Ngồi + Nhảy để Siêu Nhảy!"] +name = "Siêu Nhảy" +description = "Lợi Thế Chiều Cao Vượt Trội." +lore1 = "Chiều Cao Nhảy Tối Đa" +lore2 = "Ngồi + Nhảy để Siêu Nhảy!" +lore = ["Chiều Cao Nhảy Tối Đa", "Ngồi + Nhảy để Siêu Nhảy!"] [agility.wall_jump] - name = "Nhảy Tường" - description = "Giữ shift khi đang lơ lửng sát tường để bám tường & nhảy!" - lore1 = "Số Lần Nhảy Tối Đa" - lore2 = "Chiều Cao Nhảy" - lore = ["Số Lần Nhảy Tối Đa", "Chiều Cao Nhảy"] +name = "Nhảy Tường" +description = "Giữ shift khi đang lơ lửng sát tường để bám tường & nhảy!" +lore1 = "Số Lần Nhảy Tối Đa" +lore2 = "Chiều Cao Nhảy" +lore = ["Số Lần Nhảy Tối Đa", "Chiều Cao Nhảy"] [agility.wind_up] - name = "Tăng Tốc" - description = "Chạy nhanh hơn khi bạn chạy nước rút càng lâu!" - lore1 = "Tốc Độ Tối Đa" - lore2 = "Thời Gian Tăng Tốc" - lore = ["Tốc Độ Tối Đa", "Thời Gian Tăng Tốc"] +name = "Tăng Tốc" +description = "Chạy nhanh hơn khi bạn chạy nước rút càng lâu!" +lore1 = "Tốc Độ Tối Đa" +lore2 = "Thời Gian Tăng Tốc" +lore = ["Tốc Độ Tối Đa", "Thời Gian Tăng Tốc"] # architect [architect] [architect.elevator] - name = "Thang Máy" - description = "Cho phép bạn xây thang máy để dịch chuyển theo chiều dọc nhanh chóng!" - lore1 = "Mở khóa công thức thang máy: X=LEN, Y=NGỌC TRAI ENDER" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["Mở khóa công thức thang máy: X=LEN, Y=NGỌC TRAI ENDER", "XXX", "XYX", "XXX"] +name = "Thang Máy" +description = "Cho phép bạn xây thang máy để dịch chuyển theo chiều dọc nhanh chóng!" +lore1 = "Mở khóa công thức thang máy: X=LEN, Y=NGỌC TRAI ENDER" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["Mở khóa công thức thang máy: X=LEN, Y=NGỌC TRAI ENDER", "XXX", "XYX", "XXX"] [architect.foundation] - name = "Nền Tảng Ma Thuật" - description = "Cho phép bạn ngồi và đặt một nền tảng tạm thời bên dưới bạn!" - lore1 = "Tạo ra một cách kỳ diệu: " - lore2 = "Khối bên dưới bạn!" - lore = ["Tạo ra một cách kỳ diệu: ", "Khối bên dưới bạn!"] +name = "Nền Tảng Ma Thuật" +description = "Cho phép bạn ngồi và đặt một nền tảng tạm thời bên dưới bạn!" +lore1 = "Tạo ra một cách kỳ diệu: " +lore2 = "Khối bên dưới bạn!" +lore = ["Tạo ra một cách kỳ diệu: ", "Khối bên dưới bạn!"] [architect.glass] - name = "Cảm Ứng Lụa Kính" - description = "Cho phép bạn ngăn mất khối kính khi bạn đập chúng bằng tay không!" - lore1 = "Tay bạn có được cảm ứng lụa cho Kính" - lore = ["Tay bạn có được cảm ứng lụa cho Kính"] +name = "Cảm Ứng Lụa Kính" +description = "Cho phép bạn ngăn mất khối kính khi bạn đập chúng bằng tay không!" +lore1 = "Tay bạn có được cảm ứng lụa cho Kính" +lore = ["Tay bạn có được cảm ứng lụa cho Kính"] [architect.wireless_redstone] - name = "Điều Khiển Redstone Từ Xa" - description = "Cho phép bạn dùng đuốc redstone để bật/tắt redstone từ xa!" - lore1 = "Mục Tiêu + Đuốc Redstone + Ngọc Trai Ender = 1 Điều Khiển Redstone Từ Xa" - lore = ["Mục Tiêu + Đuốc Redstone + Ngọc Trai Ender = 1 Điều Khiển Redstone Từ Xa"] +name = "Điều Khiển Redstone Từ Xa" +description = "Cho phép bạn dùng đuốc redstone để bật/tắt redstone từ xa!" +lore1 = "Mục Tiêu + Đuốc Redstone + Ngọc Trai Ender = 1 Điều Khiển Redstone Từ Xa" +lore = ["Mục Tiêu + Đuốc Redstone + Ngọc Trai Ender = 1 Điều Khiển Redstone Từ Xa"] [architect.placement] - name = "Gậy Xây Dựng" - description = "Cho phép bạn đặt nhiều khối cùng lúc - ngồi, cầm khối trùng với khối bạn đang nhìn và đặt! Lưu ý, bạn có thể cần di chuyển chút ít để kích hoạt vùng giới hạn!" - lore1 = "Bạn cần" - lore2 = "khối trong tay để đặt" - lore3 = "Gậy Xây Dựng Vật Liệu" - lore = ["Bạn cần", "khối trong tay để đặt", "Gậy Xây Dựng Vật Liệu"] +name = "Gậy Xây Dựng" +description = "Cho phép bạn đặt nhiều khối cùng lúc - ngồi, cầm khối trùng với khối bạn đang nhìn và đặt! Lưu ý, bạn có thể cần di chuyển chút ít để kích hoạt vùng giới hạn!" +lore1 = "Bạn cần" +lore2 = "khối trong tay để đặt" +lore3 = "Gậy Xây Dựng Vật Liệu" +lore = ["Bạn cần", "khối trong tay để đặt", "Gậy Xây Dựng Vật Liệu"] # axe [axe] [axe.chop] - name = "Chặt Rìu" - description = "Chặt cây bằng cách chuột phải vào khúc gỗ gốc!" - lore1 = "Khối Mỗi Lần Chặt" - lore2 = "Thời Gian Hồi Chặt" - lore3 = "Hao Mòn Công Cụ" - lore = ["Khối Mỗi Lần Chặt", "Thời Gian Hồi Chặt", "Hao Mòn Công Cụ"] +name = "Chặt Rìu" +description = "Chặt cây bằng cách chuột phải vào khúc gỗ gốc!" +lore1 = "Khối Mỗi Lần Chặt" +lore2 = "Thời Gian Hồi Chặt" +lore3 = "Hao Mòn Công Cụ" +lore = ["Khối Mỗi Lần Chặt", "Thời Gian Hồi Chặt", "Hao Mòn Công Cụ"] [axe.log_swap] - name = "Đổi Gỗ Của Lucy" - description = "Đổi loại gỗ trong Bàn Chế Tạo!" - lore1 = "8 Gỗ bất kỳ + 1 cây non = 8 gỗ cùng loại cây non" - lore = ["8 Gỗ bất kỳ + 1 cây non = 8 gỗ cùng loại cây non"] +name = "Đổi Gỗ Của Lucy" +description = "Đổi loại gỗ trong Bàn Chế Tạo!" +lore1 = "8 Gỗ bất kỳ + 1 cây non = 8 gỗ cùng loại cây non" +lore = ["8 Gỗ bất kỳ + 1 cây non = 8 gỗ cùng loại cây non"] [axe.drop_to_inventory] - name = "Rìu Nhặt Vào Túi Đồ" +name = "Rìu Nhặt Vào Túi Đồ" [axe.ground_smash] - name = "Rìu Đập Đất" - description = "Nhảy, rồi ngồi và đập tan tất cả kẻ thù gần đó." - lore1 = "Sát Thương" - lore2 = "Bán Kính Khối" - lore3 = "Lực Đẩy" - lore4 = "Thời Gian Hồi Đập" - lore = ["Sát Thương", "Bán Kính Khối", "Lực Đẩy", "Thời Gian Hồi Đập"] +name = "Rìu Đập Đất" +description = "Nhảy, rồi ngồi và đập tan tất cả kẻ thù gần đó." +lore1 = "Sát Thương" +lore2 = "Bán Kính Khối" +lore3 = "Lực Đẩy" +lore4 = "Thời Gian Hồi Đập" +lore = ["Sát Thương", "Bán Kính Khối", "Lực Đẩy", "Thời Gian Hồi Đập"] [axe.leaf_miner] - name = "Phá Lá Hàng Loạt" - description = "Cho phép bạn phá lá cây hàng loạt cùng lúc!" - lore1 = "Ngồi, và đào LÁ" - lore2 = "phạm vi phá lá" - lore3 = "Bạn sẽ không nhận được vật phẩm rơi từ lá (Chống Lợi Dụng)" - lore = ["Ngồi, và đào LÁ", "phạm vi phá lá", "Bạn sẽ không nhận được vật phẩm rơi từ lá (Chống Lợi Dụng)"] +name = "Phá Lá Hàng Loạt" +description = "Cho phép bạn phá lá cây hàng loạt cùng lúc!" +lore1 = "Ngồi, và đào LÁ" +lore2 = "phạm vi phá lá" +lore3 = "Bạn sẽ không nhận được vật phẩm rơi từ lá (Chống Lợi Dụng)" +lore = ["Ngồi, và đào LÁ", "phạm vi phá lá", "Bạn sẽ không nhận được vật phẩm rơi từ lá (Chống Lợi Dụng)"] [axe.wood_miner] - name = "Phá Gỗ Hàng Loạt" - description = "Cho phép bạn phá gỗ hàng loạt cùng lúc!" - lore1 = "Ngồi, và đào GỖ/THÂN CÂY (Không phải Ván)" - lore2 = "phạm vi phá gỗ" - lore3 = "Hoạt động với Nhặt Vào Túi Đồ" - lore = ["Ngồi, và đào GỖ/THÂN CÂY (Không phải Ván)", "phạm vi phá gỗ", "Hoạt động với Nhặt Vào Túi Đồ"] +name = "Phá Gỗ Hàng Loạt" +description = "Cho phép bạn phá gỗ hàng loạt cùng lúc!" +lore1 = "Ngồi, và đào GỖ/THÂN CÂY (Không phải Ván)" +lore2 = "phạm vi phá gỗ" +lore3 = "Hoạt động với Nhặt Vào Túi Đồ" +lore = ["Ngồi, và đào GỖ/THÂN CÂY (Không phải Ván)", "phạm vi phá gỗ", "Hoạt động với Nhặt Vào Túi Đồ"] # brewing [brewing] [brewing.lingering] - name = "Thuốc Kéo Dài" - description = "Thuốc pha chế có tác dụng lâu hơn!" - lore1 = "Thời Lượng" - lore2 = "Thời Lượng" - lore = ["Thời Lượng", "Thời Lượng"] +name = "Thuốc Kéo Dài" +description = "Thuốc pha chế có tác dụng lâu hơn!" +lore1 = "Thời Lượng" +lore2 = "Thời Lượng" +lore = ["Thời Lượng", "Thời Lượng"] [brewing.super_heated] - name = "Thuốc Siêu Nóng" - description = "Giá pha chế hoạt động nhanh hơn khi càng nóng." - lore1 = "Mỗi Khối Lửa Chạm Vào" - lore2 = "Mỗi Khối Dung Nham Chạm Vào" - lore = ["Mỗi Khối Lửa Chạm Vào", "Mỗi Khối Dung Nham Chạm Vào"] +name = "Thuốc Siêu Nóng" +description = "Giá pha chế hoạt động nhanh hơn khi càng nóng." +lore1 = "Mỗi Khối Lửa Chạm Vào" +lore2 = "Mỗi Khối Dung Nham Chạm Vào" +lore = ["Mỗi Khối Lửa Chạm Vào", "Mỗi Khối Dung Nham Chạm Vào"] [brewing.darkness] - name = "Bóng Tối Đóng Chai" - description = "Không chắc sao bạn cần cái này, nhưng đây nè!" - lore1 = "Thuốc Nhìn Đêm + Bê Tông Đen = Thuốc Bóng Tối (30 giây)" - lore2 = "Lưu ý rằng thuốc này ngăn người dùng chạy nước rút!" - lore = ["Thuốc Nhìn Đêm + Bê Tông Đen = Thuốc Bóng Tối (30 giây)", "Lưu ý rằng thuốc này ngăn người dùng chạy nước rút!"] +name = "Bóng Tối Đóng Chai" +description = "Không chắc sao bạn cần cái này, nhưng đây nè!" +lore1 = "Thuốc Nhìn Đêm + Bê Tông Đen = Thuốc Bóng Tối (30 giây)" +lore2 = "Lưu ý rằng thuốc này ngăn người dùng chạy nước rút!" +lore = ["Thuốc Nhìn Đêm + Bê Tông Đen = Thuốc Bóng Tối (30 giây)", "Lưu ý rằng thuốc này ngăn người dùng chạy nước rút!"] [brewing.haste] - name = "Tốc Độ Đóng Chai" - description = "Khi Hiệu Suất là chưa đủ" - lore1 = "Thuốc Tốc Độ + Mảnh Thạch Anh Tím = Thuốc Haste (60 giây)" - lore2 = "Thuốc Tốc Độ + Khối Thạch Anh Tím = Thuốc Haste-2 (30 giây)" - lore = ["Thuốc Tốc Độ + Mảnh Thạch Anh Tím = Thuốc Haste (60 giây)", "Thuốc Tốc Độ + Khối Thạch Anh Tím = Thuốc Haste-2 (30 giây)"] +name = "Tốc Độ Đóng Chai" +description = "Khi Hiệu Suất là chưa đủ" +lore1 = "Thuốc Tốc Độ + Mảnh Thạch Anh Tím = Thuốc Haste (60 giây)" +lore2 = "Thuốc Tốc Độ + Khối Thạch Anh Tím = Thuốc Haste-2 (30 giây)" +lore = ["Thuốc Tốc Độ + Mảnh Thạch Anh Tím = Thuốc Haste (60 giây)", "Thuốc Tốc Độ + Khối Thạch Anh Tím = Thuốc Haste-2 (30 giây)"] [brewing.absorption] - name = "Hấp Thụ Đóng Chai" - description = "Làm cứng cơ thể!" - lore1 = "Hồi Máu Tức Thì + Thạch Anh = Thuốc Hấp Thụ (60 giây)" - lore2 = "Hồi Máu Tức Thì + Khối Thạch Anh = Thuốc Hấp Thụ-2 (30 giây)" - lore = ["Hồi Máu Tức Thì + Thạch Anh = Thuốc Hấp Thụ (60 giây)", "Hồi Máu Tức Thì + Khối Thạch Anh = Thuốc Hấp Thụ-2 (30 giây)"] +name = "Hấp Thụ Đóng Chai" +description = "Làm cứng cơ thể!" +lore1 = "Hồi Máu Tức Thì + Thạch Anh = Thuốc Hấp Thụ (60 giây)" +lore2 = "Hồi Máu Tức Thì + Khối Thạch Anh = Thuốc Hấp Thụ-2 (30 giây)" +lore = ["Hồi Máu Tức Thì + Thạch Anh = Thuốc Hấp Thụ (60 giây)", "Hồi Máu Tức Thì + Khối Thạch Anh = Thuốc Hấp Thụ-2 (30 giây)"] [brewing.fatigue] - name = "Mệt Mỏi Đóng Chai" - description = "Làm suy yếu cơ thể!" - lore1 = "Thuốc Yếu Đuối + Bóng Slime = Thuốc Mệt Mỏi (30 giây)" - lore2 = "Thuốc Yếu Đuối + Khối Slime = Thuốc Mệt Mỏi-2 (15 giây)" - lore = ["Thuốc Yếu Đuối + Bóng Slime = Thuốc Mệt Mỏi (30 giây)", "Thuốc Yếu Đuối + Khối Slime = Thuốc Mệt Mỏi-2 (15 giây)"] +name = "Mệt Mỏi Đóng Chai" +description = "Làm suy yếu cơ thể!" +lore1 = "Thuốc Yếu Đuối + Bóng Slime = Thuốc Mệt Mỏi (30 giây)" +lore2 = "Thuốc Yếu Đuối + Khối Slime = Thuốc Mệt Mỏi-2 (15 giây)" +lore = ["Thuốc Yếu Đuối + Bóng Slime = Thuốc Mệt Mỏi (30 giây)", "Thuốc Yếu Đuối + Khối Slime = Thuốc Mệt Mỏi-2 (15 giây)"] [brewing.hunger] - name = "Đói Đóng Chai" - description = "Cho kẻ ăn không biết no!" - lore1 = "Thuốc Vụng Về + Thịt Thối = Thuốc Đói (30 giây)" - lore2 = "Thuốc Yếu Đuối + Thịt Thối = Thuốc Đói-3 (15 giây)" - lore = ["Thuốc Vụng Về + Thịt Thối = Thuốc Đói (30 giây)", "Thuốc Yếu Đuối + Thịt Thối = Thuốc Đói-3 (15 giây)"] +name = "Đói Đóng Chai" +description = "Cho kẻ ăn không biết no!" +lore1 = "Thuốc Vụng Về + Thịt Thối = Thuốc Đói (30 giây)" +lore2 = "Thuốc Yếu Đuối + Thịt Thối = Thuốc Đói-3 (15 giây)" +lore = ["Thuốc Vụng Về + Thịt Thối = Thuốc Đói (30 giây)", "Thuốc Yếu Đuối + Thịt Thối = Thuốc Đói-3 (15 giây)"] [brewing.nausea] - name = "Buồn Nôn Đóng Chai" - description = "Bạn làm tôi phát ốm!" - lore1 = "Thuốc Vụng Về + Nấm Nâu = Thuốc Buồn Nôn (16 giây)" - lore2 = "Thuốc Vụng Về + Nấm Đỏ Thẫm = Thuốc Buồn Nôn-2 (8 giây)" - lore = ["Thuốc Vụng Về + Nấm Nâu = Thuốc Buồn Nôn (16 giây)", "Thuốc Vụng Về + Nấm Đỏ Thẫm = Thuốc Buồn Nôn-2 (8 giây)"] +name = "Buồn Nôn Đóng Chai" +description = "Bạn làm tôi phát ốm!" +lore1 = "Thuốc Vụng Về + Nấm Nâu = Thuốc Buồn Nôn (16 giây)" +lore2 = "Thuốc Vụng Về + Nấm Đỏ Thẫm = Thuốc Buồn Nôn-2 (8 giây)" +lore = ["Thuốc Vụng Về + Nấm Nâu = Thuốc Buồn Nôn (16 giây)", "Thuốc Vụng Về + Nấm Đỏ Thẫm = Thuốc Buồn Nôn-2 (8 giây)"] [brewing.blindness] - name = "Mù Lòa Đóng Chai" - description = "Bạn là một người tệ hại..." - lore1 = "Thuốc Vụng Về + Túi Mực = Thuốc Mù Lòa (30 giây)" - lore2 = "Thuốc Vụng Về + Túi Mực Phát Sáng = Thuốc Mù Lòa-2 (15 giây)" - lore = ["Thuốc Vụng Về + Túi Mực = Thuốc Mù Lòa (30 giây)", "Thuốc Vụng Về + Túi Mực Phát Sáng = Thuốc Mù Lòa-2 (15 giây)"] +name = "Mù Lòa Đóng Chai" +description = "Bạn là một người tệ hại..." +lore1 = "Thuốc Vụng Về + Túi Mực = Thuốc Mù Lòa (30 giây)" +lore2 = "Thuốc Vụng Về + Túi Mực Phát Sáng = Thuốc Mù Lòa-2 (15 giây)" +lore = ["Thuốc Vụng Về + Túi Mực = Thuốc Mù Lòa (30 giây)", "Thuốc Vụng Về + Túi Mực Phát Sáng = Thuốc Mù Lòa-2 (15 giây)"] [brewing.resistance] - name = "Kháng Cự Đóng Chai" - description = "Phòng thủ ở đỉnh cao!" - lore1 = "Thuốc Vụng Về + Thỏi Sắt = Thuốc Kháng Cự (60 giây)" - lore2 = "Thuốc Vụng Về + Khối Sắt = Thuốc Kháng Cự-2 (30 giây)" - lore = ["Thuốc Vụng Về + Thỏi Sắt = Thuốc Kháng Cự (60 giây)", "Thuốc Vụng Về + Khối Sắt = Thuốc Kháng Cự-2 (30 giây)"] +name = "Kháng Cự Đóng Chai" +description = "Phòng thủ ở đỉnh cao!" +lore1 = "Thuốc Vụng Về + Thỏi Sắt = Thuốc Kháng Cự (60 giây)" +lore2 = "Thuốc Vụng Về + Khối Sắt = Thuốc Kháng Cự-2 (30 giây)" +lore = ["Thuốc Vụng Về + Thỏi Sắt = Thuốc Kháng Cự (60 giây)", "Thuốc Vụng Về + Khối Sắt = Thuốc Kháng Cự-2 (30 giây)"] [brewing.health_boost] - name = "Sinh Mệnh Đóng Chai" - description = "Khi máu tối đa là chưa đủ..." - lore1 = "Thuốc Hồi Máu Tức Thì + Táo Vàng = Thuốc Tăng Máu (120 giây)" - lore2 = "Thuốc Hồi Máu Tức Thì + Táo Vàng Phù Phép = Thuốc Tăng Máu-2 (120 giây)" - lore = ["Thuốc Hồi Máu Tức Thì + Táo Vàng = Thuốc Tăng Máu (120 giây)", "Thuốc Hồi Máu Tức Thì + Táo Vàng Phù Phép = Thuốc Tăng Máu-2 (120 giây)"] +name = "Sinh Mệnh Đóng Chai" +description = "Khi máu tối đa là chưa đủ..." +lore1 = "Thuốc Hồi Máu Tức Thì + Táo Vàng = Thuốc Tăng Máu (120 giây)" +lore2 = "Thuốc Hồi Máu Tức Thì + Táo Vàng Phù Phép = Thuốc Tăng Máu-2 (120 giây)" +lore = ["Thuốc Hồi Máu Tức Thì + Táo Vàng = Thuốc Tăng Máu (120 giây)", "Thuốc Hồi Máu Tức Thì + Táo Vàng Phù Phép = Thuốc Tăng Máu-2 (120 giây)"] [brewing.decay] - name = "Phân Hủy Đóng Chai" - description = "Ai biết rác rưởi lại hữu ích đến vậy?" - lore1 = "Thuốc Yếu Đuối + Khoai Tây Độc = Thuốc Héo Úa (16 giây)" - lore2 = "Thuốc Yếu Đuối + Rễ Đỏ Thẫm = Thuốc Héo Úa-2 (8 giây)" - lore = ["Thuốc Yếu Đuối + Khoai Tây Độc = Thuốc Héo Úa (16 giây)", "Thuốc Yếu Đuối + Rễ Đỏ Thẫm = Thuốc Héo Úa-2 (8 giây)"] +name = "Phân Hủy Đóng Chai" +description = "Ai biết rác rưởi lại hữu ích đến vậy?" +lore1 = "Thuốc Yếu Đuối + Khoai Tây Độc = Thuốc Héo Úa (16 giây)" +lore2 = "Thuốc Yếu Đuối + Rễ Đỏ Thẫm = Thuốc Héo Úa-2 (8 giây)" +lore = ["Thuốc Yếu Đuối + Khoai Tây Độc = Thuốc Héo Úa (16 giây)", "Thuốc Yếu Đuối + Rễ Đỏ Thẫm = Thuốc Héo Úa-2 (8 giây)"] [brewing.saturation] - name = "Bão Hòa Đóng Chai" - description = "Biết không... Tôi thậm chí còn không đói..." - lore1 = "Thuốc Hồi Sinh + Khoai Tây Nướng = Thuốc Bão Hòa" - lore2 = "Thuốc Hồi Sinh + Bó Cỏ Khô = Thuốc Bão Hòa-2" - lore = ["Thuốc Hồi Sinh + Khoai Tây Nướng = Thuốc Bão Hòa", "Thuốc Hồi Sinh + Bó Cỏ Khô = Thuốc Bão Hòa-2"] +name = "Bão Hòa Đóng Chai" +description = "Biết không... Tôi thậm chí còn không đói..." +lore1 = "Thuốc Hồi Sinh + Khoai Tây Nướng = Thuốc Bão Hòa" +lore2 = "Thuốc Hồi Sinh + Bó Cỏ Khô = Thuốc Bão Hòa-2" +lore = ["Thuốc Hồi Sinh + Khoai Tây Nướng = Thuốc Bão Hòa", "Thuốc Hồi Sinh + Bó Cỏ Khô = Thuốc Bão Hòa-2"] # blocking [blocking] [blocking.chain_armorer] - name = "Xích Của Mephistopheles" - description = "Cho phép bạn chế tạo Giáp Xích" - lore1 = "Công thức chế tạo giống các giáp khác, nhưng dùng cốm sắt thay thế" - lore = ["Công thức chế tạo giống các giáp khác, nhưng dùng cốm sắt thay thế"] +name = "Xích Của Mephistopheles" +description = "Cho phép bạn chế tạo Giáp Xích" +lore1 = "Công thức chế tạo giống các giáp khác, nhưng dùng cốm sắt thay thế" +lore = ["Công thức chế tạo giống các giáp khác, nhưng dùng cốm sắt thay thế"] [blocking.horse_armorer] - name = "Giáp Ngựa Chế Tạo Được" - description = "Cho phép bạn chế tạo Giáp Ngựa" - lore1 = "Bao quanh yên ngựa bằng vật liệu bạn muốn dùng để chế tạo giáp" - lore = ["Bao quanh yên ngựa bằng vật liệu bạn muốn dùng để chế tạo giáp"] +name = "Giáp Ngựa Chế Tạo Được" +description = "Cho phép bạn chế tạo Giáp Ngựa" +lore1 = "Bao quanh yên ngựa bằng vật liệu bạn muốn dùng để chế tạo giáp" +lore = ["Bao quanh yên ngựa bằng vật liệu bạn muốn dùng để chế tạo giáp"] [blocking.saddle_crafter] - name = "Yên Ngựa Chế Tạo Được" - description = "Chế tạo Yên Ngựa bằng Da" - lore1 = "Công thức: 5 Da:" - lore = ["Công thức: 5 Da:"] +name = "Yên Ngựa Chế Tạo Được" +description = "Chế tạo Yên Ngựa bằng Da" +lore1 = "Công thức: 5 Da:" +lore = ["Công thức: 5 Da:"] [blocking.multi_armor] - name = "Đa Giáp" - description = "Gắn Elytra vào Giáp" - lore1 = "Đây là kỹ năng tuyệt vời để di chuyển." - lore2 = "Kết hợp và thay đổi Giáp/Elytra linh hoạt khi đang bay!" - lore3 = "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ." - lore4 = "Để tháo Giáp, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời." - lore5 = "Nếu Đa Giáp bị phá hủy, bạn sẽ mất tất cả vật phẩm trong đó." - lore6 = "Tôi không cần giáp, Nó làm tôi thất vọng..." - lore = ["Đây là kỹ năng tuyệt vời để di chuyển.", "Kết hợp và thay đổi Giáp/Elytra linh hoạt khi đang bay!", "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ.", "Để tháo Giáp, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời.", "Nếu Đa Giáp bị phá hủy, bạn sẽ mất tất cả vật phẩm trong đó.", "Tôi không cần giáp, Nó làm tôi thất vọng..."] +name = "Đa Giáp" +description = "Gắn Elytra vào Giáp" +lore1 = "Đây là kỹ năng tuyệt vời để di chuyển." +lore2 = "Kết hợp và thay đổi Giáp/Elytra linh hoạt khi đang bay!" +lore3 = "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ." +lore4 = "Để tháo Giáp, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời." +lore5 = "Nếu Đa Giáp bị phá hủy, bạn sẽ mất tất cả vật phẩm trong đó." +lore6 = "Tôi không cần giáp, Nó làm tôi thất vọng..." +lore = ["Đây là kỹ năng tuyệt vời để di chuyển.", "Kết hợp và thay đổi Giáp/Elytra linh hoạt khi đang bay!", "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ.", "Để tháo Giáp, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời.", "Nếu Đa Giáp bị phá hủy, bạn sẽ mất tất cả vật phẩm trong đó.", "Tôi không cần giáp, Nó làm tôi thất vọng..."] # crafting [crafting] [crafting.deconstruction] - name = "Tháo Dỡ" - description = "Tháo dỡ khối & vật phẩm thành nguyên liệu cơ bản có thể tái sử dụng!" - lore1 = "Thả bất kỳ vật phẩm nào xuống đất." - lore2 = "Sau đó, Ngồi và Chuột Phải bằng Kéo" - lore = ["Thả bất kỳ vật phẩm nào xuống đất.", "Sau đó, Ngồi và Chuột Phải bằng Kéo"] +name = "Tháo Dỡ" +description = "Tháo dỡ khối & vật phẩm thành nguyên liệu cơ bản có thể tái sử dụng!" +lore1 = "Thả bất kỳ vật phẩm nào xuống đất." +lore2 = "Sau đó, Ngồi và Chuột Phải bằng Kéo" +lore = ["Thả bất kỳ vật phẩm nào xuống đất.", "Sau đó, Ngồi và Chuột Phải bằng Kéo"] [crafting.xp] - name = "XP Chế Tạo" - description = "Nhận XP thụ động khi chế tạo" - lore1 = "Nhận XP khi chế tạo" - lore = ["Nhận XP khi chế tạo"] +name = "XP Chế Tạo" +description = "Nhận XP thụ động khi chế tạo" +lore1 = "Nhận XP khi chế tạo" +lore = ["Nhận XP khi chế tạo"] [crafting.reconstruction] - name = "Tái Tạo Quặng" - description = "Chế tạo lại quặng từ nguyên liệu cơ bản!" - lore1 = "8 Vật Phẩm Rơi và 1 Vật Chủ = 1 Quặng (không định hình)" - lore2 = "Vật phẩm rơi phải được nung (nếu áp dụng)" - lore3 = "Không bao gồm: Mảnh Vụn, Thạch Anh, và Ngọc Lục Bảo v.v..." - lore4 = "Vật Chủ = Lớp Bọc. vd: Đá, Đá Địa Ngục, Đá Sâu" - lore = ["8 Vật Phẩm Rơi và 1 Vật Chủ = 1 Quặng (không định hình)", "Vật phẩm rơi phải được nung (nếu áp dụng)", "Không bao gồm: Mảnh Vụn, Thạch Anh, và Ngọc Lục Bảo v.v...", "Vật Chủ = Lớp Bọc. vd: Đá, Đá Địa Ngục, Đá Sâu"] +name = "Tái Tạo Quặng" +description = "Chế tạo lại quặng từ nguyên liệu cơ bản!" +lore1 = "8 Vật Phẩm Rơi và 1 Vật Chủ = 1 Quặng (không định hình)" +lore2 = "Vật phẩm rơi phải được nung (nếu áp dụng)" +lore3 = "Không bao gồm: Mảnh Vụn, Thạch Anh, và Ngọc Lục Bảo v.v..." +lore4 = "Vật Chủ = Lớp Bọc. vd: Đá, Đá Địa Ngục, Đá Sâu" +lore = ["8 Vật Phẩm Rơi và 1 Vật Chủ = 1 Quặng (không định hình)", "Vật phẩm rơi phải được nung (nếu áp dụng)", "Không bao gồm: Mảnh Vụn, Thạch Anh, và Ngọc Lục Bảo v.v...", "Vật Chủ = Lớp Bọc. vd: Đá, Đá Địa Ngục, Đá Sâu"] [crafting.leather] - name = "Da Chế Tạo Được" - description = "Chế tạo Da từ Thịt Thối" - lore1 = "Chỉ cần ném (thịt thối) lên lửa trại!" - lore = ["Chỉ cần ném (thịt thối) lên lửa trại!"] +name = "Da Chế Tạo Được" +description = "Chế tạo Da từ Thịt Thối" +lore1 = "Chỉ cần ném (thịt thối) lên lửa trại!" +lore = ["Chỉ cần ném (thịt thối) lên lửa trại!"] [crafting.backpacks] - name = "Ba Lô Của Boutilier!" - description = "Mang tính năng Bó Đồ của Mojang vào trò chơi!" - lore1 = "Bạn cần ở chế độ Sinh Tồn để sử dụng" - lore2 = "XLX : Da, Dây Dắt, Da" - lore3 = "XSX : Da, Thùng, Da" - lore4 = "XCX : Da, Rương, Da" - lore = ["Bạn cần ở chế độ Sinh Tồn để sử dụng", "XLX : Da, Dây Dắt, Da", "XSX : Da, Thùng, Da", "XCX : Da, Rương, Da"] +name = "Ba Lô Của Boutilier!" +description = "Mang tính năng Bó Đồ của Mojang vào trò chơi!" +lore1 = "Bạn cần ở chế độ Sinh Tồn để sử dụng" +lore2 = "XLX : Da, Dây Dắt, Da" +lore3 = "XSX : Da, Thùng, Da" +lore4 = "XCX : Da, Rương, Da" +lore = ["Bạn cần ở chế độ Sinh Tồn để sử dụng", "XLX : Da, Dây Dắt, Da", "XSX : Da, Thùng, Da", "XCX : Da, Rương, Da"] [crafting.stations] - name = "Bàn Di Động!" - description = "Sử dụng bàn chế tạo ngay trong lòng bàn tay!" - lore2 = "MỌI VẬT PHẨM BẠN QUÊN TRONG BÀN KHI ĐÓNG SẼ MẤT VĨNH VIỄN!" - lore3 = "Bàn hợp lệ: Đe, Chế Tạo, Đá Mài, Bản Đồ Học, Máy Cắt Đá, Máy Dệt" - lore = ["MỌI VẬT PHẨM BẠN QUÊN TRONG BÀN KHI ĐÓNG SẼ MẤT VĨNH VIỄN!", "Bàn hợp lệ: Đe, Chế Tạo, Đá Mài, Bản Đồ Học, Máy Cắt Đá, Máy Dệt"] +name = "Bàn Di Động!" +description = "Sử dụng bàn chế tạo ngay trong lòng bàn tay!" +lore2 = "MỌI VẬT PHẨM BẠN QUÊN TRONG BÀN KHI ĐÓNG SẼ MẤT VĨNH VIỄN!" +lore3 = "Bàn hợp lệ: Đe, Chế Tạo, Đá Mài, Bản Đồ Học, Máy Cắt Đá, Máy Dệt" +lore = ["MỌI VẬT PHẨM BẠN QUÊN TRONG BÀN KHI ĐÓNG SẼ MẤT VĨNH VIỄN!", "Bàn hợp lệ: Đe, Chế Tạo, Đá Mài, Bản Đồ Học, Máy Cắt Đá, Máy Dệt"] [crafting.skulls] - name = "Đầu Lâu Chế Tạo Được!" - description = "Dùng Nguyên Liệu để Chế Tạo Đầu Lâu Quái Vật!" - lore1 = "Bao quanh Khối Xương bằng các vật liệu sau để nhận đầu lâu:" - lore2 = "Zombie: Thịt Thối" - lore3 = "Skeleton: Xương" - lore4 = "Creeper: Thuốc Súng" - lore5 = "Wither: Gạch Địa Ngục" - lore6 = "Rồng: Hơi Thở Rồng" - lore = ["Bao quanh Khối Xương bằng các vật liệu sau để nhận đầu lâu:", "Zombie: Thịt Thối", "Skeleton: Xương", "Creeper: Thuốc Súng", "Wither: Gạch Địa Ngục", "Rồng: Hơi Thở Rồng"] +name = "Đầu Lâu Chế Tạo Được!" +description = "Dùng Nguyên Liệu để Chế Tạo Đầu Lâu Quái Vật!" +lore1 = "Bao quanh Khối Xương bằng các vật liệu sau để nhận đầu lâu:" +lore2 = "Zombie: Thịt Thối" +lore3 = "Skeleton: Xương" +lore4 = "Creeper: Thuốc Súng" +lore5 = "Wither: Gạch Địa Ngục" +lore6 = "Rồng: Hơi Thở Rồng" +lore = ["Bao quanh Khối Xương bằng các vật liệu sau để nhận đầu lâu:", "Zombie: Thịt Thối", "Skeleton: Xương", "Creeper: Thuốc Súng", "Wither: Gạch Địa Ngục", "Rồng: Hơi Thở Rồng"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "Thời Gian Trong Chai" - description = "Mang theo một chai thời gian tích trữ thời gian và tiêu thụ để tăng tốc khối có bộ hẹn giờ, cây trồng, và thực thể có tuổi như động vật con. Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh." - lore1 = "Số giây tích trữ mỗi tick" - lore2 = "Tăng tốc thời gian mỗi giây tích trữ" - lore3 = "Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh" - lore = ["Số giây tích trữ mỗi tick", "Tăng tốc thời gian mỗi giây tích trữ", "Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh"] +name = "Thời Gian Trong Chai" +description = "Mang theo một chai thời gian tích trữ thời gian và tiêu thụ để tăng tốc khối có bộ hẹn giờ, cây trồng, và thực thể có tuổi như động vật con. Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh." +lore1 = "Số giây tích trữ mỗi tick" +lore2 = "Tăng tốc thời gian mỗi giây tích trữ" +lore3 = "Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh" +lore = ["Số giây tích trữ mỗi tick", "Tăng tốc thời gian mỗi giây tích trữ", "Công thức (Không Định Hình): Thuốc Tốc Độ + Đồng Hồ + Chai Thủy Tinh"] [chronos.aberrant_touch] - name = "Chạm Dị Thường" - description = "Đòn cận chiến áp dụng chậm cộng dồn với cái giá là đói, với giới hạn PvP nghiêm ngặt, và ghim mục tiêu tại 5 cộng dồn." - lore1 = "Đòn cận chiến áp dụng chậm cộng dồn" - lore2 = "Giới hạn thời lượng chậm PvE" - lore3 = "Giới hạn cường độ chậm PvP" - lore = ["Đòn cận chiến áp dụng chậm cộng dồn", "Giới hạn thời lượng chậm PvE", "Giới hạn cường độ chậm PvP"] +name = "Chạm Dị Thường" +description = "Đòn cận chiến áp dụng chậm cộng dồn với cái giá là đói, với giới hạn PvP nghiêm ngặt, và ghim mục tiêu tại 5 cộng dồn." +lore1 = "Đòn cận chiến áp dụng chậm cộng dồn" +lore2 = "Giới hạn thời lượng chậm PvE" +lore3 = "Giới hạn cường độ chậm PvP" +lore = ["Đòn cận chiến áp dụng chậm cộng dồn", "Giới hạn thời lượng chậm PvE", "Giới hạn cường độ chậm PvP"] [chronos.instant_recall] - name = "Triệu Hồi Tức Thì" - description = "Chuột trái hoặc phải với đồng hồ trong tay để tua lại đến ảnh chụp gần đây với máu và đói được phục hồi." - lore1 = "Thời lượng tua lại" - lore2 = "Thời gian hồi" - lore3 = "Không hoàn lại túi đồ" - lore = ["Thời lượng tua lại", "Thời gian hồi", "Không hoàn lại túi đồ"] +name = "Triệu Hồi Tức Thì" +description = "Chuột trái hoặc phải với đồng hồ trong tay để tua lại đến ảnh chụp gần đây với máu và đói được phục hồi." +lore1 = "Thời lượng tua lại" +lore2 = "Thời gian hồi" +lore3 = "Không hoàn lại túi đồ" +lore = ["Thời lượng tua lại", "Thời gian hồi", "Không hoàn lại túi đồ"] [chronos.time_bomb] - name = "Bom Thời Gian" - description = "Ném một quả bom thời gian đã chế tạo tạo ra trường thời không, làm chậm thực thể, và đóng băng đạn đạo." - lore1 = "Bán kính trường thời không" - lore2 = "Thời lượng trường thời không" - lore3 = "Thời gian hồi bom" - lore4 = "Công thức (Không Định Hình): Đồng Hồ + Cầu Tuyết + Kim Cương + Cát" - lore = ["Bán kính trường thời không", "Thời lượng trường thời không", "Thời gian hồi bom", "Công thức (Không Định Hình): Đồng Hồ + Cầu Tuyết + Kim Cương + Cát"] +name = "Bom Thời Gian" +description = "Ném một quả bom thời gian đã chế tạo tạo ra trường thời không, làm chậm thực thể, và đóng băng đạn đạo." +lore1 = "Bán kính trường thời không" +lore2 = "Thời lượng trường thời không" +lore3 = "Thời gian hồi bom" +lore4 = "Công thức (Không Định Hình): Đồng Hồ + Cầu Tuyết + Kim Cương + Cát" +lore = ["Bán kính trường thời không", "Thời lượng trường thời không", "Thời gian hồi bom", "Công thức (Không Định Hình): Đồng Hồ + Cầu Tuyết + Kim Cương + Cát"] # discovery [discovery] [discovery.armor] - name = "Giáp Thế Giới" - description = "Giáp thụ động dựa trên độ cứng khối gần đó." - lore1 = "Giáp Thụ Động" - lore2 = "Dựa trên độ cứng khối gần đó" - lore3 = "Sức Mạnh Giáp:" - lore = ["Giáp Thụ Động", "Dựa trên độ cứng khối gần đó", "Sức Mạnh Giáp:"] +name = "Giáp Thế Giới" +description = "Giáp thụ động dựa trên độ cứng khối gần đó." +lore1 = "Giáp Thụ Động" +lore2 = "Dựa trên độ cứng khối gần đó" +lore3 = "Sức Mạnh Giáp:" +lore = ["Giáp Thụ Động", "Dựa trên độ cứng khối gần đó", "Sức Mạnh Giáp:"] [discovery.unity] - name = "Thống Nhất Thực Nghiệm" - description = "Thu thập Quả Cầu Kinh Nghiệm sẽ thêm XP vào kỹ năng ngẫu nhiên." - lore1 = "XP " - lore2 = "Mỗi Quả Cầu" - lore = ["XP ", "Mỗi Quả Cầu"] +name = "Thống Nhất Thực Nghiệm" +description = "Thu thập Quả Cầu Kinh Nghiệm sẽ thêm XP vào kỹ năng ngẫu nhiên." +lore1 = "XP " +lore2 = "Mỗi Quả Cầu" +lore = ["XP ", "Mỗi Quả Cầu"] [discovery.resist] - name = "Kháng Cự Thực Nghiệm" - description = "Tiêu thụ kinh nghiệm để giảm sát thương chỉ khi một đòn đánh sẽ hạ bạn xuống dưới 5 trái tim hoặc giết bạn." - lore0 = "Chỉ kích hoạt khi máu nguy hiểm (<= 5 trái tim) mỗi 15 giây" - lore1 = " Giảm Sát Thương" - lore2 = "kinh nghiệm bị tiêu hao" - lore = ["Chỉ kích hoạt khi máu nguy hiểm (<= 5 trái tim) mỗi 15 giây", " Giảm Sát Thương", "kinh nghiệm bị tiêu hao"] +name = "Kháng Cự Thực Nghiệm" +description = "Tiêu thụ kinh nghiệm để giảm sát thương chỉ khi một đòn đánh sẽ hạ bạn xuống dưới 5 trái tim hoặc giết bạn." +lore0 = "Chỉ kích hoạt khi máu nguy hiểm (<= 5 trái tim) mỗi 15 giây" +lore1 = " Giảm Sát Thương" +lore2 = "kinh nghiệm bị tiêu hao" +lore = ["Chỉ kích hoạt khi máu nguy hiểm (<= 5 trái tim) mỗi 15 giây", " Giảm Sát Thương", "kinh nghiệm bị tiêu hao"] [discovery.villager] - name = "Thu Hút Dân Làng" - description = "Cho phép bạn có giao dịch tốt hơn với dân làng!" - lore1 = "Tiêu tốn XP mỗi lần tương tác với Dân Làng" - lore2 = "Cơ hội mỗi lần tương tác để tiêu XP và nâng cấp giao dịch" - lore3 = "XP cần tiêu hao mỗi Lần Tương Tác" - lore = ["Tiêu tốn XP mỗi lần tương tác với Dân Làng", "Cơ hội mỗi lần tương tác để tiêu XP và nâng cấp giao dịch", "XP cần tiêu hao mỗi Lần Tương Tác"] +name = "Thu Hút Dân Làng" +description = "Cho phép bạn có giao dịch tốt hơn với dân làng!" +lore1 = "Tiêu tốn XP mỗi lần tương tác với Dân Làng" +lore2 = "Cơ hội mỗi lần tương tác để tiêu XP và nâng cấp giao dịch" +lore3 = "XP cần tiêu hao mỗi Lần Tương Tác" +lore = ["Tiêu tốn XP mỗi lần tương tác với Dân Làng", "Cơ hội mỗi lần tương tác để tiêu XP và nâng cấp giao dịch", "XP cần tiêu hao mỗi Lần Tương Tác"] # enchanting [enchanting] [enchanting.lapis_return] - name = "Hoàn Lapis" - description = "Với cái giá tăng thêm 1 cấp XP, và có cơ hội nhận lại lapis miễn phí" - lore1 = "Mỗi cấp độ tăng chi phí phù phép thêm 1, nhưng có thể trả lại tối đa 3 Lapis" - lore = ["Mỗi cấp độ tăng chi phí phù phép thêm 1, nhưng có thể trả lại tối đa 3 Lapis"] +name = "Hoàn Lapis" +description = "Với cái giá tăng thêm 1 cấp XP, và có cơ hội nhận lại lapis miễn phí" +lore1 = "Mỗi cấp độ tăng chi phí phù phép thêm 1, nhưng có thể trả lại tối đa 3 Lapis" +lore = ["Mỗi cấp độ tăng chi phí phù phép thêm 1, nhưng có thể trả lại tối đa 3 Lapis"] [enchanting.quick_enchant] - name = "Phù Phép Nhanh" - description = "Phù phép vật phẩm bằng cách bấm sách phù phép trực tiếp lên chúng." - lore1 = "Cấp Kết Hợp Tối Đa" - lore2 = "Không thể phù phép vật phẩm có nhiều hơn " - lore3 = "năng lượng" - lore = ["Cấp Kết Hợp Tối Đa", "Không thể phù phép vật phẩm có nhiều hơn ", "năng lượng"] +name = "Phù Phép Nhanh" +description = "Phù phép vật phẩm bằng cách bấm sách phù phép trực tiếp lên chúng." +lore1 = "Cấp Kết Hợp Tối Đa" +lore2 = "Không thể phù phép vật phẩm có nhiều hơn " +lore3 = "năng lượng" +lore = ["Cấp Kết Hợp Tối Đa", "Không thể phù phép vật phẩm có nhiều hơn ", "năng lượng"] [enchanting.return] - name = "Hoàn XP" - description = "XP phù phép được trả lại khi bạn phù phép vật phẩm." - lore1 = "Kinh nghiệm đã tiêu có cơ hội được hoàn lại khi phù phép" - lore2 = "Kinh Nghiệm mỗi Lần Phù Phép" - lore = ["Kinh nghiệm đã tiêu có cơ hội được hoàn lại khi phù phép", "Kinh Nghiệm mỗi Lần Phù Phép"] +name = "Hoàn XP" +description = "XP phù phép được trả lại khi bạn phù phép vật phẩm." +lore1 = "Kinh nghiệm đã tiêu có cơ hội được hoàn lại khi phù phép" +lore2 = "Kinh Nghiệm mỗi Lần Phù Phép" +lore = ["Kinh nghiệm đã tiêu có cơ hội được hoàn lại khi phù phép", "Kinh Nghiệm mỗi Lần Phù Phép"] # excavation [excavation] [excavation.haste] - name = "Đào Nhanh" - description = "Tăng tốc quá trình khai quật với HASTE!" - lore1 = "Nhận Haste khi đang khai quật" - lore2 = "x Cấp Haste khi bạn bắt đầu đào BẤT KỲ khối nào." - lore = ["Nhận Haste khi đang khai quật", "x Cấp Haste khi bạn bắt đầu đào BẤT KỲ khối nào."] +name = "Đào Nhanh" +description = "Tăng tốc quá trình khai quật với HASTE!" +lore1 = "Nhận Haste khi đang khai quật" +lore2 = "x Cấp Haste khi bạn bắt đầu đào BẤT KỲ khối nào." +lore = ["Nhận Haste khi đang khai quật", "x Cấp Haste khi bạn bắt đầu đào BẤT KỲ khối nào."] [excavation.spelunker] - name = "Thám Hiểm Hang Siêu Nhìn!" - description = "Nhìn thấy Quặng bằng mắt xuyên qua đất!" - lore1 = "Quặng ở tay phụ, Glowberry ở tay chính, và Ngồi!" - lore2 = "Phạm Vi Khối: " - lore3 = "Tiêu thụ Glowberry khi sử dụng" - lore = ["Quặng ở tay phụ, Glowberry ở tay chính, và Ngồi!", "Phạm Vi Khối: ", "Tiêu thụ Glowberry khi sử dụng"] +name = "Thám Hiểm Hang Siêu Nhìn!" +description = "Nhìn thấy Quặng bằng mắt xuyên qua đất!" +lore1 = "Quặng ở tay phụ, Glowberry ở tay chính, và Ngồi!" +lore2 = "Phạm Vi Khối: " +lore3 = "Tiêu thụ Glowberry khi sử dụng" +lore = ["Quặng ở tay phụ, Glowberry ở tay chính, và Ngồi!", "Phạm Vi Khối: ", "Tiêu thụ Glowberry khi sử dụng"] [excavation.drop_to_inventory] - name = "Xẻng Nhặt Vào Túi Đồ" +name = "Xẻng Nhặt Vào Túi Đồ" [excavation.omni_tool] - name = "DỤNG CỤ TOÀN NĂNG" - description = "Chiếc dao đa năng thiết kế cầu kỳ của Tackle" - lore1 = "Có lẽ mạnh nhất trong tất cả, cho phép bạn" - lore2 = "linh hoạt kết hợp và thay đổi công cụ khi đang dùng, tùy nhu cầu." - lore3 = "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ." - lore4 = "Để tháo công cụ, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời." - lore5 = "Bạn không thể phá hủy công cụ trong dụng cụ toàn năng nhưng không thể dùng công cụ hỏng" - lore6 = "tổng vật phẩm có thể kết hợp." - lore7 = "Bạn có thể dùng năm sáu công cụ, hoặc chỉ một!" - lore = ["Có lẽ mạnh nhất trong tất cả, cho phép bạn", "linh hoạt kết hợp và thay đổi công cụ khi đang dùng, tùy nhu cầu.", "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ.", "Để tháo công cụ, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời.", "Bạn không thể phá hủy công cụ trong dụng cụ toàn năng nhưng không thể dùng công cụ hỏng", "tổng vật phẩm có thể kết hợp.", "Bạn có thể dùng năm sáu công cụ, hoặc chỉ một!"] +name = "DỤNG CỤ TOÀN NĂNG" +description = "Chiếc dao đa năng thiết kế cầu kỳ của Tackle" +lore1 = "Có lẽ mạnh nhất trong tất cả, cho phép bạn" +lore2 = "linh hoạt kết hợp và thay đổi công cụ khi đang dùng, tùy nhu cầu." +lore3 = "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ." +lore4 = "Để tháo công cụ, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời." +lore5 = "Bạn không thể phá hủy công cụ trong dụng cụ toàn năng nhưng không thể dùng công cụ hỏng" +lore6 = "tổng vật phẩm có thể kết hợp." +lore7 = "Bạn có thể dùng năm sáu công cụ, hoặc chỉ một!" +lore = ["Có lẽ mạnh nhất trong tất cả, cho phép bạn", "linh hoạt kết hợp và thay đổi công cụ khi đang dùng, tùy nhu cầu.", "Để kết hợp, shift + chuột vào vật phẩm trong túi đồ.", "Để tháo công cụ, Ngồi + Thả vật phẩm, nó sẽ tự tháo rời.", "Bạn không thể phá hủy công cụ trong dụng cụ toàn năng nhưng không thể dùng công cụ hỏng", "tổng vật phẩm có thể kết hợp.", "Bạn có thể dùng năm sáu công cụ, hoặc chỉ một!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "Hào Quang Tăng Trưởng" - description = "Phát triển thiên nhiên xung quanh bạn trong một hào quang" - lore1 = "Bán Kính Khối" - lore2 = "Sức Mạnh Hào Quang Tăng Trưởng" - lore3 = "Chi Phí Thức Ăn" - lore = ["Bán Kính Khối", "Sức Mạnh Hào Quang Tăng Trưởng", "Chi Phí Thức Ăn"] +name = "Hào Quang Tăng Trưởng" +description = "Phát triển thiên nhiên xung quanh bạn trong một hào quang" +lore1 = "Bán Kính Khối" +lore2 = "Sức Mạnh Hào Quang Tăng Trưởng" +lore3 = "Chi Phí Thức Ăn" +lore = ["Bán Kính Khối", "Sức Mạnh Hào Quang Tăng Trưởng", "Chi Phí Thức Ăn"] [herbalism.hippo] - name = "Hà Mã Thảo Dược" - description = "Ăn thức ăn cho bạn thêm độ no" - lore1 = "Thức ăn) điểm bão hòa thêm khi ăn" - lore = ["Thức ăn) điểm bão hòa thêm khi ăn"] +name = "Hà Mã Thảo Dược" +description = "Ăn thức ăn cho bạn thêm độ no" +lore1 = "Thức ăn) điểm bão hòa thêm khi ăn" +lore = ["Thức ăn) điểm bão hòa thêm khi ăn"] [herbalism.myconid] - name = "Nấm Sợi Thảo Dược" - description = "Cho bạn khả năng chế tạo Sợi Nấm" - lore1 = "Bất kỳ Đất nào, và một Nấm Nâu & Đỏ sẽ tạo ra Sợi Nấm." - lore = ["Bất kỳ Đất nào, và một Nấm Nâu & Đỏ sẽ tạo ra Sợi Nấm."] +name = "Nấm Sợi Thảo Dược" +description = "Cho bạn khả năng chế tạo Sợi Nấm" +lore1 = "Bất kỳ Đất nào, và một Nấm Nâu & Đỏ sẽ tạo ra Sợi Nấm." +lore = ["Bất kỳ Đất nào, và một Nấm Nâu & Đỏ sẽ tạo ra Sợi Nấm."] [herbalism.terralid] - name = "Khối Cỏ Thảo Dược" - description = "Cho bạn khả năng chế tạo Khối Cỏ" - lore1 = "Ba Hạt Giống, trên 3 Đất, sẽ tạo ra 3 Khối Cỏ." - lore = ["Ba Hạt Giống, trên 3 Đất, sẽ tạo ra 3 Khối Cỏ."] +name = "Khối Cỏ Thảo Dược" +description = "Cho bạn khả năng chế tạo Khối Cỏ" +lore1 = "Ba Hạt Giống, trên 3 Đất, sẽ tạo ra 3 Khối Cỏ." +lore = ["Ba Hạt Giống, trên 3 Đất, sẽ tạo ra 3 Khối Cỏ."] [herbalism.cobweb] - name = "Thợ Dệt Mạng" - description = "Cho bạn khả năng chế tạo Mạng Nhện trong Bàn Chế Tạo" - lore1 = "Chín Sợi Dây sẽ tạo ra một Mạng Nhện." - lore = ["Chín Sợi Dây sẽ tạo ra một Mạng Nhện."] +name = "Thợ Dệt Mạng" +description = "Cho bạn khả năng chế tạo Mạng Nhện trong Bàn Chế Tạo" +lore1 = "Chín Sợi Dây sẽ tạo ra một Mạng Nhện." +lore = ["Chín Sợi Dây sẽ tạo ra một Mạng Nhện."] [herbalism.mushroom_blocks] - name = "Chế Tạo Khối Nấm" - description = "Cho bạn khả năng chế tạo Khối Nấm trong Bàn Chế Tạo" - lore1 = "Bốn Nấm để tạo khối, hoặc một khối để tạo thân cây." - lore = ["Bốn Nấm để tạo khối, hoặc một khối để tạo thân cây."] +name = "Chế Tạo Khối Nấm" +description = "Cho bạn khả năng chế tạo Khối Nấm trong Bàn Chế Tạo" +lore1 = "Bốn Nấm để tạo khối, hoặc một khối để tạo thân cây." +lore = ["Bốn Nấm để tạo khối, hoặc một khối để tạo thân cây."] [herbalism.drop_to_inventory] - name = "Cuốc Nhặt Vào Túi Đồ" +name = "Cuốc Nhặt Vào Túi Đồ" [herbalism.hungry_shield] - name = "Khiên Đói" - description = "Nhận sát thương vào thanh đói trước thanh máu." - lore1 = "Kháng Bằng Đói" - lore = ["Kháng Bằng Đói"] +name = "Khiên Đói" +description = "Nhận sát thương vào thanh đói trước thanh máu." +lore1 = "Kháng Bằng Đói" +lore = ["Kháng Bằng Đói"] [herbalism.luck] - name = "May Mắn Thảo Dược" - description = "Khi phá Cỏ/Hoa, bạn có cơ hội nhận vật phẩm ngẫu nhiên" - lore0 = "Hoa = Thức Ăn, và Cỏ = Hạt Giống" - lore1 = "Cơ hội nhận vật phẩm từ phá Hoa" - lore2 = "Cơ hội nhận vật phẩm từ phá Cỏ" - lore = ["Hoa = Thức Ăn, và Cỏ = Hạt Giống", "Cơ hội nhận vật phẩm từ phá Hoa", "Cơ hội nhận vật phẩm từ phá Cỏ"] +name = "May Mắn Thảo Dược" +description = "Khi phá Cỏ/Hoa, bạn có cơ hội nhận vật phẩm ngẫu nhiên" +lore0 = "Hoa = Thức Ăn, và Cỏ = Hạt Giống" +lore1 = "Cơ hội nhận vật phẩm từ phá Hoa" +lore2 = "Cơ hội nhận vật phẩm từ phá Cỏ" +lore = ["Hoa = Thức Ăn, và Cỏ = Hạt Giống", "Cơ hội nhận vật phẩm từ phá Hoa", "Cơ hội nhận vật phẩm từ phá Cỏ"] [herbalism.replant] - name = "Thu Hoạch & Trồng Lại" - description = "Chuột phải vào cây trồng bằng cuốc để thu hoạch & trồng lại." - lore1 = "Bán Kính Trồng Lại" - lore = ["Bán Kính Trồng Lại"] +name = "Thu Hoạch & Trồng Lại" +description = "Chuột phải vào cây trồng bằng cuốc để thu hoạch & trồng lại." +lore1 = "Bán Kính Trồng Lại" +lore = ["Bán Kính Trồng Lại"] # hunter [hunter] [hunter.adrenaline] - name = "Adrenaline" - description = "Gây thêm sát thương khi máu bạn càng thấp (Cận Chiến)" - lore1 = "Sát Thương Tối Đa" - lore = ["Sát Thương Tối Đa"] +name = "Adrenaline" +description = "Gây thêm sát thương khi máu bạn càng thấp (Cận Chiến)" +lore1 = "Sát Thương Tối Đa" +lore = ["Sát Thương Tối Đa"] [hunter.penalty] - name = "" - description = "" - lore1 = "Bạn sẽ bị Độc nếu hết thanh đói" - lore = ["Bạn sẽ bị Độc nếu hết thanh đói"] +name = "" +description = "" +lore1 = "Bạn sẽ bị Độc nếu hết thanh đói" +lore = ["Bạn sẽ bị Độc nếu hết thanh đói"] [hunter.drop_to_inventory] - name = "Vật Phẩm Nhặt Vào Túi Đồ" - description = "Khi bạn giết thứ gì đó / Phá khối bằng kiếm, vật phẩm rơi sẽ dịch chuyển vào túi đồ" - lore1 = "Bất cứ khi nào vật phẩm rơi từ quái/khối bạn phá, nó sẽ vào túi đồ nếu có chỗ." - lore = ["Bất cứ khi nào vật phẩm rơi từ quái/khối bạn phá, nó sẽ vào túi đồ nếu có chỗ."] +name = "Vật Phẩm Nhặt Vào Túi Đồ" +description = "Khi bạn giết thứ gì đó / Phá khối bằng kiếm, vật phẩm rơi sẽ dịch chuyển vào túi đồ" +lore1 = "Bất cứ khi nào vật phẩm rơi từ quái/khối bạn phá, nó sẽ vào túi đồ nếu có chỗ." +lore = ["Bất cứ khi nào vật phẩm rơi từ quái/khối bạn phá, nó sẽ vào túi đồ nếu có chỗ."] [hunter.invisibility] - name = "Bước Biến Mất" - description = "Khi bị đánh, bạn nhận được tàng hình, với cái giá là đói" - lore1 = "Nhận tàng hình thụ động khi bị đánh" - lore2 = "x Cộng dồn tàng hình trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Thời lượng tàng hình" - lore = ["Nhận tàng hình thụ động khi bị đánh", "x Cộng dồn tàng hình trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Thời lượng tàng hình"] +name = "Bước Biến Mất" +description = "Khi bị đánh, bạn nhận được tàng hình, với cái giá là đói" +lore1 = "Nhận tàng hình thụ động khi bị đánh" +lore2 = "x Cộng dồn tàng hình trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Thời lượng tàng hình" +lore = ["Nhận tàng hình thụ động khi bị đánh", "x Cộng dồn tàng hình trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Thời lượng tàng hình"] [hunter.jump_boost] - name = "Đỉnh Cao Thợ Săn" - description = "Khi bị đánh, bạn nhận được tăng nhảy, với cái giá là đói" - lore1 = "Nhận tăng nhảy thụ động khi bị đánh" - lore2 = "x Cộng dồn tăng nhảy trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn tăng nhảy, không phải thời lượng." - lore = ["Nhận tăng nhảy thụ động khi bị đánh", "x Cộng dồn tăng nhảy trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn tăng nhảy, không phải thời lượng."] +name = "Đỉnh Cao Thợ Săn" +description = "Khi bị đánh, bạn nhận được tăng nhảy, với cái giá là đói" +lore1 = "Nhận tăng nhảy thụ động khi bị đánh" +lore2 = "x Cộng dồn tăng nhảy trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn tăng nhảy, không phải thời lượng." +lore = ["Nhận tăng nhảy thụ động khi bị đánh", "x Cộng dồn tăng nhảy trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn tăng nhảy, không phải thời lượng."] [hunter.luck] - name = "May Mắn Thợ Săn" - description = "Khi bị đánh, bạn nhận được may mắn, với cái giá là đói" - lore1 = "Nhận may mắn thụ động khi bị đánh" - lore2 = "x Cộng dồn may mắn trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn may mắn, không phải thời lượng." - lore = ["Nhận may mắn thụ động khi bị đánh", "x Cộng dồn may mắn trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn may mắn, không phải thời lượng."] +name = "May Mắn Thợ Săn" +description = "Khi bị đánh, bạn nhận được may mắn, với cái giá là đói" +lore1 = "Nhận may mắn thụ động khi bị đánh" +lore2 = "x Cộng dồn may mắn trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn may mắn, không phải thời lượng." +lore = ["Nhận may mắn thụ động khi bị đánh", "x Cộng dồn may mắn trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn may mắn, không phải thời lượng."] [hunter.regen] - name = "Hồi Phục Thợ Săn" - description = "Khi bị đánh, bạn nhận được hồi phục, với cái giá là đói" - lore1 = "Nhận hồi phục thụ động khi bị đánh" - lore2 = "x Cộng dồn hồi phục trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn hồi phục, không phải thời lượng." - lore = ["Nhận hồi phục thụ động khi bị đánh", "x Cộng dồn hồi phục trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn hồi phục, không phải thời lượng."] +name = "Hồi Phục Thợ Săn" +description = "Khi bị đánh, bạn nhận được hồi phục, với cái giá là đói" +lore1 = "Nhận hồi phục thụ động khi bị đánh" +lore2 = "x Cộng dồn hồi phục trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn hồi phục, không phải thời lượng." +lore = ["Nhận hồi phục thụ động khi bị đánh", "x Cộng dồn hồi phục trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn hồi phục, không phải thời lượng."] [hunter.resistance] - name = "Kháng Cự Thợ Săn" - description = "Khi bị đánh, bạn nhận được kháng cự, với cái giá là đói" - lore1 = "Nhận kháng cự thụ động khi bị đánh" - lore2 = "x Cộng dồn kháng cự trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn kháng cự, không phải thời lượng." - lore = ["Nhận kháng cự thụ động khi bị đánh", "x Cộng dồn kháng cự trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn kháng cự, không phải thời lượng."] +name = "Kháng Cự Thợ Săn" +description = "Khi bị đánh, bạn nhận được kháng cự, với cái giá là đói" +lore1 = "Nhận kháng cự thụ động khi bị đánh" +lore2 = "x Cộng dồn kháng cự trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn kháng cự, không phải thời lượng." +lore = ["Nhận kháng cự thụ động khi bị đánh", "x Cộng dồn kháng cự trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn kháng cự, không phải thời lượng."] [hunter.speed] - name = "Tốc Độ Thợ Săn" - description = "Khi bị đánh, bạn nhận được tốc độ, với cái giá là đói" - lore1 = "Nhận tốc độ thụ động khi bị đánh" - lore2 = "x Cộng dồn tốc độ trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn tốc độ, không phải thời lượng." - lore = ["Nhận tốc độ thụ động khi bị đánh", "x Cộng dồn tốc độ trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn tốc độ, không phải thời lượng."] +name = "Tốc Độ Thợ Săn" +description = "Khi bị đánh, bạn nhận được tốc độ, với cái giá là đói" +lore1 = "Nhận tốc độ thụ động khi bị đánh" +lore2 = "x Cộng dồn tốc độ trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn tốc độ, không phải thời lượng." +lore = ["Nhận tốc độ thụ động khi bị đánh", "x Cộng dồn tốc độ trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn tốc độ, không phải thời lượng."] [hunter.strength] - name = "Sức Mạnh Thợ Săn" - description = "Khi bị đánh, bạn nhận được sức mạnh, với cái giá là đói" - lore1 = "Nhận sức mạnh thụ động khi bị đánh" - lore2 = "x Cộng dồn sức mạnh trong 3 giây mỗi đòn" - lore3 = "x Cộng dồn đói" - lore4 = "Thời lượng và hệ số cộng dồn đói." - lore5 = "Hệ số cộng dồn sức mạnh, không phải thời lượng." - lore = ["Nhận sức mạnh thụ động khi bị đánh", "x Cộng dồn sức mạnh trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn sức mạnh, không phải thời lượng."] +name = "Sức Mạnh Thợ Săn" +description = "Khi bị đánh, bạn nhận được sức mạnh, với cái giá là đói" +lore1 = "Nhận sức mạnh thụ động khi bị đánh" +lore2 = "x Cộng dồn sức mạnh trong 3 giây mỗi đòn" +lore3 = "x Cộng dồn đói" +lore4 = "Thời lượng và hệ số cộng dồn đói." +lore5 = "Hệ số cộng dồn sức mạnh, không phải thời lượng." +lore = ["Nhận sức mạnh thụ động khi bị đánh", "x Cộng dồn sức mạnh trong 3 giây mỗi đòn", "x Cộng dồn đói", "Thời lượng và hệ số cộng dồn đói.", "Hệ số cộng dồn sức mạnh, không phải thời lượng."] # nether [nether] [nether.skull_toss] - name = "Ném Đầu Lâu Wither" - description1 = "Giải phóng Wither bên trong bạn bằng cách dùng" - description2 = "đầu" - description3 = "của ai đó." - lore1 = "Giây hồi giữa các lần ném đầu lâu." - lore2 = "Dùng Đầu Lâu Wither: Ném một " - lore3 = "Đầu Lâu Wither" - lore4 = "nổ khi va chạm." - lore = ["Giây hồi giữa các lần ném đầu lâu.", "Dùng Đầu Lâu Wither: Ném một ", "Đầu Lâu Wither", "nổ khi va chạm."] +name = "Ném Đầu Lâu Wither" +description1 = "Giải phóng Wither bên trong bạn bằng cách dùng" +description2 = "đầu" +description3 = "của ai đó." +lore1 = "Giây hồi giữa các lần ném đầu lâu." +lore2 = "Dùng Đầu Lâu Wither: Ném một " +lore3 = "Đầu Lâu Wither" +lore4 = "nổ khi va chạm." +lore = ["Giây hồi giữa các lần ném đầu lâu.", "Dùng Đầu Lâu Wither: Ném một ", "Đầu Lâu Wither", "nổ khi va chạm."] [nether.wither_resist] - name = "Kháng Wither" - description = "Kháng hiệu ứng héo úa nhờ sức mạnh của Netherite." - lore1 = "cơ hội vô hiệu hóa héo úa (mỗi mảnh giáp)." - lore2 = "Thụ động: Mặc Giáp Netherite có cơ hội vô hiệu hóa " - lore3 = "hiệu ứng héo úa." - lore = ["cơ hội vô hiệu hóa héo úa (mỗi mảnh giáp).", "Thụ động: Mặc Giáp Netherite có cơ hội vô hiệu hóa ", "hiệu ứng héo úa."] +name = "Kháng Wither" +description = "Kháng hiệu ứng héo úa nhờ sức mạnh của Netherite." +lore1 = "cơ hội vô hiệu hóa héo úa (mỗi mảnh giáp)." +lore2 = "Thụ động: Mặc Giáp Netherite có cơ hội vô hiệu hóa " +lore3 = "hiệu ứng héo úa." +lore = ["cơ hội vô hiệu hóa héo úa (mỗi mảnh giáp).", "Thụ động: Mặc Giáp Netherite có cơ hội vô hiệu hóa ", "hiệu ứng héo úa."] [nether.fire_resist] - name = "Kháng Lửa" - description = "Kháng lửa bằng cách làm cứng da." - lore1 = "cơ hội vô hiệu hóa hiệu ứng cháy!" - lore = ["cơ hội vô hiệu hóa hiệu ứng cháy!"] +name = "Kháng Lửa" +description = "Kháng lửa bằng cách làm cứng da." +lore1 = "cơ hội vô hiệu hóa hiệu ứng cháy!" +lore = ["cơ hội vô hiệu hóa hiệu ứng cháy!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "Tự Động Nung" - description = "Cho phép bạn nung quặng Vanilla khi khai thác" - lore1 = "Quặng có thể nung sẽ được nung tự động" - lore2 = "% cơ hội nhận thêm" - lore = ["Quặng có thể nung sẽ được nung tự động", "% cơ hội nhận thêm"] +name = "Tự Động Nung" +description = "Cho phép bạn nung quặng Vanilla khi khai thác" +lore1 = "Quặng có thể nung sẽ được nung tự động" +lore2 = "% cơ hội nhận thêm" +lore = ["Quặng có thể nung sẽ được nung tự động", "% cơ hội nhận thêm"] [pickaxe.chisel] - name = "Đục Quặng" - description = "Chuột Phải vào Quặng để Đục thêm quặng, với cái giá hao mòn độ bền cao." - lore1 = "Cơ Hội Rơi" - lore2 = "Hao Mòn Công Cụ" - lore = ["Cơ Hội Rơi", "Hao Mòn Công Cụ"] +name = "Đục Quặng" +description = "Chuột Phải vào Quặng để Đục thêm quặng, với cái giá hao mòn độ bền cao." +lore1 = "Cơ Hội Rơi" +lore2 = "Hao Mòn Công Cụ" +lore = ["Cơ Hội Rơi", "Hao Mòn Công Cụ"] [pickaxe.drop_to_inventory] - name = "Cuốc Chim Nhặt Vào Túi Đồ" - description = "Khi bạn phá khối, vật phẩm dịch chuyển vào túi đồ" - lore1 = "Bất cứ khi nào vật phẩm rơi từ khối bạn phá, nó sẽ vào túi đồ nếu có chỗ." - lore = ["Bất cứ khi nào vật phẩm rơi từ khối bạn phá, nó sẽ vào túi đồ nếu có chỗ."] +name = "Cuốc Chim Nhặt Vào Túi Đồ" +description = "Khi bạn phá khối, vật phẩm dịch chuyển vào túi đồ" +lore1 = "Bất cứ khi nào vật phẩm rơi từ khối bạn phá, nó sẽ vào túi đồ nếu có chỗ." +lore = ["Bất cứ khi nào vật phẩm rơi từ khối bạn phá, nó sẽ vào túi đồ nếu có chỗ."] [pickaxe.silk_spawner] - name = "Cuốc Chim Lấy Spawner" - description = "Spawner sẽ rơi ra khi bị phá" - lore1 = "Spawner có thể phá bằng Cảm Ứng Lụa." - lore2 = "Spawner có thể phá khi đang ngồi." - lore = ["Spawner có thể phá bằng Cảm Ứng Lụa.", "Spawner có thể phá khi đang ngồi."] +name = "Cuốc Chim Lấy Spawner" +description = "Spawner sẽ rơi ra khi bị phá" +lore1 = "Spawner có thể phá bằng Cảm Ứng Lụa." +lore2 = "Spawner có thể phá khi đang ngồi." +lore = ["Spawner có thể phá bằng Cảm Ứng Lụa.", "Spawner có thể phá khi đang ngồi."] [pickaxe.vein_miner] - name = "Đào Mạch Quặng" - description = "Cho phép bạn phá khối theo Mạch/Cụm quặng Vanilla" - lore1 = "Ngồi, và đào QUẶNG" - lore2 = "phạm vi đào mạch quặng" - lore3 = "Kỹ năng này KHÔNG gom tất cả vật phẩm rơi lại!" - lore = ["Ngồi, và đào QUẶNG", "phạm vi đào mạch quặng", "Kỹ năng này KHÔNG gom tất cả vật phẩm rơi lại!"] +name = "Đào Mạch Quặng" +description = "Cho phép bạn phá khối theo Mạch/Cụm quặng Vanilla" +lore1 = "Ngồi, và đào QUẶNG" +lore2 = "phạm vi đào mạch quặng" +lore3 = "Kỹ năng này KHÔNG gom tất cả vật phẩm rơi lại!" +lore = ["Ngồi, và đào QUẶNG", "phạm vi đào mạch quặng", "Kỹ năng này KHÔNG gom tất cả vật phẩm rơi lại!"] # ranged [ranged] [ranged.arrow_recovery] - name = "Thu Hồi Mũi Tên" - description = "Thu hồi Mũi Tên sau khi bạn tiêu diệt kẻ thù." - lore1 = "Cơ hội Thu Hồi Mũi Tên khi Trúng/Hạ Gục" - lore2 = "Cơ hội: " - lore = ["Cơ hội Thu Hồi Mũi Tên khi Trúng/Hạ Gục", "Cơ hội: "] +name = "Thu Hồi Mũi Tên" +description = "Thu hồi Mũi Tên sau khi bạn tiêu diệt kẻ thù." +lore1 = "Cơ hội Thu Hồi Mũi Tên khi Trúng/Hạ Gục" +lore2 = "Cơ hội: " +lore = ["Cơ hội Thu Hồi Mũi Tên khi Trúng/Hạ Gục", "Cơ hội: "] [ranged.web_shot] - name = "Bẫy Mạng Nhện" - description = "Bao quanh mục tiêu bằng mạng nhện khi bạn bắn trúng!" - lore1 = "8 Mạng Nhện quanh Cầu Tuyết, và ném!" - lore2 = "giây bẫy lồng, xấp xỉ." - lore = ["8 Mạng Nhện quanh Cầu Tuyết, và ném!", "giây bẫy lồng, xấp xỉ."] +name = "Bẫy Mạng Nhện" +description = "Bao quanh mục tiêu bằng mạng nhện khi bạn bắn trúng!" +lore1 = "8 Mạng Nhện quanh Cầu Tuyết, và ném!" +lore2 = "giây bẫy lồng, xấp xỉ." +lore = ["8 Mạng Nhện quanh Cầu Tuyết, và ném!", "giây bẫy lồng, xấp xỉ."] [ranged.force_shot] - name = "Phát Bắn Mạnh" - description = "Bắn đạn xa hơn, nhanh hơn!" - advancementname = "Phát Bắn Xa" - advancementlore = "Bắn trúng mục tiêu từ hơn 30 khối!" - lore1 = "Tốc Độ Đạn" - lore = ["Tốc Độ Đạn"] +name = "Phát Bắn Mạnh" +description = "Bắn đạn xa hơn, nhanh hơn!" +advancementname = "Phát Bắn Xa" +advancementlore = "Bắn trúng mục tiêu từ hơn 30 khối!" +lore1 = "Tốc Độ Đạn" +lore = ["Tốc Độ Đạn"] [ranged.lunge_shot] - name = "Phát Bắn Lao" - description = "Khi đang rơi, mũi tên đẩy bạn theo hướng ngẫu nhiên" - lore1 = "Tốc Độ Bùng Nổ Ngẫu Nhiên" - lore = ["Tốc Độ Bùng Nổ Ngẫu Nhiên"] +name = "Phát Bắn Lao" +description = "Khi đang rơi, mũi tên đẩy bạn theo hướng ngẫu nhiên" +lore1 = "Tốc Độ Bùng Nổ Ngẫu Nhiên" +lore = ["Tốc Độ Bùng Nổ Ngẫu Nhiên"] [ranged.arrow_piercing] - name = "Mũi Tên Xuyên Thấu" - description = "Thêm Xuyên Thấu cho đạn! Bắn xuyên qua mọi thứ!" - lore1 = "Mục Tiêu Xuyên Qua" - lore = ["Mục Tiêu Xuyên Qua"] +name = "Mũi Tên Xuyên Thấu" +description = "Thêm Xuyên Thấu cho đạn! Bắn xuyên qua mọi thứ!" +lore1 = "Mục Tiêu Xuyên Qua" +lore = ["Mục Tiêu Xuyên Qua"] # rift [rift] [rift.remote_access] - name = "Truy Cập Từ Xa" - description = "Kéo từ hư không và truy cập thùng chứa đã đánh dấu." - lore1 = "Ngọc Trai Ender + La Bàn = Chìa Khóa Thánh Tích" - lore2 = "Vật phẩm này cho phép bạn truy cập thùng chứa từ xa" - lore3 = "Sau khi chế tạo, nhìn vào vật phẩm để xem cách dùng" - notcontainer = "Đó không phải thùng chứa" - lore = ["Ngọc Trai Ender + La Bàn = Chìa Khóa Thánh Tích", "Vật phẩm này cho phép bạn truy cập thùng chứa từ xa", "Sau khi chế tạo, nhìn vào vật phẩm để xem cách dùng"] +name = "Truy Cập Từ Xa" +description = "Kéo từ hư không và truy cập thùng chứa đã đánh dấu." +lore1 = "Ngọc Trai Ender + La Bàn = Chìa Khóa Thánh Tích" +lore2 = "Vật phẩm này cho phép bạn truy cập thùng chứa từ xa" +lore3 = "Sau khi chế tạo, nhìn vào vật phẩm để xem cách dùng" +notcontainer = "Đó không phải thùng chứa" +lore = ["Ngọc Trai Ender + La Bàn = Chìa Khóa Thánh Tích", "Vật phẩm này cho phép bạn truy cập thùng chứa từ xa", "Sau khi chế tạo, nhìn vào vật phẩm để xem cách dùng"] [rift.blink] - name = "Chớp Nhoáng" - description = "Dịch chuyển tức thì tầm ngắn, Chỉ trong chớp mắt!" - lore1 = "Khối khi chớp (2x theo chiều dọc)" - lore2 = "Khi đang chạy nước rút: Nhấn đúp Nhảy để " - lore3 = "Chớp Nhoáng" - lore = ["Khối khi chớp (2x theo chiều dọc)", "Khi đang chạy nước rút: Nhấn đúp Nhảy để ", "Chớp Nhoáng"] +name = "Chớp Nhoáng" +description = "Dịch chuyển tức thì tầm ngắn, Chỉ trong chớp mắt!" +lore1 = "Khối khi chớp (2x theo chiều dọc)" +lore2 = "Khi đang chạy nước rút: Nhấn đúp Nhảy để " +lore3 = "Chớp Nhoáng" +lore = ["Khối khi chớp (2x theo chiều dọc)", "Khi đang chạy nước rút: Nhấn đúp Nhảy để ", "Chớp Nhoáng"] [rift.chest] - name = "Rương Ender Dễ Dàng" - description = "Mở rương ender bằng cách Chuột Trái vào nó trong tay." - lore1 = "Bấm vào Rương Ender trong tay để mở (Đừng đặt nó xuống)" - lore = ["Bấm vào Rương Ender trong tay để mở (Đừng đặt nó xuống)"] +name = "Rương Ender Dễ Dàng" +description = "Mở rương ender bằng cách Chuột Trái vào nó trong tay." +lore1 = "Bấm vào Rương Ender trong tay để mở (Đừng đặt nó xuống)" +lore = ["Bấm vào Rương Ender trong tay để mở (Đừng đặt nó xuống)"] [rift.descent] - name = "Chống Bay Lên" - description = "Bạn chán bị mắc kẹt trên không? Đây là kỹ năng dành cho bạn!" - lore1 = "Chỉ cần Ngồi để hạ xuống, bạn sẽ rơi chậm hơn bình thường!" - lore2 = "Thời Gian Hồi:" - lore = ["Chỉ cần Ngồi để hạ xuống, bạn sẽ rơi chậm hơn bình thường!", "Thời Gian Hồi:"] +name = "Chống Bay Lên" +description = "Bạn chán bị mắc kẹt trên không? Đây là kỹ năng dành cho bạn!" +lore1 = "Chỉ cần Ngồi để hạ xuống, bạn sẽ rơi chậm hơn bình thường!" +lore2 = "Thời Gian Hồi:" +lore = ["Chỉ cần Ngồi để hạ xuống, bạn sẽ rơi chậm hơn bình thường!", "Thời Gian Hồi:"] [rift.gate] - name = "Cổng Khe Nứt" - description = "Dịch chuyển đến vị trí đã đánh dấu." - lore1 = "CHẾ TẠO: Ngọc Lục Bảo + Mảnh Thạch Anh Tím + Ngọc Trai Ender" - lore2 = "Đọc trước khi dùng!" - lore3 = "Trì hoãn 5 giây, " - lore4 = "bạn có thể chết trong lúc thực hiện" - lore = ["CHẾ TẠO: Ngọc Lục Bảo + Mảnh Thạch Anh Tím + Ngọc Trai Ender", "Đọc trước khi dùng!", "Trì hoãn 5 giây, ", "bạn có thể chết trong lúc thực hiện"] +name = "Cổng Khe Nứt" +description = "Dịch chuyển đến vị trí đã đánh dấu." +lore1 = "CHẾ TẠO: Ngọc Lục Bảo + Mảnh Thạch Anh Tím + Ngọc Trai Ender" +lore2 = "Đọc trước khi dùng!" +lore3 = "Trì hoãn 5 giây, " +lore4 = "bạn có thể chết trong lúc thực hiện" +lore = ["CHẾ TẠO: Ngọc Lục Bảo + Mảnh Thạch Anh Tím + Ngọc Trai Ender", "Đọc trước khi dùng!", "Trì hoãn 5 giây, ", "bạn có thể chết trong lúc thực hiện"] [rift.resist] - name = "Kháng Khe Nứt" - description = "Nhận Kháng Cự khi sử dụng Vật Phẩm & Kỹ Năng Ender" - lore1 = "+ Thụ động: Cung cấp kháng cự khi bạn dùng kỹ năng khe nứt, hoặc Vật Phẩm Ender" - lore2 = "KHÔNG bao gồm Rương Ender Di Động, chỉ những thứ bạn có thể tiêu thụ" - lore = ["+ Thụ động: Cung cấp kháng cự khi bạn dùng kỹ năng khe nứt, hoặc Vật Phẩm Ender", "KHÔNG bao gồm Rương Ender Di Động, chỉ những thứ bạn có thể tiêu thụ"] +name = "Kháng Khe Nứt" +description = "Nhận Kháng Cự khi sử dụng Vật Phẩm & Kỹ Năng Ender" +lore1 = "+ Thụ động: Cung cấp kháng cự khi bạn dùng kỹ năng khe nứt, hoặc Vật Phẩm Ender" +lore2 = "KHÔNG bao gồm Rương Ender Di Động, chỉ những thứ bạn có thể tiêu thụ" +lore = ["+ Thụ động: Cung cấp kháng cự khi bạn dùng kỹ năng khe nứt, hoặc Vật Phẩm Ender", "KHÔNG bao gồm Rương Ender Di Động, chỉ những thứ bạn có thể tiêu thụ"] [rift.visage] - name = "Diện Mạo Khe Nứt" - description = "Ngăn Endermen trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ." - lore1 = "Endermen sẽ không trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ." - lore = ["Endermen sẽ không trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ."] +name = "Diện Mạo Khe Nứt" +description = "Ngăn Endermen trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ." +lore1 = "Endermen sẽ không trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ." +lore = ["Endermen sẽ không trở nên hung hãn nếu bạn có Ngọc Trai Ender trong túi đồ."] # seaborn [seaborn] [seaborn.oxygen] - name = "Bình Oxy Hữu Cơ" - description = "Chứa thêm oxy trong lá phổi nhỏ bé của bạn!" - lore1 = "Tăng Dung Tích Oxy" - lore = ["Tăng Dung Tích Oxy"] +name = "Bình Oxy Hữu Cơ" +description = "Chứa thêm oxy trong lá phổi nhỏ bé của bạn!" +lore1 = "Tăng Dung Tích Oxy" +lore = ["Tăng Dung Tích Oxy"] [seaborn.fishers_fantasy] - name = "Giấc Mơ Ngư Phủ" - description = "Kiếm thêm XP từ câu cá, và nhận nhiều cá hơn!" - lore1 = "Mỗi cấp độ có cơ hội nhận thêm XP và Cá!" - lore = ["Mỗi cấp độ có cơ hội nhận thêm XP và Cá!"] +name = "Giấc Mơ Ngư Phủ" +description = "Kiếm thêm XP từ câu cá, và nhận nhiều cá hơn!" +lore1 = "Mỗi cấp độ có cơ hội nhận thêm XP và Cá!" +lore = ["Mỗi cấp độ có cơ hội nhận thêm XP và Cá!"] [seaborn.haste] - name = "Thợ Mỏ Rùa" - description = "Khi đào dưới nước, bạn nhận được tốc độ đào!" - lore1 = "Haste 3 áp dụng dưới nước khi đào (cộng dồn AquaAffinity) sau khi hiệu ứng thở dưới nước hết!" - lore = ["Haste 3 áp dụng dưới nước khi đào (cộng dồn AquaAffinity) sau khi hiệu ứng thở dưới nước hết!"] +name = "Thợ Mỏ Rùa" +description = "Khi đào dưới nước, bạn nhận được tốc độ đào!" +lore1 = "Haste 3 áp dụng dưới nước khi đào (cộng dồn AquaAffinity) sau khi hiệu ứng thở dưới nước hết!" +lore = ["Haste 3 áp dụng dưới nước khi đào (cộng dồn AquaAffinity) sau khi hiệu ứng thở dưới nước hết!"] [seaborn.night_vision] - name = "Tầm Nhìn Rùa" - description = "Khi ở dưới nước, bạn nhận được Nhìn Đêm" - lore1 = "Đơn giản nhận Nhìn Đêm khi ở dưới nước sau khi hiệu ứng thở dưới nước hết!" - lore = ["Đơn giản nhận Nhìn Đêm khi ở dưới nước sau khi hiệu ứng thở dưới nước hết!"] +name = "Tầm Nhìn Rùa" +description = "Khi ở dưới nước, bạn nhận được Nhìn Đêm" +lore1 = "Đơn giản nhận Nhìn Đêm khi ở dưới nước sau khi hiệu ứng thở dưới nước hết!" +lore = ["Đơn giản nhận Nhìn Đêm khi ở dưới nước sau khi hiệu ứng thở dưới nước hết!"] [seaborn.dolphin_grace] - name = "Ân Sủng Cá Heo" - description = "Bơi như cá heo, không cần cá heo" - lore1 = "+ Thụ động: nhận " - lore2 = "x tốc độ (ân sủng cá heo)" - lore3 = "kỹ thuật chính xác kiểu Đức- khoan, không đúng... Không tương thích với Depth Strider" - lore = ["+ Thụ động: nhận ", "x tốc độ (ân sủng cá heo)", "kỹ thuật chính xác kiểu Đức- khoan, không đúng... Không tương thích với Depth Strider"] +name = "Ân Sủng Cá Heo" +description = "Bơi như cá heo, không cần cá heo" +lore1 = "+ Thụ động: nhận " +lore2 = "x tốc độ (ân sủng cá heo)" +lore3 = "kỹ thuật chính xác kiểu Đức- khoan, không đúng... Không tương thích với Depth Strider" +lore = ["+ Thụ động: nhận ", "x tốc độ (ân sủng cá heo)", "kỹ thuật chính xác kiểu Đức- khoan, không đúng... Không tương thích với Depth Strider"] # stealth [stealth] [stealth.ghost_armor] - name = "Giáp Ma" - description = "Giáp tăng dần khi không nhận sát thương, chỉ chịu được 1 đòn" - lore1 = "Giáp Tối Đa" - lore2 = "Tốc Độ" - lore = ["Giáp Tối Đa", "Tốc Độ"] +name = "Giáp Ma" +description = "Giáp tăng dần khi không nhận sát thương, chỉ chịu được 1 đòn" +lore1 = "Giáp Tối Đa" +lore2 = "Tốc Độ" +lore = ["Giáp Tối Đa", "Tốc Độ"] [stealth.night_vision] - name = "Nhìn Đêm Tàng Hình" - description = "Nhận nhìn đêm khi ngồi" - lore1 = "Nhận một đợt " - lore2 = "nhìn đêm" - lore3 = "khi đang ngồi" - lore = ["Nhận một đợt ", "nhìn đêm", "khi đang ngồi"] +name = "Nhìn Đêm Tàng Hình" +description = "Nhận nhìn đêm khi ngồi" +lore1 = "Nhận một đợt " +lore2 = "nhìn đêm" +lore3 = "khi đang ngồi" +lore = ["Nhận một đợt ", "nhìn đêm", "khi đang ngồi"] [stealth.snatch] - name = "Cướp Vật Phẩm" - description = "Cướp vật phẩm rơi tức thì khi đang ngồi!" - lore1 = "Bán Kính Cướp" - lore = ["Bán Kính Cướp"] +name = "Cướp Vật Phẩm" +description = "Cướp vật phẩm rơi tức thì khi đang ngồi!" +lore1 = "Bán Kính Cướp" +lore = ["Bán Kính Cướp"] [stealth.speed] - name = "Tốc Độ Rón Rén" - description = "Nhận tốc độ khi đang ngồi" - lore1 = "Tốc Độ Rón Rén" - lore = ["Tốc Độ Rón Rén"] +name = "Tốc Độ Rón Rén" +description = "Nhận tốc độ khi đang ngồi" +lore1 = "Tốc Độ Rón Rén" +lore = ["Tốc Độ Rón Rén"] [stealth.ender_veil] - name = "Màn Che Ender" - description = "Không cần Bí Ngô để tránh Enderman tấn công nữa" - lore1 = "Ngăn Enderman tấn công khi đang ngồi" - lore2 = "Ngăn tất cả Enderman tấn công" - lore = ["Ngăn Enderman tấn công khi đang ngồi", "Ngăn tất cả Enderman tấn công"] +name = "Màn Che Ender" +description = "Không cần Bí Ngô để tránh Enderman tấn công nữa" +lore1 = "Ngăn Enderman tấn công khi đang ngồi" +lore2 = "Ngăn tất cả Enderman tấn công" +lore = ["Ngăn Enderman tấn công khi đang ngồi", "Ngăn tất cả Enderman tấn công"] # sword [sword] [sword.machete] - name = "Dao Rựa" - description = "Cắt qua tán lá dễ dàng!" - lore1 = "Bán Kính Chém" - lore2 = "Thời Gian Hồi Chặt" - lore3 = "Hao Mòn Công Cụ" - lore = ["Bán Kính Chém", "Thời Gian Hồi Chặt", "Hao Mòn Công Cụ"] +name = "Dao Rựa" +description = "Cắt qua tán lá dễ dàng!" +lore1 = "Bán Kính Chém" +lore2 = "Thời Gian Hồi Chặt" +lore3 = "Hao Mòn Công Cụ" +lore = ["Bán Kính Chém", "Thời Gian Hồi Chặt", "Hao Mòn Công Cụ"] [sword.bloody_blade] - name = "Lưỡi Kiếm Đẫm Máu" - description = "Đòn kiếm gây ra Chảy Máu!" - lore1 = "Tấn công thực thể sống bằng Kiếm gây Chảy Máu" - lore2 = "Thời Lượng Chảy Máu" - lore3 = "Thời Gian Hồi Chảy Máu" - lore = ["Tấn công thực thể sống bằng Kiếm gây Chảy Máu", "Thời Lượng Chảy Máu", "Thời Gian Hồi Chảy Máu"] +name = "Lưỡi Kiếm Đẫm Máu" +description = "Đòn kiếm gây ra Chảy Máu!" +lore1 = "Tấn công thực thể sống bằng Kiếm gây Chảy Máu" +lore2 = "Thời Lượng Chảy Máu" +lore3 = "Thời Gian Hồi Chảy Máu" +lore = ["Tấn công thực thể sống bằng Kiếm gây Chảy Máu", "Thời Lượng Chảy Máu", "Thời Gian Hồi Chảy Máu"] [sword.poisoned_blade] - name = "Lưỡi Kiếm Tẩm Độc" - description = "Đòn kiếm gây ra Ngộ Độc!" - lore1 = "Tấn công thực thể sống bằng Kiếm gây Ngộ Độc" - lore2 = "Thời Lượng Ngộ Độc" - lore3 = "Thời Gian Hồi Ngộ Độc" - lore = ["Tấn công thực thể sống bằng Kiếm gây Ngộ Độc", "Thời Lượng Ngộ Độc", "Thời Gian Hồi Ngộ Độc"] +name = "Lưỡi Kiếm Tẩm Độc" +description = "Đòn kiếm gây ra Ngộ Độc!" +lore1 = "Tấn công thực thể sống bằng Kiếm gây Ngộ Độc" +lore2 = "Thời Lượng Ngộ Độc" +lore3 = "Thời Gian Hồi Ngộ Độc" +lore = ["Tấn công thực thể sống bằng Kiếm gây Ngộ Độc", "Thời Lượng Ngộ Độc", "Thời Gian Hồi Ngộ Độc"] # taming [taming] [taming.damage] - name = "Sát Thương Thú Nuôi" - description = "Tăng sát thương thú nuôi đã thuần hóa." - lore1 = "Tăng Sát Thương" - lore = ["Tăng Sát Thương"] +name = "Sát Thương Thú Nuôi" +description = "Tăng sát thương thú nuôi đã thuần hóa." +lore1 = "Tăng Sát Thương" +lore = ["Tăng Sát Thương"] [taming.health] - name = "Máu Thú Nuôi" - description = "Tăng máu thú nuôi đã thuần hóa." - lore1 = "Tăng Máu" - lore = ["Tăng Máu"] +name = "Máu Thú Nuôi" +description = "Tăng máu thú nuôi đã thuần hóa." +lore1 = "Tăng Máu" +lore = ["Tăng Máu"] [taming.regeneration] - name = "Hồi Phục Thú Nuôi" - description = "Tăng tốc độ hồi phục thú nuôi đã thuần hóa." - lore1 = "HP/giây" - lore = ["HP/giây"] +name = "Hồi Phục Thú Nuôi" +description = "Tăng tốc độ hồi phục thú nuôi đã thuần hóa." +lore1 = "HP/giây" +lore = ["HP/giây"] # tragoul [tragoul] [tragoul.thorns] - name = "Gai Nhọn" - description = "Phản sát thương lại kẻ tấn công!" - lore1 = "Sát thương phản đòn khi bị đánh" - lore = ["Sát thương phản đòn khi bị đánh"] +name = "Gai Nhọn" +description = "Phản sát thương lại kẻ tấn công!" +lore1 = "Sát thương phản đòn khi bị đánh" +lore = ["Sát thương phản đòn khi bị đánh"] [tragoul.globe] - name = "Cầu Đau Đớn" - description = "Chia sát thương bạn gây ra dựa trên số kẻ thù xung quanh!" - lore1 = "Càng nhiều kẻ thù xung quanh, sát thương cho mỗi kẻ càng giảm" - lore2 = "Phạm Vi: " - lore3 = "Sát Thương Thêm cho tất cả Thực Thể: " - lore = ["Càng nhiều kẻ thù xung quanh, sát thương cho mỗi kẻ càng giảm", "Phạm Vi: ", "Sát Thương Thêm cho tất cả Thực Thể: "] +name = "Cầu Đau Đớn" +description = "Chia sát thương bạn gây ra dựa trên số kẻ thù xung quanh!" +lore1 = "Càng nhiều kẻ thù xung quanh, sát thương cho mỗi kẻ càng giảm" +lore2 = "Phạm Vi: " +lore3 = "Sát Thương Thêm cho tất cả Thực Thể: " +lore = ["Càng nhiều kẻ thù xung quanh, sát thương cho mỗi kẻ càng giảm", "Phạm Vi: ", "Sát Thương Thêm cho tất cả Thực Thể: "] [tragoul.healing] - name = "Ý Chí Đau Đớn" - description = "Hồi máu dựa trên sát thương bạn gây ra!" - lore1 = "Gây đau cho kẻ khác chưa bao giờ dễ chịu đến thế! Hồi máu từ Sát Thương Gây Ra" - lore2 = "Có khoảng thời gian sát thương 3 Giây để hồi máu và 1 Giây thời gian hồi " - lore3 = "Phần Trăm Hồi Máu Mỗi Sát Thương: " - lore = ["Gây đau cho kẻ khác chưa bao giờ dễ chịu đến thế! Hồi máu từ Sát Thương Gây Ra", "Có khoảng thời gian sát thương 3 Giây để hồi máu và 1 Giây thời gian hồi ", "Phần Trăm Hồi Máu Mỗi Sát Thương: "] +name = "Ý Chí Đau Đớn" +description = "Hồi máu dựa trên sát thương bạn gây ra!" +lore1 = "Gây đau cho kẻ khác chưa bao giờ dễ chịu đến thế! Hồi máu từ Sát Thương Gây Ra" +lore2 = "Có khoảng thời gian sát thương 3 Giây để hồi máu và 1 Giây thời gian hồi " +lore3 = "Phần Trăm Hồi Máu Mỗi Sát Thương: " +lore = ["Gây đau cho kẻ khác chưa bao giờ dễ chịu đến thế! Hồi máu từ Sát Thương Gây Ra", "Có khoảng thời gian sát thương 3 Giây để hồi máu và 1 Giây thời gian hồi ", "Phần Trăm Hồi Máu Mỗi Sát Thương: "] [tragoul.lance] - name = "Thương Xác Chết" - description = "Giết kẻ thù hoặc kỹ năng giết kẻ thù sẽ tạo ra thương gây sát thương cho kẻ thù gần đó!" - lore1 = "Thương sẽ phóng từ bất cứ thứ gì bạn giết, VÀ nếu Kỹ Năng này giết kẻ thù." - lore2 = "Hy sinh một phần sinh mệnh để tạo thương (có thể giết bạn)" - lore3 = "Số Thương Tối Đa: 1 + " - lore = ["Thương sẽ phóng từ bất cứ thứ gì bạn giết, VÀ nếu Kỹ Năng này giết kẻ thù.", "Hy sinh một phần sinh mệnh để tạo thương (có thể giết bạn)", "Số Thương Tối Đa: 1 + "] +name = "Thương Xác Chết" +description = "Giết kẻ thù hoặc kỹ năng giết kẻ thù sẽ tạo ra thương gây sát thương cho kẻ thù gần đó!" +lore1 = "Thương sẽ phóng từ bất cứ thứ gì bạn giết, VÀ nếu Kỹ Năng này giết kẻ thù." +lore2 = "Hy sinh một phần sinh mệnh để tạo thương (có thể giết bạn)" +lore3 = "Số Thương Tối Đa: 1 + " +lore = ["Thương sẽ phóng từ bất cứ thứ gì bạn giết, VÀ nếu Kỹ Năng này giết kẻ thù.", "Hy sinh một phần sinh mệnh để tạo thương (có thể giết bạn)", "Số Thương Tối Đa: 1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "Đại Bác Thủy Tinh" - description = "Sát thương Tay Không tăng thêm khi giá trị giáp càng thấp" - lore1 = "x Sát thương ở 0 giáp" - lore2 = "Sát Thương Thêm Mỗi Cấp" - lore = ["x Sát thương ở 0 giáp", "Sát Thương Thêm Mỗi Cấp"] +name = "Đại Bác Thủy Tinh" +description = "Sát thương Tay Không tăng thêm khi giá trị giáp càng thấp" +lore1 = "x Sát thương ở 0 giáp" +lore2 = "Sát Thương Thêm Mỗi Cấp" +lore = ["x Sát thương ở 0 giáp", "Sát Thương Thêm Mỗi Cấp"] [unarmed.power] - name = "Sức Mạnh Tay Không" - description = "Tăng Sát Thương Tay Không" - lore1 = "Sát Thương" - lore = ["Sát Thương"] +name = "Sức Mạnh Tay Không" +description = "Tăng Sát Thương Tay Không" +lore1 = "Sát Thương" +lore = ["Sát Thương"] [unarmed.sucker_punch] - name = "Cú Đấm Bất Ngờ" - description = "Cú đấm khi chạy nước rút, nhưng chết chóc hơn." - lore1 = "Sát Thương" - lore2 = "Sát thương tăng theo tốc độ khi đấm" - lore = ["Sát Thương", "Sát thương tăng theo tốc độ khi đấm"] +name = "Cú Đấm Bất Ngờ" +description = "Cú đấm khi chạy nước rút, nhưng chết chóc hơn." +lore1 = "Sát Thương" +lore2 = "Sát thương tăng theo tốc độ khi đấm" +lore = ["Sát Thương", "Sát thương tăng theo tốc độ khi đấm"] diff --git a/src/main/resources/zh_CN.toml b/src/main/resources/zh_CN.toml index 6d848197b..afdcf3d8f 100644 --- a/src/main/resources/zh_CN.toml +++ b/src/main/resources/zh_CN.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "动起来!" - description = "走过总计 1 千米的距离。(1,000 格方块)" +title = "动起来!" +description = "走过总计 1 千米的距离。(1,000 格方块)" [advancement.challenge_sprint_5k] - title = "长跑运动员" - description = "走过总计 5 千米的距离。(5,000 格方块)" +title = "长跑运动员" +description = "走过总计 5 千米的距离。(5,000 格方块)" [advancement.challenge_sprint_50k] - title = "五万变焦" - description = "走过总计 50 千米的距离。(50,000 格方块)" +title = "五万变焦" +description = "走过总计 50 千米的距离。(50,000 格方块)" [advancement.challenge_sprint_500k] - title = "穿越宇宙!!" - description = "走过总计 500 千米的距离。(500,000 格方块)" +title = "穿越宇宙!!" +description = "走过总计 500 千米的距离。(500,000 格方块)" [advancement.challenge_sprint_marathon] - title = "疾跑(真正的)马拉松!" - description = "以疾跑的方式跑过 42,195 格方块!" +title = "疾跑(真正的)马拉松!" +description = "以疾跑的方式跑过 42,195 格方块!" [advancement.challenge_place_1k] - title = "建筑师入门!" - description = "放置 1,000 个方块" +title = "建筑师入门!" +description = "放置 1,000 个方块" [advancement.challenge_place_5k] - title = "中级建筑师!" - description = "放置 5,000 个方块" +title = "中级建筑师!" +description = "放置 5,000 个方块" [advancement.challenge_place_50k] - title = "高级建筑师!" - description = "放置 50,000 个方块" +title = "高级建筑师!" +description = "放置 50,000 个方块" [advancement.challenge_place_500k] - title = "建筑大师!" - description = "放置 500,000 个方块" +title = "建筑大师!" +description = "放置 500,000 个方块" [advancement.challenge_place_5m] - title = "对称之徒!" - description = "现实是你的游乐场!(五百万个方块)" +title = "对称之徒!" +description = "现实是你的游乐场!(五百万个方块)" [advancement.challenge_chop_1k] - title = "伐木工入门!" - description = "砍伐 1,000 个方块" +title = "伐木工入门!" +description = "砍伐 1,000 个方块" [advancement.challenge_chop_5k] - title = "中级伐木工!" - description = "砍伐 5,000 个方块" +title = "中级伐木工!" +description = "砍伐 5,000 个方块" [advancement.challenge_chop_50k] - title = "高级伐木工!" - description = "砍伐 50,000 个方块" +title = "高级伐木工!" +description = "砍伐 50,000 个方块" [advancement.challenge_chop_500k] - title = "伐木大师!" - description = "砍伐 500,000 个方块" +title = "伐木大师!" +description = "砍伐 500,000 个方块" [advancement.challenge_chop_5m] - title = "狗狗杰克逊" - description = "最棒的好孩子!(五百万个方块)" +title = "狗狗杰克逊" +description = "最棒的好孩子!(五百万个方块)" [advancement.challenge_block_1k] - title = "防御入门!" - description = "防御 1,000 次攻击" +title = "防御入门!" +description = "防御 1,000 次攻击" [advancement.challenge_block_5k] - title = "防御之乐!" - description = "防御 5,000 次攻击" +title = "防御之乐!" +description = "防御 5,000 次攻击" [advancement.challenge_block_50k] - title = "防御即人生!" - description = "防御 50,000 次攻击" +title = "防御即人生!" +description = "防御 50,000 次攻击" [advancement.challenge_block_500k] - title = "防御即使命!" - description = "防御 500,000 次攻击" +title = "防御即使命!" +description = "防御 500,000 次攻击" [advancement.challenge_block_5m] - title = "伤人之手" - description = "防御 5,000,000 次攻击" +title = "伤人之手" +description = "防御 5,000,000 次攻击" [advancement.challenge_brew_1k] - title = "炼金术士入门!" - description = "饮用 1,000 瓶药水" +title = "炼金术士入门!" +description = "饮用 1,000 瓶药水" [advancement.challenge_brew_5k] - title = "中级炼金术士!" - description = "饮用 5,000 瓶药水" +title = "中级炼金术士!" +description = "饮用 5,000 瓶药水" [advancement.challenge_brew_50k] - title = "高级炼金术士!" - description = "饮用 50,000 瓶药水" +title = "高级炼金术士!" +description = "饮用 50,000 瓶药水" [advancement.challenge_brew_500k] - title = "炼金大师!" - description = "饮用 500,000 瓶药水" +title = "炼金大师!" +description = "饮用 500,000 瓶药水" [advancement.challenge_brew_5m] - title = "炼金师" - description = "饮用 5,000,000 瓶药水" +title = "炼金师" +description = "饮用 5,000,000 瓶药水" [advancement.challenge_brewsplash_1k] - title = "喷溅入门!" - description = "投掷 1,000 瓶喷溅药水" +title = "喷溅入门!" +description = "投掷 1,000 瓶喷溅药水" [advancement.challenge_brewsplash_5k] - title = "中级喷溅者!" - description = "投掷 5,000 瓶喷溅药水" +title = "中级喷溅者!" +description = "投掷 5,000 瓶喷溅药水" [advancement.challenge_brewsplash_50k] - title = "高级喷溅者!" - description = "投掷 50,000 瓶喷溅药水" +title = "高级喷溅者!" +description = "投掷 50,000 瓶喷溅药水" [advancement.challenge_brewsplash_500k] - title = "喷溅大师!" - description = "投掷 500,000 瓶喷溅药水" +title = "喷溅大师!" +description = "投掷 500,000 瓶喷溅药水" [advancement.challenge_brewsplash_5m] - title = "投掷宗师" - description = "投掷 5,000,000 瓶喷溅药水" +title = "投掷宗师" +description = "投掷 5,000,000 瓶喷溅药水" [advancement.challenge_craft_1k] - title = "精妙合成!" - description = "合成 1,000 个物品" +title = "精妙合成!" +description = "合成 1,000 个物品" [advancement.challenge_craft_5k] - title = "暴躁合成!" - description = "合成 5,000 个物品" +title = "暴躁合成!" +description = "合成 5,000 个物品" [advancement.challenge_craft_50k] - title = "勤勉合成!" - description = "合成 50,000 个物品" +title = "勤勉合成!" +description = "合成 50,000 个物品" [advancement.challenge_craft_500k] - title = "巧夺天工!" - description = "合成 500,000 个物品" +title = "巧夺天工!" +description = "合成 500,000 个物品" [advancement.challenge_craft_5m] - title = "灾厄合成脸" - description = "合成 5,000,000 个物品" +title = "灾厄合成脸" +description = "合成 5,000,000 个物品" [advancement.challenge_enchant_1k] - title = "附魔入门!" - description = "附魔 1,000 件物品" +title = "附魔入门!" +description = "附魔 1,000 件物品" [advancement.challenge_enchant_5k] - title = "中级附魔师!" - description = "附魔 5,000 件物品" +title = "中级附魔师!" +description = "附魔 5,000 件物品" [advancement.challenge_enchant_50k] - title = "高级附魔师!" - description = "附魔 50,000 件物品" +title = "高级附魔师!" +description = "附魔 50,000 件物品" [advancement.challenge_enchant_500k] - title = "附魔大师!" - description = "附魔 500,000 件物品" +title = "附魔大师!" +description = "附魔 500,000 件物品" [advancement.challenge_enchant_5m] - title = "神秘附魔师" - description = "附魔 5,000,000 件物品" +title = "神秘附魔师" +description = "附魔 5,000,000 件物品" [advancement.challenge_excavate_1k] - title = "挖掘入门!" - description = "挖掘 1,000 个方块" +title = "挖掘入门!" +description = "挖掘 1,000 个方块" [advancement.challenge_excavate_5k] - title = "中级挖掘者!" - description = "挖掘 5,000 个方块" +title = "中级挖掘者!" +description = "挖掘 5,000 个方块" [advancement.challenge_excavate_50k] - title = "高级挖掘者!" - description = "挖掘 50,000 个方块" +title = "高级挖掘者!" +description = "挖掘 50,000 个方块" [advancement.challenge_excavate_500k] - title = "挖掘大师!" - description = "挖掘 500,000 个方块" +title = "挖掘大师!" +description = "挖掘 500,000 个方块" [advancement.challenge_excavate_5m] - title = "神秘挖掘者" - description = "挖掘 5,000,000 个方块" +title = "神秘挖掘者" +description = "挖掘 5,000,000 个方块" [advancement.horrible_person] - title = "你真是个可怕的人" - description = "不可思议,真的" +title = "你真是个可怕的人" +description = "不可思议,真的" [advancement.challenge_turtle_egg_smasher] - title = "海龟蛋破坏者!" - description = "破坏 100 个海龟蛋" +title = "海龟蛋破坏者!" +description = "破坏 100 个海龟蛋" [advancement.challenge_turtle_egg_annihilator] - title = "海龟蛋歼灭者!" - description = "破坏 500 个海龟蛋" +title = "海龟蛋歼灭者!" +description = "破坏 500 个海龟蛋" [advancement.challenge_novice_hunter] - title = "狩猎入门!" - description = "击杀 100 个实体" +title = "狩猎入门!" +description = "击杀 100 个实体" [advancement.challenge_intermediate_hunter] - title = "中级猎人!" - description = "击杀 500 个实体" +title = "中级猎人!" +description = "击杀 500 个实体" [advancement.challenge_advanced_hunter] - title = "高级猎人!" - description = "击杀 5,000 个实体" +title = "高级猎人!" +description = "击杀 5,000 个实体" [advancement.challenge_creeper_conqueror] - title = "苦力怕征服者!" - description = "击杀 50 只苦力怕" +title = "苦力怕征服者!" +description = "击杀 50 只苦力怕" [advancement.challenge_creeper_annihilator] - title = "苦力怕歼灭者!" - description = "击杀 200 只苦力怕" +title = "苦力怕歼灭者!" +description = "击杀 200 只苦力怕" [advancement.challenge_pickaxe_1k] - title = "初级矿工" - description = "破坏 1,000 个方块" +title = "初级矿工" +description = "破坏 1,000 个方块" [advancement.challenge_pickaxe_5k] - title = "熟练矿工" - description = "破坏 5,000 个方块" +title = "熟练矿工" +description = "破坏 5,000 个方块" [advancement.challenge_pickaxe_50k] - title = "专家矿工" - description = "破坏 50,000 个方块" +title = "专家矿工" +description = "破坏 50,000 个方块" [advancement.challenge_pickaxe_500k] - title = "矿工大师" - description = "破坏 500,000 个方块" +title = "矿工大师" +description = "破坏 500,000 个方块" [advancement.challenge_pickaxe_5m] - title = "传奇矿工" - description = "破坏 5,000,000 个方块" +title = "传奇矿工" +description = "破坏 5,000,000 个方块" [advancement.challenge_eat_100] - title = "大快朵颐!" - description = "吃掉超过 100 个食物!" +title = "大快朵颐!" +description = "吃掉超过 100 个食物!" [advancement.challenge_eat_1000] - title = "永无止境的饥饿!" - description = "吃掉超过 1,000 个食物!" +title = "永无止境的饥饿!" +description = "吃掉超过 1,000 个食物!" [advancement.challenge_eat_10000] - title = "饕餮之饥!" - description = "吃掉超过 10,000 个食物!" +title = "饕餮之饥!" +description = "吃掉超过 10,000 个食物!" [advancement.challenge_harvest_100] - title = "丰收时节" - description = "收获超过 100 种农作物!" +title = "丰收时节" +description = "收获超过 100 种农作物!" [advancement.challenge_harvest_1000] - title = "大丰收" - description = "收获超过 1,000 种农作物!" +title = "大丰收" +description = "收获超过 1,000 种农作物!" [advancement.challenge_swim_1nm] - title = "人体潜艇!" - description = "游泳 1 海里(1,852 格方块)" +title = "人体潜艇!" +description = "游泳 1 海里(1,852 格方块)" [advancement.challenge_sneak_1k] - title = "膝盖疼痛" - description = "潜行超过一千米(1,000 格方块)" +title = "膝盖疼痛" +description = "潜行超过一千米(1,000 格方块)" [advancement.challenge_sneak_5k] - title = "暗影行者" - description = "潜行超过 5,000 格方块" +title = "暗影行者" +description = "潜行超过 5,000 格方块" [advancement.challenge_sneak_20k] - title = "幽灵" - description = "潜行超过 20,000 格方块" +title = "幽灵" +description = "潜行超过 20,000 格方块" [advancement.challenge_swim_5k] - title = "深海潜水员" - description = "游泳超过 5,000 格方块" +title = "深海潜水员" +description = "游泳超过 5,000 格方块" [advancement.challenge_swim_20k] - title = "海神之选" - description = "游泳超过 20,000 格方块" +title = "海神之选" +description = "游泳超过 20,000 格方块" [advancement.challenge_sword_100] - title = "初见血光" - description = "用剑命中 100 次" +title = "初见血光" +description = "用剑命中 100 次" [advancement.challenge_sword_1k] - title = "剑舞者" - description = "用剑命中 1,000 次" +title = "剑舞者" +description = "用剑命中 1,000 次" [advancement.challenge_sword_10k] - title = "千刀万剐" - description = "用剑命中 10,000 次" +title = "千刀万剐" +description = "用剑命中 10,000 次" [advancement.challenge_unarmed_100] - title = "酒吧打手" - description = "徒手命中 100 次" +title = "酒吧打手" +description = "徒手命中 100 次" [advancement.challenge_unarmed_1k] - title = "铁拳" - description = "徒手命中 1,000 次" +title = "铁拳" +description = "徒手命中 1,000 次" [advancement.challenge_unarmed_10k] - title = "一拳超人" - description = "徒手命中 10,000 次" +title = "一拳超人" +description = "徒手命中 10,000 次" [advancement.challenge_trag_1k] - title = "血的代价" - description = "承受 1,000 点伤害" +title = "血的代价" +description = "承受 1,000 点伤害" [advancement.challenge_trag_10k] - title = "赤潮" - description = "承受 10,000 点伤害" +title = "赤潮" +description = "承受 10,000 点伤害" [advancement.challenge_trag_100k] - title = "苦难化身" - description = "承受 100,000 点伤害" +title = "苦难化身" +description = "承受 100,000 点伤害" [advancement.challenge_ranged_100] - title = "靶场练习" - description = "发射 100 枚弹射物" +title = "靶场练习" +description = "发射 100 枚弹射物" [advancement.challenge_ranged_1k] - title = "鹰眼" - description = "发射 1,000 枚弹射物" +title = "鹰眼" +description = "发射 1,000 枚弹射物" [advancement.challenge_ranged_10k] - title = "箭雨风暴" - description = "发射 10,000 枚弹射物" +title = "箭雨风暴" +description = "发射 10,000 枚弹射物" [advancement.challenge_chronos_1h] - title = "滴答滴答" - description = "在线 1 小时" +title = "滴答滴答" +description = "在线 1 小时" [advancement.challenge_chronos_24h] - title = "时间之沙" - description = "在线 24 小时" +title = "时间之沙" +description = "在线 24 小时" [advancement.challenge_chronos_168h] - title = "永恒不朽" - description = "在线 168 小时(1 周)" +title = "永恒不朽" +description = "在线 168 小时(1 周)" [advancement.challenge_nether_50] - title = "地狱守门人" - description = "击杀 50 只下界生物" +title = "地狱守门人" +description = "击杀 50 只下界生物" [advancement.challenge_nether_500] - title = "深渊守望者" - description = "击杀 500 只下界生物" +title = "深渊守望者" +description = "击杀 500 只下界生物" [advancement.challenge_nether_5k] - title = "下界之主" - description = "击杀 5,000 只下界生物" +title = "下界之主" +description = "击杀 5,000 只下界生物" [advancement.challenge_rift_50] - title = "空间异常" - description = "传送 50 次" +title = "空间异常" +description = "传送 50 次" [advancement.challenge_rift_500] - title = "虚空行者" - description = "传送 500 次" +title = "虚空行者" +description = "传送 500 次" [advancement.challenge_rift_5k] - title = "穿梭世界" - description = "传送 5,000 次" +title = "穿梭世界" +description = "传送 5,000 次" [advancement.challenge_taming_10] - title = "动物低语者" - description = "繁殖 10 只动物" +title = "动物低语者" +description = "繁殖 10 只动物" [advancement.challenge_taming_50] - title = "兽群首领" - description = "繁殖 50 只动物" +title = "兽群首领" +description = "繁殖 50 只动物" [advancement.challenge_taming_500] - title = "驯兽大师" - description = "繁殖 500 只动物" +title = "驯兽大师" +description = "繁殖 500 只动物" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "速度恶魔" - description = "疾跑超过 5 公里 (5,000 格)" +title = "速度恶魔" +description = "疾跑超过 5 公里 (5,000 格)" [advancement.challenge_sprint_dist_50k] - title = "闪电之足" - description = "疾跑超过 50 公里 (50,000 格)" +title = "闪电之足" +description = "疾跑超过 50 公里 (50,000 格)" [advancement.challenge_agility_swim_1k] - title = "水上行者" - description = "游泳超过 1 公里 (1,000 格)" +title = "水上行者" +description = "游泳超过 1 公里 (1,000 格)" [advancement.challenge_agility_swim_10k] - title = "远洋航行者" - description = "游泳超过 10 公里 (10,000 格)" +title = "远洋航行者" +description = "游泳超过 10 公里 (10,000 格)" [advancement.challenge_fly_1k] - title = "翱翔之舞" - description = "飞行超过 1 公里 (1,000 格)" +title = "翱翔之舞" +description = "飞行超过 1 公里 (1,000 格)" [advancement.challenge_fly_10k] - title = "御风者" - description = "飞行超过 10 公里 (10,000 格)" +title = "御风者" +description = "飞行超过 10 公里 (10,000 格)" [advancement.challenge_agility_sneak_500] - title = "轻步潜行" - description = "潜行超过 500 格" +title = "轻步潜行" +description = "潜行超过 500 格" [advancement.challenge_agility_sneak_5k] - title = "幽灵步伐" - description = "潜行超过 5 公里 (5,000 格)" +title = "幽灵步伐" +description = "潜行超过 5 公里 (5,000 格)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "拆除小队" - description = "破坏 500 个方块" +title = "拆除小队" +description = "破坏 500 个方块" [advancement.challenge_demolish_5k] - title = "毁灭之球" - description = "破坏 5,000 个方块" +title = "毁灭之球" +description = "破坏 5,000 个方块" [advancement.challenge_value_placed_10k] - title = "价值建造者" - description = "放置价值 10,000 的方块" +title = "价值建造者" +description = "放置价值 10,000 的方块" [advancement.challenge_value_placed_100k] - title = "建筑大师" - description = "放置价值 100,000 的方块" +title = "建筑大师" +description = "放置价值 100,000 的方块" [advancement.challenge_demolish_val_5k] - title = "回收专家" - description = "从拆除中回收 5,000 方块价值" +title = "回收专家" +description = "从拆除中回收 5,000 方块价值" [advancement.challenge_demolish_val_50k] - title = "彻底解构" - description = "从拆除中回收 50,000 方块价值" +title = "彻底解构" +description = "从拆除中回收 50,000 方块价值" [advancement.challenge_high_build_100] - title = "高空建造者" - description = "在 Y=128 以上放置 100 个方块" +title = "高空建造者" +description = "在 Y=128 以上放置 100 个方块" [advancement.challenge_high_build_1k] - title = "云端建筑师" - description = "在 Y=128 以上放置 1,000 个方块" +title = "云端建筑师" +description = "在 Y=128 以上放置 1,000 个方块" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "挥斧者" - description = "挥舞斧头 500 次" +title = "挥斧者" +description = "挥舞斧头 500 次" [advancement.challenge_axe_swing_5k] - title = "狂战士" - description = "挥舞斧头 5,000 次" +title = "狂战士" +description = "挥舞斧头 5,000 次" [advancement.challenge_axe_damage_1k] - title = "劈裂者" - description = "用斧头造成 1,000 伤害" +title = "劈裂者" +description = "用斧头造成 1,000 伤害" [advancement.challenge_axe_damage_10k] - title = "刽子手之斧" - description = "用斧头造成 10,000 伤害" +title = "刽子手之斧" +description = "用斧头造成 10,000 伤害" [advancement.challenge_axe_value_5k] - title = "木材商人" - description = "采伐价值 5,000 的木材" +title = "木材商人" +description = "采伐价值 5,000 的木材" [advancement.challenge_axe_value_50k] - title = "伐木大亨" - description = "采伐价值 50,000 的木材" +title = "伐木大亨" +description = "采伐价值 50,000 的木材" [advancement.challenge_leaves_500] - title = "吹叶机" - description = "用斧头清除 500 个树叶方块" +title = "吹叶机" +description = "用斧头清除 500 个树叶方块" [advancement.challenge_leaves_5k] - title = "落叶清除者" - description = "用斧头清除 5,000 个树叶方块" +title = "落叶清除者" +description = "用斧头清除 5,000 个树叶方块" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "伤害吸收者" - description = "用盾牌格挡 1,000 伤害" +title = "伤害吸收者" +description = "用盾牌格挡 1,000 伤害" [advancement.challenge_block_dmg_10k] - title = "人肉盾牌" - description = "用盾牌格挡 10,000 伤害" +title = "人肉盾牌" +description = "用盾牌格挡 10,000 伤害" [advancement.challenge_block_proj_100] - title = "箭矢偏转者" - description = "用盾牌格挡 100 个投射物" +title = "箭矢偏转者" +description = "用盾牌格挡 100 个投射物" [advancement.challenge_block_proj_1k] - title = "投射物盾牌" - description = "用盾牌格挡 1,000 个投射物" +title = "投射物盾牌" +description = "用盾牌格挡 1,000 个投射物" [advancement.challenge_block_melee_500] - title = "格挡大师" - description = "用盾牌格挡 500 次近战攻击" +title = "格挡大师" +description = "用盾牌格挡 500 次近战攻击" [advancement.challenge_block_melee_5k] - title = "铁壁堡垒" - description = "用盾牌格挡 5,000 次近战攻击" +title = "铁壁堡垒" +description = "用盾牌格挡 5,000 次近战攻击" [advancement.challenge_block_heavy_50] - title = "坦克" - description = "格挡 50 次重击 (超过 5 伤害)" +title = "坦克" +description = "格挡 50 次重击 (超过 5 伤害)" [advancement.challenge_block_heavy_500] - title = "不动如山" - description = "格挡 500 次重击 (超过 5 伤害)" +title = "不动如山" +description = "格挡 500 次重击 (超过 5 伤害)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "酿造起步" - description = "放置 10 个酿造台" +title = "酿造起步" +description = "放置 10 个酿造台" [advancement.challenge_brew_stands_50] - title = "药水工厂" - description = "放置 50 个酿造台" +title = "药水工厂" +description = "放置 50 个酿造台" [advancement.challenge_brew_strong_25] - title = "烈性药剂" - description = "饮用 25 瓶强化药水" +title = "烈性药剂" +description = "饮用 25 瓶强化药水" [advancement.challenge_brew_strong_250] - title = "极致药效" - description = "饮用 250 瓶强化药水" +title = "极致药效" +description = "饮用 250 瓶强化药水" [advancement.challenge_brew_splash_hits_50] - title = "飞溅区域" - description = "用喷溅药水命中 50 个实体" +title = "飞溅区域" +description = "用喷溅药水命中 50 个实体" [advancement.challenge_brew_splash_hits_500] - title = "瘟疫医生" - description = "用喷溅药水命中 500 个实体" +title = "瘟疫医生" +description = "用喷溅药水命中 500 个实体" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "永不停歇" - description = "活跃时行进 1 公里" +title = "永不停歇" +description = "活跃时行进 1 公里" [advancement.challenge_active_dist_10k] - title = "探路先锋" - description = "活跃时行进 10 公里" +title = "探路先锋" +description = "活跃时行进 10 公里" [advancement.challenge_active_dist_100k] - title = "永动之力" - description = "活跃时行进 100 公里" +title = "永动之力" +description = "活跃时行进 100 公里" [advancement.challenge_beds_10] - title = "早起鸟儿" - description = "在床上睡觉 10 次" +title = "早起鸟儿" +description = "在床上睡觉 10 次" [advancement.challenge_beds_100] - title = "时间跳跃者" - description = "在床上睡觉 100 次" +title = "时间跳跃者" +description = "在床上睡觉 100 次" [advancement.challenge_chronos_tp_50] - title = "时空转移" - description = "传送 50 次" +title = "时空转移" +description = "传送 50 次" [advancement.challenge_chronos_tp_500] - title = "时间扭曲" - description = "传送 500 次" +title = "时间扭曲" +description = "传送 500 次" [advancement.challenge_chronos_deaths_10] - title = "凡人" - description = "死亡 10 次" +title = "凡人" +description = "死亡 10 次" [advancement.challenge_chronos_deaths_100] - title = "死亡挑战者" - description = "死亡 100 次" +title = "死亡挑战者" +description = "死亡 100 次" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "匠心独运" - description = "制作总价值 10,000 的物品" +title = "匠心独运" +description = "制作总价值 10,000 的物品" [advancement.challenge_craft_value_100k] - title = "工匠大师" - description = "制作总价值 100,000 的物品" +title = "工匠大师" +description = "制作总价值 100,000 的物品" [advancement.challenge_craft_tools_25] - title = "工具匠" - description = "制作 25 件工具" +title = "工具匠" +description = "制作 25 件工具" [advancement.challenge_craft_tools_250] - title = "锻造大师" - description = "制作 250 件工具" +title = "锻造大师" +description = "制作 250 件工具" [advancement.challenge_craft_armor_25] - title = "铠甲匠" - description = "制作 25 件护甲" +title = "铠甲匠" +description = "制作 25 件护甲" [advancement.challenge_craft_armor_250] - title = "护甲大师" - description = "制作 250 件护甲" +title = "护甲大师" +description = "制作 250 件护甲" [advancement.challenge_craft_mass_25k] - title = "批量生产" - description = "制作 25,000 个物品" +title = "批量生产" +description = "制作 25,000 个物品" [advancement.challenge_craft_mass_250k] - title = "工业革命" - description = "制作 250,000 个物品" +title = "工业革命" +description = "制作 250,000 个物品" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "收集者" - description = "发现 50 种独特物品" +title = "收集者" +description = "发现 50 种独特物品" [advancement.challenge_discover_items_250] - title = "编目者" - description = "发现 250 种独特物品" +title = "编目者" +description = "发现 250 种独特物品" [advancement.challenge_discover_blocks_50] - title = "勘测员" - description = "发现 50 种独特方块" +title = "勘测员" +description = "发现 50 种独特方块" [advancement.challenge_discover_blocks_250] - title = "地质学家" - description = "发现 250 种独特方块" +title = "地质学家" +description = "发现 250 种独特方块" [advancement.challenge_discover_mobs_25] - title = "观察者" - description = "发现 25 种独特生物" +title = "观察者" +description = "发现 25 种独特生物" [advancement.challenge_discover_mobs_75] - title = "博物学家" - description = "发现 75 种独特生物" +title = "博物学家" +description = "发现 75 种独特生物" [advancement.challenge_discover_biomes_10] - title = "漫游者" - description = "发现 10 种独特生物群系" +title = "漫游者" +description = "发现 10 种独特生物群系" [advancement.challenge_discover_biomes_40] - title = "环球旅行家" - description = "发现 40 种独特生物群系" +title = "环球旅行家" +description = "发现 40 种独特生物群系" [advancement.challenge_discover_foods_10] - title = "美食家" - description = "发现 10 种独特食物" +title = "美食家" +description = "发现 10 种独特食物" [advancement.challenge_discover_foods_30] - title = "烹饪大师" - description = "发现 30 种独特食物" +title = "烹饪大师" +description = "发现 30 种独特食物" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "织力者" - description = "积累 100 附魔之力" +title = "织力者" +description = "积累 100 附魔之力" [advancement.challenge_enchant_power_1k] - title = "奥术宗师" - description = "积累 1,000 附魔之力" +title = "奥术宗师" +description = "积累 1,000 附魔之力" [advancement.challenge_enchant_levels_1k] - title = "等级消耗者" - description = "在附魔上花费 1,000 经验等级" +title = "等级消耗者" +description = "在附魔上花费 1,000 经验等级" [advancement.challenge_enchant_levels_10k] - title = "经验黑洞" - description = "在附魔上花费 10,000 经验等级" +title = "经验黑洞" +description = "在附魔上花费 10,000 经验等级" [advancement.challenge_enchant_high_25] - title = "豪赌者" - description = "进行 25 次最高级附魔" +title = "豪赌者" +description = "进行 25 次最高级附魔" [advancement.challenge_enchant_high_250] - title = "传奇附魔师" - description = "进行 250 次最高级附魔" +title = "传奇附魔师" +description = "进行 250 次最高级附魔" [advancement.challenge_enchant_total_500] - title = "等级燃烧者" - description = "在所有附魔上累计花费 500 等级" +title = "等级燃烧者" +description = "在所有附魔上累计花费 500 等级" [advancement.challenge_enchant_total_5k] - title = "奥术投资" - description = "在所有附魔上累计花费 5,000 等级" +title = "奥术投资" +description = "在所有附魔上累计花费 5,000 等级" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "挖掘者" - description = "挥舞铲子 500 次" +title = "挖掘者" +description = "挥舞铲子 500 次" [advancement.challenge_dig_swing_5k] - title = "挖掘机" - description = "挥舞铲子 5,000 次" +title = "挖掘机" +description = "挥舞铲子 5,000 次" [advancement.challenge_dig_damage_1k] - title = "铲骑士" - description = "用铲子造成 1,000 伤害" +title = "铲骑士" +description = "用铲子造成 1,000 伤害" [advancement.challenge_dig_damage_10k] - title = "铲王" - description = "用铲子造成 10,000 伤害" +title = "铲王" +description = "用铲子造成 10,000 伤害" [advancement.challenge_dig_value_5k] - title = "泥土商人" - description = "挖掘价值 5,000 的方块" +title = "泥土商人" +description = "挖掘价值 5,000 的方块" [advancement.challenge_dig_value_50k] - title = "大地领主" - description = "挖掘价值 50,000 的方块" +title = "大地领主" +description = "挖掘价值 50,000 的方块" [advancement.challenge_dig_gravel_500] - title = "砾石研磨者" - description = "挖掘 500 个砾石、沙子或粘土方块" +title = "砾石研磨者" +description = "挖掘 500 个砾石、沙子或粘土方块" [advancement.challenge_dig_gravel_5k] - title = "沙砾筛选者" - description = "挖掘 5,000 个砾石、沙子或粘土方块" +title = "沙砾筛选者" +description = "挖掘 5,000 个砾石、沙子或粘土方块" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "播种者" - description = "种植 100 株作物" +title = "播种者" +description = "种植 100 株作物" [advancement.challenge_plant_1k] - title = "绿手指" - description = "种植 1,000 株作物" +title = "绿手指" +description = "种植 1,000 株作物" [advancement.challenge_plant_5k] - title = "农业大亨" - description = "种植 5,000 株作物" +title = "农业大亨" +description = "种植 5,000 株作物" [advancement.challenge_compost_50] - title = "回收利用者" - description = "堆肥 50 个物品" +title = "回收利用者" +description = "堆肥 50 个物品" [advancement.challenge_compost_500] - title = "土壤改良者" - description = "堆肥 500 个物品" +title = "土壤改良者" +description = "堆肥 500 个物品" [advancement.challenge_shear_50] - title = "剪毛者" - description = "剪切 50 个实体" +title = "剪毛者" +description = "剪切 50 个实体" [advancement.challenge_shear_250] - title = "牧群之主" - description = "剪切 250 个实体" +title = "牧群之主" +description = "剪切 250 个实体" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "屠戮者" - description = "击杀 500 个生物" +title = "屠戮者" +description = "击杀 500 个生物" [advancement.challenge_kills_5k] - title = "行刑者" - description = "击杀 5,000 个生物" +title = "行刑者" +description = "击杀 5,000 个生物" [advancement.challenge_boss_1] - title = "挑战首领" - description = "击杀一个首领怪物" +title = "挑战首领" +description = "击杀一个首领怪物" [advancement.challenge_boss_10] - title = "传奇杀手" - description = "击杀 10 个首领怪物" +title = "传奇杀手" +description = "击杀 10 个首领怪物" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "凋零之痛" - description = "承受 500 凋零伤害" +title = "凋零之痛" +description = "承受 500 凋零伤害" [advancement.challenge_wither_dmg_5k] - title = "枯萎幸存者" - description = "承受 5,000 凋零伤害" +title = "枯萎幸存者" +description = "承受 5,000 凋零伤害" [advancement.challenge_wither_skel_25] - title = "骸骨收集者" - description = "击杀 25 个凋灵骷髅" +title = "骸骨收集者" +description = "击杀 25 个凋灵骷髅" [advancement.challenge_wither_skel_250] - title = "骷髅克星" - description = "击杀 250 个凋灵骷髅" +title = "骷髅克星" +description = "击杀 250 个凋灵骷髅" [advancement.challenge_wither_boss_1] - title = "凋灵杀手" - description = "击败凋灵" +title = "凋灵杀手" +description = "击败凋灵" [advancement.challenge_wither_boss_10] - title = "下界主宰" - description = "击败凋灵 10 次" +title = "下界主宰" +description = "击败凋灵 10 次" [advancement.challenge_roses_10] - title = "死亡园丁" - description = "破坏 10 朵凋灵玫瑰" +title = "死亡园丁" +description = "破坏 10 朵凋灵玫瑰" [advancement.challenge_roses_100] - title = "枯萎收集者" - description = "破坏 100 朵凋灵玫瑰" +title = "枯萎收集者" +description = "破坏 100 朵凋灵玫瑰" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "矿工之臂" - description = "挥舞镐子 500 次" +title = "矿工之臂" +description = "挥舞镐子 500 次" [advancement.challenge_pick_swing_5k] - title = "隧道挖掘者" - description = "挥舞镐子 5,000 次" +title = "隧道挖掘者" +description = "挥舞镐子 5,000 次" [advancement.challenge_pick_damage_1k] - title = "镐战者" - description = "用镐子造成 1,000 伤害" +title = "镐战者" +description = "用镐子造成 1,000 伤害" [advancement.challenge_pick_damage_10k] - title = "战镐" - description = "用镐子造成 10,000 伤害" +title = "战镐" +description = "用镐子造成 10,000 伤害" [advancement.challenge_pick_value_5k] - title = "宝石探索者" - description = "开采价值 5,000 的方块" +title = "宝石探索者" +description = "开采价值 5,000 的方块" [advancement.challenge_pick_value_50k] - title = "矿石大亨" - description = "开采价值 50,000 的方块" +title = "矿石大亨" +description = "开采价值 50,000 的方块" [advancement.challenge_pick_ores_500] - title = "勘探者" - description = "开采 500 个矿石方块" +title = "勘探者" +description = "开采 500 个矿石方块" [advancement.challenge_pick_ores_5k] - title = "采矿大师" - description = "开采 5,000 个矿石方块" +title = "采矿大师" +description = "开采 5,000 个矿石方块" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "神射手" - description = "造成 1,000 远程伤害" +title = "神射手" +description = "造成 1,000 远程伤害" [advancement.challenge_ranged_dmg_10k] - title = "致命弓手" - description = "造成 10,000 远程伤害" +title = "致命弓手" +description = "造成 10,000 远程伤害" [advancement.challenge_ranged_dist_5k] - title = "远距射击" - description = "发射投射物总飞行距离达 5,000 格" +title = "远距射击" +description = "发射投射物总飞行距离达 5,000 格" [advancement.challenge_ranged_dist_50k] - title = "千里射手" - description = "发射投射物总飞行距离达 50,000 格" +title = "千里射手" +description = "发射投射物总飞行距离达 50,000 格" [advancement.challenge_ranged_kills_50] - title = "弓箭手" - description = "用远程武器击杀 50 个生物" +title = "弓箭手" +description = "用远程武器击杀 50 个生物" [advancement.challenge_ranged_kills_500] - title = "射术宗师" - description = "用远程武器击杀 500 个生物" +title = "射术宗师" +description = "用远程武器击杀 500 个生物" [advancement.challenge_longshot_25] - title = "狙击手" - description = "命中 25 次远距射击 (超过 30 格)" +title = "狙击手" +description = "命中 25 次远距射击 (超过 30 格)" [advancement.challenge_longshot_250] - title = "鹰眼" - description = "命中 250 次远距射击 (超过 30 格)" +title = "鹰眼" +description = "命中 250 次远距射击 (超过 30 格)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "珍珠投掷者" - description = "投掷 50 颗末影珍珠" +title = "珍珠投掷者" +description = "投掷 50 颗末影珍珠" [advancement.challenge_rift_pearls_500] - title = "传送狂人" - description = "投掷 500 颗末影珍珠" +title = "传送狂人" +description = "投掷 500 颗末影珍珠" [advancement.challenge_rift_enderman_50] - title = "末影人猎手" - description = "击杀 50 个末影人" +title = "末影人猎手" +description = "击杀 50 个末影人" [advancement.challenge_rift_enderman_500] - title = "虚空追猎者" - description = "击杀 500 个末影人" +title = "虚空追猎者" +description = "击杀 500 个末影人" [advancement.challenge_rift_dragon_500] - title = "屠龙战士" - description = "对末影龙造成 500 伤害" +title = "屠龙战士" +description = "对末影龙造成 500 伤害" [advancement.challenge_rift_dragon_5k] - title = "屠龙者" - description = "对末影龙造成 5,000 伤害" +title = "屠龙者" +description = "对末影龙造成 5,000 伤害" [advancement.challenge_rift_crystal_10] - title = "水晶破坏者" - description = "摧毁 10 个末地水晶" +title = "水晶破坏者" +description = "摧毁 10 个末地水晶" [advancement.challenge_rift_crystal_100] - title = "末地毁灭者" - description = "摧毁 100 个末地水晶" +title = "末地毁灭者" +description = "摧毁 100 个末地水晶" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "垂钓者" - description = "捕获 25 条鱼" +title = "垂钓者" +description = "捕获 25 条鱼" [advancement.challenge_fish_250] - title = "钓鱼大师" - description = "捕获 250 条鱼" +title = "钓鱼大师" +description = "捕获 250 条鱼" [advancement.challenge_drowned_25] - title = "溺尸猎人" - description = "击杀 25 个溺尸" +title = "溺尸猎人" +description = "击杀 25 个溺尸" [advancement.challenge_drowned_250] - title = "海洋净化者" - description = "击杀 250 个溺尸" +title = "海洋净化者" +description = "击杀 250 个溺尸" [advancement.challenge_guardian_10] - title = "守卫者猎手" - description = "击杀 10 个守卫者" +title = "守卫者猎手" +description = "击杀 10 个守卫者" [advancement.challenge_guardian_100] - title = "神殿掠夺者" - description = "击杀 100 个守卫者" +title = "神殿掠夺者" +description = "击杀 100 个守卫者" [advancement.challenge_underwater_blocks_100] - title = "水下矿工" - description = "在水下破坏 100 个方块" +title = "水下矿工" +description = "在水下破坏 100 个方块" [advancement.challenge_underwater_blocks_1k] - title = "水下工程师" - description = "在水下破坏 1,000 个方块" +title = "水下工程师" +description = "在水下破坏 1,000 个方块" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "背刺者" - description = "潜行时造成 500 伤害" +title = "背刺者" +description = "潜行时造成 500 伤害" [advancement.challenge_stealth_dmg_5k] - title = "无声杀手" - description = "潜行时造成 5,000 伤害" +title = "无声杀手" +description = "潜行时造成 5,000 伤害" [advancement.challenge_stealth_kills_10] - title = "刺客" - description = "潜行时击杀 10 个生物" +title = "刺客" +description = "潜行时击杀 10 个生物" [advancement.challenge_stealth_kills_100] - title = "暗影死神" - description = "潜行时击杀 100 个生物" +title = "暗影死神" +description = "潜行时击杀 100 个生物" [advancement.challenge_stealth_time_1h] - title = "耐心者" - description = "潜行 1 小时 (3,600 秒)" +title = "耐心者" +description = "潜行 1 小时 (3,600 秒)" [advancement.challenge_stealth_time_10h] - title = "暗影之主" - description = "潜行 10 小时 (36,000 秒)" +title = "暗影之主" +description = "潜行 10 小时 (36,000 秒)" [advancement.challenge_stealth_arrows_50] - title = "暗箭手" - description = "潜行时射出 50 支箭" +title = "暗箭手" +description = "潜行时射出 50 支箭" [advancement.challenge_stealth_arrows_500] - title = "幽灵弓手" - description = "潜行时射出 500 支箭" +title = "幽灵弓手" +description = "潜行时射出 500 支箭" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "剑术学徒" - description = "用剑造成 1,000 伤害" +title = "剑术学徒" +description = "用剑造成 1,000 伤害" [advancement.challenge_sword_dmg_10k] - title = "剑客" - description = "用剑造成 10,000 伤害" +title = "剑客" +description = "用剑造成 10,000 伤害" [advancement.challenge_sword_kills_50] - title = "决斗者" - description = "用剑击杀 50 个生物" +title = "决斗者" +description = "用剑击杀 50 个生物" [advancement.challenge_sword_kills_500] - title = "角斗士" - description = "用剑击杀 500 个生物" +title = "角斗士" +description = "用剑击杀 500 个生物" [advancement.challenge_sword_crit_50] - title = "暴击者" - description = "用剑造成 50 次暴击" +title = "暴击者" +description = "用剑造成 50 次暴击" [advancement.challenge_sword_crit_500] - title = "精准大师" - description = "用剑造成 500 次暴击" +title = "精准大师" +description = "用剑造成 500 次暴击" [advancement.challenge_sword_heavy_25] - title = "重击者" - description = "用剑造成 25 次重击 (超过 8 伤害)" +title = "重击者" +description = "用剑造成 25 次重击 (超过 8 伤害)" [advancement.challenge_sword_heavy_250] - title = "毁灭之击" - description = "用剑造成 250 次重击 (超过 8 伤害)" +title = "毁灭之击" +description = "用剑造成 250 次重击 (超过 8 伤害)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "驯兽师" - description = "你的宠物共造成 500 伤害" +title = "驯兽师" +description = "你的宠物共造成 500 伤害" [advancement.challenge_pet_dmg_5k] - title = "战争统帅" - description = "你的宠物共造成 5,000 伤害" +title = "战争统帅" +description = "你的宠物共造成 5,000 伤害" [advancement.challenge_tamed_10] - title = "动物之友" - description = "驯服 10 只动物" +title = "动物之友" +description = "驯服 10 只动物" [advancement.challenge_tamed_100] - title = "动物园长" - description = "驯服 100 只动物" +title = "动物园长" +description = "驯服 100 只动物" [advancement.challenge_pet_kills_25] - title = "群攻战术" - description = "你的宠物击杀 25 个生物" +title = "群攻战术" +description = "你的宠物击杀 25 个生物" [advancement.challenge_pet_kills_250] - title = "首领指挥官" - description = "你的宠物击杀 250 个生物" +title = "首领指挥官" +description = "你的宠物击杀 250 个生物" [advancement.challenge_taming_2500] - title = "繁殖专家" - description = "繁殖 2,500 只动物" +title = "繁殖专家" +description = "繁殖 2,500 只动物" [advancement.challenge_taming_25k] - title = "基因大师" - description = "繁殖 25,000 只动物" +title = "基因大师" +description = "繁殖 25,000 只动物" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "沙袋" - description = "受到 500 次攻击" +title = "沙袋" +description = "受到 500 次攻击" [advancement.challenge_trag_hits_5k] - title = "受虐狂" - description = "受到 5,000 次攻击" +title = "受虐狂" +description = "受到 5,000 次攻击" [advancement.challenge_trag_deaths_10] - title = "九条命" - description = "死亡 10 次" +title = "九条命" +description = "死亡 10 次" [advancement.challenge_trag_deaths_100] - title = "反复噩梦" - description = "死亡 100 次" +title = "反复噩梦" +description = "死亡 100 次" [advancement.challenge_trag_fire_500] - title = "火焰受害者" - description = "承受 500 火焰伤害" +title = "火焰受害者" +description = "承受 500 火焰伤害" [advancement.challenge_trag_fire_5k] - title = "浴火凤凰" - description = "承受 5,000 火焰伤害" +title = "浴火凤凰" +description = "承受 5,000 火焰伤害" [advancement.challenge_trag_fall_500] - title = "重力测试" - description = "承受 500 跌落伤害" +title = "重力测试" +description = "承受 500 跌落伤害" [advancement.challenge_trag_fall_5k] - title = "极限速度" - description = "承受 5,000 跌落伤害" +title = "极限速度" +description = "承受 5,000 跌落伤害" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "拳击手" - description = "用空手造成 1,000 伤害" +title = "拳击手" +description = "用空手造成 1,000 伤害" [advancement.challenge_unarmed_dmg_10k] - title = "武术家" - description = "用空手造成 10,000 伤害" +title = "武术家" +description = "用空手造成 10,000 伤害" [advancement.challenge_unarmed_kills_25] - title = "赤手空拳" - description = "用空手击杀 25 个生物" +title = "赤手空拳" +description = "用空手击杀 25 个生物" [advancement.challenge_unarmed_kills_250] - title = "传奇之拳" - description = "用空手击杀 250 个生物" +title = "传奇之拳" +description = "用空手击杀 250 个生物" [advancement.challenge_unarmed_crit_25] - title = "致命一拳" - description = "用空手造成 25 次暴击" +title = "致命一拳" +description = "用空手造成 25 次暴击" [advancement.challenge_unarmed_crit_250] - title = "精准之拳" - description = "用空手造成 250 次暴击" +title = "精准之拳" +description = "用空手造成 250 次暴击" [advancement.challenge_unarmed_heavy_25] - title = "力量之拳" - description = "用空手造成 25 次重击 (超过 6 伤害)" +title = "力量之拳" +description = "用空手造成 25 次重击 (超过 6 伤害)" [advancement.challenge_unarmed_heavy_250] - title = "拳王" - description = "用空手造成 250 次重击 (超过 6 伤害)" +title = "拳王" +description = "用空手造成 250 次重击 (超过 6 伤害)" # items [items] [items.bound_ender_peral] - name = "遗物传送钥" - usage1 = "潜行 + 左键点击绑定" - usage2 = "右键点击访问已绑定的物品栏" +name = "遗物传送钥" +usage1 = "潜行 + 左键点击绑定" +usage2 = "右键点击访问已绑定的物品栏" [items.bound_eye_of_ender] - name = "视觉锚点" - usage1 = "右键点击消耗此物品并传送至绑定位置" - usage2 = "潜行 + 左键点击方块以绑定" +name = "视觉锚点" +usage1 = "右键点击消耗此物品并传送至绑定位置" +usage2 = "潜行 + 左键点击方块以绑定" [items.bound_redstone_torch] - name = "红石遥控器" - usage1 = "右键点击创建一个 1 Tick 的红石脉冲" - usage2 = "潜行 + 左键点击'目标'方块以绑定" +name = "红石遥控器" +usage1 = "右键点击创建一个 1 Tick 的红石脉冲" +usage2 = "潜行 + 左键点击'目标'方块以绑定" [items.bound_snowball] - name = "蛛网陷阱!" - usage1 = "投掷以在目标位置创建一个临时蛛网陷阱" +name = "蛛网陷阱!" +usage1 = "投掷以在目标位置创建一个临时蛛网陷阱" [items.chrono_time_bottle] - name = "时光之瓶" - usage1 = "放在物品栏中时会自动储存时间" - usage2 = "右键点击计时方块或幼年动物以消耗储存的时间" - stored = "已储存时间" +name = "时光之瓶" +usage1 = "放在物品栏中时会自动储存时间" +usage2 = "右键点击计时方块或幼年动物以消耗储存的时间" +stored = "已储存时间" [items.chrono_time_bomb] - name = "时间炸弹" - usage1 = "右键点击发射一颗时空弹,制造一个时间力场" +name = "时间炸弹" +usage1 = "右键点击发射一颗时空弹,制造一个时间力场" [items.elevator_block] - name = "电梯方块" - usage1 = "跳跃向上传送" - usage2 = "潜行向下传送" - usage3 = "电梯之间至少需要 2 个空气方块" +name = "电梯方块" +usage1 = "跳跃向上传送" +usage2 = "潜行向下传送" +usage3 = "电梯之间至少需要 2 个空气方块" # snippets [snippets] [snippets.gui] - level = "等级" - knowledge = "知识" - power_used = "已用能力" - not_learned = "尚未学习" - xp = "经验至" - welcome = "欢迎!" - welcome_back = "欢迎回来!" - xp_bonus_for_time = "经验加成持续" - max_ability_power = "最大能力值" - unlock_this_by_clicking = "右键点击此方块以解锁:" - back = "返回" - unlearn_all = "忘却全部" - unlearned_all = "已全部忘却" +level = "等级" +knowledge = "知识" +power_used = "已用能力" +not_learned = "尚未学习" +xp = "经验至" +welcome = "欢迎!" +welcome_back = "欢迎回来!" +xp_bonus_for_time = "经验加成持续" +max_ability_power = "最大能力值" +unlock_this_by_clicking = "右键点击此方块以解锁:" +back = "返回" +unlearn_all = "忘却全部" +unlearned_all = "已全部忘却" [snippets.adapt_menu] - may_not_unlearn = "无法忘却此技能" - may_unlearn = "可学习/忘却" - knowledge_cost = "知识消耗" - knowledge_available = "可用知识" - already_learned = "已学习" - unlearn_refund = "点击忘却并退还" - no_refunds = "极限模式,退还已禁用" - knowledge = "知识" - click_learn = "点击以学习" - no_knowledge = "(你没有任何知识)" - you_only_have = "你只有" - how_to_level_up = "升级技能以提升最大能力值。" - not_enough_power = "能力不足!每个技能等级消耗 1 点能力。" - power = "能力" - power_drain = "能力消耗" - learned = "已学习 " - unlearned = "已忘却 " - activator_block = "书架" +may_not_unlearn = "无法忘却此技能" +may_unlearn = "可学习/忘却" +knowledge_cost = "知识消耗" +knowledge_available = "可用知识" +already_learned = "已学习" +unlearn_refund = "点击忘却并退还" +no_refunds = "极限模式,退还已禁用" +knowledge = "知识" +click_learn = "点击以学习" +no_knowledge = "(你没有任何知识)" +you_only_have = "你只有" +how_to_level_up = "升级技能以提升最大能力值。" +not_enough_power = "能力不足!每个技能等级消耗 1 点能力。" +power = "能力" +power_drain = "能力消耗" +learned = "已学习 " +unlearned = "已忘却 " +activator_block = "书架" [snippets.knowledge_orb] - contains = "包含" - knowledge = "知识" - rightclick = "右键点击" - togainknowledge = "以获取此知识" - knowledge_orb = "知识之珠" +contains = "包含" +knowledge = "知识" +rightclick = "右键点击" +togainknowledge = "以获取此知识" +knowledge_orb = "知识之珠" [snippets.experience_orb] - contains = "包含" - xp = "经验" - rightclick = "右键点击" - togainxp = "以获取此经验" - xporb = "经验球" +contains = "包含" +xp = "经验" +rightclick = "右键点击" +togainxp = "以获取此经验" +xporb = "经验球" # skill [skill] [skill.agility] - name = "敏捷" - icon = "⇉" - description = "敏捷是面对障碍时快速灵活移动的能力。" +name = "敏捷" +icon = "⇉" +description = "敏捷是面对障碍时快速灵活移动的能力。" [skill.architect] - name = "建筑师" - icon = "⬧" - description = "结构是世界的基石。现实掌握在你手中,由你掌控。" +name = "建筑师" +icon = "⬧" +description = "结构是世界的基石。现实掌握在你手中,由你掌控。" [skill.axes] - name = "斧技" - icon = "🪓" - description1 = "与其砍树,不如砍" - description2 = "别的东西" - description3 = ",结果都一样!" +name = "斧技" +icon = "🪓" +description1 = "与其砍树,不如砍" +description2 = "别的东西" +description3 = ",结果都一样!" [skill.brewing] - name = "酿造" - icon = "❦" - description = "双层泡泡,三层泡泡,四层泡泡——我还是没法把药水倒进炼药锅里" +name = "酿造" +icon = "❦" +description = "双层泡泡,三层泡泡,四层泡泡——我还是没法把药水倒进炼药锅里" [skill.blocking] - name = "防御" - icon = "🛡" - description = "棍棒和石头不会折断你的骨头,但盾牌可以。" +name = "防御" +icon = "🛡" +description = "棍棒和石头不会折断你的骨头,但盾牌可以。" [skill.crafting] - name = "合成" - icon = "⌂" - description = "当没有东西可以拼凑的时候,为何不再做一个呢?" +name = "合成" +icon = "⌂" +description = "当没有东西可以拼凑的时候,为何不再做一个呢?" [skill.discovery] - name = "探索" - icon = "⚛" - description = "随着感知的扩展,你的心智将揭开那些未曾发现的奥秘。" +name = "探索" +icon = "⚛" +description = "随着感知的扩展,你的心智将揭开那些未曾发现的奥秘。" [skill.enchanting] - name = "附魔" - icon = "♰" - description = "你在说什么?预言、幻象、还是迷信的胡言乱语?" +name = "附魔" +icon = "♰" +description = "你在说什么?预言、幻象、还是迷信的胡言乱语?" [skill.excavation] - name = "挖掘" - icon = "ᛳ" - description = "挖呀挖呀挖……" +name = "挖掘" +icon = "ᛳ" +description = "挖呀挖呀挖……" [skill.herbalism] - name = "草药学" - icon = "⚘" - description = "我找不到任何植物,但我能找到一些种子还有——那是……杂草?" +name = "草药学" +icon = "⚘" +description = "我找不到任何植物,但我能找到一些种子还有——那是……杂草?" [skill.hunter] - name = "狩猎" - icon = "☠" - description = "狩猎重在过程,而非结果。" +name = "狩猎" +icon = "☠" +description = "狩猎重在过程,而非结果。" [skill.nether] - name = "下界" - icon = "₪" - description = "来自下界深处。" +name = "下界" +icon = "₪" +description = "来自下界深处。" [skill.pickaxe] - name = "采矿" - icon = "⛏" - description = "矮人才是矿工,但我也从中学到了一两招。我是瑞典人!" +name = "采矿" +icon = "⛏" +description = "矮人才是矿工,但我也从中学到了一两招。我是瑞典人!" [skill.ranged] - name = "箭术" - icon = "🏹" - description = "距离是胜利的关键,也是生存的关键。" +name = "箭术" +icon = "🏹" +description = "距离是胜利的关键,也是生存的关键。" [skill.rift] - name = "裂隙" - icon = "❍" - description = "裂隙是危险的束缚,但你已驾驭了这股力量。" +name = "裂隙" +icon = "❍" +description = "裂隙是危险的束缚,但你已驾驭了这股力量。" [skill.seaborne] - name = "航海" - icon = "🎣" - description = "拥有这项技能,你便能驾驭水中的奇迹。" +name = "航海" +icon = "🎣" +description = "拥有这项技能,你便能驾驭水中的奇迹。" [skill.stealth] - name = "潜行" - icon = "☯" - description = "隐匿的艺术。行走于暗影之中。" +name = "潜行" +icon = "☯" +description = "隐匿的艺术。行走于暗影之中。" [skill.swords] - name = "剑术" - icon = "⚔" - description = "凭借灰石之力!" +name = "剑术" +icon = "⚔" +description = "凭借灰石之力!" [skill.taming] - name = "驯兽" - icon = "♥" - description = "鹦鹉和蜜蜂……还有你?" +name = "驯兽" +icon = "♥" +description = "鹦鹉和蜜蜂……还有你?" [skill.tragoul] - name = "塔格奥" - icon = "🗡" - description = "血液在宇宙的脉管中流淌,被你的双手所束缚。" +name = "塔格奥" +icon = "🗡" +description = "血液在宇宙的脉管中流淌,被你的双手所束缚。" [skill.chronos] - name = "时空" - icon = "🕒" - description = "拨动宇宙之钟,感受时间的流逝。打碎它,成为它。" +name = "时空" +icon = "🕒" +description = "拨动宇宙之钟,感受时间的流逝。打碎它,成为它。" [skill.unarmed] - name = "搏击" - icon = "»" - description = "没有武器并不意味着没有力量。" +name = "搏击" +icon = "»" +description = "没有武器并不意味着没有力量。" # agility [agility] [agility.armor_up] - name = "装甲蓄力" - description = "冲刺越久,获得的护甲值越多!" - lore1 = "最大护甲值" - lore2 = "蓄力时间" - lore = ["最大护甲值", "蓄力时间"] +name = "装甲蓄力" +description = "冲刺越久,获得的护甲值越多!" +lore1 = "最大护甲值" +lore2 = "蓄力时间" +lore = ["最大护甲值", "蓄力时间"] [agility.ladder_slide] - name = "梯子滑行" - description = "大幅加快梯子上下攀爬速度。" - lore1 = "梯子速度倍率" - lore2 = "快速下降速度" - lore = ["梯子速度倍率", "快速下降速度"] +name = "梯子滑行" +description = "大幅加快梯子上下攀爬速度。" +lore1 = "梯子速度倍率" +lore2 = "快速下降速度" +lore = ["梯子速度倍率", "快速下降速度"] [agility.super_jump] - name = "超级跳跃" - description = "卓越的高度优势。" - lore1 = "最大跳跃高度" - lore2 = "潜行 + 跳跃进行超级跳跃!" - lore = ["最大跳跃高度", "潜行 + 跳跃进行超级跳跃!"] +name = "超级跳跃" +description = "卓越的高度优势。" +lore1 = "最大跳跃高度" +lore2 = "潜行 + 跳跃进行超级跳跃!" +lore = ["最大跳跃高度", "潜行 + 跳跃进行超级跳跃!"] [agility.wall_jump] - name = "蹬墙跳" - description = "在空中靠近墙壁时按住潜行键以附墙并跳跃!" - lore1 = "最大跳跃次数" - lore2 = "跳跃高度" - lore = ["最大跳跃次数", "跳跃高度"] +name = "蹬墙跳" +description = "在空中靠近墙壁时按住潜行键以附墙并跳跃!" +lore1 = "最大跳跃次数" +lore2 = "跳跃高度" +lore = ["最大跳跃次数", "跳跃高度"] [agility.wind_up] - name = "蓄力加速" - description = "冲刺时间越长,速度越快!" - lore1 = "最大速度" - lore2 = "蓄力时间" - lore = ["最大速度", "蓄力时间"] +name = "蓄力加速" +description = "冲刺时间越长,速度越快!" +lore1 = "最大速度" +lore2 = "蓄力时间" +lore = ["最大速度", "蓄力时间"] # architect [architect] [architect.elevator] - name = "电梯" - description = "建造电梯以快速垂直传送!" - lore1 = "解锁电梯配方:X=羊毛,Y=末影珍珠" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["解锁电梯配方:X=羊毛,Y=末影珍珠", "XXX", "XYX", "XXX"] +name = "电梯" +description = "建造电梯以快速垂直传送!" +lore1 = "解锁电梯配方:X=羊毛,Y=末影珍珠" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["解锁电梯配方:X=羊毛,Y=末影珍珠", "XXX", "XYX", "XXX"] [architect.foundation] - name = "魔法地基" - description = "潜行时在脚下放置临时地基!" - lore1 = "魔法生成:" - lore2 = "个方块在你脚下!" - lore = ["魔法生成:", "个方块在你脚下!"] +name = "魔法地基" +description = "潜行时在脚下放置临时地基!" +lore1 = "魔法生成:" +lore2 = "个方块在你脚下!" +lore = ["魔法生成:", "个方块在你脚下!"] [architect.glass] - name = "精准触碰玻璃" - description = "空手打碎玻璃时不再损失玻璃方块!" - lore1 = "你的双手对玻璃具有精准采集效果" - lore = ["你的双手对玻璃具有精准采集效果"] +name = "精准触碰玻璃" +description = "空手打碎玻璃时不再损失玻璃方块!" +lore1 = "你的双手对玻璃具有精准采集效果" +lore = ["你的双手对玻璃具有精准采集效果"] [architect.wireless_redstone] - name = "红石遥控器" - description = "使用红石火把远程切换红石信号!" - lore1 = "标靶 + 红石火把 + 末影珍珠 = 1 个红石遥控器" - lore = ["标靶 + 红石火把 + 末影珍珠 = 1 个红石遥控器"] +name = "红石遥控器" +description = "使用红石火把远程切换红石信号!" +lore1 = "标靶 + 红石火把 + 末影珍珠 = 1 个红石遥控器" +lore = ["标靶 + 红石火把 + 末影珍珠 = 1 个红石遥控器"] [architect.placement] - name = "建筑师之杖" - description = "一次放置多个方块!按住潜行,手持与目标方块相同的方块并放置!请注意,你可能需要稍微移动来触发边界计算!" - lore1 = "你需要手持" - lore2 = "个方块才能放置" - lore3 = "材质建筑师之杖" - lore = ["你需要手持", "个方块才能放置", "材质建筑师之杖"] +name = "建筑师之杖" +description = "一次放置多个方块!按住潜行,手持与目标方块相同的方块并放置!请注意,你可能需要稍微移动来触发边界计算!" +lore1 = "你需要手持" +lore2 = "个方块才能放置" +lore3 = "材质建筑师之杖" +lore = ["你需要手持", "个方块才能放置", "材质建筑师之杖"] # axe [axe] [axe.chop] - name = "伐木劈砍" - description = "右键点击底部原木来砍倒整棵树!" - lore1 = "每次劈砍方块数" - lore2 = "劈砍冷却" - lore3 = "耐久消耗" - lore = ["每次劈砍方块数", "劈砍冷却", "耐久消耗"] +name = "伐木劈砍" +description = "右键点击底部原木来砍倒整棵树!" +lore1 = "每次劈砍方块数" +lore2 = "劈砍冷却" +lore3 = "耐久消耗" +lore = ["每次劈砍方块数", "劈砍冷却", "耐久消耗"] [axe.log_swap] - name = "露西的原木转换器" - description = "在工作台中转换原木种类!" - lore1 = "8 个任意种类的原木 + 1 株树苗 = 8 个树苗对应种类的原木" - lore = ["8 个任意种类的原木 + 1 株树苗 = 8 个树苗对应种类的原木"] +name = "露西的原木转换器" +description = "在工作台中转换原木种类!" +lore1 = "8 个任意种类的原木 + 1 株树苗 = 8 个树苗对应种类的原木" +lore = ["8 个任意种类的原木 + 1 株树苗 = 8 个树苗对应种类的原木"] [axe.drop_to_inventory] - name = "斧头掉落物直入背包" +name = "斧头掉落物直入背包" [axe.ground_smash] - name = "地面震击" - description = "跳起后蹲下,震击附近所有敌人。" - lore1 = "伤害" - lore2 = "方块半径" - lore3 = "力量" - lore4 = "震击冷却" - lore = ["伤害", "方块半径", "力量", "震击冷却"] +name = "地面震击" +description = "跳起后蹲下,震击附近所有敌人。" +lore1 = "伤害" +lore2 = "方块半径" +lore3 = "力量" +lore4 = "震击冷却" +lore = ["伤害", "方块半径", "力量", "震击冷却"] [axe.leaf_miner] - name = "树叶矿工" - description = "一次破坏大量树叶!" - lore1 = "潜行并挖掘树叶" - lore2 = "树叶挖掘范围" - lore3 = "不会获得树叶的掉落物(防滥用)" - lore = ["潜行并挖掘树叶", "树叶挖掘范围", "不会获得树叶的掉落物(防滥用)"] +name = "树叶矿工" +description = "一次破坏大量树叶!" +lore1 = "潜行并挖掘树叶" +lore2 = "树叶挖掘范围" +lore3 = "不会获得树叶的掉落物(防滥用)" +lore = ["潜行并挖掘树叶", "树叶挖掘范围", "不会获得树叶的掉落物(防滥用)"] [axe.wood_miner] - name = "原木矿工" - description = "一次砍伐大量木头!" - lore1 = "潜行并砍伐木头/原木(木板无效)" - lore2 = "木材挖掘范围" - lore3 = "与掉落物直入背包兼容" - lore = ["潜行并砍伐木头/原木(木板无效)", "木材挖掘范围", "与掉落物直入背包兼容"] +name = "原木矿工" +description = "一次砍伐大量木头!" +lore1 = "潜行并砍伐木头/原木(木板无效)" +lore2 = "木材挖掘范围" +lore3 = "与掉落物直入背包兼容" +lore = ["潜行并砍伐木头/原木(木板无效)", "木材挖掘范围", "与掉落物直入背包兼容"] # brewing [brewing] [brewing.lingering] - name = "持久酿造" - description = "酿造的药水持续时间更长!" - lore1 = "持续时间" - lore2 = "持续时间" - lore = ["持续时间", "持续时间"] +name = "持久酿造" +description = "酿造的药水持续时间更长!" +lore1 = "持续时间" +lore2 = "持续时间" +lore = ["持续时间", "持续时间"] [brewing.super_heated] - name = "超级加热酿造" - description = "酿造台越热,工作速度越快。" - lore1 = "每接触一个火焰方块" - lore2 = "每接触一个岩浆方块" - lore = ["每接触一个火焰方块", "每接触一个岩浆方块"] +name = "超级加热酿造" +description = "酿造台越热,工作速度越快。" +lore1 = "每接触一个火焰方块" +lore2 = "每接触一个岩浆方块" +lore = ["每接触一个火焰方块", "每接触一个岩浆方块"] [brewing.darkness] - name = "瓶装黑暗" - description = "不知道你为什么需要这个,但给你了!" - lore1 = "夜视药水 + 黑色混凝土 = 黑暗药水(30 秒)" - lore2 = "注意:此药水会阻止使用者疾跑!" - lore = ["夜视药水 + 黑色混凝土 = 黑暗药水(30 秒)", "注意:此药水会阻止使用者疾跑!"] +name = "瓶装黑暗" +description = "不知道你为什么需要这个,但给你了!" +lore1 = "夜视药水 + 黑色混凝土 = 黑暗药水(30 秒)" +lore2 = "注意:此药水会阻止使用者疾跑!" +lore = ["夜视药水 + 黑色混凝土 = 黑暗药水(30 秒)", "注意:此药水会阻止使用者疾跑!"] [brewing.haste] - name = "瓶装急迫" - description = "当效率附魔还不够的时候" - lore1 = "速度药水 + 紫水晶碎片 = 急迫药水(60 秒)" - lore2 = "速度药水 + 紫水晶块 = 急迫 II 药水(30 秒)" - lore = ["速度药水 + 紫水晶碎片 = 急迫药水(60 秒)", "速度药水 + 紫水晶块 = 急迫 II 药水(30 秒)"] +name = "瓶装急迫" +description = "当效率附魔还不够的时候" +lore1 = "速度药水 + 紫水晶碎片 = 急迫药水(60 秒)" +lore2 = "速度药水 + 紫水晶块 = 急迫 II 药水(30 秒)" +lore = ["速度药水 + 紫水晶碎片 = 急迫药水(60 秒)", "速度药水 + 紫水晶块 = 急迫 II 药水(30 秒)"] [brewing.absorption] - name = "瓶装伤害吸收" - description = "强化身体!" - lore1 = "瞬间治疗 + 石英 = 伤害吸收药水(60 秒)" - lore2 = "瞬间治疗 + 石英块 = 伤害吸收 II 药水(30 秒)" - lore = ["瞬间治疗 + 石英 = 伤害吸收药水(60 秒)", "瞬间治疗 + 石英块 = 伤害吸收 II 药水(30 秒)"] +name = "瓶装伤害吸收" +description = "强化身体!" +lore1 = "瞬间治疗 + 石英 = 伤害吸收药水(60 秒)" +lore2 = "瞬间治疗 + 石英块 = 伤害吸收 II 药水(30 秒)" +lore = ["瞬间治疗 + 石英 = 伤害吸收药水(60 秒)", "瞬间治疗 + 石英块 = 伤害吸收 II 药水(30 秒)"] [brewing.fatigue] - name = "瓶装疲劳" - description = "削弱身体!" - lore1 = "虚弱药水 + 史莱姆球 = 挖掘疲劳药水(30 秒)" - lore2 = "虚弱药水 + 史莱姆块 = 挖掘疲劳 II 药水(15 秒)" - lore = ["虚弱药水 + 史莱姆球 = 挖掘疲劳药水(30 秒)", "虚弱药水 + 史莱姆块 = 挖掘疲劳 II 药水(15 秒)"] +name = "瓶装疲劳" +description = "削弱身体!" +lore1 = "虚弱药水 + 史莱姆球 = 挖掘疲劳药水(30 秒)" +lore2 = "虚弱药水 + 史莱姆块 = 挖掘疲劳 II 药水(15 秒)" +lore = ["虚弱药水 + 史莱姆球 = 挖掘疲劳药水(30 秒)", "虚弱药水 + 史莱姆块 = 挖掘疲劳 II 药水(15 秒)"] [brewing.hunger] - name = "瓶装饥饿" - description = "喂饱那永不满足的胃!" - lore1 = "粗制的药水 + 腐肉 = 饥饿药水(30 秒)" - lore2 = "虚弱药水 + 腐肉 = 饥饿 III 药水(15 秒)" - lore = ["粗制的药水 + 腐肉 = 饥饿药水(30 秒)", "虚弱药水 + 腐肉 = 饥饿 III 药水(15 秒)"] +name = "瓶装饥饿" +description = "喂饱那永不满足的胃!" +lore1 = "粗制的药水 + 腐肉 = 饥饿药水(30 秒)" +lore2 = "虚弱药水 + 腐肉 = 饥饿 III 药水(15 秒)" +lore = ["粗制的药水 + 腐肉 = 饥饿药水(30 秒)", "虚弱药水 + 腐肉 = 饥饿 III 药水(15 秒)"] [brewing.nausea] - name = "瓶装反胃" - description = "你让我恶心!" - lore1 = "粗制的药水 + 棕色蘑菇 = 反胃药水(16 秒)" - lore2 = "粗制的药水 + 绯红菌 = 反胃 II 药水(8 秒)" - lore = ["粗制的药水 + 棕色蘑菇 = 反胃药水(16 秒)", "粗制的药水 + 绯红菌 = 反胃 II 药水(8 秒)"] +name = "瓶装反胃" +description = "你让我恶心!" +lore1 = "粗制的药水 + 棕色蘑菇 = 反胃药水(16 秒)" +lore2 = "粗制的药水 + 绯红菌 = 反胃 II 药水(8 秒)" +lore = ["粗制的药水 + 棕色蘑菇 = 反胃药水(16 秒)", "粗制的药水 + 绯红菌 = 反胃 II 药水(8 秒)"] [brewing.blindness] - name = "瓶装失明" - description = "你真是个可怕的人……" - lore1 = "粗制的药水 + 墨囊 = 失明药水(30 秒)" - lore2 = "粗制的药水 + 荧光墨囊 = 失明 II 药水(15 秒)" - lore = ["粗制的药水 + 墨囊 = 失明药水(30 秒)", "粗制的药水 + 荧光墨囊 = 失明 II 药水(15 秒)"] +name = "瓶装失明" +description = "你真是个可怕的人……" +lore1 = "粗制的药水 + 墨囊 = 失明药水(30 秒)" +lore2 = "粗制的药水 + 荧光墨囊 = 失明 II 药水(15 秒)" +lore = ["粗制的药水 + 墨囊 = 失明药水(30 秒)", "粗制的药水 + 荧光墨囊 = 失明 II 药水(15 秒)"] [brewing.resistance] - name = "瓶装抗性" - description = "最佳防御!" - lore1 = "粗制的药水 + 铁锭 = 抗性提升药水(60 秒)" - lore2 = "粗制的药水 + 铁块 = 抗性提升 II 药水(30 秒)" - lore = ["粗制的药水 + 铁锭 = 抗性提升药水(60 秒)", "粗制的药水 + 铁块 = 抗性提升 II 药水(30 秒)"] +name = "瓶装抗性" +description = "最佳防御!" +lore1 = "粗制的药水 + 铁锭 = 抗性提升药水(60 秒)" +lore2 = "粗制的药水 + 铁块 = 抗性提升 II 药水(30 秒)" +lore = ["粗制的药水 + 铁锭 = 抗性提升药水(60 秒)", "粗制的药水 + 铁块 = 抗性提升 II 药水(30 秒)"] [brewing.health_boost] - name = "瓶装生命" - description = "当最大生命值还不够的时候……" - lore1 = "瞬间治疗药水 + 金苹果 = 生命提升药水(120 秒)" - lore2 = "瞬间治疗药水 + 附魔金苹果 = 生命提升 II 药水(120 秒)" - lore = ["瞬间治疗药水 + 金苹果 = 生命提升药水(120 秒)", "瞬间治疗药水 + 附魔金苹果 = 生命提升 II 药水(120 秒)"] +name = "瓶装生命" +description = "当最大生命值还不够的时候……" +lore1 = "瞬间治疗药水 + 金苹果 = 生命提升药水(120 秒)" +lore2 = "瞬间治疗药水 + 附魔金苹果 = 生命提升 II 药水(120 秒)" +lore = ["瞬间治疗药水 + 金苹果 = 生命提升药水(120 秒)", "瞬间治疗药水 + 附魔金苹果 = 生命提升 II 药水(120 秒)"] [brewing.decay] - name = "瓶装腐朽" - description = "谁知道垃圾竟然这么有用?" - lore1 = "虚弱药水 + 毒马铃薯 = 凋零药水(16 秒)" - lore2 = "虚弱药水 + 绯红菌索 = 凋零 II 药水(8 秒)" - lore = ["虚弱药水 + 毒马铃薯 = 凋零药水(16 秒)", "虚弱药水 + 绯红菌索 = 凋零 II 药水(8 秒)"] +name = "瓶装腐朽" +description = "谁知道垃圾竟然这么有用?" +lore1 = "虚弱药水 + 毒马铃薯 = 凋零药水(16 秒)" +lore2 = "虚弱药水 + 绯红菌索 = 凋零 II 药水(8 秒)" +lore = ["虚弱药水 + 毒马铃薯 = 凋零药水(16 秒)", "虚弱药水 + 绯红菌索 = 凋零 II 药水(8 秒)"] [brewing.saturation] - name = "瓶装饱和" - description = "你知道吗……其实我一点也不饿……" - lore1 = "生命恢复药水 + 烤马铃薯 = 饱和药水" - lore2 = "生命恢复药水 + 干草块 = 饱和 II 药水" - lore = ["生命恢复药水 + 烤马铃薯 = 饱和药水", "生命恢复药水 + 干草块 = 饱和 II 药水"] +name = "瓶装饱和" +description = "你知道吗……其实我一点也不饿……" +lore1 = "生命恢复药水 + 烤马铃薯 = 饱和药水" +lore2 = "生命恢复药水 + 干草块 = 饱和 II 药水" +lore = ["生命恢复药水 + 烤马铃薯 = 饱和药水", "生命恢复药水 + 干草块 = 饱和 II 药水"] # blocking [blocking] [blocking.chain_armorer] - name = "梅菲斯特的锁链" - description = "允许你合成锁链盔甲" - lore1 = "合成配方与其他盔甲相同,但使用铁粒代替" - lore = ["合成配方与其他盔甲相同,但使用铁粒代替"] +name = "梅菲斯特的锁链" +description = "允许你合成锁链盔甲" +lore1 = "合成配方与其他盔甲相同,但使用铁粒代替" +lore = ["合成配方与其他盔甲相同,但使用铁粒代替"] [blocking.horse_armorer] - name = "可合成马铠" - description = "允许你合成马铠" - lore1 = "用你想要的材料围绕马鞍即可合成对应材质的马铠" - lore = ["用你想要的材料围绕马鞍即可合成对应材质的马铠"] +name = "可合成马铠" +description = "允许你合成马铠" +lore1 = "用你想要的材料围绕马鞍即可合成对应材质的马铠" +lore = ["用你想要的材料围绕马鞍即可合成对应材质的马铠"] [blocking.saddle_crafter] - name = "可合成马鞍" - description = "用皮革合成马鞍" - lore1 = "配方:5 块皮革:" - lore = ["配方:5 块皮革:"] +name = "可合成马鞍" +description = "用皮革合成马鞍" +lore1 = "配方:5 块皮革:" +lore = ["配方:5 块皮革:"] [blocking.multi_armor] - name = "复合盔甲" - description = "将鞘翅绑定到盔甲上" - lore1 = "这是旅行的绝佳技能。" - lore2 = "动态合并并随时切换盔甲/鞘翅!" - lore3 = "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。" - lore4 = "解除绑定:潜行时丢弃该物品,它就会拆解。" - lore5 = "如果你的复合盔甲被摧毁,其中所有物品都将丢失。" - lore6 = "我不需要盔甲,它让我失望……" - lore = ["这是旅行的绝佳技能。", "动态合并并随时切换盔甲/鞘翅!", "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。", "解除绑定:潜行时丢弃该物品,它就会拆解。", "如果你的复合盔甲被摧毁,其中所有物品都将丢失。", "我不需要盔甲,它让我失望……"] +name = "复合盔甲" +description = "将鞘翅绑定到盔甲上" +lore1 = "这是旅行的绝佳技能。" +lore2 = "动态合并并随时切换盔甲/鞘翅!" +lore3 = "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。" +lore4 = "解除绑定:潜行时丢弃该物品,它就会拆解。" +lore5 = "如果你的复合盔甲被摧毁,其中所有物品都将丢失。" +lore6 = "我不需要盔甲,它让我失望……" +lore = ["这是旅行的绝佳技能。", "动态合并并随时切换盔甲/鞘翅!", "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。", "解除绑定:潜行时丢弃该物品,它就会拆解。", "如果你的复合盔甲被摧毁,其中所有物品都将丢失。", "我不需要盔甲,它让我失望……"] # crafting [crafting] [crafting.deconstruction] - name = "分解" - description = "将方块和物品分解为可回收的基础材料!" - lore1 = "将任意物品丢在地上。" - lore2 = "然后潜行并用剪刀右键点击" - lore = ["将任意物品丢在地上。", "然后潜行并用剪刀右键点击"] +name = "分解" +description = "将方块和物品分解为可回收的基础材料!" +lore1 = "将任意物品丢在地上。" +lore2 = "然后潜行并用剪刀右键点击" +lore = ["将任意物品丢在地上。", "然后潜行并用剪刀右键点击"] [crafting.xp] - name = "合成经验" - description = "合成时获得被动经验" - lore1 = "合成时获得经验" - lore = ["合成时获得经验"] +name = "合成经验" +description = "合成时获得被动经验" +lore1 = "合成时获得经验" +lore = ["合成时获得经验"] [crafting.reconstruction] - name = "矿石重铸" - description = "用基础材料重新合成矿石!" - lore1 = "8 个掉落物 + 1 个载体 = 1 个矿石(无序合成)" - lore2 = "掉落物需先熔炼(若适用)" - lore3 = "不包括:残片、石英和绿宝石等……" - lore4 = "载体 = 包裹矿石的方块。如:石头、下界岩、深板岩" - lore = ["8 个掉落物 + 1 个载体 = 1 个矿石(无序合成)", "掉落物需先熔炼(若适用)", "不包括:残片、石英和绿宝石等……", "载体 = 包裹矿石的方块。如:石头、下界岩、深板岩"] +name = "矿石重铸" +description = "用基础材料重新合成矿石!" +lore1 = "8 个掉落物 + 1 个载体 = 1 个矿石(无序合成)" +lore2 = "掉落物需先熔炼(若适用)" +lore3 = "不包括:残片、石英和绿宝石等……" +lore4 = "载体 = 包裹矿石的方块。如:石头、下界岩、深板岩" +lore = ["8 个掉落物 + 1 个载体 = 1 个矿石(无序合成)", "掉落物需先熔炼(若适用)", "不包括:残片、石英和绿宝石等……", "载体 = 包裹矿石的方块。如:石头、下界岩、深板岩"] [crafting.leather] - name = "可合成皮革" - description = "用腐肉合成皮革" - lore1 = "把腐肉丢到营火上就行了!" - lore = ["把腐肉丢到营火上就行了!"] +name = "可合成皮革" +description = "用腐肉合成皮革" +lore1 = "把腐肉丢到营火上就行了!" +lore = ["把腐肉丢到营火上就行了!"] [crafting.backpacks] - name = "布蒂利埃的背包!" - description = "将 Mojang 的收纳袋功能加入游戏!" - lore1 = "需要在生存模式下才能使用" - lore2 = "XLX:皮革、拴绳、皮革" - lore3 = "XSX:皮革、木桶、皮革" - lore4 = "XCX:皮革、箱子、皮革" - lore = ["需要在生存模式下才能使用", "XLX:皮革、拴绳、皮革", "XSX:皮革、木桶、皮革", "XCX:皮革、箱子、皮革"] +name = "布蒂利埃的背包!" +description = "将 Mojang 的收纳袋功能加入游戏!" +lore1 = "需要在生存模式下才能使用" +lore2 = "XLX:皮革、拴绳、皮革" +lore3 = "XSX:皮革、木桶、皮革" +lore4 = "XCX:皮革、箱子、皮革" +lore = ["需要在生存模式下才能使用", "XLX:皮革、拴绳、皮革", "XSX:皮革、木桶、皮革", "XCX:皮革、箱子、皮革"] [crafting.stations] - name = "便携工作台!" - description = "在手掌中使用工作台!" - lore2 = "关闭工作台时遗忘在里面的物品将永远丢失!" - lore3 = "可用工作台:铁砧、工作台、砂轮、制图台、切石机、织布机" - lore = ["关闭工作台时遗忘在里面的物品将永远丢失!", "可用工作台:铁砧、工作台、砂轮、制图台、切石机、织布机"] +name = "便携工作台!" +description = "在手掌中使用工作台!" +lore2 = "关闭工作台时遗忘在里面的物品将永远丢失!" +lore3 = "可用工作台:铁砧、工作台、砂轮、制图台、切石机、织布机" +lore = ["关闭工作台时遗忘在里面的物品将永远丢失!", "可用工作台:铁砧、工作台、砂轮、制图台、切石机、织布机"] [crafting.skulls] - name = "可合成头颅!" - description = "使用材料合成生物头颅!" - lore1 = "用以下材料围绕骨块来获得头颅:" - lore2 = "僵尸:腐肉" - lore3 = "骷髅:骨头" - lore4 = "苦力怕:火药" - lore5 = "凋灵:下界砖" - lore6 = "龙:龙息" - lore = ["用以下材料围绕骨块来获得头颅:", "僵尸:腐肉", "骷髅:骨头", "苦力怕:火药", "凋灵:下界砖", "龙:龙息"] +name = "可合成头颅!" +description = "使用材料合成生物头颅!" +lore1 = "用以下材料围绕骨块来获得头颅:" +lore2 = "僵尸:腐肉" +lore3 = "骷髅:骨头" +lore4 = "苦力怕:火药" +lore5 = "凋灵:下界砖" +lore6 = "龙:龙息" +lore = ["用以下材料围绕骨块来获得头颅:", "僵尸:腐肉", "骷髅:骨头", "苦力怕:火药", "凋灵:下界砖", "龙:龙息"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "时光之瓶" - description = "携带一个储存时间的时光瓶,消耗储存的时间来加速计时方块、可生长物和可成长的实体(如幼年动物)。配方(无序):迅捷药水 + 时钟 + 玻璃瓶。" - lore1 = "每 Tick 充入的储存秒数" - lore2 = "每储存秒数的时间加速量" - lore3 = "配方(无序):迅捷药水 + 时钟 + 玻璃瓶" - lore = ["每 Tick 充入的储存秒数", "每储存秒数的时间加速量", "配方(无序):迅捷药水 + 时钟 + 玻璃瓶"] +name = "时光之瓶" +description = "携带一个储存时间的时光瓶,消耗储存的时间来加速计时方块、可生长物和可成长的实体(如幼年动物)。配方(无序):迅捷药水 + 时钟 + 玻璃瓶。" +lore1 = "每 Tick 充入的储存秒数" +lore2 = "每储存秒数的时间加速量" +lore3 = "配方(无序):迅捷药水 + 时钟 + 玻璃瓶" +lore = ["每 Tick 充入的储存秒数", "每储存秒数的时间加速量", "配方(无序):迅捷药水 + 时钟 + 玻璃瓶"] [chronos.aberrant_touch] - name = "异常之触" - description = "近战攻击以饥饿为代价施加叠加缓慢效果,对 PvP 有严格上限,5 层时定身目标。" - lore1 = "近战攻击施加叠加缓慢效果" - lore2 = "PvE 缓慢持续时间上限" - lore3 = "PvP 缓慢效果等级上限" - lore = ["近战攻击施加叠加缓慢效果", "PvE 缓慢持续时间上限", "PvP 缓慢效果等级上限"] +name = "异常之触" +description = "近战攻击以饥饿为代价施加叠加缓慢效果,对 PvP 有严格上限,5 层时定身目标。" +lore1 = "近战攻击施加叠加缓慢效果" +lore2 = "PvE 缓慢持续时间上限" +lore3 = "PvP 缓慢效果等级上限" +lore = ["近战攻击施加叠加缓慢效果", "PvE 缓慢持续时间上限", "PvP 缓慢效果等级上限"] [chronos.instant_recall] - name = "瞬间回溯" - description = "手持时钟左键或右键点击,回溯到最近的快照并恢复生命值和饥饿值。" - lore1 = "回溯持续时间" - lore2 = "冷却时间" - lore3 = "不回溯物品栏" - lore = ["回溯持续时间", "冷却时间", "不回溯物品栏"] +name = "瞬间回溯" +description = "手持时钟左键或右键点击,回溯到最近的快照并恢复生命值和饥饿值。" +lore1 = "回溯持续时间" +lore2 = "冷却时间" +lore3 = "不回溯物品栏" +lore = ["回溯持续时间", "冷却时间", "不回溯物品栏"] [chronos.time_bomb] - name = "时间炸弹" - description = "投掷合成的时空炸弹,制造时间力场,减速实体并冻结弹射物。" - lore1 = "时间力场半径" - lore2 = "时间力场持续时间" - lore3 = "炸弹冷却时间" - lore4 = "配方(无序):时钟 + 雪球 + 钻石 + 沙子" - lore = ["时间力场半径", "时间力场持续时间", "炸弹冷却时间", "配方(无序):时钟 + 雪球 + 钻石 + 沙子"] +name = "时间炸弹" +description = "投掷合成的时空炸弹,制造时间力场,减速实体并冻结弹射物。" +lore1 = "时间力场半径" +lore2 = "时间力场持续时间" +lore3 = "炸弹冷却时间" +lore4 = "配方(无序):时钟 + 雪球 + 钻石 + 沙子" +lore = ["时间力场半径", "时间力场持续时间", "炸弹冷却时间", "配方(无序):时钟 + 雪球 + 钻石 + 沙子"] # discovery [discovery] [discovery.armor] - name = "世界之甲" - description = "根据附近方块硬度获得被动护甲。" - lore1 = "被动护甲" - lore2 = "基于附近方块硬度" - lore3 = "护甲强度:" - lore = ["被动护甲", "基于附近方块硬度", "护甲强度:"] +name = "世界之甲" +description = "根据附近方块硬度获得被动护甲。" +lore1 = "被动护甲" +lore2 = "基于附近方块硬度" +lore3 = "护甲强度:" +lore = ["被动护甲", "基于附近方块硬度", "护甲强度:"] [discovery.unity] - name = "实验性统合" - description = "拾取经验球时为随机技能增加经验。" - lore1 = "经验 " - lore2 = "每个经验球" - lore = ["经验 ", "每个经验球"] +name = "实验性统合" +description = "拾取经验球时为随机技能增加经验。" +lore1 = "经验 " +lore2 = "每个经验球" +lore = ["经验 ", "每个经验球"] [discovery.resist] - name = "实验性抗性" - description = "消耗经验来减免伤害,仅当一次攻击会使你降至 5 颗心以下或致死时触发。" - lore0 = "仅在危急生命值时触发(<= 5 颗心),每 15 秒一次" - lore1 = " 减免伤害" - lore2 = "经验消耗" - lore = ["仅在危急生命值时触发(<= 5 颗心),每 15 秒一次", " 减免伤害", "经验消耗"] +name = "实验性抗性" +description = "消耗经验来减免伤害,仅当一次攻击会使你降至 5 颗心以下或致死时触发。" +lore0 = "仅在危急生命值时触发(<= 5 颗心),每 15 秒一次" +lore1 = " 减免伤害" +lore2 = "经验消耗" +lore = ["仅在危急生命值时触发(<= 5 颗心),每 15 秒一次", " 减免伤害", "经验消耗"] [discovery.villager] - name = "村民吸引力" - description = "与村民交易时获得更好的价格!" - lore1 = "每次与村民互动时消耗经验" - lore2 = "每次互动有概率消耗经验并提升交易" - lore3 = "每次互动所需经验消耗" - lore = ["每次与村民互动时消耗经验", "每次互动有概率消耗经验并提升交易", "每次互动所需经验消耗"] +name = "村民吸引力" +description = "与村民交易时获得更好的价格!" +lore1 = "每次与村民互动时消耗经验" +lore2 = "每次互动有概率消耗经验并提升交易" +lore3 = "每次互动所需经验消耗" +lore = ["每次与村民互动时消耗经验", "每次互动有概率消耗经验并提升交易", "每次互动所需经验消耗"] # enchanting [enchanting] [enchanting.lapis_return] - name = "青金石回馈" - description = "以多消耗 1 级经验为代价,有概率免费返还青金石" - lore1 = "每级会多消耗 1 级附魔经验,但最多可返还 3 个青金石" - lore = ["每级会多消耗 1 级附魔经验,但最多可返还 3 个青金石"] +name = "青金石回馈" +description = "以多消耗 1 级经验为代价,有概率免费返还青金石" +lore1 = "每级会多消耗 1 级附魔经验,但最多可返还 3 个青金石" +lore = ["每级会多消耗 1 级附魔经验,但最多可返还 3 个青金石"] [enchanting.quick_enchant] - name = "快捷附魔" - description = "直接点击附魔书到物品上即可附魔。" - lore1 = "最大合并等级" - lore2 = "物品附魔总等级不能超过 " - lore3 = "能力值" - lore = ["最大合并等级", "物品附魔总等级不能超过 ", "能力值"] +name = "快捷附魔" +description = "直接点击附魔书到物品上即可附魔。" +lore1 = "最大合并等级" +lore2 = "物品附魔总等级不能超过 " +lore3 = "能力值" +lore = ["最大合并等级", "物品附魔总等级不能超过 ", "能力值"] [enchanting.return] - name = "经验返还" - description = "附魔物品时返还部分经验。" - lore1 = "附魔时消耗的经验有概率被退还" - lore2 = "每次附魔返还经验" - lore = ["附魔时消耗的经验有概率被退还", "每次附魔返还经验"] +name = "经验返还" +description = "附魔物品时返还部分经验。" +lore1 = "附魔时消耗的经验有概率被退还" +lore2 = "每次附魔返还经验" +lore = ["附魔时消耗的经验有概率被退还", "每次附魔返还经验"] # excavation [excavation] [excavation.haste] - name = "急速挖掘者" - description = "用急迫效果加速挖掘过程!" - lore1 = "挖掘时获得急迫效果" - lore2 = "x 级急迫效果(开始挖掘任意方块时)" - lore = ["挖掘时获得急迫效果", "x 级急迫效果(开始挖掘任意方块时)"] +name = "急速挖掘者" +description = "用急迫效果加速挖掘过程!" +lore1 = "挖掘时获得急迫效果" +lore2 = "x 级急迫效果(开始挖掘任意方块时)" +lore = ["挖掘时获得急迫效果", "x 级急迫效果(开始挖掘任意方块时)"] [excavation.spelunker] - name = "超级透视探矿者!" - description = "透过地面看到矿石!" - lore1 = "副手持矿石,主手持发光浆果,然后潜行!" - lore2 = "方块范围:" - lore3 = "使用时消耗发光浆果" - lore = ["副手持矿石,主手持发光浆果,然后潜行!", "方块范围:", "使用时消耗发光浆果"] +name = "超级透视探矿者!" +description = "透过地面看到矿石!" +lore1 = "副手持矿石,主手持发光浆果,然后潜行!" +lore2 = "方块范围:" +lore3 = "使用时消耗发光浆果" +lore = ["副手持矿石,主手持发光浆果,然后潜行!", "方块范围:", "使用时消耗发光浆果"] [excavation.drop_to_inventory] - name = "铲子掉落物直入背包" +name = "铲子掉落物直入背包" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Tackle 精心设计的豪华多功能工具" - lore1 = "可能是最强大的功能之一,允许你" - lore2 = "根据需要动态合并和切换工具。" - lore3 = "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。" - lore4 = "解除绑定:潜行时丢弃该物品,它就会拆解。" - lore5 = "在这个多功能工具中你不能损坏工具,但也不能使用已损坏的工具" - lore6 = "可合并物品总数。" - lore7 = "你可以用五六种工具,或者只用一种!" - lore = ["可能是最强大的功能之一,允许你", "根据需要动态合并和切换工具。", "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。", "解除绑定:潜行时丢弃该物品,它就会拆解。", "在这个多功能工具中你不能损坏工具,但也不能使用已损坏的工具", "可合并物品总数。", "你可以用五六种工具,或者只用一种!"] +name = "OMNI - T.O.O.L." +description = "Tackle 精心设计的豪华多功能工具" +lore1 = "可能是最强大的功能之一,允许你" +lore2 = "根据需要动态合并和切换工具。" +lore3 = "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。" +lore4 = "解除绑定:潜行时丢弃该物品,它就会拆解。" +lore5 = "在这个多功能工具中你不能损坏工具,但也不能使用已损坏的工具" +lore6 = "可合并物品总数。" +lore7 = "你可以用五六种工具,或者只用一种!" +lore = ["可能是最强大的功能之一,允许你", "根据需要动态合并和切换工具。", "合并方法:在物品栏中按住 Shift 点击一个物品放到另一个上。", "解除绑定:潜行时丢弃该物品,它就会拆解。", "在这个多功能工具中你不能损坏工具,但也不能使用已损坏的工具", "可合并物品总数。", "你可以用五六种工具,或者只用一种!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "生长光环" - description = "在周围的范围内加速自然生长" - lore1 = "方块半径" - lore2 = "生长光环强度" - lore3 = "食物消耗" - lore = ["方块半径", "生长光环强度", "食物消耗"] +name = "生长光环" +description = "在周围的范围内加速自然生长" +lore1 = "方块半径" +lore2 = "生长光环强度" +lore3 = "食物消耗" +lore = ["方块半径", "生长光环强度", "食物消耗"] [herbalism.hippo] - name = "草药师的河马" - description = "吃食物时获得更多饱和度" - lore1 = "食物)食用时额外获得饱和点数" - lore = ["食物)食用时额外获得饱和点数"] +name = "草药师的河马" +description = "吃食物时获得更多饱和度" +lore1 = "食物)食用时额外获得饱和点数" +lore = ["食物)食用时额外获得饱和点数"] [herbalism.myconid] - name = "草药师的菌丝体" - description = "赋予你合成菌丝的能力" - lore1 = "任意泥土 + 棕色蘑菇 + 红色蘑菇可合成菌丝。" - lore = ["任意泥土 + 棕色蘑菇 + 红色蘑菇可合成菌丝。"] +name = "草药师的菌丝体" +description = "赋予你合成菌丝的能力" +lore1 = "任意泥土 + 棕色蘑菇 + 红色蘑菇可合成菌丝。" +lore = ["任意泥土 + 棕色蘑菇 + 红色蘑菇可合成菌丝。"] [herbalism.terralid] - name = "草药师的地衣" - description = "赋予你合成草方块的能力" - lore1 = "3 个种子放在 3 块泥土上方,可合成 3 块草方块。" - lore = ["3 个种子放在 3 块泥土上方,可合成 3 块草方块。"] +name = "草药师的地衣" +description = "赋予你合成草方块的能力" +lore1 = "3 个种子放在 3 块泥土上方,可合成 3 块草方块。" +lore = ["3 个种子放在 3 块泥土上方,可合成 3 块草方块。"] [herbalism.cobweb] - name = "蛛网编织者" - description = "赋予你在工作台中合成蛛网的能力" - lore1 = "九根线可合成一张蛛网。" - lore = ["九根线可合成一张蛛网。"] +name = "蛛网编织者" +description = "赋予你在工作台中合成蛛网的能力" +lore1 = "九根线可合成一张蛛网。" +lore = ["九根线可合成一张蛛网。"] [herbalism.mushroom_blocks] - name = "蘑菇制造者" - description = "赋予你在工作台中合成蘑菇方块的能力" - lore1 = "四个蘑菇合成一个蘑菇块,或一个蘑菇块合成一个蘑菇柄。" - lore = ["四个蘑菇合成一个蘑菇块,或一个蘑菇块合成一个蘑菇柄。"] +name = "蘑菇制造者" +description = "赋予你在工作台中合成蘑菇方块的能力" +lore1 = "四个蘑菇合成一个蘑菇块,或一个蘑菇块合成一个蘑菇柄。" +lore = ["四个蘑菇合成一个蘑菇块,或一个蘑菇块合成一个蘑菇柄。"] [herbalism.drop_to_inventory] - name = "锄头掉落物直入背包" +name = "锄头掉落物直入背包" [herbalism.hungry_shield] - name = "饥饿护盾" - description = "先消耗饥饿值而非生命值来承受伤害。" - lore1 = "以饥饿值抵御伤害" - lore = ["以饥饿值抵御伤害"] +name = "饥饿护盾" +description = "先消耗饥饿值而非生命值来承受伤害。" +lore1 = "以饥饿值抵御伤害" +lore = ["以饥饿值抵御伤害"] [herbalism.luck] - name = "草药师的幸运" - description = "破坏草/花时有概率获得随机物品" - lore0 = "花 = 食物,草 = 种子" - lore1 = "破坏花时获得物品的概率" - lore2 = "破坏草时获得物品的概率" - lore = ["花 = 食物,草 = 种子", "破坏花时获得物品的概率", "破坏草时获得物品的概率"] +name = "草药师的幸运" +description = "破坏草/花时有概率获得随机物品" +lore0 = "花 = 食物,草 = 种子" +lore1 = "破坏花时获得物品的概率" +lore2 = "破坏草时获得物品的概率" +lore = ["花 = 食物,草 = 种子", "破坏花时获得物品的概率", "破坏草时获得物品的概率"] [herbalism.replant] - name = "收割与重种" - description = "手持锄头右键点击作物以收割并自动重种。" - lore1 = "重种方块半径" - lore = ["重种方块半径"] +name = "收割与重种" +description = "手持锄头右键点击作物以收割并自动重种。" +lore1 = "重种方块半径" +lore = ["重种方块半径"] # hunter [hunter] [hunter.adrenaline] - name = "肾上腺素" - description = "生命值越低,近战伤害越高" - lore1 = "最大伤害" - lore = ["最大伤害"] +name = "肾上腺素" +description = "生命值越低,近战伤害越高" +lore1 = "最大伤害" +lore = ["最大伤害"] [hunter.penalty] - name = "" - description = "" - lore1 = "饥饿值耗尽时将获得中毒效果" - lore = ["饥饿值耗尽时将获得中毒效果"] +name = "" +description = "" +lore1 = "饥饿值耗尽时将获得中毒效果" +lore = ["饥饿值耗尽时将获得中毒效果"] [hunter.drop_to_inventory] - name = "掉落物直入背包" - description = "击杀生物/用剑破坏方块时,掉落物自动进入背包" - lore1 = "生物/方块的掉落物会自动进入你的背包(若有空间)。" - lore = ["生物/方块的掉落物会自动进入你的背包(若有空间)。"] +name = "掉落物直入背包" +description = "击杀生物/用剑破坏方块时,掉落物自动进入背包" +lore1 = "生物/方块的掉落物会自动进入你的背包(若有空间)。" +lore = ["生物/方块的掉落物会自动进入你的背包(若有空间)。"] [hunter.invisibility] - name = "消隐步伐" - description = "受到攻击时获得隐身效果,以饥饿为代价" - lore1 = "受击时获得被动隐身" - lore2 = "x 隐身效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "隐身持续时间" - lore = ["受击时获得被动隐身", "x 隐身效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "隐身持续时间"] +name = "消隐步伐" +description = "受到攻击时获得隐身效果,以饥饿为代价" +lore1 = "受击时获得被动隐身" +lore2 = "x 隐身效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "隐身持续时间" +lore = ["受击时获得被动隐身", "x 隐身效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "隐身持续时间"] [hunter.jump_boost] - name = "猎人之跃" - description = "受到攻击时获得跳跃提升效果,以饥饿为代价" - lore1 = "受击时获得被动跳跃提升" - lore2 = "x 跳跃提升效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "跳跃提升叠加倍率,非持续时间。" - lore = ["受击时获得被动跳跃提升", "x 跳跃提升效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "跳跃提升叠加倍率,非持续时间。"] +name = "猎人之跃" +description = "受到攻击时获得跳跃提升效果,以饥饿为代价" +lore1 = "受击时获得被动跳跃提升" +lore2 = "x 跳跃提升效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "跳跃提升叠加倍率,非持续时间。" +lore = ["受击时获得被动跳跃提升", "x 跳跃提升效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "跳跃提升叠加倍率,非持续时间。"] [hunter.luck] - name = "猎人之运" - description = "受到攻击时获得幸运效果,以饥饿为代价" - lore1 = "受击时获得被动幸运" - lore2 = "x 幸运效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "幸运叠加倍率,非持续时间。" - lore = ["受击时获得被动幸运", "x 幸运效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "幸运叠加倍率,非持续时间。"] +name = "猎人之运" +description = "受到攻击时获得幸运效果,以饥饿为代价" +lore1 = "受击时获得被动幸运" +lore2 = "x 幸运效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "幸运叠加倍率,非持续时间。" +lore = ["受击时获得被动幸运", "x 幸运效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "幸运叠加倍率,非持续时间。"] [hunter.regen] - name = "猎人之愈" - description = "受到攻击时获得生命恢复效果,以饥饿为代价" - lore1 = "受击时获得被动生命恢复" - lore2 = "x 生命恢复效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "生命恢复叠加倍率,非持续时间。" - lore = ["受击时获得被动生命恢复", "x 生命恢复效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "生命恢复叠加倍率,非持续时间。"] +name = "猎人之愈" +description = "受到攻击时获得生命恢复效果,以饥饿为代价" +lore1 = "受击时获得被动生命恢复" +lore2 = "x 生命恢复效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "生命恢复叠加倍率,非持续时间。" +lore = ["受击时获得被动生命恢复", "x 生命恢复效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "生命恢复叠加倍率,非持续时间。"] [hunter.resistance] - name = "猎人之盾" - description = "受到攻击时获得抗性提升效果,以饥饿为代价" - lore1 = "受击时获得被动抗性提升" - lore2 = "x 抗性提升效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "抗性提升叠加倍率,非持续时间。" - lore = ["受击时获得被动抗性提升", "x 抗性提升效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "抗性提升叠加倍率,非持续时间。"] +name = "猎人之盾" +description = "受到攻击时获得抗性提升效果,以饥饿为代价" +lore1 = "受击时获得被动抗性提升" +lore2 = "x 抗性提升效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "抗性提升叠加倍率,非持续时间。" +lore = ["受击时获得被动抗性提升", "x 抗性提升效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "抗性提升叠加倍率,非持续时间。"] [hunter.speed] - name = "猎人之速" - description = "受到攻击时获得速度效果,以饥饿为代价" - lore1 = "受击时获得被动速度提升" - lore2 = "x 速度效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "速度叠加倍率,非持续时间。" - lore = ["受击时获得被动速度提升", "x 速度效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "速度叠加倍率,非持续时间。"] +name = "猎人之速" +description = "受到攻击时获得速度效果,以饥饿为代价" +lore1 = "受击时获得被动速度提升" +lore2 = "x 速度效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "速度叠加倍率,非持续时间。" +lore = ["受击时获得被动速度提升", "x 速度效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "速度叠加倍率,非持续时间。"] [hunter.strength] - name = "猎人之力" - description = "受到攻击时获得力量效果,以饥饿为代价" - lore1 = "受击时获得被动力量提升" - lore2 = "x 力量效果,受击后持续 3 秒" - lore3 = "x 叠加饥饿" - lore4 = "饥饿叠加持续时间和倍率。" - lore5 = "力量叠加倍率,非持续时间。" - lore = ["受击时获得被动力量提升", "x 力量效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "力量叠加倍率,非持续时间。"] +name = "猎人之力" +description = "受到攻击时获得力量效果,以饥饿为代价" +lore1 = "受击时获得被动力量提升" +lore2 = "x 力量效果,受击后持续 3 秒" +lore3 = "x 叠加饥饿" +lore4 = "饥饿叠加持续时间和倍率。" +lore5 = "力量叠加倍率,非持续时间。" +lore = ["受击时获得被动力量提升", "x 力量效果,受击后持续 3 秒", "x 叠加饥饿", "饥饿叠加持续时间和倍率。", "力量叠加倍率,非持续时间。"] # nether [nether] [nether.skull_toss] - name = "凋灵头颅投掷" - description1 = "释放你内心的凋灵,用" - description2 = "某人的" - description3 = "头颅。" - lore1 = "头颅投掷冷却秒数。" - lore2 = "使用凋灵头颅:投掷一颗" - lore3 = "凋灵头颅" - lore4 = "撞击时爆炸。" - lore = ["头颅投掷冷却秒数。", "使用凋灵头颅:投掷一颗", "凋灵头颅", "撞击时爆炸。"] +name = "凋灵头颅投掷" +description1 = "释放你内心的凋灵,用" +description2 = "某人的" +description3 = "头颅。" +lore1 = "头颅投掷冷却秒数。" +lore2 = "使用凋灵头颅:投掷一颗" +lore3 = "凋灵头颅" +lore4 = "撞击时爆炸。" +lore = ["头颅投掷冷却秒数。", "使用凋灵头颅:投掷一颗", "凋灵头颅", "撞击时爆炸。"] [nether.wither_resist] - name = "凋零抗性" - description = "通过下界合金之力抵抗凋零效果。" - lore1 = "概率免疫凋零效果(每件装备)。" - lore2 = "被动:穿戴下界合金盔甲有概率免疫" - lore3 = "凋零效果。" - lore = ["概率免疫凋零效果(每件装备)。", "被动:穿戴下界合金盔甲有概率免疫", "凋零效果。"] +name = "凋零抗性" +description = "通过下界合金之力抵抗凋零效果。" +lore1 = "概率免疫凋零效果(每件装备)。" +lore2 = "被动:穿戴下界合金盔甲有概率免疫" +lore3 = "凋零效果。" +lore = ["概率免疫凋零效果(每件装备)。", "被动:穿戴下界合金盔甲有概率免疫", "凋零效果。"] [nether.fire_resist] - name = "火焰抗性" - description = "通过强化皮肤来抵抗火焰。" - lore1 = "概率免疫燃烧效果!" - lore = ["概率免疫燃烧效果!"] +name = "火焰抗性" +description = "通过强化皮肤来抵抗火焰。" +lore1 = "概率免疫燃烧效果!" +lore = ["概率免疫燃烧效果!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "自动熔炼" - description = "自动熔炼挖到的原版矿石" - lore1 = "可熔炼的矿石将自动被熔炼" - lore2 = "% 概率获得额外产物" - lore = ["可熔炼的矿石将自动被熔炼", "% 概率获得额外产物"] +name = "自动熔炼" +description = "自动熔炼挖到的原版矿石" +lore1 = "可熔炼的矿石将自动被熔炼" +lore2 = "% 概率获得额外产物" +lore = ["可熔炼的矿石将自动被熔炼", "% 概率获得额外产物"] [pickaxe.chisel] - name = "矿石凿击" - description = "右键点击矿石以凿出更多矿物,但会严重消耗耐久。" - lore1 = "额外掉落概率" - lore2 = "耐久消耗" - lore = ["额外掉落概率", "耐久消耗"] +name = "矿石凿击" +description = "右键点击矿石以凿出更多矿物,但会严重消耗耐久。" +lore1 = "额外掉落概率" +lore2 = "耐久消耗" +lore = ["额外掉落概率", "耐久消耗"] [pickaxe.drop_to_inventory] - name = "镐子掉落物直入背包" - description = "破坏方块时掉落物自动进入背包" - lore1 = "方块的掉落物会自动进入你的背包(若有空间)。" - lore = ["方块的掉落物会自动进入你的背包(若有空间)。"] +name = "镐子掉落物直入背包" +description = "破坏方块时掉落物自动进入背包" +lore1 = "方块的掉落物会自动进入你的背包(若有空间)。" +lore = ["方块的掉落物会自动进入你的背包(若有空间)。"] [pickaxe.silk_spawner] - name = "精准采集刷怪笼" - description = "使刷怪笼被破坏时掉落" - lore1 = "使刷怪笼可被精准采集的镐子采集。" - lore2 = "使刷怪笼在潜行时可被破坏。" - lore = ["使刷怪笼可被精准采集的镐子采集。", "使刷怪笼在潜行时可被破坏。"] +name = "精准采集刷怪笼" +description = "使刷怪笼被破坏时掉落" +lore1 = "使刷怪笼可被精准采集的镐子采集。" +lore2 = "使刷怪笼在潜行时可被破坏。" +lore = ["使刷怪笼可被精准采集的镐子采集。", "使刷怪笼在潜行时可被破坏。"] [pickaxe.vein_miner] - name = "连锁挖矿" - description = "挖掘原版矿石时可连锁开采整条矿脉" - lore1 = "潜行并挖掘矿石" - lore2 = "连锁挖矿范围" - lore3 = "此技能不会将掉落物合并在一起!" - lore = ["潜行并挖掘矿石", "连锁挖矿范围", "此技能不会将掉落物合并在一起!"] +name = "连锁挖矿" +description = "挖掘原版矿石时可连锁开采整条矿脉" +lore1 = "潜行并挖掘矿石" +lore2 = "连锁挖矿范围" +lore3 = "此技能不会将掉落物合并在一起!" +lore = ["潜行并挖掘矿石", "连锁挖矿范围", "此技能不会将掉落物合并在一起!"] # ranged [ranged] [ranged.arrow_recovery] - name = "箭矢回收" - description = "击杀敌人后回收箭矢。" - lore1 = "命中/击杀时回收箭矢的概率" - lore2 = "概率:" - lore = ["命中/击杀时回收箭矢的概率", "概率:"] +name = "箭矢回收" +description = "击杀敌人后回收箭矢。" +lore1 = "命中/击杀时回收箭矢的概率" +lore2 = "概率:" +lore = ["命中/击杀时回收箭矢的概率", "概率:"] [ranged.web_shot] - name = "蛛网陷阱" - description = "命中目标时在其周围生成蛛网!" - lore1 = "8 个蛛网围绕 1 个雪球合成,然后投掷!" - lore2 = "秒的牢笼,大约。" - lore = ["8 个蛛网围绕 1 个雪球合成,然后投掷!", "秒的牢笼,大约。"] +name = "蛛网陷阱" +description = "命中目标时在其周围生成蛛网!" +lore1 = "8 个蛛网围绕 1 个雪球合成,然后投掷!" +lore2 = "秒的牢笼,大约。" +lore = ["8 个蛛网围绕 1 个雪球合成,然后投掷!", "秒的牢笼,大约。"] [ranged.force_shot] - name = "强力射击" - description = "射出的弹射物更远、更快!" - advancementname = "远距离射击" - advancementlore = "从 30 格方块外命中目标!" - lore1 = "弹射物速度" - lore = ["弹射物速度"] +name = "强力射击" +description = "射出的弹射物更远、更快!" +advancementname = "远距离射击" +advancementlore = "从 30 格方块外命中目标!" +lore1 = "弹射物速度" +lore = ["弹射物速度"] [ranged.lunge_shot] - name = "突刺射击" - description = "下落时射出的箭会将你随机弹向一个方向" - lore1 = "随机爆发速度" - lore = ["随机爆发速度"] +name = "突刺射击" +description = "下落时射出的箭会将你随机弹向一个方向" +lore1 = "随机爆发速度" +lore = ["随机爆发速度"] [ranged.arrow_piercing] - name = "箭矢穿透" - description = "为弹射物添加穿透效果!射穿一切!" - lore1 = "穿透目标数" - lore = ["穿透目标数"] +name = "箭矢穿透" +description = "为弹射物添加穿透效果!射穿一切!" +lore1 = "穿透目标数" +lore = ["穿透目标数"] # rift [rift] [rift.remote_access] - name = "远程访问" - description = "从虚空中取物,远程打开已标记的容器。" - lore1 = "末影珍珠 + 指南针 = 遗物传送钥" - lore2 = "此物品允许你远程访问容器" - lore3 = "合成后查看物品了解使用方法" - notcontainer = "那不是一个容器" - lore = ["末影珍珠 + 指南针 = 遗物传送钥", "此物品允许你远程访问容器", "合成后查看物品了解使用方法"] +name = "远程访问" +description = "从虚空中取物,远程打开已标记的容器。" +lore1 = "末影珍珠 + 指南针 = 遗物传送钥" +lore2 = "此物品允许你远程访问容器" +lore3 = "合成后查看物品了解使用方法" +notcontainer = "那不是一个容器" +lore = ["末影珍珠 + 指南针 = 遗物传送钥", "此物品允许你远程访问容器", "合成后查看物品了解使用方法"] [rift.blink] - name = "裂隙闪现" - description = "短距离瞬间传送,不过是眨眼之间!" - lore1 = "闪现格数(垂直 2 倍)" - lore2 = "疾跑时:双击跳跃键以" - lore3 = "闪现" - lore = ["闪现格数(垂直 2 倍)", "疾跑时:双击跳跃键以", "闪现"] +name = "裂隙闪现" +description = "短距离瞬间传送,不过是眨眼之间!" +lore1 = "闪现格数(垂直 2 倍)" +lore2 = "疾跑时:双击跳跃键以" +lore3 = "闪现" +lore = ["闪现格数(垂直 2 倍)", "疾跑时:双击跳跃键以", "闪现"] [rift.chest] - name = "便携末影箱" - description = "手持末影箱左键点击即可打开。" - lore1 = "点击手中的末影箱即可打开(无需放置)" - lore = ["点击手中的末影箱即可打开(无需放置)"] +name = "便携末影箱" +description = "手持末影箱左键点击即可打开。" +lore1 = "点击手中的末影箱即可打开(无需放置)" +lore = ["点击手中的末影箱即可打开(无需放置)"] [rift.descent] - name = "反飘浮" - description = "厌倦了被困在半空中?这个技能就是为你准备的!" - lore1 = "潜行即可下降,下落速度低于正常值!" - lore2 = "冷却:" - lore = ["潜行即可下降,下落速度低于正常值!", "冷却:"] +name = "反飘浮" +description = "厌倦了被困在半空中?这个技能就是为你准备的!" +lore1 = "潜行即可下降,下落速度低于正常值!" +lore2 = "冷却:" +lore = ["潜行即可下降,下落速度低于正常值!", "冷却:"] [rift.gate] - name = "裂隙之门" - description = "传送到标记的位置。" - lore1 = "合成:绿宝石 + 紫水晶碎片 + 末影珍珠" - lore2 = "使用前请先阅读!" - lore3 = "5 秒延迟," - lore4 = "在传送动画中你可能会死亡" - lore = ["合成:绿宝石 + 紫水晶碎片 + 末影珍珠", "使用前请先阅读!", "5 秒延迟,", "在传送动画中你可能会死亡"] +name = "裂隙之门" +description = "传送到标记的位置。" +lore1 = "合成:绿宝石 + 紫水晶碎片 + 末影珍珠" +lore2 = "使用前请先阅读!" +lore3 = "5 秒延迟," +lore4 = "在传送动画中你可能会死亡" +lore = ["合成:绿宝石 + 紫水晶碎片 + 末影珍珠", "使用前请先阅读!", "5 秒延迟,", "在传送动画中你可能会死亡"] [rift.resist] - name = "裂隙抗性" - description = "使用末影物品和技能时获得抗性提升" - lore1 = "+ 被动:使用裂隙技能或末影物品时提供抗性提升" - lore2 = "不包括便携末影箱,仅限可消耗的物品" - lore = ["+ 被动:使用裂隙技能或末影物品时提供抗性提升", "不包括便携末影箱,仅限可消耗的物品"] +name = "裂隙抗性" +description = "使用末影物品和技能时获得抗性提升" +lore1 = "+ 被动:使用裂隙技能或末影物品时提供抗性提升" +lore2 = "不包括便携末影箱,仅限可消耗的物品" +lore = ["+ 被动:使用裂隙技能或末影物品时提供抗性提升", "不包括便携末影箱,仅限可消耗的物品"] [rift.visage] - name = "裂隙面容" - description = "背包中有末影珍珠时可阻止末影人变得具有攻击性。" - lore1 = "背包中有末影珍珠时末影人不会变得具有攻击性。" - lore = ["背包中有末影珍珠时末影人不会变得具有攻击性。"] +name = "裂隙面容" +description = "背包中有末影珍珠时可阻止末影人变得具有攻击性。" +lore1 = "背包中有末影珍珠时末影人不会变得具有攻击性。" +lore = ["背包中有末影珍珠时末影人不会变得具有攻击性。"] # seaborn [seaborn] [seaborn.oxygen] - name = "有机氧气罐" - description = "让你的小肺容纳更多氧气!" - lore1 = "氧气容量增加" - lore = ["氧气容量增加"] +name = "有机氧气罐" +description = "让你的小肺容纳更多氧气!" +lore1 = "氧气容量增加" +lore = ["氧气容量增加"] [seaborn.fishers_fantasy] - name = "渔夫的幻想" - description = "钓鱼获得更多经验,捕获更多鱼!" - lore1 = "每级都有概率获得更多经验和鱼!" - lore = ["每级都有概率获得更多经验和鱼!"] +name = "渔夫的幻想" +description = "钓鱼获得更多经验,捕获更多鱼!" +lore1 = "每级都有概率获得更多经验和鱼!" +lore = ["每级都有概率获得更多经验和鱼!"] [seaborn.haste] - name = "海龟矿工" - description = "在水下挖掘时获得急迫效果!" - lore1 = "水下呼吸效果消失后,在水下挖掘时获得急迫 3 效果(与水下速掘叠加)!" - lore = ["水下呼吸效果消失后,在水下挖掘时获得急迫 3 效果(与水下速掘叠加)!"] +name = "海龟矿工" +description = "在水下挖掘时获得急迫效果!" +lore1 = "水下呼吸效果消失后,在水下挖掘时获得急迫 3 效果(与水下速掘叠加)!" +lore = ["水下呼吸效果消失后,在水下挖掘时获得急迫 3 效果(与水下速掘叠加)!"] [seaborn.night_vision] - name = "海龟之眼" - description = "在水下时获得夜视效果" - lore1 = "水下呼吸效果消失后,在水下时直接获得夜视效果!" - lore = ["水下呼吸效果消失后,在水下时直接获得夜视效果!"] +name = "海龟之眼" +description = "在水下时获得夜视效果" +lore1 = "水下呼吸效果消失后,在水下时直接获得夜视效果!" +lore = ["水下呼吸效果消失后,在水下时直接获得夜视效果!"] [seaborn.dolphin_grace] - name = "海豚之恩" - description = "无需海豚也能像海豚一样游泳" - lore1 = "+ 被动:获得" - lore2 = "x 速度(海豚恩惠)" - lore3 = "精准的德国工程——等等不太对……不与深海探索者兼容" - lore = ["+ 被动:获得", "x 速度(海豚恩惠)", "精准的德国工程——等等不太对……不与深海探索者兼容"] +name = "海豚之恩" +description = "无需海豚也能像海豚一样游泳" +lore1 = "+ 被动:获得" +lore2 = "x 速度(海豚恩惠)" +lore3 = "精准的德国工程——等等不太对……不与深海探索者兼容" +lore = ["+ 被动:获得", "x 速度(海豚恩惠)", "精准的德国工程——等等不太对……不与深海探索者兼容"] # stealth [stealth] [stealth.ghost_armor] - name = "幽灵战甲" - description = "未受伤时缓慢构建护甲,持续 1 次攻击" - lore1 = "最大护甲值" - lore2 = "速度" - lore = ["最大护甲值", "速度"] +name = "幽灵战甲" +description = "未受伤时缓慢构建护甲,持续 1 次攻击" +lore1 = "最大护甲值" +lore2 = "速度" +lore = ["最大护甲值", "速度"] [stealth.night_vision] - name = "潜行视觉" - description = "潜行时获得夜视效果" - lore1 = "获得一次爆发的" - lore2 = "夜视效果" - lore3 = "(潜行时)" - lore = ["获得一次爆发的", "夜视效果", "(潜行时)"] +name = "潜行视觉" +description = "潜行时获得夜视效果" +lore1 = "获得一次爆发的" +lore2 = "夜视效果" +lore3 = "(潜行时)" +lore = ["获得一次爆发的", "夜视效果", "(潜行时)"] [stealth.snatch] - name = "物品夺取" - description = "潜行时立即拾取周围的掉落物!" - lore1 = "夺取半径" - lore = ["夺取半径"] +name = "物品夺取" +description = "潜行时立即拾取周围的掉落物!" +lore1 = "夺取半径" +lore = ["夺取半径"] [stealth.speed] - name = "潜行加速" - description = "潜行时获得速度提升" - lore1 = "潜行速度" - lore = ["潜行速度"] +name = "潜行加速" +description = "潜行时获得速度提升" +lore1 = "潜行速度" +lore = ["潜行速度"] [stealth.ender_veil] - name = "末影面纱" - description = "不再需要南瓜来防止末影人攻击了" - lore1 = "潜行时防止末影人攻击" - lore2 = "阻止所有末影人攻击" - lore = ["潜行时防止末影人攻击", "阻止所有末影人攻击"] +name = "末影面纱" +description = "不再需要南瓜来防止末影人攻击了" +lore1 = "潜行时防止末影人攻击" +lore2 = "阻止所有末影人攻击" +lore = ["潜行时防止末影人攻击", "阻止所有末影人攻击"] # sword [sword] [sword.machete] - name = "弯刀" - description = "轻松砍开植被!" - lore1 = "斩击半径" - lore2 = "斩击冷却" - lore3 = "耐久消耗" - lore = ["斩击半径", "斩击冷却", "耐久消耗"] +name = "弯刀" +description = "轻松砍开植被!" +lore1 = "斩击半径" +lore2 = "斩击冷却" +lore3 = "耐久消耗" +lore = ["斩击半径", "斩击冷却", "耐久消耗"] [sword.bloody_blade] - name = "嗜血之刃" - description = "用剑攻击造成流血效果!" - lore1 = "用剑攻击活体生物时造成流血" - lore2 = "流血持续时间" - lore3 = "流血冷却" - lore = ["用剑攻击活体生物时造成流血", "流血持续时间", "流血冷却"] +name = "嗜血之刃" +description = "用剑攻击造成流血效果!" +lore1 = "用剑攻击活体生物时造成流血" +lore2 = "流血持续时间" +lore3 = "流血冷却" +lore = ["用剑攻击活体生物时造成流血", "流血持续时间", "流血冷却"] [sword.poisoned_blade] - name = "淬毒之刃" - description = "用剑攻击造成中毒效果!" - lore1 = "用剑攻击活体生物时造成中毒" - lore2 = "中毒持续时间" - lore3 = "中毒冷却" - lore = ["用剑攻击活体生物时造成中毒", "中毒持续时间", "中毒冷却"] +name = "淬毒之刃" +description = "用剑攻击造成中毒效果!" +lore1 = "用剑攻击活体生物时造成中毒" +lore2 = "中毒持续时间" +lore3 = "中毒冷却" +lore = ["用剑攻击活体生物时造成中毒", "中毒持续时间", "中毒冷却"] # taming [taming] [taming.damage] - name = "驯兽伤害" - description = "提升已驯服动物的伤害。" - lore1 = "伤害提升" - lore = ["伤害提升"] +name = "驯兽伤害" +description = "提升已驯服动物的伤害。" +lore1 = "伤害提升" +lore = ["伤害提升"] [taming.health] - name = "驯兽生命" - description = "提升已驯服动物的生命值。" - lore1 = "生命提升" - lore = ["生命提升"] +name = "驯兽生命" +description = "提升已驯服动物的生命值。" +lore1 = "生命提升" +lore = ["生命提升"] [taming.regeneration] - name = "驯兽再生" - description = "提升已驯服动物的生命恢复速度。" - lore1 = "生命/秒" - lore = ["生命/秒"] +name = "驯兽再生" +description = "提升已驯服动物的生命恢复速度。" +lore1 = "生命/秒" +lore = ["生命/秒"] # tragoul [tragoul] [tragoul.thorns] - name = "荆棘" - description = "将伤害反弹给攻击者!" - lore1 = "受击时反弹伤害" - lore = ["受击时反弹伤害"] +name = "荆棘" +description = "将伤害反弹给攻击者!" +lore1 = "受击时反弹伤害" +lore = ["受击时反弹伤害"] [tragoul.globe] - name = "痛苦之球" - description = "根据周围敌人数量分摊你造成的伤害!" - lore1 = "周围敌人越多,对每个敌人造成的伤害越少" - lore2 = "范围:" - lore3 = "对所有实体的附加伤害:" - lore = ["周围敌人越多,对每个敌人造成的伤害越少", "范围:", "对所有实体的附加伤害:"] +name = "痛苦之球" +description = "根据周围敌人数量分摊你造成的伤害!" +lore1 = "周围敌人越多,对每个敌人造成的伤害越少" +lore2 = "范围:" +lore3 = "对所有实体的附加伤害:" +lore = ["周围敌人越多,对每个敌人造成的伤害越少", "范围:", "对所有实体的附加伤害:"] [tragoul.healing] - name = "痛苦之志" - description = "根据你造成的伤害恢复生命值!" - lore1 = "伤害别人从未感觉如此美妙!通过造成伤害恢复生命" - lore2 = "3 秒伤害窗口期内恢复生命,冷却 1 秒" - lore3 = "每伤害百分比回复生命:" - lore = ["伤害别人从未感觉如此美妙!通过造成伤害恢复生命", "3 秒伤害窗口期内恢复生命,冷却 1 秒", "每伤害百分比回复生命:"] +name = "痛苦之志" +description = "根据你造成的伤害恢复生命值!" +lore1 = "伤害别人从未感觉如此美妙!通过造成伤害恢复生命" +lore2 = "3 秒伤害窗口期内恢复生命,冷却 1 秒" +lore3 = "每伤害百分比回复生命:" +lore = ["伤害别人从未感觉如此美妙!通过造成伤害恢复生命", "3 秒伤害窗口期内恢复生命,冷却 1 秒", "每伤害百分比回复生命:"] [tragoul.lance] - name = "尸骨之矛" - description = "击杀敌人或技能击杀敌人时,生成长矛对附近敌人造成伤害!" - lore1 = "长矛会从你击杀的目标中射出,如果此技能击杀了敌人也会触发。" - lore2 = "牺牲部分生命来生成长矛(这可能会杀死你)" - lore3 = "最大长矛数:1 + " - lore = ["长矛会从你击杀的目标中射出,如果此技能击杀了敌人也会触发。", "牺牲部分生命来生成长矛(这可能会杀死你)", "最大长矛数:1 + "] +name = "尸骨之矛" +description = "击杀敌人或技能击杀敌人时,生成长矛对附近敌人造成伤害!" +lore1 = "长矛会从你击杀的目标中射出,如果此技能击杀了敌人也会触发。" +lore2 = "牺牲部分生命来生成长矛(这可能会杀死你)" +lore3 = "最大长矛数:1 + " +lore = ["长矛会从你击杀的目标中射出,如果此技能击杀了敌人也会触发。", "牺牲部分生命来生成长矛(这可能会杀死你)", "最大长矛数:1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "玻璃大炮" - description = "护甲值越低,空手伤害越高" - lore1 = "x 伤害(0 护甲时)" - lore2 = "每级额外伤害" - lore = ["x 伤害(0 护甲时)", "每级额外伤害"] +name = "玻璃大炮" +description = "护甲值越低,空手伤害越高" +lore1 = "x 伤害(0 护甲时)" +lore2 = "每级额外伤害" +lore = ["x 伤害(0 护甲时)", "每级额外伤害"] [unarmed.power] - name = "空手之力" - description = "提升空手伤害" - lore1 = "伤害" - lore = ["伤害"] +name = "空手之力" +description = "提升空手伤害" +lore1 = "伤害" +lore = ["伤害"] [unarmed.sucker_punch] - name = "偷袭拳" - description = "疾跑出拳,更加致命。" - lore1 = "伤害" - lore2 = "出拳时伤害随移动速度增加" - lore = ["伤害", "出拳时伤害随移动速度增加"] +name = "偷袭拳" +description = "疾跑出拳,更加致命。" +lore1 = "伤害" +lore2 = "出拳时伤害随移动速度增加" +lore = ["伤害", "出拳时伤害随移动速度增加"] diff --git a/src/main/resources/zh_TW.toml b/src/main/resources/zh_TW.toml index af8fdf2d0..432ac778c 100644 --- a/src/main/resources/zh_TW.toml +++ b/src/main/resources/zh_TW.toml @@ -2,2210 +2,2210 @@ [advancement] [advancement.challenge_move_1k] - title = "上路啦!" - description = "總計走過1公里的距離(1,000 格方塊)" +title = "上路啦!" +description = "總計走過1公里的距離(1,000 格方塊)" [advancement.challenge_sprint_5k] - title = "長跑5公里!" - description = "總計走過5公里(5,000 格方塊)" +title = "長跑5公里!" +description = "總計走過5公里(5,000 格方塊)" [advancement.challenge_sprint_50k] - title = "飛速衝刺50公里!" - description = "總計走過50公里(50,000 格方塊)" +title = "飛速衝刺50公里!" +description = "總計走過50公里(50,000 格方塊)" [advancement.challenge_sprint_500k] - title = "穿越宇宙!!" - description = "總計走過500公里(500,000 格方塊)" +title = "穿越宇宙!!" +description = "總計走過500公里(500,000 格方塊)" [advancement.challenge_sprint_marathon] - title = "衝刺一場(真正的)馬拉松!" - description = "以疾跑的方式跑過 42,195 格方塊!" +title = "衝刺一場(真正的)馬拉松!" +description = "以疾跑的方式跑過 42,195 格方塊!" [advancement.challenge_place_1k] - title = "初學建築師!" - description = "放置 1,000 格方塊" +title = "初學建築師!" +description = "放置 1,000 格方塊" [advancement.challenge_place_5k] - title = "中級建築師!" - description = "放置 5,000 格方塊" +title = "中級建築師!" +description = "放置 5,000 格方塊" [advancement.challenge_place_50k] - title = "高級建築師!" - description = "放置 50,000 格方塊" +title = "高級建築師!" +description = "放置 50,000 格方塊" [advancement.challenge_place_500k] - title = "建築大師!" - description = "放置 500,000 格方塊" +title = "建築大師!" +description = "放置 500,000 格方塊" [advancement.challenge_place_5m] - title = "對稱之信徒!" - description = "現實是你的遊樂場!(500萬格方塊)" +title = "對稱之信徒!" +description = "現實是你的遊樂場!(500萬格方塊)" [advancement.challenge_chop_1k] - title = "初學伐木工!" - description = "砍伐 1,000 格方塊" +title = "初學伐木工!" +description = "砍伐 1,000 格方塊" [advancement.challenge_chop_5k] - title = "中級伐木工!" - description = "砍伐 5,000 格方塊" +title = "中級伐木工!" +description = "砍伐 5,000 格方塊" [advancement.challenge_chop_50k] - title = "高級伐木工!" - description = "砍伐 50,000 格方塊" +title = "高級伐木工!" +description = "砍伐 50,000 格方塊" [advancement.challenge_chop_500k] - title = "伐木大師!" - description = "砍伐 500,000 格方塊" +title = "伐木大師!" +description = "砍伐 500,000 格方塊" [advancement.challenge_chop_5m] - title = "傑克森小狗" - description = "最棒的好孩子!(500萬格方塊)" +title = "傑克森小狗" +description = "最棒的好孩子!(500萬格方塊)" [advancement.challenge_block_1k] - title = "初學格擋!" - description = "格擋 1000 次攻擊" +title = "初學格擋!" +description = "格擋 1000 次攻擊" [advancement.challenge_block_5k] - title = "格擋真有趣!" - description = "格擋 5000 次攻擊" +title = "格擋真有趣!" +description = "格擋 5000 次攻擊" [advancement.challenge_block_50k] - title = "為格擋而生!" - description = "格擋 50,000 次攻擊" +title = "為格擋而生!" +description = "格擋 50,000 次攻擊" [advancement.challenge_block_500k] - title = "格擋即使命!" - description = "格擋 500,000 次攻擊" +title = "格擋即使命!" +description = "格擋 500,000 次攻擊" [advancement.challenge_block_5m] - title = "傷害之手" - description = "格擋 5,000,000 次攻擊" +title = "傷害之手" +description = "格擋 5,000,000 次攻擊" [advancement.challenge_brew_1k] - title = "初學煉金術士!" - description = "飲用 1000 瓶藥水" +title = "初學煉金術士!" +description = "飲用 1000 瓶藥水" [advancement.challenge_brew_5k] - title = "中級煉金術士!" - description = "飲用 5000 瓶藥水" +title = "中級煉金術士!" +description = "飲用 5000 瓶藥水" [advancement.challenge_brew_50k] - title = "高級煉金術士!" - description = "飲用 50,000 瓶藥水" +title = "高級煉金術士!" +description = "飲用 50,000 瓶藥水" [advancement.challenge_brew_500k] - title = "煉金大師!" - description = "飲用 500,000 瓶藥水" +title = "煉金大師!" +description = "飲用 500,000 瓶藥水" [advancement.challenge_brew_5m] - title = "大煉金術師" - description = "飲用 5,000,000 瓶藥水" +title = "大煉金術師" +description = "飲用 5,000,000 瓶藥水" [advancement.challenge_brewsplash_1k] - title = "初學藥水投擲手!" - description = "投擲 1000 瓶藥水" +title = "初學藥水投擲手!" +description = "投擲 1000 瓶藥水" [advancement.challenge_brewsplash_5k] - title = "中級藥水投擲手!" - description = "投擲 5000 瓶藥水" +title = "中級藥水投擲手!" +description = "投擲 5000 瓶藥水" [advancement.challenge_brewsplash_50k] - title = "高級藥水投擲手!" - description = "投擲 50,000 瓶藥水" +title = "高級藥水投擲手!" +description = "投擲 50,000 瓶藥水" [advancement.challenge_brewsplash_500k] - title = "藥水投擲大師!" - description = "投擲 500,000 瓶藥水" +title = "藥水投擲大師!" +description = "投擲 500,000 瓶藥水" [advancement.challenge_brewsplash_5m] - title = "潑灑宗師" - description = "投擲 5,000,000 瓶藥水" +title = "潑灑宗師" +description = "投擲 5,000,000 瓶藥水" [advancement.challenge_craft_1k] - title = "巧手製造匠!" - description = "製作 1000 件物品" +title = "巧手製造匠!" +description = "製作 1000 件物品" [advancement.challenge_craft_5k] - title = "暴躁製造匠!" - description = "製作 5000 件物品" +title = "暴躁製造匠!" +description = "製作 5000 件物品" [advancement.challenge_craft_50k] - title = "勤勉製造匠!" - description = "製作 50,000 件物品" +title = "勤勉製造匠!" +description = "製作 50,000 件物品" [advancement.challenge_craft_500k] - title = "喧鬧製造匠!" - description = "製作 500,000 件物品" +title = "喧鬧製造匠!" +description = "製作 500,000 件物品" [advancement.challenge_craft_5m] - title = "災難級工匠大人" - description = "製作 5,000,000 件物品" +title = "災難級工匠大人" +description = "製作 5,000,000 件物品" [advancement.challenge_enchant_1k] - title = "初學附魔師!" - description = "附魔 1000 件物品" +title = "初學附魔師!" +description = "附魔 1000 件物品" [advancement.challenge_enchant_5k] - title = "中級附魔師!" - description = "附魔 5000 件物品" +title = "中級附魔師!" +description = "附魔 5000 件物品" [advancement.challenge_enchant_50k] - title = "高級附魔師!" - description = "附魔 50,000 件物品" +title = "高級附魔師!" +description = "附魔 50,000 件物品" [advancement.challenge_enchant_500k] - title = "附魔大師!" - description = "附魔 500,000 件物品" +title = "附魔大師!" +description = "附魔 500,000 件物品" [advancement.challenge_enchant_5m] - title = "神秘附魔家" - description = "附魔 5,000,000 件物品" +title = "神秘附魔家" +description = "附魔 5,000,000 件物品" [advancement.challenge_excavate_1k] - title = "初學挖掘者!" - description = "挖掘 1000 格方塊" +title = "初學挖掘者!" +description = "挖掘 1000 格方塊" [advancement.challenge_excavate_5k] - title = "中級挖掘者!" - description = "挖掘 5000 格方塊" +title = "中級挖掘者!" +description = "挖掘 5000 格方塊" [advancement.challenge_excavate_50k] - title = "高級挖掘者!" - description = "挖掘 50,000 格方塊" +title = "高級挖掘者!" +description = "挖掘 50,000 格方塊" [advancement.challenge_excavate_500k] - title = "挖掘大師!" - description = "挖掘 500,000 格方塊" +title = "挖掘大師!" +description = "挖掘 500,000 格方塊" [advancement.challenge_excavate_5m] - title = "神秘挖掘者" - description = "挖掘 5,000,000 格方塊" +title = "神秘挖掘者" +description = "挖掘 5,000,000 格方塊" [advancement.horrible_person] - title = "你是一個可怕的人" - description = "實在是深不可測" +title = "你是一個可怕的人" +description = "實在是深不可測" [advancement.challenge_turtle_egg_smasher] - title = "海龜蛋粉碎機!" - description = "打碎 100 個海龜蛋" +title = "海龜蛋粉碎機!" +description = "打碎 100 個海龜蛋" [advancement.challenge_turtle_egg_annihilator] - title = "海龜蛋殲滅者!" - description = "打碎 500 個海龜蛋" +title = "海龜蛋殲滅者!" +description = "打碎 500 個海龜蛋" [advancement.challenge_novice_hunter] - title = "新手獵人!" - description = "殺死 100 個實體" +title = "新手獵人!" +description = "殺死 100 個實體" [advancement.challenge_intermediate_hunter] - title = "中級獵人!" - description = "殺死 500 個實體" +title = "中級獵人!" +description = "殺死 500 個實體" [advancement.challenge_advanced_hunter] - title = "高級獵人!" - description = "殺死 5000 個實體" +title = "高級獵人!" +description = "殺死 5000 個實體" [advancement.challenge_creeper_conqueror] - title = "苦力怕征服者!" - description = "殺死 50 隻苦力怕" +title = "苦力怕征服者!" +description = "殺死 50 隻苦力怕" [advancement.challenge_creeper_annihilator] - title = "苦力怕殲滅者!" - description = "殺死 200 隻苦力怕" +title = "苦力怕殲滅者!" +description = "殺死 200 隻苦力怕" [advancement.challenge_pickaxe_1k] - title = "新手礦工" - description = "挖碎 1000 格方塊" +title = "新手礦工" +description = "挖碎 1000 格方塊" [advancement.challenge_pickaxe_5k] - title = "熟練礦工" - description = "挖碎 5000 格方塊" +title = "熟練礦工" +description = "挖碎 5000 格方塊" [advancement.challenge_pickaxe_50k] - title = "專家礦工" - description = "挖碎 50,000 格方塊" +title = "專家礦工" +description = "挖碎 50,000 格方塊" [advancement.challenge_pickaxe_500k] - title = "礦工大師" - description = "挖碎 500,000 格方塊" +title = "礦工大師" +description = "挖碎 500,000 格方塊" [advancement.challenge_pickaxe_5m] - title = "傳奇礦工" - description = "挖碎 5,000,000 格方塊" +title = "傳奇礦工" +description = "挖碎 5,000,000 格方塊" [advancement.challenge_eat_100] - title = "好多東西吃!" - description = "吃下超過 100 個食物!" +title = "好多東西吃!" +description = "吃下超過 100 個食物!" [advancement.challenge_eat_1000] - title = "永無止境的飢餓!" - description = "吃下超過 1,000 個食物!" +title = "永無止境的飢餓!" +description = "吃下超過 1,000 個食物!" [advancement.challenge_eat_10000] - title = "永恆的飢渴!" - description = "吃下超過 10,000 個食物!" +title = "永恆的飢渴!" +description = "吃下超過 10,000 個食物!" [advancement.challenge_harvest_100] - title = "滿載收穫" - description = "收割超過 100 株作物!" +title = "滿載收穫" +description = "收割超過 100 株作物!" [advancement.challenge_harvest_1000] - title = "豐收慶典" - description = "收割超過 1,000 株作物!" +title = "豐收慶典" +description = "收割超過 1,000 株作物!" [advancement.challenge_swim_1nm] - title = "人體潛水艇!" - description = "游泳 1 海里(1,852 格方塊)" +title = "人體潛水艇!" +description = "游泳 1 海里(1,852 格方塊)" [advancement.challenge_sneak_1k] - title = "膝蓋好痛" - description = "潛行超過 1 公里(1,000 格方塊)" +title = "膝蓋好痛" +description = "潛行超過 1 公里(1,000 格方塊)" [advancement.challenge_sneak_5k] - title = "暗影行者" - description = "潛行超過 5,000 格方塊" +title = "暗影行者" +description = "潛行超過 5,000 格方塊" [advancement.challenge_sneak_20k] - title = "幽靈" - description = "潛行超過 20,000 格方塊" +title = "幽靈" +description = "潛行超過 20,000 格方塊" [advancement.challenge_swim_5k] - title = "深海潛水員" - description = "游泳超過 5,000 格方塊" +title = "深海潛水員" +description = "游泳超過 5,000 格方塊" [advancement.challenge_swim_20k] - title = "海神之選" - description = "游泳超過 20,000 格方塊" +title = "海神之選" +description = "游泳超過 20,000 格方塊" [advancement.challenge_sword_100] - title = "初見血光" - description = "用劍命中 100 次" +title = "初見血光" +description = "用劍命中 100 次" [advancement.challenge_sword_1k] - title = "劍舞者" - description = "用劍命中 1,000 次" +title = "劍舞者" +description = "用劍命中 1,000 次" [advancement.challenge_sword_10k] - title = "千刀萬剮" - description = "用劍命中 10,000 次" +title = "千刀萬剮" +description = "用劍命中 10,000 次" [advancement.challenge_unarmed_100] - title = "酒吧打手" - description = "徒手命中 100 次" +title = "酒吧打手" +description = "徒手命中 100 次" [advancement.challenge_unarmed_1k] - title = "鐵拳" - description = "徒手命中 1,000 次" +title = "鐵拳" +description = "徒手命中 1,000 次" [advancement.challenge_unarmed_10k] - title = "一拳超人" - description = "徒手命中 10,000 次" +title = "一拳超人" +description = "徒手命中 10,000 次" [advancement.challenge_trag_1k] - title = "血的代價" - description = "承受 1,000 點傷害" +title = "血的代價" +description = "承受 1,000 點傷害" [advancement.challenge_trag_10k] - title = "赤潮" - description = "承受 10,000 點傷害" +title = "赤潮" +description = "承受 10,000 點傷害" [advancement.challenge_trag_100k] - title = "苦難化身" - description = "承受 100,000 點傷害" +title = "苦難化身" +description = "承受 100,000 點傷害" [advancement.challenge_ranged_100] - title = "靶場練習" - description = "發射 100 枚彈射物" +title = "靶場練習" +description = "發射 100 枚彈射物" [advancement.challenge_ranged_1k] - title = "鷹眼" - description = "發射 1,000 枚彈射物" +title = "鷹眼" +description = "發射 1,000 枚彈射物" [advancement.challenge_ranged_10k] - title = "箭雨風暴" - description = "發射 10,000 枚彈射物" +title = "箭雨風暴" +description = "發射 10,000 枚彈射物" [advancement.challenge_chronos_1h] - title = "滴答滴答" - description = "在線 1 小時" +title = "滴答滴答" +description = "在線 1 小時" [advancement.challenge_chronos_24h] - title = "時間之沙" - description = "在線 24 小時" +title = "時間之沙" +description = "在線 24 小時" [advancement.challenge_chronos_168h] - title = "永恆不朽" - description = "在線 168 小時(1 週)" +title = "永恆不朽" +description = "在線 168 小時(1 週)" [advancement.challenge_nether_50] - title = "地獄守門人" - description = "擊殺 50 隻地獄生物" +title = "地獄守門人" +description = "擊殺 50 隻地獄生物" [advancement.challenge_nether_500] - title = "深淵守望者" - description = "擊殺 500 隻地獄生物" +title = "深淵守望者" +description = "擊殺 500 隻地獄生物" [advancement.challenge_nether_5k] - title = "地獄之主" - description = "擊殺 5,000 隻地獄生物" +title = "地獄之主" +description = "擊殺 5,000 隻地獄生物" [advancement.challenge_rift_50] - title = "空間異常" - description = "傳送 50 次" +title = "空間異常" +description = "傳送 50 次" [advancement.challenge_rift_500] - title = "虛空行者" - description = "傳送 500 次" +title = "虛空行者" +description = "傳送 500 次" [advancement.challenge_rift_5k] - title = "穿梭世界" - description = "傳送 5,000 次" +title = "穿梭世界" +description = "傳送 5,000 次" [advancement.challenge_taming_10] - title = "動物低語者" - description = "繁殖 10 隻動物" +title = "動物低語者" +description = "繁殖 10 隻動物" [advancement.challenge_taming_50] - title = "獸群首領" - description = "繁殖 50 隻動物" +title = "獸群首領" +description = "繁殖 50 隻動物" [advancement.challenge_taming_500] - title = "馴獸大師" - description = "繁殖 500 隻動物" +title = "馴獸大師" +description = "繁殖 500 隻動物" # Agility - New Achievement Chains [advancement.challenge_sprint_dist_5k] - title = "速度惡魔" - description = "疾跑超過 5 公里 (5,000 格)" +title = "速度惡魔" +description = "疾跑超過 5 公里 (5,000 格)" [advancement.challenge_sprint_dist_50k] - title = "閃電之足" - description = "疾跑超過 50 公里 (50,000 格)" +title = "閃電之足" +description = "疾跑超過 50 公里 (50,000 格)" [advancement.challenge_agility_swim_1k] - title = "水面行者" - description = "游泳超過 1 公里 (1,000 格)" +title = "水面行者" +description = "游泳超過 1 公里 (1,000 格)" [advancement.challenge_agility_swim_10k] - title = "遠洋航行者" - description = "游泳超過 10 公里 (10,000 格)" +title = "遠洋航行者" +description = "游泳超過 10 公里 (10,000 格)" [advancement.challenge_fly_1k] - title = "翱翔之舞" - description = "飛行超過 1 公里 (1,000 格)" +title = "翱翔之舞" +description = "飛行超過 1 公里 (1,000 格)" [advancement.challenge_fly_10k] - title = "馭風者" - description = "飛行超過 10 公里 (10,000 格)" +title = "馭風者" +description = "飛行超過 10 公里 (10,000 格)" [advancement.challenge_agility_sneak_500] - title = "輕步潛行" - description = "潛行超過 500 格" +title = "輕步潛行" +description = "潛行超過 500 格" [advancement.challenge_agility_sneak_5k] - title = "幽靈步伐" - description = "潛行超過 5 公里 (5,000 格)" +title = "幽靈步伐" +description = "潛行超過 5 公里 (5,000 格)" # Architect - New Achievement Chains [advancement.challenge_demolish_500] - title = "拆除小隊" - description = "破壞 500 個方塊" +title = "拆除小隊" +description = "破壞 500 個方塊" [advancement.challenge_demolish_5k] - title = "毀滅之球" - description = "破壞 5,000 個方塊" +title = "毀滅之球" +description = "破壞 5,000 個方塊" [advancement.challenge_value_placed_10k] - title = "價值建造者" - description = "放置價值 10,000 的方塊" +title = "價值建造者" +description = "放置價值 10,000 的方塊" [advancement.challenge_value_placed_100k] - title = "建築大師" - description = "放置價值 100,000 的方塊" +title = "建築大師" +description = "放置價值 100,000 的方塊" [advancement.challenge_demolish_val_5k] - title = "回收專家" - description = "從拆除中回收 5,000 方塊價值" +title = "回收專家" +description = "從拆除中回收 5,000 方塊價值" [advancement.challenge_demolish_val_50k] - title = "徹底解構" - description = "從拆除中回收 50,000 方塊價值" +title = "徹底解構" +description = "從拆除中回收 50,000 方塊價值" [advancement.challenge_high_build_100] - title = "高空建造者" - description = "在 Y=128 以上放置 100 個方塊" +title = "高空建造者" +description = "在 Y=128 以上放置 100 個方塊" [advancement.challenge_high_build_1k] - title = "雲端建築師" - description = "在 Y=128 以上放置 1,000 個方塊" +title = "雲端建築師" +description = "在 Y=128 以上放置 1,000 個方塊" # Axes - New Achievement Chains [advancement.challenge_axe_swing_500] - title = "揮斧者" - description = "揮舞斧頭 500 次" +title = "揮斧者" +description = "揮舞斧頭 500 次" [advancement.challenge_axe_swing_5k] - title = "狂戰士" - description = "揮舞斧頭 5,000 次" +title = "狂戰士" +description = "揮舞斧頭 5,000 次" [advancement.challenge_axe_damage_1k] - title = "劈裂者" - description = "用斧頭造成 1,000 傷害" +title = "劈裂者" +description = "用斧頭造成 1,000 傷害" [advancement.challenge_axe_damage_10k] - title = "劊子手之斧" - description = "用斧頭造成 10,000 傷害" +title = "劊子手之斧" +description = "用斧頭造成 10,000 傷害" [advancement.challenge_axe_value_5k] - title = "木材商人" - description = "砍伐價值 5,000 的木材" +title = "木材商人" +description = "砍伐價值 5,000 的木材" [advancement.challenge_axe_value_50k] - title = "伐木大亨" - description = "砍伐價值 50,000 的木材" +title = "伐木大亨" +description = "砍伐價值 50,000 的木材" [advancement.challenge_leaves_500] - title = "吹葉機" - description = "用斧頭清除 500 個樹葉方塊" +title = "吹葉機" +description = "用斧頭清除 500 個樹葉方塊" [advancement.challenge_leaves_5k] - title = "落葉清除者" - description = "用斧頭清除 5,000 個樹葉方塊" +title = "落葉清除者" +description = "用斧頭清除 5,000 個樹葉方塊" # Blocking - New Achievement Chains [advancement.challenge_block_dmg_1k] - title = "傷害吸收者" - description = "用盾牌格擋 1,000 傷害" +title = "傷害吸收者" +description = "用盾牌格擋 1,000 傷害" [advancement.challenge_block_dmg_10k] - title = "人肉盾牌" - description = "用盾牌格擋 10,000 傷害" +title = "人肉盾牌" +description = "用盾牌格擋 10,000 傷害" [advancement.challenge_block_proj_100] - title = "箭矢偏轉者" - description = "用盾牌格擋 100 個投射物" +title = "箭矢偏轉者" +description = "用盾牌格擋 100 個投射物" [advancement.challenge_block_proj_1k] - title = "投射物盾牌" - description = "用盾牌格擋 1,000 個投射物" +title = "投射物盾牌" +description = "用盾牌格擋 1,000 個投射物" [advancement.challenge_block_melee_500] - title = "格擋大師" - description = "用盾牌格擋 500 次近戰攻擊" +title = "格擋大師" +description = "用盾牌格擋 500 次近戰攻擊" [advancement.challenge_block_melee_5k] - title = "鐵壁堡壘" - description = "用盾牌格擋 5,000 次近戰攻擊" +title = "鐵壁堡壘" +description = "用盾牌格擋 5,000 次近戰攻擊" [advancement.challenge_block_heavy_50] - title = "坦克" - description = "格擋 50 次重擊 (超過 5 傷害)" +title = "坦克" +description = "格擋 50 次重擊 (超過 5 傷害)" [advancement.challenge_block_heavy_500] - title = "不動如山" - description = "格擋 500 次重擊 (超過 5 傷害)" +title = "不動如山" +description = "格擋 500 次重擊 (超過 5 傷害)" # Brewing - New Achievement Chains [advancement.challenge_brew_stands_10] - title = "釀造起步" - description = "放置 10 個釀造台" +title = "釀造起步" +description = "放置 10 個釀造台" [advancement.challenge_brew_stands_50] - title = "藥水工廠" - description = "放置 50 個釀造台" +title = "藥水工廠" +description = "放置 50 個釀造台" [advancement.challenge_brew_strong_25] - title = "烈性藥劑" - description = "飲用 25 瓶強化藥水" +title = "烈性藥劑" +description = "飲用 25 瓶強化藥水" [advancement.challenge_brew_strong_250] - title = "極致藥效" - description = "飲用 250 瓶強化藥水" +title = "極致藥效" +description = "飲用 250 瓶強化藥水" [advancement.challenge_brew_splash_hits_50] - title = "飛濺區域" - description = "用噴濺藥水命中 50 個實體" +title = "飛濺區域" +description = "用噴濺藥水命中 50 個實體" [advancement.challenge_brew_splash_hits_500] - title = "瘟疫醫生" - description = "用噴濺藥水命中 500 個實體" +title = "瘟疫醫生" +description = "用噴濺藥水命中 500 個實體" # Chronos - New Achievement Chains [advancement.challenge_active_dist_1k] - title = "永不停歇" - description = "活躍時行進 1 公里" +title = "永不停歇" +description = "活躍時行進 1 公里" [advancement.challenge_active_dist_10k] - title = "探路先鋒" - description = "活躍時行進 10 公里" +title = "探路先鋒" +description = "活躍時行進 10 公里" [advancement.challenge_active_dist_100k] - title = "永動之力" - description = "活躍時行進 100 公里" +title = "永動之力" +description = "活躍時行進 100 公里" [advancement.challenge_beds_10] - title = "早起鳥兒" - description = "在床上睡覺 10 次" +title = "早起鳥兒" +description = "在床上睡覺 10 次" [advancement.challenge_beds_100] - title = "時間跳躍者" - description = "在床上睡覺 100 次" +title = "時間跳躍者" +description = "在床上睡覺 100 次" [advancement.challenge_chronos_tp_50] - title = "時空轉移" - description = "傳送 50 次" +title = "時空轉移" +description = "傳送 50 次" [advancement.challenge_chronos_tp_500] - title = "時間扭曲" - description = "傳送 500 次" +title = "時間扭曲" +description = "傳送 500 次" [advancement.challenge_chronos_deaths_10] - title = "凡人" - description = "死亡 10 次" +title = "凡人" +description = "死亡 10 次" [advancement.challenge_chronos_deaths_100] - title = "死亡挑戰者" - description = "死亡 100 次" +title = "死亡挑戰者" +description = "死亡 100 次" # Crafting - New Achievement Chains [advancement.challenge_craft_value_10k] - title = "匠心獨運" - description = "製作總價值 10,000 的物品" +title = "匠心獨運" +description = "製作總價值 10,000 的物品" [advancement.challenge_craft_value_100k] - title = "工匠大師" - description = "製作總價值 100,000 的物品" +title = "工匠大師" +description = "製作總價值 100,000 的物品" [advancement.challenge_craft_tools_25] - title = "工具匠" - description = "製作 25 件工具" +title = "工具匠" +description = "製作 25 件工具" [advancement.challenge_craft_tools_250] - title = "鍛造大師" - description = "製作 250 件工具" +title = "鍛造大師" +description = "製作 250 件工具" [advancement.challenge_craft_armor_25] - title = "鎧甲匠" - description = "製作 25 件護甲" +title = "鎧甲匠" +description = "製作 25 件護甲" [advancement.challenge_craft_armor_250] - title = "護甲大師" - description = "製作 250 件護甲" +title = "護甲大師" +description = "製作 250 件護甲" [advancement.challenge_craft_mass_25k] - title = "批量生產" - description = "製作 25,000 個物品" +title = "批量生產" +description = "製作 25,000 個物品" [advancement.challenge_craft_mass_250k] - title = "工業革命" - description = "製作 250,000 個物品" +title = "工業革命" +description = "製作 250,000 個物品" # Discovery - New Achievement Chains [advancement.challenge_discover_items_50] - title = "收集者" - description = "發現 50 種獨特物品" +title = "收集者" +description = "發現 50 種獨特物品" [advancement.challenge_discover_items_250] - title = "編目者" - description = "發現 250 種獨特物品" +title = "編目者" +description = "發現 250 種獨特物品" [advancement.challenge_discover_blocks_50] - title = "勘測員" - description = "發現 50 種獨特方塊" +title = "勘測員" +description = "發現 50 種獨特方塊" [advancement.challenge_discover_blocks_250] - title = "地質學家" - description = "發現 250 種獨特方塊" +title = "地質學家" +description = "發現 250 種獨特方塊" [advancement.challenge_discover_mobs_25] - title = "觀察者" - description = "發現 25 種獨特生物" +title = "觀察者" +description = "發現 25 種獨特生物" [advancement.challenge_discover_mobs_75] - title = "博物學家" - description = "發現 75 種獨特生物" +title = "博物學家" +description = "發現 75 種獨特生物" [advancement.challenge_discover_biomes_10] - title = "漫遊者" - description = "發現 10 種獨特生態域" +title = "漫遊者" +description = "發現 10 種獨特生態域" [advancement.challenge_discover_biomes_40] - title = "環球旅行家" - description = "發現 40 種獨特生態域" +title = "環球旅行家" +description = "發現 40 種獨特生態域" [advancement.challenge_discover_foods_10] - title = "美食家" - description = "發現 10 種獨特食物" +title = "美食家" +description = "發現 10 種獨特食物" [advancement.challenge_discover_foods_30] - title = "烹飪大師" - description = "發現 30 種獨特食物" +title = "烹飪大師" +description = "發現 30 種獨特食物" # Enchanting - New Achievement Chains [advancement.challenge_enchant_power_100] - title = "織力者" - description = "累積 100 附魔之力" +title = "織力者" +description = "累積 100 附魔之力" [advancement.challenge_enchant_power_1k] - title = "奧術宗師" - description = "累積 1,000 附魔之力" +title = "奧術宗師" +description = "累積 1,000 附魔之力" [advancement.challenge_enchant_levels_1k] - title = "等級消耗者" - description = "在附魔上花費 1,000 經驗等級" +title = "等級消耗者" +description = "在附魔上花費 1,000 經驗等級" [advancement.challenge_enchant_levels_10k] - title = "經驗黑洞" - description = "在附魔上花費 10,000 經驗等級" +title = "經驗黑洞" +description = "在附魔上花費 10,000 經驗等級" [advancement.challenge_enchant_high_25] - title = "豪賭者" - description = "進行 25 次最高級附魔" +title = "豪賭者" +description = "進行 25 次最高級附魔" [advancement.challenge_enchant_high_250] - title = "傳奇附魔師" - description = "進行 250 次最高級附魔" +title = "傳奇附魔師" +description = "進行 250 次最高級附魔" [advancement.challenge_enchant_total_500] - title = "等級燃燒者" - description = "在所有附魔上累計花費 500 等級" +title = "等級燃燒者" +description = "在所有附魔上累計花費 500 等級" [advancement.challenge_enchant_total_5k] - title = "奧術投資" - description = "在所有附魔上累計花費 5,000 等級" +title = "奧術投資" +description = "在所有附魔上累計花費 5,000 等級" # Excavation - New Achievement Chains [advancement.challenge_dig_swing_500] - title = "挖掘者" - description = "揮舞鏟子 500 次" +title = "挖掘者" +description = "揮舞鏟子 500 次" [advancement.challenge_dig_swing_5k] - title = "挖掘機" - description = "揮舞鏟子 5,000 次" +title = "挖掘機" +description = "揮舞鏟子 5,000 次" [advancement.challenge_dig_damage_1k] - title = "鏟騎士" - description = "用鏟子造成 1,000 傷害" +title = "鏟騎士" +description = "用鏟子造成 1,000 傷害" [advancement.challenge_dig_damage_10k] - title = "鏟王" - description = "用鏟子造成 10,000 傷害" +title = "鏟王" +description = "用鏟子造成 10,000 傷害" [advancement.challenge_dig_value_5k] - title = "泥土商人" - description = "挖掘價值 5,000 的方塊" +title = "泥土商人" +description = "挖掘價值 5,000 的方塊" [advancement.challenge_dig_value_50k] - title = "大地領主" - description = "挖掘價值 50,000 的方塊" +title = "大地領主" +description = "挖掘價值 50,000 的方塊" [advancement.challenge_dig_gravel_500] - title = "礫石研磨者" - description = "挖掘 500 個礫石、沙子或黏土方塊" +title = "礫石研磨者" +description = "挖掘 500 個礫石、沙子或黏土方塊" [advancement.challenge_dig_gravel_5k] - title = "砂礫篩選者" - description = "挖掘 5,000 個礫石、沙子或黏土方塊" +title = "砂礫篩選者" +description = "挖掘 5,000 個礫石、沙子或黏土方塊" # Herbalism - New Achievement Chains [advancement.challenge_plant_100] - title = "播種者" - description = "種植 100 株作物" +title = "播種者" +description = "種植 100 株作物" [advancement.challenge_plant_1k] - title = "綠手指" - description = "種植 1,000 株作物" +title = "綠手指" +description = "種植 1,000 株作物" [advancement.challenge_plant_5k] - title = "農業大亨" - description = "種植 5,000 株作物" +title = "農業大亨" +description = "種植 5,000 株作物" [advancement.challenge_compost_50] - title = "回收利用者" - description = "堆肥 50 個物品" +title = "回收利用者" +description = "堆肥 50 個物品" [advancement.challenge_compost_500] - title = "土壤改良者" - description = "堆肥 500 個物品" +title = "土壤改良者" +description = "堆肥 500 個物品" [advancement.challenge_shear_50] - title = "剪毛者" - description = "剪切 50 個實體" +title = "剪毛者" +description = "剪切 50 個實體" [advancement.challenge_shear_250] - title = "牧群之主" - description = "剪切 250 個實體" +title = "牧群之主" +description = "剪切 250 個實體" # Hunter - New Achievement Chains [advancement.challenge_kills_500] - title = "屠戮者" - description = "擊殺 500 個生物" +title = "屠戮者" +description = "擊殺 500 個生物" [advancement.challenge_kills_5k] - title = "行刑者" - description = "擊殺 5,000 個生物" +title = "行刑者" +description = "擊殺 5,000 個生物" [advancement.challenge_boss_1] - title = "挑戰首領" - description = "擊殺一個首領怪物" +title = "挑戰首領" +description = "擊殺一個首領怪物" [advancement.challenge_boss_10] - title = "傳奇殺手" - description = "擊殺 10 個首領怪物" +title = "傳奇殺手" +description = "擊殺 10 個首領怪物" # Nether - New Achievement Chains [advancement.challenge_wither_dmg_500] - title = "凋零之痛" - description = "承受 500 凋零傷害" +title = "凋零之痛" +description = "承受 500 凋零傷害" [advancement.challenge_wither_dmg_5k] - title = "枯萎倖存者" - description = "承受 5,000 凋零傷害" +title = "枯萎倖存者" +description = "承受 5,000 凋零傷害" [advancement.challenge_wither_skel_25] - title = "骸骨收集者" - description = "擊殺 25 個凋靈骷髏" +title = "骸骨收集者" +description = "擊殺 25 個凋靈骷髏" [advancement.challenge_wither_skel_250] - title = "骷髏剋星" - description = "擊殺 250 個凋靈骷髏" +title = "骷髏剋星" +description = "擊殺 250 個凋靈骷髏" [advancement.challenge_wither_boss_1] - title = "凋靈殺手" - description = "擊敗凋靈" +title = "凋靈殺手" +description = "擊敗凋靈" [advancement.challenge_wither_boss_10] - title = "地獄主宰" - description = "擊敗凋靈 10 次" +title = "地獄主宰" +description = "擊敗凋靈 10 次" [advancement.challenge_roses_10] - title = "死亡園丁" - description = "破壞 10 朵凋零玫瑰" +title = "死亡園丁" +description = "破壞 10 朵凋零玫瑰" [advancement.challenge_roses_100] - title = "枯萎收集者" - description = "破壞 100 朵凋零玫瑰" +title = "枯萎收集者" +description = "破壞 100 朵凋零玫瑰" # Pickaxes - New Achievement Chains [advancement.challenge_pick_swing_500] - title = "礦工之臂" - description = "揮舞鎬子 500 次" +title = "礦工之臂" +description = "揮舞鎬子 500 次" [advancement.challenge_pick_swing_5k] - title = "隧道挖掘者" - description = "揮舞鎬子 5,000 次" +title = "隧道挖掘者" +description = "揮舞鎬子 5,000 次" [advancement.challenge_pick_damage_1k] - title = "鎬戰者" - description = "用鎬子造成 1,000 傷害" +title = "鎬戰者" +description = "用鎬子造成 1,000 傷害" [advancement.challenge_pick_damage_10k] - title = "戰鎬" - description = "用鎬子造成 10,000 傷害" +title = "戰鎬" +description = "用鎬子造成 10,000 傷害" [advancement.challenge_pick_value_5k] - title = "寶石探索者" - description = "開採價值 5,000 的方塊" +title = "寶石探索者" +description = "開採價值 5,000 的方塊" [advancement.challenge_pick_value_50k] - title = "礦石大亨" - description = "開採價值 50,000 的方塊" +title = "礦石大亨" +description = "開採價值 50,000 的方塊" [advancement.challenge_pick_ores_500] - title = "勘探者" - description = "開採 500 個礦石方塊" +title = "勘探者" +description = "開採 500 個礦石方塊" [advancement.challenge_pick_ores_5k] - title = "採礦大師" - description = "開採 5,000 個礦石方塊" +title = "採礦大師" +description = "開採 5,000 個礦石方塊" # Ranged - New Achievement Chains [advancement.challenge_ranged_dmg_1k] - title = "神射手" - description = "造成 1,000 遠程傷害" +title = "神射手" +description = "造成 1,000 遠程傷害" [advancement.challenge_ranged_dmg_10k] - title = "致命弓手" - description = "造成 10,000 遠程傷害" +title = "致命弓手" +description = "造成 10,000 遠程傷害" [advancement.challenge_ranged_dist_5k] - title = "遠距射擊" - description = "發射投射物總飛行距離達 5,000 格" +title = "遠距射擊" +description = "發射投射物總飛行距離達 5,000 格" [advancement.challenge_ranged_dist_50k] - title = "千里射手" - description = "發射投射物總飛行距離達 50,000 格" +title = "千里射手" +description = "發射投射物總飛行距離達 50,000 格" [advancement.challenge_ranged_kills_50] - title = "弓箭手" - description = "用遠程武器擊殺 50 個生物" +title = "弓箭手" +description = "用遠程武器擊殺 50 個生物" [advancement.challenge_ranged_kills_500] - title = "射術宗師" - description = "用遠程武器擊殺 500 個生物" +title = "射術宗師" +description = "用遠程武器擊殺 500 個生物" [advancement.challenge_longshot_25] - title = "狙擊手" - description = "命中 25 次遠距射擊 (超過 30 格)" +title = "狙擊手" +description = "命中 25 次遠距射擊 (超過 30 格)" [advancement.challenge_longshot_250] - title = "鷹眼" - description = "命中 250 次遠距射擊 (超過 30 格)" +title = "鷹眼" +description = "命中 250 次遠距射擊 (超過 30 格)" # Rift - New Achievement Chains [advancement.challenge_rift_pearls_50] - title = "珍珠投擲者" - description = "投擲 50 顆乃影珍珠" +title = "珍珠投擲者" +description = "投擲 50 顆乃影珍珠" [advancement.challenge_rift_pearls_500] - title = "傳送狂人" - description = "投擲 500 顆乃影珍珠" +title = "傳送狂人" +description = "投擲 500 顆乃影珍珠" [advancement.challenge_rift_enderman_50] - title = "乃影人獵手" - description = "擊殺 50 個乃影人" +title = "乃影人獵手" +description = "擊殺 50 個乃影人" [advancement.challenge_rift_enderman_500] - title = "虛空追獵者" - description = "擊殺 500 個乃影人" +title = "虛空追獵者" +description = "擊殺 500 個乃影人" [advancement.challenge_rift_dragon_500] - title = "屠龍戰士" - description = "對乃影龍造成 500 傷害" +title = "屠龍戰士" +description = "對乃影龍造成 500 傷害" [advancement.challenge_rift_dragon_5k] - title = "屠龍者" - description = "對乃影龍造成 5,000 傷害" +title = "屠龍者" +description = "對乃影龍造成 5,000 傷害" [advancement.challenge_rift_crystal_10] - title = "水晶破壞者" - description = "摧毀 10 個乃地水晶" +title = "水晶破壞者" +description = "摧毀 10 個乃地水晶" [advancement.challenge_rift_crystal_100] - title = "終界毀滅者" - description = "摧毀 100 個乃地水晶" +title = "終界毀滅者" +description = "摧毀 100 個乃地水晶" # Seaborne - New Achievement Chains [advancement.challenge_fish_25] - title = "垂釣者" - description = "捕獲 25 條魚" +title = "垂釣者" +description = "捕獲 25 條魚" [advancement.challenge_fish_250] - title = "釣魚大師" - description = "捕獲 250 條魚" +title = "釣魚大師" +description = "捕獲 250 條魚" [advancement.challenge_drowned_25] - title = "溺屍獵人" - description = "擊殺 25 個溺屍" +title = "溺屍獵人" +description = "擊殺 25 個溺屍" [advancement.challenge_drowned_250] - title = "海洋淨化者" - description = "擊殺 250 個溺屍" +title = "海洋淨化者" +description = "擊殺 250 個溺屍" [advancement.challenge_guardian_10] - title = "守衛者獵手" - description = "擊殺 10 個守衛者" +title = "守衛者獵手" +description = "擊殺 10 個守衛者" [advancement.challenge_guardian_100] - title = "神殿掠奪者" - description = "擊殺 100 個守衛者" +title = "神殿掠奪者" +description = "擊殺 100 個守衛者" [advancement.challenge_underwater_blocks_100] - title = "水下礦工" - description = "在水下破壞 100 個方塊" +title = "水下礦工" +description = "在水下破壞 100 個方塊" [advancement.challenge_underwater_blocks_1k] - title = "水下工程師" - description = "在水下破壞 1,000 個方塊" +title = "水下工程師" +description = "在水下破壞 1,000 個方塊" # Stealth - New Achievement Chains [advancement.challenge_stealth_dmg_500] - title = "背刺者" - description = "潛行時造成 500 傷害" +title = "背刺者" +description = "潛行時造成 500 傷害" [advancement.challenge_stealth_dmg_5k] - title = "無聲殺手" - description = "潛行時造成 5,000 傷害" +title = "無聲殺手" +description = "潛行時造成 5,000 傷害" [advancement.challenge_stealth_kills_10] - title = "刺客" - description = "潛行時擊殺 10 個生物" +title = "刺客" +description = "潛行時擊殺 10 個生物" [advancement.challenge_stealth_kills_100] - title = "暗影死神" - description = "潛行時擊殺 100 個生物" +title = "暗影死神" +description = "潛行時擊殺 100 個生物" [advancement.challenge_stealth_time_1h] - title = "耐心者" - description = "潛行 1 小時 (3,600 秒)" +title = "耐心者" +description = "潛行 1 小時 (3,600 秒)" [advancement.challenge_stealth_time_10h] - title = "暗影之主" - description = "潛行 10 小時 (36,000 秒)" +title = "暗影之主" +description = "潛行 10 小時 (36,000 秒)" [advancement.challenge_stealth_arrows_50] - title = "暗箭手" - description = "潛行時射出 50 支箭" +title = "暗箭手" +description = "潛行時射出 50 支箭" [advancement.challenge_stealth_arrows_500] - title = "幽靈弓手" - description = "潛行時射出 500 支箭" +title = "幽靈弓手" +description = "潛行時射出 500 支箭" # Swords - New Achievement Chains [advancement.challenge_sword_dmg_1k] - title = "劍術學徒" - description = "用劍造成 1,000 傷害" +title = "劍術學徒" +description = "用劍造成 1,000 傷害" [advancement.challenge_sword_dmg_10k] - title = "劍客" - description = "用劍造成 10,000 傷害" +title = "劍客" +description = "用劍造成 10,000 傷害" [advancement.challenge_sword_kills_50] - title = "決鬥者" - description = "用劍擊殺 50 個生物" +title = "決鬥者" +description = "用劍擊殺 50 個生物" [advancement.challenge_sword_kills_500] - title = "角鬥士" - description = "用劍擊殺 500 個生物" +title = "角鬥士" +description = "用劍擊殺 500 個生物" [advancement.challenge_sword_crit_50] - title = "暴擊者" - description = "用劍造成 50 次暴擊" +title = "暴擊者" +description = "用劍造成 50 次暴擊" [advancement.challenge_sword_crit_500] - title = "精準大師" - description = "用劍造成 500 次暴擊" +title = "精準大師" +description = "用劍造成 500 次暴擊" [advancement.challenge_sword_heavy_25] - title = "重擊者" - description = "用劍造成 25 次重擊 (超過 8 傷害)" +title = "重擊者" +description = "用劍造成 25 次重擊 (超過 8 傷害)" [advancement.challenge_sword_heavy_250] - title = "毀滅之擊" - description = "用劍造成 250 次重擊 (超過 8 傷害)" +title = "毀滅之擊" +description = "用劍造成 250 次重擊 (超過 8 傷害)" # Taming - New Achievement Chains [advancement.challenge_pet_dmg_500] - title = "馴獸師" - description = "你的寵物共造成 500 傷害" +title = "馴獸師" +description = "你的寵物共造成 500 傷害" [advancement.challenge_pet_dmg_5k] - title = "戰爭統帥" - description = "你的寵物共造成 5,000 傷害" +title = "戰爭統帥" +description = "你的寵物共造成 5,000 傷害" [advancement.challenge_tamed_10] - title = "動物之友" - description = "馴服 10 隻動物" +title = "動物之友" +description = "馴服 10 隻動物" [advancement.challenge_tamed_100] - title = "動物園長" - description = "馴服 100 隻動物" +title = "動物園長" +description = "馴服 100 隻動物" [advancement.challenge_pet_kills_25] - title = "群攻戰術" - description = "你的寵物擊殺 25 個生物" +title = "群攻戰術" +description = "你的寵物擊殺 25 個生物" [advancement.challenge_pet_kills_250] - title = "首領指揮官" - description = "你的寵物擊殺 250 個生物" +title = "首領指揮官" +description = "你的寵物擊殺 250 個生物" [advancement.challenge_taming_2500] - title = "繁殖專家" - description = "繁殖 2,500 隻動物" +title = "繁殖專家" +description = "繁殖 2,500 隻動物" [advancement.challenge_taming_25k] - title = "基因大師" - description = "繁殖 25,000 隻動物" +title = "基因大師" +description = "繁殖 25,000 隻動物" # TragOul - New Achievement Chains [advancement.challenge_trag_hits_500] - title = "沙包" - description = "受到 500 次攻擊" +title = "沙包" +description = "受到 500 次攻擊" [advancement.challenge_trag_hits_5k] - title = "受虐狂" - description = "受到 5,000 次攻擊" +title = "受虐狂" +description = "受到 5,000 次攻擊" [advancement.challenge_trag_deaths_10] - title = "九條命" - description = "死亡 10 次" +title = "九條命" +description = "死亡 10 次" [advancement.challenge_trag_deaths_100] - title = "反覆噩夢" - description = "死亡 100 次" +title = "反覆噩夢" +description = "死亡 100 次" [advancement.challenge_trag_fire_500] - title = "火焰受害者" - description = "承受 500 火焰傷害" +title = "火焰受害者" +description = "承受 500 火焰傷害" [advancement.challenge_trag_fire_5k] - title = "浴火鳳凰" - description = "承受 5,000 火焰傷害" +title = "浴火鳳凰" +description = "承受 5,000 火焰傷害" [advancement.challenge_trag_fall_500] - title = "重力測試" - description = "承受 500 墜落傷害" +title = "重力測試" +description = "承受 500 墜落傷害" [advancement.challenge_trag_fall_5k] - title = "極限速度" - description = "承受 5,000 墜落傷害" +title = "極限速度" +description = "承受 5,000 墜落傷害" # Unarmed - New Achievement Chains [advancement.challenge_unarmed_dmg_1k] - title = "拳擊手" - description = "用空手造成 1,000 傷害" +title = "拳擊手" +description = "用空手造成 1,000 傷害" [advancement.challenge_unarmed_dmg_10k] - title = "武術家" - description = "用空手造成 10,000 傷害" +title = "武術家" +description = "用空手造成 10,000 傷害" [advancement.challenge_unarmed_kills_25] - title = "赤手空拳" - description = "用空手擊殺 25 個生物" +title = "赤手空拳" +description = "用空手擊殺 25 個生物" [advancement.challenge_unarmed_kills_250] - title = "傳奇之拳" - description = "用空手擊殺 250 個生物" +title = "傳奇之拳" +description = "用空手擊殺 250 個生物" [advancement.challenge_unarmed_crit_25] - title = "致命一拳" - description = "用空手造成 25 次暴擊" +title = "致命一拳" +description = "用空手造成 25 次暴擊" [advancement.challenge_unarmed_crit_250] - title = "精準之拳" - description = "用空手造成 250 次暴擊" +title = "精準之拳" +description = "用空手造成 250 次暴擊" [advancement.challenge_unarmed_heavy_25] - title = "力量之拳" - description = "用空手造成 25 次重擊 (超過 6 傷害)" +title = "力量之拳" +description = "用空手造成 25 次重擊 (超過 6 傷害)" [advancement.challenge_unarmed_heavy_250] - title = "拳王" - description = "用空手造成 250 次重擊 (超過 6 傷害)" +title = "拳王" +description = "用空手造成 250 次重擊 (超過 6 傷害)" # items [items] [items.bound_ender_peral] - name = "亞空間之鑰" - usage1 = "Shift + 左鍵點擊以綁定" - usage2 = "右鍵點擊以存取已綁定的容器" +name = "亞空間之鑰" +usage1 = "Shift + 左鍵點擊以綁定" +usage2 = "右鍵點擊以存取已綁定的容器" [items.bound_eye_of_ender] - name = "視界錨點" - usage1 = "右鍵點擊以消耗並傳送到綁定的位置" - usage2 = "Shift + 左鍵點擊以綁定方塊" +name = "視界錨點" +usage1 = "右鍵點擊以消耗並傳送到綁定的位置" +usage2 = "Shift + 左鍵點擊以綁定方塊" [items.bound_redstone_torch] - name = "紅石遙控器" - usage1 = "右鍵點擊以發送 1 刻紅石脈衝" - usage2 = "對「標靶」方塊 Shift + 左鍵點擊以綁定" +name = "紅石遙控器" +usage1 = "右鍵點擊以發送 1 刻紅石脈衝" +usage2 = "對「標靶」方塊 Shift + 左鍵點擊以綁定" [items.bound_snowball] - name = "蛛網陷阱!" - usage1 = "拋出以在該位置創建臨時蛛網陷阱" +name = "蛛網陷阱!" +usage1 = "拋出以在該位置創建臨時蛛網陷阱" [items.chrono_time_bottle] - name = "時光之瓶" - usage1 = "放在物品欄中時會被動儲存時間" - usage2 = "右鍵點擊計時方塊或幼年動物以消耗儲存的時間" - stored = "已儲存時間" +name = "時光之瓶" +usage1 = "放在物品欄中時會被動儲存時間" +usage2 = "右鍵點擊計時方塊或幼年動物以消耗儲存的時間" +stored = "已儲存時間" [items.chrono_time_bomb] - name = "時間炸彈" - usage1 = "右鍵點擊以發射時空彈,產生時間力場" +name = "時間炸彈" +usage1 = "右鍵點擊以發射時空彈,產生時間力場" [items.elevator_block] - name = "電梯方塊" - usage1 = "跳躍以向上傳送" - usage2 = "潛行以向下傳送" - usage3 = "電梯之間至少需要 2 格空氣方塊" +name = "電梯方塊" +usage1 = "跳躍以向上傳送" +usage2 = "潛行以向下傳送" +usage3 = "電梯之間至少需要 2 格空氣方塊" # snippets [snippets] [snippets.gui] - level = "等級" - knowledge = "知識" - power_used = "已使用能力" - not_learned = "尚未學習" - xp = "經驗至" - welcome = "歡迎!" - welcome_back = "歡迎回來!" - xp_bonus_for_time = "經驗增益,持續" - max_ability_power = "最大技能能力" - unlock_this_by_clicking = "右鍵點擊以解鎖:" - back = "返回" - unlearn_all = "遺忘全部技能" - unlearned_all = "已全部遺忘" +level = "等級" +knowledge = "知識" +power_used = "已使用能力" +not_learned = "尚未學習" +xp = "經驗至" +welcome = "歡迎!" +welcome_back = "歡迎回來!" +xp_bonus_for_time = "經驗增益,持續" +max_ability_power = "最大技能能力" +unlock_this_by_clicking = "右鍵點擊以解鎖:" +back = "返回" +unlearn_all = "遺忘全部技能" +unlearned_all = "已全部遺忘" [snippets.adapt_menu] - may_not_unlearn = "你無法遺忘此技能" - may_unlearn = "可學習/遺忘" - knowledge_cost = "知識花費" - knowledge_available = "可用知識" - already_learned = "已學習" - unlearn_refund = "點擊以遺忘並退還" - no_refunds = "極限模式,已禁用退還" - knowledge = "知識" - click_learn = "點擊以學習" - no_knowledge = "(你沒有任何知識)" - you_only_have = "你只有" - how_to_level_up = "升級技能以提升你的最大能力。" - not_enough_power = "能力不足!每級技能需要花費 1 點能力。" - power = "能力" - power_drain = "能力消耗" - learned = "已學習 " - unlearned = "已遺忘 " - activator_block = "書架" +may_not_unlearn = "你無法遺忘此技能" +may_unlearn = "可學習/遺忘" +knowledge_cost = "知識花費" +knowledge_available = "可用知識" +already_learned = "已學習" +unlearn_refund = "點擊以遺忘並退還" +no_refunds = "極限模式,已禁用退還" +knowledge = "知識" +click_learn = "點擊以學習" +no_knowledge = "(你沒有任何知識)" +you_only_have = "你只有" +how_to_level_up = "升級技能以提升你的最大能力。" +not_enough_power = "能力不足!每級技能需要花費 1 點能力。" +power = "能力" +power_drain = "能力消耗" +learned = "已學習 " +unlearned = "已遺忘 " +activator_block = "書架" [snippets.knowledge_orb] - contains = "包含" - knowledge = "知識" - rightclick = "右鍵點擊" - togainknowledge = "以獲得此知識" - knowledge_orb = "知識之珠" +contains = "包含" +knowledge = "知識" +rightclick = "右鍵點擊" +togainknowledge = "以獲得此知識" +knowledge_orb = "知識之珠" [snippets.experience_orb] - contains = "包含" - xp = "經驗" - rightclick = "右鍵點擊" - togainxp = "以獲得此經驗" - xporb = "經驗球" +contains = "包含" +xp = "經驗" +rightclick = "右鍵點擊" +togainxp = "以獲得此經驗" +xporb = "經驗球" # skill [skill] [skill.agility] - name = "敏捷" - icon = "⇉" - description = "敏捷是面對障礙時快速且流暢移動的能力。" +name = "敏捷" +icon = "⇉" +description = "敏捷是面對障礙時快速且流暢移動的能力。" [skill.architect] - name = "建築師" - icon = "⬧" - description = "結構是世界的基石。現實掌握在你手中,由你掌控。" +name = "建築師" +icon = "⬧" +description = "結構是世界的基石。現實掌握在你手中,由你掌控。" [skill.axes] - name = "斧技" - icon = "🪓" - description1 = "與其砍樹,不如砍" - description2 = "東西" - description3 = ",結果都一樣嘛!" +name = "斧技" +icon = "🪓" +description1 = "與其砍樹,不如砍" +description2 = "東西" +description3 = ",結果都一樣嘛!" [skill.brewing] - name = "釀造" - icon = "❦" - description = "咕嘟咕嘟,再咕嘟——我還是沒辦法把這瓶藥水倒進煉藥鍋裡" +name = "釀造" +icon = "❦" +description = "咕嘟咕嘟,再咕嘟——我還是沒辦法把這瓶藥水倒進煉藥鍋裡" [skill.blocking] - name = "格擋" - icon = "🛡" - description = "棍棒和石頭不會折斷你的骨頭,但是盾牌會。" +name = "格擋" +icon = "🛡" +description = "棍棒和石頭不會折斷你的骨頭,但是盾牌會。" [skill.crafting] - name = "合成" - icon = "⌂" - description = "當沒有東西可以放了,何不再做一個呢?" +name = "合成" +icon = "⌂" +description = "當沒有東西可以放了,何不再做一個呢?" [skill.discovery] - name = "探索" - icon = "⚛" - description = "隨著感知力的擴展,你的心智逐漸解開,發現那些你未曾察覺的事物。" +name = "探索" +icon = "⚛" +description = "隨著感知力的擴展,你的心智逐漸解開,發現那些你未曾察覺的事物。" [skill.enchanting] - name = "附魔" - icon = "♰" - description = "你在說什麼呢?預言、幻象,還是迷信的胡言亂語?" +name = "附魔" +icon = "♰" +description = "你在說什麼呢?預言、幻象,還是迷信的胡言亂語?" [skill.excavation] - name = "挖掘" - icon = "ᛳ" - description = "挖呀挖呀挖......" +name = "挖掘" +icon = "ᛳ" +description = "挖呀挖呀挖......" [skill.herbalism] - name = "草藥學" - icon = "⚘" - description = "我找不到任何植物,但我能找到一些種子還有——那是……雜草?" +name = "草藥學" +icon = "⚘" +description = "我找不到任何植物,但我能找到一些種子還有——那是……雜草?" [skill.hunter] - name = "狩獵" - icon = "☠" - description = "狩獵重在過程,而非結果。" +name = "狩獵" +icon = "☠" +description = "狩獵重在過程,而非結果。" [skill.nether] - name = "地獄" - icon = "₪" - description = "來自地獄的最深處。" +name = "地獄" +icon = "₪" +description = "來自地獄的最深處。" [skill.pickaxe] - name = "鎬技" - icon = "⛏" - description = "矮人才是礦工,但我也學到了一些東西。我是瑞典人!" +name = "鎬技" +icon = "⛏" +description = "矮人才是礦工,但我也學到了一些東西。我是瑞典人!" [skill.ranged] - name = "箭術" - icon = "🏹" - description = "距離是勝利的關鍵,也是生存的關鍵。" +name = "箭術" +icon = "🏹" +description = "距離是勝利的關鍵,也是生存的關鍵。" [skill.rift] - name = "裂痕" - icon = "❍" - description = "裂痕是一道灼熱的束縛,但你已駕馭了它。" +name = "裂痕" +icon = "❍" +description = "裂痕是一道灼熱的束縛,但你已駕馭了它。" [skill.seaborne] - name = "航海" - icon = "🎣" - description = "擁有此技能,你將能駕馭水中的奇跡。" +name = "航海" +icon = "🎣" +description = "擁有此技能,你將能駕馭水中的奇跡。" [skill.stealth] - name = "隱匿" - icon = "☯" - description = "隱於無形的藝術。行走於陰影之中。" +name = "隱匿" +icon = "☯" +description = "隱於無形的藝術。行走於陰影之中。" [skill.swords] - name = "劍術" - icon = "⚔" - description = "以灰岩石之力!" +name = "劍術" +icon = "⚔" +description = "以灰岩石之力!" [skill.taming] - name = "馴獸" - icon = "♥" - description = "鸚鵡和蜜蜂……還有你?" +name = "馴獸" +icon = "♥" +description = "鸚鵡和蜜蜂……還有你?" [skill.tragoul] - name = "血咒" - icon = "🗡" - description = "血液在宇宙的血管中流淌,受你雙手所束縛。" +name = "血咒" +icon = "🗡" +description = "血液在宇宙的血管中流淌,受你雙手所束縛。" [skill.chronos] - name = "時空" - icon = "🕒" - description = "為宇宙之鐘上發條,感受時間的流動。打破時鐘,化身其中。" +name = "時空" +icon = "🕒" +description = "為宇宙之鐘上發條,感受時間的流動。打破時鐘,化身其中。" [skill.unarmed] - name = "搏擊" - icon = "»" - description = "沒有武器並不意味著沒有力量。" +name = "搏擊" +icon = "»" +description = "沒有武器並不意味著沒有力量。" # agility [agility] [agility.armor_up] - name = "裝甲強化" - description = "衝刺時間越長,獲得的護甲值越多!" - lore1 = "最大護甲值" - lore2 = "護甲強化所需時間" - lore = ["最大護甲值", "護甲強化所需時間"] +name = "裝甲強化" +description = "衝刺時間越長,獲得的護甲值越多!" +lore1 = "最大護甲值" +lore2 = "護甲強化所需時間" +lore = ["最大護甲值", "護甲強化所需時間"] [agility.ladder_slide] - name = "梯子滑行" - description = "大幅提升攀爬和滑下梯子的速度。" - lore1 = "梯子速度倍率" - lore2 = "快速下滑速度" - lore = ["梯子速度倍率", "快速下滑速度"] +name = "梯子滑行" +description = "大幅提升攀爬和滑下梯子的速度。" +lore1 = "梯子速度倍率" +lore2 = "快速下滑速度" +lore = ["梯子速度倍率", "快速下滑速度"] [agility.super_jump] - name = "超級跳躍" - description = "卓越的高度優勢。" - lore1 = "最大跳躍高度" - lore2 = "潛行 + 跳躍以超級跳躍!" - lore = ["最大跳躍高度", "潛行 + 跳躍以超級跳躍!"] +name = "超級跳躍" +description = "卓越的高度優勢。" +lore1 = "最大跳躍高度" +lore2 = "潛行 + 跳躍以超級跳躍!" +lore = ["最大跳躍高度", "潛行 + 跳躍以超級跳躍!"] [agility.wall_jump] - name = "蹬牆跳" - description = "在半空中緊貼牆壁時按住潛行鍵以攀附並跳躍!" - lore1 = "最大跳躍次數" - lore2 = "跳躍高度" - lore = ["最大跳躍次數", "跳躍高度"] +name = "蹬牆跳" +description = "在半空中緊貼牆壁時按住潛行鍵以攀附並跳躍!" +lore1 = "最大跳躍次數" +lore2 = "跳躍高度" +lore = ["最大跳躍次數", "跳躍高度"] [agility.wind_up] - name = "蓄力加速" - description = "衝刺時間越長,速度越快!" - lore1 = "最大速度" - lore2 = "蓄力時間" - lore = ["最大速度", "蓄力時間"] +name = "蓄力加速" +description = "衝刺時間越長,速度越快!" +lore1 = "最大速度" +lore2 = "蓄力時間" +lore = ["最大速度", "蓄力時間"] # architect [architect] [architect.elevator] - name = "電梯" - description = "允許你建造電梯以快速垂直傳送!" - lore1 = "解鎖電梯配方:X=羊毛,Y=乖珍珠" - lore2 = "XXX" - lore3 = "XYX" - lore4 = "XXX" - lore = ["解鎖電梯配方:X=羊毛,Y=乖珍珠", "XXX", "XYX", "XXX"] +name = "電梯" +description = "允許你建造電梯以快速垂直傳送!" +lore1 = "解鎖電梯配方:X=羊毛,Y=乖珍珠" +lore2 = "XXX" +lore3 = "XYX" +lore4 = "XXX" +lore = ["解鎖電梯配方:X=羊毛,Y=乖珍珠", "XXX", "XYX", "XXX"] [architect.foundation] - name = "魔法地基" - description = "潛行時可以在腳下臨時放置方塊!" - lore1 = "魔法生成:" - lore2 = "格方塊在你腳下!" - lore = ["魔法生成:", "格方塊在你腳下!"] +name = "魔法地基" +description = "潛行時可以在腳下臨時放置方塊!" +lore1 = "魔法生成:" +lore2 = "格方塊在你腳下!" +lore = ["魔法生成:", "格方塊在你腳下!"] [architect.glass] - name = "絲綢觸感玻璃" - description = "空手打破玻璃方塊時不會損失玻璃!" - lore1 = "你的雙手對玻璃擁有絲綢觸感" - lore = ["你的雙手對玻璃擁有絲綢觸感"] +name = "絲綢觸感玻璃" +description = "空手打破玻璃方塊時不會損失玻璃!" +lore1 = "你的雙手對玻璃擁有絲綢觸感" +lore = ["你的雙手對玻璃擁有絲綢觸感"] [architect.wireless_redstone] - name = "紅石遙控器" - description = "使用紅石火把遠程遙控紅石!" - lore1 = "標靶方塊 + 紅石火把 + 乖珍珠 = 1 個紅石遙控器" - lore = ["標靶方塊 + 紅石火把 + 乖珍珠 = 1 個紅石遙控器"] +name = "紅石遙控器" +description = "使用紅石火把遠程遙控紅石!" +lore1 = "標靶方塊 + 紅石火把 + 乖珍珠 = 1 個紅石遙控器" +lore = ["標靶方塊 + 紅石火把 + 乖珍珠 = 1 個紅石遙控器"] [architect.placement] - name = "建築師之杖" - description = "允許你一次放置多格方塊!啟動方式為潛行,手持與你注視的方塊相同的方塊並放置!請注意,你可能需要稍微移動以觸發邊界框!" - lore1 = "你需要" - lore2 = "格手中方塊才能放置" - lore3 = "材料建築師之杖" - lore = ["你需要", "格手中方塊才能放置", "材料建築師之杖"] +name = "建築師之杖" +description = "允許你一次放置多格方塊!啟動方式為潛行,手持與你注視的方塊相同的方塊並放置!請注意,你可能需要稍微移動以觸發邊界框!" +lore1 = "你需要" +lore2 = "格手中方塊才能放置" +lore3 = "材料建築師之杖" +lore = ["你需要", "格手中方塊才能放置", "材料建築師之杖"] # axe [axe] [axe.chop] - name = "斧頭砍伐" - description = "右鍵點擊原木底部以砍倒整棵樹!" - lore1 = "每次砍伐數量" - lore2 = "砍伐冷卻" - lore3 = "工具磨損" - lore = ["每次砍伐數量", "砍伐冷卻", "工具磨損"] +name = "斧頭砍伐" +description = "右鍵點擊原木底部以砍倒整棵樹!" +lore1 = "每次砍伐數量" +lore2 = "砍伐冷卻" +lore3 = "工具磨損" +lore = ["每次砍伐數量", "砍伐冷卻", "工具磨損"] [axe.log_swap] - name = "露西的原木轉換器" - description = "在工作台中更換原木種類!" - lore1 = "8 根任意種類原木 + 1 株樹苗 = 8 根樹苗對應種類的原木" - lore = ["8 根任意種類原木 + 1 株樹苗 = 8 根樹苗對應種類的原木"] +name = "露西的原木轉換器" +description = "在工作台中更換原木種類!" +lore1 = "8 根任意種類原木 + 1 株樹苗 = 8 根樹苗對應種類的原木" +lore = ["8 根任意種類原木 + 1 株樹苗 = 8 根樹苗對應種類的原木"] [axe.drop_to_inventory] - name = "斧頭掉落物自動入包" +name = "斧頭掉落物自動入包" [axe.ground_smash] - name = "山崩地裂" - description = "跳起後蹲下以猛擊附近所有敵人。" - lore1 = "傷害" - lore2 = "方塊半徑" - lore3 = "力量" - lore4 = "猛擊冷卻" - lore = ["傷害", "方塊半徑", "力量", "猛擊冷卻"] +name = "山崩地裂" +description = "跳起後蹲下以猛擊附近所有敵人。" +lore1 = "傷害" +lore2 = "方塊半徑" +lore3 = "力量" +lore4 = "猛擊冷卻" +lore = ["傷害", "方塊半徑", "力量", "猛擊冷卻"] [axe.leaf_miner] - name = "樹葉粉碎機" - description = "允許你一次破壞大量樹葉!" - lore1 = "潛行並挖掘樹葉" - lore2 = "樹葉挖掘範圍" - lore3 = "你不會獲得樹葉的掉落物(防止濫用)" - lore = ["潛行並挖掘樹葉", "樹葉挖掘範圍", "你不會獲得樹葉的掉落物(防止濫用)"] +name = "樹葉粉碎機" +description = "允許你一次破壞大量樹葉!" +lore1 = "潛行並挖掘樹葉" +lore2 = "樹葉挖掘範圍" +lore3 = "你不會獲得樹葉的掉落物(防止濫用)" +lore = ["潛行並挖掘樹葉", "樹葉挖掘範圍", "你不會獲得樹葉的掉落物(防止濫用)"] [axe.wood_miner] - name = "木材礦工" - description = "允許你一次砍伐大量木材!" - lore1 = "潛行並砍伐木頭/原木(木板無效)" - lore2 = "木材挖掘範圍" - lore3 = "與掉落物自動入包相容" - lore = ["潛行並砍伐木頭/原木(木板無效)", "木材挖掘範圍", "與掉落物自動入包相容"] +name = "木材礦工" +description = "允許你一次砍伐大量木材!" +lore1 = "潛行並砍伐木頭/原木(木板無效)" +lore2 = "木材挖掘範圍" +lore3 = "與掉落物自動入包相容" +lore = ["潛行並砍伐木頭/原木(木板無效)", "木材挖掘範圍", "與掉落物自動入包相容"] # brewing [brewing] [brewing.lingering] - name = "持久藥效" - description = "釀造的藥水效果持續更久!" - lore1 = "持續時間" - lore2 = "持續時間" - lore = ["持續時間", "持續時間"] +name = "持久藥效" +description = "釀造的藥水效果持續更久!" +lore1 = "持續時間" +lore2 = "持續時間" +lore = ["持續時間", "持續時間"] [brewing.super_heated] - name = "超級加熱釀造" - description = "釀造台越熱,工作速度越快。" - lore1 = "每接觸火焰方塊" - lore2 = "每接觸岩漿方塊" - lore = ["每接觸火焰方塊", "每接觸岩漿方塊"] +name = "超級加熱釀造" +description = "釀造台越熱,工作速度越快。" +lore1 = "每接觸火焰方塊" +lore2 = "每接觸岩漿方塊" +lore = ["每接觸火焰方塊", "每接觸岩漿方塊"] [brewing.darkness] - name = "瓶裝黑暗" - description = "不知道你為什麼需要這個,但給你吧!" - lore1 = "夜視藥水 + 黑色混凝土 = 黑暗藥水(30 秒)" - lore2 = "注意:效果期間無法疾跑!" - lore = ["夜視藥水 + 黑色混凝土 = 黑暗藥水(30 秒)", "注意:效果期間無法疾跑!"] +name = "瓶裝黑暗" +description = "不知道你為什麼需要這個,但給你吧!" +lore1 = "夜視藥水 + 黑色混凝土 = 黑暗藥水(30 秒)" +lore2 = "注意:效果期間無法疾跑!" +lore = ["夜視藥水 + 黑色混凝土 = 黑暗藥水(30 秒)", "注意:效果期間無法疾跑!"] [brewing.haste] - name = "瓶裝急迫" - description = "當效率附魔不夠用的時候" - lore1 = "速度藥水 + 紫水晶碎片 = 急迫藥水(60 秒)" - lore2 = "速度藥水 + 紫水晶塊 = 急迫 II 藥水(30 秒)" - lore = ["速度藥水 + 紫水晶碎片 = 急迫藥水(60 秒)", "速度藥水 + 紫水晶塊 = 急迫 II 藥水(30 秒)"] +name = "瓶裝急迫" +description = "當效率附魔不夠用的時候" +lore1 = "速度藥水 + 紫水晶碎片 = 急迫藥水(60 秒)" +lore2 = "速度藥水 + 紫水晶塊 = 急迫 II 藥水(30 秒)" +lore = ["速度藥水 + 紫水晶碎片 = 急迫藥水(60 秒)", "速度藥水 + 紫水晶塊 = 急迫 II 藥水(30 秒)"] [brewing.absorption] - name = "瓶裝吸收" - description = "強化身軀!" - lore1 = "瞬間治療 + 石英 = 吸收藥水(60 秒)" - lore2 = "瞬間治療 + 石英塊 = 吸收 II 藥水(30 秒)" - lore = ["瞬間治療 + 石英 = 吸收藥水(60 秒)", "瞬間治療 + 石英塊 = 吸收 II 藥水(30 秒)"] +name = "瓶裝吸收" +description = "強化身軀!" +lore1 = "瞬間治療 + 石英 = 吸收藥水(60 秒)" +lore2 = "瞬間治療 + 石英塊 = 吸收 II 藥水(30 秒)" +lore = ["瞬間治療 + 石英 = 吸收藥水(60 秒)", "瞬間治療 + 石英塊 = 吸收 II 藥水(30 秒)"] [brewing.fatigue] - name = "瓶裝疲勞" - description = "削弱身軀!" - lore1 = "虛弱藥水 + 史萊姆球 = 挖掘疲勞藥水(30 秒)" - lore2 = "虛弱藥水 + 史萊姆塊 = 挖掘疲勞 II 藥水(15 秒)" - lore = ["虛弱藥水 + 史萊姆球 = 挖掘疲勞藥水(30 秒)", "虛弱藥水 + 史萊姆塊 = 挖掘疲勞 II 藥水(15 秒)"] +name = "瓶裝疲勞" +description = "削弱身軀!" +lore1 = "虛弱藥水 + 史萊姆球 = 挖掘疲勞藥水(30 秒)" +lore2 = "虛弱藥水 + 史萊姆塊 = 挖掘疲勞 II 藥水(15 秒)" +lore = ["虛弱藥水 + 史萊姆球 = 挖掘疲勞藥水(30 秒)", "虛弱藥水 + 史萊姆塊 = 挖掘疲勞 II 藥水(15 秒)"] [brewing.hunger] - name = "瓶裝飢餓" - description = "餵飽那些永不滿足的人!" - lore1 = "粗製藥水 + 腐肉 = 飢餓藥水(30 秒)" - lore2 = "虛弱藥水 + 腐肉 = 飢餓 III 藥水(15 秒)" - lore = ["粗製藥水 + 腐肉 = 飢餓藥水(30 秒)", "虛弱藥水 + 腐肉 = 飢餓 III 藥水(15 秒)"] +name = "瓶裝飢餓" +description = "餵飽那些永不滿足的人!" +lore1 = "粗製藥水 + 腐肉 = 飢餓藥水(30 秒)" +lore2 = "虛弱藥水 + 腐肉 = 飢餓 III 藥水(15 秒)" +lore = ["粗製藥水 + 腐肉 = 飢餓藥水(30 秒)", "虛弱藥水 + 腐肉 = 飢餓 III 藥水(15 秒)"] [brewing.nausea] - name = "瓶裝噁心" - description = "你真令我作嘔!" - lore1 = "粗製藥水 + 棕色蘑菇 = 噁心藥水(16 秒)" - lore2 = "粗製藥水 + 緋紅蕈菇 = 噁心 II 藥水(8 秒)" - lore = ["粗製藥水 + 棕色蘑菇 = 噁心藥水(16 秒)", "粗製藥水 + 緋紅蕈菇 = 噁心 II 藥水(8 秒)"] +name = "瓶裝噁心" +description = "你真令我作嘔!" +lore1 = "粗製藥水 + 棕色蘑菇 = 噁心藥水(16 秒)" +lore2 = "粗製藥水 + 緋紅蕈菇 = 噁心 II 藥水(8 秒)" +lore = ["粗製藥水 + 棕色蘑菇 = 噁心藥水(16 秒)", "粗製藥水 + 緋紅蕈菇 = 噁心 II 藥水(8 秒)"] [brewing.blindness] - name = "瓶裝失明" - description = "你真是個可怕的人……" - lore1 = "粗製藥水 + 墨囊 = 失明藥水(30 秒)" - lore2 = "粗製藥水 + 螢光墨囊 = 失明 II 藥水(15 秒)" - lore = ["粗製藥水 + 墨囊 = 失明藥水(30 秒)", "粗製藥水 + 螢光墨囊 = 失明 II 藥水(15 秒)"] +name = "瓶裝失明" +description = "你真是個可怕的人……" +lore1 = "粗製藥水 + 墨囊 = 失明藥水(30 秒)" +lore2 = "粗製藥水 + 螢光墨囊 = 失明 II 藥水(15 秒)" +lore = ["粗製藥水 + 墨囊 = 失明藥水(30 秒)", "粗製藥水 + 螢光墨囊 = 失明 II 藥水(15 秒)"] [brewing.resistance] - name = "瓶裝抗性" - description = "最強防禦!" - lore1 = "粗製藥水 + 鐵錠 = 抗性提升藥水(60 秒)" - lore2 = "粗製藥水 + 鐵方塊 = 抗性提升 II 藥水(30 秒)" - lore = ["粗製藥水 + 鐵錠 = 抗性提升藥水(60 秒)", "粗製藥水 + 鐵方塊 = 抗性提升 II 藥水(30 秒)"] +name = "瓶裝抗性" +description = "最強防禦!" +lore1 = "粗製藥水 + 鐵錠 = 抗性提升藥水(60 秒)" +lore2 = "粗製藥水 + 鐵方塊 = 抗性提升 II 藥水(30 秒)" +lore = ["粗製藥水 + 鐵錠 = 抗性提升藥水(60 秒)", "粗製藥水 + 鐵方塊 = 抗性提升 II 藥水(30 秒)"] [brewing.health_boost] - name = "瓶裝生命" - description = "當最大生命值不夠用的時候……" - lore1 = "瞬間治療藥水 + 金蘋果 = 生命提升藥水(120 秒)" - lore2 = "瞬間治療藥水 + 附魔金蘋果 = 生命提升 II 藥水(120 秒)" - lore = ["瞬間治療藥水 + 金蘋果 = 生命提升藥水(120 秒)", "瞬間治療藥水 + 附魔金蘋果 = 生命提升 II 藥水(120 秒)"] +name = "瓶裝生命" +description = "當最大生命值不夠用的時候……" +lore1 = "瞬間治療藥水 + 金蘋果 = 生命提升藥水(120 秒)" +lore2 = "瞬間治療藥水 + 附魔金蘋果 = 生命提升 II 藥水(120 秒)" +lore = ["瞬間治療藥水 + 金蘋果 = 生命提升藥水(120 秒)", "瞬間治療藥水 + 附魔金蘋果 = 生命提升 II 藥水(120 秒)"] [brewing.decay] - name = "瓶裝衰敗" - description = "誰知道腐質竟然這麼有用?" - lore1 = "虛弱藥水 + 毒馬鈴薯 = 凋零藥水(16 秒)" - lore2 = "虛弱藥水 + 緋紅蕈根 = 凋零 II 藥水(8 秒)" - lore = ["虛弱藥水 + 毒馬鈴薯 = 凋零藥水(16 秒)", "虛弱藥水 + 緋紅蕈根 = 凋零 II 藥水(8 秒)"] +name = "瓶裝衰敗" +description = "誰知道腐質竟然這麼有用?" +lore1 = "虛弱藥水 + 毒馬鈴薯 = 凋零藥水(16 秒)" +lore2 = "虛弱藥水 + 緋紅蕈根 = 凋零 II 藥水(8 秒)" +lore = ["虛弱藥水 + 毒馬鈴薯 = 凋零藥水(16 秒)", "虛弱藥水 + 緋紅蕈根 = 凋零 II 藥水(8 秒)"] [brewing.saturation] - name = "瓶裝飽和" - description = "你知道嗎……其實我還不太餓……" - lore1 = "再生藥水 + 烤馬鈴薯 = 飽和藥水" - lore2 = "再生藥水 + 乾草塊 = 飽和 II 藥水" - lore = ["再生藥水 + 烤馬鈴薯 = 飽和藥水", "再生藥水 + 乾草塊 = 飽和 II 藥水"] +name = "瓶裝飽和" +description = "你知道嗎……其實我還不太餓……" +lore1 = "再生藥水 + 烤馬鈴薯 = 飽和藥水" +lore2 = "再生藥水 + 乾草塊 = 飽和 II 藥水" +lore = ["再生藥水 + 烤馬鈴薯 = 飽和藥水", "再生藥水 + 乾草塊 = 飽和 II 藥水"] # blocking [blocking] [blocking.chain_armorer] - name = "梅菲斯特的鎖鏈" - description = "允許你合成鎖鏈甲" - lore1 = "合成配方與其他盔甲相同,但使用鐵粒替代" - lore = ["合成配方與其他盔甲相同,但使用鐵粒替代"] +name = "梅菲斯特的鎖鏈" +description = "允許你合成鎖鏈甲" +lore1 = "合成配方與其他盔甲相同,但使用鐵粒替代" +lore = ["合成配方與其他盔甲相同,但使用鐵粒替代"] [blocking.horse_armorer] - name = "可合成馬鎧" - description = "允許你合成馬鎧" - lore1 = "在工作台中用所需材料圍繞馬鞍以合成該材質的馬鎧" - lore = ["在工作台中用所需材料圍繞馬鞍以合成該材質的馬鎧"] +name = "可合成馬鎧" +description = "允許你合成馬鎧" +lore1 = "在工作台中用所需材料圍繞馬鞍以合成該材質的馬鎧" +lore = ["在工作台中用所需材料圍繞馬鞍以合成該材質的馬鎧"] [blocking.saddle_crafter] - name = "可合成馬鞍" - description = "用皮革合成馬鞍" - lore1 = "配方:5 塊皮革:" - lore = ["配方:5 塊皮革:"] +name = "可合成馬鞍" +description = "用皮革合成馬鞍" +lore1 = "配方:5 塊皮革:" +lore = ["配方:5 塊皮革:"] [blocking.multi_armor] - name = "複合盔甲" - description = "將鞘翅綁定到盔甲上" - lore1 = "這是旅行的絕佳技能。" - lore2 = "飛行中動態合併及切換盔甲/鞘翅!" - lore3 = "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。" - lore4 = "要拆解盔甲,潛行時丟棄物品即可拆解。" - lore5 = "如果你的複合盔甲被摧毀,裡面所有物品都會丟失。" - lore6 = "我不需要盔甲,它令我失望……" - lore = ["這是旅行的絕佳技能。", "飛行中動態合併及切換盔甲/鞘翅!", "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。", "要拆解盔甲,潛行時丟棄物品即可拆解。", "如果你的複合盔甲被摧毀,裡面所有物品都會丟失。", "我不需要盔甲,它令我失望……"] +name = "複合盔甲" +description = "將鞘翅綁定到盔甲上" +lore1 = "這是旅行的絕佳技能。" +lore2 = "飛行中動態合併及切換盔甲/鞘翅!" +lore3 = "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。" +lore4 = "要拆解盔甲,潛行時丟棄物品即可拆解。" +lore5 = "如果你的複合盔甲被摧毀,裡面所有物品都會丟失。" +lore6 = "我不需要盔甲,它令我失望……" +lore = ["這是旅行的絕佳技能。", "飛行中動態合併及切換盔甲/鞘翅!", "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。", "要拆解盔甲,潛行時丟棄物品即可拆解。", "如果你的複合盔甲被摧毀,裡面所有物品都會丟失。", "我不需要盔甲,它令我失望……"] # crafting [crafting] [crafting.deconstruction] - name = "拆解" - description = "將方塊和物品拆解為可回收的基礎材料!" - lore1 = "將任何物品丟在地上。" - lore2 = "然後,潛行並用剪刀右鍵點擊" - lore = ["將任何物品丟在地上。", "然後,潛行並用剪刀右鍵點擊"] +name = "拆解" +description = "將方塊和物品拆解為可回收的基礎材料!" +lore1 = "將任何物品丟在地上。" +lore2 = "然後,潛行並用剪刀右鍵點擊" +lore = ["將任何物品丟在地上。", "然後,潛行並用剪刀右鍵點擊"] [crafting.xp] - name = "合成經驗" - description = "合成時獲得被動經驗" - lore1 = "合成時獲得經驗" - lore = ["合成時獲得經驗"] +name = "合成經驗" +description = "合成時獲得被動經驗" +lore1 = "合成時獲得經驗" +lore = ["合成時獲得經驗"] [crafting.reconstruction] - name = "礦石重建" - description = "用基礎材料重新合成礦石!" - lore1 = "8 個掉落物 + 1 個載體 = 1 個礦石(無序合成)" - lore2 = "掉落物必須先熔煉(如果適用)" - lore3 = "不包括:殘骸、石英、綠寶石等……" - lore4 = "載體 = 外殼方塊。例如:石頭、地獄石、深板岩" - lore = ["8 個掉落物 + 1 個載體 = 1 個礦石(無序合成)", "掉落物必須先熔煉(如果適用)", "不包括:殘骸、石英、綠寶石等……", "載體 = 外殼方塊。例如:石頭、地獄石、深板岩"] +name = "礦石重建" +description = "用基礎材料重新合成礦石!" +lore1 = "8 個掉落物 + 1 個載體 = 1 個礦石(無序合成)" +lore2 = "掉落物必須先熔煉(如果適用)" +lore3 = "不包括:殘骸、石英、綠寶石等……" +lore4 = "載體 = 外殼方塊。例如:石頭、地獄石、深板岩" +lore = ["8 個掉落物 + 1 個載體 = 1 個礦石(無序合成)", "掉落物必須先熔煉(如果適用)", "不包括:殘骸、石英、綠寶石等……", "載體 = 外殼方塊。例如:石頭、地獄石、深板岩"] [crafting.leather] - name = "可合成皮革" - description = "用腐肉製作皮革" - lore1 = "只需要把腐肉丟到營火上!" - lore = ["只需要把腐肉丟到營火上!"] +name = "可合成皮革" +description = "用腐肉製作皮革" +lore1 = "只需要把腐肉丟到營火上!" +lore = ["只需要把腐肉丟到營火上!"] [crafting.backpacks] - name = "布蒂利埃的背包!" - description = "將 Mojang 的收納袋功能加入遊戲!" - lore1 = "你需要在生存模式下才能使用" - lore2 = "XLX:皮革、拴繩、皮革" - lore3 = "XSX:皮革、木桶、皮革" - lore4 = "XCX:皮革、箱子、皮革" - lore = ["你需要在生存模式下才能使用", "XLX:皮革、拴繩、皮革", "XSX:皮革、木桶、皮革", "XCX:皮革、箱子、皮革"] +name = "布蒂利埃的背包!" +description = "將 Mojang 的收納袋功能加入遊戲!" +lore1 = "你需要在生存模式下才能使用" +lore2 = "XLX:皮革、拴繩、皮革" +lore3 = "XSX:皮革、木桶、皮革" +lore4 = "XCX:皮革、箱子、皮革" +lore = ["你需要在生存模式下才能使用", "XLX:皮革、拴繩、皮革", "XSX:皮革、木桶、皮革", "XCX:皮革、箱子、皮革"] [crafting.stations] - name = "便攜工作台!" - description = "在手掌中使用工作台!" - lore2 = "關閉時遺忘在工作台裡的任何物品都會永遠丟失!" - lore3 = "有效工作台:鐵砧、工作台、砂輪、製圖台、切石機、織布機" - lore = ["關閉時遺忘在工作台裡的任何物品都會永遠丟失!", "有效工作台:鐵砧、工作台、砂輪、製圖台、切石機、織布機"] +name = "便攜工作台!" +description = "在手掌中使用工作台!" +lore2 = "關閉時遺忘在工作台裡的任何物品都會永遠丟失!" +lore3 = "有效工作台:鐵砧、工作台、砂輪、製圖台、切石機、織布機" +lore = ["關閉時遺忘在工作台裡的任何物品都會永遠丟失!", "有效工作台:鐵砧、工作台、砂輪、製圖台、切石機、織布機"] [crafting.skulls] - name = "可合成頭顱!" - description = "用材料可以合成生物頭顱!" - lore1 = "用以下物品圍繞骨塊以獲得對應頭顱:" - lore2 = "殭屍:腐肉" - lore3 = "骷髏:骨頭" - lore4 = "苦力怕:火藥" - lore5 = "凋零骷髏:地獄磚" - lore6 = "龍首:龍息" - lore = ["用以下物品圍繞骨塊以獲得對應頭顱:", "殭屍:腐肉", "骷髏:骨頭", "苦力怕:火藥", "凋零骷髏:地獄磚", "龍首:龍息"] +name = "可合成頭顱!" +description = "用材料可以合成生物頭顱!" +lore1 = "用以下物品圍繞骨塊以獲得對應頭顱:" +lore2 = "殭屍:腐肉" +lore3 = "骷髏:骨頭" +lore4 = "苦力怕:火藥" +lore5 = "凋零骷髏:地獄磚" +lore6 = "龍首:龍息" +lore = ["用以下物品圍繞骨塊以獲得對應頭顱:", "殭屍:腐肉", "骷髏:骨頭", "苦力怕:火藥", "凋零骷髏:地獄磚", "龍首:龍息"] # chronos [chronos] [chronos.time_in_a_bottle] - name = "時光之瓶" - description = "隨身攜帶的時間之瓶會儲存時間,消耗儲存的時間以加速計時方塊、可生長物和幼年動物等可成長實體。配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶。" - lore1 = "每刻充能的儲存秒數" - lore2 = "每儲存秒數的時間加速" - lore3 = "配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶" - lore = ["每刻充能的儲存秒數", "每儲存秒數的時間加速", "配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶"] +name = "時光之瓶" +description = "隨身攜帶的時間之瓶會儲存時間,消耗儲存的時間以加速計時方塊、可生長物和幼年動物等可成長實體。配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶。" +lore1 = "每刻充能的儲存秒數" +lore2 = "每儲存秒數的時間加速" +lore3 = "配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶" +lore = ["每刻充能的儲存秒數", "每儲存秒數的時間加速", "配方(無序):迅捷藥水 + 時鐘 + 玻璃瓶"] [chronos.aberrant_touch] - name = "異常觸碰" - description = "近戰攻擊以飢餓為代價施加疊加緩速效果,PvP 有嚴格上限,5 層時定身目標。" - lore1 = "近戰攻擊施加疊加緩速" - lore2 = "PvE 緩速持續時間上限" - lore3 = "PvP 緩速強度上限" - lore = ["近戰攻擊施加疊加緩速", "PvE 緩速持續時間上限", "PvP 緩速強度上限"] +name = "異常觸碰" +description = "近戰攻擊以飢餓為代價施加疊加緩速效果,PvP 有嚴格上限,5 層時定身目標。" +lore1 = "近戰攻擊施加疊加緩速" +lore2 = "PvE 緩速持續時間上限" +lore3 = "PvP 緩速強度上限" +lore = ["近戰攻擊施加疊加緩速", "PvE 緩速持續時間上限", "PvP 緩速強度上限"] [chronos.instant_recall] - name = "瞬間回溯" - description = "手持時鐘左鍵或右鍵點擊以回溯到最近的快照,恢復生命值和飢餓值。" - lore1 = "回溯時長" - lore2 = "冷卻時間" - lore3 = "不回溯物品欄" - lore = ["回溯時長", "冷卻時間", "不回溯物品欄"] +name = "瞬間回溯" +description = "手持時鐘左鍵或右鍵點擊以回溯到最近的快照,恢復生命值和飢餓值。" +lore1 = "回溯時長" +lore2 = "冷卻時間" +lore3 = "不回溯物品欄" +lore = ["回溯時長", "冷卻時間", "不回溯物品欄"] [chronos.time_bomb] - name = "時間炸彈" - description = "投擲合成的時空炸彈,產生時間力場,減速實體並凍結投射物。" - lore1 = "時間力場半徑" - lore2 = "時間力場持續時間" - lore3 = "炸彈冷卻時間" - lore4 = "配方(無序):時鐘 + 雪球 + 鑽石 + 沙子" - lore = ["時間力場半徑", "時間力場持續時間", "炸彈冷卻時間", "配方(無序):時鐘 + 雪球 + 鑽石 + 沙子"] +name = "時間炸彈" +description = "投擲合成的時空炸彈,產生時間力場,減速實體並凍結投射物。" +lore1 = "時間力場半徑" +lore2 = "時間力場持續時間" +lore3 = "炸彈冷卻時間" +lore4 = "配方(無序):時鐘 + 雪球 + 鑽石 + 沙子" +lore = ["時間力場半徑", "時間力場持續時間", "炸彈冷卻時間", "配方(無序):時鐘 + 雪球 + 鑽石 + 沙子"] # discovery [discovery] [discovery.armor] - name = "世界之甲" - description = "根據附近方塊硬度獲得被動護甲。" - lore1 = "被動護甲" - lore2 = "取決於附近方塊硬度" - lore3 = "護甲強度:" - lore = ["被動護甲", "取決於附近方塊硬度", "護甲強度:"] +name = "世界之甲" +description = "根據附近方塊硬度獲得被動護甲。" +lore1 = "被動護甲" +lore2 = "取決於附近方塊硬度" +lore3 = "護甲強度:" +lore = ["被動護甲", "取決於附近方塊硬度", "護甲強度:"] [discovery.unity] - name = "實驗性統合" - description = "拾取經驗球時為隨機技能增加經驗。" - lore1 = "經驗 " - lore2 = "每個經驗球" - lore = ["經驗 ", "每個經驗球"] +name = "實驗性統合" +description = "拾取經驗球時為隨機技能增加經驗。" +lore1 = "經驗 " +lore2 = "每個經驗球" +lore = ["經驗 ", "每個經驗球"] [discovery.resist] - name = "實驗性抗性" - description = "消耗經驗來減免傷害,僅在攻擊會讓你降至 5 顆心以下或致死時觸發。" - lore0 = "僅在危急生命值(<= 5 顆心)時觸發,每 15 秒一次" - lore1 = " 減免傷害" - lore2 = "經驗消耗" - lore = ["僅在危急生命值(<= 5 顆心)時觸發,每 15 秒一次", " 減免傷害", "經驗消耗"] +name = "實驗性抗性" +description = "消耗經驗來減免傷害,僅在攻擊會讓你降至 5 顆心以下或致死時觸發。" +lore0 = "僅在危急生命值(<= 5 顆心)時觸發,每 15 秒一次" +lore1 = " 減免傷害" +lore2 = "經驗消耗" +lore = ["僅在危急生命值(<= 5 顆心)時觸發,每 15 秒一次", " 減免傷害", "經驗消耗"] [discovery.villager] - name = "村民吸引力" - description = "與村民交易時獲得更好的交易!" - lore1 = "每次與村民互動時消耗經驗" - lore2 = "每次互動有機率消耗經驗以增強交易" - lore3 = "每次互動所需經驗消耗" - lore = ["每次與村民互動時消耗經驗", "每次互動有機率消耗經驗以增強交易", "每次互動所需經驗消耗"] +name = "村民吸引力" +description = "與村民交易時獲得更好的交易!" +lore1 = "每次與村民互動時消耗經驗" +lore2 = "每次互動有機率消耗經驗以增強交易" +lore3 = "每次互動所需經驗消耗" +lore = ["每次與村民互動時消耗經驗", "每次互動有機率消耗經驗以增強交易", "每次互動所需經驗消耗"] # enchanting [enchanting] [enchanting.lapis_return] - name = "青金石回饋" - description = "多消耗 1 級經驗,但有機率免費返還青金石" - lore1 = "每升一級,附魔花費增加 1 級,但最多可返還 3 個青金石" - lore = ["每升一級,附魔花費增加 1 級,但最多可返還 3 個青金石"] +name = "青金石回饋" +description = "多消耗 1 級經驗,但有機率免費返還青金石" +lore1 = "每升一級,附魔花費增加 1 級,但最多可返還 3 個青金石" +lore = ["每升一級,附魔花費增加 1 級,但最多可返還 3 個青金石"] [enchanting.quick_enchant] - name = "快速附魔" - description = "直接用附魔書點擊物品來附魔。" - lore1 = "最大合併等級" - lore2 = "無法對超過 " - lore3 = "能力的物品附魔" - lore = ["最大合併等級", "無法對超過 ", "能力的物品附魔"] +name = "快速附魔" +description = "直接用附魔書點擊物品來附魔。" +lore1 = "最大合併等級" +lore2 = "無法對超過 " +lore3 = "能力的物品附魔" +lore = ["最大合併等級", "無法對超過 ", "能力的物品附魔"] [enchanting.return] - name = "經驗返還" - description = "附魔物品時返還部分經驗。" - lore1 = "附魔消耗的經驗有機率被退還" - lore2 = "每次附魔返還經驗" - lore = ["附魔消耗的經驗有機率被退還", "每次附魔返還經驗"] +name = "經驗返還" +description = "附魔物品時返還部分經驗。" +lore1 = "附魔消耗的經驗有機率被退還" +lore2 = "每次附魔返還經驗" +lore = ["附魔消耗的經驗有機率被退還", "每次附魔返還經驗"] # excavation [excavation] [excavation.haste] - name = "急迫挖掘者" - description = "挖掘時獲得急迫效果,加速挖掘過程!" - lore1 = "挖掘時獲得急迫效果" - lore2 = "x 級急迫效果(開始挖掘任何方塊時觸發)" - lore = ["挖掘時獲得急迫效果", "x 級急迫效果(開始挖掘任何方塊時觸發)"] +name = "急迫挖掘者" +description = "挖掘時獲得急迫效果,加速挖掘過程!" +lore1 = "挖掘時獲得急迫效果" +lore2 = "x 級急迫效果(開始挖掘任何方塊時觸發)" +lore = ["挖掘時獲得急迫效果", "x 級急迫效果(開始挖掘任何方塊時觸發)"] [excavation.spelunker] - name = "超級透視洞穴探險家!" - description = "透過地面看見礦石!" - lore1 = "副手持礦石,主手持螢光漿果,然後潛行!" - lore2 = "方塊範圍:" - lore3 = "使用時消耗螢光漿果" - lore = ["副手持礦石,主手持螢光漿果,然後潛行!", "方塊範圍:", "使用時消耗螢光漿果"] +name = "超級透視洞穴探險家!" +description = "透過地面看見礦石!" +lore1 = "副手持礦石,主手持螢光漿果,然後潛行!" +lore2 = "方塊範圍:" +lore3 = "使用時消耗螢光漿果" +lore = ["副手持礦石,主手持螢光漿果,然後潛行!", "方塊範圍:", "使用時消耗螢光漿果"] [excavation.drop_to_inventory] - name = "鏟子掉落物自動入包" +name = "鏟子掉落物自動入包" [excavation.omni_tool] - name = "OMNI - T.O.O.L." - description = "Tackle 精心設計的豪華萬能工具" - lore1 = "可能是最強大的功能之一,允許你" - lore2 = "根據需求動態合併及切換工具。" - lore3 = "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。" - lore4 = "要拆解工具,潛行時丟棄物品即可拆解。" - lore5 = "萬能工具中的工具不會損壞,但壞掉的工具無法使用" - lore6 = "個可合併物品。" - lore7 = "你可以帶五六個工具,或者只帶這一個!" - lore = ["可能是最強大的功能之一,允許你", "根據需求動態合併及切換工具。", "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。", "要拆解工具,潛行時丟棄物品即可拆解。", "萬能工具中的工具不會損壞,但壞掉的工具無法使用", "個可合併物品。", "你可以帶五六個工具,或者只帶這一個!"] +name = "OMNI - T.O.O.L." +description = "Tackle 精心設計的豪華萬能工具" +lore1 = "可能是最強大的功能之一,允許你" +lore2 = "根據需求動態合併及切換工具。" +lore3 = "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。" +lore4 = "要拆解工具,潛行時丟棄物品即可拆解。" +lore5 = "萬能工具中的工具不會損壞,但壞掉的工具無法使用" +lore6 = "個可合併物品。" +lore7 = "你可以帶五六個工具,或者只帶這一個!" +lore = ["可能是最強大的功能之一,允許你", "根據需求動態合併及切換工具。", "要合併,在物品欄中按住 Shift 點擊一個物品到另一個物品上。", "要拆解工具,潛行時丟棄物品即可拆解。", "萬能工具中的工具不會損壞,但壞掉的工具無法使用", "個可合併物品。", "你可以帶五六個工具,或者只帶這一個!"] # herbalism [herbalism] [herbalism.growth_aura] - name = "生長光環" - description = "在你周圍的光環中加速自然生長" - lore1 = "方塊半徑" - lore2 = "生長光環強度" - lore3 = "食物消耗" - lore = ["方塊半徑", "生長光環強度", "食物消耗"] +name = "生長光環" +description = "在你周圍的光環中加速自然生長" +lore1 = "方塊半徑" +lore2 = "生長光環強度" +lore3 = "食物消耗" +lore = ["方塊半徑", "生長光環強度", "食物消耗"] [herbalism.hippo] - name = "草藥師的河馬" - description = "食用食物時獲得更多飽和度" - lore1 = "食物)食用時額外飽和度" - lore = ["食物)食用時額外飽和度"] +name = "草藥師的河馬" +description = "食用食物時獲得更多飽和度" +lore1 = "食物)食用時額外飽和度" +lore = ["食物)食用時額外飽和度"] [herbalism.myconid] - name = "草藥師的菌絲體" - description = "賦予你合成菌絲的能力" - lore1 = "任意泥土 + 棕色蘑菇 + 紅色蘑菇即可合成菌絲。" - lore = ["任意泥土 + 棕色蘑菇 + 紅色蘑菇即可合成菌絲。"] +name = "草藥師的菌絲體" +description = "賦予你合成菌絲的能力" +lore1 = "任意泥土 + 棕色蘑菇 + 紅色蘑菇即可合成菌絲。" +lore = ["任意泥土 + 棕色蘑菇 + 紅色蘑菇即可合成菌絲。"] [herbalism.terralid] - name = "草藥師的地衣" - description = "賦予你合成草地方塊的能力" - lore1 = "3 個種子放在 3 塊泥土上方,即可合成 3 塊草地方塊。" - lore = ["3 個種子放在 3 塊泥土上方,即可合成 3 塊草地方塊。"] +name = "草藥師的地衣" +description = "賦予你合成草地方塊的能力" +lore1 = "3 個種子放在 3 塊泥土上方,即可合成 3 塊草地方塊。" +lore = ["3 個種子放在 3 塊泥土上方,即可合成 3 塊草地方塊。"] [herbalism.cobweb] - name = "蛛網織者" - description = "賦予你在工作台中合成蛛網的能力" - lore1 = "9 根線即可合成一個蛛網。" - lore = ["9 根線即可合成一個蛛網。"] +name = "蛛網織者" +description = "賦予你在工作台中合成蛛網的能力" +lore1 = "9 根線即可合成一個蛛網。" +lore = ["9 根線即可合成一個蛛網。"] [herbalism.mushroom_blocks] - name = "蘑菇製造者" - description = "賦予你在工作台中合成蘑菇方塊的能力" - lore1 = "4 個蘑菇合成一個蘑菇塊,或一個蘑菇塊合成蘑菇柄。" - lore = ["4 個蘑菇合成一個蘑菇塊,或一個蘑菇塊合成蘑菇柄。"] +name = "蘑菇製造者" +description = "賦予你在工作台中合成蘑菇方塊的能力" +lore1 = "4 個蘑菇合成一個蘑菇塊,或一個蘑菇塊合成蘑菇柄。" +lore = ["4 個蘑菇合成一個蘑菇塊,或一個蘑菇塊合成蘑菇柄。"] [herbalism.drop_to_inventory] - name = "鋤頭掉落物自動入包" +name = "鋤頭掉落物自動入包" [herbalism.hungry_shield] - name = "飢餓護盾" - description = "受到傷害時先消耗飢餓值而非生命值。" - lore1 = "飢餓抵抗傷害" - lore = ["飢餓抵抗傷害"] +name = "飢餓護盾" +description = "受到傷害時先消耗飢餓值而非生命值。" +lore1 = "飢餓抵抗傷害" +lore = ["飢餓抵抗傷害"] [herbalism.luck] - name = "草藥師的幸運" - description = "破壞草/花時有機率獲得隨機物品" - lore0 = "花 = 食物,草 = 種子" - lore1 = "破壞花時獲得物品的機率" - lore2 = "破壞草時獲得物品的機率" - lore = ["花 = 食物,草 = 種子", "破壞花時獲得物品的機率", "破壞草時獲得物品的機率"] +name = "草藥師的幸運" +description = "破壞草/花時有機率獲得隨機物品" +lore0 = "花 = 食物,草 = 種子" +lore1 = "破壞花時獲得物品的機率" +lore2 = "破壞草時獲得物品的機率" +lore = ["花 = 食物,草 = 種子", "破壞花時獲得物品的機率", "破壞草時獲得物品的機率"] [herbalism.replant] - name = "收割與重種" - description = "手持鋤頭右鍵點擊作物以收割並重新種植。" - lore1 = "重種範圍半徑" - lore = ["重種範圍半徑"] +name = "收割與重種" +description = "手持鋤頭右鍵點擊作物以收割並重新種植。" +lore1 = "重種範圍半徑" +lore = ["重種範圍半徑"] # hunter [hunter] [hunter.adrenaline] - name = "腎上腺素" - description = "生命值越低,近戰傷害越高" - lore1 = "最大傷害" - lore = ["最大傷害"] +name = "腎上腺素" +description = "生命值越低,近戰傷害越高" +lore1 = "最大傷害" +lore = ["最大傷害"] [hunter.penalty] - name = "" - description = "" - lore1 = "飢餓值耗盡時會獲得中毒效果" - lore = ["飢餓值耗盡時會獲得中毒效果"] +name = "" +description = "" +lore1 = "飢餓值耗盡時會獲得中毒效果" +lore = ["飢餓值耗盡時會獲得中毒效果"] [hunter.drop_to_inventory] - name = "物品自動入包" - description = "擊殺生物或用劍破壞方塊時,掉落物傳送到你的物品欄" - lore1 = "生物/方塊掉落的物品會自動進入你的物品欄(如果有空間)。" - lore = ["生物/方塊掉落的物品會自動進入你的物品欄(如果有空間)。"] +name = "物品自動入包" +description = "擊殺生物或用劍破壞方塊時,掉落物傳送到你的物品欄" +lore1 = "生物/方塊掉落的物品會自動進入你的物品欄(如果有空間)。" +lore = ["生物/方塊掉落的物品會自動進入你的物品欄(如果有空間)。"] [hunter.invisibility] - name = "隱形之步" - description = "受到攻擊時獲得隱身效果,以飢餓為代價" - lore1 = "受擊時獲得被動隱身效果" - lore2 = "x 隱身效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "隱身持續時間" - lore = ["受擊時獲得被動隱身效果", "x 隱身效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "隱身持續時間"] +name = "隱形之步" +description = "受到攻擊時獲得隱身效果,以飢餓為代價" +lore1 = "受擊時獲得被動隱身效果" +lore2 = "x 隱身效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "隱身持續時間" +lore = ["受擊時獲得被動隱身效果", "x 隱身效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "隱身持續時間"] [hunter.jump_boost] - name = "獵人之巔" - description = "受到攻擊時獲得跳躍提升效果,以飢餓為代價" - lore1 = "受擊時獲得被動跳躍提升效果" - lore2 = "x 跳躍提升效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "跳躍提升疊加倍率,非持續時間。" - lore = ["受擊時獲得被動跳躍提升效果", "x 跳躍提升效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "跳躍提升疊加倍率,非持續時間。"] +name = "獵人之巔" +description = "受到攻擊時獲得跳躍提升效果,以飢餓為代價" +lore1 = "受擊時獲得被動跳躍提升效果" +lore2 = "x 跳躍提升效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "跳躍提升疊加倍率,非持續時間。" +lore = ["受擊時獲得被動跳躍提升效果", "x 跳躍提升效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "跳躍提升疊加倍率,非持續時間。"] [hunter.luck] - name = "獵人的幸運" - description = "受到攻擊時獲得幸運效果,以飢餓為代價" - lore1 = "受擊時獲得被動幸運效果" - lore2 = "x 幸運效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "幸運疊加倍率,非持續時間。" - lore = ["受擊時獲得被動幸運效果", "x 幸運效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "幸運疊加倍率,非持續時間。"] +name = "獵人的幸運" +description = "受到攻擊時獲得幸運效果,以飢餓為代價" +lore1 = "受擊時獲得被動幸運效果" +lore2 = "x 幸運效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "幸運疊加倍率,非持續時間。" +lore = ["受擊時獲得被動幸運效果", "x 幸運效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "幸運疊加倍率,非持續時間。"] [hunter.regen] - name = "獵人的再生" - description = "受到攻擊時獲得再生效果,以飢餓為代價" - lore1 = "受擊時獲得被動再生效果" - lore2 = "x 再生效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "再生疊加倍率,非持續時間。" - lore = ["受擊時獲得被動再生效果", "x 再生效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "再生疊加倍率,非持續時間。"] +name = "獵人的再生" +description = "受到攻擊時獲得再生效果,以飢餓為代價" +lore1 = "受擊時獲得被動再生效果" +lore2 = "x 再生效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "再生疊加倍率,非持續時間。" +lore = ["受擊時獲得被動再生效果", "x 再生效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "再生疊加倍率,非持續時間。"] [hunter.resistance] - name = "獵人的抗性" - description = "受到攻擊時獲得抗性提升效果,以飢餓為代價" - lore1 = "受擊時獲得被動抗性提升效果" - lore2 = "x 抗性提升效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "抗性提升疊加倍率,非持續時間。" - lore = ["受擊時獲得被動抗性提升效果", "x 抗性提升效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "抗性提升疊加倍率,非持續時間。"] +name = "獵人的抗性" +description = "受到攻擊時獲得抗性提升效果,以飢餓為代價" +lore1 = "受擊時獲得被動抗性提升效果" +lore2 = "x 抗性提升效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "抗性提升疊加倍率,非持續時間。" +lore = ["受擊時獲得被動抗性提升效果", "x 抗性提升效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "抗性提升疊加倍率,非持續時間。"] [hunter.speed] - name = "獵人的速度" - description = "受到攻擊時獲得速度效果,以飢餓為代價" - lore1 = "受擊時獲得被動速度效果" - lore2 = "x 速度效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "速度疊加倍率,非持續時間。" - lore = ["受擊時獲得被動速度效果", "x 速度效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "速度疊加倍率,非持續時間。"] +name = "獵人的速度" +description = "受到攻擊時獲得速度效果,以飢餓為代價" +lore1 = "受擊時獲得被動速度效果" +lore2 = "x 速度效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "速度疊加倍率,非持續時間。" +lore = ["受擊時獲得被動速度效果", "x 速度效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "速度疊加倍率,非持續時間。"] [hunter.strength] - name = "獵人的力量" - description = "受到攻擊時獲得力量效果,以飢餓為代價" - lore1 = "受擊時獲得被動力量效果" - lore2 = "x 力量效果疊加,受擊後持續 3 秒" - lore3 = "x 疊加飢餓" - lore4 = "飢餓疊加持續時間和倍率。" - lore5 = "力量疊加倍率,非持續時間。" - lore = ["受擊時獲得被動力量效果", "x 力量效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "力量疊加倍率,非持續時間。"] +name = "獵人的力量" +description = "受到攻擊時獲得力量效果,以飢餓為代價" +lore1 = "受擊時獲得被動力量效果" +lore2 = "x 力量效果疊加,受擊後持續 3 秒" +lore3 = "x 疊加飢餓" +lore4 = "飢餓疊加持續時間和倍率。" +lore5 = "力量疊加倍率,非持續時間。" +lore = ["受擊時獲得被動力量效果", "x 力量效果疊加,受擊後持續 3 秒", "x 疊加飢餓", "飢餓疊加持續時間和倍率。", "力量疊加倍率,非持續時間。"] # nether [nether] [nether.skull_toss] - name = "凋零頭顱投擲" - description1 = "釋放你內心的凋零,使用" - description2 = "某人的" - description3 = "頭顱。" - lore1 = "頭顱投擲間的冷卻秒數。" - lore2 = "使用凋零骷髏頭:投擲" - lore3 = "凋零頭顱" - lore4 = "撞擊時爆炸。" - lore = ["頭顱投擲間的冷卻秒數。", "使用凋零骷髏頭:投擲", "凋零頭顱", "撞擊時爆炸。"] +name = "凋零頭顱投擲" +description1 = "釋放你內心的凋零,使用" +description2 = "某人的" +description3 = "頭顱。" +lore1 = "頭顱投擲間的冷卻秒數。" +lore2 = "使用凋零骷髏頭:投擲" +lore3 = "凋零頭顱" +lore4 = "撞擊時爆炸。" +lore = ["頭顱投擲間的冷卻秒數。", "使用凋零骷髏頭:投擲", "凋零頭顱", "撞擊時爆炸。"] [nether.wither_resist] - name = "凋零抗性" - description = "借助乖合金之力抵抗凋零效果。" - lore1 = "機率抵消凋零效果(每件裝備)。" - lore2 = "被動:穿著乖合金裝備有機率抵消" - lore3 = "凋零效果。" - lore = ["機率抵消凋零效果(每件裝備)。", "被動:穿著乖合金裝備有機率抵消", "凋零效果。"] +name = "凋零抗性" +description = "借助乖合金之力抵抗凋零效果。" +lore1 = "機率抵消凋零效果(每件裝備)。" +lore2 = "被動:穿著乖合金裝備有機率抵消" +lore3 = "凋零效果。" +lore = ["機率抵消凋零效果(每件裝備)。", "被動:穿著乖合金裝備有機率抵消", "凋零效果。"] [nether.fire_resist] - name = "火焰抗性" - description = "通過硬化皮膚來抵禦火焰。" - lore1 = "機率抵消燃燒效果!" - lore = ["機率抵消燃燒效果!"] +name = "火焰抗性" +description = "通過硬化皮膚來抵禦火焰。" +lore1 = "機率抵消燃燒效果!" +lore = ["機率抵消燃燒效果!"] # pickaxe [pickaxe] [pickaxe.auto_smelt] - name = "自動熔煉" - description = "允許你自動熔煉開採的原版礦石" - lore1 = "可熔煉的礦石會自動熔煉" - lore2 = "% 機率獲得額外產物" - lore = ["可熔煉的礦石會自動熔煉", "% 機率獲得額外產物"] +name = "自動熔煉" +description = "允許你自動熔煉開採的原版礦石" +lore1 = "可熔煉的礦石會自動熔煉" +lore2 = "% 機率獲得額外產物" +lore = ["可熔煉的礦石會自動熔煉", "% 機率獲得額外產物"] [pickaxe.chisel] - name = "礦石鑿子" - description = "右鍵點擊礦石以鑿出更多產物,但會大量消耗耐久。" - lore1 = "掉落機率" - lore2 = "工具磨損" - lore = ["掉落機率", "工具磨損"] +name = "礦石鑿子" +description = "右鍵點擊礦石以鑿出更多產物,但會大量消耗耐久。" +lore1 = "掉落機率" +lore2 = "工具磨損" +lore = ["掉落機率", "工具磨損"] [pickaxe.drop_to_inventory] - name = "鎬子掉落物自動入包" - description = "破壞方塊時掉落物自動傳送到你的物品欄" - lore1 = "方塊掉落的物品會自動進入你的物品欄(如果有空間)。" - lore = ["方塊掉落的物品會自動進入你的物品欄(如果有空間)。"] +name = "鎬子掉落物自動入包" +description = "破壞方塊時掉落物自動傳送到你的物品欄" +lore1 = "方塊掉落的物品會自動進入你的物品欄(如果有空間)。" +lore = ["方塊掉落的物品會自動進入你的物品欄(如果有空間)。"] [pickaxe.silk_spawner] - name = "鎬子絲綢刷怪籠" - description = "使刷怪籠被破壞時掉落" - lore1 = "使用絲綢觸感可以採集刷怪籠。" - lore2 = "潛行時可以破壞刷怪籠。" - lore = ["使用絲綢觸感可以採集刷怪籠。", "潛行時可以破壞刷怪籠。"] +name = "鎬子絲綢刷怪籠" +description = "使刷怪籠被破壞時掉落" +lore1 = "使用絲綢觸感可以採集刷怪籠。" +lore2 = "潛行時可以破壞刷怪籠。" +lore = ["使用絲綢觸感可以採集刷怪籠。", "潛行時可以破壞刷怪籠。"] [pickaxe.vein_miner] - name = "連鎖挖礦" - description = "允許你連鎖挖掘原版礦石的礦脈/群" - lore1 = "潛行並挖掘礦石" - lore2 = "連鎖挖礦範圍" - lore3 = "此技能不會合併所有掉落物!" - lore = ["潛行並挖掘礦石", "連鎖挖礦範圍", "此技能不會合併所有掉落物!"] +name = "連鎖挖礦" +description = "允許你連鎖挖掘原版礦石的礦脈/群" +lore1 = "潛行並挖掘礦石" +lore2 = "連鎖挖礦範圍" +lore3 = "此技能不會合併所有掉落物!" +lore = ["潛行並挖掘礦石", "連鎖挖礦範圍", "此技能不會合併所有掉落物!"] # ranged [ranged] [ranged.arrow_recovery] - name = "箭矢回收" - description = "擊殺敵人後回收箭矢。" - lore1 = "命中/擊殺時回收箭矢的機率" - lore2 = "機率:" - lore = ["命中/擊殺時回收箭矢的機率", "機率:"] +name = "箭矢回收" +description = "擊殺敵人後回收箭矢。" +lore1 = "命中/擊殺時回收箭矢的機率" +lore2 = "機率:" +lore = ["命中/擊殺時回收箭矢的機率", "機率:"] [ranged.web_shot] - name = "蛛網陷阱" - description = "命中目標時在其周圍產生蛛網!" - lore1 = "8 個蛛網圍繞 1 個雪球,然後投擲!" - lore2 = "秒的籠子,大約。" - lore = ["8 個蛛網圍繞 1 個雪球,然後投擲!", "秒的籠子,大約。"] +name = "蛛網陷阱" +description = "命中目標時在其周圍產生蛛網!" +lore1 = "8 個蛛網圍繞 1 個雪球,然後投擲!" +lore2 = "秒的籠子,大約。" +lore = ["8 個蛛網圍繞 1 個雪球,然後投擲!", "秒的籠子,大約。"] [ranged.force_shot] - name = "強力射擊" - description = "投射物飛得更遠、更快!" - advancementname = "遠距離射擊" - advancementlore = "從超過 30 格方塊外命中目標!" - lore1 = "投射物速度" - lore = ["投射物速度"] +name = "強力射擊" +description = "投射物飛得更遠、更快!" +advancementname = "遠距離射擊" +advancementlore = "從超過 30 格方塊外命中目標!" +lore1 = "投射物速度" +lore = ["投射物速度"] [ranged.lunge_shot] - name = "弓步射擊" - description = "下落時射箭會將你拋向隨機方向" - lore1 = "隨機爆發速度" - lore = ["隨機爆發速度"] +name = "弓步射擊" +description = "下落時射箭會將你拋向隨機方向" +lore1 = "隨機爆發速度" +lore = ["隨機爆發速度"] [ranged.arrow_piercing] - name = "箭矢穿透" - description = "為投射物添加穿透效果!穿越目標!" - lore1 = "穿透目標數" - lore = ["穿透目標數"] +name = "箭矢穿透" +description = "為投射物添加穿透效果!穿越目標!" +lore1 = "穿透目標數" +lore = ["穿透目標數"] # rift [rift] [rift.remote_access] - name = "遠程存取" - description = "從虛空中取物,存取已標記的容器。" - lore1 = "乖珍珠 + 指南針 = 亞空間之鑰" - lore2 = "此物品允許你遠程存取容器" - lore3 = "製作完成後查看物品以瞭解使用方法" - notcontainer = "那不是容器" - lore = ["乖珍珠 + 指南針 = 亞空間之鑰", "此物品允許你遠程存取容器", "製作完成後查看物品以瞭解使用方法"] +name = "遠程存取" +description = "從虛空中取物,存取已標記的容器。" +lore1 = "乖珍珠 + 指南針 = 亞空間之鑰" +lore2 = "此物品允許你遠程存取容器" +lore3 = "製作完成後查看物品以瞭解使用方法" +notcontainer = "那不是容器" +lore = ["乖珍珠 + 指南針 = 亞空間之鑰", "此物品允許你遠程存取容器", "製作完成後查看物品以瞭解使用方法"] [rift.blink] - name = "裂痕閃現" - description = "短距離瞬間傳送,只需一眨眼!" - lore1 = "閃現距離(垂直方向 2 倍)" - lore2 = "疾跑時:連按兩次跳躍以" - lore3 = "閃現" - lore = ["閃現距離(垂直方向 2 倍)", "疾跑時:連按兩次跳躍以", "閃現"] +name = "裂痕閃現" +description = "短距離瞬間傳送,只需一眨眼!" +lore1 = "閃現距離(垂直方向 2 倍)" +lore2 = "疾跑時:連按兩次跳躍以" +lore3 = "閃現" +lore = ["閃現距離(垂直方向 2 倍)", "疾跑時:連按兩次跳躍以", "閃現"] [rift.chest] - name = "便攜乖影箱" - description = "手持乖影箱左鍵點擊即可打開。" - lore1 = "手持乖影箱點擊即可打開(不用放置)" - lore = ["手持乖影箱點擊即可打開(不用放置)"] +name = "便攜乖影箱" +description = "手持乖影箱左鍵點擊即可打開。" +lore1 = "手持乖影箱點擊即可打開(不用放置)" +lore = ["手持乖影箱點擊即可打開(不用放置)"] [rift.descent] - name = "反漂浮" - description = "厭倦了被困在空中?這個技能就是為你準備的!" - lore1 = "只需潛行即可下降,且下降速度低於正常!" - lore2 = "冷卻:" - lore = ["只需潛行即可下降,且下降速度低於正常!", "冷卻:"] +name = "反漂浮" +description = "厭倦了被困在空中?這個技能就是為你準備的!" +lore1 = "只需潛行即可下降,且下降速度低於正常!" +lore2 = "冷卻:" +lore = ["只需潛行即可下降,且下降速度低於正常!", "冷卻:"] [rift.gate] - name = "裂痕之門" - description = "傳送到標記的位置。" - lore1 = "合成:綠寶石 + 紫水晶碎片 + 乖珍珠" - lore2 = "使用前請仔細閱讀!" - lore3 = "5 秒延遲," - lore4 = "在此動畫期間你可能會死亡" - lore = ["合成:綠寶石 + 紫水晶碎片 + 乖珍珠", "使用前請仔細閱讀!", "5 秒延遲,", "在此動畫期間你可能會死亡"] +name = "裂痕之門" +description = "傳送到標記的位置。" +lore1 = "合成:綠寶石 + 紫水晶碎片 + 乖珍珠" +lore2 = "使用前請仔細閱讀!" +lore3 = "5 秒延遲," +lore4 = "在此動畫期間你可能會死亡" +lore = ["合成:綠寶石 + 紫水晶碎片 + 乖珍珠", "使用前請仔細閱讀!", "5 秒延遲,", "在此動畫期間你可能會死亡"] [rift.resist] - name = "裂痕抗性" - description = "使用乖影物品或技能時獲得抗性提升" - lore1 = "+ 被動:使用裂痕技能或乖影物品時獲得抗性提升" - lore2 = "不包括便攜乖影箱,僅限可消耗的物品" - lore = ["+ 被動:使用裂痕技能或乖影物品時獲得抗性提升", "不包括便攜乖影箱,僅限可消耗的物品"] +name = "裂痕抗性" +description = "使用乖影物品或技能時獲得抗性提升" +lore1 = "+ 被動:使用裂痕技能或乖影物品時獲得抗性提升" +lore2 = "不包括便攜乖影箱,僅限可消耗的物品" +lore = ["+ 被動:使用裂痕技能或乖影物品時獲得抗性提升", "不包括便攜乖影箱,僅限可消耗的物品"] [rift.visage] - name = "裂痕面紗" - description = "物品欄中有乖珍珠時,終界使者不會對你產生敵意。" - lore1 = "物品欄中有乖珍珠時,終界使者不會對你產生敵意。" - lore = ["物品欄中有乖珍珠時,終界使者不會對你產生敵意。"] +name = "裂痕面紗" +description = "物品欄中有乖珍珠時,終界使者不會對你產生敵意。" +lore1 = "物品欄中有乖珍珠時,終界使者不會對你產生敵意。" +lore = ["物品欄中有乖珍珠時,終界使者不會對你產生敵意。"] # seaborn [seaborn] [seaborn.oxygen] - name = "有機氧氣罐" - description = "小肺也能容納更多氧氣!" - lore1 = "氧氣容量增加" - lore = ["氧氣容量增加"] +name = "有機氧氣罐" +description = "小肺也能容納更多氧氣!" +lore1 = "氧氣容量增加" +lore = ["氧氣容量增加"] [seaborn.fishers_fantasy] - name = "釣魚達人" - description = "釣魚時獲得更多經驗和更多魚!" - lore1 = "每升一級都有機率獲得更多經驗和魚!" - lore = ["每升一級都有機率獲得更多經驗和魚!"] +name = "釣魚達人" +description = "釣魚時獲得更多經驗和更多魚!" +lore1 = "每升一級都有機率獲得更多經驗和魚!" +lore = ["每升一級都有機率獲得更多經驗和魚!"] [seaborn.haste] - name = "海龜礦工" - description = "在水下挖掘時獲得急迫效果!" - lore1 = "水下呼吸效果結束後,在水下挖掘時獲得急迫 3 效果(可與親水性疊加)!" - lore = ["水下呼吸效果結束後,在水下挖掘時獲得急迫 3 效果(可與親水性疊加)!"] +name = "海龜礦工" +description = "在水下挖掘時獲得急迫效果!" +lore1 = "水下呼吸效果結束後,在水下挖掘時獲得急迫 3 效果(可與親水性疊加)!" +lore = ["水下呼吸效果結束後,在水下挖掘時獲得急迫 3 效果(可與親水性疊加)!"] [seaborn.night_vision] - name = "海龜視覺" - description = "在水下時獲得夜視效果" - lore1 = "水下呼吸效果結束後,在水下時直接獲得夜視效果!" - lore = ["水下呼吸效果結束後,在水下時直接獲得夜視效果!"] +name = "海龜視覺" +description = "在水下時獲得夜視效果" +lore1 = "水下呼吸效果結束後,在水下時直接獲得夜視效果!" +lore = ["水下呼吸效果結束後,在水下時直接獲得夜視效果!"] [seaborn.dolphin_grace] - name = "海豚恩惠" - description = "不需要海豚也能像海豚一樣游泳" - lore1 = "+ 被動:獲得" - lore2 = "x 速度(海豚恩惠)" - lore3 = "精密的德國工程——等等,好像不對……與深海漫步不相容" - lore = ["+ 被動:獲得", "x 速度(海豚恩惠)", "精密的德國工程——等等,好像不對……與深海漫步不相容"] +name = "海豚恩惠" +description = "不需要海豚也能像海豚一樣游泳" +lore1 = "+ 被動:獲得" +lore2 = "x 速度(海豚恩惠)" +lore3 = "精密的德國工程——等等,好像不對……與深海漫步不相容" +lore = ["+ 被動:獲得", "x 速度(海豚恩惠)", "精密的德國工程——等等,好像不對……與深海漫步不相容"] # stealth [stealth] [stealth.ghost_armor] - name = "幽靈戰甲" - description = "不受傷害時緩慢構建護甲,持續 1 次受擊" - lore1 = "最大護甲值" - lore2 = "構建速度" - lore = ["最大護甲值", "構建速度"] +name = "幽靈戰甲" +description = "不受傷害時緩慢構建護甲,持續 1 次受擊" +lore1 = "最大護甲值" +lore2 = "構建速度" +lore = ["最大護甲值", "構建速度"] [stealth.night_vision] - name = "隱匿視覺" - description = "潛行時獲得夜視效果" - lore1 = "獲得一段" - lore2 = "夜視" - lore3 = "效果(潛行時)" - lore = ["獲得一段", "夜視", "效果(潛行時)"] +name = "隱匿視覺" +description = "潛行時獲得夜視效果" +lore1 = "獲得一段" +lore2 = "夜視" +lore3 = "效果(潛行時)" +lore = ["獲得一段", "夜視", "效果(潛行時)"] [stealth.snatch] - name = "物品奪取" - description = "潛行時立即拾取附近的掉落物!" - lore1 = "奪取半徑" - lore = ["奪取半徑"] +name = "物品奪取" +description = "潛行時立即拾取附近的掉落物!" +lore1 = "奪取半徑" +lore = ["奪取半徑"] [stealth.speed] - name = "潛行加速" - description = "潛行時獲得速度加成" - lore1 = "潛行速度" - lore = ["潛行速度"] +name = "潛行加速" +description = "潛行時獲得速度加成" +lore1 = "潛行速度" +lore = ["潛行速度"] [stealth.ender_veil] - name = "終界面紗" - description = "不再需要南瓜頭來防止終界使者攻擊" - lore1 = "潛行時防止終界使者攻擊" - lore2 = "防止所有終界使者攻擊" - lore = ["潛行時防止終界使者攻擊", "防止所有終界使者攻擊"] +name = "終界面紗" +description = "不再需要南瓜頭來防止終界使者攻擊" +lore1 = "潛行時防止終界使者攻擊" +lore2 = "防止所有終界使者攻擊" +lore = ["潛行時防止終界使者攻擊", "防止所有終界使者攻擊"] # sword [sword] [sword.machete] - name = "砍刀" - description = "輕鬆砍穿叢林植物!" - lore1 = "砍伐半徑" - lore2 = "砍伐冷卻" - lore3 = "工具磨損" - lore = ["砍伐半徑", "砍伐冷卻", "工具磨損"] +name = "砍刀" +description = "輕鬆砍穿叢林植物!" +lore1 = "砍伐半徑" +lore2 = "砍伐冷卻" +lore3 = "工具磨損" +lore = ["砍伐半徑", "砍伐冷卻", "工具磨損"] [sword.bloody_blade] - name = "血刃" - description = "用劍攻擊會造成流血效果!" - lore1 = "用劍攻擊生物時造成流血" - lore2 = "流血持續時間" - lore3 = "流血冷卻" - lore = ["用劍攻擊生物時造成流血", "流血持續時間", "流血冷卻"] +name = "血刃" +description = "用劍攻擊會造成流血效果!" +lore1 = "用劍攻擊生物時造成流血" +lore2 = "流血持續時間" +lore3 = "流血冷卻" +lore = ["用劍攻擊生物時造成流血", "流血持續時間", "流血冷卻"] [sword.poisoned_blade] - name = "淬毒之刃" - description = "用劍攻擊會造成中毒效果!" - lore1 = "用劍攻擊生物時造成中毒" - lore2 = "中毒持續時間" - lore3 = "中毒冷卻" - lore = ["用劍攻擊生物時造成中毒", "中毒持續時間", "中毒冷卻"] +name = "淬毒之刃" +description = "用劍攻擊會造成中毒效果!" +lore1 = "用劍攻擊生物時造成中毒" +lore2 = "中毒持續時間" +lore3 = "中毒冷卻" +lore = ["用劍攻擊生物時造成中毒", "中毒持續時間", "中毒冷卻"] # taming [taming] [taming.damage] - name = "馴獸傷害" - description = "提高馴養動物的傷害輸出。" - lore1 = "傷害增加" - lore = ["傷害增加"] +name = "馴獸傷害" +description = "提高馴養動物的傷害輸出。" +lore1 = "傷害增加" +lore = ["傷害增加"] [taming.health] - name = "馴獸生命" - description = "提高馴養動物的生命值。" - lore1 = "生命增加" - lore = ["生命增加"] +name = "馴獸生命" +description = "提高馴養動物的生命值。" +lore1 = "生命增加" +lore = ["生命增加"] [taming.regeneration] - name = "馴獸再生" - description = "提高馴養動物的再生速度。" - lore1 = "生命/秒" - lore = ["生命/秒"] +name = "馴獸再生" +description = "提高馴養動物的再生速度。" +lore1 = "生命/秒" +lore = ["生命/秒"] # tragoul [tragoul] [tragoul.thorns] - name = "荊棘" - description = "將傷害反彈給攻擊者!" - lore1 = "受擊時反彈的傷害" - lore = ["受擊時反彈的傷害"] +name = "荊棘" +description = "將傷害反彈給攻擊者!" +lore1 = "受擊時反彈的傷害" +lore = ["受擊時反彈的傷害"] [tragoul.globe] - name = "痛苦之球" - description = "根據你周圍敵人的數量分配你造成的傷害!" - lore1 = "你周圍的敵人越多,對每個敵人造成的傷害越少" - lore2 = "範圍:" - lore3 = "對所有實體的額外傷害:" - lore = ["你周圍的敵人越多,對每個敵人造成的傷害越少", "範圍:", "對所有實體的額外傷害:"] +name = "痛苦之球" +description = "根據你周圍敵人的數量分配你造成的傷害!" +lore1 = "你周圍的敵人越多,對每個敵人造成的傷害越少" +lore2 = "範圍:" +lore3 = "對所有實體的額外傷害:" +lore = ["你周圍的敵人越多,對每個敵人造成的傷害越少", "範圍:", "對所有實體的額外傷害:"] [tragoul.healing] - name = "痛苦意志" - description = "根據你造成的傷害恢復生命!" - lore1 = "傷害別人的感覺從未如此美好!從造成的傷害中治療" - lore2 = "有 3 秒的傷害窗口用於治療和 1 秒的冷卻時間" - lore3 = "每傷害百分比的治療量:" - lore = ["傷害別人的感覺從未如此美好!從造成的傷害中治療", "有 3 秒的傷害窗口用於治療和 1 秒的冷卻時間", "每傷害百分比的治療量:"] +name = "痛苦意志" +description = "根據你造成的傷害恢復生命!" +lore1 = "傷害別人的感覺從未如此美好!從造成的傷害中治療" +lore2 = "有 3 秒的傷害窗口用於治療和 1 秒的冷卻時間" +lore3 = "每傷害百分比的治療量:" +lore = ["傷害別人的感覺從未如此美好!從造成的傷害中治療", "有 3 秒的傷害窗口用於治療和 1 秒的冷卻時間", "每傷害百分比的治療量:"] [tragoul.lance] - name = "屍矛" - description = "擊殺敵人或由技能擊殺敵人時,產生一把長矛對附近敵人造成傷害!" - lore1 = "長矛會從你擊殺的一切中射出,且如果此技能擊殺了敵人也會觸發。" - lore2 = "犧牲一部分生命來製造長矛(這可能會殺死你)" - lore3 = "最大長矛數:1 + " - lore = ["長矛會從你擊殺的一切中射出,且如果此技能擊殺了敵人也會觸發。", "犧牲一部分生命來製造長矛(這可能會殺死你)", "最大長矛數:1 + "] +name = "屍矛" +description = "擊殺敵人或由技能擊殺敵人時,產生一把長矛對附近敵人造成傷害!" +lore1 = "長矛會從你擊殺的一切中射出,且如果此技能擊殺了敵人也會觸發。" +lore2 = "犧牲一部分生命來製造長矛(這可能會殺死你)" +lore3 = "最大長矛數:1 + " +lore = ["長矛會從你擊殺的一切中射出,且如果此技能擊殺了敵人也會觸發。", "犧牲一部分生命來製造長矛(這可能會殺死你)", "最大長矛數:1 + "] # unarmed [unarmed] [unarmed.glass_cannon] - name = "玻璃大砲" - description = "護甲值越低,空手傷害越高" - lore1 = "x 傷害(0 護甲時)" - lore2 = "每級額外傷害" - lore = ["x 傷害(0 護甲時)", "每級額外傷害"] +name = "玻璃大砲" +description = "護甲值越低,空手傷害越高" +lore1 = "x 傷害(0 護甲時)" +lore2 = "每級額外傷害" +lore = ["x 傷害(0 護甲時)", "每級額外傷害"] [unarmed.power] - name = "空手之力" - description = "提升空手傷害" - lore1 = "傷害" - lore = ["傷害"] +name = "空手之力" +description = "提升空手傷害" +lore1 = "傷害" +lore = ["傷害"] [unarmed.sucker_punch] - name = "偷襲拳" - description = "疾跑時出拳,更加致命。" - lore1 = "傷害" - lore2 = "出拳時傷害隨移動速度增加" - lore = ["傷害", "出拳時傷害隨移動速度增加"] +name = "偷襲拳" +description = "疾跑時出拳,更加致命。" +lore1 = "傷害" +lore2 = "出拳時傷害隨移動速度增加" +lore = ["傷害", "出拳時傷害隨移動速度增加"] From 47fbd95914f8add8ecbaa4a6f0732021fcbcff65 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Tue, 17 Feb 2026 23:57:47 -0500 Subject: [PATCH 14/25] g --- src/main/java/art/arcane/adapt/Adapt.java | 38 ++- .../adapt/api/adaptation/Adaptation.java | 12 + .../adaptation/AdaptationRuntimeGuards.java | 61 +++- .../api/telemetry/AbilityCheckTelemetry.java | 196 ++++++++++-- .../arcane/adapt/command/CommandDebug.java | 29 +- .../adaptation/agility/AgilityArmorUp.java | 3 +- .../adaptation/agility/AgilityWallJump.java | 3 +- .../adaptation/agility/AgilityWindUp.java | 3 +- .../ranged/RangedTrajectorySight.java | 2 +- .../adaptation/rift/RiftVoidMagnet.java | 6 +- .../seaborrne/SeabornePressureDiver.java | 2 +- .../adaptation/tragoul/TragoulBloodPact.java | 2 +- .../unarmed/UnarmedBatteringCharge.java | 5 +- .../util/AdvancementUtils.java | 296 +++++++++++++++++- 14 files changed, 609 insertions(+), 49 deletions(-) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index d4444e134..326f2db06 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -64,6 +64,8 @@ import java.io.*; import java.lang.annotation.Annotation; import java.net.URL; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.text.MessageFormat; import java.util.HashMap; import java.util.List; @@ -200,6 +202,32 @@ public static int getJavaVersion() { return Integer.parseInt(version); } + private static String getServerVersion() { + String version = Bukkit.getVersion(); + int mcMarkerIndex = version.indexOf(" (MC:"); + if (mcMarkerIndex != -1) { + version = version.substring(0, mcMarkerIndex); + } + return version; + } + + private static String getStartupDate() { + return LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); + } + + private static String getReleaseTrain(String version) { + String value = version; + int suffixIndex = value.indexOf('-'); + if (suffixIndex >= 0) { + value = value.substring(0, suffixIndex); + } + String[] split = value.split("\\."); + if (split.length >= 2) { + return split[0] + "." + split[1]; + } + return value; + } + public static void printInformation() { debug("XP Curve: " + AdaptConfig.get().getXpCurve()); debug("XP/Level base: " + AdaptConfig.get().getPlayerXpPerSkillLevelUpBase()); @@ -477,11 +505,11 @@ private void startupPrint() { int game = r.nextInt(100); if (game < 90) { Adapt.info("\n" + C.DARK_GRAY + " █████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "█████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "████████" + C.DARK_RED + "╗\n" + - C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + - C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Java Version: " + C.DARK_RED + getJavaVersion() + " \n" + - C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║ \n" + - C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ \n"); + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.DARK_RED + " Adapt, " + C.RED + "Abilities Refined" + C.RED + "[" + getReleaseTrain(instance.getDescription().getVersion()) + " RELEASE]\n" + + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Server: " + C.DARK_RED + getServerVersion() + "\n" + + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ " + C.WHITE + " Java: " + C.DARK_RED + getJavaVersion() + C.WHITE + " | Date: " + C.DARK_RED + getStartupDate() + "\n"); } else { info(SecretSplash.getSecretSplash().getRandom()); } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 93b407460..34ff3469e 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -748,6 +748,18 @@ default int getLevel(Player p) { return AdaptationRuntimeGuards.getLevel(this, p); } + default int getLevel(AdaptPlayer adaptPlayer) { + return AdaptationRuntimeGuards.getLevel(this, adaptPlayer); + } + + default boolean hasLearnedAdaptation(AdaptPlayer adaptPlayer) { + return getLevel(adaptPlayer) > 0; + } + + default List learnedCandidates(long now) { + return AdaptationRuntimeGuards.learnedCandidates(this, now); + } + /** * Learned level normalized to 0..1. */ diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index 00852d97e..20d42bdff 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -50,9 +50,11 @@ final class AdaptationRuntimeGuards { private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); private static final Map PROTECTOR_CACHE = new ConcurrentHashMap<>(); private static final Map USAGE_CONFLICT_CACHE = new ConcurrentHashMap<>(); + private static final Map LEARNED_CANDIDATE_CACHE = new ConcurrentHashMap<>(); private static final int ACTIVE_LEVEL_CACHE_SOFT_LIMIT = 16_384; private static final int ACTIVE_LEVEL_CACHE_SWEEP_INTERVAL_TICKS = 64; private static final int ACTIVE_LEVEL_CACHE_RETENTION_TICKS = 2; + private static final long LEARNED_CANDIDATE_REFRESH_MS = 250L; private static volatile long lastActiveCacheSweepTick = Long.MIN_VALUE; private AdaptationRuntimeGuards() { @@ -508,11 +510,19 @@ static int getActiveLevel(Adaptation adaptation, Player p) { String key = cacheKey(adaptation, p); ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); if (cached != null && cached.tick() == tick && cached.learnedLevel() == learnedLevel) { + AbilityCheckTelemetry.recordCacheHit(); return cached.level(); } + AbilityCheckTelemetry.recordCacheMiss(); AbilityCheckTelemetry.recordCheckAttempt(); - int level = resolveActiveLevelUncached(adaptation, p, learnedLevel); + long startNs = System.nanoTime(); + int level; + try { + level = resolveActiveLevelUncached(adaptation, p, learnedLevel); + } finally { + AbilityCheckTelemetry.recordCheckTimingNanos(System.nanoTime() - startNs); + } ACTIVE_LEVEL_CACHE.put(key, new ActiveLevelCacheEntry(tick, learnedLevel, level)); sweepActiveLevelCache(tick); @@ -549,13 +559,57 @@ static int getLevel(Adaptation adaptation, Player p) { return 0; } AdaptPlayer adaptPlayer = adaptation.getPlayer(p); - PlayerSkillLine line = adaptPlayer.getData().getSkillLine(adaptation.getSkill().getName()); + return getLevel(adaptation, adaptPlayer); + } + + static int getLevel(Adaptation adaptation, AdaptPlayer adaptPlayer) { + if (adaptation == null || adaptPlayer == null) { + return 0; + } + if (!adaptation.isEnabled()) { + return 0; + } + if (!adaptation.getSkill().isEnabled()) { + return 0; + } + + PlayerData data = adaptPlayer.getData(); + if (data == null) { + return 0; + } + + PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); if (line == null) { return 0; } return line.getAdaptationLevel(adaptation.getName()); } + static List learnedCandidates(Adaptation adaptation, long now) { + if (adaptation == null) { + return List.of(); + } + + String key = adaptation.getName(); + LearnedCandidateCacheEntry cached = LEARNED_CANDIDATE_CACHE.get(key); + if (cached != null && now - cached.refreshedAtMs() <= LEARNED_CANDIDATE_REFRESH_MS) { + return cached.players(); + } + + List online = adaptation.getServer().getOnlineAdaptPlayerSnapshot(); + ArrayList candidates = new ArrayList<>(online.size()); + for (AdaptPlayer adaptPlayer : online) { + if (getLevel(adaptation, adaptPlayer) <= 0) { + continue; + } + candidates.add(adaptPlayer); + } + + List immutable = Collections.unmodifiableList(new ArrayList<>(candidates)); + LEARNED_CANDIDATE_CACHE.put(key, new LearnedCandidateCacheEntry(now, immutable)); + return immutable; + } + static F getStorage(Adaptation adaptation, Player p, String key, F defaultValue) { PlayerData data = adaptation.getPlayer(p).getData(); PlayerSkillLine line = data.getSkillLineNullable(adaptation.getSkill().getName()); @@ -750,4 +804,7 @@ private record ProtectorCacheEntry(int signature, Set protectors) { private record UsageConflictCacheEntry(int signature, Set denied) { } + + private record LearnedCandidateCacheEntry(long refreshedAtMs, List players) { + } } diff --git a/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java index a25bbbfb9..716aca478 100644 --- a/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java +++ b/src/main/java/art/arcane/adapt/api/telemetry/AbilityCheckTelemetry.java @@ -18,66 +18,200 @@ package art.arcane.adapt.api.telemetry; -import java.util.ArrayDeque; +import java.util.concurrent.atomic.AtomicLongArray; public final class AbilityCheckTelemetry { - private static final long WINDOW_MS = 60_000L; - private static final Object LOCK = new Object(); - private static final ArrayDeque checkOps = new ArrayDeque<>(); - private static final ArrayDeque successfulOps = new ArrayDeque<>(); + private static final int WINDOW_SECONDS = 60; + private static final AtomicLongArray checkOps = new AtomicLongArray(WINDOW_SECONDS); + private static final AtomicLongArray successfulOps = new AtomicLongArray(WINDOW_SECONDS); + private static final AtomicLongArray cacheHits = new AtomicLongArray(WINDOW_SECONDS); + private static final AtomicLongArray cacheMisses = new AtomicLongArray(WINDOW_SECONDS); + private static final AtomicLongArray timingMicros = new AtomicLongArray(WINDOW_SECONDS); + private static final AtomicLongArray timingSamples = new AtomicLongArray(WINDOW_SECONDS); private AbilityCheckTelemetry() { } public static void recordCheckAttempt() { long now = System.currentTimeMillis(); - synchronized (LOCK) { - checkOps.addLast(now); - trim(checkOps, now); - trim(successfulOps, now); - } + increment(checkOps, now, 1); } public static void recordSuccessfulCheck() { long now = System.currentTimeMillis(); - synchronized (LOCK) { - successfulOps.addLast(now); - trim(checkOps, now); - trim(successfulOps, now); + increment(successfulOps, now, 1); + } + + public static void recordCacheHit() { + long now = System.currentTimeMillis(); + increment(cacheHits, now, 1); + } + + public static void recordCacheMiss() { + long now = System.currentTimeMillis(); + increment(cacheMisses, now, 1); + } + + public static void recordCheckTimingNanos(long nanos) { + if (nanos <= 0L) { + return; } + + long now = System.currentTimeMillis(); + long microsLong = Math.min(Integer.MAX_VALUE, Math.max(1L, nanos / 1_000L)); + increment(timingMicros, now, (int) microsLong); + increment(timingSamples, now, 1); } public static long checksPerMinute(long now) { - synchronized (LOCK) { - trim(checkOps, now); - return checkOps.size(); - } + return sumWindow(checkOps, now); } public static long successfulChecksPerMinute(long now) { - synchronized (LOCK) { - trim(successfulOps, now); - return successfulOps.size(); + return sumWindow(successfulOps, now); + } + + public static long checksPerSecond(long now) { + return currentSecondValue(checkOps, now); + } + + public static long successfulChecksPerSecond(long now) { + return currentSecondValue(successfulOps, now); + } + + public static long cacheHitsPerMinute(long now) { + return sumWindow(cacheHits, now); + } + + public static long cacheMissesPerMinute(long now) { + return sumWindow(cacheMisses, now); + } + + public static double cacheHitRatio(long now) { + long hits = cacheHitsPerMinute(now); + long misses = cacheMissesPerMinute(now); + long total = hits + misses; + if (total <= 0L) { + return 0D; } + + return hits / (double) total; } - public static double checksPerTick(long now) { - synchronized (LOCK) { - trim(checkOps, now); - return checkOps.size() / 1200D; + public static double averageCheckMicros(long now) { + long samples = sumWindow(timingSamples, now); + if (samples <= 0L) { + return 0D; + } + + long micros = sumWindow(timingMicros, now); + return micros / (double) samples; + } + + public static double estimatedTimingMillisPerSecond(long now) { + double checksPerSecond = checksPerSecond(now); + if (checksPerSecond <= 0D) { + return 0D; + } + + double avgMicros = averageCheckMicros(now); + if (avgMicros <= 0D) { + return 0D; + } + + return (checksPerSecond * avgMicros) / 1_000D; + } + + public static double timingBudgetPercent(long now) { + double millisPerSecond = estimatedTimingMillisPerSecond(now); + if (millisPerSecond <= 0D) { + return 0D; } + + double percent = (millisPerSecond / 50D) * 100D; + if (!Double.isFinite(percent)) { + return 0D; + } + return Math.max(0D, percent); + } + + public static double checksPerTick(long now) { + return checksPerMinute(now) / 1200D; } public static void clear() { - synchronized (LOCK) { - checkOps.clear(); - successfulOps.clear(); + for (int i = 0; i < WINDOW_SECONDS; i++) { + checkOps.set(i, 0L); + successfulOps.set(i, 0L); + cacheHits.set(i, 0L); + cacheMisses.set(i, 0L); + timingMicros.set(i, 0L); + timingSamples.set(i, 0L); } } - private static void trim(ArrayDeque samples, long now) { - while (!samples.isEmpty() && (now - samples.peekFirst()) > WINDOW_MS) { - samples.removeFirst(); + private static void increment(AtomicLongArray buckets, long now, int delta) { + if (delta <= 0) { + return; + } + + long epochSecondLong = now / 1_000L; + int epochSecond = (int) epochSecondLong; + int slot = (int) (epochSecondLong % WINDOW_SECONDS); + int safeDelta = Math.max(0, delta); + while (true) { + long packed = buckets.get(slot); + int slotSecond = unpackSecond(packed); + long slotValue = Integer.toUnsignedLong(unpackValue(packed)); + long nextValueLong = slotSecond == epochSecond + ? Math.min(Integer.MAX_VALUE, slotValue + safeDelta) + : Math.min(Integer.MAX_VALUE, safeDelta); + long next = pack(epochSecond, (int) nextValueLong); + if (buckets.compareAndSet(slot, packed, next)) { + return; + } } } + + private static long sumWindow(AtomicLongArray buckets, long now) { + long epochSecondLong = now / 1_000L; + int epochSecond = (int) epochSecondLong; + long total = 0L; + for (int i = 0; i < WINDOW_SECONDS; i++) { + long packed = buckets.get(i); + int slotSecond = unpackSecond(packed); + long age = Integer.toUnsignedLong(epochSecond - slotSecond); + if (age >= WINDOW_SECONDS) { + continue; + } + + total += Integer.toUnsignedLong(unpackValue(packed)); + } + return total; + } + + private static long currentSecondValue(AtomicLongArray buckets, long now) { + long epochSecondLong = now / 1_000L; + int epochSecond = (int) epochSecondLong; + int slot = (int) (epochSecondLong % WINDOW_SECONDS); + long packed = buckets.get(slot); + if (unpackSecond(packed) != epochSecond) { + return 0L; + } + return Integer.toUnsignedLong(unpackValue(packed)); + } + + private static long pack(int epochSecond, int value) { + long epochPart = Integer.toUnsignedLong(epochSecond) << 32; + long valuePart = Integer.toUnsignedLong(value); + return epochPart | valuePart; + } + + private static int unpackSecond(long packed) { + return (int) (packed >>> 32); + } + + private static int unpackValue(long packed) { + return (int) packed; + } } diff --git a/src/main/java/art/arcane/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java index 10d9081c2..211cbc572 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -2,6 +2,7 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.telemetry.AbilityCheckTelemetry; import art.arcane.adapt.util.command.FConst; import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.volmlib.util.director.DirectorOrigin; @@ -13,6 +14,7 @@ import org.bukkit.entity.Player; import java.util.List; +import java.util.Locale; @Director(name = "debug", origin = DirectorOrigin.BOTH, description = "Adapt Debug Command", aliases = {"dev"}) public class CommandDebug { @@ -96,6 +98,30 @@ public void perf( return; } + long now = System.currentTimeMillis(); + long checksPerSecond = AbilityCheckTelemetry.checksPerSecond(now); + long successfulPerSecond = AbilityCheckTelemetry.successfulChecksPerSecond(now); + long checksPerMinute = AbilityCheckTelemetry.checksPerMinute(now); + long successfulPerMinute = AbilityCheckTelemetry.successfulChecksPerMinute(now); + long cacheHits = AbilityCheckTelemetry.cacheHitsPerMinute(now); + long cacheMisses = AbilityCheckTelemetry.cacheMissesPerMinute(now); + double cacheHitRatio = AbilityCheckTelemetry.cacheHitRatio(now) * 100D; + double averageMicros = AbilityCheckTelemetry.averageCheckMicros(now); + double timingMillisPerSecond = AbilityCheckTelemetry.estimatedTimingMillisPerSecond(now); + double timingBudgetPercent = AbilityCheckTelemetry.timingBudgetPercent(now); + + FConst.info("Ability checks: " + checksPerSecond + "/s (" + checksPerMinute + "/m)").send(BukkitDirectorContext.sender()); + FConst.info("Successful checks: " + successfulPerSecond + "/s (" + successfulPerMinute + "/m)").send(BukkitDirectorContext.sender()); + FConst.info("Active-level cache hit ratio: " + + String.format(Locale.US, "%.1f%%", cacheHitRatio) + + " (" + cacheHits + " hit, " + cacheMisses + " miss)") + .send(BukkitDirectorContext.sender()); + FConst.info("Ability check timing budget: " + + String.format(Locale.US, "%.2f%%", timingBudgetPercent) + + " (" + String.format(Locale.US, "%.2fms/s", timingMillisPerSecond) + + ", " + String.format(Locale.US, "%.1fus/check", averageMicros) + ")") + .send(BukkitDirectorContext.sender()); + List lines = Adapt.instance.getTicker().topMetrics(top); long windowMs = Adapt.instance.getTicker().getMetricsWindowMs(); FConst.success("Ticker window: " + windowMs + "ms").send(BukkitDirectorContext.sender()); @@ -107,7 +133,8 @@ public void perf( if (reset) { Adapt.instance.getTicker().resetMetrics(); - FConst.success("Ticker metrics reset.").send(BukkitDirectorContext.sender()); + AbilityCheckTelemetry.clear(); + FConst.success("Ticker and ability telemetry reset.").send(BukkitDirectorContext.sender()); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java index 374d3e420..d8977129d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityArmorUp.java @@ -102,7 +102,8 @@ private double getWindupArmor(double factor) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java index 8d6ee99b8..8453177f1 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWallJump.java @@ -145,7 +145,8 @@ public void on(PlayerMoveEvent e) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java index f75ed3c08..eae5d6a19 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/agility/AgilityWindUp.java @@ -138,7 +138,8 @@ private double getWindupSpeed(double factor) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java index 655b5e824..3440790b4 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/ranged/RangedTrajectorySight.java @@ -260,7 +260,7 @@ private void refreshPreviewCandidates(long now) { } lastPreviewCandidateRefreshMs = now; - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index aec735cd6..2c4e76e19 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -80,8 +80,12 @@ public void addStats(int level, Element v) { @Override public void onTick() { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } int level = getActiveLevel(p, Player::isSneaking); if (level <= 0 || p.getTicksLived() % getPulseTicks(level) != 0) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java index 6f428fa5d..1174e8cfa 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/seaborrne/SeabornePressureDiver.java @@ -106,7 +106,7 @@ public void on(EntityDamageEvent e) { @Override public void onTick() { long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java index cb8a17250..2fa45271a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulBloodPact.java @@ -261,7 +261,7 @@ public void onTick() { } private void applySpeedBursts(long now) { - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); if (p == null || !p.isOnline()) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java index e5d13c694..f6f510b7f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedBatteringCharge.java @@ -208,8 +208,11 @@ private int getCooldownTicks(int level) { @Override public void onTick() { long now = System.currentTimeMillis(); - for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : getServer().getOnlineAdaptPlayerSnapshot()) { + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } UUID id = p.getUniqueId(); int level = getActiveLevel(p); if (level <= 0) { diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java index 4dae9d225..a0cec1988 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java @@ -34,11 +34,13 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Consumer; public class AdvancementUtils { @@ -276,11 +278,26 @@ public static void runSync(@NotNull AdvancementMain main, long delay, @NotNull R public static void runSync(@NotNull Plugin plugin, long delay, @NotNull Runnable runnable) { Preconditions.checkNotNull(plugin, "Plugin is null."); Preconditions.checkNotNull(runnable, "Runnable is null."); + if (!plugin.isEnabled()) { + return; + } + int safeDelay = sanitizeDelay(delay); if (scheduleFoliaSync(plugin, runnable, safeDelay)) { return; } + if (hasFoliaScheduler()) { + if (safeDelay <= 0 && FoliaScheduler.isPrimaryThread()) { + runnable.run(); + return; + } + + plugin.getLogger().warning("Failed to schedule advancement sync task on Folia for plugin " + plugin.getName() + + " (" + safeDelay + "t)."); + return; + } + if (safeDelay <= 0) { J.s(runnable); } else { @@ -302,17 +319,292 @@ private static int sanitizeDelay(long delay) { private static boolean scheduleFoliaSync(@NotNull Plugin plugin, @NotNull Runnable runnable, int safeDelay) { Player player = extractPlayer(runnable); - if (player != null && player.isOnline() && FoliaScheduler.runEntity(plugin, player, runnable, safeDelay)) { + if (player != null && player.isOnline()) { + if (FoliaScheduler.runEntity(plugin, player, runnable, safeDelay)) { + return true; + } + + if (scheduleEntityReflective(plugin, player, runnable, safeDelay)) { + return true; + } + } + + if (FoliaScheduler.runGlobal(plugin, runnable, safeDelay)) { return true; } - return FoliaScheduler.runGlobal(plugin, runnable, safeDelay); + return scheduleGlobalReflective(plugin, runnable, safeDelay); } private static boolean hasFoliaScheduler() { return FoliaScheduler.isFolia(Bukkit.getServer()); } + private static boolean scheduleEntityReflective(@NotNull Plugin plugin, @NotNull Player player, @NotNull Runnable runnable, int safeDelay) { + Object scheduler = invokeNoThrow(player, "getScheduler", new Class[0]); + if (scheduler == null) { + return false; + } + + Runnable retired = () -> { + }; + Consumer consumer = task -> runnable.run(); + long safeLongDelay = Math.max(0L, safeDelay); + if (safeLongDelay <= 0L) { + Object immediateExecuted = invokeNoThrow( + scheduler, + "execute", + new Class[]{Plugin.class, Runnable.class, Runnable.class, long.class}, + plugin, + runnable, + retired, + 0L + ); + if (immediateExecuted instanceof Boolean done) { + return done; + } + + if (invokeVoidNoThrow( + scheduler, + "run", + new Class[]{Plugin.class, Consumer.class, Runnable.class}, + plugin, + consumer, + retired + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "run", + new Class[]{Plugin.class, Runnable.class, Runnable.class}, + plugin, + runnable, + retired + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Consumer.class, Runnable.class, long.class}, + plugin, + consumer, + retired, + 1L + )) { + return true; + } + + return invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Runnable.class, Runnable.class, long.class}, + plugin, + runnable, + retired, + 1L + ); + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Consumer.class, Runnable.class, long.class}, + plugin, + consumer, + retired, + safeLongDelay + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Consumer.class, Runnable.class, int.class}, + plugin, + consumer, + retired, + safeDelay + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Runnable.class, Runnable.class, long.class}, + plugin, + runnable, + retired, + safeLongDelay + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Runnable.class, Runnable.class, int.class}, + plugin, + runnable, + retired, + safeDelay + )) { + return true; + } + + Object delayedExecuted = invokeNoThrow( + scheduler, + "execute", + new Class[]{Plugin.class, Runnable.class, Runnable.class, long.class}, + plugin, + runnable, + retired, + safeLongDelay + ); + return delayedExecuted instanceof Boolean done && done; + } + + private static boolean scheduleGlobalReflective(@NotNull Plugin plugin, @NotNull Runnable runnable, int safeDelay) { + Object scheduler = getGlobalScheduler(plugin); + if (scheduler == null) { + return false; + } + + Consumer consumer = task -> runnable.run(); + long safeLongDelay = Math.max(0L, safeDelay); + if (safeLongDelay <= 0L) { + if (invokeVoidNoThrow( + scheduler, + "execute", + new Class[]{Plugin.class, Runnable.class}, + plugin, + runnable + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "run", + new Class[]{Plugin.class, Consumer.class}, + plugin, + consumer + )) { + return true; + } + + return invokeVoidNoThrow( + scheduler, + "run", + new Class[]{Plugin.class, Runnable.class}, + plugin, + runnable + ); + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Consumer.class, long.class}, + plugin, + consumer, + safeLongDelay + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Consumer.class, int.class}, + plugin, + consumer, + safeDelay + )) { + return true; + } + + if (invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Runnable.class, long.class}, + plugin, + runnable, + safeLongDelay + )) { + return true; + } + + return invokeVoidNoThrow( + scheduler, + "runDelayed", + new Class[]{Plugin.class, Runnable.class, int.class}, + plugin, + runnable, + safeDelay + ); + } + + @Nullable + private static Object getGlobalScheduler(@NotNull Plugin plugin) { + Object serverScheduler = invokeNoThrow(plugin.getServer(), "getGlobalRegionScheduler", new Class[0]); + if (serverScheduler != null) { + return serverScheduler; + } + + return invokeStaticNoThrow(Bukkit.class, "getGlobalRegionScheduler", new Class[0]); + } + + @Nullable + private static Object invokeStaticNoThrow( + @NotNull Class owner, + @NotNull String methodName, + @NotNull Class[] parameterTypes, + Object... args + ) { + try { + Method method = owner.getMethod(methodName, parameterTypes); + return method.invoke(null, args); + } catch (Throwable ignored) { + return null; + } + } + + @Nullable + private static Object invokeNoThrow( + @NotNull Object target, + @NotNull String methodName, + @NotNull Class[] parameterTypes, + Object... args + ) { + try { + Method method = target.getClass().getMethod(methodName, parameterTypes); + return method.invoke(target, args); + } catch (Throwable ignored) { + return null; + } + } + + private static boolean invokeVoidNoThrow( + @NotNull Object target, + @NotNull String methodName, + @NotNull Class[] parameterTypes, + Object... args + ) { + try { + Method method = target.getClass().getMethod(methodName, parameterTypes); + method.invoke(target, args); + return true; + } catch (Throwable ignored) { + return false; + } + } + @Nullable private static Player extractPlayer(@NotNull Runnable runnable) { Class current = runnable.getClass(); From 6b054619c6b286aa2ea0d72de6c535c5ff58f48e Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 18 Feb 2026 15:07:10 -0500 Subject: [PATCH 15/25] v --- build.gradle.kts | 39 +- src/main/java/art/arcane/adapt/Adapt.java | 3 +- .../adapt/api/adaptation/Adaptation.java | 8 +- .../api/adaptation/AdaptationGuiSupport.java | 2 +- .../adaptation/AdaptationRuntimeGuards.java | 27 +- .../adapt/api/runtime/AdaptationGate.java | 2 +- .../art/arcane/adapt/api/skill/Skill.java | 10 +- .../adapt/api/skill/SkillGuiSupport.java | 2 +- .../adapt/api/skill/SkillRuntimeGuards.java | 18 +- .../arcane/adapt/command/CommandAdapt.java | 4 +- .../arcane/adapt/command/CommandDebug.java | 4 +- .../arcane/adapt/content/gui/SkillsGui.java | 2 +- .../adapt/content/skill/SkillTragOul.java | 2 +- src/main/resources/plugin.yml | 774 +++++++++--------- 14 files changed, 440 insertions(+), 457 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3b3f11847..67c205361 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,21 +21,18 @@ import io.github.slimjar.resolver.data.Mirror import org.gradle.api.plugins.JavaPluginExtension import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers -import xyz.jpenilla.runpaper.task.RunServer import kotlin.system.exitProcess plugins { `java-library` alias(libs.plugins.lombok) alias(libs.plugins.shadow) - alias(libs.plugins.runPaper) - alias(libs.plugins.runVelocity) alias(libs.plugins.idea) alias(libs.plugins.slimjar) } -version = "2.0.0-1.20.2-1.21.11-Dev1" -val apiVersion = "1.20" +version = "2.0.0-1.21.11" +val apiVersion = "1.21" val main = "art.arcane.adapt.Adapt" val volmLibCoordinate: String = providers.gradleProperty("volmLibCoordinate") .orElse("com.github.VolmitSoftware:VolmLib:master-SNAPSHOT") @@ -56,38 +53,6 @@ registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGi registerCustomOutputTaskUnix("the456gamer", "/home/the456gamer/projects/minecraft/adapt-testserver/plugins/update/", false) // ============================================================== -val supported = listOf("1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.8", "1.21.10", "1.21.11") -val jdk = listOf("1.20.2", "1.20.4") - -val MIN_HEAP_SIZE = "2G" -val MAX_HEAP_SIZE = "8G" -//Valid values are: none, truecolor, indexed256, indexed16, indexed8 -val COLOR = "truecolor" - -supported.forEach { version -> - tasks.register("runServer-$version") { - group = "servers" - minecraftVersion(version) - minHeapSize = MIN_HEAP_SIZE - maxHeapSize = MAX_HEAP_SIZE - systemProperty("disable.watchdog", "") - systemProperty("net.kyori.ansi.colorLevel", COLOR) - systemProperty("com.mojang.eula.agree", true) - pluginJars(tasks.shadowJar.flatMap { it.archiveFile} ) - runDirectory.convention(layout.buildDirectory.dir("run/$version")) - - if (!jdk.contains(version)) { - javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(21)} - } - } -} - -tasks.runVelocity { - group = "servers" - velocityVersion(libs.versions.velocity.get()) - runDirectory.convention(layout.buildDirectory.dir("run/velocity")) -} - /** * Expand properties into plugin yml */ diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 326f2db06..9c83dc554 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -501,6 +501,7 @@ private void startupPrint() { if (!AdaptConfig.get().isSplashScreen()) { return; } + String supportedMcVersion = "1.21.11"; Random r = new Random(); int game = r.nextInt(100); if (game < 90) { @@ -508,7 +509,7 @@ private void startupPrint() { C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.DARK_RED + " Adapt, " + C.RED + "Abilities Refined" + C.RED + "[" + getReleaseTrain(instance.getDescription().getVersion()) + " RELEASE]\n" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + - C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Server: " + C.DARK_RED + getServerVersion() + "\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Server: " + C.DARK_RED + getServerVersion() + C.WHITE + " | MC Support: " + C.DARK_RED + supportedMcVersion + "\n" + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ " + C.WHITE + " Java: " + C.DARK_RED + getJavaVersion() + C.WHITE + " | Date: " + C.DARK_RED + getStartupDate() + "\n"); } else { info(SecretSplash.getSecretSplash().getRandom()); diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 34ff3469e..79b9f5e44 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -213,11 +213,11 @@ default boolean canUse(Player player) { } /** - * Returns true when this player is blacklisted from this adaptation via + * Returns true when this player has use permission for this adaptation via * permission. */ - default boolean hasBlacklistPermission(Player p, Adaptation a) { - return AdaptationRuntimeGuards.hasBlacklistPermission(this, p, a); + default boolean hasUsePermission(Player p, Adaptation a) { + return AdaptationRuntimeGuards.hasUsePermission(this, p, a); } /** @@ -875,7 +875,7 @@ default CustomModel getModel(int level) { } /** - * Opens adaptation GUI and optionally applies blacklist permission checks. + * Opens adaptation GUI and optionally applies use permission checks. */ default boolean openGui(Player player, boolean checkPermissions) { return AdaptationGuiSupport.openGui(this, player, checkPermissions); diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index a218078fd..9299cdf80 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -167,7 +167,7 @@ static CustomModel getModel(Adaptation adaptation, int level) { } static boolean openGui(Adaptation adaptation, Player player, boolean checkPermissions) { - if (checkPermissions && adaptation.hasBlacklistPermission(player, adaptation)) { + if (checkPermissions && !adaptation.hasUsePermission(player, adaptation)) { return false; } else { openGui(adaptation, player); diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index 20d42bdff..248875a9a 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -194,15 +194,26 @@ static boolean canUse(Adaptation adaptation, Player player) { return canUse(adaptation, adaptation.getPlayer(player)); } - static boolean hasBlacklistPermission(Adaptation adaptation, Player p, Adaptation targetAdaptation) { - if (p.isOp()) { + static boolean hasUsePermission(Adaptation adaptation, Player p, Adaptation targetAdaptation) { + if (p == null) { return false; } + if (p.isOp()) { + return true; + } Adaptation target = targetAdaptation == null ? adaptation : targetAdaptation; - String blacklistPermission = "adapt.blacklist." + target.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + p.getName() + " has blacklist permission " + blacklistPermission); - - return p.hasPermission(blacklistPermission); + if (target == null) { + return false; + } + String usePermission = "adapt.use." + target.getName().replaceAll("-", ""); + boolean permissionSet = p.isPermissionSet(usePermission); + boolean permissionAllowed = p.hasPermission(usePermission); + Adapt.verbose("Checking use permission " + usePermission + " for " + p.getName() + + " (set=" + permissionSet + ", value=" + permissionAllowed + ")"); + if (!permissionSet) { + return true; + } + return permissionAllowed; } static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity target) { @@ -662,8 +673,8 @@ private static int resolveActiveLevelUncached(Adaptation adaptation, Player p return 0; } - if (adaptation.hasBlacklistPermission(p, adaptation)) { - Adapt.verbose("Player " + p.getName() + " has blacklist permission for adaptation " + adaptation.getName()); + if (!adaptation.hasUsePermission(p, adaptation)) { + Adapt.verbose("Player " + p.getName() + " is blocked by use permission for adaptation " + adaptation.getName()); return 0; } if (hasUsageConflict(adaptation, p)) { diff --git a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java index 7db146943..42e640b35 100644 --- a/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java +++ b/src/main/java/art/arcane/adapt/api/runtime/AdaptationGate.java @@ -38,7 +38,7 @@ public static boolean shouldSkipPlayer(Player player, Skill skill, boolean ha } return !skill.isEnabled() - || skill.hasBlacklistPermission(player, skill) + || !skill.hasUsePermission(player, skill) || isWorldBlacklisted(player) || isInCreativeOrSpectator(player) || !hasAdaptPlayer; diff --git a/src/main/java/art/arcane/adapt/api/skill/Skill.java b/src/main/java/art/arcane/adapt/api/skill/Skill.java index 5f63910f8..a79ed034b 100644 --- a/src/main/java/art/arcane/adapt/api/skill/Skill.java +++ b/src/main/java/art/arcane/adapt/api/skill/Skill.java @@ -148,11 +148,11 @@ default void checkStatTrackers(AdaptPlayer player) { void onRegisterAdvancements(KList advancements); /** - * Returns true when this player is blacklisted from this skill via + * Returns true when this player has use permission for this skill via * permission. */ - default boolean hasBlacklistPermission(Player p, Skill s) { - return SkillRuntimeGuards.hasBlacklistPermission(p, s); + default boolean hasUsePermission(Player p, Skill s) { + return SkillRuntimeGuards.hasUsePermission(p, s); } /** @@ -268,10 +268,10 @@ default void knowledge(Player p, long k) { } /** - * Opens the skill GUI and optionally enforces blacklist permission checks. + * Opens the skill GUI and optionally enforces use permission checks. */ default boolean openGui(Player player, boolean checkPermissions) { - if (checkPermissions && hasBlacklistPermission(player, this)) { + if (checkPermissions && !hasUsePermission(player, this)) { return false; } else { openGui(player); diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index d0b825550..9a305901e 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -132,7 +132,7 @@ static void openGui(Skill skill, Player player, int page) { if (!adaptation.getSkill().isEnabled()) { continue; } - if (adaptation.hasBlacklistPermission(player, adaptation)) { + if (!adaptation.hasUsePermission(player, adaptation)) { continue; } visibleAdaptations.add(adaptation); diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java index 6caa7f507..a180c1cfc 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java @@ -73,16 +73,22 @@ static void checkStatTrackers(Skill skill, AdaptPlayer player) { } } - static boolean hasBlacklistPermission(Player player, Skill skill) { + static boolean hasUsePermission(Player player, Skill skill) { if (player == null || skill == null) { - return true; + return false; } if (player.isOp()) { - return false; + return true; + } + String usePermission = "adapt.use." + skill.getName().replaceAll("-", ""); + boolean permissionSet = player.isPermissionSet(usePermission); + boolean permissionAllowed = player.hasPermission(usePermission); + Adapt.verbose("Checking use permission " + usePermission + " for " + player.getName() + + " (set=" + permissionSet + ", value=" + permissionAllowed + ")"); + if (!permissionSet) { + return true; } - String blacklistPermission = "adapt.blacklist." + skill.getName().replaceAll("-", ""); - Adapt.verbose("Checking if player " + player.getName() + " has blacklist permission " + blacklistPermission); - return player.hasPermission(blacklistPermission); + return permissionAllowed; } static boolean shouldSkipPlayer(Skill skill, Player player) { diff --git a/src/main/java/art/arcane/adapt/command/CommandAdapt.java b/src/main/java/art/arcane/adapt/command/CommandAdapt.java index 80b0f5751..2b23ed23c 100644 --- a/src/main/java/art/arcane/adapt/command/CommandAdapt.java +++ b/src/main/java/art/arcane/adapt/command/CommandAdapt.java @@ -117,7 +117,7 @@ public void gui( if (force || skill.openGui(targetPlayer, true)) { FConst.success("Opened GUI for " + skill.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } else { - FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); + FConst.error("Failed to open GUI for " + skill.getName() + " for " + targetPlayer.getName() + " - Permission denied by adapt.use node.").send(BukkitDirectorContext.sender()); } return; } @@ -134,7 +134,7 @@ public void gui( if (force || adaptation.openGui(targetPlayer, true)) { FConst.success("Opened GUI for " + adaptation.getName() + " for " + targetPlayer.getName()).send(BukkitDirectorContext.sender()); } else { - FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - No Permission, remove from blacklist!").send(BukkitDirectorContext.sender()); + FConst.error("Failed to open GUI for " + adaptation.getName() + " for " + targetPlayer.getName() + " - Permission denied by adapt.use node.").send(BukkitDirectorContext.sender()); } return; } diff --git a/src/main/java/art/arcane/adapt/command/CommandDebug.java b/src/main/java/art/arcane/adapt/command/CommandDebug.java index 211cbc572..e6cc90fde 100644 --- a/src/main/java/art/arcane/adapt/command/CommandDebug.java +++ b/src/main/java/art/arcane/adapt/command/CommandDebug.java @@ -39,7 +39,7 @@ public void pap() { StringBuilder builder = new StringBuilder(); Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> skill.getAdaptations().forEach(adaptation -> builder - .append("adapt.blacklist.") + .append("adapt.use.") .append(adaptation.getName() .replaceAll("-", "")) .append("\n"))); @@ -56,7 +56,7 @@ public void psp() { StringBuilder builder = new StringBuilder(); Adapt.instance.getAdaptServer().getSkillRegistry().getSkills().forEach(skill -> builder - .append("adapt.blacklist.") + .append("adapt.use.") .append(skill.getName() .replaceAll("-", "")) .append("\n")); diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 2805dc6d8..14c4ff01f 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -76,7 +76,7 @@ public static void open(Player player, int page) { if (line == null) { continue; } - if (skill.hasBlacklistPermission(adaptPlayer.getPlayer(), skill) || line.getLevel() < 0) { + if (!skill.hasUsePermission(adaptPlayer.getPlayer(), skill) || line.getLevel() < 0) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 3ba9c62aa..200e658a7 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -236,7 +236,7 @@ public void on(PlayerDeathEvent e) { ce.start(); } - if (this.hasBlacklistPermission(p, this)) { + if (!this.hasUsePermission(p, this)) { return; } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b68c1e9b8..ca6f88871 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -47,411 +47,411 @@ permissions: adapt.clear: description: Allows clearing player progression data default: op - adapt.blacklist.agility: - description: Blacklist the skill Agility - default: false + adapt.use.agility: + description: Allow use of the skill Agility + default: true children: - adapt.blacklist.agilityarmorup: - description: Blacklist the adaptation AgilityArmorUp - default: false - adapt.blacklist.agilitysuperjump: - description: Blacklist the adaptation AgilitySuperJump - default: false - adapt.blacklist.agilitywalljump: - description: Blacklist the adaptation AgilityWallJump - default: false - adapt.blacklist.agilitywindup: - description: Blacklist the adaptation AgilityWindUp - default: false - adapt.blacklist.architect: - description: Blacklist the skill Architect - default: false + adapt.use.agilityarmorup: + description: Allow use of the adaptation AgilityArmorUp + default: true + adapt.use.agilitysuperjump: + description: Allow use of the adaptation AgilitySuperJump + default: true + adapt.use.agilitywalljump: + description: Allow use of the adaptation AgilityWallJump + default: true + adapt.use.agilitywindup: + description: Allow use of the adaptation AgilityWindUp + default: true + adapt.use.architect: + description: Allow use of the skill Architect + default: true children: - adapt.blacklist.architectfoundation: - description: Blacklist the adaptation ArchitectFoundation - default: false - adapt.blacklist.architectglass: - description: Blacklist the adaptation ArchitectGlass - default: false - adapt.blacklist.architectplacement: - description: Blacklist the adaptation ArchitectPlacement - default: false - adapt.blacklist.architectwirelessredstone: - description: Blacklist the adaptation ArchitectWirelessRedstone - default: false - adapt.blacklist.axes: - description: Blacklist the skill Axes - default: false + adapt.use.architectfoundation: + description: Allow use of the adaptation ArchitectFoundation + default: true + adapt.use.architectglass: + description: Allow use of the adaptation ArchitectGlass + default: true + adapt.use.architectplacement: + description: Allow use of the adaptation ArchitectPlacement + default: true + adapt.use.architectwirelessredstone: + description: Allow use of the adaptation ArchitectWirelessRedstone + default: true + adapt.use.axes: + description: Allow use of the skill Axes + default: true children: - adapt.blacklist.axechop: - description: Blacklist the adaptation AxesChop - default: false - adapt.blacklist.axedroptoinventory: - description: Blacklist the adaptation AxesDropToInventory - default: false - adapt.blacklist.axegroundsmash: - description: Blacklist the adaptation AxesGroundSmash - default: false - adapt.blacklist.axeleafveinminer: - description: Blacklist the adaptation AxesLeafVeinMiner - default: false - adapt.blacklist.axelogswap: - description: Blacklist the adaptation AxesLogSwap - default: false - adapt.blacklist.axewoodveinminer: - description: Blacklist the adaptation AxesWoodVeinMiner - default: false - adapt.blacklist.blocking: - description: Blacklist the skill Blocking - default: false + adapt.use.axechop: + description: Allow use of the adaptation AxesChop + default: true + adapt.use.axedroptoinventory: + description: Allow use of the adaptation AxesDropToInventory + default: true + adapt.use.axegroundsmash: + description: Allow use of the adaptation AxesGroundSmash + default: true + adapt.use.axeleafveinminer: + description: Allow use of the adaptation AxesLeafVeinMiner + default: true + adapt.use.axelogswap: + description: Allow use of the adaptation AxesLogSwap + default: true + adapt.use.axewoodveinminer: + description: Allow use of the adaptation AxesWoodVeinMiner + default: true + adapt.use.blocking: + description: Allow use of the skill Blocking + default: true children: - adapt.blacklist.blockingchainarmorer: - description: Blacklist the adaptation BlockingChainArmorer - default: false - adapt.blacklist.blockinghorsearmorer: - description: Blacklist the adaptation BlockingHorseArmorer - default: false - adapt.blacklist.blockingmultiarmor: - description: Blacklist the adaptation BlockingMultiArmor - default: false - adapt.blacklist.blockingsaddlecrafter: - description: Blacklist the adaptation BlockingSaddleCrafter - default: false - adapt.blacklist.brewing: - description: Blacklist the skill Brewing - default: false + adapt.use.blockingchainarmorer: + description: Allow use of the adaptation BlockingChainArmorer + default: true + adapt.use.blockinghorsearmorer: + description: Allow use of the adaptation BlockingHorseArmorer + default: true + adapt.use.blockingmultiarmor: + description: Allow use of the adaptation BlockingMultiArmor + default: true + adapt.use.blockingsaddlecrafter: + description: Allow use of the adaptation BlockingSaddleCrafter + default: true + adapt.use.brewing: + description: Allow use of the skill Brewing + default: true children: - adapt.blacklist.brewingabsorption: - description: Blacklist the adaptation BrewingAbsorption - default: false - adapt.blacklist.brewingblindness: - description: Blacklist the adaptation BrewingBlindness - default: false - adapt.blacklist.brewingdarkness: - description: Blacklist the adaptation BrewingDarkness - default: false - adapt.blacklist.brewingdecay: - description: Blacklist the adaptation BrewingDecay - default: false - adapt.blacklist.brewingfatigue: - description: Blacklist the adaptation BrewingFatigue - default: false - adapt.blacklist.brewinghaste: - description: Blacklist the adaptation BrewingHaste - default: false - adapt.blacklist.brewinghealthboost: - description: Blacklist the adaptation BrewingHealthBoost - default: false - adapt.blacklist.brewinghunger: - description: Blacklist the adaptation BrewingHunger - default: false - adapt.blacklist.brewinglingering: - description: Blacklist the adaptation BrewingLingering - default: false - adapt.blacklist.brewingnausea: - description: Blacklist the adaptation BrewingNausea - default: false - adapt.blacklist.brewingresistance: - description: Blacklist the adaptation BrewingResistance - default: false - adapt.blacklist.brewingsaturation: - description: Blacklist the adaptation BrewingSaturation - default: false - adapt.blacklist.brewingsuperheated: - description: Blacklist the adaptation BrewingSuperheated - default: false - adapt.blacklist.crafting: - description: Blacklist the skill Crafting - default: false + adapt.use.brewingabsorption: + description: Allow use of the adaptation BrewingAbsorption + default: true + adapt.use.brewingblindness: + description: Allow use of the adaptation BrewingBlindness + default: true + adapt.use.brewingdarkness: + description: Allow use of the adaptation BrewingDarkness + default: true + adapt.use.brewingdecay: + description: Allow use of the adaptation BrewingDecay + default: true + adapt.use.brewingfatigue: + description: Allow use of the adaptation BrewingFatigue + default: true + adapt.use.brewinghaste: + description: Allow use of the adaptation BrewingHaste + default: true + adapt.use.brewinghealthboost: + description: Allow use of the adaptation BrewingHealthBoost + default: true + adapt.use.brewinghunger: + description: Allow use of the adaptation BrewingHunger + default: true + adapt.use.brewinglingering: + description: Allow use of the adaptation BrewingLingering + default: true + adapt.use.brewingnausea: + description: Allow use of the adaptation BrewingNausea + default: true + adapt.use.brewingresistance: + description: Allow use of the adaptation BrewingResistance + default: true + adapt.use.brewingsaturation: + description: Allow use of the adaptation BrewingSaturation + default: true + adapt.use.brewingsuperheated: + description: Allow use of the adaptation BrewingSuperheated + default: true + adapt.use.crafting: + description: Allow use of the skill Crafting + default: true children: - adapt.blacklist.craftingbackpacks: - description: Blacklist the adaptation CraftingBackpacks - default: false - adapt.blacklist.craftingdeconstruction: - description: Blacklist the adaptation CraftingDeconstruction - default: false - adapt.blacklist.craftingleather: - description: Blacklist the adaptation CraftingLeather - default: false - adapt.blacklist.craftingreconstruction: - description: Blacklist the adaptation CraftingReconstruction - default: false - adapt.blacklist.craftingskulls: - description: Blacklist the adaptation CraftingSkulls - default: false - adapt.blacklist.craftingstations: - description: Blacklist the adaptation CraftingStations - default: false - adapt.blacklist.craftingxp: - description: Blacklist the adaptation CraftingXP - default: false - adapt.blacklist.chronos: - description: Blacklist the skill Chronos - default: false + adapt.use.craftingbackpacks: + description: Allow use of the adaptation CraftingBackpacks + default: true + adapt.use.craftingdeconstruction: + description: Allow use of the adaptation CraftingDeconstruction + default: true + adapt.use.craftingleather: + description: Allow use of the adaptation CraftingLeather + default: true + adapt.use.craftingreconstruction: + description: Allow use of the adaptation CraftingReconstruction + default: true + adapt.use.craftingskulls: + description: Allow use of the adaptation CraftingSkulls + default: true + adapt.use.craftingstations: + description: Allow use of the adaptation CraftingStations + default: true + adapt.use.craftingxp: + description: Allow use of the adaptation CraftingXP + default: true + adapt.use.chronos: + description: Allow use of the skill Chronos + default: true children: - adapt.blacklist.chronostimebottle: - description: Blacklist the adaptation ChronosTimeInABottle - default: false - adapt.blacklist.chronosaberranttouch: - description: Blacklist the adaptation ChronosAberrantTouch - default: false - adapt.blacklist.chronosinstantrecall: - description: Blacklist the adaptation ChronosInstantRecall - default: false - adapt.blacklist.chronostimebomb: - description: Blacklist the adaptation ChronosTimeBomb - default: false - adapt.blacklist.discovery: - description: Blacklist the skill Discovery - default: false + adapt.use.chronostimebottle: + description: Allow use of the adaptation ChronosTimeInABottle + default: true + adapt.use.chronosaberranttouch: + description: Allow use of the adaptation ChronosAberrantTouch + default: true + adapt.use.chronosinstantrecall: + description: Allow use of the adaptation ChronosInstantRecall + default: true + adapt.use.chronostimebomb: + description: Allow use of the adaptation ChronosTimeBomb + default: true + adapt.use.discovery: + description: Allow use of the skill Discovery + default: true children: - adapt.blacklist.discoveryunity: - description: Blacklist the adaptation DiscoveryUnity - default: false - adapt.blacklist.discoveryvillageratt: - description: Blacklist the adaptation DiscoveryVillagerAtt - default: false - adapt.blacklist.discoveryworldarmor: - description: Blacklist the adaptation DiscoveryWorldArmor - default: false - adapt.blacklist.discoveryxpresist: - description: Blacklist the adaptation DiscoveryXpResist - default: false - adapt.blacklist.enchanting: - description: Blacklist the skill Enchanting - default: false + adapt.use.discoveryunity: + description: Allow use of the adaptation DiscoveryUnity + default: true + adapt.use.discoveryvillageratt: + description: Allow use of the adaptation DiscoveryVillagerAtt + default: true + adapt.use.discoveryworldarmor: + description: Allow use of the adaptation DiscoveryWorldArmor + default: true + adapt.use.discoveryxpresist: + description: Allow use of the adaptation DiscoveryXpResist + default: true + adapt.use.enchanting: + description: Allow use of the skill Enchanting + default: true children: - adapt.blacklist.enchantinglapisreturn: - description: Blacklist the adaptation EnchantingLapisReturn - default: false - adapt.blacklist.enchantingquickenchant: - description: Blacklist the adaptation EnchantingQuickEnchant - default: false - adapt.blacklist.enchantingxpreturn: - description: Blacklist the adaptation EnchantingXpReturn - default: false - adapt.blacklist.excavation: - description: Blacklist the skill Excavation - default: false + adapt.use.enchantinglapisreturn: + description: Allow use of the adaptation EnchantingLapisReturn + default: true + adapt.use.enchantingquickenchant: + description: Allow use of the adaptation EnchantingQuickEnchant + default: true + adapt.use.enchantingxpreturn: + description: Allow use of the adaptation EnchantingXpReturn + default: true + adapt.use.excavation: + description: Allow use of the skill Excavation + default: true children: - adapt.blacklist.excavationdroptoinventory: - description: Blacklist the adaptation ExcavationDropToInventory - default: false - adapt.blacklist.excavationhaste: - description: Blacklist the adaptation ExcavationHaste - default: false - adapt.blacklist.excavationomnitool: - description: Blacklist the adaptation ExcavationOmnitool - default: false - adapt.blacklist.excavationspelunker: - description: Blacklist the adaptation ExcavationSpelunker - default: false - adapt.blacklist.herbalism: - description: Blacklist the skill Herbalism - default: false + adapt.use.excavationdroptoinventory: + description: Allow use of the adaptation ExcavationDropToInventory + default: true + adapt.use.excavationhaste: + description: Allow use of the adaptation ExcavationHaste + default: true + adapt.use.excavationomnitool: + description: Allow use of the adaptation ExcavationOmnitool + default: true + adapt.use.excavationspelunker: + description: Allow use of the adaptation ExcavationSpelunker + default: true + adapt.use.herbalism: + description: Allow use of the skill Herbalism + default: true children: - adapt.blacklist.herbalismcobweb: - description: Blacklist the adaptation HerbalismCobweb - default: false - adapt.blacklist.herbalismdroptoinventory: - description: Blacklist the adaptation HerbalismDropToInventory - default: false - adapt.blacklist.herbalismgrowthaura: - description: Blacklist the adaptation HerbalismGrowthAura - default: false - adapt.blacklist.herbalismhippo: - description: Blacklist the adaptation HerbalismHippo - default: false - adapt.blacklist.herbalismluck: - description: Blacklist the adaptation HerbalismLuck - default: false - adapt.blacklist.herbalismmyconid: - description: Blacklist the adaptation HerbalismMyconid - default: false - adapt.blacklist.herbalismreplant: - description: Blacklist the adaptation HerbalismReplant - default: false - adapt.blacklist.herbalismterralid: - description: Blacklist the adaptation HerbalismTerralid - default: false - adapt.blacklist.herbalismhungryshield: - description: Blacklist the adaptation HerbalismHungryShield - default: false - adapt.blacklist.hunter: - description: Blacklist the skill Hunter - default: false + adapt.use.herbalismcobweb: + description: Allow use of the adaptation HerbalismCobweb + default: true + adapt.use.herbalismdroptoinventory: + description: Allow use of the adaptation HerbalismDropToInventory + default: true + adapt.use.herbalismgrowthaura: + description: Allow use of the adaptation HerbalismGrowthAura + default: true + adapt.use.herbalismhippo: + description: Allow use of the adaptation HerbalismHippo + default: true + adapt.use.herbalismluck: + description: Allow use of the adaptation HerbalismLuck + default: true + adapt.use.herbalismmyconid: + description: Allow use of the adaptation HerbalismMyconid + default: true + adapt.use.herbalismreplant: + description: Allow use of the adaptation HerbalismReplant + default: true + adapt.use.herbalismterralid: + description: Allow use of the adaptation HerbalismTerralid + default: true + adapt.use.herbalismhungryshield: + description: Allow use of the adaptation HerbalismHungryShield + default: true + adapt.use.hunter: + description: Allow use of the skill Hunter + default: true children: - adapt.blacklist.hunteradrenaline: - description: Blacklist the adaptation HunterAdrenaline - default: false - adapt.blacklist.hunterdroptoinventory: - description: Blacklist the adaptation HunterDropToInventory - default: false - adapt.blacklist.hunterinvis: - description: Blacklist the adaptation HunterInvis - default: false - adapt.blacklist.hunterjumpboost: - description: Blacklist the adaptation HunterJumpBoost - default: false - adapt.blacklist.hunterluck: - description: Blacklist the adaptation HunterLuck - default: false - adapt.blacklist.hunterspeed: - description: Blacklist the adaptation HunterSpeed - default: false - adapt.blacklist.hunterstrength: - description: Blacklist the adaptation HunterStrength - default: false - adapt.blacklist.hunterregen: - description: Blacklist the adaptation HunterRegen - default: false - adapt.blacklist.hunterresistance: - description: Blacklist the adaptation HunterResistance - default: false - adapt.blacklist.nether: - description: Blacklist the skill Nether - default: false + adapt.use.hunteradrenaline: + description: Allow use of the adaptation HunterAdrenaline + default: true + adapt.use.hunterdroptoinventory: + description: Allow use of the adaptation HunterDropToInventory + default: true + adapt.use.hunterinvis: + description: Allow use of the adaptation HunterInvis + default: true + adapt.use.hunterjumpboost: + description: Allow use of the adaptation HunterJumpBoost + default: true + adapt.use.hunterluck: + description: Allow use of the adaptation HunterLuck + default: true + adapt.use.hunterspeed: + description: Allow use of the adaptation HunterSpeed + default: true + adapt.use.hunterstrength: + description: Allow use of the adaptation HunterStrength + default: true + adapt.use.hunterregen: + description: Allow use of the adaptation HunterRegen + default: true + adapt.use.hunterresistance: + description: Allow use of the adaptation HunterResistance + default: true + adapt.use.nether: + description: Allow use of the skill Nether + default: true children: - adapt.blacklist.netherskulltoss: - description: Blacklist the adaptation NetherSkullToss - default: false - adapt.blacklist.netherwitherresist: - description: Blacklist the adaptation NetherWitherResist - default: false - adapt.blacklist.netherfireresist: - description: Blacklist the adaptation NetherFireResist - default: false - adapt.blacklist.pickaxe: - description: Blacklist the skill Pickaxe - default: false + adapt.use.netherskulltoss: + description: Allow use of the adaptation NetherSkullToss + default: true + adapt.use.netherwitherresist: + description: Allow use of the adaptation NetherWitherResist + default: true + adapt.use.netherfireresist: + description: Allow use of the adaptation NetherFireResist + default: true + adapt.use.pickaxe: + description: Allow use of the skill Pickaxe + default: true children: - adapt.blacklist.pickaxeautosmelt: - description: Blacklist the adaptation PickaxeAutoSmelt - default: false - adapt.blacklist.pickaxedroptoinventory: - description: Blacklist the adaptation PickaxeDropToInventory - default: false - adapt.blacklist.pickaxechisel: - description: Blacklist the adaptation PickaxeChisel - default: false - adapt.blacklist.pickaxeveinminer: - description: Blacklist the adaptation PickaxeVeinMiner - default: false - adapt.blacklist.ranged: - description: Blacklist the skill Ranged - default: false + adapt.use.pickaxeautosmelt: + description: Allow use of the adaptation PickaxeAutoSmelt + default: true + adapt.use.pickaxedroptoinventory: + description: Allow use of the adaptation PickaxeDropToInventory + default: true + adapt.use.pickaxechisel: + description: Allow use of the adaptation PickaxeChisel + default: true + adapt.use.pickaxeveinminer: + description: Allow use of the adaptation PickaxeVeinMiner + default: true + adapt.use.ranged: + description: Allow use of the skill Ranged + default: true children: - adapt.blacklist.rangedforce: - description: Blacklist the adaptation RangedForce - default: false - adapt.blacklist.rangedlungeshot: - description: Blacklist the adaptation RangedLungeShot - default: false - adapt.blacklist.rangedpiercing: - description: Blacklist the adaptation RangedPiercing - default: false - adapt.blacklist.rangedrecovery: - description: Blacklist the adaptation RangedRecovery - default: false - adapt.blacklist.rangedwebshot: - description: Blacklist the adaptation RangedWebShot - default: false - adapt.blacklist.rift: - description: Blacklist the skill Rift - default: false + adapt.use.rangedforce: + description: Allow use of the adaptation RangedForce + default: true + adapt.use.rangedlungeshot: + description: Allow use of the adaptation RangedLungeShot + default: true + adapt.use.rangedpiercing: + description: Allow use of the adaptation RangedPiercing + default: true + adapt.use.rangedrecovery: + description: Allow use of the adaptation RangedRecovery + default: true + adapt.use.rangedwebshot: + description: Allow use of the adaptation RangedWebShot + default: true + adapt.use.rift: + description: Allow use of the skill Rift + default: true children: - adapt.blacklist.riftaccess: - description: Blacklist the adaptation RiftAccess - default: false - adapt.blacklist.riftblink: - description: Blacklist the adaptation RiftBlink - default: false - adapt.blacklist.riftdescent: - description: Blacklist the adaptation RiftDescent - default: false - adapt.blacklist.riftenderchest: - description: Blacklist the adaptation RiftEnderChest - default: false - adapt.blacklist.riftgate: - description: Blacklist the adaptation RiftGate - default: false - adapt.blacklist.riftresist: - description: Blacklist the adaptation RiftResist - default: false - adapt.blacklist.riftvisage: - description: Blacklist the adaptation RiftVisage - default: false - adapt.blacklist.seaborne: - description: Blacklist the skill Seaborne - default: false + adapt.use.riftaccess: + description: Allow use of the adaptation RiftAccess + default: true + adapt.use.riftblink: + description: Allow use of the adaptation RiftBlink + default: true + adapt.use.riftdescent: + description: Allow use of the adaptation RiftDescent + default: true + adapt.use.riftenderchest: + description: Allow use of the adaptation RiftEnderChest + default: true + adapt.use.riftgate: + description: Allow use of the adaptation RiftGate + default: true + adapt.use.riftresist: + description: Allow use of the adaptation RiftResist + default: true + adapt.use.riftvisage: + description: Allow use of the adaptation RiftVisage + default: true + adapt.use.seaborne: + description: Allow use of the skill Seaborne + default: true children: - adapt.blacklist.seabornefishersfantasy: - description: Blacklist the adaptation SeaborneFishersFantasy - default: false - adapt.blacklist.seabornespeed: - description: Blacklist the adaptation SeaborneSpeed - default: false - adapt.blacklist.seaborneoxygen: - description: Blacklist the adaptation SeaborneOxygen - default: false - adapt.blacklist.seaborneturtlesminingspeed: - description: Blacklist the adaptation SeaborneTurtlesMiningSpeed - default: false - adapt.blacklist.seaborneturtlesvision: - description: Blacklist the adaptation SeaborneTurtlesVision - default: false - adapt.blacklist.swords: - description: Blacklist the skill Swords - default: false + adapt.use.seabornefishersfantasy: + description: Allow use of the adaptation SeaborneFishersFantasy + default: true + adapt.use.seabornespeed: + description: Allow use of the adaptation SeaborneSpeed + default: true + adapt.use.seaborneoxygen: + description: Allow use of the adaptation SeaborneOxygen + default: true + adapt.use.seaborneturtlesminingspeed: + description: Allow use of the adaptation SeaborneTurtlesMiningSpeed + default: true + adapt.use.seaborneturtlesvision: + description: Allow use of the adaptation SeaborneTurtlesVision + default: true + adapt.use.swords: + description: Allow use of the skill Swords + default: true children: - adapt.blacklist.swordbloodyblade: - description: Blacklist the adaptation SwordBloodyBlade - default: false - adapt.blacklist.swordmachete: - description: Blacklist the adaptation SwordMachete - default: false - adapt.blacklist.swordpoisonblade: - description: Blacklist the adaptation SwordPoisonBlade - default: false - adapt.blacklist.taming: - description: Blacklist the skill Taming - default: false + adapt.use.swordbloodyblade: + description: Allow use of the adaptation SwordBloodyBlade + default: true + adapt.use.swordmachete: + description: Allow use of the adaptation SwordMachete + default: true + adapt.use.swordpoisonblade: + description: Allow use of the adaptation SwordPoisonBlade + default: true + adapt.use.taming: + description: Allow use of the skill Taming + default: true children: - adapt.blacklist.tamehealth: - description: Blacklist the adaptation TameHealth - default: false - adapt.blacklist.tamedamage: - description: Blacklist the adaptation TameDamage - default: false - adapt.blacklist.tamehealthregeneration: - description: Blacklist the adaptation TameHealthRegeneration - default: false - adapt.blacklist.tragoul: - description: Blacklist the skill Tragoul - default: false + adapt.use.tamehealth: + description: Allow use of the adaptation TameHealth + default: true + adapt.use.tamedamage: + description: Allow use of the adaptation TameDamage + default: true + adapt.use.tamehealthregeneration: + description: Allow use of the adaptation TameHealthRegeneration + default: true + adapt.use.tragoul: + description: Allow use of the skill Tragoul + default: true children: - adapt.blacklist.tragoulglobe: - description: Blacklist the adaptation TragoulGlobe - default: false - adapt.blacklist.tragoulhealing: - description: Blacklist the adaptation TragoulHealing - default: false - adapt.blacklist.tragoullance: - description: Blacklist the adaptation TragoulLance - default: false - adapt.blacklist.tragoulthorns: - description: Blacklist the adaptation TragoulThorns - default: false - adapt.blacklist.unarmed: - description: Blacklist the skill Unarmed - default: false + adapt.use.tragoulglobe: + description: Allow use of the adaptation TragoulGlobe + default: true + adapt.use.tragoulhealing: + description: Allow use of the adaptation TragoulHealing + default: true + adapt.use.tragoullance: + description: Allow use of the adaptation TragoulLance + default: true + adapt.use.tragoulthorns: + description: Allow use of the adaptation TragoulThorns + default: true + adapt.use.unarmed: + description: Allow use of the skill Unarmed + default: true children: - adapt.blacklist.unarmedglasscannon: - description: Blacklist the adaptation UnarmedGlassCannon - default: false - adapt.blacklist.unarmedpower: - description: Blacklist the adaptation UnarmedPower - default: false - adapt.blacklist.unarmedsuckerpunch: - description: Blacklist the adaptation UnarmedSuckerPunch - default: false + adapt.use.unarmedglasscannon: + description: Allow use of the adaptation UnarmedGlassCannon + default: true + adapt.use.unarmedpower: + description: Allow use of the adaptation UnarmedPower + default: true + adapt.use.unarmedsuckerpunch: + description: Allow use of the adaptation UnarmedSuckerPunch + default: true From eb20c92447eedbf443edf7a7c9520ffb76356c65 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 18 Feb 2026 20:57:29 -0500 Subject: [PATCH 16/25] f --- build.gradle.kts | 3 +- settings.gradle.kts | 32 ++++- src/main/java/art/arcane/adapt/Adapt.java | 2 +- .../api/advancement/AdvancementManager.java | 119 +++++++++++------- .../util/project/secret/SecretSplash.java | 8 +- .../util/AdvancementUtils.java | 61 ++++++++- 6 files changed, 165 insertions(+), 60 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 67c205361..be8b534c6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -133,8 +133,7 @@ dependencies { slimApi(libs.adventure.legacy) slimApi(libs.lettuce) slimApi(libs.particle) - // Keep UAA in the main shaded jar so Folia patches in source overrides take precedence. - slimApi(libs.ultimateAdvancementApi) + implementation(libs.ultimateAdvancementApi) slimApi(libs.customBlockData) slimApi(libs.lur) slimApi(libs.lang3) diff --git a/settings.gradle.kts b/settings.gradle.kts index e141bc658..af41a0ebf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,13 +2,41 @@ import java.io.File rootProject.name = "Adapt" +fun hasVolmLibSettings(directory: File): Boolean { + return directory.resolve("settings.gradle.kts").exists() || directory.resolve("settings.gradle").exists() +} + +fun resolveLocalVolmLibDirectory(): File? { + val configuredPath: String? = providers.gradleProperty("localVolmLibDirectory") + .orElse(providers.environmentVariable("VOLMLIB_DIR")) + .orNull + if (!configuredPath.isNullOrBlank()) { + val configuredDirectory: File = file(configuredPath) + if (hasVolmLibSettings(configuredDirectory)) { + return configuredDirectory + } + } + + var currentDirectory: File? = settingsDir + while (currentDirectory != null) { + val candidate: File = currentDirectory.resolve("VolmLib") + if (hasVolmLibSettings(candidate)) { + return candidate + } + + currentDirectory = currentDirectory.parentFile + } + + return null +} + val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib") .orElse("true") .map { value: String -> value.equals("true", ignoreCase = true) } .get() -val localVolmLibDirectory: File = file("../VolmLib") +val localVolmLibDirectory: File? = resolveLocalVolmLibDirectory() -if (useLocalVolmLib && localVolmLibDirectory.resolve("settings.gradle.kts").exists()) { +if (useLocalVolmLib && localVolmLibDirectory != null) { includeBuild(localVolmLibDirectory) { dependencySubstitution { substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared")) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 9c83dc554..2ba7f9fb7 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -508,7 +508,7 @@ private void startupPrint() { Adapt.info("\n" + C.DARK_GRAY + " █████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "█████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "██████" + C.DARK_RED + "╗ " + C.DARK_GRAY + "████████" + C.DARK_RED + "╗\n" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "╗╚══" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══╝" + C.DARK_RED + " Adapt, " + C.RED + "Abilities Refined" + C.RED + "[" + getReleaseTrain(instance.getDescription().getVersion()) + " RELEASE]\n" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "███████" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Version: " + C.DARK_RED + instance.getDescription().getVersion() + " \n" + - C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔══" + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "╔═══╝ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " By: " + C.WHITE + "Volmit Software (Arcane Arts)\n" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██████" + C.DARK_RED + "╔╝" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.DARK_GRAY + "██" + C.DARK_RED + "║ " + C.DARK_GRAY + "██" + C.DARK_RED + "║" + C.WHITE + " Server: " + C.DARK_RED + getServerVersion() + C.WHITE + " | MC Support: " + C.DARK_RED + supportedMcVersion + "\n" + C.DARK_RED + "╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ " + C.WHITE + " Java: " + C.DARK_RED + getJavaVersion() + C.WHITE + " | Date: " + C.DARK_RED + getStartupDate() + "\n"); } else { diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index 4be01bd56..960d4f005 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -11,9 +11,7 @@ import com.fren_gor.ultimateAdvancementAPI.advancement.Advancement; import com.fren_gor.ultimateAdvancementAPI.advancement.BaseAdvancement; import com.fren_gor.ultimateAdvancementAPI.advancement.RootAdvancement; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitTask; import java.util.HashSet; import java.util.Locale; @@ -25,6 +23,7 @@ import static art.arcane.adapt.Adapt.instance; public class AdvancementManager { + private static final int USER_LOAD_RETRIES = 12; private final AdvancementMain main; private final Map advancements; private final AtomicBoolean loaded = new AtomicBoolean(false); @@ -70,11 +69,11 @@ public void grant(AdaptPlayer player, String key, boolean toast) { return; } - attemptGrant(p, advancement, key, toast, true); + attemptGrant(p, advancement, key, toast, true, true, USER_LOAD_RETRIES); }, 5); } - private void attemptGrant(Player player, Advancement advancement, String key, boolean toast, boolean allowRetryOnGlobal) { + private void attemptGrant(Player player, Advancement advancement, String key, boolean toast, boolean allowRetryOnGlobal, boolean allowRetryOnEntity, int userLoadRetriesRemaining) { if (player == null || !player.isOnline()) { return; } @@ -83,20 +82,27 @@ private void attemptGrant(Player player, Advancement advancement, String key, bo advancement.grant(player, true); } catch (Throwable t) { if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + if (userLoadRetriesRemaining > 0) { + J.s(() -> attemptGrant(player, advancement, key, toast, allowRetryOnGlobal, allowRetryOnEntity, userLoadRetriesRemaining - 1), 5); + return; + } + + Adapt.verbose("Skipped advancement grant '" + key + "' because user data is not loaded yet for " + player.getName() + " after retries."); return; } if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); + if (allowRetryOnGlobal) { + J.s(() -> attemptGrant(player, advancement, key, toast, false, allowRetryOnEntity, userLoadRetriesRemaining), 1); return; } - if (allowRetryOnGlobal) { - J.s(() -> attemptGrant(player, advancement, key, toast, false), 1); + if (allowRetryOnEntity && J.runEntity(player, () -> attemptGrant(player, advancement, key, toast, false, false, userLoadRetriesRemaining), 1)) { return; } + + markRuntimeSchedulerUnsupported(t); + return; } Adapt.warn("Failed to grant advancement '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); @@ -111,27 +117,34 @@ private void attemptGrant(Player player, Advancement advancement, String key, bo advancement.displayToastToPlayer(player); } catch (Throwable t) { if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + if (userLoadRetriesRemaining > 0) { + J.s(() -> attemptToast(player, advancement, key, allowRetryOnGlobal, allowRetryOnEntity, userLoadRetriesRemaining - 1), 5); + return; + } + + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + " after retries."); return; } if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false, allowRetryOnEntity, userLoadRetriesRemaining), 1); return; } - if (allowRetryOnGlobal) { - J.s(() -> attemptToast(player, advancement, key, false), 1); + if (allowRetryOnEntity && J.runEntity(player, () -> attemptToast(player, advancement, key, false, false, userLoadRetriesRemaining), 1)) { return; } + + markRuntimeSchedulerUnsupported(t); + return; } Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); } } - private void attemptToast(Player player, Advancement advancement, String key, boolean allowRetryOnGlobal) { + private void attemptToast(Player player, Advancement advancement, String key, boolean allowRetryOnGlobal, boolean allowRetryOnEntity, int userLoadRetriesRemaining) { if (player == null || !player.isOnline()) { return; } @@ -140,20 +153,27 @@ private void attemptToast(Player player, Advancement advancement, String key, bo advancement.displayToastToPlayer(player); } catch (Throwable t) { if (isUserNotLoadedError(t)) { - Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + "."); + if (userLoadRetriesRemaining > 0) { + J.s(() -> attemptToast(player, advancement, key, allowRetryOnGlobal, allowRetryOnEntity, userLoadRetriesRemaining - 1), 5); + return; + } + + Adapt.verbose("Skipped advancement toast '" + key + "' because user data is not loaded yet for " + player.getName() + " after retries."); return; } if (isSchedulerContextMismatch(t)) { - if (J.isFoliaThreading()) { - markRuntimeSchedulerUnsupported(t); + if (allowRetryOnGlobal) { + J.s(() -> attemptToast(player, advancement, key, false, allowRetryOnEntity, userLoadRetriesRemaining), 1); return; } - if (allowRetryOnGlobal) { - J.s(() -> attemptToast(player, advancement, key, false), 1); + if (allowRetryOnEntity && J.runEntity(player, () -> attemptToast(player, advancement, key, false, false, userLoadRetriesRemaining), 1)) { return; } + + markRuntimeSchedulerUnsupported(t); + return; } Adapt.warn("Failed to display advancement toast '" + key + "' for " + player.getName() + ": " + summarizeThrowable(t)); @@ -167,6 +187,7 @@ private void markRuntimeSchedulerUnsupported(Throwable throwable) { Adapt.info("UltimateAdvancementAPI live packet grants/toasts are unavailable on this Folia runtime; stored advancement grants will continue without live packets/toasts."); if (throwable != null) { + Adapt.warn("UltimateAdvancementAPI live packet fallback cause: " + summarizeThrowable(throwable)); Adapt.verbose("UltimateAdvancementAPI fallback cause: " + summarizeThrowable(throwable)); } } @@ -253,27 +274,48 @@ public void unlockExisting(AdaptPlayer player, AdvancementHandler handler) { } J.runEntity(target, () -> { - instance.getAdaptServer() - .getSkillRegistry() - .getSkills() - .stream() - .map(Skill::buildAdvancements) - .forEach(aa -> unlockExisting(player, aa)); + for (Skill skill : instance.getAdaptServer().getSkillRegistry().getSkills()) { + AdaptAdvancement advancement = skill.buildAdvancements(); + ensureSkillRootGranted(player, advancement); + unlockExisting(player, advancement); + } handler.setReady(true); }, 20); } + private void ensureSkillRootGranted(AdaptPlayer player, AdaptAdvancement advancement) { + if (player == null || advancement == null) { + return; + } + + String key = advancement.getKey(); + if (key == null || key.isBlank() || !key.startsWith("skill_")) { + return; + } + + if (player.getData().isGranted(key)) { + return; + } + + grant(player, key, false); + } + private void unlockExisting(AdaptPlayer player, AdaptAdvancement aa) { + if (aa == null) { + return; + } + + String key = aa.getKey(); + if (key != null && !key.isBlank() && player.getData().isGranted(key)) { + grant(player, key, false); + } + if (aa.getChildren() != null) { for (AdaptAdvancement i : aa.getChildren()) { unlockExisting(player, i); } } - - if (player.getData().isGranted(aa.getKey())) { - grant(player, aa.getKey(), false); - } } public void enable() { @@ -295,10 +337,6 @@ public void enable() { main.enableSQLite(instance.getDataFile("data", "advancements.db")); } - if (J.isFoliaThreading() && isLegacyAsyncSchedulerUnsupported()) { - markRuntimeSchedulerUnsupported(null); - } - for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { AdaptAdvancement aa = i.buildAdvancements(); Set set = new HashSet<>(); @@ -331,17 +369,4 @@ public void disable() { loaded.set(false); runtimeSchedulerUnsupported.set(false); } - - private boolean isLegacyAsyncSchedulerUnsupported() { - try { - BukkitTask probe = Bukkit.getScheduler().runTaskTimerAsynchronously(instance, () -> { - }, 1L, 1L); - probe.cancel(); - return false; - } catch (UnsupportedOperationException ignored) { - return true; - } catch (Throwable ignored) { - return false; - } - } } diff --git a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java index 4bf25513e..85a820cd7 100644 --- a/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java +++ b/src/main/java/art/arcane/adapt/util/project/secret/SecretSplash.java @@ -34,7 +34,7 @@ public class SecretSplash { C.BLUE + " ⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇ \n" + C.BLUE + " ⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀ " + C.DARK_RED + "Adapt\n" + C.BLUE + " ⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + - C.BLUE + " ⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.BLUE + " ⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀ " + C.GRAY + "By: " + C.WHITE + "Volmit Software (Arcane Arts)\n" + C.BLUE + " ⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + C.BLUE + " ⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀ ⠀\n" + C.BLUE + " ⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀ ⠀⠀⠀\n" + @@ -46,7 +46,7 @@ public class SecretSplash { "\n :::. :::::::-. :::. ::::::::::. :::::::::::: \n" + " ;;`;; ;;, `';, ;;`;; `;;;```.;;;;;;;;;;;'''' " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + "\n" + - " ,[[ '[[, `[[ [[ ,[[ '[[, `]]nnn]]' [[ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + " ,[[ '[[, `[[ [[ ,[[ '[[, `]]nnn]]' [[ " + C.GRAY + "By: " + C.WHITE + "Volmit Software (Arcane Arts)\n" + " $$$$$$$$ $$, $$ $$$$$$$$ $$$\"\" $$ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + "\n" + " 888 888,888_,o8P' 888 888,888o 88, \n" + " YMM \"\"` MMMMP\"` YMM \"\"` YMMMb MMM \n", @@ -54,7 +54,7 @@ public class SecretSplash { C.GRAY + "\n ██░ ██ ▓█████ ██▓ ██▓███ ███▄ ▄███▓▓█████ \n" + C.GRAY + "▓██░ ██▒▓█ ▀ ▓██▒ ▓██░ ██▒ ▓██▒▀█▀ ██▒▓█ ▀ " + C.DARK_RED + "Adapt \n" + C.GRAY + "▒██▀▀██░▒███ ▒██░ ▓██░ ██▓▒ ▓██ ▓██░▒███ " + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + - C.GRAY + "░▓█ ░██ ▒▓█ ▄ ▒██░ ▒██▄█▓▒ ▒ ▒██ ▒██ ▒▓█ ▄ " + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.GRAY + "░▓█ ░██ ▒▓█ ▄ ▒██░ ▒██▄█▓▒ ▒ ▒██ ▒██ ▒▓█ ▄ " + C.GRAY + "By: " + C.WHITE + "Volmit Software (Arcane Arts)\n" + C.GRAY + "░▓█▒░██▓░▒████▒░██████▒▒██▒ ░ ░ ▒██▒ ░██▒░▒████▒ " + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + C.GRAY + " ▒ ░░▒░▒░░ ▒░ ░░ ▒░▓ ░▒▓▒░ ░ ░ ░ ▒░ ░ ░░░ ▒░ ░ ", @@ -65,7 +65,7 @@ public class SecretSplash { C.GRAY + "⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⣴⣿⣿⣿⠉⠀⠀⠀⠀⠀⠈⢻⣿⣿⣇⠀⠀⠀ \n" + C.GRAY + "⠀⠀⠀⠀⢀⣠⣼⣿⣿⡏⠀⢠⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⡀⠀ ⠀" + C.DARK_RED + "Adapt \n" + C.GRAY + "⠀⠀⠀⣰⣿⣿⣿⣿⣿⡇⠀⢸⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⡇⠀ ⠀" + C.GRAY + "Version: " + C.DARK_RED + Adapt.instance.getDescription().getVersion() + " \n" + - C.GRAY + "⠀⠀⢰⣿⣿⡿⣿⣿⣿⡇⠀⠘⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⢀⣸⣿⣿⣿⠁⠀ ⠀" + C.GRAY + "By: " + C.RED + "A" + C.GOLD + "r" + C.YELLOW + "c" + C.GREEN + "a" + C.DARK_GRAY + "n" + C.AQUA + "e " + C.AQUA + "A" + C.BLUE + "r" + C.DARK_BLUE + "t" + C.DARK_PURPLE + "s" + C.WHITE + " (Volmit Software)\n" + + C.GRAY + "⠀⠀⢰⣿⣿⡿⣿⣿⣿⡇⠀⠘⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⢀⣸⣿⣿⣿⠁⠀ ⠀" + C.GRAY + "By: " + C.WHITE + "Volmit Software (Arcane Arts)\n" + C.GRAY + "⠀⠀⣿⣿⣿⠁⣿⣿⣿⡇⠀⠀⠻⣿⣿⣿⣷⣶⣶⣶⣶⣶⣿⣿⣿⣿⠃⠀ ⠀⠀" + C.GRAY + "Java Version: " + C.DARK_RED + Adapt.getJavaVersion() + " \n" + C.GRAY + "⠀⢰⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀ ⠀\n" + C.GRAY + "⠀⢸⣿⣿⡇⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠉⢉⣿⣿⠀⠀⠀⠀⠀ ⠀\n" + diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java index a0cec1988..d1696c7d1 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/AdvancementUtils.java @@ -298,11 +298,21 @@ public static void runSync(@NotNull Plugin plugin, long delay, @NotNull Runnable return; } - if (safeDelay <= 0) { - J.s(runnable); - } else { - J.s(runnable, safeDelay); + if (scheduleLegacySync(runnable, safeDelay)) { + return; + } + + if (scheduleFoliaSync(plugin, runnable, safeDelay)) { + return; } + + if (safeDelay <= 0 && FoliaScheduler.isPrimaryThread()) { + runnable.run(); + return; + } + + plugin.getLogger().warning("Failed to schedule advancement sync task for plugin " + plugin.getName() + + " (" + safeDelay + "t)."); } private static int sanitizeDelay(long delay) { @@ -336,6 +346,49 @@ private static boolean scheduleFoliaSync(@NotNull Plugin plugin, @NotNull Runnab return scheduleGlobalReflective(plugin, runnable, safeDelay); } + private static boolean scheduleLegacySync(@NotNull Runnable runnable, int safeDelay) { + try { + if (safeDelay <= 0) { + J.s(runnable); + } else { + J.s(runnable, safeDelay); + } + return true; + } catch (Throwable ex) { + if (isUnsupportedScheduler(ex)) { + return false; + } + + if (ex instanceof RuntimeException runtimeException) { + throw runtimeException; + } + + if (ex instanceof Error error) { + throw error; + } + + throw new RuntimeException(ex); + } + } + + private static boolean isUnsupportedScheduler(@Nullable Throwable throwable) { + Throwable current = throwable; + while (current != null) { + if (current instanceof UnsupportedOperationException) { + return true; + } + + String message = current.getMessage(); + if (message != null && message.contains("BukkitScheduler unsupported")) { + return true; + } + + current = current.getCause(); + } + + return false; + } + private static boolean hasFoliaScheduler() { return FoliaScheduler.isFolia(Bukkit.getServer()); } From 42cd5ae0790fc9abe92a54f0b698b3fc479cff34 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 21 Feb 2026 04:14:34 -0500 Subject: [PATCH 17/25] dwa --- .../adaptation/stealth/StealthSight.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java index 5c9553a97..e46b5ef39 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSight.java @@ -45,6 +45,7 @@ public class StealthSight extends SimpleAdaptation { private final Set sneaking; + private final Set appliedNightVision; public StealthSight() { @@ -59,6 +60,7 @@ public StealthSight() { setCostFactor(getConfig().costFactor); setMaxLevel(getConfig().maxLevel); sneaking = ConcurrentHashMap.newKeySet(); + appliedNightVision = ConcurrentHashMap.newKeySet(); registerAdvancement(AdaptAdvancement.builder() .icon(Material.ENDER_EYE) .key("challenge_stealth_sight_72k") @@ -83,20 +85,20 @@ public void on(PlayerToggleSneakEvent e) { SoundPlayer sp = SoundPlayer.of(p); if (!hasActiveAdaptation(p)) { sneaking.remove(id); - p.removePotionEffect(PotionEffectType.NIGHT_VISION); + clearNightVisionIfApplied(p, id); return; } if (e.isSneaking()) { sneaking.add(id); sp.play(p.getLocation(), Sound.BLOCK_FUNGUS_BREAK, 1, 0.99f); - p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false)); + applyNightVisionIfNeeded(p, id); getPlayer(p).getData().addStat("stealth.sight.time-in-darkness", 1); return; } sneaking.remove(id); - p.removePotionEffect(PotionEffectType.NIGHT_VISION); + clearNightVisionIfApplied(p, id); }); } @@ -108,13 +110,14 @@ public void onTick() { Player p = Bukkit.getPlayer(id); if (p == null || !p.isOnline()) { sneaking.remove(id); + appliedNightVision.remove(id); continue; } Runnable check = () -> { if (getActiveLevel(p, Player::isSneaking) <= 0) { sneaking.remove(id); - J.runEntity(p, () -> p.removePotionEffect(PotionEffectType.NIGHT_VISION)); + J.runEntity(p, () -> clearNightVisionIfApplied(p, id)); } }; @@ -137,6 +140,29 @@ public boolean isPermanent() { return getConfig().permanent; } + private void applyNightVisionIfNeeded(Player player, UUID id) { + if (player.hasPotionEffect(PotionEffectType.NIGHT_VISION)) { + appliedNightVision.remove(id); + return; + } + + PotionEffect effect = new PotionEffect(PotionEffectType.NIGHT_VISION, 1000, 0, false, false); + boolean applied = player.addPotionEffect(effect); + if (applied) { + appliedNightVision.add(id); + } else { + appliedNightVision.remove(id); + } + } + + private void clearNightVisionIfApplied(Player player, UUID id) { + if (!appliedNightVision.remove(id)) { + return; + } + + player.removePotionEffect(PotionEffectType.NIGHT_VISION); + } + @NoArgsConstructor @ConfigDescription("Gain night vision while sneaking.") protected static class Config { From 342638771cf2ecd8925148d2904f7303e4e78b1a Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 22 Feb 2026 09:01:09 -0500 Subject: [PATCH 18/25] dwa --- build.gradle | 261 ++++++++++++++++++ build.gradle.kts | 253 ----------------- settings.gradle | 49 ++++ settings.gradle.kts | 51 ---- src/main/java/art/arcane/adapt/Adapt.java | 2 +- .../api/adaptation/AdaptationGuiSupport.java | 12 +- .../api/adaptation/chunk/ChunkLoading.java | 8 +- .../adapt/api/skill/SkillGuiSupport.java | 6 +- .../arcane/adapt/api/world/AdaptPlayer.java | 2 +- .../arcane/adapt/content/gui/ConfigGui.java | 4 +- .../arcane/adapt/content/gui/SkillsGui.java | 2 +- .../util/common/inventorygui/GuiConfirm.java | 2 +- .../util/project/command/VirtualCommand.java | 11 +- velocity/build.gradle | 37 +++ velocity/build.gradle.kts | 34 --- 15 files changed, 378 insertions(+), 356 deletions(-) create mode 100644 build.gradle delete mode 100644 build.gradle.kts create mode 100644 settings.gradle delete mode 100644 settings.gradle.kts create mode 100644 velocity/build.gradle delete mode 100644 velocity/build.gradle.kts diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..aaa66f859 --- /dev/null +++ b/build.gradle @@ -0,0 +1,261 @@ +/* + * Adapt is Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * 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 . + */ + +import io.freefair.gradle.plugins.lombok.LombokPlugin +import io.github.slimjar.resolver.data.Mirror +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.jvm.toolchain.JavaLanguageVersion + +plugins { + id 'java-library' + alias(libs.plugins.lombok) + alias(libs.plugins.shadow) + alias(libs.plugins.idea) + alias(libs.plugins.slimjar) +} + +version = '2.0.0-1.21.11' +def apiVersion = '1.21' +def main = 'art.arcane.adapt.Adapt' +String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') + .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') + .get() + +def shadowJarTask = tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) + +// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED +// ======================== WINDOWS ============================= +registerCustomOutputTask('Cyberpwn', 'C://Users/cyberpwn/Documents/development/server/plugins') +registerCustomOutputTask('Psycho', 'C://Dan/MinecraftDevelopment/Server/plugins') +registerCustomOutputTask('ArcaneArts', 'C://Users/arcane/Documents/development/server/plugins') +registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.20/plugins') +registerCustomOutputTask('Nowhere', 'E://Desktop/server/plugins') +registerCustomOutputTask('CrazyDev22', 'C://Users/Julian/Desktop/server/plugins') +registerCustomOutputTask('Pixel', 'D://Iris Dimension Engine//1.20.4 - Development//plugins') +// ========================== UNIX ============================== +registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins') +registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins') +registerCustomOutputTaskUnix('the456gamer', '/home/the456gamer/projects/minecraft/adapt-testserver/plugins/update/', false) +// ============================================================== + +/** + * Expand properties into plugin yml + */ +tasks.named('processResources').configure { + def pluginProperties = [ + name : rootProject.name, + version : project.version, + main : main, + apiVersion: apiVersion, + ] + + inputs.properties(pluginProperties) + filesMatching('**/plugin.yml') { + expand(pluginProperties) + } +} + +allprojects { + apply plugin: 'java' + apply plugin: LombokPlugin + + java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } + + repositories { + mavenCentral() + maven { url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/') } + maven { url = uri('https://repo.papermc.io/repository/maven-public/') } + maven { url = uri('https://repo.codemc.org/repository/maven-public') } + maven { url = uri('https://mvn.lumine.io/repository/maven-public/') } + maven { url = uri('https://nexus.frengor.com/repository/public/') } + maven { url = uri('https://repo.extendedclip.com/content/repositories/placeholderapi/') } + maven { url = uri('https://repo.glaremasters.me/repository/bloodshot/') } + maven { url = uri('https://maven.enginehub.org/repo/') } + maven { url = uri('https://repo.oraxen.com/releases') } + maven { url = uri('https://repo.alessiodp.com/releases') } + maven { url = uri('https://jitpack.io') } + } + + /** + * We need parameter meta for the decree command system + */ + tasks.withType(JavaCompile).configureEach { + options.compilerArgs.add('-parameters') + options.encoding = 'UTF-8' + options.debugOptions.debugLevel = 'none' + options.release.set(21) + } +} + +dependencies { + implementation(project(':velocity')) + implementation(volmLibCoordinate) { + changing = true + transitive = false + } + implementation('de.crazydev22.slimjar.helper:spigot:2.1.7') + implementation('de.crazydev22.slimjar.helper:velocity:2.1.7') + slimApi(libs.platformUtils) { + transitive = false + } + + compileOnly(libs.spigot) + + // Cancer + slimApi(libs.fukkit) { + exclude(group: 'org.spigotmc', module: 'spigot-api') + } + slimApi(libs.amulet) + slimApi(libs.chrono) + slimApi(libs.spatial) + slimApi(libs.kotlin.coroutines) + + // Dynamically Loaded + slimApi(libs.adventure.minimessage) + slimApi(libs.adventure.platform) + slimApi(libs.adventure.gson) + slimApi(libs.adventure.legacy) + slimApi(libs.lettuce) + slimApi(libs.particle) + implementation(libs.ultimateAdvancementApi) + slimApi(libs.customBlockData) + slimApi(libs.lur) + slimApi(libs.lang3) + slimApi(libs.effectLib) + slimApi(libs.gson) + slimApi(libs.toml4j) + slimApi(libs.fastutil) + slimApi(libs.glowingentities) + slimApi(libs.caffeine) + + //Random Api's + compileOnlyApi('me.clip:placeholderapi:2.11.6') + compileOnlyApi('com.github.DeadSilenceIV:AdvancedChestsAPI:2.9-BETA') + compileOnlyApi('com.sk89q.worldguard:worldguard-bukkit:7.0.8') + compileOnlyApi('com.github.FrancoBM12:API-MagicCosmetics:2.2.8') + compileOnlyApi('com.massivecraft:Factions:1.6.9.5-U0.6.21') + compileOnlyApi('com.github.angeschossen:ChestProtectAPI:3.9.1') + compileOnlyApi('com.github.TechFortress:GriefPrevention:16.18.1') + compileOnlyApi('com.griefdefender:api:2.1.0-SNAPSHOT') + compileOnlyApi(fileTree('libs') { include('*.jar') }) +} + +def lib = 'art.arcane.adapt.util' +slimJar { + mirrors = [ + new Mirror( + uri('https://maven-central.storage-download.googleapis.com/maven2').toURL(), + uri('https://repo.maven.apache.org/maven2/').toURL() + ) + ] + + relocate('manifold', "${lib}.manifold") + relocate('art.arcane.volmlib', "${lib}.arcane.volmlib") + relocate('Fukkit.extensions', "${lib}.extensions") + relocate('Amulet.extensions', "${lib}.extensions") + relocate('com.fren_gor.ultimateAdvancementAPI', "${lib}.advancements") + relocate('net.byteflux.libby', "${lib}.libby") + relocate('com.jeff_media.customblockdata', "${lib}.customblocks") +} + +/** + * Configure Adapt for shading + */ +shadowJarTask.configure { +// minimize() + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + exclude('net/kyori/**') + exclude('com/google/gson/**') +} + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor(0, 'seconds') + resolutionStrategy.cacheDynamicVersionsFor(0, 'seconds') +} + +if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { + System.err.println() + System.err.println('=========================================================================================================') + System.err.println('You must run gradle on Java 21 or newer. You are using ' + JavaVersion.current()) + System.err.println() + System.err.println('=== For IDEs ===') + System.err.println('1. Configure the project for Java 21') + System.err.println('2. Configure the bundled gradle to use Java 21 in settings') + System.err.println() + System.err.println('=== For Command Line (gradlew) ===') + System.err.println('1. Install JDK 21 from https://www.oracle.com/java/technologies/downloads/#java21') + System.err.println('2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:/Program Files/Java/jdk-21') + System.err.println('3. Open a new command prompt window to get the new environment variables if need be.') + System.err.println('=========================================================================================================') + System.err.println() + System.exit(69) +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +tasks.named('build').configure { + dependsOn(shadowJarTask) +} + +tasks.register('adapt', Copy) { + from(shadowJarTask.flatMap { it.archiveFile }) + into(layout.buildDirectory) + rename { String ignored -> "Adapt-${version}.jar" } +} + +void registerCustomOutputTask(String name, String path, boolean doRename = true) { + if (!System.getProperty('os.name').toLowerCase().contains('windows')) { + return + } + createOutputTask(name, path, doRename) +} + +void registerCustomOutputTaskUnix(String name, String path, boolean doRename = true) { + if (System.getProperty('os.name').toLowerCase().contains('windows')) { + return + } + + createOutputTask(name, path, doRename) +} + +void createOutputTask(String name, String path, boolean doRename = true) { + def shadowJarTask = tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) + tasks.register("build${name}", Copy) { + group = 'development' + outputs.upToDateWhen { false } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + dependsOn(shadowJarTask) + from(shadowJarTask.flatMap { it.archiveFile }) + into(file(path)) + if (doRename) { + rename { String ignored -> 'Adapt.jar' } + } + } +} + +idea.project.settings { + taskTriggers { + afterSync(':velocity:generateTemplates') + } +} diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index be8b534c6..000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Adapt is Copyright (c) 2021 Arcane Arts (Volmit Software) - * - * 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 . - */ - -import io.freefair.gradle.plugins.lombok.LombokPlugin -import io.github.slimjar.func.slimjarHelper -import io.github.slimjar.resolver.data.Mirror -import org.gradle.api.plugins.JavaPluginExtension -import org.jetbrains.gradle.ext.settings -import org.jetbrains.gradle.ext.taskTriggers -import kotlin.system.exitProcess - -plugins { - `java-library` - alias(libs.plugins.lombok) - alias(libs.plugins.shadow) - alias(libs.plugins.idea) - alias(libs.plugins.slimjar) -} - -version = "2.0.0-1.21.11" -val apiVersion = "1.21" -val main = "art.arcane.adapt.Adapt" -val volmLibCoordinate: String = providers.gradleProperty("volmLibCoordinate") - .orElse("com.github.VolmitSoftware:VolmLib:master-SNAPSHOT") - .get() - -// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED -// ======================== WINDOWS ============================= -registerCustomOutputTask("Cyberpwn", "C://Users/cyberpwn/Documents/development/server/plugins") -registerCustomOutputTask("Psycho", "C://Dan/MinecraftDevelopment/Server/plugins") -registerCustomOutputTask("ArcaneArts", "C://Users/arcane/Documents/development/server/plugins") -registerCustomOutputTask("Vatuu", "D://Minecraft/Servers/1.20/plugins") -registerCustomOutputTask("Nowhere", "E://Desktop/server/plugins") -registerCustomOutputTask("CrazyDev22", "C://Users/Julian/Desktop/server/plugins") -registerCustomOutputTask("Pixel", "D://Iris Dimension Engine//1.20.4 - Development//plugins") -// ========================== UNIX ============================== -registerCustomOutputTaskUnix("CyberpwnLT", "/Users/danielmills/development/server/plugins") -registerCustomOutputTaskUnix("PsychoLT", "/Users/brianfopiano/Developer/RemoteGit/[Minecraft Server]/consumers/plugin-consumers/dropins/plugins") -registerCustomOutputTaskUnix("the456gamer", "/home/the456gamer/projects/minecraft/adapt-testserver/plugins/update/", false) -// ============================================================== - -/** - * Expand properties into plugin yml - */ -tasks.processResources { - inputs.properties( - "name" to rootProject.name, - "version" to version, - "main" to main, - "apiVersion" to apiVersion, - ) - - filesMatching("**/plugin.yml") { - expand(inputs.properties) - } -} - -allprojects { - apply() - apply() - - extensions.configure { - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) - } - - repositories { - mavenCentral() - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") - maven("https://repo.papermc.io/repository/maven-public/") - maven("https://repo.codemc.org/repository/maven-public") - maven("https://mvn.lumine.io/repository/maven-public/") - maven("https://nexus.frengor.com/repository/public/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") - maven("https://repo.glaremasters.me/repository/bloodshot/") - maven("https://maven.enginehub.org/repo/") - maven("https://repo.oraxen.com/releases") - maven("https://repo.alessiodp.com/releases") - maven("https://jitpack.io") - } - - /** - * We need parameter meta for the decree command system - */ - tasks.compileJava { - options.compilerArgs.add("-parameters") - options.encoding = "UTF-8" - options.debugOptions.debugLevel = "none" - options.release.set(21) - } -} - -dependencies { - implementation(project(":velocity")) - implementation(volmLibCoordinate) { - isChanging = true - isTransitive = false - } - implementation(slimjarHelper("spigot")) - implementation(slimjarHelper("velocity")) - slimApi(libs.platformUtils) { - isTransitive = false - } - - compileOnly(libs.spigot) - - // Cancer - slimApi(libs.fukkit) { - exclude(group = "org.spigotmc", module = "spigot-api") - } - slimApi(libs.amulet) - slimApi(libs.chrono) - slimApi(libs.spatial) - slimApi(libs.kotlin.coroutines) - - // Dynamically Loaded - slimApi(libs.adventure.minimessage) - slimApi(libs.adventure.platform) - slimApi(libs.adventure.gson) - slimApi(libs.adventure.legacy) - slimApi(libs.lettuce) - slimApi(libs.particle) - implementation(libs.ultimateAdvancementApi) - slimApi(libs.customBlockData) - slimApi(libs.lur) - slimApi(libs.lang3) - slimApi(libs.effectLib) - slimApi(libs.gson) - slimApi(libs.toml4j) - slimApi(libs.fastutil) - slimApi(libs.glowingentities) - slimApi(libs.caffeine) - - //Random Api's - compileOnlyApi("me.clip:placeholderapi:2.11.6") - compileOnlyApi("com.github.DeadSilenceIV:AdvancedChestsAPI:2.9-BETA") - compileOnlyApi("com.sk89q.worldguard:worldguard-bukkit:7.0.8") - compileOnlyApi("com.github.FrancoBM12:API-MagicCosmetics:2.2.8") - compileOnlyApi("com.massivecraft:Factions:1.6.9.5-U0.6.21") - compileOnlyApi("com.github.angeschossen:ChestProtectAPI:3.9.1") - compileOnlyApi("com.github.TechFortress:GriefPrevention:16.18.1") - compileOnlyApi("com.griefdefender:api:2.1.0-SNAPSHOT") - compileOnlyApi(fileTree("libs") { include("*.jar") }) -} - -val lib = "art.arcane.adapt.util" -slimJar { - mirrors = listOf(Mirror( - uri("https://maven-central.storage-download.googleapis.com/maven2").toURL(), - uri("https://repo.maven.apache.org/maven2/").toURL() - )) - - relocate("manifold", "$lib.manifold") - relocate("art.arcane.volmlib", "$lib.arcane.volmlib") - relocate("Fukkit.extensions", "$lib.extensions") - relocate("Amulet.extensions", "$lib.extensions") - relocate("com.fren_gor.ultimateAdvancementAPI", "$lib.advancements") - relocate("net.byteflux.libby", "$lib.libby") - relocate("com.jeff_media.customblockdata", "$lib.customblocks") -} - -/** - * Configure Adapt for shading - */ -tasks.shadowJar { -// minimize() - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - exclude("net/kyori/**") - exclude("com/google/gson/**") -} - -configurations.configureEach { - resolutionStrategy.cacheChangingModulesFor(0, "seconds") - resolutionStrategy.cacheDynamicVersionsFor(0, "seconds") -} - -if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { - System.err.println() - System.err.println("=========================================================================================================") - System.err.println("You must run gradle on Java 21 or newer. You are using " + JavaVersion.current()) - System.err.println() - System.err.println("=== For IDEs ===") - System.err.println("1. Configure the project for Java 21") - System.err.println("2. Configure the bundled gradle to use Java 21 in settings") - System.err.println() - System.err.println("=== For Command Line (gradlew) ===") - System.err.println("1. Install JDK 21 from https://www.oracle.com/java/technologies/downloads/#java21") - System.err.println("2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:/Program Files/Java/jdk-21") - System.err.println("3. Open a new command prompt window to get the new environment variables if need be.") - System.err.println("=========================================================================================================") - System.err.println() - exitProcess(69) -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -tasks { - build { dependsOn(shadowJar) } - - register("adapt") { - from(shadowJar.flatMap { it.archiveFile }) - into(layout.buildDirectory) - rename { "Adapt-$version.jar" } - } -} - -fun registerCustomOutputTask(name: String, path: String, doRename: Boolean = true) { - if (!System.getProperty("os.name").lowercase().contains("windows")) { - return - } - createOutputTask(name, path, doRename) -} - -fun registerCustomOutputTaskUnix(name: String, path: String, doRename: Boolean = true) { - if (System.getProperty("os.name").lowercase().contains("windows")) { - return - } - - createOutputTask(name, path, doRename) -} - -fun createOutputTask(name: String, path: String, doRename: Boolean = true) { - tasks.register("build$name") { - group = "development" - outputs.upToDateWhen { false } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - dependsOn(tasks.shadowJar) - from(tasks.shadowJar.flatMap { it.archiveFile }) - into(file(path)) - if (doRename) rename { "Adapt.jar" } - } -} - -idea.project.settings.taskTriggers { - afterSync(":velocity:generateTemplates") -} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..0aae7e772 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,49 @@ +import java.io.File + +rootProject.name = 'Adapt' + +boolean hasVolmLibSettings(File directory) { + new File(directory, 'settings.gradle.kts').exists() || new File(directory, 'settings.gradle').exists() +} + +File resolveLocalVolmLibDirectory() { + String configuredPath = providers.gradleProperty('localVolmLibDirectory') + .orElse(providers.environmentVariable('VOLMLIB_DIR')) + .orNull + if (configuredPath != null && !configuredPath.isBlank()) { + File configuredDirectory = file(configuredPath) + if (hasVolmLibSettings(configuredDirectory)) { + return configuredDirectory + } + } + + File currentDirectory = settingsDir + while (currentDirectory != null) { + File candidate = new File(currentDirectory, 'VolmLib') + if (hasVolmLibSettings(candidate)) { + return candidate + } + + currentDirectory = currentDirectory.parentFile + } + + null +} + +boolean useLocalVolmLib = providers.gradleProperty('useLocalVolmLib') + .orElse('true') + .map { String value -> value.equalsIgnoreCase('true') } + .get() +File localVolmLibDirectory = resolveLocalVolmLibDirectory() + +if (useLocalVolmLib && localVolmLibDirectory != null) { + includeBuild(localVolmLibDirectory) { + dependencySubstitution { + substitute(module('com.github.VolmitSoftware:VolmLib')).using(project(':shared')) + substitute(module('com.github.VolmitSoftware.VolmLib:shared')).using(project(':shared')) + substitute(module('com.github.VolmitSoftware.VolmLib:volmlib-shared')).using(project(':shared')) + } + } +} + +include(':velocity') diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index af41a0ebf..000000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -import java.io.File - -rootProject.name = "Adapt" - -fun hasVolmLibSettings(directory: File): Boolean { - return directory.resolve("settings.gradle.kts").exists() || directory.resolve("settings.gradle").exists() -} - -fun resolveLocalVolmLibDirectory(): File? { - val configuredPath: String? = providers.gradleProperty("localVolmLibDirectory") - .orElse(providers.environmentVariable("VOLMLIB_DIR")) - .orNull - if (!configuredPath.isNullOrBlank()) { - val configuredDirectory: File = file(configuredPath) - if (hasVolmLibSettings(configuredDirectory)) { - return configuredDirectory - } - } - - var currentDirectory: File? = settingsDir - while (currentDirectory != null) { - val candidate: File = currentDirectory.resolve("VolmLib") - if (hasVolmLibSettings(candidate)) { - return candidate - } - - currentDirectory = currentDirectory.parentFile - } - - return null -} - -val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib") - .orElse("true") - .map { value: String -> value.equals("true", ignoreCase = true) } - .get() -val localVolmLibDirectory: File? = resolveLocalVolmLibDirectory() - -if (useLocalVolmLib && localVolmLibDirectory != null) { - includeBuild(localVolmLibDirectory) { - dependencySubstitution { - substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared")) - substitute(module("com.github.VolmitSoftware.VolmLib:shared")).using(project(":shared")) - substitute(module("com.github.VolmitSoftware.VolmLib:volmlib-shared")).using(project(":shared")) - } - } -} - -include( - ":velocity", -) diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 2ba7f9fb7..a6cbbb351 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -237,7 +237,7 @@ public static void printInformation() { @SneakyThrows public static void autoUpdateCheck() { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts").openStream()))) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle").openStream()))) { info("Checking for updates..."); String inputLine; while ((inputLine = in.readLine()) != null) { diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index 9299cdf80..226d4e57b 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -188,7 +188,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { } if (!J.isPrimaryThread()) { int targetPage = page; - J.s(() -> openGui(adaptation, player, targetPage)); + J.runEntity(player, () -> openGui(adaptation, player, targetPage)); return; } @@ -269,7 +269,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { if (delayTicks != 0) { player.sendTitle(" ", C.RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.may_not_unlearn") + " " + adaptation.getDisplayName(currentLevel), 1, 10, 11); } - J.s(() -> openAdaptationPage(adaptation, player, currentPage), delayTicks); + J.runEntity(player, () -> openAdaptationPage(adaptation, player, currentPage), delayTicks); return; } @@ -278,7 +278,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { if (adaptation.isPermanent() && !consumePermanentLearnConfirmation(player, adaptation, lvl)) { spw.play(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.7f, 0.85f); player.sendTitle(" ", C.GOLD + "" + C.BOLD + "Click again to confirm permanent learn", 1, 16, 8); - J.s(() -> openAdaptationPage(adaptation, player, currentPage), 1); + J.runEntity(player, () -> openAdaptationPage(adaptation, player, currentPage), 1); return; } @@ -358,7 +358,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { } w.setTitle(adaptation.getDisplayName()); - w.onClosed((vv) -> J.s(() -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); + w.onClosed((vv) -> J.runEntity(player, () -> onGuiClosed(adaptation, player, !AdaptConfig.get().isEscClosesAllGuis()))); w.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), w); } @@ -466,7 +466,7 @@ private static void openAdaptationPage(Adaptation adaptation, Player player, private static void closeAndReopenAfterLevelChange(Adaptation adaptation, Player player, int page, int delayTicks) { closeCurrentAdaptationGui(player); int reopenDelay = Math.max(0, delayTicks); - J.s(() -> reopenAdaptationPageIfReady(adaptation, player, page), reopenDelay); + J.runEntity(player, () -> reopenAdaptationPageIfReady(adaptation, player, page), reopenDelay); } private static void closeCurrentAdaptationGui(Player player) { @@ -510,7 +510,7 @@ private static void onGuiClosed(Adaptation adaptation, Player player, boolean playCloseSound(player); if (openPrevGui) { - J.s(() -> { + J.runEntity(player, () -> { if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { adaptation.getSkill().openGui(player); } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java index 4a7396ac8..879f6e865 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/chunk/ChunkLoading.java @@ -32,6 +32,12 @@ public static void loadChunkAsync(Location l, Consumer chunk) { return; } Adapt.verbose("Loading chunk async for " + l); - Adapt.platform.getChunkAtAsync(l).thenAccept(c -> J.s(() -> chunk.accept(c))); + Adapt.platform.getChunkAtAsync(l).thenAccept(c -> { + if (!J.runAt(l, () -> chunk.accept(c))) { + if (!J.isFoliaThreading()) { + J.s(() -> chunk.accept(c)); + } + } + }); } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index 9a305901e..7591846e6 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -110,7 +110,7 @@ static void openGui(Skill skill, Player player, int page) { if (!J.isPrimaryThread()) { int targetPage = page; - J.s(() -> openGui(skill, player, targetPage)); + J.runEntity(player, () -> openGui(skill, player, targetPage)); return; } @@ -231,7 +231,7 @@ static void openGui(Skill skill, Player player, int page) { } window.setTitle(skill.getDisplayName(adaptPlayer.getSkillLine(skill.getName()).getLevel()) + " " + Form.pc(XP.getLevelProgress(adaptPlayer.getSkillLine(skill.getName()).getXp())) + " (" + Form.f((int) XP.getXpUntilLevelUp(adaptPlayer.getSkillLine(skill.getName()).getXp())) + Localizer.dLocalize("snippets.gui.xp") + " " + (adaptPlayer.getSkillLine(skill.getName()).getLevel() + 1) + ")"); - window.onClosed((vv) -> J.s(() -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); + window.onClosed((vv) -> J.runEntity(player, () -> onGuiClosed(player, !AdaptConfig.get().isEscClosesAllGuis()))); window.open(); Adapt.instance.getGuiLeftovers().put(player.getUniqueId().toString(), window); } @@ -263,7 +263,7 @@ private static void onGuiClosed(Player player, boolean openPrevGui) { playCloseSound(player); if (openPrevGui) { - J.s(() -> { + J.runEntity(player, () -> { if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { SkillsGui.open(player); } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index e8589d13a..851878b2a 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -265,7 +265,7 @@ public void delete(UUID uuid) { return; } - J.s(() -> p.kickPlayer("Your data has been deleted."), 20); + J.runEntity(p, () -> p.kickPlayer("Your data has been deleted."), 20); } public boolean shouldUnload() { diff --git a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java index d826d6ad1..7260b803b 100644 --- a/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/ConfigGui.java @@ -78,7 +78,7 @@ public static void open(Player player, String sectionPath, int page) { if (!J.isPrimaryThread()) { String path = sectionPath; int targetPage = page; - J.s(() -> open(player, path, targetPage)); + J.runEntity(player, () -> open(player, path, targetPage)); return; } @@ -1489,7 +1489,7 @@ private static void onGuiClosed(Player player, String currentPath) { } String parent = parentPath(safePath); - J.s(() -> { + J.runEntity(player, () -> { if (player.isOnline() && player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) { open(player, parent, 0); } diff --git a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java index 14c4ff01f..4b55aa4e3 100644 --- a/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java +++ b/src/main/java/art/arcane/adapt/content/gui/SkillsGui.java @@ -54,7 +54,7 @@ public static void open(Player player) { public static void open(Player player, int page) { if (!J.isPrimaryThread()) { int targetPage = page; - J.s(() -> open(player, targetPage)); + J.runEntity(player, () -> open(player, targetPage)); return; } diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java index b0a4ac5dd..9342f36ad 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/GuiConfirm.java @@ -25,7 +25,7 @@ public static void open( } if (!J.isPrimaryThread()) { - J.s(() -> open(player, title, message, onConfirm, onCancel)); + J.runEntity(player, () -> open(player, title, message, onConfirm, onCancel)); return; } diff --git a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java index 2134646ee..481bcaec1 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java +++ b/src/main/java/art/arcane/adapt/util/project/command/VirtualCommand.java @@ -27,6 +27,7 @@ import art.arcane.volmlib.util.reflect.V; import org.bukkit.Sound; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import java.lang.reflect.Field; import java.util.List; @@ -172,12 +173,18 @@ private boolean checkPermissions(CommandSender sender, ICommand command2) { for (String i : command.getRequiredPermissions()) { if (!sender.hasPermission(i)) { failed = true; - J.s(() -> Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "- " + C.WHITE + i)); + Player player = sender.getServer().getPlayer(sender.getName()); + if (player != null) { + J.runEntity(player, () -> Adapt.messagePlayer(player, "- " + C.WHITE + i)); + } } } if (failed) { - Adapt.messagePlayer(sender.getServer().getPlayer(sender.getName()), "Insufficient Permissions"); + Player player = sender.getServer().getPlayer(sender.getName()); + if (player != null) { + Adapt.messagePlayer(player, "Insufficient Permissions"); + } return false; } diff --git a/velocity/build.gradle b/velocity/build.gradle new file mode 100644 index 000000000..9145d6493 --- /dev/null +++ b/velocity/build.gradle @@ -0,0 +1,37 @@ + +plugins { + id 'java' +} + +dependencies { + compileOnly(libs.velocity) + annotationProcessor(libs.velocity) + + compileOnly('de.crazydev22.slimjar.helper:velocity:2.1.7') + compileOnly(libs.lettuce) + compileOnly(libs.toml4j) + compileOnly(libs.fastutil) +} + +def templateSource = file('src/main/templates') +def templateDest = layout.buildDirectory.dir('generated/sources/templates') +def generateTemplates = tasks.register('generateTemplates', Copy) { + inputs.properties([ + id : rootProject.name.toLowerCase(), + name : rootProject.name, + version: rootProject.version, + ]) + + from(templateSource) + into(templateDest) + rename { String fileName -> "art/arcane/adapt/${fileName}" } + expand(inputs.properties) +} + +sourceSets { + main { + java { + srcDir(generateTemplates.map { it.outputs }) + } + } +} diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts deleted file mode 100644 index 4cbb13e64..000000000 --- a/velocity/build.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -import io.github.slimjar.func.slimjarHelper - -plugins { - java -} - -dependencies { - compileOnly(libs.velocity) - annotationProcessor(libs.velocity) - - compileOnly(slimjarHelper("velocity")) - compileOnly(libs.lettuce) - compileOnly(libs.toml4j) - compileOnly(libs.fastutil) -} - -val templateSource = file("src/main/templates") -val templateDest = layout.buildDirectory.dir("generated/sources/templates") -val generateTemplates = tasks.register("generateTemplates") { - inputs.properties( - "id" to rootProject.name.lowercase(), - "name" to rootProject.name, - "version" to rootProject.version, - ) - - from(templateSource) - into(templateDest) - rename { "art/arcane/adapt/$it" } - expand(inputs.properties) -} - -sourceSets.main { - java.srcDir(generateTemplates.map { it.outputs }) -} From ded0cd2da48d87e61851bfa2696e62d28c18a639 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 13 Apr 2026 17:06:13 -0400 Subject: [PATCH 19/25] gr --- README.md | 8 +- build.gradle | 23 ++-- gradle/libs.versions.toml | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/art/arcane/adapt/PapiExpansion.java | 130 +++++++++++------- .../arcane/adapt/api/skill/SkillRegistry.java | 13 ++ velocity/build.gradle | 2 +- 7 files changed, 110 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index b7e00b79f..55eb4018c 100644 --- a/README.md +++ b/README.md @@ -47,17 +47,17 @@ plugin [Manifold](https://plugins.jetbrains.com/plugin/10057-manifold) ## Preface: if you need help compiling and you are a developer / intend to help out in the community or with development we would love to help you regardless in the discord! however do not come to the discord asking for free copies, or a tutorial on how to compile. -1. Install [Java JDK 21](https://www.oracle.com/java/technologies/downloads/#java21) +1. Install [Java JDK 25](https://adoptium.net/temurin/releases/?version=25) 2. Set the JDK installation path to `JAVA_HOME` as an environment variable. * Windows 1. Start > Type `env` and press Enter 2. Advanced > Environment Variables 3. Under System Variables, click `New...` 4. Variable Name: `JAVA_HOME` - 5. Variable Value: `C:\Program Files\Java\jdk-21` (verify this exists after installing java don't just copy + 5. Variable Value: `C:\Program Files\Java\jdk-25` (verify this exists after installing java don't just copy the example text) * MacOS - 1. Run `/usr/libexec/java_home -V` and look for Java 21 + 1. Run `/usr/libexec/java_home -V` and look for Java 25 2. Run `sudo nano ~/.zshenv` 3. Add `export JAVA_HOME=$(/usr/libexec/java_home)` as a new line 4. Use `CTRL + X`, then Press `Y`, Then `ENTER` @@ -69,7 +69,7 @@ plugin [Manifold](https://plugins.jetbrains.com/plugin/10057-manifold) Gradle Setup * Run `gradlew setup` any time you get dependency issues with craftbukkit -* Configure ITJ Gradle to use JDK 21 (in settings, search for gradle) +* Configure ITJ Gradle to use JDK 25 (in settings, search for gradle) * Resync the project & run your newly created task (under the development folder in gradle tasks!) diff --git a/build.gradle b/build.gradle index aaa66f859..bd2ef5907 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,7 @@ String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .get() def shadowJarTask = tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) +tasks.named('slimJar').get() // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ======================== WINDOWS ============================= @@ -75,7 +76,7 @@ allprojects { apply plugin: LombokPlugin java { - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + toolchain.languageVersion.set(JavaLanguageVersion.of(25)) } repositories { @@ -100,7 +101,7 @@ allprojects { options.compilerArgs.add('-parameters') options.encoding = 'UTF-8' options.debugOptions.debugLevel = 'none' - options.release.set(21) + options.release.set(25) } } @@ -110,8 +111,8 @@ dependencies { changing = true transitive = false } - implementation('de.crazydev22.slimjar.helper:spigot:2.1.7') - implementation('de.crazydev22.slimjar.helper:velocity:2.1.7') + implementation('de.crazydev22.slimjar.helper:spigot:2.1.9') + implementation('de.crazydev22.slimjar.helper:velocity:2.1.9') slimApi(libs.platformUtils) { transitive = false } @@ -190,18 +191,18 @@ configurations.configureEach { resolutionStrategy.cacheDynamicVersionsFor(0, 'seconds') } -if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { +if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_25)) { System.err.println() System.err.println('=========================================================================================================') - System.err.println('You must run gradle on Java 21 or newer. You are using ' + JavaVersion.current()) + System.err.println('You must run gradle on Java 25 or newer. You are using ' + JavaVersion.current()) System.err.println() System.err.println('=== For IDEs ===') - System.err.println('1. Configure the project for Java 21') - System.err.println('2. Configure the bundled gradle to use Java 21 in settings') + System.err.println('1. Configure the project for Java 25') + System.err.println('2. Configure the bundled gradle to use Java 25 in settings') System.err.println() System.err.println('=== For Command Line (gradlew) ===') - System.err.println('1. Install JDK 21 from https://www.oracle.com/java/technologies/downloads/#java21') - System.err.println('2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:/Program Files/Java/jdk-21') + System.err.println('1. Install JDK 25 from https://adoptium.net/temurin/releases/?version=25') + System.err.println('2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:/Program Files/Java/jdk-25') System.err.println('3. Open a new command prompt window to get the new environment variables if need be.') System.err.println('=========================================================================================================') System.err.println() @@ -210,7 +211,7 @@ if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba6cbe2c9..efaddacb9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,10 @@ [versions] # Plugins -lombok = "8.14" +lombok = "9.2.0" shadow = "9.0.0-rc3" runTask = "2.3.1" idea = "1.2" -slimjar = "2.1.7" +slimjar = "2.1.9" kotlin = "2.2.0" kotlin-coroutines = "1.10.2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f24029cc4..fcccf27e1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -17,7 +17,7 @@ # distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/art/arcane/adapt/PapiExpansion.java b/src/main/java/art/arcane/adapt/PapiExpansion.java index 100bbc10d..97a0c66d9 100644 --- a/src/main/java/art/arcane/adapt/PapiExpansion.java +++ b/src/main/java/art/arcane/adapt/PapiExpansion.java @@ -1,6 +1,7 @@ package art.arcane.adapt; import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.skill.SkillRegistry; import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerData; @@ -12,11 +13,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; public class PapiExpansion extends PlaceholderExpansion { private static final Locale LOCALE = Locale.ROOT; + private volatile CatalogSnapshot catalogSnapshot = CatalogSnapshot.empty(); @Override public @NotNull String getIdentifier() { @@ -291,19 +295,8 @@ private Skill resolveSkill(String rawSkillId) { } String skillId = rawSkillId.trim(); - Skill direct = Adapt.instance.getAdaptServer().getSkillRegistry().getAnySkill(skillId); - if (direct != null) { - return direct; - } - - List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : allSkills) { - if (skill.getName().equalsIgnoreCase(skillId)) { - return skill; - } - } - - return null; + SkillRegistry registry = Adapt.instance.getAdaptServer().getSkillRegistry(); + return registry.getAnySkill(skillId); } private Adaptation resolveAdaptation(String rawAdaptationId) { @@ -311,39 +304,8 @@ private Adaptation resolveAdaptation(String rawAdaptationId) { return null; } - String adaptationId = rawAdaptationId.trim().toLowerCase(LOCALE); - List> allSkills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : allSkills) { - for (Adaptation adaptation : skill.getAdaptations()) { - if (matchesAdaptationIdentifier(adaptation, adaptationId)) { - return adaptation; - } - } - } - - return null; - } - - private boolean matchesAdaptationIdentifier(Adaptation adaptation, String normalizedTarget) { - String name = adaptation.getName(); - if (name != null && name.equalsIgnoreCase(normalizedTarget)) { - return true; - } - - String fullId = adaptation.getId(); - if (fullId != null && fullId.equalsIgnoreCase(normalizedTarget)) { - return true; - } - - if (fullId != null && fullId.length() > 37) { - String legacyId = fullId.substring(37); - if (legacyId.equalsIgnoreCase(normalizedTarget)) { - return true; - } - } - - String scoped = adaptation.getSkill().getName() + ":" + adaptation.getName(); - return scoped.equalsIgnoreCase(normalizedTarget); + String adaptationId = normalizeCatalogKey(rawAdaptationId); + return getCatalogSnapshot().findAdaptation(adaptationId); } private int countSeenThings(PlayerData playerData) { @@ -363,7 +325,7 @@ private int countSeenThings(PlayerData playerData) { } private int countSkills() { - return Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills().size(); + return getCatalogSnapshot().skillCount(); } private int countKnownSkills(PlayerData playerData) { @@ -377,12 +339,7 @@ private int countKnownSkills(PlayerData playerData) { } private int countAdaptations() { - int total = 0; - List> skills = Adapt.instance.getAdaptServer().getSkillRegistry().getAllSkills(); - for (Skill skill : skills) { - total += skill.getAdaptations().size(); - } - return total; + return getCatalogSnapshot().adaptationCount(); } private int countLearnedAdaptations(PlayerData playerData) { @@ -434,4 +391,71 @@ private String format2(double value) { private String format4(double value) { return String.format(LOCALE, "%.4f", value); } + + private CatalogSnapshot getCatalogSnapshot() { + SkillRegistry registry = Adapt.instance.getAdaptServer().getSkillRegistry(); + long revision = registry.getCatalogRevision(); + CatalogSnapshot snapshot = catalogSnapshot; + if (snapshot.revision() == revision) { + return snapshot; + } + + CatalogSnapshot rebuilt = CatalogSnapshot.create(revision, registry.getAllSkills()); + catalogSnapshot = rebuilt; + return rebuilt; + } + + private static String normalizeCatalogKey(String raw) { + return raw.trim().toLowerCase(LOCALE); + } + + private static void indexAdaptationIdentifier(Map> index, String rawKey, Adaptation adaptation) { + if (rawKey == null || rawKey.isBlank() || adaptation == null) { + return; + } + + index.putIfAbsent(normalizeCatalogKey(rawKey), adaptation); + } + + private static CatalogSnapshot buildCatalogSnapshot(long revision, List> skills) { + Map> adaptationsById = new HashMap<>(); + int adaptationCount = 0; + for (Skill skill : skills) { + if (skill == null) { + continue; + } + + List> adaptations = skill.getAdaptations(); + adaptationCount += adaptations.size(); + for (Adaptation adaptation : adaptations) { + if (adaptation == null) { + continue; + } + + indexAdaptationIdentifier(adaptationsById, adaptation.getName(), adaptation); + String fullId = adaptation.getId(); + indexAdaptationIdentifier(adaptationsById, fullId, adaptation); + if (fullId != null && fullId.length() > 37) { + indexAdaptationIdentifier(adaptationsById, fullId.substring(37), adaptation); + } + indexAdaptationIdentifier(adaptationsById, adaptation.getSkill().getName() + ":" + adaptation.getName(), adaptation); + } + } + + return new CatalogSnapshot(revision, skills.size(), adaptationCount, Map.copyOf(adaptationsById)); + } + + private record CatalogSnapshot(long revision, int skillCount, int adaptationCount, Map> adaptationsById) { + private static CatalogSnapshot empty() { + return new CatalogSnapshot(-1L, 0, 0, Map.of()); + } + + private static CatalogSnapshot create(long revision, List> skills) { + return buildCatalogSnapshot(revision, skills); + } + + private Adaptation findAdaptation(String normalizedId) { + return adaptationsById.get(normalizedId); + } + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java index d500c3332..457032a8c 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRegistry.java @@ -52,6 +52,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; public class SkillRegistry extends TickedObject { public static final KMap> skills = new KMap<>(); @@ -61,6 +62,7 @@ public class SkillRegistry extends TickedObject { private final KMap>> skillTypes = new KMap<>(); private final Map> adaptationRecipeIndex = new ConcurrentHashMap<>(); private final Deque> deferredBootstrapRecipeRegistration = new ArrayDeque<>(); + private final AtomicLong catalogRevision = new AtomicLong(); private volatile boolean deferredBootstrapRecipeTaskScheduled; private volatile boolean bootstrapLoading = true; private volatile boolean foliaRecipeRegistrationWaitWarned; @@ -231,6 +233,10 @@ public List> getAllSkills() { return new ArrayList<>(knownSkills.v()); } + public long getCatalogRevision() { + return catalogRevision.get(); + } + public synchronized void registerSkill(Class> skillType) { long started = System.currentTimeMillis(); long instantiateStarted = started; @@ -251,6 +257,7 @@ public synchronized void registerSkill(Class> skillType) { if (!skill.isEnabled()) { skill.unregister(); skills.remove(skillName); + catalogRevision.incrementAndGet(); return; } @@ -265,6 +272,7 @@ public synchronized void registerSkill(Class> skillType) { if (totalMs >= SLOW_SKILL_REG_MS || instantiateMs >= SLOW_SKILL_REG_MS) { Adapt.warn("Skill registration slow-path [" + skillName + "] total=" + totalMs + "ms instantiate=" + instantiateMs + "ms bootstrap=" + bootstrapLoading + "."); } + catalogRevision.incrementAndGet(); } public synchronized boolean hotReloadSkillConfig(String skillName) { @@ -283,6 +291,7 @@ public synchronized boolean hotReloadSkillConfig(String skillName) { loaded.unregister(); } skills.remove(normalized); + catalogRevision.incrementAndGet(); return true; } @@ -293,6 +302,7 @@ public synchronized boolean hotReloadSkillConfig(String skillName) { skills.put(normalized, loaded); unregisterRecipes(loaded); registerRecipes(loaded); + catalogRevision.incrementAndGet(); return true; } @@ -337,6 +347,7 @@ private boolean replaceSkillInstance(String normalizedName, Class Date: Fri, 29 May 2026 19:36:05 -0400 Subject: [PATCH 20/25] Patches --- build.gradle | 20 +- gradle/libs.versions.toml | 2 +- src/main/java/art/arcane/adapt/Adapt.java | 50 ++++- .../api/advancement/AdvancementManager.java | 102 ++++++++--- .../excavation/ExcavationSpelunker.java | 3 + .../common/inventorygui/PhantomInventory.java | 17 +- .../util/common/plugin/VolmitSender.java | 5 + .../util/project/command/CommandDummy.java | 7 + .../util/project/command/MortarSender.java | 6 + .../nms/util/ReflectionUtil.java | 109 +++++++++++ .../MinecraftKeyWrapper_v1_21_R7.java | 45 +++++ .../nms/v1_21_R7/Util.java | 116 ++++++++++++ .../AdvancementDisplayWrapper_v1_21_R7.java | 97 ++++++++++ .../AdvancementFrameTypeWrapper_v1_21_R7.java | 31 ++++ .../AdvancementWrapper_v1_21_R7.java | 90 +++++++++ .../PreparedAdvancementWrapper_v1_21_R7.java | 57 ++++++ ...etPlayOutAdvancementsWrapper_v1_21_R7.java | 49 +++++ ...tSelectAdvancementTabWrapper_v1_21_R7.java | 26 +++ .../ultimateAdvancementAPI/util/Versions.java | 171 ++++++++++++++++++ .../java/fr/skytasul/reflection/Version.java | 85 +++++++++ 20 files changed, 1052 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/util/ReflectionUtil.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/MinecraftKeyWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementFrameTypeWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutAdvancementsWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7.java create mode 100644 src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java create mode 100644 src/main/java/fr/skytasul/reflection/Version.java diff --git a/build.gradle b/build.gradle index bd2ef5907..2f97fcdcb 100644 --- a/build.gradle +++ b/build.gradle @@ -29,9 +29,18 @@ plugins { alias(libs.plugins.slimjar) } -version = '2.0.0-1.21.11' -def apiVersion = '1.21' +version = '2.0.0-26.1.2' +def apiVersion = '26.1' def main = 'art.arcane.adapt.Adapt' +String configuredNmsJar = findProperty('adaptNmsJar') != null ? findProperty('adaptNmsJar').toString() : System.getenv('ADAPT_NMS_JAR') +File detectedActiveInstanceVersionsDir = rootDir.toPath().resolve('../../[Minecraft Server]/active-instance/versions').normalize().toFile() +File detectedVersionedNmsJar = detectedActiveInstanceVersionsDir.exists() + ? fileTree(detectedActiveInstanceVersionsDir).matching { include('**/*.jar') }.files.toList().sort { File left, File right -> Long.compare(right.lastModified(), left.lastModified()) }.find() + : null +File detectedLauncherNmsJar = rootDir.toPath().resolve('../../[Minecraft Server]/active-instance/server.jar').normalize().toFile() +File nmsCompileJar = configuredNmsJar != null && !configuredNmsJar.isBlank() + ? file(configuredNmsJar) + : detectedVersionedNmsJar != null ? detectedVersionedNmsJar : detectedLauncherNmsJar String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() @@ -117,7 +126,10 @@ dependencies { transitive = false } - compileOnly(libs.spigot) + compileOnly('io.papermc.paper:paper-api:26.1.2.build.66-stable') + if (nmsCompileJar.exists()) { + compileOnly(files(nmsCompileJar)) + } // Cancer slimApi(libs.fukkit) { @@ -143,7 +155,7 @@ dependencies { slimApi(libs.gson) slimApi(libs.toml4j) slimApi(libs.fastutil) - slimApi(libs.glowingentities) + implementation(libs.glowingentities) slimApi(libs.caffeine) //Random Api's diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index efaddacb9..d3035ee60 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ chrono = "22.9.10" spatial = "22.11.2" velocity = "3.4.0-SNAPSHOT" -spigot = "1.21.11-R0.2-SNAPSHOT" +spigot = "26.1.2-R0.1-SNAPSHOT" platformUtils = "e17ac2c698" # Dynamically Loaded diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index a6cbbb351..618b78a2e 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -45,6 +45,7 @@ import art.arcane.adapt.util.config.ConfigMigrationManager; import art.arcane.adapt.util.project.redis.RedisSync; import art.arcane.adapt.util.secret.SecretSplash; +import art.arcane.volmlib.integration.ReloadAware; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.inventorygui.UIWindow; @@ -71,12 +72,14 @@ import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import static art.arcane.adapt.util.director.context.AdaptationListingHandler.initializeAdaptationListings; -public class Adapt extends VolmitPlugin { +public class Adapt extends VolmitPlugin implements ReloadAware { private static final long STARTUP_SLOW_PHASE_MS = 1500L; + private final AtomicBoolean alreadyDrained = new AtomicBoolean(false); private static final boolean SLIMJAR_DEBUG = Boolean.getBoolean("adapt.debug-slimjar"); private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("adapt.disable-remapper"); public static Adapt instance; @@ -385,13 +388,47 @@ public void start() { if (getServer().getPluginManager().getPlugin("LockettePro") != null) { protectorRegistry.registerProtector(new LocketteProProtector()); } - glowingEntities = new GlowingEntities(this); + initializeGlowingEntities(); initializeAdaptationListings(); services.values().forEach(AdaptService::onEnable); services.values().forEach(this::registerListener); ConfigFileSupport.flushCreatedConfigSummary(); } + private void initializeGlowingEntities() { + try { + glowingEntities = new GlowingEntities(this); + } catch (Throwable t) { + glowingEntities = null; + warn("GlowingEntities is unavailable: " + summarizeThrowable(t) + ". Glow-based effects will be disabled."); + } + } + + private String summarizeThrowable(Throwable throwable) { + if (throwable == null) { + return "unknown"; + } + + Throwable root = throwable; + while (root.getCause() != null && root.getCause() != root) { + root = root.getCause(); + } + + StringBuilder summary = new StringBuilder(throwable.getClass().getSimpleName()); + appendMessage(summary, throwable.getMessage()); + if (root != throwable) { + summary.append(" | cause=").append(root.getClass().getSimpleName()); + appendMessage(summary, root.getMessage()); + } + return summary.toString(); + } + + private void appendMessage(StringBuilder builder, String message) { + if (message != null && !message.isBlank()) { + builder.append(": ").append(message); + } + } + private void migrateAllSkillAndAdaptationConfigs() { if (adaptServer == null || adaptServer.getSkillRegistry() == null) { return; @@ -466,6 +503,9 @@ public void stopSim() { @Override public void stop() { + if (!alreadyDrained.compareAndSet(false, true)) { + return; + } if (services != null) { services.values().forEach(AdaptService::onDisable); } @@ -497,6 +537,12 @@ public void stop() { } } + @Override + public void onPreUnload(ReloadAware.PreUnloadReason reason) { + Adapt.info("BileTools pre-unload hook fired (" + reason + "). Draining Adapt (persistence flush + services)."); + stop(); + } + private void startupPrint() { if (!AdaptConfig.get().isSplashScreen()) { return; diff --git a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java index 960d4f005..1cb8b0cde 100644 --- a/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java +++ b/src/main/java/art/arcane/adapt/api/advancement/AdvancementManager.java @@ -324,47 +324,93 @@ public void enable() { } runtimeSchedulerUnsupported.set(false); + if (!AdaptConfig.get().isAdvancements() || !enabled.compareAndSet(false, true)) { + return; + } - if (loaded.compareAndSet(false, true)) - main.load(); + try { + if (loaded.compareAndSet(false, true)) { + main.load(); + } - if (!AdaptConfig.get().isAdvancements() || !enabled.compareAndSet(false, true)) - return; - if (AdaptConfig.get().isUseSql()) { - AdaptConfig.SqlSettings sql = AdaptConfig.get().getSql(); - main.enableMySQL(sql.getUsername(), sql.getPassword(), sql.getDatabase(), sql.getHost(), sql.getPort(), sql.getPoolSize(), sql.getConnectionTimeout()); - } else { - main.enableSQLite(instance.getDataFile("data", "advancements.db")); - } - - for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { - AdaptAdvancement aa = i.buildAdvancements(); - Set set = new HashSet<>(); - RootAdvancement root = null; - - for (com.fren_gor.ultimateAdvancementAPI.advancement.Advancement a : aa.toAdvancements().reverse()) { - advancements.put(a.getKey().getKey(), a); - if (a instanceof RootAdvancement r && root == null) root = r; - else if (a instanceof BaseAdvancement b) set.add(b); + advancements.clear(); + + if (AdaptConfig.get().isUseSql()) { + AdaptConfig.SqlSettings sql = AdaptConfig.get().getSql(); + main.enableMySQL(sql.getUsername(), sql.getPassword(), sql.getDatabase(), sql.getHost(), sql.getPort(), sql.getPoolSize(), sql.getConnectionTimeout()); + } else { + main.enableSQLite(instance.getDataFile("data", "advancements.db")); } - if (root == null) { - Adapt.error("Root advancement not found for " + i.getId()); - continue; + for (Skill i : instance.getAdaptServer().getSkillRegistry().getSkills()) { + AdaptAdvancement aa = i.buildAdvancements(); + Set set = new HashSet<>(); + RootAdvancement root = null; + + for (com.fren_gor.ultimateAdvancementAPI.advancement.Advancement a : aa.toAdvancements().reverse()) { + advancements.put(a.getKey().getKey(), a); + if (a instanceof RootAdvancement r && root == null) root = r; + else if (a instanceof BaseAdvancement b) set.add(b); + } + + if (root == null) { + Adapt.error("Root advancement not found for " + i.getId()); + continue; + } + + root.getAdvancementTab().registerAdvancements(root, set); } - root.getAdvancementTab().registerAdvancements(root, set); + } catch (Throwable t) { + Adapt.warn("UltimateAdvancementAPI failed during enable: " + summarizeThrowable(t) + ". Advancements will be disabled."); + shutdownMain(t); } } public void disable() { if (main == null) { - enabled.set(false); - loaded.set(false); - runtimeSchedulerUnsupported.set(false); + resetState(); return; } - main.disable(); + shutdownMain(null); + } + + private void shutdownMain(Throwable cause) { + advancements.clear(); + + try { + main.disable(); + } catch (Throwable t) { + if (isPartialInitialisationError(t)) { + Adapt.verbose("Skipped UltimateAdvancementAPI disable cleanup after partial initialisation: " + summarizeThrowable(t)); + } else { + Adapt.warn("UltimateAdvancementAPI disable failed: " + summarizeThrowable(t)); + if (cause != null) { + Adapt.verbose("UltimateAdvancementAPI original enable failure: " + summarizeThrowable(cause)); + } + } + } finally { + resetState(); + } + } + + private boolean isPartialInitialisationError(Throwable throwable) { + Throwable current = throwable; + while (current != null) { + if (current instanceof IllegalStateException) { + String message = current.getMessage(); + if (message != null && message.contains("has not been initialised yet")) { + return true; + } + } + + current = current.getCause(); + } + + return false; + } + + private void resetState() { enabled.set(false); loaded.set(false); runtimeSchedulerUnsupported.set(false); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java index 6b7360bd8..77671cdbb 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSpelunker.java @@ -156,6 +156,9 @@ private void searchForOres(Player p, int radius) { SoundPlayer spw = SoundPlayer.of(world); spw.play(block.getLocation().add(0.5, 0, 0.5), Sound.BLOCK_BEACON_ACTIVATE, 1, 1); + if (glowingEntities == null) { + continue; + } Slime slime = block.getWorld().spawn(block.getLocation().add(0.5, 0, 0.5), Slime.class, (s) -> { s.setRotation(0, 0); s.setInvulnerable(true); diff --git a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java index d0e8d870d..1a34f7b6c 100644 --- a/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java +++ b/src/main/java/art/arcane/adapt/util/common/inventorygui/PhantomInventory.java @@ -42,6 +42,11 @@ public HashMap addItem(ItemStack... arg0) throws IllegalArgu return i.addItem(arg0); } + @Override + public int close() { + return i.close(); + } + @Override public HashMap all(Material arg0) throws IllegalArgumentException { return i.all(arg0); @@ -122,6 +127,11 @@ public InventoryHolder getHolder() { return i.getHolder(); } + @Override + public InventoryHolder getHolder(boolean useSnapshot) { + return i.getHolder(useSnapshot); + } + @Override public ItemStack getItem(int arg0) { return i.getItem(arg0); @@ -177,6 +187,11 @@ public HashMap removeItem(ItemStack... arg0) throws IllegalA return i.removeItem(arg0); } + @Override + public HashMap removeItemAnySlot(ItemStack... arg0) throws IllegalArgumentException { + return i.removeItemAnySlot(arg0); + } + @Override public void setItem(int arg0, ItemStack arg1) { i.setItem(arg0, arg1); @@ -214,4 +229,4 @@ public ItemStack[] getStorageContents() { public void setStorageContents(ItemStack[] arg0) throws IllegalArgumentException { i.setStorageContents(arg0); } -} \ No newline at end of file +} diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index 9d0f8d51c..fdec87eac 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -436,6 +436,11 @@ public String getName() { return s.getName(); } + @Override + public Component name() { + return Component.text(getName()); + } + @Override public Spigot spigot() { return s.spigot(); diff --git a/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java index 0dd5e7281..82ccbf7b2 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java +++ b/src/main/java/art/arcane/adapt/util/project/command/CommandDummy.java @@ -19,6 +19,7 @@ package art.arcane.adapt.util.project.command; +import net.kyori.adventure.text.Component; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.permissions.Permission; @@ -64,6 +65,12 @@ public String getName() { return null; } + @NotNull + @Override + public Component name() { + return Component.text(""); + } + @NotNull @Override public Spigot spigot() { diff --git a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java index a819224dd..724b23a3b 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java +++ b/src/main/java/art/arcane/adapt/util/project/command/MortarSender.java @@ -21,6 +21,7 @@ import art.arcane.adapt.util.common.format.C; import lombok.Getter; import lombok.Setter; +import net.kyori.adventure.text.Component; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -207,6 +208,11 @@ public String getName() { return s.getName(); } + @Override + public Component name() { + return Component.text(getName()); + } + @Override public Spigot spigot() { return s.spigot(); diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/util/ReflectionUtil.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/util/ReflectionUtil.java new file mode 100644 index 000000000..2b548b908 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/util/ReflectionUtil.java @@ -0,0 +1,109 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.util; + +import com.fren_gor.ultimateAdvancementAPI.util.Versions; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +public final class ReflectionUtil { + public static final String MINECRAFT_VERSION = Bukkit.getBukkitVersion().split("-")[0]; + private static final String COMPATIBLE_MINECRAFT_VERSION = Versions.normalizeMinecraftVersion(MINECRAFT_VERSION); + public static final int MINOR_VERSION = resolveMinorVersion(COMPATIBLE_MINECRAFT_VERSION); + private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName(); + public static final int VERSION = resolveVersion(COMPATIBLE_MINECRAFT_VERSION); + private static final boolean IS_1_17 = VERSION >= 17; + + private ReflectionUtil() { + throw new UnsupportedOperationException("Utility class."); + } + + private static int resolveVersion(String version) { + String[] split = version.split("\\."); + if (split.length >= 2 && "1".equals(split[0])) { + return Integer.parseInt(split[1]); + } + if (split.length >= 1) { + return Integer.parseInt(split[0]); + } + return 0; + } + + private static int resolveMinorVersion(String version) { + String[] split = version.split("\\."); + if (split.length >= 3 && "1".equals(split[0])) { + return Integer.parseInt(split[2]); + } + if (split.length >= 2 && !"1".equals(split[0])) { + return Integer.parseInt(split[1]); + } + return 0; + } + + public static boolean classExists(@NotNull String className) { + Objects.requireNonNull(className, "ClassName cannot be null."); + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + @Nullable + public static Class getNMSClass(@NotNull String name, @NotNull String mcPackage) { + String path; + if (IS_1_17) { + path = "net.minecraft." + mcPackage + '.' + name; + } else { + Optional version = Versions.getNMSVersion(); + if (version.isEmpty()) { + Bukkit.getLogger().severe("[UltimateAdvancementAPI] Unsupported Minecraft version! (" + MINECRAFT_VERSION + ")"); + return null; + } + path = "net.minecraft.server." + version.get() + '.' + name; + } + + try { + return Class.forName(path); + } catch (ClassNotFoundException e) { + Bukkit.getLogger().severe("[UltimateAdvancementAPI] Can't find NMS Class! (" + path + ")"); + return null; + } + } + + @Nullable + public static Class getCBClass(@NotNull String name) { + String cb = CRAFTBUKKIT_PACKAGE + "." + name; + try { + return Class.forName(cb); + } catch (ClassNotFoundException e) { + Bukkit.getLogger().severe("[UltimateAdvancementAPI] Can't find CB Class! (" + cb + ")"); + return null; + } + } + + @Nullable + public static Class getWrapperClass(@NotNull Class clazz) { + Optional version = Versions.getNMSVersion(); + if (version.isEmpty()) { + Bukkit.getLogger().severe("[UltimateAdvancementAPI] Unsupported Minecraft version! (" + MINECRAFT_VERSION + ")"); + return null; + } + + String name = clazz.getName(); + String validPackage = "com.fren_gor.ultimateAdvancementAPI.nms.wrappers."; + if (!name.startsWith(validPackage)) { + throw new IllegalArgumentException("Invalid class " + name + '.'); + } + String wrapper = "com.fren_gor.ultimateAdvancementAPI.nms." + version.get() + "." + name.substring(validPackage.length()) + '_' + version.get(); + try { + return Class.forName(wrapper).asSubclass(clazz); + } catch (ClassNotFoundException e) { + Bukkit.getLogger().severe("[UltimateAdvancementAPI] Can't find Wrapper Class! (" + wrapper + ")"); + return null; + } + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/MinecraftKeyWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/MinecraftKeyWrapper_v1_21_R7.java new file mode 100644 index 000000000..5a816e1cd --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/MinecraftKeyWrapper_v1_21_R7.java @@ -0,0 +1,45 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7; + +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import net.minecraft.IdentifierException; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.NotNull; + +public class MinecraftKeyWrapper_v1_21_R7 extends MinecraftKeyWrapper { + private final Identifier key; + + public MinecraftKeyWrapper_v1_21_R7(@NotNull Object key) { + this.key = (Identifier) key; + } + + public MinecraftKeyWrapper_v1_21_R7(@NotNull String namespace, @NotNull String key) { + try { + this.key = Identifier.fromNamespaceAndPath(namespace, key); + } catch (IdentifierException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + + @Override + @NotNull + public String getNamespace() { + return key.getNamespace(); + } + + @Override + @NotNull + public String getKey() { + return key.getPath(); + } + + @Override + public int compareTo(@NotNull MinecraftKeyWrapper obj) { + return key.compareTo((Identifier) obj.toNMS()); + } + + @Override + @NotNull + public Identifier toNMS() { + return key; + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java new file mode 100644 index 000000000..5e8795e4f --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java @@ -0,0 +1,116 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.CriterionProgress; +import net.minecraft.advancements.criterion.ImpossibleTrigger; +import net.minecraft.advancements.criterion.ImpossibleTrigger.TriggerInstance; +import net.minecraft.core.ClientAsset; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.Identifier; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +public final class Util { + public static final Logger ERROR = Logger.getLogger("UltimateAdvancementAPI-NMS"); + + private Util() { + throw new UnsupportedOperationException("Utility class."); + } + + @NotNull + public static Map> getAdvancementCriteria(@Range(from = 1, to = Integer.MAX_VALUE) int maxProgression) { + Preconditions.checkArgument(maxProgression >= 1, "Max progression must be >= 1."); + + Map> advCriteria = Maps.newHashMapWithExpectedSize(maxProgression); + for (int i = 0; i < maxProgression; i++) { + advCriteria.put(String.valueOf(i), new Criterion<>(new ImpossibleTrigger(), new TriggerInstance())); + } + + return advCriteria; + } + + @NotNull + public static AdvancementRequirements getAdvancementRequirements(@NotNull Map> advCriteria) { + Preconditions.checkNotNull(advCriteria, "Advancement criteria map is null."); + + List> list = new ArrayList<>(advCriteria.size()); + for (String name : advCriteria.keySet()) { + list.add(List.of(name)); + } + + return new AdvancementRequirements(list); + } + + @NotNull + public static AdvancementProgress getAdvancementProgress(@NotNull AdvancementHolder mcAdv, @Range(from = 0, to = Integer.MAX_VALUE) int progression) { + Preconditions.checkNotNull(mcAdv, "NMS Advancement is null."); + Preconditions.checkArgument(progression >= 0, "Progression must be >= 0."); + + AdvancementProgress advPrg = new AdvancementProgress(); + advPrg.update(mcAdv.value().requirements()); + + for (int i = 0; i < progression; i++) { + CriterionProgress criteriaPrg = advPrg.getCriterion(String.valueOf(i)); + if (criteriaPrg != null) { + criteriaPrg.grant(); + } + } + + return advPrg; + } + + @NotNull + public static Component fromString(@NotNull String string) { + if (string.isEmpty()) { + return CommonComponents.EMPTY; + } + + return CraftChatMessage.fromStringOrNull(string, true); + } + + @NotNull + public static Component fromComponent(@NotNull BaseComponent component) { + Component base = CraftChatMessage.fromJSONOrNull(ComponentSerializer.toString(component)); + return base == null ? CommonComponents.EMPTY : base; + } + + @Nullable + public static ClientAsset.ResourceTexture parseBackgroundTexture(@Nullable String backgroundTexture) { + if (backgroundTexture == null) { + return null; + } + + Identifier texturePath = Identifier.parse(backgroundTexture); + if (!texturePath.getPath().startsWith("textures/") || !texturePath.getPath().endsWith(".png")) { + ERROR.severe("Invalid background texture \"" + backgroundTexture + "\" (the path should be in the form \"textures/**.png\")"); + return null; + } + + Identifier id = texturePath.withPath(path -> path.substring("textures/".length(), path.length() - ".png".length())); + return new ClientAsset.ResourceTexture(id, texturePath); + } + + public static void sendTo(@NotNull Player player, @NotNull Packet packet) { + Preconditions.checkNotNull(player, "Player is null."); + Preconditions.checkNotNull(packet, "Packet is null."); + ((CraftPlayer) player).getHandle().connection.send(packet); + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java new file mode 100644 index 000000000..f877d5dd8 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java @@ -0,0 +1,97 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.advancement; + +import com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.Util; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementDisplayWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementFrameTypeWrapper; +import net.md_5.bungee.api.chat.BaseComponent; +import net.minecraft.advancements.AdvancementType; +import net.minecraft.advancements.DisplayInfo; +import net.minecraft.core.ClientAsset; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class AdvancementDisplayWrapper_v1_21_R7 extends AdvancementDisplayWrapper { + private final DisplayInfo display; + private final AdvancementFrameTypeWrapper frameType; + + public AdvancementDisplayWrapper_v1_21_R7(@NotNull ItemStack icon, @NotNull String title, @NotNull String description, @NotNull AdvancementFrameTypeWrapper frameType, float x, float y, boolean showToast, boolean announceChat, boolean hidden, @Nullable String backgroundTexture) { + ClientAsset.ResourceTexture clientAsset = Util.parseBackgroundTexture(backgroundTexture); + this.display = new DisplayInfo(CraftItemStack.asTemplate(icon), Util.fromString(title), Util.fromString(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); + this.display.setLocation(x, y); + this.frameType = frameType; + } + + public AdvancementDisplayWrapper_v1_21_R7(@NotNull ItemStack icon, @NotNull BaseComponent title, @NotNull BaseComponent description, @NotNull AdvancementFrameTypeWrapper frameType, float x, float y, boolean showToast, boolean announceChat, boolean hidden, @Nullable String backgroundTexture) { + ClientAsset.ResourceTexture clientAsset = Util.parseBackgroundTexture(backgroundTexture); + this.display = new DisplayInfo(CraftItemStack.asTemplate(icon), Util.fromComponent(title), Util.fromComponent(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); + this.display.setLocation(x, y); + this.frameType = frameType; + } + + @Override + @NotNull + public ItemStack getIcon() { + return CraftItemStack.asBukkitCopy(display.getIcon()); + } + + @Override + @NotNull + public String getTitle() { + return CraftChatMessage.fromComponent(display.getTitle()); + } + + @Override + @NotNull + public String getDescription() { + return CraftChatMessage.fromComponent(display.getDescription()); + } + + @Override + @NotNull + public AdvancementFrameTypeWrapper getAdvancementFrameType() { + return frameType; + } + + @Override + public float getX() { + return display.getX(); + } + + @Override + public float getY() { + return display.getY(); + } + + @Override + public boolean doesShowToast() { + return display.shouldShowToast(); + } + + @Override + public boolean doesAnnounceToChat() { + return display.shouldAnnounceChat(); + } + + @Override + public boolean isHidden() { + return display.isHidden(); + } + + @Override + @Nullable + public String getBackgroundTexture() { + Optional texture = display.getBackground(); + return texture.isEmpty() ? null : texture.get().texturePath().toString(); + } + + @Override + @NotNull + public DisplayInfo toNMS() { + return display; + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementFrameTypeWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementFrameTypeWrapper_v1_21_R7.java new file mode 100644 index 000000000..a16f7a306 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementFrameTypeWrapper_v1_21_R7.java @@ -0,0 +1,31 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.advancement; + +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementFrameTypeWrapper; +import net.minecraft.advancements.AdvancementType; +import org.jetbrains.annotations.NotNull; + +public class AdvancementFrameTypeWrapper_v1_21_R7 extends AdvancementFrameTypeWrapper { + private final AdvancementType mcFrameType; + private final FrameType frameType; + + public AdvancementFrameTypeWrapper_v1_21_R7(@NotNull FrameType frameType) { + this.frameType = frameType; + this.mcFrameType = switch (frameType) { + case TASK -> AdvancementType.TASK; + case GOAL -> AdvancementType.GOAL; + case CHALLENGE -> AdvancementType.CHALLENGE; + }; + } + + @Override + @NotNull + public FrameType getFrameType() { + return frameType; + } + + @Override + @NotNull + public AdvancementType toNMS() { + return mcFrameType; + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java new file mode 100644 index 000000000..df6e381fc --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java @@ -0,0 +1,90 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.advancement; + +import com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.Util; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementDisplayWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.AdvancementRewards; +import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.DisplayInfo; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import java.util.Map; +import java.util.Optional; + +public class AdvancementWrapper_v1_21_R7 extends AdvancementWrapper { + private final AdvancementHolder advancementHolder; + private final MinecraftKeyWrapper key; + private final AdvancementWrapper parent; + private final AdvancementDisplayWrapper display; + + public AdvancementWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key, @NotNull AdvancementDisplayWrapper display, @Range(from = 1, to = Integer.MAX_VALUE) int maxProgression) { + Map> advCriteria = Util.getAdvancementCriteria(maxProgression); + Advancement advancement = new Advancement(Optional.empty(), Optional.of((DisplayInfo) display.toNMS()), AdvancementRewards.EMPTY, advCriteria, Util.getAdvancementRequirements(advCriteria), false, Optional.empty()); + this.advancementHolder = new AdvancementHolder((Identifier) key.toNMS(), advancement); + this.key = key; + this.parent = null; + this.display = display; + } + + public AdvancementWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key, @NotNull AdvancementWrapper parent, @NotNull AdvancementDisplayWrapper display, @Range(from = 1, to = Integer.MAX_VALUE) int maxProgression) { + Map> advCriteria = Util.getAdvancementCriteria(maxProgression); + Advancement advancement = new Advancement(Optional.of((Identifier) parent.getKey().toNMS()), Optional.of((DisplayInfo) display.toNMS()), AdvancementRewards.EMPTY, advCriteria, Util.getAdvancementRequirements(advCriteria), false, Optional.empty()); + this.advancementHolder = new AdvancementHolder((Identifier) key.toNMS(), advancement); + this.key = key; + this.parent = parent; + this.display = display; + } + + protected AdvancementWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key, @NotNull AdvancementDisplayWrapper display, @NotNull Map> advCriteria, @NotNull AdvancementRequirements advRequirements) { + Advancement advancement = new Advancement(Optional.empty(), Optional.of((DisplayInfo) display.toNMS()), AdvancementRewards.EMPTY, advCriteria, advRequirements, false, Optional.empty()); + this.advancementHolder = new AdvancementHolder((Identifier) key.toNMS(), advancement); + this.key = key; + this.parent = null; + this.display = display; + } + + protected AdvancementWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key, @NotNull AdvancementWrapper parent, @NotNull AdvancementDisplayWrapper display, @NotNull Map> advCriteria, @NotNull AdvancementRequirements advRequirements) { + Advancement advancement = new Advancement(Optional.of((Identifier) parent.getKey().toNMS()), Optional.of((DisplayInfo) display.toNMS()), AdvancementRewards.EMPTY, advCriteria, advRequirements, false, Optional.empty()); + this.advancementHolder = new AdvancementHolder((Identifier) key.toNMS(), advancement); + this.key = key; + this.parent = parent; + this.display = display; + } + + @Override + @NotNull + public MinecraftKeyWrapper getKey() { + return key; + } + + @Override + @Nullable + public AdvancementWrapper getParent() { + return parent; + } + + @Override + @NotNull + public AdvancementDisplayWrapper getDisplay() { + return display; + } + + @Override + @Range(from = 1, to = Integer.MAX_VALUE) + public int getMaxProgression() { + return advancementHolder.value().requirements().size(); + } + + @Override + @NotNull + public AdvancementHolder toNMS() { + return advancementHolder; + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java new file mode 100644 index 000000000..f6a55fd0b --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java @@ -0,0 +1,57 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.advancement; + +import com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.Util; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementDisplayWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.PreparedAdvancementWrapper; +import net.minecraft.advancements.AdvancementRequirements; +import net.minecraft.advancements.Criterion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; + +import java.util.Map; + +public class PreparedAdvancementWrapper_v1_21_R7 extends PreparedAdvancementWrapper { + private final MinecraftKeyWrapper key; + private final AdvancementDisplayWrapper display; + private final Map> advCriteria; + private final AdvancementRequirements advRequirements; + + public PreparedAdvancementWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key, @NotNull AdvancementDisplayWrapper display, @Range(from = 1, to = Integer.MAX_VALUE) int maxProgression) { + this.key = key; + this.display = display; + this.advCriteria = Util.getAdvancementCriteria(maxProgression); + this.advRequirements = Util.getAdvancementRequirements(advCriteria); + } + + @Override + @NotNull + public MinecraftKeyWrapper getKey() { + return key; + } + + @Override + @NotNull + public AdvancementDisplayWrapper getDisplay() { + return display; + } + + @Override + @Range(from = 1, to = Integer.MAX_VALUE) + public int getMaxProgression() { + return advRequirements.size(); + } + + @Override + @NotNull + public AdvancementWrapper toRootAdvancementWrapper() { + return new AdvancementWrapper_v1_21_R7(key, display, advCriteria, advRequirements); + } + + @Override + @NotNull + public AdvancementWrapper toBaseAdvancementWrapper(@NotNull AdvancementWrapper parent) { + return new AdvancementWrapper_v1_21_R7(key, parent, display, advCriteria, advRequirements); + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutAdvancementsWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutAdvancementsWrapper_v1_21_R7.java new file mode 100644 index 000000000..abd8d0891 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutAdvancementsWrapper_v1_21_R7.java @@ -0,0 +1,49 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.packets; + +import com.fren_gor.ultimateAdvancementAPI.nms.util.ListSet; +import com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.Util; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.packets.PacketPlayOutAdvancementsWrapper; +import com.google.common.collect.Maps; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket; +import net.minecraft.resources.Identifier; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class PacketPlayOutAdvancementsWrapper_v1_21_R7 extends PacketPlayOutAdvancementsWrapper { + private final ClientboundUpdateAdvancementsPacket packet; + + public PacketPlayOutAdvancementsWrapper_v1_21_R7() { + this.packet = new ClientboundUpdateAdvancementsPacket(true, Collections.emptyList(), Collections.emptySet(), Collections.emptyMap(), true); + } + + @SuppressWarnings("unchecked") + public PacketPlayOutAdvancementsWrapper_v1_21_R7(@NotNull Map toSend) { + Map map = Maps.newHashMapWithExpectedSize(toSend.size()); + for (Entry e : toSend.entrySet()) { + AdvancementWrapper adv = e.getKey(); + map.put((Identifier) adv.getKey().toNMS(), Util.getAdvancementProgress((AdvancementHolder) adv.toNMS(), e.getValue())); + } + + this.packet = new ClientboundUpdateAdvancementsPacket(false, (Collection) ListSet.fromWrapperSet(toSend.keySet()), Collections.emptySet(), map, true); + } + + @SuppressWarnings("unchecked") + public PacketPlayOutAdvancementsWrapper_v1_21_R7(@NotNull Set toRemove) { + this.packet = new ClientboundUpdateAdvancementsPacket(false, Collections.emptyList(), (Set) ListSet.fromWrapperSet(toRemove), Collections.emptyMap(), true); + } + + @Override + public void sendTo(@NotNull Player player) { + Util.sendTo(player, packet); + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7.java new file mode 100644 index 000000000..4ed0fc1f1 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/packets/PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7.java @@ -0,0 +1,26 @@ +package com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.packets; + +import com.fren_gor.ultimateAdvancementAPI.nms.v1_21_R7.Util; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.MinecraftKeyWrapper; +import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.packets.PacketPlayOutSelectAdvancementTabWrapper; +import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket; +import net.minecraft.resources.Identifier; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7 extends PacketPlayOutSelectAdvancementTabWrapper { + private final ClientboundSelectAdvancementsTabPacket packet; + + public PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7() { + this.packet = new ClientboundSelectAdvancementsTabPacket((Identifier) null); + } + + public PacketPlayOutSelectAdvancementTabWrapper_v1_21_R7(@NotNull MinecraftKeyWrapper key) { + this.packet = new ClientboundSelectAdvancementsTabPacket((Identifier) key.toNMS()); + } + + @Override + public void sendTo(@NotNull Player player) { + Util.sendTo(player, packet); + } +} diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java new file mode 100644 index 000000000..c9dfd9a52 --- /dev/null +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java @@ -0,0 +1,171 @@ +package com.fren_gor.ultimateAdvancementAPI.util; + +import com.fren_gor.ultimateAdvancementAPI.nms.util.ReflectionUtil; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +public final class Versions { + private static final String API_VERSION = "2.7.2"; + + private static final List SUPPORTED_NMS_VERSIONS = List.of( + "v1_15_R1", + "v1_16_R1", + "v1_16_R2", + "v1_16_R3", + "v1_17_R1", + "v1_18_R1", + "v1_18_R2", + "v1_19_R1", + "v1_19_R2", + "v1_19_R3", + "v1_20_R1", + "v1_20_R2", + "v1_20_R3", + "v1_20_R4", + "v1_21_R1", + "v1_21_R2", + "v1_21_R3", + "v1_21_R4", + "v1_21_R5", + "v1_21_R6", + "v1_21_R7" + ); + + private static final Map> NMS_TO_VERSIONS = Map.ofEntries( + Map.entry("v1_15_R1", List.of("1.15", "1.15.1", "1.15.2")), + Map.entry("v1_16_R1", List.of("1.16", "1.16.1", "1.16.2")), + Map.entry("v1_16_R2", List.of("1.16.3", "1.16.4")), + Map.entry("v1_16_R3", List.of("1.16.5")), + Map.entry("v1_17_R1", List.of("1.17", "1.17.1")), + Map.entry("v1_18_R1", List.of("1.18", "1.18.1")), + Map.entry("v1_18_R2", List.of("1.18.2")), + Map.entry("v1_19_R1", List.of("1.19", "1.19.2")), + Map.entry("v1_19_R2", List.of("1.19.3")), + Map.entry("v1_19_R3", List.of("1.19.4")), + Map.entry("v1_20_R1", List.of("1.20", "1.20.1")), + Map.entry("v1_20_R2", List.of("1.20.2")), + Map.entry("v1_20_R3", List.of("1.20.3", "1.20.4")), + Map.entry("v1_20_R4", List.of("1.20.5", "1.20.6")), + Map.entry("v1_21_R1", List.of("1.21", "1.21.1")), + Map.entry("v1_21_R2", List.of("1.21.2", "1.21.3")), + Map.entry("v1_21_R3", List.of("1.21.4")), + Map.entry("v1_21_R4", List.of("1.21.5")), + Map.entry("v1_21_R5", List.of("1.21.6", "1.21.7", "1.21.8")), + Map.entry("v1_21_R6", List.of("1.21.9", "1.21.10")), + Map.entry("v1_21_R7", List.of("1.21.11", "26.1", "26.1.1", "26.1.2")) + ); + + private static final Map NMS_TO_FANCY = Map.ofEntries( + Map.entry("v1_15_R1", "1.15-1.15.2"), + Map.entry("v1_16_R1", "1.16-1.16.2"), + Map.entry("v1_16_R2", "1.16.3-1.16.4"), + Map.entry("v1_16_R3", "1.16.5"), + Map.entry("v1_17_R1", "1.17-1.17.1"), + Map.entry("v1_18_R1", "1.18-1.18.1"), + Map.entry("v1_18_R2", "1.18.2"), + Map.entry("v1_19_R1", "1.19-1.19.2"), + Map.entry("v1_19_R2", "1.19.3"), + Map.entry("v1_19_R3", "1.19.4"), + Map.entry("v1_20_R1", "1.20-1.20.1"), + Map.entry("v1_20_R2", "1.20.2"), + Map.entry("v1_20_R3", "1.20.3-1.20.4"), + Map.entry("v1_20_R4", "1.20.5-1.20.6"), + Map.entry("v1_21_R1", "1.21-1.21.1"), + Map.entry("v1_21_R2", "1.21.2-1.21.3"), + Map.entry("v1_21_R3", "1.21.4"), + Map.entry("v1_21_R4", "1.21.5"), + Map.entry("v1_21_R5", "1.21.6-1.21.8"), + Map.entry("v1_21_R6", "1.21.9-1.21.10"), + Map.entry("v1_21_R7", "1.21.11, 26.1-26.1.2") + ); + + private static final List SUPPORTED_VERSIONS = SUPPORTED_NMS_VERSIONS.stream() + .flatMap(s -> NMS_TO_VERSIONS.get(s).stream()) + .toList(); + + @Nullable + private static Optional COMPLETE_VERSION; + + private Versions() { + throw new UnsupportedOperationException("Utility class."); + } + + public static String normalizeMinecraftVersion(String version) { + if (version == null || version.isBlank()) { + return version; + } + if ("26.1".equals(version) || version.startsWith("26.1.")) { + return "1.21.11"; + } + return version; + } + + @NotNull + public static Optional getNMSVersion() { + if (COMPLETE_VERSION != null) { + return COMPLETE_VERSION; + } + + String compatibleVersion = normalizeMinecraftVersion(ReflectionUtil.MINECRAFT_VERSION); + String version = NMS_TO_VERSIONS.entrySet().stream() + .filter(e -> e.getValue().contains(compatibleVersion)) + .map(Entry::getKey) + .findFirst() + .orElse(null); + + COMPLETE_VERSION = version != null ? Optional.of(version) : Optional.empty(); + return COMPLETE_VERSION; + } + + public static String getApiVersion() { + return API_VERSION; + } + + @UnmodifiableView + @NotNull + public static List<@NotNull String> getSupportedVersions() { + return SUPPORTED_VERSIONS; + } + + @UnmodifiableView + @NotNull + public static List<@NotNull String> getSupportedNMSVersions() { + return SUPPORTED_NMS_VERSIONS; + } + + @Nullable + public static String getNMSVersionsRange() { + return getNMSVersion().map(Versions::getNMSVersionsRange).orElse(null); + } + + @Nullable + @Contract("null -> null") + public static String getNMSVersionsRange(String version) { + return NMS_TO_FANCY.get(version); + } + + @UnmodifiableView + @Nullable + public static List<@NotNull String> getNMSVersionsList() { + return getNMSVersion().map(Versions::getNMSVersionsList).orElse(null); + } + + @UnmodifiableView + @Nullable + @Contract("null -> null") + public static List<@NotNull String> getNMSVersionsList(String version) { + return NMS_TO_VERSIONS.get(version); + } + + @Contract("null -> null; !null -> !null") + public static String removeInitialV(String string) { + return string == null || string.isEmpty() || string.charAt(0) != 'v' ? string : string.substring(1); + } +} diff --git a/src/main/java/fr/skytasul/reflection/Version.java b/src/main/java/fr/skytasul/reflection/Version.java new file mode 100644 index 000000000..2f508ad6b --- /dev/null +++ b/src/main/java/fr/skytasul/reflection/Version.java @@ -0,0 +1,85 @@ +package fr.skytasul.reflection; + +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public record Version(int major, int minor, int patch) implements Comparable { + private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.(\\d+))?.*$"); + public static final Version ZERO = new Version(0, 0, 0); + + public boolean is(int major, int minor, int patch) { + return major() == major && minor() == minor && patch() == patch; + } + + public boolean is(@NotNull Version version) { + return this.equals(version); + } + + public boolean isAfter(int major, int minor, int patch) { + if (major() > major) { + return true; + } + if (major() < major) { + return false; + } + if (minor() > minor) { + return true; + } + if (minor() < minor) { + return false; + } + return patch() >= patch; + } + + public boolean isAfter(@NotNull Version version) { + return isAfter(version.major(), version.minor(), version.patch()); + } + + public boolean isBefore(int major, int minor, int patch) { + return !isAfter(major, minor, patch); + } + + public boolean isBefore(@NotNull Version version) { + return isBefore(version.major(), version.minor(), version.patch()); + } + + @Override + public int compareTo(Version o) { + if (o.equals(this)) { + return 0; + } + return isAfter(o) ? 1 : -1; + } + + @Override + public @NotNull String toString() { + return toString(false); + } + + public @NotNull String toString(boolean omitPatch) { + if (omitPatch && patch == 0) { + return "%d.%d".formatted(major, minor); + } + return "%d.%d.%d".formatted(major, minor, patch); + } + + public static @NotNull Version parse(@NotNull String string) throws IllegalArgumentException { + Matcher matcher = VERSION_PATTERN.matcher(string.trim()); + if (!matcher.matches()) { + throw new IllegalArgumentException("Malformed version: " + string); + } + + int major = Integer.parseInt(matcher.group(1)); + int minor = Integer.parseInt(matcher.group(2)); + String patchGroup = matcher.group(3); + int patch = patchGroup != null ? Integer.parseInt(patchGroup) : 0; + return new Version(major, minor, patch); + } + + public static @NotNull Version @NotNull [] parseArray(String... versions) { + return Stream.of(versions).map(Version::parse).toArray(Version[]::new); + } +} From fc077d92d4176ee0a87bb4274819592afc566297 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 10 Jun 2026 13:51:37 -0400 Subject: [PATCH 21/25] jajaja content --- .../arcane/adapt/api/EventHandlerInvoker.java | 90 ++ .../adaptation/AdaptationEventRegistrar.java | 16 +- .../adaptation/AdaptationRuntimeGuards.java | 56 +- .../api/adaptation/SimpleAdaptation.java | 12 +- .../adapt/api/notification/Notifier.java | 33 +- .../arcane/adapt/api/skill/SimpleSkill.java | 13 +- .../adapt/api/skill/SkillEventRegistrar.java | 16 +- .../adapt/api/skill/SkillRuntimeGuards.java | 20 +- .../art/arcane/adapt/api/tick/Ticker.java | 19 +- .../arcane/adapt/api/world/AdaptPlayer.java | 42 +- .../arcane/adapt/api/world/AdaptServer.java | 10 + .../arcane/adapt/api/world/PlayerData.java | 6 +- .../adapt/api/world/PlayerSkillLine.java | 16 +- .../architect/ArchitectChalkLine.java | 238 +++++ .../architect/ArchitectDemolition.java | 245 +++++ .../architect/ArchitectScaffolder.java | 230 +++++ .../architect/ArchitectSmartShape.java | 9 +- .../architect/ArchitectSteadyHands.java | 208 ++++ .../architect/ArchitectStonecutterSavant.java | 196 ++++ .../architect/ArchitectSupplyLine.java | 297 ++++++ .../architect/ArchitectWirelessRedstone.java | 23 +- .../adapt/content/adaptation/axe/AxeChop.java | 60 +- .../content/adaptation/axe/AxeTimberMark.java | 7 +- .../adaptation/chronos/ChronosAccelerate.java | 269 +++++ .../chronos/ChronosBorrowedTime.java | 263 +++++ .../adaptation/chronos/ChronosDejaVu.java | 170 ++++ .../chronos/ChronosHourglassGuard.java | 218 ++++ .../chronos/ChronosInstantRecall.java | 34 + .../chronos/ChronosInstantRecallConfig.java | 4 + .../adaptation/chronos/ChronosOvertime.java | 241 +++++ .../chronos/ChronosPocketWatch.java | 189 ++++ .../adaptation/chronos/ChronosRewind.java | 309 ++++++ .../chronos/ChronosStasisField.java | 425 ++++++++ .../chronos/ChronosTemporalEcho.java | 23 +- .../chronos/ChronosTimeInABottle.java | 5 +- .../adaptation/crafting/CraftingStations.java | 100 +- .../discovery/DiscoveryCartographerPulse.java | 19 + .../enchanting/EnchantingOfferReroll.java | 43 +- .../excavation/ExcavationBurrow.java | 316 ++++++ .../excavation/ExcavationDowsing.java | 285 ++++++ .../excavation/ExcavationEarthMover.java | 273 ++++++ .../excavation/ExcavationGraveDigger.java | 296 ++++++ .../excavation/ExcavationMudlark.java | 208 ++++ .../excavation/ExcavationOmniTool.java | 9 +- .../excavation/ExcavationSoftFall.java | 162 +++ .../excavation/ExcavationTreasureHunter.java | 244 +++++ .../excavation/ExcavationTunneler.java | 228 +++++ .../herbalism/HerbalismCompostCascade.java | 22 +- .../herbalism/HerbalismReplant.java | 79 +- .../herbalism/HerbalismSeedSower.java | 19 +- .../adaptation/pickaxe/PickaxeChisel.java | 99 +- .../adaptation/pickaxe/PickaxeDeepCore.java | 153 +++ .../adaptation/pickaxe/PickaxeGemPolish.java | 170 ++++ .../pickaxe/PickaxeObsidianRush.java | 159 +++ .../pickaxe/PickaxeQuarrySense.java | 2 +- .../pickaxe/PickaxeRepairRhythm.java | 150 +++ .../adaptation/pickaxe/PickaxeStoneSkin.java | 167 ++++ .../adaptation/pickaxe/PickaxeTunnelBore.java | 197 ++++ .../pickaxe/PickaxeUnbreakablePact.java | 144 +++ .../content/adaptation/rift/RiftAccess.java | 23 +- .../content/adaptation/rift/RiftBlink.java | 25 + .../adaptation/rift/RiftEnderchest.java | 39 +- .../rift/RiftInflatedPocketDimension.java | 11 +- .../adaptation/sword/SwordsMachete.java | 172 ++-- .../adaptation/taming/TamingBeastRecall.java | 13 + .../tragoul/TragoulCorpseExplosion.java | 225 +++++ .../tragoul/TragoulCurseOfFrailty.java | 186 ++++ .../adaptation/tragoul/TragoulDeathSense.java | 173 ++++ .../adaptation/tragoul/TragoulLastRites.java | 195 ++++ .../tragoul/TragoulMarrowArmor.java | 187 ++++ .../tragoul/TragoulPlagueBearer.java | 278 ++++++ .../tragoul/TragoulSkeletalServant.java | 928 ++++++++++++++++++ .../adaptation/tragoul/TragoulSoulSiphon.java | 202 ++++ .../adaptation/unarmed/UnarmedDisarm.java | 204 ++++ .../adaptation/unarmed/UnarmedFlurry.java | 197 ++++ .../adaptation/unarmed/UnarmedGrapple.java | 273 ++++++ .../adaptation/unarmed/UnarmedIronFists.java | 166 ++++ .../adaptation/unarmed/UnarmedMeditation.java | 206 ++++ .../unarmed/UnarmedPressurePoint.java | 173 ++++ .../adaptation/unarmed/UnarmedSecondWind.java | 194 ++++ .../unarmed/UnarmedShockwaveClap.java | 245 +++++ .../adapt/content/skill/SkillArchitect.java | 6 + .../adapt/content/skill/SkillChronos.java | 8 + .../adapt/content/skill/SkillExcavation.java | 18 + .../adapt/content/skill/SkillPickaxes.java | 7 + .../adapt/content/skill/SkillTragOul.java | 8 + .../adapt/content/skill/SkillUnarmed.java | 8 + src/main/resources/en_US.toml | 796 +++++++++++++-- 88 files changed, 12239 insertions(+), 483 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/api/EventHandlerInvoker.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectChalkLine.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectDemolition.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectScaffolder.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSteadyHands.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectStonecutterSavant.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSupplyLine.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosBorrowedTime.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosDejaVu.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosOvertime.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosPocketWatch.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosRewind.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationBurrow.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDowsing.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationEarthMover.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationMudlark.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSoftFall.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTreasureHunter.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTunneler.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDeepCore.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeObsidianRush.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeRepairRhythm.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeStoneSkin.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeUnbreakablePact.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCorpseExplosion.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCurseOfFrailty.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLastRites.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulMarrowArmor.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSoulSiphon.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedFlurry.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGrapple.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedIronFists.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedMeditation.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPressurePoint.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java create mode 100644 src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedShockwaveClap.java diff --git a/src/main/java/art/arcane/adapt/api/EventHandlerInvoker.java b/src/main/java/art/arcane/adapt/api/EventHandlerInvoker.java new file mode 100644 index 000000000..23c556e50 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/EventHandlerInvoker.java @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.plugin.EventExecutor; + +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.BiConsumer; + +public final class EventHandlerInvoker { + private EventHandlerInvoker() { + } + + public static EventExecutor createExecutor(Method method, Class eventType) { + BiConsumer direct = tryCreateDirectInvoker(method); + if (direct != null) { + return (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; + } + + try { + direct.accept(target, event); + } catch (Throwable ex) { + throw new EventException(ex); + } + }; + } + + return (target, event) -> { + if (!eventType.isAssignableFrom(event.getClass())) { + return; + } + + try { + method.invoke(target, event); + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable ex) { + throw new EventException(ex); + } + }; + } + + @SuppressWarnings("unchecked") + private static BiConsumer tryCreateDirectInvoker(Method method) { + try { + MethodHandles.Lookup caller = MethodHandles.lookup(); + MethodHandles.Lookup lookup; + try { + lookup = MethodHandles.privateLookupIn(method.getDeclaringClass(), caller); + } catch (IllegalAccessException e) { + lookup = caller; + } + + MethodHandle handle = lookup.unreflect(method); + MethodType invokedType = MethodType.methodType(BiConsumer.class); + MethodType samType = MethodType.methodType(void.class, Object.class, Object.class); + MethodType instantiatedType = MethodType.methodType(void.class, method.getDeclaringClass(), method.getParameterTypes()[0]); + return (BiConsumer) LambdaMetafactory.metafactory( + lookup, "accept", invokedType, samType, handle, instantiatedType + ).getTarget().invokeExact(); + } catch (Throwable ignored) { + return null; + } + } +} diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java index 766529ad9..a01b3203f 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationEventRegistrar.java @@ -19,12 +19,12 @@ package art.arcane.adapt.api.adaptation; import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.EventHandlerInvoker; import org.bukkit.Bukkit; import org.bukkit.event.*; import org.bukkit.plugin.EventExecutor; import org.bukkit.plugin.Plugin; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; @@ -64,19 +64,7 @@ public static boolean register(Plugin plugin, Listener listener) { continue; } - EventExecutor executor = (target, event) -> { - if (!eventType.isAssignableFrom(event.getClass())) { - return; - } - - try { - method.invoke(target, event); - } catch (InvocationTargetException ex) { - throw new EventException(ex.getCause()); - } catch (Throwable ex) { - throw new EventException(ex); - } - }; + EventExecutor executor = EventHandlerInvoker.createExecutor(method, eventType); boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index 248875a9a..90af0613c 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -46,8 +46,9 @@ import java.util.function.Predicate; final class AdaptationRuntimeGuards { - private static final Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); - private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); + private static final Map USAGE_BASELINE_XP_COOLDOWNS = new ConcurrentHashMap<>(); + private static final Map ACTIVE_LEVEL_CACHE = new ConcurrentHashMap<>(); + private static final Map USE_PERMISSION_NODES = new ConcurrentHashMap<>(); private static final Map PROTECTOR_CACHE = new ConcurrentHashMap<>(); private static final Map USAGE_CONFLICT_CACHE = new ConcurrentHashMap<>(); private static final Map LEARNED_CANDIDATE_CACHE = new ConcurrentHashMap<>(); @@ -164,7 +165,7 @@ static void awardUsageBaselineXp(Adaptation adaptation, Player p, int level) long now = M.ms(); long cooldown = Math.max(250L, cfg.getUsageBaselineCooldownMillis()); - String key = p.getUniqueId() + "|" + adaptation.getName(); + PlayerAdaptationKey key = new PlayerAdaptationKey(p.getUniqueId(), adaptation.getName()); Long next = USAGE_BASELINE_XP_COOLDOWNS.get(key); if (next != null && next > now) { return; @@ -184,7 +185,9 @@ static void awardUsageBaselineXp(Adaptation adaptation, Player p, int level) } static boolean canUse(Adaptation adaptation, AdaptPlayer player) { - Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + adaptation.getName() + "..."); + if (AdaptConfig.get().isVerbose()) { + Adapt.verbose("Checking if " + player.getPlayer().getName() + " can use " + adaptation.getName() + "..."); + } AdaptAdaptationUseEvent e = new AdaptAdaptationUseEvent(!Bukkit.isPrimaryThread(), player, adaptation); Bukkit.getServer().getPluginManager().callEvent(e); return (!e.isCancelled()); @@ -205,15 +208,16 @@ static boolean hasUsePermission(Adaptation adaptation, Player p, Adaptation "adapt.use." + n.replace("-", "")); boolean permissionSet = p.isPermissionSet(usePermission); - boolean permissionAllowed = p.hasPermission(usePermission); - Adapt.verbose("Checking use permission " + usePermission + " for " + p.getName() - + " (set=" + permissionSet + ", value=" + permissionAllowed + ")"); + if (AdaptConfig.get().isVerbose()) { + Adapt.verbose("Checking use permission " + usePermission + " for " + p.getName() + + " (set=" + permissionSet + ", value=" + p.hasPermission(usePermission) + ")"); + } if (!permissionSet) { return true; } - return permissionAllowed; + return p.hasPermission(usePermission); } static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity target) { @@ -518,7 +522,7 @@ static int getActiveLevel(Adaptation adaptation, Player p) { long tick = runtimeCacheTick(); int learnedLevel = getLevel(adaptation, p); - String key = cacheKey(adaptation, p); + PlayerAdaptationKey key = new PlayerAdaptationKey(p.getUniqueId(), adaptation.getName()); ActiveLevelCacheEntry cached = ACTIVE_LEVEL_CACHE.get(key); if (cached != null && cached.tick() == tick && cached.learnedLevel() == learnedLevel) { AbilityCheckTelemetry.recordCacheHit(); @@ -660,40 +664,49 @@ private static int resolveActiveLevelUncached(Adaptation adaptation, Player p return 0; } + boolean verbose = AdaptConfig.get().isVerbose(); if (AdaptConfig.get().blacklistedWorlds.contains(p.getWorld().getName())) { - Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + adaptation.getName()); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " is in a blacklisted world. Skipping adaptation " + adaptation.getName()); + } return 0; } if (p.getGameMode().equals(GameMode.CREATIVE) || p.getGameMode().equals(GameMode.SPECTATOR)) { - Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + adaptation.getName()); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " is in creative or spectator mode. Skipping adaptation " + adaptation.getName()); + } return 0; } if (!adaptation.checkRegion(p)) { - Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + adaptation.getName() + " permission."); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " don't have adaptation - " + adaptation.getName() + " permission."); + } return 0; } if (!adaptation.hasUsePermission(p, adaptation)) { - Adapt.verbose("Player " + p.getName() + " is blocked by use permission for adaptation " + adaptation.getName()); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " is blocked by use permission for adaptation " + adaptation.getName()); + } return 0; } if (hasUsageConflict(adaptation, p)) { return 0; } if (!adaptation.canUse(p)) { - Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + adaptation.getName()); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " can't use adaptation, This is an API restriction" + adaptation.getName()); + } return 0; } awardUsageBaselineXp(adaptation, p, level); - Adapt.verbose("Player " + p.getName() + " used adaptation " + adaptation.getName()); + if (verbose) { + Adapt.verbose("Player " + p.getName() + " used adaptation " + adaptation.getName()); + } return level; } - private static String cacheKey(Adaptation adaptation, Player player) { - return player.getUniqueId() + "|" + adaptation.getName(); - } - private static long runtimeCacheTick() { return M.ms() / 50L; } @@ -807,6 +820,9 @@ private static int buildProtectorSignature(List defaults, List extends TickedObject implements Adaptation { private int maxLevel; @@ -302,4 +300,14 @@ public AdaptAdvancement buildAdvancements() { .visibility(AdvancementVisibility.PARENT_GRANTED) .build(); } + + @Override + public final boolean equals(Object obj) { + return this == obj; + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } } diff --git a/src/main/java/art/arcane/adapt/api/notification/Notifier.java b/src/main/java/art/arcane/adapt/api/notification/Notifier.java index 0be774ba4..b77dcba6f 100644 --- a/src/main/java/art/arcane/adapt/api/notification/Notifier.java +++ b/src/main/java/art/arcane/adapt/api/notification/Notifier.java @@ -27,13 +27,13 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; import lombok.Data; -import lombok.EqualsAndHashCode; import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -@EqualsAndHashCode(callSuper = true) @Data public class Notifier extends TickedObject { private final Queue queue; @@ -150,14 +150,29 @@ private void cleanupStackedNotifications() { } private void cleanupSkills() { - for (String i : lastSkills.k()) { - if (lastSkills.get(i) == null) { // Shouldn't happen, but just in case I guess. - return; - } - if (M.ms() - lastSkills.get(i) > 10000 || (M.ms() - lastInstance > 3100 && M.ms() - lastSkills.get(i) > 3100)) { - lastSkills.remove(i); - lastSkillValues.remove(i); + if (lastSkills.isEmpty()) { + return; + } + + long now = M.ms(); + Iterator> iterator = lastSkills.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + long last = entry.getValue(); + if (now - last > 10000 || (now - lastInstance > 3100 && now - last > 3100)) { + iterator.remove(); + lastSkillValues.remove(entry.getKey()); } } } + + @Override + public final boolean equals(Object obj) { + return this == obj; + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java index 9ed00cbad..58ec2f967 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java +++ b/src/main/java/art/arcane/adapt/api/skill/SimpleSkill.java @@ -34,7 +34,6 @@ import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.format.Form; import lombok.Data; -import lombok.EqualsAndHashCode; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.EntityType; @@ -47,7 +46,6 @@ import java.util.Locale; import java.util.UUID; -@EqualsAndHashCode(callSuper = false) @Data public abstract class SimpleSkill extends TickedObject implements Skill { private final String name; @@ -58,7 +56,6 @@ public abstract class SimpleSkill extends TickedObject implements Skill { private String description; private String displayName; private Material icon; - @EqualsAndHashCode.Exclude private KList> adaptations; private KList statTrackers; private KList cachedAdvancements; @@ -590,4 +587,14 @@ private C nearestLegacyColor(String hex) { @Override public abstract void onTick(); + + @Override + public final boolean equals(Object obj) { + return this == obj; + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } } diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java index c3eb1d7c4..095b48987 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillEventRegistrar.java @@ -19,13 +19,13 @@ package art.arcane.adapt.api.skill; import art.arcane.adapt.Adapt; +import art.arcane.adapt.api.EventHandlerInvoker; import art.arcane.adapt.api.adaptation.ReceiveCancelledEvents; import org.bukkit.Bukkit; import org.bukkit.event.*; import org.bukkit.plugin.EventExecutor; import org.bukkit.plugin.Plugin; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; @@ -65,19 +65,7 @@ public static boolean register(Plugin plugin, Listener listener) { continue; } - EventExecutor executor = (target, event) -> { - if (!eventType.isAssignableFrom(event.getClass())) { - return; - } - - try { - method.invoke(target, event); - } catch (InvocationTargetException ex) { - throw new EventException(ex.getCause()); - } catch (Throwable ex) { - throw new EventException(ex); - } - }; + EventExecutor executor = EventHandlerInvoker.createExecutor(method, eventType); boolean ignoreCancelled = shouldIgnoreCancelled(method, annotation, eventType); Bukkit.getPluginManager().registerEvent(eventType, listener, annotation.priority(), executor, plugin, ignoreCancelled); diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java index a180c1cfc..3d124e84e 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java @@ -33,7 +33,12 @@ import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + final class SkillRuntimeGuards { + private static final Map USE_PERMISSION_NODES = new ConcurrentHashMap<>(); + private SkillRuntimeGuards() { } @@ -80,15 +85,16 @@ static boolean hasUsePermission(Player player, Skill skill) { if (player.isOp()) { return true; } - String usePermission = "adapt.use." + skill.getName().replaceAll("-", ""); + String usePermission = USE_PERMISSION_NODES.computeIfAbsent(skill.getName(), n -> "adapt.use." + n.replace("-", "")); boolean permissionSet = player.isPermissionSet(usePermission); - boolean permissionAllowed = player.hasPermission(usePermission); - Adapt.verbose("Checking use permission " + usePermission + " for " + player.getName() - + " (set=" + permissionSet + ", value=" + permissionAllowed + ")"); + if (AdaptConfig.get().isVerbose()) { + Adapt.verbose("Checking use permission " + usePermission + " for " + player.getName() + + " (set=" + permissionSet + ", value=" + player.hasPermission(usePermission) + ")"); + } if (!permissionSet) { return true; } - return permissionAllowed; + return player.hasPermission(usePermission); } static boolean shouldSkipPlayer(Skill skill, Player player) { @@ -198,7 +204,9 @@ static void grantXp(Skill skill, Player player, Location location, double xp, if (visualBurst && location != null && xp > 50) { skill.vfxXP(player, location, (int) xp); } - Adapt.verbose("Gave " + player.getName() + " " + xp + " xp in " + skill.getName() + " " + skill.getClass()); + if (AdaptConfig.get().isVerbose()) { + Adapt.verbose("Gave " + player.getName() + " " + xp + " xp in " + skill.getName() + " " + skill.getClass()); + } } catch (Exception ex) { Adapt.verbose("Failed to give xp to " + player.getName() + " for " + skill.getName() + " (" + xp + ")"); } diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java index 9423b54ca..495179388 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -29,7 +29,7 @@ public class Ticker { private final KList ticklist; private final KList newTicks; private final KList removeTicks; - private final Map metrics; + private final Map metrics; private final AtomicLong windowStartMs; private volatile boolean ticking; @@ -103,14 +103,14 @@ public double getWindowLoadPercent() { public List topMetrics(int limit) { int safeLimit = Math.max(1, limit); - ArrayList> entries = new ArrayList<>(metrics.entrySet()); - entries.sort(Comparator.comparingLong((Map.Entry e) -> e.getValue().totalNanos.get()).reversed()); + ArrayList entries = new ArrayList<>(metrics.values()); + entries.sort(Comparator.comparingLong((TickMetric m) -> m.totalNanos.get()).reversed()); int outputSize = Math.min(safeLimit, entries.size()); ArrayList top = new ArrayList<>(outputSize); for (int i = 0; i < outputSize; i++) { - Map.Entry entry = entries.get(i); - top.add(formatMetric(entry.getKey(), entry.getValue())); + TickMetric metric = entries.get(i); + top.add(formatMetric(metric.label, metric)); } return top; } @@ -143,6 +143,7 @@ private void tick() { for (int i = 0; i < ticklist.size(); i++) { if (ticklist.get(i).getId().equals(id)) { + metrics.remove(ticklist.get(i)); ticklist.remove(i); break; } @@ -158,8 +159,7 @@ private void recordMetric(Ticked ticked, long durationNs) { return; } - String key = ticked.getGroup() + ":" + ticked.getId(); - TickMetric metric = metrics.computeIfAbsent(key, unused -> new TickMetric()); + TickMetric metric = metrics.computeIfAbsent(ticked, t -> new TickMetric(t.getGroup() + ":" + t.getId())); metric.calls.incrementAndGet(); metric.totalNanos.addAndGet(durationNs); metric.maxNanos.updateAndGet(old -> Math.max(old, durationNs)); @@ -177,8 +177,13 @@ private String formatMetric(String key, TickMetric metric) { } private static class TickMetric { + private final String label; private final AtomicLong calls = new AtomicLong(); private final AtomicLong totalNanos = new AtomicLong(); private final AtomicLong maxNanos = new AtomicLong(); + + private TickMetric(String label) { + this.label = label; + } } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java index 851878b2a..a1111c609 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptPlayer.java @@ -34,7 +34,6 @@ import art.arcane.volmlib.util.math.RollingSequence; import art.arcane.volmlib.util.scheduling.ChronoLatch; import lombok.Data; -import lombok.EqualsAndHashCode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -46,7 +45,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -@EqualsAndHashCode(callSuper = false) @Data public class AdaptPlayer extends TickedObject { private static final Set LOAD_FAILURE_GUARD = ConcurrentHashMap.newKeySet(); @@ -307,21 +305,27 @@ public void onTick() { getServer().takeSpatial(this); Location at = player.getLocation(); - - if (lastpos != null) { - if (lastpos.getWorld().equals(at.getWorld())) { - if (lastpos.distanceSquared(at) <= 7 * 7) { - speed.put(lastpos.distance(at) / ((double) (M.ms() - lastloc) / 50D)); - velocity = velocity.clone().add(at.clone().subtract(lastpos).toVector()).multiply(0.5); - velocity.setX(Math.abs(velocity.getX()) < 0.01 ? 0 : velocity.getX()); - velocity.setY(Math.abs(velocity.getY()) < 0.01 ? 0 : velocity.getY()); - velocity.setZ(Math.abs(velocity.getZ()) < 0.01 ? 0 : velocity.getZ()); - } + long now = M.ms(); + + if (lastpos != null && lastpos.getWorld().equals(at.getWorld())) { + double dx = at.getX() - lastpos.getX(); + double dy = at.getY() - lastpos.getY(); + double dz = at.getZ() - lastpos.getZ(); + double distanceSquared = (dx * dx) + (dy * dy) + (dz * dz); + + if (distanceSquared <= 7 * 7) { + speed.put(Math.sqrt(distanceSquared) / ((double) (now - lastloc) / 50D)); + double vx = (velocity.getX() + dx) * 0.5; + double vy = (velocity.getY() + dy) * 0.5; + double vz = (velocity.getZ() + dz) * 0.5; + velocity.setX(Math.abs(vx) < 0.01 ? 0 : vx); + velocity.setY(Math.abs(vy) < 0.01 ? 0 : vy); + velocity.setZ(Math.abs(vz) < 0.01 ? 0 : vz); } } - lastpos = at.clone(); - lastloc = M.ms(); + lastpos = at; + lastloc = now; } public double getSpeed() { @@ -425,4 +429,14 @@ private void queueDelete(UUID uuid, File localFile) { Adapt.instance.getSqlManager().delete(uuid); } } + + @Override + public final boolean equals(Object obj) { + return this == obj; + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } } diff --git a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java index e49d415d3..34d621491 100644 --- a/src/main/java/art/arcane/adapt/api/world/AdaptServer.java +++ b/src/main/java/art/arcane/adapt/api/world/AdaptServer.java @@ -71,6 +71,7 @@ public class AdaptServer extends TickedObject { .build(); @Getter private final List spatialTickets = new ArrayList<>(); + private volatile int spatialTicketCount; @Getter private final SkillRegistry skillRegistry = new SkillRegistry(); @Getter @@ -97,10 +98,15 @@ public void offer(SpatialXP xp) { } synchronized (spatialTickets) { spatialTickets.add(xp); + spatialTicketCount = spatialTickets.size(); } } public void takeSpatial(AdaptPlayer p) { + if (spatialTicketCount == 0) { + return; + } + try { SpatialXP x; synchronized (spatialTickets) { @@ -114,6 +120,7 @@ public void takeSpatial(AdaptPlayer p) { if (M.ms() > x.getMs()) { synchronized (spatialTickets) { spatialTickets.remove(x); + spatialTicketCount = spatialTickets.size(); } return; } @@ -121,6 +128,7 @@ public void takeSpatial(AdaptPlayer p) { if (!p.getPlayer().getClass().getSimpleName().equals("CraftPlayer")) { synchronized (spatialTickets) { spatialTickets.remove(x); + spatialTicketCount = spatialTickets.size(); } return; } @@ -136,6 +144,7 @@ public void takeSpatial(AdaptPlayer p) { if (x.getXp() < 10) { xp += x.getXp(); spatialTickets.remove(x); + spatialTicketCount = spatialTickets.size(); } } @@ -277,6 +286,7 @@ public void onTick() { synchronized (spatialTickets) { spatialTickets.removeIf(ticket -> M.ms() > ticket.getMs()); + spatialTicketCount = spatialTickets.size(); } if (!clearLock.tryLock()) diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java index cf040d512..d436d7467 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -91,11 +91,7 @@ public double getStat(String key) { } public void addStat(String key, double amt) { - if (!stats.containsKey(key)) { - stats.put(key, amt); - } else { - stats.put(key, stats.get(key) + amt); - } + stats.merge(key, amt, Double::sum); } public void update(AdaptPlayer p) { diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index 2def63aa0..b8a4bef0e 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -38,6 +38,8 @@ @Data @NoArgsConstructor public class PlayerSkillLine { + private static final KMap SKILL_ADVANCEMENT_KEYS = new KMap<>(); + private static final KMap ADAPTATION_ADVANCEMENT_KEYS = new KMap<>(); private final KMap storage = new KMap<>(); private final KMap adaptations = new KMap<>(); private final KList multipliers = new KList<>(); @@ -71,13 +73,19 @@ public void update(AdaptPlayer p, String line, PlayerData data) { } private void grantSkillsAndAdaptations(AdaptPlayer p, String line) { - if (!p.getData().isGranted("skill_" + line) && AdaptConfig.get().isAdvancements()) { - p.getAdvancementHandler().grant("skill_" + line); + if (!AdaptConfig.get().isAdvancements()) { + return; + } + + String skillAdvancement = SKILL_ADVANCEMENT_KEYS.computeIfAbsent(line, l -> "skill_" + l); + if (!p.getData().isGranted(skillAdvancement)) { + p.getAdvancementHandler().grant(skillAdvancement); } for (String i : getAdaptations().keySet()) { - if (!p.getData().isGranted("adaptation_" + i) && AdaptConfig.get().isAdvancements()) { - p.getAdvancementHandler().grant("adaptation_" + i); + String adaptationAdvancement = ADAPTATION_ADVANCEMENT_KEYS.computeIfAbsent(i, a -> "adaptation_" + a); + if (!p.getData().isGranted(adaptationAdvancement)) { + p.getAdvancementHandler().grant(adaptationAdvancement); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectChalkLine.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectChalkLine.java new file mode 100644 index 000000000..a7e7c47e9 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectChalkLine.java @@ -0,0 +1,238 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ArchitectChalkLine extends SimpleAdaptation { + private final Map anchors; + + public ArchitectChalkLine() { + super("architect-chalk-line"); + registerConfiguration(ArchitectChalkLine.Config.class); + setDescription(Localizer.dLocalize("architect.chalk_line.description")); + setDisplayName(Localizer.dLocalize("architect.chalk_line.name")); + setIcon(Material.STRING); + setInterval(500); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + anchors = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.STRING) + .key("challenge_architect_chalk_line_50") + .title(Localizer.dLocalize("advancement.challenge_architect_chalk_line_50.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_chalk_line_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.STRING) + .key("challenge_architect_chalk_line_500") + .title(Localizer.dLocalize("advancement.challenge_architect_chalk_line_500.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_chalk_line_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_chalk_line_50", "architect.chalk-line.lines-drawn", 50, 300); + registerMilestone("challenge_architect_chalk_line_500", "architect.chalk-line.lines-drawn", 500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.chalk_line.lore1")); + v.addLore(C.GREEN + "" + (getDurationMillis(getLevelPercent(level)) / 1000) + C.GRAY + " " + Localizer.dLocalize("architect.chalk_line.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (e.getHand() != EquipmentSlot.HAND) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isItem(hand) || !hand.getType().isBlock()) { + return; + } + + UUID id = p.getUniqueId(); + ChalkAnchor existing = anchors.remove(id); + if (existing != null && M.ms() < existing.expiresAt()) { + e.setUseItemInHand(Event.Result.DENY); + e.setUseInteractedBlock(Event.Result.DENY); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_BREAK, 0.5f, 1.2f); + return; + } + + Block target = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (target == null) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, target.getLocation(), Player::isSneaking); + if (context == null) { + return; + } + + e.setUseItemInHand(Event.Result.DENY); + e.setUseInteractedBlock(Event.Result.DENY); + long expiresAt = M.ms() + getDurationMillis(getLevelPercent(context.level())); + Location anchor = target.getLocation().add(0.5, 1.1, 0.5); + anchors.put(id, new ChalkAnchor(anchor, p.getFacing(), expiresAt)); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.7f, 1.5f); + getPlayer(p).getData().addStat("architect.chalk-line.lines-drawn", 1); + xp(p, getConfig().xpPerLine); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + anchors.remove(e.getPlayer().getUniqueId()); + } + + @Override + public void onTick() { + if (anchors.isEmpty()) { + return; + } + + long now = M.ms(); + double rangeSquared = getConfig().renderRangeBlocks * getConfig().renderRangeBlocks; + for (Map.Entry entry : anchors.entrySet()) { + ChalkAnchor anchor = entry.getValue(); + if (now >= anchor.expiresAt()) { + anchors.remove(entry.getKey(), anchor); + continue; + } + + Player p = Bukkit.getPlayer(entry.getKey()); + if (p == null || !p.isOnline() || !p.getWorld().equals(anchor.anchor().getWorld())) { + continue; + } + + if (p.getLocation().distanceSquared(anchor.anchor()) > rangeSquared) { + continue; + } + + withPlayerThread(p, () -> renderLine(anchor)); + } + } + + private void renderLine(ChalkAnchor anchor) { + if (!areParticlesEnabled()) { + return; + } + + Vector direction = anchor.direction().getDirection(); + Location end = anchor.anchor().clone().add(direction.multiply(getConfig().lineLengthBlocks)); + vfxParticleLine(anchor.anchor(), end, getConfig().particlesPerLine, Particle.END_ROD); + } + + private long getDurationMillis(double factor) { + return (long) Math.max(1000, M.lerp(getConfig().minDurationSeconds, getConfig().maxDurationSeconds, factor) * 1000D); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record ChalkAnchor(Location anchor, BlockFace direction, long expiresAt) { + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click a block to snap a particle guide line along your facing axis.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Chalk Line adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Guide line lifetime in seconds at level 0 progression.", impact = "Higher values keep low-level guide lines visible longer.") + double minDurationSeconds = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Guide line lifetime in seconds at maximum level progression.", impact = "Higher values keep max-level guide lines visible longer.") + double maxDurationSeconds = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Length of the rendered guide line in blocks.", impact = "Higher values render a longer alignment guide.") + double lineLengthBlocks = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Number of particles rendered along the guide line each render pass.", impact = "Higher values draw a denser line at slightly more cost.") + int particlesPerLine = 24; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum distance in blocks from the anchor at which the line still renders.", impact = "Higher values render the guide line even when the player walks farther away.") + double renderRangeBlocks = 64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Adaptation xp granted per chalk line placed.", impact = "Higher values speed up adaptation progression from chalk lines.") + double xpPerLine = 3; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectDemolition.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectDemolition.java new file mode 100644 index 000000000..ac9e81f1e --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectDemolition.java @@ -0,0 +1,245 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; + +public class ArchitectDemolition extends SimpleAdaptation { + private final Map placed; + private final Map> order; + + public ArchitectDemolition() { + super("architect-demolition"); + registerConfiguration(ArchitectDemolition.Config.class); + setDescription(Localizer.dLocalize("architect.demolition.description")); + setDisplayName(Localizer.dLocalize("architect.demolition.name")); + setIcon(Material.TNT); + setInterval(10880); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + placed = new ConcurrentHashMap<>(); + order = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TNT) + .key("challenge_architect_demolition_500") + .title(Localizer.dLocalize("advancement.challenge_architect_demolition_500.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_demolition_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.TNT) + .key("challenge_architect_demolition_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_demolition_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_demolition_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_demolition_500", "architect.demolition.blocks-demolished", 500, 300); + registerMilestone("challenge_architect_demolition_5k", "architect.demolition.blocks-demolished", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.demolition.lore1")); + v.addLore(C.GREEN + "" + (getWindowMillis(getLevelPercent(level)) / 1000) + C.GRAY + " " + Localizer.dLocalize("architect.demolition.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (getActiveLevel(p) <= 0) { + return; + } + + UUID id = p.getUniqueId(); + Block block = e.getBlock(); + ConcurrentLinkedDeque deque = order.computeIfAbsent(id, unused -> new ConcurrentLinkedDeque<>()); + deque.addLast(block); + placed.put(block, new DemolitionMark(id, M.ms())); + int cap = getConfig().maxTrackedPerPlayer; + while (deque.size() > cap) { + Block oldest = deque.pollFirst(); + if (oldest != null) { + placed.remove(oldest); + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockDamageEvent e) { + if (placed.isEmpty()) { + return; + } + + DemolitionMark mark = placed.get(e.getBlock()); + if (mark == null) { + return; + } + + Player p = e.getPlayer(); + if (!mark.owner().equals(p.getUniqueId())) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + if (M.ms() - mark.at() > getWindowMillis(getLevelPercent(context.level()))) { + placed.remove(e.getBlock(), mark); + return; + } + + e.setInstaBreak(true); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(e.getBlock().getLocation(), Sound.BLOCK_AMETHYST_BLOCK_HIT, 0.5f, 0.7f); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (placed.isEmpty()) { + return; + } + + DemolitionMark mark = placed.remove(e.getBlock()); + if (mark == null) { + return; + } + + Player p = e.getPlayer(); + if (!mark.owner().equals(p.getUniqueId())) { + return; + } + + int level = getActiveBlockBreakLevel(p, e.getBlock().getLocation()); + if (level <= 0) { + return; + } + + if (M.ms() - mark.at() > getWindowMillis(getLevelPercent(level))) { + return; + } + + Material type = e.getBlock().getType(); + if (type.isAir() || !type.isItem()) { + return; + } + + e.setDropItems(false); + e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), new ItemStack(type)); + if (areParticlesEnabled()) { + vfxCuboidOutline(e.getBlock(), Particle.SCRAPE); + } + + getPlayer(p).getData().addStat("architect.demolition.blocks-demolished", 1); + xp(p, getConfig().xpPerDemolish); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + ConcurrentLinkedDeque deque = order.remove(e.getPlayer().getUniqueId()); + if (deque == null) { + return; + } + + for (Block block : deque) { + placed.remove(block); + } + } + + private long getWindowMillis(double factor) { + return (long) Math.max(1000, M.lerp(getConfig().minWindowSeconds, getConfig().maxWindowSeconds, factor) * 1000D); + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record DemolitionMark(UUID owner, long at) { + } + + @NoArgsConstructor + @ConfigDescription("Blocks you recently placed break near-instantly and always drop their item.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Demolition adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "How many seconds a placement counts as recent at level 0 progression.", impact = "Higher values let low-level players instabreak older placements.") + double minWindowSeconds = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "How many seconds a placement counts as recent at maximum level progression.", impact = "Higher values let max-level players instabreak older placements.") + double maxWindowSeconds = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum number of recent placements tracked per player.", impact = "Higher values remember more placements at slightly more memory cost.") + int maxTrackedPerPlayer = 64; + @art.arcane.adapt.util.config.ConfigDoc(value = "Adaptation xp granted per demolished block.", impact = "Higher values speed up adaptation progression from demolition.") + double xpPerDemolish = 1; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectScaffolder.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectScaffolder.java new file mode 100644 index 000000000..0e8c023cd --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectScaffolder.java @@ -0,0 +1,230 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ArchitectScaffolder extends SimpleAdaptation { + private final Map scaffolds; + private final Map> byPlayer; + + public ArchitectScaffolder() { + super("architect-scaffolder"); + registerConfiguration(ArchitectScaffolder.Config.class); + setDescription(Localizer.dLocalize("architect.scaffolder.description")); + setDisplayName(Localizer.dLocalize("architect.scaffolder.name")); + setIcon(Material.SCAFFOLDING); + setInterval(9220); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + scaffolds = new ConcurrentHashMap<>(); + byPlayer = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SCAFFOLDING) + .key("challenge_architect_scaffolder_500") + .title(Localizer.dLocalize("advancement.challenge_architect_scaffolder_500.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_scaffolder_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SCAFFOLDING) + .key("challenge_architect_scaffolder_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_scaffolder_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_scaffolder_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_scaffolder_500", "architect.scaffolder.blocks-scaffolded", 500, 300); + registerMilestone("challenge_architect_scaffolder_5k", "architect.scaffolder.blocks-scaffolded", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.scaffolder.lore1")); + v.addLore(C.GREEN + "" + getDurationSeconds(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("architect.scaffolder.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + Block block = e.getBlock(); + Adaptation.BlockActionContext context = resolveBlockPlaceContext(p, block.getLocation(), Player::isSneaking); + if (context == null) { + return; + } + + Material type = block.getType(); + if (type.isAir() || !type.isBlock()) { + return; + } + + UUID id = p.getUniqueId(); + Set mine = byPlayer.computeIfAbsent(id, unused -> ConcurrentHashMap.newKeySet()); + if (mine.size() >= getConfig().maxScaffoldsPerPlayer) { + return; + } + + mine.add(block); + scaffolds.put(block, new ScaffoldMark(id, type)); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 0.6f, 1.6f); + if (areParticlesEnabled()) { + vfxCuboidOutline(block, Particle.CLOUD); + } + + getPlayer(p).getData().addStat("architect.scaffolder.blocks-scaffolded", 1); + int delayTicks = getDurationSeconds(getLevelPercent(context.level())) * 20; + J.runAt(block.getLocation(), () -> removeScaffold(block), delayTicks); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (scaffolds.isEmpty()) { + return; + } + + ScaffoldMark mark = scaffolds.remove(e.getBlock()); + if (mark == null) { + return; + } + + Set mine = byPlayer.get(mark.owner()); + if (mine != null) { + mine.remove(e.getBlock()); + } + } + + @EventHandler + public void on(PlayerQuitEvent e) { + byPlayer.remove(e.getPlayer().getUniqueId()); + } + + private void removeScaffold(Block block) { + ScaffoldMark mark = scaffolds.remove(block); + if (mark == null) { + return; + } + + Set mine = byPlayer.get(mark.owner()); + if (mine != null) { + mine.remove(block); + } + + if (block.getType() != mark.material()) { + return; + } + + block.setType(Material.AIR); + SoundPlayer spw = SoundPlayer.of(block.getWorld()); + spw.play(block.getLocation(), Sound.BLOCK_DEEPSLATE_BREAK, 0.6f, 1.4f); + if (areParticlesEnabled()) { + vfxCuboidOutline(block, Particle.REVERSE_PORTAL); + } + + ItemStack refund = new ItemStack(mark.material()); + Player owner = Bukkit.getPlayer(mark.owner()); + if (owner != null && owner.isOnline()) { + J.runEntity(owner, () -> safeGiveItem(owner, refund)); + } else { + block.getWorld().dropItemNaturally(block.getLocation(), refund); + } + } + + private int getDurationSeconds(double factor) { + return (int) Math.max(1, M.lerp(getConfig().minDurationSeconds, getConfig().maxDurationSeconds, factor)); + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record ScaffoldMark(UUID owner, Material material) { + } + + @NoArgsConstructor + @ConfigDescription("Sneak-place blocks as temporary scaffolds that dissolve and refund themselves.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Scaffolder adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaffold lifetime in seconds at level 0 progression.", impact = "Higher values keep low-level scaffolds in the world longer before dissolving.") + int minDurationSeconds = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaffold lifetime in seconds at maximum level progression.", impact = "Higher values keep max-level scaffolds in the world longer before dissolving.") + int maxDurationSeconds = 30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum number of active scaffolds tracked per player.", impact = "Higher values let players keep more temporary scaffolds at once.") + int maxScaffoldsPerPlayer = 24; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java index 4da03afa4..aeef79f3c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSmartShape.java @@ -106,7 +106,8 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.LEFT_CLICK_BLOCK || e.getClickedBlock() == null) { + Action action = e.getAction(); + if (action != Action.LEFT_CLICK_BLOCK && action != Action.LEFT_CLICK_AIR) { return; } @@ -125,7 +126,11 @@ public void on(PlayerInteractEvent e) { return; } - Block target = e.getClickedBlock(); + Block target = action == Action.LEFT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (target == null) { + return; + } + if (!canBlockPlace(p, target.getLocation())) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSteadyHands.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSteadyHands.java new file mode 100644 index 000000000..72313a853 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSteadyHands.java @@ -0,0 +1,208 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerVelocityEvent; +import org.bukkit.potion.PotionEffect; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ArchitectSteadyHands extends SimpleAdaptation { + private final Map lastBridge; + + public ArchitectSteadyHands() { + super("architect-steady-hands"); + registerConfiguration(ArchitectSteadyHands.Config.class); + setDescription(Localizer.dLocalize("architect.steady_hands.description")); + setDisplayName(Localizer.dLocalize("architect.steady_hands.name")); + setIcon(Material.LIGHTNING_ROD); + setInterval(10440); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + lastBridge = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.LIGHTNING_ROD) + .key("challenge_architect_steady_hands_500") + .title(Localizer.dLocalize("advancement.challenge_architect_steady_hands_500.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_steady_hands_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.LIGHTNING_ROD) + .key("challenge_architect_steady_hands_5k") + .title(Localizer.dLocalize("advancement.challenge_architect_steady_hands_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_steady_hands_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_steady_hands_500", "architect.steady-hands.bridge-blocks", 500, 300); + registerMilestone("challenge_architect_steady_hands_5k", "architect.steady-hands.bridge-blocks", 5000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.steady_hands.lore1")); + v.addLore(C.GREEN + "" + (int) getShieldedHeight(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("architect.steady_hands.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + if (!e.getBlock().getRelative(BlockFace.DOWN).getType().isAir()) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockPlaceContext(p, e.getBlock().getLocation(), Player::isSneaking); + if (context == null) { + return; + } + + lastBridge.put(p.getUniqueId(), M.ms()); + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, getConfig().hasteDurationTicks, getConfig().hasteAmplifier, false, false, true)); + getPlayer(p).getData().addStat("architect.steady-hands.bridge-blocks", 1); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void on(PlayerVelocityEvent e) { + Player p = e.getPlayer(); + Long last = lastBridge.get(p.getUniqueId()); + if (last == null || M.ms() - last > getConfig().bridgeGraceMillis) { + return; + } + + if (!p.isSneaking()) { + return; + } + + if (getActiveLevel(p) <= 0) { + return; + } + + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (e.getCause() != EntityDamageEvent.DamageCause.FALL || !(e.getEntity() instanceof Player p)) { + return; + } + + Long last = lastBridge.get(p.getUniqueId()); + if (last == null || M.ms() - last > getConfig().bridgeGraceMillis) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + double shielded = getShieldedHeight(getLevelPercent(level)); + if (e.getDamage() <= shielded) { + e.setCancelled(true); + p.setFallDistance(0); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_WOOL_BREAK, 0.5f, 0.8f); + return; + } + + e.setDamage(Math.max(0, e.getDamage() - shielded)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + lastBridge.remove(e.getPlayer().getUniqueId()); + } + + private double getShieldedHeight(double factor) { + return M.lerp(getConfig().minShieldedBlocks, getConfig().maxShieldedBlocks, factor); + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Stay rock-steady while bridging: no knockback and reduced fall damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fall damage shielded in blocks at level 0 progression.", impact = "Higher values absorb more fall damage for low-level players while bridging.") + double minShieldedBlocks = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fall damage shielded in blocks at maximum level progression.", impact = "Higher values absorb more fall damage for max-level players while bridging.") + double maxShieldedBlocks = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "How long in milliseconds after a bridge placement the protections stay active.", impact = "Higher values keep knockback and fall protection up longer between placements.") + long bridgeGraceMillis = 4000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the haste boost granted per bridge placement.", impact = "Higher values keep the placement-speed boost active longer.") + int hasteDurationTicks = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier of the haste boost granted per bridge placement.", impact = "Higher values strengthen the placement-speed boost.") + int hasteAmplifier = 0; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectStonecutterSavant.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectStonecutterSavant.java new file mode 100644 index 000000000..47d3499ee --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectStonecutterSavant.java @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ArchitectStonecutterSavant extends SimpleAdaptation { + private final Map cooldowns; + + public ArchitectStonecutterSavant() { + super("architect-stonecutter-savant"); + registerConfiguration(ArchitectStonecutterSavant.Config.class); + setDescription(Localizer.dLocalize("architect.stonecutter_savant.description")); + setDisplayName(Localizer.dLocalize("architect.stonecutter_savant.name")); + setIcon(Material.STONECUTTER); + setInterval(24420); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + cooldowns = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.STONECUTTER) + .key("challenge_architect_stonecutter_savant_50") + .title(Localizer.dLocalize("advancement.challenge_architect_stonecutter_savant_50.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_stonecutter_savant_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.STONECUTTER) + .key("challenge_architect_stonecutter_savant_500") + .title(Localizer.dLocalize("advancement.challenge_architect_stonecutter_savant_500.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_stonecutter_savant_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_stonecutter_savant_50", "architect.stonecutter-savant.uses", 50, 300); + registerMilestone("challenge_architect_stonecutter_savant_500", "architect.stonecutter-savant.uses", 500, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.stonecutter_savant.lore1")); + v.addLore(C.GREEN + "" + (getCooldownMillis(getLevelPercent(level)) / 1000) + C.GRAY + " " + Localizer.dLocalize("architect.stonecutter_savant.lore2")); + v.addLore(C.YELLOW + Localizer.dLocalize(getConfig().requireOffhand ? "architect.stonecutter_savant.lore4" : "architect.stonecutter_savant.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; + } + + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + PlayerInventory inventory = p.getInventory(); + ItemStack hand = inventory.getItemInMainHand(); + if (isItem(hand) && hand.getType() != Material.AIR) { + return; + } + + if (!hasStonecutter(inventory)) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation(), Player::isSneaking); + if (context == null) { + return; + } + + UUID id = p.getUniqueId(); + long now = M.ms(); + Long until = cooldowns.get(id); + if (until != null && now < until) { + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.3f, 0.7f); + return; + } + + cooldowns.put(id, now + getCooldownMillis(getLevelPercent(context.level()))); + withPlayerThread(p, () -> { + p.openStonecutter(null, true); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_GRINDSTONE_USE, 0.8f, 1.4f); + getPlayer(p).getData().addStat("architect.stonecutter-savant.uses", 1); + xp(p, getConfig().xpPerUse); + }); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + private boolean hasStonecutter(PlayerInventory inventory) { + if (getConfig().requireOffhand) { + return inventory.getItemInOffHand().getType() == Material.STONECUTTER; + } + + return inventory.contains(Material.STONECUTTER); + } + + private long getCooldownMillis(double factor) { + return (long) Math.max(1000, M.lerp(getConfig().maxCooldownSeconds, getConfig().minCooldownSeconds, factor) * 1000D); + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-punch the air with an empty hand while carrying a stonecutter to open it anywhere.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires the stonecutter item to be in the offhand specifically.", impact = "True only accepts a stonecutter held in the offhand; false accepts a stonecutter anywhere in the inventory.") + boolean requireOffhand = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown in seconds at level 0 progression.", impact = "Higher values make low-level players wait longer between uses.") + double maxCooldownSeconds = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown in seconds at maximum level progression.", impact = "Lower values let max-level players open the stonecutter more often.") + double minCooldownSeconds = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Adaptation xp granted per stonecutter opened.", impact = "Higher values speed up adaptation progression from uses.") + double xpPerUse = 2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSupplyLine.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSupplyLine.java new file mode 100644 index 000000000..b23703c26 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectSupplyLine.java @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.architect; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.Tag; +import org.bukkit.block.ShulkerBox; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BundleMeta; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ArchitectSupplyLine extends SimpleAdaptation { + private final Map windows; + + public ArchitectSupplyLine() { + super("architect-supply-line"); + registerConfiguration(ArchitectSupplyLine.Config.class); + setDescription(Localizer.dLocalize("architect.supply_line.description")); + setDisplayName(Localizer.dLocalize("architect.supply_line.name")); + setIcon(Material.SHULKER_BOX); + setInterval(13780); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + windows = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SHULKER_BOX) + .key("challenge_architect_supply_line_100") + .title(Localizer.dLocalize("advancement.challenge_architect_supply_line_100.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_supply_line_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SHULKER_BOX) + .key("challenge_architect_supply_line_1k") + .title(Localizer.dLocalize("advancement.challenge_architect_supply_line_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_architect_supply_line_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_architect_supply_line_100", "architect.supply-line.refills", 100, 300); + registerMilestone("challenge_architect_supply_line_1k", "architect.supply-line.refills", 1000, 1000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("architect.supply_line.lore1")); + v.addLore(C.GREEN + "" + getRefillsPerMinute(getLevelPercent(level)) + C.GRAY + " " + Localizer.dLocalize("architect.supply_line.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + ItemStack hand = e.getItemInHand(); + if (hand.getAmount() > 1) { + return; + } + + Material material = hand.getType(); + if (!material.isBlock() || material.isAir()) { + return; + } + + Player p = e.getPlayer(); + Adaptation.BlockActionContext context = resolveBlockPlaceContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + if (!tryConsumeRefill(p.getUniqueId(), getRefillsPerMinute(getLevelPercent(context.level())))) { + return; + } + + EquipmentSlot slot = e.getHand(); + J.runEntity(p, () -> refill(p, slot, material), 1); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + windows.remove(e.getPlayer().getUniqueId()); + } + + private boolean tryConsumeRefill(UUID id, int allowed) { + long now = M.ms(); + RefillWindow current = windows.get(id); + if (current == null || now - current.start() >= 60000L) { + windows.put(id, new RefillWindow(now, 1)); + return true; + } + + if (current.used() >= allowed) { + return false; + } + + windows.put(id, new RefillWindow(current.start(), current.used() + 1)); + return true; + } + + private void refill(Player p, EquipmentSlot slot, Material material) { + if (!p.isOnline()) { + return; + } + + ItemStack current = slot == EquipmentSlot.OFF_HAND ? p.getInventory().getItemInOffHand() : p.getInventory().getItemInMainHand(); + if (current != null && !current.getType().isAir()) { + return; + } + + ItemStack pulled = pullMatching(p, material); + if (pulled == null) { + return; + } + + if (slot == EquipmentSlot.OFF_HAND) { + p.getInventory().setItemInOffHand(pulled); + } else { + p.getInventory().setItemInMainHand(pulled); + } + + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.BLOCK_COMPOSTER_FILL, 0.6f, 1.3f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.WAX_ON, p.getLocation().add(0, 1, 0), 6, 0.3, 0.3, 0.3, 0); + } + + getPlayer(p).getData().addStat("architect.supply-line.refills", 1); + xp(p, getConfig().xpPerRefill); + } + + private ItemStack pullMatching(Player p, Material material) { + ItemStack[] contents = p.getInventory().getStorageContents(); + for (int i = 0; i < contents.length; i++) { + ItemStack candidate = contents[i]; + if (candidate == null || candidate.getType().isAir()) { + continue; + } + + ItemStack pulled = pullFromShulker(candidate, material); + if (pulled == null) { + pulled = pullFromBundle(candidate, material); + } + + if (pulled != null) { + return pulled; + } + } + + return null; + } + + private ItemStack pullFromShulker(ItemStack container, Material material) { + if (!Tag.SHULKER_BOXES.isTagged(container.getType())) { + return null; + } + + if (!(container.getItemMeta() instanceof BlockStateMeta meta) || !(meta.getBlockState() instanceof ShulkerBox box)) { + return null; + } + + Inventory inv = box.getInventory(); + for (int slot = 0; slot < inv.getSize(); slot++) { + ItemStack content = inv.getItem(slot); + if (content == null || content.getType() != material) { + continue; + } + + ItemStack pulled = content.clone(); + inv.setItem(slot, null); + meta.setBlockState(box); + container.setItemMeta(meta); + return pulled; + } + + return null; + } + + private ItemStack pullFromBundle(ItemStack container, Material material) { + if (!(container.getItemMeta() instanceof BundleMeta meta)) { + return null; + } + + List items = meta.getItems(); + if (items.isEmpty()) { + return null; + } + + List remaining = new ArrayList<>(items.size()); + ItemStack pulled = null; + for (ItemStack item : items) { + if (pulled == null && item != null && item.getType() == material) { + pulled = item.clone(); + continue; + } + + remaining.add(item); + } + + if (pulled == null) { + return null; + } + + meta.setItems(remaining); + container.setItemMeta(meta); + return pulled; + } + + private int getRefillsPerMinute(double factor) { + return (int) Math.max(1, M.lerp(getConfig().minRefillsPerMinute, getConfig().maxRefillsPerMinute, factor)); + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record RefillWindow(long start, int used) { + } + + @NoArgsConstructor + @ConfigDescription("Automatically refill your hand from shulker boxes or bundles when a placed stack runs out.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Particles for the Architect Supply Line adaptation.", impact = "True enables this behavior and false disables it.") + boolean showParticles = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hand refills allowed per minute at level 0 progression.", impact = "Higher values let low-level players refill more often.") + int minRefillsPerMinute = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hand refills allowed per minute at maximum level progression.", impact = "Higher values let max-level players refill more often.") + int maxRefillsPerMinute = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Adaptation xp granted per successful refill.", impact = "Higher values speed up adaptation progression from refills.") + double xpPerRefill = 2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java index e3700eaea..e6f59e35c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/architect/ArchitectWirelessRedstone.java @@ -140,7 +140,7 @@ public void onPlayerInteract(PlayerInteractEvent event) { } switch (event.getAction()) { - case LEFT_CLICK_BLOCK -> handleLeftClickBlock(event, player); + case LEFT_CLICK_BLOCK, LEFT_CLICK_AIR -> handleLeftClick(event, player); case RIGHT_CLICK_AIR, RIGHT_CLICK_BLOCK -> handleRightClick(event, player); } @@ -153,27 +153,30 @@ private boolean isRedstoneTorch(ItemStack item) { } - private void handleLeftClickBlock(PlayerInteractEvent event, Player player) { - Adapt.verbose("Player " + player.getName() + " is left clicking a block"); + private void handleLeftClick(PlayerInteractEvent event, Player player) { + Adapt.verbose("Player " + player.getName() + " is left clicking"); if (!player.isSneaking()) { return; } - // main hand only if (event.getHand() != EquipmentSlot.HAND) { return; } - if (event.getClickedBlock() == null) { - SoundPlayer sp = SoundPlayer.of(player); - sp.play(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.1f, 0.9f); + Block target = event.getClickedBlock(); + if (target == null) { + target = player.getTargetBlockExact(5); + } + + if (target == null) { return; } - // prevent breaking block - event.setUseItemInHand(Result.DENY); + if (event.getAction() == Action.LEFT_CLICK_BLOCK) { + event.setUseItemInHand(Result.DENY); + } - Location location = new Location(event.getClickedBlock().getWorld(), event.getClickedBlock().getX(), event.getClickedBlock().getY(), event.getClickedBlock().getZ()); + Location location = new Location(target.getWorld(), target.getX(), target.getY(), target.getZ()); linkTorch(player, location); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java index 83f1f66b4..4098d6c2e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeChop.java @@ -40,6 +40,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; public class AxeChop extends SimpleAdaptation { @@ -92,33 +93,50 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } + Player p = e.getPlayer(); if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { return; } - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isAxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { - if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { - return; - } - BlockData b = e.getClickedBlock().getBlockData(); - if (isLog(new ItemStack(b.getMaterial()))) { - e.setCancelled(true); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.ITEM_AXE_STRIP, 1.25f, 0.6f); - int logsChopped = 0; - for (int i = 0; i < getLevel(p); i++) { - if (breakStuff(e.getClickedBlock(), getRange(getLevel(p)), p)) { - logsChopped++; - p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); - damageHand(p, getDamagePerBlock(getLevelPercent(p))); - } + if (!isAxe(p.getInventory().getItemInMainHand()) || !hasActiveAdaptation(p)) { + return; + } + + Block target = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (target == null) { + return; + } + + if (!canBlockBreak(p, target.getLocation())) { + return; + } + + BlockData b = target.getBlockData(); + if (isLog(new ItemStack(b.getMaterial()))) { + e.setCancelled(true); + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.ITEM_AXE_STRIP, 1.25f, 0.6f); + int logsChopped = 0; + for (int i = 0; i < getLevel(p); i++) { + if (breakStuff(target, getRange(getLevel(p)), p)) { + logsChopped++; + p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); + damageHand(p, getDamagePerBlock(getLevelPercent(p))); } - if (logsChopped > 0) { - getPlayer(p).getData().addStat("axe.chop.trees-felled", 1); - if (logsChopped >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_chop_one_swing")) { - getPlayer(p).getAdvancementHandler().grant("challenge_axe_chop_one_swing"); - } + } + if (logsChopped > 0) { + getPlayer(p).getData().addStat("axe.chop.trees-felled", 1); + if (logsChopped >= 30 && AdaptConfig.get().isAdvancements() && !getPlayer(p).getData().isGranted("challenge_axe_chop_one_swing")) { + getPlayer(p).getAdvancementHandler().grant("challenge_axe_chop_one_swing"); } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java index 409ca6ac6..722824f8d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeTimberMark.java @@ -94,7 +94,8 @@ public void on(PlayerQuitEvent e) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { return; } @@ -108,8 +109,8 @@ public void on(PlayerInteractEvent e) { return; } - Block clicked = e.getClickedBlock(); - if (!isLog(new org.bukkit.inventory.ItemStack(clicked.getType()))) { + Block clicked = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (clicked == null || !isLog(new org.bukkit.inventory.ItemStack(clicked.getType()))) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java new file mode 100644 index 000000000..91e2a304f --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java @@ -0,0 +1,269 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.block.Furnace; +import org.bukkit.block.data.Ageable; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.concurrent.ThreadLocalRandom; + +public class ChronosAccelerate extends SimpleAdaptation { + public ChronosAccelerate() { + super("chronos-accelerate"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.accelerate.description")); + setDisplayName(Localizer.dLocalize("chronos.accelerate.name")); + setIcon(Material.SUGAR); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(getConfig().pulseIntervalMillis); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SUGAR) + .key("challenge_chronos_accelerate_1k") + .title(Localizer.dLocalize("advancement.challenge_chronos_accelerate_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_accelerate_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_accelerate_1k", "chronos.accelerate.blocks-accelerated", 1000, 600); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + " " + Localizer.dLocalize("chronos.accelerate.lore1")); + v.addLore(C.YELLOW + "+ " + Math.round(getGrowChance(level) * 100D) + "% " + Localizer.dLocalize("chronos.accelerate.lore2")); + v.addLore(C.GRAY + "* " + Math.round(getCookBoostFraction(level) * 100D) + "% " + Localizer.dLocalize("chronos.accelerate.lore3")); + } + + private double getRadius(int level) { + return getConfig().baseRadius + (Math.max(1, level) * getConfig().radiusPerLevel); + } + + private double getGrowChance(int level) { + return Math.max(0D, Math.min(1D, getConfig().baseGrowChance + (Math.max(1, level) * getConfig().growChancePerLevel))); + } + + private int getSamples(int level) { + return Math.min(getConfig().maxSamplesPerPulse, getConfig().baseSamplesPerPulse + level); + } + + private double getCookBoostFraction(int level) { + return Math.min(getConfig().maxCookBoostFraction, + getConfig().baseCookBoostFraction + (Math.max(1, level) * getConfig().cookBoostFractionPerLevel)); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (AdaptPlayer adaptPlayer : learnedCandidates(now)) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } + + Location center = p.getLocation().clone(); + Runnable task = () -> accelerateAround(p, center, level); + if (J.isFoliaThreading()) { + J.runAt(center, task); + } else { + task.run(); + } + } + } + + private void accelerateAround(Player p, Location center, int level) { + World world = center.getWorld(); + if (world == null) { + return; + } + + ThreadLocalRandom random = ThreadLocalRandom.current(); + int radius = Math.max(1, (int) Math.round(getRadius(level))); + int samples = getSamples(level); + double growChance = getGrowChance(level); + int accelerated = 0; + + for (int i = 0; i < samples; i++) { + int x = center.getBlockX() + random.nextInt(-radius, radius + 1); + int y = center.getBlockY() + random.nextInt(-2, 3); + int z = center.getBlockZ() + random.nextInt(-radius, radius + 1); + Block block = world.getBlockAt(x, y, z); + Material type = block.getType(); + if (type.isAir()) { + continue; + } + + if (type == Material.FURNACE || type == Material.BLAST_FURNACE || type == Material.SMOKER) { + if (block.getState() instanceof Furnace furnace && accelerateFurnace(furnace, level)) { + accelerated++; + spawnAccelerationParticle(world, x, y, z); + } + continue; + } + + if (type == Material.BREWING_STAND) { + if (block.getState() instanceof BrewingStand stand && accelerateBrewingStand(stand, level)) { + accelerated++; + spawnAccelerationParticle(world, x, y, z); + } + continue; + } + + BlockData data = block.getBlockData(); + if (data instanceof Ageable ageable + && ageable.getAge() < ageable.getMaximumAge() + && random.nextDouble() < growChance) { + ageable.setAge(ageable.getAge() + 1); + block.setBlockData(data, true); + accelerated++; + spawnAccelerationParticle(world, x, y, z); + } + } + + if (accelerated > 0) { + getPlayer(p).getData().addStat("chronos.accelerate.blocks-accelerated", accelerated); + xpSilent(p, accelerated * getConfig().xpPerAcceleratedBlock, "chronos:accelerate"); + } + } + + private boolean accelerateFurnace(Furnace furnace, int level) { + if (furnace.getBurnTime() <= 0) { + return false; + } + + ItemStack smelting = furnace.getInventory().getSmelting(); + if (smelting == null || smelting.getType().isAir()) { + return false; + } + + int total = furnace.getCookTimeTotal(); + int current = furnace.getCookTime(); + if (total <= 0 || current >= total - 1) { + return false; + } + + int bonus = (int) Math.round(total * getCookBoostFraction(level)); + if (bonus <= 0) { + return false; + } + + furnace.setCookTime((short) Math.min(total - 1, current + bonus)); + furnace.update(true, true); + return true; + } + + private boolean accelerateBrewingStand(BrewingStand stand, int level) { + int brewingTime = stand.getBrewingTime(); + if (brewingTime <= 1) { + return false; + } + + int bonus = (int) Math.round(400D * getCookBoostFraction(level)); + if (bonus <= 0) { + return false; + } + + stand.setBrewingTime(Math.max(1, brewingTime - bonus)); + stand.update(true, true); + return true; + } + + private void spawnAccelerationParticle(World world, int x, int y, int z) { + if (areParticlesEnabled()) { + world.spawnParticle(Particle.HAPPY_VILLAGER, x + 0.5, y + 0.5, z + 0.5, 2, 0.2, 0.2, 0.2, 0); + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Passively accelerate time around you, occasionally growing nearby crops and speeding furnaces and brewing stands.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between acceleration pulses.", impact = "Lower values pulse more often at more server cost.") + long pulseIntervalMillis = 3000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base aura radius in blocks.", impact = "Higher values sample blocks from a wider area.") + double baseRadius = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra aura radius per adaptation level.", impact = "Higher values make leveling widen the aura faster.") + double radiusPerLevel = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base number of random blocks sampled per pulse.", impact = "Higher values accelerate more blocks at more server cost.") + int baseSamplesPerPulse = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on blocks sampled per pulse.", impact = "Higher values raise the per pulse work ceiling.") + int maxSamplesPerPulse = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance for a sampled crop to advance one growth stage.", impact = "Higher values grow crops faster.") + double baseGrowChance = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra crop growth chance per adaptation level.", impact = "Higher values make leveling grow crops faster.") + double growChancePerLevel = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base fraction of total cook or brew time fast-forwarded per pulse hit.", impact = "Higher values complete cooks and brews in fewer pulses.") + double baseCookBoostFraction = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra cook fast-forward fraction per adaptation level.", impact = "Higher values make leveling speed cooking faster.") + double cookBoostFractionPerLevel = 0.07; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the cook fast-forward fraction per pulse hit.", impact = "Higher values allow nearly instant cooks at high levels.") + double maxCookBoostFraction = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per accelerated block.", impact = "Higher values grant more skill XP from the aura.") + double xpPerAcceleratedBlock = 1.2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosBorrowedTime.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosBorrowedTime.java new file mode 100644 index 000000000..500842d0d --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosBorrowedTime.java @@ -0,0 +1,263 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Deque; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; + +public class ChronosBorrowedTime extends SimpleAdaptation { + private final Map> deferred; + private final Set applyingDeferred; + + public ChronosBorrowedTime() { + super("chronos-borrowed-time"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.borrowed_time.description")); + setDisplayName(Localizer.dLocalize("chronos.borrowed_time.name")); + setIcon(Material.SOUL_SAND); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + deferred = new ConcurrentHashMap<>(); + applyingDeferred = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SOUL_SAND) + .key("challenge_chronos_borrowed_2500") + .title(Localizer.dLocalize("advancement.challenge_chronos_borrowed_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_borrowed_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_borrowed_2500", "chronos.borrowed-time.damage-deferred", 2500, 900); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Math.round(getDeferFraction(level) * 100D) + "% " + Localizer.dLocalize("chronos.borrowed_time.lore1")); + v.addLore(C.YELLOW + "+ " + getConfig().paybackPulses + "s " + Localizer.dLocalize("chronos.borrowed_time.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.borrowed_time.lore3")); + } + + private double getDeferFraction(int level) { + return Math.min(getConfig().maxDeferFraction, + getConfig().baseDeferFraction + (Math.max(1, level) * getConfig().deferFractionPerLevel)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + deferred.remove(id); + applyingDeferred.remove(id); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerDeathEvent e) { + UUID id = e.getEntity().getUniqueId(); + deferred.remove(id); + applyingDeferred.remove(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + UUID id = p.getUniqueId(); + if (applyingDeferred.contains(id)) { + return; + } + + double finalDamage = e.getFinalDamage(); + if (finalDamage < getConfig().minimumDeferDamage) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + double fraction = getDeferFraction(level); + if (fraction <= 0D) { + return; + } + + double deferredAmount = finalDamage * fraction; + e.setDamage(Math.max(0D, e.getDamage() * (1D - fraction))); + + int pulses = Math.max(1, getConfig().paybackPulses); + deferred.computeIfAbsent(id, k -> new ConcurrentLinkedDeque<>()) + .add(new DeferredDamage(deferredAmount / pulses, pulses)); + + getPlayer(p).getData().addStat("chronos.borrowed-time.damage-deferred", deferredAmount); + } + + @Override + public void onTick() { + if (deferred.isEmpty()) { + return; + } + + for (Map.Entry> entry : deferred.entrySet()) { + UUID id = entry.getKey(); + Player p = Bukkit.getPlayer(id); + if (p == null || !p.isOnline() || p.isDead()) { + deferred.remove(id); + continue; + } + + Deque queue = entry.getValue(); + double amount = 0D; + Iterator iterator = queue.iterator(); + while (iterator.hasNext()) { + DeferredDamage chunk = iterator.next(); + amount += chunk.perPulse(); + if (chunk.consumePulse() <= 0) { + iterator.remove(); + } + } + + if (queue.isEmpty()) { + deferred.remove(id); + } + + if (amount <= 0D) { + continue; + } + + double damage = amount; + Runnable apply = () -> { + if (!p.isOnline() || p.isDead()) { + return; + } + + double health = p.getHealth(); + if (health <= 0D) { + return; + } + + double remaining = health - damage; + if (remaining <= 0D) { + applyingDeferred.add(id); + try { + p.damage(damage); + } finally { + applyingDeferred.remove(id); + } + } else { + p.setHealth(remaining); + } + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.ASH, p.getLocation().add(0, 1, 0), 3, 0.2, 0.3, 0.2, 0); + } + }; + + if (J.isFoliaThreading()) { + J.runEntity(p, apply); + } else { + apply.run(); + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Defer a portion of incoming damage, paying it back in small ticks over the following seconds.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.42; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base fraction of incoming damage that is deferred.", impact = "Higher values move more damage into the payback window.") + double baseDeferFraction = 0.1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra deferred fraction per adaptation level.", impact = "Higher values make leveling defer more damage.") + double deferFractionPerLevel = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the deferred damage fraction.", impact = "Higher values allow more of each hit to be deferred.") + double maxDeferFraction = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum final damage required before any deferral happens.", impact = "Higher values skip deferring small hits entirely.") + double minimumDeferDamage = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Number of one second pulses the deferred damage is paid back over.", impact = "Higher values spread payback thinner over a longer window.") + int paybackPulses = 10; + } + + private static final class DeferredDamage { + private final double perPulse; + private int pulsesRemaining; + + private DeferredDamage(double perPulse, int pulsesRemaining) { + this.perPulse = perPulse; + this.pulsesRemaining = pulsesRemaining; + } + + private double perPulse() { + return perPulse; + } + + private int consumePulse() { + pulsesRemaining--; + return pulsesRemaining; + } + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosDejaVu.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosDejaVu.java new file mode 100644 index 000000000..49f32819d --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosDejaVu.java @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ChronosDejaVu extends SimpleAdaptation { + private final Map memory; + + public ChronosDejaVu() { + super("chronos-deja-vu"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.deja_vu.description")); + setDisplayName(Localizer.dLocalize("chronos.deja_vu.name")); + setIcon(Material.ITEM_FRAME); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(60000); + memory = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ITEM_FRAME) + .key("challenge_chronos_deja_vu_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_deja_vu_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_deja_vu_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_deja_vu_500", "chronos.deja-vu.damage-absorbed", 500, 700); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Math.round(getReductionFraction(level) * 100D) + "% " + Localizer.dLocalize("chronos.deja_vu.lore1")); + v.addLore(C.YELLOW + "+ " + Form.duration(getConfig().memoryWindowMillis, 1) + " " + Localizer.dLocalize("chronos.deja_vu.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.deja_vu.lore3")); + } + + private double getReductionFraction(int level) { + return Math.min(getConfig().maxReductionFraction, + getConfig().baseReductionFraction + (Math.max(1, level) * getConfig().reductionFractionPerLevel)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + memory.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + EntityDamageEvent.DamageCause cause = e.getCause(); + long now = System.currentTimeMillis(); + DamageMemory previous = memory.put(p.getUniqueId(), new DamageMemory(cause, now)); + if (previous == null || previous.cause() != cause || now - previous.timestamp() > getConfig().memoryWindowMillis) { + return; + } + + double fraction = getReductionFraction(level); + if (fraction <= 0D) { + return; + } + + double absorbed = e.getFinalDamage() * fraction; + if (absorbed <= 0D) { + return; + } + + e.setDamage(Math.max(0D, e.getDamage() * (1D - fraction))); + + getPlayer(p).getData().addStat("chronos.deja-vu.damage-absorbed", absorbed); + xpSilent(p, absorbed * getConfig().xpPerAbsorbedDamage, "chronos:deja-vu"); + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.REVERSE_PORTAL, p.getLocation().add(0, 1, 0), 6, 0.2, 0.3, 0.2, 0.02); + } + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + long window = getConfig().memoryWindowMillis; + memory.entrySet().removeIf(entry -> now - entry.getValue().timestamp() > window); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Remember the last source of pain; taking the same kind of damage again shortly after hurts less.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds during which a repeated damage cause counts as familiar.", impact = "Higher values keep the damage memory alive longer between hits.") + long memoryWindowMillis = 8000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base fraction of repeated damage that is absorbed.", impact = "Higher values reduce familiar damage more before level scaling.") + double baseReductionFraction = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra absorbed fraction per adaptation level.", impact = "Higher values make leveling reduce familiar damage faster.") + double reductionFractionPerLevel = 0.09; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the absorbed damage fraction.", impact = "Higher values allow more of a familiar hit to be absorbed.") + double maxReductionFraction = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per point of absorbed damage.", impact = "Higher values grant more skill XP from familiar hits.") + double xpPerAbsorbedDamage = 0.8; + } + + private record DamageMemory(EntityDamageEvent.DamageCause cause, long timestamp) { + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java new file mode 100644 index 000000000..b98102782 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java @@ -0,0 +1,218 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ChronosHourglassGuard extends SimpleAdaptation { + private final Map cooldowns; + private final Map invulnerableUntil; + + public ChronosHourglassGuard() { + super("chronos-hourglass-guard"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.hourglass_guard.description")); + setDisplayName(Localizer.dLocalize("chronos.hourglass_guard.name")); + setIcon(Material.TOTEM_OF_UNDYING); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + cooldowns = new ConcurrentHashMap<>(); + invulnerableUntil = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TOTEM_OF_UNDYING) + .key("challenge_chronos_hourglass_10") + .title(Localizer.dLocalize("advancement.challenge_chronos_hourglass_10.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_hourglass_10.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_hourglass_10", "chronos.hourglass-guard.saves", 10, 800); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getConfig().invulnerabilityMillis / 1000D) + "s " + Localizer.dLocalize("chronos.hourglass_guard.lore1")); + v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(level), 1) + " " + Localizer.dLocalize("chronos.hourglass_guard.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.hourglass_guard.lore3")); + } + + private long getCooldownMillis(int level) { + return Math.max(getConfig().minimumCooldownMillis, + getConfig().baseCooldownMillis - (Math.max(1, level) * getConfig().cooldownReductionPerLevelMillis)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + cooldowns.remove(id); + invulnerableUntil.remove(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + UUID id = p.getUniqueId(); + long now = M.ms(); + if (invulnerableUntil.getOrDefault(id, 0L) > now) { + e.setCancelled(true); + return; + } + + if (p.getHealth() - e.getFinalDamage() > 0D) { + return; + } + + if (cooldowns.getOrDefault(id, 0L) > now) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + e.setCancelled(true); + p.setHealth(Math.max(0.5D, getConfig().survivalHealth)); + invulnerableUntil.put(id, now + getConfig().invulnerabilityMillis); + cooldowns.put(id, now + getCooldownMillis(level)); + p.setNoDamageTicks(Math.max(p.getNoDamageTicks(), (int) (getConfig().invulnerabilityMillis / 50L))); + + slowNearbyEnemies(p); + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 30, 0.35, 0.5, 0.35, 0.12); + p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation().add(0, 1, 0), 10, 0.25, 0.4, 0.25, 0.03); + } + + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindFinish(p); + } + + getPlayer(p).getData().addStat("chronos.hourglass-guard.saves", 1); + xp(p, p.getLocation(), getConfig().xpOnSave + (level * getConfig().xpPerLevel)); + } + + private void slowNearbyEnemies(Player p) { + double radius = getConfig().enemySlowRadius; + double radiusSq = radius * radius; + for (Entity entity : p.getWorld().getNearbyEntities(p.getLocation(), radius, radius, radius)) { + if (!(entity instanceof LivingEntity living) || entity.getUniqueId().equals(p.getUniqueId())) { + continue; + } + + if (entity.getLocation().distanceSquared(p.getLocation()) > radiusSq) { + continue; + } + + if (living instanceof Player target && !canDamageTarget(p, target)) { + continue; + } + + living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, + getConfig().enemySlowTicks, getConfig().enemySlowAmplifier, true, false, false), true); + } + } + + @Override + public void onTick() { + long now = M.ms(); + invulnerableUntil.entrySet().removeIf(entry -> entry.getValue() <= now); + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("A killing blow instead leaves you at half a heart, granting brief invulnerability and slowing nearby enemies, on a long cooldown.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Hourglass Guard adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Health the player is left with after a save.", impact = "Higher values leave the player healthier after cheating death.") + double survivalHealth = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Invulnerability window in milliseconds after a save.", impact = "Higher values protect the player longer after a save.") + long invulnerabilityMillis = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base cooldown in milliseconds between saves.", impact = "Higher values force longer waits between saves.") + long baseCooldownMillis = 480000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds per adaptation level.", impact = "Higher values make leveling shorten the cooldown faster.") + long cooldownReductionPerLevelMillis = 60000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Lowest possible cooldown in milliseconds regardless of level.", impact = "Higher values keep a floor under cooldown reduction.") + long minimumCooldownMillis = 180000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Radius in blocks for the slow applied to nearby enemies on save.", impact = "Higher values slow enemies from further away.") + double enemySlowRadius = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the slow applied to nearby enemies.", impact = "Higher values keep enemies slowed longer.") + int enemySlowTicks = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "Slowness amplifier applied to nearby enemies on save.", impact = "Higher values slow enemies more severely.") + int enemySlowAmplifier = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted when a save triggers.", impact = "Higher values grant more skill XP per save.") + double xpOnSave = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra XP granted per adaptation level on save.", impact = "Higher values scale save XP with level faster.") + double xpPerLevel = 10; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java index b666cd135..e0d834fbd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecall.java @@ -136,6 +136,12 @@ public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + Form.duration(getRewindDurationMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore1")); v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(level), 1) + " " + Localizer.dLocalize("chronos.instant_recall.lore2")); v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.instant_recall.lore3")); + if (getConfig().consumeClock) { + v.addLore(C.RED + "* " + Localizer.dLocalize("chronos.instant_recall.lore_cost_clock")); + } + if (getConfig().healthCostFraction > 0) { + v.addLore(C.RED + "* " + Form.pc(getConfig().healthCostFraction, 0) + " " + Localizer.dLocalize("chronos.instant_recall.lore_cost_health")); + } List combos = getTriggerCombos(); if (combos.isEmpty()) { v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); @@ -847,6 +853,32 @@ private boolean isRecallClock(ItemStack stack) { && !ChronoTimeBombItem.isBindableItem(stack); } + private void consumeRecallClock(Player p) { + if (!getConfig().consumeClock) { + return; + } + + ItemStack main = p.getInventory().getItemInMainHand(); + if (isRecallClock(main)) { + main.setAmount(main.getAmount() - 1); + return; + } + + ItemStack off = p.getInventory().getItemInOffHand(); + if (isRecallClock(off)) { + off.setAmount(off.getAmount() - 1); + } + } + + private void applyRecallHealthCost(Player p) { + double fraction = Math.max(0D, Math.min(1D, getConfig().healthCostFraction)); + if (fraction <= 0D || p.isDead()) { + return; + } + + p.setHealth(Math.max(1.0D, p.getHealth() * (1D - fraction))); + } + private void attemptRecall(Player p) { UUID id = p.getUniqueId(); if (!isRecallEligible(p)) { @@ -887,6 +919,7 @@ private void attemptRecall(Player p) { return; } + consumeRecallClock(p); Snapshot finalSnapshot = animationPath.get(animationPath.size() - 1); RecallXPContext xpContext = buildRecallXPContext(animationPath.get(0), finalSnapshot); double healthBeforeRecall = p.getHealth(); @@ -1010,6 +1043,7 @@ && getConfig().rewindUseClientCamera J.teleport(p, finalDestination, PlayerTeleportEvent.TeleportCause.PLUGIN); } applySnapshotState(p, finalSnapshot); + applyRecallHealthCost(p); if (areParticlesEnabled()) { p.getWorld().spawnParticle(Particle.TOTEM_OF_UNDYING, p.getLocation().add(0, 1, 0), 26, 0.25, 0.35, 0.25, 0.01); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java index 7ce3e1e7c..f1824c3c3 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosInstantRecallConfig.java @@ -10,6 +10,10 @@ public class ChronosInstantRecallConfig { boolean enabled = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Consumes one clock from the casting hand when a recall successfully starts.", impact = "True makes each recall cost a clock; false keeps recalls item-free.") + boolean consumeClock = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fraction of current health lost when a recall completes; never lethal, health is floored at 1.0.", impact = "Higher values make recalls cost more health; 0 disables the health cost.") + double healthCostFraction = 0.5; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Show Rewind Trace Particles for the Chronos Instant Recall adaptation.", impact = "True enables this behavior and false disables it.") boolean showRewindTraceParticles = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Rewind Trace Points for the Chronos Instant Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosOvertime.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosOvertime.java new file mode 100644 index 000000000..fce924a17 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosOvertime.java @@ -0,0 +1,241 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ChronosOvertime extends SimpleAdaptation { + private static final Set BENEFICIAL = buildBeneficialSet(); + + private final Set extending; + + public ChronosOvertime() { + super("chronos-overtime"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.overtime.description")); + setDisplayName(Localizer.dLocalize("chronos.overtime.name")); + setIcon(Material.GLISTERING_MELON_SLICE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(60000); + extending = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.GLISTERING_MELON_SLICE) + .key("challenge_chronos_overtime_1k") + .title(Localizer.dLocalize("advancement.challenge_chronos_overtime_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_overtime_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_overtime_1k", "chronos.overtime.seconds-extended", 1000, 750); + } + + private static Set buildBeneficialSet() { + Set set = new HashSet<>(); + addIfPresent(set, PotionEffectType.SPEED); + addIfPresent(set, PotionEffectType.JUMP_BOOST); + addIfPresent(set, PotionEffectType.REGENERATION); + addIfPresent(set, PotionEffectType.RESISTANCE); + addIfPresent(set, PotionEffectType.FIRE_RESISTANCE); + addIfPresent(set, PotionEffectType.WATER_BREATHING); + addIfPresent(set, PotionEffectType.INVISIBILITY); + addIfPresent(set, PotionEffectType.NIGHT_VISION); + addIfPresent(set, PotionEffectType.HEALTH_BOOST); + addIfPresent(set, PotionEffectType.ABSORPTION); + addIfPresent(set, PotionEffectType.SATURATION); + addIfPresent(set, PotionEffectType.LUCK); + addIfPresent(set, PotionEffectType.SLOW_FALLING); + addIfPresent(set, PotionEffectType.DOLPHINS_GRACE); + addIfPresent(set, PotionEffectType.HERO_OF_THE_VILLAGE); + addIfPresent(set, PotionEffectTypes.FAST_DIGGING); + addIfPresent(set, PotionEffectTypes.INCREASE_DAMAGE); + return set; + } + + private static void addIfPresent(Set set, PotionEffectType type) { + if (type != null) { + set.add(type); + } + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Math.round(getExtensionPercent(level) * 100D) + "% " + Localizer.dLocalize("chronos.overtime.lore1")); + v.addLore(C.YELLOW + "+ " + (getConfig().maxBonusTicks / 20) + "s " + Localizer.dLocalize("chronos.overtime.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.overtime.lore3")); + } + + private double getExtensionPercent(int level) { + return Math.min(getConfig().maxExtensionPercent, + getConfig().baseExtensionPercent + (Math.max(1, level) * getConfig().extensionPercentPerLevel)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + extending.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityPotionEffectEvent e) { + EntityPotionEffectEvent.Action action = e.getAction(); + if (action != EntityPotionEffectEvent.Action.ADDED && action != EntityPotionEffectEvent.Action.CHANGED) { + return; + } + + if (!(e.getEntity() instanceof Player p)) { + return; + } + + UUID id = p.getUniqueId(); + if (extending.contains(id)) { + return; + } + + PotionEffect newEffect = e.getNewEffect(); + if (newEffect == null) { + return; + } + + int duration = newEffect.getDuration(); + if (duration < getConfig().minimumDurationTicks || duration > getConfig().maximumBaseDurationTicks) { + return; + } + + PotionEffectType type = newEffect.getType(); + if (!BENEFICIAL.contains(type)) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + int bonus = (int) Math.min(getConfig().maxBonusTicks, Math.round(duration * getExtensionPercent(level))); + if (bonus <= 0) { + return; + } + + int amplifier = newEffect.getAmplifier(); + int targetDuration = duration + bonus; + + Runnable extend = () -> { + if (!p.isOnline() || p.isDead()) { + return; + } + + PotionEffect current = p.getPotionEffect(type); + if (current == null + || current.getAmplifier() != amplifier + || current.getDuration() <= 0 + || current.getDuration() >= targetDuration) { + return; + } + + extending.add(id); + try { + p.addPotionEffect(new PotionEffect(type, targetDuration, amplifier, + current.isAmbient(), current.hasParticles(), current.hasIcon()), true); + } finally { + extending.remove(id); + } + + getPlayer(p).getData().addStat("chronos.overtime.seconds-extended", bonus / 20D); + xpSilent(p, Math.min(getConfig().maxXpPerExtension, (bonus / 20D) * getConfig().xpPerBonusSecond), "chronos:overtime"); + }; + + if (J.isFoliaThreading()) { + J.runEntity(p, extend, 1); + } else { + J.s(extend, 1); + } + } + + @Override + public void onTick() { + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Beneficial potion effects applied to you last longer, scaled by adaptation level.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.38; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base duration extension as a fraction of the original duration.", impact = "Higher values extend beneficial effects more.") + double baseExtensionPercent = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra extension fraction per adaptation level.", impact = "Higher values make leveling extend effects faster.") + double extensionPercentPerLevel = 0.07; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the duration extension fraction.", impact = "Higher values allow larger total extensions.") + double maxExtensionPercent = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum bonus ticks added to any single effect application.", impact = "Higher values let long potions gain bigger absolute extensions.") + int maxBonusTicks = 2400; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum original duration in ticks for an effect to be extended.", impact = "Higher values ignore short pulsing effects entirely.") + int minimumDurationTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum original duration in ticks eligible for extension.", impact = "Lower values stop near permanent effects from being extended.") + int maximumBaseDurationTicks = 72000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per bonus second of effect duration.", impact = "Higher values grant more skill XP per extension.") + double xpPerBonusSecond = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum XP granted by a single extension.", impact = "Higher values allow bigger XP payouts per potion.") + double maxXpPerExtension = 8; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosPocketWatch.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosPocketWatch.java new file mode 100644 index 000000000..0b623ae6b --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosPocketWatch.java @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ChronosPocketWatch extends SimpleAdaptation { + private static final long PULSE_MILLIS = 250L; + + private final Map airBudgetMillis; + + public ChronosPocketWatch() { + super("chronos-pocket-watch"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.pocket_watch.description")); + setDisplayName(Localizer.dLocalize("chronos.pocket_watch.name")); + setIcon(Material.FEATHER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(PULSE_MILLIS); + airBudgetMillis = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FEATHER) + .key("challenge_chronos_pocket_watch_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_pocket_watch_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_pocket_watch_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_chronos_pocket_watch_500", "chronos.pocket-watch.slow-fall-seconds", 500, 650); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getBudgetMillis(level) / 1000D) + "s " + Localizer.dLocalize("chronos.pocket_watch.lore1")); + v.addLore(C.YELLOW + "* " + Localizer.dLocalize("chronos.pocket_watch.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.pocket_watch.lore3")); + } + + private long getBudgetMillis(int level) { + return (long) ((getConfig().baseBudgetSeconds + (Math.max(1, level) * getConfig().budgetSecondsPerLevel)) * 1000D); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + airBudgetMillis.remove(e.getPlayer().getUniqueId()); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (AdaptPlayer adaptPlayer : learnedCandidates(now)) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + UUID id = p.getUniqueId(); + if (p.isOnGround()) { + airBudgetMillis.remove(id); + continue; + } + + int level = getActiveLevel(p, Player::isSneaking); + if (level <= 0) { + continue; + } + + if (p.isFlying() || p.isGliding() || p.isInsideVehicle() || p.isSwimming()) { + continue; + } + + if (!airBudgetMillis.containsKey(id) && p.getFallDistance() < getConfig().minFallDistance) { + continue; + } + + if (getConfig().requireClock && !p.getInventory().contains(Material.CLOCK)) { + continue; + } + + long budget = airBudgetMillis.computeIfAbsent(id, k -> getBudgetMillis(level)); + if (budget < PULSE_MILLIS) { + continue; + } + + airBudgetMillis.put(id, budget - PULSE_MILLIS); + + Runnable apply = () -> { + if (!p.isOnline() || p.isDead()) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, + getConfig().pulseDurationTicks, 0, true, false, false), true); + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CLOUD, p.getLocation(), 1, 0.1, 0, 0.1, 0); + } + }; + + if (J.isFoliaThreading()) { + J.runEntity(p, apply); + } else { + apply.run(); + } + + getPlayer(p).getData().addStat("chronos.pocket-watch.slow-fall-seconds", PULSE_MILLIS / 1000D); + xpSilent(p, getConfig().xpPerPulse, "chronos:pocket-watch"); + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak while airborne to fall in slow motion, with a level scaled time budget per airtime.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base seconds of slow falling sustainable per airtime.", impact = "Higher values let the player drift longer each fall.") + double baseBudgetSeconds = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra sustain seconds per adaptation level.", impact = "Higher values make leveling extend the drift budget faster.") + double budgetSecondsPerLevel = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of each refreshing slow falling pulse.", impact = "Higher values leave slow falling lingering longer after sneaking stops.") + int pulseDurationTicks = 15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum fall distance in blocks before the slow fall kicks in.", impact = "Lower values start the slow fall earlier in the drop.") + double minFallDistance = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Requires a clock anywhere in the inventory for the slow fall to apply.", impact = "False removes the item requirement entirely.") + boolean requireClock = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per slow fall pulse.", impact = "Higher values grant more skill XP while drifting.") + double xpPerPulse = 0.3; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosRewind.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosRewind.java new file mode 100644 index 000000000..15e34c804 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosRewind.java @@ -0,0 +1,309 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ChronosRewind extends SimpleAdaptation { + private final Map snapshots; + private final Map cooldowns; + private final Set cooldownReadyNotify; + + public ChronosRewind() { + super("chronos-rewind"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.rewind.description")); + setDisplayName(Localizer.dLocalize("chronos.rewind.name")); + setIcon(Material.ENDER_EYE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + snapshots = new ConcurrentHashMap<>(); + cooldowns = new ConcurrentHashMap<>(); + cooldownReadyNotify = ConcurrentHashMap.newKeySet(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.ENDER_EYE) + .key("challenge_chronos_rewind_50") + .title(Localizer.dLocalize("advancement.challenge_chronos_rewind_50.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_rewind_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.RECOVERY_COMPASS) + .key("challenge_chronos_rewind_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_rewind_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_rewind_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_chronos_rewind_50", "chronos.rewind.rewinds", 50, 350); + registerMilestone("challenge_chronos_rewind_500", "chronos.rewind.rewinds", 500, 1400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.duration(getConfig().snapshotWindowMillis, 1) + " " + Localizer.dLocalize("chronos.rewind.lore1")); + v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(level), 1) + " " + Localizer.dLocalize("chronos.rewind.lore2")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.rewind.lore3")); + if (getConfig().hungerCost > 0) { + v.addLore(C.RED + "* " + getConfig().hungerCost + " " + Localizer.dLocalize("chronos.rewind.lore_cost_hunger")); + } + } + + private long getCooldownMillis(int level) { + return Math.max(getConfig().minimumCooldownMillis, + getConfig().baseCooldownMillis - (Math.max(1, level) * getConfig().cooldownReductionPerLevelMillis)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + snapshots.remove(id); + cooldowns.remove(id); + cooldownReadyNotify.remove(id); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerSwapHandItemsEvent e) { + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation()); + if (context == null) { + return; + } + + e.setCancelled(true); + UUID id = p.getUniqueId(); + long now = M.ms(); + + RewindSnapshot snapshot = snapshots.get(id); + if (snapshot != null && snapshot.expiresAt() > now) { + performRewind(p, context.level(), snapshot, now); + return; + } + + long cooldownUntil = cooldowns.getOrDefault(id, 0L); + if (cooldownUntil > now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + snapshots.put(id, new RewindSnapshot(p.getLocation().clone(), p.getHealth(), p.getFoodLevel(), + now + Math.max(1000L, getConfig().snapshotWindowMillis))); + + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindStart(p); + } + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.REVERSE_PORTAL, p.getLocation().add(0, 1, 0), 14, 0.3, 0.5, 0.3, 0.02); + } + } + + private void performRewind(Player p, int level, RewindSnapshot snapshot, long now) { + UUID id = p.getUniqueId(); + int hungerCost = Math.max(0, getConfig().hungerCost); + if (hungerCost > 0 && p.getFoodLevel() < hungerCost) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + snapshots.remove(id); + + Location destination = snapshot.location(); + if (destination.getWorld() == null) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + cooldowns.put(id, now + getCooldownMillis(level)); + cooldownReadyNotify.add(id); + + Location departure = p.getLocation().clone(); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.REVERSE_PORTAL, departure.clone().add(0, 1, 0), 18, 0.3, 0.5, 0.3, 0.04); + p.getWorld().spawnParticle(Particle.PORTAL, departure.clone().add(0, 1, 0), 28, 0.4, 0.6, 0.4, 0.6); + } + + SoundPlayer.of(p.getWorld()).play(departure, Sound.ENTITY_ENDERMAN_TELEPORT, 0.7f, 0.65f); + + J.teleport(p, destination, PlayerTeleportEvent.TeleportCause.PLUGIN); + + Runnable restore = () -> { + if (!p.isOnline() || p.isDead()) { + return; + } + + if (p.getHealth() < snapshot.health()) { + AttributeInstance maxHealthAttribute = p.getAttribute(Attribute.MAX_HEALTH); + double maxHealth = maxHealthAttribute == null ? 20D : maxHealthAttribute.getValue(); + p.setHealth(Math.min(maxHealth, snapshot.health())); + } + + if (p.getFoodLevel() < snapshot.foodLevel()) { + p.setFoodLevel(Math.min(20, snapshot.foodLevel())); + } + + if (hungerCost > 0) { + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + } + + p.setFallDistance(0); + p.addPotionEffect(new PotionEffect(PotionEffectType.DARKNESS, 35, 0, true, false, false)); + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation().add(0, 1, 0), 10, 0.25, 0.4, 0.25, 0.02); + p.getWorld().spawnParticle(Particle.REVERSE_PORTAL, p.getLocation().add(0, 1, 0), 22, 0.35, 0.55, 0.35, 0.05); + p.getWorld().spawnParticle(Particle.PORTAL, p.getLocation().add(0, 1, 0), 28, 0.4, 0.6, 0.4, 0.6); + } + + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.7f, 0.85f); + }; + + if (J.isFoliaThreading()) { + J.runEntity(p, restore, 1); + } else { + J.s(restore, 1); + } + + if (getConfig().playClockSounds) { + ChronosSoundFX.playRewindFinish(p); + } + + getPlayer(p).getData().addStat("chronos.rewind.rewinds", 1); + xp(p, destination, getConfig().xpOnRewind + (level * getConfig().xpPerLevel)); + } + + @Override + public void onTick() { + long now = M.ms(); + snapshots.entrySet().removeIf(entry -> entry.getValue().expiresAt() <= now); + + for (Iterator iterator = cooldownReadyNotify.iterator(); iterator.hasNext(); ) { + UUID id = iterator.next(); + Player p = Bukkit.getPlayer(id); + if (p == null) { + iterator.remove(); + continue; + } + + if (cooldowns.getOrDefault(id, 0L) <= now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playCooldownReady(p); + } + iterator.remove(); + } + } + + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak and swap hands to mark a moment in time, then do it again within the window to snap back with health and hunger restored.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Rewind adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Food points consumed when a rewind completes.", impact = "Higher values make each rewind cost more hunger; 0 disables the cost.") + int hungerCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds after marking a snapshot during which the rewind can be completed.", impact = "Higher values give more time to trigger the snap back.") + long snapshotWindowMillis = 10000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base cooldown in milliseconds applied after a completed rewind.", impact = "Higher values force longer waits between rewinds.") + long baseCooldownMillis = 45000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds per adaptation level.", impact = "Higher values make leveling shorten the cooldown faster.") + long cooldownReductionPerLevelMillis = 4000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Lowest possible cooldown in milliseconds regardless of level.", impact = "Higher values keep a floor under cooldown reduction.") + long minimumCooldownMillis = 15000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted when a rewind completes.", impact = "Higher values grant more skill XP per rewind.") + double xpOnRewind = 18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra XP granted per adaptation level on rewind.", impact = "Higher values scale rewind XP with level faster.") + double xpPerLevel = 3; + } + + private record RewindSnapshot(Location location, double health, + int foodLevel, long expiresAt) { + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java new file mode 100644 index 000000000..6a9e8250c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java @@ -0,0 +1,425 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.chronos; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +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.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.Vector; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ThreadLocalRandom; + +public class ChronosStasisField extends SimpleAdaptation { + private final Map cooldowns; + private final List bubbles; + private final Map frozenProjectiles; + + public ChronosStasisField() { + super("chronos-stasis-field"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("chronos.stasis_field.description")); + setDisplayName(Localizer.dLocalize("chronos.stasis_field.name")); + setIcon(Material.AMETHYST_SHARD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(250); + cooldowns = new ConcurrentHashMap<>(); + bubbles = new CopyOnWriteArrayList<>(); + frozenProjectiles = new ConcurrentHashMap<>(); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.AMETHYST_SHARD) + .key("challenge_chronos_stasis_50") + .title(Localizer.dLocalize("advancement.challenge_chronos_stasis_50.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_stasis_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.CLOCK) + .key("challenge_chronos_stasis_500") + .title(Localizer.dLocalize("advancement.challenge_chronos_stasis_500.title")) + .description(Localizer.dLocalize("advancement.challenge_chronos_stasis_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_chronos_stasis_50", "chronos.stasis-field.casts", 50, 400); + registerMilestone("challenge_chronos_stasis_500", "chronos.stasis-field.casts", 500, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level)) + " " + Localizer.dLocalize("chronos.stasis_field.lore1")); + v.addLore(C.YELLOW + "+ " + Form.duration(getDurationMillis(level), 1) + " " + Localizer.dLocalize("chronos.stasis_field.lore2")); + v.addLore(C.RED + "* " + Form.duration(getCooldownMillis(), 1) + " " + Localizer.dLocalize("chronos.stasis_field.lore3")); + v.addLore(C.GRAY + "* " + Localizer.dLocalize("chronos.stasis_field.lore4")); + if (getConfig().consumeShard) { + v.addLore(C.RED + "* " + Localizer.dLocalize("chronos.stasis_field.lore_cost_shard")); + } + } + + private double getRadius(int level) { + return getConfig().baseRadius + ((Math.max(1, level) - 1) * getConfig().radiusPerLevel); + } + + private long getDurationMillis(int level) { + return getConfig().baseDurationMillis + ((Math.max(1, level) - 1L) * getConfig().durationPerLevelMillis); + } + + private long getCooldownMillis() { + return Math.max(0L, getConfig().cooldownMillis); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + EquipmentSlot hand = e.getHand(); + if (hand == null) { + return; + } + + ItemStack held = hand == EquipmentSlot.OFF_HAND + ? p.getInventory().getItemInOffHand() + : p.getInventory().getItemInMainHand(); + if (held.getType() != Material.AMETHYST_SHARD) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation()); + if (context == null) { + return; + } + + e.setCancelled(true); + long now = M.ms(); + long until = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (until > now) { + if (getConfig().playClockSounds) { + ChronosSoundFX.playClockReject(p); + } + return; + } + + if (getConfig().consumeShard) { + held.setAmount(held.getAmount() - 1); + } + + cooldowns.put(p.getUniqueId(), now + getCooldownMillis()); + deployBubble(p, context.level(), now); + } + + private void deployBubble(Player p, int level, long now) { + Location center = p.getLocation().clone().add(0, getConfig().centerYOffset, 0); + bubbles.add(new StasisBubble(p.getUniqueId(), center, getRadius(level), now + getDurationMillis(level), 0L)); + + if (getConfig().playClockSounds) { + ChronosSoundFX.playTimeBombDetonate(center); + } + + getPlayer(p).getData().addStat("chronos.stasis-field.casts", 1); + xp(p, center, getConfig().xpOnCast + (level * getConfig().xpPerLevel)); + } + + @Override + public void onTick() { + long now = M.ms(); + cooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + if (bubbles.isEmpty() && frozenProjectiles.isEmpty()) { + return; + } + + for (StasisBubble bubble : bubbles) { + if (bubble.expiresAt() <= now) { + bubbles.remove(bubble); + releaseBubble(bubble); + continue; + } + + if (J.isFoliaThreading()) { + J.runAt(bubble.center(), () -> pulse(bubble, now)); + } else { + pulse(bubble, now); + } + } + + if (bubbles.isEmpty() && !frozenProjectiles.isEmpty()) { + releaseOrphans(); + } + } + + private void releaseBubble(StasisBubble bubble) { + for (Map.Entry entry : frozenProjectiles.entrySet()) { + if (!entry.getValue().bubbleId().equals(bubble.id())) { + continue; + } + + frozenProjectiles.remove(entry.getKey()); + restoreProjectile(entry.getKey(), entry.getValue()); + } + } + + private void releaseOrphans() { + for (Map.Entry entry : frozenProjectiles.entrySet()) { + frozenProjectiles.remove(entry.getKey()); + restoreProjectile(entry.getKey(), entry.getValue()); + } + } + + private void restoreProjectile(UUID entityId, FrozenProjectileState state) { + Entity entity = Bukkit.getEntity(entityId); + if (entity == null || entity.isDead() || !entity.isValid()) { + return; + } + + Runnable restore = () -> { + if (entity.isDead() || !entity.isValid()) { + return; + } + + if (getConfig().removeProjectilesOnExpire) { + entity.remove(); + return; + } + + entity.setGravity(state.gravity()); + entity.setVelocity(state.velocity()); + }; + + if (J.isFoliaThreading()) { + J.runEntity(entity, restore); + } else { + restore.run(); + } + } + + private void pulse(StasisBubble bubble, long now) { + World world = bubble.center().getWorld(); + if (world == null) { + return; + } + + double radius = bubble.radius(); + double radiusSq = radius * radius; + Player owner = Bukkit.getPlayer(bubble.owner()); + + for (Entity entity : world.getNearbyEntities(bubble.center(), radius, radius, radius)) { + if (entity.getLocation().distanceSquared(bubble.center()) > radiusSq) { + continue; + } + + if (entity instanceof Projectile projectile) { + if (!frozenProjectiles.containsKey(projectile.getUniqueId())) { + frozenProjectiles.put(projectile.getUniqueId(), + new FrozenProjectileState(bubble.id(), projectile.getVelocity().clone(), projectile.hasGravity())); + if (owner != null) { + getPlayer(owner).getData().addStat("chronos.stasis-field.projectiles-frozen", 1); + } + } + + projectile.setGravity(false); + projectile.setVelocity(new Vector()); + continue; + } + + if (entity instanceof LivingEntity living && !(entity instanceof Player) && !entity.getUniqueId().equals(bubble.owner())) { + living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().slownessAmplifier, true, false, false), true); + if (PotionEffectTypes.JUMP != null) { + living.addPotionEffect(new PotionEffect(PotionEffectTypes.JUMP, getConfig().effectRefreshTicks, getConfig().jumpLockAmplifier, true, false, false), true); + } + } + } + + if (areParticlesEnabled() && now >= bubble.nextVisualAt()) { + spawnOutline(world, bubble); + bubble.setNextVisualAt(now + Math.max(100L, getConfig().outlineRefreshMillis)); + } + } + + private void spawnOutline(World world, StasisBubble bubble) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + int points = Math.max(4, getConfig().outlineParticleCount); + double radius = bubble.radius(); + for (int i = 0; i < points; i++) { + double theta = random.nextDouble() * Math.PI * 2D; + double phi = Math.acos((random.nextDouble() * 2D) - 1D); + double x = bubble.center().getX() + (radius * Math.sin(phi) * Math.cos(theta)); + double y = bubble.center().getY() + (radius * Math.cos(phi)); + double z = bubble.center().getZ() + (radius * Math.sin(phi) * Math.sin(theta)); + world.spawnParticle(Particle.END_ROD, x, y, z, 1, 0, 0, 0, 0); + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak and right click with an amethyst shard to deploy a stasis bubble that freezes projectiles and locks down mobs inside.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Play Clock Sounds for the Chronos Stasis Field adaptation.", impact = "True enables this behavior and false disables it.") + boolean playClockSounds = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Consumes the amethyst shard used to deploy a stasis bubble.", impact = "True makes each cast cost one shard; false keeps casts item-free.") + boolean consumeShard = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base radius of the stasis bubble in blocks.", impact = "Higher values freeze projectiles and slow mobs in a larger area.") + double baseRadius = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra bubble radius granted per adaptation level.", impact = "Higher values make leveling expand the bubble faster.") + double radiusPerLevel = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base bubble lifetime in milliseconds.", impact = "Higher values keep the stasis bubble active longer.") + long baseDurationMillis = 3000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra bubble lifetime in milliseconds per adaptation level.", impact = "Higher values make leveling extend the bubble duration faster.") + long durationPerLevelMillis = 750; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown between bubble deployments in milliseconds.", impact = "Higher values force longer waits between casts.") + long cooldownMillis = 20000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Vertical offset of the bubble center above the caster.", impact = "Higher values raise the bubble center off the ground.") + double centerYOffset = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Slowness amplifier applied to mobs inside the bubble.", impact = "Higher values slow trapped mobs more severely.") + int slownessAmplifier = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Jump boost amplifier applied to mobs inside the bubble; negative values prevent jumping.", impact = "Lower negative values suppress jumping harder.") + int jumpLockAmplifier = -6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of each refreshed potion pulse on trapped mobs.", impact = "Higher values keep effects on mobs longer between pulses.") + int effectRefreshTicks = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Removes frozen projectiles when the bubble expires instead of restoring their motion.", impact = "True deletes trapped projectiles; false releases them with their original velocity.") + boolean removeProjectilesOnExpire = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Number of outline particles spawned per visual refresh.", impact = "Higher values make the bubble edge denser at more visual cost.") + int outlineParticleCount = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds between bubble outline particle refreshes.", impact = "Lower values redraw the outline more often at more visual cost.") + long outlineRefreshMillis = 400; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted when a bubble is deployed.", impact = "Higher values grant more skill XP per cast.") + double xpOnCast = 22; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra XP granted per adaptation level on cast.", impact = "Higher values scale cast XP with level faster.") + double xpPerLevel = 3; + } + + private static final class StasisBubble { + private final UUID id; + private final UUID owner; + private final Location center; + private final double radius; + private final long expiresAt; + private long nextVisualAt; + + private StasisBubble(UUID owner, Location center, double radius, long expiresAt, long nextVisualAt) { + this.id = UUID.randomUUID(); + this.owner = owner; + this.center = center; + this.radius = radius; + this.expiresAt = expiresAt; + this.nextVisualAt = nextVisualAt; + } + + private UUID id() { + return id; + } + + private UUID owner() { + return owner; + } + + private Location center() { + return center; + } + + private double radius() { + return radius; + } + + private long expiresAt() { + return expiresAt; + } + + private long nextVisualAt() { + return nextVisualAt; + } + + private void setNextVisualAt(long nextVisualAt) { + this.nextVisualAt = nextVisualAt; + } + } + + private record FrozenProjectileState(UUID bubbleId, Vector velocity, + boolean gravity) { + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index 621b93721..89737758d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -33,9 +33,16 @@ import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; -import org.bukkit.entity.*; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Snowball; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.Vector; @@ -100,6 +107,20 @@ public void on(ProjectileLaunchEvent e) { J.runEntity(p, () -> spawnEcho(p, echoType, originalVelocity, level), delay); } + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(ProjectileHitEvent e) { + if (e.getHitEntity() == null || !e.getEntity().hasMetadata(ECHO_META)) { + return; + } + + if (!(e.getHitEntity() instanceof LivingEntity target)) { + return; + } + + target.setNoDamageTicks(0); + target.setLastDamage(0.0D); + } + private void spawnEcho(Player p, EchoType type, Vector velocity, int level) { if (!p.isOnline() || p.isDead()) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java index 4dee1a529..ee92af36d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeInABottle.java @@ -343,11 +343,10 @@ public void on(PlayerInteractEvent e) { // Chrono bottles are never drinkable; always deny vanilla potion use. e.setUseItemInHand(Event.Result.DENY); - if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + Block clicked = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (clicked == null) { return; } - - Block clicked = e.getClickedBlock(); int level = getActiveInteractLevel(p, clicked.getLocation()); if (level <= 0) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java index 67e89b720..5fca9e1c7 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/crafting/CraftingStations.java @@ -34,6 +34,7 @@ import org.bukkit.event.block.Action; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -71,10 +72,17 @@ public CraftingStations() { public void addStats(int level, Element v) { v.addLore(C.RED + Localizer.dLocalize("crafting.stations.lore2")); v.addLore(C.GRAY + Localizer.dLocalize("crafting.stations.lore3")); + if (getConfig().hungerCost > 0) { + v.addLore(C.YELLOW + "* " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("crafting.stations.lore4")); + } } @EventHandler public void on(PlayerInteractEvent e) { + if (e.getHand() != EquipmentSlot.HAND) { + return; + } + Player p = e.getPlayer(); if (!hasActiveAdaptation(p)) { return; @@ -87,59 +95,45 @@ public void on(PlayerInteractEvent e) { return; } - if ((e.getAction().equals(Action.RIGHT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK))) { - - SoundPlayer sp = SoundPlayer.of(p); - switch (hand.getType()) { - case CRAFTING_TABLE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - p.openWorkbench(null, true); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case GRINDSTONE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.GRINDSTONE); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case ANVIL -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.ANVIL); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case STONECUTTER -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.STONECUTTER); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case CARTOGRAPHY_TABLE -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.CARTOGRAPHY); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - case LOOM -> { - p.setCooldown(hand.getType(), 1000); - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - Inventory inv = Bukkit.createInventory(p, InventoryType.LOOM); - p.openInventory(inv); - getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); - } - } + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; + } + + InventoryType station = switch (hand.getType()) { + case CRAFTING_TABLE -> InventoryType.WORKBENCH; + case GRINDSTONE -> InventoryType.GRINDSTONE; + case ANVIL -> InventoryType.ANVIL; + case STONECUTTER -> InventoryType.STONECUTTER; + case CARTOGRAPHY_TABLE -> InventoryType.CARTOGRAPHY; + case LOOM -> InventoryType.LOOM; + default -> null; + }; + + if (station == null) { + return; + } + + int hungerCost = getConfig().hungerCost; + if (hungerCost > 0 && p.getFoodLevel() < hungerCost) { + return; + } + + if (hungerCost > 0) { + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + } + + p.setCooldown(hand.getType(), 1000); + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + if (station == InventoryType.WORKBENCH) { + p.openWorkbench(null, true); + } else { + Inventory inv = Bukkit.createInventory(p, station); + p.openInventory(inv); } + getPlayer(p).getData().addStat("crafting.stations.portable-opens", 1); } @Override @@ -162,6 +156,8 @@ public boolean isPermanent() { protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown for the Crafting Stations adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") public int cooldown = 125; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hunger points consumed each time a portable station is opened.", impact = "Higher values make portable station access cost more food; 0 disables the cost.") + int hungerCost = 2; @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") boolean permanent = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java index b9bb22310..c28c7b805 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/discovery/DiscoveryCartographerPulse.java @@ -38,6 +38,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.EquipmentSlot; import java.lang.reflect.Field; @@ -84,6 +85,14 @@ public DiscoveryCartographerPulse() { public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + Form.f(getSearchRange(level)) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore1")); v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore2")); + if (getConfig().hungerCost > 0) { + v.addLore(C.RED + "* " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("discovery.cartographer_pulse.lore_cost_hunger")); + } + } + + @EventHandler + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -108,6 +117,11 @@ public void on(PlayerInteractEvent e) { return; } + int hungerCost = Math.max(0, getConfig().hungerCost); + if (hungerCost > 0 && p.getFoodLevel() < hungerCost) { + return; + } + Location target = locateNearestStructureFallback(p.getWorld(), p.getLocation(), getSearchRange(level)); if (target == null) { target = p.getWorld().getSpawnLocation(); @@ -116,6 +130,9 @@ public void on(PlayerInteractEvent e) { p.setCompassTarget(target); p.sendMessage(C.AQUA + "Compass pulse: " + C.WHITE + Form.f(target.getBlockX()) + ", " + Form.f(target.getBlockZ())); cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + if (hungerCost > 0) { + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + } SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_LODESTONE_COMPASS_LOCK, 0.8f, 1.3f); xp(p, getConfig().xpPerPulse); getPlayer(p).getData().addStat("discovery.cartographer-pulse.pulses", 1); @@ -269,5 +286,7 @@ protected static class Config { double cooldownMillisFactor = 14000; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Pulse for the Discovery Cartographer Pulse adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpPerPulse = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Food points consumed per compass pulse.", impact = "Higher values make each pulse cost more hunger; 0 disables the cost.") + int hungerCost = 2; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java index 63d39e063..83c2e6d6a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/enchanting/EnchantingOfferReroll.java @@ -31,6 +31,7 @@ import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -77,26 +78,39 @@ public EnchantingOfferReroll() { public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore1")); v.addLore(C.YELLOW + "* " + getLapisCost(level) + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore2")); + if (getConfig().xpLevelCost > 0) { + v.addLore(C.YELLOW + "* " + getConfig().xpLevelCost + C.GRAY + " " + Localizer.dLocalize("enchanting.offer_reroll.lore_cost_xp")); + } } @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (e.getHand() != EquipmentSlot.HAND) { return; } Player p = e.getPlayer(); int level = getActiveLevel(p, Player::isSneaking); - if (level <= 0 || e.getClickedBlock().getType() != Material.ENCHANTING_TABLE) { + if (level <= 0 || p.hasCooldown(Material.ENCHANTING_TABLE)) { return; } - int lapisCost = getLapisCost(level); - if (p.getFoodLevel() < 0) { + Block table = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (table == null || table.getType() != Material.ENCHANTING_TABLE) { return; } - if (!consumeLapis(p, lapisCost) || p.getLevel() < getConfig().xpLevelCost) { + if (p.getLevel() < getConfig().xpLevelCost) { + return; + } + + int lapisCost = getLapisCost(level); + if (!hasLapis(p, lapisCost)) { return; } @@ -104,6 +118,7 @@ public void on(PlayerInteractEvent e) { return; } + consumeLapis(p, lapisCost); p.setLevel(Math.max(0, p.getLevel() - getConfig().xpLevelCost)); p.setCooldown(Material.ENCHANTING_TABLE, getCooldownTicks(level)); e.setCancelled(true); @@ -115,7 +130,22 @@ public void on(PlayerInteractEvent e) { getPlayer(p).getData().addStat("enchanting.offer-reroll.rerolls", 1); } - private boolean consumeLapis(Player p, int amount) { + private boolean hasLapis(Player p, int amount) { + int found = 0; + for (ItemStack stack : p.getInventory().getContents()) { + if (stack == null || stack.getType() != Material.LAPIS_LAZULI) { + continue; + } + + found += stack.getAmount(); + if (found >= amount) { + return true; + } + } + return false; + } + + private void consumeLapis(Player p, int amount) { int need = amount; for (ItemStack stack : p.getInventory().getContents()) { if (stack == null || stack.getType() != Material.LAPIS_LAZULI || need <= 0) { @@ -126,7 +156,6 @@ private boolean consumeLapis(Player p, int amount) { stack.setAmount(stack.getAmount() - used); need -= used; } - return need <= 0; } private boolean setSeed(Player p, int seed) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationBurrow.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationBurrow.java new file mode 100644 index 000000000..87ad3f836 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationBurrow.java @@ -0,0 +1,316 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ExcavationBurrow extends SimpleAdaptation { + private final Map cooldowns = new ConcurrentHashMap<>(); + + public ExcavationBurrow() { + super("excavation-burrow"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.burrow.description")); + setDisplayName(Localizer.dLocalize("excavation.burrow.name")); + setIcon(Material.COARSE_DIRT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4130); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COARSE_DIRT) + .key("challenge_excavation_burrow_100") + .title(Localizer.dLocalize("advancement.challenge_excavation_burrow_100.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_burrow_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_burrow_100", "excavation.burrow.burrows-dug", 100, 450); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxDepth(level) + C.GRAY + " " + Localizer.dLocalize("excavation.burrow.lore1")); + v.addLore(C.GREEN + "+ " + getConfig().durabilityCostPerBlock + C.GRAY + " " + Localizer.dLocalize("excavation.burrow.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.burrow.lore3")); + v.addLore(C.RED + "- " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("excavation.burrow.lore4")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isShovel(hand)) { + return; + } + + Block clicked = e.getClickedBlock(); + if (action == Action.RIGHT_CLICK_AIR) { + clicked = p.getTargetBlockExact(5); + if (clicked == null) { + clicked = p.getLocation().getBlock().getRelative(BlockFace.DOWN); + } + } + + if (clicked == null || !isShovelable(clicked.getType())) { + return; + } + + long now = System.currentTimeMillis(); + long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < nextReady) { + return; + } + + int hungerCost = getConfig().hungerCost; + if (p.getFoodLevel() < hungerCost) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, clicked.getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + + List plan = planDig(p, clicked, getMaxDepth(level)); + if (plan.isEmpty()) { + return; + } + + if (!applyDurability(p, hand, plan.size() * getConfig().durabilityCostPerBlock)) { + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ANVIL_PLACE, 0.5f, 0.65f); + return; + } + + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + e.setCancelled(true); + scheduleDig(plan, hand.clone()); + getPlayer(p).getData().addStat("excavation.burrow.burrows-dug", 1); + getPlayer(p).getData().addStat("excavation.burrow.blocks-burrowed", plan.size()); + xp(p, plan.size() * getConfig().xpPerBlock); + } + + private List planDig(Player p, Block start, int maxDepth) { + World world = start.getWorld(); + int stopY = world.getMinHeight() + getConfig().safeFloorMargin; + List plan = new ArrayList<>(maxDepth); + Block current = start; + for (int i = 0; i < maxDepth; i++) { + if (current.getY() <= stopY) { + break; + } + + if (!isShovelable(current.getType())) { + break; + } + + if (!canBlockBreak(p, current.getLocation())) { + break; + } + + Block below = current.getRelative(BlockFace.DOWN); + Material belowType = below.getType(); + if (belowType == Material.LAVA) { + break; + } + + if (belowType.isAir() && below.getRelative(BlockFace.DOWN).getType().isAir()) { + break; + } + + plan.add(current); + current = below; + } + + return plan; + } + + private void scheduleDig(List plan, ItemStack tool) { + int interval = Math.max(1, getConfig().ticksPerBlock); + for (int i = 0; i < plan.size(); i++) { + Block block = plan.get(i); + int delay = i * interval; + if (delay <= 0) { + digBlock(block, tool); + continue; + } + + J.runAt(block.getLocation(), () -> digBlock(block, tool), delay); + } + } + + private void digBlock(Block block, ItemStack tool) { + if (!isShovelable(block.getType())) { + return; + } + + if (block.getRelative(BlockFace.DOWN).getType() == Material.LAVA) { + return; + } + + block.breakNaturally(tool); + if (areParticlesEnabled()) { + block.getWorld().spawnParticle(Particle.CLOUD, block.getLocation().add(0.5, 0.5, 0.5), 3, 0.2, 0.2, 0.2, 0.01); + } + + SoundPlayer.of(block.getWorld()).play(block.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 0.45f, 1.3f); + } + + private boolean applyDurability(Player p, ItemStack hand, int cost) { + if (cost <= 0) { + return true; + } + + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return false; + } + + int maxDurability = hand.getType().getMaxDurability(); + int currentDamage = damageable.getDamage(); + if (currentDamage + cost >= maxDurability) { + return false; + } + + damageable.setDamage(currentDamage + cost); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + return true; + } + + private boolean isShovelable(Material type) { + return switch (type) { + case CLAY, DIRT, COARSE_DIRT, ROOTED_DIRT, FARMLAND, GRASS_BLOCK, + DIRT_PATH, GRAVEL, MYCELIUM, PODZOL, SAND, RED_SAND, SOUL_SAND, + SOUL_SOIL, SNOW, SNOW_BLOCK, MUD, MUDDY_MANGROVE_ROOTS -> true; + default -> false; + }; + } + + private int getMaxDepth(int level) { + return Math.max(2, (int) Math.round(getConfig().depthBase + (getLevelPercent(level) * getConfig().depthFactor))); + } + + private long getCooldownMillis(int level) { + return Math.max(2000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click soft ground with a shovel to rapidly dig straight down, stopping before hazards.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.78; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Base for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double depthBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Depth Factor for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double depthFactor = 13; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Ticks Per Block for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int ticksPerBlock = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Safe Floor Margin for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int safeFloorMargin = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Per Block for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int durabilityCostPerBlock = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hunger points consumed per burrow activation.", impact = "Higher values drain more food per dig; activation fails when food is below the cost. Set to 0 to disable the hunger cost.") + int hungerCost = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 14000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 7000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Block for the Excavation Burrow adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBlock = 2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDowsing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDowsing.java new file mode 100644 index 000000000..ca2656355 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationDowsing.java @@ -0,0 +1,285 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Color; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ExcavationDowsing extends SimpleAdaptation { + private static final int MAX_SCAN_RANGE = 24; + private static final int[][] SCAN_OFFSETS = buildScanOffsets(); + private final Map cooldowns = new ConcurrentHashMap<>(); + + public ExcavationDowsing() { + super("excavation-dowsing"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.dowsing.description")); + setDisplayName(Localizer.dLocalize("excavation.dowsing.name")); + setIcon(Material.COMPASS); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(3970); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.COMPASS) + .key("challenge_excavation_dowsing_200") + .title(Localizer.dLocalize("advancement.challenge_excavation_dowsing_200.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_dowsing_200.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_dowsing_200", "excavation.dowsing.pockets-found", 200, 400); + } + + private static int[][] buildScanOffsets() { + List offsets = new ArrayList<>(); + for (int x = -MAX_SCAN_RANGE; x <= MAX_SCAN_RANGE; x += 2) { + for (int y = -MAX_SCAN_RANGE; y <= MAX_SCAN_RANGE; y += 2) { + for (int z = -MAX_SCAN_RANGE; z <= MAX_SCAN_RANGE; z += 2) { + int d2 = (x * x) + (y * y) + (z * z); + if (d2 < 25 || d2 > MAX_SCAN_RANGE * MAX_SCAN_RANGE) { + continue; + } + + offsets.add(new int[]{x, y, z, d2}); + } + } + } + + offsets.sort(Comparator.comparingInt(o -> o[3])); + return offsets.toArray(new int[0][]); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getScanRange(level) + C.GRAY + " " + Localizer.dLocalize("excavation.dowsing.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.dowsing.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerToggleSneakEvent e) { + if (!e.isSneaking()) { + return; + } + + Player p = e.getPlayer(); + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + long now = System.currentTimeMillis(); + long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < nextReady) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + + Block pocket = findNearestPocket(p.getLocation(), getScanRange(level), getConfig().maxSamples); + if (pocket == null) { + return; + } + + Location origin = p.getEyeLocation(); + Location target = pocket.getLocation().add(0.5, 0.5, 0.5); + Vector direction = target.toVector().subtract(origin.toVector()); + if (direction.lengthSquared() <= 0.0000001) { + return; + } + + Material type = pocket.getType(); + renderTrail(p, origin, direction.normalize(), type); + playTone(p, type, origin.distance(target), getScanRange(level)); + getPlayer(p).getData().addStat("excavation.dowsing.pockets-found", 1); + xp(p, getConfig().xpPerPing); + } + + private Block findNearestPocket(Location origin, int range, int maxSamples) { + World world = origin.getWorld(); + if (world == null) { + return null; + } + + int ox = origin.getBlockX(); + int oy = origin.getBlockY(); + int oz = origin.getBlockZ(); + int minY = world.getMinHeight(); + int maxY = world.getMaxHeight() - 1; + int rangeSq = range * range; + int samples = 0; + + for (int[] offset : SCAN_OFFSETS) { + if (offset[3] > rangeSq || samples >= maxSamples) { + return null; + } + + samples++; + int by = oy + offset[1]; + if (by < minY || by > maxY) { + continue; + } + + int bx = ox + offset[0]; + int bz = oz + offset[2]; + if (!world.isChunkLoaded(bx >> 4, bz >> 4)) { + continue; + } + + Block block = world.getBlockAt(bx, by, bz); + Material type = block.getType(); + if ((type == Material.WATER || type == Material.LAVA || type.isAir()) && block.getLightFromSky() == 0) { + return block; + } + } + + return null; + } + + private void renderTrail(Player p, Location origin, Vector direction, Material type) { + if (!areParticlesEnabled()) { + return; + } + + Color color = switch (type) { + case WATER -> Color.fromRGB(70, 140, 255); + case LAVA -> Color.fromRGB(255, 120, 30); + default -> Color.fromRGB(205, 205, 215); + }; + Particle.DustOptions dust = new Particle.DustOptions(color, (float) getConfig().particleSize); + Location at = origin.clone(); + for (int i = 0; i < getConfig().trailSegments; i++) { + at = at.add(direction.clone().multiply(getConfig().segmentSpacing)); + p.spawnParticle(Particle.DUST, at, 2, 0.04, 0.04, 0.04, 0, dust); + } + + p.spawnParticle(Particle.END_ROD, at, 6, 0.1, 0.1, 0.1, 0.02); + } + + private void playTone(Player p, Material type, double distance, int range) { + double normalized = Math.min(1.0, distance / Math.max(1.0, range)); + float pitch = (float) Math.max(0.5, Math.min(2.0, 1.95 - (normalized * 1.2))); + Sound tone = switch (type) { + case WATER -> Sound.BLOCK_NOTE_BLOCK_CHIME; + case LAVA -> Sound.BLOCK_NOTE_BLOCK_BASS; + default -> Sound.BLOCK_NOTE_BLOCK_BIT; + }; + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), tone, 0.85f, pitch); + sp.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 0.3f, 1.6f); + } + + private int getScanRange(int level) { + return Math.min(MAX_SCAN_RANGE, Math.max(8, (int) Math.round(getConfig().scanRangeBase + (getLevelPercent(level) * getConfig().scanRangeFactor)))); + } + + private long getCooldownMillis(int level) { + return Math.max(2000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneaking with a shovel pings the nearest hidden cave, water, or lava pocket with a directional trail.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Base for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRangeBase = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Scan Range Factor for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double scanRangeFactor = 16; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Samples for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int maxSamples = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 9000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 4500; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Trail Segments for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int trailSegments = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Segment Spacing for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double segmentSpacing = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Particle Size for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double particleSize = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Ping for the Excavation Dowsing adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerPing = 6; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationEarthMover.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationEarthMover.java new file mode 100644 index 000000000..57f3a7f0e --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationEarthMover.java @@ -0,0 +1,273 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.RegistryUtil; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.Vector; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ExcavationEarthMover extends SimpleAdaptation { + private static final PotionEffectType SLOWNESS = RegistryUtil.find(PotionEffectType.class, "slowness", "slow"); + private final Map cooldowns = new ConcurrentHashMap<>(); + + public ExcavationEarthMover() { + super("excavation-earth-mover"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.earth_mover.description")); + setDisplayName(Localizer.dLocalize("excavation.earth_mover.name")); + setIcon(Material.DIRT); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(3730); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DIRT) + .key("challenge_excavation_earthmover_250") + .title(Localizer.dLocalize("advancement.challenge_excavation_earthmover_250.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_earthmover_250.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_earthmover_250", "excavation.earth-mover.waves-unleashed", 250, 450); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.earth_mover.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getForce(level), 0) + C.GRAY + " " + Localizer.dLocalize("excavation.earth_mover.lore2")); + v.addLore(C.GREEN + "+ " + Form.duration(getSlowTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("excavation.earth_mover.lore3")); + v.addLore(C.YELLOW + "* " + Form.duration(getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.earth_mover.lore4")); + v.addLore(C.RED + "- " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("excavation.earth_mover.lore5")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; + } + + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + long now = System.currentTimeMillis(); + long nextReady = cooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now < nextReady) { + return; + } + + int hungerCost = getConfig().hungerCost; + if (p.getFoodLevel() < hungerCost) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + + double radius = getRadius(level); + double force = getForce(level); + int slowTicks = getSlowTicks(level); + int slowAmplifier = getSlowAmplifier(level); + int hit = 0; + for (Entity nearby : p.getNearbyEntities(radius, getConfig().verticalRange, radius)) { + if (!(nearby instanceof Monster monster)) { + continue; + } + + if (!canDamageTarget(p, monster)) { + continue; + } + + Vector direction = monster.getLocation().toVector().subtract(p.getLocation().toVector()); + direction.setY(0); + if (direction.lengthSquared() < 0.01) { + direction = new Vector(1, 0, 0); + } + + direction.normalize().multiply(force).setY(getConfig().liftVelocity); + monster.setVelocity(direction); + monster.addPotionEffect(new PotionEffect(SLOWNESS, slowTicks, slowAmplifier, false, true, true)); + hit++; + } + + renderWave(p, radius); + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.BLOCK_ROOTED_DIRT_BREAK, 1.0f, 0.6f); + sp.play(p.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 0.4f, 0.5f); + getPlayer(p).getData().addStat("excavation.earth-mover.waves-unleashed", 1); + if (hit > 0) { + getPlayer(p).getData().addStat("excavation.earth-mover.mobs-launched", hit); + xp(p, hit * getConfig().xpPerMobHit); + } + } + + private void renderWave(Player p, double radius) { + if (!areParticlesEnabled()) { + return; + } + + World world = p.getWorld(); + Location base = p.getLocation().add(0, 0.2, 0); + ItemStack dirt = new ItemStack(Material.DIRT); + for (int pulse = 0; pulse < 3; pulse++) { + double r = radius * ((pulse + 1) / 3.0); + int points = 10 + (pulse * 6); + J.runEntity(p, () -> { + for (int i = 0; i < points; i++) { + double angle = (Math.PI * 2 * i) / points; + Location at = base.clone().add(Math.cos(angle) * r, 0, Math.sin(angle) * r); + world.spawnParticle(Particle.ITEM, at, 3, 0.12, 0.1, 0.12, 0.04, dirt); + world.spawnParticle(Particle.CLOUD, at, 1, 0.05, 0.05, 0.05, 0.01); + } + }, pulse * 2); + } + } + + private double getRadius(int level) { + return getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor); + } + + private double getForce(int level) { + return getConfig().forceBase + (getLevelPercent(level) * getConfig().forceFactor); + } + + private int getSlowTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().slowTicksBase + (getLevelPercent(level) * getConfig().slowTicksFactor))); + } + + private int getSlowAmplifier(int level) { + return Math.max(0, (int) Math.round(getLevelPercent(level) * getConfig().slowAmplifierMax)); + } + + private long getCooldownMillis(int level) { + return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-right-click the air with a shovel to fling a wave of earth that knocks back and slows hostile mobs.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Base for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Radius Factor for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double radiusFactor = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Vertical Range for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double verticalRange = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Base for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double forceBase = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Force Factor for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double forceFactor = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Lift Velocity for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double liftVelocity = 0.35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slow Ticks Base for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double slowTicksBase = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slow Ticks Factor for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double slowTicksFactor = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Slow Amplifier Max for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double slowAmplifierMax = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Base for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisBase = 16000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Cooldown Millis Factor for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double cooldownMillisFactor = 8000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hunger points consumed per earth wave.", impact = "Higher values drain more food per wave; activation fails when food is below the cost. Set to 0 to disable the hunger cost.") + int hungerCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Mob Hit for the Excavation Earth Mover adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerMobHit = 6; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java new file mode 100644 index 000000000..e98c49544 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java @@ -0,0 +1,296 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.Skeleton; +import org.bukkit.entity.Zombie; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class ExcavationGraveDigger extends SimpleAdaptation { + private final Map graveCooldowns = new ConcurrentHashMap<>(); + private volatile TableCache tableCache; + + public ExcavationGraveDigger() { + super("excavation-grave-digger"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.grave_digger.description")); + setDisplayName(Localizer.dLocalize("excavation.grave_digger.name")); + setIcon(Material.BONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4310); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_excavation_gravedigger_300") + .title(Localizer.dLocalize("advancement.challenge_excavation_gravedigger_300.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_gravedigger_300.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_gravedigger_300", "excavation.grave-digger.bones-unearthed", 300, 450); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getLootChance(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.grave_digger.lore1")); + v.addLore(C.RED + "+ " + Form.pc(getGraveChance(level), 2) + C.GRAY + " " + Localizer.dLocalize("excavation.grave_digger.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + graveCooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!isGraveSoil(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + Location center = e.getBlock().getLocation().add(0.5, 0.5, 0.5); + + if (random.nextDouble() < getLootChance(level)) { + dropLoot(p, center, random); + } + + if (random.nextDouble() < getGraveChance(level)) { + long now = System.currentTimeMillis(); + long nextReady = graveCooldowns.getOrDefault(p.getUniqueId(), 0L); + if (now >= nextReady) { + graveCooldowns.put(p.getUniqueId(), now + (long) getConfig().graveCooldownMillis); + disturbGrave(p, e.getBlock().getLocation(), random); + } + } + } + + private void dropLoot(Player p, Location center, ThreadLocalRandom random) { + TableCache table = getLootTable(); + if (table.entries().isEmpty()) { + return; + } + + LootEntry entry = pickWeighted(table, random.nextDouble() * table.totalWeight()); + int amount = entry.min() >= entry.max() ? entry.min() : random.nextInt(entry.min(), entry.max() + 1); + center.getWorld().dropItemNaturally(center, new ItemStack(entry.material(), amount)); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.ASH, center, 8, 0.25, 0.25, 0.25, 0.01); + } + + SoundPlayer.of(p.getWorld()).play(center, Sound.BLOCK_ROOTED_DIRT_BREAK, 0.7f, 0.8f); + getPlayer(p).getData().addStat("excavation.grave-digger.bones-unearthed", 1); + xp(p, getConfig().xpPerLoot); + } + + private void disturbGrave(Player p, Location blockLocation, ThreadLocalRandom random) { + Location spawnAt = blockLocation.add(0.5, 0, 0.5); + Monster grave = random.nextBoolean() + ? spawnAt.getWorld().spawn(spawnAt, Zombie.class, z -> z.setTarget(p)) + : spawnAt.getWorld().spawn(spawnAt, Skeleton.class, s -> s.setTarget(p)); + + if (areParticlesEnabled()) { + p.spawnParticle(Particle.SOUL, grave.getLocation().add(0, 1, 0), 14, 0.3, 0.5, 0.3, 0.02); + } + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(spawnAt, Sound.ENTITY_SKELETON_HURT, 0.8f, 0.6f); + sp.play(spawnAt, Sound.BLOCK_ROOTED_DIRT_BREAK, 1.0f, 0.5f); + getPlayer(p).getData().addStat("excavation.grave-digger.graves-disturbed", 1); + xp(p, getConfig().xpPerGrave); + } + + private LootEntry pickWeighted(TableCache table, double roll) { + List entries = table.entries(); + double remaining = roll; + for (LootEntry entry : entries) { + remaining -= entry.weight(); + if (remaining <= 0) { + return entry; + } + } + + return entries.get(entries.size() - 1); + } + + private TableCache getLootTable() { + List source = getConfig().lootTable; + TableCache local = tableCache; + if (local != null && local.source() == source) { + return local; + } + + List parsed = new ArrayList<>(source.size()); + double totalWeight = 0; + for (String raw : source) { + String[] parts = raw.split(":"); + if (parts.length < 2) { + continue; + } + + Material material = Material.matchMaterial(parts[0].trim()); + if (material == null) { + continue; + } + + double weight = parseDouble(parts[1]); + if (weight <= 0) { + continue; + } + + int min = parts.length > 2 ? Math.max(1, (int) parseDouble(parts[2])) : 1; + int max = parts.length > 3 ? Math.max(min, (int) parseDouble(parts[3])) : min; + parsed.add(new LootEntry(material, weight, min, max)); + totalWeight += weight; + } + + TableCache built = new TableCache(source, parsed, totalWeight); + tableCache = built; + return built; + } + + private double parseDouble(String raw) { + try { + return Double.parseDouble(raw.trim()); + } catch (NumberFormatException ignored) { + return 0; + } + } + + private boolean isGraveSoil(Material type) { + return switch (type) { + case DIRT, GRASS_BLOCK, COARSE_DIRT, ROOTED_DIRT, PODZOL, MYCELIUM, + DIRT_PATH -> true; + default -> false; + }; + } + + private double getLootChance(int level) { + return Math.min(getConfig().maxLootChance, getConfig().lootChanceBase + (getLevelPercent(level) * getConfig().lootChanceFactor)); + } + + private double getGraveChance(int level) { + return Math.min(getConfig().maxGraveChance, getConfig().graveChanceBase + (getLevelPercent(level) * getConfig().graveChanceFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record LootEntry(Material material, double weight, int min, int max) { + } + + private record TableCache(List source, List entries, + double totalWeight) { + } + + @NoArgsConstructor + @ConfigDescription("Digging earthen ground can unearth bone loot, and rarely disturbs a hostile grave.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.68; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Loot Chance Base for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lootChanceBase = 0.008; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Loot Chance Factor for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double lootChanceFactor = 0.035; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Loot Chance for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxLootChance = 0.045; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Grave Chance Base for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double graveChanceBase = 0.001; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Grave Chance Factor for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double graveChanceFactor = 0.004; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Grave Chance for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxGraveChance = 0.005; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Grave Cooldown Millis for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double graveCooldownMillis = 45000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Weighted loot table entries formatted as MATERIAL:weight:min:max.", impact = "Adding entries or raising weights changes which bone loot drops and how often.") + List lootTable = new ArrayList<>(List.of( + "BONE:40:1:2", + "BONE_MEAL:25:2:4", + "ROTTEN_FLESH:20:1:2", + "BONE_BLOCK:6:1:1", + "SKELETON_SKULL:2:1:1" + )); + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Loot for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerLoot = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Grave for the Excavation Grave Digger adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerGrave = 35; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationMudlark.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationMudlark.java new file mode 100644 index 000000000..27a666c99 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationMudlark.java @@ -0,0 +1,208 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +import java.util.concurrent.ThreadLocalRandom; + +public class ExcavationMudlark extends SimpleAdaptation { + public ExcavationMudlark() { + super("excavation-mudlark"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.mudlark.description")); + setDisplayName(Localizer.dLocalize("excavation.mudlark.name")); + setIcon(Material.MUD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4530); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.MUD) + .key("challenge_excavation_mudlark_1k") + .title(Localizer.dLocalize("advancement.challenge_excavation_mudlark_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_mudlark_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_mudlark_1k", "excavation.mudlark.bonus-drops", 1000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getBonusChance(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.mudlark.lore1")); + v.addLore(C.GREEN + "+ " + (getHasteAmplifier(level) + 1) + C.GRAY + " " + Localizer.dLocalize("excavation.mudlark.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDamageEvent e) { + Player p = e.getPlayer(); + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + if (!isWet(p)) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, getConfig().hasteDurationTicks, getHasteAmplifier(context.level()), false, false, true)); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + Material type = e.getBlock().getType(); + if (!isMudlarkBlock(type)) { + return; + } + + Player p = e.getPlayer(); + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + if (ThreadLocalRandom.current().nextDouble() > getBonusChance(level)) { + return; + } + + Material bonus = getBonusDrop(type); + Location drop = e.getBlock().getLocation().add(0.5, 0.5, 0.5); + e.getBlock().getWorld().dropItemNaturally(drop, new ItemStack(bonus, 1)); + if (areParticlesEnabled()) { + p.spawnParticle(Particle.SPLASH, drop, 8, 0.25, 0.2, 0.25, 0.01); + } + + SoundPlayer.of(p.getWorld()).play(drop, Sound.BLOCK_ROOTED_DIRT_BREAK, 0.6f, 1.2f); + getPlayer(p).getData().addStat("excavation.mudlark.bonus-drops", 1); + xp(p, getConfig().xpPerBonusDrop); + } + + private boolean isWet(Player p) { + if (p.isInWater()) { + return true; + } + + if (!p.getWorld().hasStorm()) { + return false; + } + + return p.getLocation().getBlock().getLightFromSky() >= 14; + } + + private boolean isMudlarkBlock(Material type) { + return switch (type) { + case CLAY, MUD, MUDDY_MANGROVE_ROOTS, SOUL_SAND, SOUL_SOIL -> true; + default -> false; + }; + } + + private Material getBonusDrop(Material type) { + return switch (type) { + case CLAY -> Material.CLAY_BALL; + case MUD, MUDDY_MANGROVE_ROOTS -> Material.MUD; + case SOUL_SAND -> Material.SOUL_SAND; + default -> Material.SOUL_SOIL; + }; + } + + private double getBonusChance(int level) { + return Math.min(getConfig().maxBonusChance, getConfig().bonusChanceBase + (getLevelPercent(level) * getConfig().bonusChanceFactor)); + } + + private int getHasteAmplifier(int level) { + return Math.max(0, (int) Math.round(getLevelPercent(level) * (getConfig().maxHasteLevel - 1))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bonus drops from muddy blocks, plus haste while digging in water or rain.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Chance Base for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusChanceBase = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Chance Factor for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusChanceFactor = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Bonus Chance for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxBonusChance = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Haste Level for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxHasteLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Haste Duration Ticks for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int hasteDurationTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Drop for the Excavation Mudlark adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBonusDrop = 3; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java index 9c88d6227..c54ab109b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationOmniTool.java @@ -48,6 +48,7 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; @@ -175,10 +176,14 @@ public void on(PlayerInteractEvent e) { if (!hasActiveAdaptation(p)) { return; } - if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + Action action = e.getAction(); + if (action == Action.RIGHT_CLICK_BLOCK || action == Action.RIGHT_CLICK_AIR) { + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } ItemStack hand = p.getInventory().getItemInMainHand(); Damageable imHand = (Damageable) hand.getItemMeta(); - Block block = e.getClickedBlock(); + Block block = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); if (block != null) { SoundPlayer sp = SoundPlayer.of(p); SoundPlayer spw = SoundPlayer.of(p.getWorld()); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSoftFall.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSoftFall.java new file mode 100644 index 000000000..9da78da3d --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSoftFall.java @@ -0,0 +1,162 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; + +public class ExcavationSoftFall extends SimpleAdaptation { + public ExcavationSoftFall() { + super("excavation-soft-fall"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.soft_fall.description")); + setDisplayName(Localizer.dLocalize("excavation.soft_fall.name")); + setIcon(Material.SAND); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(3530); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SAND) + .key("challenge_excavation_softfall_1k") + .title(Localizer.dLocalize("advancement.challenge_excavation_softfall_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_softfall_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_softfall_1k", "excavation.soft-fall.damage-prevented", 1000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getReduction(level), 0) + C.GRAY + " " + Localizer.dLocalize("excavation.soft_fall.lore1")); + v.addLore(C.GRAY + Localizer.dLocalize("excavation.soft_fall.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageEvent e) { + if (e.getCause() != EntityDamageEvent.DamageCause.FALL || !(e.getEntity() instanceof Player p)) { + return; + } + + Block feet = p.getLocation().getBlock(); + if (!isSoftGround(feet.getType()) && !isSoftGround(feet.getRelative(BlockFace.DOWN).getType())) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + double reduction = getReduction(level); + double prevented = e.getDamage() * reduction; + if (prevented <= 0) { + return; + } + + e.setDamage(Math.max(0, e.getDamage() - prevented)); + if (e.getDamage() <= 0.01) { + e.setCancelled(true); + } + + if (areParticlesEnabled()) { + p.spawnParticle(Particle.CLOUD, p.getLocation(), 8, 0.3, 0.1, 0.3, 0.02); + } + + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ROOTED_DIRT_BREAK, 0.6f, 0.7f); + getPlayer(p).getData().addStat("excavation.soft-fall.damage-prevented", prevented); + xp(p, prevented * getConfig().xpPerDamagePrevented); + }); + } + + private boolean isSoftGround(Material type) { + return switch (type) { + case DIRT, GRASS_BLOCK, COARSE_DIRT, ROOTED_DIRT, PODZOL, MYCELIUM, + DIRT_PATH, FARMLAND, SAND, RED_SAND, GRAVEL, CLAY, MUD, + MUDDY_MANGROVE_ROOTS, SOUL_SAND, SOUL_SOIL, SNOW, SNOW_BLOCK -> + true; + default -> false; + }; + } + + private double getReduction(int level) { + return Math.min(getConfig().maxReduction, getConfig().reductionBase + (getLevelPercent(level) * getConfig().reductionFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Landing on soft diggable ground reduces fall damage, up to full negation.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Base for the Excavation Soft Fall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionBase = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Reduction Factor for the Excavation Soft Fall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double reductionFactor = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Reduction for the Excavation Soft Fall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxReduction = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Damage Prevented for the Excavation Soft Fall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerDamagePrevented = 3.0; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTreasureHunter.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTreasureHunter.java new file mode 100644 index 000000000..064d06a52 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTreasureHunter.java @@ -0,0 +1,244 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class ExcavationTreasureHunter extends SimpleAdaptation { + private volatile TableCache tableCache; + + public ExcavationTreasureHunter() { + super("excavation-treasure-hunter"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.treasure_hunter.description")); + setDisplayName(Localizer.dLocalize("excavation.treasure_hunter.name")); + setIcon(Material.EMERALD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(3370); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EMERALD) + .key("challenge_excavation_treasure_500") + .title(Localizer.dLocalize("advancement.challenge_excavation_treasure_500.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_treasure_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_treasure_500", "excavation.treasure-hunter.treasures-found", 500, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getTreasureChance(level), 1) + C.GRAY + " " + Localizer.dLocalize("excavation.treasure_hunter.lore1")); + v.addLore(C.GRAY + Localizer.dLocalize("excavation.treasure_hunter.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!isTreasureBlock(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isShovel(p.getInventory().getItemInMainHand())) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + ThreadLocalRandom random = ThreadLocalRandom.current(); + if (random.nextDouble() > getTreasureChance(level)) { + return; + } + + TableCache table = getLootTable(); + if (table.entries().isEmpty()) { + return; + } + + LootEntry entry = pickWeighted(table, random.nextDouble() * table.totalWeight()); + int amount = entry.min() >= entry.max() ? entry.min() : random.nextInt(entry.min(), entry.max() + 1); + Location drop = e.getBlock().getLocation().add(0.5, 0.5, 0.5); + e.getBlock().getWorld().dropItemNaturally(drop, new ItemStack(entry.material(), amount)); + + if (areParticlesEnabled()) { + p.spawnParticle(Particle.WAX_ON, drop, 10, 0.25, 0.25, 0.25, 0.02); + } + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(drop, Sound.ITEM_BRUSH_BRUSHING_SAND_COMPLETE, 0.8f, 1.2f); + sp.play(drop, Sound.BLOCK_NOTE_BLOCK_PLING, 0.6f, 1.7f); + getPlayer(p).getData().addStat("excavation.treasure-hunter.treasures-found", 1); + xp(p, getConfig().xpPerTreasure); + } + + private LootEntry pickWeighted(TableCache table, double roll) { + List entries = table.entries(); + double remaining = roll; + for (LootEntry entry : entries) { + remaining -= entry.weight(); + if (remaining <= 0) { + return entry; + } + } + + return entries.get(entries.size() - 1); + } + + private TableCache getLootTable() { + List source = getConfig().lootTable; + TableCache local = tableCache; + if (local != null && local.source() == source) { + return local; + } + + List parsed = new ArrayList<>(source.size()); + double totalWeight = 0; + for (String raw : source) { + String[] parts = raw.split(":"); + if (parts.length < 2) { + continue; + } + + Material material = Material.matchMaterial(parts[0].trim()); + if (material == null) { + continue; + } + + double weight = parseDouble(parts[1]); + if (weight <= 0) { + continue; + } + + int min = parts.length > 2 ? Math.max(1, (int) parseDouble(parts[2])) : 1; + int max = parts.length > 3 ? Math.max(min, (int) parseDouble(parts[3])) : min; + parsed.add(new LootEntry(material, weight, min, max)); + totalWeight += weight; + } + + TableCache built = new TableCache(source, parsed, totalWeight); + tableCache = built; + return built; + } + + private double parseDouble(String raw) { + try { + return Double.parseDouble(raw.trim()); + } catch (NumberFormatException ignored) { + return 0; + } + } + + private boolean isTreasureBlock(Material type) { + return switch (type) { + case SAND, RED_SAND, GRAVEL, MUD, CLAY -> true; + default -> false; + }; + } + + private double getTreasureChance(int level) { + return Math.min(getConfig().maxTreasureChance, getConfig().treasureChanceBase + (getLevelPercent(level) * getConfig().treasureChanceFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record LootEntry(Material material, double weight, int min, int max) { + } + + private record TableCache(List source, List entries, + double totalWeight) { + } + + @NoArgsConstructor + @ConfigDescription("Digging sand, gravel, mud, or clay with a shovel can unearth archaeology treasure.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Treasure Chance Base for the Excavation Treasure Hunter adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double treasureChanceBase = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Treasure Chance Factor for the Excavation Treasure Hunter adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double treasureChanceFactor = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Max Treasure Chance for the Excavation Treasure Hunter adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double maxTreasureChance = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Weighted loot table entries formatted as MATERIAL:weight:min:max.", impact = "Adding entries or raising weights changes which treasures drop and how often.") + List lootTable = new ArrayList<>(List.of( + "BONE:30:1:2", + "FLINT:30:1:2", + "CLAY_BALL:15:1:3", + "ANGLER_POTTERY_SHERD:6:1:1", + "ARMS_UP_POTTERY_SHERD:6:1:1", + "SKULL_POTTERY_SHERD:4:1:1", + "EMERALD:3:1:1" + )); + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Treasure for the Excavation Treasure Hunter adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerTreasure = 12; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTunneler.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTunneler.java new file mode 100644 index 000000000..f37a97e3c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationTunneler.java @@ -0,0 +1,228 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.excavation; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +public class ExcavationTunneler extends SimpleAdaptation { + private static final int[][] PLANE_OFFSETS = { + {0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1} + }; + + public ExcavationTunneler() { + super("excavation-tunneler"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("excavation.tunneler.description")); + setDisplayName(Localizer.dLocalize("excavation.tunneler.name")); + setIcon(Material.IRON_SHOVEL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(3170); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_SHOVEL) + .key("challenge_excavation_tunneler_10k") + .title(Localizer.dLocalize("advancement.challenge_excavation_tunneler_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_excavation_tunneler_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_excavation_tunneler_10k", "excavation.tunneler.blocks-tunneled", 10000, 600); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getBonusBlocks(level) + C.GRAY + " " + Localizer.dLocalize("excavation.tunneler.lore1")); + v.addLore(C.YELLOW + "* " + getConfig().durabilityCostPerBonusBlock + C.GRAY + " " + Localizer.dLocalize("excavation.tunneler.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isShovel(hand)) { + return; + } + + if (!isShovelable(e.getBlock().getType())) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + int bonus = getBonusBlocks(level); + float pitch = p.getLocation().getPitch(); + float yaw = p.getLocation().getYaw(); + boolean horizontal = pitch >= 50 || pitch <= -50; + boolean facingX = !horizontal && isFacingX(yaw); + + Block origin = e.getBlock(); + int broken = 0; + for (int[] offset : PLANE_OFFSETS) { + if (broken >= bonus) { + break; + } + + int dx; + int dy; + int dz; + if (horizontal) { + dx = offset[0]; + dy = 0; + dz = offset[1]; + } else if (facingX) { + dx = 0; + dy = offset[1]; + dz = offset[0]; + } else { + dx = offset[0]; + dy = offset[1]; + dz = 0; + } + + Block target = origin.getRelative(dx, dy, dz); + if (!isShovelable(target.getType())) { + continue; + } + + if (!canBlockBreak(p, target.getLocation())) { + continue; + } + + if (!applyDurability(p, hand, getConfig().durabilityCostPerBonusBlock)) { + break; + } + + target.breakNaturally(hand); + broken++; + } + + if (broken <= 0) { + return; + } + + SoundPlayer.of(p.getWorld()).play(origin.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 0.7f, 0.8f); + getPlayer(p).getData().addStat("excavation.tunneler.blocks-tunneled", broken); + xp(p, broken * getConfig().xpPerBonusBlock); + } + + private boolean isFacingX(float yaw) { + float normalized = ((yaw % 360) + 360) % 360; + return (normalized >= 45 && normalized < 135) || (normalized >= 225 && normalized < 315); + } + + private boolean applyDurability(Player p, ItemStack hand, int cost) { + if (cost <= 0) { + return true; + } + + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return false; + } + + int maxDurability = hand.getType().getMaxDurability(); + int currentDamage = damageable.getDamage(); + if (currentDamage + cost >= maxDurability) { + return false; + } + + damageable.setDamage(currentDamage + cost); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + return true; + } + + private boolean isShovelable(Material type) { + return switch (type) { + case CLAY, DIRT, COARSE_DIRT, ROOTED_DIRT, FARMLAND, GRASS_BLOCK, + DIRT_PATH, GRAVEL, MYCELIUM, PODZOL, SAND, RED_SAND, SOUL_SAND, + SOUL_SOIL, SNOW, SNOW_BLOCK, MUD, MUDDY_MANGROVE_ROOTS -> true; + default -> false; + }; + } + + private int getBonusBlocks(int level) { + return Math.max(1, Math.min(8, (int) Math.floor(getLevelPercent(level) * getConfig().bonusBlocksMax))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak while digging soft blocks to carve a facing-oriented plane of blocks at once.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Bonus Blocks Max for the Excavation Tunneler adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double bonusBlocksMax = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Durability Cost Per Bonus Block for the Excavation Tunneler adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + int durabilityCostPerBonusBlock = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp Per Bonus Block for the Excavation Tunneler adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") + double xpPerBonusBlock = 1.5; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index 2a4314a99..e76c63fdd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -92,16 +92,26 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + Action action = e.getAction(); + if ((action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) || e.getHand() != EquipmentSlot.HAND) { return; } Player p = e.getPlayer(); - if (!hasActiveAdaptation(p) || !p.isSneaking() || e.getClickedBlock().getType() != Material.COMPOSTER || p.hasCooldown(Material.COMPOSTER)) { + if (!hasActiveAdaptation(p) || !p.isSneaking() || p.hasCooldown(Material.COMPOSTER)) { return; } - if (!(e.getClickedBlock().getBlockData() instanceof Levelled levelled)) { + Block composter = e.getClickedBlock(); + if (composter == null) { + composter = p.getTargetBlockExact(5); + } + + if (composter == null || composter.getType() != Material.COMPOSTER) { + return; + } + + if (!(composter.getBlockData() instanceof Levelled levelled)) { return; } @@ -114,7 +124,7 @@ public void on(PlayerInteractEvent e) { double fillChance = getFillChance(level); int maxItems = getMaxItems(level); double radius = getRadius(level); - Location center = e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5); + Location center = composter.getLocation().clone().add(0.5, 0.5, 0.5); World world = center.getWorld(); if (world == null) { return; @@ -131,9 +141,9 @@ public void on(PlayerInteractEvent e) { return; } - Levelled updated = (Levelled) e.getClickedBlock().getBlockData(); + Levelled updated = (Levelled) composter.getBlockData(); updated.setLevel(Math.min(8, Math.max(oldLevel, state.compostLevel))); - e.getClickedBlock().setBlockData(updated); + composter.setBlockData(updated); p.setCooldown(Material.COMPOSTER, getCooldownTicks(level)); e.setCancelled(true); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index 0a0044fa5..e53a37622 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -44,6 +44,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import java.util.Collection; @@ -101,55 +102,59 @@ private float getRadius(int lvl) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - Player p = e.getPlayer(); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (e.getClickedBlock() == null) { + Action action = e.getAction(); + if ((action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) || e.getHand() != EquipmentSlot.HAND) { return; } - if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { // you need to right-click to harvest! + + Player p = e.getPlayer(); + int lvl = getLevel(p); + if (lvl <= 0) { return; } - if (!(e.getClickedBlock().getBlockData() instanceof Ageable)) { + Block target = e.getClickedBlock(); + if (target == null) { + target = p.getTargetBlockExact(5); + } + + if (target == null || !(target.getBlockData() instanceof Ageable)) { return; } - int lvl = getLevel(p); + ItemStack right = p.getInventory().getItemInMainHand(); + ItemStack left = p.getInventory().getItemInOffHand(); - if (lvl > 0) { - ItemStack right = p.getInventory().getItemInMainHand(); - ItemStack left = p.getInventory().getItemInOffHand(); + if (isTool(left) && isHoe(left) && !p.hasCooldown(left.getType())) { + damageOffHand(p, 1 + ((lvl - 1) * 7)); + p.setCooldown(left.getType(), getCooldown(getLevelPercent(p), getLevel(p))); + } else if (isTool(right) && isHoe(right) && !p.hasCooldown(right.getType())) { + damageHand(p, 1 + ((lvl - 1) * 7)); + p.setCooldown(right.getType(), getCooldown(getLevelPercent(p), getLevel(p))); + } else { + return; + } - if (isTool(left) && isHoe(left) && !p.hasCooldown(left.getType())) { - damageOffHand(p, 1 + ((lvl - 1) * 7)); - p.setCooldown(left.getType(), getCooldown(getLevelPercent(p), getLevel(p))); - } else if (isTool(right) && isHoe(right) && !p.hasCooldown(right.getType())) { - damageHand(p, 1 + ((lvl - 1) * 7)); - p.setCooldown(right.getType(), getCooldown(getLevelPercent(p), getLevel(p))); - } else { - return; + if (lvl > 1) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + Cuboid c = new Cuboid(target.getLocation().clone().add(0.5, 0.5, 0.5)); + c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.North, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.South, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.East, Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.West, Math.round(getRadius(lvl))); + + for (Block i : c) { + J.runEntity(p, () -> hit(p, i), M.irand(1, 6)); } - - if (lvl > 1) { - Cuboid c = new Cuboid(e.getClickedBlock().getLocation().clone().add(0.5, 0.5, 0.5)); - c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.North, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.South, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.East, Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.West, Math.round(getRadius(lvl))); - - for (Block i : c) { - J.runEntity(p, () -> hit(p, i), M.irand(1, 6)); - } - spw.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 1f, 0.66f); - spw.play(p.getLocation(), Sound.BLOCK_BAMBOO_SAPLING_BREAK, 1f, 0.66f); - if (areParticlesEnabled()) { - p.spawnParticle(Particles.VILLAGER_HAPPY, p.getLocation().clone().add(0.5, 0.5, 0.5), getLevel(p) * 3, 0.3 * getLevel(p), 0.3 * getLevel(p), 0.3 * getLevel(p), 0.9); - } - } else { - hit(p, e.getClickedBlock()); + spw.play(p.getLocation(), Sound.ITEM_SHOVEL_FLATTEN, 1f, 0.66f); + spw.play(p.getLocation(), Sound.BLOCK_BAMBOO_SAPLING_BREAK, 1f, 0.66f); + if (areParticlesEnabled()) { + p.spawnParticle(Particles.VILLAGER_HAPPY, p.getLocation().clone().add(0.5, 0.5, 0.5), getLevel(p) * 3, 0.3 * getLevel(p), 0.3 * getLevel(p), 0.3 * getLevel(p), 0.9); } + } else { + hit(p, target); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java index 66a19aeaf..e13e4aefd 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSeedSower.java @@ -83,13 +83,13 @@ public void addStats(int level, Element v) { @EventHandler(priority = EventPriority.HIGHEST) public void on(PlayerInteractEvent e) { - - Player p = e.getPlayer(); - if (!hasActiveAdaptation(p) || !p.isSneaking()) { + Action action = e.getAction(); + if ((action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) || e.getHand() != EquipmentSlot.HAND) { return; } - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getHand() != EquipmentSlot.HAND || e.getClickedBlock() == null) { + Player p = e.getPlayer(); + if (!p.isSneaking() || !hasActiveAdaptation(p)) { return; } @@ -104,7 +104,16 @@ public void on(PlayerInteractEvent e) { return; } - int planted = plantNearby(p, e.getClickedBlock(), hand, seedType, cropType, getRadius(getLevel(p)), getMaxCrops(getLevel(p))); + Block origin = e.getClickedBlock(); + if (origin == null) { + origin = p.getTargetBlockExact(5); + } + + if (origin == null) { + return; + } + + int planted = plantNearby(p, origin, hand, seedType, cropType, getRadius(getLevel(p)), getMaxCrops(getLevel(p))); if (planted <= 0) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java index 5be47a487..e56b81d8a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeChisel.java @@ -34,13 +34,16 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.RayTraceResult; public class PickaxeChisel extends SimpleAdaptation { public PickaxeChisel() { @@ -90,52 +93,68 @@ private int getDamagePerBlock(double levelPercent) { @EventHandler public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (e.getHand() != null && e.getHand() != EquipmentSlot.HAND) { + return; + } + Player p = e.getPlayer(); + if (!isPickaxe(p.getInventory().getItemInMainHand()) || !hasActiveAdaptation(p)) { + return; + } - if (e.getClickedBlock() != null && e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && isPickaxe(p.getInventory().getItemInMainHand()) && hasActiveAdaptation(p)) { - if (p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH) || p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.MENDING)) { - return; - } - if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { - return; - } - if (!canBlockBreak(p, e.getClickedBlock().getLocation())) { - return; - } - BlockData b = e.getClickedBlock().getBlockData(); - if (isOre(b)) { - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 1.4f); - spw.play(p.getLocation(), Sound.BLOCK_METAL_HIT, 1.25f, 1.7f); - - p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); - damageHand(p, getDamagePerBlock(getLevelPercent(p))); - - Location c = p.rayTraceBlocks(8).getHitPosition().toLocation(p.getWorld()); - - ItemStack is = getDropFor(b); - if (M.r(getDropChance(getLevelPercent(p)))) { - if (areParticlesEnabled()) { - e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 14, 0.10, 0.01, 0.01, 0.1, is); - } - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.787f); - spw.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_PLACE, 0.55f, 1.89f); - e.getClickedBlock().getWorld().dropItemNaturally(c.clone().subtract(p.getLocation().getDirection().clone().multiply(0.1)), is); - getPlayer(p).getData().addStat("pickaxe.chisel.extra-ores", 1); - } else { - if (areParticlesEnabled()) { - e.getClickedBlock().getWorld().spawnParticle(Particles.ITEM_CRACK, c, 3, 0.01, 0.01, 0.01, 0.1, is); - e.getClickedBlock().getWorld().spawnParticle(Particles.BLOCK_CRACK, c, 9, 0.1, 0.1, 0.1, e.getClickedBlock().getBlockData()); - } - } + if (p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.SILK_TOUCH) || p.getInventory().getItemInMainHand().getEnchantments().containsKey(Enchantment.MENDING)) { + return; + } + if (p.getCooldown(p.getInventory().getItemInMainHand().getType()) > 0) { + return; + } + + Block target = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (target == null) { + return; + } - if (M.r(getBreakChance(getLevelPercent(p)))) { - spw.play(p.getLocation(), Sound.BLOCK_BASALT_BREAK, 1.25f, 0.4f); - spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.887f); - e.getClickedBlock().breakNaturally(p.getInventory().getItemInMainHand()); + if (!canBlockBreak(p, target.getLocation())) { + return; + } + BlockData b = target.getBlockData(); + if (isOre(b)) { + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 1.4f); + spw.play(p.getLocation(), Sound.BLOCK_METAL_HIT, 1.25f, 1.7f); + + p.setCooldown(p.getInventory().getItemInMainHand().getType(), getCooldownTime(getLevelPercent(p))); + damageHand(p, getDamagePerBlock(getLevelPercent(p))); + + RayTraceResult ray = p.rayTraceBlocks(8); + Location c = ray != null ? ray.getHitPosition().toLocation(p.getWorld()) : target.getLocation().add(0.5, 0.5, 0.5); + + ItemStack is = getDropFor(b); + if (M.r(getDropChance(getLevelPercent(p)))) { + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particles.ITEM_CRACK, c, 14, 0.10, 0.01, 0.01, 0.1, is); + } + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.787f); + spw.play(p.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_PLACE, 0.55f, 1.89f); + target.getWorld().dropItemNaturally(c.clone().subtract(p.getLocation().getDirection().clone().multiply(0.1)), is); + getPlayer(p).getData().addStat("pickaxe.chisel.extra-ores", 1); + } else { + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particles.ITEM_CRACK, c, 3, 0.01, 0.01, 0.01, 0.1, is); + target.getWorld().spawnParticle(Particles.BLOCK_CRACK, c, 9, 0.1, 0.1, 0.1, target.getBlockData()); } } + if (M.r(getBreakChance(getLevelPercent(p)))) { + spw.play(p.getLocation(), Sound.BLOCK_BASALT_BREAK, 1.25f, 0.4f); + spw.play(p.getLocation(), Sound.BLOCK_DEEPSLATE_PLACE, 1.25f, 0.887f); + target.breakNaturally(p.getInventory().getItemInMainHand()); + } } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDeepCore.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDeepCore.java new file mode 100644 index 000000000..0c1711275 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeDeepCore.java @@ -0,0 +1,153 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.potion.PotionEffect; + +import java.util.EnumSet; +import java.util.Set; + +public class PickaxeDeepCore extends SimpleAdaptation { + private static final Set DEEPSLATE_BLOCKS = EnumSet.of( + Material.DEEPSLATE, Material.COBBLED_DEEPSLATE, Material.POLISHED_DEEPSLATE, + Material.DEEPSLATE_BRICKS, Material.DEEPSLATE_TILES, + Material.DEEPSLATE_COAL_ORE, Material.DEEPSLATE_COPPER_ORE, + Material.DEEPSLATE_IRON_ORE, Material.DEEPSLATE_GOLD_ORE, + Material.DEEPSLATE_REDSTONE_ORE, Material.DEEPSLATE_LAPIS_ORE, + Material.DEEPSLATE_DIAMOND_ORE, Material.DEEPSLATE_EMERALD_ORE); + + public PickaxeDeepCore() { + super("pickaxe-deep-core"); + registerConfiguration(PickaxeDeepCore.Config.class); + setDescription(Localizer.dLocalize("pickaxe.deep_core.description")); + setDisplayName(Localizer.dLocalize("pickaxe.deep_core.name")); + setIcon(Material.DEEPSLATE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5825); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.DEEPSLATE) + .key("challenge_pickaxe_deepcore_5k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_deepcore_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_deepcore_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_deepcore_5k", "pickaxe.deep-core.deepslate-mined", 5000, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.deep_core.lore1")); + v.addLore(C.GREEN + "" + (getAmplifier(level) + 1) + C.GRAY + " " + Localizer.dLocalize("pickaxe.deep_core.lore2")); + } + + private int getAmplifier(int level) { + return Math.min(getConfig().maxAmplifier, getConfig().amplifierBase + level - 1); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDamageEvent e) { + if (!DEEPSLATE_BLOCKS.contains(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isPickaxe(p.getInventory().getItemInMainHand())) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, getConfig().durationTicks, getAmplifier(context.level()), false, false, true)); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!DEEPSLATE_BLOCKS.contains(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isPickaxe(p.getInventory().getItemInMainHand()) || getActiveLevel(p) <= 0) { + return; + } + + getPlayer(p).getData().addStat("pickaxe.deep-core.deepslate-mined", 1); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain Haste while mining deepslate so it digs like normal stone.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Haste amplifier granted at level 1 while mining deepslate.", impact = "Higher values make deepslate mine faster at every level.") + int amplifierBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum Haste amplifier this adaptation can grant.", impact = "Higher values allow stronger Haste at high levels.") + int maxAmplifier = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the Haste effect applied when damaging deepslate.", impact = "Higher values keep the effect active longer between swings.") + int durationTicks = 60; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java new file mode 100644 index 000000000..f1e41efa8 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +public class PickaxeGemPolish extends SimpleAdaptation { + public PickaxeGemPolish() { + super("pickaxe-gem-polish"); + registerConfiguration(PickaxeGemPolish.Config.class); + setDescription(Localizer.dLocalize("pickaxe.gem_polish.description")); + setDisplayName(Localizer.dLocalize("pickaxe.gem_polish.name")); + setIcon(Material.DIAMOND); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(6844); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EMERALD) + .key("challenge_pickaxe_gempolish_500") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_gempolish_500.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_gempolish_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_gempolish_500", "pickaxe.gem-polish.gems-polished", 500, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.gem_polish.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getGemChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("pickaxe.gem_polish.lore2")); + v.addLore(C.GREEN + "+ " + getBonusXp(level) + C.GRAY + " " + Localizer.dLocalize("pickaxe.gem_polish.lore3")); + } + + private double getGemChance(int level) { + return Math.min(getConfig().maxGemChance, getConfig().gemChanceBase + (level * getConfig().gemChancePerLevel)); + } + + private int getBonusXp(int level) { + return getConfig().bonusXpBase + (level * getConfig().bonusXpPerLevel); + } + + private Material getGemFor(Material type) { + return switch (type) { + case DIAMOND_ORE, DEEPSLATE_DIAMOND_ORE -> Material.DIAMOND; + case EMERALD_ORE, DEEPSLATE_EMERALD_ORE -> Material.EMERALD; + case LAPIS_ORE, DEEPSLATE_LAPIS_ORE -> Material.LAPIS_LAZULI; + case AMETHYST_CLUSTER -> Material.AMETHYST_SHARD; + default -> null; + }; + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + Material gem = getGemFor(e.getBlock().getType()); + if (gem == null) { + return; + } + + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isPickaxe(hand)) { + return; + } + + if (getConfig().preventSilkTouchDoubleDip && hand.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + Location drop = e.getBlock().getLocation().add(0.5, 0.5, 0.5); + int bonusXp = getBonusXp(context.level()); + if (bonusXp > 0) { + e.getBlock().getWorld().spawn(drop, org.bukkit.entity.ExperienceOrb.class).setExperience(bonusXp); + } + + if (M.r(getGemChance(context.level()))) { + e.getBlock().getWorld().dropItemNaturally(drop, new ItemStack(gem)); + getPlayer(p).getData().addStat("pickaxe.gem-polish.gems-polished", 1); + if (areParticlesEnabled()) { + e.getBlock().getWorld().spawnParticle(Particle.HAPPY_VILLAGER, drop, 6, 0.25, 0.25, 0.25); + } + } + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Mining gem ores grants bonus XP orbs and a chance for an extra matching gem.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Skips all bonuses when the pickaxe has Silk Touch.", impact = "True prevents double-dipping by silk-touching ores and mining them again.") + boolean preventSilkTouchDoubleDip = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance for an extra gem drop when mining a gem ore.", impact = "Higher values drop extra gems more often at every level.") + double gemChanceBase = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional extra-gem chance gained per adaptation level.", impact = "Higher values drop extra gems more often at higher levels.") + double gemChancePerLevel = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum total extra-gem chance.", impact = "Higher values allow more frequent extra gems at max level.") + double maxGemChance = 0.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base bonus XP orb value granted per mined gem ore.", impact = "Higher values grant more XP at every level.") + int bonusXpBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional bonus XP orb value gained per adaptation level.", impact = "Higher values grant more XP at higher levels.") + int bonusXpPerLevel = 2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeObsidianRush.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeObsidianRush.java new file mode 100644 index 000000000..6ecc3f920 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeObsidianRush.java @@ -0,0 +1,159 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +public class PickaxeObsidianRush extends SimpleAdaptation { + public PickaxeObsidianRush() { + super("pickaxe-obsidian-rush"); + registerConfiguration(PickaxeObsidianRush.Config.class); + setDescription(Localizer.dLocalize("pickaxe.obsidian_rush.description")); + setDisplayName(Localizer.dLocalize("pickaxe.obsidian_rush.name")); + setIcon(Material.CRYING_OBSIDIAN); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(6233); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.OBSIDIAN) + .key("challenge_pickaxe_obsidianrush_1k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_obsidianrush_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_obsidianrush_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_obsidianrush_1k", "pickaxe.obsidian-rush.obsidian-mined", 1000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.obsidian_rush.lore1")); + v.addLore(C.GREEN + "" + (getAmplifier(level) + 1) + C.GRAY + " " + Localizer.dLocalize("pickaxe.obsidian_rush.lore2")); + v.addLore(C.ITALIC + Localizer.dLocalize("pickaxe.obsidian_rush.lore3")); + } + + private int getAmplifier(int level) { + return Math.min(getConfig().maxAmplifier, getConfig().amplifierBase + level); + } + + private boolean isRushTarget(Material type) { + return type == Material.OBSIDIAN || type == Material.CRYING_OBSIDIAN; + } + + private boolean isRushPickaxe(ItemStack is) { + if (!isItem(is)) { + return false; + } + + return switch (is.getType()) { + case DIAMOND_PICKAXE, NETHERITE_PICKAXE -> true; + default -> false; + }; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDamageEvent e) { + if (!isRushTarget(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isRushPickaxe(p.getInventory().getItemInMainHand())) { + return; + } + + Adaptation.BlockActionContext context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, getConfig().durationTicks, getAmplifier(context.level()), false, false, true)); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!isRushTarget(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isRushPickaxe(p.getInventory().getItemInMainHand()) || getActiveLevel(p) <= 0) { + return; + } + + getPlayer(p).getData().addStat("pickaxe.obsidian-rush.obsidian-mined", 1); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Gain a strong Haste burst while mining obsidian with a diamond or netherite pickaxe.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Haste amplifier added on top of the adaptation level while mining obsidian.", impact = "Higher values make obsidian mine faster at every level.") + int amplifierBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum Haste amplifier this adaptation can grant.", impact = "Higher values allow stronger Haste at high levels.") + int maxAmplifier = 7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the Haste burst applied when damaging obsidian.", impact = "Higher values keep the burst active longer between swings.") + int durationTicks = 120; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index e6203f8e0..66de411f9 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -88,7 +88,7 @@ public void addStats(int level, Element v) { public void on(PlayerInteractEvent e) { Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeRepairRhythm.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeRepairRhythm.java new file mode 100644 index 000000000..039026ab3 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeRepairRhythm.java @@ -0,0 +1,150 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +import java.util.concurrent.ThreadLocalRandom; + +public class PickaxeRepairRhythm extends SimpleAdaptation { + public PickaxeRepairRhythm() { + super("pickaxe-repair-rhythm"); + registerConfiguration(PickaxeRepairRhythm.Config.class); + setDescription(Localizer.dLocalize("pickaxe.repair_rhythm.description")); + setDisplayName(Localizer.dLocalize("pickaxe.repair_rhythm.name")); + setIcon(Material.EXPERIENCE_BOTTLE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(7561); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.EXPERIENCE_BOTTLE) + .key("challenge_pickaxe_rhythm_5k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_rhythm_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_rhythm_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_rhythm_5k", "pickaxe.repair-rhythm.durability-restored", 5000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.repair_rhythm.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getRepairChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("pickaxe.repair_rhythm.lore2")); + } + + private double getRepairChance(int level) { + return Math.min(getConfig().maxChance, getConfig().chanceBase + (level * getConfig().chancePerLevel)); + } + + @EventHandler(ignoreCancelled = true) + public void on(BlockBreakEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (!isPickaxe(hand)) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + if (!M.r(getRepairChance(context.level()))) { + return; + } + + if (!(hand.getItemMeta() instanceof Damageable damageable)) { + return; + } + + int damage = damageable.getDamage(); + if (damage <= 0) { + return; + } + + int min = Math.max(1, getConfig().restoreMin); + int max = Math.max(min, getConfig().restoreMax); + int restore = Math.min(damage, min == max ? min : min + ThreadLocalRandom.current().nextInt((max - min) + 1)); + damageable.setDamage(damage - restore); + hand.setItemMeta(damageable); + p.getInventory().setItemInMainHand(hand); + getPlayer(p).getData().addStat("pickaxe.repair-rhythm.durability-restored", restore); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sustained mining has a chance to restore pickaxe durability per broken block.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance per broken block to restore durability.", impact = "Higher values trigger repairs more often at every level.") + double chanceBase = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional repair chance gained per adaptation level.", impact = "Higher values trigger repairs more often at higher levels.") + double chancePerLevel = 0.06; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum total repair chance per broken block.", impact = "Higher values allow more frequent repairs at max level.") + double maxChance = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum durability restored per repair proc.", impact = "Higher values restore more durability per proc.") + int restoreMin = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum durability restored per repair proc.", impact = "Higher values restore more durability per proc.") + int restoreMax = 2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeStoneSkin.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeStoneSkin.java new file mode 100644 index 000000000..9833a022a --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeStoneSkin.java @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; + +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class PickaxeStoneSkin extends SimpleAdaptation { + private static final Set STONE_BLOCKS = EnumSet.of( + Material.STONE, Material.COBBLESTONE, Material.MOSSY_COBBLESTONE, + Material.DEEPSLATE, Material.COBBLED_DEEPSLATE, Material.TUFF, + Material.CALCITE, Material.ANDESITE, Material.DIORITE, Material.GRANITE); + private final Map stacks = new ConcurrentHashMap<>(); + + public PickaxeStoneSkin() { + super("pickaxe-stone-skin"); + registerConfiguration(PickaxeStoneSkin.Config.class); + setDescription(Localizer.dLocalize("pickaxe.stone_skin.description")); + setDisplayName(Localizer.dLocalize("pickaxe.stone_skin.name")); + setIcon(Material.STONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5377); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.STONE) + .key("challenge_pickaxe_stoneskin_10k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_stoneskin_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_stoneskin_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_stoneskin_10k", "pickaxe.stone-skin.stacks-gained", 10000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.stone_skin.lore1")); + v.addLore(C.GREEN + "" + getTierCap(level) + C.GRAY + " " + Localizer.dLocalize("pickaxe.stone_skin.lore2")); + } + + private int getTierCap(int level) { + return Math.min(level, getConfig().maxAmplifier + 1); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!STONE_BLOCKS.contains(e.getBlock().getType())) { + return; + } + + Player p = e.getPlayer(); + if (!isPickaxe(p.getInventory().getItemInMainHand())) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockBreakContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + long now = System.currentTimeMillis(); + UUID id = p.getUniqueId(); + StackState state = stacks.get(id); + int current = state == null || now > state.expiresAt() ? 0 : state.stacks(); + int blocksPerStack = Math.max(1, getConfig().blocksPerStack); + int tierCap = getTierCap(context.level()); + int next = Math.min(current + 1, tierCap * blocksPerStack); + stacks.put(id, new StackState(next, now + getConfig().stackDurationMs)); + if (next > current) { + getPlayer(p).getData().addStat("pickaxe.stone-skin.stacks-gained", 1); + } + + int tier = Math.min(next / blocksPerStack, tierCap); + if (tier <= 0) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectTypes.DAMAGE_RESISTANCE, getConfig().effectDurationTicks, tier - 1, false, false, true)); + } + + @EventHandler + public void on(PlayerQuitEvent e) { + stacks.remove(e.getPlayer().getUniqueId()); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record StackState(int stacks, long expiresAt) { + } + + @NoArgsConstructor + @ConfigDescription("Breaking stone-type blocks builds short-lived stacking damage resistance.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Stone blocks that must be broken to gain one resistance tier.", impact = "Higher values make resistance tiers slower to build.") + int blocksPerStack = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds before built stacks expire without mining.", impact = "Higher values keep stacks alive longer between breaks.") + long stackDurationMs = 6000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the applied resistance effect.", impact = "Higher values keep the resistance active longer after each break.") + int effectDurationTicks = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum resistance amplifier this adaptation can reach.", impact = "Higher values allow stronger damage reduction at max stacks.") + int maxAmplifier = 3; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java new file mode 100644 index 000000000..ce8e81917 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +public class PickaxeTunnelBore extends SimpleAdaptation { + private static final Set BORE_BLOCKS = EnumSet.of( + Material.STONE, Material.COBBLESTONE, Material.MOSSY_COBBLESTONE, + Material.DEEPSLATE, Material.COBBLED_DEEPSLATE, Material.TUFF, + Material.CALCITE, Material.ANDESITE, Material.DIORITE, Material.GRANITE); + private static final int[] SINGLE = {0}; + private static final int[] PAIR = {0, 1}; + private static final int[] TRIPLE = {-1, 0, 1}; + + public PickaxeTunnelBore() { + super("pickaxe-tunnel-bore"); + registerConfiguration(PickaxeTunnelBore.Config.class); + setDescription(Localizer.dLocalize("pickaxe.tunnel_bore.description")); + setDisplayName(Localizer.dLocalize("pickaxe.tunnel_bore.name")); + setIcon(Material.COBBLESTONE); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(8123); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_PICKAXE) + .key("challenge_pickaxe_tunnelbore_10k") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_tunnelbore_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_tunnelbore_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_tunnelbore_10k", "pickaxe.tunnel-bore.blocks-bored", 10000, 500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.tunnel_bore.lore1")); + v.addLore(C.GREEN + "" + getBoreWidth(level) + "x" + getBoreHeight(level) + C.GRAY + " " + Localizer.dLocalize("pickaxe.tunnel_bore.lore2")); + v.addLore(C.RED + "- " + getConfig().durabilityPerBonusBlock + C.GRAY + " " + Localizer.dLocalize("pickaxe.tunnel_bore.lore3")); + } + + private int getBoreWidth(int level) { + return level >= 2 ? 3 : 1; + } + + private int getBoreHeight(int level) { + return level >= 3 ? 3 : 2; + } + + @EventHandler + public void on(BlockBreakEvent e) { + Block block = e.getBlock(); + if (!BORE_BLOCKS.contains(block.getType())) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking() || !isPickaxe(p.getInventory().getItemInMainHand())) { + return; + } + + Adaptation.BlockActionContext context = resolveBlockBreakContext(p, block.getLocation()); + if (context == null) { + return; + } + + List targets = collectPlane(p, block, getBoreWidth(context.level()), getBoreHeight(context.level())); + if (targets.isEmpty()) { + return; + } + + ItemStack tool = p.getInventory().getItemInMainHand().clone(); + damageHand(p, targets.size() * getConfig().durabilityPerBonusBlock); + getPlayer(p).getData().addStat("pickaxe.tunnel-bore.blocks-bored", targets.size()); + J.runEntity(p, () -> { + for (Block b : targets) { + if (!BORE_BLOCKS.contains(b.getType())) { + continue; + } + + b.breakNaturally(tool); + } + }); + } + + private List collectPlane(Player p, Block origin, int width, int height) { + List targets = new ArrayList<>(8); + int[] wOffsets = width == 1 ? SINGLE : TRIPLE; + int[] hOffsets = height == 2 ? PAIR : TRIPLE; + BlockFace facing = p.getFacing(); + float pitch = p.getLocation().getPitch(); + boolean steep = pitch > 60F || pitch < -60F; + int fx = facing.getModX(); + int fz = facing.getModZ(); + boolean widthOnX = fz != 0; + for (int h : hOffsets) { + for (int w : wOffsets) { + if (h == 0 && w == 0) { + continue; + } + + Block b; + if (steep) { + b = origin.getRelative((h * fx) - (w * fz), 0, (h * fz) + (w * fx)); + } else { + b = origin.getRelative(widthOnX ? w : 0, h, widthOnX ? 0 : w); + } + + if (!BORE_BLOCKS.contains(b.getType())) { + continue; + } + + if (!canBlockBreak(p, b.getLocation())) { + continue; + } + + targets.add(b); + } + } + + return targets; + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-mine stone-type blocks to bore a tunnel plane oriented by your facing.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Extra pickaxe durability damage taken per bonus block bored.", impact = "Higher values wear the pickaxe out faster while tunnel boring.") + int durabilityPerBonusBlock = 1; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeUnbreakablePact.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeUnbreakablePact.java new file mode 100644 index 000000000..9e1d2635f --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeUnbreakablePact.java @@ -0,0 +1,144 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.pickaxe; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import art.arcane.volmlib.util.math.M; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +public class PickaxeUnbreakablePact extends SimpleAdaptation { + public PickaxeUnbreakablePact() { + super("pickaxe-unbreakable-pact"); + registerConfiguration(PickaxeUnbreakablePact.Config.class); + setDescription(Localizer.dLocalize("pickaxe.unbreakable_pact.description")); + setDisplayName(Localizer.dLocalize("pickaxe.unbreakable_pact.name")); + setIcon(Material.ANVIL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(9122); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.NETHERITE_PICKAXE) + .key("challenge_pickaxe_pact_100") + .title(Localizer.dLocalize("advancement.challenge_pickaxe_pact_100.title")) + .description(Localizer.dLocalize("advancement.challenge_pickaxe_pact_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_pickaxe_pact_100", "pickaxe.unbreakable-pact.saves", 100, 400); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("pickaxe.unbreakable_pact.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getIgnoreChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("pickaxe.unbreakable_pact.lore2")); + } + + private double getIgnoreChance(int level) { + return Math.min(getConfig().maxIgnoreChance, level * getConfig().ignoreChancePerLevel); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(PlayerItemDamageEvent e) { + ItemStack item = e.getItem(); + if (!isPickaxe(item)) { + return; + } + + Player p = e.getPlayer(); + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (M.r(getIgnoreChance(level))) { + e.setCancelled(true); + getPlayer(p).getData().addStat("pickaxe.unbreakable-pact.damage-ignored", e.getDamage()); + return; + } + + if (!(item.getItemMeta() instanceof Damageable damageable)) { + return; + } + + int maxDurability = item.getType().getMaxDurability(); + if (damageable.getDamage() + e.getDamage() < maxDurability) { + return; + } + + e.setCancelled(true); + damageable.setDamage(maxDurability - 1); + item.setItemMeta(damageable); + getPlayer(p).getData().addStat("pickaxe.unbreakable-pact.saves", 1); + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_ANVIL_PLACE, 0.4f, 1.8f); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Your pickaxe refuses to break, surviving at 1 durability.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance per level to ignore pickaxe durability loss entirely.", impact = "Higher values make the pickaxe lose durability less often at higher levels.") + double ignoreChancePerLevel = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum total chance to ignore durability loss.", impact = "Higher values allow more durability loss to be ignored at max level.") + double maxIgnoreChance = 0.25; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java index 4dfe8365a..c0a6b6e6d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftAccess.java @@ -125,7 +125,7 @@ public void on(PlayerInteractEvent e) { // If the main hand is holding a bound enderpearl if (mainHandBound) { e.setCancelled(true); - if (hasActiveAdaptation(p)) { + if (e.getHand() == EquipmentSlot.HAND && hasActiveAdaptation(p)) { Adapt.verbose("Player using bound enderpearl."); handleEnderPearlInteraction(e, p, block); } @@ -146,13 +146,20 @@ private void handleEnderPearlInteraction(PlayerInteractEvent event, Player playe } Action action = event.getAction(); - if (action == Action.LEFT_CLICK_BLOCK) { - if (sneaking && isStorage(block.getBlockData())) { - if (canAccessChest(player, block.getLocation())) { - linkPearl(player, block, event); - } else { - Adapt.verbose("Player " + player.getName() + " doesn't have permission."); - } + if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { + if (!sneaking) { + return; + } + + Block target = action == Action.LEFT_CLICK_BLOCK ? block : player.getTargetBlockExact(5); + if (target == null || !isStorage(target.getBlockData())) { + return; + } + + if (canAccessChest(player, target.getLocation())) { + linkPearl(player, target, event); + } else { + Adapt.verbose("Player " + player.getName() + " doesn't have permission."); } } else if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { openPearl(player); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java index d11830185..97d722296 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftBlink.java @@ -30,6 +30,7 @@ import art.arcane.adapt.util.common.misc.SoundPlayer; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.inventorygui.Element; import art.arcane.volmlib.util.math.M; import lombok.NoArgsConstructor; @@ -41,10 +42,12 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.*; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; import static art.arcane.adapt.api.adaptation.chunk.ChunkLoading.loadChunkAsync; @@ -245,6 +248,9 @@ private Location findSafeGroundNear(Location base, int maxVerticalAdjustment) { @Override public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + (getBlinkDistance(level)) + C.GRAY + " " + Localizer.dLocalize("rift.blink.lore1")); + if (getConfig().pearlConsumeChance > 0) { + v.addLore(C.RED + "* " + Form.pc(getConfig().pearlConsumeChance, 0) + C.GRAY + " " + Localizer.dLocalize("rift.blink.lore_cost_pearl")); + } java.util.List combos = getTriggerCombos(); if (combos.isEmpty()) { v.addLore(C.AQUA + "* " + C.GRAY + "Trigger: " + C.WHITE + "none"); @@ -450,6 +456,7 @@ private boolean attemptBlink(Player p) { return false; } + consumeBlinkPearl(p); PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("rift"); PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; if (adaptation != null && adaptation.getLevel() > 0) { @@ -483,6 +490,22 @@ private boolean attemptBlink(Player p) { return true; } + private void consumeBlinkPearl(Player p) { + double chance = Math.max(0D, Math.min(1D, getConfig().pearlConsumeChance)); + if (chance <= 0D || ThreadLocalRandom.current().nextDouble() >= chance) { + return; + } + + for (ItemStack stack : p.getInventory().getContents()) { + if (stack == null || stack.getType() != Material.ENDER_PEARL || stack.hasItemMeta()) { + continue; + } + + stack.setAmount(stack.getAmount() - 1); + return; + } + } + private boolean isSafe(Location l) { return l.getBlock().getType().isSolid() && !l.getBlock().getRelative(BlockFace.UP).getType().isSolid() @@ -516,6 +539,8 @@ protected static class Config { boolean showParticles = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown between successful Rift Blink triggers in milliseconds.", impact = "Higher values reduce blink frequency; lower values allow faster reuse.") int cooldownMillis = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance per successful blink to consume one plain ender pearl from the inventory.", impact = "Higher values make blinking drain pearls faster; 0 disables the pearl cost.") + double pearlConsumeChance = 0.2; @art.arcane.adapt.util.config.ConfigDoc(value = "Enables double-tap jump detection for Rift Blink.", impact = "True allows jump-based activation; false disables jump activation.") boolean enableDoubleJumpTrigger = true; @art.arcane.adapt.util.config.ConfigDoc(value = "Require sprinting for the double-tap jump trigger.", impact = "True requires sprinting while double-tapping jump; false allows it without sprint.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java index e4ef594da..fa1f46082 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftEnderchest.java @@ -36,6 +36,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -69,31 +70,37 @@ public void addStats(int level, Element v) { @EventHandler public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; + } + + if (e.getHand() != EquipmentSlot.HAND) { + return; + } + Player p = e.getPlayer(); - SoundPlayer sp = SoundPlayer.of(p); ItemStack hand = p.getInventory().getItemInMainHand(); - if (hand.getType() != Material.ENDER_CHEST || !hasActiveAdaptation(p)) { return; } if (p.hasCooldown(hand.getType())) { e.setCancelled(true); - } else { - p.setCooldown(Material.ENDER_CHEST, 100); - - if ((e.getAction() == Action.RIGHT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_AIR) || (e.getAction() == Action.LEFT_CLICK_BLOCK)) { - PlayerSkillLine line = getPlayer(p).getData().getSkillLine("rift"); - PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; - if (adaptation != null && adaptation.getLevel() > 0) { - RiftResist.riftResistStackAdd(p, 10, 2); - } - sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); - sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); - p.openInventory(p.getEnderChest()); - getPlayer(p).getData().addStat("rift.enderchest.opens", 1); - } + return; } + + p.setCooldown(Material.ENDER_CHEST, 100); + PlayerSkillLine line = getPlayer(p).getData().getSkillLine("rift"); + PlayerAdaptation adaptation = line != null ? line.getAdaptation("rift-resist") : null; + if (adaptation != null && adaptation.getLevel() > 0) { + RiftResist.riftResistStackAdd(p, 10, 2); + } + SoundPlayer sp = SoundPlayer.of(p); + sp.play(p.getLocation(), Sound.PARTICLE_SOUL_ESCAPE, 1f, 0.10f); + sp.play(p.getLocation(), Sound.BLOCK_ENDER_CHEST_OPEN, 1f, 0.10f); + p.openInventory(p.getEnderChest()); + getPlayer(p).getData().addStat("rift.enderchest.opens", 1); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java index 86b3d5297..a965e2745 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftInflatedPocketDimension.java @@ -31,6 +31,7 @@ import lombok.NoArgsConstructor; import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -88,7 +89,8 @@ public void on(PlayerInteractEvent e) { return; } - if (e.getAction() != Action.RIGHT_CLICK_BLOCK || e.getClickedBlock() == null) { + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { return; } @@ -102,7 +104,12 @@ public void on(PlayerInteractEvent e) { return; } - Material requested = e.getClickedBlock().getType(); + Block target = action == Action.RIGHT_CLICK_BLOCK ? e.getClickedBlock() : p.getTargetBlockExact(5); + if (target == null) { + return; + } + + Material requested = target.getType(); if (!requested.isItem() || requested.isAir()) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java index 432332b74..394877142 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/sword/SwordsMachete.java @@ -94,101 +94,97 @@ public double getRadius(int level) { @EventHandler public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if ((action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) || e.getHand() != EquipmentSlot.HAND) { + return; + } + Player p = e.getPlayer(); - SoundPlayer spw = SoundPlayer.of(p.getWorld()); - if (e.getHand() != null && e.getHand().equals(EquipmentSlot.HAND) && e.getAction().equals(Action.LEFT_CLICK_AIR) || e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { - int dmg = 0; - ItemStack is = e.getItem(); - if (isSword(is)) { - if (is != null && !p.hasCooldown(is.getType()) && hasActiveAdaptation(p)) { - Location ctr = p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(2.25)).add(0, -0.5, 0); - - int lvl = getLevel(p); - Cuboid c = new Cuboid(ctr); - c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.North, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.South, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.East, (int) Math.round(getRadius(lvl))); - c = c.expand(Cuboid.CuboidDirection.West, (int) Math.round(getRadius(lvl))); - - if (dmg > 0) { - return; - } + ItemStack is = e.getItem(); + if (is == null || !isSword(is) || p.hasCooldown(is.getType()) || !hasActiveAdaptation(p)) { + return; + } - for (Block i : c) { - if (M.r((getLevelPercent(lvl) * 2.8) / (i.getLocation().distanceSquared(ctr)))) { - if (i.getType().equals(Material.TALL_GRASS) - || i.getType().equals(Material.CACTUS) - || i.getType().equals(Material.SUGAR_CANE) - || i.getType().equals(Material.CARROT) - || i.getType().equals(Material.POTATO) - || i.getType().equals(Material.NETHER_WART) - || i.getType().equals(Materials.GRASS) - || i.getType().equals(Material.FERN) - || i.getType().equals(Material.LARGE_FERN) - || i.getType().equals(Material.VINE) - || i.getType().equals(Material.ROSE_BUSH) - || i.getType().equals(Material.WITHER_ROSE) - || i.getType().equals(Material.ACACIA_LEAVES) - || i.getType().equals(Material.BIRCH_LEAVES) - || i.getType().equals(Material.DARK_OAK_LEAVES) - || i.getType().equals(Material.JUNGLE_LEAVES) - || i.getType().equals(Material.OAK_LEAVES) - || i.getType().equals(Material.SPRUCE_LEAVES) - || i.getType().equals(Material.BROWN_MUSHROOM) - || i.getType().equals(Material.RED_MUSHROOM) - || i.getType().equals(Material.DEAD_BUSH) - || i.getType().equals(Material.DANDELION) - || i.getType().equals(Material.TALL_SEAGRASS) - || i.getType().equals(Material.SEAGRASS) - || i.getType().equals(Material.WHITE_TULIP) - || i.getType().equals(Material.RED_TULIP) - || i.getType().equals(Material.PINK_TULIP) - || i.getType().equals(Material.ORANGE_TULIP) - || i.getType().equals(Material.LILY_OF_THE_VALLEY) - || i.getType().equals(Material.ALLIUM) - || i.getType().equals(Material.AZURE_BLUET) - || i.getType().equals(Material.SUNFLOWER) - || i.getType().equals(Material.CORNFLOWER) - || i.getType().equals(Material.CHORUS_FLOWER) - || i.getType().equals(Material.BAMBOO) - || i.getType().equals(Material.BAMBOO_SAPLING) - || i.getType().equals(Material.LILAC) - || i.getType().equals(Material.PEONY) - || i.getType().equals(Material.LILY_PAD) - || i.getType().equals(Material.COCOA) - || i.getType().equals(Material.MANGROVE_LEAVES) - - ) { - if (!canBlockBreak(p, i.getLocation())) continue; - BlockBreakEvent ee = new BlockBreakEvent(i, p); - Bukkit.getPluginManager().callEvent(ee); - - if (!ee.isCancelled()) { - dmg += 1; - J.runAt(i.getLocation(), () -> { - i.breakNaturally(); - spw.play(i.getLocation(), Sound.BLOCK_GRASS_BREAK, 0.4f, (float) (ThreadLocalRandom.current().nextDouble() * 1.85D)); - }, RNG.r.i(0, (getMaxLevel() - lvl * 2) + 1)); - } - } - } + SoundPlayer spw = SoundPlayer.of(p.getWorld()); + int dmg = 0; + Location ctr = p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(2.25)).add(0, -0.5, 0); + + int lvl = getLevel(p); + Cuboid c = new Cuboid(ctr); + c = c.expand(Cuboid.CuboidDirection.Up, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.Down, (int) Math.floor(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.North, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.South, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.East, (int) Math.round(getRadius(lvl))); + c = c.expand(Cuboid.CuboidDirection.West, (int) Math.round(getRadius(lvl))); + + for (Block i : c) { + if (M.r((getLevelPercent(lvl) * 2.8) / (i.getLocation().distanceSquared(ctr)))) { + if (i.getType().equals(Material.TALL_GRASS) + || i.getType().equals(Material.CACTUS) + || i.getType().equals(Material.SUGAR_CANE) + || i.getType().equals(Material.CARROT) + || i.getType().equals(Material.POTATO) + || i.getType().equals(Material.NETHER_WART) + || i.getType().equals(Materials.GRASS) + || i.getType().equals(Material.FERN) + || i.getType().equals(Material.LARGE_FERN) + || i.getType().equals(Material.VINE) + || i.getType().equals(Material.ROSE_BUSH) + || i.getType().equals(Material.WITHER_ROSE) + || i.getType().equals(Material.ACACIA_LEAVES) + || i.getType().equals(Material.BIRCH_LEAVES) + || i.getType().equals(Material.DARK_OAK_LEAVES) + || i.getType().equals(Material.JUNGLE_LEAVES) + || i.getType().equals(Material.OAK_LEAVES) + || i.getType().equals(Material.SPRUCE_LEAVES) + || i.getType().equals(Material.BROWN_MUSHROOM) + || i.getType().equals(Material.RED_MUSHROOM) + || i.getType().equals(Material.DEAD_BUSH) + || i.getType().equals(Material.DANDELION) + || i.getType().equals(Material.TALL_SEAGRASS) + || i.getType().equals(Material.SEAGRASS) + || i.getType().equals(Material.WHITE_TULIP) + || i.getType().equals(Material.RED_TULIP) + || i.getType().equals(Material.PINK_TULIP) + || i.getType().equals(Material.ORANGE_TULIP) + || i.getType().equals(Material.LILY_OF_THE_VALLEY) + || i.getType().equals(Material.ALLIUM) + || i.getType().equals(Material.AZURE_BLUET) + || i.getType().equals(Material.SUNFLOWER) + || i.getType().equals(Material.CORNFLOWER) + || i.getType().equals(Material.CHORUS_FLOWER) + || i.getType().equals(Material.BAMBOO) + || i.getType().equals(Material.BAMBOO_SAPLING) + || i.getType().equals(Material.LILAC) + || i.getType().equals(Material.PEONY) + || i.getType().equals(Material.LILY_PAD) + || i.getType().equals(Material.COCOA) + || i.getType().equals(Material.MANGROVE_LEAVES)) { + if (!canBlockBreak(p, i.getLocation())) { + continue; } - - if (dmg > 0) { - p.setCooldown(is.getType(), getCooldownTime(getLevelPercent(lvl))); -// if (areParticlesEnabled()) { -// ParticleEffect.SWEEP_ATTACK.display(p.getEyeLocation().clone().add(p.getLocation().getDirection().clone().multiply(1.25)).add(0, -0.5, 0), 0f, 0f, 0f, 0.1f, 1, null); -// } - spw.play(p.getEyeLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, (float) (ThreadLocalRandom.current().nextDouble() / 2D) + 0.65f); - damageHand(p, dmg * getDamagePerBlock(getLevelPercent(lvl))); - xp(p, dmg * 11.25, "foliage-cut"); - getPlayer(p).getData().addStat("swords.machete.foliage-cut", dmg); + BlockBreakEvent ee = new BlockBreakEvent(i, p); + Bukkit.getPluginManager().callEvent(ee); + + if (!ee.isCancelled()) { + dmg += 1; + J.runAt(i.getLocation(), () -> { + i.breakNaturally(); + spw.play(i.getLocation(), Sound.BLOCK_GRASS_BREAK, 0.4f, (float) (ThreadLocalRandom.current().nextDouble() * 1.85D)); + }, RNG.r.i(0, (getMaxLevel() - lvl * 2) + 1)); } } } } + + if (dmg > 0) { + p.setCooldown(is.getType(), getCooldownTime(getLevelPercent(lvl))); + spw.play(p.getEyeLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, (float) (ThreadLocalRandom.current().nextDouble() / 2D) + 0.65f); + damageHand(p, dmg * getDamagePerBlock(getLevelPercent(lvl))); + xp(p, dmg * 11.25, "foliage-cut"); + getPlayer(p).getData().addStat("swords.machete.foliage-cut", dmg); + } } private int getCooldownTime(double levelPercent) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java index 16f432cf3..d8e5b651a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/taming/TamingBeastRecall.java @@ -79,6 +79,9 @@ public TamingBeastRecall() { public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + Form.f(getSearchRadius(level)) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore1")); v.addLore(C.YELLOW + "* " + Form.duration(getCooldownTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore2")); + if (getConfig().hungerCost > 0) { + v.addLore(C.RED + "* " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("taming.beast_recall.lore_cost_hunger")); + } } @EventHandler(priority = EventPriority.HIGHEST) @@ -98,6 +101,11 @@ public void on(PlayerInteractEvent e) { return; } + int hungerCost = Math.max(0, getConfig().hungerCost); + if (hungerCost > 0 && p.getFoodLevel() < hungerCost) { + return; + } + Tameable tameable = findNearestOwnedTameable(p, getSearchRadius(level)); if (tameable == null) { return; @@ -111,6 +119,9 @@ public void on(PlayerInteractEvent e) { J.teleport(tameable, safe); tameable.setFallDistance(0); p.setCooldown(Material.LEAD, getCooldownTicks(level)); + if (hungerCost > 0) { + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + } e.setCancelled(true); SoundPlayer sp = SoundPlayer.of(p.getWorld()); @@ -218,5 +229,7 @@ protected static class Config { double cooldownTicksFactor = 280; @art.arcane.adapt.util.config.ConfigDoc(value = "Controls Xp On Recall for the Taming Beast Recall adaptation.", impact = "Higher values usually increase intensity, limits, or frequency; lower values reduce it.") double xpOnRecall = 26; + @art.arcane.adapt.util.config.ConfigDoc(value = "Food points consumed per beast recall.", impact = "Higher values make each recall cost more hunger; 0 disables the cost.") + int hungerCost = 2; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCorpseExplosion.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCorpseExplosion.java new file mode 100644 index 000000000..8934ff09c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCorpseExplosion.java @@ -0,0 +1,225 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.persistence.PersistentDataType; + +public class TragoulCorpseExplosion extends SimpleAdaptation { + private static final NamespacedKey NOVA_KEY = NamespacedKey.fromString("adapt:tragoul_nova_stamp"); + + public TragoulCorpseExplosion() { + super("tragoul-corpse-explosion"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.corpse_explosion.description")); + setDisplayName(Localizer.dLocalize("tragoul.corpse_explosion.name")); + setIcon(Material.WITHER_ROSE); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.WITHER_ROSE) + .key("challenge_tragoul_corpse_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_corpse_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_corpse_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.NETHERRACK) + .key("challenge_tragoul_corpse_5k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_corpse_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_corpse_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_corpse_500", "tragoul.corpse-explosion.mobs-detonated", 500, 400); + registerMilestone("challenge_tragoul_corpse_5k", "tragoul.corpse-explosion.mobs-detonated", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.corpse_explosion.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getRadius(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.corpse_explosion.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getVictimHealthFraction(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.corpse_explosion.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + Player killer = e.getEntity().getKiller(); + if (killer == null) { + return; + } + + LivingEntity victim = e.getEntity(); + long now = System.currentTimeMillis(); + Long novaStamp = victim.getPersistentDataContainer().get(NOVA_KEY, PersistentDataType.LONG); + if (novaStamp != null && now - novaStamp < getConfig().chainSuppressionMillis) { + return; + } + + withAdaptedPlayer(killer, () -> detonate(killer, victim, now)); + } + + static void detonateServantKill(TragoulCorpseExplosion nova, Player owner, LivingEntity victim) { + long now = System.currentTimeMillis(); + Long novaStamp = victim.getPersistentDataContainer().get(NOVA_KEY, PersistentDataType.LONG); + if (novaStamp != null && now - novaStamp < nova.getConfig().chainSuppressionMillis) { + return; + } + + nova.withAdaptedPlayer(owner, () -> nova.detonate(owner, victim, now)); + } + + private void detonate(Player credited, LivingEntity victim, long now) { + int level = getActiveLevel(credited); + if (level <= 0 || !canDamageTarget(credited, victim)) { + return; + } + + double radius = getRadius(level); + double damage = getNovaDamage(level, victim); + int hit = 0; + for (Entity entity : victim.getNearbyEntities(radius, radius, radius)) { + if (!(entity instanceof Monster monster) || monster.isDead() || !monster.isValid()) { + continue; + } + + if (!canDamageTarget(credited, monster)) { + continue; + } + + monster.getPersistentDataContainer().set(NOVA_KEY, PersistentDataType.LONG, now); + J.runEntity(monster, () -> { + if (monster.isValid() && !monster.isDead()) { + monster.damage(damage, credited); + } + }); + hit++; + if (hit >= getConfig().maxTargets) { + break; + } + } + + if (hit <= 0) { + return; + } + + if (areParticlesEnabled()) { + Particle.DustOptions dust = new Particle.DustOptions(Color.fromRGB(150, 12, 12), 1.4f); + victim.getWorld().spawnParticle(Particle.DUST, victim.getLocation().add(0, 0.9, 0), 70, radius * 0.4, 0.6, radius * 0.4, 0.01, dust); + victim.getWorld().spawnParticle(Particle.DAMAGE_INDICATOR, victim.getLocation().add(0, 1, 0), 12, 0.5, 0.5, 0.5, 0.04); + } + SoundPlayer.of(victim.getWorld()).play(victim.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 0.55f, 1.35f); + getPlayer(credited).getData().addStat("tragoul.corpse-explosion.mobs-detonated", hit); + xp(credited, hit * getConfig().xpPerMobHit); + } + + private double getRadius(int level) { + return Math.max(1, getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor)); + } + + private double getVictimHealthFraction(int level) { + return Math.max(0, getConfig().victimHealthFractionBase + (getLevelPercent(level) * getConfig().victimHealthFractionFactor)); + } + + private double getNovaDamage(int level, LivingEntity victim) { + IAttribute attribute = Version.get().getAttribute(victim, Attributes.GENERIC_MAX_HEALTH); + double victimMaxHealth = attribute == null ? 20D : attribute.getValue(); + double damage = getConfig().baseDamage + (victimMaxHealth * getVictimHealthFraction(level)); + return Math.min(getConfig().maxDamage, damage); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Mobs you kill detonate in a blood nova that damages nearby hostile mobs.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base nova radius before level scaling.", impact = "Higher values damage hostile mobs further from the corpse.") + double radiusBase = 3.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional nova radius granted at max level.", impact = "Higher values increase the level-scaled radius growth.") + double radiusFactor = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Flat nova damage applied to every hostile mob hit.", impact = "Higher values increase the guaranteed damage portion of the nova.") + double baseDamage = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fraction of the victim's max health added to nova damage before level scaling.", impact = "Higher values make tanky kills detonate harder.") + double victimHealthFractionBase = 0.10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional victim max-health fraction granted at max level.", impact = "Higher values increase the level-scaled damage growth.") + double victimHealthFractionFactor = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on nova damage per mob.", impact = "Prevents extreme bosses from producing one-shot novas.") + double maxDamage = 16.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum hostile mobs damaged per nova.", impact = "Caps per-kill work to protect server performance.") + int maxTargets = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds during which a nova-damaged mob cannot trigger another nova.", impact = "Prevents chain-reaction detonations.") + long chainSuppressionMillis = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per hostile mob hit by a nova.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerMobHit = 6; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCurseOfFrailty.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCurseOfFrailty.java new file mode 100644 index 000000000..a7cfb5b8f --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulCurseOfFrailty.java @@ -0,0 +1,186 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +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.entity.EntityDamageByEntityEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class TragoulCurseOfFrailty extends SimpleAdaptation { + private final Map attackerCooldowns = new ConcurrentHashMap<>(); + + public TragoulCurseOfFrailty() { + super("tragoul-curse-of-frailty"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.curse_of_frailty.description")); + setDisplayName(Localizer.dLocalize("tragoul.curse_of_frailty.name")); + setIcon(Material.FERMENTED_SPIDER_EYE); + setInterval(5000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.FERMENTED_SPIDER_EYE) + .key("challenge_tragoul_frailty_100") + .title(Localizer.dLocalize("advancement.challenge_tragoul_frailty_100.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_frailty_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.WITHER_SKELETON_SKULL) + .key("challenge_tragoul_frailty_1k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_frailty_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_frailty_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_frailty_100", "tragoul.curse-of-frailty.curses-applied", 100, 400); + registerMilestone("challenge_tragoul_frailty_1k", "tragoul.curse-of-frailty.curses-applied", 1000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.curse_of_frailty.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getCurseDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.curse_of_frailty.lore2")); + if (getLevelPercent(level) >= getConfig().slownessUnlockPercent) { + v.addLore(C.YELLOW + Localizer.dLocalize("tragoul.curse_of_frailty.lore3")); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + LivingEntity attacker = null; + if (e.getDamager() instanceof LivingEntity living) { + attacker = living; + } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity shooter) { + attacker = shooter; + } + + if (attacker == null || attacker == p) { + return; + } + + long now = System.currentTimeMillis(); + Long until = attackerCooldowns.get(attacker.getUniqueId()); + if (until != null && until > now) { + return; + } + + LivingEntity target = attacker; + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, target)) { + return; + } + + attackerCooldowns.put(target.getUniqueId(), now + getConfig().perAttackerCooldownMillis); + int duration = getCurseDurationTicks(level); + int weaknessAmplifier = getLevelPercent(level) >= 0.8 ? 1 : 0; + target.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, duration, weaknessAmplifier, true, true, true), true); + if (getLevelPercent(level) >= getConfig().slownessUnlockPercent) { + target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, duration, getConfig().slownessAmplifier, true, true, true), true); + } + + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.WARPED_SPORE, target.getLocation().add(0, 1.0, 0), 20, 0.3, 0.5, 0.3, 0.02); + } + SoundPlayer.of(target.getWorld()).play(target.getLocation(), Sound.ENTITY_ELDER_GUARDIAN_CURSE, 0.35f, 1.7f); + getPlayer(p).getData().addStat("tragoul.curse-of-frailty.curses-applied", 1); + xp(p, getConfig().xpPerCurse); + }); + } + + private int getCurseDurationTicks(int level) { + return Math.max(40, (int) Math.round(getConfig().curseDurationTicksBase + (getLevelPercent(level) * getConfig().curseDurationTicksFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + attackerCooldowns.entrySet().removeIf(i -> i.getValue() <= now); + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Enemies that strike you are cursed with weakness, and slowness at higher levels.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Curse duration in ticks before level scaling.", impact = "Higher values keep attackers weakened longer.") + double curseDurationTicksBase = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional curse duration ticks granted at max level.", impact = "Higher values increase level-scaled duration growth.") + double curseDurationTicksFactor = 100; + @art.arcane.adapt.util.config.ConfigDoc(value = "Level progress required before slowness is added to the curse.", impact = "Lower values unlock the slowness component earlier.") + double slownessUnlockPercent = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier of the slowness component once unlocked.", impact = "Higher values slow cursed attackers more.") + int slownessAmplifier = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Per-attacker cooldown in milliseconds between curses.", impact = "Higher values stop one attacker from being re-cursed in quick succession.") + long perAttackerCooldownMillis = 4000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per curse applied.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerCurse = 5; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java new file mode 100644 index 000000000..d1a1abc58 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class TragoulDeathSense extends SimpleAdaptation { + public TragoulDeathSense() { + super("tragoul-death-sense"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.death_sense.description")); + setDisplayName(Localizer.dLocalize("tragoul.death_sense.name")); + setIcon(Material.SPIDER_EYE); + setInterval(1250); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SPIDER_EYE) + .key("challenge_tragoul_death_sense_1k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_death_sense_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_death_sense_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()); + registerMilestone("challenge_tragoul_death_sense_1k", "tragoul.death-sense.prey-sensed", 1000, 600); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.death_sense.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getHealthThreshold(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.death_sense.lore2")); + v.addLore(C.YELLOW + "* " + Form.f(getRadius(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.death_sense.lore3")); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + withPlayerThread(p, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + double radius = getRadius(level); + double threshold = getHealthThreshold(level); + int glowTicks = getConfig().glowTicks; + int sensed = 0; + for (Entity entity : p.getNearbyEntities(radius, radius, radius)) { + if (!(entity instanceof Monster monster) || monster.isDead() || !monster.isValid()) { + continue; + } + + if (monster.hasPotionEffect(PotionEffectType.GLOWING)) { + continue; + } + + IAttribute attribute = Version.get().getAttribute(monster, Attributes.GENERIC_MAX_HEALTH); + double maxHealth = attribute == null ? 20D : attribute.getValue(); + if (maxHealth <= 0 || monster.getHealth() / maxHealth > threshold) { + continue; + } + + J.runEntity(monster, () -> { + if (monster.isValid() && !monster.isDead()) { + monster.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, glowTicks, 0, true, false, false), true); + } + }); + sensed++; + if (sensed >= getConfig().maxMarksPerPulse) { + break; + } + } + + if (sensed > 0) { + adaptPlayer.getData().addStat("tragoul.death-sense.prey-sensed", sensed); + } + }); + } + } + + private double getRadius(int level) { + return Math.max(2, getConfig().radiusBase + (getLevelPercent(level) * getConfig().radiusFactor)); + } + + private double getHealthThreshold(int level) { + return Math.min(getConfig().maxHealthThreshold, + Math.max(0, getConfig().healthThresholdBase + (getLevelPercent(level) * getConfig().healthThresholdFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Weakened hostile mobs near you briefly glow so you can sense dying prey through walls.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scan radius before level scaling.", impact = "Higher values sense prey further away but cost more scan work.") + double radiusBase = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional scan radius granted at max level.", impact = "Higher values increase level-scaled radius growth.") + double radiusFactor = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Health fraction below which hostile mobs are sensed, before level scaling.", impact = "Higher values mark healthier mobs as weakened prey.") + double healthThresholdBase = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional health fraction granted at max level.", impact = "Higher values increase level-scaled threshold growth.") + double healthThresholdFactor = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the sensed health fraction.", impact = "Prevents marking near-full-health mobs at high levels.") + double maxHealthThreshold = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the glow applied to sensed mobs.", impact = "Higher values keep prey highlighted longer between pulses.") + int glowTicks = 35; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum mobs marked per scan pulse.", impact = "Caps per-pulse work to protect server performance.") + int maxMarksPerPulse = 8; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLastRites.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLastRites.java new file mode 100644 index 000000000..ae83bb573 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulLastRites.java @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class TragoulLastRites extends SimpleAdaptation { + private final Map cooldowns = new ConcurrentHashMap<>(); + + public TragoulLastRites() { + super("tragoul-last-rites"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.last_rites.description")); + setDisplayName(Localizer.dLocalize("tragoul.last_rites.name")); + setIcon(Material.TOTEM_OF_UNDYING); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.TOTEM_OF_UNDYING) + .key("challenge_tragoul_last_rites_5") + .title(Localizer.dLocalize("advancement.challenge_tragoul_last_rites_5.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_last_rites_5.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SOUL_TORCH) + .key("challenge_tragoul_last_rites_50") + .title(Localizer.dLocalize("advancement.challenge_tragoul_last_rites_50.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_last_rites_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_last_rites_5", "tragoul.last-rites.deaths-defied", 5, 500); + registerMilestone("challenge_tragoul_last_rites_50", "tragoul.last-rites.deaths-defied", 50, 2000); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.last_rites.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getConfig().spiritDurationTicks * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.last_rites.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.last_rites.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + if (e.getFinalDamage() < p.getHealth()) { + return; + } + + long now = System.currentTimeMillis(); + Long until = cooldowns.get(p.getUniqueId()); + if (until != null && until > now) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || p.isDead()) { + return; + } + + cooldowns.put(p.getUniqueId(), now + getCooldownMillis(level)); + e.setCancelled(true); + p.setHealth(1.0); + + int spiritTicks = getConfig().spiritDurationTicks; + p.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, spiritTicks, 0, true, false, true), true); + p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, spiritTicks, getConfig().resistanceAmplifier, true, false, true), true); + + double radius = getConfig().targetClearRadius; + for (Entity entity : p.getNearbyEntities(radius, radius, radius)) { + if (!(entity instanceof Mob mob) || mob.getTarget() != p) { + continue; + } + + J.runEntity(mob, () -> { + if (mob.isValid() && mob.getTarget() == p) { + mob.setTarget(null); + } + }); + } + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SCULK_SOUL, p.getLocation().add(0, 1.0, 0), 30, 0.4, 0.7, 0.4, 0.03); + p.getWorld().spawnParticle(Particle.SOUL, p.getLocation().add(0, 0.6, 0), 18, 0.35, 0.5, 0.35, 0.02); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ITEM_TOTEM_USE, 0.8f, 1.5f); + getPlayer(p).getData().addStat("tragoul.last-rites.deaths-defied", 1); + xp(p, getConfig().xpPerSave); + }); + } + + private long getCooldownMillis(int level) { + return Math.max(30000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("A killing blow leaves you at 1 HP as a fleeting spirit instead of dying.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.85; + @art.arcane.adapt.util.config.ConfigDoc(value = "Duration in ticks of the spirit state after defying death.", impact = "Higher values keep the protection window open longer.") + int spiritDurationTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Amplifier of the resistance effect during the spirit state.", impact = "Higher values reduce more damage while in spirit form.") + int resistanceAmplifier = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Radius in which hostile mob targets locked onto you are cleared.", impact = "Higher values shake off pursuers from further away.") + double targetClearRadius = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown in milliseconds before level scaling.", impact = "Higher values make death defiance rarer.") + double cooldownMillisBase = 600000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values let high levels defy death more often.") + double cooldownMillisFactor = 300000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per death defied.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerSave = 120; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulMarrowArmor.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulMarrowArmor.java new file mode 100644 index 000000000..020d1b116 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulMarrowArmor.java @@ -0,0 +1,187 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class TragoulMarrowArmor extends SimpleAdaptation { + private final Map cooldowns = new ConcurrentHashMap<>(); + + public TragoulMarrowArmor() { + super("tragoul-marrow-armor"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.marrow_armor.description")); + setDisplayName(Localizer.dLocalize("tragoul.marrow_armor.name")); + setIcon(Material.BONE_MEAL); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE_MEAL) + .key("challenge_tragoul_marrow_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_marrow_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_marrow_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.BONE_BLOCK) + .key("challenge_tragoul_marrow_5k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_marrow_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_marrow_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_marrow_500", "tragoul.marrow-armor.damage-absorbed", 500, 400); + registerMilestone("challenge_tragoul_marrow_5k", "tragoul.marrow-armor.damage-absorbed", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.marrow_armor.lore1")); + v.addLore(C.GREEN + "+ " + Form.pc(getAbsorbPercent(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.marrow_armor.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getInternalCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.marrow_armor.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + cooldowns.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) { + return; + } + + if (e.getFinalDamage() < getConfig().minDamageToTrigger) { + return; + } + + long now = System.currentTimeMillis(); + Long until = cooldowns.get(p.getUniqueId()); + if (until != null && until > now) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + if (!p.getInventory().containsAtLeast(new ItemStack(Material.BONE), 1)) { + return; + } + + cooldowns.put(p.getUniqueId(), now + getInternalCooldownMillis(level)); + p.getInventory().removeItem(new ItemStack(Material.BONE, 1)); + double absorbed = e.getDamage() * getAbsorbPercent(level); + e.setDamage(Math.max(0, e.getDamage() - absorbed)); + + if (areParticlesEnabled()) { + Particle.DustOptions dust = new Particle.DustOptions(Color.fromRGB(235, 230, 210), 1.1f); + p.getWorld().spawnParticle(Particle.DUST, p.getLocation().add(0, 1.0, 0), 18, 0.3, 0.45, 0.3, 0.01, dust); + } + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.BLOCK_BONE_BLOCK_BREAK, 0.8f, 1.1f); + getPlayer(p).getData().addStat("tragoul.marrow-armor.damage-absorbed", absorbed); + xp(p, getConfig().xpPerAbsorb); + }); + } + + private double getAbsorbPercent(int level) { + return Math.min(getConfig().maxAbsorbPercent, + Math.max(0, getConfig().absorbPercentBase + (getLevelPercent(level) * getConfig().absorbPercentFactor))); + } + + private long getInternalCooldownMillis(int level) { + return Math.max(500L, (long) Math.round(getConfig().internalCooldownMillisBase - (getLevelPercent(level) * getConfig().internalCooldownMillisFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Consume a bone from your inventory to absorb part of incoming damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Minimum final damage required before a bone is consumed.", impact = "Higher values ignore chip damage and save bones for real hits.") + double minDamageToTrigger = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fraction of the hit absorbed before level scaling.", impact = "Higher values absorb more damage per bone.") + double absorbPercentBase = 0.20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional absorbed fraction granted at max level.", impact = "Higher values increase level-scaled absorption growth.") + double absorbPercentFactor = 0.30; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hard cap on the absorbed fraction of a hit.", impact = "Prevents full damage immunity at high levels.") + double maxAbsorbPercent = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Internal cooldown in milliseconds before level scaling.", impact = "Higher values stop damage bursts from draining the bone supply.") + double internalCooldownMillisBase = 4000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values let high levels absorb hits more often.") + double internalCooldownMillisFactor = 2000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per absorbed hit.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerAbsorb = 8; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java new file mode 100644 index 000000000..b9e1c72e1 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java @@ -0,0 +1,278 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.UUID; + +public class TragoulPlagueBearer extends SimpleAdaptation { + private static final NamespacedKey PLAGUE_OWNER_KEY = NamespacedKey.fromString("adapt:tragoul_plague_owner"); + private static final NamespacedKey PLAGUE_GENERATION_KEY = NamespacedKey.fromString("adapt:tragoul_plague_generation"); + private static final NamespacedKey PLAGUE_STAMP_KEY = NamespacedKey.fromString("adapt:tragoul_plague_stamp"); + + public TragoulPlagueBearer() { + super("tragoul-plague-bearer"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.plague_bearer.description")); + setDisplayName(Localizer.dLocalize("tragoul.plague_bearer.name")); + setIcon(Material.POISONOUS_POTATO); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.POISONOUS_POTATO) + .key("challenge_tragoul_plague_100") + .title(Localizer.dLocalize("advancement.challenge_tragoul_plague_100.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_plague_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SPIDER_EYE) + .key("challenge_tragoul_plague_1k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_plague_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_plague_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_plague_100", "tragoul.plague-bearer.mobs-infected", 100, 400); + registerMilestone("challenge_tragoul_plague_1k", "tragoul.plague-bearer.mobs-infected", 1000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.plague_bearer.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getSpreadRadius(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.plague_bearer.lore2")); + v.addLore(C.GREEN + "+ " + Form.duration(getSpreadDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.plague_bearer.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Monster monster)) { + return; + } + + Player p = null; + if (e.getDamager() instanceof Player player) { + p = player; + } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof Player shooter) { + p = shooter; + } + + if (p == null) { + return; + } + + Player owner = p; + withAdaptedPlayer(owner, e, () -> { + if (getActiveLevel(owner) <= 0) { + return; + } + + UUID ownerId = owner.getUniqueId(); + if (markIfAfflicted(monster, ownerId)) { + return; + } + + J.runEntity(monster, () -> { + if (monster.isValid() && !monster.isDead()) { + markIfAfflicted(monster, ownerId); + } + }, 2); + }); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + LivingEntity victim = e.getEntity(); + PersistentDataContainer pdc = victim.getPersistentDataContainer(); + String ownerRaw = pdc.get(PLAGUE_OWNER_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + long now = System.currentTimeMillis(); + Long stamp = pdc.get(PLAGUE_STAMP_KEY, PersistentDataType.LONG); + if (stamp == null || now - stamp > getConfig().afflictionFreshnessMillis) { + return; + } + + boolean withered = victim.hasPotionEffect(PotionEffectType.WITHER); + if (!withered && !victim.hasPotionEffect(PotionEffectType.POISON)) { + return; + } + + int generation = pdc.getOrDefault(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER, 0); + if (generation >= getConfig().maxGenerations) { + return; + } + + Player owner = Bukkit.getPlayer(UUID.fromString(ownerRaw)); + if (owner == null || !owner.isOnline()) { + return; + } + + withAdaptedPlayer(owner, () -> { + int level = getActiveLevel(owner); + if (level <= 0) { + return; + } + + double radius = getSpreadRadius(level); + int durationTicks = getSpreadDurationTicks(level); + PotionEffectType effect = withered ? PotionEffectType.WITHER : PotionEffectType.POISON; + int spread = 0; + for (Entity entity : victim.getNearbyEntities(radius, radius, radius)) { + if (!(entity instanceof Monster monster) || monster.isDead() || !monster.isValid()) { + continue; + } + + if (!canDamageTarget(owner, monster)) { + continue; + } + + PersistentDataContainer targetPdc = monster.getPersistentDataContainer(); + targetPdc.set(PLAGUE_OWNER_KEY, PersistentDataType.STRING, ownerRaw); + targetPdc.set(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER, generation + 1); + targetPdc.set(PLAGUE_STAMP_KEY, PersistentDataType.LONG, now); + J.runEntity(monster, () -> { + if (monster.isValid() && !monster.isDead()) { + monster.addPotionEffect(new PotionEffect(effect, durationTicks, 0, true, true, true), true); + } + }); + spread++; + if (spread >= getConfig().maxSpreadTargets) { + break; + } + } + + if (spread <= 0) { + return; + } + + if (areParticlesEnabled()) { + victim.getWorld().spawnParticle(Particle.SMOKE, victim.getLocation().add(0, 0.8, 0), 24, radius * 0.35, 0.5, radius * 0.35, 0.01); + } + SoundPlayer.of(victim.getWorld()).play(victim.getLocation(), Sound.ENTITY_ZOMBIE_INFECT, 0.6f, 1.4f); + getPlayer(owner).getData().addStat("tragoul.plague-bearer.mobs-infected", spread); + xp(owner, spread * getConfig().xpPerInfection); + }); + } + + private boolean markIfAfflicted(Monster monster, UUID ownerId) { + if (!monster.hasPotionEffect(PotionEffectType.POISON) && !monster.hasPotionEffect(PotionEffectType.WITHER)) { + return false; + } + + PersistentDataContainer pdc = monster.getPersistentDataContainer(); + pdc.set(PLAGUE_OWNER_KEY, PersistentDataType.STRING, ownerId.toString()); + pdc.set(PLAGUE_STAMP_KEY, PersistentDataType.LONG, System.currentTimeMillis()); + if (!pdc.has(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER)) { + pdc.set(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER, 0); + } + return true; + } + + private double getSpreadRadius(int level) { + return Math.max(1, getConfig().spreadRadiusBase + (getLevelPercent(level) * getConfig().spreadRadiusFactor)); + } + + private int getSpreadDurationTicks(int level) { + return Math.max(40, (int) Math.round(getConfig().spreadDurationTicksBase + (getLevelPercent(level) * getConfig().spreadDurationTicksFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Mobs that die poisoned or withered by you spread the affliction to nearby hostile mobs.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Spread radius before level scaling.", impact = "Higher values infect hostile mobs further from the corpse.") + double spreadRadiusBase = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional spread radius granted at max level.", impact = "Higher values increase level-scaled radius growth.") + double spreadRadiusFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Spread effect duration in ticks before level scaling.", impact = "Higher values keep infected mobs afflicted longer.") + double spreadDurationTicksBase = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional spread effect duration ticks granted at max level.", impact = "Higher values increase level-scaled duration growth.") + double spreadDurationTicksFactor = 120; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum plague generations a single affliction can chain through.", impact = "Caps chain length to prevent infinite plague cascades.") + int maxGenerations = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum hostile mobs infected per death.", impact = "Caps per-death work to protect server performance.") + int maxSpreadTargets = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds during which an affliction mark is considered fresh at death.", impact = "Higher values let older poisons still spread on death.") + long afflictionFreshnessMillis = 15000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per mob infected by the spread.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerInfection = 6; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java new file mode 100644 index 000000000..71d07e85a --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java @@ -0,0 +1,928 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Skeleton; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ThreadLocalRandom; + +public class TragoulSkeletalServant extends SimpleAdaptation { + private static final NamespacedKey SERVANT_KEY = NamespacedKey.fromString("adapt:tragoul_servant_owner"); + private static final NamespacedKey PLAGUE_OWNER_KEY = NamespacedKey.fromString("adapt:tragoul_plague_owner"); + private static final NamespacedKey PLAGUE_GENERATION_KEY = NamespacedKey.fromString("adapt:tragoul_plague_generation"); + private static final NamespacedKey PLAGUE_STAMP_KEY = NamespacedKey.fromString("adapt:tragoul_plague_stamp"); + + private static final Material[][] HELMETS = { + {Material.LEATHER_HELMET, Material.CHAINMAIL_HELMET}, + {Material.CHAINMAIL_HELMET, Material.IRON_HELMET}, + {Material.IRON_HELMET, Material.DIAMOND_HELMET} + }; + private static final Material[][] CHESTPLATES = { + {Material.LEATHER_CHESTPLATE, Material.CHAINMAIL_CHESTPLATE}, + {Material.CHAINMAIL_CHESTPLATE, Material.IRON_CHESTPLATE}, + {Material.IRON_CHESTPLATE, Material.DIAMOND_CHESTPLATE} + }; + private static final Material[][] LEGGINGS = { + {Material.LEATHER_LEGGINGS, Material.CHAINMAIL_LEGGINGS}, + {Material.CHAINMAIL_LEGGINGS, Material.IRON_LEGGINGS}, + {Material.IRON_LEGGINGS, Material.DIAMOND_LEGGINGS} + }; + private static final Material[][] BOOTS = { + {Material.LEATHER_BOOTS, Material.CHAINMAIL_BOOTS}, + {Material.CHAINMAIL_BOOTS, Material.IRON_BOOTS}, + {Material.IRON_BOOTS, Material.DIAMOND_BOOTS} + }; + private static final Material[][] SWORDS = { + {Material.WOODEN_SWORD, Material.STONE_SWORD}, + {Material.IRON_SWORD, Material.IRON_SWORD}, + {Material.IRON_SWORD, Material.DIAMOND_SWORD} + }; + private static final Material[] BOW_POOL = {Material.BOW}; + + private final Map> servants = new ConcurrentHashMap<>(); + private final Map cooldowns = new ConcurrentHashMap<>(); + private final Map threats = new ConcurrentHashMap<>(); + private final Map servantThornsCooldowns = new ConcurrentHashMap<>(); + private final Map servantCurseCooldowns = new ConcurrentHashMap<>(); + private volatile PerkRefs perkRefs; + + public TragoulSkeletalServant() { + super("tragoul-skeletal-servant"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.skeletal_servant.description")); + setDisplayName(Localizer.dLocalize("tragoul.skeletal_servant.name")); + setIcon(Material.SKELETON_SKULL); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.BONE) + .key("challenge_tragoul_servant_50") + .title(Localizer.dLocalize("advancement.challenge_tragoul_servant_50.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_servant_50.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SKELETON_SKULL) + .key("challenge_tragoul_servant_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_servant_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_servant_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_servant_50", "tragoul.skeletal-servant.servants-summoned", 50, 400); + registerMilestone("challenge_tragoul_servant_500", "tragoul.skeletal-servant.servants-summoned", 500, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + Localizer.dLocalize("tragoul.skeletal_servant.lore1")); + v.addLore(C.GREEN + "+ " + getServantCap(level) + C.GRAY + " " + Localizer.dLocalize("tragoul.skeletal_servant.lore5")); + v.addLore(C.GREEN + "+ " + Form.duration(getDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.skeletal_servant.lore2")); + v.addLore(C.YELLOW + "* " + getBoneCost(level) + C.GRAY + " " + Localizer.dLocalize("tragoul.skeletal_servant.lore3")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.skeletal_servant.lore4")); + v.addLore(C.GRAY + Localizer.dLocalize("tragoul.skeletal_servant.lore6")); + } + + @EventHandler + public void on(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; + } + + if (e.getHand() != EquipmentSlot.HAND || e.getItem() == null || e.getMaterial() != Material.BONE) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + UUID id = p.getUniqueId(); + long now = System.currentTimeMillis(); + SoundPlayer sp = SoundPlayer.of(p); + Long until = cooldowns.get(id); + if (until != null && until > now) { + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 0.8f, 0.8f); + return; + } + + CopyOnWriteArrayList list = servants.computeIfAbsent(id, k -> new CopyOnWriteArrayList<>()); + list.removeIf(servant -> !servant.isValid() || servant.isDead()); + int cap = getServantCap(level); + if (list.size() >= cap && !getConfig().replaceOldestAtCap) { + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 0.8f, 1.2f); + return; + } + + int boneCost = getBoneCost(level); + if (p.getGameMode() != GameMode.CREATIVE) { + if (!p.getInventory().containsAtLeast(new ItemStack(Material.BONE), boneCost)) { + sp.play(p, Sound.BLOCK_CONDUIT_DEACTIVATE, 0.8f, 0.6f); + return; + } + p.getInventory().removeItem(new ItemStack(Material.BONE, boneCost)); + } + + while (list.size() >= cap) { + Skeleton oldest = list.remove(0); + J.runEntity(oldest, () -> despawnServant(oldest)); + } + + cooldowns.put(id, now + getCooldownMillis(level)); + int durationTicks = getDurationTicks(level); + ThreadLocalRandom random = ThreadLocalRandom.current(); + Skeleton servant = p.getWorld().spawn(p.getLocation(), Skeleton.class, s -> { + s.getPersistentDataContainer().set(SERVANT_KEY, PersistentDataType.STRING, id.toString()); + s.setPersistent(false); + s.setRemoveWhenFarAway(false); + s.setShouldBurnInDay(false); + applyServantAttributes(s, level); + equipServant(s, level, random); + }); + list.add(servant); + Player priorityTarget = resolvePriorityTarget(id, p, now); + if (priorityTarget != null) { + servant.setTarget(priorityTarget); + } + scheduleServantPulse(servant, id, now + (durationTicks * 50L)); + J.runEntity(servant, () -> despawnServant(servant), durationTicks); + + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.SOUL, servant.getLocation().add(0, 1, 0), 16, 0.3, 0.6, 0.3, 0.02); + } + sp.play(servant.getLocation(), Sound.ENTITY_SKELETON_AMBIENT, 0.9f, 0.7f); + getPlayer(p).getData().addStat("tragoul.skeletal-servant.servants-summoned", 1); + xp(p, getConfig().xpPerSummon); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityTargetLivingEntityEvent e) { + if (!(e.getEntity() instanceof Skeleton skeleton)) { + return; + } + + LivingEntity target = e.getTarget(); + if (target == null) { + return; + } + + String ownerRaw = skeleton.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + if (target instanceof Player player) { + UUID ownerId = UUID.fromString(ownerRaw); + if (player.getUniqueId().equals(ownerId) || !isPriorityTarget(ownerId, player)) { + e.setCancelled(true); + if (skeleton.getTarget() instanceof Player) { + skeleton.setTarget(null); + } + } + return; + } + + if (target instanceof Skeleton other && other.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING)) { + e.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player victim)) { + return; + } + + Skeleton servant = resolveServantDamager(e.getDamager()); + if (servant == null) { + return; + } + + String ownerRaw = servant.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + UUID ownerId = UUID.fromString(ownerRaw); + if (victim.getUniqueId().equals(ownerId) || !isPriorityTarget(ownerId, victim)) { + e.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onCombatPerks(EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + if (entity instanceof Skeleton skeleton) { + String ownerRaw = skeleton.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw != null) { + handleServantHit(e, skeleton, ownerRaw); + return; + } + } + + if (entity instanceof Player ownerCandidate) { + handleOwnerDamaged(e, ownerCandidate); + } + + if (!(entity instanceof LivingEntity victim)) { + return; + } + + Skeleton servant = resolveServantDamager(e.getDamager()); + if (servant != null && servant != victim) { + handleServantDealtDamage(e, servant, victim); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof Skeleton skeleton)) { + return; + } + + String ownerRaw = skeleton.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + e.getDrops().clear(); + e.setDroppedExp(0); + servantThornsCooldowns.remove(skeleton.getUniqueId()); + removeServantRef(UUID.fromString(ownerRaw), skeleton); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onServantKill(EntityDeathEvent e) { + LivingEntity victim = e.getEntity(); + if (!(victim instanceof Monster)) { + return; + } + + if (victim.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING)) { + return; + } + + if (!(victim.getLastDamageCause() instanceof EntityDamageByEntityEvent damageEvent)) { + return; + } + + Skeleton servant = resolveServantDamager(damageEvent.getDamager()); + if (servant == null) { + return; + } + + String ownerRaw = servant.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + Player owner = Bukkit.getPlayer(UUID.fromString(ownerRaw)); + if (owner == null || !owner.isOnline()) { + return; + } + + TragoulCorpseExplosion nova = perks().nova(); + if (nova != null) { + TragoulCorpseExplosion.detonateServantKill(nova, owner, victim); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + cooldowns.remove(id); + threats.remove(id); + servantCurseCooldowns.remove(id); + CopyOnWriteArrayList list = servants.remove(id); + if (list == null) { + return; + } + + for (Skeleton servant : list) { + servantThornsCooldowns.remove(servant.getUniqueId()); + J.runEntity(servant, () -> { + if (servant.isValid() && !servant.isDead()) { + servant.remove(); + } + }); + } + } + + private void handleOwnerDamaged(EntityDamageByEntityEvent e, Player owner) { + Player attacker = null; + if (e.getDamager() instanceof Player player) { + attacker = player; + } else if (e.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof Player shooter) { + attacker = shooter; + } + + if (attacker == null || attacker == owner) { + return; + } + + if (getLevel(owner) <= 0) { + return; + } + + UUID ownerId = owner.getUniqueId(); + threats.put(ownerId, new PlayerThreat(attacker.getUniqueId(), System.currentTimeMillis())); + CopyOnWriteArrayList list = servants.get(ownerId); + if (list == null || list.isEmpty()) { + return; + } + + if (!canDamageTarget(owner, attacker)) { + return; + } + + Player target = attacker; + for (Skeleton servant : list) { + J.runEntity(servant, () -> { + if (servant.isValid() && !servant.isDead() && target.isOnline() && !target.isDead() && servant.getWorld() == target.getWorld()) { + servant.setTarget(target); + } + }); + } + } + + private void handleServantHit(EntityDamageByEntityEvent e, Skeleton servant, String ownerRaw) { + LivingEntity attacker = resolveLivingDamager(e.getDamager()); + if (attacker == null || attacker == servant) { + return; + } + + if (attacker instanceof Skeleton other && other.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING)) { + return; + } + + UUID ownerId = UUID.fromString(ownerRaw); + if (attacker instanceof Player player && player.getUniqueId().equals(ownerId)) { + return; + } + + Player owner = Bukkit.getPlayer(ownerId); + if (owner == null || !owner.isOnline()) { + return; + } + + long now = System.currentTimeMillis(); + if (attacker instanceof Player player) { + PlayerThreat threat = threats.get(ownerId); + if (threat != null && threat.attackerId().equals(player.getUniqueId()) && now - threat.stamp() < getConfig().playerThreatWindowMillis) { + threats.put(ownerId, new PlayerThreat(player.getUniqueId(), now)); + } + } + + PerkRefs refs = perks(); + applyThorns(refs.thorns(), owner, servant, attacker, now); + applyFrailty(refs.frailty(), owner, attacker, now); + } + + private void handleServantDealtDamage(EntityDamageByEntityEvent e, Skeleton servant, LivingEntity victim) { + String ownerRaw = servant.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw == null) { + return; + } + + Player owner = Bukkit.getPlayer(UUID.fromString(ownerRaw)); + if (owner == null || !owner.isOnline()) { + return; + } + + PerkRefs refs = perks(); + applySoulSiphon(refs.siphon(), owner, servant, victim, e); + applyPlagueMark(refs.plague(), owner, victim); + } + + private void applyThorns(TragoulThorns thorns, Player owner, Skeleton servant, LivingEntity attacker, long now) { + if (thorns == null) { + return; + } + + Long until = servantThornsCooldowns.get(servant.getUniqueId()); + if (until != null && until > now) { + return; + } + + int level = thorns.getActiveLevel(owner); + if (level <= 0 || !canDamageTarget(owner, attacker)) { + return; + } + + servantThornsCooldowns.put(servant.getUniqueId(), now + 1500L); + double reflected = thorns.getConfig().damageMultiplierPerLevel * level; + J.runEntity(attacker, () -> { + if (attacker.isValid() && !attacker.isDead()) { + attacker.damage(reflected, owner); + } + }); + } + + private void applyFrailty(TragoulCurseOfFrailty frailty, Player owner, LivingEntity attacker, long now) { + if (frailty == null) { + return; + } + + Long until = servantCurseCooldowns.get(attacker.getUniqueId()); + if (until != null && until > now) { + return; + } + + int level = frailty.getActiveLevel(owner); + if (level <= 0 || !canDamageTarget(owner, attacker)) { + return; + } + + TragoulCurseOfFrailty.Config curseConfig = frailty.getConfig(); + servantCurseCooldowns.put(attacker.getUniqueId(), now + curseConfig.perAttackerCooldownMillis); + double levelPercent = frailty.getLevelPercent(level); + int duration = Math.max(40, (int) Math.round(curseConfig.curseDurationTicksBase + (levelPercent * curseConfig.curseDurationTicksFactor))); + int weaknessAmplifier = levelPercent >= 0.8 ? 1 : 0; + boolean slowness = levelPercent >= curseConfig.slownessUnlockPercent; + int slownessAmplifier = curseConfig.slownessAmplifier; + J.runEntity(attacker, () -> { + if (!attacker.isValid() || attacker.isDead()) { + return; + } + + attacker.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, duration, weaknessAmplifier, true, true, true), true); + if (slowness) { + attacker.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, duration, slownessAmplifier, true, true, true), true); + } + }); + } + + private void applySoulSiphon(TragoulSoulSiphon siphon, Player owner, Skeleton servant, LivingEntity victim, EntityDamageByEntityEvent e) { + if (siphon == null) { + return; + } + + EntityDamageEvent.DamageCause cause = e.getCause(); + if (cause != EntityDamageEvent.DamageCause.ENTITY_ATTACK && cause != EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK) { + return; + } + + int level = siphon.getActiveLevel(owner); + if (level <= 0 || !canDamageTarget(owner, victim)) { + return; + } + + TragoulSoulSiphon.Config siphonConfig = siphon.getConfig(); + double levelPercent = siphon.getLevelPercent(level); + double percent = Math.max(0, siphonConfig.healPercentBase + (levelPercent * siphonConfig.healPercentFactor)); + double cap = Math.max(0.5, siphonConfig.healCapPerSecondBase + (levelPercent * siphonConfig.healCapPerSecondFactor)); + double heal = Math.min(cap, e.getFinalDamage() * percent); + if (heal <= 0) { + return; + } + + IAttribute attribute = Version.get().getAttribute(servant, Attributes.GENERIC_MAX_HEALTH); + double maxHealth = attribute == null ? 20D : attribute.getValue(); + double newHealth = Math.min(maxHealth, servant.getHealth() + heal); + if (newHealth <= servant.getHealth()) { + return; + } + + servant.setHealth(newHealth); + if (areParticlesEnabled()) { + servant.getWorld().spawnParticle(Particle.SOUL, servant.getLocation().add(0, 1.2, 0), 4, 0.2, 0.3, 0.2, 0.01); + } + } + + private void applyPlagueMark(TragoulPlagueBearer plague, Player owner, LivingEntity victim) { + if (plague == null || !(victim instanceof Monster monster)) { + return; + } + + if (!monster.hasPotionEffect(PotionEffectType.POISON) && !monster.hasPotionEffect(PotionEffectType.WITHER)) { + return; + } + + if (plague.getActiveLevel(owner) <= 0) { + return; + } + + PersistentDataContainer pdc = monster.getPersistentDataContainer(); + pdc.set(PLAGUE_OWNER_KEY, PersistentDataType.STRING, owner.getUniqueId().toString()); + pdc.set(PLAGUE_STAMP_KEY, PersistentDataType.LONG, System.currentTimeMillis()); + if (!pdc.has(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER)) { + pdc.set(PLAGUE_GENERATION_KEY, PersistentDataType.INTEGER, 0); + } + } + + private void scheduleServantPulse(Skeleton servant, UUID ownerId, long expiresAt) { + J.runEntity(servant, () -> { + if (!servant.isValid() || servant.isDead() || System.currentTimeMillis() >= expiresAt) { + removeServantRef(ownerId, servant); + return; + } + + retarget(servant, ownerId); + scheduleServantPulse(servant, ownerId, expiresAt); + }, getConfig().retargetIntervalTicks); + } + + private void retarget(Skeleton servant, UUID ownerId) { + PlayerThreat threat = threats.get(ownerId); + if (threat != null && System.currentTimeMillis() - threat.stamp() < getConfig().playerThreatWindowMillis) { + Player attacker = Bukkit.getPlayer(threat.attackerId()); + if (attacker != null && attacker.isOnline() && !attacker.isDead() && attacker.getWorld() == servant.getWorld()) { + Player owner = Bukkit.getPlayer(ownerId); + if (owner != null && owner.isOnline() && canDamageTarget(owner, attacker)) { + if (servant.getTarget() != attacker) { + servant.setTarget(attacker); + } + return; + } + } + } + + LivingEntity current = servant.getTarget(); + if (current instanceof Monster && current.isValid() && !current.isDead()) { + return; + } + + double range = getConfig().targetSearchRadius; + Monster nearest = null; + double best = Double.MAX_VALUE; + for (Entity entity : servant.getNearbyEntities(range, range, range)) { + if (!(entity instanceof Monster monster) || monster.isDead() || !monster.isValid()) { + continue; + } + + if (monster.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING)) { + continue; + } + + double distance = monster.getLocation().distanceSquared(servant.getLocation()); + if (distance < best) { + best = distance; + nearest = monster; + } + } + + if (nearest != null) { + servant.setTarget(nearest); + } + } + + private void despawnServant(Skeleton servant) { + String ownerRaw = servant.getPersistentDataContainer().get(SERVANT_KEY, PersistentDataType.STRING); + if (ownerRaw != null) { + removeServantRef(UUID.fromString(ownerRaw), servant); + } + + servantThornsCooldowns.remove(servant.getUniqueId()); + if (servant.isValid() && !servant.isDead()) { + if (areParticlesEnabled()) { + servant.getWorld().spawnParticle(Particle.SOUL, servant.getLocation().add(0, 1, 0), 12, 0.3, 0.6, 0.3, 0.02); + } + SoundPlayer.of(servant.getWorld()).play(servant.getLocation(), Sound.ENTITY_SKELETON_DEATH, 0.7f, 1.3f); + servant.remove(); + } + } + + private void removeServantRef(UUID ownerId, Skeleton servant) { + CopyOnWriteArrayList list = servants.get(ownerId); + if (list != null) { + list.remove(servant); + } + } + + private Player resolvePriorityTarget(UUID ownerId, Player owner, long now) { + PlayerThreat threat = threats.get(ownerId); + if (threat == null || now - threat.stamp() >= getConfig().playerThreatWindowMillis) { + return null; + } + + Player attacker = Bukkit.getPlayer(threat.attackerId()); + if (attacker == null || !attacker.isOnline() || attacker.isDead()) { + return null; + } + + if (attacker.getWorld() != owner.getWorld()) { + return null; + } + + if (!canDamageTarget(owner, attacker)) { + return null; + } + + return attacker; + } + + private boolean isPriorityTarget(UUID ownerId, Player candidate) { + PlayerThreat threat = threats.get(ownerId); + if (threat == null || !threat.attackerId().equals(candidate.getUniqueId())) { + return false; + } + + if (System.currentTimeMillis() - threat.stamp() >= getConfig().playerThreatWindowMillis) { + return false; + } + + Player owner = Bukkit.getPlayer(ownerId); + return owner != null && owner.isOnline() && canDamageTarget(owner, candidate); + } + + private void applyServantAttributes(Skeleton servant, int level) { + IAttribute maxHealth = Version.get().getAttribute(servant, Attributes.GENERIC_MAX_HEALTH); + if (maxHealth != null) { + maxHealth.setBaseValue(maxHealth.getBaseValue() + (level * getConfig().healthBonusPerLevel)); + servant.setHealth(maxHealth.getValue()); + } + + IAttribute attack = Version.get().getAttribute(servant, Attributes.GENERIC_ATTACK_DAMAGE); + if (attack != null) { + attack.setBaseValue(attack.getBaseValue() + (level * getConfig().attackBonusPerLevel)); + } + } + + private void equipServant(Skeleton servant, int level, ThreadLocalRandom random) { + EntityEquipment equipment = servant.getEquipment(); + if (equipment == null) { + return; + } + + int tier = getGearTier(level); + double pieceChance = getConfig().gearChancePerPiece; + double enchantChance = tier == 0 ? 0 : Math.max(0, getConfig().enchantChanceBase + (getLevelPercent(level) * getConfig().enchantChanceFactor)); + if (random.nextDouble() < pieceChance) { + equipment.setHelmet(rollPiece(random, HELMETS[tier], enchantChance, Enchantment.PROTECTION)); + } + if (random.nextDouble() < pieceChance) { + equipment.setChestplate(rollPiece(random, CHESTPLATES[tier], enchantChance, Enchantment.PROTECTION)); + } + if (random.nextDouble() < pieceChance) { + equipment.setLeggings(rollPiece(random, LEGGINGS[tier], enchantChance, Enchantment.PROTECTION)); + } + if (random.nextDouble() < pieceChance) { + equipment.setBoots(rollPiece(random, BOOTS[tier], enchantChance, Enchantment.PROTECTION)); + } + + ItemStack weapon = random.nextDouble() < getConfig().bowChance + ? rollPiece(random, BOW_POOL, enchantChance, Enchantment.POWER) + : rollPiece(random, SWORDS[tier], enchantChance, Enchantment.SHARPNESS); + equipment.setItemInMainHand(weapon); + + equipment.setHelmetDropChance(0f); + equipment.setChestplateDropChance(0f); + equipment.setLeggingsDropChance(0f); + equipment.setBootsDropChance(0f); + equipment.setItemInMainHandDropChance(0f); + equipment.setItemInOffHandDropChance(0f); + } + + private ItemStack rollPiece(ThreadLocalRandom random, Material[] pool, double enchantChance, Enchantment enchantment) { + Material material = pool[random.nextInt(pool.length)]; + ItemStack item = new ItemStack(material); + if (enchantChance > 0 && random.nextDouble() < enchantChance) { + item.addEnchantment(enchantment, 1 + random.nextInt(2)); + } + return item; + } + + private int getGearTier(int level) { + return switch (Math.min(Math.max(level, 1), 5)) { + case 1, 2 -> 0; + case 3, 4 -> 1; + default -> 2; + }; + } + + private PerkRefs perks() { + PerkRefs local = perkRefs; + if (local != null) { + return local; + } + + if (getSkill() == null) { + return new PerkRefs(null, null, null, null, null); + } + + TragoulThorns thornsRef = null; + TragoulSoulSiphon siphonRef = null; + TragoulCurseOfFrailty frailtyRef = null; + TragoulCorpseExplosion novaRef = null; + TragoulPlagueBearer plagueRef = null; + for (Adaptation adaptation : getSkill().getAdaptations()) { + if (adaptation instanceof TragoulThorns found) { + thornsRef = found; + } else if (adaptation instanceof TragoulSoulSiphon found) { + siphonRef = found; + } else if (adaptation instanceof TragoulCurseOfFrailty found) { + frailtyRef = found; + } else if (adaptation instanceof TragoulCorpseExplosion found) { + novaRef = found; + } else if (adaptation instanceof TragoulPlagueBearer found) { + plagueRef = found; + } + } + + local = new PerkRefs(thornsRef, siphonRef, frailtyRef, novaRef, plagueRef); + perkRefs = local; + return local; + } + + private static LivingEntity resolveLivingDamager(Entity damager) { + if (damager instanceof LivingEntity living) { + return living; + } + + if (damager instanceof Projectile projectile && projectile.getShooter() instanceof LivingEntity shooter) { + return shooter; + } + + return null; + } + + private static Skeleton resolveServantDamager(Entity damager) { + Entity source = damager; + if (source instanceof Projectile projectile && projectile.getShooter() instanceof Entity shooter) { + source = shooter; + } + + if (source instanceof Skeleton skeleton && skeleton.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING)) { + return skeleton; + } + + return null; + } + + private int getServantCap(int level) { + return Math.max(1, (int) Math.round(level * getConfig().servantCapPerLevel)); + } + + private int getBoneCost(int level) { + return Math.max(1, (int) Math.round(getConfig().boneCostBase - (getLevelPercent(level) * getConfig().boneCostReduction))); + } + + private int getDurationTicks(int level) { + return Math.max(100, (int) Math.round(getConfig().durationTicksBase + (getLevelPercent(level) * getConfig().durationTicksFactor))); + } + + private long getCooldownMillis(int level) { + return Math.max(5000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + servantCurseCooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + servantThornsCooldowns.entrySet().removeIf(entry -> entry.getValue() <= now); + long window = getConfig().playerThreatWindowMillis; + threats.entrySet().removeIf(entry -> now - entry.getValue().stamp() > window); + for (CopyOnWriteArrayList list : servants.values()) { + list.removeIf(servant -> !servant.isValid() || servant.isDead()); + } + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private record PlayerThreat(UUID attackerId, long stamp) { + } + + private record PerkRefs(TragoulThorns thorns, TragoulSoulSiphon siphon, + TragoulCurseOfFrailty frailty, + TragoulCorpseExplosion nova, + TragoulPlagueBearer plague) { + } + + @NoArgsConstructor + @ConfigDescription("Sneak right-click with bones to raise a pack of temporary skeletal servants that gear up with your level, inherit your Tragoul perks, and hunt whoever attacks you.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.75; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bones consumed per summon before level scaling.", impact = "Higher values make summoning more expensive at low levels.") + double boneCostBase = 8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bone cost reduction granted at max level.", impact = "Higher values make summoning cheaper as the player levels.") + double boneCostReduction = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Servant lifetime in ticks before level scaling.", impact = "Higher values keep each servant alive longer.") + double durationTicksBase = 400; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional servant lifetime ticks granted at max level.", impact = "Higher values increase the level-scaled lifetime growth.") + double durationTicksFactor = 800; + @art.arcane.adapt.util.config.ConfigDoc(value = "Summon cooldown in milliseconds before level scaling.", impact = "Higher values slow how often a servant can be summoned.") + double cooldownMillisBase = 90000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values let high levels summon more often.") + double cooldownMillisFactor = 45000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Living servants allowed per adaptation level.", impact = "Higher values let one necromancer field a larger pack of servants.") + double servantCapPerLevel = 1.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Replaces the oldest living servant when summoning at the cap.", impact = "False quietly refuses the summon instead of recycling the oldest servant.") + boolean replaceOldestAtCap = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds during which a player who attacked the owner stays the priority target.", impact = "Higher values keep servants hunting an aggressor for longer after the last hit.") + long playerThreatWindowMillis = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance for each armor piece to be equipped on a freshly summoned servant.", impact = "Higher values produce more heavily armored servants.") + double gearChancePerPiece = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance for an equipped piece to receive an enchantment at mid gear tiers.", impact = "Higher values enchant servant gear more often before level scaling.") + double enchantChanceBase = 0.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional enchant chance granted at max level.", impact = "Higher values make high-level servants spawn with enchanted gear more often.") + double enchantChanceFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance a servant spawns with a bow instead of a sword.", impact = "Higher values produce more ranged servants.") + double bowChance = 0.3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bonus max health granted to servants per adaptation level.", impact = "Higher values make servants tankier as the owner levels.") + double healthBonusPerLevel = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Bonus attack damage granted to servants per adaptation level.", impact = "Higher values make servants hit harder as the owner levels.") + double attackBonusPerLevel = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Ticks between servant retarget pulses.", impact = "Lower values retarget faster but cost more scheduler work.") + int retargetIntervalTicks = 20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Radius the servant scans for hostile mobs to attack.", impact = "Higher values let the servant acquire targets further away.") + double targetSearchRadius = 12; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per servant summon.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerSummon = 30; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSoulSiphon.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSoulSiphon.java new file mode 100644 index 000000000..c6914dd57 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSoulSiphon.java @@ -0,0 +1,202 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.tragoul; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.api.version.IAttribute; +import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Attributes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class TragoulSoulSiphon extends SimpleAdaptation { + private final Map budgets = new ConcurrentHashMap<>(); + + public TragoulSoulSiphon() { + super("tragoul-soul-siphon"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("tragoul.soul_siphon.description")); + setDisplayName(Localizer.dLocalize("tragoul.soul_siphon.name")); + setIcon(Material.SOUL_LANTERN); + setInterval(25000); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.SOUL_LANTERN) + .key("challenge_tragoul_siphon_500") + .title(Localizer.dLocalize("advancement.challenge_tragoul_siphon_500.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_siphon_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.SOUL_CAMPFIRE) + .key("challenge_tragoul_siphon_10k") + .title(Localizer.dLocalize("advancement.challenge_tragoul_siphon_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_tragoul_siphon_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_tragoul_siphon_500", "tragoul.soul-siphon.health-siphoned", 500, 400); + registerMilestone("challenge_tragoul_siphon_10k", "tragoul.soul-siphon.health-siphoned", 10000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getHealPercent(level), 0) + C.GRAY + " " + Localizer.dLocalize("tragoul.soul_siphon.lore1")); + v.addLore(C.YELLOW + "* " + Form.f(getHealCapPerSecond(level), 1) + C.GRAY + " " + Localizer.dLocalize("tragoul.soul_siphon.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + budgets.remove(e.getPlayer().getUniqueId()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player p)) { + return; + } + + EntityDamageEvent.DamageCause cause = e.getCause(); + if (cause != EntityDamageEvent.DamageCause.ENTITY_ATTACK && cause != EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK) { + return; + } + + if (!(e.getEntity() instanceof LivingEntity)) { + return; + } + + withAdaptedPlayer(p, e, () -> { + int level = getActiveLevel(p); + if (level <= 0 || !canDamageTarget(p, e.getEntity())) { + return; + } + + long now = System.currentTimeMillis(); + HealBudget budget = budgets.computeIfAbsent(p.getUniqueId(), k -> new HealBudget()); + if (now - budget.windowStart >= 1000L) { + budget.windowStart = now; + budget.healed = 0; + } + + double remaining = getHealCapPerSecond(level) - budget.healed; + if (remaining <= 0) { + return; + } + + double heal = Math.min(remaining, e.getFinalDamage() * getHealPercent(level)); + if (heal <= 0) { + return; + } + + IAttribute attribute = Version.get().getAttribute(p, Attributes.GENERIC_MAX_HEALTH); + double maxHealth = attribute == null ? 20D : attribute.getValue(); + double newHealth = Math.min(maxHealth, p.getHealth() + heal); + if (newHealth <= p.getHealth()) { + return; + } + + budget.healed += heal; + p.setHealth(newHealth); + if (areParticlesEnabled()) { + J.runEntity(p, () -> p.getWorld().spawnParticle(Particle.SOUL, p.getLocation().add(0, 1.2, 0), 5, 0.25, 0.3, 0.25, 0.01)); + } + getPlayer(p).getData().addStat("tragoul.soul-siphon.health-siphoned", heal); + xp(p, getConfig().xpPerHeal); + }); + } + + private double getHealPercent(int level) { + return Math.max(0, getConfig().healPercentBase + (getLevelPercent(level) * getConfig().healPercentFactor)); + } + + private double getHealCapPerSecond(int level) { + return Math.max(0.5, getConfig().healCapPerSecondBase + (getLevelPercent(level) * getConfig().healCapPerSecondFactor)); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public void onTick() { + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + private static class HealBudget { + private long windowStart; + private double healed; + } + + @NoArgsConstructor + @ConfigDescription("Melee hits heal you for a portion of the damage dealt, capped per second.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.72; + @art.arcane.adapt.util.config.ConfigDoc(value = "Fraction of melee damage returned as healing before level scaling.", impact = "Higher values increase baseline lifesteal.") + double healPercentBase = 0.05; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional lifesteal fraction granted at max level.", impact = "Higher values increase level-scaled lifesteal growth.") + double healPercentFactor = 0.20; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum health restored per second before level scaling.", impact = "Lower values harden the anti-abuse healing cap.") + double healCapPerSecondBase = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional per-second healing cap granted at max level.", impact = "Higher values let high levels sustain more healing per second.") + double healCapPerSecondFactor = 4.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per successful siphon heal.", impact = "Higher values accelerate skill progression from this adaptation.") + double xpPerHeal = 3; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java new file mode 100644 index 000000000..688a85ad2 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java @@ -0,0 +1,204 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +public class UnarmedDisarm extends SimpleAdaptation { + private final Map targetLockUntil = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedDisarm() { + super("unarmed-disarm"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.disarm.description")); + setDisplayName(Localizer.dLocalize("unarmed.disarm.name")); + setIcon(Material.STICK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5125); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_disarm_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_disarm_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_disarm_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_disarm_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_disarm_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_disarm_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_disarm_100", "unarmed.disarm.disarms", 100, 400); + registerMilestone("challenge_unarmed_disarm_1k", "unarmed.disarm.disarms", 1000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.pc(getChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("unarmed.disarm.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getConfig().targetCooldownMillis, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.disarm.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + if (!(attack.target() instanceof LivingEntity victim)) { + return; + } + + if (victim instanceof Player && !getConfig().allowDisarmPlayers) { + return; + } + + long now = System.currentTimeMillis(); + Long lock = targetLockUntil.get(victim.getUniqueId()); + if (lock != null && now < lock) { + return; + } + + if (ThreadLocalRandom.current().nextDouble() > getChance(attack.level())) { + return; + } + + EntityEquipment equipment = victim.getEquipment(); + if (equipment == null) { + return; + } + + ItemStack main = equipment.getItemInMainHand(); + ItemStack off = equipment.getItemInOffHand(); + ItemStack knocked; + if (isItem(main)) { + knocked = main.clone(); + equipment.setItemInMainHand(null); + } else if (isItem(off) && off.getType() == Material.SHIELD) { + knocked = off.clone(); + equipment.setItemInOffHand(null); + } else { + return; + } + + Item dropped = victim.getWorld().dropItemNaturally(victim.getLocation().add(0, 0.4, 0), knocked); + dropped.setPickupDelay(getConfig().pickupDelayTicks); + targetLockUntil.put(victim.getUniqueId(), now + getConfig().targetCooldownMillis); + + SoundPlayer sp = SoundPlayer.of(victim.getWorld()); + sp.play(victim.getLocation(), Sound.ENTITY_PLAYER_ATTACK_KNOCKBACK, 0.8f, 1.35f); + sp.play(victim.getLocation(), Sound.ITEM_LEAD_BREAK, 0.9f, 0.8f); + if (areParticlesEnabled()) { + victim.getWorld().spawnParticle(Particle.CRIT, victim.getLocation().add(0, 1.1, 0), 10, 0.25, 0.3, 0.25, 0.08); + } + xp(p, getConfig().xpPerDisarm, "disarm"); + getPlayer(p).getData().addStat("unarmed.disarm.disarms", 1); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + targetLockUntil.remove(e.getPlayer().getUniqueId()); + } + + private double getChance(int level) { + return Math.min(1, getConfig().chanceBase + (getLevelPercent(level) * getConfig().chanceFactor)); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + targetLockUntil.values().removeIf(until -> until <= now); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bare-hand hits can knock the target's held item to the ground.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows disarming other players, not just mobs.", impact = "True lets bare-hand hits knock items out of player hands in PVP.") + boolean allowDisarmPlayers = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance for a bare-hand hit to disarm the target.", impact = "Higher values make disarms proc more often at level 1.") + double chanceBase = 0.04; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional disarm chance granted at max level.", impact = "Higher values make disarms proc more often as levels increase.") + double chanceFactor = 0.18; + @art.arcane.adapt.util.config.ConfigDoc(value = "Pickup delay ticks applied to the knocked item.", impact = "Higher values keep the victim from re-grabbing the item for longer.") + int pickupDelayTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Per-target cooldown in milliseconds between disarms.", impact = "Higher values prevent chain-disarming the same target.") + long targetCooldownMillis = 8000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per successful disarm.", impact = "Higher values speed up unarmed skill progression from disarms.") + double xpPerDisarm = 28; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedFlurry.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedFlurry.java new file mode 100644 index 000000000..1d2ff9233 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedFlurry.java @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.UUID; + +public class UnarmedFlurry extends SimpleAdaptation { + private final Map flurries = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedFlurry() { + super("unarmed-flurry"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.flurry.description")); + setDisplayName(Localizer.dLocalize("unarmed.flurry.name")); + setIcon(Material.FEATHER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4811); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_flurry_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_flurry_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_flurry_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_flurry_10k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_flurry_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_flurry_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_flurry_1k", "unarmed.flurry.flurry-hits", 1000, 400); + registerMilestone("challenge_unarmed_flurry_10k", "unarmed.flurry.flurry-hits", 10000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getMaxStacks(level) + C.GRAY + " " + Localizer.dLocalize("unarmed.flurry.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getDamagePerStack(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.flurry.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getConfig().windowMillis, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.flurry.lore3")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + flurries.remove(p.getUniqueId()); + return; + } + + long now = System.currentTimeMillis(); + int level = attack.level(); + FlurryState state = flurries.computeIfAbsent(p.getUniqueId(), id -> new FlurryState()); + if (now - state.lastHitMillis > getConfig().windowMillis) { + state.stacks = 0; + } + + state.lastHitMillis = now; + int max = getMaxStacks(level); + state.stacks = Math.min(max, state.stacks + 1); + + double bonus = state.stacks * getDamagePerStack(level); + e.setDamage(e.getDamage() + bonus); + if (state.stacks >= max) { + SoundPlayer.of(p.getWorld()).play(e.getEntity().getLocation(), Sound.BLOCK_NOTE_BLOCK_BIT, 0.5f, 1.8f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.CRIT, e.getEntity().getLocation().add(0, 1, 0), 8, 0.2, 0.3, 0.2, 0.08); + } + } + xp(p, bonus * getConfig().xpPerBonusDamage); + getPlayer(p).getData().addStat("unarmed.flurry.flurry-hits", 1); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerItemHeldEvent e) { + FlurryState state = flurries.get(e.getPlayer().getUniqueId()); + if (state == null) { + return; + } + + ItemStack next = e.getPlayer().getInventory().getItem(e.getNewSlot()); + if (next != null && isTool(next)) { + flurries.remove(e.getPlayer().getUniqueId()); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + flurries.remove(e.getPlayer().getUniqueId()); + } + + private int getMaxStacks(int level) { + return Math.max(1, (int) Math.round(getConfig().maxStacksBase + (getLevelPercent(level) * getConfig().maxStacksFactor))); + } + + private double getDamagePerStack(int level) { + return getConfig().damagePerStackBase + (getLevelPercent(level) * getConfig().damagePerStackFactor); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Rapid consecutive bare-hand hits build flurry stacks that add bonus damage.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base maximum flurry stacks at level 1.", impact = "Higher values allow more stacked bonus damage early.") + double maxStacksBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional maximum flurry stacks granted at max level.", impact = "Higher values raise the stack cap as levels increase.") + double maxStacksFactor = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base bonus damage added per flurry stack.", impact = "Higher values make each stack hit harder.") + double damagePerStackBase = 0.15; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional per-stack bonus damage granted at max level.", impact = "Higher values make stacks hit harder as levels increase.") + double damagePerStackFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Window in milliseconds between hits before stacks reset.", impact = "Higher values make the flurry easier to maintain.") + long windowMillis = 900; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per point of flurry bonus damage dealt.", impact = "Higher values speed up unarmed skill progression from flurries.") + double xpPerBonusDamage = 3.7; + } + + private static class FlurryState { + private int stacks = 0; + private long lastHitMillis = 0L; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGrapple.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGrapple.java new file mode 100644 index 000000000..5f6ac9b6c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedGrapple.java @@ -0,0 +1,273 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Boss; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.util.Vector; + +import java.util.Map; +import java.util.UUID; + +public class UnarmedGrapple extends SimpleAdaptation { + private final Map grabs = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map cooldownUntil = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedGrapple() { + super("unarmed-grapple"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.grapple.description")); + setDisplayName(Localizer.dLocalize("unarmed.grapple.name")); + setIcon(Material.LEAD); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(2750); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_grapple_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_grapple_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_grapple_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_grapple_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_grapple_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_grapple_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_grapple_100", "unarmed.grapple.hurled-mobs", 100, 400); + registerMilestone("challenge_unarmed_grapple_1k", "unarmed.grapple.hurled-mobs", 1000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getForce(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.grapple.lore1")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.grapple.lore2")); + v.addLore(C.GRAY + Localizer.dLocalize("unarmed.grapple.lore3")); + v.addLore(C.RED + "- " + Form.f(getConfig().exhaustionPerThrow, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.grapple.lore4")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + long now = System.currentTimeMillis(); + GrabState state = grabs.remove(p.getUniqueId()); + if (state != null && now - state.grabbedAtMillis <= getConfig().grabTimeoutMillis) { + hurl(p, state.target, attack.level(), now); + return; + } + + if (!p.isSneaking()) { + return; + } + + Long lock = cooldownUntil.get(p.getUniqueId()); + if (lock != null && now < lock) { + return; + } + + if (!(attack.target() instanceof LivingEntity victim)) { + return; + } + + if (victim instanceof Player && !getConfig().allowGrapplePlayers) { + return; + } + + if (victim instanceof Boss) { + return; + } + + grabs.put(p.getUniqueId(), new GrabState(victim, now)); + SoundPlayer.of(p.getWorld()).play(victim.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 1f, 0.7f); + if (areParticlesEnabled()) { + victim.getWorld().spawnParticle(Particle.CRIT, victim.getLocation().add(0, 1, 0), 6, 0.2, 0.3, 0.2, 0.05); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerToggleSneakEvent e) { + if (e.isSneaking()) { + return; + } + + Player p = e.getPlayer(); + GrabState state = grabs.remove(p.getUniqueId()); + if (state == null) { + return; + } + + long now = System.currentTimeMillis(); + if (now - state.grabbedAtMillis > getConfig().grabTimeoutMillis) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + hurl(p, state.target, level, now); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + grabs.remove(id); + cooldownUntil.remove(id); + } + + private void hurl(Player p, LivingEntity target, int level, long now) { + if (!target.isValid() || target.isDead() || target.getWorld() != p.getWorld()) { + return; + } + + double maxRange = getConfig().maxHurlRange; + if (target.getLocation().distanceSquared(p.getLocation()) > maxRange * maxRange) { + return; + } + + cooldownUntil.put(p.getUniqueId(), now + getCooldownMillis(level)); + p.setExhaustion(p.getExhaustion() + (float) getConfig().exhaustionPerThrow); + Vector velocity = p.getLocation().getDirection().normalize().multiply(getForce(level)).setY(getConfig().upwardBoost + (getLevelPercent(level) * getConfig().upwardBoostFactor)); + J.runEntity(target, () -> { + if (target.isValid() && !target.isDead()) { + target.setVelocity(velocity); + } + }); + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(p.getLocation(), Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.75f); + sp.play(target.getLocation(), Sound.ENTITY_PLAYER_ATTACK_KNOCKBACK, 0.9f, 1.2f); + if (areParticlesEnabled()) { + target.getWorld().spawnParticle(Particle.CLOUD, target.getLocation().add(0, 0.8, 0), 12, 0.25, 0.25, 0.25, 0.05); + } + xp(p, getConfig().xpPerHurl, "grapple"); + getPlayer(p).getData().addStat("unarmed.grapple.hurled-mobs", 1); + } + + private double getForce(int level) { + return getConfig().forceBase + (getLevelPercent(level) * getConfig().forceFactor); + } + + private long getCooldownMillis(int level) { + return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + grabs.values().removeIf(state -> now - state.grabbedAtMillis > getConfig().grabTimeoutMillis || !state.target.isValid() || state.target.isDead()); + cooldownUntil.values().removeIf(until -> until <= now); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak-punch a mob to grab it, then hurl it where you look.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.65; + @art.arcane.adapt.util.config.ConfigDoc(value = "Allows grappling other players, not just mobs.", impact = "True lets sneak-punches grab players in PVP.") + boolean allowGrapplePlayers = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base hurl force at level 1.", impact = "Higher values throw grabbed mobs further.") + double forceBase = 0.9; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional hurl force granted at max level.", impact = "Higher values throw grabbed mobs further as levels increase.") + double forceFactor = 1.4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base upward component added to the hurl velocity.", impact = "Higher values arc thrown mobs higher.") + double upwardBoost = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional upward hurl component granted at max level.", impact = "Higher values arc thrown mobs higher as levels increase.") + double upwardBoostFactor = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum distance in blocks a grabbed mob can be hurled from.", impact = "Higher values let the grab persist over larger gaps.") + double maxHurlRange = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds before an unused grab expires.", impact = "Higher values keep the grab primed for longer.") + long grabTimeoutMillis = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base grapple cooldown in milliseconds at level 1.", impact = "Higher values make the ability usable less often.") + double cooldownMillisBase = 9000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values make the ability recharge faster as levels increase.") + double cooldownMillisFactor = 5000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Exhaustion added to the player per hurled mob.", impact = "Higher values drain saturation and food faster with each throw. Set to 0 to disable the exhaustion cost.") + double exhaustionPerThrow = 2.0; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per hurled mob.", impact = "Higher values speed up unarmed skill progression from grapples.") + double xpPerHurl = 32; + } + + private static class GrabState { + private final LivingEntity target; + private final long grabbedAtMillis; + + private GrabState(LivingEntity target, long grabbedAtMillis) { + this.target = target; + this.grabbedAtMillis = grabbedAtMillis; + } + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedIronFists.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedIronFists.java new file mode 100644 index 000000000..2edafd1fb --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedIronFists.java @@ -0,0 +1,166 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.PotionEffectTypes; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.potion.PotionEffect; + +public class UnarmedIronFists extends SimpleAdaptation { + public UnarmedIronFists() { + super("unarmed-iron-fists"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.iron_fists.description")); + setDisplayName(Localizer.dLocalize("unarmed.iron_fists.name")); + setIcon(Material.ANVIL); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4622); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_iron_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_iron_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_iron_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_iron_10k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_iron_10k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_iron_10k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_iron_1k", "unarmed.iron-fists.iron-hits", 1000, 400); + registerMilestone("challenge_unarmed_iron_10k", "unarmed.iron-fists.iron-hits", 10000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getDamageBonus(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.iron_fists.lore1")); + v.addLore(C.GREEN + "+ " + (getHasteAmplifier(level) + 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.iron_fists.lore2")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + e.setDamage(e.getDamage() + getDamageBonus(attack.level())); + xp(p, getConfig().xpPerHit); + getPlayer(p).getData().addStat("unarmed.iron-fists.iron-hits", 1); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(BlockDamageEvent e) { + Player p = e.getPlayer(); + if (isItem(p.getInventory().getItemInMainHand())) { + return; + } + + float hardness = e.getBlock().getType().getHardness(); + if (hardness < 0 || hardness > getConfig().softBlockMaxHardness) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, e.getBlock().getLocation()); + if (context == null) { + return; + } + + p.addPotionEffect(new PotionEffect(PotionEffectTypes.FAST_DIGGING, getConfig().hasteDurationTicks, getHasteAmplifier(context.level()), false, false, true)); + } + + private double getDamageBonus(int level) { + return getConfig().damageBase + (getLevelPercent(level) * getConfig().damageFactor); + } + + private int getHasteAmplifier(int level) { + return Math.max(0, (int) Math.round(getLevelPercent(level) * getConfig().hasteAmplifierFactor)); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bare fists hit harder and punch through soft blocks faster.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base flat bare-hand damage bonus at level 1.", impact = "Higher values make every bare-hand hit stronger.") + double damageBase = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional flat damage bonus granted at max level.", impact = "Higher values make bare-hand hits stronger as levels increase.") + double damageFactor = 2.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum block hardness still considered a soft block.", impact = "Higher values let the punch-haste apply to tougher blocks.") + double softBlockMaxHardness = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Haste duration in ticks while punching soft blocks.", impact = "Higher values keep the dig-speed buff active longer.") + int hasteDurationTicks = 25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Haste amplifier granted at max level while punching soft blocks.", impact = "Higher values speed up bare-hand block breaking.") + double hasteAmplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per bare-hand hit.", impact = "Higher values speed up unarmed skill progression from hits.") + double xpPerHit = 2.4; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedMeditation.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedMeditation.java new file mode 100644 index 000000000..53854d584 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedMeditation.java @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.adapt.util.reflect.registries.Particles; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.Map; +import java.util.UUID; + +public class UnarmedMeditation extends SimpleAdaptation { + private final Map lastCombatMillis = new java.util.concurrent.ConcurrentHashMap<>(); + private final Map lastPositions = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedMeditation() { + super("unarmed-meditation"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.meditation.description")); + setDisplayName(Localizer.dLocalize("unarmed.meditation.name")); + setIcon(Material.AMETHYST_CLUSTER); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(1000); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_meditate_500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_meditate_500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_meditate_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_meditate_5k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_meditate_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_meditate_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_meditate_500", "unarmed.meditation.absorption-gained", 500, 400); + registerMilestone("challenge_unarmed_meditate_5k", "unarmed.meditation.absorption-gained", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getAbsorptionCap(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.meditation.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getConfig().gainPerPulse) + C.GRAY + " " + Localizer.dLocalize("unarmed.meditation.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getConfig().combatLockoutMillis, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.meditation.lore3")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDamageByEntityEvent e) { + long now = System.currentTimeMillis(); + if (e.getDamager() instanceof Player attacker) { + lastCombatMillis.put(attacker.getUniqueId(), now); + } + if (e.getEntity() instanceof Player victim) { + lastCombatMillis.put(victim.getUniqueId(), now); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + UUID id = e.getPlayer().getUniqueId(); + lastCombatMillis.remove(id); + lastPositions.remove(id); + } + + @Override + public void onTick() { + long now = System.currentTimeMillis(); + for (art.arcane.adapt.api.world.AdaptPlayer adaptPlayer : learnedCandidates(now)) { + Player p = adaptPlayer.getPlayer(); + if (p == null || !p.isOnline()) { + continue; + } + + UUID id = p.getUniqueId(); + if (!p.isSneaking()) { + lastPositions.remove(id); + continue; + } + + if (isItem(p.getInventory().getItemInMainHand()) || isItem(p.getInventory().getItemInOffHand())) { + lastPositions.remove(id); + continue; + } + + Long combat = lastCombatMillis.get(id); + if (combat != null && now - combat < getConfig().combatLockoutMillis) { + continue; + } + + Location current = p.getLocation(); + Location previous = lastPositions.put(id, current); + if (previous == null || previous.getWorld() != current.getWorld() || previous.distanceSquared(current) > getConfig().stationaryEpsilonSquared) { + continue; + } + + int level = getActiveLevel(p); + if (level <= 0) { + continue; + } + + double cap = getAbsorptionCap(level); + double absorption = p.getAbsorptionAmount(); + if (absorption >= cap) { + continue; + } + + double gained = Math.min(cap - absorption, getConfig().gainPerPulse); + J.runEntity(p, () -> { + if (p.isOnline() && !p.isDead()) { + p.setAbsorptionAmount(Math.min(cap, p.getAbsorptionAmount() + gained)); + } + }); + + SoundPlayer.of(p.getWorld()).play(current, Sound.BLOCK_AMETHYST_BLOCK_CHIME, 0.3f, 1.5f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particles.ENCHANTMENT_TABLE, current.clone().add(0, 1.4, 0), 8, 0.3, 0.4, 0.3, 0.04); + } + xpSilent(p, getConfig().xpPerPulse, "meditation"); + adaptPlayer.getData().addStat("unarmed.meditation.absorption-gained", gained); + } + } + + private double getAbsorptionCap(int level) { + return getConfig().absorptionCapBase + (getLevelPercent(level) * getConfig().absorptionCapFactor); + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Meditate while sneaking, still, and empty-handed to slowly build absorption hearts.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.55; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base absorption cap in health points at level 1.", impact = "Higher values allow more stored absorption early.") + double absorptionCapBase = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional absorption cap granted at max level.", impact = "Higher values allow more stored absorption as levels increase.") + double absorptionCapFactor = 10; + @art.arcane.adapt.util.config.ConfigDoc(value = "Absorption health points gained per meditation pulse.", impact = "Higher values build absorption faster.") + double gainPerPulse = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Milliseconds after combat before meditation can resume.", impact = "Higher values force a longer calm period after fighting.") + long combatLockoutMillis = 8000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum squared movement distance still considered stationary.", impact = "Higher values tolerate more drift while meditating.") + double stationaryEpsilonSquared = 0.01; + @art.arcane.adapt.util.config.ConfigDoc(value = "Silent XP granted per meditation pulse.", impact = "Higher values speed up unarmed skill progression from meditating.") + double xpPerPulse = 1.2; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPressurePoint.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPressurePoint.java new file mode 100644 index 000000000..deabe9e80 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedPressurePoint.java @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class UnarmedPressurePoint extends SimpleAdaptation { + public UnarmedPressurePoint() { + super("unarmed-pressure-point"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.pressure_point.description")); + setDisplayName(Localizer.dLocalize("unarmed.pressure_point.name")); + setIcon(Material.TRIPWIRE_HOOK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4733); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_pressure_500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_pressure_500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_pressure_500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_pressure_5k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_pressure_5k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_pressure_5k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_pressure_500", "unarmed.pressure-point.pressure-strikes", 500, 400); + registerMilestone("challenge_unarmed_pressure_5k", "unarmed.pressure-point.pressure-strikes", 5000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + (getMaxSlownessAmplifier(level) + 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.pressure_point.lore1")); + if (isWeaknessUnlocked(level)) { + v.addLore(C.GREEN + "+ " + (getConfig().maxWeaknessAmplifier + 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.pressure_point.lore2")); + } else { + v.addLore(C.GRAY + Localizer.dLocalize("unarmed.pressure_point.lore3")); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(EntityDamageByEntityEvent e) { + art.arcane.adapt.api.adaptation.Adaptation.AttackContext attack = resolveAttackContext(e); + if (attack == null) { + return; + } + + Player p = attack.attacker(); + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + if (!(attack.target() instanceof LivingEntity victim)) { + return; + } + + int level = attack.level(); + applyStack(victim, PotionEffectType.SLOWNESS, getMaxSlownessAmplifier(level), getConfig().slownessDurationTicks); + if (isWeaknessUnlocked(level)) { + applyStack(victim, PotionEffectType.WEAKNESS, getConfig().maxWeaknessAmplifier, getConfig().weaknessDurationTicks); + } + + SoundPlayer.of(victim.getWorld()).play(victim.getLocation(), Sound.BLOCK_POINTED_DRIPSTONE_LAND, 0.6f, 1.6f); + if (areParticlesEnabled()) { + victim.getWorld().spawnParticle(Particle.CRIT, victim.getLocation().add(0, 1.2, 0), 5, 0.15, 0.2, 0.15, 0.05); + } + xp(p, getConfig().xpPerStrike); + getPlayer(p).getData().addStat("unarmed.pressure-point.pressure-strikes", 1); + } + + private void applyStack(LivingEntity victim, PotionEffectType type, int maxAmplifier, int durationTicks) { + PotionEffect existing = victim.getPotionEffect(type); + int amplifier = existing == null ? 0 : Math.min(maxAmplifier, existing.getAmplifier() + 1); + victim.addPotionEffect(new PotionEffect(type, durationTicks, amplifier, false, true, true), true); + } + + private int getMaxSlownessAmplifier(int level) { + return Math.max(0, (int) Math.round(getConfig().maxSlownessAmplifierBase + (getLevelPercent(level) * getConfig().maxSlownessAmplifierFactor))); + } + + private boolean isWeaknessUnlocked(int level) { + return getLevelPercent(level) >= getConfig().weaknessUnlockPercent; + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bare-hand hits apply stacking slowness, with weakness at higher levels.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base maximum slowness amplifier at level 1.", impact = "Higher values allow stronger slowness stacking early.") + double maxSlownessAmplifierBase = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional maximum slowness amplifier granted at max level.", impact = "Higher values allow stronger slowness stacking as levels increase.") + double maxSlownessAmplifierFactor = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Slowness duration in ticks per pressure strike.", impact = "Higher values keep targets slowed for longer.") + int slownessDurationTicks = 60; + @art.arcane.adapt.util.config.ConfigDoc(value = "Level percent required before weakness stacking unlocks.", impact = "Lower values unlock the weakness effect at earlier levels.") + double weaknessUnlockPercent = 0.6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum weakness amplifier once unlocked.", impact = "Higher values allow stronger weakness stacking.") + int maxWeaknessAmplifier = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Weakness duration in ticks per pressure strike.", impact = "Higher values keep targets weakened for longer.") + int weaknessDurationTicks = 50; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per pressure strike.", impact = "Higher values speed up unarmed skill progression from strikes.") + double xpPerStrike = 3.1; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java new file mode 100644 index 000000000..ef2a27e6c --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java @@ -0,0 +1,194 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.common.scheduling.J; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; + +public class UnarmedSecondWind extends SimpleAdaptation { + private final Map lastTriggerMillis = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedSecondWind() { + super("unarmed-second-wind"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.second_wind.description")); + setDisplayName(Localizer.dLocalize("unarmed.second_wind.name")); + setIcon(Material.COOKED_BEEF); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(4960); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_second_wind_100") + .title(Localizer.dLocalize("advancement.challenge_unarmed_second_wind_100.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_second_wind_100.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_second_wind_1k") + .title(Localizer.dLocalize("advancement.challenge_unarmed_second_wind_1k.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_second_wind_1k.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_second_wind_100", "unarmed.second-wind.second-winds", 100, 400); + registerMilestone("challenge_unarmed_second_wind_1k", "unarmed.second-wind.second-winds", 1000, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + getFoodRestore(level) + C.GRAY + " " + Localizer.dLocalize("unarmed.second_wind.lore1")); + v.addLore(C.GREEN + "+ " + Form.duration(getRegenDurationTicks(level) * 50D, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.second_wind.lore2")); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(EntityDeathEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim) || victim instanceof Player) { + return; + } + + if (!(victim.getLastDamageCause() instanceof EntityDamageByEntityEvent dmg) || !(dmg.getDamager() instanceof Player p)) { + return; + } + + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + int level = getActiveLevel(p); + if (level <= 0) { + return; + } + + long now = System.currentTimeMillis(); + Long last = lastTriggerMillis.get(p.getUniqueId()); + if (last != null && now - last < getConfig().cooldownMillis) { + return; + } + + lastTriggerMillis.put(p.getUniqueId(), now); + int foodRestore = getFoodRestore(level); + int regenTicks = getRegenDurationTicks(level); + J.runEntity(p, () -> { + if (!p.isOnline() || p.isDead()) { + return; + } + p.setFoodLevel(Math.min(20, p.getFoodLevel() + foodRestore)); + p.setSaturation((float) Math.min(p.getFoodLevel(), p.getSaturation() + getConfig().saturationRestore)); + p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, regenTicks, getConfig().regenAmplifier, false, true, true), true); + }); + + SoundPlayer.of(p.getWorld()).play(p.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.5f, 1.7f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.HEART, p.getLocation().add(0, 1.6, 0), 3, 0.3, 0.2, 0.3, 0); + } + xp(p, getConfig().xpPerSecondWind, "second-wind"); + getPlayer(p).getData().addStat("unarmed.second-wind.second-winds", 1); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + lastTriggerMillis.remove(e.getPlayer().getUniqueId()); + } + + private int getFoodRestore(int level) { + return Math.max(1, (int) Math.round(getConfig().foodRestoreBase + (getLevelPercent(level) * getConfig().foodRestoreFactor))); + } + + private int getRegenDurationTicks(int level) { + return Math.max(20, (int) Math.round(getConfig().regenDurationTicksBase + (getLevelPercent(level) * getConfig().regenDurationTicksFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Bare-hand kills restore hunger and grant a short regeneration burst.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base hunger points restored per bare-hand kill.", impact = "Higher values restore more food per kill.") + double foodRestoreBase = 1; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional hunger points restored at max level.", impact = "Higher values restore more food per kill as levels increase.") + double foodRestoreFactor = 4; + @art.arcane.adapt.util.config.ConfigDoc(value = "Saturation restored per bare-hand kill.", impact = "Higher values keep the hunger bar full for longer.") + double saturationRestore = 1.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base regeneration duration in ticks per bare-hand kill.", impact = "Higher values keep the regen burst active longer.") + double regenDurationTicksBase = 40; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional regeneration duration ticks granted at max level.", impact = "Higher values keep the regen burst active longer as levels increase.") + double regenDurationTicksFactor = 80; + @art.arcane.adapt.util.config.ConfigDoc(value = "Regeneration amplifier applied by the burst.", impact = "Higher values heal faster during the burst.") + int regenAmplifier = 0; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown in milliseconds between second wind triggers.", impact = "Higher values prevent rapid kill-chains from spamming the heal.") + long cooldownMillis = 3000; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per second wind trigger.", impact = "Higher values speed up unarmed skill progression from kills.") + double xpPerSecondWind = 18; + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedShockwaveClap.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedShockwaveClap.java new file mode 100644 index 000000000..a1ffb9018 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedShockwaveClap.java @@ -0,0 +1,245 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.adaptation.unarmed; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.advancement.AdaptAdvancement; +import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; +import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.adapt.util.config.ConfigDescription; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.inventorygui.Element; +import lombok.NoArgsConstructor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.util.Vector; + +import java.util.Map; +import java.util.UUID; + +public class UnarmedShockwaveClap extends SimpleAdaptation { + private final Map nextClapAt = new java.util.concurrent.ConcurrentHashMap<>(); + + public UnarmedShockwaveClap() { + super("unarmed-shockwave-clap"); + registerConfiguration(Config.class); + setDescription(Localizer.dLocalize("unarmed.shockwave_clap.description")); + setDisplayName(Localizer.dLocalize("unarmed.shockwave_clap.name")); + setIcon(Material.NOTE_BLOCK); + setBaseCost(getConfig().baseCost); + setMaxLevel(getConfig().maxLevel); + setInitialCost(getConfig().initialCost); + setCostFactor(getConfig().costFactor); + setInterval(5230); + registerAdvancement(AdaptAdvancement.builder() + .icon(Material.IRON_INGOT) + .key("challenge_unarmed_clap_250") + .title(Localizer.dLocalize("advancement.challenge_unarmed_clap_250.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_clap_250.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .child(AdaptAdvancement.builder() + .icon(Material.DIAMOND) + .key("challenge_unarmed_clap_2500") + .title(Localizer.dLocalize("advancement.challenge_unarmed_clap_2500.title")) + .description(Localizer.dLocalize("advancement.challenge_unarmed_clap_2500.description")) + .frame(AdaptAdvancementFrame.CHALLENGE) + .visibility(AdvancementVisibility.PARENT_GRANTED) + .build()) + .build()); + registerMilestone("challenge_unarmed_clap_250", "unarmed.shockwave-clap.mobs-clapped", 250, 400); + registerMilestone("challenge_unarmed_clap_2500", "unarmed.shockwave-clap.mobs-clapped", 2500, 1500); + } + + @Override + public void addStats(int level, Element v) { + v.addLore(C.GREEN + "+ " + Form.f(getRange(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.shockwave_clap.lore1")); + v.addLore(C.GREEN + "+ " + Form.f(getForce(level)) + C.GRAY + " " + Localizer.dLocalize("unarmed.shockwave_clap.lore2")); + v.addLore(C.YELLOW + "* " + Form.duration((double) getCooldownMillis(level), 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.shockwave_clap.lore3")); + v.addLore(C.RED + "- " + getConfig().hungerCost + C.GRAY + " " + Localizer.dLocalize("unarmed.shockwave_clap.lore4")); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void on(PlayerInteractEvent e) { + Action action = e.getAction(); + if (action != Action.LEFT_CLICK_AIR && action != Action.LEFT_CLICK_BLOCK) { + return; + } + + Player p = e.getPlayer(); + if (!p.isSneaking()) { + return; + } + + long now = System.currentTimeMillis(); + Long next = nextClapAt.get(p.getUniqueId()); + if (next != null && now < next) { + return; + } + + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { + return; + } + + int hungerCost = getConfig().hungerCost; + if (p.getFoodLevel() < hungerCost) { + return; + } + + art.arcane.adapt.api.adaptation.Adaptation.BlockActionContext context = resolveInteractContext(p, p.getLocation()); + if (context == null) { + return; + } + + int level = context.level(); + nextClapAt.put(p.getUniqueId(), now + getCooldownMillis(level)); + p.setFoodLevel(Math.max(0, p.getFoodLevel() - hungerCost)); + + double range = getRange(level); + double force = getForce(level); + Location origin = p.getLocation(); + Vector look = origin.getDirection().normalize(); + int affected = 0; + for (Entity nearby : p.getWorld().getNearbyEntities(origin, range, range, range)) { + if (!(nearby instanceof LivingEntity hit) || hit == p) { + continue; + } + + Vector to = hit.getLocation().toVector().subtract(origin.toVector()); + double lengthSquared = to.lengthSquared(); + if (lengthSquared <= 0.0001 || lengthSquared > range * range) { + continue; + } + + to.multiply(1 / Math.sqrt(lengthSquared)); + if (look.dot(to) < getConfig().coneDotThreshold) { + continue; + } + + if (!canDamageTarget(p, hit)) { + continue; + } + + hit.setVelocity(hit.getVelocity().multiply(0.2).add(to.multiply(force).setY(getUpwardForce(level)))); + affected++; + } + + SoundPlayer sp = SoundPlayer.of(p.getWorld()); + sp.play(origin, Sound.ENTITY_PLAYER_ATTACK_SWEEP, 1f, 0.6f); + sp.play(origin, Sound.BLOCK_BELL_RESONATE, 0.7f, 1.4f); + if (areParticlesEnabled()) { + p.getWorld().spawnParticle(Particle.EXPLOSION, origin.clone().add(look.clone().multiply(1.2)).add(0, 1, 0), 1, 0.1, 0.1, 0.1, 0.02); + p.getWorld().spawnParticle(Particle.CLOUD, origin.clone().add(look.clone().multiply(1.5)).add(0, 0.8, 0), 20, 0.6, 0.4, 0.6, 0.08); + } + + getPlayer(p).getData().addStat("unarmed.shockwave-clap.claps", 1); + if (affected > 0) { + xp(p, getConfig().xpPerTargetHit * affected); + getPlayer(p).getData().addStat("unarmed.shockwave-clap.mobs-clapped", affected); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + nextClapAt.remove(e.getPlayer().getUniqueId()); + } + + private double getRange(int level) { + return getConfig().rangeBase + (getLevelPercent(level) * getConfig().rangeFactor); + } + + private double getForce(int level) { + return getConfig().forceBase + (getLevelPercent(level) * getConfig().forceFactor); + } + + private double getUpwardForce(int level) { + return getConfig().upwardForceBase + (getLevelPercent(level) * getConfig().upwardForceFactor); + } + + private long getCooldownMillis(int level) { + return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + } + + @Override + public void onTick() { + + } + + @Override + public boolean isEnabled() { + return getConfig().enabled; + } + + @Override + public boolean isPermanent() { + return getConfig().permanent; + } + + @NoArgsConstructor + @ConfigDescription("Sneak and punch the air to clap a shockwave that knocks back enemies in a cone.") + protected static class Config { + @art.arcane.adapt.util.config.ConfigDoc(value = "Keeps this adaptation permanently active once learned.", impact = "True removes the normal learn/unlearn flow and treats it as always learned.") + boolean permanent = false; + @art.arcane.adapt.util.config.ConfigDoc(value = "Enables or disables this feature.", impact = "Set to false to disable behavior without uninstalling files.") + boolean enabled = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knowledge cost used when learning this adaptation.", impact = "Higher values make each level cost more knowledge.") + int baseCost = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Maximum level a player can reach for this adaptation.", impact = "Higher values allow more levels; lower values cap progression sooner.") + int maxLevel = 5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Knowledge cost required to purchase level 1.", impact = "Higher values make unlocking the first level more expensive.") + int initialCost = 6; + @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") + double costFactor = 0.7; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base shockwave range in blocks at level 1.", impact = "Higher values let the clap reach more distant enemies.") + double rangeBase = 3.5; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional shockwave range granted at max level.", impact = "Higher values extend the clap reach as levels increase.") + double rangeFactor = 3; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base knockback force at level 1.", impact = "Higher values launch enemies further.") + double forceBase = 0.8; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional knockback force granted at max level.", impact = "Higher values launch enemies further as levels increase.") + double forceFactor = 1.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base upward knockback component at level 1.", impact = "Higher values pop enemies into the air more.") + double upwardForceBase = 0.25; + @art.arcane.adapt.util.config.ConfigDoc(value = "Additional upward knockback granted at max level.", impact = "Higher values pop enemies higher as levels increase.") + double upwardForceFactor = 0.2; + @art.arcane.adapt.util.config.ConfigDoc(value = "Look-direction dot threshold that defines the cone width.", impact = "Lower values widen the cone; higher values narrow it.") + double coneDotThreshold = 0.45; + @art.arcane.adapt.util.config.ConfigDoc(value = "Base clap cooldown in milliseconds at level 1.", impact = "Higher values make the ability usable less often.") + double cooldownMillisBase = 10000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values make the ability recharge faster as levels increase.") + double cooldownMillisFactor = 6000; + @art.arcane.adapt.util.config.ConfigDoc(value = "Hunger points consumed per shockwave clap.", impact = "Higher values drain more food per clap; activation fails when food is below the cost. Set to 0 to disable the hunger cost.") + int hungerCost = 2; + @art.arcane.adapt.util.config.ConfigDoc(value = "XP granted per enemy knocked back by a clap.", impact = "Higher values speed up unarmed skill progression from claps.") + double xpPerTargetHit = 14; + } +} diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index 4308cde39..0262d2a5b 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -185,6 +185,12 @@ public SkillArchitect() { registerAdaptation(new ArchitectWirelessRedstone()); registerAdaptation(new ArchitectElevator()); registerAdaptation(new ArchitectSmartShape()); + registerAdaptation(new ArchitectScaffolder()); + registerAdaptation(new ArchitectSupplyLine()); + registerAdaptation(new ArchitectSteadyHands()); + registerAdaptation(new ArchitectChalkLine()); + registerAdaptation(new ArchitectDemolition()); + registerAdaptation(new ArchitectStonecutterSavant()); } @EventHandler(priority = EventPriority.MONITOR) diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java index bf295798d..3f63f9243 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillChronos.java @@ -77,6 +77,14 @@ public SkillChronos() { registerAdaptation(new ChronosInstantRecall()); registerAdaptation(new ChronosTimeBomb()); registerAdaptation(new ChronosTemporalEcho()); + registerAdaptation(new ChronosStasisField()); + registerAdaptation(new ChronosRewind()); + registerAdaptation(new ChronosBorrowedTime()); + registerAdaptation(new ChronosOvertime()); + registerAdaptation(new ChronosAccelerate()); + registerAdaptation(new ChronosHourglassGuard()); + registerAdaptation(new ChronosPocketWatch()); + registerAdaptation(new ChronosDejaVu()); lastPositions = new ConcurrentHashMap<>(); positionHistory = new ConcurrentHashMap<>(); recentActionTypes = new ConcurrentHashMap<>(); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index aea9fa458..4b0e1cf53 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -23,10 +23,19 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.content.adaptation.excavation.ExcavationBurrow; +import art.arcane.adapt.content.adaptation.excavation.ExcavationDowsing; import art.arcane.adapt.content.adaptation.excavation.ExcavationDropToInventory; +import art.arcane.adapt.content.adaptation.excavation.ExcavationEarthMover; +import art.arcane.adapt.content.adaptation.excavation.ExcavationGraveDigger; import art.arcane.adapt.content.adaptation.excavation.ExcavationHaste; +import art.arcane.adapt.content.adaptation.excavation.ExcavationMudlark; import art.arcane.adapt.content.adaptation.excavation.ExcavationOmniTool; +import art.arcane.adapt.content.adaptation.excavation.ExcavationSeismicPing; +import art.arcane.adapt.content.adaptation.excavation.ExcavationSoftFall; import art.arcane.adapt.content.adaptation.excavation.ExcavationSpelunker; +import art.arcane.adapt.content.adaptation.excavation.ExcavationTreasureHunter; +import art.arcane.adapt.content.adaptation.excavation.ExcavationTunneler; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; @@ -59,6 +68,15 @@ public SkillExcavation() { registerAdaptation(new ExcavationSpelunker()); registerAdaptation(new ExcavationOmniTool()); registerAdaptation(new ExcavationDropToInventory()); + registerAdaptation(new ExcavationSeismicPing()); + registerAdaptation(new ExcavationTunneler()); + registerAdaptation(new ExcavationTreasureHunter()); + registerAdaptation(new ExcavationSoftFall()); + registerAdaptation(new ExcavationEarthMover()); + registerAdaptation(new ExcavationDowsing()); + registerAdaptation(new ExcavationBurrow()); + registerAdaptation(new ExcavationGraveDigger()); + registerAdaptation(new ExcavationMudlark()); registerAdvancement(AdaptAdvancement.builder() .icon(Material.WOODEN_SHOVEL).key("challenge_excavate_1k") .title(Localizer.dLocalize("advancement.challenge_excavate_1k.title")) diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index 5c6acb12a..a752dc9f6 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -60,6 +60,13 @@ public SkillPickaxes() { registerAdaptation(new PickaxeDropToInventory()); registerAdaptation(new PickaxeSilkSpawner()); registerAdaptation(new PickaxeQuarrySense()); + registerAdaptation(new PickaxeTunnelBore()); + registerAdaptation(new PickaxeDeepCore()); + registerAdaptation(new PickaxeObsidianRush()); + registerAdaptation(new PickaxeUnbreakablePact()); + registerAdaptation(new PickaxeRepairRhythm()); + registerAdaptation(new PickaxeGemPolish()); + registerAdaptation(new PickaxeStoneSkin()); registerAdvancement(AdaptAdvancement.builder() .icon(Material.WOODEN_PICKAXE) .key("challenge_pickaxe_1k") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java index 200e658a7..1a672de7a 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTragOul.java @@ -67,6 +67,14 @@ public SkillTragOul() { registerAdaptation(new TragoulLance()); registerAdaptation(new TragoulBloodPact()); registerAdaptation(new TragoulBoneHarvest()); + registerAdaptation(new TragoulCorpseExplosion()); + registerAdaptation(new TragoulSoulSiphon()); + registerAdaptation(new TragoulSkeletalServant()); + registerAdaptation(new TragoulMarrowArmor()); + registerAdaptation(new TragoulCurseOfFrailty()); + registerAdaptation(new TragoulDeathSense()); + registerAdaptation(new TragoulPlagueBearer()); + registerAdaptation(new TragoulLastRites()); registerAdvancement(AdaptAdvancement.builder() .icon(Material.CRIMSON_ROOTS) .key("challenge_trag_1k") diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java index 27b1f528f..a3edb6ec7 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillUnarmed.java @@ -56,6 +56,14 @@ public SkillUnarmed() { registerAdaptation(new UnarmedGlassCannon()); registerAdaptation(new UnarmedBatteringCharge()); registerAdaptation(new UnarmedComboChain()); + registerAdaptation(new UnarmedDisarm()); + registerAdaptation(new UnarmedFlurry()); + registerAdaptation(new UnarmedPressurePoint()); + registerAdaptation(new UnarmedShockwaveClap()); + registerAdaptation(new UnarmedIronFists()); + registerAdaptation(new UnarmedGrapple()); + registerAdaptation(new UnarmedSecondWind()); + registerAdaptation(new UnarmedMeditation()); setIcon(Material.FIRE_CHARGE); registerAdvancement(AdaptAdvancement.builder() .icon(Material.FIRE_CHARGE) diff --git a/src/main/resources/en_US.toml b/src/main/resources/en_US.toml index 49a731263..a18930abd 100644 --- a/src/main/resources/en_US.toml +++ b/src/main/resources/en_US.toml @@ -19,7 +19,7 @@ title = "Zoom a 50K!" description = "Walk over 50 Kilometers (50,000 blocks)" [advancement.challenge_sprint_500k] -title = "Traverse the Universe!!" +title = "Traverse the Universe!" description = "Walk over 500 Kilometers (500,000 blocks)" [advancement.challenge_sprint_marathon] @@ -1184,114 +1184,114 @@ xporb = "Experience Orb" [skill.agility] name = "Agility" icon = "⇉" -description = "Agility is the ability to move quick and fluidly in the face of obstacles." +description = "Agility is the ability to move quick and fluidly in the face of obstacles. Level up by sprinting, swimming, jumping, and climbing." [skill.architect] name = "Architect" icon = "⬧" -description = "Structures are the building blocks of the world. Reality is in your hands, yours to control." +description = "Structures are the building blocks of the world. Reality is in your hands, yours to control. Level up by placing blocks." [skill.axes] name = "Axes" icon = "🪓" -description1 = "Why chop down trees, when you could chop " +description1 = "Level up by chopping wood and fighting with axes. Why chop down trees, when you could chop " description2 = "things" -description3 = "instead, same end result!" +description3 = "instead? Same end result!" [skill.brewing] name = "Brewing" icon = "❦" -description = "Double Bubble, Triple Bubble, Quadruple Bubble- I still cant put this potion into a cauldron" +description = "Level up by brewing potions, then unlock recipes vanilla never gave you. Double bubble, triple bubble - still can't put a potion in a cauldron." [skill.blocking] name = "Blocking" icon = "🛡" -description = "Sticks and stones Won't break your bones, But a shield will." +description = "Sticks and stones won't break your bones, but a shield will. Level up by blocking hits with a shield." [skill.crafting] name = "Crafting" icon = "⌂" -description = "With no more pieces left to place, why not make another?" +description = "With no more pieces left to place, why not make another? Level up by crafting items." [skill.discovery] name = "Discovery" icon = "⚛" -description = "As your perception expands, your mind unravels to discover that which you did not." +description = "As your perception expands, your mind unravels to discover that which you did not. Level up by exploring and collecting experience." [skill.enchanting] name = "Enchanting" icon = "♰" -description = "What are you going on about? Prophecies, visions, superstitious jibber-jabber?" +description = "Bend glimmering knowledge to your will. Level up by enchanting gear at the enchanting table." [skill.excavation] name = "Excavation" icon = "ᛳ" -description = "Diggy Diggy Hole..." +description = "Diggy diggy hole... Level up by digging dirt, sand, gravel, and other soft ground with shovels." [skill.herbalism] name = "Herbalism" icon = "⚘" -description = "I can't find any plants, but I can find some seeds and- is that... Weed?" +description = "Master the green and growing things. Level up by farming, harvesting crops, and gathering plants." [skill.hunter] name = "Hunter" icon = "☠" -description = "Hunting is about the journey not the outcome." +description = "Hunting is about the journey, not the outcome. Level up by slaying mobs; its boons trigger when you are struck, at the cost of hunger." [skill.nether] name = "Nether" icon = "₪" -description = "From the depths of the Nether itself." +description = "From the depths of the Nether itself. Level up by braving the Nether and its monsters." [skill.pickaxe] name = "Pickaxe" icon = "⛏" -description = "Dwarves are the miners, but ive learned a thing or two in my time. IM SWEDISH" +description = "Dwarves are the miners, but I've learned a thing or two in my time. Level up by mining stone and ores with pickaxes." [skill.ranged] name = "Ranged" icon = "🏹" -description = "Distance is the key to victory, and the key to survival." +description = "Distance is the key to victory, and the key to survival. Level up by landing projectile hits." [skill.rift] name = "Rift" icon = "❍" -description = "The Rift is a caustic harness, but you have harnessed the harness." +description = "Harness the caustic energy of the Rift. Level up by teleporting and using ender items." [skill.seaborne] name = "Seaborne" icon = "🎣" -description = "With this skill, you may will the wonders of the water." +description = "With this skill, you may will the wonders of the water. Level up by swimming and fishing." [skill.stealth] name = "Stealth" icon = "☯" -description = "The art of the unseen. Walk in the shadows." +description = "The art of the unseen. Walk in the shadows. Level up by sneaking and striking while hidden." [skill.swords] name = "Swords" icon = "⚔" -description = "By the power of GreyStone!" +description = "By the power of GreyStone! Level up by dealing damage with swords." [skill.taming] name = "Taming" icon = "♥" -description = "The parrots and the bees... and you?" +description = "The parrots and the bees... and you? Level up by taming, breeding, and fighting alongside your pets." [skill.tragoul] name = "TragOul" icon = "🗡" -description = "Blood flows through the veins of the universe. Constricted by your hands." +description = "Blood flows through the veins of the universe, constricted by your hands. Level up by taking damage and surviving at low health." [skill.chronos] name = "Chronos" icon = "🕒" -description = "Wind the Clock of the universe, experience the flow. Break the clock, become it." +description = "Wind the clock of the universe, experience the flow. Break the clock, become it. Level up by moving, sleeping, and surviving the passage of time." [skill.unarmed] name = "Unarmed" icon = "»" -description = "Without a weapon is not without strength." +description = "Without a weapon is not without strength. Level up by fighting bare-handed." # agility [agility] @@ -1312,7 +1312,7 @@ lore = ["Ladder speed multiplier", "Fast descent speed"] [agility.super_jump] name = "Super Jump" -description = "Exceptional Height Advantage." +description = "Sneak and jump to launch a super jump. Jump height scales with level." lore1 = "Max Jump Height" lore2 = "Sneak + Jump to Super Jump!" lore = ["Max Jump Height", "Sneak + Jump to Super Jump!"] @@ -1368,19 +1368,19 @@ lore = ["Magically create: ", "Blocks beneath you!"] [architect.glass] name = "Silk-Touch Glass" -description = "This allows for you to essentially prevent the loss of glass blocks when you break them with an empty hand!" +description = "Break glass blocks with an empty hand to pick them up without shattering them." lore1 = "Your hands gain silk touch for Glass" lore = ["Your hands gain silk touch for Glass"] [architect.wireless_redstone] name = "Redstone Remote" description = "This allows for you to use a redstone torch to toggle redstone, remotely!" -lore1 = "Target + Redstone Torch + Enderpearl = 1 Redstone Remote" -lore = ["Target + Redstone Torch + Enderpearl = 1 Redstone Remote"] +lore1 = "Target + Redstone Torch + Ender Pearl = 1 Redstone Remote" +lore = ["Target + Redstone Torch + Ender Pearl = 1 Redstone Remote"] [architect.placement] name = "Builders Wand" -description = "Allows for you to place multiple blocks at once to activate Sneak, and hold a block that matches your looking block and place! Keep in mind, you may need to move a tad to trigger bounding the boxes!" +description = "Sneak while holding a block that matches the surface you are looking at to place multiple blocks across it at once. You may need to move slightly to refresh the placement preview." lore1 = "You need" lore2 = "blocks in your hand to place this" lore3 = "A Material Builders Wand" @@ -1464,77 +1464,77 @@ lore = ["Per Touching Fire Block", "Per Touching Lava Block"] [brewing.darkness] name = "Bottled Darkness" -description = "Not sure why you need this, but here you go!" +description = "Unlocks brewing Potions of Darkness, which shroud vision and prevent sprinting." lore1 = "NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)" -lore2 = "It Should be noted that This prevents the user from Sprinting!" -lore = ["NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)", "It Should be noted that This prevents the user from Sprinting!"] +lore2 = "Note: Darkness prevents the drinker from sprinting!" +lore = ["NightVision Potion + Black Concrete = Potion of Darkness (30 seconds)", "Note: Darkness prevents the drinker from sprinting!"] [brewing.haste] name = "Bottled Haste" -description = "When Efficiency is not enough" +description = "Unlocks brewing Potions of Haste for faster mining, when Efficiency is not enough." lore1 = "Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)" lore2 = "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)" lore = ["Speed Potion + Amethyst Shard = Potion of Haste (60 seconds)", "Speed Potion + Amethyst Block = Potion of Haste-2 (30 seconds)"] [brewing.absorption] name = "Bottled Absorption" -description = "Harden the body!" +description = "Unlocks brewing Potions of Absorption for temporary bonus hearts." lore1 = "Instant Heal + Quartz = Potion of Absorption (60 seconds)" lore2 = "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)" lore = ["Instant Heal + Quartz = Potion of Absorption (60 seconds)", "Instant Heal + Quartz Block = Potion of Absorption-2 (30 seconds)"] [brewing.fatigue] name = "Bottled Fatigue" -description = "Weaken the body!" +description = "Unlocks brewing Potions of Mining Fatigue, which slow a target's digging and attacks." lore1 = "Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)" lore2 = "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)" lore = ["Weakness Potion + Slime Ball = Potion of Fatigue (30 seconds)", "Weakness Potion + Slime Block = Potion of Fatigue-2 (15 seconds)"] [brewing.hunger] name = "Bottled Hunger" -description = "Feed the Insatiable!" +description = "Unlocks brewing Potions of Hunger, which drain a target's food." lore1 = "Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)" lore2 = "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)" lore = ["Awkward Potion + Rotten Flesh = Potion of Hunger (30 seconds)", "Weakness Potion + Rotten Flesh = Potion of Hunger-3 (15 seconds)"] [brewing.nausea] name = "Bottled Nausea" -description = "You make me sick!" +description = "Unlocks brewing Potions of Nausea, which warp a target's vision." lore1 = "Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)" lore2 = "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)" lore = ["Awkward Potion + Brown Mushroom = Potion of Nausea (16 seconds)", "Awkward Potion + Crimson Fungus = Potion of Nausea-2 (8 seconds)"] [brewing.blindness] name = "Bottled Blindness" -description = "You're a horrible person..." +description = "Unlocks brewing Potions of Blindness, which shroud a target's sight." lore1 = "Awkward Potion + Ink sack = Potion of Blindness (30 seconds)" lore2 = "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)" lore = ["Awkward Potion + Ink sack = Potion of Blindness (30 seconds)", "Awkward Potion + Glowing Ink Sack = Potion of Blindness-2 (15 seconds)"] [brewing.resistance] name = "Bottled Resistance" -description = "Fortification at its finest!" +description = "Unlocks brewing Potions of Resistance, which reduce incoming damage." lore1 = "Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)" lore2 = "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)" lore = ["Awkward Potion + Iron Ingot = Potion of Resistance (60 seconds)", "Awkward Potion + Iron Block = Potion of Resistance-2 (30 seconds)"] [brewing.health_boost] name = "Bottled Life" -description = "When Maximum health is not enough..." +description = "Unlocks brewing Potions of Health Boost for extra maximum hearts." lore1 = "Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)" lore2 = "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)" lore = ["Instant-Healing Potion + Golden Apple = Potion of Health Boost (120 seconds)", "Instant-Healing Potion + Enchanted Golden Apple = Potion of Health Boost-2 (120 seconds)"] [brewing.decay] name = "Bottled Decay" -description = "Who knew Detritus would be so useful?" +description = "Unlocks brewing Potions of Wither, which afflict a target with decay." lore1 = "Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)" lore2 = "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)" lore = ["Weakness Potion + Poisonous Potato = Potion of Wither (16 seconds)", "Weakness Potion + Crimson Roots = Potion of Wither-2 (8 seconds)"] [brewing.saturation] name = "Bottled Saturation" -description = "Ya know... Im not even Hungry..." +description = "Unlocks brewing Potions of Saturation, which restore hunger." lore1 = "Regen Potion + Baked Potato = Potion of Saturation" lore2 = "Regen Potion + Hay Bale = Potion of Saturation-2" lore = ["Regen Potion + Baked Potato = Potion of Saturation", "Regen Potion + Hay Bale = Potion of Saturation-2"] @@ -1562,7 +1562,7 @@ lore = ["Recipe: 5 Leather:"] [blocking.multi_armor] name = "Multi-Armor" -description = "Bind Elytras to Armor" +description = "Bind an elytra to your chestplate and swap between them on the fly." lore1 = "This Is an amazing skill for traveling." lore2 = "Dynamically merge and change Armor/Elytra on the Fly!" lore3 = "To merge, shift click an item over another in your inventory." @@ -1636,7 +1636,7 @@ lore = ["Just toss it(rotten flesh) on the campfire!"] [crafting.backpacks] name = "A Boutilier's Backpacks!" -description = "This just Brings the Mojang Bundle into the game!" +description = "Unlocks crafting bundles to carry mixed item stacks in a single slot." lore1 = "You need to be in Survival to use this" lore2 = "XLX : Leather, Lead, Leather" lore3 = "XSX : Leather, Barrel Box, Leather" @@ -1645,10 +1645,11 @@ lore = ["You need to be in Survival to use this", "XLX : Leather, Lead, Leather" [crafting.stations] name = "Portable Tables!" -description = "Use a table in the palm of your hand!" +description = "Click the air while holding an anvil, crafting table, grindstone, stonecutter, cartography table, or loom to open it without placing it. Each open costs hunger." lore2 = "ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!" lore3 = "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom" lore = ["ANY ITEMS THAT YOU FORGET IN THE TABLE WHEN CLOSED ARE LOST FOREVER!", "Valid tables: Anvil, Crafting, Grindstone, Cartography, Stone-Cutter, Loom"] +lore4 = "Hunger consumed per portable station opened" [crafting.skulls] name = "Craftable skulls!" @@ -1682,11 +1683,13 @@ lore = ["Melee attacks apply stacking slowness", "PvE slowness duration cap", "P [chronos.instant_recall] name = "Instant Recall" -description = "Left or right click with a clock in hand to rewind to a recent snapshot with health and hunger restored." +description = "Left or right click with a clock in hand to rewind to a recent snapshot with health and hunger restored. Costs the clock and half your remaining health, but never kills you." lore1 = "Rewind duration" lore2 = "Cooldown" lore3 = "No inventory rollback" lore = ["Rewind duration", "Cooldown", "No inventory rollback"] +lore_cost_clock = "Consumes the clock on recall" +lore_cost_health = "of remaining health lost on recall" [chronos.time_bomb] name = "Time Bomb" @@ -1733,7 +1736,7 @@ lore = ["Triggers only at critical health (<= 5 hearts) once per 15 seconds", " [discovery.villager] name = "Villager Attraction" -description = "Allows for you to get Better trades with villagers!" +description = "Get better trades with villagers, at the cost of XP per interaction." lore1 = "This consumes XP per interaction with Villagers" lore2 = "Chance Per interaction to consume XP, and enhance trades" lore3 = "required XP drain per Interaction" @@ -1757,19 +1760,20 @@ lore = ["Bonus Reward Chance", "Rare Reward Chance", "Reward Cooldown"] [discovery.cartographer_pulse] name = "Cartographer Pulse" -description = "Sneak-right-click with a compass to lock your compass toward a nearby structure." +description = "Sneak-right-click with a compass to lock it toward a nearby structure. Each pulse costs hunger." lore1 = "Structure Search Range" lore2 = "Pulse Cooldown" lore = ["Structure Search Range", "Pulse Cooldown"] # enchanting +lore_cost_hunger = "Hunger cost per pulse" [enchanting] [enchanting.lapis_return] name = "Lapis Return" -description = "At the cost of 1 more level of XP, and has a chance to give you free lapis in return" -lore1 = "For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis" -lore = ["For every level, it increases the cost of enchanting, by 1, but can return upwards of 3 Lapis"] +description = "Enchanting at a table has a chance to refund lapis, more at higher levels." +lore1 = "Chance to drop free lapis when you enchant; the amount scales with your level" +lore = ["Chance to drop free lapis when you enchant; the amount scales with your level"] [enchanting.quick_enchant] name = "Quick-Click Enchant" @@ -1794,10 +1798,11 @@ lore = ["Anvil Cost Reduction"] [enchanting.offer_reroll] name = "Offer Reroll" -description = "Sneak-right-click an enchanting table to reroll offers for lapis and XP." +description = "Sneak-right-click an enchanting table to reroll its offers. Each reroll costs lapis and XP levels." lore1 = "Reroll Cooldown" lore2 = "Lapis Cost" lore = ["Reroll Cooldown", "Lapis Cost"] +lore_cost_xp = "XP Level Cost" [enchanting.bookshelf_attunement] name = "Bookshelf Attunement" @@ -1825,7 +1830,7 @@ lore = ["Gain Haste while excavating", "x Levels of haste when you start mining [excavation.spelunker] name = "Super-Seeing Spelunker!" -description = "See Ores with your eyes, but through the ground!" +description = "Hold glow berries in your main hand to see ores through the ground." lore1 = "Ore in your offhand, Glowberries in your main hand, and Sneak!" lore2 = "Block Range: " lore3 = "Consumes Glowberry on use" @@ -1844,15 +1849,15 @@ name = "Shovel Drop-To-Inventory" [excavation.omni_tool] name = "OMNI - T.O.O.L." -description = "Tackle's overdesigned opulent Leatherman" -lore1 = "Probably the most powerful of many allows you to" -lore2 = "dynamically merge and change tools on the fly, based on your needs." +description = "Merge your tools into one omni-tool that swaps to the right tool for the job. Shift-click one tool onto another in your inventory to merge; sneak-drop to disassemble." +lore1 = "Merges your tools into a single omni-tool that" +lore2 = "dynamically swaps to the right tool on the fly, based on your needs." lore3 = "To merge, shift click an item over another in your inventory." lore4 = "To unbind tools, Sneak-Drop the item, and it will disassemble." -lore5 = "You can't break tools in this leatherman but you can't use broken tools" +lore5 = "Merged tools never break, but tools at zero durability can't be used" lore6 = "total merge-able items." lore7 = "You could use five or six tools, or just one!" -lore = ["Probably the most powerful of many allows you to", "dynamically merge and change tools on the fly, based on your needs.", "To merge, shift click an item over another in your inventory.", "To unbind tools, Sneak-Drop the item, and it will disassemble.", "You can't break tools in this leatherman but you can't use broken tools", "total merge-able items.", "You could use five or six tools, or just one!"] +lore = ["Merges your tools into a single omni-tool that", "dynamically swaps to the right tool on the fly, based on your needs.", "To merge, shift click an item over another in your inventory.", "To unbind tools, Sneak-Drop the item, and it will disassemble.", "Merged tools never break, but tools at zero durability can't be used", "total merge-able items.", "You could use five or six tools, or just one!"] # herbalism [herbalism] @@ -1969,14 +1974,14 @@ lore1 = "Max Damage" lore = ["Max Damage"] [hunter.penalty] -name = "" -description = "" -lore1 = "You will Gain Poison stacks if you run out of hunger" -lore = ["You will Gain Poison stacks if you run out of hunger"] +name = "Hunter's Penalty" +description = "Hunter boons drain hunger; running out of hunger builds Poison stacks." +lore1 = "Poison stacks if you run out of hunger" +lore = ["Poison stacks if you run out of hunger"] [hunter.drop_to_inventory] name = "Items Drop-To-Inventory" -description = "When you Kill something / Break a block With a sword it teleports the drops into your inventory" +description = "Kills and blocks broken with a sword in hand send their drops straight into your inventory." lore1 = "Whenever an item is dropped from a mob/block you break it goes into your inventory if it can." lore = ["Whenever an item is dropped from a mob/block you break it goes into your inventory if it can."] @@ -2064,7 +2069,7 @@ lore = ["Gain passive strength when struck", "x Strength stacks for a 3 seconds [nether.skull_toss] name = "Wither Skull Throw" description1 = "Unleash your inner Wither by using" -description2 = "someones" +description2 = "someone's" description3 = "head." lore1 = "Seconds of cooldown between skull tosses." lore2 = "Using Wither Skull: Toss a " @@ -2135,7 +2140,7 @@ lore = ["Chance to Drop", "Tool Wear"] [pickaxe.drop_to_inventory] name = "Pickaxe Drop-To-Inventory" -description = "When you break a block it teleports the item into your inventory" +description = "Blocks you break send their drops straight into your inventory." lore1 = "Whenever an item is dropped from a block you break it goes into your inventory if it can." lore = ["Whenever an item is dropped from a block you break it goes into your inventory if it can."] @@ -2235,37 +2240,38 @@ lore = ["Max Ricochets", "Speed Bonus Per Ricochet", "Bonus Damage Per Ricochet" [rift.remote_access] name = "Remote Access" -description = "Pull from the void, and get into a marked container." -lore1 = "Enderpearl + Compass = Reliquary Portkey" +description = "Craft a Reliquary Portkey (ender pearl + compass), bind it to a container, and use it to open that container from anywhere." +lore1 = "Ender Pearl + Compass = Reliquary Portkey" lore2 = "This item allows you to access containers remotely" lore3 = "Once crafted look at item to see usage" notcontainer = "That's not a container" -lore = ["Enderpearl + Compass = Reliquary Portkey", "This item allows you to access containers remotely", "Once crafted look at item to see usage"] +lore = ["Ender Pearl + Compass = Reliquary Portkey", "This item allows you to access containers remotely", "Once crafted look at item to see usage"] [rift.blink] name = "Rift Blink" -description = "Short ranged instant teleportation, Just a blink away!" +description = "Short-range instant teleport. Double-tap jump while sprinting, right-click while sprinting, or click with an ender pearl in hand to blink. Each blink has a chance to consume an ender pearl." lore1 = "Blocks on blink (2x Vertical)" lore2 = "While Sprinting: Double tap Jump to " lore3 = "Blink" lore = ["Blocks on blink (2x Vertical)", "While Sprinting: Double tap Jump to ", "Blink"] +lore_cost_pearl = "chance to consume an ender pearl on blink" [rift.chest] name = "Easy Enderchest" -description = "Open an enderchest by Left-clicking it in your hand." -lore1 = "Click an Enderchest in your hand to open (Just dont place it)" -lore = ["Click an Enderchest in your hand to open (Just dont place it)"] +description = "Click while holding an ender chest to open your ender chest without placing it." +lore1 = "Click an Ender Chest in your hand to open it (just don't place it)" +lore = ["Click an Ender Chest in your hand to open it (just don't place it)"] [rift.descent] name = "Anti-Levitation" -description = "Are you tired of being stuck in the air? This is the skill for you!" +description = "Tap sneak while levitating to cancel Levitation and drift down gently with Slow Falling." lore1 = "Just Sneak to descend, and you will fall at a less than normal rate!" lore2 = "Cooldown:" lore = ["Just Sneak to descend, and you will fall at a less than normal rate!", "Cooldown:"] [rift.gate] name = "Rift Gate" -description = "Teleport to a marked location." +description = "Sneak-left-click with an Eye of Ender to bind it to your location, then right-click to teleport back after a 5 second channel." lore1 = "CRAFTING: Emerald + Amethyst shard + Ender Pearl" lore2 = "Read before using!" lore3 = "5s delay, " @@ -2281,9 +2287,9 @@ lore = ["+ Passive: Provides resistance when you use rift abilities, or Ender It [rift.visage] name = "Rift Visage" -description = "Prevents Endermen from becoming aggressive if you have Enderpearls in your inventory." -lore1 = "Endermen will not become aggressive if you have Enderpearls in your inventory." -lore = ["Endermen will not become aggressive if you have Enderpearls in your inventory."] +description = "Prevents Endermen from becoming aggressive if you have Ender Pearls in your inventory." +lore1 = "Endermen will not become aggressive if you have Ender Pearls in your inventory." +lore = ["Endermen will not become aggressive if you have Ender Pearls in your inventory."] [rift.void_magnet] name = "Void Magnet" @@ -2365,7 +2371,7 @@ lore = ["Minimum Depth Requirement", "Depth Damage Reduction", "Mining Fatigue R [stealth.ghost_armor] name = "Ghost's Armor" -description = "Slow building armor when not taking damage, Lasts for 1 hit" +description = "Slowly builds bonus armor while you avoid damage; it is consumed by the next hit." lore1 = "Max Armor" lore2 = "Speed" lore = ["Max Armor", "Speed"] @@ -2392,7 +2398,7 @@ lore = ["Sneaking Speed"] [stealth.ender_veil] name = "Enderveil" -description = "No more Pumpkins to prevent Enderman attacks" +description = "Look at Endermen freely - prevents Enderman aggression without wearing a pumpkin." lore1 = "Prevent enderman attacks while sneaking" lore2 = "Prevent all enderman attacks" lore = ["Prevent enderman attacks while sneaking", "Prevent all enderman attacks"] @@ -2499,10 +2505,11 @@ lore = ["Aura Radius", "Aura Strength"] [taming.beast_recall] name = "Beast Recall" -description = "Sneak-right-click with a lead to recall your nearest tamed companion to a safe nearby spot." +description = "Sneak-right-click with a lead to recall your nearest tamed companion to a safe nearby spot. Each recall costs hunger." lore1 = "Recall Radius" lore2 = "Recall Cooldown" lore = ["Recall Radius", "Recall Cooldown"] +lore_cost_hunger = "Hunger cost per recall" [taming.shared_pain] name = "Shared Pain" @@ -2578,7 +2585,7 @@ lore = ["x Damage at 0 armor", "PerLevel Bonus Damage"] [unarmed.power] name = "Unarmed Power" -description = "Improved Unarmed Damage" +description = "Your bare-handed strikes deal more damage." lore1 = "Damage" lore = ["Damage"] @@ -3788,3 +3795,630 @@ description = "Charge into 300 enemies" [advancement.challenge_unarmed_charge_kills_100] title = "Unstoppable Force" description = "Kill 100 mobs with battering charge" + +[excavation.tunneler] +name = "Tunneler" +description = "Sneak while digging soft blocks to carve a whole plane at once." +lore1 = "Bonus Blocks Per Dig" +lore2 = "Extra Durability Per Bonus Block" +lore = ["Bonus Blocks Per Dig", "Extra Durability Per Bonus Block"] + +[excavation.treasure_hunter] +name = "Treasure Hunter" +description = "Digging sand, gravel, mud, or clay can unearth archaeology treasure." +lore1 = "Treasure Chance" +lore2 = "Treasures roll from a weighted archaeology table" +lore = ["Treasure Chance", "Treasures roll from a weighted archaeology table"] + +[excavation.soft_fall] +name = "Soft Fall" +description = "Landing on soft diggable ground reduces fall damage, up to full negation." +lore1 = "Fall Damage Reduction" +lore2 = "Applies when landing on dirt, sand, gravel, clay, mud, or soul sand" +lore = ["Fall Damage Reduction", "Applies when landing on dirt, sand, gravel, clay, mud, or soul sand"] + +[excavation.earth_mover] +name = "Earth Mover" +description = "Sneak-right-click the air with a shovel to fling a wave of earth that knocks back and slows hostile mobs. Each wave costs hunger." +lore1 = "Wave Radius" +lore2 = "Knockback Force" +lore3 = "Slow Duration" +lore4 = "Wave Cooldown" +lore = ["Wave Radius", "Knockback Force", "Slow Duration", "Wave Cooldown", "Hunger Cost"] +lore5 = "Hunger Cost" + +[excavation.dowsing] +name = "Dowsing" +description = "Sneaking with a shovel pings the nearest hidden cave, water, or lava pocket with a directional trail." +lore1 = "Dowsing Range" +lore2 = "Dowsing Cooldown" +lore = ["Dowsing Range", "Dowsing Cooldown"] + +[excavation.burrow] +name = "Burrow" +description = "Sneak-right-click soft ground with a shovel to rapidly dig straight down, stopping before hazards. Each burrow costs hunger and tool durability." +lore1 = "Max Burrow Depth" +lore2 = "Durability Per Block" +lore3 = "Burrow Cooldown" +lore = ["Max Burrow Depth", "Durability Per Block", "Burrow Cooldown", "Hunger Cost"] +lore4 = "Hunger Cost" + +[excavation.grave_digger] +name = "Grave Digger" +description = "Digging earthen ground can unearth bone loot, and rarely disturbs a hostile grave." +lore1 = "Bone Loot Chance" +lore2 = "Disturbed Grave Chance" +lore = ["Bone Loot Chance", "Disturbed Grave Chance"] + +[excavation.mudlark] +name = "Mudlark" +description = "Bonus drops from muddy blocks, plus haste while digging in water or rain." +lore1 = "Bonus Drop Chance" +lore2 = "x Levels of haste while digging wet" +lore = ["Bonus Drop Chance", "x Levels of haste while digging wet"] + +[advancement.challenge_excavation_tunneler_10k] +title = "Boring Machine" +description = "Tunnel 10,000 bonus blocks" + +[advancement.challenge_excavation_treasure_500] +title = "Amateur Archaeologist" +description = "Unearth 500 treasures" + +[advancement.challenge_excavation_softfall_1k] +title = "Pillow Earth" +description = "Soak 1,000 fall damage on soft ground" + +[advancement.challenge_excavation_earthmover_250] +title = "Landslide" +description = "Unleash 250 earth waves" + +[advancement.challenge_excavation_dowsing_200] +title = "Water Witcher" +description = "Dowse 200 hidden pockets" + +[advancement.challenge_excavation_burrow_100] +title = "Going Down" +description = "Dig 100 burrows" + +[advancement.challenge_excavation_gravedigger_300] +title = "Rest In Pieces" +description = "Unearth 300 bone troves" + +[advancement.challenge_excavation_mudlark_1k] +title = "Riverbank Forager" +description = "Collect 1,000 bonus muddy drops" + +# chronos (new adaptations) + +[chronos.stasis_field] +name = "Stasis Field" +description = "Sneak and right click with an amethyst shard to deploy a stasis bubble that freezes projectiles in midair and locks down mobs inside. Consumes the shard on cast." +lore1 = "Stasis bubble radius" +lore2 = "Stasis bubble duration" +lore3 = "Cooldown" +lore4 = "Sneak + Right Click with an Amethyst Shard" +lore = ["Stasis bubble radius", "Stasis bubble duration", "Cooldown", "Sneak + Right Click with an Amethyst Shard"] +lore_cost_shard = "Consumes the amethyst shard on cast" + +[chronos.rewind] +name = "Rewind" +description = "Sneak and swap hands to mark a moment in time, then do it again within the window to snap back with health and hunger restored. Each rewind costs hunger." +lore1 = "Rewind window" +lore2 = "Cooldown after a rewind" +lore3 = "Sneak + Swap Hands to mark, repeat to rewind" +lore = ["Rewind window", "Cooldown after a rewind", "Sneak + Swap Hands to mark, repeat to rewind"] +lore_cost_hunger = "Hunger cost per rewind" + +[chronos.borrowed_time] +name = "Borrowed Time" +description = "A portion of incoming damage is deferred and quietly drained back once per second over the following seconds." +lore1 = "Damage deferred" +lore2 = "Payback window" +lore3 = "Deferred damage cannot be deferred again" +lore = ["Damage deferred", "Payback window", "Deferred damage cannot be deferred again"] + +[chronos.overtime] +name = "Overtime" +description = "Beneficial potion effects applied to you last longer, scaled by adaptation level." +lore1 = "Extra effect duration" +lore2 = "Maximum bonus per effect" +lore3 = "Only beneficial effects are extended" +lore = ["Extra effect duration", "Maximum bonus per effect", "Only beneficial effects are extended"] + +[chronos.accelerate] +name = "Accelerate" +description = "Passively accelerate time around you, occasionally growing nearby crops and fast-forwarding furnaces, smokers, blast furnaces, and brewing stands." +lore1 = "Aura radius" +lore2 = "Crop growth chance per pulse" +lore3 = "of a cook or brew fast-forwarded per pulse hit" +lore = ["Aura radius", "Crop growth chance per pulse", "of a cook or brew fast-forwarded per pulse hit"] + +[chronos.hourglass_guard] +name = "Hourglass Guard" +description = "A killing blow instead leaves you at half a heart, granting brief invulnerability and slowing nearby enemies, on a long cooldown." +lore1 = "Invulnerability after a save" +lore2 = "Cooldown" +lore3 = "Nearby enemies are briefly slowed" +lore = ["Invulnerability after a save", "Cooldown", "Nearby enemies are briefly slowed"] + +[chronos.pocket_watch] +name = "Pocket Watch" +description = "Sneak while falling with a clock in your inventory to drift in slow motion for a limited, level scaled duration each airtime." +lore1 = "Slow fall budget per airtime" +lore2 = "Budget refills on landing" +lore3 = "Hold Sneak while falling with a Clock in your inventory" +lore = ["Slow fall budget per airtime", "Budget refills on landing", "Hold Sneak while falling with a Clock in your inventory"] + +[chronos.deja_vu] +name = "Deja Vu" +description = "Your body remembers recent pain; taking the same kind of damage again within a short window hurts noticeably less." +lore1 = "Repeat damage absorbed" +lore2 = "Damage memory window" +lore3 = "The memory refreshes on every hit of the same kind" +lore = ["Repeat damage absorbed", "Damage memory window", "The memory refreshes on every hit of the same kind"] + +[advancement.challenge_chronos_stasis_50] +title = "Suspended Animation" +description = "Deploy 50 stasis fields" + +[advancement.challenge_chronos_stasis_500] +title = "Master of Stillness" +description = "Deploy 500 stasis fields" + +[advancement.challenge_chronos_rewind_50] +title = "Do Over" +description = "Complete 50 rewinds" + +[advancement.challenge_chronos_rewind_500] +title = "Unstuck in Time" +description = "Complete 500 rewinds" + +[advancement.challenge_chronos_borrowed_2500] +title = "Pay It Later" +description = "Defer 2,500 damage to the future" + +[advancement.challenge_chronos_overtime_1k] +title = "Overtime Pay" +description = "Gain 1,000 bonus seconds of potion duration" + +[advancement.challenge_chronos_accelerate_1k] +title = "Fast Forward" +description = "Accelerate 1,000 blocks" + +[advancement.challenge_chronos_hourglass_10] +title = "Out of Time" +description = "Cheat death 10 times with the hourglass" + +[advancement.challenge_chronos_pocket_watch_500] +title = "Falling Slowly" +description = "Drift through 500 seconds of slowed falls" + +[advancement.challenge_chronos_deja_vu_500] +title = "Haven't We Met?" +description = "Absorb 500 familiar damage through deja vu" + +[unarmed.disarm] +name = "Disarm" +description = "Bare-hand hits can knock the target's held item to the ground." +lore1 = "Disarm Chance" +lore2 = "Per-Target Cooldown" +lore = ["Disarm Chance", "Per-Target Cooldown"] + +[unarmed.flurry] +name = "Flurry" +description = "Rapid consecutive bare-hand hits build flurry stacks that add bonus damage." +lore1 = "Max Flurry Stacks" +lore2 = "Damage Per Stack" +lore3 = "Flurry Window" +lore = ["Max Flurry Stacks", "Damage Per Stack", "Flurry Window"] + +[unarmed.pressure_point] +name = "Pressure Point" +description = "Bare-hand hits apply stacking slowness, with weakness at higher levels." +lore1 = "Max Slowness Stacks" +lore2 = "Max Weakness Stacks" +lore3 = "Weakness unlocks at higher levels" +lore = ["Max Slowness Stacks", "Max Weakness Stacks", "Weakness unlocks at higher levels"] + +[unarmed.shockwave_clap] +name = "Shockwave Clap" +description = "Sneak and punch the air to clap a shockwave that knocks back enemies in a cone. Each clap costs hunger." +lore1 = "Shockwave Range" +lore2 = "Knockback Force" +lore3 = "Clap Cooldown" +lore = ["Shockwave Range", "Knockback Force", "Clap Cooldown", "Hunger Cost"] +lore4 = "Hunger Cost" + +[unarmed.iron_fists] +name = "Iron Fists" +description = "Bare fists hit harder and punch through soft blocks faster." +lore1 = "Flat Punch Damage" +lore2 = "Soft Block Punch Haste" +lore = ["Flat Punch Damage", "Soft Block Punch Haste"] + +[unarmed.grapple] +name = "Grapple" +description = "Sneak-punch a mob to grab it, then hurl it where you look. Each throw adds exhaustion." +lore1 = "Hurl Force" +lore2 = "Grapple Cooldown" +lore3 = "Hit again or release sneak to hurl" +lore = ["Hurl Force", "Grapple Cooldown", "Hit again or release sneak to hurl", "Exhaustion per Throw"] +lore4 = "Exhaustion per Throw" + +[unarmed.second_wind] +name = "Second Wind" +description = "Bare-hand kills restore hunger and grant a short regeneration burst." +lore1 = "Hunger Restored" +lore2 = "Regeneration Duration" +lore = ["Hunger Restored", "Regeneration Duration"] + +[unarmed.meditation] +name = "Meditation" +description = "Meditate while sneaking, still, and empty-handed to slowly build absorption hearts." +lore1 = "Max Absorption" +lore2 = "Absorption Per Pulse" +lore3 = "Combat Lockout" +lore = ["Max Absorption", "Absorption Per Pulse", "Combat Lockout"] + +[advancement.challenge_unarmed_disarm_100] +title = "Butterfingers" +description = "Disarm 100 enemies with your bare hands" + +[advancement.challenge_unarmed_disarm_1k] +title = "Weapon Repossessor" +description = "Disarm 1,000 enemies with your bare hands" + +[advancement.challenge_unarmed_flurry_1k] +title = "Hands of Fury" +description = "Land 1,000 flurry hits" + +[advancement.challenge_unarmed_flurry_10k] +title = "Hurricane Hands" +description = "Land 10,000 flurry hits" + +[advancement.challenge_unarmed_pressure_500] +title = "Nerve Striker" +description = "Land 500 pressure point strikes" + +[advancement.challenge_unarmed_pressure_5k] +title = "Anatomy Scholar" +description = "Land 5,000 pressure point strikes" + +[advancement.challenge_unarmed_clap_250] +title = "Thunderclap" +description = "Knock back 250 enemies with shockwave claps" + +[advancement.challenge_unarmed_clap_2500] +title = "Sonic Boom" +description = "Knock back 2,500 enemies with shockwave claps" + +[advancement.challenge_unarmed_iron_1k] +title = "Knuckles of Steel" +description = "Land 1,000 iron fist punches" + +[advancement.challenge_unarmed_iron_10k] +title = "Living Anvil" +description = "Land 10,000 iron fist punches" + +[advancement.challenge_unarmed_grapple_100] +title = "Suplex City" +description = "Hurl 100 grabbed mobs" + +[advancement.challenge_unarmed_grapple_1k] +title = "Mob Launcher" +description = "Hurl 1,000 grabbed mobs" + +[advancement.challenge_unarmed_second_wind_100] +title = "Battle Snack" +description = "Trigger second wind 100 times" + +[advancement.challenge_unarmed_second_wind_1k] +title = "Endless Stamina" +description = "Trigger second wind 1,000 times" + +[advancement.challenge_unarmed_meditate_500] +title = "Inner Peace" +description = "Build 500 absorption health through meditation" + +[advancement.challenge_unarmed_meditate_5k] +title = "Transcendence" +description = "Build 5,000 absorption health through meditation" + +[architect.scaffolder] +name = "Scaffolder" +description = "Sneak-place blocks as temporary scaffolds that dissolve on their own and refund the block to you!" +lore1 = "Sneak-placed blocks dissolve automatically" +lore2 = "Seconds before a scaffold dissolves and refunds" +lore = ["Sneak-placed blocks dissolve automatically", "Seconds before a scaffold dissolves and refunds"] + +[architect.supply_line] +name = "Supply Line" +description = "When the stack in your hand runs out, it refills automatically from shulker boxes or bundles in your inventory!" +lore1 = "Hand auto-refills from shulkers and bundles" +lore2 = "Refills per minute" +lore = ["Hand auto-refills from shulkers and bundles", "Refills per minute"] + +[architect.steady_hands] +name = "Steady Hands" +description = "While bridging over open air you take no knockback, shrug off falls, and place with a steadier rhythm!" +lore1 = "No knockback while bridging" +lore2 = "Blocks of fall damage shielded" +lore = ["No knockback while bridging", "Blocks of fall damage shielded"] + +[architect.chalk_line] +name = "Chalk Line" +description = "Sneak-right-click a block while holding any block to snap a straight particle guide line along the axis you face. Do it again to clear it!" +lore1 = "Particle guide line for aligning builds" +lore2 = "Seconds the guide line stays visible" +lore = ["Particle guide line for aligning builds", "Seconds the guide line stays visible"] + +[architect.demolition] +name = "Mason's Eraser" +description = "Erase your own recent placements near-instantly and the block pops right back as a drop!" +lore1 = "Your fresh placements break near-instantly" +lore2 = "Seconds a placement counts as fresh" +lore = ["Your fresh placements break near-instantly", "Seconds a placement counts as fresh"] + +[architect.stonecutter_savant] +name = "Stonecutter Savant" +description = "Sneak-punch the air with an empty hand to open a stonecutter wherever you are, as long as you carry a stonecutter!" +lore1 = "Portable stonecutter on demand" +lore2 = "Seconds of cooldown between uses" +lore3 = "Requires a stonecutter item in your inventory" +lore4 = "Requires a stonecutter in your offhand" +lore = ["Portable stonecutter on demand", "Seconds of cooldown between uses", "Requires a stonecutter item in your inventory", "Requires a stonecutter in your offhand"] + +[advancement.challenge_architect_scaffolder_500] +title = "Temporary Measures" +description = "Place 500 temporary scaffolds" + +[advancement.challenge_architect_scaffolder_5k] +title = "Master Rigger" +description = "Place 5,000 temporary scaffolds" + +[advancement.challenge_architect_supply_line_100] +title = "Keep It Coming" +description = "Auto-refill your hand 100 times" + +[advancement.challenge_architect_supply_line_1k] +title = "Quartermaster" +description = "Auto-refill your hand 1,000 times" + +[advancement.challenge_architect_steady_hands_500] +title = "Nerves of Steel" +description = "Place 500 blocks while bridging" + +[advancement.challenge_architect_steady_hands_5k] +title = "Sky Walker" +description = "Place 5,000 blocks while bridging" + +[advancement.challenge_architect_chalk_line_50] +title = "Straight and True" +description = "Snap 50 chalk lines" + +[advancement.challenge_architect_chalk_line_500] +title = "Surveyor" +description = "Snap 500 chalk lines" + +[advancement.challenge_architect_demolition_500] +title = "Controlled Demolition" +description = "Demolish 500 of your own placements" + +[advancement.challenge_architect_demolition_5k] +title = "Wrecking Crew" +description = "Demolish 5,000 of your own placements" + +[advancement.challenge_architect_stonecutter_savant_50] +title = "Cutting Corners" +description = "Open 50 portable stonecutters" + +[advancement.challenge_architect_stonecutter_savant_500] +title = "Master Mason" +description = "Open 500 portable stonecutters" + +[pickaxe.tunnel_bore] +name = "Tunnel Bore" +description = "Sneak and mine stone-type blocks to bore out a whole tunnel face at once" +lore1 = "Sneak, and mine STONE" +lore2 = "tunnel face bored per block" +lore3 = "extra durability per bonus block" +lore = ["Sneak, and mine STONE", "tunnel face bored per block", "extra durability per bonus block"] + +[pickaxe.deep_core] +name = "Deep Core" +description = "Mining deepslate grants Haste so it digs like normal stone" +lore1 = "Mine DEEPSLATE to gain Haste" +lore2 = "Haste level while mining deepslate" +lore = ["Mine DEEPSLATE to gain Haste", "Haste level while mining deepslate"] + +[pickaxe.obsidian_rush] +name = "Obsidian Rush" +description = "Mining obsidian with a diamond or netherite pickaxe grants a strong Haste burst" +lore1 = "Mine OBSIDIAN with a diamond+ pickaxe" +lore2 = "Haste level while mining obsidian" +lore3 = "Also works on crying obsidian!" +lore = ["Mine OBSIDIAN with a diamond+ pickaxe", "Haste level while mining obsidian", "Also works on crying obsidian!"] + +[pickaxe.unbreakable_pact] +name = "Unbreakable Pact" +description = "Your pickaxe refuses to break, surviving at 1 durability instead" +lore1 = "Pickaxes never break, stopping at 1 durability" +lore2 = "chance to ignore durability loss entirely" +lore = ["Pickaxes never break, stopping at 1 durability", "chance to ignore durability loss entirely"] + +[pickaxe.repair_rhythm] +name = "Repair Rhythm" +description = "Sustained mining has a chance to restore durability to your pickaxe" +lore1 = "Each broken block can restore 1-2 durability" +lore2 = "chance to repair per broken block" +lore = ["Each broken block can restore 1-2 durability", "chance to repair per broken block"] + +[pickaxe.gem_polish] +name = "Gem Polish" +description = "Mining gem ores grants bonus XP orbs and a chance for an extra gem" +lore1 = "Mine diamond, emerald, lapis or amethyst" +lore2 = "chance for an extra matching gem" +lore3 = "bonus XP per gem ore mined" +lore = ["Mine diamond, emerald, lapis or amethyst", "chance for an extra matching gem", "bonus XP per gem ore mined"] + +[pickaxe.stone_skin] +name = "Stone Skin" +description = "Breaking stone-type blocks builds short-lived stacking damage resistance" +lore1 = "Mine stone to build Stone Skin stacks" +lore2 = "maximum Resistance level" +lore = ["Mine stone to build Stone Skin stacks", "maximum Resistance level"] + +[advancement.challenge_pickaxe_tunnelbore_10k] +title = "Boring Machine" +description = "Bore 10,000 bonus blocks while tunneling" + +[advancement.challenge_pickaxe_deepcore_5k] +title = "Core Sampler" +description = "Mine 5,000 deepslate blocks with Deep Core" + +[advancement.challenge_pickaxe_obsidianrush_1k] +title = "Black Gold" +description = "Mine 1,000 obsidian blocks with Obsidian Rush" + +[advancement.challenge_pickaxe_pact_100] +title = "Pact Keeper" +description = "Save your pickaxe from breaking 100 times" + +[advancement.challenge_pickaxe_rhythm_5k] +title = "Perpetual Pick" +description = "Restore 5,000 durability through Repair Rhythm" + +[advancement.challenge_pickaxe_gempolish_500] +title = "Lapidary" +description = "Polish out 500 extra gems" + +[advancement.challenge_pickaxe_stoneskin_10k] +title = "Hide of Granite" +description = "Build 10,000 Stone Skin stacks" + +[tragoul.corpse_explosion] +name = "Corpse Explosion" +description = "Mobs you kill detonate in a blood nova that damages nearby hostile mobs!" +lore1 = "Kills detonate the corpse, damaging nearby hostile mobs" +lore2 = "Nova Radius" +lore3 = "of the victim's max health added as nova damage" +lore = ["Kills detonate the corpse, damaging nearby hostile mobs", "Nova Radius", "of the victim's max health added as nova damage"] + +[tragoul.soul_siphon] +name = "Soul Siphon" +description = "Your melee hits drain life from your victims, healing you for part of the damage dealt!" +lore1 = "of melee damage dealt returned as health" +lore2 = "max health restored per second" +lore = ["of melee damage dealt returned as health", "max health restored per second"] + +[tragoul.skeletal_servant] +name = "Skeletal Servant" +description = "Sneak and right-click with bones to raise temporary skeletal servants, one living servant per level! Servants spawn with level-scaled random gear, inherit your other Tragoul perks, and immediately hunt any player who attacked you in the last few seconds. Summoning at the cap recycles your oldest servant. Consumes bones on summon, fewer at higher levels." +lore1 = "Sneak + Right-Click with bones in hand to summon a servant" +lore2 = "Servant Lifetime" +lore3 = "Bones consumed per summon" +lore4 = "Summon Cooldown" +lore5 = "Max living servants" +lore6 = "Servants gear up with your level, inherit your Tragoul perks, and prioritize players who attack you" +lore = ["Sneak + Right-Click with bones in hand to summon a servant", "Servant Lifetime", "Bones consumed per summon", "Summon Cooldown", "Max living servants", "Servants gear up with your level, inherit your Tragoul perks, and prioritize players who attack you"] + +[tragoul.marrow_armor] +name = "Marrow Armor" +description = "Bones in your inventory shatter to absorb part of incoming hits!" +lore1 = "Consumes 1 bone to absorb part of a hit" +lore2 = "of the hit absorbed per bone" +lore3 = "Internal Cooldown" +lore = ["Consumes 1 bone to absorb part of a hit", "of the hit absorbed per bone", "Internal Cooldown"] + +[tragoul.curse_of_frailty] +name = "Curse of Frailty" +description = "Enemies that dare strike you are cursed with weakness, and slowness at higher levels!" +lore1 = "Attackers are cursed with Weakness" +lore2 = "Curse Duration" +lore3 = "Attackers are also cursed with Slowness" +lore = ["Attackers are cursed with Weakness", "Curse Duration", "Attackers are also cursed with Slowness"] + +[tragoul.death_sense] +name = "Death Sense" +description = "Sense weakened hostile mobs near you - dying prey briefly glows through walls!" +lore1 = "Weakened hostile mobs near you briefly glow" +lore2 = "health or lower marks a mob as dying prey" +lore3 = "Sense Radius" +lore = ["Weakened hostile mobs near you briefly glow", "health or lower marks a mob as dying prey", "Sense Radius"] + +[tragoul.plague_bearer] +name = "Plague Bearer" +description = "Mobs that die poisoned or withered by you spread the affliction to nearby hostile mobs!" +lore1 = "Your poison and wither spread on death" +lore2 = "Spread Radius" +lore3 = "Spread Effect Duration" +lore = ["Your poison and wither spread on death", "Spread Radius", "Spread Effect Duration"] + +[tragoul.last_rites] +name = "Last Rites" +description = "A killing blow leaves you at 1 HP as a fleeting spirit instead of dying!" +lore1 = "Death is denied - you linger as a spirit at 1 HP" +lore2 = "Spirit Duration" +lore3 = "Cooldown" +lore = ["Death is denied - you linger as a spirit at 1 HP", "Spirit Duration", "Cooldown"] + +[advancement.challenge_tragoul_corpse_500] +title = "Walking Detonator" +description = "Detonate 500 hostile mobs with blood novas" + +[advancement.challenge_tragoul_corpse_5k] +title = "Field of Gore" +description = "Detonate 5,000 hostile mobs with blood novas" + +[advancement.challenge_tragoul_siphon_500] +title = "Soul Drinker" +description = "Siphon 500 health from your victims" + +[advancement.challenge_tragoul_siphon_10k] +title = "Hollowing Hunger" +description = "Siphon 10,000 health from your victims" + +[advancement.challenge_tragoul_servant_50] +title = "Necromancer" +description = "Summon 50 skeletal servants" + +[advancement.challenge_tragoul_servant_500] +title = "Lord of Bones" +description = "Summon 500 skeletal servants" + +[advancement.challenge_tragoul_marrow_500] +title = "Bone Shield" +description = "Absorb 500 damage with marrow armor" + +[advancement.challenge_tragoul_marrow_5k] +title = "Ossified" +description = "Absorb 5,000 damage with marrow armor" + +[advancement.challenge_tragoul_frailty_100] +title = "Brittle Touch" +description = "Curse 100 attackers with frailty" + +[advancement.challenge_tragoul_frailty_1k] +title = "Wasting Word" +description = "Curse 1,000 attackers with frailty" + +[advancement.challenge_tragoul_death_sense_1k] +title = "Reaper's Eye" +description = "Sense 1,000 dying prey through walls" + +[advancement.challenge_tragoul_plague_100] +title = "Patient Zero" +description = "Spread your plague to 100 hostile mobs" + +[advancement.challenge_tragoul_plague_1k] +title = "Pandemic" +description = "Spread your plague to 1,000 hostile mobs" + +[advancement.challenge_tragoul_last_rites_5] +title = "Not Today" +description = "Defy death 5 times" + +[advancement.challenge_tragoul_last_rites_50] +title = "Unfinished Business" +description = "Defy death 50 times" From 292a554bbe3fd517226cb1038e2fd526fc1c6d5d Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 10 Jun 2026 23:40:58 -0400 Subject: [PATCH 22/25] clarity --- build.gradle | 7 + settings.gradle | 38 ++ src/main/java/art/arcane/adapt/Adapt.java | 97 ++++- .../java/art/arcane/adapt/AdaptConfig.java | 33 ++ .../java/art/arcane/adapt/api/Component.java | 57 ++- .../adapt/api/adaptation/Adaptation.java | 4 + .../api/adaptation/AdaptationGuiSupport.java | 2 +- .../adaptation/AdaptationRuntimeGuards.java | 43 +++ .../art/arcane/adapt/api/data/WorldData.java | 2 + .../adapt/api/data/unit/PlacementStamp.java | 57 +++ .../adapt/api/skill/SkillGuiSupport.java | 2 +- .../adapt/api/skill/SkillRuntimeGuards.java | 2 + .../arcane/adapt/api/world/PlayerData.java | 11 +- .../adapt/api/world/PlayerSkillLine.java | 97 ++++- .../art/arcane/adapt/api/xp/XpNovelty.java | 350 ++++++++++++++++++ .../adapt/api/xp/XpNoveltyListener.java | 31 ++ .../art/arcane/adapt/api/xp/XpProvenance.java | 127 +++++++ .../adapt/api/xp/XpProvenanceListener.java | 67 ++++ .../adaptation/axe/AxeGroundSmash.java | 3 +- .../adaptation/axe/AxeLeafVeinminer.java | 10 +- .../adaptation/axe/AxeWoodVeinminer.java | 2 +- .../adaptation/chronos/ChronosAccelerate.java | 7 +- .../chronos/ChronosHourglassGuard.java | 4 + .../chronos/ChronosStasisField.java | 4 + .../chronos/ChronosTemporalEcho.java | 5 + .../adaptation/chronos/ChronosTimeBomb.java | 4 + .../excavation/ExcavationGraveDigger.java | 18 +- .../excavation/ExcavationSeismicPing.java | 24 +- .../herbalism/HerbalismCompostCascade.java | 8 +- .../herbalism/HerbalismReplant.java | 4 + .../herbalism/HerbalismSporeBloom.java | 10 +- .../adaptation/pickaxe/PickaxeGemPolish.java | 4 +- .../pickaxe/PickaxeQuarrySense.java | 6 + .../adaptation/pickaxe/PickaxeTunnelBore.java | 3 +- .../adaptation/pickaxe/PickaxeVeinminer.java | 38 +- .../adaptation/rift/RiftVoidMagnet.java | 12 + .../stealth/StealthShadowDecoy.java | 4 + .../stealth/StealthShadowDecoyPackets.java | 64 ++-- .../adaptation/stealth/StealthSnatch.java | 53 +-- .../adaptation/tragoul/TragoulDeathSense.java | 4 + .../tragoul/TragoulPlagueBearer.java | 2 +- .../tragoul/TragoulSkeletalServant.java | 10 +- .../adaptation/unarmed/UnarmedDisarm.java | 67 +++- .../adaptation/unarmed/UnarmedSecondWind.java | 4 + .../hiddenore/HiddenOreBridge.java | 254 +++++++++++++ .../integration/hiddenore/HiddenOreLink.java | 67 ++++ .../adapt/content/skill/SkillArchitect.java | 9 +- .../arcane/adapt/content/skill/SkillAxes.java | 8 +- .../adapt/content/skill/SkillExcavation.java | 4 + .../adapt/content/skill/SkillHerbalism.java | 5 +- .../adapt/content/skill/SkillHunter.java | 6 + .../adapt/content/skill/SkillPickaxes.java | 4 + .../adapt/content/skill/SkillStealth.java | 7 + .../adapt/content/skill/SkillTaming.java | 7 + .../adapt/util/common/misc/CustomModel.java | 116 ++++-- .../adapt/util/common/misc/Impulse.java | 11 + .../AdvancementDisplayWrapper_v1_21_R7.java | 7 +- src/main/resources/en_US.toml | 8 +- src/main/resources/plugin.yml | 1 + 59 files changed, 1754 insertions(+), 161 deletions(-) create mode 100644 src/main/java/art/arcane/adapt/api/data/unit/PlacementStamp.java create mode 100644 src/main/java/art/arcane/adapt/api/xp/XpNovelty.java create mode 100644 src/main/java/art/arcane/adapt/api/xp/XpNoveltyListener.java create mode 100644 src/main/java/art/arcane/adapt/api/xp/XpProvenance.java create mode 100644 src/main/java/art/arcane/adapt/api/xp/XpProvenanceListener.java create mode 100644 src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreBridge.java create mode 100644 src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreLink.java diff --git a/build.gradle b/build.gradle index 2f97fcdcb..40c48034c 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,9 @@ File nmsCompileJar = configuredNmsJar != null && !configuredNmsJar.isBlank() String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() +String hiddenOreCoordinate = providers.gradleProperty('hiddenOreCoordinate') + .orElse('com.github.VolmitSoftware:HiddenOre:master-SNAPSHOT') + .get() def shadowJarTask = tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) tasks.named('slimJar').get() @@ -120,6 +123,10 @@ dependencies { changing = true transitive = false } + compileOnly(hiddenOreCoordinate) { + changing = true + transitive = false + } implementation('de.crazydev22.slimjar.helper:spigot:2.1.9') implementation('de.crazydev22.slimjar.helper:velocity:2.1.9') slimApi(libs.platformUtils) { diff --git a/settings.gradle b/settings.gradle index 0aae7e772..b7d9aca51 100644 --- a/settings.gradle +++ b/settings.gradle @@ -46,4 +46,42 @@ if (useLocalVolmLib && localVolmLibDirectory != null) { } } +File resolveLocalHiddenOreDirectory() { + String configuredPath = providers.gradleProperty('localHiddenOreDirectory') + .orElse(providers.environmentVariable('HIDDENORE_DIR')) + .orNull + if (configuredPath != null && !configuredPath.isBlank()) { + File configuredDirectory = file(configuredPath) + if (hasVolmLibSettings(configuredDirectory)) { + return configuredDirectory + } + } + + File currentDirectory = settingsDir + while (currentDirectory != null) { + File candidate = new File(currentDirectory, 'HiddenOre') + if (hasVolmLibSettings(candidate)) { + return candidate + } + + currentDirectory = currentDirectory.parentFile + } + + null +} + +boolean useLocalHiddenOre = providers.gradleProperty('useLocalHiddenOre') + .orElse('true') + .map { String value -> value.equalsIgnoreCase('true') } + .get() +File localHiddenOreDirectory = resolveLocalHiddenOreDirectory() + +if (useLocalHiddenOre && localHiddenOreDirectory != null) { + includeBuild(localHiddenOreDirectory) { + dependencySubstitution { + substitute(module('com.github.VolmitSoftware:HiddenOre')).using(project(':')) + } + } +} + include(':velocity') diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index 618b78a2e..aac6e6f2b 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -31,6 +31,9 @@ import art.arcane.adapt.api.version.Version; import art.arcane.adapt.api.world.AdaptServer; import art.arcane.adapt.api.world.PlayerDataPersistenceQueue; +import art.arcane.adapt.api.xp.XpNoveltyListener; +import art.arcane.adapt.api.xp.XpProvenanceListener; +import art.arcane.adapt.content.integration.hiddenore.HiddenOreLink; import art.arcane.adapt.content.protector.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -58,7 +61,6 @@ import fr.skytasul.glowingentities.GlowingEntities; import io.github.slimjar.app.builder.SpigotApplicationBuilder; import lombok.Getter; -import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -68,6 +70,10 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.text.MessageFormat; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -238,28 +244,75 @@ public static void printInformation() { info("Language: " + AdaptConfig.get().getLanguage() + " - Language Fallback: " + AdaptConfig.get().getFallbackLanguageDontChangeUnlessYouKnowWhatYouAreDoing()); } - @SneakyThrows public static void autoUpdateCheck() { - try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle").openStream()))) { - info("Checking for updates..."); - String inputLine; - while ((inputLine = in.readLine()) != null) { - if (inputLine.contains("version '")) { - String version = inputLine.replace("version '", "").replace("'", "").replace("// Needs to be version specific", "").replace(" ", ""); - if (instance.getDescription().getVersion().contains("development")) { - info("Development build detected. Skipping update check."); - return; - } else if (!version.equals(instance.getDescription().getVersion())) { - info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", instance.getDescription().getVersion(), version)); - } else { - info("You are running the latest version of Adapt!"); + String localVersion = instance.getDescription().getVersion(); + if (localVersion.contains("development")) { + info("Development build detected. Skipping update check."); + return; + } + + info("Checking for updates..."); + String remoteVersion = fetchRemoteVersion(); + if (remoteVersion == null) { + error("Failed to check for updates."); + return; + } + + int comparison = compareVersionPrefixes(localVersion, remoteVersion); + if (comparison < 0) { + info(MessageFormat.format("Please update your Adapt plugin to the latest version! (Current: {0} Latest: {1})", localVersion, remoteVersion)); + } else if (comparison > 0) { + info("Running a build ahead of the published release. (Current: " + localVersion + " Published: " + remoteVersion + ")"); + } else { + info("You are running the latest version of Adapt!"); + } + } + + private static String fetchRemoteVersion() { + String[] sources = { + "https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle.kts", + "https://raw.githubusercontent.com/VolmitSoftware/Adapt/main/build.gradle" + }; + Pattern versionPattern = Pattern.compile("^version\\s*=?\\s*['\"]([^'\"]+)['\"]"); + + for (String source : sources) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL(source).openStream()))) { + String line; + while ((line = in.readLine()) != null) { + Matcher matcher = versionPattern.matcher(line.trim()); + if (matcher.find()) { + return matcher.group(1); } - break; } + } catch (Throwable ignored) { } - } catch (Throwable e) { - error("Failed to check for updates."); } + + return null; + } + + private static int compareVersionPrefixes(String local, String remote) { + int[] a = parseVersionPrefix(local); + int[] b = parseVersionPrefix(remote); + for (int i = 0; i < 3; i++) { + if (a[i] != b[i]) { + return Integer.compare(a[i], b[i]); + } + } + + return 0; + } + + private static int[] parseVersionPrefix(String version) { + int[] parts = new int[3]; + Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.(\\d+))?").matcher(version); + if (matcher.find()) { + parts[0] = Integer.parseInt(matcher.group(1)); + parts[1] = Integer.parseInt(matcher.group(2)); + parts[2] = matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3)); + } + + return parts; } public static void actionbar(Player p, String msg) { @@ -360,6 +413,8 @@ public void start() { }); CustomBlockData.registerListener(this); registerListener(new BrewingManager()); + registerListener(new XpProvenanceListener()); + registerListener(new XpNoveltyListener()); registerListener(Version.get()); setupMetrics(); startupPrint(); // Splash screen @@ -388,6 +443,9 @@ public void start() { if (getServer().getPluginManager().getPlugin("LockettePro") != null) { protectorRegistry.registerProtector(new LocketteProProtector()); } + if (getServer().getPluginManager().getPlugin("HiddenOre") != null) { + HiddenOreLink.activate(this); + } initializeGlowingEntities(); initializeAdaptationListings(); services.values().forEach(AdaptService::onEnable); @@ -395,7 +453,10 @@ public void start() { ConfigFileSupport.flushCreatedConfigSummary(); } + private static final Logger GLOWING_ENTITIES_LOGGER = Logger.getLogger("GlowingEntities"); + private void initializeGlowingEntities() { + GLOWING_ENTITIES_LOGGER.setFilter(record -> record.getLevel().intValue() >= Level.WARNING.intValue()); try { glowingEntities = new GlowingEntities(this); } catch (Throwable t) { diff --git a/src/main/java/art/arcane/adapt/AdaptConfig.java b/src/main/java/art/arcane/adapt/AdaptConfig.java index 8facc6e61..c9f6abb8d 100644 --- a/src/main/java/art/arcane/adapt/AdaptConfig.java +++ b/src/main/java/art/arcane/adapt/AdaptConfig.java @@ -77,6 +77,7 @@ public class AdaptConfig { private Effects effects = new Effects(); private FarmPrevention farmPrevention = new FarmPrevention(); private AdaptationXp adaptationXp = new AdaptationXp(); + private XpIntegrity xpIntegrity = new XpIntegrity(); private RedisConfig redis = new RedisConfig(); private SqlSettings sql = new SqlSettings(); private Protector protectorSupport = new Protector(); @@ -206,6 +207,38 @@ public static class FarmPrevention { private double crossSkillRecoveryFactor = 0.9; } + @Getter + public static class XpIntegrity { + private boolean provenanceEnabled = true; + private long placedBlockTtlMillis = 86400000; + private long replaceDenyTtlMillis = 900000; + private boolean bonemealTrackingEnabled = true; + private long bonemealTtlMillis = 600000; + private double bonemealHarvestMultiplier = 0.5; + private boolean noveltyEnabled = true; + private int spatialCellShift = 2; + private int spatialCellCap = 256; + private long spatialCellTtlMillis = 900000; + private double spatialRepeatDecay = 0.3; + private double spatialFloorMultiplier = 0.25; + private int entropyWindow = 48; + private double entropyFloorMultiplier = 0.7; + private boolean adjacencyBonusEnabled = true; + private double adjacencyBonusMax = 0.25; + private double adjacencyBonusPerStreak = 0.05; + private boolean stillnessEnabled = true; + private long stillnessWindowMillis = 60000; + private int stillnessMinEvents = 20; + private double stillnessEpsilon = 0.75; + private double stillnessFloorMultiplier = 0.25; + private long fieldCycleMillis = 240000; + private double fieldCycleFloorMultiplier = 0.15; + private boolean pooledPayoutEnabled = true; + private long pooledWindowMillis = 30000; + private long pooledIdleFlushMillis = 8000; + private boolean inspiredNotifyEnabled = true; + } + @Getter public static class Effects { private boolean particlesEnabled = true; diff --git a/src/main/java/art/arcane/adapt/api/Component.java b/src/main/java/art/arcane/adapt/api/Component.java index 8f3d3ca40..c95b8d13c 100644 --- a/src/main/java/art/arcane/adapt/api/Component.java +++ b/src/main/java/art/arcane/adapt/api/Component.java @@ -50,7 +50,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.function.IntConsumer; import java.util.function.Predicate; @@ -539,14 +541,55 @@ default void vfxZuck(Location from, Location to, Particle particle) { } } - default void safeGiveItem(Player player, Entity itemEntity, ItemStack is) { - EntityPickupItemEvent e = new EntityPickupItemEvent(player, (Item) itemEntity, 0); + default boolean safeGiveItem(Player player, Entity itemEntity, ItemStack is) { + if (!(itemEntity instanceof Item item) || !item.isValid() || is == null || is.getType().isAir() || is.getAmount() <= 0) { + return false; + } + + EntityPickupItemEvent e = new EntityPickupItemEvent(player, item, 0); Bukkit.getPluginManager().callEvent(e); - if (!e.isCancelled()) { - itemEntity.remove(); - if (!player.getInventory().addItem(is).isEmpty()) { - player.getWorld().dropItem(player.getLocation(), is); - } + if (e.isCancelled()) { + return false; + } + + int requested = is.getAmount(); + Map leftover = player.getInventory().addItem(is.clone()); + if (leftover.isEmpty()) { + item.remove(); + return true; + } + + ItemStack remaining = leftover.values().iterator().next(); + if (remaining == null || remaining.getAmount() >= requested) { + return false; + } + + item.setItemStack(remaining); + return true; + } + + default boolean canSnatchItem(Player player, Item item) { + if (!item.isValid() || item.isInvulnerable()) { + return false; + } + + if (item.getPickupDelay() >= Short.MAX_VALUE) { + return false; + } + + UUID owner = item.getOwner(); + if (owner != null && !owner.equals(player.getUniqueId())) { + return false; + } + + if (item.hasMetadata("NPC") || item.hasMetadata("shopitem") || item.hasMetadata("hologram")) { + return false; + } + + try { + return item.canPlayerPickup(); + } catch (NoSuchMethodError ignored) { + return true; } } diff --git a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java index 79b9f5e44..5a7998947 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/Adaptation.java @@ -481,6 +481,10 @@ default boolean canDamageTarget(Player attacker, Entity target) { return AdaptationRuntimeGuards.canDamageTarget(this, attacker, target); } + default boolean isProtectedFriendly(Player actor, Entity target) { + return AdaptationRuntimeGuards.isProtectedFriendly(actor, target); + } + /** * Returns active level only when player is in survival. */ diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java index 226d4e57b..98aa84bed 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationGuiSupport.java @@ -232,7 +232,7 @@ static void openGui(Adaptation adaptation, Player player, int page) { .setName(adaptation.getDisplayName(lvl)) .setEnchanted(mylevel >= lvl) .setProgress(1D) - .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 40)) + .addLore(C.GRAY + adaptation.getDescription()) .addLore(mylevel >= lvl ? ("") : ("" + C.WHITE + c + C.GRAY + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost") + " " + (AdaptConfig.get().isHardcoreNoRefunds() ? C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds") : ""))) .addLore(mylevel >= lvl ? AdaptConfig.get().isHardcoreNoRefunds() ? (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.DARK_RED + "" + C.BOLD + Localizer.dLocalize("snippets.adapt_menu.no_refunds")) : (adaptation.isPermanent() ? "" : (C.GREEN + Localizer.dLocalize("snippets.adapt_menu.already_learned") + " " + C.GRAY + Localizer.dLocalize("snippets.adapt_menu.unlearn_refund") + " " + C.GREEN + rc + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_cost"))) : (k >= c ? (C.BLUE + Localizer.dLocalize("snippets.adapt_menu.click_learn") + " " + adaptation.getDisplayName(lvl)) : (k == 0 ? (C.RED + Localizer.dLocalize("snippets.adapt_menu.no_knowledge")) : (C.RED + "(" + Localizer.dLocalize("snippets.adapt_menu.you_only_have") + " " + C.WHITE + k + C.RED + " " + Localizer.dLocalize("snippets.adapt_menu.knowledge_available") + ")")))) .addLore(mylevel < lvl && adaptation.getPlayer(player).getData().hasPowerAvailable(pc) ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : mylevel >= lvl ? C.GREEN + "" + lvl + " " + Localizer.dLocalize("snippets.adapt_menu.power_drain") : C.RED + Localizer.dLocalize("snippets.adapt_menu.not_enough_power") + "\n" + C.RED + Localizer.dLocalize("snippets.adapt_menu.how_to_level_up")) diff --git a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java index 90af0613c..863af338c 100644 --- a/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/adaptation/AdaptationRuntimeGuards.java @@ -26,16 +26,22 @@ import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.adaptation.tragoul.TragoulSkeletalServant; import art.arcane.adapt.content.event.AdaptAdaptationUseEvent; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.math.M; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Display; import org.bukkit.entity.Entity; +import org.bukkit.entity.Interaction; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Tameable; import org.bukkit.event.Cancellable; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.ItemStack; @@ -225,6 +231,10 @@ static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity return false; } + if (isProtectedFriendly(attacker, target)) { + return false; + } + if (target instanceof Player victim) { return adaptation.canPVP(attacker, victim.getLocation()); } @@ -232,6 +242,39 @@ static boolean canDamageTarget(Adaptation adaptation, Player attacker, Entity return adaptation.canPVE(attacker, target.getLocation()); } + static boolean isProtectedFriendly(Player actor, Entity target) { + if (target == null) { + return false; + } + + if (target instanceof Display || target instanceof Interaction) { + return true; + } + + if (target instanceof ArmorStand stand && stand.isMarker()) { + return true; + } + + if (target.isInvulnerable()) { + return true; + } + + if (target.hasMetadata("NPC")) { + return true; + } + + if (TragoulSkeletalServant.isServant(target)) { + return true; + } + + if (actor != null && target instanceof Tameable tameable && tameable.isTamed()) { + AnimalTamer tamer = tameable.getOwner(); + return tamer != null && actor.getUniqueId().equals(tamer.getUniqueId()); + } + + return false; + } + static int getActiveSurvivalLevel(Adaptation adaptation, Player player) { if (player == null || player.getGameMode() != GameMode.SURVIVAL) { return 0; diff --git a/src/main/java/art/arcane/adapt/api/data/WorldData.java b/src/main/java/art/arcane/adapt/api/data/WorldData.java index bdfeb3343..0bf01a010 100644 --- a/src/main/java/art/arcane/adapt/api/data/WorldData.java +++ b/src/main/java/art/arcane/adapt/api/data/WorldData.java @@ -20,6 +20,7 @@ import art.arcane.adapt.Adapt; import art.arcane.adapt.api.data.unit.Earnings; +import art.arcane.adapt.api.data.unit.PlacementStamp; import art.arcane.adapt.api.tick.TickedObject; import art.arcane.adapt.util.common.parallel.MultiBurst; import art.arcane.adapt.util.common.scheduling.J; @@ -49,6 +50,7 @@ public class WorldData extends TickedObject { static { SpatialMatter.registerSliceType(new Earnings.EarningsMatter()); + SpatialMatter.registerSliceType(new PlacementStamp.PlacementStampMatter()); ClassReader.add(WorldData.class.getClassLoader()); } diff --git a/src/main/java/art/arcane/adapt/api/data/unit/PlacementStamp.java b/src/main/java/art/arcane/adapt/api/data/unit/PlacementStamp.java new file mode 100644 index 000000000..726f7224d --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/data/unit/PlacementStamp.java @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.data.unit; + +import art.arcane.spatial.matter.slices.RawMatter; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Data +@AllArgsConstructor +public class PlacementStamp { + private final int placedAt; + private final int brokenAt; + private final int bonemealedAt; + + public static class PlacementStampMatter extends RawMatter { + public PlacementStampMatter() { + this(1, 1, 1); + } + + public PlacementStampMatter(int width, int height, int depth) { + super(width, height, depth, PlacementStamp.class); + } + + @Override + public void writeNode(PlacementStamp stamp, DataOutputStream dataOutputStream) throws IOException { + dataOutputStream.writeInt(stamp.getPlacedAt()); + dataOutputStream.writeInt(stamp.getBrokenAt()); + dataOutputStream.writeInt(stamp.getBonemealedAt()); + } + + @Override + public PlacementStamp readNode(DataInputStream dataInputStream) throws IOException { + return new PlacementStamp(dataInputStream.readInt(), dataInputStream.readInt(), dataInputStream.readInt()); + } + } +} diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java index 7591846e6..e1437b5ad 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillGuiSupport.java @@ -173,7 +173,7 @@ static void openGui(Skill skill, Player player, int page) { .setMaterial(new MaterialBlock(adaptation.getIcon())) .setBaseItemStack(adaptation.getModel().toItemStack()) .setName(adaptation.getDisplayName(level)) - .addLore(Form.wrapWordsPrefixed(adaptation.getDescription(), "" + C.GRAY, 45)) + .addLore(C.GRAY + adaptation.getDescription()) .addLore(level == 0 ? (C.DARK_GRAY + Localizer.dLocalize("snippets.gui.not_learned")) : (C.GRAY + Localizer.dLocalize("snippets.gui.level") + " " + C.WHITE + Form.toRoman(level))) .setProgress(1D) .onLeftClick((e) -> openAdaptation(adaptation, player)); diff --git a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java index 3d124e84e..845731112 100644 --- a/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java +++ b/src/main/java/art/arcane/adapt/api/skill/SkillRuntimeGuards.java @@ -27,6 +27,7 @@ import art.arcane.adapt.api.world.AdaptStatTracker; import art.arcane.adapt.api.world.PlayerData; import art.arcane.adapt.api.xp.XP; +import art.arcane.adapt.api.xp.XpNovelty; import art.arcane.adapt.util.common.scheduling.J; import org.bukkit.Location; import org.bukkit.World; @@ -195,6 +196,7 @@ static void grantXp(Skill skill, Player player, Location location, double xp, return; } try { + xp *= XpNovelty.noveltyMultiplier(player, location, rewardKey); if (silent) { XP.xpSilent(player, skill, xp, rewardKey); } else { diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerData.java b/src/main/java/art/arcane/adapt/api/world/PlayerData.java index d436d7467..5efe3a2ff 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerData.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerData.java @@ -124,7 +124,7 @@ public void update(AdaptPlayer p) { continue; } - if (lineData.getXp() == 0 && lineData.getKnowledge() == 0) { + if (lineData.getXp() == 0 && lineData.getKnowledge() == 0 && lineData.getPooledXp() == 0) { skillLines.remove(lineId, lineData); continue; } @@ -287,6 +287,10 @@ public void clearXp() { line.setXp(0); line.setLastXP(0); line.setLastLevel(0); + line.setPooledXp(0); + line.setPooledNotifyXp(0); + line.setPoolStartedAt(0); + line.setPoolLastEarnAt(0); line.setMonotonyCounter(0); line.setMonotonyMultiplier(1.0); line.setLastXpTimestamp(0); @@ -376,6 +380,11 @@ public void clearAll() { public String toJson(boolean raw) { synchronized (skillLines) { + for (PlayerSkillLine line : skillLines.values()) { + if (line != null) { + line.flushXpPool(null); + } + } return Json.toJson(this, !raw); } } diff --git a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java index b8a4bef0e..bce01355d 100644 --- a/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java +++ b/src/main/java/art/arcane/adapt/api/world/PlayerSkillLine.java @@ -28,6 +28,8 @@ import art.arcane.adapt.api.skill.Skill; import art.arcane.adapt.api.xp.XP; import art.arcane.adapt.api.xp.XPMultiplier; +import art.arcane.adapt.util.common.format.C; +import art.arcane.adapt.util.common.format.Localizer; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.math.M; @@ -58,12 +60,20 @@ public class PlayerSkillLine { private double monotonyMultiplier = 1.0; private RewardStalenessState skillStaleness = new RewardStalenessState(); private long lastStalenessCleanup = 0; + private transient double pooledXp = 0; + private transient double pooledNotifyXp = 0; + private transient long poolStartedAt = 0; + private transient long poolLastEarnAt = 0; + private transient long inspiredPendingAt = 0; + private transient long inspiredLastNotifyAt = 0; private static double diff(long a, long b) { return Math.abs(a - b / (double) (a == 0 ? 1 : a)); } public void update(AdaptPlayer p, String line, PlayerData data) { + flushPoolIfReady(p); + pulseInspired(p); grantSkillsAndAdaptations(p, line); checkMaxLevel(p, line); updateFreshness(); @@ -157,6 +167,20 @@ public void giveXP(Notifier p, double xp, String rewardKey) { monotonyMultiplier = computeStalenessMultiplier(xp, rewardKey, now); xp = multiplier * monotonyMultiplier * xp; + + if (AdaptConfig.get().getXpIntegrity().isPooledPayoutEnabled()) { + if (pooledXp == 0) { + poolStartedAt = now; + } + pooledXp += xp; + poolLastEarnAt = now; + if (p != null) { + last = M.ms(); + pooledNotifyXp += xp; + } + return; + } + this.xp += xp; if (p != null) { @@ -167,6 +191,68 @@ public void giveXP(Notifier p, double xp, String rewardKey) { } } + private void flushPoolIfReady(AdaptPlayer p) { + if (pooledXp == 0 && pooledNotifyXp == 0) { + return; + } + + if (pooledXp != 0) { + AdaptConfig.XpIntegrity integrity = AdaptConfig.get().getXpIntegrity(); + long now = System.currentTimeMillis(); + if (now - poolStartedAt < integrity.getPooledWindowMillis() && now - poolLastEarnAt < integrity.getPooledIdleFlushMillis()) { + return; + } + } + + flushXpPool(p.getNot()); + } + + public void flushXpPool(Notifier p) { + if (pooledXp == 0 && pooledNotifyXp == 0) { + return; + } + + double gained = pooledXp; + pooledXp = 0; + poolStartedAt = 0; + poolLastEarnAt = 0; + this.xp += gained; + + if (p == null) { + return; + } + + double display = pooledNotifyXp; + pooledNotifyXp = 0; + if (display != 0 && AdaptConfig.get().isActionbarNotifyXp()) { + p.notifyXP(line, display); + } + } + + private void pulseInspired(AdaptPlayer p) { + if (inspiredPendingAt == 0) { + return; + } + + inspiredPendingAt = 0; + long now = System.currentTimeMillis(); + if (now - inspiredLastNotifyAt < 30000) { + return; + } + + Skill skill = p.getServer().getSkillRegistry().getSkill(line); + if (skill == null) { + return; + } + + inspiredLastNotifyAt = now; + p.getActionBarNotifier().queue(ActionBarNotification.builder() + .duration(1250) + .group("inspired" + line) + .title(skill.getDisplayName() + C.RESET + " " + C.GREEN + Localizer.dLocalize("snippets.xp.inspired")) + .build()); + } + public void relaxStalenessForActivitySwitch() { AdaptConfig.FarmPrevention prevention = AdaptConfig.get().getFarmPrevention(); if (prevention == null || !prevention.isEnabled()) { @@ -176,9 +262,14 @@ public void relaxStalenessForActivitySwitch() { } double factor = clamp(prevention.getCrossSkillRecoveryFactor(), 0.0, 1.0); - applyRecoveryFactor(skillStaleness, factor); - for (RewardStalenessState state : activityStaleness.values()) { - applyRecoveryFactor(state, factor); + double curve = prevention.getSkillDecayCurve(); + RewardStalenessState state = ensureSkillStaleness(); + if (factor < 1.0 && curve > 0 && state.getPressure() > curve * 0.25 && AdaptConfig.get().getXpIntegrity().isInspiredNotifyEnabled()) { + inspiredPendingAt = System.currentTimeMillis(); + } + applyRecoveryFactor(state, factor); + for (RewardStalenessState activityState : activityStaleness.values()) { + applyRecoveryFactor(activityState, factor); } monotonyCounter = 0; } diff --git a/src/main/java/art/arcane/adapt/api/xp/XpNovelty.java b/src/main/java/art/arcane/adapt/api/xp/XpNovelty.java new file mode 100644 index 000000000..80efad49d --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/xp/XpNovelty.java @@ -0,0 +1,350 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.xp; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.volmlib.util.math.M; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public final class XpNovelty { + private static final Map STATES = new ConcurrentHashMap<>(); + private static final String NULL_KEY = "null"; + private static final int STILLNESS_RING = 32; + private static final float STILLNESS_YAW_RANGE = 10.0f; + private static final double ENTROPY_SATURATION_KEYS = 3.0; + + private XpNovelty() { + } + + public static double noveltyMultiplier(Player p, Location at, String rewardKey) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isNoveltyEnabled() || p == null) { + return 1.0; + } + + State state = stateOf(p.getUniqueId(), config); + long now = M.ms(); + synchronized (state) { + double spatial = 1.0; + if (at != null && at.getWorld() != null) { + long key = cellKey(at.getWorld(), at.getBlockX(), at.getBlockY(), at.getBlockZ(), config.getSpatialCellShift()); + spatial = state.spatialVisit(key, now, config); + } + + String entropyKey = rewardKey == null ? NULL_KEY : rewardKey; + double entropy = state.entropySample(entropyKey, config); + double combined = spatial * entropy; + + if (config.isStillnessEnabled()) { + Location pos = p.getLocation(state.scratch); + if (state.stillnessSample(pos.getX(), pos.getY(), pos.getZ(), pos.getYaw(), now, config)) { + combined = Math.min(combined, config.getStillnessFloorMultiplier()); + } + } + + double floor = config.getSpatialFloorMultiplier() * config.getEntropyFloorMultiplier() * config.getStillnessFloorMultiplier(); + return Math.max(combined, floor); + } + } + + public static double adjacencyBonusMultiplier(Player p, Block placed) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isAdjacencyBonusEnabled() || p == null || placed == null) { + return 1.0; + } + + boolean touching = XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.DOWN)) + || XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.UP)) + || XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.NORTH)) + || XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.SOUTH)) + || XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.EAST)) + || XpProvenance.isPlayerPlaced(placed.getRelative(BlockFace.WEST)); + State state = stateOf(p.getUniqueId(), config); + double perStreak = config.getAdjacencyBonusPerStreak(); + double max = config.getAdjacencyBonusMax(); + synchronized (state) { + if (touching) { + long key = cellKey(placed.getWorld(), placed.getX(), placed.getY(), placed.getZ(), config.getSpatialCellShift()); + int visits = state.spatialPeek(key, M.ms(), config); + if (perStreak > 0.0 && visits * config.getSpatialRepeatDecay() <= 1.0 && state.adjacencyStreak * perStreak < max) { + state.adjacencyStreak++; + } + } else { + state.adjacencyStreak >>= 1; + } + + double bonus = Math.min(max, state.adjacencyStreak * perStreak); + return 1.0 + Math.max(0.0, bonus); + } + } + + public static double fieldCycleMultiplier(Player p, Block crop) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isNoveltyEnabled() || p == null || crop == null) { + return 1.0; + } + + State state = stateOf(p.getUniqueId(), config); + long now = M.ms(); + long key = cellKey(crop.getWorld(), crop.getX(), crop.getY(), crop.getZ(), config.getSpatialCellShift()); + synchronized (state) { + return state.fieldCycle(key, now, config); + } + } + + public static void clear(UUID id) { + STATES.remove(id); + } + + private static State stateOf(UUID id, AdaptConfig.XpIntegrity config) { + State state = STATES.get(id); + if (state != null) { + return state; + } + + State created = new State(config); + State prior = STATES.putIfAbsent(id, created); + return prior == null ? created : prior; + } + + private static long cellKey(World world, int x, int y, int z, int shift) { + long cx = x >> shift; + long cy = y >> shift; + long cz = z >> shift; + long key = ((cx & 0x3FFFFFFL) << 38) | ((cy & 0xFFFL) << 26) | (cz & 0x3FFFFFFL); + return key ^ ((long) world.hashCode() * 0x9E3779B97F4A7C15L); + } + + private static final class Cell { + private int count; + private long touch; + } + + private static final class LruMap extends LinkedHashMap { + private final int cap; + + private LruMap(int cap) { + super(16, 0.75f, true); + this.cap = Math.max(1, cap); + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > cap; + } + } + + private static final class State { + private final LruMap spatialCells; + private final LruMap fieldCells; + private final String[] window; + private final HashMap counts; + private final long[] times; + private final Location scratch; + private int entropyHead; + private int entropyFilled; + private int distinct; + private int stillHead; + private int stillCount; + private int runEvents; + private double minX; + private double maxX; + private double minY; + private double maxY; + private double minZ; + private double maxZ; + private float minYaw; + private float maxYaw; + private int adjacencyStreak; + + private State(AdaptConfig.XpIntegrity config) { + this.spatialCells = new LruMap(config.getSpatialCellCap()); + this.fieldCells = new LruMap(config.getSpatialCellCap()); + int windowSize = config.getEntropyWindow(); + this.window = windowSize > 1 ? new String[windowSize] : null; + this.counts = new HashMap<>(); + this.times = new long[STILLNESS_RING]; + this.scratch = new Location(null, 0.0, 0.0, 0.0); + } + + private double spatialVisit(long key, long now, AdaptConfig.XpIntegrity config) { + Cell cell = spatialCells.get(key); + if (cell == null) { + cell = new Cell(); + spatialCells.put(key, cell); + } + + long ttl = config.getSpatialCellTtlMillis(); + if (ttl > 0 && now - cell.touch > ttl) { + cell.count = 0; + } + + int n = cell.count; + cell.count = n + 1; + cell.touch = now; + double m = 1.0 / (1.0 + config.getSpatialRepeatDecay() * n); + return Math.max(config.getSpatialFloorMultiplier(), m); + } + + private int spatialPeek(long key, long now, AdaptConfig.XpIntegrity config) { + Cell cell = spatialCells.get(key); + if (cell == null) { + return 0; + } + + long ttl = config.getSpatialCellTtlMillis(); + if (ttl > 0 && now - cell.touch > ttl) { + return 0; + } + + return cell.count; + } + + private double entropySample(String key, AdaptConfig.XpIntegrity config) { + if (window == null) { + return 1.0; + } + + if (entropyFilled == window.length) { + String old = window[entropyHead]; + Integer oldCount = counts.get(old); + if (oldCount != null) { + if (oldCount.intValue() <= 1) { + counts.remove(old); + distinct--; + } else { + counts.put(old, oldCount.intValue() - 1); + } + } + } + + window[entropyHead] = key; + entropyHead = entropyHead + 1 == window.length ? 0 : entropyHead + 1; + if (entropyFilled < window.length) { + entropyFilled++; + } + + Integer newCount = counts.get(key); + if (newCount == null) { + counts.put(key, 1); + distinct++; + } else { + counts.put(key, newCount.intValue() + 1); + } + + if (entropyFilled < window.length) { + return 1.0; + } + + double floor = config.getEntropyFloorMultiplier(); + double normalized = (distinct - 1) / (ENTROPY_SATURATION_KEYS - 1.0); + double smooth = normalized >= 1.0 ? 1.0 : Math.sqrt(Math.max(0.0, normalized)); + return floor + (1.0 - floor) * smooth; + } + + private boolean stillnessSample(double x, double y, double z, float yaw, long now, AdaptConfig.XpIntegrity config) { + double eps = config.getStillnessEpsilon(); + if (runEvents == 0) { + beginRun(x, y, z, yaw, now); + return false; + } + + double nMinX = Math.min(minX, x); + double nMaxX = Math.max(maxX, x); + double nMinY = Math.min(minY, y); + double nMaxY = Math.max(maxY, y); + double nMinZ = Math.min(minZ, z); + double nMaxZ = Math.max(maxZ, z); + float nMinYaw = Math.min(minYaw, yaw); + float nMaxYaw = Math.max(maxYaw, yaw); + if (nMaxX - nMinX >= eps || nMaxY - nMinY >= eps || nMaxZ - nMinZ >= eps || nMaxYaw - nMinYaw >= STILLNESS_YAW_RANGE) { + beginRun(x, y, z, yaw, now); + return false; + } + + minX = nMinX; + maxX = nMaxX; + minY = nMinY; + maxY = nMaxY; + minZ = nMinZ; + maxZ = nMaxZ; + minYaw = nMinYaw; + maxYaw = nMaxYaw; + times[stillHead] = now; + stillHead = stillHead + 1 == STILLNESS_RING ? 0 : stillHead + 1; + if (stillCount < STILLNESS_RING) { + stillCount++; + } + runEvents++; + + if (runEvents < config.getStillnessMinEvents()) { + return false; + } + + long oldest = stillCount == STILLNESS_RING ? times[stillHead] : times[0]; + return now - oldest >= config.getStillnessWindowMillis(); + } + + private void beginRun(double x, double y, double z, float yaw, long now) { + minX = x; + maxX = x; + minY = y; + maxY = y; + minZ = z; + maxZ = z; + minYaw = yaw; + maxYaw = yaw; + times[0] = now; + stillHead = 1; + stillCount = 1; + runEvents = 1; + } + + private double fieldCycle(long key, long now, AdaptConfig.XpIntegrity config) { + Cell cell = fieldCells.get(key); + double result = 1.0; + if (cell == null) { + cell = new Cell(); + fieldCells.put(key, cell); + } else { + long cycle = config.getFieldCycleMillis(); + if (cycle > 0) { + long elapsed = now - cell.touch; + if (elapsed < cycle) { + double floor = config.getFieldCycleFloorMultiplier(); + result = floor + (1.0 - floor) * ((double) elapsed / (double) cycle); + } + } + } + + cell.touch = now; + return result; + } + } +} diff --git a/src/main/java/art/arcane/adapt/api/xp/XpNoveltyListener.java b/src/main/java/art/arcane/adapt/api/xp/XpNoveltyListener.java new file mode 100644 index 000000000..fc366961d --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/xp/XpNoveltyListener.java @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.xp; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +public class XpNoveltyListener implements Listener { + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerQuitEvent e) { + XpNovelty.clear(e.getPlayer().getUniqueId()); + } +} diff --git a/src/main/java/art/arcane/adapt/api/xp/XpProvenance.java b/src/main/java/art/arcane/adapt/api/xp/XpProvenance.java new file mode 100644 index 000000000..8c559b30a --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/xp/XpProvenance.java @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.xp; + +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.data.WorldData; +import art.arcane.adapt.api.data.unit.PlacementStamp; +import art.arcane.volmlib.util.math.M; +import org.bukkit.block.Block; + +public final class XpProvenance { + private XpProvenance() { + } + + public static void recordPlacement(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isProvenanceEnabled()) { + return; + } + + WorldData data = WorldData.of(block.getWorld()); + PlacementStamp stamp = data.get(block, PlacementStamp.class); + int brokenAt = stamp == null ? 0 : stamp.getBrokenAt(); + int bonemealedAt = stamp == null ? 0 : stamp.getBonemealedAt(); + data.set(block, new PlacementStamp(nowSeconds(), brokenAt, bonemealedAt)); + } + + public static boolean isPlayerPlaced(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isProvenanceEnabled()) { + return false; + } + + PlacementStamp stamp = WorldData.of(block.getWorld()).get(block, PlacementStamp.class); + return stamp != null && withinTtl(stamp.getPlacedAt(), config.getPlacedBlockTtlMillis()); + } + + public static void recordPlayerPlacedBreak(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isProvenanceEnabled()) { + return; + } + + WorldData data = WorldData.of(block.getWorld()); + PlacementStamp stamp = data.get(block, PlacementStamp.class); + int bonemealedAt = stamp == null ? 0 : stamp.getBonemealedAt(); + data.set(block, new PlacementStamp(0, nowSeconds(), bonemealedAt)); + } + + public static boolean isReplaceDenied(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isProvenanceEnabled()) { + return false; + } + + PlacementStamp stamp = WorldData.of(block.getWorld()).get(block, PlacementStamp.class); + return stamp != null && withinTtl(stamp.getBrokenAt(), config.getReplaceDenyTtlMillis()); + } + + public static void recordBonemeal(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isBonemealTrackingEnabled()) { + return; + } + + WorldData data = WorldData.of(block.getWorld()); + PlacementStamp stamp = data.get(block, PlacementStamp.class); + int placedAt = stamp == null ? 0 : stamp.getPlacedAt(); + int brokenAt = stamp == null ? 0 : stamp.getBrokenAt(); + data.set(block, new PlacementStamp(placedAt, brokenAt, nowSeconds())); + } + + public static boolean isBonemealed(Block block) { + AdaptConfig.XpIntegrity config = AdaptConfig.get().getXpIntegrity(); + if (!config.isBonemealTrackingEnabled()) { + return false; + } + + PlacementStamp stamp = WorldData.of(block.getWorld()).get(block, PlacementStamp.class); + return stamp != null && withinTtl(stamp.getBonemealedAt(), config.getBonemealTtlMillis()); + } + + public static double placeXpMultiplier(Block block) { + return isReplaceDenied(block) ? 0.0 : 1.0; + } + + public static double breakXpMultiplier(Block block) { + return isPlayerPlaced(block) || isReplaceDenied(block) ? 0.0 : 1.0; + } + + public static double harvestXpMultiplier(Block block) { + return isBonemealed(block) ? AdaptConfig.get().getXpIntegrity().getBonemealHarvestMultiplier() : 1.0; + } + + private static boolean withinTtl(int stampSeconds, long ttlMillis) { + if (stampSeconds == 0) { + return false; + } + + if (ttlMillis <= 0) { + return true; + } + + long ageMillis = (nowSeconds() - (long) stampSeconds) * 1000L; + return ageMillis <= ttlMillis; + } + + private static int nowSeconds() { + return (int) (M.ms() / 1000L); + } +} diff --git a/src/main/java/art/arcane/adapt/api/xp/XpProvenanceListener.java b/src/main/java/art/arcane/adapt/api/xp/XpProvenanceListener.java new file mode 100644 index 000000000..c9831c9f8 --- /dev/null +++ b/src/main/java/art/arcane/adapt/api/xp/XpProvenanceListener.java @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.api.xp; + +import art.arcane.adapt.AdaptConfig; +import org.bukkit.block.BlockState; +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.BlockFertilizeEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +import java.util.List; + +public class XpProvenanceListener implements Listener { + private static final int FERTILIZE_BLOCK_CAP = 64; + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockPlaceEvent e) { + if (!AdaptConfig.get().getXpIntegrity().isProvenanceEnabled()) { + return; + } + + XpProvenance.recordPlacement(e.getBlock()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockBreakEvent e) { + if (!AdaptConfig.get().getXpIntegrity().isProvenanceEnabled()) { + return; + } + + if (XpProvenance.isPlayerPlaced(e.getBlock())) { + XpProvenance.recordPlayerPlacedBreak(e.getBlock()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockFertilizeEvent e) { + if (!AdaptConfig.get().getXpIntegrity().isBonemealTrackingEnabled()) { + return; + } + + List blocks = e.getBlocks(); + int limit = Math.min(blocks.size(), FERTILIZE_BLOCK_CAP); + for (int i = 0; i < limit; i++) { + XpProvenance.recordBonemeal(blocks.get(i).getBlock()); + } + } +} diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java index 330daea7a..07fdb790a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeGroundSmash.java @@ -100,10 +100,11 @@ public void on(EntityDamageByEntityEvent e) { new Impulse(radius) .damage(getDamage(f), getFalloffDamage(f)) .force(getForce(f)) + .filter(nearby -> nearby != p && canDamageTarget(p, nearby)) .punch(e.getEntity().getLocation()); int mobsHit = 0; for (Entity nearby : e.getEntity().getWorld().getNearbyEntities(e.getEntity().getLocation(), radius, radius, radius)) { - if (nearby instanceof LivingEntity && nearby != p) { + if (nearby instanceof LivingEntity && nearby != p && canDamageTarget(p, nearby)) { mobsHit++; } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java index c6f96425a..c0c4a8d7d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeLeafVeinminer.java @@ -85,7 +85,7 @@ private int getRadius(int lvl) { return lvl + getConfig().baseRange; } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockBreakEvent e) { if (VEIN_MINED.get(e.getBlock())) { @@ -111,6 +111,10 @@ public void on(BlockBreakEvent e) { return; } + if (!canBlockBreak(p, e.getBlock().getLocation())) { + return; + } + VEIN_MINED.add(e.getBlock()); Block block = e.getBlock(); @@ -160,7 +164,7 @@ public void on(BlockBreakEvent e) { VEIN_MINED.add(b); if (adaptation != null && adaptation.getLevel() > 0) { - Collection items = block.getDrops(); + Collection items = b.getDrops(p.getInventory().getItemInMainHand(), p); for (ItemStack i : items) { sp.play(p.getLocation(), Sound.BLOCK_CALCITE_HIT, 0.01f, 0.01f); HashMap extra = p.getInventory().addItem(i); @@ -168,7 +172,7 @@ public void on(BlockBreakEvent e) { p.getWorld().dropItem(p.getLocation(), extra.get(0)); } } - p.breakBlock(b); + b.setType(Material.AIR); } else { b.breakNaturally(p.getItemInUse()); SoundPlayer spw = SoundPlayer.of(block.getWorld()); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java index 3256cdcfb..ceec3649a 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/axe/AxeWoodVeinminer.java @@ -96,7 +96,7 @@ private int getRadius(int lvl) { return lvl + getConfig().baseRange; } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockBreakEvent e) { if (VEIN_MINED.get(e.getBlock())) { return; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java index 91e2a304f..b975e146e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosAccelerate.java @@ -138,7 +138,7 @@ private void accelerateAround(Player p, Location center, int level) { } if (type == Material.FURNACE || type == Material.BLAST_FURNACE || type == Material.SMOKER) { - if (block.getState() instanceof Furnace furnace && accelerateFurnace(furnace, level)) { + if (canInteract(p, block.getLocation()) && block.getState() instanceof Furnace furnace && accelerateFurnace(furnace, level)) { accelerated++; spawnAccelerationParticle(world, x, y, z); } @@ -146,7 +146,7 @@ private void accelerateAround(Player p, Location center, int level) { } if (type == Material.BREWING_STAND) { - if (block.getState() instanceof BrewingStand stand && accelerateBrewingStand(stand, level)) { + if (canInteract(p, block.getLocation()) && block.getState() instanceof BrewingStand stand && accelerateBrewingStand(stand, level)) { accelerated++; spawnAccelerationParticle(world, x, y, z); } @@ -156,7 +156,8 @@ private void accelerateAround(Player p, Location center, int level) { BlockData data = block.getBlockData(); if (data instanceof Ageable ageable && ageable.getAge() < ageable.getMaximumAge() - && random.nextDouble() < growChance) { + && random.nextDouble() < growChance + && canInteract(p, block.getLocation())) { ageable.setAge(ageable.getAge() + 1); block.setBlockData(data, true); accelerated++; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java index b98102782..252e7b2d0 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosHourglassGuard.java @@ -151,6 +151,10 @@ private void slowNearbyEnemies(Player p) { continue; } + if (isProtectedFriendly(p, living)) { + continue; + } + if (living instanceof Player target && !canDamageTarget(p, target)) { continue; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java index 6a9e8250c..cea114588 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosStasisField.java @@ -287,6 +287,10 @@ private void pulse(StasisBubble bubble, long now) { } if (entity instanceof LivingEntity living && !(entity instanceof Player) && !entity.getUniqueId().equals(bubble.owner())) { + if (isProtectedFriendly(owner, living)) { + continue; + } + living.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, getConfig().effectRefreshTicks, getConfig().slownessAmplifier, true, false, false), true); if (PotionEffectTypes.JUMP != null) { living.addPotionEffect(new PotionEffect(PotionEffectTypes.JUMP, getConfig().effectRefreshTicks, getConfig().jumpLockAmplifier, true, false, false), true); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java index 89737758d..a805e6b1d 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTemporalEcho.java @@ -117,6 +117,11 @@ public void on(ProjectileHitEvent e) { return; } + Player shooter = e.getEntity().getShooter() instanceof Player p ? p : null; + if (isProtectedFriendly(shooter, target)) { + return; + } + target.setNoDamageTicks(0); target.setLastDamage(0.0D); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java index b039b959a..0ac394a1b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/chronos/ChronosTimeBomb.java @@ -418,6 +418,10 @@ private void applyField(TemporalField field, long now) { } if (!(entity instanceof Player)) { + if (isProtectedFriendly(owner, entity)) { + continue; + } + boolean wasNew = !frozenEntities.containsKey(entity.getUniqueId()); freezeEntity(entity); if (wasNew && owner != null) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java index e98c49544..62761d45e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationGraveDigger.java @@ -31,8 +31,10 @@ import lombok.NoArgsConstructor; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.Particle; import org.bukkit.Sound; +import org.bukkit.entity.Entity; import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.entity.Skeleton; @@ -42,6 +44,7 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; import java.util.ArrayList; import java.util.List; @@ -51,9 +54,14 @@ import java.util.concurrent.ThreadLocalRandom; public class ExcavationGraveDigger extends SimpleAdaptation { + private static final NamespacedKey GRAVE_MOB_KEY = NamespacedKey.fromString("adapt:excavation_grave_mob"); private final Map graveCooldowns = new ConcurrentHashMap<>(); private volatile TableCache tableCache; + public static boolean isGraveMob(Entity entity) { + return entity.getPersistentDataContainer().has(GRAVE_MOB_KEY, PersistentDataType.BYTE); + } + public ExcavationGraveDigger() { super("excavation-grave-digger"); registerConfiguration(Config.class); @@ -142,8 +150,14 @@ private void dropLoot(Player p, Location center, ThreadLocalRandom random) { private void disturbGrave(Player p, Location blockLocation, ThreadLocalRandom random) { Location spawnAt = blockLocation.add(0.5, 0, 0.5); Monster grave = random.nextBoolean() - ? spawnAt.getWorld().spawn(spawnAt, Zombie.class, z -> z.setTarget(p)) - : spawnAt.getWorld().spawn(spawnAt, Skeleton.class, s -> s.setTarget(p)); + ? spawnAt.getWorld().spawn(spawnAt, Zombie.class, z -> { + z.getPersistentDataContainer().set(GRAVE_MOB_KEY, PersistentDataType.BYTE, (byte) 1); + z.setTarget(p); + }) + : spawnAt.getWorld().spawn(spawnAt, Skeleton.class, s -> { + s.getPersistentDataContainer().set(GRAVE_MOB_KEY, PersistentDataType.BYTE, (byte) 1); + s.setTarget(p); + }); if (areParticlesEnabled()) { p.spawnParticle(Particle.SOUL, grave.getLocation().add(0, 1, 0), 14, 0.3, 0.5, 0.3, 0.02); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java index 5635c839d..b0cc1f217 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/excavation/ExcavationSeismicPing.java @@ -22,6 +22,7 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.content.integration.hiddenore.HiddenOreLink; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; @@ -104,13 +105,28 @@ public void on(BlockBreakEvent e) { return; } - Block target = findNearestOre(e.getBlock().getLocation(), getScanRange(level)); - if (target == null) { + Location blockLocation = e.getBlock().getLocation(); + Block target = findNearestOre(blockLocation, getScanRange(level)); + HiddenOreLink.VeinTarget hidden = HiddenOreLink.nearestVein(blockLocation, getScanRange(level)); + + Location targetCenter = null; + Material targetValueType = null; + if (target != null) { + targetCenter = target.getLocation().add(0.5, 0.5, 0.5); + targetValueType = target.getType(); + } + if (hidden != null) { + Location hiddenCenter = hidden.location().clone().add(0.5, 0.5, 0.5); + if (targetCenter == null || hiddenCenter.distanceSquared(blockLocation) < targetCenter.distanceSquared(blockLocation)) { + targetCenter = hiddenCenter; + targetValueType = hidden.display(); + } + } + if (targetCenter == null) { return; } Location origin = p.getEyeLocation(); - Location targetCenter = target.getLocation().add(0.5, 0.5, 0.5); Vector direction = targetCenter.toVector().subtract(origin.toVector()); if (direction.lengthSquared() <= 0.0000001) { return; @@ -119,7 +135,7 @@ public void on(BlockBreakEvent e) { renderDirectionHint(p, origin, direction.normalize(), getHintSegments(level)); playPingSound(p, origin.distance(targetCenter), getScanRange(level)); getPlayer(p).getData().addStat("excavation.seismic-ping.pings-triggered", 1); - xp(p, getConfig().xpPerPing + (getValue(target.getType()) * getConfig().targetValueXpMultiplier)); + xp(p, getConfig().xpPerPing + (getValue(targetValueType) * getConfig().targetValueXpMultiplier)); } private void renderDirectionHint(Player p, Location origin, Vector direction, int segments) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java index e76c63fdd..0e145aa33 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismCompostCascade.java @@ -132,7 +132,7 @@ public void on(PlayerInteractEvent e) { CompostState state = new CompostState(oldLevel); - processDroppedItems(world, center, radius, state, maxItems, fillChance); + processDroppedItems(p, world, center, radius, state, maxItems, fillChance); processMatureCrops(p, world, center, radius, state, maxItems, fillChance); processLeafBlocks(p, world, center, radius, level, state, maxItems, fillChance); processInventoryItems(p, state, maxItems, fillChance); @@ -161,7 +161,7 @@ public void on(PlayerInteractEvent e) { dropRewards(world, center, level, oldLevel, updated.getLevel(), state.consumed); } - private void processDroppedItems(World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { + private void processDroppedItems(Player p, World world, Location center, double radius, CompostState state, int maxItems, double fillChance) { if (isComposterDone(state, maxItems)) { return; } @@ -171,6 +171,10 @@ private void processDroppedItems(World world, Location center, double radius, Co continue; } + if (!canSnatchItem(p, item)) { + continue; + } + ItemStack stack = item.getItemStack(); if (!isItem(stack) || !isCompostable(stack.getType())) { continue; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java index e53a37622..c81dcf4db 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismReplant.java @@ -164,6 +164,10 @@ private void hit(Player p, Block b) { return; } + if (!canBlockBreak(p, b.getLocation()) || !canBlockPlace(p, b.getLocation())) { + return; + } + xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).harvestPerAgeXP * aa.getAge()); xp(p, b.getLocation().clone().add(0.5, 0.5, 0.5), ((SkillHerbalism.Config) getSkill().getConfig()).plantCropSeedsXP); PlayerSkillLine line = getPlayer(p).getData().getSkillLineNullable("herbalism"); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java index 647578770..6edb2552e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/herbalism/HerbalismSporeBloom.java @@ -117,7 +117,7 @@ private void startBloom(org.bukkit.entity.Player player, Block center, Material int pulseChanged = 0; int batch = getBlocksPerPulse(level); for (int i = 0; i < batch && cursor[0] < path.size(); i++) { - pulseChanged += spreadAt(path.get(cursor[0]++), catalyst, spreadSurface); + pulseChanged += spreadAt(player, path.get(cursor[0]++), catalyst, spreadSurface); } if (pulseChanged > 0) { @@ -244,7 +244,7 @@ private double normalizeAngle(double angle) { return out < 0 ? out + (Math.PI * 2D) : out; } - private int spreadAt(Block floor, Material catalyst, Material spreadSurface) { + private int spreadAt(org.bukkit.entity.Player player, Block floor, Material catalyst, Material spreadSurface) { int changed = 0; Block ground = resolveTopSurfaceSoil(floor); if (ground == null) { @@ -252,14 +252,16 @@ private int spreadAt(Block floor, Material catalyst, Material spreadSurface) { } Block above = ground.getRelative(0, 1, 0); - if (spreadSurface != null && isConvertibleSoil(ground.getType()) && ground.getType() != spreadSurface) { + if (spreadSurface != null && isConvertibleSoil(ground.getType()) && ground.getType() != spreadSurface + && canBlockBreak(player, ground.getLocation()) && canBlockPlace(player, ground.getLocation())) { ground.setType(spreadSurface, false); changed++; } if (getConfig().swapFlowersToMushrooms && isFlower(above.getType())) { Material replacement = getFlowerReplacement(above.getType(), catalyst); - if (replacement != null && above.getType() != replacement) { + if (replacement != null && above.getType() != replacement + && canBlockBreak(player, above.getLocation()) && canBlockPlace(player, above.getLocation())) { above.setType(replacement, false); changed++; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java index f1e41efa8..90d16bb1e 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeGemPolish.java @@ -70,11 +70,11 @@ public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + getBonusXp(level) + C.GRAY + " " + Localizer.dLocalize("pickaxe.gem_polish.lore3")); } - private double getGemChance(int level) { + public double getGemChance(int level) { return Math.min(getConfig().maxGemChance, getConfig().gemChanceBase + (level * getConfig().gemChancePerLevel)); } - private int getBonusXp(int level) { + public int getBonusXp(int level) { return getConfig().bonusXpBase + (level * getConfig().bonusXpPerLevel); } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java index 66de411f9..02b96b92f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeQuarrySense.java @@ -23,6 +23,7 @@ import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; +import art.arcane.adapt.content.integration.hiddenore.HiddenOreLink; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.SoundPlayer; @@ -168,6 +169,11 @@ private List findNearbyOres(Location origin, int radius, int maxResults) } } + for (HiddenOreLink.VeinTarget vein : HiddenOreLink.veins(origin, radius)) { + Location at = vein.location(); + ores.add(origin.getWorld().getBlockAt(at.getBlockX(), at.getBlockY(), at.getBlockZ())); + } + ores.sort(Comparator.comparingDouble(b -> b.getLocation().distanceSquared(origin))); if (ores.size() > maxResults) { return new ArrayList<>(ores.subList(0, maxResults)); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java index ce8e81917..aa2f32060 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeTunnelBore.java @@ -34,6 +34,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; @@ -88,7 +89,7 @@ private int getBoreHeight(int level) { return level >= 3 ? 3 : 2; } - @EventHandler + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockBreakEvent e) { Block block = e.getBlock(); if (!BORE_BLOCKS.contains(block.getType())) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java index 6478f749d..b08a91353 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/pickaxe/PickaxeVeinminer.java @@ -26,6 +26,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.world.PlayerAdaptation; import art.arcane.adapt.api.world.PlayerSkillLine; +import art.arcane.adapt.content.integration.hiddenore.HiddenOreLink; import art.arcane.adapt.content.item.ItemListings; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -41,6 +42,7 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; @@ -89,7 +91,7 @@ private int getRadius(int lvl) { return lvl + getConfig().baseRange; } - @EventHandler + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void on(BlockBreakEvent e) { if (VEIN_MINED.get(e.getBlock())) { return; @@ -103,6 +105,7 @@ public void on(BlockBreakEvent e) { if (!e.getBlock().getBlockData().getMaterial().name().endsWith("_ORE")) { if (!e.getBlock().getType().equals(Material.OBSIDIAN)) { + chainHiddenVein(e.getBlock(), p, level); return; } } @@ -205,6 +208,39 @@ public void on(BlockBreakEvent e) { }); } + private void chainHiddenVein(Block block, Player p, int level) { + List siblings = HiddenOreLink.veinSiblings(block); + if (siblings.isEmpty()) { + return; + } + + int radius = getRadius(level); + int radiusSquared = radius * radius; + Location origin = block.getLocation(); + List targets = new ArrayList<>(); + for (Block sibling : siblings) { + if (sibling.getLocation().distanceSquared(origin) <= radiusSquared && canBlockBreak(p, sibling.getLocation())) { + targets.add(sibling); + } + } + + if (targets.isEmpty()) { + return; + } + + getPlayer(p).getData().addStat("pickaxe.veinminer.ores-veinmined", targets.size()); + J.runEntity(p, () -> { + for (Block target : targets) { + if (VEIN_MINED.get(target)) { + continue; + } + VEIN_MINED.add(target); + p.breakBlock(target); + VEIN_MINED.remove(target); + } + }); + } + @Override public boolean isEnabled() { return getConfig().enabled; diff --git a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java index 2c4e76e19..3809d6c2b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/rift/RiftVoidMagnet.java @@ -29,12 +29,14 @@ import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.inventorygui.Element; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.inventory.ItemStack; import java.util.Map; @@ -120,6 +122,10 @@ private int collectNearbyItems(Player p, int level) { continue; } + if (!canSnatchItem(p, item)) { + continue; + } + ItemStack stack = item.getItemStack(); if (stack == null || stack.getType().isAir()) { continue; @@ -130,6 +136,12 @@ private int collectNearbyItems(Player p, int level) { continue; } + EntityPickupItemEvent pickupEvent = new EntityPickupItemEvent(p, item, 0); + Bukkit.getPluginManager().callEvent(pickupEvent); + if (pickupEvent.isCancelled()) { + continue; + } + ItemStack toChest = stack.clone(); toChest.setAmount(requestAmount); Map chestOverflow = p.getEnderChest().addItem(toChest); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java index c1af34994..76288dc83 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoy.java @@ -279,6 +279,10 @@ private void redirectAggro(Player owner, LivingEntity target, int level) { continue; } + if (isProtectedFriendly(owner, mob)) { + continue; + } + if (mob.getTarget() == owner || mob.hasLineOfSight(owner)) { mob.setTarget(target); getPlayer(owner).getData().addStat("stealth.shadow-decoy.mobs-distracted", 1); diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java index 2d913134a..1d2ce7731 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthShadowDecoyPackets.java @@ -309,29 +309,29 @@ private PacketDecoyBridge() throws ReflectiveOperationException { Class setEquipmentPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket"); Class craftItemStackClass = Class.forName(craftPackage + ".inventory.CraftItemStack"); - this.craftServerGetServer = craftServerClass.getMethod("getServer"); - this.craftWorldGetHandle = craftWorldClass.getMethod("getHandle"); - this.craftPlayerGetHandle = craftPlayerClass.getMethod("getHandle"); + this.craftServerGetServer = findMethod(craftServerClass, "getServer"); + this.craftWorldGetHandle = findMethod(craftWorldClass, "getHandle"); + this.craftPlayerGetHandle = findMethod(craftPlayerClass, "getHandle"); this.serverPlayerConstructor = serverPlayerClass.getConstructor(minecraftServerClass, serverLevelClass, gameProfileClass, clientInformationClass); - this.clientInformationCreateDefault = clientInformationClass.getMethod("createDefault"); + this.clientInformationCreateDefault = findMethod(clientInformationClass, "createDefault"); this.gameProfileBasicConstructor = findConstructor(gameProfileClass, UUID.class, String.class); this.gameProfileWithPropertiesConstructor = findOptionalConstructor(gameProfileClass, UUID.class, String.class, findPropertyMapClass(gameProfileClass)); this.gameProfilePropertiesAccessor = findOptionalMethod(gameProfileClass, "properties", "getProperties"); - this.playerGetGameProfile = nmsPlayerClass.getMethod("getGameProfile"); - - this.entitySetPos = entityClass.getMethod("setPos", double.class, double.class, double.class); - this.entitySetRot = entityClass.getMethod("setRot", float.class, float.class); - this.entitySetOnGround = entityClass.getMethod("setOnGround", boolean.class); - this.livingSetYHeadRot = livingEntityClass.getMethod("setYHeadRot", float.class); - this.livingSetYBodyRot = livingEntityClass.getMethod("setYBodyRot", float.class); - this.entityGetId = entityClass.getMethod("getId"); - this.entityGetType = entityClass.getMethod("getType"); - this.entityGetEntityData = entityClass.getMethod("getEntityData"); - this.synchedEntityDataGetNonDefaultValues = synchedEntityDataClass.getMethod("getNonDefaultValues"); + this.playerGetGameProfile = findMethod(nmsPlayerClass, "getGameProfile"); + + this.entitySetPos = findMethod(entityClass, "setPos", double.class, double.class, double.class); + this.entitySetRot = findMethod(entityClass, "setRot", float.class, float.class); + this.entitySetOnGround = findMethod(entityClass, "setOnGround", boolean.class); + this.livingSetYHeadRot = findMethod(livingEntityClass, "setYHeadRot", float.class); + this.livingSetYBodyRot = findMethod(livingEntityClass, "setYBodyRot", float.class); + this.entityGetId = findMethod(entityClass, "getId"); + this.entityGetType = findMethod(entityClass, "getType"); + this.entityGetEntityData = findMethod(entityClass, "getEntityData"); + this.synchedEntityDataGetNonDefaultValues = findMethod(synchedEntityDataClass, "getNonDefaultValues"); this.synchedEntityDataPackAll = findOptionalMethod(synchedEntityDataClass, "packAll", new Class[0]); - this.synchedEntityDataSet = synchedEntityDataClass.getMethod("set", entityDataAccessorClass, Object.class); + this.synchedEntityDataSet = findMethod(synchedEntityDataClass, "set", entityDataAccessorClass, Object.class); this.playerInfoCreateSingleInitializing = findOptionalMethod(playerInfoPacketClass, "createSinglePlayerInitializing", serverPlayerClass, boolean.class); this.playerInfoActionConstructor = findOptionalConstructor(playerInfoPacketClass, playerInfoActionEnumClass, serverPlayerClass); @@ -349,16 +349,16 @@ private PacketDecoyBridge() throws ReflectiveOperationException { this.rotateHeadConstructor = rotateHeadPacketClass.getConstructor(entityClass, byte.class); this.removeEntitiesConstructor = removeEntitiesPacketClass.getConstructor(int[].class); this.playerInfoRemoveConstructor = playerInfoRemovePacketClass.getConstructor(List.class); - this.entityPositionSyncOf = entityPositionSyncPacketClass.getMethod("of", entityClass); + this.entityPositionSyncOf = findMethod(entityPositionSyncPacketClass, "of", entityClass); this.hurtAnimationConstructor = findOptionalConstructor(hurtAnimationPacketClass, int.class, float.class); this.setEquipmentConstructor = findOptionalConstructor(setEquipmentPacketClass, int.class, List.class); - this.pairOfMethod = pairClass.getMethod("of", Object.class, Object.class); - this.craftItemStackAsNmsCopy = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class); + this.pairOfMethod = findMethod(pairClass, "of", Object.class, Object.class); + this.craftItemStackAsNmsCopy = findMethod(craftItemStackClass, "asNMSCopy", ItemStack.class); this.equipmentSlotClass = equipmentSlotClass; this.avatarModelCustomizationAccessor = avatarClass.getField("DATA_PLAYER_MODE_CUSTOMISATION"); this.serverPlayerConnectionField = serverPlayerClass.getField("connection"); - this.connectionSendPacket = connectionClass.getMethod("send", packetClass); + this.connectionSendPacket = findMethod(connectionClass, "send", packetClass); this.supported = true; } @@ -443,11 +443,29 @@ private static Constructor findOptionalConstructor(Class type, Class... } } + private static Method findMethod(Class type, String name, Class... parameterTypes) throws NoSuchMethodException { + try { + return type.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + Class current = type; + while (current != null) { + try { + Method method = current.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ignored) { + } + + current = current.getSuperclass(); + } + + throw e; + } + } + private static Method findOptionalMethod(Class type, String name, Class... parameterTypes) { try { - Method method = type.getMethod(name, parameterTypes); - method.setAccessible(true); - return method; + return findMethod(type, name, parameterTypes); } catch (NoSuchMethodException e) { return null; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java index bd9fd302c..137e45c3f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/stealth/StealthSnatch.java @@ -42,7 +42,6 @@ import java.util.HashSet; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; @@ -92,9 +91,6 @@ public void on(PlayerToggleSneakEvent e) { if (!hasActiveAdaptation(p)) { return; } - if (!canAccessChest(p, p.getLocation())) { - return; - } if (e.isSneaking()) { snatch(p); } @@ -107,36 +103,41 @@ private void snatch(Player player) { return; } + if (!canAccessChest(player, player.getLocation())) { + return; + } + double range = getRange(factor); HashSet items = new HashSet<>(); for (Entity droppedItemEntity : player.getWorld().getNearbyEntities(player.getLocation(), range, range / 1.5, range)) { - if (droppedItemEntity instanceof Item droppedItem) { - if (droppedItem.getPickupDelay() <= 0 || droppedItem.getTicksLived() > 1) { - UUID owner = droppedItem.getOwner(); - if (owner == null || owner.equals(player.getUniqueId())) - items.add(droppedItem); - } + if (droppedItemEntity instanceof Item droppedItem && canSnatchItem(player, droppedItem)) { + items.add(droppedItem); } } for (Item droppedItemEntity : items) { - if (!holds.contains(droppedItemEntity.getEntityId())) { - double dist = droppedItemEntity.getLocation().distanceSquared(player.getLocation()); - if (dist < range * range) { - ItemStack is = droppedItemEntity.getItemStack().clone(); - - if (Inventories.hasSpace(player.getInventory(), is)) { - holds.add(droppedItemEntity.getEntityId()); - SoundPlayer spw = SoundPlayer.of(player.getWorld()); - spw.play(player.getLocation(), Sound.BLOCK_LAVA_POP, 1f, (float) (1.0 + (ThreadLocalRandom.current().nextDouble() / 3D))); - safeGiveItem(player, droppedItemEntity, is); - getPlayer(player).getData().addStat("stealth.snatch.items-snatched", 1); - //sendCollected(player, droppedItemEntity); - int id = droppedItemEntity.getEntityId(); - J.runEntity(player, () -> holds.remove(Integer.valueOf(id)), 1); - } - } + if (holds.contains(droppedItemEntity.getEntityId())) { + continue; + } + + double dist = droppedItemEntity.getLocation().distanceSquared(player.getLocation()); + if (dist >= range * range) { + continue; + } + + ItemStack is = droppedItemEntity.getItemStack().clone(); + if (!Inventories.hasSpace(player.getInventory(), is)) { + continue; + } + + holds.add(droppedItemEntity.getEntityId()); + int id = droppedItemEntity.getEntityId(); + if (safeGiveItem(player, droppedItemEntity, is)) { + SoundPlayer spw = SoundPlayer.of(player.getWorld()); + spw.play(player.getLocation(), Sound.BLOCK_LAVA_POP, 1f, (float) (1.0 + (ThreadLocalRandom.current().nextDouble() / 3D))); + getPlayer(player).getData().addStat("stealth.snatch.items-snatched", 1); } + J.runEntity(player, () -> holds.remove(Integer.valueOf(id)), 1); } } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java index d1a1abc58..6b706804f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulDeathSense.java @@ -97,6 +97,10 @@ public void onTick() { continue; } + if (isProtectedFriendly(p, monster)) { + continue; + } + IAttribute attribute = Version.get().getAttribute(monster, Attributes.GENERIC_MAX_HEALTH); double maxHealth = attribute == null ? 20D : attribute.getValue(); if (maxHealth <= 0 || monster.getHealth() / maxHealth > threshold) { diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java index b9e1c72e1..f55c5671b 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulPlagueBearer.java @@ -113,7 +113,7 @@ public void on(EntityDamageByEntityEvent e) { Player owner = p; withAdaptedPlayer(owner, e, () -> { - if (getActiveLevel(owner) <= 0) { + if (getActiveLevel(owner) <= 0 || isProtectedFriendly(owner, monster)) { return; } diff --git a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java index 71d07e85a..f664a714c 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/tragoul/TragoulSkeletalServant.java @@ -105,6 +105,10 @@ public class TragoulSkeletalServant extends SimpleAdaptation> servants = new ConcurrentHashMap<>(); private final Map cooldowns = new ConcurrentHashMap<>(); + + public static boolean isServant(org.bukkit.entity.Entity entity) { + return entity.getPersistentDataContainer().has(SERVANT_KEY, PersistentDataType.STRING); + } private final Map threats = new ConcurrentHashMap<>(); private final Map servantThornsCooldowns = new ConcurrentHashMap<>(); private final Map servantCurseCooldowns = new ConcurrentHashMap<>(); @@ -839,7 +843,7 @@ private int getDurationTicks(int level) { } private long getCooldownMillis(int level) { - return Math.max(5000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); + return Math.max(1000L, (long) Math.round(getConfig().cooldownMillisBase - (getLevelPercent(level) * getConfig().cooldownMillisFactor))); } @Override @@ -897,9 +901,9 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Additional servant lifetime ticks granted at max level.", impact = "Higher values increase the level-scaled lifetime growth.") double durationTicksFactor = 800; @art.arcane.adapt.util.config.ConfigDoc(value = "Summon cooldown in milliseconds before level scaling.", impact = "Higher values slow how often a servant can be summoned.") - double cooldownMillisBase = 90000; + double cooldownMillisBase = 10000; @art.arcane.adapt.util.config.ConfigDoc(value = "Cooldown reduction in milliseconds granted at max level.", impact = "Higher values let high levels summon more often.") - double cooldownMillisFactor = 45000; + double cooldownMillisFactor = 9000; @art.arcane.adapt.util.config.ConfigDoc(value = "Living servants allowed per adaptation level.", impact = "Higher values let one necromancer field a larger pack of servants.") double servantCapPerLevel = 1.0; @art.arcane.adapt.util.config.ConfigDoc(value = "Replaces the oldest living servant when summoning at the cap.", impact = "False quietly refuses the summon instead of recycling the oldest servant.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java index 688a85ad2..220480504 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedDisarm.java @@ -19,6 +19,7 @@ package art.arcane.adapt.content.adaptation.unarmed; import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.content.adaptation.tragoul.TragoulSkeletalServant; import art.arcane.adapt.api.advancement.AdaptAdvancement; import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; @@ -84,6 +85,7 @@ public UnarmedDisarm() { public void addStats(int level, Element v) { v.addLore(C.GREEN + "+ " + Form.pc(getChance(level), 0) + C.GRAY + " " + Localizer.dLocalize("unarmed.disarm.lore1")); v.addLore(C.YELLOW + "* " + Form.duration((double) getConfig().targetCooldownMillis, 1) + C.GRAY + " " + Localizer.dLocalize("unarmed.disarm.lore2")); + v.addLore(C.GREEN + "+ " + Form.pc(getConfig().mobArmorDropChance, 0) + C.GRAY + " " + Localizer.dLocalize("unarmed.disarm.lore3")); } @EventHandler(priority = EventPriority.HIGHEST) @@ -102,7 +104,8 @@ public void on(EntityDamageByEntityEvent e) { return; } - if (victim instanceof Player && !getConfig().allowDisarmPlayers) { + boolean victimIsPlayer = victim instanceof Player; + if (victimIsPlayer && !getConfig().allowDisarmPlayers) { return; } @@ -116,6 +119,10 @@ public void on(EntityDamageByEntityEvent e) { return; } + if (TragoulSkeletalServant.isServant(victim)) { + return; + } + EntityEquipment equipment = victim.getEquipment(); if (equipment == null) { return; @@ -123,19 +130,34 @@ public void on(EntityDamageByEntityEvent e) { ItemStack main = equipment.getItemInMainHand(); ItemStack off = equipment.getItemInOffHand(); - ItemStack knocked; + ItemStack knocked = null; if (isItem(main)) { knocked = main.clone(); equipment.setItemInMainHand(null); } else if (isItem(off) && off.getType() == Material.SHIELD) { knocked = off.clone(); equipment.setItemInOffHand(null); - } else { + } + + ItemStack armor = null; + if (!victimIsPlayer && ThreadLocalRandom.current().nextDouble() < getConfig().mobArmorDropChance) { + armor = takeRandomArmorPiece(equipment); + } + + if (knocked == null && armor == null) { return; } - Item dropped = victim.getWorld().dropItemNaturally(victim.getLocation().add(0, 0.4, 0), knocked); - dropped.setPickupDelay(getConfig().pickupDelayTicks); + if (knocked != null) { + Item dropped = victim.getWorld().dropItemNaturally(victim.getLocation().add(0, 0.4, 0), knocked); + dropped.setPickupDelay(getConfig().pickupDelayTicks); + } + + if (armor != null) { + Item droppedArmor = victim.getWorld().dropItemNaturally(victim.getLocation().add(0, 0.4, 0), armor); + droppedArmor.setPickupDelay(getConfig().pickupDelayTicks); + } + targetLockUntil.put(victim.getUniqueId(), now + getConfig().targetCooldownMillis); SoundPlayer sp = SoundPlayer.of(victim.getWorld()); @@ -157,6 +179,37 @@ private double getChance(int level) { return Math.min(1, getConfig().chanceBase + (getLevelPercent(level) * getConfig().chanceFactor)); } + private ItemStack takeRandomArmorPiece(EntityEquipment equipment) { + ItemStack[] armor = equipment.getArmorContents(); + int worn = 0; + for (ItemStack piece : armor) { + if (isItem(piece)) { + worn++; + } + } + + if (worn == 0) { + return null; + } + + int pick = ThreadLocalRandom.current().nextInt(worn); + for (int i = 0; i < armor.length; i++) { + if (!isItem(armor[i])) { + continue; + } + + if (pick == 0) { + ItemStack taken = armor[i].clone(); + armor[i] = null; + equipment.setArmorContents(armor); + return taken; + } + pick--; + } + + return null; + } + @Override public void onTick() { long now = System.currentTimeMillis(); @@ -189,7 +242,9 @@ protected static class Config { @art.arcane.adapt.util.config.ConfigDoc(value = "Scaling factor applied to higher adaptation levels.", impact = "Higher values increase level-to-level cost growth.") double costFactor = 0.55; @art.arcane.adapt.util.config.ConfigDoc(value = "Allows disarming other players, not just mobs.", impact = "True lets bare-hand hits knock items out of player hands in PVP.") - boolean allowDisarmPlayers = false; + boolean allowDisarmPlayers = true; + @art.arcane.adapt.util.config.ConfigDoc(value = "Chance that a successful disarm against a mob also knocks loose a worn armor piece.", impact = "Higher values strip mob armor faster; players never lose armor.") + double mobArmorDropChance = 0.5; @art.arcane.adapt.util.config.ConfigDoc(value = "Base chance for a bare-hand hit to disarm the target.", impact = "Higher values make disarms proc more often at level 1.") double chanceBase = 0.04; @art.arcane.adapt.util.config.ConfigDoc(value = "Additional disarm chance granted at max level.", impact = "Higher values make disarms proc more often as levels increase.") diff --git a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java index ef2a27e6c..d51bb684f 100644 --- a/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java +++ b/src/main/java/art/arcane/adapt/content/adaptation/unarmed/UnarmedSecondWind.java @@ -96,6 +96,10 @@ public void on(EntityDeathEvent e) { return; } + if (isProtectedFriendly(p, victim)) { + return; + } + if (isTool(p.getInventory().getItemInMainHand()) || isTool(p.getInventory().getItemInOffHand())) { return; } diff --git a/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreBridge.java b/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreBridge.java new file mode 100644 index 000000000..2379b76ae --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreBridge.java @@ -0,0 +1,254 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.integration.hiddenore; + +import art.arcane.adapt.Adapt; +import art.arcane.adapt.AdaptConfig; +import art.arcane.adapt.api.adaptation.Adaptation; +import art.arcane.adapt.api.skill.Skill; +import art.arcane.adapt.api.skill.SkillRegistry; +import art.arcane.adapt.content.adaptation.pickaxe.PickaxeAutosmelt; +import art.arcane.adapt.content.adaptation.pickaxe.PickaxeDropToInventory; +import art.arcane.adapt.content.adaptation.pickaxe.PickaxeGemPolish; +import art.arcane.adapt.util.common.misc.SoundPlayer; +import art.arcane.hiddenore.HiddenOre; +import art.arcane.hiddenore.api.HiddenOreAPI; +import art.arcane.hiddenore.api.HiddenVein; +import art.arcane.hiddenore.api.event.HiddenOreDropsEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +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.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public final class HiddenOreBridge implements Listener { + static HiddenOreLink.VeinTarget nearestVein(Location origin, int range) { + HiddenOreAPI api = api(); + if (api == null) { + return null; + } + + HiddenVein best = null; + double bestDistanceSq = Double.MAX_VALUE; + for (HiddenVein vein : api.veinsNear(origin, range)) { + double distanceSq = distanceSquared(origin, vein); + if (distanceSq < bestDistanceSq) { + best = vein; + bestDistanceSq = distanceSq; + } + } + + if (best == null) { + return null; + } + return toTarget(origin.getWorld(), best); + } + + static List veins(Location origin, int radius) { + HiddenOreAPI api = api(); + if (api == null) { + return List.of(); + } + + List found = api.veinsNear(origin, radius); + if (found.isEmpty()) { + return List.of(); + } + List result = new ArrayList<>(found.size()); + for (HiddenVein vein : found) { + result.add(toTarget(origin.getWorld(), vein)); + } + return result; + } + + static List veinSiblings(Block block) { + HiddenOreAPI api = api(); + if (api == null) { + return List.of(); + } + + List siblings = api.veinSiblings(block); + if (siblings.isEmpty()) { + return List.of(); + } + World world = block.getWorld(); + List result = new ArrayList<>(siblings.size()); + for (HiddenVein vein : siblings) { + result.add(world.getBlockAt(vein.x(), vein.y(), vein.z())); + } + return result; + } + + @EventHandler + public void on(HiddenOreDropsEvent e) { + Player p = e.getPlayer(); + applyGemPolish(p, e); + applyAutosmelt(p, e); + applyDropToInventory(p, e); + awardOreXp(p, e); + } + + private void applyGemPolish(Player p, HiddenOreDropsEvent e) { + HiddenVein vein = e.getVein(); + if (vein == null) { + return; + } + + Material gem = switch (vein.item()) { + case DIAMOND, EMERALD, LAPIS_LAZULI -> vein.item(); + default -> null; + }; + if (gem == null) { + return; + } + + PickaxeGemPolish gemPolish = adaptation(PickaxeGemPolish.class); + if (gemPolish == null) { + return; + } + int level = gemPolish.getActiveLevel(p); + if (level <= 0) { + return; + } + + e.setExperience(e.getExperience() + gemPolish.getBonusXp(level)); + if (ThreadLocalRandom.current().nextDouble() < gemPolish.getGemChance(level)) { + e.getDrops().add(new ItemStack(gem)); + Adapt.instance.getAdaptServer().getPlayer(p).getData().addStat("pickaxe.gem-polish.gems-polished", 1); + if (particlesEnabled()) { + e.getBlock().getWorld().spawnParticle(Particle.HAPPY_VILLAGER, e.getBlock().getLocation().add(0.5, 0.5, 0.5), 6, 0.25, 0.25, 0.25); + } + } + } + + private void applyAutosmelt(Player p, HiddenOreDropsEvent e) { + PickaxeAutosmelt autosmelt = adaptation(PickaxeAutosmelt.class); + if (autosmelt == null || autosmelt.getActiveLevel(p) <= 0) { + return; + } + + boolean smelted = false; + List drops = e.getDrops(); + for (int i = 0; i < drops.size(); i++) { + ItemStack stack = drops.get(i); + if (stack == null) { + continue; + } + Material result = switch (stack.getType()) { + case RAW_IRON -> Material.IRON_INGOT; + case RAW_GOLD -> Material.GOLD_INGOT; + case RAW_COPPER -> Material.COPPER_INGOT; + default -> null; + }; + if (result == null) { + continue; + } + drops.set(i, new ItemStack(result, stack.getAmount())); + smelted = true; + } + + if (!smelted) { + return; + } + Adapt.instance.getAdaptServer().getPlayer(p).getData().addStat("pickaxe.autosmelt.ores-smelted", 1); + Location at = e.getBlock().getLocation(); + if (soundsEnabled()) { + SoundPlayer.of(e.getBlock().getWorld()).play(at, Sound.BLOCK_LAVA_POP, 1, 1); + } + if (particlesEnabled()) { + e.getBlock().getWorld().spawnParticle(Particle.LAVA, at, 3, 0.5, 0.5, 0.5); + } + } + + private void applyDropToInventory(Player p, HiddenOreDropsEvent e) { + PickaxeDropToInventory dropToInventory = adaptation(PickaxeDropToInventory.class); + if (dropToInventory != null && dropToInventory.getActiveLevel(p) > 0) { + e.setToInventory(true); + } + } + + private void awardOreXp(Player p, HiddenOreDropsEvent e) { + HiddenVein vein = e.getVein(); + if (vein == null) { + return; + } + Material display = vein.oreDisplay() != null ? vein.oreDisplay() : vein.item(); + Skill pickaxe = SkillRegistry.skills.get("pickaxe"); + if (pickaxe == null) { + return; + } + double value = pickaxe.getValue(display); + if (value <= 0) { + return; + } + pickaxe.xp(p, e.getBlock().getLocation(), value); + } + + private static T adaptation(Class type) { + Skill pickaxe = SkillRegistry.skills.get("pickaxe"); + if (pickaxe == null) { + return null; + } + for (Adaptation adaptation : pickaxe.getAdaptations()) { + if (type.isInstance(adaptation)) { + return type.cast(adaptation); + } + } + return null; + } + + private static HiddenOreAPI api() { + if (!(Bukkit.getPluginManager().getPlugin("HiddenOre") instanceof HiddenOre hiddenOre)) { + return null; + } + return hiddenOre.getApi(); + } + + private static HiddenOreLink.VeinTarget toTarget(World world, HiddenVein vein) { + Material display = vein.oreDisplay() != null ? vein.oreDisplay() : vein.item(); + return new HiddenOreLink.VeinTarget(new Location(world, vein.x(), vein.y(), vein.z()), display); + } + + private static double distanceSquared(Location origin, HiddenVein vein) { + double dx = (vein.x() + 0.5) - origin.getX(); + double dy = (vein.y() + 0.5) - origin.getY(); + double dz = (vein.z() + 0.5) - origin.getZ(); + return (dx * dx) + (dy * dy) + (dz * dz); + } + + private static boolean particlesEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isParticlesEnabled(); + } + + private static boolean soundsEnabled() { + AdaptConfig.Effects effects = AdaptConfig.get().getEffects(); + return effects == null || effects.isSoundsEnabled(); + } +} diff --git a/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreLink.java b/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreLink.java new file mode 100644 index 000000000..2f6ea32c0 --- /dev/null +++ b/src/main/java/art/arcane/adapt/content/integration/hiddenore/HiddenOreLink.java @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------------ + - Adapt is a Skill/Integration plugin for Minecraft Bukkit Servers + - Copyright (c) 2022 Arcane Arts (Volmit Software) + - + - 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 art.arcane.adapt.content.integration.hiddenore; + +import art.arcane.adapt.Adapt; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; + +import java.util.List; + +public final class HiddenOreLink { + private static volatile boolean active = false; + + private HiddenOreLink() { + } + + public record VeinTarget(Location location, Material display) { + } + + public static void activate(Adapt plugin) { + plugin.registerListener(new HiddenOreBridge()); + active = true; + Adapt.info("HiddenOre link active: seismic ping, quarry sense, veinminer + drop transforms wired to hidden veins"); + } + + public static boolean isActive() { + return active; + } + + public static VeinTarget nearestVein(Location origin, int range) { + if (!active) { + return null; + } + return HiddenOreBridge.nearestVein(origin, range); + } + + public static List veins(Location origin, int radius) { + if (!active) { + return List.of(); + } + return HiddenOreBridge.veins(origin, radius); + } + + public static List veinSiblings(Block block) { + if (!active) { + return List.of(); + } + return HiddenOreBridge.veinSiblings(block); + } +} diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java index 0262d2a5b..dedab1ede 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillArchitect.java @@ -24,6 +24,8 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.xp.XpNovelty; +import art.arcane.adapt.api.xp.XpProvenance; import art.arcane.adapt.content.adaptation.architect.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -208,7 +210,12 @@ public void on(BlockPlaceEvent e) { handleBlockCooldown(p, () -> { try { - xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), getConfig().xpBase + v)); + double integrity = XpProvenance.placeXpMultiplier(e.getBlock()); + if (integrity <= 0) { + return; + } + double adjacency = XpNovelty.adjacencyBonusMultiplier(p, e.getBlock()); + xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), getConfig().xpBase + v) * integrity * adjacency); } catch (Exception ignored) { Adapt.verbose("Failed to give XP to " + p.getName() + " for placing " + e.getBlock().getType().name()); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java index cada80007..8bd0a0b2b 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillAxes.java @@ -23,6 +23,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.xp.XpProvenance; import art.arcane.adapt.content.adaptation.axe.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -220,7 +221,12 @@ public void on(BlockBreakEvent e) { AdaptPlayer a = getPlayer(p); a.getData().addStat("axes.blocks.broken", 1); a.getData().addStat("axes.blocks.value", getValue(e.getBlock().getBlockData())); - handleCooldown(p, () -> xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v))); + handleCooldown(p, () -> { + if (XpProvenance.breakXpMultiplier(e.getBlock()) <= 0) { + return; + } + xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v)); + }); } if (e.getBlock().getType().name().endsWith("_LEAVES")) { getPlayer(p).getData().addStat("axes.leaves", 1); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java index 4b0e1cf53..51fb0c648 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillExcavation.java @@ -23,6 +23,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.xp.XpProvenance; import art.arcane.adapt.content.adaptation.excavation.ExcavationBurrow; import art.arcane.adapt.content.adaptation.excavation.ExcavationDowsing; import art.arcane.adapt.content.adaptation.excavation.ExcavationDropToInventory; @@ -252,6 +253,9 @@ private void handleBlockBreakWithShovel(Player p, BlockBreakEvent e) { if (cooldown != null && cooldown + getConfig().cooldownDelay > System.currentTimeMillis()) return; cooldowns.put(p.getUniqueId(), System.currentTimeMillis()); + if (XpProvenance.breakXpMultiplier(e.getBlock()) <= 0) { + return; + } double v = getValue(e.getBlock().getType()); xp(p, e.getBlock().getLocation().clone().add(0.5, 0.5, 0.5), blockXP(e.getBlock(), v)); } diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java index 8f9decc3c..955a356fe 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHerbalism.java @@ -22,6 +22,8 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.api.xp.XpNovelty; +import art.arcane.adapt.api.xp.XpProvenance; import art.arcane.adapt.content.adaptation.herbalism.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -279,7 +281,8 @@ private void handleHerbCooldown(Player p, Runnable action) { private void handleEvent(Cancellable e, Player p, Block block, String stat) { handleHerbCooldown(p, () -> { if (block.getBlockData() instanceof Ageable ageableBlock) { - xp(p, block.getLocation().clone().add(0.5, 0.5, 0.5), getConfig().harvestPerAgeXP * ageableBlock.getAge()); + double integrity = XpProvenance.harvestXpMultiplier(block) * XpNovelty.fieldCycleMultiplier(p, block); + xp(p, block.getLocation().clone().add(0.5, 0.5, 0.5), getConfig().harvestPerAgeXP * ageableBlock.getAge() * integrity); getPlayer(p).getData().addStat(stat, 1); } }); diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java index ef1680646..9ad2261dc 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillHunter.java @@ -23,7 +23,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.version.Version; +import art.arcane.adapt.content.adaptation.excavation.ExcavationGraveDigger; import art.arcane.adapt.content.adaptation.hunter.*; +import art.arcane.adapt.content.adaptation.tragoul.TragoulSkeletalServant; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; @@ -239,6 +241,10 @@ public void on(EntityDeathEvent e) { return; } + if (TragoulSkeletalServant.isServant(e.getEntity()) || ExcavationGraveDigger.isGraveMob(e.getEntity())) { + return; + } + shouldReturnForPlayer(p, () -> { if (e.getEntity().getType().equals(EntityType.CREEPER)) { double cmult = getConfig().creeperKillMultiplier; diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java index a752dc9f6..c5cb68b6d 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillPickaxes.java @@ -23,6 +23,7 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.api.xp.XpProvenance; import art.arcane.adapt.content.adaptation.pickaxe.*; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; @@ -239,6 +240,9 @@ public void on(BlockBreakEvent e) { } handleCooldown(p, () -> { + if (XpProvenance.breakXpMultiplier(e.getBlock()) <= 0) { + return; + } if (mainHand.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { xp(p, 5); } else { diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java index 640c80219..a86ac12cc 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillStealth.java @@ -23,7 +23,9 @@ import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; import art.arcane.adapt.api.world.AdaptPlayer; +import art.arcane.adapt.content.adaptation.excavation.ExcavationGraveDigger; import art.arcane.adapt.content.adaptation.stealth.*; +import art.arcane.adapt.content.adaptation.tragoul.TragoulSkeletalServant; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; @@ -195,6 +197,11 @@ public void on(EntityDeathEvent e) { if (e.getEntity().getKiller() == null) { return; } + + if (TragoulSkeletalServant.isServant(e.getEntity()) || ExcavationGraveDigger.isGraveMob(e.getEntity())) { + return; + } + Player p = e.getEntity().getKiller(); if (p.isSneaking()) { shouldReturnForPlayer(p, () -> { diff --git a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java index e10aaae51..10c68cbdb 100644 --- a/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java +++ b/src/main/java/art/arcane/adapt/content/skill/SkillTaming.java @@ -22,7 +22,9 @@ import art.arcane.adapt.api.advancement.AdaptAdvancementFrame; import art.arcane.adapt.api.advancement.AdvancementVisibility; import art.arcane.adapt.api.skill.SimpleSkill; +import art.arcane.adapt.content.adaptation.excavation.ExcavationGraveDigger; import art.arcane.adapt.content.adaptation.taming.*; +import art.arcane.adapt.content.adaptation.tragoul.TragoulSkeletalServant; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.misc.CustomModel; @@ -217,6 +219,11 @@ public void on(EntityDeathEvent e) { if (e.getEntity().getKiller() != null) { return; } + + if (TragoulSkeletalServant.isServant(e.getEntity()) || ExcavationGraveDigger.isGraveMob(e.getEntity())) { + return; + } + if (e.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent damageEvent) { if (damageEvent.getDamager() instanceof Tameable tameable && tameable.isTamed() && tameable.getOwner() instanceof Player p) { shouldReturnForPlayer(p, () -> { diff --git a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java index 957cd15da..b653a6403 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/CustomModel.java @@ -4,6 +4,7 @@ import art.arcane.adapt.AdaptConfig; import art.arcane.adapt.api.version.Version; import art.arcane.adapt.util.common.io.Json; +import art.arcane.adapt.util.common.scheduling.J; import art.arcane.adapt.util.config.ConfigFileSupport; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.io.IO; @@ -15,26 +16,38 @@ import java.io.File; import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; import static art.arcane.adapt.Adapt.instance; public record CustomModel(Material material, int model, NamespacedKey modelKey) { public static final NamespacedKey EMPTY_KEY = NamespacedKey.minecraft("empty"); - private static UpdateChecker updateChecker = null; + private static volatile UpdateChecker updateChecker = null; + + private static UpdateChecker checker() { + UpdateChecker current = updateChecker; + if (current == null) { + synchronized (CustomModel.class) { + current = updateChecker; + if (current == null) { + current = new UpdateChecker(); + updateChecker = current; + } + } + } + + return current; + } public static CustomModel get(Material fallback, String... path) { if (!AdaptConfig.get().isCustomModels()) return new CustomModel(fallback, 0, null); - if (updateChecker == null) - updateChecker = new UpdateChecker(); - return updateChecker.get(fallback, path); + return checker().get(fallback, path); } public static void clear() { - if (updateChecker == null) - return; updateChecker = null; } @@ -43,10 +56,7 @@ public static boolean reloadFromDisk() { } public static boolean reloadFromDisk(boolean quiet) { - if (updateChecker == null) - updateChecker = new UpdateChecker(); - - return updateChecker.reloadFromDisk(quiet); + return checker().reloadFromDisk(quiet); } public ItemStack toItemStack() { @@ -65,6 +75,7 @@ public ItemStack toItemStack(ItemStack itemStack) { private static class UpdateChecker { private final Object lock = new Object(); + private final AtomicBoolean writeQueued = new AtomicBoolean(false); private final File modelsFile; private final File legacyModelsFile; private final KMap cache = new KMap<>(); @@ -99,52 +110,79 @@ public boolean reloadFromDisk(boolean quiet) { } public CustomModel get(Material fallback, String... path) { - return cache.computeIfAbsent(String.join("", path), k -> { - com.google.gson.JsonObject json = this.json; - for (java.lang.String s : path) { - if (!json.has(s)) + String key = String.join("", path); + CustomModel cached = cache.get(key); + if (cached != null) { + return cached; + } + + CustomModel resolved = resolve(fallback, path); + CustomModel raced = cache.putIfAbsent(key, resolved); + return raced != null ? raced : resolved; + } + + private CustomModel resolve(Material fallback, String... path) { + synchronized (lock) { + JsonObject node = this.json; + for (String s : path) { + if (!node.has(s)) { return set(new CustomModel(fallback, 0, EMPTY_KEY), path); - com.google.gson.JsonElement v = json.get(s); + } + + JsonElement v = node.get(s); if (!v.isJsonObject()) { Adapt.warn("Invalid json at path: " + String.join(".", path)); return new CustomModel(fallback, 0, EMPTY_KEY); } - json = v.getAsJsonObject(); + node = v.getAsJsonObject(); } return new CustomModel( - json.has("material") ? Material.valueOf(json.get("material").getAsString()) : fallback, - json.has("model") ? json.get("model").getAsInt() : 0, - json.has("modelKey") ? NamespacedKey.fromString(json.get("modelKey").getAsString()) : EMPTY_KEY + node.has("material") ? Material.valueOf(node.get("material").getAsString()) : fallback, + node.has("model") ? node.get("model").getAsInt() : 0, + node.has("modelKey") ? NamespacedKey.fromString(node.get("modelKey").getAsString()) : EMPTY_KEY ); - }); + } } public CustomModel set(CustomModel data, String... path) { - com.google.gson.JsonObject json = this.json; - for (java.lang.String s : path) { - if (!json.has(s)) - json.add(s, new JsonObject()); - - com.google.gson.JsonElement v = json.get(s); - if (!v.isJsonObject()) { - v = new JsonObject(); - json.add(s, v); + synchronized (lock) { + JsonObject node = this.json; + for (String s : path) { + if (!node.has(s)) + node.add(s, new JsonObject()); + + JsonElement v = node.get(s); + if (!v.isJsonObject()) { + v = new JsonObject(); + node.add(s, v); + } + node = v.getAsJsonObject(); } - json = v.getAsJsonObject(); + + node.addProperty("material", data.material.name()); + node.addProperty("model", data.model); + node.addProperty("modelKey", (data.modelKey == null ? EMPTY_KEY : data.modelKey).toString()); } - json.addProperty("material", data.material.name()); - json.addProperty("model", data.model); - json.addProperty("modelKey", (data.modelKey == null ? EMPTY_KEY : data.modelKey).toString()); + scheduleWrite(); + return data; + } - try { - writeFile(); - } catch (IOException e) { - Adapt.error("Failed to write models.toml"); - e.printStackTrace(); + private void scheduleWrite() { + if (!writeQueued.compareAndSet(false, true)) { + return; } - return data; + + J.a(() -> { + writeQueued.set(false); + try { + writeFile(); + } catch (IOException e) { + Adapt.error("Failed to write models.toml"); + e.printStackTrace(); + } + }); } public void readFile() throws IOException { diff --git a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java index 62c198f69..bc0780f1e 100644 --- a/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java +++ b/src/main/java/art/arcane/adapt/util/common/misc/Impulse.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; public class Impulse { private final List ignore; @@ -34,6 +35,7 @@ public class Impulse { private double forceMin; private double damageMin; private double damageMax; + private Predicate filter; public Impulse(double radius) { ignore = new ArrayList<>(); @@ -71,6 +73,11 @@ public Impulse damage(double damageMax, double damageMin) { return this; } + public Impulse filter(Predicate filter) { + this.filter = filter; + return this; + } + public void punch(Location at) { Area a = new Area(at, radius); @@ -79,6 +86,10 @@ public void punch(Location at) { continue; } + if (filter != null && !filter.test(i)) { + continue; + } + Vector force = VectorMath.direction(at, i.getLocation()); double damage = 0; double distance = i.getLocation().distance(at); diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java index f877d5dd8..de5204e97 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementDisplayWrapper_v1_21_R7.java @@ -7,6 +7,7 @@ import net.minecraft.advancements.AdvancementType; import net.minecraft.advancements.DisplayInfo; import net.minecraft.core.ClientAsset; +import net.minecraft.world.item.ItemStackTemplate; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.inventory.ItemStack; @@ -21,14 +22,14 @@ public class AdvancementDisplayWrapper_v1_21_R7 extends AdvancementDisplayWrappe public AdvancementDisplayWrapper_v1_21_R7(@NotNull ItemStack icon, @NotNull String title, @NotNull String description, @NotNull AdvancementFrameTypeWrapper frameType, float x, float y, boolean showToast, boolean announceChat, boolean hidden, @Nullable String backgroundTexture) { ClientAsset.ResourceTexture clientAsset = Util.parseBackgroundTexture(backgroundTexture); - this.display = new DisplayInfo(CraftItemStack.asTemplate(icon), Util.fromString(title), Util.fromString(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); + this.display = new DisplayInfo(ItemStackTemplate.fromNonEmptyStack(CraftItemStack.asNMSCopy(icon)), Util.fromString(title), Util.fromString(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); this.display.setLocation(x, y); this.frameType = frameType; } public AdvancementDisplayWrapper_v1_21_R7(@NotNull ItemStack icon, @NotNull BaseComponent title, @NotNull BaseComponent description, @NotNull AdvancementFrameTypeWrapper frameType, float x, float y, boolean showToast, boolean announceChat, boolean hidden, @Nullable String backgroundTexture) { ClientAsset.ResourceTexture clientAsset = Util.parseBackgroundTexture(backgroundTexture); - this.display = new DisplayInfo(CraftItemStack.asTemplate(icon), Util.fromComponent(title), Util.fromComponent(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); + this.display = new DisplayInfo(ItemStackTemplate.fromNonEmptyStack(CraftItemStack.asNMSCopy(icon)), Util.fromComponent(title), Util.fromComponent(description), Optional.ofNullable(clientAsset), (AdvancementType) frameType.toNMS(), showToast, announceChat, hidden); this.display.setLocation(x, y); this.frameType = frameType; } @@ -36,7 +37,7 @@ public AdvancementDisplayWrapper_v1_21_R7(@NotNull ItemStack icon, @NotNull Base @Override @NotNull public ItemStack getIcon() { - return CraftItemStack.asBukkitCopy(display.getIcon()); + return CraftItemStack.asBukkitCopy(display.getIcon().create()); } @Override diff --git a/src/main/resources/en_US.toml b/src/main/resources/en_US.toml index a18930abd..e72ff824b 100644 --- a/src/main/resources/en_US.toml +++ b/src/main/resources/en_US.toml @@ -4000,10 +4000,11 @@ description = "Absorb 500 familiar damage through deja vu" [unarmed.disarm] name = "Disarm" -description = "Bare-hand hits can knock the target's held item to the ground." +description = "Bare-hand hits can knock the held item out of players and mobs alike, and mobs may have a worn armor piece knocked loose too." lore1 = "Disarm Chance" lore2 = "Per-Target Cooldown" -lore = ["Disarm Chance", "Per-Target Cooldown"] +lore3 = "chance a disarmed mob also drops a worn armor piece" +lore = ["Disarm Chance", "Per-Target Cooldown", "chance a disarmed mob also drops a worn armor piece"] [unarmed.flurry] name = "Flurry" @@ -4422,3 +4423,6 @@ description = "Defy death 5 times" [advancement.challenge_tragoul_last_rites_50] title = "Unfinished Business" description = "Defy death 50 times" + +[snippets.xp] +inspired = "Inspired! Fresh activity bonus" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ca6f88871..1dac13608 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -13,6 +13,7 @@ softdepend: - Factions - ChestProtect - Residence + - HiddenOre commands: adapt: { } From 9ad0ce32a5aafc393997850be3b1f2f08ddbb644 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 14 Jun 2026 01:03:19 -0400 Subject: [PATCH 23/25] dwa --- .gitignore | 2 ++ build.gradle | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 2ce32f0b1..66bc64283 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ build/ .DS_Store .gradle/ + +.codegraph/ diff --git a/build.gradle b/build.gradle index 40c48034c..8dd265f04 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,19 @@ * along with this program. If not, see . */ +buildscript { + repositories { + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.21') + } +} + import io.freefair.gradle.plugins.lombok.LombokPlugin import io.github.slimjar.resolver.data.Mirror +import io.papermc.paperweight.userdev.PaperweightUser import org.gradle.api.tasks.Copy import org.gradle.api.tasks.compile.JavaCompile import org.gradle.jvm.toolchain.JavaLanguageVersion @@ -29,18 +40,12 @@ plugins { alias(libs.plugins.slimjar) } +apply plugin: PaperweightUser + version = '2.0.0-26.1.2' def apiVersion = '26.1' def main = 'art.arcane.adapt.Adapt' -String configuredNmsJar = findProperty('adaptNmsJar') != null ? findProperty('adaptNmsJar').toString() : System.getenv('ADAPT_NMS_JAR') -File detectedActiveInstanceVersionsDir = rootDir.toPath().resolve('../../[Minecraft Server]/active-instance/versions').normalize().toFile() -File detectedVersionedNmsJar = detectedActiveInstanceVersionsDir.exists() - ? fileTree(detectedActiveInstanceVersionsDir).matching { include('**/*.jar') }.files.toList().sort { File left, File right -> Long.compare(right.lastModified(), left.lastModified()) }.find() - : null -File detectedLauncherNmsJar = rootDir.toPath().resolve('../../[Minecraft Server]/active-instance/server.jar').normalize().toFile() -File nmsCompileJar = configuredNmsJar != null && !configuredNmsJar.isBlank() - ? file(configuredNmsJar) - : detectedVersionedNmsJar != null ? detectedVersionedNmsJar : detectedLauncherNmsJar +String paperDevBundleVersion = '26.1.2.build.66-stable' String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() @@ -118,6 +123,8 @@ allprojects { } dependencies { + paperweight.paperDevBundle(paperDevBundleVersion) + implementation(project(':velocity')) implementation(volmLibCoordinate) { changing = true @@ -134,9 +141,6 @@ dependencies { } compileOnly('io.papermc.paper:paper-api:26.1.2.build.66-stable') - if (nmsCompileJar.exists()) { - compileOnly(files(nmsCompileJar)) - } // Cancer slimApi(libs.fukkit) { From d825e959a28d83657a52fe7938352a6fcb978b22 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 17 Jun 2026 20:49:07 -0400 Subject: [PATCH 24/25] d --- .../art/arcane/adapt/api/tick/Ticker.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/art/arcane/adapt/api/tick/Ticker.java b/src/main/java/art/arcane/adapt/api/tick/Ticker.java index 495179388..b9eb42a57 100644 --- a/src/main/java/art/arcane/adapt/api/tick/Ticker.java +++ b/src/main/java/art/arcane/adapt/api/tick/Ticker.java @@ -18,6 +18,7 @@ package art.arcane.adapt.api.tick; +import art.arcane.adapt.Adapt; import art.arcane.adapt.util.common.scheduling.J; import art.arcane.volmlib.util.collection.KList; @@ -124,6 +125,7 @@ private void tick() { try { t.tick(); } catch (Throwable exxx) { + Adapt.error("Exception ticking " + t.getGroup() + ":" + t.getId()); exxx.printStackTrace(); } finally { recordMetric(t, System.nanoTime() - start); @@ -138,16 +140,16 @@ private void tick() { } synchronized (removeTicks) { - while (removeTicks.isNotEmpty()) { - String id = removeTicks.popRandom(); - - for (int i = 0; i < ticklist.size(); i++) { - if (ticklist.get(i).getId().equals(id)) { - metrics.remove(ticklist.get(i)); - ticklist.remove(i); - break; + if (removeTicks.isNotEmpty()) { + Set idsToRemove = new HashSet<>(removeTicks); + removeTicks.clear(); + ticklist.removeIf(t -> { + if (t != null && idsToRemove.contains(t.getId())) { + metrics.remove(t); + return true; } - } + return false; + }); } } From f9baf116ed87171e380102db1b92373c9382463e Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Fri, 26 Jun 2026 14:02:20 -0400 Subject: [PATCH 25/25] tests --- .gitignore | 2 + build.gradle | 25 +- gradle/libs.versions.toml | 4 +- src/main/java/art/arcane/adapt/Adapt.java | 18 +- .../util/common/plugin/VolmitSender.java | 62 ++-- .../adapt/util/project/command/Feedback.java | 15 +- .../nms/v1_21_R7/Util.java | 6 +- .../AdvancementWrapper_v1_21_R7.java | 2 +- .../PreparedAdvancementWrapper_v1_21_R7.java | 2 +- .../ultimateAdvancementAPI/util/Versions.java | 6 +- .../glowingentities/GlowingEntities.java | 283 ++++++++++++++++++ .../java/art/arcane/adapt/AdaptTestBase.java | 38 +++ .../arcane/adapt/ConcurrencyStressTest.java | 104 +++++++ .../art/arcane/adapt/LoadHarnessTest.java | 103 +++++++ .../adapt/api/EventHandlerInvokerTest.java | 107 +++++++ .../api/SkillIdentityRegressionTest.java | 47 +++ .../art/arcane/adapt/api/tick/TickerTest.java | 72 +++++ .../adapt/api/world/PlayerDataTest.java | 64 ++++ .../adapt/api/world/PlayerSkillLineTest.java | 53 ++++ .../arcane/adapt/api/xp/SpatialXpTest.java | 40 +++ .../arcane/adapt/api/xp/XPMultiplierTest.java | 44 +++ .../art/arcane/adapt/api/xp/XpMathTest.java | 50 ++++ .../arcane/adapt/api/xp/XpNoveltyTest.java | 83 +++++ 23 files changed, 1169 insertions(+), 61 deletions(-) create mode 100644 src/main/java/fr/skytasul/glowingentities/GlowingEntities.java create mode 100644 src/test/java/art/arcane/adapt/AdaptTestBase.java create mode 100644 src/test/java/art/arcane/adapt/ConcurrencyStressTest.java create mode 100644 src/test/java/art/arcane/adapt/LoadHarnessTest.java create mode 100644 src/test/java/art/arcane/adapt/api/EventHandlerInvokerTest.java create mode 100644 src/test/java/art/arcane/adapt/api/SkillIdentityRegressionTest.java create mode 100644 src/test/java/art/arcane/adapt/api/tick/TickerTest.java create mode 100644 src/test/java/art/arcane/adapt/api/world/PlayerDataTest.java create mode 100644 src/test/java/art/arcane/adapt/api/world/PlayerSkillLineTest.java create mode 100644 src/test/java/art/arcane/adapt/api/xp/SpatialXpTest.java create mode 100644 src/test/java/art/arcane/adapt/api/xp/XPMultiplierTest.java create mode 100644 src/test/java/art/arcane/adapt/api/xp/XpMathTest.java create mode 100644 src/test/java/art/arcane/adapt/api/xp/XpNoveltyTest.java diff --git a/.gitignore b/.gitignore index 66bc64283..c804447ea 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ build/ .gradle/ .codegraph/ + +TEST_SUITE_PLAN.md diff --git a/build.gradle b/build.gradle index 8dd265f04..dea81c8f7 100644 --- a/build.gradle +++ b/build.gradle @@ -42,10 +42,10 @@ plugins { apply plugin: PaperweightUser -version = '2.0.0-26.1.2' -def apiVersion = '26.1' +version = '2.0.0-26.2' +def apiVersion = '26.2' def main = 'art.arcane.adapt.Adapt' -String paperDevBundleVersion = '26.1.2.build.66-stable' +String paperDevBundleVersion = '26.2.build.25-alpha' String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() @@ -140,7 +140,7 @@ dependencies { transitive = false } - compileOnly('io.papermc.paper:paper-api:26.1.2.build.66-stable') + compileOnly('io.papermc.paper:paper-api:26.2.build.25-alpha') // Cancer slimApi(libs.fukkit) { @@ -179,6 +179,23 @@ dependencies { compileOnlyApi('com.github.TechFortress:GriefPrevention:16.18.1') compileOnlyApi('com.griefdefender:api:2.1.0-SNAPSHOT') compileOnlyApi(fileTree('libs') { include('*.jar') }) + + testImplementation('io.papermc.paper:paper-api:26.2.build.25-alpha') + testImplementation('org.junit.jupiter:junit-jupiter:5.11.4') + testImplementation('org.mockito:mockito-core:5.14.2') + testImplementation('org.mockito:mockito-junit-jupiter:5.14.2') + testImplementation('org.assertj:assertj-core:3.26.3') + testRuntimeOnly('org.junit.platform:junit-platform-launcher') +} + +tasks.named('test', Test) { + useJUnitPlatform() + jvmArgs('-Dnet.bytebuddy.experimental=true') + testLogging { + events('passed', 'skipped', 'failed') + showStandardStreams = true + exceptionFormat = 'full' + } } def lib = 'art.arcane.adapt.util' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d3035ee60..5d2cebedc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,11 +15,11 @@ chrono = "22.9.10" spatial = "22.11.2" velocity = "3.4.0-SNAPSHOT" -spigot = "26.1.2-R0.1-SNAPSHOT" +spigot = "26.2-R0.1-SNAPSHOT" platformUtils = "e17ac2c698" # Dynamically Loaded -adventure-api = "4.24.0" # https://github.com/KyoriPowered/adventure +adventure-api = "5.1.1" # https://github.com/KyoriPowered/adventure adventure-platform = "4.4.1" # https://github.com/KyoriPowered/adventure-platform lettuce = "6.5.1.RELEASE" diff --git a/src/main/java/art/arcane/adapt/Adapt.java b/src/main/java/art/arcane/adapt/Adapt.java index aac6e6f2b..e606f6c9b 100644 --- a/src/main/java/art/arcane/adapt/Adapt.java +++ b/src/main/java/art/arcane/adapt/Adapt.java @@ -34,7 +34,13 @@ import art.arcane.adapt.api.xp.XpNoveltyListener; import art.arcane.adapt.api.xp.XpProvenanceListener; import art.arcane.adapt.content.integration.hiddenore.HiddenOreLink; -import art.arcane.adapt.content.protector.*; +import art.arcane.adapt.content.protector.ChestProtectProtector; +import art.arcane.adapt.content.protector.FactionsClaimProtector; +import art.arcane.adapt.content.protector.GriefDefenderProtector; +import art.arcane.adapt.content.protector.GriefPreventionProtector; +import art.arcane.adapt.content.protector.LocketteProProtector; +import art.arcane.adapt.content.protector.ResidenceProtector; +import art.arcane.adapt.content.protector.WorldGuardProtector; import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.format.Localizer; import art.arcane.adapt.util.common.io.SQLManager; @@ -64,12 +70,16 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.annotation.Annotation; import java.net.URL; +import java.text.MessageFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import java.text.MessageFormat; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -608,7 +618,7 @@ private void startupPrint() { if (!AdaptConfig.get().isSplashScreen()) { return; } - String supportedMcVersion = "1.21.11"; + String supportedMcVersion = "26.2"; Random r = new Random(); int game = r.nextInt(100); if (game < 90) { diff --git a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java index fdec87eac..eef5ee160 100644 --- a/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java +++ b/src/main/java/art/arcane/adapt/util/common/plugin/VolmitSender.java @@ -298,24 +298,40 @@ private Component createNoPrefixComponentNoProcessing(String message) { } private Component createComponent(String message) { + return AdventureCompat.deserialize(createMiniMessage(message)); + } + + private String createMiniMessage(String message) { if (!canUseCustomColors(this)) { String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); - return AdventureCompat.deserialize(t); + return t; } String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - return AdventureCompat.deserialize(a); + return C.aura(t, spinh, spins, spinb); } private Component createComponentRaw(String message) { + return AdventureCompat.deserialize(createMiniMessageRaw(message)); + } + + private String createMiniMessageRaw(String message) { if (!canUseCustomColors(this)) { String t = C.translateAlternateColorCodes('&', AdventureCompat.stripTags(getTag() + message)); - return AdventureCompat.deserialize(t); + return t; } String t = C.translateAlternateColorCodes('&', getTag() + message); - return AdventureCompat.deserialize(t); + return t; + } + + private boolean deliverRichMessage(String miniMessage) { + try { + s.getClass().getMethod("sendRichMessage", String.class).invoke(s, miniMessage); + return true; + } catch (Throwable ignored) { + return false; + } } public void showWaiting(String passive, CompletableFuture f) { @@ -357,21 +373,11 @@ public void sendMessage(String message) { return; } - try { - Adapt.audiences.sender(s).sendMessage(createComponent(message)); - } catch (Throwable e) { - System.out.println("============================================="); - e.printStackTrace(); - if (e.getCause() != null) { - e.getCause().printStackTrace(); - } - System.out.println("============================================="); - String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - - Adapt.debug("Failure to parse " + a); - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + if (deliverRichMessage(createMiniMessage(message))) { + return; } + + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); } public void sendMessageBasic(String message) { @@ -393,21 +399,11 @@ public void sendMessageRaw(String message) { return; } - try { - Adapt.audiences.sender(s).sendMessage(createComponentRaw(message)); - } catch (Throwable e) { - String t = C.translateAlternateColorCodes('&', getTag() + message); - String a = C.aura(t, spinh, spins, spinb); - - System.out.println("============================================="); - e.printStackTrace(); - if (e.getCause() != null) { - e.getCause().printStackTrace(); - } - System.out.println("============================================="); - Adapt.debug("Failure to parse " + a); - s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); + if (deliverRichMessage(createMiniMessageRaw(message))) { + return; } + + s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); } @Override diff --git a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java index 487e7c5a6..7bcd310a3 100644 --- a/src/main/java/art/arcane/adapt/util/project/command/Feedback.java +++ b/src/main/java/art/arcane/adapt/util/project/command/Feedback.java @@ -1,14 +1,13 @@ package art.arcane.adapt.util.command; -import art.arcane.adapt.Adapt; +import art.arcane.adapt.util.common.format.C; import art.arcane.adapt.util.common.plugin.VolmitSender; import lombok.Builder; import lombok.Data; import lombok.Singular; import lombok.experimental.Accessors; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,14 +29,10 @@ public void send(CommandSender serverOrPlayer) { } } - Component prefix = Component.text("[").color(NamedTextColor.GRAY) - .append(Component.text("Adapt").color(NamedTextColor.DARK_RED)) - .append(Component.text("] ")); + LegacyComponentSerializer serializer = LegacyComponentSerializer.legacySection(); + String prefix = C.GRAY + "[" + C.ADAPT + "Adapt" + C.GRAY + "] "; for (TextComponent i : messages) { - Adapt.audiences.sender(serverOrPlayer).sendMessage(Component.text() - .append(prefix) - .append(i) - .build()); + serverOrPlayer.sendMessage(prefix + serializer.serialize(i)); } } diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java index 5e8795e4f..0066c7f30 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/Util.java @@ -7,10 +7,10 @@ import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementProgress; import net.minecraft.advancements.AdvancementRequirements; -import net.minecraft.advancements.Criterion; import net.minecraft.advancements.CriterionProgress; -import net.minecraft.advancements.criterion.ImpossibleTrigger; -import net.minecraft.advancements.criterion.ImpossibleTrigger.TriggerInstance; +import net.minecraft.advancements.triggers.Criterion; +import net.minecraft.advancements.triggers.ImpossibleTrigger; +import net.minecraft.advancements.triggers.ImpossibleTrigger.TriggerInstance; import net.minecraft.core.ClientAsset; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java index df6e381fc..69421c94b 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/AdvancementWrapper_v1_21_R7.java @@ -8,8 +8,8 @@ import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementRequirements; import net.minecraft.advancements.AdvancementRewards; -import net.minecraft.advancements.Criterion; import net.minecraft.advancements.DisplayInfo; +import net.minecraft.advancements.triggers.Criterion; import net.minecraft.resources.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java index f6a55fd0b..6904cfeec 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/nms/v1_21_R7/advancement/PreparedAdvancementWrapper_v1_21_R7.java @@ -6,7 +6,7 @@ import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.AdvancementWrapper; import com.fren_gor.ultimateAdvancementAPI.nms.wrappers.advancement.PreparedAdvancementWrapper; import net.minecraft.advancements.AdvancementRequirements; -import net.minecraft.advancements.Criterion; +import net.minecraft.advancements.triggers.Criterion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Range; diff --git a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java index c9dfd9a52..02565db5b 100644 --- a/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java +++ b/src/main/java/com/fren_gor/ultimateAdvancementAPI/util/Versions.java @@ -59,7 +59,7 @@ public final class Versions { Map.entry("v1_21_R4", List.of("1.21.5")), Map.entry("v1_21_R5", List.of("1.21.6", "1.21.7", "1.21.8")), Map.entry("v1_21_R6", List.of("1.21.9", "1.21.10")), - Map.entry("v1_21_R7", List.of("1.21.11", "26.1", "26.1.1", "26.1.2")) + Map.entry("v1_21_R7", List.of("1.21.11", "26.2")) ); private static final Map NMS_TO_FANCY = Map.ofEntries( @@ -83,7 +83,7 @@ public final class Versions { Map.entry("v1_21_R4", "1.21.5"), Map.entry("v1_21_R5", "1.21.6-1.21.8"), Map.entry("v1_21_R6", "1.21.9-1.21.10"), - Map.entry("v1_21_R7", "1.21.11, 26.1-26.1.2") + Map.entry("v1_21_R7", "1.21.11, 26.2") ); private static final List SUPPORTED_VERSIONS = SUPPORTED_NMS_VERSIONS.stream() @@ -101,7 +101,7 @@ public static String normalizeMinecraftVersion(String version) { if (version == null || version.isBlank()) { return version; } - if ("26.1".equals(version) || version.startsWith("26.1.")) { + if ("26.2".equals(version) || version.startsWith("26.2.") || version.startsWith("26.2-")) { return "1.21.11"; } return version; diff --git a/src/main/java/fr/skytasul/glowingentities/GlowingEntities.java b/src/main/java/fr/skytasul/glowingentities/GlowingEntities.java new file mode 100644 index 000000000..9c954cf62 --- /dev/null +++ b/src/main/java/fr/skytasul/glowingentities/GlowingEntities.java @@ -0,0 +1,283 @@ +package fr.skytasul.glowingentities; + +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Scoreboard; +import net.minecraft.world.scores.Team; +import net.minecraft.world.scores.TeamColor; +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +public class GlowingEntities implements Listener { + private static final byte GLOWING_FLAG = 1 << 6; + + private final @NotNull Plugin plugin; + private final EntityDataAccessor sharedFlagsAccessor; + private final Scoreboard scoreboard; + private final EnumMap teams; + + private Map glowing; + private boolean enabled; + private int uid; + + public GlowingEntities(@NotNull Plugin plugin) { + this.plugin = Objects.requireNonNull(plugin); + this.sharedFlagsAccessor = loadSharedFlagsAccessor(); + this.scoreboard = new Scoreboard(); + this.teams = new EnumMap<>(ChatColor.class); + enable(); + } + + public void enable() { + if (enabled) { + throw new IllegalStateException("The Glowing Entities API has already been enabled."); + } + + plugin.getServer().getPluginManager().registerEvents(this, plugin); + glowing = new HashMap<>(); + uid = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); + enabled = true; + } + + public void disable() { + if (!enabled) { + return; + } + + HandlerList.unregisterAll(this); + glowing.clear(); + glowing = null; + uid = 0; + enabled = false; + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + if (glowing != null) { + glowing.remove(event.getPlayer()); + } + } + + public void setGlowing(org.bukkit.entity.Entity entity, Player receiver) throws ReflectiveOperationException { + setGlowing(entity, receiver, null); + } + + public void setGlowing(org.bukkit.entity.Entity entity, Player receiver, ChatColor color) + throws ReflectiveOperationException { + String teamID = entity instanceof Player ? entity.getName() : entity.getUniqueId().toString(); + byte flags = getEntityFlags(entity); + setGlowing(entity.getEntityId(), teamID, receiver, color, flags); + } + + public void setGlowing(int entityID, String teamID, Player receiver) throws ReflectiveOperationException { + setGlowing(entityID, teamID, receiver, null, (byte) 0); + } + + public void setGlowing(int entityID, String teamID, Player receiver, ChatColor color) + throws ReflectiveOperationException { + setGlowing(entityID, teamID, receiver, color, (byte) 0); + } + + public void setGlowing(int entityID, String teamID, Player receiver, ChatColor color, byte otherFlags) + throws ReflectiveOperationException { + ensureEnabled(); + if (color != null && !color.isColor()) { + throw new IllegalArgumentException("ChatColor must be a color format"); + } + + PlayerData playerData = glowing.computeIfAbsent(receiver, PlayerData::new); + GlowingData glowingData = playerData.glowingDatas.get(entityID); + if (glowingData == null) { + glowingData = new GlowingData(entityID, teamID, color, otherFlags); + playerData.glowingDatas.put(entityID, glowingData); + } else { + if (glowingData.color != null && glowingData.color != color) { + removeGlowingColor(playerData, glowingData); + } + glowingData.teamID = teamID; + glowingData.color = color; + glowingData.otherFlags = otherFlags; + } + + sendMetadata(receiver, entityID, computeFlags(glowingData)); + if (color != null) { + setGlowingColor(playerData, glowingData); + } + } + + public void unsetGlowing(org.bukkit.entity.Entity entity, Player receiver) throws ReflectiveOperationException { + unsetGlowing(entity.getEntityId(), receiver); + } + + public void unsetGlowing(int entityID, Player receiver) throws ReflectiveOperationException { + ensureEnabled(); + + PlayerData playerData = glowing.get(receiver); + if (playerData == null) { + return; + } + + GlowingData glowingData = playerData.glowingDatas.remove(entityID); + if (glowingData == null) { + return; + } + + sendMetadata(receiver, entityID, glowingData.otherFlags); + if (glowingData.color != null) { + removeGlowingColor(playerData, glowingData); + } + } + + private void ensureEnabled() { + if (!enabled) { + throw new IllegalStateException("The Glowing Entities API is not enabled."); + } + } + + private byte getEntityFlags(org.bukkit.entity.Entity entity) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + return nmsEntity.getEntityData().get(sharedFlagsAccessor); + } + + private byte computeFlags(GlowingData glowingData) { + return (byte) (glowingData.otherFlags | GLOWING_FLAG); + } + + private void sendMetadata(Player player, int entityID, byte flags) { + SynchedEntityData.DataValue value = SynchedEntityData.DataValue.create(sharedFlagsAccessor, flags); + sendPacket(player, new ClientboundSetEntityDataPacket(entityID, List.of(value))); + } + + private void setGlowingColor(PlayerData playerData, GlowingData glowingData) { + boolean sendCreation = false; + if (playerData.sentColors == null) { + playerData.sentColors = EnumSet.of(glowingData.color); + sendCreation = true; + } else if (playerData.sentColors.add(glowingData.color)) { + sendCreation = true; + } + + TeamData teamData = getTeamData(glowingData.color); + if (sendCreation) { + sendPacket(playerData.player, ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(teamData.team, true)); + } + sendPacket(playerData.player, + ClientboundSetPlayerTeamPacket.createPlayerPacket( + teamData.team, + glowingData.teamID, + ClientboundSetPlayerTeamPacket.Action.ADD + )); + } + + private void removeGlowingColor(PlayerData playerData, GlowingData glowingData) { + TeamData teamData = teams.get(glowingData.color); + if (teamData == null) { + return; + } + + sendPacket(playerData.player, + ClientboundSetPlayerTeamPacket.createPlayerPacket( + teamData.team, + glowingData.teamID, + ClientboundSetPlayerTeamPacket.Action.REMOVE + )); + } + + private TeamData getTeamData(ChatColor color) { + TeamData teamData = teams.get(color); + if (teamData != null) { + return teamData; + } + + TeamData created = new TeamData(uid, color, scoreboard); + teams.put(color, created); + return created; + } + + private void sendPacket(Player player, Packet packet) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + serverPlayer.connection.send(packet); + } + + private static EntityDataAccessor loadSharedFlagsAccessor() { + try { + Field field = Entity.class.getDeclaredField("DATA_SHARED_FLAGS_ID"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + EntityDataAccessor accessor = (EntityDataAccessor) field.get(null); + return accessor; + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Unable to locate entity shared flags metadata accessor.", e); + } + } + + private static final class PlayerData { + private final Player player; + private final Map glowingDatas; + private EnumSet sentColors; + + private PlayerData(Player player) { + this.player = player; + this.glowingDatas = new HashMap<>(); + } + } + + private static final class GlowingData { + private final int entityID; + private String teamID; + private ChatColor color; + private byte otherFlags; + + private GlowingData(int entityID, String teamID, ChatColor color, byte otherFlags) { + this.entityID = entityID; + this.teamID = teamID; + this.color = color; + this.otherFlags = otherFlags; + } + } + + private static final class TeamData { + private final PlayerTeam team; + + private TeamData(int uid, ChatColor color, Scoreboard scoreboard) { + this.team = new PlayerTeam(scoreboard, "glow-" + uid + color.getChar()); + team.setCollisionRule(Team.CollisionRule.NEVER); + team.setColor(Optional.of(resolveTeamColor(color))); + } + + private static TeamColor resolveTeamColor(ChatColor color) { + String name = color.name().toLowerCase(Locale.ROOT); + TeamColor teamColor = TeamColor.byName(name); + if (teamColor == null) { + return TeamColor.WHITE; + } + return teamColor; + } + } +} diff --git a/src/test/java/art/arcane/adapt/AdaptTestBase.java b/src/test/java/art/arcane/adapt/AdaptTestBase.java new file mode 100644 index 000000000..98abe0515 --- /dev/null +++ b/src/test/java/art/arcane/adapt/AdaptTestBase.java @@ -0,0 +1,38 @@ +package art.arcane.adapt; + +import art.arcane.adapt.api.tick.Ticker; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.util.logging.Logger; + +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +public abstract class AdaptTestBase { + + @TempDir + protected File dataFolder; + + protected Adapt plugin; + protected Ticker ticker; + private Adapt previousInstance; + + @BeforeEach + void installMockPlugin() { + previousInstance = Adapt.instance; + plugin = mock(Adapt.class); + ticker = mock(Ticker.class); + lenient().when(plugin.getDataFolder()).thenReturn(dataFolder); + lenient().when(plugin.getTicker()).thenReturn(ticker); + lenient().when(plugin.getLogger()).thenReturn(Logger.getLogger("AdaptTest")); + Adapt.instance = plugin; + } + + @AfterEach + void restoreInstance() { + Adapt.instance = previousInstance; + } +} diff --git a/src/test/java/art/arcane/adapt/ConcurrencyStressTest.java b/src/test/java/art/arcane/adapt/ConcurrencyStressTest.java new file mode 100644 index 000000000..7eb7a105f --- /dev/null +++ b/src/test/java/art/arcane/adapt/ConcurrencyStressTest.java @@ -0,0 +1,104 @@ +package art.arcane.adapt; + +import art.arcane.adapt.api.tick.Ticked; +import art.arcane.adapt.api.tick.Ticker; +import art.arcane.adapt.api.world.PlayerData; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +class ConcurrencyStressTest extends AdaptTestBase { + + @Test + @DisplayName("concurrent addStat on one player loses no updates") + void concurrentAddStatNoLostUpdates() throws Exception { + PlayerData data = new PlayerData(); + int threads = 16; + int perThread = 5000; + ExecutorService pool = Executors.newFixedThreadPool(threads); + CountDownLatch start = new CountDownLatch(1); + List> futures = new ArrayList<>(); + for (int i = 0; i < threads; i++) { + futures.add(pool.submit(() -> { + start.await(); + for (int j = 0; j < perThread; j++) { + data.addStat("blocks", 1.0); + } + return null; + })); + } + start.countDown(); + for (Future f : futures) { + f.get(); + } + pool.shutdownNow(); + assertThat(data.getStat("blocks")).isEqualTo((double) threads * perThread); + } + + @Test + @DisplayName("concurrent register, unregister and tick on the Ticker stays consistent") + void concurrentTickerAccessIsSafe() throws Exception { + Ticker ticker = new Ticker(); + Method tick = Ticker.class.getDeclaredMethod("tick"); + tick.setAccessible(true); + List tickeds = new ArrayList<>(); + for (int i = 0; i < 8; i++) { + Ticked m = mock(Ticked.class); + lenient().when(m.shouldTick()).thenReturn(true); + lenient().when(m.getId()).thenReturn("t" + i); + lenient().when(m.getGroup()).thenReturn("g"); + lenient().when(m.getInterval()).thenReturn(0L); + lenient().when(m.getLastTick()).thenReturn(0L); + tickeds.add(m); + } + AtomicBoolean stop = new AtomicBoolean(false); + AtomicReference error = new AtomicReference<>(); + ExecutorService ex = Executors.newFixedThreadPool(6); + ex.submit(() -> { + try { + while (!stop.get()) { + tick.invoke(ticker); + } + } catch (Throwable e) { + error.set(e); + } + }); + List> futures = new ArrayList<>(); + for (int w = 0; w < 4; w++) { + futures.add(ex.submit(() -> { + try { + for (int j = 0; j < 3000; j++) { + Ticked m = tickeds.get(j % tickeds.size()); + ticker.register(m); + ticker.unregister(m); + } + } catch (Throwable e) { + error.set(e); + } + return null; + })); + } + for (Future f : futures) { + f.get(); + } + stop.set(true); + ex.shutdownNow(); + ex.awaitTermination(5, TimeUnit.SECONDS); + ticker.clear(); + assertThat(error.get()).isNull(); + } +} diff --git a/src/test/java/art/arcane/adapt/LoadHarnessTest.java b/src/test/java/art/arcane/adapt/LoadHarnessTest.java new file mode 100644 index 000000000..0c9c28043 --- /dev/null +++ b/src/test/java/art/arcane/adapt/LoadHarnessTest.java @@ -0,0 +1,103 @@ +package art.arcane.adapt; + +import art.arcane.adapt.api.EventHandlerInvoker; +import art.arcane.adapt.api.world.PlayerData; +import art.arcane.adapt.api.world.PlayerSkillLine; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class LoadHarnessTest extends AdaptTestBase { + + public static class LoadEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + } + + public static class NoopListener implements Listener { + public void onLoad(LoadEvent e) { + } + } + + @Test + @DisplayName("simulated 1000-player tick of dispatch and xp work stays within budget (report only)") + void thousandPlayerLoadReport() throws Exception { + int players = 1000; + List data = new ArrayList<>(players); + List lines = new ArrayList<>(players); + for (int i = 0; i < players; i++) { + data.add(new PlayerData()); + lines.add(new PlayerSkillLine()); + } + Method handler = NoopListener.class.getDeclaredMethod("onLoad", LoadEvent.class); + handler.setAccessible(true); + EventExecutor executor = EventHandlerInvoker.createExecutor(handler, LoadEvent.class); + NoopListener listener = new NoopListener(); + LoadEvent event = new LoadEvent(); + + Runnable oneTick = () -> { + for (int i = 0; i < players; i++) { + PlayerData pd = data.get(i); + pd.addStat("blocks", 1.0); + PlayerSkillLine line = lines.get(i); + line.giveXP(null, 1.0); + if ((i & 7) == 0) { + line.flushXpPool(null); + } + try { + executor.execute(listener, event); + executor.execute(listener, event); + executor.execute(listener, event); + } catch (Exception ignored) { + } + } + }; + + for (int warmup = 0; warmup < 3; warmup++) { + oneTick.run(); + } + + int samples = 7; + long[] nanos = new long[samples]; + for (int s = 0; s < samples; s++) { + long t0 = System.nanoTime(); + oneTick.run(); + nanos[s] = System.nanoTime() - t0; + } + Arrays.sort(nanos); + long medianNanos = nanos[samples / 2]; + double msPerTick = medianNanos / 1_000_000.0; + double tickBudgetMs = 50.0; + double budgetPercent = (msPerTick / tickBudgetMs) * 100.0; + String verdict = msPerTick <= tickBudgetMs ? "PASS" : "WARN"; + + System.out.println("==================== ADAPT 1K LOAD HARNESS ===================="); + System.out.println("players : " + players); + System.out.println("work per player/tick : addStat + giveXP (flush 1/8) + 3 LMF event dispatches"); + System.out.printf("median tick time : %.3f ms%n", msPerTick); + System.out.printf("server tick budget : %.1f ms (one 20 TPS tick)%n", tickBudgetMs); + System.out.printf("budget used : %.2f %% [%s]%n", budgetPercent, verdict); + System.out.println("==============================================================="); + + assertThat(data).hasSize(players); + assertThat(medianNanos).isGreaterThan(0L); + } +} diff --git a/src/test/java/art/arcane/adapt/api/EventHandlerInvokerTest.java b/src/test/java/art/arcane/adapt/api/EventHandlerInvokerTest.java new file mode 100644 index 000000000..3f57bb7b5 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/EventHandlerInvokerTest.java @@ -0,0 +1,107 @@ +package art.arcane.adapt.api; + +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class EventHandlerInvokerTest { + + public static class TestEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + } + + public static class OtherEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } + } + + public static class PublicListener implements Listener { + final AtomicInteger hits = new AtomicInteger(); + + public void onTest(TestEvent e) { + hits.incrementAndGet(); + } + } + + public static class PrivateListener implements Listener { + final AtomicInteger hits = new AtomicInteger(); + + private void onTest(TestEvent e) { + hits.incrementAndGet(); + } + } + + public static class ThrowingListener implements Listener { + public void onTest(TestEvent e) { + throw new IllegalStateException("boom"); + } + } + + private Method handler(Class type) throws NoSuchMethodException { + Method m = type.getDeclaredMethod("onTest", TestEvent.class); + m.setAccessible(true); + return m; + } + + @Test + @DisplayName("createExecutor dispatches to a public handler method") + void publicDispatch() throws Exception { + PublicListener l = new PublicListener(); + EventExecutor ex = EventHandlerInvoker.createExecutor(handler(PublicListener.class), TestEvent.class); + ex.execute(l, new TestEvent()); + assertThat(l.hits.get()).isEqualTo(1); + } + + @Test + @DisplayName("createExecutor dispatches to a private handler method") + void privateDispatch() throws Exception { + PrivateListener l = new PrivateListener(); + EventExecutor ex = EventHandlerInvoker.createExecutor(handler(PrivateListener.class), TestEvent.class); + ex.execute(l, new TestEvent()); + assertThat(l.hits.get()).isEqualTo(1); + } + + @Test + @DisplayName("createExecutor ignores events of the wrong type") + void typeMismatchSkipped() throws Exception { + PublicListener l = new PublicListener(); + EventExecutor ex = EventHandlerInvoker.createExecutor(handler(PublicListener.class), TestEvent.class); + ex.execute(l, new OtherEvent()); + assertThat(l.hits.get()).isZero(); + } + + @Test + @DisplayName("createExecutor wraps handler exceptions in EventException") + void exceptionWrapped() throws Exception { + ThrowingListener l = new ThrowingListener(); + EventExecutor ex = EventHandlerInvoker.createExecutor(handler(ThrowingListener.class), TestEvent.class); + assertThatThrownBy(() -> ex.execute(l, new TestEvent())).isInstanceOf(EventException.class); + } +} diff --git a/src/test/java/art/arcane/adapt/api/SkillIdentityRegressionTest.java b/src/test/java/art/arcane/adapt/api/SkillIdentityRegressionTest.java new file mode 100644 index 000000000..39c4b41e6 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/SkillIdentityRegressionTest.java @@ -0,0 +1,47 @@ +package art.arcane.adapt.api; + +import art.arcane.adapt.api.adaptation.SimpleAdaptation; +import art.arcane.adapt.api.skill.SimpleSkill; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class SkillIdentityRegressionTest { + + @Test + @DisplayName("skills compare by identity, not by field value") + void skillsUseIdentityEquality() { + SimpleSkill a = mock(SimpleSkill.class); + SimpleSkill b = mock(SimpleSkill.class); + assertThat(a).isEqualTo(a); + assertThat(a).isNotEqualTo(b); + assertThat(a.hashCode()).isEqualTo(System.identityHashCode(a)); + } + + @Test + @DisplayName("adaptations compare by identity, not by field value") + void adaptationsUseIdentityEquality() { + SimpleAdaptation a = mock(SimpleAdaptation.class); + SimpleAdaptation b = mock(SimpleAdaptation.class); + assertThat(a).isEqualTo(a); + assertThat(a).isNotEqualTo(b); + assertThat(a.hashCode()).isEqualTo(System.identityHashCode(a)); + } + + @Test + @DisplayName("skills and adaptations are recursion-safe as map keys") + void identityHashingIsRecursionSafe() { + SimpleSkill skill = mock(SimpleSkill.class); + SimpleAdaptation adaptation = mock(SimpleAdaptation.class); + Map map = new HashMap<>(); + map.put(skill, "skill"); + map.put(adaptation, "adaptation"); + assertThat(map.get(skill)).isEqualTo("skill"); + assertThat(map.get(adaptation)).isEqualTo("adaptation"); + } +} diff --git a/src/test/java/art/arcane/adapt/api/tick/TickerTest.java b/src/test/java/art/arcane/adapt/api/tick/TickerTest.java new file mode 100644 index 000000000..8ac56f282 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/tick/TickerTest.java @@ -0,0 +1,72 @@ +package art.arcane.adapt.api.tick; + +import art.arcane.adapt.AdaptTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +class TickerTest extends AdaptTestBase { + + private static Method tickMethod() throws NoSuchMethodException { + Method m = Ticker.class.getDeclaredMethod("tick"); + m.setAccessible(true); + return m; + } + + private static Ticked baseMock(String id) { + Ticked t = mock(Ticked.class); + lenient().when(t.shouldTick()).thenReturn(true); + lenient().when(t.getId()).thenReturn(id); + lenient().when(t.getGroup()).thenReturn("test"); + lenient().when(t.getInterval()).thenReturn(0L); + lenient().when(t.getLastTick()).thenReturn(0L); + return t; + } + + @Test + @DisplayName("an exception thrown by one ticked does not stop the others") + void exceptionIsolation() throws Exception { + Ticker t = new Ticker(); + AtomicInteger goodTicks = new AtomicInteger(); + Ticked good = baseMock("good"); + doAnswer(invocation -> { + goodTicks.incrementAndGet(); + return null; + }).when(good).tick(); + Ticked bad = baseMock("bad"); + doThrow(new RuntimeException("boom")).when(bad).tick(); + t.register(good); + t.register(bad); + Method tick = tickMethod(); + for (int i = 0; i < 4; i++) { + tick.invoke(t); + } + assertThat(goodTicks.get()).isGreaterThan(0); + t.clear(); + } + + @Test + @DisplayName("registering and ticking many objects does not throw") + void registerAndTickManyDoesNotThrow() throws Exception { + Ticker t = new Ticker(); + for (int i = 0; i < 200; i++) { + t.register(baseMock("t" + i)); + } + Method tick = tickMethod(); + assertThatCode(() -> { + for (int i = 0; i < 5; i++) { + tick.invoke(t); + } + }).doesNotThrowAnyException(); + t.clear(); + } +} diff --git a/src/test/java/art/arcane/adapt/api/world/PlayerDataTest.java b/src/test/java/art/arcane/adapt/api/world/PlayerDataTest.java new file mode 100644 index 000000000..7284fa68c --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/world/PlayerDataTest.java @@ -0,0 +1,64 @@ +package art.arcane.adapt.api.world; + +import art.arcane.adapt.AdaptTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PlayerDataTest extends AdaptTestBase { + + @Test + @DisplayName("addStat accumulates by sum") + void addStatAccumulates() { + PlayerData d = new PlayerData(); + d.addStat("blocks", 3.0); + d.addStat("blocks", 4.0); + assertThat(d.getStat("blocks")).isEqualTo(7.0); + } + + @Test + @DisplayName("an absent stat reads as zero") + void absentStatReadsZero() { + assertThat(new PlayerData().getStat("nope")).isEqualTo(0.0); + } + + @Test + @DisplayName("stats survive a json round trip") + void statsSurviveJsonRoundTrip() { + PlayerData d = new PlayerData(); + d.addStat("mined", 10.0); + d.addStat("crafted", 2.5); + PlayerData back = PlayerData.fromJson(d.toJson(false)); + assertThat(back.getStat("mined")).isEqualTo(10.0); + assertThat(back.getStat("crafted")).isEqualTo(2.5); + } + + @Test + @DisplayName("globalXPMultiplier registers a multiplier") + void globalMultiplierIsRegistered() { + PlayerData d = new PlayerData(); + int before = d.getMultipliers().size(); + d.globalXPMultiplier(0.5, 60000); + assertThat(d.getMultipliers().size()).isEqualTo(before + 1); + } + + @Test + @DisplayName("granting master xp raises the player level") + void masterXpRaisesLevel() { + PlayerData d = new PlayerData(); + int start = d.getLevel(); + d.giveMasterXp(250000.0); + assertThat(d.getLevel()).isGreaterThanOrEqualTo(start); + assertThat(d.getMasterXp()).isGreaterThan(0.0); + } + + @Test + @DisplayName("clearStats empties the stat map") + void clearStatsEmpties() { + PlayerData d = new PlayerData(); + d.addStat("a", 1.0); + d.clearStats(); + assertThat(d.getStat("a")).isEqualTo(0.0); + } +} diff --git a/src/test/java/art/arcane/adapt/api/world/PlayerSkillLineTest.java b/src/test/java/art/arcane/adapt/api/world/PlayerSkillLineTest.java new file mode 100644 index 000000000..43148f216 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/world/PlayerSkillLineTest.java @@ -0,0 +1,53 @@ +package art.arcane.adapt.api.world; + +import art.arcane.adapt.AdaptTestBase; +import art.arcane.adapt.api.adaptation.Adaptation; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +class PlayerSkillLineTest extends AdaptTestBase { + + @Test + @DisplayName("granted xp is realized after the pool is flushed") + void grantedXpRealizedAfterFlush() { + PlayerSkillLine line = new PlayerSkillLine(); + line.giveXP(null, 100.0); + line.flushXpPool(null); + assertThat(line.getXp()).isGreaterThan(0.0); + } + + @Test + @DisplayName("knowledge can be granted and spent") + void knowledgeGrantAndSpend() { + PlayerSkillLine line = new PlayerSkillLine(); + line.giveKnowledge(100L); + assertThat(line.spendKnowledge(30)).isTrue(); + assertThat(line.getKnowledge()).isEqualTo(70L); + } + + @Test + @DisplayName("spending more knowledge than available fails and leaves the balance intact") + void overspendingKnowledgeFails() { + PlayerSkillLine line = new PlayerSkillLine(); + line.giveKnowledge(10L); + assertThat(line.spendKnowledge(1000)).isFalse(); + assertThat(line.getKnowledge()).isEqualTo(10L); + } + + @Test + @DisplayName("adaptation levels are stored and cleared by level zero") + void adaptationLevelStoredAndCleared() { + Adaptation a = mock(Adaptation.class); + lenient().when(a.getName()).thenReturn("testadapt"); + lenient().when(a.getMaxLevel()).thenReturn(10); + PlayerSkillLine line = new PlayerSkillLine(); + line.setAdaptation(a, 5); + assertThat(line.getAdaptationLevel("testadapt")).isEqualTo(5); + line.setAdaptation(a, 0); + assertThat(line.getAdaptationLevel("testadapt")).isEqualTo(0); + } +} diff --git a/src/test/java/art/arcane/adapt/api/xp/SpatialXpTest.java b/src/test/java/art/arcane/adapt/api/xp/SpatialXpTest.java new file mode 100644 index 000000000..d3980e3d6 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/xp/SpatialXpTest.java @@ -0,0 +1,40 @@ +package art.arcane.adapt.api.xp; + +import org.bukkit.Location; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class SpatialXpTest { + + @Test + @DisplayName("constructor exposes location, xp and radius") + void constructorExposesFields() { + Location loc = mock(Location.class); + SpatialXP s = new SpatialXP(loc, null, 5.0, 3.0, 1000L); + assertThat(s.getXp()).isEqualTo(5.0); + assertThat(s.getRadius()).isEqualTo(3.0); + assertThat(s.getLocation()).isSameAs(loc); + } + + @Test + @DisplayName("expiry timestamp is in the future for a positive duration") + void expiryInFutureForPositiveDuration() { + Location loc = mock(Location.class); + SpatialXP s = new SpatialXP(loc, null, 1.0, 1.0, 5000L); + assertThat(s.getMs()).isGreaterThan(0L); + } + + @Test + @DisplayName("setters mutate the orb state") + void settersMutate() { + Location loc = mock(Location.class); + SpatialXP s = new SpatialXP(loc, null, 1.0, 1.0, 1000L); + s.setXp(9.0); + s.setRadius(7.0); + assertThat(s.getXp()).isEqualTo(9.0); + assertThat(s.getRadius()).isEqualTo(7.0); + } +} diff --git a/src/test/java/art/arcane/adapt/api/xp/XPMultiplierTest.java b/src/test/java/art/arcane/adapt/api/xp/XPMultiplierTest.java new file mode 100644 index 000000000..4c6ebb5bc --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/xp/XPMultiplierTest.java @@ -0,0 +1,44 @@ +package art.arcane.adapt.api.xp; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class XPMultiplierTest { + + @Test + @DisplayName("explicit constructor stores the multiplier") + void explicitConstructorStoresMultiplier() { + XPMultiplier m = new XPMultiplier(0.25, 60000L); + assertThat(m.getMultiplier()).isEqualTo(0.25); + } + + @Test + @DisplayName("a future expiry is not yet expired") + void futureExpiryNotExpired() { + assertThat(new XPMultiplier(0.5, 60000L).isExpired()).isFalse(); + } + + @Test + @DisplayName("a past expiry is already expired") + void pastExpiryIsExpired() { + assertThat(new XPMultiplier(0.5, -1000L).isExpired()).isTrue(); + } + + @Test + @DisplayName("no-arg constructor yields an unexpired default window") + void noArgConstructorUnexpired() { + assertThat(new XPMultiplier().isExpired()).isFalse(); + } + + @Test + @DisplayName("equal field values are equal") + void equalityByValue() { + XPMultiplier a = new XPMultiplier(0.5, 60000L); + XPMultiplier b = new XPMultiplier(0.5, 60000L); + b.setGoodFor(a.getGoodFor()); + assertThat(a).isEqualTo(b); + assertThat(a.hashCode()).isEqualTo(b.hashCode()); + } +} diff --git a/src/test/java/art/arcane/adapt/api/xp/XpMathTest.java b/src/test/java/art/arcane/adapt/api/xp/XpMathTest.java new file mode 100644 index 000000000..ed7c6569d --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/xp/XpMathTest.java @@ -0,0 +1,50 @@ +package art.arcane.adapt.api.xp; + +import art.arcane.adapt.AdaptTestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; + +class XpMathTest extends AdaptTestBase { + + @Test + @DisplayName("xp required for a level increases monotonically") + void xpForLevelIsMonotonic() { + double previous = Double.NEGATIVE_INFINITY; + for (int level = 1; level <= 100; level++) { + double xp = XP.getXpForLevel(level); + assertThat(xp).isGreaterThan(previous); + previous = xp; + } + } + + @Test + @DisplayName("getLevelForXp inverts getXpForLevel") + void levelForXpInvertsXpForLevel() { + for (int level = 1; level <= 100; level++) { + double xp = XP.getXpForLevel(level); + double recovered = XP.getLevelForXp(xp); + assertThat(recovered).isCloseTo(level, within(0.05)); + } + } + + @Test + @DisplayName("level progress stays within [0, 1)") + void levelProgressWithinUnitInterval() { + for (double xp = 0.0; xp < 500000.0; xp += 137.0) { + double progress = XP.getLevelProgress(xp); + assertThat(progress).isGreaterThanOrEqualTo(0.0); + assertThat(progress).isLessThan(1.0); + } + } + + @Test + @DisplayName("xp until level up is never negative") + void xpUntilLevelUpNeverNegative() { + for (double xp = 0.0; xp < 500000.0; xp += 211.0) { + assertThat(XP.getXpUntilLevelUp(xp)).isGreaterThanOrEqualTo(0.0); + } + } +} diff --git a/src/test/java/art/arcane/adapt/api/xp/XpNoveltyTest.java b/src/test/java/art/arcane/adapt/api/xp/XpNoveltyTest.java new file mode 100644 index 000000000..d5f1cff61 --- /dev/null +++ b/src/test/java/art/arcane/adapt/api/xp/XpNoveltyTest.java @@ -0,0 +1,83 @@ +package art.arcane.adapt.api.xp; + +import art.arcane.adapt.AdaptTestBase; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +class XpNoveltyTest extends AdaptTestBase { + + private Player stillPlayerAt(UUID id, Location loc) { + Player p = mock(Player.class); + lenient().when(p.getUniqueId()).thenReturn(id); + lenient().when(p.getLocation()).thenReturn(loc); + lenient().when(p.getLocation(any(Location.class))).thenReturn(loc); + return p; + } + + private Location fixedLocation() { + World world = mock(World.class); + lenient().when(world.getName()).thenReturn("world"); + Location loc = mock(Location.class, RETURNS_DEEP_STUBS); + lenient().when(loc.getWorld()).thenReturn(world); + lenient().when(loc.getBlockX()).thenReturn(100); + lenient().when(loc.getBlockY()).thenReturn(64); + lenient().when(loc.getBlockZ()).thenReturn(100); + lenient().when(loc.getX()).thenReturn(100.0); + lenient().when(loc.getY()).thenReturn(64.0); + lenient().when(loc.getZ()).thenReturn(100.0); + return loc; + } + + @Test + @DisplayName("the first novelty award sits within (0, 1]") + void firstAwardWithinUnit() { + UUID id = UUID.randomUUID(); + Location loc = fixedLocation(); + double first = XpNovelty.noveltyMultiplier(stillPlayerAt(id, loc), loc, "mine-stone"); + assertThat(first).isGreaterThan(0.0).isLessThanOrEqualTo(1.0); + XpNovelty.clear(id); + } + + @Test + @DisplayName("repeating the same action in place decays the multiplier") + void repeatedActionDecays() { + UUID id = UUID.randomUUID(); + Location loc = fixedLocation(); + Player p = stillPlayerAt(id, loc); + double first = XpNovelty.noveltyMultiplier(p, loc, "mine-stone"); + double last = first; + for (int i = 0; i < 30; i++) { + last = XpNovelty.noveltyMultiplier(p, loc, "mine-stone"); + } + assertThat(last).isGreaterThan(0.0); + assertThat(last).isLessThanOrEqualTo(first); + XpNovelty.clear(id); + } + + @Test + @DisplayName("clearing a player resets the decay") + void clearResetsDecay() { + UUID id = UUID.randomUUID(); + Location loc = fixedLocation(); + Player p = stillPlayerAt(id, loc); + double decayed = 1.0; + for (int i = 0; i < 30; i++) { + decayed = XpNovelty.noveltyMultiplier(p, loc, "mine-stone"); + } + XpNovelty.clear(id); + double afterClear = XpNovelty.noveltyMultiplier(p, loc, "mine-stone"); + assertThat(afterClear).isGreaterThanOrEqualTo(decayed); + XpNovelty.clear(id); + } +}